summaryrefslogtreecommitdiffstats
path: root/src/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/Qt3DSActivationManager.cpp1027
-rw-r--r--src/runtime/Qt3DSActivationManager.h157
-rw-r--r--src/runtime/Qt3DSAnimationSystem.cpp432
-rw-r--r--src/runtime/Qt3DSAnimationSystem.h68
-rw-r--r--src/runtime/Qt3DSApplication.cpp2180
-rw-r--r--src/runtime/Qt3DSApplication.h261
-rw-r--r--src/runtime/Qt3DSApplicationValues.h369
-rw-r--r--src/runtime/Qt3DSAttributeHashes.cpp296
-rw-r--r--src/runtime/Qt3DSAttributeHashes.h286
-rw-r--r--src/runtime/Qt3DSAttributeHashes.txt249
-rw-r--r--src/runtime/Qt3DSCommandEventTypes.h83
-rw-r--r--src/runtime/Qt3DSCommandHelper.cpp129
-rw-r--r--src/runtime/Qt3DSCommandHelper.h55
-rw-r--r--src/runtime/Qt3DSComponentManager.cpp487
-rw-r--r--src/runtime/Qt3DSComponentManager.h143
-rw-r--r--src/runtime/Qt3DSElementHelper.cpp141
-rw-r--r--src/runtime/Qt3DSElementHelper.h57
-rw-r--r--src/runtime/Qt3DSElementSystem.cpp855
-rw-r--r--src/runtime/Qt3DSElementSystem.h616
-rw-r--r--src/runtime/Qt3DSEvent.h62
-rw-r--r--src/runtime/Qt3DSEventCallbacks.cpp258
-rw-r--r--src/runtime/Qt3DSEventCallbacks.h135
-rw-r--r--src/runtime/Qt3DSFrameworkTypes.h52
-rw-r--r--src/runtime/Qt3DSIComponentManager.h147
-rw-r--r--src/runtime/Qt3DSIInputSystem.h62
-rw-r--r--src/runtime/Qt3DSIScene.h160
-rw-r--r--src/runtime/Qt3DSIScriptBridge.h206
-rw-r--r--src/runtime/Qt3DSIStateful.h69
-rw-r--r--src/runtime/Qt3DSIText.h52
-rw-r--r--src/runtime/Qt3DSInputDefs.h413
-rw-r--r--src/runtime/Qt3DSInputEngine.cpp849
-rw-r--r--src/runtime/Qt3DSInputEngine.h125
-rw-r--r--src/runtime/Qt3DSInputEventTypes.h96
-rw-r--r--src/runtime/Qt3DSInputFrame.h118
-rw-r--r--src/runtime/Qt3DSKernelTypes.h225
-rw-r--r--src/runtime/Qt3DSLogicSystem.cpp293
-rw-r--r--src/runtime/Qt3DSLogicSystem.h60
-rw-r--r--src/runtime/Qt3DSOutputMemoryStream.cpp150
-rw-r--r--src/runtime/Qt3DSOutputMemoryStream.h123
-rw-r--r--src/runtime/Qt3DSParametersSystem.cpp171
-rw-r--r--src/runtime/Qt3DSParametersSystem.h66
-rw-r--r--src/runtime/Qt3DSPickFrame.h70
-rw-r--r--src/runtime/Qt3DSPresentation.cpp841
-rw-r--r--src/runtime/Qt3DSPresentation.h231
-rw-r--r--src/runtime/Qt3DSPresentationFrameData.cpp105
-rw-r--r--src/runtime/Qt3DSPresentationFrameData.h87
-rw-r--r--src/runtime/Qt3DSQmlElementHelper.cpp323
-rw-r--r--src/runtime/Qt3DSQmlElementHelper.h57
-rw-r--r--src/runtime/Qt3DSQmlEngine.cpp2690
-rw-r--r--src/runtime/Qt3DSQmlEngine.h226
-rw-r--r--src/runtime/Qt3DSRuntimeFactory.h108
-rw-r--r--src/runtime/Qt3DSSceneManager.h167
-rw-r--r--src/runtime/Qt3DSSlideSystem.cpp675
-rw-r--r--src/runtime/Qt3DSSlideSystem.h161
-rw-r--r--src/runtime/Qt3DSTimePolicy.cpp407
-rw-r--r--src/runtime/Qt3DSTimePolicy.h124
-rw-r--r--src/runtime/RuntimePrefix.h104
-rw-r--r--src/runtime/q3dsmaterialdefinitionparser.cpp138
-rw-r--r--src/runtime/q3dsmaterialdefinitionparser.h49
-rw-r--r--src/runtime/q3dsqmlbehavior.cpp123
-rw-r--r--src/runtime/q3dsqmlbehavior.h83
-rw-r--r--src/runtime/q3dsqmlscript.cpp742
-rw-r--r--src/runtime/q3dsqmlscript.h109
-rw-r--r--src/runtime/q3dsvariantconfig.cpp122
-rw-r--r--src/runtime/q3dsvariantconfig_p.h79
65 files changed, 19604 insertions, 0 deletions
diff --git a/src/runtime/Qt3DSActivationManager.cpp b/src/runtime/Qt3DSActivationManager.cpp
new file mode 100644
index 0000000..3d05274
--- /dev/null
+++ b/src/runtime/Qt3DSActivationManager.cpp
@@ -0,0 +1,1027 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "RuntimePrefix.h"
+#include "Qt3DSActivationManager.h"
+#include "Qt3DSElementSystem.h"
+#include "Qt3DSComponentManager.h"
+#include "foundation/Qt3DSFlags.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSInvasiveSet.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/StringTable.h"
+#include "EASTL/sort.h"
+#include "Qt3DSAttributeHashes.h"
+#include "Qt3DSPresentation.h"
+#include "foundation/Qt3DSPerfTimer.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSSync.h"
+#include "Qt3DSRenderThreadPool.h"
+
+using namespace qt3ds::runtime;
+using namespace qt3ds::runtime::element;
+using qt3ds::render::IThreadPool;
+using qt3ds::foundation::Time;
+
+namespace {
+
+typedef nvvector<TElementAndSortKey> TElementAndSortKeyList;
+
+struct STimeEvent
+{
+ enum Type {
+ Start = 0,
+ Stop,
+ };
+
+ Type m_Type;
+ TTimeUnit m_Time;
+ nvvector<SElement *> m_AffectedNodes;
+
+ STimeEvent(Type inType, NVAllocatorCallback &alloc, TTimeUnit inTime)
+ : m_Type(inType)
+ , m_Time(inTime)
+ , m_AffectedNodes(alloc, "TimeEvent")
+ {
+ }
+
+ void Reset()
+ {
+ m_Time = 0;
+ m_AffectedNodes.clear();
+ }
+};
+
+struct STimeEventGreaterThan
+{
+ bool operator()(const STimeEvent *lhs, const STimeEvent *rhs) const
+ {
+ if (lhs->m_Time == rhs->m_Time)
+ return lhs->m_Type > rhs->m_Type;
+
+ return lhs->m_Time > rhs->m_Time;
+ }
+};
+
+struct STimeEventLessThan
+{
+ bool operator()(const STimeEvent *lhs, const STimeEvent *rhs) const
+ {
+ if (lhs->m_Time == rhs->m_Time)
+ return lhs->m_Type < rhs->m_Type;
+
+ return lhs->m_Time < rhs->m_Time;
+ }
+};
+
+typedef nvvector<STimeEvent *> TTimeEventList;
+struct STimeContext;
+typedef NVDataRef<NVScopedRefCounted<STimeContext>> TTimeContextSet;
+
+// Tree navigation
+
+SElement *GetElementNodeFirstChild(SElement &inNode)
+{
+ return inNode.m_Child;
+}
+SElement *GetElementNodeNextSibling(SElement &inNode)
+{
+ return inNode.m_Sibling;
+}
+SElement *GetElementNodeParent(SElement &inNode)
+{
+ return inNode.m_Parent;
+}
+
+struct SScanBufferEntry
+{
+ SElement *m_Node;
+ QT3DSU32 m_StartTime;
+ QT3DSU32 m_EndTime;
+
+ SScanBufferEntry()
+ : m_Node(NULL)
+ , m_StartTime(0)
+ , m_EndTime(QT3DS_MAX_U32)
+ {
+ }
+ SScanBufferEntry(SElement *inNode)
+ : m_Node(inNode)
+ , m_StartTime(0)
+ , m_EndTime(QT3DS_MAX_U32)
+ {
+ }
+ SScanBufferEntry(SElement *inNode, QT3DSU32 start, QT3DSU32 end)
+ : m_Node(inNode)
+ , m_StartTime(start)
+ , m_EndTime(end)
+ {
+ }
+
+ SScanBufferEntry(SElement *inNode, bool inParentActive)
+ : m_Node(inNode)
+ , m_StartTime(inParentActive ? 1 : 0)
+ , m_EndTime(0)
+ {
+ }
+
+ bool IsParentActive() const { return m_StartTime ? true : false; }
+};
+
+typedef nvvector<SScanBufferEntry> TScanBuffer;
+typedef nvvector<SElement *> TElementNodePtrList;
+
+struct SElementIndexPairSorter
+{
+ bool operator()(const eastl::pair<element::SElement *, QT3DSU32> &lhs,
+ const eastl::pair<element::SElement *, QT3DSU32> &rhs) const
+ {
+ return lhs.second < rhs.second;
+ }
+};
+
+struct SElementPtrSort
+{
+ bool operator()(const SElement *lhs, const SElement *rhs)
+ {
+ return lhs->Depth() < rhs->Depth();
+ }
+};
+
+DEFINE_INVASIVE_SINGLE_LIST(TimeContext);
+
+struct STimeContext
+{
+ NVAllocatorCallback &m_Allocator;
+ SComponent &m_Component;
+ CTimePolicy m_TimePolicy;
+ SComponentTimePolicyOverride *m_TimePolicyOverride;
+ TTimeUnit m_CurrentTime;
+ TElementNodePtrList m_Elements;
+ TTimeEventList m_TimeEventBackingStore;
+ TTimeEventList m_TimeEvents;
+ SActivationManagerNodeDirtyList m_DirtyList;
+ QT3DSU32 m_TimeEventBackingStoreIndex;
+ QT3DSI32 mRefCount;
+ STimeContext *m_NextSibling;
+ TTimeContextList m_Children;
+ Mutex &m_ElementAccessMutex;
+ bool m_AllNodesDirty;
+ bool m_TimeDataNeedsUpdate;
+
+ STimeContext(NVAllocatorCallback &alloc, SComponent &inComponent, Mutex &inElementAccessMutex)
+ : m_Allocator(alloc)
+ , m_Component(inComponent)
+ , m_TimePolicyOverride(NULL)
+ , m_CurrentTime(-1)
+ , m_Elements(alloc, "m_Elements")
+ , m_TimeEventBackingStore(alloc, "TimeEventBackingStore")
+ , m_TimeEvents(alloc, "TimeEvents")
+ , m_DirtyList(alloc)
+ , m_TimeEventBackingStoreIndex(0)
+ , mRefCount(0)
+ , m_NextSibling(NULL)
+ , m_ElementAccessMutex(inElementAccessMutex)
+ , m_AllNodesDirty(true)
+ , m_TimeDataNeedsUpdate(true)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+ ~STimeContext()
+ {
+ Reset();
+ for (TTimeEventList::iterator iter = m_TimeEventBackingStore.begin(),
+ end = m_TimeEventBackingStore.end();
+ iter != end; ++iter) {
+ NVDelete(m_Allocator, *iter);
+ }
+
+ m_TimeEventBackingStore.clear();
+ RemoveOverride();
+ }
+
+ void Reset()
+ {
+ for (TTimeEventList::iterator iter = m_TimeEvents.begin(), end = m_TimeEvents.end();
+ iter != end; ++iter)
+ (*iter)->Reset();
+
+ m_TimeEvents.clear();
+ m_TimeEventBackingStoreIndex = 0;
+ m_CurrentTime = -1;
+ m_DirtyList.clear();
+ m_AllNodesDirty = true;
+ m_TimeDataNeedsUpdate = true;
+ }
+
+ STimeEvent &GetTimeEventForTime(TTimeUnit inTime, STimeEvent::Type inType)
+ {
+ TTimeEventList::iterator inserter = m_TimeEvents.end();
+ for (TTimeEventList::iterator iter = m_TimeEvents.begin(), end = m_TimeEvents.end();
+ iter != end && inserter == end; ++iter) {
+ if ((*iter)->m_Time == inTime) {
+ if ((*iter)->m_Type == inType)
+ return **iter;
+ // Start comes before stop
+ if ((*iter)->m_Type == STimeEvent::Stop)
+ inserter = iter;
+ }
+
+ if ((*iter)->m_Time > inTime)
+ inserter = iter;
+ }
+
+ if (m_TimeEventBackingStoreIndex == m_TimeEventBackingStore.size())
+ m_TimeEventBackingStore.push_back(
+ QT3DS_NEW(m_Allocator, STimeEvent)(inType, m_Allocator, inTime));
+
+ STimeEvent *newEvent = m_TimeEventBackingStore[m_TimeEventBackingStoreIndex];
+ ++m_TimeEventBackingStoreIndex;
+
+ newEvent->m_Time = inTime;
+ newEvent->m_Type = inType;
+ newEvent->m_AffectedNodes.clear();
+
+ if (inserter == m_TimeEvents.end())
+ m_TimeEvents.push_back(newEvent);
+ else
+ m_TimeEvents.insert(inserter, newEvent);
+
+ return *newEvent;
+ };
+
+ void UpdateNodeList(nvvector<SElement *> &inList, bool timeActive)
+ {
+ for (QT3DSU32 elemIdx = 0, endIdx = inList.size(); elemIdx < endIdx; ++elemIdx) {
+ SActivationManagerNode &theNode = inList[elemIdx]->m_ActivationManagerNode;
+ if (timeActive != theNode.m_Flags.IsTimeActive()) {
+ theNode.m_Flags.SetTimeActive(timeActive);
+ SetElementDirty(*inList[elemIdx]);
+ }
+ }
+ }
+
+ // Returns true if we have reached the end time.
+ bool CalculateNewTime(TTimeUnit inGlobalTime)
+ {
+ Option<TTimeUnit> theNewTime;
+ if (m_TimePolicyOverride) {
+ SComponentTimePolicyOverride *theOverride = m_TimePolicyOverride;
+ eastl::pair<Q3DStudio::BOOL, TTimeUnit> theEvalResult =
+ theOverride->ComputeLocalTime(m_TimePolicy, inGlobalTime);
+ if (theEvalResult.first && theOverride->m_TimeCallback) {
+ Mutex::ScopedLock __locker(m_ElementAccessMutex);
+ theOverride->m_TimeCallback->OnTimeFinished();
+ theOverride->m_TimeCallback->Release();
+ theOverride->m_TimeCallback = NULL;
+ }
+ return false;
+ } else {
+ Q3DStudio::BOOL theReachedEndTime = m_TimePolicy.ComputeTime(inGlobalTime);
+ if (theReachedEndTime)
+ return true;
+
+ return false;
+ }
+ }
+
+ // There are four conditions this happens:
+ // 1. This object is getting destroyed
+ // 2. A new override is being set.
+ // 3. The compoent switched slides.
+ // 4. The component was deactivated.
+ void RemoveOverride()
+ {
+ if (m_TimePolicyOverride) {
+ if (m_TimePolicyOverride->m_TimeCallback) {
+ Mutex::ScopedLock __locker(m_ElementAccessMutex);
+ m_TimePolicyOverride->m_TimeCallback->Release();
+ m_TimePolicyOverride->m_TimeCallback = NULL;
+ }
+
+ NVDelete(m_Allocator, m_TimePolicyOverride);
+ m_TimePolicyOverride = NULL;
+ }
+ }
+
+ void UpdateLocalTime(TTimeUnit inNewTime, TScanBuffer &inScanBuffer, IActivityZone &inZone)
+ {
+ if (m_TimeDataNeedsUpdate) {
+ BuildTimeContext(inScanBuffer, inZone);
+ }
+
+ TTimeUnit newTime = inNewTime;
+ if (newTime != m_CurrentTime) {
+ SComponent &myNode = m_Component;
+ {
+ Mutex::ScopedLock __locker(m_ElementAccessMutex);
+ myNode.SetDirty();
+ }
+ STimeEvent searchEvent(STimeEvent::Start, m_Allocator, m_CurrentTime);
+ bool forward = newTime > m_CurrentTime;
+ m_CurrentTime = NVMax((TTimeUnit)0, newTime);
+
+ if (forward) {
+ TTimeEventList::iterator iter = eastl::lower_bound(
+ m_TimeEvents.begin(), m_TimeEvents.end(), &searchEvent, STimeEventLessThan());
+
+ for (TTimeEventList::iterator end = m_TimeEvents.end();
+ iter != end && (*iter)->m_Time <= newTime; ++iter) {
+ // Stop only works if the time is greater.
+ // start works if the time is equal.
+ bool isActive = true;
+ if ((*iter)->m_Type == STimeEvent::Stop)
+ isActive = newTime <= (*iter)->m_Time;
+
+ UpdateNodeList((*iter)->m_AffectedNodes, isActive);
+ }
+ } else {
+ searchEvent.m_Type = STimeEvent::Stop;
+ TTimeEventList::reverse_iterator iter =
+ eastl::lower_bound(m_TimeEvents.rbegin(), m_TimeEvents.rend(), &searchEvent,
+ STimeEventGreaterThan());
+
+ for (TTimeEventList::reverse_iterator end = m_TimeEvents.rend();
+ iter != end && (*iter)->m_Time >= newTime; ++iter) {
+ bool isActive = true;
+ if ((*iter)->m_Type == STimeEvent::Start)
+ isActive = newTime >= (*iter)->m_Time;
+
+ UpdateNodeList((*iter)->m_AffectedNodes, isActive);
+ }
+ }
+ }
+ }
+
+ void UpdateNodeElementInformation(SElement &inNode)
+ {
+ Q3DStudio::UVariant attValue;
+ if (inNode.GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue))
+ inNode.m_ActivationManagerNode.m_StartTime = (QT3DSU32)attValue.m_INT32;
+
+ if (inNode.GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue))
+ inNode.m_ActivationManagerNode.m_StopTime = (QT3DSU32)attValue.m_INT32;
+
+ inNode.m_ActivationManagerNode.m_Flags.SetTimeActive(false);
+ // Note that we don't set the script information here. The reason is that this requires the
+ // script list
+ // and we don't have access to it. Plus it can't change when a slide changes so it is kind
+ // of irrelevant.
+ }
+
+ bool BuildTimeContextElementNode(SElement &inNode, QT3DSU32 inGlobalMin, QT3DSU32 inGlobalMax)
+ {
+ // Note that just because a node itself does not participate in the time graph,
+ // this does not mean that its children do not participate in the time graph.
+ if (inNode.DoesParticipateInTimeGraph()) {
+ UpdateNodeElementInformation(inNode);
+ if (inNode.IsUserActive()) {
+ inGlobalMin = NVMax(inNode.m_ActivationManagerNode.m_StartTime, inGlobalMin);
+ inGlobalMax = NVMin(inNode.m_ActivationManagerNode.m_StopTime, inGlobalMax);
+ if (inGlobalMin < inGlobalMax) {
+ GetTimeEventForTime(inGlobalMin, STimeEvent::Start)
+ .m_AffectedNodes.push_back(&inNode);
+ GetTimeEventForTime(inGlobalMax, STimeEvent::Stop)
+ .m_AffectedNodes.push_back(&inNode);
+ }
+ }
+ } else {
+ inNode.m_ActivationManagerNode.m_Flags.SetTimeActive(true);
+ // Carry global min/max information down so children get setup correctly.
+ inNode.m_ActivationManagerNode.m_StartTime = inGlobalMin;
+ inNode.m_ActivationManagerNode.m_StopTime = inGlobalMax;
+ }
+ // Is it worth looking at children at all
+ return inNode.IsUserActive();
+ }
+
+ void BuildTimeContext(TScanBuffer &inScanBuffer, IActivityZone &inZone)
+ {
+ Mutex::ScopedLock __locker(m_ElementAccessMutex);
+ QT3DS_ASSERT(m_TimeDataNeedsUpdate);
+ m_TimeDataNeedsUpdate = false;
+ SComponent &myNode = m_Component;
+ SElement *theChild = myNode.m_Child;
+ if (theChild == NULL) {
+ return;
+ }
+ inScanBuffer.clear();
+ for (SElement *theChild = myNode.m_Child; theChild; theChild = theChild->m_Sibling) {
+ inScanBuffer.push_back(theChild);
+ }
+ for (QT3DSU32 idx = 0, end = (QT3DSU32)inScanBuffer.size(); idx < end; ++idx) {
+ SScanBufferEntry theEntry(inScanBuffer[idx]);
+ bool careAboutChildren = BuildTimeContextElementNode(
+ *theEntry.m_Node, theEntry.m_StartTime, theEntry.m_EndTime);
+ if (careAboutChildren && theEntry.m_Node->IsComponent() == false) {
+ for (SElement *theChild = theEntry.m_Node->m_Child; theChild;
+ theChild = theChild->m_Sibling) {
+ inScanBuffer.push_back(SScanBufferEntry(
+ theChild, theEntry.m_Node->m_ActivationManagerNode.m_StartTime,
+ theEntry.m_Node->m_ActivationManagerNode.m_StopTime));
+ }
+ end = (QT3DSU32)inScanBuffer.size();
+ }
+ if (theEntry.m_Node->IsComponent())
+ inZone.AddActivityItems(*theEntry.m_Node);
+ }
+ }
+
+ void SetElementDirty(SElement &inNode)
+ {
+ SElement *theComponentParent = &inNode.GetComponentParent();
+ if (inNode.IsComponent() && inNode.m_Parent)
+ theComponentParent = &inNode.m_Parent->GetComponentParent();
+
+ QT3DS_ASSERT(theComponentParent == &m_Component);
+ if (m_AllNodesDirty == false) {
+ // We check independent items every frame anyway so it isn't worth setting them dirty
+ // here.
+ if (inNode.IsIndependent() == false) {
+ m_DirtyList.insert(inNode);
+ }
+
+ // Dirty to root time context. This ensures that if we run an activate scan starting at
+ // a parent
+ // we won't have to re-run the scan at this node because it will not be dirty.
+ // This also makes sure that if we mark all nodes dirty we continue the activate scan
+ // till we hit at least
+ // this node.
+ SElement *theParent = inNode.m_Parent;
+ while (theParent) {
+ if (theParent->IsComponent()
+ || theParent->m_ActivationManagerNode.m_Flags.IsChildDirty())
+ theParent = NULL;
+ else {
+ SActivationManagerNode &theNode = theParent->m_ActivationManagerNode;
+ theNode.m_Flags.SetChildDirty();
+ theParent = theParent->m_Parent;
+ }
+ }
+ }
+ }
+
+ static void UpdateItemScriptStatus(SElement &inNode, bool activeAndHasScript,
+ TElementAndSortKeyList &scriptBuffer)
+ {
+ QT3DSU32 theIndex = inNode.Depth();
+ eastl::pair<element::SElement *, QT3DSU32> theInsertedItem(&inNode, theIndex);
+ if (activeAndHasScript) {
+ if (inNode.m_ActivationManagerNode.m_Flags.HasScript() == false) {
+ scriptBuffer.push_back(theInsertedItem);
+ inNode.m_ActivationManagerNode.m_Flags.SetHasScript(true);
+ }
+ } else {
+ if (inNode.m_ActivationManagerNode.m_Flags.HasScript() == true) {
+ TElementAndSortKeyList::iterator theFind =
+ eastl::find(scriptBuffer.begin(), scriptBuffer.end(), theInsertedItem);
+ if (theFind != scriptBuffer.end())
+ scriptBuffer.erase(theFind);
+ inNode.m_ActivationManagerNode.m_Flags.SetHasScript(false);
+ }
+ }
+ }
+
+ static void HandleActivationChange(SElement &inNode, TElementAndSortKeyList &activateBuffer,
+ TElementAndSortKeyList &deactivateBuffer,
+ TElementAndSortKeyList &scriptBuffer,
+ Mutex &inElementAccessMutex, bool &scriptBufferRequiresSort,
+ bool inIsActive)
+ {
+ Mutex::ScopedLock __locker(inElementAccessMutex);
+ TElementAndSortKey theKey(&inNode, inNode.Depth());
+ if (inIsActive) {
+ activateBuffer.push_back(TElementAndSortKey(theKey));
+ } else {
+ deactivateBuffer.push_back(theKey);
+ }
+
+ if (inNode.Flags().HasScriptCallbacks()) {
+ UpdateItemScriptStatus(inNode, inIsActive, scriptBuffer);
+ scriptBufferRequiresSort = true;
+ }
+ inNode.SetGlobalActive(inIsActive);
+ }
+
+ // Runs once we notice that the item's global active status has changed.
+ // Note we explicitly do not update independent items because those are updated
+ // as the first step of each time context update itself.
+ // We do, however, remove them from the dirty list here.
+ static void RunActivateScan(SElement &inNode, TScanBuffer &inScanBuffer,
+ TElementAndSortKeyList &activateBuffer,
+ TElementAndSortKeyList &deactivateBuffer,
+ TElementAndSortKeyList &scriptBuffer, Mutex &inElementAccessMutex,
+ bool &scriptBufferRequiresSort, bool inRunFullScan)
+ {
+ inScanBuffer.clear();
+
+ // Block used to hide variables to avoid an error.
+ {
+ bool parentActive = true;
+ SElement *theParent = inNode.m_Parent;
+ if (theParent != NULL)
+ parentActive = theParent->IsGlobalActive();
+
+ inScanBuffer.push_back(SScanBufferEntry(&inNode, parentActive));
+ }
+
+ for (QT3DSU32 idx = 0, end = inScanBuffer.size(); idx < end; ++idx) {
+ SScanBufferEntry theEntry(inScanBuffer[idx]);
+ SElement *theScanNode = theEntry.m_Node;
+ QT3DS_ASSERT(theScanNode->IsIndependent() == false);
+ bool parentActive = theEntry.IsParentActive();
+ bool wasActive = theScanNode->IsGlobalActive();
+ bool isActive = theScanNode->IsGlobalActive(parentActive);
+ bool wasChildDirty = theScanNode->m_ActivationManagerNode.m_Flags.IsChildDirty();
+ theScanNode->m_ActivationManagerNode.m_Flags.ClearChildDirty();
+ bool activateChange = isActive != wasActive;
+ bool checkChildren = activateChange || (isActive && (wasChildDirty || inRunFullScan));
+ if (activateChange) {
+ HandleActivationChange(*theScanNode, activateBuffer, deactivateBuffer, scriptBuffer,
+ inElementAccessMutex, scriptBufferRequiresSort, isActive);
+ }
+
+ if (checkChildren && theScanNode->m_Child) {
+ for (SElement *theScanNodeChild = theScanNode->m_Child; theScanNodeChild;
+ theScanNodeChild = theScanNodeChild->m_Sibling) {
+ if (theScanNodeChild->IsIndependent() == false)
+ inScanBuffer.push_back(SScanBufferEntry(theScanNodeChild, isActive));
+ }
+ end = inScanBuffer.size();
+ }
+ }
+ }
+
+ void RunDirtyScan(TScanBuffer &inScanBuffer, TElementNodePtrList &inTempDirtyList,
+ TElementAndSortKeyList &activateBuffer,
+ TElementAndSortKeyList &deactivateBuffer,
+ TElementAndSortKeyList &scriptBuffer, bool &scriptBufferRequiresSort)
+ {
+ if (m_AllNodesDirty == true) {
+ inTempDirtyList.clear();
+ SComponent &myNode = m_Component;
+ for (SElement *theChild = myNode.m_Child; theChild; theChild = theChild->m_Sibling) {
+ // Independent nodes don't need to be in the dirty list.
+ if (theChild->IsIndependent() == false)
+ inTempDirtyList.push_back(theChild);
+ }
+ } else {
+ inTempDirtyList.assign(m_DirtyList.begin(), m_DirtyList.end());
+ // Reset nodes that are dirty to *not* be dirty.
+ m_DirtyList.clear();
+ // We have to sort the dirty list because it may have nodes out of order and the active
+ // algorithm requires parents before children. We use the depth member variable to
+ // achieve this.
+ eastl::sort(inTempDirtyList.begin(), inTempDirtyList.end(), SElementPtrSort());
+ }
+ for (QT3DSU32 idx = 0, end = (QT3DSU32)inTempDirtyList.size(); idx < end; ++idx) {
+ SElement &dirtyNode(*inTempDirtyList[idx]);
+ // This is slightly inefficient in the case where a both a child and parent are in the
+ // dirty list.
+ // This case has got to be extremely rare in practice, however.
+ RunActivateScan(dirtyNode, inScanBuffer, activateBuffer, deactivateBuffer, scriptBuffer,
+ m_ElementAccessMutex, scriptBufferRequiresSort, m_AllNodesDirty);
+ }
+ // Set at end so while debugging you can see if all nodes were dirty on method entry.
+ m_AllNodesDirty = false;
+ }
+
+ bool Update(TTimeUnit inGlobalTime, TScanBuffer &inScanBuffer,
+ TElementNodePtrList &inTempDirtyList, TElementAndSortKeyList &activateBuffer,
+ TElementAndSortKeyList &deactivateBuffer, TElementAndSortKeyList &scriptBuffer,
+ bool &scriptBufferRequiresSort, Q3DStudio::CComponentManager &inComponentManager,
+ IPerfTimer &inPerfTimer, IActivityZone &inZone)
+ {
+ QT3DSU64 start = qt3ds::foundation::Time::getCurrentCounterValue();
+ SComponent &theContextNode = m_Component;
+ bool parentActive = true;
+ SElement *theParent = theContextNode.m_Parent;
+ if (theParent != NULL)
+ parentActive = theParent->IsGlobalActive();
+
+ bool wasActive = theContextNode.IsGlobalActive();
+ bool isActive = theContextNode.IsGlobalActive(parentActive);
+ bool activationChange = isActive != wasActive;
+ inPerfTimer.Update("ActivationManager - Update Initial Vars",
+ qt3ds::foundation::Time::getCurrentCounterValue() - start);
+ if (activationChange) {
+ SStackPerfTimer __timer(inPerfTimer, "ActivationManager - Activation Change");
+ HandleActivationChange(theContextNode, activateBuffer, deactivateBuffer, scriptBuffer,
+ m_ElementAccessMutex, scriptBufferRequiresSort, isActive);
+ m_DirtyList.clear();
+ m_AllNodesDirty = true;
+ if (isActive) {
+ Mutex::ScopedLock __locker(m_ElementAccessMutex);
+ inComponentManager.GotoSlideIndex(&theContextNode, 1, false);
+ } else
+ RemoveOverride();
+ }
+ if (isActive) {
+ SStackPerfTimer __timer(inPerfTimer, "ActivationManager - Update Local Time");
+ bool atEndOfTime = CalculateNewTime(inGlobalTime);
+ if (atEndOfTime) {
+ Mutex::ScopedLock __locker(m_ElementAccessMutex);
+ if (theContextNode.IsPlayThrough())
+ inComponentManager.PlaythroughToSlide(&theContextNode);
+ }
+ // Note the slide may have changed here and thus updated our time policy.
+ // Set time active flags based on time.
+ UpdateLocalTime(m_TimePolicy.GetTime(), inScanBuffer, inZone);
+ }
+ if (isActive || activationChange) {
+ if (m_AllNodesDirty || m_DirtyList.size()) {
+ SStackPerfTimer __timer(inPerfTimer, "ActivationManager - Dirty Scan");
+ RunDirtyScan(inScanBuffer, inTempDirtyList, activateBuffer, deactivateBuffer,
+ scriptBuffer, scriptBufferRequiresSort);
+ }
+ }
+ return isActive || activationChange;
+ }
+
+ void GoToTime(TTimeUnit inTime)
+ {
+ if (m_TimePolicyOverride) {
+ m_TimePolicyOverride->SetTime(m_TimePolicy, inTime);
+ } else {
+ m_TimePolicy.SetTime(inTime);
+ }
+ }
+};
+
+IMPLEMENT_INVASIVE_SINGLE_LIST(TimeContext, m_NextSibling);
+
+struct SActivityZone : public IActivityZone
+{
+ typedef nvhash_map<SElement *, NVScopedRefCounted<STimeContext>> TComponentTimeContextMap;
+ NVFoundationBase &m_Foundation;
+ Q3DStudio::CPresentation &m_Presentation;
+ IStringTable &m_StringTable;
+ nvvector<SElement *> m_RootElements;
+ TComponentTimeContextMap m_TimeContexts;
+ // These could potentially not be sorted but for 7.5 and TZ3 I don't want to risk the
+ // bugs assocated with not producing the exact same output as the original algorithm.
+ TElementAndSortKeyList m_ActivatedItems;
+ TElementAndSortKeyList m_DeactivatedItems;
+
+ // This will always need to be sorted because it is maintained across update calls
+ TElementAndSortKeyList m_ScriptItems;
+
+ // Temporaries used while scanning nodes for various features.
+ TScanBuffer m_ScanBuffer;
+ TElementNodePtrList m_TempDirtyList;
+ nvvector<STimeContext *> m_TimeContextScanBuffer;
+
+ TTimeContextList m_RootContexts;
+ Mutex &m_ElementAccessMutex;
+ Mutex m_UpdateCheckMutex;
+ Sync m_UpdateSync;
+ TTimeUnit m_GlobalTime;
+ IPerfTimer *m_PerfTimer;
+ STypeDesc m_DummyTypeDesc;
+ SComponent m_DummyComponent;
+ STimeContext m_DummyContext;
+ bool m_Active;
+ // True if m_ScriptItems needs to be resorted.
+ bool m_ScriptRequiresSort;
+ bool m_Updating;
+
+ QT3DSI32 mRefCount;
+
+ SActivityZone(NVFoundationBase &inFnd, Q3DStudio::CPresentation &inPres,
+ IStringTable &inStrTable, Mutex &inElementAccessMutex)
+ : m_Foundation(inFnd)
+ , m_Presentation(inPres)
+ , m_StringTable(inStrTable)
+ , m_RootElements(inFnd.getAllocator(), "Elements")
+ , m_TimeContexts(inFnd.getAllocator(), "TimeContexts")
+ , m_ActivatedItems(inFnd.getAllocator(), "m_ActivatedItems")
+ , m_DeactivatedItems(inFnd.getAllocator(), "m_DeactivatedItems")
+ , m_ScriptItems(inFnd.getAllocator(), "m_ScriptItems")
+ , m_ScanBuffer(inFnd.getAllocator(), "m_ScanBuffer")
+ , m_TempDirtyList(inFnd.getAllocator(), "m_TempDirtyList")
+ , m_TimeContextScanBuffer(inFnd.getAllocator(), "m_TimeContextScanBuffer")
+ , m_ElementAccessMutex(inElementAccessMutex)
+ , m_UpdateCheckMutex(inFnd.getAllocator())
+ , m_UpdateSync(inFnd.getAllocator())
+ , m_GlobalTime(0)
+ , m_PerfTimer(NULL)
+ , m_DummyComponent(m_DummyTypeDesc)
+ , m_DummyContext(m_Foundation.getAllocator(), m_DummyComponent, m_ElementAccessMutex)
+ , m_Active(true)
+ , m_ScriptRequiresSort(true)
+ , m_Updating(false)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ Q3DStudio::CPresentation &GetPresentation() override { return m_Presentation; }
+
+ static void SortElementBuffer(nvvector<eastl::pair<element::SElement *, QT3DSU32>> &inBuffer)
+ {
+ eastl::sort(inBuffer.begin(), inBuffer.end(), SElementIndexPairSorter());
+ }
+
+ static void AddElementNodeToBuffer(nvvector<eastl::pair<element::SElement *, QT3DSU32>> &inBuffer,
+ SElement &inNode)
+ {
+ eastl::pair<element::SElement *, QT3DSU32> insertItem(&inNode, inNode.Depth());
+ QT3DS_ASSERT(eastl::find(inBuffer.begin(), inBuffer.end(), insertItem) == inBuffer.end());
+ inBuffer.push_back(insertItem);
+ }
+
+ TActivityItemBuffer GetActivatedItems() override { return m_ActivatedItems; }
+
+ TActivityItemBuffer GetDeactivatedItems() override { return m_DeactivatedItems; }
+ // All active items that are script enabled.
+ TActivityItemBuffer GetScriptItems() override
+ {
+ // Created in a delayed way.
+ if (m_ScriptRequiresSort) {
+ m_ScriptRequiresSort = false;
+ SortElementBuffer(m_ScriptItems);
+ }
+ return m_ScriptItems;
+ }
+
+ void SetZoneActive(bool inActive) override { m_Active = inActive; }
+ bool IsZoneActive() override { return m_Active; }
+
+ SActivationManagerNode *GetElementNodeForElement(TActivityItem item)
+ {
+ return &item.m_ActivationManagerNode;
+ }
+
+ STimeContext &GetItemTimeContext(SElement &inNode)
+ {
+ SElement &theComponent = inNode.GetComponentParent();
+ if (!theComponent.IsComponent()) {
+ QT3DS_ASSERT(false);
+ return m_DummyContext;
+ }
+
+ eastl::pair<TComponentTimeContextMap::iterator, bool> inserter = m_TimeContexts.insert(
+ eastl::make_pair(&theComponent, NVScopedRefCounted<STimeContext>()));
+ if (inserter.second) {
+ inserter.first->second = QT3DS_NEW(m_Foundation.getAllocator(), STimeContext)(
+ m_Foundation.getAllocator(), static_cast<SComponent &>(theComponent),
+ m_ElementAccessMutex);
+ STimeContext &theNewContext = *inserter.first->second;
+ if (theComponent.m_Parent == NULL)
+ m_RootContexts.push_back(theNewContext);
+ else {
+ STimeContext &parentContext = GetItemTimeContext(*theComponent.m_Parent);
+ parentContext.m_Children.push_back(theNewContext);
+ }
+ }
+ return *inserter.first->second.mPtr;
+ }
+
+ void AddActivityItems(TActivityItem inNode) override
+ {
+ if (inNode.m_Parent == NULL)
+ m_RootElements.push_back(&inNode);
+
+ GetItemTimeContext(inNode);
+ }
+
+ CTimePolicy *GetOwnedTimePolicy(TActivityItem item) override
+ {
+ return &GetItemTimeContext(item).m_TimePolicy;
+ }
+
+ virtual SComponentTimePolicyOverride *
+ GetOrCreateItemComponentOverride(TActivityItem item, float inMultiplier, TTimeUnit inEndTime,
+ IComponentTimeOverrideFinishedCallback *inCallback) override
+ {
+ STimeContext &theContext = GetItemTimeContext(item);
+
+ theContext.RemoveOverride();
+ theContext.m_TimePolicyOverride =
+ QT3DS_NEW(m_Foundation.getAllocator(),
+ SComponentTimePolicyOverride)(&item, inMultiplier, inEndTime, inCallback);
+ theContext.m_TimePolicy.SetPaused(false);
+
+ return theContext.m_TimePolicyOverride;
+ }
+
+ // If I am independent, then I am my own time parent.
+ // else travel up the chain till you find an indpendent node.
+ SElement &GetItemTimeParentNode(SElement &inNode)
+ {
+ if (inNode.m_Parent != NULL) {
+ SElement &theParent = *inNode.m_Parent;
+ if (theParent.IsIndependent())
+ return theParent;
+ return GetItemTimeParentNode(theParent);
+ }
+ // All roots are time independent by definition.
+ return inNode;
+ }
+
+ TActivityItemPtr GetItemTimeParent(SElement &inNode) override
+ {
+ return &GetItemTimeParentNode(inNode);
+ }
+
+ void InsertIntoAppropriateDirtyList(SElement &inNode)
+ {
+ // This may need to be faster at some point...
+ GetItemTimeContext(inNode).SetElementDirty(inNode);
+ }
+
+ bool GetItemUserActive(TActivityItem item) override { return item.IsExplicitActive(); }
+
+ void UpdateItemScriptStatus(TActivityItem item) override
+ {
+ if (item.IsGlobalActive()) {
+ STimeContext::UpdateItemScriptStatus(item, item.Flags().HasScriptCallbacks(),
+ m_ScriptItems);
+ m_ScriptRequiresSort = true;
+ }
+ }
+
+ void UpdateItemInfo(TActivityItem item) override { GetItemTimeContext(item).Reset(); }
+
+ // Order of events will be:
+ // 1. On Slide Change
+ // 2. Time Update
+ // 3. On Slide Change
+ // 4. Update Local Time
+ void OnSlideChange(TActivityItem item) override
+ {
+ STimeContext &theContext = GetItemTimeContext(item);
+ // Rebuilding the context sets all the elements dirty.
+ // This takes care of everything *but* the script item changes.
+ theContext.Reset();
+ theContext.RemoveOverride();
+ }
+
+ bool GetItemTimeActive(TActivityItem item) override
+ {
+ SActivationManagerNode *theNode = GetElementNodeForElement(item);
+ if (theNode)
+ return theNode->m_Flags.IsTimeActive();
+ return false;
+ }
+
+ TTimeUnit GetItemLocalTime(TActivityItem item) override
+ {
+ SElement *theElem = &item.GetComponentParent();
+ if (item.IsComponent() && item.m_Parent)
+ theElem = &item.m_Parent->GetComponentParent();
+
+ return GetItemTimeContext(*theElem).m_CurrentTime;
+ }
+
+ TTimeUnit GetItemComponentTime(TActivityItem item) override
+ {
+ return GetItemTimeContext(item).m_CurrentTime;
+ }
+
+ bool IsUpdating() override
+ {
+ Mutex::ScopedLock __locker(m_UpdateCheckMutex);
+ return m_Updating;
+ }
+
+ void EndUpdate() override
+ {
+ m_UpdateSync.reset();
+ while (IsUpdating())
+ m_UpdateSync.wait();
+ }
+
+ void DoUpdate()
+ {
+ SStackPerfTimer __timer(m_PerfTimer, "ActivationManager - DoUpdate");
+ if (m_Active) {
+ // We know that parent elements are added before children.
+ // So we know the time contexts are in an appropriate order, assuming they completely
+ // resolve their results before the next time context runs.
+ Q3DStudio::CComponentManager &theManager =
+ static_cast<Q3DStudio::CComponentManager &>(m_Presentation.GetComponentManager());
+ m_TimeContextScanBuffer.clear();
+
+ for (TTimeContextList::iterator iter = m_RootContexts.begin(),
+ end = m_RootContexts.end();
+ iter != end; ++iter)
+ m_TimeContextScanBuffer.push_back(&(*iter));
+
+ for (QT3DSU32 idx = 0, end = m_TimeContextScanBuffer.size(); idx < end; ++idx) {
+ STimeContext &theContext = *m_TimeContextScanBuffer[idx];
+ bool checkChildren =
+ theContext.Update(m_GlobalTime, m_ScanBuffer, m_TempDirtyList, m_ActivatedItems,
+ m_DeactivatedItems, m_ScriptItems, m_ScriptRequiresSort,
+ theManager, *m_PerfTimer, *this);
+ if (checkChildren) {
+ for (TTimeContextList::iterator timeIter = theContext.m_Children.begin(),
+ timeEnd = theContext.m_Children.end();
+ timeIter != timeEnd; ++timeIter) {
+ m_TimeContextScanBuffer.push_back(&(*timeIter));
+ }
+ end = m_TimeContextScanBuffer.size();
+ }
+ }
+ }
+
+ eastl::sort(m_ActivatedItems.begin(), m_ActivatedItems.end(), SElementIndexPairSorter());
+ eastl::sort(m_DeactivatedItems.begin(), m_DeactivatedItems.end(),
+ SElementIndexPairSorter());
+ Mutex::ScopedLock __locker(m_UpdateCheckMutex);
+ {
+ m_Updating = false;
+ m_UpdateSync.set();
+ }
+ }
+
+ static void UpdateCallback(void *data)
+ {
+ SActivityZone *theZone = reinterpret_cast<SActivityZone *>(data);
+ theZone->DoUpdate();
+ }
+
+ void BeginUpdate(TTimeUnit inGlobalTime, IPerfTimer &inPerfTimer,
+ IThreadPool &inThreadPool) override
+ {
+ // It is expected that an update is not running.
+ m_ActivatedItems.clear();
+ m_DeactivatedItems.clear();
+ m_GlobalTime = inGlobalTime;
+ m_PerfTimer = &inPerfTimer;
+ m_Updating = true;
+ inThreadPool.AddTask(this, UpdateCallback, NULL);
+ }
+
+ void GoToTime(TActivityItem inItem, TTimeUnit inTime) override
+ {
+ GetItemTimeContext(inItem).GoToTime(inTime);
+ }
+};
+
+struct SActivityZoneManager : public IActivityZoneManager
+{
+ NVFoundationBase &m_Foundation;
+ IStringTable &m_StringTable;
+ // Zones may access the element mutex on destruction *so* it is important that
+ // it is destroyed *after* the zone list.
+ Mutex m_ElementAccessMutex;
+ nvvector<NVScopedRefCounted<SActivityZone>> m_Zones;
+ QT3DSI32 mRefCount;
+
+ SActivityZoneManager(NVFoundationBase &fnd, IStringTable &inStrTable)
+ : m_Foundation(fnd)
+ , m_StringTable(inStrTable)
+ , m_ElementAccessMutex(m_Foundation.getAllocator())
+ , m_Zones(m_Foundation.getAllocator(), "m_Zones")
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ IActivityZone &CreateActivityZone(Q3DStudio::CPresentation &inPresentation) override
+ {
+ m_Zones.push_back(QT3DS_NEW(m_Foundation.getAllocator(), SActivityZone)(
+ m_Foundation, inPresentation, m_StringTable, m_ElementAccessMutex));
+ return *m_Zones.back().mPtr;
+ }
+};
+}
+
+IActivityZoneManager &
+IActivityZoneManager::CreateActivityZoneManager(NVFoundationBase &inFoundation,
+ IStringTable &inStrTable)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(), SActivityZoneManager)(inFoundation, inStrTable);
+}
diff --git a/src/runtime/Qt3DSActivationManager.h b/src/runtime/Qt3DSActivationManager.h
new file mode 100644
index 0000000..f343ac9
--- /dev/null
+++ b/src/runtime/Qt3DSActivationManager.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_ACTIVATION_MANAGER_H
+#define QT3DS_ACTIVATION_MANAGER_H
+#include "foundation/Qt3DSRefCounted.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSAllocator.h"
+#include "foundation/Utils.h"
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSMetadata.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "Qt3DSTimePolicy.h"
+
+namespace Q3DStudio {
+struct IComponentTimeOverrideFinishedCallback;
+struct SComponentTimePolicyOverride;
+class CPresentation;
+}
+
+namespace qt3ds {
+namespace foundation {
+ class IPerfTimer;
+}
+}
+
+namespace qt3ds {
+namespace render {
+ class IThreadPool;
+}
+}
+
+namespace qt3ds {
+namespace runtime {
+ using namespace qt3ds::foundation;
+ using namespace qt3ds;
+ using Q3DStudio::CTimePolicy;
+ using Q3DStudio::SComponentTimePolicyOverride;
+ using Q3DStudio::IComponentTimeOverrideFinishedCallback;
+ using Q3DStudio::CPresentation;
+ using Q3DStudio::TTimeUnit;
+ using qt3ds::render::IThreadPool;
+
+ class IActivationManager;
+
+ typedef element::SElement &TActivityItem;
+ typedef element::SElement *TActivityItemPtr;
+
+ typedef eastl::pair<element::SElement *, QT3DSU32> TElementAndSortKey;
+
+ typedef NVConstDataRef<TElementAndSortKey> TActivityItemBuffer;
+
+ // Items can have user active and time active. Time active is controlled by this object
+ // but user active can be set. An item is only active if it both actives are true.
+ // Inactive items have completely inactive children.
+ class IActivityZone
+ {
+ protected:
+ virtual ~IActivityZone() {}
+ public:
+ virtual Q3DStudio::CPresentation &GetPresentation() = 0;
+
+ // Items activated this cycle
+ virtual TActivityItemBuffer GetActivatedItems() = 0;
+ // Items deactivated this cycle
+ virtual TActivityItemBuffer GetDeactivatedItems() = 0;
+ // All active items that are script enabled.
+ virtual TActivityItemBuffer GetScriptItems() = 0;
+
+ // Inactive zones don't update their item lists when the manager updates.
+ virtual void SetZoneActive(bool inActive) = 0;
+ virtual bool IsZoneActive() = 0;
+
+ virtual void AddActivityItems(TActivityItem root) = 0;
+
+ // Get the time policy that is owned by this element.
+ virtual CTimePolicy *GetOwnedTimePolicy(TActivityItem item) = 0;
+ // Get the local time for this element. This is the time the animation system should see
+ virtual TTimeUnit GetItemLocalTime(TActivityItem item) = 0;
+ // Get the local time if this isn't independent, else get the time context time for the
+ // component.
+ virtual TTimeUnit GetItemComponentTime(TActivityItem item) = 0;
+
+ virtual SComponentTimePolicyOverride *
+ GetOrCreateItemComponentOverride(TActivityItem item, float inMultiplier,
+ TTimeUnit inEndTime,
+ IComponentTimeOverrideFinishedCallback *inCallback) = 0;
+
+ // If this item is independent, return this item.
+ // else get time parent of my parent.
+ virtual TActivityItemPtr GetItemTimeParent(TActivityItem item) = 0;
+
+ bool IsIndependent(TActivityItem item) { return GetOwnedTimePolicy(item) != NULL; }
+
+ // Any time the item has script flag changes after its initial creation.
+ // Note that this flag cannot change due to slide changes, it can only change due
+ // to something that had a script error and thus is disabled.
+ virtual void UpdateItemScriptStatus(TActivityItem item) = 0;
+
+ // If the start, end, or explicit active flags change *outside* of a slide change.
+ virtual void UpdateItemInfo(TActivityItem item) = 0;
+ virtual bool GetItemUserActive(TActivityItem item) = 0;
+
+ // Callback when the slide changes, the time context needs to be rebuilt in this case.
+ virtual void OnSlideChange(TActivityItem item) = 0;
+
+ virtual bool GetItemTimeActive(TActivityItem item) = 0;
+
+ virtual void BeginUpdate(QT3DSI64 inGlobalTime, IPerfTimer &inPerfTimer,
+ IThreadPool &inThreadPool) = 0;
+ virtual bool IsUpdating() = 0;
+ virtual void EndUpdate() = 0;
+
+ virtual void GoToTime(TActivityItem item, TTimeUnit inTime) = 0;
+ };
+
+ class IActivityZoneManager : public NVRefCounted
+ {
+ protected:
+ virtual ~IActivityZoneManager() {}
+ public:
+ virtual IActivityZone &CreateActivityZone(Q3DStudio::CPresentation &inPresentation) = 0;
+
+ static IActivityZoneManager &CreateActivityZoneManager(NVFoundationBase &inFoundation,
+ IStringTable &inStrTable);
+ };
+}
+}
+#endif
diff --git a/src/runtime/Qt3DSAnimationSystem.cpp b/src/runtime/Qt3DSAnimationSystem.cpp
new file mode 100644
index 0000000..84d1c6d
--- /dev/null
+++ b/src/runtime/Qt3DSAnimationSystem.cpp
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+#include "Qt3DSTypes.h"
+#include "Qt3DSAnimationSystem.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSInvasiveSet.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSPool.h"
+#include "Qt3DSElementSystem.h"
+#include "Qt3DSCubicRoots.h"
+#include "Qt3DSBezierEval.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/IOStreams.h"
+#include "EASTL/sort.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSIndexableLinkedList.h"
+
+using namespace qt3ds::runtime;
+using namespace qt3ds::runtime::element;
+
+namespace {
+
+struct SAnimationKey
+{
+ QT3DSF32 m_Time; ///< Time
+ QT3DSF32 m_Value; ///< Value
+ QT3DSF32 m_C1Time; ///< Control 1 time
+ QT3DSF32 m_C1Value; ///< Control 1 value
+ QT3DSF32 m_C2Time; ///< Control 2 time
+ QT3DSF32 m_C2Value; ///< Control 2 value
+ SAnimationKey()
+ : m_Time(0)
+ , m_Value(0)
+ , m_C1Time(0)
+ , m_C1Value(0)
+ , m_C2Time(0)
+ , m_C2Value(0)
+ {
+ }
+
+ SAnimationKey(float time, float val, float c1time, float c1value, float c2time, float c2value)
+ : m_Time(time)
+ , m_Value(val)
+ , m_C1Time(c1time)
+ , m_C1Value(c1value)
+ , m_C2Time(c2time)
+ , m_C2Value(c2value)
+ {
+ }
+};
+
+struct SAnimationKeyNode
+{
+ enum {
+ AnimationKeyArraySize = 4,
+ };
+
+ SAnimationKey m_Data[AnimationKeyArraySize];
+ SAnimationKeyNode *m_NextNode;
+ SAnimationKeyNode()
+ : m_NextNode(NULL)
+ {
+ }
+};
+
+typedef IndexableLinkedList<SAnimationKeyNode, SAnimationKey,
+ SAnimationKeyNode::AnimationKeyArraySize>
+ TAnimationKeyNodeList;
+
+struct SAnimationTrack
+{
+ SElement *m_Element;
+ QT3DSI32 m_Id;
+ QT3DSU32 m_PropertyIndex;
+ QT3DSU32 m_KeyCount;
+ SAnimationKeyNode *m_Keys;
+ QT3DSU32 m_ActiveSetIndex;
+ bool m_Dynamic;
+
+ SAnimationTrack()
+ : m_Element(NULL)
+ , m_Id(0)
+ , m_PropertyIndex(0)
+ , m_KeyCount(0)
+ , m_Keys(NULL)
+ , m_ActiveSetIndex(QT3DS_MAX_U32)
+ , m_Dynamic(false)
+ {
+ }
+ SAnimationTrack(SElement &inElement, QT3DSI32 inId, QT3DSU32 inPropertyIndex, bool inIsDynamic)
+ : m_Element(&inElement)
+ , m_Id(inId)
+ , m_PropertyIndex(inPropertyIndex)
+ , m_KeyCount(0)
+ , m_Keys(NULL)
+ , m_ActiveSetIndex(QT3DS_MAX_U32)
+ , m_Dynamic(inIsDynamic)
+ {
+ }
+};
+
+struct GetAnimationTrackActiveSetIndex
+{
+ QT3DSU32 operator()(const SAnimationTrack &inTrack) const { return inTrack.m_ActiveSetIndex; }
+};
+
+struct SetAnimationTrackActiveSetIndex
+{
+ void operator()(SAnimationTrack &inTrack, QT3DSU32 inIdx) { inTrack.m_ActiveSetIndex = inIdx; }
+};
+
+typedef InvasiveSet<SAnimationTrack, GetAnimationTrackActiveSetIndex,
+ SetAnimationTrackActiveSetIndex>
+ TAnimationTrackActiveSet;
+
+struct SAnimationTrackComparator
+{
+ bool operator()(SAnimationTrack *lhs, SAnimationTrack *rhs) const
+ {
+ return lhs->m_Id < rhs->m_Id;
+ }
+};
+
+struct SAnimSystem : public IAnimationSystem
+{
+ typedef nvhash_map<QT3DSI32, SAnimationTrack *> TAnimationTrackHash;
+
+ NVFoundationBase &m_Foundation;
+ Pool<SAnimationTrack, ForwardingAllocator> m_AnimationTrackPool;
+ Pool<SAnimationKeyNode, ForwardingAllocator> m_AnimationKeyNodePool;
+ TAnimationTrackHash m_Tracks;
+ SAnimationTrack *m_LastInsertedTrack;
+ TAnimationTrackActiveSet m_ActiveSet;
+ NVDataRef<QT3DSU8> m_LoadData;
+ QT3DSI32 m_NextTrackId;
+ QT3DSI32 m_RefCount;
+
+ SAnimSystem(NVFoundationBase &inFoundation)
+ : m_Foundation(inFoundation)
+ , m_AnimationTrackPool(
+ ForwardingAllocator(inFoundation.getAllocator(), "AnimationTrackPool"))
+ , m_AnimationKeyNodePool(
+ ForwardingAllocator(inFoundation.getAllocator(), "AnimationKeyNodePool"))
+ , m_Tracks(inFoundation.getAllocator(), "m_Tracks")
+ , m_LastInsertedTrack(NULL)
+ , m_ActiveSet(inFoundation.getAllocator(), "m_ActiveSet")
+ , m_NextTrackId(1)
+ , m_RefCount(0)
+ {
+ }
+
+ static QT3DSI32 IncrementTrackId(QT3DSI32 inTrackId)
+ {
+ ++inTrackId;
+ if (!inTrackId)
+ ++inTrackId;
+ return inTrackId;
+ }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc = m_Foundation.getAllocator();
+ NVDelete(alloc, this);
+ }
+ }
+
+ QT3DSI32 CreateAnimationTrack(element::SElement &inElement, QT3DSU32 inPropertyName,
+ bool inDynamic) override
+ {
+ // first, make sure we can find the property.
+ Option<QT3DSU32> theIndex = inElement.FindPropertyIndex(inPropertyName);
+ Option<TPropertyDescAndValuePtr> theProperty;
+ if (theIndex.hasValue())
+ theProperty = inElement.GetPropertyByIndex(*theIndex);
+
+ if (theProperty.hasValue() == false
+ || theProperty->first.m_Type != Q3DStudio::ATTRIBUTETYPE_FLOAT) {
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+
+ eastl::pair<TAnimationTrackHash::iterator, bool> inserter;
+ // Find unused track id.
+ for (inserter = m_Tracks.insert(eastl::make_pair(m_NextTrackId, (SAnimationTrack *)NULL));
+ inserter.second == false; m_NextTrackId = IncrementTrackId(m_NextTrackId)) {
+ }
+
+ SAnimationTrack *theNewTrack =
+ reinterpret_cast<SAnimationTrack *>(m_AnimationTrackPool.allocate(__FILE__, __LINE__));
+ new (theNewTrack) SAnimationTrack(inElement, inserter.first->first, *theIndex, inDynamic);
+ inserter.first->second = theNewTrack;
+ m_LastInsertedTrack = theNewTrack;
+ ++m_NextTrackId;
+ return inserter.first->first;
+ }
+
+ SAnimationKey *GetKeyByIndex(QT3DSU32 inIndex, SAnimationTrack &inTrack)
+ {
+ if (inIndex >= inTrack.m_KeyCount)
+ return 0;
+
+ return &TAnimationKeyNodeList::GetObjAtIdx(inTrack.m_Keys, inIndex);
+ }
+
+ SAnimationKey &GetOrCreateKeyByIndex(QT3DSU32 inIndex, SAnimationTrack &inTrack)
+ {
+ if (inIndex < inTrack.m_KeyCount)
+ return *GetKeyByIndex(inIndex, inTrack);
+
+ return TAnimationKeyNodeList::Create(inTrack.m_Keys, inTrack.m_KeyCount,
+ m_AnimationKeyNodePool);
+ }
+
+ void AddKey(QT3DSF32 inTime, QT3DSF32 inValue, QT3DSF32 inC1Time, QT3DSF32 inC1Value,
+ QT3DSF32 inC2Time, QT3DSF32 inC2Value) override
+ {
+ if (m_LastInsertedTrack == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ SAnimationKey &theNewKey =
+ GetOrCreateKeyByIndex(m_LastInsertedTrack->m_KeyCount, *m_LastInsertedTrack);
+ theNewKey = SAnimationKey(inTime, inValue, inC1Time, inC1Value, inC2Time, inC2Value);
+ }
+
+ SAnimationTrack *GetAnimationTrack(QT3DSI32 inTrackId)
+ {
+ TAnimationTrackHash::iterator iter = m_Tracks.find(inTrackId);
+ if (iter != m_Tracks.end())
+ return iter->second;
+ return NULL;
+ }
+
+ struct SNextKeyRetVal
+ {
+ SAnimationKeyNode *m_Node;
+ SAnimationKey *m_Start;
+ SAnimationKey *m_End;
+
+ SNextKeyRetVal(SAnimationKeyNode &inNode)
+ : m_Node(&inNode)
+ , m_Start(NULL)
+ , m_End(NULL)
+ {
+ }
+ SNextKeyRetVal(SAnimationKeyNode &inNode, SAnimationKey &inLastKey)
+ : m_Node(&inNode)
+ , m_Start(NULL)
+ , m_End(&inLastKey)
+ {
+ }
+ SNextKeyRetVal(SAnimationKeyNode &inNode, SAnimationKey &inStart, SAnimationKey &inLastKey)
+ : m_Node(&inNode)
+ , m_Start(&inStart)
+ , m_End(&inLastKey)
+ {
+ }
+ SNextKeyRetVal()
+ : m_Node(NULL)
+ , m_Start(NULL)
+ , m_End(NULL)
+ {
+ }
+ };
+
+ SNextKeyRetVal NextKeyIsGreaterThan(TAnimationKeyNodeList::iterator iter, QT3DSF32 inTime)
+ {
+ SAnimationKey *theStartKey = &(*iter);
+ QT3DSU32 nextIdx = iter.m_Idx + 1;
+ if (nextIdx == iter.m_Count)
+ return SNextKeyRetVal(*iter.m_Node, *theStartKey);
+
+ TAnimationKeyNodeList::iterator next = iter;
+ ++next;
+
+ SAnimationKey &nextKey = *next;
+
+ if (nextKey.m_Time >= inTime)
+ return SNextKeyRetVal(*iter.m_Node, *theStartKey, nextKey);
+ return SNextKeyRetVal(*iter.m_Node);
+ }
+
+ QT3DSF32 Evaluate(SAnimationTrack &inTrack, QT3DSF32 inTime)
+ {
+ if (inTrack.m_KeyCount < 2) {
+ if (inTrack.m_KeyCount == 1)
+ return GetKeyByIndex(0, inTrack)->m_Value;
+ return 0.0f;
+ }
+
+ TAnimationKeyNodeList::iterator iter =
+ TAnimationKeyNodeList::begin(inTrack.m_Keys, inTrack.m_KeyCount);
+
+ if ((*iter).m_Time >= inTime) {
+ return (*iter).m_Value;
+ }
+
+ // We know it isn't the first key.
+ SNextKeyRetVal theKeyData;
+ for (theKeyData = NextKeyIsGreaterThan(iter, inTime); theKeyData.m_End == NULL;
+ ++iter, theKeyData = NextKeyIsGreaterThan(iter, inTime)) {
+ }
+
+ if (theKeyData.m_Node == NULL && theKeyData.m_End == NULL) {
+ QT3DS_ASSERT(false);
+ return 0.0f;
+ }
+
+ if (theKeyData.m_Start != NULL) {
+ SAnimationKey &start(*theKeyData.m_Start);
+ SAnimationKey &end(*theKeyData.m_End);
+ return Q3DStudio::EvaluateBezierKeyframe(
+ inTime, start.m_Time, start.m_Value, start.m_C1Time, start.m_C1Value,
+ start.m_C2Time, start.m_C2Value, end.m_Time, end.m_Value);
+ }
+ return theKeyData.m_End->m_Value;
+ }
+
+ void Update() override
+ {
+ for (QT3DSU32 idx = 0, end = m_ActiveSet.size(); idx < end; ++idx) {
+ SAnimationTrack &theTrack = *m_ActiveSet[idx];
+ SElement &theElement = *theTrack.m_Element;
+ QT3DSF32 theTime = static_cast<QT3DSF32>(theElement.GetOuterTime());
+ TPropertyDescAndValuePtr theProperty =
+ *theElement.GetPropertyByIndex(theTrack.m_PropertyIndex);
+ Q3DStudio::UVariant &theValue(*theProperty.second);
+ QT3DSF32 newValue = Evaluate(theTrack, theTime);
+ if (fabs(theValue.m_FLOAT - newValue) > SElement::SmallestDifference()) {
+ theValue.m_FLOAT = newValue;
+ theElement.SetDirty();
+ }
+ }
+ }
+
+ void SetActive(QT3DSI32 inTrackId, bool inActive) override
+ {
+ SAnimationTrack *theTrack = GetAnimationTrack(inTrackId);
+ if (theTrack) {
+ if (inActive)
+ m_ActiveSet.insert(*theTrack);
+ else
+ m_ActiveSet.remove(*theTrack);
+ }
+ }
+ //==============================================================================
+ /**
+ * Recomputes control point values for Studio animation based off of new start
+ * value.
+ * @param inNewStartValue new start value for the keyframe
+ * @param inKeyStart start index of the keyframes
+ */
+ void RecomputeControlPoints(QT3DSF32 inNewStartValue, SAnimationTrack &inTrack)
+ {
+ SAnimationKey &theFirstKey = *GetKeyByIndex(0, inTrack);
+ SAnimationKey &theSecondKey = *GetKeyByIndex(1, inTrack);
+
+ // Determine original control point ratios
+ Q3DStudio::FLOAT theOriginalInterval =
+ (theSecondKey.m_Value - theFirstKey.m_Value) * (1.0f / 3.0f);
+ Q3DStudio::FLOAT theEaseOutRatio = theOriginalInterval != 0.0f
+ ? (theFirstKey.m_C1Value - theFirstKey.m_Value) / theOriginalInterval
+ : 0.0f;
+ Q3DStudio::FLOAT theEaseInRatio = theOriginalInterval != 0.0f
+ ? (theSecondKey.m_Value - theFirstKey.m_C2Value) / theOriginalInterval
+ : 0.0f;
+
+ // Apply ratios based off new start value
+ Q3DStudio::FLOAT theNewInterval = (theSecondKey.m_Value - inNewStartValue) * (1.0f / 3.0f);
+ theFirstKey.m_C1Value = inNewStartValue + (theNewInterval * theEaseOutRatio);
+ theFirstKey.m_C2Value = theSecondKey.m_Value - (theNewInterval * theEaseInRatio);
+ }
+
+ void UpdateDynamicKey(QT3DSI32 inTrackId) override
+ {
+ SAnimationTrack *theTrack = GetAnimationTrack(inTrackId);
+ if (theTrack && theTrack->m_Dynamic) {
+ SAnimationKey *theFirstKey = GetKeyByIndex(0, *theTrack);
+ if (theFirstKey) {
+ Q3DStudio::UVariant *thePropData =
+ theTrack->m_Element->GetPropertyByIndex(theTrack->m_PropertyIndex)->second;
+
+ // Recompute interpolation
+ if (theTrack->m_KeyCount > 1)
+ RecomputeControlPoints(thePropData->m_FLOAT, *theTrack);
+
+ // Update start value
+ theFirstKey->m_Value = thePropData->m_FLOAT;
+ }
+ }
+ }
+};
+}
+
+IAnimationSystem &IAnimationSystem::CreateAnimationSystem(NVFoundationBase &inFoundation)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(), SAnimSystem)(inFoundation);
+}
diff --git a/src/runtime/Qt3DSAnimationSystem.h b/src/runtime/Qt3DSAnimationSystem.h
new file mode 100644
index 0000000..24542ad
--- /dev/null
+++ b/src/runtime/Qt3DSAnimationSystem.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 QT3DS_ANIMATION_SYSTEM_H
+#define QT3DS_ANIMATION_SYSTEM_H
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSRefCounted.h"
+
+#pragma once
+namespace qt3ds {
+namespace foundation {
+ class IOutStream;
+}
+}
+
+namespace qt3ds {
+namespace runtime {
+ using namespace qt3ds;
+ using namespace qt3ds::foundation;
+
+ namespace element {
+ struct SElement;
+ }
+ class IElementAllocator;
+
+ class IAnimationSystem : public NVRefCounted
+ {
+ public:
+ virtual QT3DSI32 CreateAnimationTrack(element::SElement &inElement, QT3DSU32 inPropertyName,
+ bool inDynamic) = 0;
+ virtual void AddKey(QT3DSF32 inTime, QT3DSF32 inValue, QT3DSF32 inC1Time, QT3DSF32 inC1Value,
+ QT3DSF32 inC2Time, QT3DSF32 inC2Value) = 0;
+ virtual void Update() = 0;
+ virtual void SetActive(QT3DSI32 inTrackId, bool inActive) = 0;
+ virtual void UpdateDynamicKey(QT3DSI32 inTrackId) = 0;
+
+ static IAnimationSystem &CreateAnimationSystem(NVFoundationBase &inFoundation);
+ };
+}
+}
+
+#endif
diff --git a/src/runtime/Qt3DSApplication.cpp b/src/runtime/Qt3DSApplication.cpp
new file mode 100644
index 0000000..baa94dd
--- /dev/null
+++ b/src/runtime/Qt3DSApplication.cpp
@@ -0,0 +1,2180 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+
+// We need a Qt header first here because Qt's metatype system insists that Bool
+// can not be defined first before Qt headers are included and the includes below
+// define Bool by way of Xll/XLib.h via khronos -> egl -> X11
+#include <QImage>
+
+#include "RuntimePrefix.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSApplicationValues.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "Qt3DSMemory.h"
+#include "Qt3DSRuntimeFactory.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/FileTools.h"
+#include "Qt3DSIScriptBridge.h"
+#include "foundation/Qt3DSOption.h"
+#include "foundation/XML.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSContainers.h"
+#include "EASTL/hash_map.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSInputEventTypes.h"
+#include "Qt3DSSceneManager.h"
+#include "Qt3DSIScene.h"
+#include "Qt3DSInputEngine.h"
+#include "Qt3DSMetadata.h"
+#include "Qt3DSUIPParser.h"
+#include "foundation/Socket.h"
+#include "EventPollingSystem.h"
+#include "Qt3DSRenderContextCore.h"
+#include "foundation/Qt3DSPerfTimer.h"
+#include "foundation/SerializationTypes.h"
+#include "EASTL/sort.h"
+#include "Qt3DSRenderBufferLoader.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSSync.h"
+#include "Qt3DSTextRenderer.h"
+#include "Qt3DSRenderThreadPool.h"
+#include "foundation/StringConversionImpl.h"
+#include "Qt3DSRenderLoadedTexture.h"
+#include "render/Qt3DSRenderContext.h"
+#include "Qt3DSActivationManager.h"
+#include "Qt3DSRenderer.h"
+#include "Qt3DSRenderShaderCache.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "Qt3DSAudioPlayer.h"
+#include "Qt3DSElementSystem.h"
+#include "Qt3DSSlideSystem.h"
+#include "Qt3DSQmlElementHelper.h"
+#include "Qt3DSRenderBufferManager.h"
+#include "Qt3DSRenderRenderList.h"
+#include "Qt3DSRenderImageBatchLoader.h"
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qdir.h>
+#include "q3dsvariantconfig_p.h"
+
+using namespace qt3ds;
+using namespace qt3ds::runtime;
+using namespace qt3ds::render;
+using namespace Q3DStudio;
+
+namespace qt3ds {
+namespace foundation {
+template <>
+struct StringConversion<QT3DSVec2>
+{
+ void StrTo(const char8_t *buffer, QT3DSVec2 &item)
+ {
+ char *endPtr = NULL;
+ item.x = (float)strtod(buffer, &endPtr);
+ if (endPtr)
+ item.y = (float)strtod(endPtr, NULL);
+ }
+};
+}
+}
+
+bool qt3ds::runtime::isImagePath(const QString &path)
+{
+ int index = path.lastIndexOf(QLatin1Char('.'));
+ if (index < 0)
+ return false;
+ const QString ext = path.right(path.length() - index - 1);
+ return (ext == QLatin1String("jpg") || ext == QLatin1String("jpeg")
+ || ext == QLatin1String("png") || ext == QLatin1String("hdr")
+ || ext == QLatin1String("dds") || ext == QLatin1String("ktx"));
+}
+
+struct SFrameTimer
+{
+ int m_FrameCount;
+ QT3DSU64 m_FrameTime;
+ SFrameTimer(QT3DSU64 fc = 0)
+ : m_FrameCount(fc)
+ , m_FrameTime(qt3ds::foundation::Time::getCurrentCounterValue())
+ {
+ }
+
+ QT3DSF32 GetElapsedSeconds(QT3DSU64 currentTime) const
+ {
+ QT3DSU64 diff = currentTime - m_FrameTime;
+ QT3DSF32 diffNanos
+ = static_cast<QT3DSF32>(qt3ds::foundation::Time::sCounterFreq.toTensOfNanos(diff));
+ return diffNanos / qt3ds::foundation::Time::sNumTensOfNanoSecondsInASecond;
+ }
+
+ QT3DSF32 GetElapsedSeconds() const
+ {
+ return GetElapsedSeconds(qt3ds::foundation::Time::getCurrentCounterValue());
+ }
+
+ QPair<QT3DSF32, int> GetFPS(int updateFC)
+ {
+ int elapsedFrames = updateFC - m_FrameCount;
+ QT3DSU64 currentTime = qt3ds::foundation::Time::getCurrentCounterValue();
+ QT3DSF32 elapsedSeconds = GetElapsedSeconds(currentTime);
+ QT3DSF32 retval = elapsedFrames / elapsedSeconds;
+ m_FrameCount = updateFC;
+ m_FrameTime = currentTime;
+ return qMakePair(retval, elapsedFrames);
+ }
+};
+
+struct SRefCountedAssetValue : public SAssetValue
+{
+ NVFoundationBase &m_Foundation;
+ QT3DSI32 mRefCount;
+ SRefCountedAssetValue(NVFoundationBase &fnd)
+ : SAssetValue()
+ , m_Foundation(fnd)
+ , mRefCount(0)
+ {
+ }
+
+ SRefCountedAssetValue(NVFoundationBase &fnd, const SAssetValue &asset)
+ : SAssetValue(asset)
+ , m_Foundation(fnd)
+ , mRefCount(0)
+ {
+ }
+
+ SRefCountedAssetValue(NVFoundationBase &fnd, const SPresentationAsset &asset)
+ : SAssetValue(asset)
+ , m_Foundation(fnd)
+ , mRefCount(0)
+ {
+ }
+
+ SRefCountedAssetValue(NVFoundationBase &fnd, const SBehaviorAsset &asset)
+ : SAssetValue(asset)
+ , m_Foundation(fnd)
+ , mRefCount(0)
+ {
+ }
+
+ SRefCountedAssetValue(NVFoundationBase &fnd, const SRenderPluginAsset &asset)
+ : SAssetValue(asset)
+ , m_Foundation(fnd)
+ , mRefCount(0)
+ {
+ }
+
+ SRefCountedAssetValue(NVFoundationBase &fnd, const SSCXMLAsset &asset)
+ : SAssetValue(asset)
+ , m_Foundation(fnd)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+};
+
+typedef nvhash_map<CRegisteredString, NVScopedRefCounted<SRefCountedAssetValue>> TIdAssetMap;
+typedef nvhash_map<THashValue, CRegisteredString> THashStrMap;
+typedef nvvector<eastl::pair<CRegisteredString, NVScopedRefCounted<SRefCountedAssetValue>>>
+TIdAssetList;
+typedef eastl::pair<QT3DSU32, TElement *> THandleElementPair;
+typedef NVConstDataRef<THandleElementPair> THandleElementDataBuffer;
+typedef nvvector<THandleElementDataBuffer> THandleElementDataBufferList;
+typedef nvhash_map<QT3DSU32, TElement *> THandleElementMap;
+
+struct SHandleElementPairComparator
+{
+ bool operator()(const THandleElementPair &lhs, const THandleElementPair &rhs) const
+ {
+ return lhs.first < rhs.first;
+ }
+};
+
+struct SSlideResourceCounter
+{
+ QHash<QString, int> counters;
+ QSet<QString> createSet;
+ QSet<QString> deleteSet;
+
+ QVector<QString> loadedSlides;
+
+ void increment(const QSet<QString> &set)
+ {
+ for (auto &r : set) {
+ if (counters.value(r, 0) == 0)
+ createSet.insert(r);
+ counters[r]++;
+ }
+ }
+ void decrement(const QSet<QString> &set)
+ {
+ for (auto &r : set) {
+ if (counters.contains(r)) {
+ int count = qMax(counters[r] - 1, 0);
+ if (count == 0)
+ deleteSet.insert(r);
+ counters[r] = count;
+ }
+ }
+ }
+ void begin()
+ {
+ createSet.clear();
+ deleteSet.clear();
+ }
+ void reset()
+ {
+ loadedSlides.clear();
+ counters.clear();
+ begin();
+ }
+ QSet<QString> toImageSet(const QVector<QString> &vec)
+ {
+ QSet<QString> s;
+ for (const auto &x : vec) {
+ if (isImagePath(x))
+ s.insert(x);
+ }
+ return s;
+ }
+ void handleLoadSlide(const QString &slide, SSlideKey key, ISlideSystem &slideSystem)
+ {
+ if (loadedSlides.contains(slide))
+ return;
+ loadedSlides.push_back(slide);
+ begin();
+ increment(toImageSet(slideSystem.GetSourcePaths(key)));
+ print();
+ }
+ void handleUnloadSlide(const QString &slide, SSlideKey key, ISlideSystem &slideSystem)
+ {
+ if (!loadedSlides.contains(slide))
+ return;
+ loadedSlides.removeOne(slide);
+ begin();
+ decrement(toImageSet(slideSystem.GetSourcePaths(key)));
+ print();
+ }
+ void print()
+ {
+ static const bool debugging = qEnvironmentVariableIntValue("QT3DS_DEBUG") >= 1;
+ if (debugging) {
+ qDebug() << "SlideResourceCounter resources:";
+ const auto keys = counters.keys();
+ for (auto &x : keys)
+ qDebug() << x << ": " << counters[x];
+ if (createSet.size()) {
+ qDebug() << "New resources: ";
+ for (auto y : qAsConst(createSet))
+ qDebug() << y;
+ }
+ if (deleteSet.size()) {
+ qDebug() << "Deleted resources: ";
+ for (auto y : qAsConst(deleteSet))
+ qDebug() << y;
+ }
+ }
+ }
+};
+
+struct STextureUploadRenderTask : public IRenderTask, public IImageLoadListener
+{
+ IImageBatchLoader &m_batchLoader;
+ IBufferManager &m_bufferManager;
+ NVRenderContextType m_type;
+ bool m_preferKtx;
+ QSet<QString> m_uploadSet;
+ QSet<QString> m_uploadWaitSet;
+ QSet<QString> m_deleteSet;
+ QMutex m_updateMutex;
+ QHash<QT3DSU32, QSet<QString>> m_batches;
+ volatile QT3DSI32 mRefCount;
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_bufferManager.GetStringTable()
+ .GetAllocator())
+
+ STextureUploadRenderTask(IImageBatchLoader &loader, IBufferManager &mgr,
+ NVRenderContextType type, bool preferKtx)
+ : m_batchLoader(loader), m_bufferManager(mgr), m_type(type), m_preferKtx(preferKtx),
+ mRefCount(0)
+ {
+
+ }
+ void Run() override
+ {
+ QMutexLocker loc(&m_updateMutex);
+ if (!m_uploadSet.isEmpty()) {
+ nvvector<CRegisteredString> sourcePaths(m_bufferManager.GetStringTable().GetAllocator(),
+ "TempSourcePathList");
+ for (auto &s : qAsConst(m_uploadSet))
+ sourcePaths.push_back(m_bufferManager.GetStringTable().RegisterStr(s));
+ QT3DSU32 id = m_batchLoader.LoadImageBatch(sourcePaths, CRegisteredString(),
+ this, m_type, m_preferKtx, false);
+ if (id)
+ m_batches[id] = m_uploadSet;
+ }
+ if (!m_uploadWaitSet.isEmpty()) {
+ nvvector<CRegisteredString> sourcePaths(m_bufferManager.GetStringTable().GetAllocator(),
+ "TempSourcePathList");
+ for (auto &s : qAsConst(m_uploadWaitSet))
+ sourcePaths.push_back(m_bufferManager.GetStringTable().RegisterStr(s));
+ QT3DSU32 id = m_batchLoader.LoadImageBatch(sourcePaths, CRegisteredString(),
+ this, m_type, m_preferKtx, false);
+ if (id) {
+ m_batchLoader.BlockUntilLoaded(id);
+ m_bufferManager.loadSet(m_uploadWaitSet);
+ }
+ }
+ m_bufferManager.unloadSet(m_deleteSet);
+ }
+ void add(const QSet<QString> &set, bool wait)
+ {
+ QMutexLocker loc(&m_updateMutex);
+ if (wait)
+ m_uploadWaitSet.unite(set);
+ else
+ m_uploadSet.unite(set);
+ m_deleteSet.subtract(set);
+ }
+ void remove(const QSet<QString> &set)
+ {
+ QMutexLocker loc(&m_updateMutex);
+ m_uploadSet.subtract(set);
+ m_uploadWaitSet.subtract(set);
+ m_deleteSet.unite(set);
+ }
+ bool persistent() const override
+ {
+ return true;
+ }
+ void OnImageLoadComplete(CRegisteredString inPath, ImageLoadResult::Enum inResult) override
+ {
+ Q_UNUSED(inPath);
+ Q_UNUSED(inResult);
+ }
+ void OnImageBatchComplete(QT3DSU64 inBatch) override
+ {
+ m_bufferManager.loadSet(m_batches[inBatch]);
+ }
+};
+
+struct SApp;
+
+class IAppLoadContext : public NVRefCounted
+{
+public:
+ virtual void EndLoad() = 0;
+ virtual bool OnGraphicsInitialized(IRuntimeFactory &inFactory) = 0;
+ virtual bool HasCompletedLoading() = 0;
+ static IAppLoadContext &CreateXMLLoadContext(
+ SApp &inApp, const char8_t *inScaleMode);
+};
+
+inline float Clamp(float val, float inMin = 0.0f, float inMax = 1.0f)
+{
+ if (val < inMin)
+ return inMin;
+ if (val > inMax)
+ return inMax;
+ return val;
+}
+
+// A set of common settings that may come from the UIA or from the command line.
+// command line settings always override uia settings.
+struct SApplicationSettings
+{
+ Option<bool> m_LayerCacheEnabled;
+ Option<bool> m_LayerGpuProfilingEnabled;
+ Option<bool> m_ShaderCachePersistenceEnabled;
+
+ SApplicationSettings() {}
+
+ template <typename TDataType>
+ static Option<TDataType> Choose(const Option<TDataType> &inCommandLine,
+ const Option<TDataType> &inUIAFile)
+ {
+ if (inCommandLine.hasValue())
+ return inCommandLine;
+ return inUIAFile;
+ }
+
+ SApplicationSettings(const SApplicationSettings &inCommandLine,
+ const SApplicationSettings &inUIAFileSettings)
+ : m_LayerCacheEnabled(
+ Choose(inCommandLine.m_LayerCacheEnabled, inUIAFileSettings.m_LayerCacheEnabled))
+ , m_LayerGpuProfilingEnabled(Choose(inCommandLine.m_LayerGpuProfilingEnabled,
+ inUIAFileSettings.m_LayerGpuProfilingEnabled))
+ , m_ShaderCachePersistenceEnabled(Choose(inCommandLine.m_ShaderCachePersistenceEnabled,
+ inUIAFileSettings.m_ShaderCachePersistenceEnabled))
+ {
+ }
+
+ static const char8_t *LayerCacheName() { return "layer-caching"; }
+ static const char8_t *LayerGpuProfilerName() { return "layer-gpu-profiling"; }
+ static const char8_t *ShaderCacheName() { return "shader-cache-persistence"; }
+
+ void ParseBoolEnableDisableItem(const IDOMReader &inReader, const char8_t *itemName,
+ Option<bool> &itemValue)
+ {
+ const char8_t *inItem;
+ if (const_cast<IDOMReader &>(inReader).UnregisteredAtt(itemName, inItem)) {
+ if (AreEqualCaseless(inItem, "disabled"))
+ itemValue = false;
+ else
+ itemValue = true;
+ }
+ }
+
+ void ParseBoolEnableDisableItem(const eastl::vector<eastl::string> &inCommandLine,
+ const char8_t *itemName, Option<bool> &itemValue)
+ {
+ eastl::string temp;
+ temp.assign("-");
+ temp.append(itemName);
+ for (QT3DSU32 idx = 0, end = inCommandLine.size(); idx < end; ++idx) {
+ if (inCommandLine[idx].find(temp) == 0) {
+ if (inCommandLine[idx].length() == temp.size()) {
+ qCWarning(qt3ds::INVALID_OPERATION)
+ << "Unable to parse parameter %s. Please pass =enable|disable as "
+ << "part of the parameter. " << temp.c_str();
+ } else {
+ temp = inCommandLine[idx].substr(temp.size() + 1);
+ eastl::string::size_type start = temp.find_first_of("'\"");
+ if (start != eastl::string::npos)
+ temp.erase(0, start);
+
+ eastl::string::size_type end = temp.find_first_of("'\"");
+ if (end != eastl::string::npos)
+ temp.erase(end);
+ if (AreEqualCaseless(temp.c_str(), "disabled"))
+ itemValue = false;
+ else
+ itemValue = true;
+ qCInfo(qt3ds::INVALID_OPERATION)
+ << "Item " << itemName
+ << (itemValue ? " enabled" : " disabled");
+ }
+ }
+ }
+ }
+
+ template <typename TReaderType>
+ void ParseItems(const TReaderType &inReader)
+ {
+ ParseBoolEnableDisableItem(inReader, LayerCacheName(), m_LayerCacheEnabled);
+ ParseBoolEnableDisableItem(inReader, LayerGpuProfilerName(), m_LayerGpuProfilingEnabled);
+ ParseBoolEnableDisableItem(inReader, ShaderCacheName(), m_ShaderCachePersistenceEnabled);
+ }
+
+ void Parse(IDOMReader &inReader) { ParseItems(inReader); }
+
+ void Parse(const eastl::vector<eastl::string> &inCommandLine) { ParseItems(inCommandLine); }
+
+ struct SOptionSerializer
+ {
+ bool m_HasValue;
+ bool m_Value;
+ QT3DSU8 m_Padding[2];
+ SOptionSerializer(const Option<bool> &inValue = Empty())
+ : m_HasValue(inValue.hasValue())
+ , m_Value(inValue.hasValue() ? *inValue : false)
+ {
+ m_Padding[0] = 0;
+ m_Padding[1] = 0;
+ }
+
+ operator Option<bool>() const
+ {
+ if (m_HasValue)
+ return m_Value;
+ return Empty();
+ }
+ };
+
+ void Save(IOutStream &outStream) const
+ {
+ outStream.Write(SOptionSerializer(m_LayerCacheEnabled));
+ outStream.Write(SOptionSerializer(m_LayerGpuProfilingEnabled));
+ outStream.Write(SOptionSerializer(m_ShaderCachePersistenceEnabled));
+ }
+
+ void Load(IInStream &inStream)
+ {
+ SOptionSerializer s;
+ inStream.Read(s);
+ m_LayerCacheEnabled = s;
+ inStream.Read(s);
+ m_LayerGpuProfilingEnabled = s;
+ inStream.Read(s);
+ m_ShaderCachePersistenceEnabled = s;
+ }
+};
+
+struct SDummyAudioPlayer : public IAudioPlayer
+{
+ virtual ~SDummyAudioPlayer() {}
+ bool PlaySoundFile(const char *inFilePath) override
+ {
+ (void *)inFilePath;
+ qCWarning(qt3ds::TRACE_INFO)
+ << "Qt3DSTegraApplication: Unimplemented method IAudioPlayer::PlaySoundFile";
+ return false;
+ }
+} g_DummyAudioPlayer;
+
+struct SAudioPlayerWrapper : public IAudioPlayer
+{
+private:
+ IApplication *m_Application;
+ IAudioPlayer *m_RealPlayer;
+
+public:
+ SAudioPlayerWrapper()
+ : m_Application(0)
+ , m_RealPlayer(&g_DummyAudioPlayer)
+ {
+ }
+ virtual ~SAudioPlayerWrapper() {}
+
+ void SetApplication(IApplication &inApplication) { m_Application = &inApplication; }
+
+ void SetPlayer(IAudioPlayer *inPlayer)
+ {
+ if (inPlayer)
+ m_RealPlayer = inPlayer;
+ else
+ m_RealPlayer = &g_DummyAudioPlayer;
+ }
+
+ bool PlaySoundFile(const char *inFilePath) override
+ {
+ eastl::string theFilePath(nonNull(inFilePath));
+ if (m_RealPlayer != &g_DummyAudioPlayer) {
+ qt3ds::foundation::CFileTools::CombineBaseAndRelative(
+ m_Application->GetProjectDirectory().c_str(), inFilePath, theFilePath);
+ }
+ return m_RealPlayer->PlaySoundFile(theFilePath.c_str());
+ }
+};
+
+struct SApp : public IApplication
+{
+ NVScopedRefCounted<Q3DStudio::IRuntimeFactoryCore> m_CoreFactory;
+ NVScopedRefCounted<Q3DStudio::IRuntimeFactory> m_RuntimeFactory;
+
+ Q3DStudio::CInputEngine *m_InputEnginePtr;
+ CAppStr m_ApplicationDir;
+ CAppStr m_ProjectDir;
+ CAppStr m_PresentationId;
+ CAppStr m_DLLDirectory;
+ TIdAssetMap m_AssetMap;
+ // Keep the assets ordered. This enables the uia order to mean something.
+ TIdAssetList m_OrderedAssets;
+ SPickFrame m_PickFrame;
+ SPickFrame m_MousePickCache;
+ SPickFrame m_MouseOverCache;
+ THashStrMap m_HashStrMap;
+ CTimer m_Timer;
+ Q3DStudio::INT64 m_ManualTime;
+ SFrameTimer m_FrameTimer;
+ Q3DStudio::INT32 m_FrameCount;
+ // the name of the file without extension.
+ eastl::string m_Filename;
+ Q3DSVariantConfig m_variantConfig;
+ NVScopedRefCounted<STextureUploadRenderTask> m_uploadRenderTask;
+
+ qt3ds::foundation::NVScopedReleasable<IRuntimeMetaData> m_MetaData;
+ nvvector<eastl::pair<SBehaviorAsset, bool>> m_Behaviors;
+ NVScopedRefCounted<SocketSystem> m_SocketSystem;
+ NVScopedRefCounted<SocketStream> m_ServerStream;
+ NVScopedRefCounted<IActivityZoneManager> m_ActivityZoneManager;
+ NVScopedRefCounted<IElementAllocator> m_ElementAllocator;
+
+ // Handles are loaded sorted but only added to the handle map when needed.
+ nvvector<char8_t> m_LoadBuffer;
+ nvvector<CPresentation *> m_PresentationBuffer;
+ Mutex m_RunnableMutex;
+ nvvector<NVScopedRefCounted<IAppRunnable>> m_ThreadRunnables;
+ nvvector<NVScopedRefCounted<IAppRunnable>> m_MainThreadRunnables;
+ NVScopedRefCounted<IAppLoadContext> m_AppLoadContext;
+ bool m_DisableState;
+ bool m_ProfileLogging;
+ bool m_LastRenderWasDirty;
+ QT3DSU64 m_LastFrameStartTime;
+ QT3DSU64 m_ThisFrameStartTime;
+ double m_MillisecondsSinceLastFrame;
+ // We get odd oscillations if we do are too quick to report that the frame wasn't dirty
+ // after input.
+ int m_DirtyCountdown;
+ SApplicationSettings m_UIAFileSettings;
+ eastl::pair<NVDataRef<Q3DStudio::TElement *>, size_t> m_ElementLoadResult;
+
+ SAudioPlayerWrapper m_AudioPlayer;
+
+ Qt3DSAssetVisitor *m_visitor;
+
+ bool m_createSuccessful;
+
+ DataInputMap m_dataInputDefs;
+ DataOutputMap m_dataOutputDefs;
+
+ bool m_initialFrame = true;
+
+ SSlideResourceCounter m_resourceCounter;
+ QSet<QString> m_createSet;
+
+ QT3DSI32 mRefCount;
+ SApp(Q3DStudio::IRuntimeFactoryCore &inFactory, const char8_t *inAppDir)
+ : m_CoreFactory(inFactory)
+ , m_InputEnginePtr(NULL)
+ , m_ApplicationDir(inFactory.GetFoundation().getAllocator())
+ , m_ProjectDir(inFactory.GetFoundation().getAllocator())
+ , m_PresentationId(inFactory.GetFoundation().getAllocator())
+ , m_DLLDirectory(inFactory.GetFoundation().getAllocator())
+ , m_AssetMap(inFactory.GetFoundation().getAllocator(), "SApp::m_AssetMap")
+ , m_OrderedAssets(inFactory.GetFoundation().getAllocator(), "SApp::m_OrderedAssets")
+ , m_HashStrMap(inFactory.GetFoundation().getAllocator(), "SApp::m_HashStrMap")
+ , m_Timer(inFactory.GetTimeProvider())
+ , m_ManualTime(0)
+ , m_FrameCount(0)
+ , m_Behaviors(inFactory.GetFoundation().getAllocator(), "SApp::m_Behaviors")
+ , m_ActivityZoneManager(IActivityZoneManager::CreateActivityZoneManager(
+ inFactory.GetFoundation(), inFactory.GetStringTable()))
+ , m_ElementAllocator(IElementAllocator::CreateElementAllocator(inFactory.GetFoundation(),
+ inFactory.GetStringTable()))
+ , m_LoadBuffer(inFactory.GetFoundation().getAllocator(), "SApp::m_LoadBuffer")
+ , m_PresentationBuffer(inFactory.GetFoundation().getAllocator(),
+ "SApp::m_PresentationBuffer")
+ , m_RunnableMutex(inFactory.GetFoundation().getAllocator())
+ , m_ThreadRunnables(inFactory.GetFoundation().getAllocator(), "SApp::m_ThreadRunnables")
+ , m_MainThreadRunnables(inFactory.GetFoundation().getAllocator(),
+ "SApp::m_MainThreadRunnables")
+ , m_DisableState(true)
+ , m_ProfileLogging(false)
+ , m_LastRenderWasDirty(true)
+ , m_LastFrameStartTime(0)
+ , m_ThisFrameStartTime(0)
+ , m_MillisecondsSinceLastFrame(0)
+ , m_DirtyCountdown(5)
+ , m_visitor(nullptr)
+ , m_createSuccessful(false)
+ , mRefCount(0)
+ {
+ m_PresentationId.append("__initial");
+ m_AudioPlayer.SetApplication(*this);
+ eastl::string tempStr(inAppDir);
+ CFileTools::NormalizePath(tempStr);
+ m_ApplicationDir.assign(tempStr.c_str());
+
+ Q3DStudio_memset(&m_PickFrame, 0, sizeof(SPickFrame));
+ Q3DStudio_memset(&m_MousePickCache, 0, sizeof(SPickFrame));
+ Q3DStudio_memset(&m_MouseOverCache, 0, sizeof(SPickFrame));
+
+ m_Timer.Start();
+
+ m_CoreFactory->SetApplicationCore(this);
+ m_CoreFactory->GetScriptEngineQml().SetApplicationCore(*this);
+
+ m_CoreFactory->AddSearchPath(tempStr.c_str());
+ }
+
+ ~SApp()
+ {
+ EndLoad();
+ {
+ Mutex::ScopedLock __locker(m_RunnableMutex);
+ m_ThreadRunnables.clear();
+ }
+ // Ensure we stop the timer.
+ HasCompletedLoading();
+ m_AppLoadContext = NULL;
+
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ SAssetValue &theAsset = *m_OrderedAssets[idx].second;
+ if (theAsset.getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &thePresAsset = *theAsset.getDataPtr<SPresentationAsset>();
+ if (thePresAsset.m_Presentation) {
+ Q3DStudio_delete(thePresAsset.m_Presentation, CPresentation);
+ thePresAsset.m_Presentation = NULL;
+ }
+ }
+ }
+ }
+
+ void setPresentationId(const QString &id) override
+ {
+ QString oldId = QString::fromLocal8Bit(m_PresentationId.c_str());
+ if (oldId == id)
+ return;
+
+ CRegisteredString idStr = m_CoreFactory->GetStringTable().RegisterStr(id);
+ // Update id key in m_AssetMap
+ TIdAssetMap::iterator iter
+ = m_AssetMap.find(m_CoreFactory->GetStringTable().RegisterStr(oldId));
+ if (iter != m_AssetMap.end()
+ && iter->second->getType() == AssetValueTypes::Presentation) {
+ m_AssetMap.insert(eastl::make_pair(idStr, iter->second));
+ m_AssetMap.erase(iter);
+ }
+ for (unsigned i = 0; i < m_OrderedAssets.size(); i++) {
+ auto &asset = m_OrderedAssets[i];
+ if (oldId == asset.first.c_str()) {
+ asset.first = idStr;
+ break;
+ }
+ }
+
+ m_PresentationId.assign(qPrintable(id));
+ }
+
+ void setAssetVisitor(qt3ds::Qt3DSAssetVisitor *v) override
+ {
+ m_visitor = v;
+ }
+
+ NVDataRef<CPresentation *> GetPresentations()
+ {
+ if (m_PresentationBuffer.empty()) {
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ SAssetValue &theAsset = *m_OrderedAssets[idx].second;
+ if (theAsset.getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &thePresAsset = *theAsset.getDataPtr<SPresentationAsset>();
+ if (thePresAsset.m_Presentation)
+ m_PresentationBuffer.push_back(thePresAsset.m_Presentation);
+ }
+ }
+ }
+ return m_PresentationBuffer;
+ }
+ QVector<CPresentation *> getPresentations()
+ {
+ QVector<CPresentation *> presentations;
+ if (m_PresentationBuffer.empty()) {
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ SAssetValue &theAsset = *m_OrderedAssets[idx].second;
+ if (theAsset.getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &thePresAsset = *theAsset.getDataPtr<SPresentationAsset>();
+ if (thePresAsset.m_Presentation)
+ presentations.push_back(thePresAsset.m_Presentation);
+ }
+ }
+ }
+ return presentations;
+ }
+
+ void addRef() override { atomicIncrement(&mRefCount); }
+
+ void release() override
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0)
+ NVDelete(m_CoreFactory->GetFoundation().getAllocator(), this);
+ }
+
+ void QueueForMainThread(IAppRunnable &inRunnable) override
+ {
+ Mutex::ScopedLock __locker(m_RunnableMutex);
+ m_ThreadRunnables.push_back(inRunnable);
+ }
+
+ virtual void EnableProfileLogging()
+ {
+ m_ProfileLogging = true;
+ if (m_RuntimeFactory)
+ m_RuntimeFactory->GetScriptEngineQml().EnableProfiling();
+ }
+
+ // Verbose logging is disabled by default.
+ virtual void SetVerboseLogging(bool inEnableVerboseLogging)
+ {
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Update rhythm implementations
+ ////////////////////////////////////////////////////////////////////////////
+
+ void SetPickFrame(const SPickFrame &inPickFrame)
+ {
+ // The model has changed, fire enter and exit mouse events
+ if (inPickFrame.m_Model != m_PickFrame.m_Model) {
+ // For determining onGroupedMouseOver/Out:
+ // arg1 = the original onMouseOut model and arg2 = the original onMouseOver model
+ UVariant theMouseOutModel;
+ UVariant theMouseOverModel;
+ theMouseOutModel.m_VoidPointer = m_PickFrame.m_Model;
+ theMouseOverModel.m_VoidPointer = inPickFrame.m_Model;
+
+ // It seems like you would want to 'onMouseOut' before you 'onMouseOver' something new?
+ if (m_PickFrame.m_Model) {
+ m_PickFrame.m_Model->GetBelongedPresentation()->FireEvent(
+ ON_MOUSEOUT, m_PickFrame.m_Model, &theMouseOutModel, &theMouseOverModel,
+ ATTRIBUTETYPE_POINTER, ATTRIBUTETYPE_POINTER);
+ }
+ if (inPickFrame.m_Model) {
+ inPickFrame.m_Model->GetBelongedPresentation()->FireEvent(
+ ON_MOUSEOVER, inPickFrame.m_Model, &theMouseOutModel, &theMouseOverModel,
+ ATTRIBUTETYPE_POINTER, ATTRIBUTETYPE_POINTER);
+ m_MouseOverCache = inPickFrame;
+ }
+ }
+
+ const TEventCommandHash *theEventArray[] = { &ON_MOUSEDOWN, &ON_MOUSEUP,
+ &ON_MIDDLEMOUSEDOWN, &ON_MIDDLEMOUSEUP,
+ &ON_RIGHTMOUSEDOWN, &ON_RIGHTMOUSEUP };
+ const TEventCommandHash *theClickEventArray[] = { &ON_MOUSECLICK, &ON_MIDDLEMOUSECLICK,
+ &ON_RIGHTMOUSECLICK };
+
+ // Click events...
+ // NOTE: This is a fancy way to iterate programatically over all the handled mouse inputs
+ // handled (declared in AKPickFrame.h for now)
+ // we iterate to INPUTBUTTONCOUNT (see comment in AKPickFrame.h) * 2, because we handle
+ // mouse down and up
+ for (QT3DSI32 theMouseEvtIter = 0; theMouseEvtIter < MOUSEBUTTONCOUNT - 1;
+ theMouseEvtIter++) {
+ // we effectively iterate to MOUSEBUTTONCOUNT * 2 (see comment in AKPickFrame.h) to
+ // handle mouse down and up
+ QT3DSI32 theMouseDownFlag = 1 << (theMouseEvtIter * 2);
+ QT3DSI32 theMouseUpFlag = 1 << (theMouseEvtIter * 2 + 1);
+
+ // on*MouseDown
+ // if this frame, the mouse button is down, and last frame it wasn't (new down click)
+ if (inPickFrame.m_Model && inPickFrame.m_InputFrame.m_MouseFlags & theMouseDownFlag
+ && !(m_PickFrame.m_InputFrame.m_MouseFlags & theMouseDownFlag)) {
+ // fire the 'on*MouseDown' event - which is at the even indices since the down
+ // events for each button are before the up
+ inPickFrame.m_Model->GetBelongedPresentation()->FireEvent(
+ *theEventArray[theMouseEvtIter * 2], inPickFrame.m_Model);
+
+ // cache this as the last item we 'onMouseDown' on
+ m_MousePickCache = inPickFrame;
+ }
+
+ // on*MouseUp
+ // if we mouse up on anything, send the event
+ if (inPickFrame.m_InputFrame.m_MouseFlags & theMouseUpFlag) {
+ // fire the 'on*MouseUp' event - odd indices (1,3,5 etc)
+ if (m_MousePickCache.m_Model) {
+ m_MousePickCache.m_Model->GetBelongedPresentation()->FireEvent(
+ *theEventArray[theMouseEvtIter * 2 + 1], m_MousePickCache.m_Model);
+ }
+
+ // on*MouseClick
+ // if we had a up click on the same item we were mouse down on last frame ... we had
+ // a click
+ if (inPickFrame.m_Model && inPickFrame.m_Model == m_MousePickCache.m_Model) {
+ inPickFrame.m_Model->GetBelongedPresentation()->FireEvent(
+ *theClickEventArray[theMouseEvtIter], inPickFrame.m_Model);
+ }
+
+ // clear the stored 'last mouse down' since we just got a mouse up
+ Q3DStudio_memset(&m_MousePickCache, 0, sizeof(SPickFrame));
+ }
+
+ // on*MouseDblClick
+ }
+
+ if (m_MouseOverCache.m_Model) {
+
+ if (inPickFrame.m_InputFrame.m_MouseFlags & VSCROLLWHEEL) {
+ UVariant theScrollValue;
+ theScrollValue.m_INT32 = inPickFrame.m_InputFrame.m_ScrollValue;
+ m_MouseOverCache.m_Model->GetBelongedPresentation()->FireEvent(
+ ON_VERTICALSCROLLWHEEL, m_MouseOverCache.m_Model, &theScrollValue,
+ NULL, ATTRIBUTETYPE_INT32);
+ } else if (inPickFrame.m_InputFrame.m_MouseFlags & HSCROLLWHEEL) {
+ UVariant theScrollValue;
+ theScrollValue.m_INT32 = inPickFrame.m_InputFrame.m_ScrollValue;
+ m_MouseOverCache.m_Model->GetBelongedPresentation()->FireEvent(
+ ON_HORIZONTALSCROLLWHEEL, m_MouseOverCache.m_Model, &theScrollValue,
+ NULL, ATTRIBUTETYPE_INT32);
+ }
+ }
+
+ // Do this last
+ m_PickFrame = inPickFrame;
+ }
+
+ void ClearPresentationDirtyLists()
+ {
+ NVDataRef<CPresentation *> thePresentations(GetPresentations());
+ for (QT3DSU32 idx = 0, end = thePresentations.size(); idx < end; ++idx)
+ thePresentations[idx]->ClearDirtyList();
+ }
+
+ void UpdatePresentations()
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(), "UpdatePresentations");
+ // Transfer the input frame to the kernel for pick processing
+ // the scene manager now handles the picking on each of its scenes
+ SetPickFrame(m_RuntimeFactory->GetSceneManager().AdvancePickFrame(
+ m_InputEnginePtr->GetInputFrame()));
+ // clear up mouse flag for horizontal and vertical scroll
+ m_InputEnginePtr->GetInputFrame().m_MouseFlags &= !(HSCROLLWHEEL | VSCROLLWHEEL);
+
+ // Update all the presentations.
+ // Animations are advanced based on m_Timer by default, but this can be overridden via
+ // SetTimeMilliSecs().
+ Q3DStudio::INT64 globalTime(GetTimeMilliSecs());
+
+ NVDataRef<CPresentation *> thePresentations(GetPresentations());
+
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(),
+ "UpdatePresentations - pre update");
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &theAsset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *thePresentation = theAsset.m_Presentation;
+ if (thePresentation && thePresentation->GetActive())
+ thePresentation->PreUpdate(globalTime);
+ }
+ }
+ }
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(),
+ "UpdatePresentations - begin update");
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &theAsset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *thePresentation = theAsset.m_Presentation;
+ if (thePresentation && thePresentation->GetActive())
+ thePresentation->BeginUpdate();
+ }
+ }
+ }
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(),
+ "UpdatePresentations - end update");
+
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &theAsset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *thePresentation = theAsset.m_Presentation;
+ // allow EndUpdate also for inactive presentations so that we can
+ // activate it
+ if (thePresentation)
+ thePresentation->EndUpdate();
+ }
+ }
+ }
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(),
+ "UpdatePresentations - postupdate");
+
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &theAsset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *thePresentation = theAsset.m_Presentation;
+ // allow PostUpdate also for inactive presentations so that we can
+ // activate it
+ if (thePresentation)
+ thePresentation->PostUpdate(globalTime);
+ }
+ }
+ }
+
+ // Run the garbage collection
+ m_CoreFactory->GetScriptEngineQml().StepGC();
+ }
+
+ void NotifyDataOutputs()
+ {
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(),
+ "NotifyDataOutputs");
+
+ // Allow presentations to notify of registered data output changes
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &asset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *presentation = asset.m_Presentation;
+ // allow PostUpdate also for inactive presentations so that we can
+ // activate it
+ if (presentation)
+ presentation->NotifyDataOutputs();
+ }
+ }
+
+ // Notify @timeline attribute changes and store latest value to notified DataOutputDef
+ QMutableMapIterator<QString, DataOutputDef> iter(m_dataOutputDefs);
+ while (iter.hasNext()) {
+ iter.next();
+ DataOutputDef &outDef = iter.value();
+ if (outDef.observedAttribute.propertyType == ATTRIBUTETYPE_DATAINPUT_TIMELINE
+ && outDef.timelineComponent) {
+ qreal newValue = outDef.timelineComponent->GetTimePolicy().GetTime();
+ qreal timelineEndTime
+ = outDef.timelineComponent->GetTimePolicy().GetLoopingDuration();
+
+ // Normalize the value to dataOutput range (if defined)
+ if (outDef.min < outDef.max && timelineEndTime != 0.0) {
+ newValue = (newValue/timelineEndTime) * qreal(outDef.max - outDef.min);
+ newValue += qreal(outDef.min);
+ } else {
+ // Normalize to milliseconds
+ newValue *= 1000.0;
+ }
+
+ if (!outDef.value.isValid() || newValue != outDef.value.toReal()) {
+ outDef.value.setValue(newValue);;
+ GetPrimaryPresentation()->signalProxy()->SigDataOutputValueUpdated(
+ outDef.name, outDef.value);
+ }
+ }
+ }
+ } // End SStackPerfTimer scope
+ }
+
+ void UpdateScenes() { m_RuntimeFactory->GetSceneManager().Update(); }
+
+ void Render()
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(), "Render");
+ CPresentation *pres = GetPrimaryPresentation();
+ if (pres) {
+ m_LastRenderWasDirty = m_RuntimeFactory->GetSceneManager()
+ .RenderPresentation(pres, m_initialFrame);
+ m_initialFrame = false;
+ }
+ }
+
+ void ResetDirtyCounter() { m_DirtyCountdown = 5; }
+
+ // Update all the presentations and render them.
+ void UpdateAndRender() override
+ {
+ QT3DS_ASSERT(m_AppLoadContext.mPtr == NULL);
+ m_ThisFrameStartTime = qt3ds::foundation::Time::getCurrentCounterValue();
+ if (m_LastFrameStartTime) {
+ QT3DSU64 durationSinceLastFrame = m_ThisFrameStartTime - m_LastFrameStartTime;
+ m_MillisecondsSinceLastFrame =
+ qt3ds::foundation::Time::sCounterFreq.toTensOfNanos(durationSinceLastFrame)
+ * (1.0 / 100000.0);
+ } else {
+ m_MillisecondsSinceLastFrame = 0;
+ }
+
+ ++m_FrameCount;
+
+ // First off, update any application level behaviors.
+ IScriptBridge &theScriptEngine = m_CoreFactory->GetScriptEngineQml();
+ for (QT3DSU32 idx = 0, end = m_Behaviors.size(); idx < end; ++idx) {
+ eastl::pair<SBehaviorAsset, bool> &entry(m_Behaviors[idx]);
+ if (!entry.second) {
+ entry.second = true;
+ theScriptEngine.ExecuteApplicationScriptFunction(entry.first.m_Handle,
+ "onInitialize");
+ }
+ }
+
+ // TODO: Initialize presentations
+
+ for (QT3DSU32 idx = 0, end = m_Behaviors.size(); idx < end; ++idx) {
+ eastl::pair<SBehaviorAsset, bool> &entry(m_Behaviors[idx]);
+ theScriptEngine.ExecuteApplicationScriptFunction(entry.first.m_Handle, "onUpdate");
+ }
+
+ UpdatePresentations();
+
+ UpdateScenes();
+
+ Render();
+
+ m_InputEnginePtr->ClearInputFrame();
+
+ NotifyDataOutputs();
+
+ ClearPresentationDirtyLists();
+
+ if (!m_CoreFactory->GetEventSystem().GetAndClearEventFetchedFlag())
+ m_CoreFactory->GetEventSystem().PurgeEvents(); // GetNextEvents of event system has not
+ // been called in this round, so clear
+ // events to avoid events to be piled up
+
+ m_RuntimeFactory->GetQt3DSRenderContext().SetFrameTime(m_MillisecondsSinceLastFrame);
+ if (floor(m_FrameTimer.GetElapsedSeconds()) > 0.0f) {
+ QPair<QT3DSF32, int> fps = m_FrameTimer.GetFPS(m_FrameCount);
+ m_RuntimeFactory->GetQt3DSRenderContext().SetFPS(fps);
+ if (m_ProfileLogging) {
+ qCInfo(PERF_INFO, "Render Statistics: %3.2ffps, frame count %d",
+ fps.first, fps.second);
+ }
+ }
+
+ m_CoreFactory->GetPerfTimer().ResetTimerData();
+
+ fflush(stdout);
+ m_LastFrameStartTime = m_ThisFrameStartTime;
+ if (m_LastRenderWasDirty)
+ ResetDirtyCounter();
+ else
+ m_DirtyCountdown = NVMax(0, m_DirtyCountdown - 1);
+ }
+
+ // hook this up to -layer-caching.
+ // otherwise it might be hard to measure performance
+ bool IsApplicationDirty() override
+ {
+ return (m_DirtyCountdown > 0);
+ }
+
+ double GetMillisecondsSinceLastFrame() override { return m_MillisecondsSinceLastFrame; }
+
+ void MarkApplicationDirty() override { ResetDirtyCounter(); }
+
+ Q3DStudio::IAudioPlayer &GetAudioPlayer() override { return m_AudioPlayer; }
+ ////////////////////////////////////////////////////////////////////////////////
+ // Generalized save/load
+ ////////////////////////////////////////////////////////////////////////////////
+
+ void loadComponentSlideResources(TElement *component, CPresentation *presentation, int index,
+ const QString slideName, bool wait)
+ {
+ if (m_RuntimeFactory->GetQt3DSRenderContext().GetBufferManager()
+ .isReloadableResourcesEnabled()) {
+ auto &slidesystem = presentation->GetSlideSystem();
+ SSlideKey key;
+ key.m_Component = component;
+ key.m_Index = index;
+ slidesystem.setUnloadSlide(key, false);
+ const QString completeName = presentation->GetName() + QLatin1Char(':')
+ + QString::fromUtf8(key.m_Component->m_Name) + QLatin1Char(':') + slideName;
+ qCInfo(PERF_INFO) << "Load component slide resources: " << completeName;
+ m_resourceCounter.handleLoadSlide(completeName, key, slidesystem);
+ if (m_uploadRenderTask)
+ m_uploadRenderTask->add(m_resourceCounter.createSet, wait);
+ else
+ m_createSet.unite(m_resourceCounter.createSet);
+ }
+ }
+
+ void unloadComponentSlideResources(TElement *component, CPresentation *presentation, int index,
+ const QString slideName)
+ {
+ if (m_RuntimeFactory->GetQt3DSRenderContext().GetBufferManager()
+ .isReloadableResourcesEnabled()) {
+ auto &slidesystem = presentation->GetSlideSystem();
+ SSlideKey key;
+ key.m_Component = component;
+ key.m_Index = index;
+ slidesystem.setUnloadSlide(key, true);
+ if (!slidesystem.isActiveSlide(key)) {
+ const QString completeName = presentation->GetName() + QLatin1Char(':')
+ + QString::fromUtf8(key.m_Component->m_Name) + QLatin1Char(':') + slideName;
+ qCInfo(PERF_INFO) << "Unload component slide resources: " << completeName;
+ m_resourceCounter.handleUnloadSlide(completeName, key, slidesystem);
+
+ if (m_uploadRenderTask)
+ m_uploadRenderTask->remove(m_resourceCounter.deleteSet);
+ }
+ }
+ }
+
+ bool LoadUIP(SPresentationAsset &inAsset,
+ NVConstDataRef<SElementAttributeReference> inExternalReferences)
+ {
+ GetMetaData();
+ eastl::string theFile;
+ CFileTools::CombineBaseAndRelative(GetProjectDirectory().c_str(), inAsset.m_Src.c_str(),
+ theFile);
+ // Check if the file event exists
+ eastl::string fullPath;
+ NVScopedRefCounted<qt3ds::render::IRefCountedInputStream> theStream
+ = m_CoreFactory->GetRenderContextCore().GetInputStreamFactory().GetStreamForFile(
+ theFile.c_str());
+ if (theStream) {
+ theStream = NULL;
+ CPresentation *thePresentation
+ = Q3DStudio_new(CPresentation) CPresentation(inAsset.m_Id.c_str(),
+ GetProjectDirectory().c_str(),
+ this);
+ inAsset.m_Presentation = thePresentation;
+ thePresentation->SetFilePath(theFile.c_str());
+ NVScopedReleasable<IUIPParser> theUIPParser(IUIPParser::Create(
+ theFile.c_str(), *m_MetaData,
+ m_CoreFactory->GetInputStreamFactory(),
+ m_CoreFactory->GetStringTable()));
+ Q3DStudio::IScene *newScene = NULL;
+ m_PresentationBuffer.clear();
+ if (theUIPParser->Load(*thePresentation, inExternalReferences)) {
+ // Load the scene graph portion of the scene.
+ newScene = m_RuntimeFactory->GetSceneManager().LoadScene(
+ thePresentation, theUIPParser.mPtr,
+ m_CoreFactory->GetScriptEngineQml(),
+ m_variantConfig);
+ }
+
+ if (newScene == NULL) {
+ Q3DStudio_delete(thePresentation, CPresentation);
+ qCWarning(qt3ds::INVALID_OPERATION)
+ << "Unable to load presentation " << theFile.c_str();
+ inAsset.m_Presentation = NULL;
+ return false;
+ } else {
+ if (inAsset.m_Id.IsValid() && m_PresentationId.empty())
+ m_PresentationId.assign(inAsset.m_Id);
+
+ if (inAsset.m_Id.IsValid())
+ newScene->RegisterOffscreenRenderer(inAsset.m_Id);
+
+ QVector<TElement *> components;
+ thePresentation->GetRoot()->findComponents(components);
+ for (auto &component : qAsConst(components))
+ loadComponentSlideResources(component, thePresentation, 0, "Master", true);
+
+ return true;
+ }
+ }
+ qCWarning(qt3ds::INVALID_OPERATION) << "Unable to load presentation " << theFile.c_str();
+ return false;
+ }
+
+ bool LoadUIA(IDOMReader &inReader, NVFoundationBase &fnd)
+ {
+ IDOMReader::Scope __preparseScope(inReader);
+ {
+ m_UIAFileSettings.Parse(inReader);
+ }
+ {
+ IDOMReader::Scope __assetsScope(inReader);
+ if (!inReader.MoveToFirstChild("assets")) {
+ qCCritical(INVALID_OPERATION,
+ "UIA input xml doesn't contain <assets> tag; load failed");
+ return false;
+ }
+
+ eastl::string pathString;
+
+ const char8_t *initialItem = "";
+ inReader.UnregisteredAtt("initial", initialItem);
+ m_PresentationId.clear();
+ if (!isTrivial(initialItem)) {
+ if (initialItem[0] == '#')
+ ++initialItem;
+
+ m_PresentationId.assign(initialItem);
+ }
+ eastl::vector<SElementAttributeReference> theUIPReferences;
+ eastl::string tempString;
+
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __assetScope(inReader);
+ const char8_t *itemId("");
+ inReader.UnregisteredAtt("id", itemId);
+ const char8_t *src = "";
+ inReader.UnregisteredAtt("src", src);
+ pathString.clear();
+ if (!isTrivial(src))
+ CFileTools::CombineBaseAndRelative(m_ProjectDir.c_str(), src, pathString);
+
+ const char8_t *assetName = inReader.GetElementName();
+ if (AreEqual(assetName, "presentation")) {
+ SPresentationAsset theAsset(RegisterStr(itemId), RegisterStr(src));
+ bool activeFlag;
+ if (inReader.Att("active", activeFlag))
+ theAsset.m_Active = activeFlag;
+ RegisterAsset(theAsset);
+ } else if (AreEqual(assetName, "dataInput")) {
+ DataInputDef diDef;
+ const char8_t *name = "";
+ const char8_t *type = "";
+ const char8_t *evaluator = "";
+ const char8_t *metadataStr = "";
+ diDef.value = QVariant::Invalid;
+ inReader.UnregisteredAtt("name", name);
+ inReader.UnregisteredAtt("type", type);
+ inReader.Att("min", diDef.min);
+ inReader.Att("max", diDef.max);
+ inReader.UnregisteredAtt("evaluator", evaluator);
+ if (AreEqual(type, "Ranged Number"))
+ diDef.type = DataInOutTypeRangedNumber;
+ else if (AreEqual(type, "String"))
+ diDef.type = DataInOutTypeString;
+ else if (AreEqual(type, "Float"))
+ diDef.type = DataInOutTypeFloat;
+ else if (AreEqual(type, "Vector4"))
+ diDef.type = DataInOutTypeVector4;
+ else if (AreEqual(type, "Vector3"))
+ diDef.type = DataInOutTypeVector3;
+ else if (AreEqual(type, "Vector2"))
+ diDef.type = DataInOutTypeVector2;
+ else if (AreEqual(type, "Boolean"))
+ diDef.type = DataInOutTypeBoolean;
+ else if (AreEqual(type, "Variant"))
+ diDef.type = DataInOutTypeVariant;
+
+ if (AreEqual(type, "Evaluator")) {
+ diDef.type = DataInOutTypeEvaluator;
+ diDef.evaluator = QString::fromUtf8(evaluator);
+ }
+
+ inReader.UnregisteredAtt("metadata", metadataStr);
+ QString metaData = QString(metadataStr);
+ if (!metaData.isEmpty()) {
+ auto metadataList = metaData.split(QLatin1Char('$'));
+
+ if (metadataList.size() & 1) {
+ qWarning("Malformed datainput metadata for datainput %s, cannot"
+ "parse key - value pairs. Stop parsing metadata.",
+ qUtf8Printable(name));
+ } else {
+ for (int i = 0; i < metadataList.size(); i += 2) {
+ if (metadataList[i].isEmpty()) {
+ qWarning("Malformed datainput metadata for datainput %s "
+ "- metadata key empty. Stop parsing metadata.",
+ qUtf8Printable(name));
+ break;
+ }
+ diDef.metadata.insert(metadataList[i], metadataList[i+1]);
+ }
+ }
+ }
+ m_dataInputDefs.insert(QString::fromUtf8(name), diDef);
+// #TODO Remove below once QT3DS-3510 task has been completed.
+ // By default data inputs should not have data outputs, but this is needed
+ // until editor can support configuring data nodes as in/out/in-out types
+ DataOutputDef outDef;
+ outDef.type = diDef.type;
+ outDef.value = diDef.value;
+ outDef.name = QString::fromUtf8(name);
+ m_dataOutputDefs.insert(QString::fromUtf8(name), outDef);
+// #TODO Remove above once QT3DS-3510 UI change has been done
+ } else if (AreEqual(assetName, "dataOutput")) {
+ DataOutputDef outDef;
+ const char8_t *name = "";
+ const char8_t *type = "";
+ outDef.value = QVariant::Invalid;
+ inReader.UnregisteredAtt("name", name);
+ inReader.UnregisteredAtt("type", type);
+ inReader.Att("min", outDef.min);
+ inReader.Att("max", outDef.max);
+ if (type) {
+ if (AreEqual(type, "Ranged Number"))
+ outDef.type = DataInOutTypeRangedNumber;
+ else if (AreEqual(type, "String"))
+ outDef.type = DataInOutTypeString;
+ else if (AreEqual(type, "Float"))
+ outDef.type = DataInOutTypeFloat;
+ else if (AreEqual(type, "Vector4"))
+ outDef.type = DataInOutTypeVector4;
+ else if (AreEqual(type, "Vector3"))
+ outDef.type = DataInOutTypeVector3;
+ else if (AreEqual(type, "Vector2"))
+ outDef.type = DataInOutTypeVector2;
+ else if (AreEqual(type, "Boolean"))
+ outDef.type = DataInOutTypeBoolean;
+ else if (AreEqual(type, "Variant"))
+ outDef.type = DataInOutTypeVariant;
+ }
+
+ outDef.name = QString::fromUtf8(name);
+ m_dataOutputDefs.insert(QString::fromUtf8(name), outDef);
+ } else if (AreEqual(assetName, "renderplugin")) {
+ const char8_t *pluginArgs = "";
+ inReader.UnregisteredAtt("args", pluginArgs);
+ RegisterAsset(SRenderPluginAsset(RegisterStr(itemId), RegisterStr(src),
+ RegisterStr(pluginArgs)));
+ } else if (AreEqual(assetName, "behavior")) {
+ SBehaviorAsset theAsset(RegisterStr(itemId), RegisterStr(src), 0);
+ RegisterAsset(theAsset);
+ } else if (AreEqual(assetName, "presentation-qml")) {
+ const char8_t *args = "";
+ inReader.UnregisteredAtt("args", args);
+ RegisterAsset(SQmlPresentationAsset(RegisterStr(itemId), RegisterStr(src),
+ RegisterStr(args)));
+ } else {
+ qCWarning(WARNING, "Unrecognized <assets> child %s", assetName);
+ }
+ }
+ } // end assets scope
+ const char8_t *initialScaleMode = "";
+ inReader.UnregisteredAtt("scalemode", initialScaleMode);
+
+ m_AppLoadContext
+ = IAppLoadContext::CreateXMLLoadContext(*this,
+ initialScaleMode);
+ return true;
+ }
+
+ DataInputMap &dataInputMap() override
+ {
+ return m_dataInputDefs;
+ }
+
+ DataOutputMap &dataOutputMap() override
+ {
+ return m_dataOutputDefs;
+ }
+
+ QList<QString> dataInputs() const override
+ {
+ return m_dataInputDefs.keys();
+ }
+
+ QList<QString> dataOutputs() const override
+ {
+ return m_dataOutputDefs.keys();
+ }
+
+ float dataInputMax(const QString &name) const override
+ {
+ return m_dataInputDefs[name].max;
+ }
+
+ float dataInputMin(const QString &name) const override
+ {
+ return m_dataInputDefs[name].min;
+ }
+
+ QHash<QString, QString> dataInputMetadata(const QString &name) const override
+ {
+ return m_dataInputDefs[name].metadata;
+ }
+
+ struct SAppXMLErrorHandler : public qt3ds::foundation::CXmlErrorHandler
+ {
+ NVFoundationBase &m_Foundation;
+ const char8_t *m_FilePath;
+ SAppXMLErrorHandler(NVFoundationBase &fnd, const char8_t *filePath)
+ : m_Foundation(fnd)
+ , m_FilePath(filePath)
+ {
+ }
+
+ void OnXmlError(TXMLCharPtr errorName, int line, int /*column*/) override
+ {
+ qCWarning(INVALID_OPERATION, m_FilePath, line, "%s", errorName);
+ }
+ };
+
+ bool BeginLoad(const QString &sourcePath, const QStringList &variantList) override
+ {
+ SStackPerfTimer __loadTimer(m_CoreFactory->GetPerfTimer(), "Application: Begin Load");
+ eastl::string directory;
+ eastl::string filename;
+ eastl::string extension;
+ CFileTools::Split(sourcePath.toUtf8().constData(), directory, filename, extension);
+ eastl::string projectDirectory(directory);
+
+ m_ProjectDir.assign(projectDirectory.c_str());
+ m_CoreFactory->AddSearchPath(projectDirectory.c_str());
+
+ // add additional search path
+ QString projectDir = CFileTools::NormalizePathForQtUsage(projectDirectory.c_str());
+ if (!projectDir.startsWith(QStringLiteral(":"))) {
+ eastl::string relativeProjectDir;
+ CFileTools::CombineBaseAndRelative(m_ApplicationDir.c_str(), projectDirectory.c_str(),
+ relativeProjectDir);
+ m_CoreFactory->AddSearchPath(relativeProjectDir.c_str());
+ }
+
+ // For QT3DS-3353 assume project fonts are in a subdirectory relative to project.
+ eastl::string projectFontDirectory = projectDirectory + "/fonts";
+
+ NVFoundationBase &fnd(m_CoreFactory->GetFoundation());
+
+ if (m_CoreFactory->GetRenderContextCore().getDistanceFieldRenderer()) {
+ m_CoreFactory->GetRenderContextCore().getDistanceFieldRenderer()
+ ->AddProjectFontDirectory(projectFontDirectory.c_str());
+ }
+
+ if (m_CoreFactory->GetRenderContextCore().GetTextRendererCore()) {
+ m_CoreFactory->GetRenderContextCore().GetTextRendererCore()->AddProjectFontDirectory(
+ projectFontDirectory.c_str());
+ m_CoreFactory->GetRenderContextCore().GetTextRendererCore()->BeginPreloadFonts(
+ m_CoreFactory->GetRenderContextCore().GetThreadPool(),
+ m_CoreFactory->GetRenderContextCore().GetPerfTimer());
+ }
+ m_Filename = filename;
+ m_variantConfig.setVariantList(variantList);
+ bool retval = false;
+ if (extension.comparei("uip") == 0) {
+ m_PresentationId.assign("__initial");
+ eastl::string relativePath = "./";
+ relativePath.append(filename);
+ relativePath.append(".");
+ relativePath.append("uip");
+ RegisterAsset(SPresentationAsset(RegisterStr(m_PresentationId.c_str()),
+ RegisterStr(relativePath.c_str())));
+ m_AppLoadContext = IAppLoadContext::CreateXMLLoadContext(*this, "");
+
+ retval = true;
+ } else if (extension.comparei("uia") == 0) {
+ CFileSeekableIOStream inputStream(sourcePath, FileReadFlags());
+ if (inputStream.IsOpen()) {
+ NVScopedRefCounted<IStringTable> strTable(
+ IStringTable::CreateStringTable(fnd.getAllocator()));
+ NVScopedRefCounted<IDOMFactory> domFactory(
+ IDOMFactory::CreateDOMFactory(fnd.getAllocator(), strTable));
+ SAppXMLErrorHandler errorHandler(fnd, sourcePath.toUtf8().constData());
+ eastl::pair<SNamespacePairNode *, SDOMElement *> readResult =
+ CDOMSerializer::Read(*domFactory, inputStream, &errorHandler);
+ if (!readResult.second) {
+ qCCritical(INVALID_PARAMETER, "%s doesn't appear to be valid xml",
+ sourcePath.toUtf8().constData());
+ } else {
+ NVScopedRefCounted<IDOMReader> domReader = IDOMReader::CreateDOMReader(
+ fnd.getAllocator(), *readResult.second, strTable, domFactory);
+ if (m_visitor)
+ m_visitor->visit(sourcePath.toUtf8().constData());
+ retval = LoadUIA(*domReader, fnd);
+ }
+ } else {
+ qCCritical(INVALID_PARAMETER, "Unable to open input file %s",
+ sourcePath.toUtf8().constData());
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ return retval;
+ }
+
+ void EndLoad() override
+ {
+ if (m_AppLoadContext)
+ m_AppLoadContext->EndLoad();
+ }
+
+ void RunAllRunnables()
+ {
+ {
+ Mutex::ScopedLock __locker(m_RunnableMutex);
+ m_MainThreadRunnables = m_ThreadRunnables;
+ m_ThreadRunnables.clear();
+ }
+ for (QT3DSU32 idx = 0, end = m_MainThreadRunnables.size(); idx < end; ++idx)
+ m_MainThreadRunnables[idx]->Run();
+ m_MainThreadRunnables.clear();
+ }
+
+ bool HasCompletedLoading() override
+ {
+ RunAllRunnables();
+ if (m_AppLoadContext)
+ return m_AppLoadContext->HasCompletedLoading();
+
+ return true;
+ }
+
+ bool createSuccessful() override
+ {
+ return m_createSuccessful;
+ }
+
+ bool presentationComponentSlide(const QString &elementPath,
+ Q3DStudio::CPresentation *&presentation,
+ TElement *&component,
+ QString &slideName,
+ int &index)
+ {
+ presentation = GetPrimaryPresentation();
+ slideName = elementPath;
+ QString componentName;
+ if (elementPath.contains(QLatin1Char(':'))) {
+ // presentation : component : slide
+ QStringList splits = elementPath.split(QLatin1Char(':'));
+ if (splits.size() == 3) {
+ presentation = GetPresentationById(qPrintable(splits[0]));
+ componentName = splits[1];
+ slideName = splits[2];
+ } else {
+ componentName = splits[0];
+ slideName = splits[1];
+ }
+ // else assume main presentation and component:slide
+ }
+ component = presentation->GetRoot();
+ if (!componentName.isNull() && componentName != component->m_Name)
+ component = component->FindChild(CHash::HashString(qPrintable(componentName)));
+ if (!component) {
+ qCWarning(WARNING) << "Could not find slide: " << elementPath;
+ return false;
+ }
+ ISlideSystem &s = presentation->GetSlideSystem();
+ index = s.FindSlide(*component, qPrintable(slideName));
+ return true;
+ }
+
+ void preloadSlide(const QString &slide) override
+ {
+ CPresentation *pres = nullptr;
+ TElement *component = nullptr;
+ QString slideName;
+ int index;
+ if (presentationComponentSlide(slide, pres, component, slideName, index))
+ loadComponentSlideResources(component, pres, index, slideName, false);
+ }
+
+ void unloadSlide(const QString &slide) override
+ {
+ CPresentation *pres = nullptr;
+ TElement *component = nullptr;
+ QString slideName;
+ int index;
+ if (presentationComponentSlide(slide, pres, component, slideName, index))
+ unloadComponentSlideResources(component, pres, index, slideName);
+ }
+
+ void setDelayedLoading(bool enable)
+ {
+ m_RuntimeFactory->GetQt3DSRenderContext().GetBufferManager()
+ .enableReloadableResources(enable);
+ }
+
+ void ComponentSlideEntered(Q3DStudio::CPresentation *presentation,
+ Q3DStudio::TElement *component,
+ const QString &elementPath, int slideIndex,
+ const QString &slideName) override
+ {
+ loadComponentSlideResources(component, presentation, slideIndex, slideName, true);
+ }
+
+ void ComponentSlideExited(Q3DStudio::CPresentation *presentation,
+ Q3DStudio::TElement *component,
+ const QString &elementPath, int slideIndex,
+ const QString &slideName) override
+ {
+ unloadComponentSlideResources(component, presentation, slideIndex, slideName);
+ }
+
+ // will force loading to end if endLoad hasn't been called yet. Will fire off loading
+ // of resources that need to be uploaded to opengl. Maintains reference to runtime factory
+ IApplication &CreateApplication(Q3DStudio::CInputEngine &inInputEngine,
+ Q3DStudio::IAudioPlayer *inAudioPlayer,
+ Q3DStudio::IRuntimeFactory &inFactory) override
+ {
+ {
+ SStackPerfTimer __loadTimer(m_CoreFactory->GetPerfTimer(),
+ "Application: Initialize Graphics");
+
+ {
+ SStackPerfTimer __timer(m_CoreFactory->GetPerfTimer(), "Application: EndLoad");
+ EndLoad();
+ }
+ m_InputEnginePtr = &inInputEngine;
+ m_RuntimeFactory = inFactory;
+
+ {
+ SStackPerfTimer __timer(m_CoreFactory->GetPerfTimer(),
+ "Application: Load Context Graphics Initialized");
+ if (m_AppLoadContext)
+ m_createSuccessful = m_AppLoadContext->OnGraphicsInitialized(inFactory);
+ // Guarantees the end of the multithreaded access to the various components
+ m_AppLoadContext = NULL;
+ if (!m_createSuccessful)
+ return *this;
+ }
+
+ {
+ SStackPerfTimer __loadTimer(m_CoreFactory->GetPerfTimer(),
+ "Application: End Font Preload");
+ if (m_CoreFactory->GetRenderContextCore().GetTextRendererCore())
+ m_CoreFactory->GetRenderContextCore()
+ .GetTextRendererCore()
+ ->EndPreloadFonts();
+ }
+
+ RunAllRunnables();
+ // Moving set application to the end ensures that the application load context is not
+ // accessing
+ // the lua state in another thread while we are calling set application. This
+ // apparently may cause
+ // the call to set application to fail miserably.
+ m_RuntimeFactory->SetApplication(this);
+ m_RuntimeFactory->GetStringTable().DisableMultithreadedAccess();
+
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &theAsset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *thePresentation = theAsset.m_Presentation;
+ if (thePresentation) {
+ SStackPerfTimer __loadTimer(m_CoreFactory->GetPerfTimer(),
+ "Application: SetActivityZone");
+ thePresentation->SetActivityZone(
+ &m_ActivityZoneManager->CreateActivityZone(*thePresentation));
+ thePresentation->SetActive(theAsset.m_Active);
+ }
+ }
+ }
+
+ inInputEngine.SetApplication(this);
+ }
+ SApplicationSettings finalSettings(/*m_CommandLineSettings, */m_UIAFileSettings);
+ if (finalSettings.m_LayerCacheEnabled.hasValue()) {
+ inFactory.GetQt3DSRenderContext().GetRenderer().EnableLayerCaching(
+ *finalSettings.m_LayerCacheEnabled);
+ }
+
+ m_CoreFactory->GetPerfTimer().OutputTimerData();
+
+ m_AudioPlayer.SetPlayer(inAudioPlayer);
+
+ auto &rc = m_RuntimeFactory->GetQt3DSRenderContext();
+ m_uploadRenderTask = QT3DS_NEW(m_CoreFactory->GetFoundation().getAllocator(),
+ STextureUploadRenderTask(rc.GetImageBatchLoader(),
+ rc.GetBufferManager(),
+ rc.GetRenderContext().GetRenderContextType(),
+ GetPrimaryPresentation()->GetScene()->preferKtx()));
+ m_uploadRenderTask->add(m_createSet, true);
+ m_RuntimeFactory->GetQt3DSRenderContext().GetRenderList()
+ .AddRenderTask(*m_uploadRenderTask);
+ m_createSet.clear();
+ return *this;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Getters/Setters
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ CRegisteredString RegisterStr(const char8_t *inStr)
+ {
+ return m_CoreFactory->GetStringTable().RegisterStr(inStr);
+ }
+
+ // The directory that contains the executable and the root resource path
+ CRegisteredString GetApplicationDirectory() const override
+ {
+ return const_cast<SApp &>(*this).m_CoreFactory->GetStringTable().RegisterStr(
+ m_ApplicationDir.c_str());
+ }
+ // Directory that contained the XIF file.
+ CRegisteredString GetProjectDirectory() const override
+ {
+ QT3DS_ASSERT(m_ProjectDir.size());
+ return const_cast<SApp &>(*this).m_CoreFactory->GetStringTable().RegisterStr(
+ m_ProjectDir.c_str());
+ }
+
+ CRegisteredString GetDllDir() const override
+ {
+ if (m_DLLDirectory.size()) {
+ return const_cast<SApp &>(*this).m_CoreFactory->GetStringTable().RegisterStr(
+ m_DLLDirectory.c_str());
+ }
+ return CRegisteredString();
+ }
+
+ void SetDllDir(const char *inDllDir) override
+ {
+ m_DLLDirectory.assign(nonNull(inDllDir));
+ m_CoreFactory->SetDllDir(inDllDir);
+ }
+
+ Q3DStudio::IRuntimeFactory &GetRuntimeFactory() const override { return *m_RuntimeFactory.mPtr; }
+ Q3DStudio::IRuntimeFactoryCore &GetRuntimeFactoryCore() override { return *m_CoreFactory; }
+
+ Q3DStudio::CPresentation *GetPrimaryPresentation() override
+ {
+ return GetPresentationById(m_PresentationId.c_str());
+ }
+
+ Q3DStudio::CPresentation *GetPresentationById(const char8_t *inId) override
+ {
+ if (!isTrivial(inId)) {
+ TIdAssetMap::iterator iter
+ = m_AssetMap.find(m_CoreFactory->GetStringTable().RegisterStr(inId));
+ if (iter != m_AssetMap.end()
+ && iter->second->getType() == AssetValueTypes::Presentation) {
+ return iter->second->getData<SPresentationAsset>().m_Presentation;
+ }
+ }
+ return NULL;
+ }
+
+ // Returns a list of all presentations in the application
+ // The primary presentation is returned at index 0
+ QList<Q3DStudio::CPresentation *> GetPresentationList() override
+ {
+ QList<Q3DStudio::CPresentation *> list;
+ for (TIdAssetMap::iterator iter = m_AssetMap.begin(); iter != m_AssetMap.end(); ++iter) {
+ if (iter->second->getType() == AssetValueTypes::Presentation) {
+ Q3DStudio::CPresentation *presentation
+ = iter->second->getData<SPresentationAsset>().m_Presentation;
+ if (presentation) {
+ if (iter->first == m_PresentationId)
+ list.prepend(presentation);
+ else
+ list.append(presentation);
+ }
+ }
+ }
+ return list;
+ }
+
+ template <typename TAssetType>
+ void RegisterAsset(const TAssetType &inAsset)
+ {
+ NVScopedRefCounted<SRefCountedAssetValue> theValue(
+ QT3DS_NEW(m_CoreFactory->GetFoundation().getAllocator(),
+ SRefCountedAssetValue(m_CoreFactory->GetFoundation(), inAsset)));
+ if (inAsset.m_Id.IsValid())
+ m_AssetMap.insert(eastl::make_pair(inAsset.m_Id, theValue));
+
+ m_OrderedAssets.push_back(eastl::make_pair(inAsset.m_Id, theValue));
+
+ if (m_visitor) {
+ m_visitor->visit(inAsset.Type(), inAsset.m_Id.c_str(), inAsset.m_Src.c_str(),
+ inAsset.m_Args.c_str());
+ }
+ }
+
+ THashValue HashString(const char *inStr) override
+ {
+ if (inStr == NULL)
+ inStr = "";
+ THashValue retval = CHash::HashString(inStr);
+ eastl::pair<THashStrMap::iterator, bool> insertResult
+ = m_HashStrMap.insert(eastl::make_pair(retval, CRegisteredString()));
+ if (insertResult.second)
+ insertResult.first->second = m_CoreFactory->GetStringTable().RegisterStr(inStr);
+ return retval;
+ }
+
+ const char *ReverseHash(THashValue theValue) override
+ {
+ THashStrMap::iterator find = m_HashStrMap.find(theValue);
+ if (find != m_HashStrMap.end())
+ return find->second.c_str();
+ return "";
+ }
+
+ void SetFrameCount(Q3DStudio::INT32 inFrameCount) override { m_FrameCount = inFrameCount; }
+
+ Q3DStudio::INT32 GetFrameCount() override { return m_FrameCount; }
+
+ void SetTimeMilliSecs(Q3DStudio::INT64 inMilliSecs) override { m_ManualTime = inMilliSecs; }
+
+ Q3DStudio::INT64 GetTimeMilliSecs() override
+ {
+ return m_ManualTime == 0 ? m_Timer.GetTimeMilliSecs() : m_ManualTime;
+ }
+
+ void ResetTime() override
+ {
+ m_Timer.Reset();
+ m_ManualTime = 0;
+ }
+
+ Q3DStudio::CInputEngine &GetInputEngine() override
+ {
+ QT3DS_ASSERT(m_InputEnginePtr);
+ return *m_InputEnginePtr;
+ }
+
+ Q3DStudio::IRuntimeMetaData &GetMetaData() override
+ {
+ if (!m_MetaData) {
+ m_MetaData = &IRuntimeMetaData::Create(m_CoreFactory->GetInputStreamFactory());
+ if (!m_MetaData) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "IRuntimeMetaData::Create: Failed to create meta data";
+ }
+ }
+ return *m_MetaData;
+ }
+
+ IActivityZoneManager &GetActivityZoneManager() override { return *m_ActivityZoneManager; }
+
+ IElementAllocator &GetElementAllocator() override { return *m_ElementAllocator; }
+
+ Q3DStudio::UINT32 GetHandleForElement(Q3DStudio::TElement *inElement) override
+ {
+ return inElement->GetHandle();
+ }
+
+ Q3DStudio::TElement *GetElementByHandle(Q3DStudio::UINT32 inHandle) override
+ {
+ return GetElementAllocator().FindElementByHandle(inHandle);
+ }
+};
+
+struct SXMLLoader : public IAppLoadContext
+{
+ SApp &m_App;
+ eastl::string m_ScaleMode;
+ QT3DSI32 mRefCount;
+
+ SXMLLoader(SApp &inApp, const char8_t *sc)
+ : m_App(inApp)
+ , m_ScaleMode(nonNull(sc))
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_App.m_CoreFactory->GetFoundation().getAllocator())
+
+ void EndLoad() override {}
+
+ bool HasCompletedLoading() override { return true; }
+
+ bool OnGraphicsInitialized(IRuntimeFactory &inFactory) override
+ {
+ eastl::vector<SElementAttributeReference> theUIPReferences;
+ eastl::string tempString;
+ for (QT3DSU32 idx = 0, end = m_App.m_OrderedAssets.size(); idx < end; ++idx) {
+ SAssetValue &theAsset = *m_App.m_OrderedAssets[idx].second;
+ eastl::string thePathStr;
+
+ CFileTools::CombineBaseAndRelative(m_App.GetProjectDirectory().c_str(),
+ theAsset.GetSource(), thePathStr);
+ switch (theAsset.getType()) {
+ case AssetValueTypes::Presentation: {
+ QDir::addSearchPath(QStringLiteral("qt3dstudio"),
+ QFileInfo(QString(thePathStr.c_str()))
+ .absoluteDir().absolutePath());
+ SPresentationAsset &thePresentationAsset
+ = *theAsset.getDataPtr<SPresentationAsset>();
+ theUIPReferences.clear();
+
+ if (!m_App.LoadUIP(thePresentationAsset,
+ toConstDataRef(theUIPReferences.data(),
+ (QT3DSU32)theUIPReferences.size()))) {
+ qCCritical(INVALID_OPERATION, "Unable to load presentation %s",
+ thePathStr.c_str());
+ }
+ } break;
+ case AssetValueTypes::Behavior: {
+ SBehaviorAsset &theBehaviorAsset = *theAsset.getDataPtr<SBehaviorAsset>();
+ Q3DStudio::INT32 scriptId
+ = m_App.m_CoreFactory->GetScriptEngineQml().InitializeApplicationBehavior(
+ theBehaviorAsset.m_Src);
+ if (scriptId == 0) {
+ qCCritical(INVALID_OPERATION, "Unable to load application behavior %s",
+ theBehaviorAsset.m_Src.c_str());
+ } else {
+ theBehaviorAsset.m_Handle = scriptId;
+ m_App.m_Behaviors.push_back(eastl::make_pair(theBehaviorAsset, false));
+ }
+ } break;
+ case AssetValueTypes::RenderPlugin: {
+ SRenderPluginAsset &thePluginAsset = *theAsset.getDataPtr<SRenderPluginAsset>();
+
+ inFactory.GetSceneManager().LoadRenderPlugin(
+ thePluginAsset.m_Id, thePathStr.c_str(), thePluginAsset.m_Args);
+ } break;
+
+ case AssetValueTypes::QmlPresentation: {
+ SQmlPresentationAsset &asset = *theAsset.getDataPtr<SQmlPresentationAsset>();
+ inFactory.GetSceneManager().LoadQmlStreamerPlugin(asset.m_Id);
+ } break;
+ // SCXML, NoAssetValue do not need processing here
+ default:
+ break;
+ }
+ }
+ if (m_ScaleMode.empty() == false) {
+ const char8_t *initialScaleMode(m_ScaleMode.c_str());
+ // Force loading to finish here, just like used to happen.
+ if (AreEqual(initialScaleMode, "center")) {
+ inFactory.GetQt3DSRenderContext().SetScaleMode(qt3ds::render::ScaleModes::ExactSize);
+ } else if (AreEqual(initialScaleMode, "fit")) {
+ inFactory.GetQt3DSRenderContext().SetScaleMode(qt3ds::render::ScaleModes::ScaleToFit);
+ } else if (AreEqual(initialScaleMode, "fill")) {
+ inFactory.GetQt3DSRenderContext().SetScaleMode(qt3ds::render::ScaleModes::ScaleToFill);
+ } else {
+ qCCritical(INVALID_PARAMETER, "Unrecognized scale mode attribute value: ",
+ initialScaleMode);
+ }
+ }
+ return true;
+ }
+
+ virtual void OnFirstRender() {}
+};
+
+IAppLoadContext &IAppLoadContext::CreateXMLLoadContext(
+ SApp &inApp, const char8_t *inScaleMode)
+{
+ return *QT3DS_NEW(inApp.m_CoreFactory->GetFoundation().getAllocator(),
+ SXMLLoader)(inApp, inScaleMode);
+}
+
+CAppStr::CAppStr(NVAllocatorCallback &alloc, const char8_t *inStr)
+ : TBase(inStr, ForwardingAllocator(alloc, "CAppStr"))
+{
+}
+
+CAppStr::CAppStr(const CAppStr &inOther)
+ : TBase(inOther)
+{
+}
+
+CAppStr::CAppStr()
+ : TBase()
+{
+}
+
+CAppStr &CAppStr::operator=(const CAppStr &inOther)
+{
+ TBase::operator=(inOther);
+ return *this;
+}
+
+IApplication &IApplication::CreateApplicationCore(Q3DStudio::IRuntimeFactoryCore &inFactory,
+ const char8_t *inApplicationDirectory)
+{
+ return *QT3DS_NEW(inFactory.GetFoundation().getAllocator(), SApp)(inFactory,
+ inApplicationDirectory);
+}
+
+// Checks if the event is one that can cause picking
+bool IApplication::isPickingEvent(TEventCommandHash event)
+{
+ return (event == ON_MOUSEDOWN
+ || event == ON_MOUSEUP
+ || event == ON_MIDDLEMOUSEDOWN
+ || event == ON_MIDDLEMOUSEUP
+ || event == ON_RIGHTMOUSEDOWN
+ || event == ON_RIGHTMOUSEUP
+ || event == ON_MOUSECLICK
+ || event == ON_MIDDLEMOUSECLICK
+ || event == ON_RIGHTMOUSECLICK
+ || event == ON_MOUSEOVER
+ || event == ON_MOUSEOUT
+ || event == ON_GROUPEDMOUSEOVER
+ || event == ON_GROUPEDMOUSEOUT);
+}
+
+QDebug operator<<(QDebug debug, const DataInOutAttribute &value)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "DataInOutAttribute(";
+ debug.nospace() << "elementPath:" << value.elementPath;
+ debug.nospace() << ", attributeNames: {";
+ for (auto name : value.attributeName)
+ debug << QString::fromUtf8(name);
+
+ debug.nospace() << "}, propertyType:" << value.propertyType;
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const DataInOutType &value)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "DataInOutType::";
+ switch (value) {
+ case DataInOutType::DataInOutTypeInvalid:
+ debug.nospace() << "DataInOutTypeInvalid";
+ break;
+ case DataInOutType::DataInOutTypeRangedNumber:
+ debug.nospace() << "DataInOutTypeRangedNumber";
+ break;
+ case DataInOutType::DataInOutTypeString:
+ debug.nospace() << "DataInOutTypeString";
+ break;
+ case DataInOutType::DataInOutTypeFloat:
+ debug.nospace() << "DataInOutTypeFloat";
+ break;
+ case DataInOutType::DataInOutTypeEvaluator:
+ debug.nospace() << "DataInOutTypeEvaluator";
+ break;
+ case DataInOutType::DataInOutTypeBoolean:
+ debug.nospace() << "DataInOutTypeBoolean";
+ break;
+ case DataInOutType::DataInOutTypeVector4:
+ debug.nospace() << "DataInOutTypeVector4";
+ break;
+ case DataInOutType::DataInOutTypeVector3:
+ debug.nospace() << "DataInOutTypeVector3";
+ break;
+ case DataInOutType::DataInOutTypeVector2:
+ debug.nospace() << "DataInOutTypeVector2";
+ break;
+ case DataInOutType::DataInOutTypeVariant:
+ debug.nospace() << "DataInOutTypeVariant";
+ break;
+ default:
+ debug.nospace() << "UNKNOWN";
+ }
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const DataInputValueRole &value)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "DataInputValueRole::";
+ switch (value) {
+ case DataInputValueRole::Value:
+ debug.nospace() << "Value";
+ break;
+ case DataInputValueRole::Min:
+ debug.nospace() << "Min";
+ break;
+ case DataInputValueRole::Max:
+ debug.nospace() << "Max";
+ break;
+ default:
+ debug.nospace() << "UNKNOWN";
+ }
+ return debug;
+}
+
+// TODO: optionally print out also metadata, but note that it is not
+// relevant for any runtime or editor -side code debugging (strictly user-side
+// information).
+QDebug operator<<(QDebug debug, const DataInputDef &value)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "DataInputDef(";
+ debug.nospace() << "type:" << value.type;
+ debug.nospace() << ", controlledAttributes: {";
+ for (auto attr : value.controlledAttributes)
+ debug << attr;
+
+ debug.nospace() << "}, min:" << value.min;
+ debug.nospace() << ", min:" << value.min << ", max:" << value.max;
+ debug.nospace() << ", evaluator:" << value.evaluator;
+ debug.nospace() << ", value:" << value.value;
+ debug.nospace() << ", dependents:{";
+ for (auto dep : value.dependents)
+ debug << dep;
+
+ debug.nospace() << "})";
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const DataOutputDef &value)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "DataOutputDef(";
+ debug.nospace() << "name:" << value.name << ", type:" << value.type;
+ debug.nospace() << ", observedHandle:" << value.observedHandle;
+ debug.nospace() << ", min:" << value.min << ", max:" << value.max;
+ debug.nospace() << ", timelineComponent:" << value.timelineComponent << ")";
+ return debug;
+}
diff --git a/src/runtime/Qt3DSApplication.h b/src/runtime/Qt3DSApplication.h
new file mode 100644
index 0000000..f38b182
--- /dev/null
+++ b/src/runtime/Qt3DSApplication.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_APPLICATION_H
+#define QT3DS_APPLICATION_H
+#include "foundation/Qt3DSRefCounted.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSAllocator.h"
+#include "foundation/Utils.h"
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSMetadata.h"
+#include "QtQml/qjsengine.h"
+
+namespace Q3DStudio {
+class IRuntimeFactory;
+class IRuntimeFactoryCore;
+class CRuntime;
+class CPresentation;
+class CInputEngine;
+class IAudioPlayer;
+}
+
+namespace qt3ds {
+namespace state {
+namespace debugger {
+class IDebugger;
+class ISceneGraphRuntimeDebugger;
+}
+}
+}
+
+namespace qt3ds {
+class Qt3DSAssetVisitor;
+namespace runtime {
+using namespace qt3ds::foundation;
+using namespace qt3ds;
+
+class IElementAllocator;
+class IActivityZoneManager;
+
+class CAppStr : public eastl::basic_string<char8_t, ForwardingAllocator>
+{
+ typedef eastl::basic_string<char8_t, ForwardingAllocator> TBase;
+
+public:
+ CAppStr(NVAllocatorCallback &alloc, const char8_t *inStr = NULL);
+ CAppStr(const CAppStr &inOther);
+ CAppStr();
+ CAppStr &operator=(const CAppStr &inOther);
+};
+
+class IApplication;
+
+class IAppRunnable : public NVRefCounted
+{
+public:
+ virtual void Run() = 0;
+};
+
+struct DataInOutAttribute
+{
+ QByteArray elementPath;
+ QVector<QByteArray> attributeName;
+ Q3DStudio::EAttributeType propertyType = Q3DStudio::ATTRIBUTETYPE_NONE;
+};
+
+enum DataInOutType {
+ DataInOutTypeInvalid = 0,
+ DataInOutTypeRangedNumber,
+ DataInOutTypeString,
+ DataInOutTypeFloat,
+ DataInOutTypeEvaluator,
+ DataInOutTypeBoolean,
+ DataInOutTypeVector4,
+ DataInOutTypeVector3,
+ DataInOutTypeVector2,
+ DataInOutTypeVariant
+};
+
+// Duplicated from Q3DSDataInput class on viewer side
+enum class DataInputValueRole {
+ Value = 0,
+ Min = 1,
+ Max = 2
+};
+
+struct DataInputDef
+{
+ QVector<DataInOutAttribute> controlledAttributes;
+ DataInOutType type = DataInOutTypeInvalid;
+ float min = 0.0f;
+ float max = 0.0f;
+ QString evaluator;
+ QJSValue evalFunc; // keep both evaluator string and JS function
+ // to avoid having to evaluate string several times
+ QVariant value; // most recently set value
+ // evaluator datainputs that need to re-evaluate when this datainput changes value
+ QVector<QString> dependents;
+ QHash<QString, QString> metadata;
+};
+
+struct DataOutputDef
+{
+ DataInOutAttribute observedAttribute;
+ DataInOutType type = DataInOutTypeInvalid;
+ QVariant value; // most recently notified value
+ QString name;
+ QT3DSU32 observedHandle;
+ float min = 0.0f;
+ float max = 0.0f;
+ Q3DStudio::TComponent *timelineComponent; // Valid only for @timeline
+};
+
+typedef QMap<QString, DataInputDef> DataInputMap;
+typedef QMap<QString, DataOutputDef> DataOutputMap;
+
+class QT3DS_AUTOTEST_EXPORT IApplication : public NVRefCounted
+{
+public:
+ virtual Q3DStudio::IRuntimeFactory &GetRuntimeFactory() const = 0;
+
+ virtual void SetFrameCount(Q3DStudio::INT32 inFrameCount) = 0;
+ virtual Q3DStudio::INT32 GetFrameCount() = 0;
+
+ // Allow overriding the global wall time. This way animations can be
+ // advanced at an arbitrary rate when rendering offscreen. Set to 0 to
+ // disable (the default).
+ virtual void SetTimeMilliSecs(Q3DStudio::INT64 inMilliSecs) = 0;
+
+ virtual Q3DStudio::INT64 GetTimeMilliSecs() = 0;
+ virtual void ResetTime() = 0;
+
+ // Setup during UpdateAndRender. Returns 0 for first frame.
+ virtual double GetMillisecondsSinceLastFrame() = 0;
+
+ virtual Q3DStudio::CInputEngine &GetInputEngine() = 0;
+
+ virtual Q3DStudio::CPresentation *GetPrimaryPresentation() = 0;
+
+ virtual Q3DStudio::CPresentation *GetPresentationById(const char8_t *inId) = 0;
+
+ virtual QList<Q3DStudio::CPresentation *> GetPresentationList() = 0;
+
+ // Update all the presentations and render them. Called exactly once per frame.
+ virtual void UpdateAndRender() = 0;
+
+ virtual bool IsApplicationDirty() = 0;
+
+ virtual void MarkApplicationDirty() = 0;
+
+ virtual Q3DStudio::IAudioPlayer &GetAudioPlayer() = 0;
+
+ virtual bool createSuccessful() = 0;
+
+ virtual DataInputMap &dataInputMap() = 0;
+ virtual DataOutputMap &dataOutputMap() = 0;
+
+ virtual QList<QString> dataInputs() const = 0;
+ virtual QList<QString> dataOutputs() const = 0;
+
+ virtual float dataInputMax(const QString &name) const = 0;
+ virtual float dataInputMin(const QString &name) const = 0;
+ virtual QHash<QString, QString> dataInputMetadata(const QString &name) const = 0;
+
+ virtual void setPresentationId(const QString &id) = 0;
+
+ virtual void preloadSlide(const QString &slide) = 0;
+ virtual void unloadSlide(const QString &slide) = 0;
+ virtual void setDelayedLoading(bool enable) = 0;
+
+ // threadsafe call.
+ virtual void QueueForMainThread(IAppRunnable &inRunnable) = 0;
+
+ // The directory that contains the executable and the root resource path
+ virtual CRegisteredString GetApplicationDirectory() const = 0;
+ // Directory that contained the UIA file.
+ virtual CRegisteredString GetProjectDirectory() const = 0;
+ // Directory where we will copy shared object files to before we load them.
+ // This is specifically for android and the case where the place where the project exists
+ // is not always the place where we can load the project.
+ virtual CRegisteredString GetDllDir() const = 0;
+ virtual void SetDllDir(const char *inDllDir) = 0;
+ virtual Q3DStudio::THashValue HashString(const char *inStr) = 0;
+ virtual const char *ReverseHash(Q3DStudio::THashValue theValue) = 0;
+
+ virtual Q3DStudio::IRuntimeMetaData &GetMetaData() = 0;
+ // Element handles are unique across all presentations.
+ virtual Q3DStudio::TElement *GetElementByHandle(Q3DStudio::UINT32 inHandle) = 0;
+ // Passing in NULL gets you zero as the return handle.
+ virtual Q3DStudio::UINT32 GetHandleForElement(Q3DStudio::TElement *inElement) = 0;
+
+ virtual Q3DStudio::IRuntimeFactoryCore &GetRuntimeFactoryCore() = 0;
+
+ virtual IActivityZoneManager &GetActivityZoneManager() = 0;
+ virtual IElementAllocator &GetElementAllocator() = 0;
+
+ // nonblocking call to begin loading, loads uia file alone and returns.
+ virtual bool BeginLoad(const QString &sourcePath, const QStringList &variantList) = 0;
+ // blocking call to end all loading threads and such/wait till finished
+ virtual void EndLoad() = 0;
+ // Will EndLoad cause nontrivial blocking.
+ // Runs any queued runnables.
+ virtual bool HasCompletedLoading() = 0;
+
+ virtual void setAssetVisitor(qt3ds::Qt3DSAssetVisitor *) = 0;
+
+ virtual void ComponentSlideEntered(Q3DStudio::CPresentation *presentation,
+ Q3DStudio::TElement *component,
+ const QString &elementPath, int slideIndex,
+ const QString &slideName) = 0;
+ virtual void ComponentSlideExited(Q3DStudio::CPresentation *presentation,
+ Q3DStudio::TElement *component,
+ const QString &elementPath, int slideIndex,
+ const QString &slideName) = 0;
+
+ // will force loading to end if endLoad hasn't been called yet. Will fire off loading
+ // of resources that need to be uploaded to opengl. Maintains reference to runtime factory
+ virtual IApplication &CreateApplication(Q3DStudio::CInputEngine &inInputEngine,
+ Q3DStudio::IAudioPlayer *inAudioPlayer,
+ Q3DStudio::IRuntimeFactory &inFactory) = 0;
+
+ // maintains reference to runtime factory core. AppDir is where the executable is located;
+ // the system will expect res directory
+ // next to executable.
+ static IApplication &CreateApplicationCore(Q3DStudio::IRuntimeFactoryCore &inFactory,
+ const char8_t *inApplicationDirectory);
+ static bool isPickingEvent(Q3DStudio::TEventCommandHash event);
+};
+
+bool isImagePath(const QString &path);
+}
+}
+
+#endif
diff --git a/src/runtime/Qt3DSApplicationValues.h b/src/runtime/Qt3DSApplicationValues.h
new file mode 100644
index 0000000..80e8f73
--- /dev/null
+++ b/src/runtime/Qt3DSApplicationValues.h
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#pragma once
+#include "Qt3DSApplication.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "foundation/StringTable.h"
+
+namespace qt3ds {
+
+class Qt3DSAssetVisitor
+{
+public:
+ virtual void visit(const char *path) = 0;
+ virtual void visit(const char *type, const char *id, const char *src, const char *args) = 0;
+};
+
+namespace runtime {
+
+ using qt3ds::foundation::CRegisteredString;
+
+ struct AssetValueTypes
+ {
+ enum Enum {
+ NoAssetValue = 0,
+ Presentation,
+ SCXML,
+ RenderPlugin,
+ Behavior,
+ QmlPresentation,
+ };
+ };
+ struct SAssetBase
+ {
+ CRegisteredString m_Id;
+ CRegisteredString m_Src;
+ CRegisteredString m_Args;
+ SAssetBase(CRegisteredString inId = CRegisteredString(),
+ CRegisteredString inSrc = CRegisteredString(),
+ CRegisteredString inArgs = CRegisteredString())
+ : m_Id(inId)
+ , m_Src(inSrc)
+ , m_Args(inArgs)
+ {
+ }
+ bool operator==(const SAssetBase &inOther) const
+ {
+ return m_Id == inOther.m_Id && m_Src == inOther.m_Src
+ && m_Args == inOther.m_Args;
+ }
+ template <typename TRemapper>
+ void Remap(TRemapper &item)
+ {
+ item.Remap(m_Id);
+ item.Remap(m_Src);
+ item.Remap(m_Args);
+ }
+ virtual const char *Type() const
+ {
+ return nullptr;
+ }
+ };
+
+ struct SPresentationAsset : public SAssetBase
+ {
+ Q3DStudio::CPresentation *m_Presentation;
+ bool m_Active;
+
+ SPresentationAsset(CRegisteredString inId = CRegisteredString(),
+ CRegisteredString inRelPath = CRegisteredString(),
+ Q3DStudio::CPresentation *inPresentation = NULL)
+ : SAssetBase(inId, inRelPath, CRegisteredString())
+ , m_Presentation(inPresentation)
+ , m_Active(true)
+ {
+ }
+
+ bool operator==(const SPresentationAsset &inOther) const
+ {
+ return SAssetBase::operator==(inOther) && m_Presentation == inOther.m_Presentation
+ && m_Active == inOther.m_Active;
+ }
+
+ template <typename TRemapper>
+ void Remap(TRemapper &item)
+ {
+ m_Presentation = NULL;
+ SAssetBase::Remap(item);
+ }
+ const char *Type() const override
+ {
+ return "presentation";
+ }
+ };
+
+ struct SSCXMLAsset : public SAssetBase
+ {
+ CRegisteredString m_Datamodel;
+ SSCXMLAsset(CRegisteredString inId = CRegisteredString(),
+ CRegisteredString inRelPath = CRegisteredString(),
+ CRegisteredString inDatamodel = CRegisteredString())
+ : SAssetBase(inId, inRelPath, CRegisteredString())
+ , m_Datamodel(inDatamodel)
+ {
+ }
+ bool operator==(const SSCXMLAsset &inOther) const
+ {
+ return SAssetBase::operator==(inOther) && m_Datamodel == inOther.m_Datamodel;
+ }
+ template <typename TRemapper>
+ void Remap(TRemapper &item)
+ {
+ SAssetBase::Remap(item);
+ item.Remap(m_Datamodel);
+ }
+ const char *Type() const override
+ {
+ return "scxml";
+ }
+ };
+
+ struct SRenderPluginAsset : public SAssetBase
+ {
+ SRenderPluginAsset(CRegisteredString inId = CRegisteredString(),
+ CRegisteredString inRelPath = CRegisteredString(),
+ CRegisteredString inArgs = CRegisteredString())
+ : SAssetBase(inId, inRelPath, inArgs)
+ {
+ }
+ const char *Type() const override
+ {
+ return "renderplugin";
+ }
+ };
+
+ struct SQmlPresentationAsset : public SAssetBase
+ {
+ SQmlPresentationAsset(CRegisteredString inId = CRegisteredString(),
+ CRegisteredString inRelPath = CRegisteredString(),
+ CRegisteredString inArgs = CRegisteredString())
+ : SAssetBase(inId, inRelPath, inArgs)
+ {
+ }
+ const char *Type() const override
+ {
+ return "presentation-qml";
+ }
+ };
+
+ struct SBehaviorAsset : public SAssetBase
+ {
+ Q3DStudio::INT32 m_Handle;
+ SBehaviorAsset(CRegisteredString inId = CRegisteredString(),
+ CRegisteredString inRelPath = CRegisteredString(),
+ Q3DStudio::INT32 inHandle = 0)
+ : SAssetBase(inId, inRelPath, CRegisteredString())
+ , m_Handle(inHandle)
+ {
+ }
+ bool operator==(const SBehaviorAsset &inOther) const
+ {
+ return SAssetBase::operator==(inOther) && m_Handle == inOther.m_Handle;
+ }
+ template <typename TRemapper>
+ void Remap(TRemapper &item)
+ {
+ m_Handle = -1;
+ SAssetBase::Remap(item);
+ }
+ const char *Type() const override
+ {
+ return "behaviour";
+ }
+ };
+}
+}
+
+namespace qt3ds {
+namespace foundation {
+ template <>
+ struct DestructTraits<qt3ds::runtime::SPresentationAsset>
+ {
+ void destruct(qt3ds::runtime::SPresentationAsset &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::runtime::SSCXMLAsset>
+ {
+ void destruct(qt3ds::runtime::SSCXMLAsset &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::runtime::SRenderPluginAsset>
+ {
+ void destruct(qt3ds::runtime::SRenderPluginAsset &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::runtime::SBehaviorAsset>
+ {
+ void destruct(qt3ds::runtime::SBehaviorAsset &) {}
+ };
+}
+}
+
+namespace qt3ds {
+namespace runtime {
+
+ // Force compile error if unsupported datatype requested
+ template <typename TDataType>
+ struct SAssetValueTypeMap
+ {
+ };
+
+ template <>
+ struct SAssetValueTypeMap<SPresentationAsset>
+ {
+ static AssetValueTypes::Enum GetType() { return AssetValueTypes::Presentation; }
+ };
+ template <>
+ struct SAssetValueTypeMap<SSCXMLAsset>
+ {
+ static AssetValueTypes::Enum GetType() { return AssetValueTypes::SCXML; }
+ };
+ template <>
+ struct SAssetValueTypeMap<SRenderPluginAsset>
+ {
+ static AssetValueTypes::Enum GetType() { return AssetValueTypes::RenderPlugin; }
+ };
+ template <>
+ struct SAssetValueTypeMap<SQmlPresentationAsset>
+ {
+ static AssetValueTypes::Enum GetType() { return AssetValueTypes::QmlPresentation; }
+ };
+ template <>
+ struct SAssetValueTypeMap<SBehaviorAsset>
+ {
+ static AssetValueTypes::Enum GetType() { return AssetValueTypes::Behavior; }
+ };
+
+ struct SAssetValueUnionTraits
+ {
+ typedef AssetValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(SPresentationAsset),
+ };
+
+ static TIdType getNoDataId() { return AssetValueTypes::NoAssetValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SAssetValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case AssetValueTypes::Presentation:
+ return inVisitor(*NVUnionCast<SPresentationAsset *>(inData));
+ case AssetValueTypes::SCXML:
+ return inVisitor(*NVUnionCast<SSCXMLAsset *>(inData));
+ case AssetValueTypes::RenderPlugin:
+ return inVisitor(*NVUnionCast<SRenderPluginAsset *>(inData));
+ case AssetValueTypes::Behavior:
+ return inVisitor(*NVUnionCast<SBehaviorAsset *>(inData));
+ case AssetValueTypes::QmlPresentation:
+ return inVisitor(*NVUnionCast<SQmlPresentationAsset*>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case AssetValueTypes::NoAssetValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case AssetValueTypes::Presentation:
+ return inVisitor(*NVUnionCast<const SPresentationAsset *>(inData));
+ case AssetValueTypes::SCXML:
+ return inVisitor(*NVUnionCast<const SSCXMLAsset *>(inData));
+ case AssetValueTypes::RenderPlugin:
+ return inVisitor(*NVUnionCast<const SRenderPluginAsset *>(inData));
+ case AssetValueTypes::Behavior:
+ return inVisitor(*NVUnionCast<const SBehaviorAsset *>(inData));
+ case AssetValueTypes::QmlPresentation:
+ return inVisitor(*NVUnionCast<const SQmlPresentationAsset *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case AssetValueTypes::NoAssetValue:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SAssetValueUnionTraits,
+ SAssetValueUnionTraits::TBufferSize>,
+ SAssetValueUnionTraits::TBufferSize>
+ TAssetValueUnionType;
+
+ struct SAssetValue : public TAssetValueUnionType
+ {
+ SAssetValue() {}
+
+ SAssetValue(const SAssetValue &inOther)
+ : TAssetValueUnionType(static_cast<const TAssetValueUnionType &>(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SAssetValue(const TDataType &inDt)
+ : TAssetValueUnionType(inDt)
+ {
+ }
+
+ SAssetValue &operator=(const SAssetValue &inOther)
+ {
+ TAssetValueUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SAssetValue &inOther) const
+ {
+ return TAssetValueUnionType::operator==(inOther);
+ }
+ bool operator!=(const SAssetValue &inOther) const
+ {
+ return TAssetValueUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == AssetValueTypes::NoAssetValue; }
+
+ CRegisteredString GetSource()
+ {
+ if (empty())
+ return CRegisteredString();
+ return reinterpret_cast<SAssetBase *>(m_Data)->m_Src;
+ }
+ };
+}
+}
diff --git a/src/runtime/Qt3DSAttributeHashes.cpp b/src/runtime/Qt3DSAttributeHashes.cpp
new file mode 100644
index 0000000..1daddc1
--- /dev/null
+++ b/src/runtime/Qt3DSAttributeHashes.cpp
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2019 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSAttributeHashes.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+// !!!!! AUTOGENERATED CODE - DO NOT MODIFY MANUALLY !!!!!
+
+// Run the AttributeHashes project to regenerate this file from Attributehashes.txt list
+
+
+
+/// Function providing reverse hash lookup
+const char *GetAttributeString(const EAttribute inAttribute)
+{
+ switch (inAttribute) {
+ case ATTRIBUTE_NAME: return "name";
+ case ATTRIBUTE_TYPE: return "type";
+ case ATTRIBUTE_OPACITY: return "opacity";
+ case ATTRIBUTE_STARTTIME: return "starttime";
+ case ATTRIBUTE_ENDTIME: return "endtime";
+ case ATTRIBUTE_SOURCEPATH: return "sourcepath";
+ case ATTRIBUTE_IMPORTID: return "importid";
+ case ATTRIBUTE_EYEBALL: return "eyeball";
+ case ATTRIBUTE_POSITION: return "position";
+ case ATTRIBUTE_POSITION_X: return "position.x";
+ case ATTRIBUTE_POSITION_Y: return "position.y";
+ case ATTRIBUTE_POSITION_Z: return "position.z";
+ case ATTRIBUTE_ROTATION: return "rotation";
+ case ATTRIBUTE_ROTATION_X: return "rotation.x";
+ case ATTRIBUTE_ROTATION_Y: return "rotation.y";
+ case ATTRIBUTE_ROTATION_Z: return "rotation.z";
+ case ATTRIBUTE_SCALE: return "scale";
+ case ATTRIBUTE_SCALE_X: return "scale.x";
+ case ATTRIBUTE_SCALE_Y: return "scale.y";
+ case ATTRIBUTE_SCALE_Z: return "scale.z";
+ case ATTRIBUTE_PIVOT: return "pivot";
+ case ATTRIBUTE_PIVOT_X: return "pivot.x";
+ case ATTRIBUTE_PIVOT_Y: return "pivot.y";
+ case ATTRIBUTE_PIVOT_Z: return "pivot.z";
+ case ATTRIBUTE_ROTATIONORDER: return "rotationorder";
+ case ATTRIBUTE_ORIENTATION: return "orientation";
+ case ATTRIBUTE_SHADOWCASTER: return "shadowcaster";
+ case ATTRIBUTE_TESSELLATION: return "tessellation";
+ case ATTRIBUTE_EDGETESS: return "edgetess";
+ case ATTRIBUTE_INNERTESS: return "innertess";
+ case ATTRIBUTE_ORTHOGRAPHIC: return "orthographic";
+ case ATTRIBUTE_CLIPNEAR: return "clipnear";
+ case ATTRIBUTE_CLIPFAR: return "clipfar";
+ case ATTRIBUTE_FOV: return "fov";
+ case ATTRIBUTE_FOVHORIZONTAL: return "fovhorizontal";
+ case ATTRIBUTE_SCALEMODE: return "scalemode";
+ case ATTRIBUTE_SCALEANCHOR: return "scaleanchor";
+ case ATTRIBUTE_BRIGHTNESS: return "brightness";
+ case ATTRIBUTE_LINEARFADE: return "linearfade";
+ case ATTRIBUTE_EXPFADE: return "expfade";
+ case ATTRIBUTE_LIGHTTYPE: return "lighttype";
+ case ATTRIBUTE_SCOPE: return "scope";
+ case ATTRIBUTE_LIGHTDIFFUSE: return "lightdiffuse";
+ case ATTRIBUTE_LIGHTDIFFUSE_R: return "lightdiffuse.r";
+ case ATTRIBUTE_LIGHTDIFFUSE_G: return "lightdiffuse.g";
+ case ATTRIBUTE_LIGHTDIFFUSE_B: return "lightdiffuse.b";
+ case ATTRIBUTE_LIGHTDIFFUSE_A: return "lightdiffuse.a";
+ case ATTRIBUTE_LIGHTAMBIENT: return "lightambient";
+ case ATTRIBUTE_LIGHTAMBIENT_R: return "lightambient.r";
+ case ATTRIBUTE_LIGHTAMBIENT_G: return "lightambient.g";
+ case ATTRIBUTE_LIGHTAMBIENT_B: return "lightambient.b";
+ case ATTRIBUTE_LIGHTAMBIENT_A: return "lightambient.a";
+ case ATTRIBUTE_LIGHTSPECULAR: return "lightspecular";
+ case ATTRIBUTE_LIGHTSPECULAR_R: return "lightspecular.r";
+ case ATTRIBUTE_LIGHTSPECULAR_G: return "lightspecular.g";
+ case ATTRIBUTE_LIGHTSPECULAR_B: return "lightspecular.b";
+ case ATTRIBUTE_LIGHTSPECULAR_A: return "lightspecular.a";
+ case ATTRIBUTE_AREAWIDTH: return "areawidth";
+ case ATTRIBUTE_AREAHEIGHT: return "areaheight";
+ case ATTRIBUTE_CASTSHADOW: return "castshadow";
+ case ATTRIBUTE_SHDWBIAS: return "shdwbias";
+ case ATTRIBUTE_SHDWFACTOR: return "shdwfactor";
+ case ATTRIBUTE_SHDWMAPRES: return "shdwmapres";
+ case ATTRIBUTE_SHDWMAPFAR: return "shdwmapfar";
+ case ATTRIBUTE_SHDWMAPFOV: return "shdwmapfov";
+ case ATTRIBUTE_SHDWFILTER: return "shdwfilter";
+ case ATTRIBUTE_LIGHTMAPINDIRECT: return "lightmapindirect";
+ case ATTRIBUTE_LIGHTMAPRADIOSITY: return "lightmapradiosity";
+ case ATTRIBUTE_LIGHTMAPSHADOW: return "lightmapshadow";
+ case ATTRIBUTE_IBLPROBE: return "iblprobe";
+ case ATTRIBUTE_SHADERLIGHTING: return "shaderlighting";
+ case ATTRIBUTE_EMISSIVEPOWER: return "emissivepower";
+ case ATTRIBUTE_EMISSIVECOLOR: return "emissivecolor";
+ case ATTRIBUTE_EMISSIVECOLOR_R: return "emissivecolor.r";
+ case ATTRIBUTE_EMISSIVECOLOR_G: return "emissivecolor.g";
+ case ATTRIBUTE_EMISSIVECOLOR_B: return "emissivecolor.b";
+ case ATTRIBUTE_EMISSIVECOLOR_A: return "emissivecolor.a";
+ case ATTRIBUTE_DIFFUSE: return "diffuse";
+ case ATTRIBUTE_DIFFUSE_R: return "diffuse.r";
+ case ATTRIBUTE_DIFFUSE_G: return "diffuse.g";
+ case ATTRIBUTE_DIFFUSE_B: return "diffuse.b";
+ case ATTRIBUTE_DIFFUSE_A: return "diffuse.a";
+ case ATTRIBUTE_SPECULARMAP: return "specularmap";
+ case ATTRIBUTE_SPECULARMODEL: return "specularmodel";
+ case ATTRIBUTE_SPECULARTINT: return "speculartint";
+ case ATTRIBUTE_SPECULARTINT_R: return "speculartint.r";
+ case ATTRIBUTE_SPECULARTINT_G: return "speculartint.g";
+ case ATTRIBUTE_SPECULARTINT_B: return "speculartint.b";
+ case ATTRIBUTE_SPECULARTINT_A: return "speculartint.a";
+ case ATTRIBUTE_IOR: return "ior";
+ case ATTRIBUTE_FRESNELPOWER: return "fresnelPower";
+ case ATTRIBUTE_SPECULARAMOUNT: return "specularamount";
+ case ATTRIBUTE_SPECULARROUGHNESS: return "specularroughness";
+ case ATTRIBUTE_ROUGHNESSMAP: return "roughnessmap";
+ case ATTRIBUTE_BLENDMODE: return "blendmode";
+ case ATTRIBUTE_CULLING: return "culling";
+ case ATTRIBUTE_ZBUFFERWRITE: return "zbufferwrite";
+ case ATTRIBUTE_DIFFUSEMAP: return "diffusemap";
+ case ATTRIBUTE_DIFFUSEMAP2: return "diffusemap2";
+ case ATTRIBUTE_DIFFUSEMAP3: return "diffusemap3";
+ case ATTRIBUTE_SPECULARREFLECTION: return "specularreflection";
+ case ATTRIBUTE_OPACITYMAP: return "opacitymap";
+ case ATTRIBUTE_EMISSIVEMAP: return "emissivemap";
+ case ATTRIBUTE_EMISSIVEMAP2: return "emissivemap2";
+ case ATTRIBUTE_BUMPMAP: return "bumpmap";
+ case ATTRIBUTE_BUMPAMOUNT: return "bumpamount";
+ case ATTRIBUTE_NORMALMAP: return "normalmap";
+ case ATTRIBUTE_DISPLACEMENTMAP: return "displacementmap";
+ case ATTRIBUTE_DISPLACEAMOUNT: return "displaceamount";
+ case ATTRIBUTE_TRANSLUCENCYMAP: return "translucencymap";
+ case ATTRIBUTE_TRANSLUCENTFALLOFF: return "translucentfalloff";
+ case ATTRIBUTE_DIFFUSELIGHTWRAP: return "diffuselightwrap";
+ case ATTRIBUTE_REFERENCEDMATERIAL: return "referencedmaterial";
+ case ATTRIBUTE_VERTEXCOLORS: return "vertexcolors";
+ case ATTRIBUTE_ROTATIONUV: return "rotationuv";
+ case ATTRIBUTE_POSITIONU: return "positionu";
+ case ATTRIBUTE_POSITIONV: return "positionv";
+ case ATTRIBUTE_SCALEU: return "scaleu";
+ case ATTRIBUTE_SCALEV: return "scalev";
+ case ATTRIBUTE_PIVOTU: return "pivotu";
+ case ATTRIBUTE_PIVOTV: return "pivotv";
+ case ATTRIBUTE_TILINGMODEHORZ: return "tilingmodehorz";
+ case ATTRIBUTE_TILINGMODEVERT: return "tilingmodevert";
+ case ATTRIBUTE_MAPPINGTYPE: return "mappingtype";
+ case ATTRIBUTE_MAPPINGMODE: return "mappingmode";
+ case ATTRIBUTE_SUBPRESENTATION: return "subpresentation";
+ case ATTRIBUTE_URI: return "uri";
+ case ATTRIBUTE_TRANSPARENT: return "transparent";
+ case ATTRIBUTE_PROGRESSIVEAA: return "progressiveaa";
+ case ATTRIBUTE_MULTISAMPLEAA: return "multisampleaa";
+ case ATTRIBUTE_TEMPORALAA: return "temporalaa";
+ case ATTRIBUTE_BLENDTYPE: return "blendtype";
+ case ATTRIBUTE_HORZFIELDS: return "horzfields";
+ case ATTRIBUTE_LEFT: return "left";
+ case ATTRIBUTE_LEFTUNITS: return "leftunits";
+ case ATTRIBUTE_WIDTH: return "width";
+ case ATTRIBUTE_WIDTHUNITS: return "widthunits";
+ case ATTRIBUTE_RIGHT: return "right";
+ case ATTRIBUTE_RIGHTUNITS: return "rightunits";
+ case ATTRIBUTE_VERTFIELDS: return "vertfields";
+ case ATTRIBUTE_TOP: return "top";
+ case ATTRIBUTE_TOPUNITS: return "topunits";
+ case ATTRIBUTE_HEIGHT: return "height";
+ case ATTRIBUTE_HEIGHTUNITS: return "heightunits";
+ case ATTRIBUTE_BOTTOM: return "bottom";
+ case ATTRIBUTE_BOTTOMUNITS: return "bottomunits";
+ case ATTRIBUTE_AOSTRENGTH: return "aostrength";
+ case ATTRIBUTE_AODISTANCE: return "aodistance";
+ case ATTRIBUTE_AOSOFTNESS: return "aosoftness";
+ case ATTRIBUTE_AOBIAS: return "aobias";
+ case ATTRIBUTE_AOSAMPLERATE: return "aosamplerate";
+ case ATTRIBUTE_AODITHER: return "aodither";
+ case ATTRIBUTE_SHADOWSTRENGTH: return "shadowstrength";
+ case ATTRIBUTE_SHADOWDIST: return "shadowdist";
+ case ATTRIBUTE_SHADOWSOFTNESS: return "shadowsoftness";
+ case ATTRIBUTE_SHADOWBIAS: return "shadowbias";
+ case ATTRIBUTE_LIGHTPROBE: return "lightprobe";
+ case ATTRIBUTE_PROBEBRIGHT: return "probebright";
+ case ATTRIBUTE_FASTIBL: return "fastibl";
+ case ATTRIBUTE_PROBEHORIZON: return "probehorizon";
+ case ATTRIBUTE_PROBEFOV: return "probefov";
+ case ATTRIBUTE_LIGHTPROBE2: return "lightprobe2";
+ case ATTRIBUTE_PROBE2FADE: return "probe2fade";
+ case ATTRIBUTE_PROBE2WINDOW: return "probe2window";
+ case ATTRIBUTE_PROBE2POS: return "probe2pos";
+ case ATTRIBUTE_DISABLEDEPTHTEST: return "disabledepthtest";
+ case ATTRIBUTE_DISABLEDEPTHPREPASS: return "disabledepthprepass";
+ case ATTRIBUTE_TEXTCOLOR: return "textcolor";
+ case ATTRIBUTE_TEXTCOLOR_R: return "textcolor.r";
+ case ATTRIBUTE_TEXTCOLOR_G: return "textcolor.g";
+ case ATTRIBUTE_TEXTCOLOR_B: return "textcolor.b";
+ case ATTRIBUTE_TEXTCOLOR_A: return "textcolor.a";
+ case ATTRIBUTE_SIZE: return "size";
+ case ATTRIBUTE_FONT: return "font";
+ case ATTRIBUTE_DROPSHADOW: return "dropshadow";
+ case ATTRIBUTE_DROPSHADOWSTRENGTH: return "dropshadowstrength";
+ case ATTRIBUTE_DROPSHADOWOFFSETX: return "dropshadowoffsetx";
+ case ATTRIBUTE_DROPSHADOWOFFSETY: return "dropshadowoffsety";
+ case ATTRIBUTE_BOUNDINGBOX: return "boundingbox";
+ case ATTRIBUTE_BOUNDINGBOX_X: return "boundingbox.x";
+ case ATTRIBUTE_BOUNDINGBOX_Y: return "boundingbox.y";
+ case ATTRIBUTE_ELIDE: return "elide";
+ case ATTRIBUTE_TRACKING: return "tracking";
+ case ATTRIBUTE_LEADING: return "leading";
+ case ATTRIBUTE_RENDERSTYLE: return "renderstyle";
+ case ATTRIBUTE_TEXTSTRING: return "textstring";
+ case ATTRIBUTE_BACKCOLOR_R: return "backcolor.r";
+ case ATTRIBUTE_BACKCOLOR_G: return "backcolor.g";
+ case ATTRIBUTE_BACKCOLOR_B: return "backcolor.b";
+ case ATTRIBUTE_BACKCOLOR_A: return "backcolor.a";
+ case ATTRIBUTE_TEXTTYPE: return "texttype";
+ case ATTRIBUTE_USEBACKCOLOR: return "usebackcolor";
+ case ATTRIBUTE_WORDWRAP: return "wordwrap";
+ case ATTRIBUTE_HORZSCROLL: return "horzscroll";
+ case ATTRIBUTE_HORZALIGN: return "horzalign";
+ case ATTRIBUTE_VERTSCROLL: return "vertscroll";
+ case ATTRIBUTE_VERTALIGN: return "vertalign";
+ case ATTRIBUTE_BOXHEIGHT: return "boxheight";
+ case ATTRIBUTE_BOXWIDTH: return "boxwidth";
+ case ATTRIBUTE_REMOTESTRINGSOURCE: return "remotestringsource";
+ case ATTRIBUTE_CACHEDTEXTSTRING: return "cachedtextstring";
+ case ATTRIBUTE_ENABLEACCELERATEDFONT: return "enableacceleratedfont";
+ case ATTRIBUTE_BEHAVIORSCRIPTS: return "BehaviorScripts";
+ case ATTRIBUTE_UICCUSTOMOBJTYPE: return "UICCustomObjType";
+ case ATTRIBUTE_BGCOLORENABLE: return "bgcolorenable";
+ case ATTRIBUTE_BACKGROUND: return "background";
+ case ATTRIBUTE_BACKGROUNDCOLOR_R: return "backgroundcolor.r";
+ case ATTRIBUTE_BACKGROUNDCOLOR_G: return "backgroundcolor.g";
+ case ATTRIBUTE_BACKGROUNDCOLOR_B: return "backgroundcolor.b";
+ case ATTRIBUTE_BACKGROUNDCOLOR_A: return "backgroundcolor.a";
+ case ATTRIBUTE_PATHTYPE: return "pathtype";
+ case ATTRIBUTE_LINEARERROR: return "linearerror";
+ case ATTRIBUTE_EDGETESSAMOUNT: return "edgetessamount";
+ case ATTRIBUTE_INNERTESSAMOUNT: return "innertessamount";
+ case ATTRIBUTE_BEGINCAP: return "begincap";
+ case ATTRIBUTE_BEGINCAPOFFSET: return "begincapoffset";
+ case ATTRIBUTE_BEGINCAPOPACITY: return "begincapopacity";
+ case ATTRIBUTE_BEGINCAPWIDTH: return "begincapwidth";
+ case ATTRIBUTE_ENDCAP: return "endcap";
+ case ATTRIBUTE_ENDCAPOFFSET: return "endcapoffset";
+ case ATTRIBUTE_ENDCAPOPACITY: return "endcapopacity";
+ case ATTRIBUTE_ENDCAPWIDTH: return "endcapwidth";
+ case ATTRIBUTE_PAINTSTYLE: return "paintstyle";
+ case ATTRIBUTE_CLOSED: return "closed";
+ case ATTRIBUTE_INCOMINGANGLE: return "incomingangle";
+ case ATTRIBUTE_INCOMINGDISTANCE: return "incomingdistance";
+ case ATTRIBUTE_OUTGOINGDISTANCE: return "outgoingdistance";
+ case ATTRIBUTE_PARTICLETYPE: return "particletype";
+ case ATTRIBUTE_MAXPARTICLES: return "maxparticles";
+ case ATTRIBUTE_PARTICLESIZE: return "particlesize";
+ case ATTRIBUTE_LIFETIME: return "lifetime";
+ case ATTRIBUTE_CONTROLLEDPROPERTY: return "controlledproperty";
+ case ATTRIBUTE_OBSERVEDPROPERTY: return "observedproperty";
+ case ATTRIBUTE_QT_IO: return "qt.io";
+ default: {
+ static char s_UnknownHash[16];
+ sprintf(s_UnknownHash, "(0x%08X)", inAttribute);
+ return s_UnknownHash;
+ }
+ }
+}
+
+} // namespace Q3DStudio
+
diff --git a/src/runtime/Qt3DSAttributeHashes.h b/src/runtime/Qt3DSAttributeHashes.h
new file mode 100644
index 0000000..1f712a8
--- /dev/null
+++ b/src/runtime/Qt3DSAttributeHashes.h
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 NVIDIA Corporation.
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+// !!!!! AUTOGENERATED CODE - DO NOT MODIFY MANUALLY !!!!!
+
+// Run the AttributeHashes project to regenerate this file from Attributehashes.txt list
+
+/// Key for the CElement attribute-value pair
+enum EAttribute {
+ ATTRIBUTE_NAME = 0x02B79D95, // name
+ ATTRIBUTE_TYPE = 0x005F9806, // type
+ ATTRIBUTE_OPACITY = 0x0191C315, // opacity
+ ATTRIBUTE_STARTTIME = 0x010A57B1, // starttime
+ ATTRIBUTE_ENDTIME = 0x003BF5F8, // endtime
+ ATTRIBUTE_SOURCEPATH = 0x0009EA60, // sourcepath
+ ATTRIBUTE_IMPORTID = 0x008F7900, // importid
+ ATTRIBUTE_EYEBALL = 0x02F454F0, // eyeball
+ ATTRIBUTE_POSITION = 0x00E9B7D7, // position
+ ATTRIBUTE_POSITION_X = 0x027C230D, // position.x
+ ATTRIBUTE_POSITION_Y = 0x027D234C, // position.y
+ ATTRIBUTE_POSITION_Z = 0x027E238B, // position.z
+ ATTRIBUTE_ROTATION = 0x03E51862, // rotation
+ ATTRIBUTE_ROTATION_X = 0x0239EE18, // rotation.x
+ ATTRIBUTE_ROTATION_Y = 0x023AEE57, // rotation.y
+ ATTRIBUTE_ROTATION_Z = 0x023BEE96, // rotation.z
+ ATTRIBUTE_SCALE = 0x01012856, // scale
+ ATTRIBUTE_SCALE_X = 0x0065440C, // scale.x
+ ATTRIBUTE_SCALE_Y = 0x0066444B, // scale.y
+ ATTRIBUTE_SCALE_Z = 0x0067448A, // scale.z
+ ATTRIBUTE_PIVOT = 0x009E907E, // pivot
+ ATTRIBUTE_PIVOT_X = 0x03811834, // pivot.x
+ ATTRIBUTE_PIVOT_Y = 0x03821873, // pivot.y
+ ATTRIBUTE_PIVOT_Z = 0x038318B2, // pivot.z
+ ATTRIBUTE_ROTATIONORDER = 0x03CE5F70, // rotationorder
+ ATTRIBUTE_ORIENTATION = 0x001A90B0, // orientation
+ ATTRIBUTE_SHADOWCASTER = 0x0363F874, // shadowcaster
+ ATTRIBUTE_TESSELLATION = 0x0335861F, // tessellation
+ ATTRIBUTE_EDGETESS = 0x023933D2, // edgetess
+ ATTRIBUTE_INNERTESS = 0x01529259, // innertess
+ ATTRIBUTE_ORTHOGRAPHIC = 0x0244BB70, // orthographic
+ ATTRIBUTE_CLIPNEAR = 0x0068FF28, // clipnear
+ ATTRIBUTE_CLIPFAR = 0x037EF699, // clipfar
+ ATTRIBUTE_FOV = 0x00D60213, // fov
+ ATTRIBUTE_FOVHORIZONTAL = 0x01BDB34F, // fovhorizontal
+ ATTRIBUTE_SCALEMODE = 0x01FD2FD3, // scalemode
+ ATTRIBUTE_SCALEANCHOR = 0x02CFCF41, // scaleanchor
+ ATTRIBUTE_BRIGHTNESS = 0x0230D3AF, // brightness
+ ATTRIBUTE_LINEARFADE = 0x0104E9FF, // linearfade
+ ATTRIBUTE_EXPFADE = 0x006B9267, // expfade
+ ATTRIBUTE_LIGHTTYPE = 0x0033F1D0, // lighttype
+ ATTRIBUTE_SCOPE = 0x0258D0CC, // scope
+ ATTRIBUTE_LIGHTDIFFUSE = 0x01246FD4, // lightdiffuse
+ ATTRIBUTE_LIGHTDIFFUSE_R = 0x035AAB10, // lightdiffuse.r
+ ATTRIBUTE_LIGHTDIFFUSE_G = 0x034FA85B, // lightdiffuse.g
+ ATTRIBUTE_LIGHTDIFFUSE_B = 0x034AA720, // lightdiffuse.b
+ ATTRIBUTE_LIGHTDIFFUSE_A = 0x0349A6E1, // lightdiffuse.a
+ ATTRIBUTE_LIGHTAMBIENT = 0x00DA56DE, // lightambient
+ ATTRIBUTE_LIGHTAMBIENT_R = 0x0179AD1A, // lightambient.r
+ ATTRIBUTE_LIGHTAMBIENT_G = 0x016EAA65, // lightambient.g
+ ATTRIBUTE_LIGHTAMBIENT_B = 0x0169A92A, // lightambient.b
+ ATTRIBUTE_LIGHTAMBIENT_A = 0x0168A8EB, // lightambient.a
+ ATTRIBUTE_LIGHTSPECULAR = 0x03E39A07, // lightspecular
+ ATTRIBUTE_LIGHTSPECULAR_R = 0x0241EBC3, // lightspecular.r
+ ATTRIBUTE_LIGHTSPECULAR_G = 0x0236E90E, // lightspecular.g
+ ATTRIBUTE_LIGHTSPECULAR_B = 0x0231E7D3, // lightspecular.b
+ ATTRIBUTE_LIGHTSPECULAR_A = 0x0230E794, // lightspecular.a
+ ATTRIBUTE_AREAWIDTH = 0x005A8BE7, // areawidth
+ ATTRIBUTE_AREAHEIGHT = 0x00334D2C, // areaheight
+ ATTRIBUTE_CASTSHADOW = 0x0335FD81, // castshadow
+ ATTRIBUTE_SHDWBIAS = 0x0125E79F, // shdwbias
+ ATTRIBUTE_SHDWFACTOR = 0x01B11BE9, // shdwfactor
+ ATTRIBUTE_SHDWMAPRES = 0x01E53834, // shdwmapres
+ ATTRIBUTE_SHDWMAPFAR = 0x019A30FD, // shdwmapfar
+ ATTRIBUTE_SHDWMAPFOV = 0x00830B07, // shdwmapfov
+ ATTRIBUTE_SHDWFILTER = 0x0176E1E0, // shdwfilter
+ ATTRIBUTE_LIGHTMAPINDIRECT = 0x004F1D6C, // lightmapindirect
+ ATTRIBUTE_LIGHTMAPRADIOSITY = 0x00AC7C50, // lightmapradiosity
+ ATTRIBUTE_LIGHTMAPSHADOW = 0x00191F3A, // lightmapshadow
+ ATTRIBUTE_IBLPROBE = 0x0039FD03, // iblprobe
+ ATTRIBUTE_SHADERLIGHTING = 0x0068A84F, // shaderlighting
+ ATTRIBUTE_EMISSIVEPOWER = 0x03D6F9F2, // emissivepower
+ ATTRIBUTE_EMISSIVECOLOR = 0x00B7AC94, // emissivecolor
+ ATTRIBUTE_EMISSIVECOLOR_R = 0x039B87D0, // emissivecolor.r
+ ATTRIBUTE_EMISSIVECOLOR_G = 0x0390851B, // emissivecolor.g
+ ATTRIBUTE_EMISSIVECOLOR_B = 0x038B83E0, // emissivecolor.b
+ ATTRIBUTE_EMISSIVECOLOR_A = 0x038A83A1, // emissivecolor.a
+ ATTRIBUTE_DIFFUSE = 0x0105521E, // diffuse
+ ATTRIBUTE_DIFFUSE_R = 0x015B085A, // diffuse.r
+ ATTRIBUTE_DIFFUSE_G = 0x015005A5, // diffuse.g
+ ATTRIBUTE_DIFFUSE_B = 0x014B046A, // diffuse.b
+ ATTRIBUTE_DIFFUSE_A = 0x014A042B, // diffuse.a
+ ATTRIBUTE_SPECULARMAP = 0x034CD047, // specularmap
+ ATTRIBUTE_SPECULARMODEL = 0x039EBE5A, // specularmodel
+ ATTRIBUTE_SPECULARTINT = 0x03535E02, // speculartint
+ ATTRIBUTE_SPECULARTINT_R = 0x0399623E, // speculartint.r
+ ATTRIBUTE_SPECULARTINT_G = 0x038E5F89, // speculartint.g
+ ATTRIBUTE_SPECULARTINT_B = 0x03895E4E, // speculartint.b
+ ATTRIBUTE_SPECULARTINT_A = 0x03885E0F, // speculartint.a
+ ATTRIBUTE_IOR = 0x00667354, // ior
+ ATTRIBUTE_FRESNELPOWER = 0x022178B6, // fresnelPower
+ ATTRIBUTE_SPECULARAMOUNT = 0x01144425, // specularamount
+ ATTRIBUTE_SPECULARROUGHNESS = 0x03925653, // specularroughness
+ ATTRIBUTE_ROUGHNESSMAP = 0x01088174, // roughnessmap
+ ATTRIBUTE_BLENDMODE = 0x01923A6C, // blendmode
+ ATTRIBUTE_CULLING = 0x03C539F0, // culling
+ ATTRIBUTE_ZBUFFERWRITE = 0x03E19B3B, // zbufferwrite
+ ATTRIBUTE_DIFFUSEMAP = 0x00FF8126, // diffusemap
+ ATTRIBUTE_DIFFUSEMAP2 = 0x0038D4A8, // diffusemap2
+ ATTRIBUTE_DIFFUSEMAP3 = 0x0039D4E7, // diffusemap3
+ ATTRIBUTE_SPECULARREFLECTION = 0x006B4C12, // specularreflection
+ ATTRIBUTE_OPACITYMAP = 0x00DA796F, // opacitymap
+ ATTRIBUTE_EMISSIVEMAP = 0x00F6427B, // emissivemap
+ ATTRIBUTE_EMISSIVEMAP2 = 0x03476893, // emissivemap2
+ ATTRIBUTE_BUMPMAP = 0x024EE11A, // bumpmap
+ ATTRIBUTE_BUMPAMOUNT = 0x01BC4192, // bumpamount
+ ATTRIBUTE_NORMALMAP = 0x03BD578B, // normalmap
+ ATTRIBUTE_DISPLACEMENTMAP = 0x01BCD1FB, // displacementmap
+ ATTRIBUTE_DISPLACEAMOUNT = 0x01EC1EAF, // displaceamount
+ ATTRIBUTE_TRANSLUCENCYMAP = 0x01D8F015, // translucencymap
+ ATTRIBUTE_TRANSLUCENTFALLOFF = 0x0097E985, // translucentfalloff
+ ATTRIBUTE_DIFFUSELIGHTWRAP = 0x038F6522, // diffuselightwrap
+ ATTRIBUTE_REFERENCEDMATERIAL = 0x035FDA80, // referencedmaterial
+ ATTRIBUTE_VERTEXCOLORS = 0x000814EC, // vertexcolors
+ ATTRIBUTE_ROTATIONUV = 0x012E3A61, // rotationuv
+ ATTRIBUTE_POSITIONU = 0x01D05AB4, // positionu
+ ATTRIBUTE_POSITIONV = 0x01D15AF3, // positionv
+ ATTRIBUTE_SCALEU = 0x001409F5, // scaleu
+ ATTRIBUTE_SCALEV = 0x00150A34, // scalev
+ ATTRIBUTE_PIVOTU = 0x03F8ABCD, // pivotu
+ ATTRIBUTE_PIVOTV = 0x03F9AC0C, // pivotv
+ ATTRIBUTE_TILINGMODEHORZ = 0x02562203, // tilingmodehorz
+ ATTRIBUTE_TILINGMODEVERT = 0x03F92B21, // tilingmodevert
+ ATTRIBUTE_MAPPINGTYPE = 0x02CA9058, // mappingtype
+ ATTRIBUTE_MAPPINGMODE = 0x002715CF, // mappingmode
+ ATTRIBUTE_SUBPRESENTATION = 0x03CA7426, // subpresentation
+ ATTRIBUTE_URI = 0x00296894, // uri
+ ATTRIBUTE_TRANSPARENT = 0x0316BA2E, // transparent
+ ATTRIBUTE_PROGRESSIVEAA = 0x019F1955, // progressiveaa
+ ATTRIBUTE_MULTISAMPLEAA = 0x013D29FD, // multisampleaa
+ ATTRIBUTE_TEMPORALAA = 0x00212AFE, // temporalaa
+ ATTRIBUTE_BLENDTYPE = 0x0035B4F5, // blendtype
+ ATTRIBUTE_HORZFIELDS = 0x02B8A818, // horzfields
+ ATTRIBUTE_LEFT = 0x0196B9B9, // left
+ ATTRIBUTE_LEFTUNITS = 0x02F9D2D8, // leftunits
+ ATTRIBUTE_WIDTH = 0x00C4D65A, // width
+ ATTRIBUTE_WIDTHUNITS = 0x01D7DF77, // widthunits
+ ATTRIBUTE_RIGHT = 0x039EAB44, // right
+ ATTRIBUTE_RIGHTUNITS = 0x0357EF0D, // rightunits
+ ATTRIBUTE_VERTFIELDS = 0x03462436, // vertfields
+ ATTRIBUTE_TOP = 0x002F6B0B, // top
+ ATTRIBUTE_TOPUNITS = 0x03D58806, // topunits
+ ATTRIBUTE_HEIGHT = 0x00CE9F79, // height
+ ATTRIBUTE_HEIGHTUNITS = 0x00C91D18, // heightunits
+ ATTRIBUTE_BOTTOM = 0x00F4EE75, // bottom
+ ATTRIBUTE_BOTTOMUNITS = 0x0174091C, // bottomunits
+ ATTRIBUTE_AOSTRENGTH = 0x010F7ED1, // aostrength
+ ATTRIBUTE_AODISTANCE = 0x01DC349D, // aodistance
+ ATTRIBUTE_AOSOFTNESS = 0x02CCDC71, // aosoftness
+ ATTRIBUTE_AOBIAS = 0x01818219, // aobias
+ ATTRIBUTE_AOSAMPLERATE = 0x0039B568, // aosamplerate
+ ATTRIBUTE_AODITHER = 0x0274316C, // aodither
+ ATTRIBUTE_SHADOWSTRENGTH = 0x0039ED5F, // shadowstrength
+ ATTRIBUTE_SHADOWDIST = 0x038213FA, // shadowdist
+ ATTRIBUTE_SHADOWSOFTNESS = 0x01F74AFF, // shadowsoftness
+ ATTRIBUTE_SHADOWBIAS = 0x02CB3EA7, // shadowbias
+ ATTRIBUTE_LIGHTPROBE = 0x02D47DC6, // lightprobe
+ ATTRIBUTE_PROBEBRIGHT = 0x029DC5B6, // probebright
+ ATTRIBUTE_FASTIBL = 0x02559509, // fastibl
+ ATTRIBUTE_PROBEHORIZON = 0x014DAAF5, // probehorizon
+ ATTRIBUTE_PROBEFOV = 0x03D66903, // probefov
+ ATTRIBUTE_LIGHTPROBE2 = 0x00430008, // lightprobe2
+ ATTRIBUTE_PROBE2FADE = 0x02ED0742, // probe2fade
+ ATTRIBUTE_PROBE2WINDOW = 0x016B224E, // probe2window
+ ATTRIBUTE_PROBE2POS = 0x024B0C0E, // probe2pos
+ ATTRIBUTE_DISABLEDEPTHTEST = 0x000B8353, // disabledepthtest
+ ATTRIBUTE_DISABLEDEPTHPREPASS = 0x02AE1EA7, // disabledepthprepass
+ ATTRIBUTE_TEXTCOLOR = 0x02D9114A, // textcolor
+ ATTRIBUTE_TEXTCOLOR_R = 0x00E9F186, // textcolor.r
+ ATTRIBUTE_TEXTCOLOR_G = 0x00DEEED1, // textcolor.g
+ ATTRIBUTE_TEXTCOLOR_B = 0x00D9ED96, // textcolor.b
+ ATTRIBUTE_TEXTCOLOR_A = 0x00D8ED57, // textcolor.a
+ ATTRIBUTE_SIZE = 0x00F2C81F, // size
+ ATTRIBUTE_FONT = 0x03412331, // font
+ ATTRIBUTE_DROPSHADOW = 0x03E3F231, // dropshadow
+ ATTRIBUTE_DROPSHADOWSTRENGTH = 0x03F8B7D0, // dropshadowstrength
+ ATTRIBUTE_DROPSHADOWOFFSETX = 0x013298AA, // dropshadowoffsetx
+ ATTRIBUTE_DROPSHADOWOFFSETY = 0x013398E9, // dropshadowoffsety
+ ATTRIBUTE_BOUNDINGBOX = 0x02F3B6D9, // boundingbox
+ ATTRIBUTE_BOUNDINGBOX_X = 0x0272C10F, // boundingbox.x
+ ATTRIBUTE_BOUNDINGBOX_Y = 0x0273C14E, // boundingbox.y
+ ATTRIBUTE_ELIDE = 0x022937DD, // elide
+ ATTRIBUTE_TRACKING = 0x02A25049, // tracking
+ ATTRIBUTE_LEADING = 0x016A6BDA, // leading
+ ATTRIBUTE_RENDERSTYLE = 0x03567B85, // renderstyle
+ ATTRIBUTE_TEXTSTRING = 0x01124062, // textstring
+ ATTRIBUTE_BACKCOLOR_R = 0x0290CCE0, // backcolor.r
+ ATTRIBUTE_BACKCOLOR_G = 0x0285CA2B, // backcolor.g
+ ATTRIBUTE_BACKCOLOR_B = 0x0280C8F0, // backcolor.b
+ ATTRIBUTE_BACKCOLOR_A = 0x027FC8B1, // backcolor.a
+ ATTRIBUTE_TEXTTYPE = 0x0240ADD9, // texttype
+ ATTRIBUTE_USEBACKCOLOR = 0x0243BACB, // usebackcolor
+ ATTRIBUTE_WORDWRAP = 0x0134B04C, // wordwrap
+ ATTRIBUTE_HORZSCROLL = 0x005B3CC4, // horzscroll
+ ATTRIBUTE_HORZALIGN = 0x00BA002A, // horzalign
+ ATTRIBUTE_VERTSCROLL = 0x00E8B8E2, // vertscroll
+ ATTRIBUTE_VERTALIGN = 0x03759C8C, // vertalign
+ ATTRIBUTE_BOXHEIGHT = 0x0079AF8E, // boxheight
+ ATTRIBUTE_BOXWIDTH = 0x016B7105, // boxwidth
+ ATTRIBUTE_REMOTESTRINGSOURCE = 0x025DFEEE, // remotestringsource
+ ATTRIBUTE_CACHEDTEXTSTRING = 0x0095DBA0, // cachedtextstring
+ ATTRIBUTE_ENABLEACCELERATEDFONT = 0x0053A92D, // enableacceleratedfont
+ ATTRIBUTE_BEHAVIORSCRIPTS = 0x01DF916A, // BehaviorScripts
+ ATTRIBUTE_UICCUSTOMOBJTYPE = 0x029F1BCF, // UICCustomObjType
+ ATTRIBUTE_BGCOLORENABLE = 0x0021EE1F, // bgcolorenable
+ ATTRIBUTE_BACKGROUND = 0x006AA932, // background
+ ATTRIBUTE_BACKGROUNDCOLOR_R = 0x02AF0767, // backgroundcolor.r
+ ATTRIBUTE_BACKGROUNDCOLOR_G = 0x02A404B2, // backgroundcolor.g
+ ATTRIBUTE_BACKGROUNDCOLOR_B = 0x029F0377, // backgroundcolor.b
+ ATTRIBUTE_BACKGROUNDCOLOR_A = 0x029E0338, // backgroundcolor.a
+ ATTRIBUTE_PATHTYPE = 0x02D2A5E1, // pathtype
+ ATTRIBUTE_LINEARERROR = 0x0378A51D, // linearerror
+ ATTRIBUTE_EDGETESSAMOUNT = 0x02577E3A, // edgetessamount
+ ATTRIBUTE_INNERTESSAMOUNT = 0x0027A241, // innertessamount
+ ATTRIBUTE_BEGINCAP = 0x03373D37, // begincap
+ ATTRIBUTE_BEGINCAPOFFSET = 0x01FEFE64, // begincapoffset
+ ATTRIBUTE_BEGINCAPOPACITY = 0x02C2761E, // begincapopacity
+ ATTRIBUTE_BEGINCAPWIDTH = 0x0102BDE3, // begincapwidth
+ ATTRIBUTE_ENDCAP = 0x00ADB3A9, // endcap
+ ATTRIBUTE_ENDCAPOFFSET = 0x0382A9D6, // endcapoffset
+ ATTRIBUTE_ENDCAPOPACITY = 0x019BA72C, // endcapopacity
+ ATTRIBUTE_ENDCAPWIDTH = 0x03A315F1, // endcapwidth
+ ATTRIBUTE_PAINTSTYLE = 0x03ADEC8D, // paintstyle
+ ATTRIBUTE_CLOSED = 0x01807034, // closed
+ ATTRIBUTE_INCOMINGANGLE = 0x03890AB3, // incomingangle
+ ATTRIBUTE_INCOMINGDISTANCE = 0x005EB2A5, // incomingdistance
+ ATTRIBUTE_OUTGOINGDISTANCE = 0x017C597F, // outgoingdistance
+ ATTRIBUTE_PARTICLETYPE = 0x01C01260, // particletype
+ ATTRIBUTE_MAXPARTICLES = 0x00BE66B7, // maxparticles
+ ATTRIBUTE_PARTICLESIZE = 0x02534279, // particlesize
+ ATTRIBUTE_LIFETIME = 0x0033D297, // lifetime
+ ATTRIBUTE_CONTROLLEDPROPERTY = 0x022C0A1D, // controlledproperty
+ ATTRIBUTE_OBSERVEDPROPERTY = 0x02D1CE03, // observedproperty
+ ATTRIBUTE_QT_IO = 0x010EF2CF, // qt.io
+}; // enum EAttribute
+
+#define AK_STRING_QT_IO "qt.io"
+
+/// Function providing reverse hash lookup
+const char *GetAttributeString(const EAttribute inAttribute);
+
+} // namespace Q3DStudio
+
diff --git a/src/runtime/Qt3DSAttributeHashes.txt b/src/runtime/Qt3DSAttributeHashes.txt
new file mode 100644
index 0000000..4168bbd
--- /dev/null
+++ b/src/runtime/Qt3DSAttributeHashes.txt
@@ -0,0 +1,249 @@
+name
+type
+opacity
+
+starttime
+endtime
+
+sourcepath
+importid
+eyeball
+position
+position.x
+position.y
+position.z
+rotation
+rotation.x
+rotation.y
+rotation.z
+scale
+scale.x
+scale.y
+scale.z
+pivot
+pivot.x
+pivot.y
+pivot.z
+rotationorder
+orientation
+shadowcaster
+tessellation
+edgetess
+innertess
+
+orthographic
+clipnear
+clipfar
+fov
+fovhorizontal
+scalemode
+scaleanchor
+
+brightness
+linearfade
+expfade
+lighttype
+scope
+lightdiffuse
+lightdiffuse.r
+lightdiffuse.g
+lightdiffuse.b
+lightdiffuse.a
+lightambient
+lightambient.r
+lightambient.g
+lightambient.b
+lightambient.a
+lightspecular
+lightspecular.r
+lightspecular.g
+lightspecular.b
+lightspecular.a
+areawidth
+areaheight
+castshadow
+shdwbias
+shdwfactor
+shdwmapres
+shdwmapfar
+shdwmapfov
+shdwfilter
+
+lightmapindirect
+lightmapradiosity
+lightmapshadow
+
+iblprobe
+
+shaderlighting
+emissivepower
+emissivecolor
+emissivecolor.r
+emissivecolor.g
+emissivecolor.b
+emissivecolor.a
+diffuse
+diffuse.r
+diffuse.g
+diffuse.b
+diffuse.a
+specularmap
+specularmodel
+speculartint
+speculartint.r
+speculartint.g
+speculartint.b
+speculartint.a
+ior
+fresnelPower
+specularamount
+specularroughness
+roughnessmap
+blendmode
+culling
+zbufferwrite
+diffusemap
+diffusemap2
+diffusemap3
+specularreflection
+opacitymap
+emissivemap
+emissivemap2
+bumpmap
+bumpamount
+normalmap
+displacementmap
+displaceamount
+translucencymap
+translucentfalloff
+diffuselightwrap
+referencedmaterial
+vertexcolors
+
+rotationuv
+positionu
+positionv
+scaleu
+scalev
+pivotu
+pivotv
+tilingmodehorz
+tilingmodevert
+mappingtype
+mappingmode
+subpresentation
+uri
+
+transparent
+progressiveaa
+multisampleaa
+temporalaa
+blendtype
+horzfields
+left
+leftunits
+width
+widthunits
+right
+rightunits
+vertfields
+top
+topunits
+height
+heightunits
+bottom
+bottomunits
+aostrength
+aodistance
+aosoftness
+aobias
+aosamplerate
+aodither
+shadowstrength
+shadowdist
+shadowsoftness
+shadowbias
+lightprobe
+probebright
+fastibl
+probehorizon
+probefov
+lightprobe2
+probe2fade
+probe2window
+probe2pos
+disabledepthtest
+disabledepthprepass
+
+textcolor
+textcolor.r
+textcolor.g
+textcolor.b
+textcolor.a
+size
+font
+dropshadow
+dropshadowstrength
+dropshadowoffsetx
+dropshadowoffsety
+boundingbox
+boundingbox.x
+boundingbox.y
+elide
+tracking
+leading
+renderstyle
+textstring
+backcolor.r
+backcolor.g
+backcolor.b
+backcolor.a
+texttype
+usebackcolor
+wordwrap
+horzscroll
+horzalign
+vertscroll
+vertalign
+boxheight
+boxwidth
+remotestringsource
+cachedtextstring
+enableacceleratedfont
+
+BehaviorScripts
+UICCustomObjType
+
+bgcolorenable
+background
+backgroundcolor.r
+backgroundcolor.g
+backgroundcolor.b
+backgroundcolor.a
+
+pathtype
+linearerror
+edgetessamount
+innertessamount
+begincap
+begincapoffset
+begincapopacity
+begincapwidth
+endcap
+endcapoffset
+endcapopacity
+endcapwidth
+paintstyle
+closed
+
+incomingangle
+incomingdistance
+outgoingdistance
+
+particletype
+maxparticles
+particlesize
+lifetime
+
+controlledproperty
+observedproperty
diff --git a/src/runtime/Qt3DSCommandEventTypes.h b/src/runtime/Qt3DSCommandEventTypes.h
new file mode 100644
index 0000000..01a12b4
--- /dev/null
+++ b/src/runtime/Qt3DSCommandEventTypes.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSHash.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Commands
+//==============================================================================
+
+// Command types are 31-bit hash instead of 32-bit
+// The 1 bit flag is being used to differentiate between a Command and an Event
+// Commands trigger execution, both commands and events can trigger logic
+
+// Asset
+const TEventCommandHash COMMAND_SETPROPERTY = CHash::HashEventCommand("COMMAND_SETPROPERTY");
+const TEventCommandHash COMMAND_FIREEVENT = CHash::HashEventCommand("COMMAND_FIREEVENT");
+const TEventCommandHash COMMAND_SCRIPTLET = CHash::HashEventCommand("COMMAND_SCRIPTLET");
+const TEventCommandHash COMMAND_PLAYSOUND = CHash::HashEventCommand("COMMAND_PLAYSOUND");
+const TEventCommandHash COMMAND_EMITSIGNAL = CHash::HashEventCommand("COMMAND_EMITSIGNAL");
+
+// Time
+const TEventCommandHash COMMAND_PLAY = CHash::HashEventCommand("COMMAND_PLAY");
+const TEventCommandHash COMMAND_PAUSE = CHash::HashEventCommand("COMMAND_PAUSE");
+const TEventCommandHash COMMAND_GOTOTIME = CHash::HashEventCommand("COMMAND_GOTOTIME");
+const TEventCommandHash COMMAND_GOTOTIMELABEL = CHash::HashEventCommand("COMMAND_GOTOTIMELABEL");
+
+// Slide
+const TEventCommandHash COMMAND_GOTOSLIDE = CHash::HashEventCommand("COMMAND_GOTOSLIDE");
+const TEventCommandHash COMMAND_GOTOSLIDENAME = CHash::HashEventCommand("COMMAND_GOTOSLIDENAME");
+const TEventCommandHash COMMAND_GOTONEXTSLIDE = CHash::HashEventCommand("COMMAND_GOTONEXTSLIDE");
+const TEventCommandHash COMMAND_GOTOPREVIOUSSLIDE =
+ CHash::HashEventCommand("COMMAND_GOTOPREVIOUSSLIDE");
+const TEventCommandHash COMMAND_BACKSLIDE = CHash::HashEventCommand("COMMAND_BACKSLIDE");
+
+// Behavior
+const TEventCommandHash COMMAND_CUSTOMACTION = CHash::HashEventCommand("COMMAND_CUSTOMACTION");
+const TEventCommandHash COMMAND_CUSTOMCALLBACK = CHash::HashEventCommand("COMMAND_CALLBACK");
+//==============================================================================
+// Events
+//==============================================================================
+
+// Slide Events
+const TEventCommandHash EVENT_ONSLIDEENTER = CHash::HashEventCommand("onSlideEnter");
+const TEventCommandHash EVENT_ONSLIDEEXIT = CHash::HashEventCommand("onSlideExit");
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSCommandHelper.cpp b/src/runtime/Qt3DSCommandHelper.cpp
new file mode 100644
index 0000000..53c7648
--- /dev/null
+++ b/src/runtime/Qt3DSCommandHelper.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+#include "Qt3DSCommandHelper.h"
+#include "Qt3DSCommandEventTypes.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSSlideSystem.h"
+
+namespace Q3DStudio {
+
+CCommandHelper::CCommandHelper()
+{
+}
+
+CCommandHelper::~CCommandHelper()
+{
+}
+
+static Option<Q3DStudio::UINT8> FindSlide(TElement &inElement, const char *slideName)
+{
+ IPresentation *thePresentation = inElement.GetBelongedPresentation();
+
+ int theSlideHashName = thePresentation->GetApplication().HashString(slideName);
+ TComponent *theComponent = thePresentation->GetComponentManager().GetComponent(&inElement);
+ UINT8 theSlideIndex =
+ thePresentation->GetSlideSystem().FindSlide(*theComponent, theSlideHashName);
+ if (theSlideIndex >= theComponent->GetSlideCount()) {
+ qt3ds::foundation::CRegisteredString elemPath = thePresentation->GetElementPath(inElement);
+ if (elemPath.IsValid()) {
+ qCCritical(qt3ds::INVALID_PARAMETER)
+ << "CCommandHelper: FindSlide: Unable to find slide "
+ << slideName << " on element " << elemPath.c_str();
+ } else {
+ qCCritical(qt3ds::INVALID_PARAMETER)
+ << "CCommandHelper: FindSlide: Unable to find slide " << slideName;
+ }
+ return Empty();
+ }
+
+ return theSlideIndex;
+}
+
+bool CCommandHelper::SetupGotoSlideCommand(TElement &inElement, Q3DStudio::INT32 inSlideIndex,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ IPresentation *thePresentation = inElement.GetBelongedPresentation();
+ TElement *theTarget = GetComponentParent(&inElement);
+ SComponentGotoSlideData theSlideData(inSlideIndex);
+ theSlideData.m_Mode = inArgs.m_Mode;
+ theSlideData.m_Paused = inArgs.m_Paused;
+ theSlideData.m_Rate = inArgs.m_Rate;
+ theSlideData.m_Reverse = inArgs.m_Reverse;
+ theSlideData.m_StartTime = inArgs.m_StartTime;
+ // Resolve playthroughto if it has a valid value.
+ if (!isTrivial(inArgs.m_PlaythroughTo)) {
+ if (AreEqual(inArgs.m_PlaythroughTo, "next"))
+ theSlideData.m_PlaythroughTo = -1;
+ else if (AreEqual(inArgs.m_PlaythroughTo, "previous"))
+ theSlideData.m_PlaythroughTo = -2;
+ else {
+ // Find the slide if possible. If not, then just error leave things as they are.
+
+ Option<UINT8> theSlideIndex = FindSlide(inElement, inArgs.m_PlaythroughTo);
+ if (theSlideIndex.hasValue())
+ theSlideData.m_PlaythroughTo = *theSlideIndex;
+ }
+ }
+ thePresentation->GetComponentManager().SetupComponentGotoSlideCommand(theTarget, theSlideData);
+ UVariant theArg1;
+ UVariant theArg2;
+ qt3ds::intrinsics::memZero(&theArg1, sizeof(UVariant));
+ qt3ds::intrinsics::memZero(&theArg2, sizeof(UVariant));
+ thePresentation->FireCommand(COMMAND_GOTOSLIDE, theTarget, &theArg1, &theArg2);
+ return true;
+}
+
+bool CCommandHelper::SetupGotoSlideCommand(TElement &inElement, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ Option<UINT8> theSlideIndex = FindSlide(inElement, slideName);
+ if (theSlideIndex.hasValue())
+ return SetupGotoSlideCommand(inElement, *theSlideIndex, inArgs);
+ return false;
+}
+
+//==============================================================================
+/**
+ * Checks if the given element is a component. If it isn't walks up the tree and looks for one.
+ * @param inElementManager the object that manages the elements in the presentation
+ * @param inElement the element to check if it's a component
+ * @return the component
+ */
+TElement *CCommandHelper::GetComponentParent(TElement *inElement)
+{
+ Q3DStudio_ASSERT(inElement);
+ return &inElement->GetComponentParent();
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSCommandHelper.h b/src/runtime/Qt3DSCommandHelper.h
new file mode 100644
index 0000000..412a51a
--- /dev/null
+++ b/src/runtime/Qt3DSCommandHelper.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "Qt3DSIScriptBridge.h"
+
+namespace Q3DStudio {
+
+class CCommandHelper
+{
+private:
+ CCommandHelper();
+ CCommandHelper(const CCommandHelper &);
+ CCommandHelper &operator=(const CCommandHelper &);
+ virtual ~CCommandHelper();
+
+public:
+ static bool SetupGotoSlideCommand(TElement &inElement, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs);
+ static bool SetupGotoSlideCommand(TElement &inElement, Q3DStudio::INT32 inSlide,
+ const SScriptEngineGotoSlideArgs &inArgs);
+
+public:
+ static TElement *GetComponentParent(TElement *inParent);
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSComponentManager.cpp b/src/runtime/Qt3DSComponentManager.cpp
new file mode 100644
index 0000000..d7ce0b6
--- /dev/null
+++ b/src/runtime/Qt3DSComponentManager.cpp
@@ -0,0 +1,487 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSComponentManager.h"
+#include "Qt3DSIPresentation.h"
+#include "Qt3DSSlideSystem.h"
+#include "Qt3DSCommandEventTypes.h"
+#include "Qt3DSPresentationFrameData.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSActivationManager.h"
+#include "Qt3DSPresentation.h"
+
+using qt3ds::runtime::SSlideKey;
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Constructor
+ * @param inPresentation reference to its presentation
+ */
+CComponentManager::CComponentManager(IPresentation &inPresentation)
+ : m_Presentation(static_cast<CPresentation &>(inPresentation))
+{
+}
+
+//==============================================================================
+/**
+ * Enforces the contract that slide switching requires a reset to the master
+ * slide (slide 0) before switching to the desired slide index.
+ * Also updates the TComponent's current slide number.
+ * @param inComponent TComponent to set slide on
+ * @param inSlideIndex relative index of the slide to switch to
+ * @param inSlideExit true if exiting the slide, false otherwise
+ */
+void CComponentManager::GotoSlideIndex(TElement *inComponent,
+ const SComponentGotoSlideData &inGotoData,
+ BOOL inSlideExit /*= true*/)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+
+ if (theComponent == NULL) {
+ return;
+ }
+
+ if (inGotoData.m_Slide < 1 || inGotoData.m_Slide >= theComponent->GetSlideCount()) {
+ qCCritical(qt3ds::INVALID_PARAMETER)
+ << "GotoSlide: Index out of range: Index " << inGotoData.m_Slide
+ << " SlideCount: "<< theComponent->GetSlideCount();
+ return;
+ }
+
+ if (theComponent->GetActive() == false) {
+ m_ComponentInitialSlideMap[inComponent] = inGotoData;
+ return;
+ }
+
+ SComponentGotoSlideData theGotoSlideData(inGotoData);
+
+ TComponentGotoSlideDataMap::iterator iter = m_ComponentInitialSlideMap.find(inComponent);
+ if (iter != m_ComponentInitialSlideMap.end()) {
+ theGotoSlideData = iter->second;
+ m_ComponentInitialSlideMap.erase(iter);
+ }
+
+ const UINT8 theCurrentSlideIndex = theComponent->GetCurrentSlide();
+
+ QString elementPath = QString::fromUtf8(inComponent->m_Path.c_str());
+
+ if (inSlideExit) {
+ SEventCommand theEvent = { inComponent, EVENT_ONSLIDEEXIT };
+ theEvent.m_IsEvent = true;
+ m_Presentation.ProcessEvent(theEvent);
+
+ m_Presentation.GetApplication().ComponentSlideExited(&m_Presentation, inComponent,
+ elementPath, theCurrentSlideIndex,
+ GetCurrentSlideName(inComponent));
+
+ // Signal previous slide change
+ m_Presentation.signalProxy()->SigSlideExited(elementPath, theCurrentSlideIndex,
+ GetCurrentSlideName(inComponent));
+
+ // m_Presentation.FireEvent(EVENT_ONSLIDEEXIT, inComponent);
+ // m_Presentation.FlushEventCommandQueue();
+ }
+
+ // Update dynamic keys to use current values before slide switching, with the exception of
+ // master slides because playback only starts from non-master slides.
+ if (theCurrentSlideIndex > 0) {
+ m_Presentation.GetSlideSystem().InitializeDynamicKeys(
+ SSlideKey(*theComponent, (qt3ds::QT3DSU8)theGotoSlideData.m_Slide),
+ m_Presentation.GetAnimationSystem());
+ // deactivate actions and animation tracks on the current slide.
+ m_Presentation.GetSlideSystem().RollbackSlide(
+ SSlideKey(*inComponent, (qt3ds::QT3DSU8)theCurrentSlideIndex),
+ m_Presentation.GetAnimationSystem(), m_Presentation.GetLogicSystem());
+ } else {
+ m_Presentation.GetSlideSystem().ExecuteSlide(SSlideKey(*inComponent, 0),
+ m_Presentation.GetAnimationSystem(),
+ m_Presentation.GetLogicSystem());
+ }
+ // Execute new slide.
+ m_Presentation.GetSlideSystem().ExecuteSlide(
+ SSlideKey(*inComponent, (qt3ds::QT3DSU8)theGotoSlideData.m_Slide),
+ m_Presentation.GetAnimationSystem(), m_Presentation.GetLogicSystem());
+ CTimePolicy &thePolicy(theComponent->GetTimePolicy());
+ TTimeUnit theLoopingDuration = thePolicy.GetLoopingDuration();
+ if (theGotoSlideData.m_Mode.hasValue()) {
+ inComponent->SetPlayThrough(false);
+ switch (*theGotoSlideData.m_Mode) {
+ case TimePolicyModes::Looping:
+ thePolicy.Initialize(theLoopingDuration, 0, false);
+ break;
+ case TimePolicyModes::PingPong:
+ thePolicy.Initialize(theLoopingDuration, 0, true);
+ break;
+ case TimePolicyModes::StopAtEnd:
+ thePolicy.Initialize(theLoopingDuration, 1, false);
+ break;
+ case TimePolicyModes::Ping:
+ thePolicy.Initialize(theLoopingDuration, 1, true);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ // Update the current index which also updates the back index
+ theComponent->SetCurrentSlide(static_cast<UINT8>(theGotoSlideData.m_Slide));
+ m_PlaythroughOverrideMap.erase(inComponent);
+ if (theGotoSlideData.m_PlaythroughTo.hasValue()) {
+ m_PlaythroughOverrideMap.insert(
+ eastl::make_pair(inComponent, *theGotoSlideData.m_PlaythroughTo));
+ theComponent->SetPlayThrough(true);
+ }
+
+ // Reset time for component to 0
+
+ if (theGotoSlideData.m_Paused.hasValue())
+ thePolicy.SetPaused(*theGotoSlideData.m_Paused);
+
+ thePolicy.SetRate(theGotoSlideData.m_Rate);
+ thePolicy.SetReverse(theGotoSlideData.m_Reverse);
+
+ if (theGotoSlideData.m_StartTime.hasValue())
+ thePolicy.SetTime(*theGotoSlideData.m_StartTime);
+
+ inComponent->SetDirty();
+ m_Presentation.FireEvent(EVENT_ONSLIDEENTER, inComponent);
+ qt3ds::runtime::IActivityZone *theZone = m_Presentation.GetActivityZone();
+ if (theZone)
+ theZone->OnSlideChange(*inComponent);
+
+ m_Presentation.GetApplication().ComponentSlideEntered(&m_Presentation, inComponent,
+ elementPath, theGotoSlideData.m_Slide,
+ GetCurrentSlideName(inComponent));
+ // Signal current slide change
+ m_Presentation.signalProxy()->SigSlideEntered(elementPath, GetCurrentSlide(inComponent),
+ GetCurrentSlideName(inComponent));
+}
+
+//==============================================================================
+/**
+ * Sets a component to a specified slide using the slide name hash.
+ * Performs a switch to Slide 0 (master) first, before switching to the
+ * required slide.
+ * @param inComponent reference to the TComponent
+ * @param inSlideHashName hash of the slide to switch to
+ */
+void CComponentManager::GotoSlideName(TElement *inComponent, const TStringHash inSlideHashName)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+ UINT8 theSlideIndex = m_Presentation.GetSlideSystem().FindSlide(*theComponent, inSlideHashName);
+
+ if (theSlideIndex < 1 || theSlideIndex >= theComponent->GetSlideCount()) {
+ const char *slideName = m_Presentation.GetApplication().ReverseHash(inSlideHashName);
+ if (slideName && *slideName)
+ qCCritical(qt3ds::INVALID_PARAMETER) << "GotoSlide: Name not found: " << slideName;
+ else
+ qCCritical(qt3ds::INVALID_PARAMETER) << "GotoSlide: Name not found: " << inSlideHashName;
+
+ } else
+ GotoSlideIndex(inComponent, theSlideIndex);
+}
+
+//==============================================================================
+/**
+ * Return the index of the current slide of the component.
+ * @param inComponent reference to the TComponent
+ * @return the index of current slide
+ */
+UINT8 CComponentManager::GetCurrentSlide(TElement *inComponent)
+{
+ return GetComponent(inComponent)->GetCurrentSlide();
+}
+
+//==============================================================================
+/**
+ * Return the number of slides on a component.
+ * @param inComponent reference to the TComponent
+ * @return the number of slides
+ */
+UINT8 CComponentManager::GetSlideCount(TElement *inComponent)
+{
+ return GetComponent(inComponent)->GetSlideCount();
+}
+
+//==============================================================================
+/**
+ * Performs a switch to the slide following the current slide (current slide + 1).
+ * @param inComponent reference to the TComponent
+ * @param inIncrement slide increment offset
+ */
+void CComponentManager::GoToNextSlide(TElement *inComponent, const INT32 inIncrement)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+ INT32 theNewIndex = Q3DStudio_clamp<INT32>(inIncrement + theComponent->GetCurrentSlide(), 1L,
+ theComponent->GetSlideCount() - 1);
+
+ // Trigger goto to slide only if the next slide is not the current
+ if (theComponent->GetActive()) {
+ if (theNewIndex != theComponent->GetCurrentSlide())
+ GotoSlideIndex(inComponent, theNewIndex);
+ } else {
+ qCCritical(qt3ds::INVALID_PARAMETER)
+ << "Runtime: Attempt to goto slide on an inactive component!";
+ }
+}
+
+//==============================================================================
+/**
+ * Performs a switch to the slide following the current slide (current slide - 1).
+ * @param inComponent reference to the TComponent
+ * @param inDecrement slide decrement offset
+ */
+void CComponentManager::GoToPreviousSlide(TElement *inComponent, const INT32 inDecrement)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+ INT32 theNewIndex = Q3DStudio_clamp<INT32>(theComponent->GetCurrentSlide() - inDecrement, 1L,
+ theComponent->GetSlideCount() - 1);
+
+ // Trigger goto to slide only if the next slide is not the current
+ if (theNewIndex != theComponent->GetCurrentSlide())
+ GotoSlideIndex(inComponent, theNewIndex);
+}
+
+//==============================================================================
+/**
+ * Playthrough to the next slide that is specified.
+ * @param inComponent reference to the TComponent
+ */
+void CComponentManager::PlaythroughToSlide(TElement *inComponent)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+ TComponentIntMap::iterator iter = m_PlaythroughOverrideMap.find(inComponent);
+ INT32 thePlaythroughTo = 0;
+ if (iter != m_PlaythroughOverrideMap.end()) {
+ thePlaythroughTo = iter->second;
+ m_PlaythroughOverrideMap.erase(iter);
+ } else {
+ thePlaythroughTo = m_Presentation.GetSlideSystem().GetPlaythroughToSlideIndex(
+ SSlideKey(*theComponent, (qt3ds::QT3DSU8)theComponent->GetCurrentSlide()));
+ if (thePlaythroughTo == -1) {
+ qCCritical(qt3ds::INVALID_OPERATION) << "Missing slide to play through to";
+ inComponent->SetPlayThrough(
+ false); // clear this flag, so that this is equivalent to a "Stop At End"
+ }
+ }
+
+ if (thePlaythroughTo == -2) {
+ GoToPreviousSlide(inComponent, 1);
+ } else if (thePlaythroughTo == -1) {
+ GoToNextSlide(inComponent, 1);
+ } else {
+ GotoSlideIndex(inComponent, thePlaythroughTo);
+ }
+}
+
+//==============================================================================
+/**
+ * Performs a switch to the previous slide.
+ * @param inComponent reference to the TComponent
+ */
+void CComponentManager::GoToBackSlide(TElement *inComponent)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+ GotoSlideIndex(theComponent, theComponent->GetPreviousSlide());
+}
+
+//==============================================================================
+/**
+ * Set the component's local time using the supplied time.
+ * @param inComponent reference to the TComponent
+ * @param inTime supplied time
+ */
+void CComponentManager::GoToTime(TElement *inComponent, const TTimeUnit inTime)
+{
+ if (inComponent == NULL)
+ return;
+ if (inComponent->GetActive() == false) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "Runtime: Attempt to goto time on inactive component!";
+ return;
+ }
+ m_Presentation.GetActivityZone()->GoToTime(*inComponent, inTime);
+ inComponent->SetDirty();
+}
+
+//==============================================================================
+/**
+ * Get the component's playback state
+ * @param inComponent reference to the TComponent
+ * @return true if it's paused, false if it's playing
+ */
+BOOL CComponentManager::GetPause(TElement *inComponent)
+{
+ CTimePolicy &theTimePolicy = GetComponent(inComponent)->GetTimePolicy();
+ return theTimePolicy.GetPaused();
+}
+
+//==============================================================================
+/**
+ * Get the component's ping pong direction
+ * @param inComponent reference to the TComponent
+ * @return true if it's pingponging forward, false if it's pingponging backwards
+ */
+BOOL CComponentManager::GetPlayBackDirection(TElement *inComponent)
+{
+ return GetComponent(inComponent)->GetPlayBackDirection();
+}
+
+//==============================================================================
+/**
+ * Update time policy of the component during each slide execution
+ * @param inComponent reference to the TComponent
+ * @param inLoopDuration loop duration of component at that slide
+ * @param inRepetitions number of repetitions
+ * @param inPingPong if true the component will move with respect to reverse time
+ *sequence
+ * @param inPlayThrough if true the component will continue to execute next slide
+ */
+void CComponentManager::SetTimePolicy(TElement *inComponent, const TTimeUnit inLoopDuration,
+ const UINT32 inRepetitions, const BOOL inPingPong,
+ const BOOL inPlayThrough)
+{
+ CTimePolicy &theTimePolicy = GetComponent(inComponent)->GetTimePolicy();
+ theTimePolicy.Initialize(inLoopDuration, inRepetitions, inPingPong);
+ inComponent->SetPlayThrough(inPlayThrough);
+}
+
+//==============================================================================
+/**
+ * Set the component's playback to either play or pause
+ * @param inComponent reference to the TComponent
+ * @param inPause true = pause, false = play
+ */
+void CComponentManager::SetPause(TElement *inComponent, const BOOL inPause)
+{
+ CTimePolicy &theTimePolicy = GetComponent(inComponent)->GetTimePolicy();
+ BOOL wasPaused = theTimePolicy.GetPaused();
+ theTimePolicy.SetPaused(inPause);
+ if (wasPaused != inPause)
+ inComponent->SetDirty();
+}
+
+//==============================================================================
+/**
+ * Promote from TElement* to TComponent.
+ * @param inElement TElement pointer to be recast as TComponent
+ * @return pointer to the component
+ */
+TComponent *CComponentManager::GetComponent(TElement *inElement)
+{
+ Q3DStudio_ASSERT(inElement->IsComponent());
+ return static_cast<TComponent *>(inElement);
+}
+
+//==============================================================================
+/**
+ * Gets the string name of the current slide.
+ * @param inComponent the component to query for it's current slide name
+ * @return the char buffer storing the name
+ */
+const CHAR *CComponentManager::GetCurrentSlideName(TElement *inComponent)
+{
+ TComponent *theComponent = GetComponent(inComponent);
+ return m_Presentation.GetSlideSystem().GetSlideName(
+ SSlideKey(*theComponent, (qt3ds::QT3DSU8)theComponent->GetCurrentSlide()));
+}
+
+void CComponentManager::OnElementDeactivated(TElement *)
+{
+}
+
+void CComponentManager::SetComponentTimeOverride(TElement *inElement, TTimeUnit inEndTime,
+ FLOAT inInterpolation,
+ IComponentTimeOverrideFinishedCallback *inCallback)
+{
+ if (inElement->IsComponent()) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "ComponentManager: SetComponentTimeOverride called on object that "
+ << "wasn't an element";
+ if (inCallback)
+ inCallback->Release();
+ Q3DStudio_ASSERT(false);
+ return;
+ }
+ // sanitize end time.
+ CTimePolicy &theTimePolicy = GetComponent(inElement)->GetTimePolicy();
+ if (inEndTime < 0)
+ inEndTime = 0;
+ if (inEndTime > theTimePolicy.GetLoopingDuration())
+ inEndTime = theTimePolicy.GetLoopingDuration();
+ m_Presentation.GetActivityZone()->GetOrCreateItemComponentOverride(*inElement, inInterpolation,
+ inEndTime, inCallback);
+
+ // Force the time policy object to respect its own time. If we played through till the end and
+ // sat there for a while
+ // we need the actual time will travel further and further from the time policy's local time.
+ // If we then play backward we
+ // will jump until the offset is synchronized with the actual time.
+ theTimePolicy.SetTime(theTimePolicy.GetTime());
+ inElement->SetDirty();
+}
+
+void CComponentManager::SetupComponentGotoSlideCommand(TElement *inElement,
+ const SComponentGotoSlideData &inSlide)
+{
+ m_ComponentGotoSlideMap[inElement] = inSlide;
+}
+
+bool CComponentManager::HasComponentGotoSlideCommand(TElement *inElement)
+{
+ return m_ComponentGotoSlideMap.find(inElement) != m_ComponentGotoSlideMap.end();
+}
+
+SComponentGotoSlideData CComponentManager::GetComponentGotoSlideCommand(TElement *inElement)
+{
+ TComponentGotoSlideDataMap::iterator iter = m_ComponentGotoSlideMap.find(inElement);
+ if (iter != m_ComponentGotoSlideMap.end())
+ return iter->second;
+ return -1;
+}
+
+void CComponentManager::ReleaseComponentGotoSlideCommand(TElement *inElement)
+{
+ TComponentGotoSlideDataMap::iterator iter = m_ComponentGotoSlideMap.find(inElement);
+ if (iter != m_ComponentGotoSlideMap.end())
+ m_ComponentGotoSlideMap.erase(iter);
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSComponentManager.h b/src/runtime/Qt3DSComponentManager.h
new file mode 100644
index 0000000..865b8aa
--- /dev/null
+++ b/src/runtime/Qt3DSComponentManager.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSIComponentManager.h"
+#include "EASTL/hash_map.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+class CTimePolicy;
+class CPresentation;
+struct SComponentTimePolicyOverride
+{
+ TElement *m_Element;
+ FLOAT m_TimeMultiplier;
+ TTimeUnit m_EndTime;
+ IComponentTimeOverrideFinishedCallback *m_TimeCallback;
+ SComponentTimePolicyOverride(TElement *inElement, FLOAT inMultiplier, TTimeUnit inEndTime,
+ IComponentTimeOverrideFinishedCallback *inCallback)
+ : m_Element(inElement)
+ , m_TimeMultiplier(inMultiplier)
+ , m_EndTime(inEndTime)
+ , m_TimeCallback(inCallback)
+ {
+ }
+ // Returns the local time and a boolean indicating if we have reached the end.
+ // Implemented in UICTimePolicy.cpp so that I can compare the CTimePolicy::ComputeTime method
+ // with
+ // SComponentTimePolicyOverride::ComputLocalTime method
+ eastl::pair<BOOL, TTimeUnit> ComputeLocalTime(CTimePolicy &inTimePolicy,
+ TTimeUnit inGlobalTime);
+ // Implemented in UICTimePolicy.cpp
+ void SetTime(CTimePolicy &inTimePolicy, TTimeUnit inLocalTime);
+};
+
+typedef eastl::hash_map<TElement *, SComponentGotoSlideData> TComponentGotoSlideDataMap;
+typedef eastl::hash_map<TElement *, Q3DStudio::INT32> TComponentIntMap;
+
+//==============================================================================
+/**
+ * The Component Manager is a factory and container of all components
+ * within the presentation.
+ */
+class CComponentManager : public IComponentManager
+{
+ CPresentation &m_Presentation;
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ CComponentManager(IPresentation &inPresentation);
+
+public: // Slide
+ void GotoSlideIndex(TElement *inComponent, const SComponentGotoSlideData &inGotoData,
+ BOOL inSlideExit = true) override;
+ void GotoSlideName(TElement *inComponent, const TStringHash inSlideHashName) override;
+ void GoToBackSlide(TElement *inComponent) override;
+ void GoToNextSlide(TElement *inComponent, const INT32 inDecrement = 1) override;
+ void GoToPreviousSlide(TElement *inComponent, const INT32 inDecrement = 1) override;
+ void PlaythroughToSlide(TElement *inComponent);
+
+ UINT8 GetSlideCount(TElement *inComponent) override;
+ UINT8 GetCurrentSlide(TElement *inComponent) override;
+ const CHAR *GetCurrentSlideName(TElement *inComponent) override;
+
+ void OnElementDeactivated(TElement *inElement) override;
+
+ void SetComponentTimeOverride(TElement *inElement, TTimeUnit inEndTime, FLOAT inInterpolation,
+ IComponentTimeOverrideFinishedCallback *inCallback) override;
+
+ // Allows multiple gotoslide command operating on the same element to work correctly.
+ // This API is meant to fix a scenario where gotoslide is called multiple times in a frame
+ // on the same component. This isn't avoidable in some cases without very complex scene logic.
+
+ // later calls override earlier calls
+ void SetupComponentGotoSlideCommand(TElement *inElement,
+ const SComponentGotoSlideData &inSlide) override;
+ bool HasComponentGotoSlideCommand(TElement *inElement) override;
+ SComponentGotoSlideData GetComponentGotoSlideCommand(TElement *inElement) override;
+ void ReleaseComponentGotoSlideCommand(TElement *inElement) override;
+
+public: // Time
+ void GoToTime(TElement *inComponent, const TTimeUnit inTime) override;
+ void SetPause(TElement *inComponent, const BOOL inPause) override;
+ void SetTimePolicy(TElement *inComponent, const TTimeUnit inLoopDuration,
+ const UINT32 inRepetitions, const BOOL inPingPong, const BOOL inPlayThrough) override;
+
+ TTimeUnit ComputeComponentLocalTime(TElement *inComponent, const TTimeUnit inGlobalTime);
+ BOOL GetPause(TElement *inComponent) override;
+ BOOL GetPlayBackDirection(TElement *inComponent) override;
+
+protected: // Promotion
+ TComponent *GetComponent(TElement *inElement) override;
+
+private:
+ // Disabled Copy Construction
+ CComponentManager(const CComponentManager &);
+ CComponentManager &operator=(const CComponentManager &);
+
+ TComponentGotoSlideDataMap m_ComponentInitialSlideMap;
+ TComponentGotoSlideDataMap m_ComponentGotoSlideMap;
+ TComponentIntMap m_PlaythroughOverrideMap;
+
+ //==============================================================================
+ // Friends
+ //==============================================================================
+ friend class CSlideBuilder;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSElementHelper.cpp b/src/runtime/Qt3DSElementHelper.cpp
new file mode 100644
index 0000000..acf8849
--- /dev/null
+++ b/src/runtime/Qt3DSElementHelper.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+#include "Qt3DSElementHelper.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSCommandEventTypes.h"
+
+using namespace qt3ds;
+
+namespace Q3DStudio {
+
+const char PRESENTATION_DELIMITER = ':';
+const char NODE_DELIMITER = '.';
+const TStringHash RESERVED_THIS = CHash::HashString("this");
+const TStringHash RESERVED_PARENT = CHash::HashString("parent");
+const TStringHash RESERVED_SCENE = CHash::HashString("Scene");
+
+CElementHelper::CElementHelper()
+{
+}
+
+CElementHelper::~CElementHelper()
+{
+}
+
+TElement *CElementHelper::GetElement(qt3ds::runtime::IApplication &inApplication,
+ IPresentation *inDefaultPresentation, const char *inPath,
+ TElement *inStartElement)
+{
+ if (!inPath || *inPath == 0)
+ return nullptr;
+ const char *thePath(inPath);
+ const char *theSubPath = nullptr;
+ IPresentation *thePresentation = nullptr;
+ size_t thePathLength = ::strlen(thePath) + 1;
+ char *theToken = Q3DStudio_allocate_desc(CHAR, thePathLength, "Token:TempPath");
+ // Try to get the specified presentation
+ theSubPath = ::strchr(thePath, PRESENTATION_DELIMITER);
+ TElement *theElement = inStartElement;
+ if (theSubPath) {
+ UINT32 theSubPathLength = static_cast<UINT32>(theSubPath - thePath);
+
+ ::strncpy(theToken, thePath, theSubPathLength);
+ theToken[theSubPathLength] = '\0';
+
+ thePath = theSubPath + 1;
+
+ const CHAR *thePresentationName = theToken;
+
+ thePresentation = inApplication.GetPresentationById(thePresentationName);
+ }
+
+ if (!thePresentation)
+ thePresentation = inDefaultPresentation;
+
+ // Return nil if the inStartElement is not in the specified presentation
+ if (theElement
+ && (!theSubPath && theElement->GetBelongedPresentation() != thePresentation)) {
+ thePresentation = theElement->GetBelongedPresentation();
+ }
+
+ if (!thePresentation)
+ return nullptr;
+
+ TStringHash theName;
+ INT32 theParseCounter = 0;
+
+ while (thePath && thePath[0] != '\0') {
+ ++theParseCounter;
+
+ // Do some strtok() work here
+ theSubPath = ::strchr(thePath, NODE_DELIMITER);
+ if (theSubPath) {
+ UINT32 theSubPathLength = static_cast<UINT32>(theSubPath - thePath);
+ Q3DStudio_ASSERT(theSubPathLength < thePathLength);
+
+ ::strncpy(theToken, thePath, theSubPathLength);
+ theToken[theSubPathLength] = '\0';
+
+ thePath = theSubPath + 1;
+ } else {
+ ::strcpy(theToken, thePath);
+ thePath = nullptr;
+ }
+
+ // Hash the token and do some element searching
+ theName = CHash::HashString(theToken);
+
+ if (theName == RESERVED_PARENT) {
+ if (theElement)
+ theElement = theElement->GetParent();
+ } else if (theName == RESERVED_THIS) {
+ ;
+ } else {
+ if (theName == RESERVED_SCENE && theParseCounter == 1) {
+ // theElement is nullptr, so using absolute path
+ theElement = thePresentation->GetRoot();
+ } else if (theElement) {
+ // Using relative path
+ theElement = theElement->FindChild(theName);
+ }
+ }
+
+ if (!theElement)
+ thePath = nullptr;
+ } // while
+
+ Q3DStudio_free(theToken, CHAR, thePathLength);
+ return theElement;
+}
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSElementHelper.h b/src/runtime/Qt3DSElementHelper.h
new file mode 100644
index 0000000..df2afdd
--- /dev/null
+++ b/src/runtime/Qt3DSElementHelper.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qt3ds {
+namespace runtime {
+ class IApplication;
+}
+}
+
+namespace Q3DStudio {
+
+class IPresentation;
+
+class CElementHelper
+{
+private:
+ CElementHelper();
+ CElementHelper(const CElementHelper &);
+ CElementHelper &operator=(const CElementHelper &);
+ virtual ~CElementHelper();
+
+public:
+ static TElement *GetElement(qt3ds::runtime::IApplication &inApplication,
+ IPresentation *inDefaultPresentation, const char *inPath,
+ TElement *inStartElement = nullptr);
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSElementSystem.cpp b/src/runtime/Qt3DSElementSystem.cpp
new file mode 100644
index 0000000..d8e1833
--- /dev/null
+++ b/src/runtime/Qt3DSElementSystem.cpp
@@ -0,0 +1,855 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+#include "Qt3DSElementSystem.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSPool.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "EASTL/sort.h"
+#include "foundation/Qt3DSIntrinsics.h"
+#include "Qt3DSHash.h"
+#include "Qt3DSActivationManager.h"
+#include "Qt3DSIPresentation.h"
+#include "Qt3DSPresentationFrameData.h"
+#include "Qt3DSAttributeHashes.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSIndexableLinkedList.h"
+
+using namespace qt3ds::runtime::element;
+using namespace qt3ds;
+using namespace qt3ds::foundation;
+using namespace qt3ds::intrinsics;
+namespace eastl {
+template <>
+struct hash<STypeDesc>
+{
+ size_t operator()(const STypeDesc &inDesc) const { return inDesc.HashCode(); }
+};
+}
+
+namespace {
+typedef IndexableLinkedList<SPropertyValueGroup, Q3DStudio::UVariant,
+ SPropertyValueGroup::NumValues>
+ TElementPropertyList;
+struct SPropertyDescSorter
+{
+ bool operator()(const TPropertyDescAndValue &lhs, const TPropertyDescAndValue &rhs) const
+ {
+ return strcmp(lhs.first.m_Name.c_str(), rhs.first.m_Name.c_str()) < 0;
+ }
+};
+QT3DSU32 GetNumValueAllocations(const STypeDesc &inTypeDesc)
+{
+ return ((inTypeDesc.m_Properties.size() + SPropertyValueGroup::NumValues - 1)
+ / SPropertyValueGroup::NumValues);
+}
+struct SElementAllocator : public qt3ds::runtime::IElementAllocator
+{
+ typedef Pool<SElement, ForwardingAllocator> TElementPool;
+ typedef Pool<SComponent, ForwardingAllocator> TComponentPool;
+ typedef TElementPropertyList::TPoolType TValuePool;
+ typedef nvhash_map<QT3DSU32, SElement *> THandleElementMap;
+ typedef nvhash_map<SElement *, QT3DSU32> TElementOffsetMap;
+ typedef nvhash_set<STypeDesc> TTypeDescSet;
+ typedef nvhash_map<const STypeDesc *, QT3DSU32> TTypeDescOffsetMap;
+ typedef nvhash_map<QT3DSU32, const STypeDesc *> TOffsetTypeDescMap;
+ typedef nvvector<NVDataRef<QT3DSU8>> TLoadBufferList;
+
+ NVFoundationBase &m_Foundation;
+ IStringTable &m_StringTable;
+ TElementPool m_Elements;
+ TComponentPool m_Components;
+ TValuePool m_Values;
+ THandleElementMap m_HandleToElements;
+ TElementOffsetMap m_ElementOffsets;
+ TTypeDescOffsetMap m_TypeDescOffsets;
+ TOffsetTypeDescMap m_OffsetsToTypeDescs;
+ TTypeDescSet m_TypeDescriptors;
+ nvvector<TPropertyDescAndValue> m_TempPropertyDescsAndValues;
+ nvvector<SPropertyDesc> m_TempPropertyDescs;
+ nvvector<CRegisteredString> m_IgnoredProperties;
+ // Upon load we allocate a set of buffers
+ nvvector<QT3DSU8> m_LoadBuffer;
+ TLoadBufferList m_AllocatedBuffers;
+ SSAutoDeallocatorAllocator m_AutoAllocator;
+ nvvector<SElement *> m_LoadElements;
+
+ QT3DSU32 m_NextElementHandle;
+
+ eastl::string m_Workspace;
+ eastl::string m_WorkspaceExt;
+
+ QT3DSI32 m_RefCount;
+
+ SElementAllocator(NVFoundationBase &inFoundation, IStringTable &inStrTable)
+ : m_Foundation(inFoundation)
+ , m_StringTable(inStrTable)
+ , m_Elements(ForwardingAllocator(m_Foundation.getAllocator(), "m_Elements"))
+ , m_Components(ForwardingAllocator(m_Foundation.getAllocator(), "m_Components"))
+ , m_Values(ForwardingAllocator(m_Foundation.getAllocator(), "m_Values"))
+ , m_HandleToElements(m_Foundation.getAllocator(), "m_HandleToElements")
+ , m_ElementOffsets(m_Foundation.getAllocator(), "m_ElementOffsets")
+ , m_TypeDescOffsets(m_Foundation.getAllocator(), "m_TypeDescOffsets")
+ , m_OffsetsToTypeDescs(m_Foundation.getAllocator(), "m_OffsetsToTypeDescs")
+ , m_TypeDescriptors(m_Foundation.getAllocator(), "m_TypeDescriptors")
+ , m_TempPropertyDescsAndValues(m_Foundation.getAllocator(), "m_TempPropertyDescsAndValues")
+ , m_TempPropertyDescs(m_Foundation.getAllocator(), "m_TempPropertyDescs")
+ , m_IgnoredProperties(m_Foundation.getAllocator(), "m_IgnoredProperties")
+ , m_LoadBuffer(m_Foundation.getAllocator(), "m_LoadBuffer")
+ , m_AllocatedBuffers(m_Foundation.getAllocator(), "m_AllocatedBuffers")
+ , m_AutoAllocator(inFoundation)
+ , m_LoadElements(inFoundation.getAllocator(), "m_LoadElements")
+ , m_NextElementHandle(1)
+ , m_RefCount(0)
+ {
+ }
+
+ ~SElementAllocator() {}
+
+ void GetIgnoredProperties()
+ {
+ if (m_IgnoredProperties.empty())
+ m_IgnoredProperties.push_back(m_StringTable.RegisterStr("name"));
+ }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Foundation.getAllocator());
+ NVDelete(alloc, this);
+ }
+ }
+
+ SElement &CreateElement(CRegisteredString inName, CRegisteredString inType,
+ CRegisteredString inSubType,
+ NVConstDataRef<TPropertyDescAndValue> inPropertyDescriptions,
+ Q3DStudio::IPresentation *inPresentation, SElement *inParent,
+ bool inIsComponent) override
+ {
+ m_TempPropertyDescsAndValues.clear();
+ m_TempPropertyDescs.clear();
+ bool participatesInTimeGraph = false;
+ GetIgnoredProperties();
+ for (QT3DSU32 idx = 0, end = inPropertyDescriptions.size(); idx < end; ++idx) {
+ QT3DSU32 nameHash = inPropertyDescriptions[idx].first.GetNameHash();
+ if (nameHash == Q3DStudio::ATTRIBUTE_STARTTIME
+ || nameHash == Q3DStudio::ATTRIBUTE_ENDTIME)
+ participatesInTimeGraph = true;
+ if (eastl::find(m_IgnoredProperties.begin(), m_IgnoredProperties.end(),
+ inPropertyDescriptions[idx].first.m_Name)
+ == m_IgnoredProperties.end()) {
+ m_TempPropertyDescsAndValues.push_back(inPropertyDescriptions[idx]);
+ }
+ }
+ eastl::sort(m_TempPropertyDescsAndValues.begin(), m_TempPropertyDescsAndValues.end(),
+ SPropertyDescSorter());
+
+ for (QT3DSU32 idx = 0, end = m_TempPropertyDescsAndValues.size(); idx < end; ++idx) {
+ m_TempPropertyDescs.push_back(m_TempPropertyDescsAndValues[idx].first);
+ }
+
+ STypeDesc theDesc;
+ theDesc.m_TypeName = inType;
+ theDesc.m_SubtypeName = inSubType;
+ theDesc.m_Properties = m_TempPropertyDescs;
+ theDesc.SetHashValue();
+ eastl::pair<TTypeDescSet::iterator, bool> inserter = m_TypeDescriptors.insert(theDesc);
+
+ const STypeDesc &theTypeDesc = *inserter.first;
+ if (inserter.second) {
+ size_t allocSize = theDesc.m_Properties.size() * sizeof(SPropertyDesc);
+ if (allocSize) {
+ SPropertyDesc *newProps = (SPropertyDesc *)m_AutoAllocator.allocate(
+ allocSize, "TypeDescData", __FILE__, __LINE__, 0);
+ memCopy(newProps, theDesc.m_Properties.begin(), (QT3DSU32)allocSize);
+ // Note that this does not change the hash value.
+ const_cast<STypeDesc &>(theTypeDesc).m_Properties =
+ toConstDataRef(newProps, theDesc.m_Properties.size());
+ }
+ }
+
+ SElement *retval = inIsComponent ? m_Components.construct(theTypeDesc, __FILE__, __LINE__)
+ : m_Elements.construct(theTypeDesc, __FILE__, __LINE__);
+ retval->m_BelongedPresentation = inPresentation;
+ retval->m_Name = inName;
+ // children
+ if (inParent) {
+ retval->m_Parent = inParent;
+ if (inParent->m_Child == NULL) {
+ inParent->m_Child = retval;
+ } else {
+ SElement *lastChild = inParent->m_Child;
+ while (lastChild->m_Sibling)
+ lastChild = lastChild->m_Sibling;
+ lastChild->m_Sibling = retval;
+ }
+ }
+ while (FindElementByHandle(m_NextElementHandle)) {
+ ++m_NextElementHandle;
+ if (!m_NextElementHandle)
+ ++m_NextElementHandle;
+ }
+
+ // required flags
+ retval->m_Handle = m_NextElementHandle;
+ if (inIsComponent)
+ retval->Flags().clearOrSet(true, Q3DStudio::ELEMENTFLAG_COMPONENT);
+
+ if (participatesInTimeGraph)
+ retval->Flags().clearOrSet(true, Q3DStudio::ELEMENTFLAG_TIMELINE);
+
+ retval->Flags().clearOrSet(true, Q3DStudio::ELEMENTFLAG_EXPLICITACTIVE);
+
+ retval->SetDepth();
+
+ retval->SetDirty();
+
+ m_HandleToElements.insert(eastl::make_pair(retval->m_Handle, retval));
+
+ // property values;
+ QT3DSU32 propIdx = 0;
+ TElementPropertyList::CreateAll(retval->m_PropertyValues, theTypeDesc.m_Properties.size(),
+ m_Values);
+ for (TElementPropertyList::iterator
+ iter = TElementPropertyList::begin(retval->m_PropertyValues,
+ theTypeDesc.m_Properties.size()),
+ end = TElementPropertyList::end(retval->m_PropertyValues,
+ theTypeDesc.m_Properties.size());
+ iter != end; ++iter, ++propIdx) {
+ (*iter) = m_TempPropertyDescsAndValues[propIdx].second;
+ }
+
+ return *retval;
+ }
+
+ void ReleaseElement(SElement &inElement, bool inRecurse) override
+ {
+ if (inRecurse) {
+ while (inElement.m_Child)
+ ReleaseElement(*inElement.m_Child, true);
+ }
+ // Trim out the element.
+ if (inElement.m_Parent) {
+ SElement *theParent = inElement.m_Parent;
+ if (theParent->m_Child == &inElement)
+ theParent->m_Child = inElement.m_Sibling;
+ else {
+ SElement *thePreviousChild = NULL;
+ // Empty loop to find the previous child
+ for (thePreviousChild = theParent->m_Child;
+ thePreviousChild->m_Sibling != &inElement && thePreviousChild;
+ thePreviousChild = thePreviousChild->m_Sibling) {
+ }
+ if (thePreviousChild)
+ thePreviousChild->m_Sibling = inElement.m_Sibling;
+ }
+ }
+
+ m_HandleToElements.erase(inElement.m_Handle);
+
+ QT3DSU8 *elemAddr = reinterpret_cast<QT3DSU8 *>(&inElement);
+ for (QT3DSU32 idx = 0, end = m_AllocatedBuffers.size(); idx < end; ++idx) {
+ NVDataRef<QT3DSU8> theBuffer(m_AllocatedBuffers[idx]);
+ // Preloaded element, do not release back to element pools
+ if (elemAddr >= theBuffer.begin() && elemAddr < theBuffer.end())
+ return;
+ }
+
+ SPropertyValueGroup *theValueGroup = inElement.m_PropertyValues;
+ while (theValueGroup) {
+ SPropertyValueGroup *currentGroup = theValueGroup;
+ theValueGroup = theValueGroup->m_NextNode;
+ m_Values.deallocate(currentGroup);
+ }
+
+ if (inElement.IsComponent())
+ m_Components.deallocate(&inElement);
+ else
+ m_Elements.deallocate(&inElement);
+ }
+
+ QT3DSI32 GetExtensionIndex(eastl::string &inExt)
+ {
+ QT3DSI32 retVal = 0;
+
+ if (!inExt.compare(".x") || !inExt.compare(".r"))
+ retVal = 0;
+ else if (!inExt.compare(".y") || !inExt.compare(".g"))
+ retVal = 1;
+ if (!inExt.compare(".z") || !inExt.compare(".b"))
+ retVal = 2;
+
+ return retVal;
+ }
+
+ virtual Option<TPropertyDescAndValuePtr>
+ CreateDynamicProperty(Q3DStudio::IRuntimeMetaData &theMetaData, SElement &element,
+ CRegisteredString inName) override
+ {
+ SPropertyDesc *newProp = NULL;
+ m_TempPropertyDescsAndValues.clear();
+ m_TempPropertyDescs.clear();
+
+ // create dynamic descriptor
+ if (!element.m_DynamicTypeDescription) {
+ STypeDesc *theDesc;
+ theDesc = (STypeDesc *)m_AutoAllocator.allocate(sizeof(STypeDesc), "TypeDescData",
+ __FILE__, __LINE__, 0);
+ theDesc->m_TypeName = element.m_TypeDescription->m_TypeName;
+ theDesc->m_SubtypeName = element.m_TypeDescription->m_SubtypeName;
+ theDesc->m_Properties = m_TempPropertyDescs;
+ theDesc->SetHashValue();
+ // we need this palcement new here
+ ::new (theDesc) STypeDesc();
+ element.m_DynamicTypeDescription = const_cast<STypeDesc *>(theDesc);
+ }
+
+ STypeDesc *theTypeDesc = element.m_DynamicTypeDescription;
+
+ // remove the extension that we can find the property
+ QT3DSI32 extIndex = 0;
+ m_Workspace.assign(inName.c_str());
+ eastl::string::size_type theCharPos = m_Workspace.rfind('.');
+ if (theCharPos != eastl::string::npos) {
+ m_WorkspaceExt.assign(m_Workspace, theCharPos, m_Workspace.length());
+ m_Workspace.resize(theCharPos);
+ extIndex = GetExtensionIndex(m_WorkspaceExt);
+ }
+
+ CRegisteredString theWorkSpaceString = m_StringTable.RegisterStr(m_Workspace.c_str());
+ Q3DStudio::ERuntimeDataModelDataType thePropertyType =
+ theMetaData.GetPropertyType(element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+
+ // using separate properties
+ bool separateProperties = theCharPos != eastl::string::npos;
+
+ // single property
+ size_t allocSize = sizeof(SPropertyDesc);
+ if (allocSize) {
+ newProp = (SPropertyDesc *)m_AutoAllocator.allocate(allocSize, "SPropertyDesc",
+ __FILE__, __LINE__, 0);
+
+ newProp->m_Name = inName;
+
+ // convert to appropriate type
+ if (thePropertyType == Q3DStudio::ERuntimeDataModelDataTypeFloat) {
+ float value = theMetaData.GetPropertyValueFloat(
+ element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_FLOAT;
+
+ Q3DStudio::UVariant theValue;
+ theValue.m_FLOAT = value;
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_FLOAT), theValue));
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ } else if (thePropertyType == Q3DStudio::ERuntimeDataModelDataTypeFloat2) {
+ qt3ds::QT3DSVec2 value = theMetaData.GetPropertyValueVector2(
+ element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_FLOAT;
+
+ Q3DStudio::UVariant theVarValue;
+ theVarValue.m_FLOAT = value[extIndex];
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_FLOAT),
+ theVarValue));
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ } else if (thePropertyType == Q3DStudio::ERuntimeDataModelDataTypeFloat3) {
+ qt3ds::QT3DSVec3 value = theMetaData.GetPropertyValueVector3(
+ element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+
+ if (separateProperties) {
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_FLOAT;
+
+ Q3DStudio::UVariant theVarValue;
+ theVarValue.m_FLOAT = value[extIndex];
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_FLOAT),
+ theVarValue));
+ } else {
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_FLOAT3;
+
+ Q3DStudio::UVariant theVarValue;
+ theVarValue.m_FLOAT3[0] = value[0];
+ theVarValue.m_FLOAT3[1] = value[1];
+ theVarValue.m_FLOAT3[2] = value[2];
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_FLOAT3),
+ theVarValue));
+ }
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ } else if (thePropertyType == Q3DStudio::ERuntimeDataModelDataTypeLong) {
+ QT3DSI32 value = (QT3DSI32)theMetaData.GetPropertyValueLong(
+ element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_INT32;
+
+ Q3DStudio::UVariant theVarValue;
+ theVarValue.m_INT32 = value;
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_INT32),
+ theVarValue));
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ } else if (thePropertyType == Q3DStudio::ERuntimeDataModelDataTypeBool) {
+ bool value = theMetaData.GetPropertyValueBool(
+ element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_BOOL;
+
+ Q3DStudio::UVariant theVarValue;
+ theVarValue.m_INT32 = value;
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_BOOL), theVarValue));
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ } else if (thePropertyType == Q3DStudio::ERuntimeDataModelDataTypeString) {
+ Option<eastl::string> theRuntimeStr = theMetaData.GetPropertyValueString(
+ element.m_TypeDescription->m_TypeName, theWorkSpaceString,
+ element.m_TypeDescription->m_SubtypeName);
+
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_STRING;
+
+ CStringHandle theString = m_StringTable.GetHandle(theRuntimeStr->c_str());
+ Q3DStudio::UVariant theVarValue;
+ theVarValue.m_StringHandle = theString.handle();
+ m_TempPropertyDescsAndValues.push_back(eastl::make_pair(
+ SPropertyDesc(theWorkSpaceString, Q3DStudio::ATTRIBUTETYPE_STRING),
+ theVarValue));
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ } else {
+ newProp->m_Type = Q3DStudio::ATTRIBUTETYPE_FLOAT;
+
+ theTypeDesc->m_DynamicProperties.push_back(*newProp);
+ }
+ }
+
+ // property values;
+ QT3DSU32 newListSize = theTypeDesc->m_DynamicProperties.size() - 1;
+ TElementPropertyList::Create(element.m_DynamicPropertyValues, newListSize, m_Values);
+
+ Q3DStudio::UVariant &propertyValue =
+ TElementPropertyList::GetObjAtIdx(element.m_DynamicPropertyValues, newListSize - 1);
+ propertyValue = m_TempPropertyDescsAndValues[0].second;
+
+ return eastl::make_pair(
+ element.m_DynamicTypeDescription->m_DynamicProperties[newListSize - 1],
+ &TElementPropertyList::GetObjAtIdx(element.m_DynamicPropertyValues, newListSize - 1));
+ }
+
+ SElement *FindElementByHandle(QT3DSU32 inElementHandle) override
+ {
+ THandleElementMap::iterator theFind = m_HandleToElements.find(inElementHandle);
+ if (theFind != m_HandleToElements.end())
+ return theFind->second;
+ return NULL;
+ }
+
+ // Returns an element pointer that when added to the return value of load will be a valid
+ // element.
+ SElement *GetRemappedElementAddress(SElement *inElement) const override
+ {
+ if (inElement == NULL)
+ return NULL;
+ TElementOffsetMap::const_iterator iter = m_ElementOffsets.find(inElement);
+ if (iter != m_ElementOffsets.end())
+ return reinterpret_cast<SElement *>(iter->second);
+ return NULL;
+ }
+
+ const STypeDesc *GetRemappedTypeDescAddress(const STypeDesc *inTypeDesc) const override
+ {
+ TTypeDescOffsetMap::const_iterator iter = m_TypeDescOffsets.find((STypeDesc *)inTypeDesc);
+ if (iter != m_TypeDescOffsets.end())
+ return reinterpret_cast<STypeDesc *>(iter->second);
+ return NULL;
+ }
+ const STypeDesc *RemapTypeDesc(const STypeDesc *inOffset)
+ {
+ size_t memoryOffset = reinterpret_cast<size_t>(inOffset);
+ TOffsetTypeDescMap::iterator iter =
+ m_OffsetsToTypeDescs.find(static_cast<QT3DSU32>(memoryOffset));
+ if (iter != m_OffsetsToTypeDescs.end())
+ return iter->second;
+ return NULL;
+ }
+
+ SElement *RemapElement(SElement *inElem, size_t memoryOffset)
+ {
+ if (inElem)
+ return reinterpret_cast<SElement *>(reinterpret_cast<size_t>(inElem) + memoryOffset);
+ return NULL;
+ }
+};
+}
+
+bool STypeDesc::operator==(const STypeDesc &inOther) const
+{
+ if (m_TypeName == inOther.m_TypeName && m_SubtypeName == inOther.m_SubtypeName
+ && m_Properties.size() == inOther.m_Properties.size()) {
+ for (QT3DSU32 idx = 0, end = m_Properties.size(); idx < end; ++idx) {
+ const SPropertyDesc &lhs = m_Properties[idx];
+ const SPropertyDesc &rhs = inOther.m_Properties[idx];
+ if (lhs.m_Name != rhs.m_Name || lhs.m_Type != rhs.m_Type)
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+Option<QT3DSU32> STypeDesc::FindProperty(QT3DSU32 inNameHash) const
+{
+ for (QT3DSU32 idx = 0, end = m_Properties.size(); idx < end; ++idx) {
+ const SPropertyDesc &theDesc = m_Properties[idx];
+ if (Q3DStudio::CHash::HashAttribute(theDesc.m_Name) == inNameHash)
+ return idx;
+ }
+ return Empty();
+}
+
+Option<QT3DSU32> STypeDesc::FindProperty(CRegisteredString inName) const
+{
+ for (QT3DSU32 idx = 0, end = m_Properties.size(); idx < end; ++idx) {
+ const SPropertyDesc &theDesc = m_Properties[idx];
+ if (theDesc.m_Name == inName)
+ return idx;
+ }
+ return Empty();
+}
+
+Option<QT3DSU32> STypeDesc::FindDynamicProperty(QT3DSU32 inNameHash) const
+{
+ for (QT3DSU32 idx = 0, end = m_DynamicProperties.size(); idx < end; ++idx) {
+ const SPropertyDesc &theDesc = m_DynamicProperties[idx];
+ if (Q3DStudio::CHash::HashAttribute(theDesc.m_Name) == inNameHash)
+ return idx;
+ }
+ return Empty();
+}
+
+void STypeDesc::SetHashValue()
+{
+ size_t typeDescHash = m_TypeName.hash() ^ m_SubtypeName.hash();
+ for (QT3DSU32 idx = 0, end = m_Properties.size(); idx < end; ++idx) {
+ typeDescHash = typeDescHash ^ m_Properties[idx].m_Name.hash();
+ typeDescHash = typeDescHash ^ eastl::hash<QT3DSU32>()(m_Properties[idx].m_Type);
+ }
+ // TODO check 64 bit compatibility
+ m_HashValue = (QT3DSU32)typeDescHash;
+}
+
+QT3DSU32 SPropertyDesc::GetNameHash() const
+{
+ return Q3DStudio::CHash::HashAttribute(m_Name.c_str());
+}
+
+Q3DStudio::SAttributeKey SPropertyDesc::GetAttributeKey() const
+{
+ Q3DStudio::SAttributeKey retval;
+ memZero(&retval, sizeof(retval));
+ retval.m_Type = m_Type;
+ retval.m_Hash = GetNameHash();
+ return retval;
+}
+
+bool SElement::IsUserActive() const
+{
+ return m_Flags.IsExplicitActive();
+}
+
+bool SElement::IsIndependent() const
+{
+ return IsComponent();
+}
+
+bool SElement::IsGlobalActive() const
+{
+ return GetActive();
+}
+// This may require a mutex to be held.
+void SElement::SetGlobalActive(bool active)
+{
+ SetActive(active);
+}
+
+bool SElement::GetAttribute(QT3DSU32 inHashName, Q3DStudio::UVariant &outValue) const
+{
+ if (inHashName == Q3DStudio::ATTRIBUTE_NAME) {
+ outValue.m_StringHandle = m_BelongedPresentation->GetStringTable().GetHandle(m_Name);
+ return true;
+ }
+ if (inHashName == Q3DStudio::CHash::HashAttribute("path")) {
+ outValue.m_StringHandle = m_BelongedPresentation->GetStringTable().GetHandle(m_Path);
+ return true;
+ }
+ const Q3DStudio::UVariant *valPtr = FindPropertyValue(inHashName);
+ if (valPtr) {
+ outValue = *valPtr;
+ return true;
+ }
+ return false;
+}
+
+void SElement::SetAttribute(const Q3DStudio::TAttributeHash inKey,
+ const Q3DStudio::UVariant inValue)
+{
+ Option<TPropertyDescAndValuePtr> existing = FindProperty(inKey);
+ if (existing.hasValue() == false)
+ return;
+ SetAttribute(*existing, inValue);
+}
+
+void SElement::SetAttribute(TPropertyDescAndValuePtr inKey, const Q3DStudio::UVariant inValue)
+{
+ Q3DStudio::EAttributeType theType = inKey.first.m_Type;
+ Q3DStudio::UVariant *currentValue = inKey.second;
+ QT3DSU32 attHash = inKey.first.GetNameHash();
+ switch (theType) {
+ case Q3DStudio::ATTRIBUTETYPE_FLOAT: // Early return
+ if (fabs(currentValue->m_FLOAT - inValue.m_FLOAT) < SmallestDifference())
+ return;
+ break;
+ case Q3DStudio::ATTRIBUTETYPE_FLOAT3: // Early return
+ if (fabs(currentValue->m_FLOAT3[0] - inValue.m_FLOAT3[0]) < SmallestDifference()
+ && fabs(currentValue->m_FLOAT3[1] - inValue.m_FLOAT3[1]) < SmallestDifference()
+ && fabs(currentValue->m_FLOAT3[2] - inValue.m_FLOAT3[2]) < SmallestDifference()) {
+ return;
+ }
+ break;
+ case Q3DStudio::ATTRIBUTETYPE_STRING:
+ if (currentValue->m_StringHandle == inValue.m_StringHandle)
+ return;
+ break;
+ default: // Early return
+ if (currentValue->m_INT32 == inValue.m_INT32)
+ return;
+ break;
+ }
+ *currentValue = inValue;
+
+ if (Q3DStudio::ATTRIBUTE_EYEBALL == attHash)
+ SetFlag(Q3DStudio::ELEMENTFLAG_EXPLICITACTIVE, inValue.m_INT32 ? true : false);
+
+ if (Q3DStudio::ATTRIBUTE_STARTTIME == attHash || Q3DStudio::ATTRIBUTE_ENDTIME == attHash)
+ GetActivityZone().UpdateItemInfo(*this);
+
+ /*
+ if ( inElement->m_Flags & ELEMENTFLAG_REGISTEREDFORATTRIBUTECHANGE )
+ m_AttributeChangeCallbacks.FireCallbacks( inElement, theAttribute->m_Key,
+ thePreviousValue, inValue );
+ */
+ SetDirty();
+}
+
+// SElement implementation
+QT3DSU32 SElement::GetNameHash() const
+{
+ return Q3DStudio::CHash::HashString(m_Name.c_str());
+}
+
+// Q3DStudio::CHash::HashAttribute
+Option<QT3DSU32> SElement::FindPropertyIndex(QT3DSU32 inNameHash) const
+{
+ return m_TypeDescription->FindProperty(inNameHash);
+}
+
+Option<QT3DSU32> SElement::FindPropertyIndex(CRegisteredString inName) const
+{
+ return m_TypeDescription->FindProperty(inName);
+}
+
+Option<QT3DSU32> SElement::FindDynamicPropertyIndex(QT3DSU32 inNameHash) const
+{
+ if (m_DynamicTypeDescription)
+ return m_DynamicTypeDescription->FindDynamicProperty(inNameHash);
+ else
+ return Empty();
+}
+
+QT3DSU32 SElement::GetNumProperties() const
+{
+ return m_TypeDescription->m_Properties.size();
+}
+
+QT3DSU32 SElement::GetNumDynamicProperties() const
+{
+ if (m_DynamicTypeDescription)
+ return m_DynamicTypeDescription->m_DynamicProperties.size();
+ else
+ return 0;
+}
+
+Option<TPropertyDescAndValuePtr> SElement::GetPropertyByIndex(QT3DSU32 inIdx)
+{
+ if (inIdx < this->m_TypeDescription->m_Properties.size())
+ return eastl::make_pair(m_TypeDescription->m_Properties[inIdx],
+ &TElementPropertyList::GetObjAtIdx(m_PropertyValues, inIdx));
+
+ return Empty();
+}
+
+Option<TPropertyDescAndValuePtr> SElement::GetDynamicPropertyByIndex(QT3DSU32 inIdx)
+{
+ if (inIdx < this->m_DynamicTypeDescription->m_DynamicProperties.size())
+ return eastl::make_pair(m_DynamicTypeDescription->m_DynamicProperties[inIdx],
+ &TElementPropertyList::GetObjAtIdx(m_DynamicPropertyValues, inIdx));
+
+ return Empty();
+}
+
+Option<TPropertyDescAndConstValuePtr> SElement::GetPropertyByIndex(QT3DSU32 inIdx) const
+{
+ Option<TPropertyDescAndValuePtr> retval =
+ const_cast<SElement &>(*this).GetPropertyByIndex(inIdx);
+ if (retval.hasValue())
+ return TPropertyDescAndConstValuePtr(*retval);
+ return Empty();
+}
+
+void SElement::SetDirty()
+{
+ if (!m_Flags.IsDirty()) {
+ m_Flags.SetDirty(true);
+ // The element is dirty, so place it on the frame data dirty list
+ m_BelongedPresentation->GetFrameData().GetDirtyList().Push(this);
+ m_Flags.SetDirty(true);
+ }
+}
+
+void SElement::SetFlag(Q3DStudio::EElementFlag inFlag, bool inValue)
+{
+ bool existing = m_Flags & inFlag;
+ if (existing != inValue && HasActivityZone()) {
+ m_Flags.clearOrSet(inValue, inFlag);
+ if (inFlag == Q3DStudio::ELEMENTFLAG_EXPLICITACTIVE)
+ GetActivityZone().UpdateItemInfo(*this);
+ else if (inFlag == Q3DStudio::ELEMENTFLAG_SCRIPTCALLBACKS)
+ GetActivityZone().UpdateItemScriptStatus(*this);
+ SetDirty();
+ }
+}
+
+bool SElement::HasActivityZone() const
+{
+ return m_BelongedPresentation->GetActivityZone() != NULL;
+}
+
+qt3ds::runtime::IActivityZone &SElement::GetActivityZone() const
+{
+ return *m_BelongedPresentation->GetActivityZone();
+}
+
+SElement &SElement::GetComponentParent()
+{
+ if (IsComponent())
+ return *this;
+
+ SElement *theParent = GetActivityZone().GetItemTimeParent(*this);
+ if (theParent)
+ return *theParent;
+ QT3DS_ASSERT(false);
+ return *this;
+}
+
+const SElement &SElement::GetComponentParent() const
+{
+ return const_cast<SElement *>(this)->GetComponentParent();
+}
+
+SElement *SElement::FindChild(QT3DSU32 inNameHash)
+{
+ for (SElement *theChild = m_Child; theChild; theChild = theChild->m_Sibling) {
+ if (theChild->GetNameHash() == inNameHash)
+ return theChild;
+ }
+ return NULL;
+}
+
+Q3DStudio::TTimeUnit SElement::GetInnerTime() const
+{
+ return GetActivityZone().GetItemComponentTime(const_cast<SElement &>(*this));
+}
+// Get the time I am animating at.
+Q3DStudio::TTimeUnit SElement::GetOuterTime() const
+{
+ return GetActivityZone().GetItemLocalTime(const_cast<SElement &>(*this));
+}
+
+bool SElement::IsDescendent(SElement &inPossibleParent) const
+{
+ if (m_Parent == NULL)
+ return false;
+ if (m_Parent == &inPossibleParent)
+ return true;
+ return m_Parent->IsDescendent(inPossibleParent);
+}
+
+bool SComponent::GetPaused() const
+{
+ return GetTimePolicy().GetPaused();
+}
+
+bool SComponent::GetPlayBackDirection() const
+{
+ return GetTimePolicy().GetPlayBackDirection();
+}
+
+Q3DStudio::CTimePolicy &SComponent::GetTimePolicy()
+{
+ return *GetActivityZone().GetOwnedTimePolicy(*this);
+}
+
+const Q3DStudio::CTimePolicy &SComponent::GetTimePolicy() const
+{
+ return const_cast<SComponent *>(this)->GetTimePolicy();
+}
+
+qt3ds::runtime::IElementAllocator &
+qt3ds::runtime::IElementAllocator::CreateElementAllocator(NVFoundationBase &inFoundation,
+ IStringTable &inStringTable)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(), SElementAllocator)(inFoundation, inStringTable);
+}
diff --git a/src/runtime/Qt3DSElementSystem.h b/src/runtime/Qt3DSElementSystem.h
new file mode 100644
index 0000000..12ebebd
--- /dev/null
+++ b/src/runtime/Qt3DSElementSystem.h
@@ -0,0 +1,616 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+#ifndef QT3DS_ELEMENT_SYSTEM_H
+#define QT3DS_ELEMENT_SYSTEM_H
+#include "foundation/Qt3DSRefCounted.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSAllocator.h"
+#include "foundation/Utils.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSKernelTypes.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/Qt3DSFlags.h"
+#include "foundation/Qt3DSOption.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSInvasiveSet.h"
+#include "Qt3DSMetadata.h"
+
+// The qt3ds runtime element system contains the element graph and the data in the element
+// graph.
+
+namespace Q3DStudio {
+// class CPresentation;
+class IPresentation;
+class CTimePolicy;
+}
+
+namespace qt3ds {
+namespace foundation {
+ class IInStream;
+ class IOutStream;
+}
+}
+
+namespace qt3ds {
+namespace runtime {
+ using namespace qt3ds::foundation;
+ using namespace qt3ds;
+ class IActivityZone;
+ namespace element {
+ struct SPropertyDesc
+ {
+ CRegisteredString m_Name;
+ Q3DStudio::EAttributeType m_Type;
+ SPropertyDesc()
+ : m_Type(Q3DStudio::ATTRIBUTETYPE_NONE)
+ {
+ }
+ SPropertyDesc(CRegisteredString inStr, Q3DStudio::EAttributeType inType)
+ : m_Name(inStr)
+ , m_Type(inType)
+ {
+ }
+ QT3DSU32 GetNameHash() const; // CHash::HashAttribute
+ Q3DStudio::SAttributeKey GetAttributeKey() const;
+ };
+
+ struct STypeDesc
+ {
+ CRegisteredString m_TypeName;
+ CRegisteredString m_SubtypeName;
+ // Properties are sorted so that we can quickly create new type descriptions
+ // when necessary.
+ NVConstDataRef<SPropertyDesc> m_Properties;
+ eastl::vector<SPropertyDesc> m_DynamicProperties;
+
+ private:
+ QT3DSU32 m_HashValue; // Cached hash value for this type descriptor
+ public:
+ STypeDesc()
+ : m_HashValue(0)
+ {
+ }
+ bool operator==(const STypeDesc &inOther) const;
+ // Q3DStudio::CHash::HashAttribute
+ Option<QT3DSU32> FindProperty(QT3DSU32 inNameHash) const;
+ Option<QT3DSU32> FindProperty(CRegisteredString inName) const;
+ Option<QT3DSU32> FindDynamicProperty(QT3DSU32 inNameHash) const;
+ void SetHashValue();
+ QT3DSU32 HashCode() const { return m_HashValue; }
+ };
+
+ struct SActivationManagerNodeFlagValues
+ {
+ enum Enum {
+ TimeActive = 1 << 1,
+ TempTimeActive = 1 << 4,
+ ParticipatesInTimeGraph = 1 << 5,
+ Script = 1 << 6,
+ ChildDirty = 1 << 7,
+ };
+ };
+
+ struct SActivationManagerNodeFlags : public NVFlags<SActivationManagerNodeFlagValues::Enum>
+ {
+ bool IsTimeActive() const
+ {
+ return operator&(SActivationManagerNodeFlagValues::TimeActive);
+ }
+ void SetTimeActive(bool active)
+ {
+ clearOrSet(active, SActivationManagerNodeFlagValues::TimeActive);
+ }
+
+ bool IsTempTimeActive() const
+ {
+ return operator&(SActivationManagerNodeFlagValues::TempTimeActive);
+ }
+ void SetTempTimeActive(bool value)
+ {
+ clearOrSet(value, SActivationManagerNodeFlagValues::TempTimeActive);
+ }
+
+ // Is this item in the activation manager's scripts list.
+ bool HasScript() const { return operator&(SActivationManagerNodeFlagValues::Script); }
+ void SetHasScript(bool value)
+ {
+ clearOrSet(value, SActivationManagerNodeFlagValues::Script);
+ }
+
+ bool IsChildDirty() const
+ {
+ return operator&(SActivationManagerNodeFlagValues::ChildDirty);
+ }
+ void SetChildDirty() { clearOrSet(true, SActivationManagerNodeFlagValues::ChildDirty); }
+ void ClearChildDirty()
+ {
+ clearOrSet(false, SActivationManagerNodeFlagValues::ChildDirty);
+ }
+ };
+
+ struct SElement;
+
+ struct SActivationManagerNode
+ {
+ // IT may seem wasteful to always have start,end time on the node. We need to do this
+ // however
+ // in order to transmit information down the timeline at times. Furthermore the
+ // activation manager
+ // is free to mangle these times as it sees fit; unlike the attribute start,end time.
+ QT3DSU32 m_StartTime;
+ QT3DSU32 m_StopTime;
+ // Used by the activation manager specifically.
+ QT3DSU32 m_DirtyIndex;
+
+ // If m_Flags.IsIndependent(), first child is implicitly zero and instead
+ // this records the time context index assocated with this element node.
+ // Else this records the first child index.
+ // These could perhaps be moved to the element flags except it breaks some isolation and
+ // it just doesn't seem worth it.
+ SActivationManagerNodeFlags m_Flags;
+
+ SActivationManagerNode()
+ : m_StartTime(QT3DS_MAX_U32)
+ , m_StopTime(QT3DS_MAX_U32)
+ , m_DirtyIndex(QT3DS_MAX_U32)
+ {
+ }
+ };
+
+ struct SElementFlag : public NVFlags<Q3DStudio::EElementFlag, QT3DSU16>
+ {
+ void SetDirty(bool inDirty) { clearOrSet(inDirty, Q3DStudio::ELEMENTFLAG_DIRTY); }
+ bool IsDirty() const { return this->operator&(Q3DStudio::ELEMENTFLAG_DIRTY); }
+
+ bool IsComponent() const { return this->operator&(Q3DStudio::ELEMENTFLAG_COMPONENT); }
+
+ void SetExplicitActive(bool inValue)
+ {
+ clearOrSet(inValue, Q3DStudio::ELEMENTFLAG_EXPLICITACTIVE);
+ }
+ bool IsExplicitActive() const
+ {
+ return this->operator&(Q3DStudio::ELEMENTFLAG_EXPLICITACTIVE);
+ }
+
+ void SetActive(bool inValue)
+ {
+ clearOrSet(inValue, Q3DStudio::ELEMENTFLAG_GLOBALACTIVE);
+ }
+ bool IsActive() const { return this->operator&(Q3DStudio::ELEMENTFLAG_GLOBALACTIVE); }
+
+ void SetPickEnabled(bool inValue)
+ {
+ clearOrSet(inValue, Q3DStudio::ELEMENTFLAG_PICKENABLED);
+ }
+ bool IsPickEnabled() const
+ {
+ return this->operator&(Q3DStudio::ELEMENTFLAG_PICKENABLED);
+ }
+
+ void SetPlayThrough(bool inValue)
+ {
+ clearOrSet(inValue, Q3DStudio::ELEMENTFLAG_PLAYTHROUGH);
+ }
+ bool IsPlayThrough() const
+ {
+ return this->operator&(Q3DStudio::ELEMENTFLAG_PLAYTHROUGH);
+ }
+
+ void SetScriptCallbacks(bool inValue)
+ {
+ clearOrSet(inValue, Q3DStudio::ELEMENTFLAG_SCRIPTCALLBACKS);
+ }
+ bool HasScriptCallbacks() const
+ {
+ return this->operator&(Q3DStudio::ELEMENTFLAG_SCRIPTCALLBACKS);
+ }
+
+ bool HasStartEndTime() const
+ {
+ return this->operator&(Q3DStudio::ELEMENTFLAG_TIMELINE);
+ }
+ };
+
+ typedef eastl::pair<SPropertyDesc, Q3DStudio::UVariant *> TPropertyDescAndValuePtr;
+ typedef eastl::pair<SPropertyDesc, const Q3DStudio::UVariant *>
+ TPropertyDescAndConstValuePtr;
+ typedef eastl::pair<SPropertyDesc, Q3DStudio::UVariant> TPropertyDescAndValue;
+ // There is probably some balance here between number of values in a group
+ // and number of groups. A heuristic of a large application would tell you
+ // the optimum number of values to groups.
+ struct SPropertyValueGroup
+ {
+ enum {
+ NumValues = 4,
+ };
+ Q3DStudio::UVariant m_Data[4];
+ SPropertyValueGroup *m_NextNode;
+ SPropertyValueGroup()
+ : m_NextNode(NULL)
+ {
+ for (QT3DSU32 idx = 0; idx < NumValues; ++idx)
+ m_Data[idx].m_INT32 = 0;
+ }
+ };
+
+ struct SElement
+ {
+ CRegisteredString m_Name; // const, do not set after creation.
+ CRegisteredString m_Path;
+ const STypeDesc *m_TypeDescription; ///< static information created on load time
+ // The property values are in order described in the type description.
+ SPropertyValueGroup *m_PropertyValues;
+ // dynamic section
+ STypeDesc *m_DynamicTypeDescription; ///< we may create this on the fly
+ SPropertyValueGroup *m_DynamicPropertyValues;
+
+ protected:
+ SElementFlag m_Flags; // Setting these changes things.
+ public:
+ QT3DSU32 m_Handle;
+ QT3DSU32 m_ScriptID; ///< Superfluous, could use handle to link to script representation
+ QT3DSU32 m_Depth; ///< Distance from this node to the root of the graph.
+ SElement *m_Parent; ///< Parent element in activity graph
+ SElement *m_Sibling; ///< Next sibling element in activity graph
+ SElement *m_Child; ///< First child element in activity graph
+ void *m_Association; ///< Link to associated asset in scene
+ Q3DStudio::IPresentation *m_BelongedPresentation;
+ SActivationManagerNode m_ActivationManagerNode;
+
+ SElement(const STypeDesc &inDesc)
+ : m_TypeDescription(&inDesc)
+ , m_PropertyValues(NULL)
+ , m_DynamicTypeDescription(NULL)
+ , m_DynamicPropertyValues(NULL)
+ , m_Handle(0)
+ , m_ScriptID(0)
+ , m_Depth(0)
+ , m_Parent(NULL)
+ , m_Sibling(NULL)
+ , m_Child(NULL)
+ , m_Association(NULL)
+ , m_BelongedPresentation(NULL)
+ {
+ }
+ QT3DSU32 GetHandle() const { return m_Handle; }
+ const STypeDesc &GetTypeDescription() const { return *m_TypeDescription; }
+ void SetTypeDescription(const STypeDesc *inDesc) { m_TypeDescription = inDesc; }
+ // Q3DStudio::CHash::HashString
+ QT3DSU32 GetNameHash() const;
+ const SPropertyValueGroup *GetFirstPropertyGroup() const { return m_PropertyValues; }
+ void SetFirstPropertyGroup(SPropertyValueGroup *inGroup) { m_PropertyValues = inGroup; }
+
+ SPropertyValueGroup *&UnsafeGetFirstPropertyGroup() { return m_PropertyValues; }
+
+ // In general, use these accessors for attributes, especially setting values.
+ // The other accessors are fast paths that do not:
+ // 1. Set dirty flags
+ // 2. Notify the activation zone of changes if setting start,end time.
+ // 3. Notify any other subsystems.
+ bool GetAttribute(QT3DSU32 inHashName, Q3DStudio::UVariant &outValue) const;
+ // Triggers updating various subcomponents based on attribute key.
+ void SetAttribute(const Q3DStudio::TAttributeHash inKey,
+ const Q3DStudio::UVariant inValue);
+ // Triggers updating various subcomponents without requiring a new property lookup.
+ void SetAttribute(TPropertyDescAndValuePtr inKey, const Q3DStudio::UVariant inNewValue);
+
+ // Q3DStudio::CHash::HashAttribute
+ Option<QT3DSU32> FindPropertyIndex(QT3DSU32 inNameHash) const;
+ Option<QT3DSU32> FindPropertyIndex(CRegisteredString inName) const;
+
+ Option<QT3DSU32> FindDynamicPropertyIndex(QT3DSU32 inNameHash) const;
+
+ QT3DSU32 GetNumProperties() const;
+ QT3DSU32 GetAttributeCount() const { return GetNumProperties(); }
+
+ QT3DSU32 GetNumDynamicProperties() const;
+ QT3DSU32 GetDynamicAttributeCount() const { return GetNumDynamicProperties(); }
+
+ // Note that if you set a value then this object is dirty.
+ // Bypasses special checks in setAttribute. In general, use setAttribute.
+ Option<TPropertyDescAndValuePtr> GetPropertyByIndex(QT3DSU32 inIdx);
+ Option<TPropertyDescAndConstValuePtr> GetPropertyByIndex(QT3DSU32 inIdx) const;
+
+ Option<TPropertyDescAndValuePtr> GetDynamicPropertyByIndex(QT3DSU32 inIdx);
+
+ Q3DStudio::UVariant *FindPropertyValue(QT3DSU32 inNameHash)
+ {
+ Option<QT3DSU32> idx = FindPropertyIndex(inNameHash);
+ if (idx.hasValue()) {
+ Option<TPropertyDescAndValuePtr> theVal = GetPropertyByIndex(idx);
+ if (theVal.hasValue())
+ return theVal->second;
+ }
+ return NULL;
+ }
+ const Q3DStudio::UVariant *FindPropertyValue(QT3DSU32 inNameHash) const
+ {
+ Option<QT3DSU32> idx = FindPropertyIndex(inNameHash);
+ if (idx.hasValue()) {
+ Option<TPropertyDescAndConstValuePtr> theVal = GetPropertyByIndex(idx);
+ if (theVal.hasValue())
+ return theVal->second;
+ }
+ return NULL;
+ }
+ Q3DStudio::UVariant *FindPropertyValue(CRegisteredString inNameHash)
+ {
+ Option<QT3DSU32> idx = FindPropertyIndex(inNameHash);
+ if (idx.hasValue()) {
+ Option<TPropertyDescAndValuePtr> theVal = GetPropertyByIndex(idx);
+ if (theVal.hasValue())
+ return theVal->second;
+ }
+ return NULL;
+ }
+
+ const Q3DStudio::UVariant *FindPropertyValue(CRegisteredString inNameHash) const
+ {
+ Option<QT3DSU32> idx = FindPropertyIndex(inNameHash);
+ if (idx.hasValue()) {
+ Option<TPropertyDescAndConstValuePtr> theVal = GetPropertyByIndex(idx);
+ if (theVal.hasValue())
+ return theVal->second;
+ }
+ return NULL;
+ }
+
+ Option<TPropertyDescAndValuePtr> FindProperty(QT3DSU32 inNameHash)
+ {
+ Option<QT3DSU32> idx = FindPropertyIndex(inNameHash);
+ if (idx.hasValue())
+ return GetPropertyByIndex(*idx);
+ return Empty();
+ }
+
+ Option<TPropertyDescAndValuePtr> FindDynamicProperty(QT3DSU32 inNameHash)
+ {
+ Option<QT3DSU32> idx = FindDynamicPropertyIndex(inNameHash);
+ if (idx.hasValue())
+ return GetDynamicPropertyByIndex(*idx);
+
+ return Empty();
+ }
+
+ SElement *GetParent() { return m_Parent; }
+ const SElement *GetParent() const { return m_Parent; }
+ SElement *GetSibling() { return m_Sibling; }
+ const SElement *GetSibling() const { return m_Sibling; }
+ SElement *GetChild() { return m_Child; }
+ const SElement *GetChild() const { return m_Child; }
+ CRegisteredString GetType() const { return m_TypeDescription->m_TypeName; }
+ bool IsComponent() const { return m_Flags.IsComponent(); }
+
+ Q3DStudio::IPresentation *GetBelongedPresentation() { return m_BelongedPresentation; }
+ void SetBelongedPresentation(Q3DStudio::IPresentation &inPresentation)
+ {
+ m_BelongedPresentation = &inPresentation;
+ }
+
+ // Set flags bypassing the dirty system *and* updates to the activity zone.
+ // do not do this unless you are sure you do not want any side effects.
+ SElementFlag &Flags() { return m_Flags; }
+
+ // User flag that is set and is persistent
+ void SetExplicitActive(bool inValue)
+ {
+ SetFlag(Q3DStudio::ELEMENTFLAG_EXPLICITACTIVE, inValue);
+ }
+ bool IsExplicitActive() const { return m_Flags.IsExplicitActive(); }
+
+ // Flag set by the activity manager.
+ void SetActive(bool inValue) { SetFlag(Q3DStudio::ELEMENTFLAG_GLOBALACTIVE, inValue); }
+ bool GetActive() const { return m_Flags.IsActive(); }
+
+ void SetPickEnabled(bool inValue)
+ {
+ SetFlag(Q3DStudio::ELEMENTFLAG_PICKENABLED, inValue);
+ }
+ bool IsPickEnabled() const { return m_Flags.IsPickEnabled(); }
+
+ void SetPlayThrough(bool inValue)
+ {
+ SetFlag(Q3DStudio::ELEMENTFLAG_PLAYTHROUGH, inValue);
+ }
+ bool IsPlayThrough() const { return m_Flags.IsPlayThrough(); }
+
+ void SetScriptCallbacks(bool inValue)
+ {
+ SetFlag(Q3DStudio::ELEMENTFLAG_SCRIPTCALLBACKS, inValue);
+ }
+ bool HasScriptCallbacks() const { return m_Flags.HasScriptCallbacks(); }
+
+ bool IsDirty() const { return m_Flags.IsDirty(); }
+
+ bool GetFlag(Q3DStudio::EElementFlag inFlag) const { return m_Flags.operator&(inFlag); }
+
+ void SetDirty();
+ void SetFlag(Q3DStudio::EElementFlag inFlag, bool inValue);
+ void SetFlag(Q3DStudio::EElementFlag inFlag, int inValue)
+ {
+ SetFlag(inFlag, inValue ? true : false);
+ }
+ qt3ds::runtime::IActivityZone &GetActivityZone() const;
+ bool HasActivityZone() const;
+ // Floating point number differences above this trigger a setDirty call.
+ static float SmallestDifference() { return 0.000001f; }
+ SElement &GetComponentParent();
+ const SElement &GetComponentParent() const;
+ SElement *FindChild(QT3DSU32 inNameHash);
+ // Get the time of my first noncomponent child or
+ // my time if I am not a component.
+ Q3DStudio::TTimeUnit GetInnerTime() const;
+ // Get the time I am animating at.
+ Q3DStudio::TTimeUnit GetOuterTime() const;
+ bool IsDescendent(SElement &inPossibleParent) const;
+ void *GetAssociation() const { return m_Association; }
+ void SetAssociation(void *inAssoc) { m_Association = inAssoc; }
+ QT3DSU32 GetUncachedDepth() const
+ {
+ if (m_Parent)
+ return m_Parent->GetUncachedDepth() + 1;
+ return 0;
+ }
+ void SetDepth() { m_Depth = GetUncachedDepth(); }
+ QT3DSU32 Depth() const { return m_Depth; }
+
+ bool IsUserActive() const;
+
+ bool IsIndependent() const;
+
+ bool IsGlobalActive() const;
+ // This may require a mutex to be held.
+ void SetGlobalActive(bool active);
+
+ bool IsTimeActive() const
+ {
+ if (m_Parent != NULL)
+ return m_ActivationManagerNode.m_Flags.IsTimeActive();
+ return true;
+ }
+
+ bool DoesParticipateInTimeGraph() const { return m_Flags.HasStartEndTime(); }
+
+ bool IsGlobalActive(bool inParentActive) const
+ {
+ bool ta = IsTimeActive();
+ bool ua = IsUserActive();
+
+ return ta && ua && inParentActive;
+ }
+ void findComponents(QVector<SElement *> &components)
+ {
+ if (IsComponent())
+ components.push_back(this);
+ SElement *child = m_Child;
+ while (child) {
+ child->findComponents(components);
+ child = child->m_Sibling;
+ }
+ }
+ };
+
+ struct SGetElementNodeDirtyIndex
+ {
+ QT3DSU32 operator()(const SElement &inNode)
+ {
+ return inNode.m_ActivationManagerNode.m_DirtyIndex;
+ }
+ };
+ struct SSetElementNodeDirtyIndex
+ {
+ void operator()(SElement &inNode, QT3DSU32 val)
+ {
+ inNode.m_ActivationManagerNode.m_DirtyIndex = val;
+ }
+ };
+
+ struct SActivationManagerNodeDirtyList
+ : public InvasiveSet<SElement, SGetElementNodeDirtyIndex, SSetElementNodeDirtyIndex>
+ {
+ typedef InvasiveSet<SElement, SGetElementNodeDirtyIndex, SSetElementNodeDirtyIndex>
+ TBaseType;
+ SActivationManagerNodeDirtyList(NVAllocatorCallback &callback)
+ : TBaseType(callback, "SActivationManagerNodeDirtyList")
+ {
+ }
+ };
+
+ struct SComponent : public SElement
+ {
+ Q3DStudio::SAlignedTimeUnit m_BeginTime;
+ Q3DStudio::SAlignedTimeUnit m_Duration;
+
+ // Slide related
+ QT3DSU8 m_SlideCount; ///< Number of slides starting from base index
+ QT3DSU8 m_CurrentSlide; ///< Current slide number
+ QT3DSU8 m_PreviousSlide; ///< Previous slide number
+ SComponent(const STypeDesc &inDesc)
+ : SElement(inDesc)
+ , m_SlideCount(0)
+ , m_CurrentSlide(0)
+ , m_PreviousSlide(0)
+ {
+ }
+
+ QT3DSU8 GetCurrentSlide() const { return m_CurrentSlide; }
+ void SetCurrentSlide(QT3DSU8 inSlide)
+ {
+ m_PreviousSlide = m_CurrentSlide;
+ m_CurrentSlide = inSlide;
+ }
+ QT3DSU8 GetSlideCount() const { return m_SlideCount; }
+ QT3DSU8 GetPreviousSlide() const { return m_PreviousSlide; }
+ bool GetPaused() const;
+ bool GetPlayBackDirection() const;
+ Q3DStudio::CTimePolicy &GetTimePolicy();
+ const Q3DStudio::CTimePolicy &GetTimePolicy() const;
+ };
+ }
+ // Global store of all elements, values.
+ class IElementAllocator : public NVRefCounted
+ {
+ public:
+ // Performs coalesing of element types so that there are the least number of type
+ // descriptions
+ // Certain properties, like name, eyeball, are never in the property description.
+ virtual element::SElement &
+ CreateElement(CRegisteredString inName, CRegisteredString inType,
+ CRegisteredString inSubType,
+ NVConstDataRef<element::TPropertyDescAndValue> inPropertyDescriptions,
+ Q3DStudio::IPresentation *inPresentation, element::SElement *inParent,
+ bool inIsComponent) = 0;
+
+ virtual void ReleaseElement(element::SElement &inElement, bool inRecurse) = 0;
+
+ virtual Option<element::TPropertyDescAndValuePtr>
+ CreateDynamicProperty(Q3DStudio::IRuntimeMetaData &theMetaData, element::SElement &element,
+ CRegisteredString inName) = 0;
+
+ virtual element::SElement *FindElementByHandle(QT3DSU32 inElementHandle) = 0;
+ // Returns an element pointer that when added to the return value of load will be a valid
+ // element.
+ virtual element::SElement *
+ GetRemappedElementAddress(element::SElement *inElement) const = 0;
+ virtual const element::STypeDesc *
+ GetRemappedTypeDescAddress(const element::STypeDesc *inTypeDesc) const = 0;
+ // If load is successful, then returns an address that can be added to elements and the root
+ // element.
+
+ static IElementAllocator &CreateElementAllocator(NVFoundationBase &inFoundation,
+ IStringTable &inStringTable);
+ };
+}
+}
+
+#endif
diff --git a/src/runtime/Qt3DSEvent.h b/src/runtime/Qt3DSEvent.h
new file mode 100644
index 0000000..aef317a
--- /dev/null
+++ b/src/runtime/Qt3DSEvent.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSElementSystem.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+/// Common structure used as both Events and Commands
+struct SEventCommand
+{
+ TElement *m_Target; ///< The target of this action
+ TEventCommandHash m_Type; ///< Type of action to perform
+ UVariant m_Arg1; ///< Argument 1
+ UVariant m_Arg2; ///< Argument 2
+ UINT8 m_Arg1Type; ///< EAttributeType for arg1 variant
+ UINT8 m_Arg2Type; ///< EAttributeType for arg2 variant
+
+ BOOL m_IsEvent : 1; ///< This is an Event or Command
+ BOOL m_BubbleUp : 1; ///< Bubble up to scene parent (2.4.2: theEvent:stopPropagation)
+ BOOL m_BubbleDown : 1; ///< Bubble down to scene children (2.4.2: theEvent:stopPropagation)
+ BOOL m_Done : 1; ///< Stop further handling (2.4.3: theEvent:stopImmediatePropagation)
+
+ UINT8 m_Unused; ///< (padding)
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSEventCallbacks.cpp b/src/runtime/Qt3DSEventCallbacks.cpp
new file mode 100644
index 0000000..25d720e
--- /dev/null
+++ b/src/runtime/Qt3DSEventCallbacks.cpp
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSEventCallbacks.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CEventCallbacks::CEventCallbacks()
+ : m_EventCallbacksList(0, 0, "EventCallbacksList")
+ , m_CallbackList(0, 0, "CallbackList")
+{
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CEventCallbacks::~CEventCallbacks()
+{
+ UnregisterAllCallbacks();
+}
+
+//==============================================================================
+/**
+ * Registers a callback to be triggered when a specific event is fired on an
+ * element.
+ * @param inElement element to monitor
+ * @param inEventHash event hash to monitor
+ * @param inCallback static callback function
+ * @param inContextData arbitary data pointer
+ */
+void CEventCallbacks::RegisterCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData)
+{
+ TEventCallbacksList *theEventCallbackList = NULL;
+
+ // Look for the list of callbacks for this element
+ FOR_ARRAY(SElementCallbackEntry *, theEntry, m_EventCallbacksList)
+ {
+ if (inElement == (*theEntry)->m_Element) {
+ theEventCallbackList = &(*theEntry)->m_EventEntries;
+ break;
+ }
+ }
+
+ // Create a new list of callbacks if it does not already exist
+ if (!theEventCallbackList) {
+ SElementCallbackEntry *theElementCallbackEntry =
+ Q3DStudio_new(SElementCallbackEntry) SElementCallbackEntry();
+ theElementCallbackEntry->m_Element = inElement;
+ m_EventCallbacksList.Push(theElementCallbackEntry);
+ theEventCallbackList = &m_EventCallbacksList.Top()->m_EventEntries;
+ }
+
+ TCallbackList *theElementCallbackList = NULL;
+
+ // Look for the list of callbacks for this event
+ FOR_ARRAY(SEventCallbackEntry *, theEntry, (*theEventCallbackList))
+ {
+ if (inEventHash == (*theEntry)->m_EventHash) {
+ theElementCallbackList = &(*theEntry)->m_Callbacks;
+ break;
+ }
+ }
+
+ // Create a new list of callbacks if it does not already exist
+ if (!theElementCallbackList) {
+ SEventCallbackEntry *theEventCallbackEntry =
+ Q3DStudio_new(SEventCallbackEntry) SEventCallbackEntry();
+ theEventCallbackEntry->m_EventHash = inEventHash;
+ theEventCallbackList->Push(theEventCallbackEntry);
+ theElementCallbackList = &theEventCallbackList->Top()->m_Callbacks;
+ }
+
+ // Insert callback
+ SCallbackEntry theCallbackEntry = { inCallback, inContextData };
+ theElementCallbackList->Push(theCallbackEntry);
+}
+
+//==============================================================================
+/**
+ * Unregisters an event callback.
+ * @param inElement element to monitor
+ * @param inEventHash event hash to monitor
+ * @param inCallback static callback function
+ * @param inContextData arbitary data pointer
+ * @param outLast indicates if element no longer has callbacks registered
+ * @return BOOL true if callback is unregistered successfully
+ */
+BOOL CEventCallbacks::UnregisterCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData,
+ BOOL &outLast)
+{
+ outLast = false;
+
+ TEventCallbacksList *theEventCallbackList = NULL;
+
+ INT32 theEventListIndex = 0;
+ SElementCallbackEntry *theFoundEntry = NULL;
+ FOR_ARRAY(SElementCallbackEntry *, theEntry, m_EventCallbacksList)
+ {
+ if (inElement == (*theEntry)->m_Element) {
+ theFoundEntry = *theEntry;
+ theEventCallbackList = &(*theEntry)->m_EventEntries;
+ break;
+ }
+ ++theEventListIndex;
+ }
+
+ if (!theEventCallbackList)
+ return false;
+
+ TCallbackList *theElementCallbackList = NULL;
+
+ INT32 theElementListIndex = 0;
+ SEventCallbackEntry *theEventCallbackEntry = NULL;
+ FOR_ARRAY(SEventCallbackEntry *, theEntry, (*theEventCallbackList))
+ {
+ if (inEventHash == (*theEntry)->m_EventHash) {
+ theEventCallbackEntry = *theEntry;
+ theElementCallbackList = &(*theEntry)->m_Callbacks;
+ break;
+ }
+ ++theElementListIndex;
+ }
+
+ if (!theElementCallbackList)
+ return false;
+
+ INT32 theCallbackIndex = 0;
+ FOR_ARRAY(SCallbackEntry, theEntry, (*theElementCallbackList))
+ {
+ if (inCallback == theEntry->m_Function && inContextData == theEntry->m_ContextData) {
+ theElementCallbackList->Remove(theCallbackIndex);
+
+ if (theElementCallbackList->GetCount() == 0) {
+ Q3DStudio_delete(theEventCallbackEntry, SEventCallbackEntry);
+ theEventCallbackList->Remove(theElementListIndex);
+ }
+
+ if (theEventCallbackList->GetCount() == 0) {
+ Q3DStudio_delete(theFoundEntry, SElementCallbackEntry);
+ m_EventCallbacksList.Remove(theEventListIndex);
+ outLast = true;
+ }
+
+ return true;
+ }
+ ++theCallbackIndex;
+ }
+
+ return false;
+}
+
+//==============================================================================
+/**
+ * Unregisters all callbacks.
+ */
+void CEventCallbacks::UnregisterAllCallbacks()
+{
+ FOR_ARRAY(SElementCallbackEntry *, theEntry, m_EventCallbacksList)
+ Q3DStudio_delete(*theEntry, SElementCallbackEntry);
+ m_EventCallbacksList.Clear();
+}
+
+//==============================================================================
+/**
+ * Fires event callbacks
+ * @param ioEvent the event that was fired
+ */
+void CEventCallbacks::FireCallbacks(SEventCommand &ioEvent)
+{
+ TEventCallbacksList *theEventCallbackList = NULL;
+
+ // Look for the list of callbacks for this element
+ FOR_ARRAY(SElementCallbackEntry *, theEntry, m_EventCallbacksList)
+ {
+ if (ioEvent.m_Target == (*theEntry)->m_Element) {
+ theEventCallbackList = &(*theEntry)->m_EventEntries;
+ break;
+ }
+ }
+
+ if (!theEventCallbackList)
+ return;
+
+ // Look for the list of callbacks for this event
+ FOR_ARRAY(SEventCallbackEntry *, theEntry, (*theEventCallbackList))
+ {
+ if (ioEvent.m_Type == (*theEntry)->m_EventHash) {
+ PerformCallbacks((*theEntry)->m_Callbacks, ioEvent);
+ break;
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Executing a list of callbacks
+ * @param inCallbackList the list of callbacks to invoke
+ * @param ioEvent the event to pass to the callbacks
+ */
+void CEventCallbacks::PerformCallbacks(TCallbackList &inCallbackList, SEventCommand &ioEvent)
+{
+ // As the callbacks can potentially do a register/unregister that would alter the call list,
+ // it is safer to get a clean copy of the call list to iterate through.
+
+ // Clumsy way to do a m_CallbackList = inCallbackList
+ m_CallbackList.Clear(false);
+ m_CallbackList.Reserve(inCallbackList.GetCount());
+ FOR_ARRAY(SCallbackEntry, theEntry, inCallbackList)
+ m_CallbackList.Push(*theEntry);
+
+ // Invoking the callbacks
+ FOR_ARRAY(SCallbackEntry, theEntry, m_CallbackList)
+ theEntry->m_Function(theEntry->m_ContextData, ioEvent);
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSEventCallbacks.h b/src/runtime/Qt3DSEventCallbacks.h
new file mode 100644
index 0000000..5c17477
--- /dev/null
+++ b/src/runtime/Qt3DSEventCallbacks.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "Qt3DSEvent.h"
+#include "Qt3DSMemory.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Typedefs
+//==============================================================================
+typedef void (*TEventCallback)(void *inContextData, SEventCommand &ioEvent);
+
+//==============================================================================
+/**
+ * Manages the various events callbacks in the system. It fires the
+ * registered callbacks when an event was triggered.
+ */
+class CEventCallbacks
+{
+ //==============================================================================
+ // Structs
+ //==============================================================================
+protected:
+ struct SCallbackEntry
+ {
+ TEventCallback m_Function; ///< Callback function pointer
+ void *m_ContextData; ///< User data
+ };
+ typedef CArray<SCallbackEntry> TCallbackList; ///< Array of callbacks regisgtered
+
+ struct SEventCallbackEntry
+ {
+ SEventCallbackEntry()
+ : m_EventHash(0)
+ {
+ }
+ ~SEventCallbackEntry() {}
+
+ TEventCommandHash m_EventHash; ///< The event of interest
+ TCallbackList m_Callbacks; ///< List of callbacks listening to this event
+
+ private: // Disabled Copy Construction
+ SEventCallbackEntry(const SEventCallbackEntry &);
+ SEventCallbackEntry &operator=(const SEventCallbackEntry &);
+ };
+ typedef CArray<SEventCallbackEntry *> TEventCallbacksList; ///< Array of events with callback
+
+ struct SElementCallbackEntry
+ {
+ SElementCallbackEntry()
+ : m_Element(NULL)
+ {
+ }
+ ~SElementCallbackEntry()
+ {
+ FOR_ARRAY(SEventCallbackEntry *, theEntry, m_EventEntries)
+ Q3DStudio_delete(*theEntry, SEventCallbackEntry);
+ }
+
+ TElement *m_Element; ///< Element to monitor for event
+ TEventCallbacksList m_EventEntries; ///< List of events listened on this element
+
+ private: // Disabled Copy Construction
+ SElementCallbackEntry(const SElementCallbackEntry &);
+ SElementCallbackEntry &operator=(const SElementCallbackEntry &);
+ };
+ typedef CArray<SElementCallbackEntry *>
+ TElementCallbacksList; ///< Array of elements with callbacks
+
+ //==============================================================================
+ // Fields
+ //==============================================================================
+protected:
+ TElementCallbacksList m_EventCallbacksList; ///< List of event callbacks
+ TCallbackList m_CallbackList;
+
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ CEventCallbacks();
+ ~CEventCallbacks();
+
+public: // Registration
+ void RegisterCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData);
+ BOOL UnregisterCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData, BOOL &outLast);
+ void UnregisterAllCallbacks();
+
+public: // Operation
+ void FireCallbacks(SEventCommand &ioEvent);
+
+protected:
+ void PerformCallbacks(TCallbackList &inCallbackList, SEventCommand &ioEvent);
+
+private: // Disabled Copy Construction
+ CEventCallbacks(const CEventCallbacks &);
+ CEventCallbacks &operator=(const CEventCallbacks &);
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSFrameworkTypes.h b/src/runtime/Qt3DSFrameworkTypes.h
new file mode 100644
index 0000000..8cb5c3b
--- /dev/null
+++ b/src/runtime/Qt3DSFrameworkTypes.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSTypes.h"
+#include "Qt3DSArray.h"
+#include "Qt3DSVector3.h"
+#include "Qt3DSMatrix.h"
+#include "Qt3DSBoundingBox.h"
+#include "Qt3DSKernelTypes.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSIComponentManager.h b/src/runtime/Qt3DSIComponentManager.h
new file mode 100644
index 0000000..8185da2
--- /dev/null
+++ b/src/runtime/Qt3DSIComponentManager.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+#include "Qt3DSTimePolicy.h"
+#include "foundation/Qt3DSOption.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+namespace qt3ds {
+namespace runtime {
+ namespace element {
+ struct TElement;
+ struct TComponent;
+ }
+}
+}
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+struct IComponentTimeOverrideFinishedCallback
+{
+protected:
+ virtual ~IComponentTimeOverrideFinishedCallback() {}
+public:
+ virtual void OnTimeFinished() = 0;
+ virtual void Release() = 0;
+};
+
+typedef qt3ds::foundation::Option<TimePolicyModes::Enum> TTimePolicyModeOption;
+typedef qt3ds::foundation::Option<INT32> TInt32Option;
+typedef qt3ds::foundation::Option<unsigned long> TULongOption;
+typedef qt3ds::foundation::Option<bool> TBoolOption;
+
+struct SComponentGotoSlideData
+{
+ INT32 m_Slide;
+ TTimePolicyModeOption m_Mode;
+ //-2 means previous, -1 means next, 1-N means slide
+ TInt32Option m_PlaythroughTo;
+ TULongOption m_StartTime;
+ FLOAT m_Rate;
+ bool m_Reverse;
+ TBoolOption m_Paused;
+
+ // No mode means use the mode in the slide data.
+ SComponentGotoSlideData(INT32 inSlide = 0,
+ TTimePolicyModeOption inMode = TTimePolicyModeOption(),
+ TInt32Option inPlayThrough = TInt32Option(),
+ TULongOption inStartTime = TULongOption(), FLOAT inRate = 1.0f,
+ bool inReverse = false, TBoolOption inPaused = TBoolOption())
+ : m_Slide(inSlide)
+ , m_Mode(inMode)
+ , m_PlaythroughTo(inPlayThrough)
+ , m_StartTime(inStartTime)
+ , m_Rate(inRate)
+ , m_Reverse(inReverse)
+ , m_Paused(inPaused)
+ {
+ }
+};
+
+//==============================================================================
+/**
+ * @interface IComponentManager
+ * Base interface of the container of all the components in the presentation.
+ * This specify the basic operations required to implement a Component manager
+ * object that works well with the kernel.
+ */
+class IComponentManager
+{
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ virtual ~IComponentManager() {}
+
+public: // Promotion
+ virtual TComponent *GetComponent(TElement *inElement) = 0;
+
+public: // Slides
+ virtual void GotoSlideIndex(TElement *inComponent, const SComponentGotoSlideData &inGotoData,
+ BOOL inSlideExit = true) = 0;
+ virtual void GotoSlideName(TElement *inComponent, const TStringHash inSlideHashName) = 0;
+ virtual void GoToBackSlide(TElement *inComponent) = 0;
+ virtual void GoToNextSlide(TElement *inComponent, const INT32 inDecrement = 1) = 0;
+ virtual void GoToPreviousSlide(TElement *inComponent, const INT32 inDecrement = 1) = 0;
+
+ virtual UINT8 GetSlideCount(TElement *inComponent) = 0;
+ virtual UINT8 GetCurrentSlide(TElement *inComponent) = 0;
+ virtual const CHAR *GetCurrentSlideName(TElement *inComponent) = 0;
+
+ virtual void OnElementDeactivated(TElement *inElement) = 0;
+
+ virtual void SetComponentTimeOverride(TElement *inElement, TTimeUnit inEndTime,
+ FLOAT inInterpolation,
+ IComponentTimeOverrideFinishedCallback *inCallback) = 0;
+
+ virtual void SetupComponentGotoSlideCommand(TElement *inElement,
+ const SComponentGotoSlideData &inGotoSlideData) = 0;
+ virtual bool HasComponentGotoSlideCommand(TElement *inElement) = 0;
+ virtual SComponentGotoSlideData GetComponentGotoSlideCommand(TElement *inElement) = 0;
+ virtual void ReleaseComponentGotoSlideCommand(TElement *inElement) = 0;
+
+public: // Time
+ virtual void GoToTime(TElement *inComponent, const TTimeUnit inTime) = 0;
+ virtual void SetPause(TElement *inComponent, const BOOL inPause) = 0;
+ virtual void SetTimePolicy(TElement *inComponent, const TTimeUnit inLoopDuration,
+ const UINT32 inRepetitions, const BOOL inPingPong,
+ const BOOL inPlayThrough) = 0;
+
+ virtual BOOL GetPause(TElement *inComponent) = 0;
+ virtual BOOL GetPlayBackDirection(TElement *inComponent) = 0;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSIInputSystem.h b/src/runtime/Qt3DSIInputSystem.h
new file mode 100644
index 0000000..6cd201f
--- /dev/null
+++ b/src/runtime/Qt3DSIInputSystem.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "Qt3DSInputEngine.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Input system interface
+ */
+class IInputSystem : public CInputEngine
+{
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ virtual ~IInputSystem() {}
+
+public: // Processing
+ virtual void UpdateInput() = 0;
+ virtual void ProcessInput() = 0;
+
+protected: // Input System handler
+ virtual void CheckMouseEvent() = 0;
+ virtual void CheckKeyboardEvent() = 0;
+ virtual void CheckJoystickEvent() = 0;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSIScene.h b/src/runtime/Qt3DSIScene.h
new file mode 100644
index 0000000..421fe9c
--- /dev/null
+++ b/src/runtime/Qt3DSIScene.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSBounds3.h"
+#include "Qt3DSBoundingBox.h"
+namespace qt3ds {
+namespace render {
+ class IImageLoadListener;
+}
+ class NVAllocatorCallback;
+}
+
+namespace Q3DStudio {
+
+class IPresentation;
+class RuntimeMatrix;
+struct SPickFrame;
+
+/**
+* @interface IScene
+*
+* Runtime interface to the renderer's representation of a presentation. Scenes are
+* created by the SceneManager and rendered via a lower level rendering system.
+*/
+struct STextSizes
+{
+ INT32 m_Width;
+ INT32 m_Height;
+ STextSizes()
+ : m_Width(0)
+ , m_Height(0)
+ {
+ }
+ STextSizes(INT32 w, INT32 h)
+ : m_Width(w)
+ , m_Height(h)
+ {
+ }
+};
+
+struct SMousePosition
+{
+ INT32 m_X;
+ INT32 m_Y;
+ SMousePosition(INT32 x, INT32 y)
+ : m_X(x)
+ , m_Y(y)
+ {
+ }
+ SMousePosition()
+ : m_X(0)
+ , m_Y(0)
+ {
+ }
+};
+
+struct SCameraRect
+{
+ float m_Left;
+ float m_Top;
+ float m_Right;
+ float m_Bottom;
+ SCameraRect(float l = 0.0f, float t = 0.0f, float r = 0.0f, float b = 0.0f)
+ : m_Left(l)
+ , m_Top(t)
+ , m_Right(r)
+ , m_Bottom(b)
+ {
+ }
+
+ bool IsValid() const { return fabs(m_Right - m_Left) > 0.0f; }
+};
+
+class IScene
+{
+protected:
+ virtual ~IScene() {}
+
+public: // Base Interface
+ virtual IPresentation &GetPresentation() = 0;
+
+ virtual void SetUserData(void *inUserData) = 0;
+ virtual void *GetUserData() = 0;
+
+ virtual void CalculateGlobalTransform(TElement *inElement, RuntimeMatrix &outTransform) = 0;
+ virtual void SetLocalTransformMatrix(TElement *inElement, const RuntimeMatrix &inTransform) = 0;
+ // Get bounding box in global space
+ virtual CBoundingBox GetBoundingBox(TElement *inElement, bool inSelfOnly) = 0;
+ // Get bounding box in local space.
+ virtual CBoundingBox GetLocalBoundingBox(TElement *inElement, bool inSelfOnly) = 0;
+
+ // The final argument, inHasTransparency has 3 possible values,
+ // 0 for no transparency, 1 for hasTransparency, -1 for unknown
+ virtual void SetTextureData(TElement *inElement, const unsigned char *inBuffer,
+ INT32 inBufferLength, INT32 inWidth, INT32 inHeight,
+ qt3ds::render::NVRenderTextureFormats::Enum inFormat,
+ INT32 inHasTransparency = -1) = 0;
+
+ virtual bool CreateOrSetMeshData(const char *inPathStr, unsigned char *vertData,
+ unsigned int numVerts, unsigned int vertStride,
+ unsigned int *indexData, unsigned int numIndices,
+ qt3ds::NVBounds3 &objBounds) = 0;
+
+ virtual STextSizes MeasureText(TElement *inElement, const char *inTextStr) = 0;
+
+ virtual STextSizes GetPresentationDesignDimensions() = 0;
+ // If the rect's right - left == 0.0, this method failed. Possibly because the layer is just
+ // direct-rendering a sub-presentation.
+ virtual SCameraRect GetCameraBounds(TElement &inElement) = 0;
+
+ virtual void PositionToScreen(TElement &inElement, qt3ds::QT3DSVec3 &inPos,
+ qt3ds::QT3DSVec3 &outScreen) = 0;
+ virtual void ScreenToPosition(TElement &inElement, qt3ds::QT3DSVec3 &inScreen,
+ qt3ds::QT3DSVec3 &outPos) = 0;
+
+ virtual qt3ds::foundation::CRegisteredString RegisterStr(const char *inStr) = 0;
+
+ virtual SMousePosition WindowToPresentation(const SMousePosition &inWindowPos) = 0;
+
+ virtual void RegisterOffscreenRenderer(const char *inKey) = 0;
+
+ virtual void Release() = 0;
+
+ virtual bool preferKtx() const = 0;
+
+ virtual qt3ds::NVAllocatorCallback &allocator() = 0;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSIScriptBridge.h b/src/runtime/Qt3DSIScriptBridge.h
new file mode 100644
index 0000000..889968d
--- /dev/null
+++ b/src/runtime/Qt3DSIScriptBridge.h
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+#include "Qt3DSIComponentManager.h"
+#include "EASTL/vector.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "Qt3DSApplication.h"
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qstringlist.h>
+
+namespace qt3dsimp {
+ struct Mesh;
+}
+
+namespace qt3ds {
+namespace runtime {
+ class IApplication;
+}
+}
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ class IMultiProtocolSocket;
+ }
+}
+}
+namespace qt3ds {
+namespace render {
+ class IThreadPool;
+ class IQt3DSRenderer;
+ class ICustomMaterialSystem;
+ class IDynamicObjectSystem;
+ class IBufferManager;
+}
+}
+
+struct script_State;
+
+namespace Q3DStudio {
+
+struct SEventCommand;
+class IPresentation;
+
+/**
+ * @interface IScriptBridge
+ * @brief Callback and load interface for a script engine.
+ */
+
+class IScriptTableProvider
+{
+protected:
+ virtual ~IScriptTableProvider() {}
+public:
+ virtual void CreateTable(script_State *inState) = 0;
+};
+
+struct SScriptEngineGotoSlideArgs
+{
+ TTimePolicyModeOption m_Mode;
+ //-2 means previous, -1 means next, 1-N means slide
+ const char *m_PlaythroughTo;
+ TULongOption m_StartTime;
+ float m_Rate;
+ bool m_Reverse;
+ TBoolOption m_Paused;
+ SScriptEngineGotoSlideArgs()
+ : m_PlaythroughTo(NULL)
+ , m_Rate(1.0f)
+ , m_Reverse(false)
+ {
+ }
+};
+
+class CScriptEngineCallFunctionArgRetriever
+{
+public:
+ CScriptEngineCallFunctionArgRetriever(const char *inArguments)
+ : m_ArgumentString(inArguments)
+ {
+ }
+ virtual ~CScriptEngineCallFunctionArgRetriever() {}
+ // Retrieve argument
+ // Return value: -1 error; otherwise it indicates argument count
+ virtual int RetrieveArgument(script_State *inState);
+ virtual eastl::string GetArgDescription();
+
+protected:
+ const char *m_ArgumentString;
+};
+
+class IScriptBridge : public qt3ds::foundation::NVRefCounted
+{
+public:
+ virtual ~IScriptBridge() {}
+
+public: // thread
+ // After this call all public functions are protected by a mutex
+ virtual void EnableMultithreadedAccess() = 0;
+ // After this call all public functions are not threadsafe.
+ virtual void DisableMultithreadedAccess() = 0;
+
+public: // Settings
+ virtual void SetApplicationCore(qt3ds::runtime::IApplication &inApplication) = 0;
+ virtual void SetApplication(qt3ds::runtime::IApplication &inApplication) = 0;
+
+public: // Scripts
+ // Both loads script, create an self table -> scriptIndex in a behaviors table
+ // LoadScript goes further by registering scriptIndex->inPresentation, and inOwner->m_ScriptID=
+ // scriptIndex
+ virtual void LoadScript(IPresentation *inPresentation, TElement *inOwner,
+ const CHAR *inName) = 0;
+ virtual Q3DStudio::INT32 InitializeApplicationBehavior(const char *inProjectRelativePath) = 0;
+
+public: // Script functions and Callbacks
+ virtual void ProcessFrameCallbacks(IPresentation *inPresentation) = 0;
+ // Call a member function inFnName from self table whose script index is given by inApp
+ virtual void ExecuteApplicationScriptFunction(Q3DStudio::INT32 inApp, const char *inFnName) = 0;
+ virtual void CallFunction(const char *behavior, const char *handler,
+ CScriptEngineCallFunctionArgRetriever &inArgRetriever) = 0;
+
+public: // Custom Actions
+ virtual void ProcessSignal(IPresentation *inPresentation,
+ const SEventCommand &inCommand) = 0;
+ virtual void ProcessCustomActions(IPresentation *inPresentation,
+ const SEventCommand &inCommand) = 0;
+ virtual void ProcessCustomCallback(IPresentation *inPresentation,
+ const SEventCommand &inCommand) = 0;
+
+public: // Elements
+ // Use inProvider to create a new table and associate with inElement: currently a render plugin
+ // element this mimics render plugin as an behavior element
+ virtual void SetTableForElement(TElement &inElement, IScriptTableProvider &inProvider) = 0;
+ virtual void SetAttribute(TElement *element, const char *attName, const char *value) = 0;
+ virtual void SetAttribute(const char *element, const char *attName, const char *value) = 0;
+ virtual void FireEvent(const char *element, const char *evtName) = 0;
+ virtual void SetDataInputValue(
+ const QString &name, const QVariant &value,
+ qt3ds::runtime::DataInputValueRole property
+ = qt3ds::runtime::DataInputValueRole::Value) = 0;
+ virtual void createElements(const QString &parentElementPath, const QString &slideName,
+ const QVector<QHash<QString, QVariant>> &properties,
+ qt3ds::render::IQt3DSRenderer *renderer) = 0;
+ virtual void deleteElements(const QStringList &elementPath,
+ qt3ds::render::IQt3DSRenderer *renderer) = 0;
+ virtual void createMaterials(const QString &subPresId, const QStringList &materialDefinitions,
+ qt3ds::render::ICustomMaterialSystem *customMaterialSystem,
+ qt3ds::render::IDynamicObjectSystem *dynamicObjectSystem,
+ qt3ds::render::IQt3DSRenderer *renderer) = 0;
+ virtual void deleteMaterials(const QStringList &materialNames,
+ qt3ds::render::IQt3DSRenderer *renderer) = 0;
+ virtual void createMesh(const QString &name, qt3dsimp::Mesh *mesh,
+ qt3ds::render::IBufferManager *bufferManager) = 0;
+ virtual void deleteMeshes(const QStringList &elementPath,
+ qt3ds::render::IBufferManager *bufferManager) = 0;
+
+public: // Components
+ virtual void GotoSlide(const char *component, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs) = 0;
+ virtual void GotoSlideRelative(const char *component, bool inNextSlide, bool inWrap,
+ const SScriptEngineGotoSlideArgs &inArgs) = 0;
+
+public: // Presentation
+ virtual void SetPresentationAttribute(const char *presId, const char *attName,
+ const char *attValue) = 0;
+
+public: // Multimedia
+ virtual bool PlaySoundFile(const char *soundPath) = 0;
+
+public: // Miscellaneous
+ virtual void EnableDebugging(qt3ds::state::debugger::IMultiProtocolSocket &socket) = 0;
+ virtual void EnableProfiling() = 0;
+ virtual void StepGC() = 0;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSIStateful.h b/src/runtime/Qt3DSIStateful.h
new file mode 100644
index 0000000..0eca293
--- /dev/null
+++ b/src/runtime/Qt3DSIStateful.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSArray.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+class IStatefulStackBase
+{
+public:
+ virtual void PushState() = 0;
+ virtual void PopState() = 0;
+};
+
+template <typename T>
+class IStatefulStack : public IStatefulStackBase
+{
+public:
+ IStatefulStack(T &inStateProperty)
+ : m_StateProperty(inStateProperty)
+ {
+ }
+
+ virtual ~IStatefulStack(){};
+
+public:
+ virtual void PushState() { m_States.Push(m_StateProperty); }
+ virtual void PopState() { m_StateProperty = m_States.Pop(); }
+
+protected:
+ T &m_StateProperty;
+ CArray<T> m_States;
+};
+
+} // namespace Q3DStudio \ No newline at end of file
diff --git a/src/runtime/Qt3DSIText.h b/src/runtime/Qt3DSIText.h
new file mode 100644
index 0000000..c4694fe
--- /dev/null
+++ b/src/runtime/Qt3DSIText.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CWorld;
+class CRenderStateManager;
+
+class IText
+{
+public: // Text update
+ virtual void Update(CWorld &inWorld, CVector3 &outBoxMin, CVector3 &outBoxMax) = 0;
+ virtual void Apply(CRenderStateManager &inRenderStateManager) = 0;
+ static IText *GetIText(INT32 inAssociation);
+};
+
+} // namespace Q3DStudio \ No newline at end of file
diff --git a/src/runtime/Qt3DSInputDefs.h b/src/runtime/Qt3DSInputDefs.h
new file mode 100644
index 0000000..447e424
--- /dev/null
+++ b/src/runtime/Qt3DSInputDefs.h
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Mouse button state flags
+ * There is some discrepancy between mouse state and mouse events.
+ *
+ * Mouse button state refers to the actual state of the button as captured
+ * by the input engine. The logical states are: up, pressed, down and released.
+ *
+ * Mouse events are generated by Kernel (in CKernel::SetPickFrame), and current
+ * support is only for pressed and released. This translates to the respective
+ * MOUSE_DOWN and MOUSE_UP.
+ *
+ * Therefore, a mapping would be:
+ * STATE_MOUSE_PRESSED -> EVENT_MOUSE_DOWN
+ * STATE_MOUSE_RELEASED -> EVENT_MOUSE_UP
+ * STATE_MOUSE_UP -> No corresponding event
+ * STATE_MOUSE_DOWN -> No corresponding event
+ *
+ */
+enum EMouseInputFlags {
+ NO_INPUT = 0,
+ //
+ LMOUSE_PRESSED = 1 << 0,
+ LMOUSE_RELEASED = 1 << 1,
+ MMOUSE_PRESSED = 1 << 2,
+ MMOUSE_RELEASED = 1 << 3,
+ RMOUSE_PRESSED = 1 << 4,
+ RMOUSE_RELEASED = 1 << 5,
+ //
+ LMOUSE_DOWN = 1 << 6,
+ LMOUSE_UP = 1 << 7,
+ MMOUSE_DOWN = 1 << 8,
+ MMOUSE_UP = 1 << 9,
+ RMOUSE_DOWN = 1 << 10,
+ RMOUSE_UP = 1 << 11,
+ MOUSEBUTTONCOUNT = 3, // left, middle, right - could potentially have more mouse buttons
+
+ HSCROLLWHEEL = 1 << 12,
+ VSCROLLWHEEL = 1 << 13,
+};
+
+//==============================================================================
+/**
+ * Key code/index to SInputFrame::m_KeyStates
+ */
+// enum EKeyCode
+//{
+// KEY_NOKEY = 0x00,
+// KEY_ESCAPE,
+// KEY_1,
+// KEY_2,
+// KEY_3,
+// KEY_4,
+// KEY_5,
+// KEY_6,
+// KEY_7,
+// KEY_8,
+// KEY_9,
+// KEY_0,
+// KEY_MINUS, /* - on main keyboard */
+// KEY_EQUALS,
+// KEY_BACK, /* backspace */
+// KEY_TAB,
+// KEY_Q,
+// KEY_W,
+// KEY_E,
+// KEY_R,
+// KEY_T,
+// KEY_Y,
+// KEY_U,
+// KEY_I,
+// KEY_O,
+// KEY_P,
+// KEY_LBRACKET,
+// KEY_RBRACKET,
+// KEY_RETURN, /* Enter on main keyboard */
+// KEY_LCONTROL,
+// KEY_A,
+// KEY_S,
+// KEY_D,
+// KEY_F,
+// KEY_G,
+// KEY_H,
+// KEY_J,
+// KEY_K,
+// KEY_L,
+// KEY_SEMICOLON,
+// KEY_APOSTROPHE,
+// KEY_GRAVE, /* accent grave */
+// KEY_LSHIFT,
+// KEY_BACKSLASH,
+// KEY_Z,
+// KEY_X,
+// KEY_C,
+// KEY_V,
+// KEY_B,
+// KEY_N,
+// KEY_M,
+// KEY_COMMA,
+// KEY_PERIOD, /* . on main keyboard */
+// KEY_SLASH, /* / on main keyboard */
+// KEY_RSHIFT,
+// KEY_MULTIPLY, /* * on numeric keypad */
+// KEY_LMENU, /* left Alt */
+// KEY_SPACE,
+// KEY_CAPITAL,
+// KEY_F1,
+// KEY_F2,
+// KEY_F3,
+// KEY_F4,
+// KEY_F5,
+// KEY_F6,
+// KEY_F7,
+// KEY_F8,
+// KEY_F9,
+// KEY_F10,
+// KEY_NUMLOCK,
+// KEY_SCROLL, /* Scroll Lock */
+// KEY_NUMPAD7,
+// KEY_NUMPAD8,
+// KEY_NUMPAD9,
+// KEY_SUBTRACT, /* - on numeric keypad */
+// KEY_NUMPAD4,
+// KEY_NUMPAD5,
+// KEY_NUMPAD6,
+// KEY_ADD, /* + on numeric keypad */
+// KEY_NUMPAD1,
+// KEY_NUMPAD2,
+// KEY_NUMPAD3,
+// KEY_NUMPAD0,
+// KEY_DECIMAL, /* . on numeric keypad */
+// KEY_OEM_102, /* <> or \| on RT 102-key keyboard (Non-U.S.) */
+// KEY_F11,
+// KEY_F12,
+// KEY_F13, /* (NEC PC98) */
+// KEY_F14, /* (NEC PC98) */
+// KEY_F15, /* (NEC PC98) */
+// KEY_KANA, /* (Japanese keyboard) */
+// KEY_ABNT_C1, /* /? on Brazilian keyboard */
+// KEY_CONVERT, /* (Japanese keyboard) */
+// KEY_NOCONVERT, /* (Japanese keyboard) */
+// KEY_YEN, /* (Japanese keyboard) */
+// KEY_ABNT_C2, /* Numpad . on Brazilian keyboard */
+// KEY_NUMPADEQUALS, /* = on numeric keypad (NEC PC98) */
+// KEY_PREVTRACK, /* Previous Track
+// (DIK_CIRCUMFLEX on Japanese keyboard) */
+// KEY_AT, /* (NEC PC98) */
+// KEY_COLON, /* (NEC PC98) */
+// KEY_UNDERLINE, /* (NEC PC98) */
+// KEY_KANJI, /* (Japanese keyboard) */
+// KEY_STOP, /* (NEC PC98) */
+// KEY_AX, /* (Japan AX) */
+// KEY_UNLABELED, /* (J3100) */
+// KEY_NEXTTRACK, /* Next Track */
+// KEY_NUMPADENTER, /* Enter on numeric keypad */
+// KEY_RCONTROL,
+// KEY_MUTE, /* Mute */
+// KEY_CALCULATOR, /* Calculator */
+// KEY_PLAYPAUSE, /* Play / Pause */
+// KEY_MEDIASTOP, /* Media Stop */
+// KEY_VOLUMEDOWN, /* Volume - */
+// KEY_VOLUMEUP, /* Volume + */
+// KEY_WEBHOME, /* Web home */
+// KEY_NUMPADPERIOD, /* . on numeric keypad (NEC PC98) */
+// KEY_DIVIDE, /* / on numeric keypad */
+// KEY_SYSRQ,
+// KEY_RMENU, /* right Alt */
+// KEY_PAUSE, /* Pause */
+// KEY_HOME, /* Home on arrow keypad */
+// KEY_UP, /* UpArrow on arrow keypad */
+// KEY_PRIOR, /* PgUp on arrow keypad */
+// KEY_LEFT, /* LeftArrow on arrow keypad */
+// KEY_RIGHT, /* RightArrow on arrow keypad */
+// KEY_END, /* End on arrow keypad */
+// KEY_DOWN, /* DownArrow on arrow keypad */
+// KEY_NEXT, /* PgDn on arrow keypad */
+// KEY_INSERT, /* Insert on arrow keypad */
+// KEY_DELETE, /* Delete on arrow keypad */
+// KEY_LWIN, /* Left Windows key */
+// KEY_RWIN, /* Right Windows key */
+// KEY_APPS, /* AppMenu key */
+// KEY_POWER, /* System Power */
+// KEY_SLEEP, /* System Sleep */
+// KEY_WAKE, /* System Wake */
+// KEY_WEBSEARCH, /* Web Search */
+// KEY_WEBFAVORITES, /* Web Favorites */
+// KEY_WEBREFRESH, /* Web Refresh */
+// KEY_WEBSTOP, /* Web Stop */
+// KEY_WEBFORWARD, /* Web Forward */
+// KEY_WEBBACK, /* Web Back */
+// KEY_MYCOMPUTER, /* My Computer */
+// KEY_MAIL, /* Mail */
+// KEY_MEDIASELECT, /* Media Select */
+// //
+// KEY_TOTAL_COUNT
+//};
+
+typedef enum _EKeyCode {
+ KEY_NOKEY = 0,
+ KEY_ESCAPE,
+ KEY_1,
+ KEY_2,
+ KEY_3,
+ KEY_4,
+ KEY_5,
+ KEY_6,
+ KEY_7,
+ KEY_8,
+ KEY_9,
+ KEY_0,
+ KEY_SUBTRACT,
+ KEY_EQUALS,
+ KEY_BACK,
+ KEY_TAB,
+ KEY_Q,
+ KEY_W,
+ KEY_E,
+ KEY_R,
+ KEY_T,
+ KEY_Y,
+ KEY_U,
+ KEY_I,
+ KEY_O,
+ KEY_P,
+ KEY_LBRACKET,
+ KEY_RBRACKET,
+ KEY_RETURN,
+ KEY_LCONTROL,
+ KEY_A, // 30
+ KEY_S,
+ KEY_D,
+ KEY_F,
+ KEY_G,
+ KEY_H,
+ KEY_J,
+ KEY_K,
+ KEY_L,
+ KEY_SEMICOLON,
+ KEY_APOSTROPHE, // 40
+ KEY_GRAVE,
+ KEY_LSHIFT,
+ KEY_BACKSLASH,
+ KEY_Z,
+ KEY_X,
+ KEY_C,
+ KEY_V,
+ KEY_B,
+ KEY_N,
+ KEY_M,
+ KEY_COMMA,
+ KEY_PERIOD,
+ KEY_SLASH,
+ KEY_RSHIFT,
+ KEY_MULTIPLY,
+ KEY_LALT,
+ KEY_SPACE,
+ KEY_CAPITAL,
+ KEY_F1,
+ KEY_F2, // 60
+ KEY_F3,
+ KEY_F4,
+ KEY_F5,
+ KEY_F6,
+ KEY_F7,
+ KEY_F8,
+ KEY_F9,
+ KEY_F10,
+ KEY_NUMLOCK,
+ KEY_SCROLL, // 70
+ KEY_NUMPAD7,
+ KEY_NUMPAD8,
+ KEY_NUMPAD9,
+ KEY_NUMPADSUBTRACT,
+ KEY_NUMPAD4,
+ KEY_NUMPAD5,
+ KEY_NUMPAD6,
+ KEY_NUMPADADD,
+ KEY_NUMPAD1,
+ KEY_NUMPAD2, // 80
+ KEY_NUMPAD3,
+ KEY_NUMPAD0,
+ KEY_NUMPADDECIMAL,
+ KEY_NOOP,
+ KEY_ZENKAKUHANKAKU,
+ KEY_102ND,
+ KEY_F11,
+ KEY_F12,
+ KEY_F13,
+ KEY_F14, // 90
+ KEY_HIRAGANA,
+ KEY_HENKAN,
+ KEY_KATAKANAHIRAGANA,
+ KEY_MUHENKAN,
+ KEY_KPJPCOMMA,
+ KEY_NUMPADENTER,
+ KEY_RCONTROL,
+ KEY_NUMPADDIVIDE,
+ KEY_PRINTSCREEN,
+ KEY_RALT, // 100
+ KEY_LINEFEED,
+ KEY_HOME,
+ KEY_UP,
+ KEY_PGUP,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_END,
+ KEY_DOWN,
+ KEY_PGDN,
+ KEY_INSERT, // 110
+ KEY_DELETE,
+ KEY_MACRO,
+ KEY_MUTE,
+ KEY_VOLUMEDOWN,
+ KEY_VOLUMEUP,
+ KEY_POWER,
+ KEY_KPEQUAL,
+ KEY_KPPLUSMINUS,
+ KEY_PAUSE,
+ KEY_SCALE,
+ KEY_TOTAL_COUNT
+} EKeyCode;
+
+//==============================================================================
+/**
+ * Keyboard modifier flags
+ */
+enum EKeyboardModifierFlags {
+ MODIFIER_NONE = 0,
+ MODIFIER_SHIFT = 1 << 0,
+ MODIFIER_CTRL = 1 << 1,
+ MODIFIER_ALT = 1 << 2,
+ MODIFIER_KEYDOWN = 1 << 3, ///< One or more key is down
+};
+
+//==============================================================================
+/**
+ * Button codes
+ */
+enum EButtonCodes {
+ BUTTON_A = 0,
+ BUTTON_B,
+ BUTTON_X,
+ BUTTON_Y,
+ BUTTON_L1,
+ BUTTON_R1,
+ BUTTON_THUMBL,
+ BUTTON_THUMBR,
+ BUTTON_SELECT,
+ BUTTON_START,
+ BUTTON_MODE,
+ BUTTON_UP,
+ BUTTON_DOWN,
+ BUTTON_LEFT,
+ BUTTON_RIGHT,
+ BUTTON_CENTER,
+ BUTTON_ENTER,
+ BUTTON_TOTAL_COUNT
+};
+
+//==============================================================================
+/**
+ * Axis codes
+ */
+enum EAxisCodes {
+ AXIS_X = 0,
+ AXIS_Y,
+ AXIS_Z,
+ AXIS_RZ,
+ AXIS_HAT_X,
+ AXIS_HAT_Y,
+ AXIS_LTRIGGER,
+ AXIS_RTRIGGER,
+ AXIS_TOTAL_COUNT
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSInputEngine.cpp b/src/runtime/Qt3DSInputEngine.cpp
new file mode 100644
index 0000000..d3cbe42
--- /dev/null
+++ b/src/runtime/Qt3DSInputEngine.cpp
@@ -0,0 +1,849 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+#include <EASTL/list.h>
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSInputEngine.h"
+#include "Qt3DSSceneManager.h"
+#include "Qt3DSInputEventTypes.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSRuntimeFactory.h"
+#include "EventPollingSystem.h"
+#include "foundation/Qt3DSAssert.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+class CInputEventProvider : public qt3ds::evt::IEventProvider
+{
+ int m_RefCount;
+
+public:
+ CInputEventProvider()
+ : m_RefCount(0)
+ {
+ }
+ ~CInputEventProvider()
+ {
+ while (!m_Events.empty()) {
+ delete m_Events.front();
+ m_Events.pop_front();
+ }
+ }
+ struct SEvent
+ {
+ enum EType { TYPE_NONE = 0, TYPE_KEYBOARD, TYPE_BUTTON, TYPE_COUNT };
+ SEvent(EType inType)
+ : m_Type(inType)
+ {
+ }
+ EType m_Type;
+
+ virtual SEvent *Clone() const = 0;
+ virtual Qt3DSEventSystemEvent *GenEventSystemEvent(qt3ds::evt::IEventFactory &inFactory) = 0;
+ };
+ struct SKeyboardEvent : public SEvent
+ {
+ SKeyboardEvent()
+ : SEvent(TYPE_KEYBOARD)
+ , m_KeyCode(KEY_NOKEY)
+ , m_KeyEvent(0)
+ {
+ }
+ SKeyboardEvent(INT32 inKeyCode, UINT32 inKeyEvent)
+ : SEvent(TYPE_KEYBOARD)
+ , m_KeyCode(inKeyCode)
+ , m_KeyEvent(inKeyEvent)
+ {
+ }
+ SEvent *Clone() const override
+ {
+ return new SKeyboardEvent(this->m_KeyCode, this->m_KeyEvent);
+ }
+ Qt3DSEventSystemEvent *GenEventSystemEvent(qt3ds::evt::IEventFactory &inFactory) override
+ {
+ if (m_KeyEvent == ON_KEYDOWN || m_KeyEvent == ON_KEYUP || m_KeyEvent == ON_KEYREPEAT) {
+ Qt3DSEventSystemEvent &theEvent = inFactory.CreateEvent(3);
+ int theIndex = 0;
+ theEvent.m_Data[theIndex].m_Name = inFactory.RegisterStr("category");
+ theEvent.m_Data[theIndex].m_Value.m_Type = QT3DSEventSystemEventTypesString;
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("kb");
+ ++theIndex;
+ theEvent.m_Data[theIndex].m_Name = inFactory.RegisterStr("code");
+ theEvent.m_Data[theIndex].m_Value.m_Type = QT3DSEventSystemEventTypesNumber;
+ theEvent.m_Data[theIndex].m_Value.m_Number = m_KeyCode;
+ ++theIndex;
+ theEvent.m_Data[theIndex].m_Name = inFactory.RegisterStr("action");
+ theEvent.m_Data[theIndex].m_Value.m_Type = QT3DSEventSystemEventTypesString;
+ if (m_KeyEvent == ON_KEYDOWN)
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("down");
+ else if (m_KeyEvent == ON_KEYUP)
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("up");
+ else if (m_KeyEvent == ON_KEYREPEAT)
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("repeat");
+ ++theIndex;
+ return &theEvent;
+ }
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+ INT32 m_KeyCode;
+ UINT32 m_KeyEvent;
+ };
+ struct SButtonEvent : public SEvent
+ {
+ SButtonEvent()
+ : SEvent(TYPE_BUTTON)
+ , m_ButtonCode(BUTTON_TOTAL_COUNT)
+ , m_ButtonEvent(0)
+ {
+ }
+ SButtonEvent(INT32 inButtonCode, UINT32 inButtonEvent)
+ : SEvent(TYPE_BUTTON)
+ , m_ButtonCode(inButtonCode)
+ , m_ButtonEvent(inButtonEvent)
+ {
+ }
+ SEvent *Clone() const override
+ {
+ return new SKeyboardEvent(this->m_ButtonCode, this->m_ButtonEvent);
+ }
+ Qt3DSEventSystemEvent *GenEventSystemEvent(qt3ds::evt::IEventFactory &inFactory) override
+ {
+ if (m_ButtonEvent == ON_BUTTONDOWN || m_ButtonEvent == ON_BUTTONUP
+ || m_ButtonEvent == ON_BUTTONREPEAT) {
+ Qt3DSEventSystemEvent &theEvent = inFactory.CreateEvent(3);
+ int theIndex = 0;
+ theEvent.m_Data[theIndex].m_Name = inFactory.RegisterStr("category");
+ theEvent.m_Data[theIndex].m_Value.m_Type = QT3DSEventSystemEventTypesString;
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("button");
+ ++theIndex;
+ theEvent.m_Data[theIndex].m_Name = inFactory.RegisterStr("code");
+ theEvent.m_Data[theIndex].m_Value.m_Type = QT3DSEventSystemEventTypesNumber;
+ theEvent.m_Data[theIndex].m_Value.m_Number = m_ButtonCode;
+ ++theIndex;
+ theEvent.m_Data[theIndex].m_Name = inFactory.RegisterStr("action");
+ theEvent.m_Data[theIndex].m_Value.m_Type = QT3DSEventSystemEventTypesString;
+ if (m_ButtonEvent == ON_BUTTONDOWN)
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("down");
+ else if (m_ButtonEvent == ON_BUTTONUP)
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("up");
+ else if (m_ButtonEvent == ON_BUTTONREPEAT)
+ theEvent.m_Data[theIndex].m_Value.m_String = inFactory.AllocateStr("repeat");
+ ++theIndex;
+ return &theEvent;
+ }
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+ INT32 m_ButtonCode;
+ UINT32 m_ButtonEvent;
+ };
+ size_t GetNextEvents(qt3ds::evt::IEventFactory &inFactory, Qt3DSEventSystemEvent **outBuffer,
+ size_t inBufLenInEvent) override
+ {
+ size_t theEventCount = 0;
+ while (!m_Events.empty() && inBufLenInEvent--) {
+ SEvent *theBufferedEvent = m_Events.front();
+ Qt3DSEventSystemEvent *theEventSystemEvent =
+ theBufferedEvent->GenEventSystemEvent(inFactory);
+ if (theEventSystemEvent)
+ outBuffer[theEventCount++] = theEventSystemEvent;
+ m_Events.pop_front();
+ }
+ return theEventCount;
+ }
+
+ void addRef() { ++m_RefCount; }
+
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+
+ void Release() override { release(); }
+
+ void AddEvent(const SEvent &inEvent)
+ {
+ SEvent *theEvent = inEvent.Clone();
+ if (theEvent)
+ m_Events.push_back(theEvent);
+ else
+ QT3DS_ASSERT(false);
+ }
+
+private:
+ eastl::list<SEvent *> m_Events;
+};
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CInputEngine::CInputEngine()
+ : m_BeginPickInput(false)
+ , m_Application(NULL)
+ , m_InputEventProvider(NULL)
+{
+ m_InputFrame.m_PickValid = false;
+ m_InputFrame.m_PickX = 0;
+ m_InputFrame.m_PickY = 0;
+
+ // TODO: SK - To quickly restore functionality, but this is should be in tegra-specific project.
+ m_InputFrame.m_DeltaX = 0;
+ m_InputFrame.m_DeltaY = 0;
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CInputEngine::~CInputEngine()
+{
+}
+
+//==============================================================================
+/**
+ * Set the top-level interface of the Runtime object
+ * @param inRuntime the top-level interface of the Runtime object
+ */
+void CInputEngine::SetApplication(qt3ds::runtime::IApplication *inApplication)
+{
+ m_Application = inApplication;
+ // Create this here. If we die before the application does, then the system will crash unless
+ // the application event system is reponsibe for the keyboard provider. Since the provider
+ // never
+ // access back to this object, this is the most sensible relationship. Furthermore if we have
+ // never been given an application, it doesn't make sense to have a keyboard event provider.
+ // In reality, this entire system should be ref counted.
+ m_InputEventProvider = new CInputEventProvider();
+
+ // Add a ref for the event system because it does not add its own ref.
+ m_InputEventProvider->addRef();
+ m_Application->GetRuntimeFactory().GetEventSystem().AddProvider(*m_InputEventProvider);
+ MarkApplicationDirty();
+}
+
+//==============================================================================
+/**
+ * Get the input information for the current frame
+ * @return the SInputFrame that defines the current frame input information
+ */
+SInputFrame &CInputEngine::GetInputFrame()
+{
+ return m_InputFrame;
+}
+
+//==============================================================================
+/**
+ * Return the key input portion of the input frame.
+ */
+SKeyInputFrame &CInputEngine::GetKeyInputFrame()
+{
+ return m_InputFrame.m_KeyInputFrame;
+}
+
+void CInputEngine::BeginPickInput()
+{
+ m_PickInput.clear();
+ m_BeginPickInput = true;
+}
+
+//==============================================================================
+/**
+ * Set the pick information the the input frame structure
+ * @param inX the X value of the pick position
+ * @param inY the Y value of the pick position
+ * @param inValid whether the X and Y value are valid
+ */
+void CInputEngine::SetPickInput(FLOAT inX, FLOAT inY, BOOL inValid /*= true*/)
+{
+ m_InputFrame.m_PickValid = inValid;
+ m_InputFrame.m_PickX = inX;
+ m_InputFrame.m_PickY = inY;
+ m_InputFrame.m_MouseFlags = 0;
+ MarkApplicationDirty();
+ if (m_BeginPickInput && m_PickInput.size() < 1000)
+ m_PickInput.push_back(eastl::make_pair(inX, inY));
+}
+
+void CInputEngine::EndPickInput()
+{
+ m_BeginPickInput = false;
+}
+
+CInputEngine::TPickInputList CInputEngine::GetPickInput() const
+{
+ return qt3ds::foundation::toDataRef(m_PickInput.data(), m_PickInput.size());
+}
+
+//==============================================================================
+/**
+ * Set the mouse button state flag
+ * @param inFlags the current mouse state
+ */
+void CInputEngine::SetPickFlags(INT32 inFlags)
+{
+ m_InputFrame.m_MouseFlags |= inFlags;
+}
+
+//==============================================================================
+/**
+ * Set the keyboard status
+ * @param inKeyCode the identifier of the key
+ * @param inDown identify whether the key is down
+ */
+void CInputEngine::SetKeyState(INT32 inKeyCode, BOOL inDown)
+{
+ if (!inDown)
+ m_InputFrame.m_KeyInputFrame.m_KeyStates.erase(inKeyCode);
+ else {
+ SKeyInputFrame::MAP_KEYCODE_COUNT::iterator theIter =
+ m_InputFrame.m_KeyInputFrame.m_KeyStates.find(inKeyCode);
+ if (theIter == m_InputFrame.m_KeyInputFrame.m_KeyStates.end())
+ m_InputFrame.m_KeyInputFrame.m_KeyStates.insert(
+ eastl::make_pair<INT32, UINT16>(inKeyCode, 1));
+ else if (theIter->second < 255)
+ ++theIter->second;
+ }
+ MarkApplicationDirty();
+}
+
+//==============================================================================
+/**
+ * Set the button status
+ * @param inButtonCode the identifier of the button
+ * @param inDown identify whether the button is down
+ */
+void CInputEngine::SetButtonState(INT32 inButtonCode, BOOL inDown)
+{
+ if (!inDown)
+ m_InputFrame.m_ButtonStates.erase(inButtonCode);
+ else {
+ SInputFrame::MAP_BUTTONCODE_COUNT::iterator theIter =
+ m_InputFrame.m_ButtonStates.find(inButtonCode);
+ if (theIter == m_InputFrame.m_ButtonStates.end())
+ m_InputFrame.m_ButtonStates.insert(eastl::make_pair<INT32, UINT16>(inButtonCode, 1));
+ else if (theIter->second < 255)
+ ++theIter->second;
+ }
+ MarkApplicationDirty();
+}
+
+//==============================================================================
+/**
+ * Set the scroll value
+ * @param inMouseFlag flag to indicate whether this is a vertical or horizontal
+ *scroll
+ * @param inValue value of scroll performed
+ */
+void CInputEngine::SetScrollValue(INT32 inMouseFlag, INT16 inValue)
+{
+ m_InputFrame.m_MouseFlags = inMouseFlag;
+ m_InputFrame.m_ScrollValue = inValue;
+ MarkApplicationDirty();
+}
+//==============================================================================
+/**
+ * Set whether the keyboard is modified
+ * @param inFlag true if the keyboard status is modified
+ */
+void CInputEngine::SetModifierFlag(INT32 inFlag)
+{
+ m_InputFrame.m_KeyInputFrame.m_ModifierFlags |= inFlag;
+}
+
+// Currently unused
+//==============================================================================
+/**
+ * Converts KeyCode in index format to ASCII format.
+ * This will translate upper/lower-case letters too.
+ * @param inKeyCode the index format keycode
+ * @param inShift whether the shift button is pressed
+ * @return the ASCII format keycode
+ */
+// INT32 CInputEngine::ConvertKeyCode( INT32 inKeyCode, BOOL inShift )
+//{
+// static INT32 s_ASCIILowerCase[] =
+// {
+// 0,//KEY_NOKEY = 0x00,
+// 27,//KEY_ESCAPE,
+// '1',//KEY_1,
+// '2',//KEY_2,
+// '3',//KEY_3,
+// '4',//KEY_4,
+// '5',//KEY_5,
+// '6',//KEY_6,
+// '7',//KEY_7,
+// '8',//KEY_8,
+// '9',//KEY_9,
+// '0',//KEY_0,
+// '-',//KEY_MINUS, /* - on main keyboard */
+// '=',//KEY_EQUALS,
+// 8,//KEY_BACK, /* backspace */
+// '\t',//KEY_TAB,
+// 'q',//KEY_Q,
+// 'w',//KEY_W,
+// 'e',//KEY_E,
+// 'r',//KEY_R,
+// 't',//KEY_T,
+// 'y',//KEY_Y,
+// 'u',//KEY_U,
+// 'i',//KEY_I,
+// 'o',//KEY_O,
+// 'p',//KEY_P,
+// '[',//KEY_LBRACKET,
+// ']',//KEY_RBRACKET,
+// '\n',//KEY_RETURN, /* Enter on main keyboard */
+// 0,//KEY_LCONTROL,
+// 'a',//KEY_A,
+// 's',//KEY_S,
+// 'd',//KEY_D,
+// 'f',//KEY_F,
+// 'g',//KEY_G,
+// 'h',//KEY_H,
+// 'j',//KEY_J,
+// 'k',//KEY_K,
+// 'l',//KEY_L,
+// ';',//KEY_SEMICOLON,
+// '\'',//KEY_APOSTROPHE,
+// '`',//KEY_GRAVE, /* accent grave */
+// 0,//KEY_LSHIFT,
+// '\\',//KEY_BACKSLASH,
+// 'z',//KEY_Z,
+// 'x',//KEY_X,
+// 'c',//KEY_C,
+// 'v',//KEY_V,
+// 'b',//KEY_B,
+// 'n',//KEY_N,
+// 'm',//KEY_M,
+// ',',//KEY_COMMA,
+// '.',//KEY_PERIOD, /* . on main keyboard */
+// '/',//KEY_SLASH, /* / on main keyboard */
+// 0,//KEY_RSHIFT,
+// '*',//KEY_MULTIPLY, /* * on numeric keypad */
+// 0,//KEY_LMENU, /* left Alt */
+// ' ',//KEY_SPACE,
+// 0,//KEY_CAPITAL,
+// 0,//KEY_F1,
+// 0,//KEY_F2,
+// 0,//KEY_F3,
+// 0,//KEY_F4,
+// 0,//KEY_F5,
+// 0,//KEY_F6,
+// 0,//KEY_F7,
+// 0,//KEY_F8,
+// 0,//KEY_F9,
+// 0,//KEY_F10,
+// 0,//KEY_NUMLOCK,
+// 0,//KEY_SCROLL, /* Scroll Lock */
+// '7',//KEY_NUMPAD7,
+// '8',//KEY_NUMPAD8,
+// '9',//KEY_NUMPAD9,
+// '-',//KEY_SUBTRACT, /* - on numeric keypad */
+// '4',//KEY_NUMPAD4,
+// '5',//KEY_NUMPAD5,
+// '6',//KEY_NUMPAD6,
+// '+',//KEY_ADD, /* + on numeric keypad */
+// '1',//KEY_NUMPAD1,
+// '2',//KEY_NUMPAD2,
+// '3',//KEY_NUMPAD3,
+// '0',//KEY_NUMPAD0,
+// '.',//KEY_DECIMAL, /* . on numeric keypad */
+// 0,//KEY_OEM_102, /* <> or \| on RT 102-key keyboard (Non-U.S.) */
+// 0,//KEY_F11,
+// 0,//KEY_F12,
+// 0,//KEY_F13, /* (NEC PC98) */
+// 0,//KEY_F14, /* (NEC PC98) */
+// 0,//KEY_F15, /* (NEC PC98) */
+// 0,//KEY_KANA, /* (Japanese keyboard) */
+// 0,//KEY_ABNT_C1, /* /? on Brazilian keyboard */
+// 0,//KEY_CONVERT, /* (Japanese keyboard) */
+// 0,//KEY_NOCONVERT, /* (Japanese keyboard) */
+// 0,//KEY_YEN, /* (Japanese keyboard) */
+// 0,//KEY_ABNT_C2, /* Numpad . on Brazilian keyboard */
+// 0,//KEY_NUMPADEQUALS, /* = on numeric keypad (NEC PC98) */
+// 0,//KEY_PREVTRACK, /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
+// 0,//KEY_AT, /* (NEC PC98) */
+// 0,//KEY_COLON, /* (NEC PC98) */
+// 0,//KEY_UNDERLINE, /* (NEC PC98) */
+// 0,//KEY_KANJI, /* (Japanese keyboard) */
+// 0,//KEY_STOP, /* (NEC PC98) */
+// 0,//KEY_AX, /* (Japan AX) */
+// 0,//KEY_UNLABELED, /* (J3100) */
+// 0,//KEY_NEXTTRACK, /* Next Track */
+// 0,//KEY_NUMPADENTER, /* Enter on numeric keypad */
+// 0,//KEY_RCONTROL,
+// 0,//KEY_MUTE, /* Mute */
+// 0,//KEY_CALCULATOR, /* Calculator */
+// 0,//KEY_PLAYPAUSE, /* Play / Pause */
+// 0,//KEY_MEDIASTOP, /* Media Stop */
+// 0,//KEY_VOLUMEDOWN, /* Volume - */
+// 0,//KEY_VOLUMEUP, /* Volume + */
+// 0,//KEY_WEBHOME, /* Web home */
+// 0,//KEY_NUMPADCOMMA, /* , on numeric keypad (NEC PC98) */
+// 0,//KEY_DIVIDE, /* / on numeric keypad */
+// 0,//KEY_SYSRQ,
+// 0,//KEY_RMENU, /* right Alt */
+// 0,//KEY_PAUSE, /* Pause */
+// 0,//KEY_HOME, /* Home on arrow keypad */
+// 0,//KEY_UP, /* UpArrow on arrow keypad */
+// 0,//KEY_PRIOR, /* PgUp on arrow keypad */
+// 0,//KEY_LEFT, /* LeftArrow on arrow keypad */
+// 0,//KEY_RIGHT, /* RightArrow on arrow keypad */
+// 0,//KEY_END, /* End on arrow keypad */
+// 0,//KEY_DOWN, /* DownArrow on arrow keypad */
+// 0,//KEY_NEXT, /* PgDn on arrow keypad */
+// 0,//KEY_INSERT, /* Insert on arrow keypad */
+// 127,//KEY_DELETE, /* Delete on arrow keypad */
+// 0,//KEY_LWIN, /* Left Windows key */
+// 0,//KEY_RWIN, /* Right Windows key */
+// 0,//KEY_APPS, /* AppMenu key */
+// 0,//KEY_POWER, /* System Power */
+// 0,//KEY_SLEEP, /* System Sleep */
+// 0,//KEY_WAKE, /* System Wake */
+// 0,//KEY_WEBSEARCH, /* Web Search */
+// 0,//KEY_WEBFAVORITES, /* Web Favorites */
+// 0,//KEY_WEBREFRESH, /* Web Refresh */
+// 0,//KEY_WEBSTOP, /* Web Stop */
+// 0,//KEY_WEBFORWARD, /* Web Forward */
+// 0,//KEY_WEBBACK, /* Web Back */
+// 0,//KEY_MYCOMPUTER, /* My Computer */
+// 0,//KEY_MAIL, /* Mail */
+// 0//KEY_MEDIASELECT, /* Media Select */
+// };
+//
+// static INT32 s_ASCIIUpperCase[] =
+// {
+// 0,//KEY_NOKEY = 0x00,
+// 27,//KEY_ESCAPE,
+// '!',//KEY_1,
+// '@',//KEY_2,
+// '#',//KEY_3,
+// '$',//KEY_4,
+// '%',//KEY_5,
+// '^',//KEY_6,
+// '&',//KEY_7,
+// '*',//KEY_8,
+// '(',//KEY_9,
+// ')',//KEY_0,
+// '_',//KEY_MINUS, /* - on main keyboard */
+// '+',//KEY_EQUALS,
+// 8,//KEY_BACK, /* backspace */
+// '\t',//KEY_TAB,
+// 'Q',//KEY_Q,
+// 'W',//KEY_W,
+// 'E',//KEY_E,
+// 'R',//KEY_R,
+// 'T',//KEY_T,
+// 'Y',//KEY_Y,
+// 'U',//KEY_U,
+// 'I',//KEY_I,
+// 'O',//KEY_O,
+// 'P',//KEY_P,
+// '{',//KEY_LBRACKET,
+// '}',//KEY_RBRACKET,
+// '\n',//KEY_RETURN, /* Enter on main keyboard */
+// 0,//KEY_LCONTROL,
+// 'A',//KEY_A,
+// 'S',//KEY_S,
+// 'D',//KEY_D,
+// 'F',//KEY_F,
+// 'G',//KEY_G,
+// 'H',//KEY_H,
+// 'J',//KEY_J,
+// 'K',//KEY_K,
+// 'L',//KEY_L,
+// ':',//KEY_SEMICOLON,
+// '\"',//KEY_APOSTROPHE,
+// '~',//KEY_GRAVE, /* accent grave */
+// 0,//KEY_LSHIFT,
+// '|',//KEY_BACKSLASH,
+// 'Z',//KEY_Z,
+// 'X',//KEY_X,
+// 'C',//KEY_C,
+// 'V',//KEY_V,
+// 'B',//KEY_B,
+// 'N',//KEY_N,
+// 'M',//KEY_M,
+// '<',//KEY_COMMA,
+// '>',//KEY_PERIOD, /* . on main keyboard */
+// '?',//KEY_SLASH, /* / on main keyboard */
+// 0,//KEY_RSHIFT,
+// '*',//KEY_MULTIPLY, /* * on numeric keypad */
+// 0,//KEY_LMENU, /* left Alt */
+// ' ',//KEY_SPACE,
+// 0,//KEY_CAPITAL,
+// 0,//KEY_F1,
+// 0,//KEY_F2,
+// 0,//KEY_F3,
+// 0,//KEY_F4,
+// 0,//KEY_F5,
+// 0,//KEY_F6,
+// 0,//KEY_F7,
+// 0,//KEY_F8,
+// 0,//KEY_F9,
+// 0,//KEY_F10,
+// 0,//KEY_NUMLOCK,
+// 0,//KEY_SCROLL, /* Scroll Lock */
+// '7',//KEY_NUMPAD7,
+// '8',//KEY_NUMPAD8,
+// '9',//KEY_NUMPAD9,
+// '-',//KEY_SUBTRACT, /* - on numeric keypad */
+// '4',//KEY_NUMPAD4,
+// '5',//KEY_NUMPAD5,
+// '6',//KEY_NUMPAD6,
+// '+',//KEY_ADD, /* + on numeric keypad */
+// '1',//KEY_NUMPAD1,
+// '2',//KEY_NUMPAD2,
+// '3',//KEY_NUMPAD3,
+// '0',//KEY_NUMPAD0,
+// '.',//KEY_DECIMAL, /* . on numeric keypad */
+// 0,//KEY_OEM_102, /* <> or \| on RT 102-key keyboard (Non-U.S.) */
+// 0,//KEY_F11,
+// 0,//KEY_F12,
+// 0,//KEY_F13, /* (NEC PC98) */
+// 0,//KEY_F14, /* (NEC PC98) */
+// 0,//KEY_F15, /* (NEC PC98) */
+// 0,//KEY_KANA, /* (Japanese keyboard) */
+// 0,//KEY_ABNT_C1, /* /? on Brazilian keyboard */
+// 0,//KEY_CONVERT, /* (Japanese keyboard) */
+// 0,//KEY_NOCONVERT, /* (Japanese keyboard) */
+// 0,//KEY_YEN, /* (Japanese keyboard) */
+// 0,//KEY_ABNT_C2, /* Numpad . on Brazilian keyboard */
+// 0,//KEY_NUMPADEQUALS, /* = on numeric keypad (NEC PC98) */
+// 0,//KEY_PREVTRACK, /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
+// 0,//KEY_AT, /* (NEC PC98) */
+// 0,//KEY_COLON, /* (NEC PC98) */
+// 0,//KEY_UNDERLINE, /* (NEC PC98) */
+// 0,//KEY_KANJI, /* (Japanese keyboard) */
+// 0,//KEY_STOP, /* (NEC PC98) */
+// 0,//KEY_AX, /* (Japan AX) */
+// 0,//KEY_UNLABELED, /* (J3100) */
+// 0,//KEY_NEXTTRACK, /* Next Track */
+// 0,//KEY_NUMPADENTER, /* Enter on numeric keypad */
+// 0,//KEY_RCONTROL,
+// 0,//KEY_MUTE, /* Mute */
+// 0,//KEY_CALCULATOR, /* Calculator */
+// 0,//KEY_PLAYPAUSE, /* Play / Pause */
+// 0,//KEY_MEDIASTOP, /* Media Stop */
+// 0,//KEY_VOLUMEDOWN, /* Volume - */
+// 0,//KEY_VOLUMEUP, /* Volume + */
+// 0,//KEY_WEBHOME, /* Web home */
+// 0,//KEY_NUMPADCOMMA, /* , on numeric keypad (NEC PC98) */
+// 0,//KEY_DIVIDE, /* / on numeric keypad */
+// 0,//KEY_SYSRQ,
+// 0,//KEY_RMENU, /* right Alt */
+// 0,//KEY_PAUSE, /* Pause */
+// 0,//KEY_HOME, /* Home on arrow keypad */
+// 0,//KEY_UP, /* UpArrow on arrow keypad */
+// 0,//KEY_PRIOR, /* PgUp on arrow keypad */
+// 0,//KEY_LEFT, /* LeftArrow on arrow keypad */
+// 0,//KEY_RIGHT, /* RightArrow on arrow keypad */
+// 0,//KEY_END, /* End on arrow keypad */
+// 0,//KEY_DOWN, /* DownArrow on arrow keypad */
+// 0,//KEY_NEXT, /* PgDn on arrow keypad */
+// 0,//KEY_INSERT, /* Insert on arrow keypad */
+// 127,//KEY_DELETE, /* Delete on arrow keypad */
+// 0,//KEY_LWIN, /* Left Windows key */
+// 0,//KEY_RWIN, /* Right Windows key */
+// 0,//KEY_APPS, /* AppMenu key */
+// 0,//KEY_POWER, /* System Power */
+// 0,//KEY_SLEEP, /* System Sleep */
+// 0,//KEY_WAKE, /* System Wake */
+// 0,//KEY_WEBSEARCH, /* Web Search */
+// 0,//KEY_WEBFAVORITES, /* Web Favorites */
+// 0,//KEY_WEBREFRESH, /* Web Refresh */
+// 0,//KEY_WEBSTOP, /* Web Stop */
+// 0,//KEY_WEBFORWARD, /* Web Forward */
+// 0,//KEY_WEBBACK, /* Web Back */
+// 0,//KEY_MYCOMPUTER, /* My Computer */
+// 0,//KEY_MAIL, /* Mail */
+// 0//KEY_MEDIASELECT, /* Media Select */
+// };
+//
+// if ( inKeyCode < 0 || inKeyCode > KEY_TOTAL_COUNT )
+// return 0;
+//
+// if ( inShift )
+// return s_ASCIIUpperCase[inKeyCode];
+//
+// return s_ASCIILowerCase[inKeyCode];
+//}
+
+//==============================================================================
+/**
+ * Clear the current input frame data, typically after the event has been processed.
+ */
+void CInputEngine::ClearInputFrame()
+{
+ // TODO: SK - To quickly restore functionality, but this is should be in tegra-specific project.
+ m_InputFrame.m_PickValid = false;
+}
+
+//==============================================================================
+/**
+ * Can be more appropriately named and perhaps moved to a COutputEngine class.
+ * to translate event triggered from script back to a "device" event
+ * e.g to simulate clicking on Home button.
+ *
+ * Base class does nothing.
+ */
+BOOL CInputEngine::TranslateEvent(const CHAR *inEvent)
+{
+ Q3DStudio_UNREFERENCED_PARAMETER(inEvent);
+ return false;
+}
+
+//==============================================================================
+/**
+ * Handle firing of keyboard events when a scancode is received.
+ */
+void CInputEngine::HandleKeyboard(INT32 inKeyCode, INT32 inPressed, INT32 inRepeat)
+{
+ if (inKeyCode >= KEY_TOTAL_COUNT || inKeyCode <= KEY_NOKEY) {
+ qCDebug(qt3ds::TRACE_INFO)
+ << "Input: Key input out of range. key code: " << inKeyCode
+ << " pressed: " << inPressed << " repeat: " << inRepeat;
+ }
+ if (m_Application == NULL)
+ return;
+ MarkApplicationDirty();
+
+ SetKeyState(inKeyCode, inPressed ? true : false);
+
+ CPresentation *theActivePresentation(m_Application->GetPrimaryPresentation());
+ if (theActivePresentation) {
+ TElement *theScene = theActivePresentation->GetRoot();
+ UVariant theKeycode;
+ theKeycode.m_INT32 = inKeyCode;
+
+ TEventCommandHash theEvent;
+ if (inPressed) {
+ UINT16 theKeyCount = m_InputFrame.m_KeyInputFrame.GetKeyCount(inKeyCode);
+ QT3DS_ASSERT(theKeyCount >= 1);
+ if (theKeyCount == 1 && inRepeat == 0)
+ theEvent = ON_KEYDOWN;
+ else
+ theEvent = ON_KEYREPEAT;
+ } else {
+ theEvent = ON_KEYUP;
+ }
+
+ theActivePresentation->FireEvent(theEvent, theScene, &theKeycode, NULL, ATTRIBUTETYPE_INT32,
+ ATTRIBUTETYPE_NONE);
+ if (m_InputEventProvider)
+ m_InputEventProvider->AddEvent(
+ CInputEventProvider::SKeyboardEvent(theKeycode.m_INT32, theEvent));
+ }
+}
+
+//==============================================================================
+/**
+ * Handle button events.
+ */
+void CInputEngine::HandleButton(INT32 inButtonCode, INT32 inPressed, INT32 inRepeat)
+{
+ if (inButtonCode >= BUTTON_TOTAL_COUNT || inButtonCode < 0) {
+ qCDebug(qt3ds::TRACE_INFO)
+ << "Input: Key input out of range. key code: " << inButtonCode
+ << " pressed: " << inPressed << " repeat: " << inRepeat;
+ }
+ if (m_Application == NULL)
+ return;
+ MarkApplicationDirty();
+
+ SetButtonState(inButtonCode, inPressed ? true : false);
+
+ CPresentation *theActivePresentation(m_Application->GetPrimaryPresentation());
+ if (theActivePresentation) {
+ TElement *theScene = theActivePresentation->GetRoot();
+ UVariant theButtonCode;
+ theButtonCode.m_INT32 = inButtonCode;
+
+ TEventCommandHash theEvent;
+ if (inPressed) {
+ if (inRepeat == 0)
+ theEvent = ON_BUTTONDOWN;
+ else
+ theEvent = ON_BUTTONREPEAT;
+ } else {
+ theEvent = ON_BUTTONUP;
+ }
+
+ theActivePresentation->FireEvent(theEvent, theScene, &theButtonCode, NULL,
+ ATTRIBUTETYPE_INT32, ATTRIBUTETYPE_NONE);
+ if (m_InputEventProvider)
+ m_InputEventProvider->AddEvent(
+ CInputEventProvider::SButtonEvent(theButtonCode.m_INT32, theEvent));
+ }
+}
+
+//==============================================================================
+/**
+ * Handle joystick events.
+ */
+void CInputEngine::HandleAxis(INT32 inAxisCode, FLOAT inValue)
+{
+ if (m_Application == NULL)
+ return;
+ MarkApplicationDirty();
+ if (m_InputFrame.m_AxisStates[inAxisCode] != inValue) {
+ m_InputFrame.m_AxisStates[inAxisCode] = inValue;
+
+ CPresentation *theActivePresentation(m_Application->GetPrimaryPresentation());
+ if (theActivePresentation) {
+ TElement *theScene = theActivePresentation->GetRoot();
+ UVariant theAxisCode;
+ theAxisCode.m_INT32 = inAxisCode;
+ UVariant theValue;
+ theValue.m_FLOAT = inValue;
+
+ theActivePresentation->FireEvent(ON_AXISMOVED, theScene, &theAxisCode, &theValue,
+ ATTRIBUTETYPE_INT32, ATTRIBUTETYPE_FLOAT);
+ }
+ }
+}
+
+void CInputEngine::MarkApplicationDirty()
+{
+ if (m_Application)
+ m_Application->MarkApplicationDirty();
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSInputEngine.h b/src/runtime/Qt3DSInputEngine.h
new file mode 100644
index 0000000..bf75e5a
--- /dev/null
+++ b/src/runtime/Qt3DSInputEngine.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSPickFrame.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "EASTL/vector.h"
+#include "foundation/Qt3DSDataRef.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace qt3ds {
+namespace runtime {
+ class IApplication;
+}
+}
+namespace Q3DStudio {
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRuntime;
+union UVariant;
+class CInputEventProvider;
+
+//==============================================================================
+/**
+* @class CInputEngine
+* @brief The basic input engine
+*/
+class CInputEngine
+{
+ //==============================================================================
+ // Fields
+ //==============================================================================
+protected:
+ SInputFrame m_InputFrame; ///< The data describing the input state
+ eastl::vector<eastl::pair<float, float>> m_PickInput;
+ bool m_BeginPickInput;
+ qt3ds::runtime::IApplication *m_Application; ///< The Runtime object
+ qt3ds::foundation::NVScopedRefCounted<CInputEventProvider>
+ m_InputEventProvider; ///< The event provider for keyboard
+
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ typedef qt3ds::foundation::NVConstDataRef<const eastl::pair<float, float>> TPickInputList;
+ CInputEngine();
+ virtual ~CInputEngine();
+
+public: // Runtime interfaces
+ void SetApplication(qt3ds::runtime::IApplication *inApplication);
+
+public:
+ // Access
+ virtual SInputFrame &GetInputFrame();
+ virtual SKeyInputFrame &GetKeyInputFrame();
+
+ // Setters
+ // Multitouch is handled by using BeginPickInput, SetPickInput (several times), and EndPickInput
+ // pairs.
+ // The assumption here is that the pick input is static until changed. So to clear it you need
+ // a
+ // begin, end pair without any other pieces.
+ virtual void BeginPickInput();
+ virtual void SetPickInput(FLOAT inX, FLOAT inY, BOOL inValid = true);
+ virtual void EndPickInput();
+ virtual TPickInputList GetPickInput() const;
+ virtual void SetPickFlags(INT32 inFlags);
+ virtual void SetKeyState(INT32 inKeyCode, BOOL inDown);
+ virtual void SetButtonState(INT32 inButtonCode, BOOL inDown);
+ virtual void SetScrollValue(INT32 inFlags, INT16 value);
+ virtual void SetModifierFlag(INT32 inFlag);
+
+ // Keyboard hook
+ virtual void HandleKeyboard(INT32 inKeyCode, INT32 inPressed, INT32 inRepeat = 0);
+
+ // Other input
+ virtual void HandleButton(INT32 inButtonCode, INT32 inPressed, INT32 inRepeat = 0);
+ virtual void HandleAxis(INT32 inAxisCode, FLOAT inValue);
+
+ // Helpers
+ // virtual INT32 ConvertKeyCode( INT32 inKeyCode, BOOL inShift );
+ // TODO: SK - To quickly restore functionality, but this is should be in tegra-specific project.
+ virtual void ClearInputFrame();
+ virtual BOOL TranslateEvent(const CHAR *inEvent);
+
+ void MarkApplicationDirty();
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSInputEventTypes.h b/src/runtime/Qt3DSInputEventTypes.h
new file mode 100644
index 0000000..0b3c398
--- /dev/null
+++ b/src/runtime/Qt3DSInputEventTypes.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSHash.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+// Mouse events
+const TEventCommandHash ON_MOUSEOVER = CHash::HashEventCommand("onMouseOver");
+const TEventCommandHash ON_MOUSEOUT = CHash::HashEventCommand("onMouseOut");
+const TEventCommandHash ON_GROUPEDMOUSEOVER = CHash::HashEventCommand("onGroupedMouseOver");
+const TEventCommandHash ON_GROUPEDMOUSEOUT = CHash::HashEventCommand("onGroupedMouseOut");
+
+// Crude hack to pretend mouse events are gesture events, since gesture events are all you
+// can specify in studio.
+// TODO: Fix properly, preferably by bringing back gesture support
+const TEventCommandHash ON_MOUSEDOWN = CHash::HashEventCommand("onPressureDown");
+const TEventCommandHash ON_MOUSEUP = CHash::HashEventCommand("onPressureUp");
+const TEventCommandHash ON_MOUSECLICK = CHash::HashEventCommand("onTap");
+const TEventCommandHash ON_MOUSEDBLCLICK = CHash::HashEventCommand("onDoubleTap");
+//const TEventCommandHash ON_MOUSEDOWN = CHash::HashEventCommand("onMouseDown");
+//const TEventCommandHash ON_MOUSEUP = CHash::HashEventCommand("onMouseUp");
+//const TEventCommandHash ON_MOUSECLICK = CHash::HashEventCommand("onMouseClick");
+//const TEventCommandHash ON_MOUSEDBLCLICK = CHash::HashEventCommand("onMouseDblClick");
+
+const TEventCommandHash ON_MIDDLEMOUSEDOWN = CHash::HashEventCommand("onMiddleMouseDown");
+const TEventCommandHash ON_MIDDLEMOUSEUP = CHash::HashEventCommand("onMiddleMouseUp");
+const TEventCommandHash ON_MIDDLEMOUSECLICK = CHash::HashEventCommand("onMiddleMouseClick");
+const TEventCommandHash ON_MIDDLEDBLMOUSECLICK = CHash::HashEventCommand("onMiddleMouseDblClick");
+
+const TEventCommandHash ON_RIGHTMOUSEDOWN = CHash::HashEventCommand("onRightMouseDown");
+const TEventCommandHash ON_RIGHTMOUSEUP = CHash::HashEventCommand("onRightMouseUp");
+const TEventCommandHash ON_RIGHTMOUSECLICK = CHash::HashEventCommand("onRightMouseClick");
+const TEventCommandHash ON_RIGHTDBLMOUSECLICK = CHash::HashEventCommand("onRightMouseDblClick");
+
+const TEventCommandHash ON_HORIZONTALSCROLLWHEEL =
+ CHash::HashEventCommand("onHorizontalScrollWheel");
+const TEventCommandHash ON_VERTICALSCROLLWHEEL = CHash::HashEventCommand("onVerticalScrollWheel");
+
+const TEventCommandHash ON_KEYUP =
+ CHash::HashEventCommand("onKeyUp"); ///< Studio's keyboard event string
+const TEventCommandHash ON_KEYDOWN =
+ CHash::HashEventCommand("onKeyDown"); ///< Studio's keyboard event string
+const TEventCommandHash ON_KEYREPEAT =
+ CHash::HashEventCommand("onKeyRepeat"); ///< Studio's keyboard event string
+
+const TEventCommandHash ON_BUTTONUP = CHash::HashEventCommand("onButtonUp");
+const TEventCommandHash ON_BUTTONDOWN = CHash::HashEventCommand("onButtonDown");
+const TEventCommandHash ON_BUTTONREPEAT = CHash::HashEventCommand("onButtonRepeat");
+
+const TEventCommandHash ON_AXISMOVED = CHash::HashEventCommand("onAxisMoved");
+
+// Used by UIContract
+const TEventCommandHash ON_LEFT = CHash::HashEventCommand("onLeft");
+const TEventCommandHash ON_RIGHT = CHash::HashEventCommand("onRight");
+const TEventCommandHash ON_UP = CHash::HashEventCommand("onUp");
+const TEventCommandHash ON_DOWN = CHash::HashEventCommand("onDown");
+const TEventCommandHash ON_BACK = CHash::HashEventCommand("onBack");
+const TEventCommandHash ON_SELECT = CHash::HashEventCommand("onSelect");
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSInputFrame.h b/src/runtime/Qt3DSInputFrame.h
new file mode 100644
index 0000000..f80f3eb
--- /dev/null
+++ b/src/runtime/Qt3DSInputFrame.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSPlatformSpecific.h"
+#include "Qt3DSInputDefs.h"
+
+#include <EASTL/map.h>
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Input structure defining the values for keyboard input
+ */
+struct SKeyInputFrame
+{
+ UINT16 m_ModifierFlags; ///< Keyboard modifier state flags
+ typedef eastl::map<INT32, UINT16> MAP_KEYCODE_COUNT;
+ MAP_KEYCODE_COUNT
+ m_KeyStates; ///< Keycode map representing number of frames since the keys are down
+ ///< 0 - up, 255 - down for more than 254 frames
+
+ SKeyInputFrame()
+ : m_ModifierFlags(MODIFIER_NONE)
+ {
+ }
+
+ UINT16 GetKeyCount(INT32 inKeyCode)
+ {
+ MAP_KEYCODE_COUNT::iterator theIter = m_KeyStates.find(inKeyCode);
+ return theIter != m_KeyStates.end() ? theIter->second : 0;
+ }
+};
+
+//==============================================================================
+/**
+ * Input structure defining the values fed into input processing
+ */
+struct SInputFrame
+{
+ SKeyInputFrame m_KeyInputFrame; ///< Data for keyboard input
+
+ typedef eastl::map<INT32, UINT16> MAP_BUTTONCODE_COUNT;
+ MAP_BUTTONCODE_COUNT m_ButtonStates;
+
+ FLOAT m_AxisStates[AXIS_TOTAL_COUNT];
+
+ FLOAT m_PickX; ///< Horizontal screen location
+ FLOAT m_PickY; ///< Vertical screen location
+
+ INT32 m_MouseFlags; ///< Mouse button state flags
+
+ // SK - gesture-specific variables should NOT be in core runtime, its tegra-specific
+ INT32 m_GestureID; ///< a running counter/unique-id
+ INT32 m_GestureKind; ///< Gesture state flags
+
+ /* different gestures will interpret as different values. could be:
+ * DRAG: delta position
+ * FLICK: velocities
+ * ZOOM: second finger or quantized delta */
+ INT16 m_DeltaX;
+ INT16 m_DeltaY;
+
+ BOOL m_PickValid; ///< X and Y are valid inputs
+ INT16 m_ScrollValue; //< Value of scrollwheel scrolled
+
+ CHAR m_Padding[1];
+
+ SInputFrame()
+ : m_PickX(0)
+ , m_PickY(0)
+ , m_MouseFlags(NO_INPUT)
+ , m_GestureID(0)
+ , m_GestureKind(0)
+ , m_DeltaX(0)
+ , m_DeltaY(0)
+ , m_PickValid(false)
+ {
+ Q3DStudio_memset(m_AxisStates, 0, sizeof(m_AxisStates));
+ }
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSKernelTypes.h b/src/runtime/Qt3DSKernelTypes.h
new file mode 100644
index 0000000..0ad4ec6
--- /dev/null
+++ b/src/runtime/Qt3DSKernelTypes.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSArray.h"
+
+namespace qt3ds {
+namespace runtime {
+ namespace element {
+ struct SElement;
+ struct SComponent;
+ }
+}
+}
+
+namespace Q3DStudio {
+typedef qt3ds::runtime::element::SElement TElement;
+typedef qt3ds::runtime::element::SComponent TComponent;
+}
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CString;
+
+//==============================================================================
+// Typedefs
+//==============================================================================
+typedef CArray<TElement *> TElementList; ///< Dynamic list of CElements
+typedef CArray<const CString *> TEventList; ///< Dynamic list of CStrings
+typedef CArray<INT32> TAssociationList; ///< Dynamic list of IDs used for the association process
+
+typedef UINT32 TEventCommandHash; ///< Value from CHash::Hash31
+typedef UINT32 TStringHash; ///< Value from CHash::Hash
+typedef UINT32 TAttributeHash; ///< Value from CHash::Hash27
+typedef UINT32 THashValue; ///< 32 bit value indicating a hash
+
+//==============================================================================
+// Enumerations
+//==============================================================================
+
+/// Each attribute knows the type of its value and uses 4 bits for this enum
+enum EAttributeType {
+ ATTRIBUTETYPE_NONE = 0,
+ ATTRIBUTETYPE_INT32,
+ ATTRIBUTETYPE_HASH,
+ ATTRIBUTETYPE_FLOAT,
+ ATTRIBUTETYPE_BOOL,
+ ATTRIBUTETYPE_STRING,
+ ATTRIBUTETYPE_POINTER,
+ ATTRIBUTETYPE_ELEMENTREF,
+ ATTRIBUTETYPE_DATADRIVEN_PARENT,
+ ATTRIBUTETYPE_DATADRIVEN_CHILD,
+ ATTRIBUTETYPE_FLOAT4,
+ ATTRIBUTETYPE_FLOAT3,
+ ATTRIBUTETYPE_FLOAT2,
+ ATTRIBUTETYPE_DATAINPUT_TIMELINE,
+ ATTRIBUTETYPE_DATAINPUT_SLIDE,
+ ATTRIBUTETYPECOUNT
+};
+
+/// Elements represent various types of objects
+enum EElementType {
+ ELEMENTTYPE_UNKNOWN = 0,
+ ELEMENTTYPE_NODE,
+ ELEMENTTYPE_CAMERA,
+ ELEMENTTYPE_LIGHT,
+ ELEMENTTYPE_TEXT,
+ ELEMENTTYPE_MATERIAL,
+ ELEMENTTYPE_TEXTURE,
+ ELEMENTTYPE_COMPONENT,
+ ELEMENTTYPE_BEHAVIOR,
+ ELEMENTTYPE_PATH,
+ ELEMENTTYPE_PATHANCHORPOINT,
+ ELEMENTTYPE_SUBPATH,
+ ELEMENTTYPECOUNT
+};
+
+//==============================================================================
+// Enumerations
+//==============================================================================
+
+/// Various bit flags packed into a single 16bit space.
+enum EElementFlag {
+ // Persistent flags (set only at creation)
+ ELEMENTFLAG_COMPONENT = 1, ///< Element is a component
+ ELEMENTFLAG_TIMELINE = 1 << 1, ///< Element has start time and duration attributes
+ ELEMENTFLAG_CLONE = 1 << 2, ///< Element is a clone
+ ELEMENTFLAG_ATTRIBUTELOCK =
+ 1 << 3, ///< No more attributes can be added (always true during runtime)
+
+ // Configuration flags (seldom changed)
+ ELEMENTFLAG_SCRIPTCALLBACKS = 1
+ << 4, ///< At least one script has requested notify on onActivate, onDeactivate, onUpdate
+ ELEMENTFLAG_SCRIPTINITIALIZE = 1
+ << 5, ///< At least one script has requested notify on onInitialize
+ ELEMENTFLAG_REGISTEREDFORATTRIBUTECHANGE = 1 << 6, ///< Element monitored for attribute change
+ ELEMENTFLAG_REGISTEREDFOREVENTCALLBACK = 1 << 7, ///< Element registered for event callbacks
+ ELEMENTFLAG_PICKENABLED = 1 << 8, ///< At least one script or action has registered for mouse
+ ///events based on this element
+
+ // Runtime flags
+ ELEMENTFLAG_GLOBALACTIVE =
+ 1 << 9, ///< Combination active flag based on explicit, time and parent active
+ ELEMENTFLAG_EXPLICITACTIVE = 1 << 10, ///< Explicit On/Off switch on each element
+ ELEMENTFLAG_PLAYTHROUGH = 1 << 11, ///< Playthrough slides enabled if this is a component
+ ELEMENTFLAG_DIRTY = 1 << 12, ///< Attributes or user visible flags have changed
+
+ // Flags used by the activation manager
+ ELEMENTFLAG_AMGR_TIMEACTIVE = 1 << 13, ///< Is the element alive according to time information
+};
+
+// Four byte aligned time unit structure.
+struct SAlignedTimeUnit
+{
+ UINT32 m_LowWord;
+ UINT32 m_HighWord;
+
+ SAlignedTimeUnit() {}
+ SAlignedTimeUnit(const TTimeUnit &inUnit);
+ SAlignedTimeUnit(const SAlignedTimeUnit &inOther)
+ : m_LowWord(inOther.m_LowWord)
+ , m_HighWord(inOther.m_HighWord)
+ {
+ }
+ SAlignedTimeUnit &operator=(const SAlignedTimeUnit &inOther)
+ {
+ m_LowWord = inOther.m_LowWord;
+ m_HighWord = inOther.m_HighWord;
+ return *this;
+ }
+ operator TTimeUnit() const;
+
+ void operator-=(const TTimeUnit &inTime);
+ void operator%=(const TTimeUnit &inTime);
+};
+
+//==============================================================================
+// Defines
+//==============================================================================
+#define Q3DStudio_ATTRIBUTEKEYBITS 26 /* Number of bits to store attributes index */
+#define Q3DStudio_ATTRIBUTEKEYMASK 0x03ffffff /* Bit mask covering 26 bits */
+
+//==============================================================================
+// Structs
+//==============================================================================
+
+/// Each element has a number of attributes associated with it - This is the key.
+struct SAttributeKey
+{
+ TAttributeHash m_Hash : Q3DStudio_ATTRIBUTEKEYBITS; ///< hash of attribute name
+ TAttributeHash m_Type : 4; ///< Attribute type such as INT32, FLOAT, STRING
+ TAttributeHash m_Dirty : 1; ///< Dirty bit signalling that the attribute value has been modified
+ TAttributeHash
+ m_DontOptimize : 1; ///< Used by the exporter/optimizer to flag attributes as un-optimizable
+
+ void Convert(const UINT32 &inKey)
+ {
+ m_Hash = inKey & Q3DStudio_ATTRIBUTEKEYMASK;
+ m_Type = (inKey & 0x3c000000) >> Q3DStudio_ATTRIBUTEKEYBITS;
+ m_Dirty = (inKey & 0x40000000) >> (Q3DStudio_ATTRIBUTEKEYBITS + 4);
+ m_DontOptimize = (inKey & 0x80000000) >> (Q3DStudio_ATTRIBUTEKEYBITS + 5);
+ }
+};
+
+/// The structure to store a memory pool for clones.
+/// The actual data pool will start at the address after these 8 bytes.
+struct SClone
+{
+ SClone *m_Next; ///< Pointer to the next block of memory
+ UINT32 m_Size; ///< Size of the current block of memory
+};
+
+//==============================================================================
+// Unions
+//==============================================================================
+
+/// Each element has a number of attributes associated with it - This is the value.
+union UVariant {
+ INT32 m_INT32; // Integer representation
+ FLOAT m_FLOAT; // Float representation
+ THashValue m_Hash; // Explicit hash representation
+ UINT32 m_StringHandle; // Handle into the IStringTable member of the presentation
+ void *m_VoidPointer; // Generic data Pointer
+ UINT32 m_ElementHandle; // Element handle. Resolve using IApplication object.
+ FLOAT m_FLOAT3[3]; // Vector 3 representation
+ FLOAT m_FLOAT4[4]; // Vector 4 representation
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSLogicSystem.cpp b/src/runtime/Qt3DSLogicSystem.cpp
new file mode 100644
index 0000000..4a8341b
--- /dev/null
+++ b/src/runtime/Qt3DSLogicSystem.cpp
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+#include "Qt3DSLogicSystem.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSPool.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "Qt3DSIPresentation.h"
+#include "Qt3DSApplication.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/IOStreams.h"
+
+using namespace qt3ds::runtime;
+using namespace qt3ds::foundation;
+using namespace qt3ds;
+using namespace qt3ds::runtime::element;
+
+namespace {
+struct SLogicKey
+{
+ QT3DSU32 m_ElementHandle;
+ Q3DStudio::TEventCommandHash m_CommandHash;
+ SLogicKey(QT3DSU32 hdl = 0, Q3DStudio::TEventCommandHash hash = 0)
+ : m_ElementHandle(hdl)
+ , m_CommandHash(hash)
+ {
+ }
+
+ bool operator==(const SLogicKey &other) const
+ {
+ return m_ElementHandle == other.m_ElementHandle && m_CommandHash == other.m_CommandHash;
+ }
+ size_t hash() const
+ {
+ return eastl::hash<QT3DSU32>()(m_ElementHandle) ^ eastl::hash<QT3DSU32>()((QT3DSU32)m_CommandHash);
+ }
+};
+
+struct SLogicData
+{
+ SLogicData *m_NextLogicData;
+ QT3DSU32 m_Owner;
+ QT3DSU32 m_Target;
+ Q3DStudio::TEventCommandHash m_Type;
+ Q3DStudio::UVariant m_Arg1;
+ Q3DStudio::UVariant m_Arg2;
+ QT3DSI32 m_Id;
+ bool m_Active;
+
+ SLogicData()
+ : m_NextLogicData(NULL)
+ , m_Owner(0)
+ , m_Target(0)
+ , m_Type(0)
+ , m_Id(0)
+ , m_Active(false)
+ {
+ m_Arg1.m_INT32 = 0;
+ m_Arg2.m_INT32 = 0;
+ }
+
+ SLogicData(QT3DSU32 owner, QT3DSU32 target, Q3DStudio::TEventCommandHash type, Q3DStudio::UVariant a1,
+ Q3DStudio::UVariant a2, bool active, QT3DSI32 inId)
+ : m_NextLogicData(NULL)
+ , m_Owner(owner)
+ , m_Target(target)
+ , m_Type(type)
+ , m_Arg1(a1)
+ , m_Arg2(a2)
+ , m_Id(inId)
+ , m_Active(active)
+ {
+ }
+};
+
+DEFINE_INVASIVE_SINGLE_LIST(LogicData);
+IMPLEMENT_INVASIVE_SINGLE_LIST(LogicData, m_NextLogicData);
+}
+
+namespace eastl {
+template <>
+struct hash<SLogicKey>
+{
+ size_t operator()(const SLogicKey &inKey) const { return inKey.hash(); }
+};
+}
+
+namespace {
+
+struct SLogicSystem : public ILogicSystem
+{
+ typedef nvhash_map<SLogicKey, TLogicDataList> TLogicKeyHash;
+ typedef nvhash_map<QT3DSI32, SLogicKey> TIdLogicKeyHash;
+ typedef Pool<SLogicData, ForwardingAllocator> TLogicDataPool;
+
+ NVFoundationBase &m_Foundation;
+
+ TLogicKeyHash m_LogicKeys;
+ TIdLogicKeyHash m_IdToLogicKeys;
+ TLogicDataPool m_LogicDataPool;
+ QT3DSI32 m_NextId;
+ NVDataRef<QT3DSU8> m_LoadData;
+
+ QT3DSI32 m_RefCount;
+
+ SLogicSystem(NVFoundationBase &inFoundation)
+ : m_Foundation(inFoundation)
+ , m_LogicKeys(inFoundation.getAllocator(), "m_LogicKeys")
+ , m_IdToLogicKeys(inFoundation.getAllocator(), "m_IdToLogicKeys")
+ , m_LogicDataPool(ForwardingAllocator(inFoundation.getAllocator(), "m_LogicDataPool"))
+ , m_NextId(1)
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Foundation.getAllocator());
+ NVDelete(alloc, this);
+ }
+ }
+ void IncrementNextId()
+ {
+ ++m_NextId;
+ if (m_NextId == 0)
+ ++m_NextId;
+ }
+ static QT3DSU32 GetHandle(element::SElement *elem)
+ {
+ if (elem)
+ return elem->GetHandle();
+ return 0;
+ }
+ // returns the action id
+ QT3DSI32 AddAction(element::SElement &inTrigger,
+ Q3DStudio::TEventCommandHash inEventNameHash,
+ element::SElement *inTarget, element::SElement *inOwner,
+ Q3DStudio::TEventCommandHash inType, Q3DStudio::UVariant inArg1,
+ Q3DStudio::UVariant inArg2, bool inActive) override
+ {
+ SLogicKey theKey(inTrigger.GetHandle(), inEventNameHash);
+ eastl::pair<TLogicKeyHash::iterator, bool> inserter =
+ m_LogicKeys.insert(eastl::make_pair(theKey, TLogicDataList()));
+
+ eastl::pair<TIdLogicKeyHash::iterator, bool> idInserter;
+ for (idInserter = m_IdToLogicKeys.insert(eastl::make_pair(m_NextId, theKey));
+ idInserter.second == false;
+ idInserter = m_IdToLogicKeys.insert(eastl::make_pair(m_NextId, theKey))) {
+ IncrementNextId();
+ }
+ IncrementNextId();
+
+ SLogicData *theData = m_LogicDataPool.construct(__FILE__, __LINE__);
+ *theData = SLogicData(GetHandle(inOwner), GetHandle(inTarget), inType, inArg1, inArg2,
+ inActive, idInserter.first->first);
+ inserter.first->second.push_back(*theData);
+ return idInserter.first->first;
+ }
+
+ void OnEvent(Q3DStudio::TEventCommandHash inEventName, element::SElement &inTarget,
+ Q3DStudio::IPresentation &inPresentation) const override
+ {
+ SLogicKey theKey(inTarget.GetHandle(), inEventName);
+ TLogicKeyHash::const_iterator iter = m_LogicKeys.find(theKey);
+ if (iter != m_LogicKeys.end()) {
+ qt3ds::runtime::IApplication &theApplication = inPresentation.GetApplication();
+ qt3ds::runtime::IElementAllocator &theElemAllocator =
+ theApplication.GetElementAllocator();
+ for (TLogicDataList::const_iterator listIter = iter->second.begin(),
+ endIter = iter->second.end();
+ listIter != endIter; ++listIter) {
+ if (listIter->m_Active) {
+ inPresentation.FireCommand(
+ listIter->m_Type, theElemAllocator.FindElementByHandle(listIter->m_Target),
+ &listIter->m_Arg1, &listIter->m_Arg2);
+ }
+ }
+ }
+ }
+
+ void SetActive(QT3DSI32 inActionIndex, bool inActive, IElementAllocator &inElemAllocator) override
+ {
+ TIdLogicKeyHash::iterator iter = m_IdToLogicKeys.find(inActionIndex);
+ if (iter != m_IdToLogicKeys.end()) {
+ TLogicKeyHash::iterator logicIter = m_LogicKeys.find(iter->second);
+ if (logicIter != m_LogicKeys.end()) {
+ for (TLogicDataList::iterator listIter = logicIter->second.begin(),
+ listEnd = logicIter->second.end();
+ listIter != listEnd; ++listIter)
+ if (listIter->m_Id == inActionIndex) {
+ listIter->m_Active = inActive;
+ if (IApplication::isPickingEvent(logicIter->first.m_CommandHash)) {
+ SElement *theElement = inElemAllocator.FindElementByHandle(
+ logicIter->first.m_ElementHandle);
+ if (theElement && inActive)
+ theElement->SetFlag(Q3DStudio::ELEMENTFLAG_PICKENABLED, true);
+ }
+ }
+ }
+ }
+ }
+
+ void SaveBinaryData(qt3ds::foundation::IOutStream &ioStream) override
+ {
+ qt3ds::foundation::SWriteBuffer theWriter(m_Foundation.getAllocator(), "WriteBuffer");
+ theWriter.write(m_NextId);
+ theWriter.write((QT3DSU32)m_LogicKeys.size());
+ for (TLogicKeyHash::iterator iter = m_LogicKeys.begin(), end = m_LogicKeys.end();
+ iter != end; ++iter) {
+ if (iter->second.m_Head == NULL)
+ continue;
+
+ theWriter.write(iter->first);
+ QT3DSU32 datumOffset = theWriter.size();
+ theWriter.write((QT3DSU32)0);
+ QT3DSU32 datumCount = 0;
+ for (SLogicData *theDatum = iter->second.m_Head; theDatum;
+ theDatum = theDatum->m_NextLogicData, ++datumCount) {
+ theWriter.write(*theDatum);
+ }
+ QT3DSU32 *countPtr = reinterpret_cast<QT3DSU32 *>(theWriter.begin() + datumOffset);
+ *countPtr = datumCount;
+ }
+ ioStream.Write(theWriter.begin(), theWriter.size());
+ }
+
+ void LoadBinaryData(NVDataRef<QT3DSU8> inLoadData) override
+ {
+ m_LoadData = inLoadData;
+ SDataReader theReader(inLoadData.begin(), inLoadData.end());
+ m_NextId = *theReader.Load<QT3DSI32>();
+ QT3DSU32 numKeys = *theReader.Load<QT3DSU32>();
+ for (QT3DSU32 idx = 0, end = numKeys; idx < end; ++idx) {
+ SLogicKey theKey = *theReader.Load<SLogicKey>();
+ QT3DSU32 numActions = *theReader.Load<QT3DSU32>();
+ if (numActions == 0)
+ continue;
+
+ TLogicKeyHash::iterator inserter =
+ m_LogicKeys.insert(eastl::make_pair(theKey, TLogicDataList())).first;
+ SLogicData *lastDatum = NULL;
+ for (QT3DSU32 actIdx = 0, actEnd = numActions; actIdx < actEnd; ++actIdx) {
+ SLogicData *nextDatum = theReader.Load<SLogicData>();
+ if (lastDatum == NULL)
+ inserter->second.m_Head = nextDatum;
+ else
+ lastDatum->m_NextLogicData = nextDatum;
+ lastDatum = nextDatum;
+ m_IdToLogicKeys.insert(eastl::make_pair(nextDatum->m_Id, theKey));
+ }
+ if (lastDatum)
+ lastDatum->m_NextLogicData = NULL;
+ }
+ }
+};
+}
+
+ILogicSystem &ILogicSystem::CreateLogicSystem(NVFoundationBase &inFnd)
+{
+ return *QT3DS_NEW(inFnd.getAllocator(), SLogicSystem)(inFnd);
+}
diff --git a/src/runtime/Qt3DSLogicSystem.h b/src/runtime/Qt3DSLogicSystem.h
new file mode 100644
index 0000000..af13c91
--- /dev/null
+++ b/src/runtime/Qt3DSLogicSystem.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+#pragma once
+#ifndef QT3DS_LOGIC_SYSTEM_H
+#define QT3DS_LOGIC_SYSTEM_H
+#include "Qt3DSElementSystem.h"
+
+namespace qt3ds {
+namespace runtime {
+
+ class ILogicSystem : public NVRefCounted
+ {
+ public:
+ // returns the action id
+ virtual QT3DSI32 AddAction(element::SElement &inTrigger,
+ Q3DStudio::TEventCommandHash inEventNameHash,
+ element::SElement *inTarget, element::SElement *inOwner,
+ Q3DStudio::TEventCommandHash inType, Q3DStudio::UVariant inArg1,
+ Q3DStudio::UVariant inArg2, bool inActive) = 0;
+
+ virtual void OnEvent(Q3DStudio::TEventCommandHash inEventName, element::SElement &inTarget,
+ Q3DStudio::IPresentation &inPresentation) const = 0;
+ virtual void SetActive(QT3DSI32 inActionIndex, bool inActive,
+ IElementAllocator &inElemAllocator) = 0;
+
+ virtual void SaveBinaryData(qt3ds::foundation::IOutStream &ioStream) = 0;
+ virtual void LoadBinaryData(NVDataRef<QT3DSU8> inLoadData) = 0;
+
+ static ILogicSystem &CreateLogicSystem(NVFoundationBase &inFnd);
+ };
+}
+}
+#endif \ No newline at end of file
diff --git a/src/runtime/Qt3DSOutputMemoryStream.cpp b/src/runtime/Qt3DSOutputMemoryStream.cpp
new file mode 100644
index 0000000..06d0eee
--- /dev/null
+++ b/src/runtime/Qt3DSOutputMemoryStream.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSOutputMemoryStream.h"
+#include "Qt3DSHash.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * XXX
+ */
+COutputMemoryStream::COutputMemoryStream()
+{
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+COutputMemoryStream::~COutputMemoryStream()
+{
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+void COutputMemoryStream::Reset()
+{
+ m_Data.clear();
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+COutputMemoryStream &COutputMemoryStream::operator<<(const COutputMemoryStream &inStream)
+{
+ size_t theSize = inStream.m_Data.size();
+ for (size_t theCount = 0; theCount < theSize; ++theCount)
+ m_Data.push_back(inStream.m_Data[theCount]);
+ return *this;
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+void COutputMemoryStream::WriteStringHash(const CHAR *inItem)
+{
+ TStringHash theHash = CHash::HashString(inItem);
+ // DEBUG
+ //{FILE*fp=fopen("hashed.txt","a");if(fp){fprintf(fp,"%ul %s\n",theHash,inItem);fclose(fp);}}
+ operator<<(theHash);
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+void COutputMemoryStream::WriteEventCommandHash(const CHAR *inItem)
+{
+ TStringHash theHash = CHash::HashEventCommand(inItem);
+ // DEBUG
+ //{FILE*fp=fopen("hashed.txt","a");if(fp){fprintf(fp,"%ul %s\n",theHash,inItem);fclose(fp);}}
+ operator<<(theHash);
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+COutputMemoryStream &COutputMemoryStream::operator<<(const std::string &inString)
+{
+ const UINT8 *thePtr = reinterpret_cast<const UINT8 *>(inString.c_str());
+ for (size_t theCounter = 0; theCounter < inString.size(); ++theCounter) {
+ m_Data.push_back(*thePtr);
+ ++thePtr;
+ }
+
+ return *this;
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+COutputMemoryStream &COutputMemoryStream::operator<<(const EAttributeType inType)
+{
+ UINT8 theType = static_cast<UINT8>(inType);
+ m_Data.push_back(theType);
+
+ return *this;
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+UINT32 COutputMemoryStream::GetSize() const
+{
+ return static_cast<UINT32>(m_Data.size());
+}
+
+//==============================================================================
+/**
+ * XXX
+ */
+const UINT8 *COutputMemoryStream::GetData() const
+{
+ return &m_Data[0];
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSOutputMemoryStream.h b/src/runtime/Qt3DSOutputMemoryStream.h
new file mode 100644
index 0000000..da28245
--- /dev/null
+++ b/src/runtime/Qt3DSOutputMemoryStream.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSHash.h"
+#ifdef _WIN32
+#pragma warning(push)
+#pragma warning(disable : 4820) // X bytes padding added after data member
+#pragma warning( \
+ disable : 4061) // enumerator X in switch of enum Y is not explicitly handled by a case label
+#pragma warning(disable : 4062) // enumerator X in switch of enum Y is not handled
+#pragma warning(disable : 4548) // xlocale warnings
+#pragma warning( \
+ disable : 4738) // storing 32-bit float result in memory, possible loss of performance
+#endif
+#include <vector>
+#include <map>
+#include <set>
+#include <string>
+
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Class
+//==============================================================================
+class COutputMemoryStream
+{
+ //==============================================================================
+ // Fields
+ //==============================================================================
+public:
+ std::vector<UINT8> m_Data;
+
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ COutputMemoryStream();
+ virtual ~COutputMemoryStream();
+
+public: // Access
+ void Reset();
+ UINT32 GetSize() const;
+ const UINT8 *GetData() const;
+
+public: // Output
+ void WriteStringHash(const CHAR *inItem);
+ void WriteEventCommandHash(const CHAR *inItem);
+
+public: // Operators
+ COutputMemoryStream &operator<<(const COutputMemoryStream &inStream);
+ COutputMemoryStream &operator<<(const std::string &inString);
+ COutputMemoryStream &operator<<(const EAttributeType inType);
+
+ //==============================================================================
+ // Template Methods
+ //==============================================================================
+public:
+ template <typename T>
+ COutputMemoryStream &operator<<(const T &inItem)
+ {
+ size_t theSize = sizeof(T);
+ const UINT8 *thePtr = reinterpret_cast<const UINT8 *>(&inItem);
+ for (size_t theCounter = 0; theCounter < theSize; ++theCounter) {
+ m_Data.push_back(*thePtr);
+ ++thePtr;
+ }
+ return *this;
+ }
+
+ template <typename T>
+ void SetData(const T &inItem, UINT32 inByteOffset)
+ {
+ UINT8 *theSourcePtr = &m_Data[inByteOffset];
+ size_t theSize = sizeof(T);
+ const UINT8 *thePtr = reinterpret_cast<const UINT8 *>(&inItem);
+ for (size_t theCounter = 0; theCounter < theSize; ++theCounter) {
+ *theSourcePtr = *thePtr;
+ ++thePtr;
+ ++theSourcePtr;
+ }
+ }
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSParametersSystem.cpp b/src/runtime/Qt3DSParametersSystem.cpp
new file mode 100644
index 0000000..84b642c
--- /dev/null
+++ b/src/runtime/Qt3DSParametersSystem.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+#include "Qt3DSParametersSystem.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSIndexableLinkedList.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/IOStreams.h"
+
+using namespace qt3ds::runtime;
+
+namespace {
+
+struct SParameterGroup
+{
+ enum {
+ NumParametersPerGroup = 4,
+ };
+ TIdValuePair m_Data[NumParametersPerGroup];
+ SParameterGroup *m_NextNode;
+
+ SParameterGroup()
+ : m_NextNode(NULL)
+ {
+ }
+};
+
+struct SParameterGroupEntry
+{
+ QT3DSU32 m_ParameterCount;
+ SParameterGroup *m_FirstGroup;
+ SParameterGroupEntry()
+ : m_ParameterCount(0)
+ , m_FirstGroup(NULL)
+ {
+ }
+};
+
+typedef IndexableLinkedList<SParameterGroup, TIdValuePair, SParameterGroup::NumParametersPerGroup>
+ TParametersList;
+
+struct SParamSystem : public IParametersSystem
+{
+ typedef nvhash_map<QT3DSI32, SParameterGroupEntry> TIdGroupHash;
+
+ NVFoundationBase &m_Foundation;
+ TParametersList::TPoolType m_ParametersPool;
+ TIdGroupHash m_Groups;
+ QT3DSI32 m_NextId;
+ NVDataRef<QT3DSU8> m_LoadData;
+ QT3DSI32 m_RefCount;
+
+ SParamSystem(NVFoundationBase &fnd)
+ : m_Foundation(fnd)
+ , m_ParametersPool(ForwardingAllocator(fnd.getAllocator(), "ParametersPool"))
+ , m_Groups(fnd.getAllocator(), "m_Groups")
+ , m_NextId(1)
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc = m_Foundation.getAllocator();
+ NVDelete(alloc, this);
+ }
+ }
+
+ void IncrementId()
+ {
+ ++m_NextId;
+ if (!m_NextId)
+ ++m_NextId;
+ }
+
+ QT3DSI32 CreateParameterGroup() override
+ {
+ eastl::pair<TIdGroupHash::iterator, bool> inserter =
+ m_Groups.insert(eastl::make_pair(m_NextId, SParameterGroupEntry()));
+ while (inserter.second == false) {
+ IncrementId();
+ inserter = m_Groups.insert(eastl::make_pair(m_NextId, SParameterGroupEntry()));
+ }
+ return inserter.first->first;
+ }
+
+ void AddParameter(QT3DSI32 inGroup, QT3DSU32 inNameHash, Q3DStudio::UVariant inParam) override
+ {
+ TIdGroupHash::iterator iter = m_Groups.find(inGroup);
+ if (iter == m_Groups.end()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ SParameterGroupEntry &theEntry(iter->second);
+ TIdValuePair &thePair = TParametersList::Create(
+ theEntry.m_FirstGroup, theEntry.m_ParameterCount, m_ParametersPool);
+ thePair = eastl::make_pair(inNameHash, inParam);
+ }
+
+ QT3DSU32 GetNumParameters(QT3DSI32 inGroup) const override
+ {
+ TIdGroupHash::const_iterator iter = m_Groups.find(inGroup);
+ if (iter == m_Groups.end()) {
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+
+ const SParameterGroupEntry &theEntry(iter->second);
+ return theEntry.m_ParameterCount;
+ }
+ TIdValuePair GetParameter(QT3DSI32 inGroup, QT3DSU32 inIndex) const override
+ {
+ Q3DStudio::UVariant dummyValue;
+ dummyValue.m_INT32 = 0;
+ TIdValuePair retval = TIdValuePair(0, dummyValue);
+
+ TIdGroupHash::const_iterator iter = m_Groups.find(inGroup);
+ if (iter == m_Groups.end()) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+
+ const SParameterGroupEntry &theEntry(iter->second);
+
+ if (inIndex < theEntry.m_ParameterCount)
+ return TParametersList::GetObjAtIdx(theEntry.m_FirstGroup, inIndex);
+
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+};
+}
+
+IParametersSystem &IParametersSystem::CreateParametersSystem(NVFoundationBase &fnd)
+{
+ return *QT3DS_NEW(fnd.getAllocator(), SParamSystem)(fnd);
+}
diff --git a/src/runtime/Qt3DSParametersSystem.h b/src/runtime/Qt3DSParametersSystem.h
new file mode 100644
index 0000000..50a4652
--- /dev/null
+++ b/src/runtime/Qt3DSParametersSystem.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 QT3DS_PARAMETERS_SYSTEM_H
+#define QT3DS_PARAMETERS_SYSTEM_H
+#pragma once
+#include "Qt3DSKernelTypes.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "EASTL/utility.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSDataRef.h"
+
+namespace qt3ds {
+namespace foundation {
+ class IOutStream;
+}
+}
+
+namespace qt3ds {
+namespace runtime {
+ using namespace qt3ds;
+ using namespace qt3ds::foundation;
+
+ typedef eastl::pair<QT3DSU32, Q3DStudio::UVariant> TIdValuePair;
+
+ class IParametersSystem : public NVRefCounted
+ {
+ public:
+ virtual QT3DSI32 CreateParameterGroup() = 0;
+ virtual void AddParameter(QT3DSI32 inGroup, QT3DSU32 inNameHash, Q3DStudio::UVariant inParam) = 0;
+
+ virtual QT3DSU32 GetNumParameters(QT3DSI32 inGroupId) const = 0;
+ virtual TIdValuePair GetParameter(QT3DSI32 inGroupId, QT3DSU32 inIndex) const = 0;
+
+ static IParametersSystem &CreateParametersSystem(NVFoundationBase &inFoundation);
+ };
+}
+}
+
+#endif
diff --git a/src/runtime/Qt3DSPickFrame.h b/src/runtime/Qt3DSPickFrame.h
new file mode 100644
index 0000000..15da72c
--- /dev/null
+++ b/src/runtime/Qt3DSPickFrame.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSInputFrame.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class IPresentation;
+
+//==============================================================================
+/**
+ * Reporting structure describing a complete mouse pick including initial
+ * mouse XY screen position, which element was hit, UV point of hit, global
+ * intersection point and more.
+ */
+struct SPickFrame
+{
+ SInputFrame m_InputFrame; ///< Input values to process
+
+ FLOAT m_PickOrigin[3]; ///< 3D pick ray start
+ FLOAT m_PickDirection[3]; ///< 3D pick ray direction
+
+ FLOAT m_LocalHit[2]; ///< 2D pick ray intersection
+ FLOAT m_SquaredDistance; ///< Distance from camera to intersection
+ TElement *m_Model; ///< Element picked
+ // IPresentation* m_SubPresentation; ///< The picked element has a subpresentation
+
+ BOOL m_ResultValid; ///< Model found - Whole structure is valid
+ INT8 m_Unused[3]; ///< Padding
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSPresentation.cpp b/src/runtime/Qt3DSPresentation.cpp
new file mode 100644
index 0000000..0792119
--- /dev/null
+++ b/src/runtime/Qt3DSPresentation.cpp
@@ -0,0 +1,841 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "Qt3DSPresentation.h"
+#include "Qt3DSCommandEventTypes.h"
+#include "Qt3DSIScriptBridge.h"
+#include "Qt3DSInputEventTypes.h"
+#include "Qt3DSDataLogger.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSRuntimeFactory.h"
+#include "Qt3DSCommandHelper.h"
+#include "Qt3DSActivationManager.h"
+#include "Qt3DSRenderContextCore.h"
+#include "Qt3DSComponentManager.h"
+#include "Qt3DSRenderBufferLoader.h"
+#include "Qt3DSSlideSystem.h"
+#include "Qt3DSLogicSystem.h"
+#include "Qt3DSParametersSystem.h"
+#include "Qt3DSApplication.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtGui/qvector4d.h>
+#include <QtGui/qvector3d.h>
+#include <QtGui/qvector2d.h>
+
+namespace Q3DStudio {
+
+// Maximum number of Event/Command that can be queued in an Update cycle
+const INT32 Q3DStudio_EVENTCOMMANDQUEUECAPACITY = 512;
+
+// Limit to prevent infinite loop during queue processing
+const INT32 Q3DStudio_MAXEVENTCOMMANDLOOPCOUNT = Q3DStudio_EVENTCOMMANDQUEUECAPACITY * 10;
+
+#ifdef WIN32
+#pragma warning(push)
+#pragma warning(disable : 4355)
+#endif
+
+CPresentation::CPresentation(const QString &inName, const QString &projectPath,
+ qt3ds::runtime::IApplication *inApplication)
+ : m_Name(inName)
+ , m_Application(inApplication)
+ , m_Scene(nullptr)
+ , m_ActivityZone(nullptr)
+ , m_RootElement(nullptr)
+ , m_EventCommandQueue(Q3DStudio_EVENTCOMMANDQUEUECAPACITY, "EventCommandQueue")
+ , m_IsProcessingEventCommandQueue(false)
+ , m_ComponentManager(*this)
+ , m_Offset(0)
+ , m_LocalTime(0)
+ , m_PreviousGlobalTime(-1)
+ , m_Paused(false)
+ , m_OffsetInvalid(true)
+ , m_Active(true)
+{
+ m_projectPath = QFileInfo(projectPath).absoluteFilePath();
+ m_Size.m_Width = 0;
+ m_Size.m_Height = 0;
+ m_Size.m_ScaleMode = SCALEMODE_UNKNOWN;
+ m_AnimationSystem = IAnimationSystem::CreateAnimationSystem(
+ inApplication->GetRuntimeFactoryCore().GetFoundation());
+ m_SlideSystem =
+ ISlideSystem::CreateSlideSystem(inApplication->GetRuntimeFactoryCore().GetFoundation(),
+ inApplication->GetRuntimeFactoryCore().GetStringTable(),
+ inApplication->GetElementAllocator());
+ m_LogicSystem =
+ ILogicSystem::CreateLogicSystem(inApplication->GetRuntimeFactoryCore().GetFoundation());
+ m_ParametersSystem = IParametersSystem::CreateParametersSystem(
+ inApplication->GetRuntimeFactoryCore().GetFoundation());
+}
+#ifdef _WIN32
+#pragma warning(pop)
+#endif
+
+CPresentation::~CPresentation()
+{
+}
+
+/**
+ * Registers an element for notification when events fired on it.
+ * @param inElement target element to monitor
+ * @param inEventHash event hash to register for
+ * @param inCallback static callback function
+ * @param inContextData arbitrary data pointer
+ */
+void CPresentation::RegisterEventCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData)
+{
+ m_EventCallbacks.RegisterCallback(inElement, inEventHash, inCallback, inContextData);
+ inElement->SetFlag(ELEMENTFLAG_REGISTEREDFOREVENTCALLBACK, true);
+
+ if (qt3ds::runtime::IApplication::isPickingEvent(inEventHash))
+ inElement->SetFlag(ELEMENTFLAG_PICKENABLED, true);
+}
+
+/**
+ * Unregisters a previously registered event callback.
+ * @param inElement target element to monitor
+ * @param inEventHash event hash to register for
+ * @param inCallback static callback function
+ * @param inContextData arbitrary data pointer
+ */
+BOOL CPresentation::UnregisterEventCallback(TElement *inElement,
+ const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData)
+{
+ BOOL theLast = false;
+ BOOL theResult = m_EventCallbacks.UnregisterCallback(inElement, inEventHash, inCallback,
+ inContextData, theLast);
+
+ // Unflag element if there are no longer any callbacks on it
+ if (theLast)
+ inElement->SetFlag(ELEMENTFLAG_REGISTEREDFOREVENTCALLBACK, false);
+
+ if (qt3ds::runtime::IApplication::isPickingEvent(inEventHash))
+ inElement->SetFlag(ELEMENTFLAG_PICKENABLED, false);
+
+ return theResult;
+}
+
+void CPresentation::ClearDirtyList()
+{
+ FOR_ARRAY(TElement *, theElement, GetFrameData().GetDirtyList())
+ {
+ (*theElement)->Flags().SetDirty(false);
+ }
+ GetFrameData().Reset();
+}
+
+inline void ConvertActivityZoneBufferToElementList(qt3ds::runtime::TActivityItemBuffer inSource,
+ TElementList &outResult)
+{
+ for (qt3ds::QT3DSU32 idx = 0, end = inSource.size(); idx < end; ++idx)
+ outResult.Push(inSource[idx].first);
+}
+
+void CPresentation::PreUpdate(const TTimeUnit inGlobalTime)
+{
+ if (m_OffsetInvalid || m_Paused) {
+ m_OffsetInvalid = false;
+ m_Offset = m_LocalTime - inGlobalTime;
+ } else
+ m_LocalTime = inGlobalTime + m_Offset;
+
+ // Event/Command Processing Stage
+ ProcessEventCommandQueue();
+}
+
+/**
+ * Update the presentation to the current time. This will start triggering the
+ * various stages of the presentation frame rhythm
+ * @param inGlobalTime time at which to update each presentation
+ * @return true if there were events to be processed.
+ */
+void CPresentation::BeginUpdate()
+{
+ // Active Scan Stage
+ if (m_ActivityZone) {
+ m_ActivityZone->BeginUpdate(
+ m_LocalTime, m_Application->GetRuntimeFactory().GetPerfTimer(),
+ m_Application->GetRuntimeFactory().GetQt3DSRenderContext().GetThreadPool());
+ }
+}
+
+void CPresentation::EndUpdate()
+{
+ if (m_ActivityZone) {
+ m_ActivityZone->EndUpdate();
+ CPresentationFrameData &theFrameData = GetFrameData();
+ ConvertActivityZoneBufferToElementList(m_ActivityZone->GetActivatedItems(),
+ theFrameData.GetActivationList());
+ ConvertActivityZoneBufferToElementList(m_ActivityZone->GetDeactivatedItems(),
+ theFrameData.GetDeactivationList());
+ ConvertActivityZoneBufferToElementList(m_ActivityZone->GetScriptItems(),
+ theFrameData.GetScriptsList());
+ }
+
+ if (!m_Paused) {
+ // Animation Track Evaluation Stage
+ m_AnimationSystem->Update();
+ }
+ // Presentation is considered ready to accept external commands when the first frame property
+ // updates are done.
+ if (!m_presentationReady) {
+ m_SignalProxy.SigPresentationReady();
+ m_presentationReady = true;
+ }
+}
+
+void CPresentation::PostUpdate(const TTimeUnit inGlobalTime)
+{
+ if (!m_Paused) {
+ // Callback Stage
+ if (m_Application && m_PreviousGlobalTime != inGlobalTime)
+ m_Application->GetRuntimeFactoryCore().GetScriptEngineQml().ProcessFrameCallbacks(this);
+ }
+
+ m_PreviousGlobalTime = inGlobalTime;
+}
+
+void CPresentation::NotifyDataOutputs()
+{
+ if (m_pathToDataOutMap.size() == 0)
+ return;
+
+ // Based on the dirty list, check if we need to fire DataOutput notifications
+ Q3DStudio::TElementList &dirtyList = GetFrameData().GetDirtyList();
+ for (int idx = 0, end = dirtyList.GetCount(); idx < end; ++idx) {
+ Q3DStudio::TElement &element = *dirtyList[idx];
+ if (m_pathToDataOutMap.contains(element.m_Path)) {
+ auto outDefIter = m_pathToDataOutMap.find(element.m_Path);
+
+ while (outDefIter != m_pathToDataOutMap.end() && outDefIter.key() == element.m_Path) {
+ qt3ds::runtime::DataOutputDef &outDef = outDefIter.value();
+
+ // Get current value
+ Q3DStudio::UVariant value;
+ qt3ds::QT3DSU32 attribHash
+ = CHash::HashAttribute(outDef.observedAttribute.attributeName[0]);
+ element.GetAttribute(attribHash, value);
+ QVariant qvar;
+ switch (outDef.observedAttribute.propertyType) {
+ case ATTRIBUTETYPE_INT32:
+ qvar.setValue(value.m_INT32);
+ break;
+ case ATTRIBUTETYPE_FLOAT:
+ qvar.setValue(value.m_FLOAT);
+ break;
+ case ATTRIBUTETYPE_BOOL:
+ qvar.setValue(value.m_INT32);
+ break;
+ case ATTRIBUTETYPE_STRING:
+ qvar.setValue(QString::fromUtf8(
+ GetStringTable().HandleToStr(value.m_StringHandle).c_str()));
+ break;
+ case ATTRIBUTETYPE_FLOAT4: {
+ QVector4D qvalue(value.m_FLOAT4[0], value.m_FLOAT4[1],
+ value.m_FLOAT4[2], value.m_FLOAT4[3]);
+ qvar.setValue(qvalue);
+ }
+ break;
+ case ATTRIBUTETYPE_FLOAT3: {
+ QVector3D qvalue(value.m_FLOAT3[0], value.m_FLOAT3[1], value.m_FLOAT3[2]);
+ qvar.setValue(qvalue);
+ }
+ break;
+ case ATTRIBUTETYPE_FLOAT2: {
+ QVector2D qvalue(value.m_FLOAT3[0], value.m_FLOAT3[1]);
+ qvar.setValue(qvalue);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (qvar.isValid() && (outDef.value != qvar)) {
+ outDef.value.setValue(qvar);
+ m_SignalProxy.SigDataOutputValueUpdated(outDef.name, outDef.value);
+ }
+
+ ++outDefIter;
+ }
+ }
+ }
+}
+
+void CPresentation::AddToDataOutputMap(const QHash<qt3ds::foundation::CRegisteredString,
+ qt3ds::runtime::DataOutputDef> &doMap)
+{
+ if (doMap.size() > 0)
+ m_pathToDataOutMap.unite(doMap);
+}
+
+/**
+ * Process the Event/Command queue completely
+ * PostEventCommand is the method that will add new Event/Command to the queue.
+ *
+ * The contract for Event/Command processing:
+ * 1. If an Event or Command is posted during the processing stage, it will be added
+ * to the end of the queue and is guaranteed to be processed in the current cycle.
+ * For example, if an Event (which is processed) triggers a Command to set a model to red,
+ * the model will turn red in the current frame.
+ *
+ * 2. If an Event or Command is posted after the processing stage, such as during the
+ * Callback stage, it will only be processed on the next Update cycle.
+ * For example, if the "turn mode to red" Command is posted in a script callback, the
+ * model will turn red in the next frame.
+ *
+ * 3. If an Event or Command is posted before the processing stage, for example an
+ * external Event posted just before the Update cycle, it will be processed in the current
+ *cycle.
+ * For example, if the "turn mode to red" Command is posted from the game engine prior to
+ * the Update cycle, the model will turn red in the current frame.
+ *
+ * @see PostEventCommand
+ * @return true if there were events that were processed.
+ */
+BOOL CPresentation::ProcessEventCommandQueue()
+{
+ PerfLogGeneralEvent2(DATALOGGER_PROCESSEVENTS);
+
+ m_IsProcessingEventCommandQueue = true;
+
+ BOOL theResult = !m_EventCommandQueue.IsEmpty();
+ INT32 theEventProcessedCount = 0;
+ while (!m_EventCommandQueue.IsEmpty()) {
+ SEventCommand &theEventCommand = m_EventCommandQueue.Top();
+ if (theEventCommand.m_IsEvent)
+ ProcessEvent(theEventCommand, theEventProcessedCount);
+ else
+ ProcessCommand(theEventCommand);
+
+ // Infinite-loop check
+ if (theEventProcessedCount > Q3DStudio_MAXEVENTCOMMANDLOOPCOUNT) {
+ // Breakout policy: Dump remaining Event/Commands
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "ProcessEventCommandQueue exceeded maximum loop count"
+ << "Remaining Event/Commands will be cleared. Event count: "
+ << theEventProcessedCount << "Limit: " << Q3DStudio_MAXEVENTCOMMANDLOOPCOUNT;
+ m_EventCommandQueue.Clear();
+ } else {
+ m_EventCommandQueue.Pop();
+ }
+ }
+
+ m_IsProcessingEventCommandQueue = false;
+
+ return theResult;
+}
+
+/**
+ * Pass incoming event to event consumers immediately
+ * @param ioEvent the incoming event
+ */
+void CPresentation::ProcessEvent(SEventCommand &ioEvent, INT32 &ioEventCount)
+{
+ if (ioEventCount < Q3DStudio_MAXEVENTCOMMANDLOOPCOUNT) {
+ ++ioEventCount;
+ PerfLogPresentationEvent1(DATALOGGER_PROCESSEVENT);
+
+ // Callbacks can change ioEvent's bubbling flags
+ if (ioEvent.m_Target->GetFlag(ELEMENTFLAG_REGISTEREDFOREVENTCALLBACK)) {
+ m_EventCallbacks.FireCallbacks(ioEvent);
+ }
+
+ // Do an early return if callback do a "stopImmediatePropagation"
+ if (ioEvent.m_Done)
+ return;
+
+ if (ioEvent.m_Target) {
+ // Logic should not be able to change ioEvent's bubbling flags
+ // ...or can it?
+ m_LogicSystem->OnEvent(ioEvent.m_Type, *ioEvent.m_Target, *this);
+ }
+
+ ProcessEventBubbling(ioEvent, ioEventCount);
+ }
+}
+
+/**
+ * Handle event bubbling
+ * @param ioEvent the incoming event
+ */
+void CPresentation::ProcessEventBubbling(SEventCommand &ioEvent, INT32 &ioEventCount)
+{
+ PerfLogPresentationEvent1(DATALOGGER_PROCESSEVENTBUBBLING);
+
+ // Check for onGroupedMouseOver/Out
+ // arg1 = the original onMouseOut model and arg2 = the original onMouseOver model
+ if (ioEvent.m_Type == ON_MOUSEOUT) {
+ // If original onMouseOver model is nullptr or not a descendent, fire onGroupedMouseOut
+ TElement *theMouseOverModel = static_cast<TElement *>(ioEvent.m_Arg2.m_VoidPointer);
+ if (!theMouseOverModel || !ioEvent.m_Target->IsDescendent(*theMouseOverModel)) {
+ SEventCommand theEvent = ioEvent;
+ theEvent.m_Type = ON_GROUPEDMOUSEOUT;
+ theEvent.m_BubbleUp = 0; // no bubbling
+ theEvent.m_BubbleDown = 0;
+ FireEvent(theEvent);
+ }
+ // set the original onMouseOver model to the target to make IsDescendent queries less
+ // expensive
+ else if (theMouseOverModel) {
+ ioEvent.m_Arg2.m_VoidPointer = ioEvent.m_Target;
+ }
+ } else if (ioEvent.m_Type == ON_MOUSEOVER) {
+ // If original onMouseOut model is nullptr or not a descendent, fire onGroupedMouseOver
+ TElement *theMouseOutModel = static_cast<TElement *>(ioEvent.m_Arg1.m_VoidPointer);
+ if (!theMouseOutModel || !ioEvent.m_Target->IsDescendent(*theMouseOutModel)) {
+ SEventCommand theEvent = ioEvent;
+ theEvent.m_Type = ON_GROUPEDMOUSEOVER;
+ theEvent.m_BubbleUp = 0; // no bubbling
+ theEvent.m_BubbleDown = 0;
+ FireEvent(theEvent);
+ }
+ // set the original onMouseOut model to the target to make IsDescendent queries less
+ // expensive
+ else if (theMouseOutModel) {
+ ioEvent.m_Arg1.m_VoidPointer = ioEvent.m_Target;
+ }
+ }
+
+ // Do NOT bubble up onSlideEnter and onSlideExit events from current component to its parent
+ // scene
+ // since each component has its own slides.
+ if (ioEvent.m_BubbleUp && (ioEvent.m_Target->Flags() & ELEMENTFLAG_COMPONENT)
+ && (ioEvent.m_Type == EVENT_ONSLIDEENTER || ioEvent.m_Type == EVENT_ONSLIDEEXIT)) {
+ ioEvent.m_BubbleUp = FALSE;
+ }
+
+ // Event bubbling
+ if (ioEvent.m_BubbleUp) {
+ TElement *theParent = ioEvent.m_Target->m_Parent;
+ if (theParent) {
+ ioEvent.m_Target = theParent;
+ ProcessEvent(ioEvent, ioEventCount);
+ }
+ }
+}
+
+/**
+ * Execute command immediately
+ * @param inCommand incoming command structure
+ */
+void CPresentation::ProcessCommand(const SEventCommand &inCommand)
+{
+ PerfLogPresentationEvent1(DATALOGGER_PROCESSCOMMAND);
+
+ // Attributes (Arg1 = key, Arg2 = value)
+ if (inCommand.m_Type == COMMAND_SETPROPERTY) {
+ SAttributeKey theAttributeKey;
+ UINT32 theHash = static_cast<UINT32>(inCommand.m_Arg1.m_Hash);
+ theAttributeKey.Convert(
+ theHash); // Need this conversion to prevent problems arising due to endianess
+ inCommand.m_Target->SetAttribute(theAttributeKey.m_Hash, inCommand.m_Arg2);
+
+ // Events (Arg1 = hashed event name)
+ } else if (inCommand.m_Type == COMMAND_FIREEVENT) {
+ FireEvent(inCommand.m_Arg1.m_Hash, inCommand.m_Target);
+
+ // Time (Arg1 = time)
+ } else if (inCommand.m_Type == COMMAND_PLAY) {
+ GetComponentManager().SetPause(inCommand.m_Target, false);
+ } else if (inCommand.m_Type == COMMAND_PAUSE) {
+ GetComponentManager().SetPause(inCommand.m_Target, true);
+ } else if (inCommand.m_Type == COMMAND_GOTOTIME) {
+ GetComponentManager().GoToTime(inCommand.m_Target, inCommand.m_Arg1.m_INT32);
+
+ // Slide (Arg1 = slide index or slide name)
+ } else if (inCommand.m_Type == COMMAND_GOTOSLIDE) {
+ // Goto slide commands are handled differently.
+ IComponentManager &theManager(GetComponentManager());
+ Q3DStudio::SComponentGotoSlideData theGotoSlideData =
+ theManager.GetComponentGotoSlideCommand(inCommand.m_Target);
+ if (theGotoSlideData.m_Slide > 0)
+ theManager.GotoSlideIndex(inCommand.m_Target, theGotoSlideData);
+
+ theManager.ReleaseComponentGotoSlideCommand(inCommand.m_Target);
+ } else if (inCommand.m_Type == COMMAND_GOTOSLIDENAME) {
+ GetComponentManager().GotoSlideName(inCommand.m_Target, inCommand.m_Arg1.m_Hash);
+ } else if (inCommand.m_Type == COMMAND_GOTONEXTSLIDE) {
+ GetComponentManager().GoToNextSlide(inCommand.m_Target);
+ } else if (inCommand.m_Type == COMMAND_GOTOPREVIOUSSLIDE) {
+ GetComponentManager().GoToPreviousSlide(inCommand.m_Target);
+ } else if (inCommand.m_Type == COMMAND_BACKSLIDE) {
+ GetComponentManager().GoToBackSlide(inCommand.m_Target);
+
+ // Behavior
+ } else if (inCommand.m_Type == COMMAND_CUSTOMACTION) {
+ m_Application->GetRuntimeFactoryCore().GetScriptEngineQml()
+ .ProcessCustomActions(this, inCommand);
+ } else if (inCommand.m_Type == COMMAND_CUSTOMCALLBACK) {
+ m_Application->GetRuntimeFactoryCore().GetScriptEngineQml().ProcessCustomCallback(
+ this, inCommand);
+ } else if (inCommand.m_Type == COMMAND_PLAYSOUND) {
+ CRegisteredString theSoundPathReg = GetStringTable().HandleToStr(inCommand.m_Arg1.m_INT32);
+ if (theSoundPathReg.IsValid()) {
+ const char *theSoundPath = theSoundPathReg.c_str();
+ if (strlen(theSoundPath) > 0) {
+ m_Application->GetRuntimeFactoryCore().GetScriptEngineQml().PlaySoundFile(
+ theSoundPath);
+ }
+ }
+ } else if (inCommand.m_Type == COMMAND_EMITSIGNAL) {
+ CRegisteredString nameStr = GetStringTable().HandleToStr(inCommand.m_Arg1.m_INT32);
+ m_Application->GetRuntimeFactoryCore().GetScriptEngineQml().ProcessSignal(this, inCommand);
+ QString path = QString::fromLatin1(inCommand.m_Target->m_Path.c_str());
+ QString name = QString::fromLatin1(nameStr.c_str());
+ signalProxy()->SigCustomSignal(path, name);
+ } else {
+ qCCritical(qt3ds::INVALID_OPERATION) << "Command not implemented: " << inCommand.m_Type;
+ }
+}
+
+/**
+ * Put an Event in the queue to be processed later during the Event/Command
+ * processing stage in Update
+ * This method is used by Event-bubbling during ProcessEvent
+ *
+ * @param inEvent the incoming event
+ * @see PostEvent
+ */
+void CPresentation::FireEvent(const SEventCommand &inEvent)
+{
+ SEventCommand &theEventCommand = m_EventCommandQueue.NewEntry();
+
+ theEventCommand = inEvent;
+
+ theEventCommand.m_IsEvent = true;
+}
+
+/**
+ * Put an Event in the queue to be processed later during the Event/Command
+ * processing stage in Update See ProcessEventCommandQueue for more
+ * information on the contract for Event/Command processing.
+ *
+ * @param inEventType the incoming event type
+ * @param inTarget the target for the event
+ * @param inArg1 optional argument #1
+ * @param inArg2 optional argument #2
+ * @param inType1 optional type for argument #1
+ * @param inType2 optional type for argument #2
+ * @see ProcessEventCommandQueue
+ * @see PostEventCommand
+ */
+void CPresentation::FireEvent(const TEventCommandHash inEventType, TElement *inTarget,
+ const UVariant *inArg1, const UVariant *inArg2,
+ const EAttributeType inType1, const EAttributeType inType2)
+{
+
+ SEventCommand theEvent = { inTarget, inEventType };
+
+ theEvent.m_IsEvent = true;
+ theEvent.m_BubbleUp = true;
+
+ theEvent.m_Arg1Type = static_cast<UINT8>(inType1);
+ theEvent.m_Arg2Type = static_cast<UINT8>(inType2);
+
+ if (inArg1)
+ theEvent.m_Arg1 = *inArg1;
+ if (inArg2)
+ theEvent.m_Arg2 = *inArg2;
+
+ m_EventCommandQueue.NewEntry() = theEvent;
+}
+
+/**
+ * Put a Command in the queue to be processed later during the Event/Command
+ * processing stage in Update. See ProcessEventCommandQueue for more information
+ * on the contract for Event/Command processing.
+ *
+ * For cases where the Commands need to be synchronized with the Frame-Rhythm
+ * An example would be calling "SetSlide" in scripts.
+ *
+ * @see ProcessEventCommandQueue
+ * @see PostEventCommand
+ * @param inEventType the incoming command type
+ * @param inTarget the target for the command
+ * @param inArg1 optional argument #1
+ * @param inArg2 optional argument #2
+ * @param inType1 optional type for argument #1
+ * @param inType2 optional type for argument #2
+ */
+void CPresentation::FireCommand(const TEventCommandHash inEventType, TElement *inTarget,
+ const UVariant *inArg1, const UVariant *inArg2,
+ const EAttributeType inType1, const EAttributeType inType2)
+{
+ // Pre-filter gotoslidename commands.
+ if (inEventType == COMMAND_GOTOSLIDENAME) {
+ int theSlideHashName = inArg1->m_INT32;
+ TComponent *theComponent = GetComponentManager().GetComponent(inTarget);
+ UINT8 theSlideIndex = GetSlideSystem().FindSlide(*theComponent, theSlideHashName);
+ // Translate into a slide index command.
+ CCommandHelper::SetupGotoSlideCommand(*inTarget, theSlideIndex,
+ SScriptEngineGotoSlideArgs());
+ } else {
+ SEventCommand theCommand = { inTarget, inEventType };
+
+ theCommand.m_Arg1Type = static_cast<UINT8>(inType1);
+ theCommand.m_Arg2Type = static_cast<UINT8>(inType2);
+
+ if (inArg1)
+ theCommand.m_Arg1 = *inArg1;
+ if (inArg2)
+ theCommand.m_Arg2 = *inArg2;
+
+ m_EventCommandQueue.NewEntry() = theCommand;
+ }
+}
+
+void CPresentation::FlushEventCommandQueue(void)
+{
+ if (!m_IsProcessingEventCommandQueue)
+ ProcessEventCommandQueue();
+}
+
+void CPresentation::ProcessEvent(SEventCommand &inEvent)
+{
+ INT32 theEventProcessedCount = 0;
+ ProcessEvent(inEvent, theEventProcessedCount);
+}
+
+/**
+ * This method is triggered after the presentation is streamed in. At this point,
+ * all the stores will be loaded up.
+ */
+void CPresentation::OnPresentationLoaded()
+{
+ m_FrameData.Reserve(1000 /*m_ElementManager.GetElementCount( )*/);
+}
+
+/**
+ * Set the full path for this presentation. This can be used by anyone who
+ * knows the presentation to form relative paths.
+ * @param inPath the path to which to set.
+ */
+void CPresentation::SetFilePath(const CHAR *inPath)
+{
+ m_FilePath = QFileInfo(inPath).absoluteFilePath();
+}
+
+/**
+ * Gets the full file path for this presentation. This can be used by anyone who
+ * knows the presentation to form relative correct paths.
+ */
+QString CPresentation::GetFilePath() const
+{
+ return m_FilePath;
+}
+
+/**
+ * Gets the absolute file path for the project that owns this presentation.
+ */
+QString CPresentation::getProjectPath() const
+{
+ return m_projectPath;
+}
+
+/**
+ * Gets the pause state
+ * @return true if the presentation is paused, false if otherwise
+ */
+BOOL CPresentation::GetPause() const
+{
+ return m_Paused;
+}
+
+/**
+ * Sets the pause state
+ * @param inPause set true to pause, set false if otherwise
+ */
+void CPresentation::SetPause(const BOOL inPause)
+{
+ m_Paused = inPause ? true : false;
+}
+
+/**
+ * Simple manager access: Scene
+ */
+void CPresentation::SetScene(IScene *inScene)
+{
+ m_Scene = inScene;
+}
+
+void CPresentation::SetActivityZone(qt3ds::runtime::IActivityZone *inZone)
+{
+ m_ActivityZone = inZone;
+ m_ActivityZone->SetZoneActive(m_Active);
+ TElement *theSceneElement = m_RootElement;
+ qt3ds::runtime::IActivityZone &theZone(*GetActivityZone());
+ // The activity zone requires elements described to it in breadth first search order.
+ theZone.AddActivityItems(*theSceneElement);
+}
+
+void CPresentation::SetActive(bool inValue)
+{
+ m_Active = inValue;
+ m_ActivityZone->SetZoneActive(m_Active);
+}
+
+bool CPresentation::GetActive() const
+{
+ return m_Active;
+}
+
+/**
+ * Simple manager access: Scene
+ */
+IScene *CPresentation::GetScene() const
+{
+ return m_Scene;
+}
+
+/**
+* Simple manager access: Script Bridge Qml
+*/
+IScriptBridge *CPresentation::GetScriptBridgeQml()
+{
+ if (m_Application)
+ return &m_Application->GetRuntimeFactoryCore().GetScriptEngineQml();
+
+ return nullptr;
+}
+
+/**
+ * Simple manager access: Component Manager
+ */
+IComponentManager &CPresentation::GetComponentManager()
+{
+ return m_ComponentManager;
+}
+
+/**
+ * Simple manager access: Slide Manager
+ */
+ISlideSystem &CPresentation::GetSlideSystem()
+{
+ return *m_SlideSystem;
+}
+
+/**
+ * Simple manager access: Animation Manager
+ */
+qt3ds::runtime::IAnimationSystem &CPresentation::GetAnimationSystem()
+{
+ return *m_AnimationSystem;
+}
+
+/**
+ * Simple manager access: Logic Manager
+ */
+ILogicSystem &CPresentation::GetLogicSystem()
+{
+ return *m_LogicSystem;
+}
+
+/**
+ * Simple manager access: Params Manager
+ */
+IParametersSystem &CPresentation::GetParametersSystem()
+{
+ return *m_ParametersSystem;
+}
+
+void CPresentation::SetElementPath(TElement &inElement, const char8_t *inPath)
+{
+ CRegisteredString str;
+ if (m_Application)
+ str = m_Application->GetRuntimeFactoryCore().GetStringTable().RegisterStr(
+ qt3ds::foundation::nonNull(inPath));
+
+ if (str.IsValid())
+ m_ElementPathMap.insert(eastl::make_pair(&inElement, str));
+}
+
+qt3ds::foundation::CRegisteredString CPresentation::GetElementPath(TElement &inElement)
+{
+ TElemStringMap::iterator iter = m_ElementPathMap.find(&inElement);
+ if (iter != m_ElementPathMap.end())
+ return iter->second;
+
+ return qt3ds::foundation::CRegisteredString();
+}
+
+qt3ds::foundation::IStringTable &CPresentation::GetStringTable()
+{
+ return GetApplication().GetRuntimeFactoryCore().GetStringTable();
+}
+
+/**
+ * Current frame data stores traversal lists for use later
+ */
+CPresentationFrameData &CPresentation::GetFrameData()
+{
+ return m_FrameData;
+}
+
+void CPresentation::SetLoadedBuffer(qt3ds::render::ILoadedBuffer &inBuffer)
+{
+ m_LoadedBuffer = inBuffer;
+}
+
+/**
+ * Retrieve the name of the presentation. This is actually the file path.
+ * @return the name of this presentation
+ */
+const QByteArray CPresentation::GetName() const
+{
+ return m_Name.toLatin1();
+}
+
+/**
+ * Retrieve the size of the presentation in Studio
+ * @return the size of this presentation
+ */
+SPresentationSize CPresentation::GetSize() const
+{
+ return m_Size;
+}
+
+/**
+ * Set the size of the presentation as reflected in Studio
+ * @param inSize size of presentation ( width, height, scale mode ) in Studio
+ */
+void CPresentation::SetSize(const SPresentationSize &inSize)
+{
+ m_Size = inSize;
+}
+
+QPresentationSignalProxy *CPresentation::signalProxy()
+{
+ return &m_SignalProxy;
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSPresentation.h b/src/runtime/Qt3DSPresentation.h
new file mode 100644
index 0000000..76b0a6e
--- /dev/null
+++ b/src/runtime/Qt3DSPresentation.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "RuntimePrefix.h"
+#include "Qt3DSIPresentation.h"
+#include "Qt3DSPresentationFrameData.h"
+#include "Qt3DSAnimationSystem.h"
+#include "Qt3DSCircularArray.h"
+#include "Qt3DSEventCallbacks.h"
+#include "Qt3DSTimePolicy.h"
+#include "EASTL/hash_map.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSComponentManager.h"
+
+#include <QObject>
+
+class QPresentationSignalProxy : public QObject
+{
+ Q_OBJECT
+Q_SIGNALS:
+ void SigSlideEntered(const QString &elementPath, unsigned int index, const QString &name);
+ void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name);
+ void SigCustomSignal(const QString &elementPath, const QString &name);
+ void SigPresentationReady();
+ void SigElementsCreated(const QStringList &elementPaths, const QString &error);
+ void SigMaterialsCreated(const QStringList &materialNames, const QString &error);
+ void SigDataOutputValueUpdated(const QString &name, const QVariant &value);
+};
+
+namespace qt3ds {
+namespace runtime {
+ class IApplication;
+ class IActivityZone;
+ struct DataOutputDef;
+}
+}
+
+namespace qt3ds {
+namespace render {
+ class ILoadedBuffer;
+}
+}
+
+namespace Q3DStudio {
+
+/**
+ * Intelligent representation of a Studio presentation.
+ */
+class CPresentation : public IPresentation
+{
+protected:
+ QString m_Name; // Name of this presentation
+ QString m_FilePath; // Absolute path to this presentation
+ QString m_projectPath; // Absolute path to the project root
+ qt3ds::runtime::IApplication *m_Application; // Runtime object
+ IScene *m_Scene; // Connection to the associated scene (render) for this presentation
+ qt3ds::runtime::IActivityZone *m_ActivityZone; // Controls element active status
+ TElement *m_RootElement;
+
+ CPresentationFrameData m_FrameData; // Storage of data of the current frame
+ CCircularArray<SEventCommand> m_EventCommandQueue; // The Event/Command integrated queue
+ bool m_IsProcessingEventCommandQueue;
+ CEventCallbacks m_EventCallbacks; // Handles event callbacks on registered elements
+ SPresentationSize m_Size; // Native width, height and mode exported from Studio
+
+ qt3ds::foundation::NVScopedRefCounted<qt3ds::render::ILoadedBuffer>
+ m_LoadedBuffer; // Reference to loaded data when loading from binary
+
+ CComponentManager m_ComponentManager;
+ qt3ds::foundation::NVScopedRefCounted<ISlideSystem>
+ m_SlideSystem; // Container and factory of all slides
+ qt3ds::foundation::NVScopedRefCounted<ILogicSystem>
+ m_LogicSystem; // Container and factory of all logics
+ qt3ds::foundation::NVScopedRefCounted<IAnimationSystem>
+ m_AnimationSystem; // Container and factory of all animation tracks
+ qt3ds::foundation::NVScopedRefCounted<IParametersSystem>
+ m_ParametersSystem; // Container and factory of all custom actions
+
+ TTimeUnit m_Offset;
+ TTimeUnit m_LocalTime;
+ TTimeUnit m_PreviousGlobalTime;
+ bool m_Paused;
+ bool m_OffsetInvalid;
+ bool m_Active;
+ bool m_presentationReady = false;
+
+ typedef eastl::hash_map<TElement *, qt3ds::foundation::CRegisteredString> TElemStringMap;
+ TElemStringMap m_ElementPathMap;
+
+public: // Construction
+ CPresentation(const QString &inName, const QString &projectPath,
+ qt3ds::runtime::IApplication *inRuntime);
+ virtual ~CPresentation() override;
+
+public: // Execution
+ void Initialize();
+ // Clear dirty elements
+ void ClearDirtyList() override;
+ // Run events
+ void PreUpdate(const TTimeUnit inGlobalTime) override;
+ // update element graph
+ void BeginUpdate() override;
+ // end update element graph
+ void EndUpdate() override;
+ // Run behaviors.
+ void PostUpdate(const TTimeUnit inGlobalTime) override;
+ // Notify DataUpdates if any are registered to this presentation
+ void NotifyDataOutputs();
+
+public: // Bridge Control
+ IScene *GetScene() const override;
+ IScriptBridge *GetScriptBridgeQml() override;
+ void SetScene(IScene *inScene) override;
+
+ void SetActivityZone(qt3ds::runtime::IActivityZone *inZone);
+ qt3ds::runtime::IActivityZone *GetActivityZone() override { return m_ActivityZone; }
+ void SetActive(bool inValue);
+ bool GetActive() const;
+ TElement *GetRoot() override { return m_RootElement; }
+ void SetRoot(TElement &inRoot) override { m_RootElement = &inRoot; }
+
+public: // Commands and Events
+ void FireEvent(const SEventCommand &inEvent);
+ void FireEvent(const TEventCommandHash inEventType, TElement *inTarget,
+ const UVariant *inArg1 = nullptr, const UVariant *inArg2 = nullptr,
+ const EAttributeType inType1 = ATTRIBUTETYPE_NONE,
+ const EAttributeType inType2 = ATTRIBUTETYPE_NONE) override;
+ void FireCommand(const TEventCommandHash inCommandType, TElement *inTarget,
+ const UVariant *inArg1 = nullptr, const UVariant *inArg2 = nullptr,
+ const EAttributeType inType1 = ATTRIBUTETYPE_NONE,
+ const EAttributeType inType2 = ATTRIBUTETYPE_NONE) override;
+ void FlushEventCommandQueue(void) override;
+ void ProcessEvent(SEventCommand &inEvent) override;
+
+ QPresentationSignalProxy *signalProxy();
+
+public: // Data Output
+ void AddToDataOutputMap(const QHash<qt3ds::foundation::CRegisteredString,
+ qt3ds::runtime::DataOutputDef> &doMap);
+
+public: // Event Callbacks
+ void RegisterEventCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData) override;
+ BOOL UnregisterEventCallback(TElement *inElement, const TEventCommandHash inEventHash,
+ const TEventCallback inCallback, void *inContextData) override;
+
+protected: // Execution Helper Methods
+ void ProcessAllCallbacks();
+ BOOL ProcessEventCommandQueue();
+ void ProcessEvent(SEventCommand &ioEvent, INT32 &ioEventCount);
+ void ProcessEventBubbling(SEventCommand &ioEvent, INT32 &ioEventCount);
+ void ProcessCommand(const SEventCommand &inCommand);
+
+public: // Managers
+ IComponentManager &GetComponentManager() override;
+ ISlideSystem &GetSlideSystem() override;
+ IAnimationSystem &GetAnimationSystem() override;
+ ILogicSystem &GetLogicSystem() override;
+ IParametersSystem &GetParametersSystem() override;
+ qt3ds::foundation::IStringTable &GetStringTable() override;
+ qt3ds::runtime::IApplication &GetApplication() override
+ {
+ QT3DS_ASSERT(m_Application);
+ return *m_Application;
+ }
+ void SetLoadedBuffer(qt3ds::render::ILoadedBuffer &inBuffer);
+
+ void SetElementPath(TElement &inElement, const char8_t *inPath) override;
+ qt3ds::foundation::CRegisteredString GetElementPath(TElement &inElement) override;
+
+public: // Helpers
+ CPresentationFrameData &GetFrameData() override;
+ TTimeUnit GetTime() { return m_LocalTime; }
+
+public: // Hooks and callbacks
+ void OnPresentationLoaded() override;
+
+public: // Configuration access
+ SPresentationSize GetSize() const override;
+ BOOL GetPause() const;
+ const QByteArray GetName() const;
+
+ void SetSize(const SPresentationSize &inSize) override;
+ void SetPause(const BOOL inPause);
+ void SetHide(const BOOL inHide);
+ void SetUpdateLock(const BOOL inLockUpdate);
+ void SetVCAA(const BOOL inVCAA);
+
+public: // Full file paths
+ void SetFilePath(const CHAR *inPath) override;
+ QString GetFilePath() const override;
+ QString getProjectPath() const override;
+
+private: // Disabled Copy Construction
+ CPresentation(CPresentation &);
+ CPresentation &operator=(const CPresentation &);
+private:
+ QPresentationSignalProxy m_SignalProxy;
+ QHash<qt3ds::foundation::CRegisteredString, qt3ds::runtime::DataOutputDef> m_pathToDataOutMap;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSPresentationFrameData.cpp b/src/runtime/Qt3DSPresentationFrameData.cpp
new file mode 100644
index 0000000..649462a
--- /dev/null
+++ b/src/runtime/Qt3DSPresentationFrameData.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSPresentationFrameData.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Constants
+//==============================================================================
+const FLOAT DIRTY_STORE_RATIO = 0.1f;
+const FLOAT TRAVERSAL_STORE_RATIO = 0.5f;
+const FLOAT ACTIVATION_STORE_RATIO = 0.3f;
+const FLOAT DEACTIVATION_STORE_RATIO = 0.3f;
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CPresentationFrameData::CPresentationFrameData()
+ : m_DirtyList(0, 0, "Frame:DirtyList")
+ , m_TraversalList(0, 0, "Frame:TraversalList")
+ , m_ScriptsList(0, 0, "Frame:ScriptsList")
+ , m_ActivationList(0, 0, "Frame:ActivationList")
+ , m_DeactivationList(0, 0, "Frame:DeactivationList") /*,*/
+// m_SlideExitList( 0, 0, "Frame:SlideExitList" ),
+// m_SlideEnterList( 0, 0, "Frame:SlideEnterList" )
+{
+}
+
+//==============================================================================
+/**
+ * Reserve memory from the memory subsytem to store the various arrays.
+ * @param inElementCount the number of elements loaded
+ */
+void CPresentationFrameData::Reserve(const INT32 /*inElementCount*/)
+{
+}
+
+//==============================================================================
+/**
+ * Clean up the data. This is usually called at the start of the frame rhythm to
+ * clean up the data from previous frame
+ * Memory allocation should be clear by default.
+ * Previous list could be a lot longer than current or future list which means
+ * the appliation is using more memory than needed. The heuristics of both
+ * initialization and runtime freeing is dependent on the presentation.
+ */
+void CPresentationFrameData::Reset()
+{
+ // Keep the capacity of these lists every frame since they have consistent
+ // sizes from frame to frame
+ m_TraversalList.Clear(false);
+ m_ScriptsList.Clear(false);
+
+ // These lists are more sporatic (slide changes) for now, clear them as
+ // above but potential memory saving could occur by calling Clear( true ) instead
+ m_ActivationList.Clear(false);
+ m_DeactivationList.Clear(false);
+
+ // NOTE: When we clear the dirty list, we also must ensure that the elements
+ // themselves are un-dirtied to prepare for the next frame. This is done
+ // by the caller of "CPresentationFrameData::Reset"
+ m_DirtyList.Clear(false);
+
+ // m_SlideExitList.Clear( false );
+ // m_SlideEnterList.Clear( false );
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSPresentationFrameData.h b/src/runtime/Qt3DSPresentationFrameData.h
new file mode 100644
index 0000000..332cc84
--- /dev/null
+++ b/src/runtime/Qt3DSPresentationFrameData.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Presentation update cycle data storage class.
+ *
+ * This is the data collected from the Presentation on each frame. Other subsystems
+ * or external system uses these data to perform their specific operation
+ */
+class CPresentationFrameData
+{
+ //==============================================================================
+ // Fields
+ //==============================================================================
+protected:
+ TElementList m_DirtyList; ///< List of elements with modified attributes
+ TElementList m_TraversalList; ///< List of active elements
+ TElementList m_ScriptsList; ///< List of elements where scripts are active
+ TElementList
+ m_ActivationList; ///< List of elements whose global active flips from false to true
+ TElementList
+ m_DeactivationList; ///< List of elements whose global active flops from true to false
+ // TElementList m_SlideExitList; ///< List of components that exits their
+ // slides
+ // TElementList m_SlideEnterList; ///< List of components that enters their
+ // slides
+
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ CPresentationFrameData();
+ void Reserve(const INT32 inElementCount);
+
+public: // Data Management
+ void Reset();
+
+ TElementList &GetDirtyList() { return m_DirtyList; }
+ TElementList &GetTraversalList() { return m_TraversalList; }
+ TElementList &GetScriptsList() { return m_ScriptsList; }
+ TElementList &GetActivationList() { return m_ActivationList; }
+ TElementList &GetDeactivationList() { return m_DeactivationList; }
+ // TElementList& GetSlideExitList( ) { return m_SlideExitList;
+ // }
+ // TElementList& GetSlideEnterList( ) { return m_SlideEnterList; }
+
+private: // Disabled Copy Construction
+ CPresentationFrameData(const CPresentationFrameData &);
+ CPresentationFrameData &operator=(const CPresentationFrameData &);
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSQmlElementHelper.cpp b/src/runtime/Qt3DSQmlElementHelper.cpp
new file mode 100644
index 0000000..9b58a73
--- /dev/null
+++ b/src/runtime/Qt3DSQmlElementHelper.cpp
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "Qt3DSQmlElementHelper.h"
+#include "Qt3DSCommandEventTypes.h"
+#include "Qt3DSHash.h"
+#include "Qt3DSAttributeHashes.h"
+#include "Qt3DSElementSystem.h"
+#include "Qt3DSRuntimeFactory.h"
+#include "qmath.h"
+
+using namespace Q3DStudio;
+using namespace qt3ds;
+
+//==============================================================================
+// Constants
+//==============================================================================
+const char PRESENTATION_DELIMITER = ':';
+const char NODE_DELIMITER = '.';
+const TStringHash RESERVED_THIS = CHash::HashString("this");
+const TStringHash RESERVED_PARENT = CHash::HashString("parent");
+const TStringHash RESERVED_SCENE = CHash::HashString("Scene");
+
+//==============================================================================
+/**
+* Constructor
+*/
+CQmlElementHelper::CQmlElementHelper()
+{
+}
+
+//==============================================================================
+/**
+* Destructor
+*/
+CQmlElementHelper::~CQmlElementHelper()
+{
+}
+
+TElement *CQmlElementHelper::GetElement(qt3ds::runtime::IApplication &inApplication,
+ IPresentation *inDefaultPresentation, const char *inPath,
+ TElement *inStartElement)
+{
+ if (inPath == nullptr || *inPath == 0)
+ return nullptr;
+ const char *thePath(inPath);
+ const char *theSubPath = nullptr;
+ IPresentation *thePresentation = nullptr;
+ size_t thePathLength = ::strlen(thePath) + 1;
+ char *theToken =
+ Q3DStudio_allocate_desc(CHAR, thePathLength, "Token:TempPath"); // Temporary token storage
+ // Try to get the specified presentation
+ theSubPath = ::strchr(thePath, PRESENTATION_DELIMITER);
+ TElement *theElement = inStartElement;
+ if (theSubPath != nullptr) {
+ UINT32 theSubPathLength = static_cast<UINT32>(theSubPath - thePath);
+
+ ::strncpy(theToken, thePath, theSubPathLength);
+ theToken[theSubPathLength] = '\0';
+
+ thePath = theSubPath + 1;
+
+ const CHAR *thePresentationName = theToken;
+
+ thePresentation = inApplication.GetPresentationById(thePresentationName);
+ }
+ if (thePresentation == nullptr)
+ thePresentation = inDefaultPresentation;
+
+ // Return nil if the inStartElement is not in the specified presentation
+ if (theElement != nullptr
+ && (theSubPath == nullptr && theElement->GetBelongedPresentation() != thePresentation)) {
+ thePresentation = theElement->GetBelongedPresentation();
+ }
+
+ if (thePresentation == nullptr)
+ return nullptr;
+
+ TStringHash theName;
+ INT32 theParseCounter = 0;
+
+ while (thePath != nullptr && thePath[0] != '\0') {
+ ++theParseCounter;
+
+ // Do some strtok() work here
+ theSubPath = ::strchr(thePath, NODE_DELIMITER);
+ if (theSubPath) {
+ UINT32 theSubPathLength = static_cast<UINT32>(theSubPath - thePath);
+ Q3DStudio_ASSERT(theSubPathLength < thePathLength);
+
+ ::strncpy(theToken, thePath, theSubPathLength);
+ theToken[theSubPathLength] = '\0';
+
+ thePath = theSubPath + 1;
+ } else {
+ ::strcpy(theToken, thePath);
+ thePath = nullptr;
+ }
+
+ // Hash the token and do some element searching
+ theName = CHash::HashString(theToken);
+
+ if (theName == RESERVED_PARENT) {
+ if (theElement)
+ theElement = theElement->GetParent();
+ } else if (theName == RESERVED_THIS) {
+ //Keep the original element if "this" wanted
+ } else {
+ if (theName == RESERVED_SCENE && theParseCounter == 1)
+ theElement =
+ thePresentation->GetRoot(); // theElement is nullptr, so using absolute path
+
+ else if (theElement)
+ theElement = theElement->FindChild(theName); // Using relative path
+ }
+
+ if (!theElement)
+ thePath = nullptr;
+ } // while
+
+ Q3DStudio_free(theToken, CHAR, thePathLength);
+ return theElement;
+}
+
+bool CQmlElementHelper::SetAttribute(TElement *theElement, const char *theAttName,
+ const void *value, bool inDelay)
+{
+ SAttributeKey theAttributeKey;
+ theAttributeKey.m_Hash = CHash::HashAttribute(theAttName);
+
+ // Early out for our single 'read only' attribute
+ if (ATTRIBUTE_URI == theAttributeKey.m_Hash) {
+ // we didn't push anything onto the stack
+ return false;
+ }
+
+ // first search if it is a static property
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> thePropertyInfo =
+ theElement->FindProperty(theAttributeKey.m_Hash);
+
+ if (!thePropertyInfo.hasValue()) {
+ // if not search in the dynamic properties
+ thePropertyInfo = theElement->FindDynamicProperty(theAttributeKey.m_Hash);
+ if (!thePropertyInfo.hasValue()) {
+ // create a new dynamic porperty
+ qt3ds::runtime::IElementAllocator &theElemAllocator(
+ theElement->GetBelongedPresentation()->GetApplication().GetElementAllocator());
+ qt3ds::foundation::IStringTable &theStringTable(
+ theElement->GetBelongedPresentation()->GetStringTable());
+ IRuntimeMetaData &theMetaData =
+ theElement->GetBelongedPresentation()->GetApplication().GetMetaData();
+ thePropertyInfo = theElemAllocator.CreateDynamicProperty(
+ theMetaData, *theElement, theStringTable.RegisterStr(theAttName));
+ }
+ }
+
+ if (thePropertyInfo.hasValue()) {
+ UVariant theNewValue;
+ QString name(thePropertyInfo->first.m_Name.c_str());
+ EAttributeType theAttributeType = thePropertyInfo->first.m_Type;
+ switch (theAttributeType) {
+ case ATTRIBUTETYPE_INT32:
+ case ATTRIBUTETYPE_HASH:
+ theNewValue.m_INT32 = *(INT32 *)value;
+ break;
+
+ case ATTRIBUTETYPE_FLOAT:
+ theNewValue.m_FLOAT = *(FLOAT *)value;
+ if (name.startsWith(QLatin1String("rotation.")))
+ theNewValue.m_FLOAT = qDegreesToRadians(theNewValue.m_FLOAT);
+ break;
+
+ case ATTRIBUTETYPE_BOOL:
+ theNewValue.m_INT32 = *(INT32 *)value;
+ break;
+
+ case ATTRIBUTETYPE_STRING:
+ theNewValue.m_StringHandle =
+ theElement->GetBelongedPresentation()->GetStringTable().GetHandle(
+ (const char *)value);
+ break;
+
+ case ATTRIBUTETYPE_POINTER:
+ qCCritical(INVALID_OPERATION, "setAttribute: pointer attributes not handled.");
+ return false;
+ break;
+
+ case ATTRIBUTETYPE_ELEMENTREF:
+ qCCritical(INVALID_OPERATION, "setAttribute: ElementRef attributes are read only.");
+ return false;
+ break;
+
+ case ATTRIBUTETYPE_FLOAT3: {
+ FLOAT *vec3 = (FLOAT *)value;
+ theNewValue.m_FLOAT3[0] = vec3[0];
+ theNewValue.m_FLOAT3[1] = vec3[1];
+ theNewValue.m_FLOAT3[2] = vec3[2];
+ if (name == QLatin1String("rotation")) {
+ theNewValue.m_FLOAT3[0]= qDegreesToRadians(theNewValue.m_FLOAT3[0]);
+ theNewValue.m_FLOAT3[1]= qDegreesToRadians(theNewValue.m_FLOAT3[1]);
+ theNewValue.m_FLOAT3[2]= qDegreesToRadians(theNewValue.m_FLOAT3[2]);
+ }
+ } break;
+
+ case ATTRIBUTETYPE_NONE:
+ case ATTRIBUTETYPE_DATADRIVEN_PARENT:
+ case ATTRIBUTETYPE_DATADRIVEN_CHILD:
+ case ATTRIBUTETYPECOUNT:
+ default:
+ qCCritical(INVALID_OPERATION, "setAttribute: Attribute has no type!");
+ return false;
+ break;
+ }
+
+ if (inDelay) {
+ UVariant arg1;
+ arg1.m_INT32 = theAttributeKey.m_Hash;
+ theElement->GetBelongedPresentation()->FireCommand(
+ COMMAND_SETPROPERTY, theElement, &arg1, &theNewValue, ATTRIBUTETYPE_HASH,
+ theAttributeType);
+ } else {
+ theElement->SetAttribute(*thePropertyInfo, theNewValue);
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool CQmlElementHelper::GetAttribute(TElement *inElement, const char *inAttribute, void *value)
+{
+ SAttributeKey theAttributeKey;
+ theAttributeKey.m_Hash = CHash::HashAttribute(inAttribute);
+
+ // first search if it is a static property
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> thePropertyInfo =
+ inElement->FindProperty(theAttributeKey.m_Hash);
+
+ if (!thePropertyInfo.hasValue())
+ thePropertyInfo = inElement->FindDynamicProperty(theAttributeKey.m_Hash);
+
+ if (thePropertyInfo.hasValue()) {
+ UVariant *theValuePtr = thePropertyInfo->second;
+ EAttributeType theAttributeType = thePropertyInfo->first.m_Type;
+ switch (theAttributeType) {
+ case ATTRIBUTETYPE_INT32:
+ case ATTRIBUTETYPE_HASH:
+ *(INT32 *)value = theValuePtr->m_INT32;
+ break;
+
+ case ATTRIBUTETYPE_FLOAT:
+ *(FLOAT *)value = theValuePtr->m_FLOAT;
+ break;
+
+ case ATTRIBUTETYPE_BOOL:
+ *(INT32 *)value = (theValuePtr->m_INT32 != 0);
+ break;
+
+ case ATTRIBUTETYPE_STRING:
+ *(char *)value = *inElement->GetBelongedPresentation()
+ ->GetStringTable()
+ .HandleToStr(theValuePtr->m_StringHandle)
+ .c_str();
+ break;
+
+ case ATTRIBUTETYPE_POINTER:
+ qCCritical(INVALID_OPERATION, "getAttribute: pointer attributes not handled.");
+ return false;
+ break;
+
+ case ATTRIBUTETYPE_ELEMENTREF:
+ qCCritical(INVALID_OPERATION, "getAttribute: ElementRef attributes are read only.");
+ return false;
+ break;
+
+ case ATTRIBUTETYPE_FLOAT3: {
+ FLOAT *vec3 = (FLOAT *)value;
+ vec3[0] = theValuePtr->m_FLOAT3[0];
+ vec3[1] = theValuePtr->m_FLOAT3[1];
+ vec3[2] = theValuePtr->m_FLOAT3[2];
+ } break;
+
+ case ATTRIBUTETYPE_NONE:
+ case ATTRIBUTETYPE_DATADRIVEN_PARENT:
+ case ATTRIBUTETYPE_DATADRIVEN_CHILD:
+ case ATTRIBUTETYPECOUNT:
+ default:
+ qCCritical(INVALID_OPERATION, "getAttribute: Attribute has no type!");
+ return false;
+ break;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
diff --git a/src/runtime/Qt3DSQmlElementHelper.h b/src/runtime/Qt3DSQmlElementHelper.h
new file mode 100644
index 0000000..ec36c8e
--- /dev/null
+++ b/src/runtime/Qt3DSQmlElementHelper.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 QT3DS_QML_ELEMENT_HELPER_H
+#define QT3DS_QML_ELEMENT_HELPER_H
+
+#include "Qt3DSApplication.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSKernelTypes.h"
+
+namespace Q3DStudio {
+
+class CQmlElementHelper
+{
+private:
+ CQmlElementHelper();
+ virtual ~CQmlElementHelper();
+
+public:
+ static TElement *GetElement(qt3ds::runtime::IApplication &inApplication,
+ IPresentation *inDefaultPresentation, const char *inPath,
+ TElement *inStartElement = NULL);
+
+ static bool SetAttribute(TElement *inElement, const char *inAttribute, const void *value,
+ bool inDelay);
+ static bool GetAttribute(TElement *inElement, const char *inAttribute, void *value);
+};
+}
+
+#endif // QT3DS_QML_ELEMENT_HELPER_H
diff --git a/src/runtime/Qt3DSQmlEngine.cpp b/src/runtime/Qt3DSQmlEngine.cpp
new file mode 100644
index 0000000..ac958ea
--- /dev/null
+++ b/src/runtime/Qt3DSQmlEngine.cpp
@@ -0,0 +1,2690 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSQmlEngine.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSInputEngine.h"
+#include "Qt3DSSceneManager.h"
+#include "Qt3DSVector3.h"
+#include "Qt3DSColor.h"
+#include "Qt3DSAttributeHashes.h"
+#include "Qt3DSFileStream.h"
+#include "Qt3DSDataLogger.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "foundation/FileTools.h"
+#include "foundation/Qt3DSSystem.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "Qt3DSSlideSystem.h"
+#include "Qt3DSRenderModel.h"
+
+#include "EASTL/vector.h"
+#include "EASTL/list.h"
+#include <sys/stat.h>
+#include "Qt3DSCommandEventTypes.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSRuntimeFactory.h"
+#include "Qt3DSRenderContextCore.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "foundation/Qt3DSPerfTimer.h"
+#include "Qt3DSRenderThreadPool.h"
+#include "Qt3DSRenderImageBatchLoader.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "Qt3DSAudioPlayer.h"
+#include "Qt3DSActivationManager.h"
+#include "Qt3DSParametersSystem.h"
+#include "Qt3DSQmlElementHelper.h"
+#include "q3dsqmlscript.h"
+#include "Qt3DSRenderRuntimeBindingImpl.h"
+#include "Qt3DSRenderBufferManager.h"
+#include "Qt3DSImportMesh.h"
+#include "Qt3DSRenderer.h"
+#include "q3dsmaterialdefinitionparser.h"
+#include "Qt3DSRenderCustomMaterialSystem.h"
+#include "Qt3DSRenderDynamicObjectSystem.h"
+#include "Qt3DSRenderMaterialHelpers.h"
+#include "Qt3DSRenderUIPLoader.h"
+#include "Qt3DSDMMetaData.h"
+#include "Qt3DSRenderUIPSharedTranslation.h"
+
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQml/qjsengine.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qfileinfo.h>
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+#if defined(_DEBUG) || (_PROFILE)
+#define Q3DStudio_LOG_EVENT(S) // something
+#else
+#define Q3DStudio_LOG_EVENT(S)
+#endif
+
+using qt3ds::runtime::IApplication;
+using namespace qt3ds;
+
+namespace __SQmlEngineImpl_Basic_Structs__ {
+ struct QmlScopedLock
+ {
+ qt3ds::foundation::Mutex *m_Mutex;
+
+ QmlScopedLock(qt3ds::foundation::Mutex *mtx)
+ : m_Mutex(mtx)
+ {
+ if (m_Mutex)
+ m_Mutex->lock();
+ }
+
+ ~QmlScopedLock()
+ {
+ if (m_Mutex)
+ m_Mutex->unlock();
+ }
+ };
+}
+
+using namespace __SQmlEngineImpl_Basic_Structs__;
+#define QML_ENGINE_MULTITHREAD_PROTECT_METHOD QmlScopedLock __locker(m_MultithreadedMutex);
+
+//==============================================================================
+// Callback handling
+/**
+* Constructor
+*/
+CScriptCallbacks::CScriptCallbacks()
+ : m_CallbackList(0, 0, "CallbackList")
+{
+}
+
+/**
+* Destructor
+*/
+CScriptCallbacks::~CScriptCallbacks()
+{
+ FOR_ARRAY(SFrameCallbackEntry *, theEntry, m_CallbackList)
+ Q3DStudio_delete(*theEntry, SFrameCallbackEntry);
+
+ m_CallbackList.Clear();
+}
+
+bool CScriptCallbacks::RegisterCallback(Q3DStudio::UINT32 callbackType,
+ const TScriptCallback inCallback, void *inUserData)
+{
+ // currently we only support these two types of callbacks
+ if (callbackType != SCRIPT_ON_INITIALIZE && callbackType != SCRIPT_ON_UPDATE) {
+ return false;
+ }
+
+ SFrameCallbackEntry *theFrameCallbackEntry = NULL;
+
+ try {
+ // note we don't care if someone registers the same function twice.
+ theFrameCallbackEntry = Q3DStudio_new(SFrameCallbackEntry) SFrameCallbackEntry();
+
+ if (!theFrameCallbackEntry) {
+ throw "failed to allocated SFrameCallbackEntry";
+ }
+
+ SScriptCallbackEntry *theScriptCallbackEntry =
+ Q3DStudio_new(SScriptCallbackEntry) SScriptCallbackEntry();
+ if (!theScriptCallbackEntry) {
+ throw "failed to allocated SScriptCallbackEntry";
+ }
+
+ theScriptCallbackEntry->m_CallbackType = callbackType;
+ theScriptCallbackEntry->m_Function = inCallback;
+ theScriptCallbackEntry->m_UserData = inUserData;
+ theScriptCallbackEntry->m_Processed = false;
+
+ theFrameCallbackEntry->m_Callbacks.Push(theScriptCallbackEntry);
+
+ m_CallbackList.Push(theFrameCallbackEntry);
+
+ } catch (const char *) {
+ if (theFrameCallbackEntry)
+ Q3DStudio_delete(theFrameCallbackEntry, SFrameCallbackEntry);
+
+ return false;
+ }
+
+ return true;
+}
+
+void CScriptCallbacks::UnregisterCallback(Q3DStudio::UINT32, const TScriptCallback)
+{
+ // not used so far
+}
+
+void CScriptCallbacks::ProcessCallbacks()
+{
+ // Call onInitialize function
+ FOR_ARRAY(SFrameCallbackEntry *, theFrameEntry, m_CallbackList)
+ {
+ FOR_ARRAY(SScriptCallbackEntry *, theEntry, (*theFrameEntry)->m_Callbacks)
+ {
+ if (((*theEntry)->m_CallbackType == SCRIPT_ON_INITIALIZE) && !(*theEntry)->m_Processed
+ && (*theEntry)->m_Function) {
+ (*theEntry)->m_Function((*theEntry)->m_UserData);
+ (*theEntry)->m_Processed = true;
+ }
+ }
+ }
+
+ // Call onUpdate functions
+ FOR_ARRAY(SFrameCallbackEntry *, theFrameEntry, m_CallbackList)
+ {
+ FOR_ARRAY(SScriptCallbackEntry *, theEntry, (*theFrameEntry)->m_Callbacks)
+ {
+ // int type = (*theEntry)->m_CallbackType;
+ if ((*theEntry)->m_CallbackType == SCRIPT_ON_UPDATE && (*theEntry)->m_Function) {
+ (*theEntry)->m_Function((*theEntry)->m_UserData);
+ }
+ }
+ }
+}
+
+//==============================================================================
+//* End of helper class handling element operations
+//==============================================================================
+
+//==============================================================================
+/**
+* Helper class handling command operations
+*/
+class CQmlCommandHelper
+{
+ //==============================================================================
+ // Methods
+ //==============================================================================
+private: // Constructors
+ CQmlCommandHelper();
+ virtual ~CQmlCommandHelper();
+
+public: // static Functions
+ static bool SetupGotoSlideCommand(TElement &inElement, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs);
+ static bool SetupGotoSlideCommand(TElement &inElement, Q3DStudio::INT32 inSlideIndex,
+ const SScriptEngineGotoSlideArgs &inArgs);
+
+protected: // Static Hidden Helpers
+ // static TElement* FireCommand(TEventCommandHash inCommand);
+
+public: // Static Helpers
+ static TElement *GetComponentParent(TElement *inParent);
+};
+
+//==============================================================================
+/**
+* Constructor
+*/
+CQmlCommandHelper::CQmlCommandHelper()
+{
+}
+
+//==============================================================================
+/**
+* Destructor
+*/
+CQmlCommandHelper::~CQmlCommandHelper()
+{
+}
+
+static Option<Q3DStudio::UINT8> FindSlide(TElement &inElement, const char *slideName)
+{
+ IPresentation *thePresentation = inElement.GetBelongedPresentation();
+
+ int theSlideHashName = thePresentation->GetApplication().HashString(slideName);
+ TComponent *theComponent = thePresentation->GetComponentManager().GetComponent(&inElement);
+ UINT8 theSlideIndex =
+ thePresentation->GetSlideSystem().FindSlide(*theComponent, theSlideHashName);
+ if (theSlideIndex >= theComponent->GetSlideCount()) {
+ qt3ds::foundation::CRegisteredString elemPath = thePresentation->GetElementPath(inElement);
+ if (elemPath.IsValid()) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "QMLCommandHelper: goToSlide: Unable to find slide "
+ << slideName <<"on element " << elemPath.c_str();
+ } else {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "QMLCommandHelper: goToSlide: Unable to find slide "
+ << slideName;
+ }
+ return Empty();
+ }
+
+ return theSlideIndex;
+}
+
+TElement *CQmlCommandHelper::GetComponentParent(TElement *inElement)
+{
+ Q3DStudio_ASSERT(inElement);
+ return &inElement->GetComponentParent();
+}
+
+bool CQmlCommandHelper::SetupGotoSlideCommand(TElement &inElement, Q3DStudio::INT32 inSlideIndex,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ IPresentation *thePresentation = inElement.GetBelongedPresentation();
+ TElement *theTarget = GetComponentParent(&inElement);
+ SComponentGotoSlideData theSlideData(inSlideIndex);
+ theSlideData.m_Mode = inArgs.m_Mode;
+ theSlideData.m_Paused = inArgs.m_Paused;
+ theSlideData.m_Rate = inArgs.m_Rate;
+ theSlideData.m_Reverse = inArgs.m_Reverse;
+ theSlideData.m_StartTime = inArgs.m_StartTime;
+ // Resolve playthroughto if it has a valid value.
+ if (!isTrivial(inArgs.m_PlaythroughTo)) {
+ if (AreEqual(inArgs.m_PlaythroughTo, "next"))
+ theSlideData.m_PlaythroughTo = -1;
+ else if (AreEqual(inArgs.m_PlaythroughTo, "previous"))
+ theSlideData.m_PlaythroughTo = -2;
+ else {
+ // Find the slide if possible. If not, then just error leave things as they are.
+
+ Option<UINT8> theSlideIndex = FindSlide(inElement, inArgs.m_PlaythroughTo);
+ if (theSlideIndex.hasValue())
+ theSlideData.m_PlaythroughTo = *theSlideIndex;
+ }
+ }
+ thePresentation->GetComponentManager().SetupComponentGotoSlideCommand(theTarget, theSlideData);
+ UVariant theArg1;
+ UVariant theArg2;
+ qt3ds::intrinsics::memZero(&theArg1, sizeof(UVariant));
+ qt3ds::intrinsics::memZero(&theArg2, sizeof(UVariant));
+ thePresentation->FireCommand(COMMAND_GOTOSLIDE, theTarget, &theArg1, &theArg2);
+ return true;
+}
+
+bool CQmlCommandHelper::SetupGotoSlideCommand(TElement &inElement, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ Option<UINT8> theSlideIndex = FindSlide(inElement, slideName);
+ if (theSlideIndex.hasValue())
+ return SetupGotoSlideCommand(inElement, *theSlideIndex, inArgs);
+
+ return false;
+}
+
+//==============================================================================
+//* End of helper class handling command operations
+//==============================================================================
+
+struct SEmitSignalData : public NVRefCounted
+{
+ NVAllocatorCallback &m_Alloc;
+ volatile QT3DSI32 mRefCount;
+ INT32 m_SignalNameHandler; ///< The name of the signal to emit
+ TElement *m_TargetElement; ///< The source element where the event is going to occur
+
+ SEmitSignalData(NVAllocatorCallback &alloc, INT32 inSignalName, TElement *inElement)
+ : m_Alloc(alloc)
+ , mRefCount(0)
+ , m_SignalNameHandler(inSignalName)
+ , m_TargetElement(inElement)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Alloc)
+};
+
+class CQmlEngineImpl : public CQmlEngine
+{
+ using TEmitSignalPtr = NVScopedRefCounted<SEmitSignalData>;
+ using TEmitSignalQueue = eastl::list<TEmitSignalPtr, ForwardingAllocator>;
+ using TPropertyDescAndValueList = eastl::vector<qt3ds::runtime::element::TPropertyDescAndValue>;
+ using TPropertyDesc = qt3ds::runtime::element::SPropertyDesc;
+
+ static const int MAX_ACTION_QUEUE_SIZE = 100;
+
+private:
+ qt3ds::NVFoundationBase &m_Foundation;
+ IApplication *m_Application;
+ IApplication *m_ApplicationCore;
+ qt3ds::foundation::Mutex m_PreloadMutex;
+ qt3ds::foundation::Mutex *m_MultithreadedMutex;
+
+ TEmitSignalQueue m_EmitSignalDataList;
+
+ QT3DSI32 mRefCount;
+
+ CScriptCallbacks m_ScriptCallbacks;
+
+ QQmlEngine m_engine;
+ QMap<QString, QQmlComponent *> m_components;
+ QVector<Q3DSQmlScript *> m_scripts;
+
+public:
+ CQmlEngineImpl(NVFoundationBase &fnd, ITimeProvider &inTimeProvider);
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ // functions from IScriptBridge
+ void EnableMultithreadedAccess() override;
+ void DisableMultithreadedAccess() override;
+
+ void SetApplicationCore(qt3ds::runtime::IApplication &inApplication) override;
+ void SetApplication(qt3ds::runtime::IApplication &inApplication) override;
+ qt3ds::runtime::IApplication *GetApplication() override;
+ void Initialize() override;
+
+ void LoadScript(IPresentation *presentation, TElement *element, const CHAR *path) override;
+ Q3DStudio::INT32 InitializeApplicationBehavior(const char *) override
+ {
+ return -1;
+ }
+
+ void ProcessFrameCallbacks(IPresentation *) override;
+ void ExecuteApplicationScriptFunction(Q3DStudio::INT32, const char *) override {}
+ void CallFunction(const char *, const char *,
+ CScriptEngineCallFunctionArgRetriever &) override {}
+
+ void ProcessSignal(IPresentation *inPresentation,
+ const SEventCommand &inCommand) override;
+ void ProcessCustomActions(IPresentation *inPresentation,
+ const SEventCommand &inCommand) override;
+ void ProcessCustomCallback(IPresentation *, const SEventCommand &) override {}
+
+ void SetTableForElement(TElement &, IScriptTableProvider &) override {}
+ void SetAttribute(TElement *target, const char *attName, const char *value) override;
+ void SetAttribute(const char *element, const char *attName, const char *value) override;
+ bool GetAttribute(TElement *target, const char *attName, char *value) override;
+ bool GetAttribute(const char *element, const char *attName, char *value) override;
+ void FireEvent(const char *element, const char *evtName) override;
+ void SetDataInputValue(const QString &name, const QVariant &value,
+ qt3ds::runtime::DataInputValueRole valueRole) override;
+ void createElements(const QString &parentElementPath, const QString &slideName,
+ const QVector<QHash<QString, QVariant>> &properties,
+ qt3ds::render::IQt3DSRenderer *renderer) override;
+ void deleteElements(const QStringList &elementPaths,
+ qt3ds::render::IQt3DSRenderer *renderer) override;
+ void createMaterials(const QString &subPresId, const QStringList &materialDefinitions,
+ qt3ds::render::ICustomMaterialSystem *customMaterialSystem,
+ IDynamicObjectSystem *dynamicObjectSystem,
+ qt3ds::render::IQt3DSRenderer *renderer) override;
+ void deleteMaterials(const QStringList &materialNames,
+ qt3ds::render::IQt3DSRenderer *renderer) override;
+ void createMesh(const QString &name, qt3dsimp::Mesh *mesh,
+ qt3ds::render::IBufferManager *bufferManager) override;
+ void deleteMeshes(const QStringList &meshNames,
+ qt3ds::render::IBufferManager *bufferManager) override;
+
+ void GotoSlide(const char *component, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs) override;
+ void GotoSlideRelative(const char *, bool, bool, const SScriptEngineGotoSlideArgs &) override;
+
+ void SetPresentationAttribute(const char *, const char *, const char *) override;
+
+ // No need to implement here, as sound playing is done in Qt3DSViewerApp
+ bool PlaySoundFile(const char *) override { return false; }
+
+ void EnableDebugging(qt3ds::state::debugger::IMultiProtocolSocket &) override {}
+ void EnableProfiling() override {}
+ void StepGC() override {}
+
+ // functions from CQMLEngine
+ bool PeekSignal(TElement *&outElement, char *&outName) override;
+ void GotoSlideIndex(const char *component, const Q3DStudio::INT32 slideIndex,
+ const SScriptEngineGotoSlideArgs &inArgs) override;
+ bool GetSlideInfo(const char *elementPath, int &currentIndex, int &previousIndex,
+ QString &currentName, QString &previousName) override;
+ void GotoTime(const char *component, const Q3DStudio::FLOAT time) override;
+ bool RegisterCallback(Q3DStudio::UINT32 callbackType, const TScriptCallback inCallback,
+ void *inUserData) override;
+ void Shutdown(qt3ds::NVFoundationBase &inFoundation) override;
+
+private:
+ TElement *createMaterialContainer(TElement *parent, CPresentation *presentation);
+ void createComponent(QQmlComponent *component, TElement *element);
+ TElement *getTarget(const char *component);
+ void listAllElements(TElement *root, QList<TElement *> &elements);
+ void initializeDataInputsInPresentation(CPresentation &presentation, bool isPrimary,
+ QList<TElement *> inElements = QList<TElement *>());
+ void initializeDataOutputsInPresentation(CPresentation &presentation, bool isPrimary);
+ // Splits down vector attributes to components as Runtime does not really
+ // handle vectors at this level anymore
+ bool getAttributeVector3(QVector<QByteArray> &outAttVec, const QByteArray &attName,
+ TElement *elem);
+ bool getAttributeVector2(QVector<QByteArray> &outAttVec, const QByteArray &attName,
+ TElement *elem);
+ // build and evaluate Evaluator datainput type expression
+ QJSValue buildJSFunc(const QString &userFunc);
+ // pass controller name for error reporting purposes
+ QVariant callJSFunc(const QString &controllerName, qt3ds::runtime::DataInputDef &diDef,
+ const QVariant::Type type);
+ // matches all numeric datatypes so we do not get datatype mismatch when JS
+ // decides to change result datatype (f.ex from double to int when result
+ // fractional part for specific input values happens to be exactly zero)
+ bool isMatchingDatatype(QVariant::Type resultType, QVariant::Type propertyType);
+ // find out which datainputs are used in the expression
+ QVector<QString> resolveDependentDatainputs(const QString &expression,
+ const QString &controllerName);
+
+ // Methods to add element attributes to list for element creation
+ void addStringAttribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QString &inAttName, const QString &inValue);
+ void addIntAttribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QString &inAttName, int inValue);
+ void addBoolAttribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QString &inAttName, bool inValue);
+ void addFloatAttribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QString &inAttName, float inValue);
+ void addFloat2Attribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QStringList &inAttNames, const QVector2D &inValue);
+ void addFloat3Attribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QStringList &inAttNames, const QVector3D &inValue);
+ void addFloat4Attribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QStringList &inAttNames, const QVector4D &inValue);
+ void addElementRefAttribute(qt3ds::foundation::IStringTable &strTable,
+ TPropertyDescAndValueList &list,
+ const QString &inAttName, TElement *element);
+ template <typename TDataType>
+ void setDynamicObjectProperty(qt3ds::render::SDynamicObject &material,
+ const qt3ds::render::dynamic::SPropertyDefinition &propDesc,
+ const TDataType &propValue);
+ QVector2D parseFloat2Property(const QString &propValue);
+ QVector3D parseFloat3Property(const QString &propValue);
+ QVector4D parseFloat4Property(const QString &propValue);
+
+ void notifyElementCreation(const QStringList &elementNames, const QString &error);
+ void notifyMaterialCreation(const QStringList &materialNames, const QString &error);
+ void deleteElements(const QVector<TElement *> &elements,
+ qt3ds::render::IQt3DSRenderer *renderer);
+};
+
+CQmlEngineImpl::CQmlEngineImpl(NVFoundationBase &fnd, ITimeProvider &)
+ : m_Foundation(fnd)
+ , m_Application(NULL)
+ , m_ApplicationCore(NULL)
+ , m_PreloadMutex(fnd.getAllocator())
+ , m_MultithreadedMutex(NULL)
+ , m_EmitSignalDataList(
+ ForwardingAllocator(fnd.getAllocator(), "CQmlEngineImpl::m_EmitSignalDataList"))
+ , mRefCount(0)
+{
+ qmlRegisterType<Q3DSQmlBehavior>("QtStudio3D.Behavior", 1, 0, "Behavior");
+ qmlRegisterType<Q3DSQmlBehavior, 1>("QtStudio3D.Behavior", 1, 1, "Behavior");
+}
+
+void CQmlEngineImpl::Shutdown(qt3ds::NVFoundationBase &inFoundation)
+{
+}
+
+void CQmlEngineImpl::EnableMultithreadedAccess()
+{
+ m_MultithreadedMutex = &m_PreloadMutex;
+}
+
+void CQmlEngineImpl::DisableMultithreadedAccess()
+{
+ m_MultithreadedMutex = NULL;
+}
+
+void CQmlEngineImpl::SetApplicationCore(runtime::IApplication &inApplication)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ m_ApplicationCore = &inApplication;
+}
+
+void CQmlEngineImpl::SetApplication(qt3ds::runtime::IApplication &inApplication)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ m_Application = &inApplication;
+}
+
+qt3ds::runtime::IApplication *CQmlEngineImpl::GetApplication()
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ return m_Application;
+}
+
+void CQmlEngineImpl::Initialize()
+{
+ // Gather data input controlled properties
+ QList<CPresentation *> presentations = m_Application->GetPresentationList();
+
+ for (int i = 0; i < presentations.size(); ++i) {
+ initializeDataInputsInPresentation(*presentations[i], i == 0);
+ initializeDataOutputsInPresentation(*presentations[i], i == 0);
+ }
+}
+
+void CQmlEngineImpl::SetAttribute(TElement *target, const char *attName, const char *value)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ if (target) {
+ bool success = CQmlElementHelper::SetAttribute(target, attName, value, false);
+ if (!success) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::SetAttribute: "
+ << "failed to set attribute on element"
+ << target << ":" << attName << ":" << value;
+ }
+ }
+}
+
+void CQmlEngineImpl::SetAttribute(const char *element, const char *attName, const char *value)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+
+ TElement *theTarget = getTarget(element);
+ if (theTarget) {
+ bool success = CQmlElementHelper::SetAttribute(theTarget, attName, value, false);
+ if (!success) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::SetAttribute: "
+ << "failed to set attribute on element"
+ << element << ":" << attName << ":" << value;
+ }
+ }
+}
+
+bool CQmlEngineImpl::GetAttribute(TElement *target, const char *attName, char *value)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+
+ if (target) {
+ bool success = CQmlElementHelper::GetAttribute(target, attName, value);
+ if (!success) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::GetAttribute: "
+ << "failed to get attribute on element"
+ << target << ":" << attName << ":" << value;
+ }
+ return success;
+ }
+
+ return false;
+}
+
+bool CQmlEngineImpl::GetAttribute(const char *element, const char *attName, char *value)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+
+ TElement *theTarget = getTarget(element);
+ if (theTarget) {
+ bool success = CQmlElementHelper::GetAttribute(theTarget, attName, value);
+ if (!success) {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::GetAttribute: "
+ << "failed to get attribute on element"
+ << element << ":" << attName << ":" << value;
+ }
+ return success;
+ }
+
+ return false;
+}
+
+void CQmlEngineImpl::LoadScript(IPresentation *presentation, TElement *element, const CHAR *path)
+{
+ QString presPath = QFileInfo(presentation->GetFilePath()).absolutePath();
+
+ QString sourcePath(presPath + QLatin1Char('/') + path);
+ sourcePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
+
+ TElement *parent = element->GetParent();
+ if (!parent)
+ return;
+
+ if (!m_components.contains(sourcePath)) {
+ m_components[sourcePath] = new QQmlComponent(&m_engine, QUrl::fromLocalFile(sourcePath),
+ &m_engine);
+ }
+
+ QQmlComponent *component = m_components[sourcePath];
+ if (component->status() == QQmlComponent::Ready) {
+ createComponent(component, element);
+ } else if (component->status() == QQmlComponent::Error) {
+ qWarning() << "Error while loading script"
+ << component->url().toString()
+ << ":" << component->errorString();
+ } else {
+ QObject::connect(component, &QQmlComponent::statusChanged,
+ [this, component, element] (QQmlComponent::Status status) {
+ if (status == QQmlComponent::Ready) {
+ createComponent(component, element);
+ } else {
+ qWarning() << "Error while loading script"
+ << component->url().toString()
+ << ":" << component->errorString();
+ }
+ }
+ );
+ }
+}
+
+void CQmlEngineImpl::FireEvent(const char *element, const char *evtName)
+{
+ TElement *theElement = getTarget(element);
+ if (theElement && theElement->GetActive() == true) {
+ IPresentation *thePresentation = theElement->GetBelongedPresentation();
+ thePresentation->FireEvent(CHash::HashEventCommand(evtName), theElement);
+ } else {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::FireEvent: "
+ << "failed to find element: "
+ << element << " " << evtName;
+ }
+}
+
+void CQmlEngineImpl::SetDataInputValue(
+ const QString &name, const QVariant &value,
+ qt3ds::runtime::DataInputValueRole valueRole = qt3ds::runtime::DataInputValueRole::Value)
+{
+ qt3ds::runtime::DataInputMap &diMap = m_Application->dataInputMap();
+ if (diMap.contains(name)) {
+ qt3ds::runtime::DataInputDef &diDef = diMap[name];
+ switch (valueRole) {
+ case qt3ds::runtime::DataInputValueRole::Value: { // switch (valueRole)
+ diDef.value = value;
+ const QVector<qt3ds::runtime::DataInOutAttribute> &ctrlAtt
+ = diDef.controlledAttributes;
+ for (const qt3ds::runtime::DataInOutAttribute &ctrlElem : ctrlAtt) {
+ switch (ctrlElem.propertyType) {
+ case ATTRIBUTETYPE_DATAINPUT_TIMELINE: {
+ // Quietly ignore other than number type data inputs when adjusting timeline
+ if (diDef.type == qt3ds::runtime::DataInOutTypeRangedNumber) {
+ TTimeUnit endTime = 0;
+ TElement *element = getTarget(ctrlElem.elementPath.constData());
+ TComponent *component = static_cast<TComponent *>(element);
+ endTime = component->GetTimePolicy().GetLoopingDuration();
+
+ // Normalize the value to dataInput range
+ qreal newTime = qreal(endTime) * (qreal(value.toFloat() - diDef.min)
+ / qreal(diDef.max - diDef.min));
+ GotoTime(ctrlElem.elementPath.constData(), float(newTime / 1000.0));
+ }
+ break;
+ }
+ case ATTRIBUTETYPE_DATAINPUT_SLIDE: {
+ // Quietly ignore other than string type when adjusting slide
+ if (diDef.type == qt3ds::runtime::DataInOutTypeString) {
+ const QByteArray valueStr = value.toString().toUtf8();
+ GotoSlide(ctrlElem.elementPath.constData(), valueStr.constData(),
+ SScriptEngineGotoSlideArgs());
+ }
+ break;
+ }
+ // Silently ignore invalid incoming type if it does not
+ // match with the datainput type except with type Variant, for which
+ // the incoming value is cast to target property type without checking.
+ // Caveat emptor.
+
+ // For Evaluator, typecheck the JS evaluation result to see if it
+ // matches with the target property.
+
+ // Handle ranged number similarly to generic float
+ // if it is bound to properties other
+ // than timeline animation i.e. disregard range min and max
+ case ATTRIBUTETYPE_FLOAT: {
+ float valueFloat;
+ if (diDef.type == qt3ds::runtime::DataInOutTypeFloat
+ || diDef.type == qt3ds::runtime::DataInOutTypeRangedNumber
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
+ valueFloat = value.toFloat();
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
+ valueFloat = callJSFunc(name, diDef, QVariant::Type::Double).toFloat();
+ } else {
+ qWarning() << __FUNCTION__ << "Property type "
+ << ctrlElem.propertyType
+ << " not matching with Datainput " << name
+ << " data type "
+ << diDef.type;
+ break;
+ }
+
+ SetAttribute(ctrlElem.elementPath.constData(),
+ ctrlElem.attributeName.first().constData(),
+ reinterpret_cast<const char *>(&valueFloat));
+ break;
+ }
+ case ATTRIBUTETYPE_FLOAT4: {
+ QVector4D valueVec;
+ if (diDef.type == qt3ds::runtime::DataInOutTypeVector4) {
+ valueVec = value.value<QVector4D>();
+ } else {
+ qWarning() << __FUNCTION__ << "Property type "
+ << ctrlElem.propertyType
+ << " not matching with Datainput " << name
+ << " data type "
+ << diDef.type;
+ break;
+ }
+ // Set the values of vector attribute components separately
+ for (int i = 0; i < 4; i++) {
+ const float val = valueVec[i];
+ SetAttribute(ctrlElem.elementPath.constData(),
+ ctrlElem.attributeName[i].constData(),
+ reinterpret_cast<const char *>(&val));
+ }
+ break;
+ }
+ case ATTRIBUTETYPE_FLOAT3: {
+ QVector3D valueVec;
+ if (diDef.type == qt3ds::runtime::DataInOutTypeVector3
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
+ valueVec = value.value<QVector3D>();
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
+ const QVariant res = callJSFunc(name, diDef, QVariant::Type::Vector3D);
+ valueVec = res.value<QVector3D>();
+ } else {
+ qWarning() << __FUNCTION__ << "Property type "
+ << ctrlElem.propertyType
+ << " not matching with Datainput " << name
+ << " data type "
+ << diDef.type;
+ break;
+ }
+ // Set the values of vector attribute components separately
+ for (int i = 0; i < 3; i++) {
+ const float val = valueVec[i];
+ SetAttribute(ctrlElem.elementPath.constData(),
+ ctrlElem.attributeName[i].constData(),
+ reinterpret_cast<const char *>(&val));
+ }
+ break;
+ }
+ case ATTRIBUTETYPE_FLOAT2:
+ {
+ QVector2D valueVec;
+ if (diDef.type == qt3ds::runtime::DataInOutTypeVector2
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
+ valueVec = value.value<QVector2D>();
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
+ const QVariant res = callJSFunc(name, diDef, QVariant::Type::Vector2D);
+ valueVec = res.value<QVector2D>();
+ } else {
+ qWarning() << __FUNCTION__ << "Property type "
+ << ctrlElem.propertyType
+ << " not matching with Datainput " << name
+ << " data type "
+ << diDef.type;
+ break;
+ }
+ // Set the values of vector attribute components separately
+ for (int i = 0; i < 2; i++) {
+ const float val = valueVec[i];
+ SetAttribute(ctrlElem.elementPath.constData(),
+ ctrlElem.attributeName[i].constData(),
+ reinterpret_cast<const char *>(&val));
+ }
+ break;
+ }
+ case ATTRIBUTETYPE_BOOL: {
+ uint valueBool; // SetAttribute requires at least 32-bit variable
+ if (diDef.type == qt3ds::runtime::DataInOutTypeBoolean
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
+ valueBool = value.toBool();
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
+ valueBool = callJSFunc(name, diDef, QVariant::Type::Bool).toBool();
+ } else {
+ qWarning() << __FUNCTION__ << "Property type "
+ << ctrlElem.propertyType
+ << " not matching with Datainput " << name
+ << " data type "
+ << diDef.type;
+ break;
+ }
+
+ SetAttribute(ctrlElem.elementPath.constData(),
+ ctrlElem.attributeName.first().constData(),
+ reinterpret_cast<const char *>(&valueBool));
+ break;
+ }
+ case ATTRIBUTETYPE_STRING: {
+ QByteArray valueStr;
+ // Allow scalar number types also as inputs to string attribute
+ if (diDef.type == qt3ds::runtime::DataInOutTypeString
+ || diDef.type == qt3ds::runtime::DataInOutTypeRangedNumber
+ || diDef.type == qt3ds::runtime::DataInOutTypeFloat
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
+ valueStr = value.toString().toUtf8();
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
+ valueStr = callJSFunc(name, diDef, QVariant::Type::String)
+ .toString().toUtf8();
+ } else {
+ qWarning() << __FUNCTION__ << "Property type "
+ << ctrlElem.propertyType
+ << " not matching with Datainput " << name
+ << " data type "
+ << diDef.type;
+ break;
+ }
+
+ SetAttribute(ctrlElem.elementPath.constData(),
+ ctrlElem.attributeName.first().constData(),
+ valueStr.constData());
+ break;
+ }
+ default:
+ QT3DS_ALWAYS_ASSERT_MESSAGE("Unexpected data input type");
+ break;
+ }
+ }
+
+ // Trigger re-evaluation of Evaluator datainputs that use this datainput
+ // as source data. Do this by calling setDataInputValue for evaluator
+ // with the current set value of the Evaluator (_not_ the evaluator result)
+ for (auto dependent : diDef.dependents) {
+ // Dependent list also contains the name of this datainput if
+ // the value of this datainput is used as source data. In this case
+ // obviously do not cause infinite recursion.
+ if (dependent != name)
+ SetDataInputValue(dependent, diMap[dependent].value);
+ }
+ break;
+ }
+ case qt3ds::runtime::DataInputValueRole::Max: { // switch (valueRole)
+ diDef.max = value.toFloat();
+ break;
+ }
+ case qt3ds::runtime::DataInputValueRole::Min: { // switch (valueRole)
+ diDef.min = value.toFloat();
+ break;
+ }
+ }
+ }
+}
+
+static int _idCounter = 0;
+
+void CQmlEngineImpl::createElements(const QString &parentElementPath, const QString &slideName,
+ const QVector<QHash<QString, QVariant>> &properties,
+ qt3ds::render::IQt3DSRenderer *renderer)
+{
+ using namespace qt3ds::render;
+
+ int elementIndex = -1;
+ QString error;
+ CPresentation *presentation = nullptr;
+ QStringList elementPaths;
+ elementPaths.reserve(properties.size());
+ QVector<QHash<QString, QVariant>> theProperties = properties;
+ const QString namePropName = QStringLiteral("name");
+
+ for (int i = 0; i < theProperties.size(); ++i) {
+ auto &props = theProperties[i];
+ QString newElementName = props.value(namePropName).toString();
+ if (newElementName.isEmpty()) {
+ // The id number on generated name will match generated graph object identifiers
+ newElementName = QStringLiteral("NewElement_%1").arg(_idCounter + i + 1);
+ props.insert(namePropName, newElementName);
+ }
+ elementPaths << parentElementPath + QLatin1Char('.') + newElementName;
+ }
+ QVector<TElement *> createdElements;
+ TElement *parentElement = nullptr;
+
+ auto handleError = [&]() {
+ if (!error.isEmpty())
+ deleteElements(createdElements, renderer);
+ notifyElementCreation(elementPaths, error);
+ };
+
+ // Resolve parent element
+ QByteArray theParentPath = parentElementPath.toUtf8();
+ parentElement = getTarget(theParentPath.constData());
+
+ if (!parentElement) {
+ error = QObject::tr("Invalid parent element: '%1'").arg(parentElementPath);
+ handleError();
+ return;
+ }
+
+ auto parentTranslator = static_cast<Qt3DSTranslator *>(parentElement->GetAssociation());
+
+ if (!parentTranslator || !GraphObjectTypes::IsNodeType(
+ parentTranslator->GetUIPType())) {
+ error = QObject::tr("Parent element is not a valid node: '%1'").arg(parentElementPath);
+ handleError();
+ return;
+ }
+
+ TElement &component = parentElement->GetComponentParent();
+ auto &parentObject = static_cast<SNode &>(parentTranslator->RenderObject());
+ presentation = static_cast<CPresentation *>(parentElement->GetBelongedPresentation());
+
+ // Resolve slide
+ QByteArray theSlideName = slideName.toUtf8();
+ ISlideSystem &slideSystem = presentation->GetSlideSystem();
+ int slideIndex = slideSystem.FindSlide(component, theSlideName.constData());
+ int currentSlide = static_cast<TComponent &>(component).GetCurrentSlide();
+ if (slideIndex == 0xff) {
+ error = QObject::tr("Invalid slide name for time context: '%1'").arg(slideName);
+ handleError();
+ return;
+ }
+
+ auto &strTable = presentation->GetStringTable();
+
+ const QString sourcePathPropName = QStringLiteral("sourcepath");
+ const QString startTimePropName = QStringLiteral("starttime");
+ const QString endTimePropName = QStringLiteral("endtime");
+ const QString eyeBallPropName = QStringLiteral("eyeball");
+ const QString typePropName = QStringLiteral("type");
+
+ Q3DStudio::UVariant attValue;
+ parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue);
+ const int parentStartTime = int(attValue.m_INT32);
+ parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue);
+ const int parentEndTime = int(attValue.m_INT32);
+
+ for (const auto &currentProps : qAsConst(theProperties)) {
+ ++_idCounter;
+ ++elementIndex;
+
+ // Remove properties requiring custom handling
+ QHash<QString, QVariant> fixedProps = currentProps;
+ QString newElementName = fixedProps.take(namePropName).toString();
+ QByteArray newElementNameBa = newElementName.toUtf8();
+
+ QString refMatName = fixedProps.take(QStringLiteral("material")).toString();
+ int colonIndex = refMatName.indexOf(QLatin1Char(':'));
+ if (colonIndex != -1)
+ refMatName = refMatName.mid(colonIndex + 1);
+ if (refMatName.startsWith(QLatin1Char('#'))) // Absolute reference
+ refMatName = refMatName.mid(1);
+ else if (!refMatName.isEmpty() && !refMatName.contains(QLatin1Char('/')))
+ refMatName = QStringLiteral("/") + refMatName;
+
+ // Make sure the name is not duplicate
+ TElement *existingChild
+ = parentElement->FindChild(CHash::HashString(newElementNameBa.constData()));
+ if (existingChild) {
+ error = QObject::tr("Element already exists: '%1'").arg(elementPaths[elementIndex]);
+ handleError();
+ return;
+ }
+
+ const CRegisteredString regName = strTable.RegisterStr(newElementNameBa);
+ TPropertyDescAndValueList elementProperties;
+ CRegisteredString metaType;
+ const CRegisteredString elementSubType;
+ GraphObjectTypes::Enum objectType = GraphObjectTypes::Unknown;
+
+ QString typeStr = fixedProps.take(typePropName).toString();
+ if (typeStr.isEmpty() || typeStr.compare(QLatin1String("model"),
+ Qt::CaseInsensitive) == 0) {
+ metaType = strTable.RegisterStr("Model");
+ objectType = GraphObjectTypes::Model;
+ } else if (typeStr.compare(QLatin1String("group"), Qt::CaseInsensitive) == 0) {
+ metaType = strTable.RegisterStr("Group");
+ objectType = GraphObjectTypes::Node;
+ }
+
+ // Set default values for missing mandatory properties
+ bool eyeBall = true;
+ fixedProps.value(eyeBallPropName, true).toBool();
+ if (objectType == GraphObjectTypes::Model && !fixedProps.contains(sourcePathPropName)) {
+ addStringAttribute(strTable, elementProperties, sourcePathPropName,
+ QStringLiteral("#Cube"));
+ }
+ if (!fixedProps.contains(startTimePropName))
+ addIntAttribute(strTable, elementProperties, startTimePropName, parentStartTime);
+ if (!fixedProps.contains(endTimePropName))
+ addIntAttribute(strTable, elementProperties, endTimePropName, parentEndTime);
+ if (!fixedProps.contains(eyeBallPropName))
+ addBoolAttribute(strTable, elementProperties, eyeBallPropName, true);
+ else
+ eyeBall = fixedProps.value(eyeBallPropName).toBool();
+
+ QHashIterator<QString, QVariant> it(fixedProps);
+ while (it.hasNext()) {
+ it.next();
+ switch (it.value().type()) {
+ case QVariant::Double:
+ addFloatAttribute(strTable, elementProperties, it.key(), it.value().toFloat());
+ break;
+ case QVariant::Bool:
+ addBoolAttribute(strTable, elementProperties, it.key(), it.value().toBool());
+ break;
+ case QVariant::Int:
+ addIntAttribute(strTable, elementProperties, it.key(), it.value().toInt());
+ break;
+ case QVariant::String:
+ addStringAttribute(strTable, elementProperties, it.key(), it.value().toString());
+ break;
+ case QVariant::Vector3D: {
+ QVector3D vec = it.value().value<QVector3D>();
+ if (it.key() == QLatin1String("rotation")) {
+ vec.setX(qDegreesToRadians(vec.x()));
+ vec.setY(qDegreesToRadians(vec.y()));
+ vec.setZ(qDegreesToRadians(vec.z()));
+ }
+ // TODO: Need to support also colors if non-model elements what need colors are
+ // TODO: supported (QT3DS-3381)
+ QStringList atts;
+ atts << (it.key() + QLatin1String(".x"))
+ << (it.key() + QLatin1String(".y"))
+ << (it.key() + QLatin1String(".z"));
+ addFloat3Attribute(strTable, elementProperties, atts, vec);
+ break;
+ }
+ default:
+ error = QObject::tr("Unsupported property type for: '%1'").arg(it.key());
+ handleError();
+ return;
+ }
+ }
+
+ // Create new element
+ QString localElementPath = elementPaths[elementIndex];
+ colonIndex = localElementPath.indexOf(QLatin1Char(':'));
+ if (colonIndex != -1)
+ localElementPath = localElementPath.mid(colonIndex + 1);
+ TElement &newElem = m_Application->GetElementAllocator().CreateElement(
+ regName, metaType, elementSubType,
+ toConstDataRef(elementProperties.data(), QT3DSU32(elementProperties.size())),
+ presentation, parentElement, false);
+ newElem.m_Path = strTable.RegisterStr(localElementPath);
+
+ // Insert the new element into the correct slide
+ if (!slideSystem.addSlideElement(component, slideIndex, newElem, eyeBall)) {
+ // Delete created element if adding to slide failed
+ m_Application->GetElementAllocator().ReleaseElement(newElem, true);
+ error = QObject::tr("Failed to add the new element to a slide");
+ handleError();
+ return;
+ }
+
+ SGraphObject *referencedMaterial = nullptr;
+ if (objectType == GraphObjectTypes::Model) {
+ // Create material element
+ const CRegisteredString matName = strTable.RegisterStr("refmat");
+ const CRegisteredString matType = strTable.RegisterStr("ReferencedMaterial");
+ TPropertyDescAndValueList matProperties;
+ TElement &newMatElem = m_Application->GetElementAllocator().CreateElement(
+ matName, matType, elementSubType,
+ toConstDataRef(matProperties.data(), QT3DSU32(matProperties.size())),
+ presentation, &newElem, false);
+
+ QString matElemPath = localElementPath + QLatin1String(".refmat");
+ newMatElem.m_Path = strTable.RegisterStr(matElemPath);
+
+ if (!slideSystem.addSlideElement(component, slideIndex, newMatElem, eyeBall)) {
+ // Delete created element and material element if adding to slide failed
+ m_Application->GetElementAllocator().ReleaseElement(newElem, true);
+ error = QObject::tr("Failed to add the new material element to a slide");
+ handleError();
+ return;
+ }
+ // First check if we can resolve the referenced material before creating any objects
+ // Find a match in material container
+ // If the specified material is not available in original presentation, or was not
+ // specified, use the first material found as placeholder
+ TElement *rootElement = presentation->GetRoot();
+ TElement *container = rootElement->FindChild(CHash::HashString("__Container"));
+ TElement *firstChild = nullptr;
+ if (container) {
+ TElement *nextChild = container->GetChild();
+ firstChild = nextChild;
+ while (nextChild) {
+ QString childName = QString::fromUtf8(nextChild->m_Name);
+ if (childName.endsWith(refMatName)) {
+ auto tr = static_cast<Qt3DSTranslator *>(
+ nextChild->GetAssociation());
+ referencedMaterial = static_cast<SGraphObject *>(
+ &tr->RenderObject());
+ break;
+ }
+ nextChild = nextChild->GetSibling();
+ }
+ }
+
+ if (!referencedMaterial) {
+ // Empty material is assumed to be deliberate, so don't warn in that case
+ if (!refMatName.isEmpty()) {
+ qWarning() << __FUNCTION__ << "Requested material" << refMatName
+ << "was not found. Trying to find a fallback material.";
+ }
+ if (firstChild) {
+ auto tr = static_cast<Qt3DSTranslator *>(firstChild->GetAssociation());
+ referencedMaterial = static_cast<SGraphObject *>(&tr->RenderObject());
+ }
+ if (!referencedMaterial) {
+ // We could create default material into the container in case there is
+ // no materials in there, but it is unlikely that such a presentation would
+ // be used in practice.
+ m_Application->GetElementAllocator().ReleaseElement(newElem, true);
+ error = QObject::tr("Unable to resolve a fallback material");
+ handleError();
+ return;
+ }
+ }
+ }
+
+ // Create model SGraphObject
+ NVAllocatorCallback &allocator = presentation->GetScene()->allocator();
+ SModel *newObject = QT3DS_NEW(allocator, SModel)();
+ newObject->m_Id = strTable.RegisterStr((QByteArrayLiteral("__newObj_")
+ + QByteArray::number(_idCounter)).constData());
+ parentObject.AddChild(*newObject);
+
+ Qt3DSTranslator::CreateTranslatorForElement(newElem, *newObject, allocator);
+
+ if (referencedMaterial) {
+ // Create material SGraphObject
+ SReferencedMaterial *newMaterial = QT3DS_NEW(allocator, SReferencedMaterial)();
+ newMaterial->m_Id = strTable.RegisterStr(
+ (QByteArrayLiteral("__newMat_")
+ + QByteArray::number(_idCounter)).constData());
+ newMaterial->m_ReferencedMaterial = referencedMaterial;
+ newObject->AddMaterial(*newMaterial);
+ }
+
+ // Determine if element should be active based on start/end times
+ TTimeUnit startTime = 0;
+ TTimeUnit stopTime = 0;
+ if (newElem.GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue))
+ startTime = TTimeUnit(attValue.m_INT32);
+ if (newElem.GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue))
+ stopTime = TTimeUnit(attValue.m_INT32);
+ TTimeUnit localTime = newElem.GetActivityZone().GetItemLocalTime(newElem);
+
+ bool isActiveRightNow = eyeBall && localTime >= startTime && localTime <= stopTime
+ && currentSlide == slideIndex;
+
+ newElem.SetActive(isActiveRightNow);
+ newObject->m_Flags.SetActive(isActiveRightNow);
+ if (eyeBall)
+ newElem.GetActivityZone().UpdateItemInfo(newElem);
+
+ createdElements << &newElem;
+ }
+
+ bool isPrimary = presentation == m_Application->GetPrimaryPresentation() ? true : false;
+ initializeDataInputsInPresentation(*presentation, isPrimary, createdElements.toList());
+
+ renderer->ChildrenUpdated(parentObject);
+
+ handleError();
+}
+
+// Only supports deleting element types that can be added via createElement.
+void CQmlEngineImpl::deleteElements(const QStringList &elementPaths,
+ qt3ds::render::IQt3DSRenderer *renderer)
+{
+ QVector<TElement *> elements;
+ // Convert the path list to set for quicker lookups
+ QSet<QString> pathSet = elementPaths.toSet();
+
+ for (auto &elementPath : elementPaths) {
+ // Check that parent is not already included in the deleted elements
+ int idx = elementPath.lastIndexOf(QLatin1Char('.'));
+ bool parentFound = false;
+ while (idx != -1) {
+ QString parentPath = elementPath.left(idx);
+ if (pathSet.contains(parentPath)) {
+ parentFound = true;
+ break;
+ }
+ idx = parentPath.lastIndexOf(QLatin1Char('.'));
+ }
+ if (!parentFound) {
+ // Resolve element
+ QByteArray thePath = elementPath.toUtf8();
+ TElement *element = getTarget(thePath.constData());
+ if (element)
+ elements << element;
+ }
+ }
+ deleteElements(elements, renderer);
+}
+
+TElement *CQmlEngineImpl::createMaterialContainer(TElement *parent, CPresentation *presentation)
+{
+ TPropertyDescAndValueList prop;
+ auto &strTable = presentation->GetStringTable();
+ const auto matName = strTable.RegisterStr(QStringLiteral("__Container"));
+ const auto matType = strTable.RegisterStr(QStringLiteral("Material"));
+ const auto matClass = CRegisteredString();
+ TElement &element = m_Application->GetElementAllocator().CreateElement(
+ matName, matType, matClass,
+ toConstDataRef(prop.data(), prop.size()),
+ presentation, parent, false);
+ return &element;
+}
+
+/**
+ Creates material into material container of the specified subpresentation.
+ The materialDefinition parameter can contain a .materialdef file path or
+ the entire material definition in the .materialdef format.
+*/
+void CQmlEngineImpl::createMaterials(const QString &subPresId,
+ const QStringList &materialDefinitions,
+ ICustomMaterialSystem *customMaterialSystem,
+ IDynamicObjectSystem *dynamicObjectSystem,
+ qt3ds::render::IQt3DSRenderer *renderer)
+{
+ CPresentation *presentation = nullptr;
+ QString error;
+ QString presPath;
+ QString projPath;
+ struct MaterialInfo {
+ QString materialDefinition;
+ QString materialName;
+ QMap<QString, QString> materialProps;
+ QMap<QString, QMap<QString, QString>> textureProps;
+ };
+ QVector<MaterialInfo *> materialInfos;
+ QVector<TElement *> createdElements;
+
+ auto handleError = [&]() {
+ if (!error.isEmpty())
+ deleteElements(createdElements, renderer);
+ QStringList materialNames;
+ QString prefix;
+ if (!subPresId.isEmpty())
+ prefix = subPresId + QLatin1Char(':');
+ for (auto &materialInfo : materialInfos)
+ materialNames << prefix + materialInfo->materialName;
+ qDeleteAll(materialInfos);
+ notifyMaterialCreation(materialNames, error);
+ };
+
+ auto getMaterialInfos = [&]() {
+ for (auto &materialDefinition : materialDefinitions) {
+ MaterialInfo *info = new MaterialInfo;
+ info->materialDefinition = materialDefinition;
+ Q3DSMaterialDefinitionParser::getMaterialInfo(materialDefinition, projPath, presPath,
+ info->materialName, info->materialProps,
+ info->textureProps);
+ materialInfos << info;
+ }
+ };
+
+ QByteArray theSubPresId = subPresId.toUtf8();
+ if (theSubPresId.isEmpty())
+ presentation = m_Application->GetPrimaryPresentation();
+ else
+ presentation = m_Application->GetPresentationById(theSubPresId.constData());
+
+ if (!presentation) {
+ error = QObject::tr("Invalid presentation ID: '%1'").arg(subPresId);
+ presentation = m_Application->GetPrimaryPresentation();
+ presPath = QFileInfo(presentation->GetFilePath()).absolutePath();
+ projPath = presentation->getProjectPath();
+ getMaterialInfos();
+ handleError();
+ return;
+ }
+
+ presPath = QFileInfo(presentation->GetFilePath()).absolutePath();
+ projPath = presentation->getProjectPath();
+ getMaterialInfos();
+
+ // Find material container
+ auto &strTable = presentation->GetStringTable();
+ NVAllocatorCallback &allocator = presentation->GetScene()->allocator();
+ TElement *rootElement = presentation->GetRoot();
+ const auto containerName = strTable.RegisterStr("__Container");
+ TElement *container = rootElement->FindChild(CHash::HashString(containerName.c_str()));
+ if (!container)
+ container = createMaterialContainer(rootElement, presentation);
+
+ for (auto &materialInfo : materialInfos) {
+ if (materialInfo->materialName.isEmpty() || materialInfo->materialProps.isEmpty()) {
+ error = QObject::tr("Invalid material definition: '%1'")
+ .arg(materialInfo->materialDefinition);
+ handleError();
+ return;
+ }
+
+ // We don't care about the path parameter
+ const QString pathStr = QStringLiteral("path");
+ if (materialInfo->materialProps.contains(pathStr))
+ materialInfo->materialProps.remove(pathStr);
+
+ // Check that the material doesn't already exist in container
+ TElement *firstChild = nullptr;
+ TElement *nextChild = container->GetChild();
+ firstChild = nextChild;
+ while (nextChild) {
+ QString childName = QString::fromUtf8(nextChild->m_Name);
+ if (childName == materialInfo->materialName) {
+ error = QObject::tr("Material already exists in material container");
+ handleError();
+ return;
+ }
+ nextChild = nextChild->GetSibling();
+ }
+
+ // Create material element in the container based on the material definition
+ auto &metaData = m_Application->GetMetaData();
+ const auto matName = strTable.RegisterStr(materialInfo->materialName);
+ const bool isCustomMaterial = (materialInfo->materialProps.value(QStringLiteral("type"))
+ == QLatin1String("CustomMaterial"));
+ CRegisteredString matType;
+ CRegisteredString matClass;
+ QHash<QString, qt3ds::render::dynamic::SPropertyDefinition> dynPropDefs;
+
+ if (isCustomMaterial) {
+ CRegisteredString sourcePath
+ = strTable.RegisterStr(materialInfo->materialProps.value(
+ QStringLiteral("sourcepath")).toUtf8());
+ matType = strTable.RegisterStr("CustomMaterial");
+ matClass = sourcePath; // Create just one class per shader
+ if (sourcePath.IsValid()) {
+ Option<qt3dsdm::SMetaDataCustomMaterial> matMetaData =
+ metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str());
+ if (!matMetaData.hasValue()) {
+ metaData.LoadMaterialXMLFile(matType.c_str(), matClass.c_str(), matName.c_str(),
+ sourcePath.c_str());
+ matMetaData = metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str());
+ }
+ if (matMetaData.hasValue()) {
+ qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial(
+ matClass, m_Foundation, *customMaterialSystem, matMetaData,
+ strTable);
+ NVConstDataRef<qt3ds::render::dynamic::SPropertyDefinition> customProperties;
+ customProperties = dynamicObjectSystem->GetProperties(matClass);
+ for (QT3DSU32 i = 0, end = customProperties.size(); i < end; ++i) {
+ QString propName = QString::fromUtf8(customProperties[i].m_Name.c_str());
+ if (materialInfo->materialProps.contains(propName))
+ dynPropDefs.insert(propName, customProperties[i]);
+ }
+ } else {
+ error = QObject::tr("Could not resolve properties for CustomMaterial");
+ handleError();
+ return;
+ }
+ } else {
+ error = QObject::tr("Missing sourcepath in definition of CustomMaterial");
+ handleError();
+ return;
+ }
+ } else {
+ matType = strTable.RegisterStr("Material");
+ }
+
+ auto createElementPropsFromDefProps = [&](const QMap<QString, QString> &defProps,
+ TPropertyDescAndValueList &elementProps,
+ const CRegisteredString &elementType) {
+ QMapIterator<QString, QString> propIter(defProps);
+ while (propIter.hasNext()) {
+ propIter.next();
+ if (dynPropDefs.contains(propIter.key()))
+ continue; // Dynamic properties are added directly to graph objects later
+
+ auto propName = strTable.RegisterStr(propIter.key());
+ ERuntimeDataModelDataType dataType;
+ ERuntimeAdditionalMetaDataType additionalType;
+ dataType = metaData.GetPropertyType(elementType, propName);
+ additionalType = metaData.GetAdditionalType(elementType, propName);
+
+ switch (dataType) {
+ case ERuntimeDataModelDataTypeLong: {
+ addIntAttribute(strTable, elementProps, propIter.key(),
+ propIter.value().toInt());
+ break;
+ }
+ case ERuntimeDataModelDataTypeFloat: {
+ addFloatAttribute(strTable, elementProps, propIter.key(),
+ propIter.value().toFloat());
+ break;
+ }
+ case ERuntimeDataModelDataTypeFloat2: {
+ QVector2D vec = parseFloat2Property(propIter.value());
+ QStringList atts;
+ atts << (propIter.key() + QLatin1String(".x"))
+ << (propIter.key() + QLatin1String(".y"));
+ addFloat2Attribute(strTable, elementProps, atts, vec);
+ break;
+ }
+ case ERuntimeDataModelDataTypeFloat3: {
+ QVector3D vec = parseFloat3Property(propIter.value());
+ if (additionalType == ERuntimeAdditionalMetaDataTypeRotation) {
+ vec.setX(qDegreesToRadians(vec.x()));
+ vec.setY(qDegreesToRadians(vec.y()));
+ vec.setZ(qDegreesToRadians(vec.z()));
+ }
+ QStringList atts;
+ atts << (propIter.key() + QLatin1String(".x"))
+ << (propIter.key() + QLatin1String(".y"))
+ << (propIter.key() + QLatin1String(".z"));
+ addFloat3Attribute(strTable, elementProps, atts, vec);
+ break;
+ }
+ case ERuntimeDataModelDataTypeFloat4: {
+ QVector4D vec = parseFloat4Property(propIter.value());
+ QStringList atts;
+ if (additionalType == ERuntimeAdditionalMetaDataTypeColor) {
+ atts << (propIter.key() + QLatin1String(".r"))
+ << (propIter.key() + QLatin1String(".g"))
+ << (propIter.key() + QLatin1String(".b"))
+ << (propIter.key() + QLatin1String(".a"));
+ } else {
+ atts << (propIter.key() + QLatin1String(".x"))
+ << (propIter.key() + QLatin1String(".y"))
+ << (propIter.key() + QLatin1String(".z"))
+ << (propIter.key() + QLatin1String(".w"));
+ }
+ addFloat4Attribute(strTable, elementProps, atts, vec);
+ break;
+ }
+ case ERuntimeDataModelDataTypeBool: {
+ bool boolValue = propIter.value().compare(QLatin1String("true"),
+ Qt::CaseInsensitive) == 0;
+ addBoolAttribute(strTable, elementProps, propIter.key(), boolValue);
+ break;
+ }
+ case ERuntimeDataModelDataTypeStringRef:
+ case ERuntimeDataModelDataTypeString: {
+ addStringAttribute(strTable, elementProps, propIter.key(), propIter.value());
+ break;
+ }
+ case ERuntimeDataModelDataTypeLong4: {
+ if (additionalType == ERuntimeAdditionalMetaDataTypeImage) {
+ // Insert placeholder for now, will be patched later
+ addElementRefAttribute(strTable, elementProps, propIter.key(), nullptr);
+ }
+ break;
+ }
+ default:
+ error = QObject::tr("Unsupported material property type for property: '%1'")
+ .arg(propIter.key());
+ return;
+ }
+ }
+ };
+
+ TPropertyDescAndValueList elementProps;
+ createElementPropsFromDefProps(materialInfo->materialProps, elementProps, matType);
+ if (!error.isEmpty()) {
+ handleError();
+ return;
+ }
+
+ TElement &newMatElem = m_Application->GetElementAllocator().CreateElement(
+ matName, matType, matClass,
+ toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())),
+ presentation, container, false);
+ newMatElem.SetActive(true);
+
+ // Create image elements
+ CRegisteredString imageType = strTable.RegisterStr("Image");
+ QMapIterator<QString, QMap<QString, QString>> texIter(materialInfo->textureProps);
+ QHash<QString, TElement *> imageElementMap;
+ while (texIter.hasNext()) {
+ texIter.next();
+ elementProps.clear();
+ createElementPropsFromDefProps(texIter.value(), elementProps, imageType);
+ if (!error.isEmpty()) {
+ m_Application->GetElementAllocator().ReleaseElement(newMatElem, true);
+ handleError();
+ return;
+ }
+ CRegisteredString imageName = strTable.RegisterStr(texIter.key());
+
+ TElement &newImageElem = m_Application->GetElementAllocator().CreateElement(
+ imageName, imageType, CRegisteredString(),
+ toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())),
+ presentation, &newMatElem, false);
+ imageElementMap.insert(texIter.key(), &newImageElem);
+ newImageElem.SetActive(true);
+ }
+
+ // Create render object for the material
+ qt3ds::render::SGraphObject *newMaterial = nullptr;
+ CRegisteredString newMatId = strTable.RegisterStr(
+ (QByteArrayLiteral("__newMat_") + QByteArray::number(++_idCounter))
+ .constData());
+ if (isCustomMaterial) {
+ newMaterial = customMaterialSystem->CreateCustomMaterial(matClass, allocator);
+ newMaterial->m_Id = newMatId;
+ auto dynObj = static_cast<qt3ds::render::SDynamicObject *>(newMaterial);
+
+ QHashIterator<QString, qt3ds::render::dynamic::SPropertyDefinition>
+ dynPropIter(dynPropDefs);
+ while (dynPropIter.hasNext()) {
+ dynPropIter.next();
+ QByteArray propValStr = materialInfo->materialProps.value(
+ dynPropIter.key()).toUtf8();
+ const auto propDesc = dynPropIter.value();
+ switch (propDesc.m_DataType) {
+ case qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool: {
+ bool boolValue = propValStr.compare(QByteArrayLiteral("true"),
+ Qt::CaseInsensitive) == 0;
+ setDynamicObjectProperty(*dynObj, propDesc, boolValue);
+ break;
+ }
+ case qt3ds::render::NVRenderShaderDataTypes::QT3DSF32:
+ setDynamicObjectProperty(*dynObj, propDesc, propValStr.toFloat());
+ break;
+ case qt3ds::render::NVRenderShaderDataTypes::QT3DSI32:
+ if (!propDesc.m_IsEnumProperty) {
+ setDynamicObjectProperty(*dynObj, propDesc, propValStr.toInt());
+ } else {
+ const NVConstDataRef<CRegisteredString> &enumNames
+ = propDesc.m_EnumValueNames;
+ for (QT3DSU32 i = 0, end = enumNames.size(); i < end; ++i) {
+ if (propValStr.compare(enumNames[i].c_str()) == 0) {
+ setDynamicObjectProperty(*dynObj, propDesc, i);
+ break;
+ }
+ }
+ }
+ break;
+ case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2:
+ setDynamicObjectProperty(*dynObj, propDesc, parseFloat2Property(propValStr));
+ break;
+ case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3:
+ setDynamicObjectProperty(*dynObj, propDesc, parseFloat3Property(propValStr));
+ break;
+ case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4:
+ setDynamicObjectProperty(*dynObj, propDesc, parseFloat4Property(propValStr));
+ break;
+ case qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr:
+ case qt3ds::render::NVRenderShaderDataTypes::NVRenderImage2DPtr: {
+ CRegisteredString regStr;
+ regStr = strTable.RegisterStr(propValStr);
+ setDynamicObjectProperty(*dynObj, propDesc, regStr);
+ break;
+ }
+ default:
+ error = QObject::tr("Unsupported custom material property type for '%1'")
+ .arg(dynPropIter.key());
+ m_Application->GetElementAllocator().ReleaseElement(newMatElem, true);
+ handleError();
+ return;
+ }
+ }
+ } else {
+ newMaterial = QT3DS_NEW(allocator, qt3ds::render::SDefaultMaterial)();
+ newMaterial->m_Id = newMatId;
+
+ // Update element refs for image elements and create graph objects & translators
+ QHashIterator<QString, TElement *> imageIter(imageElementMap);
+ while (imageIter.hasNext()) {
+ imageIter.next();
+ TElement *imageElem = imageIter.value();
+ UVariant *propValue = newMatElem.FindPropertyValue(imageElem->m_Name);
+ if (propValue) {
+ propValue->m_ElementHandle = imageIter.value()->GetHandle();
+
+ qt3ds::render::SImage *newImageObj
+ = QT3DS_NEW(allocator, qt3ds::render::SImage)();
+ newImageObj->m_Id = strTable.RegisterStr(
+ (QByteArrayLiteral("__newImage_")
+ + QByteArray::number(++_idCounter)).constData());
+ qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(
+ *imageElem, *newImageObj, allocator);
+ }
+ }
+ createdElements << &newMatElem;
+ }
+
+ qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newMatElem, *newMaterial,
+ allocator);
+ }
+
+ handleError();
+}
+
+void CQmlEngineImpl::deleteMaterials(const QStringList &materialNames, IQt3DSRenderer *renderer)
+{
+ // Material class (i.e. the shader) is not deleted as those can be shared between materials,
+ // so we just delete the material elements from the container and the related render objects
+
+ // Sort materials to presentations
+ QMultiHash<QString, QString> presMaterialMap;
+ QSet<QString> presIds;
+ for (const auto &matName : materialNames) {
+ QString presId;
+ QString localName = matName;
+ int index = matName.indexOf(QLatin1Char(':'));
+ if (index != -1) {
+ presId = matName.left(index);
+ localName = matName.mid(index + 1);
+ }
+ presMaterialMap.insert(presId, localName);
+ presIds.insert(presId);
+ }
+ for (const auto &presId : qAsConst(presIds)) {
+ QByteArray theId = presId.toUtf8();
+ CPresentation *presentation = nullptr;
+ if (presId.isEmpty())
+ presentation = m_Application->GetPrimaryPresentation();
+ else
+ presentation = m_Application->GetPresentationById(theId.constData());
+
+ if (presentation) {
+ // Find material container
+ auto &strTable = presentation->GetStringTable();
+ TElement *rootElement = presentation->GetRoot();
+ const auto containerName = strTable.RegisterStr("__Container");
+ TElement *container = rootElement->FindChild(CHash::HashString(containerName.c_str()));
+ Q_ASSERT_X(container, __FUNCTION__,
+ QStringLiteral("No material container found for presentation: '%1'")
+ .arg(presId).toUtf8());
+
+ QVector<TElement *> elementsToDelete;
+ const QList<QString> matNames = presMaterialMap.values(presId);
+ for (const auto &materialName : matNames) {
+ TElement *firstChild = nullptr;
+ TElement *nextChild = container->GetChild();
+ firstChild = nextChild;
+ bool added = false;
+ while (nextChild) {
+ QString childName = QString::fromUtf8(nextChild->m_Name);
+ if (childName == materialName) {
+ elementsToDelete << nextChild;
+ added = true;
+ break;
+ }
+ nextChild = nextChild->GetSibling();
+ }
+ if (!added) {
+ if (presId.isEmpty()) {
+ qWarning() << __FUNCTION__
+ << QStringLiteral("Could not find material '%1'")
+ .arg(materialName);
+ } else {
+ qWarning() << __FUNCTION__
+ << QStringLiteral("Could not find material '%1' in '%2'")
+ .arg(materialName).arg(presId);
+ }
+ }
+ }
+ deleteElements(elementsToDelete, renderer);
+ } else {
+ qWarning() << __FUNCTION__ << "Warning: Presentation ID could not be resolved:"
+ << presId;
+ }
+ }
+}
+
+void CQmlEngineImpl::createMesh(const QString &name, qt3dsimp::Mesh *mesh,
+ qt3ds::render::IBufferManager *bufferManager)
+{
+ // Add the custom meshes to buffer manager.
+ bufferManager->loadCustomMesh(name, mesh);
+}
+
+void CQmlEngineImpl::deleteMeshes(const QStringList &meshNames,
+ qt3ds::render::IBufferManager *bufferManager)
+{
+ for (const auto &meshName : meshNames) {
+ if (!meshName.isEmpty()) {
+ CRegisteredString regName = bufferManager->GetStringTable().RegisterStr(meshName);
+ bufferManager->InvalidateBuffer(regName);
+ }
+ }
+}
+
+void CQmlEngineImpl::GotoSlide(const char *component, const char *slideName,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ TElement *theTarget = getTarget(component);
+ if (theTarget) {
+ CQmlCommandHelper::SetupGotoSlideCommand(*theTarget, slideName, inArgs);
+ } else {
+ qCWarning(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::GotoSlide: Unable to find component " << component;
+ }
+}
+
+void CQmlEngineImpl::GotoSlideRelative(const char *component, bool inNextSlide, bool inWrap,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ TElement *theTarget = getTarget(component);
+ if (theTarget) {
+ theTarget = &theTarget->GetComponentParent();
+ if (theTarget && theTarget->GetActive()) {
+ TComponent *theComponent = static_cast<TComponent *>(theTarget);
+ Q3DStudio::INT32 theSlide = theComponent->GetCurrentSlide();
+ Q3DStudio::INT32 theSlideCount = theComponent->GetSlideCount();
+ theSlide = inNextSlide ? theSlide + 1 : theSlide - 1;
+ if (theSlide < 1) {
+ if (inWrap)
+ theSlide = theSlideCount - 1;
+ else
+ theSlide = 1;
+ } else if (theSlide == theSlideCount) {
+ if (inWrap)
+ theSlide = 1;
+ else
+ theSlide = theSlideCount - 1;
+ }
+ if (theSlide != theComponent->GetCurrentSlide()) {
+ CQmlCommandHelper::SetupGotoSlideCommand(*theTarget, theSlide, inArgs);
+ }
+ } else {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "QmlEngine::GotoSlideRelative: Component is not active: " << component;
+ }
+ } else {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "QmlEngine::GotoSlideRelative: failed to find component: " << component;
+ }
+}
+
+void CQmlEngineImpl::SetPresentationAttribute(const char *presId, const char *, const char *value)
+{
+ if (isTrivial(presId))
+ return;
+ if (presId[0] == '#')
+ ++presId;
+ CPresentation *thePresentation = m_Application->GetPresentationById(presId);
+ if (thePresentation) {
+ bool active = AreEqualCaseless(nonNull(value), "True");
+ thePresentation->SetActive(active);
+ }
+}
+
+void CQmlEngineImpl::GotoSlideIndex(const char *component, const Q3DStudio::INT32 slideIndex,
+ const SScriptEngineGotoSlideArgs &inArgs)
+{
+ TElement *theTarget = getTarget(component);
+ if (theTarget) {
+ CQmlCommandHelper::SetupGotoSlideCommand(*theTarget, slideIndex, inArgs);
+ } else {
+ qCWarning(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::GotoSlide: Unable to find component " << component;
+ }
+}
+
+bool CQmlEngineImpl::GetSlideInfo(const char *element, int &currentIndex, int &previousIndex,
+ QString &currentName, QString &previousName)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+
+ TElement *theTarget = getTarget(element);
+ if (theTarget && theTarget->IsComponent()) {
+ IPresentation *thePresentation = theTarget->GetBelongedPresentation();
+ TComponent *theComponent = thePresentation->GetComponentManager().GetComponent(theTarget);
+ currentIndex = int(theComponent->GetCurrentSlide());
+ previousIndex = int(theComponent->GetPreviousSlide());
+ if (currentIndex > 0) {
+ currentName = QString::fromLocal8Bit(
+ thePresentation->GetSlideSystem().GetSlideName(
+ qt3ds::runtime::SSlideKey(*theComponent, QT3DSU32(currentIndex))));
+ } else {
+ currentName.clear();
+ }
+ if (previousIndex > 0) {
+ previousName = QString::fromLocal8Bit(
+ thePresentation->GetSlideSystem().GetSlideName(
+ qt3ds::runtime::SSlideKey(*theComponent, QT3DSU32(previousIndex))));
+ } else {
+ previousName.clear();
+ }
+ return true;
+ } else {
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::GetSlideInfo: Supplied element is not a component " << element;
+ }
+ return false;
+}
+
+void CQmlEngineImpl::GotoTime(const char *component, const Q3DStudio::FLOAT time)
+{
+ TElement *theTarget = getTarget(component);
+ if (theTarget && theTarget->GetActive()) {
+ UVariant theArg1;
+ UVariant theArg2;
+
+ IPresentation *thePresentation = theTarget->GetBelongedPresentation();
+
+ theArg1.m_INT32 = static_cast<INT32>(time * 1000);
+
+ TElement *theParentTarget = &theTarget->GetComponentParent();
+
+ thePresentation->FireCommand(COMMAND_GOTOTIME, theParentTarget, &theArg1, &theArg2);
+ }
+}
+
+bool CQmlEngineImpl::RegisterCallback(Q3DStudio::UINT32 callbackType,
+ const TScriptCallback inCallback, void *inUserData)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ return m_ScriptCallbacks.RegisterCallback(callbackType, inCallback, inUserData);
+}
+
+void CQmlEngineImpl::ProcessFrameCallbacks(IPresentation *)
+{
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ m_ScriptCallbacks.ProcessCallbacks();
+
+ for (Q3DSQmlScript *script : m_scripts)
+ script->update();
+}
+
+void CQmlEngineImpl::ProcessSignal(IPresentation *inPresentation,
+ const SEventCommand &inCommand)
+{
+ using qt3ds::runtime::TIdValuePair;
+ QML_ENGINE_MULTITHREAD_PROTECT_METHOD;
+ TElement *theElement = inCommand.m_Target; // the element that is a behavior
+
+ CPresentation *thePresentation = (CPresentation *)inPresentation;
+ // IParametersSystem& theParametersManager = thePresentation->GetParametersSystem();
+ qt3ds::foundation::IStringTable &theStrTable(thePresentation->GetStringTable());
+
+ CRegisteredString theSignal = theStrTable.HandleToStr(inCommand.m_Arg1.m_INT32);
+
+ if (!theSignal.IsValid() || m_EmitSignalDataList.size() > CQmlEngineImpl::MAX_ACTION_QUEUE_SIZE)
+ return;
+
+ TEmitSignalPtr theTemp = QT3DS_NEW(m_Foundation.getAllocator(), SEmitSignalData)(
+ m_Foundation.getAllocator(), inCommand.m_Arg1.m_INT32, theElement);
+ m_EmitSignalDataList.push_back(theTemp);
+}
+
+
+//==============================================================================
+/**
+* Handle custom actions
+* @param inCommand carrier of command and parameter information
+*/
+void CQmlEngineImpl::ProcessCustomActions(IPresentation *presentation,
+ const SEventCommand &command)
+{
+ TElement *element = command.m_Target;
+ for (Q3DSQmlScript *script : m_scripts) {
+ if (script->hasBehavior(element)) {
+ IParametersSystem &parametersManager
+ = static_cast<CPresentation *>(presentation)->GetParametersSystem();
+ INT32 groupId = command.m_Arg1.m_INT32;
+ UINT32 numParams = parametersManager.GetNumParameters(groupId);
+ if (numParams == 0) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ qt3ds::runtime::TIdValuePair tempData = parametersManager.GetParameter(groupId, 0);
+ if (tempData.first != CHash::HashString("string")) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ CRegisteredString functionName = presentation->GetStringTable().HandleToStr(
+ tempData.second.m_StringHandle);
+ script->call(functionName.c_str());
+ return;
+ }
+ }
+}
+
+bool CQmlEngineImpl::PeekSignal(TElement *&outElement, char *&outName)
+{
+ if (m_EmitSignalDataList.empty())
+ return false;
+
+ NVScopedRefCounted<SEmitSignalData> theAction = m_EmitSignalDataList.front();
+ m_EmitSignalDataList.pop_front();
+
+ outElement = theAction->m_TargetElement;
+ if (outElement) {
+ IPresentation *thePresentation = outElement->GetBelongedPresentation();
+ qt3ds::foundation::IStringTable &theStrTable(thePresentation->GetStringTable());
+ CRegisteredString theSignal = theStrTable.HandleToStr(theAction->m_SignalNameHandler);
+ outName = (char *)theSignal.c_str();
+ } else {
+ qCWarning(qt3ds::INVALID_OPERATION)
+ << "CQmlEngineImpl::PeekCustomAction: Unable to find element: EmitSignal queue error";
+ outName = NULL;
+ }
+
+ return true;
+}
+
+void CQmlEngineImpl::createComponent(QQmlComponent *component, TElement *element)
+{
+ TElement *parent = element->GetParent();
+ if (!parent)
+ return;
+
+ QQmlContext *context = new QQmlContext(&m_engine, &m_engine);
+ QObject *obj = component->beginCreate(context);
+ if (!obj) {
+ context->deleteLater();
+ return;
+ }
+ Q3DSQmlBehavior *behaviorObject = qobject_cast<Q3DSQmlBehavior *>(obj);
+ if (behaviorObject == nullptr) {
+ obj->deleteLater();
+ context->deleteLater();
+ return;
+ }
+
+ auto script = new Q3DSQmlScript(*this, *behaviorObject, *element, *parent);
+ component->completeCreate();
+ behaviorObject->setScript(script);
+
+ script->setParent(component);
+ obj->setParent(script);
+ m_scripts.push_back(script);
+}
+
+TElement *CQmlEngineImpl::getTarget(const char *component) {
+ TElement *target = NULL;
+ QStringList split = QString(component).split(":");
+ if (split.size() > 1) {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPresentationById(split.at(0).toStdString().c_str()),
+ split.at(1).toStdString().c_str(), NULL);
+ } else {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPrimaryPresentation(),
+ split.at(0).toStdString().c_str(), NULL);
+ }
+ return target;
+}
+
+void CQmlEngineImpl::listAllElements(TElement *root, QList<TElement *> &elements)
+{
+ elements.append(root);
+ TElement *nextChild = root->GetChild();
+ while (nextChild) {
+ listAllElements(nextChild, elements);
+ nextChild = nextChild->GetSibling();
+ }
+}
+
+// Initializes datainput bindings in the presentation starting by default from the root element.
+// If inElements is specified, only parses the specified elements.
+void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentation,
+ bool isPrimary,
+ QList<TElement *> inElements)
+{
+ QList<TElement *> elements;
+ if (!inElements.empty()) {
+ elements = inElements;
+ } else {
+ TElement *parent = presentation.GetRoot();
+ listAllElements(parent, elements);
+ }
+ qt3ds::runtime::DataInputMap &diMap = m_Application->dataInputMap();
+
+// #TODO: Remove below once QT3DS-3510 has been implemented in the editor
+ qt3ds::runtime::DataOutputMap &doMap = m_Application->dataOutputMap();
+// #TODO: Remove above once QT3DS-3510 has been implemented in the editor
+
+ qt3ds::foundation::IStringTable &strTable(presentation.GetStringTable());
+ QHash<CRegisteredString, qt3ds::runtime::DataOutputDef> elementPathToDataOutputDefMap;
+ for (TElement *element : qAsConst(elements)) {
+ Option<QT3DSU32> ctrlIndex = element->FindPropertyIndex(ATTRIBUTE_CONTROLLEDPROPERTY);
+ if (ctrlIndex.hasValue()) {
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> propertyInfo =
+ element->GetPropertyByIndex(*ctrlIndex);
+ UVariant *valuePtr = propertyInfo->second;
+ QString valueStr =
+ QString::fromUtf8(strTable.HandleToStr(valuePtr->m_StringHandle));
+ if (!valueStr.isEmpty()) {
+ QStringList splitValues = valueStr.split(QChar(' '));
+ for (int i = 1; i < splitValues.size(); i += 2) {
+ QString controllerName = splitValues[i - 1];
+ // remove datainput name prefix "$"
+ controllerName.remove(0, 1);
+ if (diMap.contains(controllerName)) {
+ qt3ds::runtime::DataInOutAttribute ctrlElem;
+ if (!isPrimary) {
+ // Prepend presentation id to element path
+ ctrlElem.elementPath = presentation.GetName();
+ ctrlElem.elementPath.append(QByteArrayLiteral(":"));
+ }
+ ctrlElem.attributeName.append(splitValues[i].toUtf8());
+ if (ctrlElem.attributeName.first() == QByteArrayLiteral("@timeline")) {
+ ctrlElem.propertyType = ATTRIBUTETYPE_DATAINPUT_TIMELINE;
+ TElement *component = &element->GetComponentParent();
+ ctrlElem.elementPath.append(component->m_Path);
+ } else if (ctrlElem.attributeName.first() == QByteArrayLiteral("@slide")) {
+ ctrlElem.propertyType = ATTRIBUTETYPE_DATAINPUT_SLIDE;
+ TElement *component = &element->GetComponentParent();
+ ctrlElem.elementPath.append(component->m_Path);
+ } else if (diMap[controllerName].type
+ == qt3ds::runtime::DataInOutTypeVector3) {
+ // special handling for vector datatype to handle
+ // expansion from <propertyname> to <propertyname>.x .y .z
+ QVector<QByteArray> attVec;
+ bool success = getAttributeVector3(
+ attVec, ctrlElem.attributeName.first().constData(),
+ element);
+ if (!attVec.empty() && success) {
+ ctrlElem.attributeName = attVec;
+ ctrlElem.elementPath.append(element->m_Path);
+ ctrlElem.propertyType = ATTRIBUTETYPE_FLOAT3;
+ } else {
+ qWarning() << __FUNCTION__ << "Property "
+ << ctrlElem.attributeName.first()
+ << " was not expanded to vector";
+ ctrlElem.propertyType = ATTRIBUTETYPE_NONE;
+ }
+ } else if (diMap[controllerName].type
+ == qt3ds::runtime::DataInOutTypeVector2) {
+ // special handling for vector datatype to handle
+ // expansion from <propertyname> to <propertyname>.x .y
+ QVector<QByteArray> attVec;
+ bool success = getAttributeVector2(
+ attVec, ctrlElem.attributeName.first().constData(),
+ element);
+ if (!attVec.empty() && success) {
+ ctrlElem.attributeName = attVec;
+ ctrlElem.elementPath.append(element->m_Path);
+ ctrlElem.propertyType = ATTRIBUTETYPE_FLOAT2;
+ } else {
+ qWarning() << __FUNCTION__ << "Property "
+ << ctrlElem.attributeName.first()
+ << " was not expanded to vector";
+ ctrlElem.propertyType = ATTRIBUTETYPE_NONE;
+ }
+ } else if (diMap[controllerName].type
+ == qt3ds::runtime::DataInOutTypeEvaluator) {
+ diMap[controllerName].evalFunc
+ = buildJSFunc(diMap[controllerName].evaluator);
+ auto referencedDIs = resolveDependentDatainputs(
+ diMap[controllerName].evaluator, controllerName);
+ // add this evaluator datainput to the dependent list
+ // for those datainputs that are used in the expression
+ // for this evaluator
+ for (auto ref : referencedDIs)
+ diMap[ref].dependents.append(controllerName);
+
+ ctrlElem.elementPath.append(element->m_Path);
+ TStringHash attHash = CHash::HashAttribute(
+ ctrlElem.attributeName.first().constData());
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> attInfo
+ = element->FindProperty(attHash);
+ if (attInfo.hasValue())
+ ctrlElem.propertyType = attInfo->first.m_Type;
+ } else {
+ // all other scalar datatypes
+ ctrlElem.elementPath.append(element->m_Path);
+ TStringHash attHash = CHash::HashAttribute(
+ ctrlElem.attributeName.first().constData());
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> attInfo
+ = element->FindProperty(attHash);
+ if (attInfo.hasValue()) {
+ ctrlElem.propertyType = attInfo->first.m_Type;
+ } else {
+ ctrlElem.propertyType = ATTRIBUTETYPE_NONE;
+ qWarning() << __FUNCTION__ << "Property "
+ << ctrlElem.attributeName.first() << " not existing!";
+ }
+ }
+ qt3ds::runtime::DataInputDef &diDef = diMap[controllerName];
+ diDef.controlledAttributes.append(ctrlElem);
+
+// #TODO: Remove below once QT3DS-3510 has been implemented in the editor
+ // Note, in this temp implementation only the LAST of multiple attributes
+ // will be notified from the object under the DataInput name..
+ qt3ds::runtime::DataInputDef inDef = diMap[controllerName];
+ qt3ds::runtime::DataOutputDef &doDef = doMap[controllerName];
+ doDef.observedAttribute = ctrlElem;
+ doDef.type = inDef.type;
+ doDef.min = inDef.min;
+ doDef.max = inDef.max;
+
+ if (ctrlElem.propertyType == ATTRIBUTETYPE_DATAINPUT_TIMELINE) {
+ // Find the TElement for the @timeline attrib
+ TElement *target = nullptr;
+ QStringList split
+ = QString(ctrlElem.elementPath).split(QLatin1Char(':'));
+ if (split.size() > 1) {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPresentationById(
+ split.at(0).toStdString().c_str()),
+ split.at(1).toStdString().c_str(), nullptr);
+ } else {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPrimaryPresentation(),
+ split.at(0).toStdString().c_str(), nullptr);
+ }
+
+ doDef.timelineComponent = static_cast<TComponent *>(target);
+ } else if (ctrlElem.propertyType == ATTRIBUTETYPE_DATAINPUT_SLIDE) {
+ // Slide notifications are already done with separate signal
+ // No need to process
+ } else {
+ // Other than slide or timeline attributes are handled by CPresentation
+ CRegisteredString rString = strTable.RegisterStr(ctrlElem.elementPath);
+ elementPathToDataOutputDefMap.insertMulti(rString, doDef);
+ }
+// #TODO: Remove above once QT3DS-3510 has been implemented in the editor
+ }
+ }
+ }
+ }
+ }
+
+// #TODO: Remove below once QT3DS-3510 has been implemented in the editor
+ presentation.AddToDataOutputMap(elementPathToDataOutputDefMap);
+// #TODO: Remove above once QT3DS-3510 has been implemented in the editor
+}
+
+void CQmlEngineImpl::initializeDataOutputsInPresentation(CPresentation &presentation,
+ bool isPrimary)
+{
+ TElement *parent = presentation.GetRoot();
+ QList<TElement *> elements;
+ listAllElements(parent, elements);
+ qt3ds::runtime::DataOutputMap &doMap = m_Application->dataOutputMap();
+
+ qt3ds::foundation::IStringTable &strTable(presentation.GetStringTable());
+ QHash<CRegisteredString, qt3ds::runtime::DataOutputDef> elementPathToDataOutputDefMap;
+ for (TElement *element : qAsConst(elements)) {
+ Option<QT3DSU32> ctrlIndex = element->FindPropertyIndex(ATTRIBUTE_OBSERVEDPROPERTY);
+ if (ctrlIndex.hasValue()) {
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> propertyInfo =
+ element->GetPropertyByIndex(*ctrlIndex);
+ UVariant *valuePtr = propertyInfo->second;
+ QString valueStr =
+ QString::fromUtf8(strTable.HandleToStr(valuePtr->m_StringHandle));
+
+ if (!valueStr.isEmpty()) {
+ QStringList splitValues = valueStr.split(QLatin1Char(' '));
+
+ // Single value pair expected for DataOutputs
+ QString observerName = splitValues[0];
+ QString observedAttribute = splitValues[1];
+ // Remove DataOutput name prefix "$"
+ observerName.remove(0, 1);
+ qt3ds::runtime::DataOutputDef &doDef = doMap[observerName];
+
+ if (doMap.contains(observerName)) {
+ qt3ds::runtime::DataInOutAttribute obsElem;
+ if (!isPrimary) {
+ // Prepend presentation id to element path
+ obsElem.elementPath = presentation.GetName();
+ obsElem.elementPath.append(QByteArrayLiteral(":"));
+ }
+
+ obsElem.attributeName.append(observedAttribute.toUtf8());
+
+ if (obsElem.attributeName.first() == QByteArrayLiteral("@timeline")) {
+ // Timeline requires special additional handling
+ obsElem.propertyType = ATTRIBUTETYPE_DATAINPUT_TIMELINE;
+ TElement *component = &element->GetComponentParent();
+ obsElem.elementPath.append(component->m_Path);
+
+ // Find the TElement for the @timeline attrib
+ TElement *target = nullptr;
+ QStringList split = QString(obsElem.elementPath).split(QLatin1Char(':'));
+ if (split.size() > 1) {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPresentationById(
+ split.at(0).toStdString().c_str()),
+ split.at(1).toStdString().c_str(), nullptr);
+ } else {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPrimaryPresentation(),
+ split.at(0).toStdString().c_str(), nullptr);
+ }
+ doDef.timelineComponent = static_cast<TComponent *>(target);
+ } else if (obsElem.attributeName.first() == QByteArrayLiteral("@slide")) {
+ // Slides are ignored if set as we have separate signal in the API for
+ // slide transitions
+ } else {
+ // Every other type is handled by CPresentation
+ obsElem.elementPath.append(element->m_Path);
+ TStringHash attHash = CHash::HashAttribute(
+ obsElem.attributeName.first().constData());
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> attInfo
+ = element->FindProperty(attHash);
+ if (attInfo.hasValue()) {
+ obsElem.propertyType = attInfo->first.m_Type;
+ } else {
+ obsElem.propertyType = ATTRIBUTETYPE_NONE;
+ qWarning() << __FUNCTION__ << "Property"
+ << obsElem.attributeName.first() << "not existing!";
+ }
+
+ doDef.observedAttribute = obsElem;
+ CRegisteredString rString = strTable.RegisterStr(obsElem.elementPath);
+ elementPathToDataOutputDefMap.insertMulti(rString, doDef);
+ }
+ }
+ }
+ }
+ }
+
+ // Inform the presentation of the ready data output defs
+ presentation.AddToDataOutputMap(elementPathToDataOutputDefMap);
+}
+
+// Bit clumsy way of getting from "position" to "position .x .y .z" and enabling datainput
+// support for vectorized types. UIP parser has already thrown away all vector
+// type attributes and at this point we are operating with scalar components only.
+// We check if this element has a property attName.x or attName.r to find out it
+// we should expand property attName to XYZ or RGB vector
+bool CQmlEngineImpl::getAttributeVector3(QVector<QByteArray> &outAttVec,
+ const QByteArray &attName,
+ TElement *elem)
+{
+ auto hashName = Q3DStudio::CHash::HashAttribute(attName + ".x");
+
+ if (!elem->FindProperty(hashName).isEmpty()) {
+ outAttVec.append(attName + ".x");
+ outAttVec.append(attName + ".y");
+ outAttVec.append(attName + ".z");
+ return true;
+ }
+ hashName = Q3DStudio::CHash::HashAttribute(attName + ".r");
+ if (!elem->FindProperty(hashName).isEmpty()) {
+ outAttVec.append(attName + ".r");
+ outAttVec.append(attName + ".g");
+ outAttVec.append(attName + ".b");
+ return true;
+ }
+ return false;
+}
+
+bool CQmlEngineImpl::getAttributeVector2(QVector<QByteArray> &outAttVec,
+ const QByteArray &attName,
+ TElement *elem)
+{
+ auto hashName = Q3DStudio::CHash::HashAttribute(attName + ".x");
+
+ if (!elem->FindProperty(hashName).isEmpty()) {
+ outAttVec.append(attName + ".x");
+ outAttVec.append(attName + ".y");
+ return true;
+ }
+
+ hashName = Q3DStudio::CHash::HashAttribute(attName + ".u");
+ if (!elem->FindProperty(hashName).isEmpty()) {
+ outAttVec.append(attName + ".u");
+ outAttVec.append(attName + ".v");
+ return true;
+ }
+ return false;
+}
+
+QJSValue CQmlEngineImpl::buildJSFunc(const QString &userFunc)
+{
+ auto res = this->m_engine.evaluate(userFunc);
+ if (res.isError()) {
+ qWarning() << __FUNCTION__
+ << "Uncaught exception during datainput evaluation. Evaluator function" << userFunc;
+ }
+ return res;
+}
+
+QVariant CQmlEngineImpl::callJSFunc(const QString &controllerName,
+ qt3ds::runtime::DataInputDef &diDef,
+ const QVariant::Type type)
+{
+ qt3ds::runtime::DataInputMap &diMap = m_Application->dataInputMap();
+ QJSValueList args;
+ QVector<QString> sourceDIs = resolveDependentDatainputs(diDef.evaluator, controllerName);
+
+ // get the most recent set values for datainput sources (arguments) in the expression
+ for (auto diVal : sourceDIs)
+ args << this->m_engine.toScriptValue(diMap[diVal].value);
+
+ if (diDef.evalFunc.isCallable()) {
+ QJSValue res = diDef.evalFunc.call(args);
+ if (res.isError()) {
+ qWarning() << __FUNCTION__ << "Error during datainput" << controllerName
+ << "evaluator call:" << res.toString() << "\nEvaluator function"
+ << diDef.evaluator;
+ return QVariant::Invalid;
+ }
+
+ QVariant ret = res.toVariant();
+ if (ret.isValid() && isMatchingDatatype(ret.type(), type)) {
+ // further check if the result is valid number
+ if (ret.type() == QVariant::Double && qIsNaN(res.toNumber())) {
+ qWarning() << __FUNCTION__ << "Datainput" << controllerName << "evaluator"
+ << "result not a number (NaN)."
+ << "\nEvaluator function" << diDef.evaluator;
+ return QVariant::Invalid;
+ } else {
+ return ret;
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Datainput" << controllerName << "evaluator"
+ << "result not valid or matching with target attribute type. Result type"
+ << QVariant::typeToName(ret.type()) << " target attribute type "
+ << QVariant::typeToName(type) << "\nEvaluator function" << diDef.evaluator;
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Datainput" << controllerName << "evaluator"
+ << diDef.evaluator << " not valid callable";
+ }
+ return QVariant::Invalid;
+}
+
+bool CQmlEngineImpl::isMatchingDatatype(QVariant::Type resultType, QVariant::Type propertyType)
+{
+ if (resultType == propertyType)
+ return true;
+ // Allow binding from numeric datainput to string target
+ if ((resultType == QVariant::Double || resultType == QVariant::Int
+ || resultType == QVariant::LongLong)
+ && (propertyType == QVariant::Double || propertyType == QVariant::Int
+ || propertyType == QVariant::LongLong || propertyType == QVariant::String)) {
+ return true;
+ }
+ return false;
+}
+
+QVector<QString> CQmlEngineImpl::resolveDependentDatainputs(const QString &expression,
+ const QString &controllerName)
+{
+ QVector<QString> ret;
+ qt3ds::runtime::DataInputMap &diMap = m_Application->dataInputMap();
+ if (!expression.contains("function", Qt::CaseInsensitive)) {
+ qWarning() << __FUNCTION__ << "Function keyword not found in datainput"
+ << controllerName << "evaluator";
+ return QVector<QString>();
+ }
+
+ int argListStart = expression.indexOf("function(") + 9;
+ int argListStop = expression.indexOf(')', argListStart);
+ QString argstr = expression.mid(argListStart , argListStop - argListStart);
+ QStringList args = argstr.split(',');
+
+ for (auto di : args) {
+ auto diTrim = di.trimmed();
+ if (diMap.contains(diTrim)) {
+ if (diMap[diTrim].type == qt3ds::runtime::DataInOutTypeEvaluator
+ && diTrim != controllerName) {
+ qWarning() << __FUNCTION__ << "Invalid evaluator function in" << controllerName
+ << ". Another evaluator is used as source data.";
+ } else {
+ ret.append(diTrim);
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Evaluator in" << controllerName << "evaluator"
+ << "is using unknown datainput" << diTrim << " as input argument name";
+ }
+ }
+ return ret;
+}
+
+void CQmlEngineImpl::addStringAttribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QString &inAttName, const QString &inValue)
+{
+ QByteArray valueBa = inValue.toUtf8();
+ qt3ds::foundation::CStringHandle strHandle = strTable.GetHandle(valueBa.constData());
+ UVariant theValue;
+ theValue.m_StringHandle = strHandle.handle();
+ const CRegisteredString attStr = strTable.RegisterStr(inAttName);
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_STRING), theValue));
+}
+
+void CQmlEngineImpl::addIntAttribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QString &inAttName, int inValue)
+{
+ UVariant theValue;
+ theValue.m_INT32 = inValue;
+ const CRegisteredString attStr = strTable.RegisterStr(inAttName);
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_INT32), theValue));
+}
+
+void CQmlEngineImpl::addBoolAttribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QString &inAttName, bool inValue)
+{
+ UVariant theValue;
+ theValue.m_INT32 = int(inValue);
+ const CRegisteredString attStr = strTable.RegisterStr(inAttName);
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_BOOL), theValue));
+}
+
+void CQmlEngineImpl::addFloatAttribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QString &inAttName, float inValue)
+{
+ UVariant theValue;
+ theValue.m_FLOAT = inValue;
+ const CRegisteredString attStr = strTable.RegisterStr(inAttName);
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue));
+}
+
+void CQmlEngineImpl::addFloat2Attribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QStringList &inAttNames, const QVector2D &inValue)
+{
+ for (int i = 0; i < 2; ++i) {
+ UVariant theValue;
+ theValue.m_FLOAT = inValue[i];
+ const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i));
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue));
+ }
+}
+
+void CQmlEngineImpl::addFloat3Attribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QStringList &inAttNames, const QVector3D &inValue)
+{
+ for (int i = 0; i < 3; ++i) {
+ UVariant theValue;
+ theValue.m_FLOAT = inValue[i];
+ const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i));
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue));
+ }
+}
+
+void CQmlEngineImpl::addFloat4Attribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QStringList &inAttNames, const QVector4D &inValue)
+{
+ for (int i = 0; i < 4; ++i) {
+ UVariant theValue;
+ theValue.m_FLOAT = inValue[i];
+ const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i));
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue));
+ }
+}
+
+void CQmlEngineImpl::addElementRefAttribute(IStringTable &strTable,
+ CQmlEngineImpl::TPropertyDescAndValueList &list,
+ const QString &inAttName, TElement *element)
+{
+ UVariant theValue;
+ if (element) {
+ theValue.m_ElementHandle = element->GetHandle();
+ QT3DS_ASSERT(theValue.m_ElementHandle);
+ } else {
+ theValue.m_ElementHandle = 0;
+ }
+ const CRegisteredString attStr = strTable.RegisterStr(inAttName);
+ list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_ELEMENTREF), theValue));
+}
+
+QVector2D CQmlEngineImpl::parseFloat2Property(const QString &propValue)
+{
+ QVector<QStringRef> values = propValue.splitRef(QLatin1Char(' '));
+ QVector2D retVal;
+ for (int i = 0; i < values.size() && i < 2; ++i)
+ retVal[i] = values[i].toFloat();
+ return retVal;
+}
+
+QVector3D CQmlEngineImpl::parseFloat3Property(const QString &propValue)
+{
+ QVector<QStringRef> values = propValue.splitRef(QLatin1Char(' '));
+ QVector3D retVal;
+ for (int i = 0; i < values.size() && i < 3; ++i)
+ retVal[i] = values[i].toFloat();
+ return retVal;
+}
+
+QVector4D CQmlEngineImpl::parseFloat4Property(const QString &propValue)
+{
+ QVector<QStringRef> values = propValue.splitRef(QLatin1Char(' '));
+ QVector4D retVal;
+ for (int i = 0; i < values.size() && i < 4; ++i)
+ retVal[i] = values[i].toFloat();
+ return retVal;
+}
+
+void CQmlEngineImpl::notifyElementCreation(const QStringList &elementNames, const QString &error)
+{
+ // Notify presentation asynchronously to give renderer time to initialize the elements properly
+ if (!error.isEmpty()) {
+ qWarning() << "Warning: Element creation failed:" << error;
+ QT3DS_ASSERT(false);
+ }
+ QTimer::singleShot(0, [this, elementNames, error]() {
+ m_Application->GetPrimaryPresentation()->signalProxy()
+ ->SigElementsCreated(elementNames, error);
+ });
+}
+
+void CQmlEngineImpl::notifyMaterialCreation(const QStringList &materialNames, const QString &error)
+{
+ // Notify presentation asynchronously to give renderer time to initialize the materials properly
+ if (!error.isEmpty()) {
+ qWarning() << "Warning: Material creation failed:" << materialNames << error;
+ QT3DS_ASSERT(false);
+ }
+ QTimer::singleShot(0, [this, materialNames, error]() {
+ m_Application->GetPrimaryPresentation()->signalProxy()
+ ->SigMaterialsCreated(materialNames, error);
+ });
+}
+
+void CQmlEngineImpl::deleteElements(const QVector<TElement *> &elements,
+ IQt3DSRenderer *renderer)
+{
+ TElement *lastParent = nullptr;
+ IPresentation *presentation = nullptr;
+ TElement *component = nullptr;
+ ISlideSystem *slideSystem = nullptr;
+ QSet<qt3ds::render::SNode *> parentNodes;
+ for (auto element : elements) {
+ TElement *parentElement = element->GetParent();
+ if (parentElement != lastParent) {
+ lastParent = parentElement;
+ presentation = element->GetBelongedPresentation();
+ component = &element->GetComponentParent();
+ slideSystem = &presentation->GetSlideSystem();
+ }
+ // Remove element recursively from slide system
+ slideSystem->removeElement(*component, *element);
+
+ Q_ASSERT(parentElement);
+
+ NVAllocatorCallback &allocator = presentation->GetScene()->allocator();
+
+ // Recursive deleter for translators and graph objects
+ std::function<void(TElement *)> deleteRenderObjects;
+ deleteRenderObjects = [&](TElement *elem) {
+ TElement *child = elem->m_Child;
+ while (child) {
+ TElement *sibling = child->m_Sibling;
+ deleteRenderObjects(child);
+ child = sibling;
+ }
+
+ auto translator = static_cast<qt3ds::render::Qt3DSTranslator *>(elem->GetAssociation());
+ if (translator) {
+ qt3ds::render::GraphObjectTypes::Enum type = translator->GetUIPType();
+ if (type == qt3ds::render::GraphObjectTypes::Model) {
+ auto model = static_cast<qt3ds::render::SModel *>(&translator->RenderObject());
+ // Delete material
+ if (model->m_FirstMaterial) {
+ auto material = static_cast<qt3ds::render::SReferencedMaterial *>(
+ model->m_FirstMaterial);
+ QT3DS_FREE(allocator, material);
+ }
+ }
+ QT3DS_FREE(allocator, &translator->RenderObject());
+ QT3DS_FREE(allocator, translator);
+ }
+ };
+
+ qt3ds::render::SNode *node = nullptr;
+ qt3ds::render::SNode *parentNode = nullptr;
+ auto translator = static_cast<qt3ds::render::Qt3DSTranslator *>(element->GetAssociation());
+
+ if (translator) {
+ if (qt3ds::render::GraphObjectTypes::IsNodeType(translator->GetUIPType())) {
+ node = &static_cast<qt3ds::render::SNode &>(translator->RenderObject());
+ auto parentTranslator = static_cast<qt3ds::render::Qt3DSTranslator *>(
+ parentElement->GetAssociation());
+ if (parentTranslator) {
+ parentNode = &static_cast<qt3ds::render::SNode &>(
+ parentTranslator->RenderObject());
+ parentNode->RemoveChild(*node);
+ parentNodes.insert(parentNode);
+ }
+ }
+ // Release child element graph objects/translators
+ deleteRenderObjects(element);
+ }
+
+ // Remove element recursively
+ m_Application->GetElementAllocator().ReleaseElement(*element, true);
+ }
+ for (auto parentNode : qAsConst(parentNodes))
+ renderer->ChildrenUpdated(*parentNode);
+}
+
+template<typename TDataType>
+void CQmlEngineImpl::setDynamicObjectProperty(qt3ds::render::SDynamicObject &material,
+ const dynamic::SPropertyDefinition &propDesc,
+ const TDataType &propValue)
+{
+ memCopy(material.GetDataSectionBegin() + propDesc.m_Offset, &propValue, sizeof(TDataType));
+}
+
+/**
+* @brief Create QML engine
+*
+* @param[in] inFoundation Pointer to foundation
+* @param[in] inTimeProvider Pointer to time provider
+*
+* @return no return
+*/
+CQmlEngine *CQmlEngine::Create(qt3ds::NVFoundationBase &inFoundation, ITimeProvider &inTimeProvider)
+{
+ return QT3DS_NEW(inFoundation.getAllocator(), CQmlEngineImpl)(inFoundation, inTimeProvider);
+}
+
+}
diff --git a/src/runtime/Qt3DSQmlEngine.h b/src/runtime/Qt3DSQmlEngine.h
new file mode 100644
index 0000000..a5ad382
--- /dev/null
+++ b/src/runtime/Qt3DSQmlEngine.h
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-20016 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$
+**
+****************************************************************************/
+
+#pragma once
+#ifndef QT3DS_QML_ENGINE_H
+#define QT3DS_QML_ENGINE_H
+
+#include "Qt3DSIScriptBridge.h"
+#include "Qt3DSIComponentManager.h"
+#include "EASTL/vector.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSSync.h"
+#include "foundation/Qt3DSMutex.h"
+#include "Qt3DSTimer.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+
+namespace Q3DStudio {
+//==============================================================================
+// Typedefs
+//==============================================================================
+typedef void (*TScriptCallback)(void *inUserData);
+
+//==============================================================================
+// Defines
+//==============================================================================
+#define SCRIPT_ON_INITIALIZE 1
+#define SCRIPT_ON_UPDATE 2
+
+//==============================================================================
+// Forwards
+//==============================================================================
+struct SEventCommand;
+class IPresentation;
+
+//==============================================================================
+/**
+* Manages the various events callbacks in the system. It fires the
+* registered callbacks when an event was triggered.
+*/
+class CScriptCallbacks
+{
+ //==============================================================================
+ // Structs
+ //==============================================================================
+protected:
+ struct SScriptCallbackEntry
+ {
+ SScriptCallbackEntry() {}
+ ~SScriptCallbackEntry() {}
+
+ TScriptCallback m_Function; ///< Callback function pointer
+ void *m_UserData; ///< User data
+ Q3DStudio::UINT32
+ m_CallbackType; ///< callback type. determines when and/or how often it is called
+ bool m_Processed; ///< Only used if the callback is of
+
+ private: // Disabled Copy Construction
+ SScriptCallbackEntry(const SScriptCallbackEntry &);
+ SScriptCallbackEntry &operator=(const SScriptCallbackEntry &);
+ };
+ typedef CArray<SScriptCallbackEntry *> TCallbackList; ///< Array of callbacks regisgtered
+
+ struct SFrameCallbackEntry
+ {
+ SFrameCallbackEntry() {}
+ ~SFrameCallbackEntry()
+ {
+ FOR_ARRAY(SScriptCallbackEntry *, theEntry, m_Callbacks)
+ Q3DStudio_delete(*theEntry, SScriptCallbackEntry);
+ }
+
+ TCallbackList m_Callbacks; ///< List of callbacks listening to this event
+
+ private: // Disabled Copy Construction
+ SFrameCallbackEntry(const SFrameCallbackEntry &);
+ SFrameCallbackEntry &operator=(const SFrameCallbackEntry &);
+ };
+
+ typedef CArray<SFrameCallbackEntry *> TFrameCallbacksList; ///< Array of frame callbacks
+
+public: // Construction
+ CScriptCallbacks();
+ ~CScriptCallbacks();
+
+ bool RegisterCallback(Q3DStudio::UINT32 callbackType, const TScriptCallback inCallback,
+ void *inUserData);
+ void UnregisterCallback(Q3DStudio::UINT32 callbackType, const TScriptCallback inCallback);
+
+ void ProcessCallbacks();
+
+private:
+ TFrameCallbacksList m_CallbackList;
+
+private: // Disabled Copy Construction
+ CScriptCallbacks(const CScriptCallbacks &);
+ CScriptCallbacks &operator=(const CScriptCallbacks &);
+};
+
+class CQmlEngine : public IScriptBridge
+{
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ virtual ~CQmlEngine() {}
+
+public: // Public functions but not functions on the script bridge
+ /**
+ * @brief Peek a custom action from the queue
+ *
+ * @param[out] outElement Target Element
+ * @param[out] outSignal Signal Name
+ *
+ * @return true if signal available
+ */
+ virtual bool PeekSignal(TElement *&outElement, char *&outName) = 0;
+
+ /**
+ * @brief Select a slide by index
+ *
+ * @param[in] component Component Name
+ * @param[in] slideIndex Slide index
+ * @param[in] inArgs Arguemnts for slide switch
+ *
+ * @return none
+ */
+ virtual void GotoSlideIndex(const char *component, const Q3DStudio::INT32 slideIndex,
+ const SScriptEngineGotoSlideArgs &inArgs) = 0;
+
+ virtual bool GetSlideInfo(const char *elementPath, int &currentIndex, int &previousIndex,
+ QString &currentName, QString &previousName) = 0;
+
+ /**
+ * @brief Go to specified time
+ *
+ * @param[in] component Component Name
+ * @param[in] time New time
+ *
+ * @return none
+ */
+ virtual void GotoTime(const char *component, const Q3DStudio::FLOAT time) = 0;
+
+ /**
+ * @brief Return values of an attribute
+ *
+ * @param[in] element Element Name
+ * @param[in] attName Attribute name
+ * @param[out] value Attribute value
+ *
+ * @return none
+ */
+ virtual bool GetAttribute(const char *element, const char *attName, char *value) = 0;
+ virtual bool GetAttribute(TElement *target, const char *attName, char *value) = 0;
+
+ /**
+ * @brief Register a callback
+ *
+ * @param[in] callbackType callback type
+* @param[in] inCallback pointer to callback
+* @param[in] inUserData pointer to user data
+ *
+ * @return true on success
+ */
+ virtual bool RegisterCallback(Q3DStudio::UINT32 callbackType, const TScriptCallback inCallback,
+ void *inUserData) = 0;
+
+ /**
+ * @brief Shutdown QML engine
+ *
+ * @param[in] inFoundation Pointer to foundation
+ *
+ * @return no return
+ */
+ virtual void Shutdown(qt3ds::NVFoundationBase &inFoundation) = 0;
+
+ virtual qt3ds::runtime::IApplication *GetApplication() = 0;
+
+ virtual void Initialize() = 0;
+
+public:
+ /**
+ * @brief Create QML engine
+ *
+ * @param[in] inFoundation Pointer to foundation
+ * @param[in] inTimeProvider Pointer to time provider
+ *
+ * @return no return
+ */
+ static CQmlEngine *Create(qt3ds::NVFoundationBase &inFoundation, ITimeProvider &inTimeProvider);
+};
+
+} // namespace Q3DStudio
+
+#endif
diff --git a/src/runtime/Qt3DSRuntimeFactory.h b/src/runtime/Qt3DSRuntimeFactory.h
new file mode 100644
index 0000000..fb46240
--- /dev/null
+++ b/src/runtime/Qt3DSRuntimeFactory.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "Qt3DSTimer.h"
+#include "foundation/Qt3DSRefCounted.h"
+
+namespace qt3ds {
+namespace render {
+ class IInputStreamFactory;
+ class IQt3DSRenderContext;
+ class IQt3DSRenderContextCore;
+}
+}
+
+namespace qt3ds {
+namespace runtime {
+ class IApplication;
+}
+}
+
+namespace qt3ds {
+namespace evt {
+ class IEventSystem;
+}
+}
+
+namespace qt3ds {
+class NVFoundationBase;
+namespace foundation {
+ class IStringTable;
+ class IPerfTimer;
+}
+}
+
+namespace Q3DStudio {
+
+class IScene;
+class CRenderEngine;
+class ISceneManager;
+class IRender;
+class CInputEngine;
+class IScriptBridge;
+class CPresentation;
+class IPresentation;
+class ITimeProvider;
+class ISceneBinaryLoader;
+
+// All of the interfaces available without opengl initialized.
+class IRuntimeFactoryCore : public qt3ds::foundation::NVRefCounted
+{
+public:
+ virtual ISceneBinaryLoader &GetSceneLoader() = 0;
+ virtual IScriptBridge &GetScriptEngineQml() = 0;
+ virtual qt3ds::render::IQt3DSRenderContextCore &GetRenderContextCore() = 0;
+ virtual qt3ds::render::IInputStreamFactory &GetInputStreamFactory() = 0;
+ virtual qt3ds::evt::IEventSystem &GetEventSystem() = 0;
+ virtual ITimeProvider &GetTimeProvider() = 0;
+ virtual qt3ds::NVFoundationBase &GetFoundation() = 0;
+ virtual qt3ds::foundation::IPerfTimer &GetPerfTimer() = 0;
+ virtual qt3ds::foundation::IStringTable &GetStringTable() = 0;
+ virtual void AddSearchPath(const char8_t *inFile) = 0;
+ virtual void SetDllDir(const char *inDir) = 0;
+ virtual qt3ds::runtime::IApplication *GetApplicationCore() = 0;
+ virtual void SetApplicationCore(qt3ds::runtime::IApplication *app) = 0;
+};
+
+class IRuntimeFactory : public IRuntimeFactoryCore
+{
+protected:
+ virtual ~IRuntimeFactory() {}
+
+public:
+ virtual ISceneManager &GetSceneManager() = 0;
+ virtual qt3ds::render::IQt3DSRenderContext &GetQt3DSRenderContext() = 0;
+ virtual qt3ds::runtime::IApplication *GetApplication() = 0;
+ virtual void SetApplication(qt3ds::runtime::IApplication *app) = 0;
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSSceneManager.h b/src/runtime/Qt3DSSceneManager.h
new file mode 100644
index 0000000..7d8297f
--- /dev/null
+++ b/src/runtime/Qt3DSSceneManager.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSPickFrame.h"
+#include "Qt3DSIStateful.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "Qt3DSIScene.h"
+
+namespace qt3ds {
+namespace foundation {
+ class IInStream;
+ class IOutStream;
+ class Mutex;
+}
+}
+
+namespace qt3ds {
+class Q3DSVariantConfig;
+namespace render {
+ class IQt3DSRenderContextCore;
+ class ILoadedBuffer;
+}
+}
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class IScene;
+class IPresentation;
+class CRenderEngine;
+class CRenderEngine;
+class IUIPParser;
+class CFileStream;
+class IRuntimeFactory;
+class IScriptBridge;
+class ISceneManager;
+
+class ISceneBinaryLoader : public qt3ds::foundation::NVRefCounted
+{
+protected:
+ virtual ~ISceneBinaryLoader() {}
+
+public:
+ virtual qt3ds::foundation::NVDataRef<qt3ds::QT3DSU8>
+ BinaryLoadManagerData(qt3ds::foundation::IInStream &inStream, const char *inBinaryDir) = 0;
+
+ // threadsafe
+ // Can be called from any thread
+ virtual bool GetBinaryLoadFileName(eastl::string &inPresentationFilename,
+ eastl::string &outResult) = 0;
+
+ // threadsafe
+ // returns a handle to the loaded object. Return value of zero means error.
+ virtual qt3ds::QT3DSU32 LoadSceneStage1(qt3ds::foundation::CRegisteredString inPresentationDirectory,
+ qt3ds::render::ILoadedBuffer &inData) = 0;
+
+ // threadsafe
+ // still does not require openGL context but has dependency on a few other things.
+ virtual void LoadSceneStage2(qt3ds::QT3DSU32 inSceneHandle, IPresentation &inPresentation,
+ size_t inElementMemoryOffset, IScriptBridge &inBridge) = 0;
+};
+struct FacePositionPlanes
+{
+ enum Enum { XY = 0, YZ, XZ };
+};
+
+//==============================================================================
+/**
+* @class ISceneManager
+* @brief Container class that creates and manages all the Scenes.
+*/
+class ISceneManager : public qt3ds::foundation::NVRefCounted
+{
+protected:
+ virtual ~ISceneManager(){}
+
+public: // Presentations
+ //==============================================================================
+ /**
+ * Load a new scene based on an existing presentation. This will create a new
+ * CScene-based class from the factory method at "CreateScene". To supply
+ * your own class, simply set "CreateScene" to the desired factory method.
+ * After creating the new scene, a load will be triggered.
+ * @param inPresentation the current presentation loaded
+ */
+ virtual IScene *LoadScene(IPresentation *inPresentation, IUIPParser *inParser,
+ IScriptBridge &inBridge,
+ const qt3ds::Q3DSVariantConfig &variantConfig) = 0;
+ virtual void LoadRenderPlugin(const CHAR *inAssetIDString, const CHAR *inPath,
+ const CHAR *inArgs) = 0;
+
+ virtual void LoadQmlStreamerPlugin(const CHAR *inAssetIDString) = 0;
+
+ virtual void BinarySaveManagerData(qt3ds::foundation::IOutStream &inStream,
+ const char *inBinaryDir) = 0;
+ // Loading flow data needs to return the string table memory block. This allows
+ // other systems to use remap their string table strings assuming they had mapped them before
+ // serialization. In general this is a horrible hack and abstraction leak but assuming you have
+ // a string
+ // table I guess it isn't too bad.
+ virtual qt3ds::foundation::NVDataRef<qt3ds::QT3DSU8>
+ BinaryLoadManagerData(qt3ds::foundation::IInStream &inStream, const char *inBinaryDir) = 0;
+
+ virtual void BinarySave(Q3DStudio::IScene &inScene) = 0;
+
+public: // Update Cycle
+ virtual BOOL Update() = 0;
+ virtual BOOL RenderPresentation(IPresentation *inPresentation, bool firstFrame) = 0;
+ virtual void OnViewResize(INT32 inViewWidth, INT32 inViewHeight) = 0;
+ virtual void GetViewSize(INT32 &outWidth, INT32 &outHeight) = 0;
+
+ virtual STextSizes GetDisplayDimensions(Q3DStudio::IPresentation *inPrimaryPresentation) = 0;
+
+public: // Picking
+ virtual Q3DStudio::TElement *UserPick(float mouseX, float mouseY) = 0;
+ virtual SPickFrame AdvancePickFrame(const SInputFrame &inInputFrame) = 0;
+ // Get the relative UV coord position of an element. This matches how mouse picking works where
+ // we pick out
+ virtual qt3ds::foundation::Option<qt3ds::QT3DSVec2>
+ FacePosition(Q3DStudio::TElement &inElement, float mouseX, float mouseY,
+ qt3ds::foundation::NVDataRef<Q3DStudio::TElement *> inMapperElements,
+ FacePositionPlanes::Enum inPlane) = 0;
+
+ virtual void Release() = 0;
+
+ static ISceneManager &Create(IRuntimeFactory &inFactory, CRenderEngine &inRenderEngine);
+};
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSSlideSystem.cpp b/src/runtime/Qt3DSSlideSystem.cpp
new file mode 100644
index 0000000..33c7544
--- /dev/null
+++ b/src/runtime/Qt3DSSlideSystem.cpp
@@ -0,0 +1,675 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+#include "Qt3DSSlideSystem.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSPool.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "Qt3DSAnimationSystem.h"
+#include "Qt3DSLogicSystem.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSHash.h"
+#include "Qt3DSTimePolicy.h"
+#include "foundation/Qt3DSIndexableLinkedList.h"
+
+using namespace qt3ds::runtime;
+using namespace qt3ds::runtime::element;
+
+namespace {
+struct SSlideAttribute
+{
+ QT3DSU32 m_Index = 0;
+ Q3DStudio::UVariant m_Value;
+ SSlideAttribute()
+ {
+ m_Value.m_INT32 = 0;
+ }
+ SSlideAttribute(QT3DSU32 idx, Q3DStudio::UVariant val)
+ : m_Index(idx)
+ , m_Value(val)
+ {
+ }
+};
+
+struct SSlideAttributeNode
+{
+ enum {
+ AttributeCount = 8,
+ };
+
+ SSlideAttribute m_Data[AttributeCount];
+ SSlideAttributeNode *m_NextNode = nullptr;
+};
+
+typedef IndexableLinkedList<SSlideAttributeNode, SSlideAttribute,
+ SSlideAttributeNode::AttributeCount>
+ TSlideAttributeNodeList;
+
+struct SSlideElement
+{
+ QT3DSU32 m_ElementHandle = 0;
+ QT3DSU32 m_AttributeCount : 31;
+ QT3DSU32 m_Active : 1;
+ SSlideElement *m_NextElement = nullptr;
+ SSlideAttributeNode *m_AttributeNodes = nullptr;
+
+ SSlideElement()
+ : m_AttributeCount(0)
+ , m_Active(false)
+ {
+ }
+};
+
+struct SSlideAnimActionNode
+{
+ enum {
+ AnimActionCount = 8,
+ };
+ SSlideAnimAction m_Data[AnimActionCount];
+ SSlideAnimActionNode *m_NextNode = nullptr;
+};
+
+typedef IndexableLinkedList<SSlideAnimActionNode, SSlideAnimAction,
+ SSlideAnimActionNode::AnimActionCount>
+ TSlideAnimActionNodeList;
+
+struct SSlide
+{
+ SSlide *m_NextSlide = nullptr;
+ CRegisteredString m_Name;
+ QT3DSU32 m_PlayMode : 3;
+ QT3DSU32 m_PlayThroughTo : 8; // OXFF means no playthrough
+ QT3DSU32 m_Paused : 1;
+ QT3DSU32 m_StartTime = 0;
+ QT3DSU32 m_EndTime = 0;
+ QT3DSU32 m_AnimActionCount = 0;
+ SSlideElement *m_FirstElement = nullptr;
+ SSlideElement *m_lastElement = nullptr;
+ SSlideAnimActionNode *m_FirstAnimActionNode = nullptr;
+ bool m_activeSlide = false;
+ bool m_unloadSlide = false;
+ QVector<QString> m_sourcePaths;
+
+ SSlide()
+ : m_PlayMode(PlayMode::StopAtEnd)
+ , m_PlayThroughTo(0xFF)
+ , m_Paused(false)
+ {
+ }
+
+ void Initialize(CRegisteredString inName, PlayMode::Enum inMode, QT3DSU8 inPlayThrough,
+ bool inPaused, QT3DSU32 inStartTime, QT3DSU32 inEndTime)
+ {
+ m_Name = inName;
+ m_PlayMode = inMode;
+ m_PlayThroughTo = inPlayThrough;
+ m_Paused = inPaused;
+ m_StartTime = inStartTime;
+ m_EndTime = inEndTime;
+ }
+};
+
+struct SSlideSystem : public ISlideSystem
+{
+ typedef nvhash_map<SElement *, SSlide *> TComponentSlideHash;
+ typedef Pool<SSlideAttributeNode, ForwardingAllocator> TAttributeNodePool;
+ typedef Pool<SSlideAnimActionNode, ForwardingAllocator> TSlideAnimActionPool;
+ typedef Pool<SSlideElement, ForwardingAllocator> TSlideElementPool;
+ typedef Pool<SSlide, ForwardingAllocator> TSlidePool;
+
+ NVFoundationBase &m_Foundation;
+ IStringTable &m_StringTable;
+ IElementAllocator &m_ElementSystem;
+ TAttributeNodePool m_AttributeNodePool;
+ TSlideAnimActionPool m_AnimActionPool;
+ TSlidePool m_SlidePool;
+ TSlideElementPool m_SlideElements;
+ TComponentSlideHash m_Slides;
+
+ SSlide *m_CurrentSlide = nullptr;
+ SSlideElement *m_CurrentSlideElement = nullptr;
+
+ NVDataRef<QT3DSU8> m_LoadData;
+
+ QT3DSI32 m_RefCount;
+
+ SSlideSystem(NVFoundationBase &inFnd, IStringTable &inStrTable, IElementAllocator &inAllocator)
+ : m_Foundation(inFnd)
+ , m_StringTable(inStrTable)
+ , m_ElementSystem(inAllocator)
+ , m_AttributeNodePool(ForwardingAllocator(inFnd.getAllocator(), "m_AttributeNodePool"))
+ , m_AnimActionPool(ForwardingAllocator(inFnd.getAllocator(), "m_AnimActionPool"))
+ , m_SlidePool(ForwardingAllocator(inFnd.getAllocator(), "m_SlidePool"))
+ , m_SlideElements(ForwardingAllocator(inFnd.getAllocator(), "m_SlideElements"))
+ , m_Slides(inFnd.getAllocator(), "m_Slides")
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Foundation.getAllocator());
+ NVDelete(alloc, this);
+ }
+ }
+
+ QT3DSU32 AddSlide(element::SElement &inComponent, const char8_t *inName,
+ PlayMode::Enum inPlayMode, bool inPaused, QT3DSU8 inPlayThroughTo,
+ QT3DSU32 inMinTime, QT3DSU32 inMaxTime) override
+ {
+ eastl::pair<TComponentSlideHash::iterator, bool> inserter =
+ m_Slides.insert(eastl::make_pair(&inComponent, static_cast<SSlide *>(nullptr)));
+ SSlide *newSlide = m_SlidePool.construct(__FILE__, __LINE__);
+ QT3DSU32 slideIndex = 0;
+ if (!inserter.first->second) {
+ inserter.first->second = newSlide;
+ } else {
+ SSlide *theSlide = nullptr;
+ for (theSlide = inserter.first->second; theSlide->m_NextSlide;
+ theSlide = theSlide->m_NextSlide) {
+ ++slideIndex;
+ }
+ theSlide->m_NextSlide = newSlide;
+ }
+ m_CurrentSlide = newSlide;
+ newSlide->Initialize(m_StringTable.RegisterStr(inName), inPlayMode, inPlayThroughTo,
+ inPaused, inMinTime, inMaxTime);
+ m_CurrentSlideElement = nullptr;
+ if (inComponent.IsComponent()) {
+ SComponent &theComponent = static_cast<SComponent &>(inComponent);
+ theComponent.m_SlideCount++;
+ }
+ return slideIndex;
+ }
+
+ void AddSourcePath(const char8_t *path) override
+ {
+ if (m_CurrentSlide)
+ m_CurrentSlide->m_sourcePaths.push_back(QString::fromUtf8(path));
+ }
+
+ QVector<QString> GetSourcePaths(SSlideKey inKey) override
+ {
+ auto slide = FindSlide(inKey);
+ if (slide)
+ return slide->m_sourcePaths;
+ return {};
+ }
+
+ void SetSlideMaxTime(QT3DSU32 inMaxTime) override
+ {
+ if (m_CurrentSlide)
+ m_CurrentSlide->m_EndTime = inMaxTime;
+ }
+
+ void AddSlideElement(element::SElement &inElement, bool inActive) override
+ {
+ if (m_CurrentSlide) {
+ SSlideElement *lastSlideElement = m_CurrentSlideElement;
+ m_CurrentSlideElement = m_SlideElements.construct(__FILE__, __LINE__);
+ m_CurrentSlideElement->m_Active = inActive;
+ m_CurrentSlideElement->m_ElementHandle = inElement.GetHandle();
+ if (!lastSlideElement) {
+ QT3DS_ASSERT(!m_CurrentSlide->m_FirstElement);
+ m_CurrentSlide->m_FirstElement = m_CurrentSlideElement;
+ } else {
+ lastSlideElement->m_NextElement = m_CurrentSlideElement;
+ }
+ m_CurrentSlide->m_lastElement = m_CurrentSlideElement;
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ // The parent element must be found from target slide.
+ // Elements cannot be added to the master slide.
+ bool addSlideElement(element::SElement &inComponent, int slideIndex,
+ element::SElement &inElement, bool eyeBall) override
+ {
+ // Note: Slide 0 contains all loaded items in the graph with m_active set to false.
+ // Other slides contain all master slide items and slide specific items with m_active
+ // set to eyeball. We shouldn't need to care about slide 0 here as it won't be
+ // executed/rolled back after initial loading of the scene.
+
+ Q_ASSERT(slideIndex > 0);
+
+ TComponentSlideHash::const_iterator theFindResult = m_Slides.find(&inComponent);
+ element::SElement *parentElement = inElement.GetParent();
+ if (theFindResult == m_Slides.end()) {
+ qWarning() << __FUNCTION__ << "Could not find slides for component";
+ return false;
+ }
+ if (!parentElement) {
+ qWarning() << __FUNCTION__ << "Element has no parent";
+ return false;
+ }
+
+ int parentFound = false;
+ SSlide *slide = theFindResult->second;
+ SSlide *targetSlide = nullptr;
+ for (int idx = 0; slide && !targetSlide; slide = slide->m_NextSlide, ++idx) {
+ if (slideIndex == idx) {
+ targetSlide = slide;
+ SSlideElement *slideElement = slide->m_FirstElement;
+ while (slideElement) {
+ if (slideElement->m_ElementHandle == parentElement->m_Handle) {
+ parentFound = true;
+ break;
+ }
+ slideElement = slideElement->m_NextElement;
+ }
+ }
+ }
+
+ if (!parentFound) {
+ qWarning() << __FUNCTION__ << "Parent element could not be found from target slide";
+ return false;
+ }
+
+ m_CurrentSlide = targetSlide;
+ m_CurrentSlideElement = m_CurrentSlide->m_lastElement;
+
+ // Explicit active state is based solely on the slide and eyeball
+ int activeSlideIndex = static_cast<SComponent &>(inComponent).GetCurrentSlide();
+ bool isActive = activeSlideIndex == slideIndex && eyeBall;
+ inElement.Flags().SetExplicitActive(isActive);
+
+ AddSlideElement(inElement, eyeBall);
+
+ return true;
+ }
+
+ void removeElementRecursive(SSlide *slide, element::SElement &inElement)
+ {
+ element::SElement *child = inElement.m_Child;
+ while (child) {
+ removeElementRecursive(slide, *child);
+ child = child->m_Sibling;
+ }
+
+ SSlideElement *slideElement = slide->m_FirstElement;
+ SSlideElement *previousElement = nullptr;
+ while (slideElement) {
+ if (slideElement->m_ElementHandle == inElement.m_Handle) {
+ if (slide->m_FirstElement == slideElement)
+ slide->m_FirstElement = slideElement->m_NextElement;
+ if (slide->m_lastElement == slideElement)
+ slide->m_lastElement = previousElement;
+ if (previousElement)
+ previousElement->m_NextElement = slideElement->m_NextElement;
+ m_SlideElements.deallocate(slideElement);
+ break;
+ }
+ previousElement = slideElement;
+ slideElement = slideElement->m_NextElement;
+ }
+ }
+
+ // Removes element and its children from all slides
+ void removeElement(element::SElement &inComponent, element::SElement &inElement) override
+ {
+ TComponentSlideHash::const_iterator theFindResult = m_Slides.find(&inComponent);
+ if (theFindResult == m_Slides.end()) {
+ qWarning() << __FUNCTION__ << "Could not find slides for component";
+ return;
+ }
+
+ SSlide *slide = theFindResult->second;
+ for (; slide; slide = slide->m_NextSlide)
+ removeElementRecursive(slide, inElement);
+ }
+
+ void AddSlideAttribute(Q3DStudio::SAttributeKey inKey, Q3DStudio::UVariant inValue) override
+ {
+ if (m_CurrentSlideElement) {
+ SElement *theElement =
+ m_ElementSystem.FindElementByHandle(m_CurrentSlideElement->m_ElementHandle);
+ Option<QT3DSU32> theIdx = theElement->FindPropertyIndex(inKey.m_Hash);
+ if (theIdx.hasValue() == false) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ QT3DSU32 count = m_CurrentSlideElement->m_AttributeCount;
+ TSlideAttributeNodeList::Create(m_CurrentSlideElement->m_AttributeNodes, count,
+ m_AttributeNodePool) =
+ SSlideAttribute(*theIdx, inValue);
+ m_CurrentSlideElement->m_AttributeCount = count;
+ }
+ }
+
+ SSlideAnimAction *AddSlideAnimAction(bool inAnimation, QT3DSI32 inId, bool inActive) override
+ {
+ if (m_CurrentSlide) {
+ SSlideAnimAction &theAnimAction = TSlideAnimActionNodeList::Create(
+ m_CurrentSlide->m_FirstAnimActionNode, m_CurrentSlide->m_AnimActionCount,
+ m_AnimActionPool);
+ theAnimAction = SSlideAnimAction(QT3DSI32(inId), inActive, inAnimation);
+ return &theAnimAction;
+ }
+
+ return nullptr;
+ }
+
+ const SSlide *FindSlide(SSlideKey inKey) const
+ {
+ TComponentSlideHash::const_iterator iter = m_Slides.find(inKey.m_Component);
+ if (iter == m_Slides.end())
+ return nullptr;
+
+ SSlide *theSlide = iter->second;
+ for (QT3DSU32 idx = inKey.m_Index; idx; --idx) {
+ if (theSlide)
+ theSlide = theSlide->m_NextSlide;
+ }
+
+ return theSlide;
+ }
+
+ SSlide *FindSlide(SSlideKey inKey)
+ {
+ TComponentSlideHash::const_iterator iter = m_Slides.find(inKey.m_Component);
+ if (iter == m_Slides.end())
+ return nullptr;
+
+ SSlide *theSlide = iter->second;
+ for (QT3DSU32 idx = inKey.m_Index; idx; --idx) {
+ if (theSlide)
+ theSlide = theSlide->m_NextSlide;
+ }
+
+ return theSlide;
+ }
+
+ template <typename TOperator>
+ static void IterateSlideAnimActions(const SSlide &inSlide, TOperator inOp)
+ {
+ for (TSlideAnimActionNodeList::const_iterator
+ iter = TSlideAnimActionNodeList::begin(inSlide.m_FirstAnimActionNode,
+ inSlide.m_AnimActionCount),
+ end = TSlideAnimActionNodeList::end(inSlide.m_FirstAnimActionNode,
+ inSlide.m_AnimActionCount);
+ iter != end; ++iter) {
+ inOp(*iter);
+ }
+ }
+
+ template <typename TOperator>
+ static void IterateSlideElementAttributes(const SSlide &inSlide, TOperator inOp,
+ IElementAllocator &inAlloc)
+ {
+ for (SSlideElement *theElement = inSlide.m_FirstElement; theElement;
+ theElement = theElement->m_NextElement) {
+ SElement *theElem = inAlloc.FindElementByHandle(theElement->m_ElementHandle);
+ if (!theElem) {
+ QT3DS_ASSERT(false);
+ } else {
+ if (inOp.handleElementActive(*theElem, theElement->m_Active)) {
+ for (TSlideAttributeNodeList::const_iterator
+ iter = TSlideAttributeNodeList::begin(theElement->m_AttributeNodes,
+ theElement->m_AttributeCount),
+ end = TSlideAttributeNodeList::end(theElement->m_AttributeNodes,
+ theElement->m_AttributeCount);
+ iter != end; ++iter) {
+ inOp.handleElementAttribute(*theElem, *iter);
+ }
+ }
+ }
+ }
+ }
+
+ struct SDynamicKeyOperator
+ {
+ IAnimationSystem &m_AnimationSystem;
+ SDynamicKeyOperator(IAnimationSystem &anim)
+ : m_AnimationSystem(anim)
+ {
+ }
+ void operator()(const SSlideAnimAction &theAction)
+ {
+ if (theAction.m_IsAnimation && theAction.m_Active)
+ m_AnimationSystem.UpdateDynamicKey(theAction.m_Id);
+ }
+ };
+
+ struct SExecuteAnimActionOperator
+ {
+ IElementAllocator &m_ElementAllocator;
+ IAnimationSystem &m_AnimationSystem;
+ ILogicSystem &m_LogicManager;
+ bool m_Invert;
+
+ SExecuteAnimActionOperator(IElementAllocator &inElemAllocator, IAnimationSystem &anim,
+ ILogicSystem &logic, bool inInvert = false)
+ : m_ElementAllocator(inElemAllocator)
+ , m_AnimationSystem(anim)
+ , m_LogicManager(logic)
+ , m_Invert(inInvert)
+ {
+ }
+
+ void operator()(const SSlideAnimAction &theAction)
+ {
+ bool active = theAction.m_Active;
+ if (m_Invert)
+ active = !active;
+ if (theAction.m_IsAnimation)
+ m_AnimationSystem.SetActive(theAction.m_Id, active);
+ else
+ m_LogicManager.SetActive(theAction.m_Id, active, m_ElementAllocator);
+ }
+ };
+
+ struct SElementOperator
+ {
+ bool m_InvertActive;
+ SElementOperator(bool inInvert = false)
+ : m_InvertActive(inInvert)
+ {
+ }
+ bool handleElementActive(SElement &inElement, bool inActive)
+ {
+ if (m_InvertActive && inElement.Flags().IsExplicitActive())
+ inActive = !inActive;
+ inElement.Flags().SetExplicitActive(inActive);
+
+ if (m_InvertActive)
+ return false;
+
+ return inActive;
+ }
+
+ void handleElementAttribute(SElement &inElement, const SSlideAttribute &inAttribute)
+ {
+ Option<TPropertyDescAndValuePtr> theProperty =
+ inElement.GetPropertyByIndex(inAttribute.m_Index);
+ if (theProperty.hasValue()) {
+ inElement.SetAttribute(*theProperty, inAttribute.m_Value);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ };
+
+ void InitializeDynamicKeys(SSlideKey inKey, IAnimationSystem &inAnimationSystem) const override
+ {
+ const SSlide *theSlide = FindSlide(inKey);
+ if (theSlide) {
+ IterateSlideAnimActions(*theSlide, SDynamicKeyOperator(inAnimationSystem));
+ }
+ }
+
+ void ExecuteSlide(SSlideKey inKey, IAnimationSystem &inAnimationSystem,
+ ILogicSystem &inLogicManager) override
+ {
+ SSlide *theSlide = FindSlide(inKey);
+ if (!theSlide) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ if (inKey.m_Component->IsComponent()) {
+ Q3DStudio::TTimeUnit theLoopDuration = theSlide->m_EndTime - theSlide->m_StartTime;
+ SComponent &theComponent = static_cast<SComponent &>(*inKey.m_Component);
+ Q3DStudio::CTimePolicy &thePolicy = theComponent.GetTimePolicy();
+ Q3DStudio::TimePolicyModes::Enum theMode = Q3DStudio::TimePolicyModes::StopAtEnd;
+ switch (theSlide->m_PlayMode) {
+ case PlayMode::Looping:
+ theMode = Q3DStudio::TimePolicyModes::Looping;
+ break;
+ case PlayMode::PingPong:
+ theMode = Q3DStudio::TimePolicyModes::PingPong;
+ break;
+ case PlayMode::Ping:
+ theMode = Q3DStudio::TimePolicyModes::Ping;
+ break;
+
+ case PlayMode::StopAtEnd:
+ case PlayMode::PlayThroughTo:
+ default:
+ theMode = Q3DStudio::TimePolicyModes::StopAtEnd;
+ break;
+ }
+ thePolicy.Initialize(theLoopDuration, theMode);
+ thePolicy.SetPaused(theSlide->m_Paused);
+
+ theComponent.SetPlayThrough(theSlide->m_PlayMode == PlayMode::PlayThroughTo);
+ }
+ theSlide->m_activeSlide = true;
+ IterateSlideElementAttributes(*theSlide, SElementOperator(false), m_ElementSystem);
+ IterateSlideAnimActions(*theSlide, SExecuteAnimActionOperator(
+ m_ElementSystem, inAnimationSystem, inLogicManager));
+ }
+
+ void RollbackSlide(SSlideKey inKey, IAnimationSystem &inAnimationSystem,
+ ILogicSystem &inLogicManager) override
+ {
+ SSlide *theSlide = FindSlide(inKey);
+
+ if (!theSlide) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ theSlide->m_activeSlide = false;
+ IterateSlideElementAttributes(*theSlide, SElementOperator(true), m_ElementSystem);
+ IterateSlideAnimActions(
+ *theSlide,
+ SExecuteAnimActionOperator(m_ElementSystem, inAnimationSystem, inLogicManager, true));
+ }
+
+ QT3DSU8 FindSlide(element::SElement &inComponent, const char8_t *inSlideName) const override
+ {
+ TComponentSlideHash::const_iterator theFindResult = m_Slides.find(&inComponent);
+ if (theFindResult == m_Slides.end()) {
+ QT3DS_ASSERT(false);
+ return 0xFF;
+ }
+ CRegisteredString theRegName = m_StringTable.RegisterStr(inSlideName);
+ QT3DSU8 idx = 0;
+ for (const SSlide *theSlide = theFindResult->second; theSlide;
+ theSlide = theSlide->m_NextSlide, ++idx) {
+ if (theSlide->m_Name == theRegName)
+ return idx;
+ }
+ return 0xFF;
+ }
+
+ QT3DSU8 FindSlide(element::SElement &inComponent, QT3DSU32 inSlideHashName) const override
+ {
+ TComponentSlideHash::const_iterator theFindResult = m_Slides.find(&inComponent);
+ if (theFindResult == m_Slides.end()) {
+ QT3DS_ASSERT(false);
+ return 0xFF;
+ }
+
+ QT3DSU8 idx = 0;
+ for (const SSlide *theSlide = theFindResult->second; theSlide;
+ theSlide = theSlide->m_NextSlide, ++idx) {
+ if (Q3DStudio::CHash::HashString(theSlide->m_Name) == inSlideHashName)
+ return idx;
+ }
+ return 0xFF;
+ }
+
+ const char8_t *GetSlideName(SSlideKey inKey) const override
+ {
+ const SSlide *theSlide = FindSlide(inKey);
+ if (theSlide)
+ return theSlide->m_Name.c_str();
+ QT3DS_ASSERT(false);
+ return "";
+ }
+
+ QT3DSU8 GetPlaythroughToSlideIndex(SSlideKey inKey) const override
+ {
+ const SSlide *theSlide = FindSlide(inKey);
+ if (theSlide)
+ return theSlide->m_PlayThroughTo;
+ QT3DS_ASSERT(false);
+ return 0xFF;
+ }
+
+ bool isActiveSlide(SSlideKey inKey) const override
+ {
+ const SSlide *theSlide = FindSlide(inKey);
+ return theSlide->m_activeSlide;
+ }
+
+ void setUnloadSlide(SSlideKey inKey, bool unload) override
+ {
+ SSlide *theSlide = FindSlide(inKey);
+ theSlide->m_unloadSlide = unload;
+ }
+
+ bool isUnloadSlideSet(SSlideKey inKey) const override
+ {
+ const SSlide *theSlide = FindSlide(inKey);
+ return theSlide->m_unloadSlide;
+ }
+
+ void setIsActiveSlide(SSlideKey inKey, bool active) override
+ {
+ SSlide *theSlide = FindSlide(inKey);
+ theSlide->m_activeSlide = active;
+ }
+};
+}
+
+ISlideSystem &ISlideSystem::CreateSlideSystem(NVFoundationBase &inFnd, IStringTable &inStrTable,
+ IElementAllocator &inElemAllocator)
+{
+ return *QT3DS_NEW(inFnd.getAllocator(), SSlideSystem)(inFnd, inStrTable, inElemAllocator);
+}
diff --git a/src/runtime/Qt3DSSlideSystem.h b/src/runtime/Qt3DSSlideSystem.h
new file mode 100644
index 0000000..d3accbd
--- /dev/null
+++ b/src/runtime/Qt3DSSlideSystem.h
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "Qt3DSElementSystem.h"
+
+namespace Q3DStudio {
+class ILogicManager;
+}
+
+namespace qt3ds {
+namespace runtime {
+ class ILogicSystem;
+ class IAnimationSystem;
+
+ struct SSlideKey
+ {
+ element::SElement *m_Component;
+ QT3DSU32 m_Index;
+ SSlideKey()
+ : m_Component(NULL)
+ , m_Index(0)
+ {
+ }
+ SSlideKey(element::SElement &inElem, QT3DSU32 inIdx)
+ : m_Component(&inElem)
+ , m_Index(inIdx)
+ {
+ }
+
+ bool IsValid() const { return m_Component != NULL; }
+
+ bool operator==(const SSlideKey &inOther) const
+ {
+ return m_Component == inOther.m_Component && m_Index == inOther.m_Index;
+ }
+ };
+
+ struct PlayMode
+ {
+ enum Enum {
+ Looping = 0,
+ StopAtEnd,
+ PingPong,
+ Ping,
+ PlayThroughTo,
+ };
+ };
+
+ struct SSlidePlayInformation
+ {
+ PlayMode::Enum m_PlayMode;
+ bool m_Paused;
+ QT3DSU8 m_PlayThroughTo;
+
+ SSlidePlayInformation()
+ : m_PlayMode(PlayMode::Looping)
+ , m_Paused(false)
+ , m_PlayThroughTo(0xFF)
+ {
+ }
+ };
+
+ struct SSlideAnimAction
+ {
+ QT3DSI32 m_Id;
+ bool m_Active;
+ bool m_IsAnimation;
+ SSlideAnimAction()
+ : m_Id(0)
+ , m_Active(false)
+ , m_IsAnimation(false)
+ {
+ }
+ SSlideAnimAction(QT3DSI32 inId, bool inActive, bool inAnimation)
+ : m_Id(inId)
+ , m_Active(inActive)
+ , m_IsAnimation(inAnimation)
+ {
+ }
+ };
+
+ // Throughout this object, a slide index of 0xFF means invalid slide index.
+ class ISlideSystem : public NVRefCounted
+ {
+ public:
+ static QT3DSU8 InvalidSlideIndex() { return 0xFF; }
+ // Building out a dataset.
+ // Returns the index
+ // This slide is now the active cursor. The rest of the building functions will implicitly
+ // refer
+ // to the last added slide
+ // Playthroughto is either an index of the new slide or -1 meaning no playthough value.
+ virtual QT3DSU32 AddSlide(element::SElement &inComponent, const char8_t *inName,
+ PlayMode::Enum inPlayMode = PlayMode::Looping, bool inPaused = false,
+ QT3DSU8 inPlaythroughTo = InvalidSlideIndex(), QT3DSU32 inMinTime = 0,
+ QT3DSU32 inMaxTime = 0) = 0;
+
+ virtual void SetSlideMaxTime(QT3DSU32 inMaxTime) = 0;
+ virtual void AddSlideElement(element::SElement &inElement, bool inActive) = 0;
+ virtual bool addSlideElement(element::SElement &inComponent, int slideIndex,
+ element::SElement &inElement, bool eyeBall) = 0;
+ virtual void removeElement(element::SElement &inComponent,
+ element::SElement &inElement) = 0;
+ virtual void AddSlideAttribute(Q3DStudio::SAttributeKey inKey,
+ Q3DStudio::UVariant inValue) = 0;
+ virtual SSlideAnimAction *AddSlideAnimAction(bool inAnimation, QT3DSI32 inIndex,
+ bool inActive) = 0;
+ virtual void AddSourcePath(const char8_t *path) = 0;
+ virtual QVector<QString> GetSourcePaths(SSlideKey inKey) = 0;
+ virtual void setIsActiveSlide(SSlideKey inKey, bool active) = 0;
+ virtual bool isActiveSlide(SSlideKey inKey) const = 0;
+ virtual void setUnloadSlide(SSlideKey inKey, bool unload) = 0;
+ virtual bool isUnloadSlideSet(SSlideKey inKey) const = 0;
+
+ // Using the dataset
+ virtual void InitializeDynamicKeys(SSlideKey inKey,
+ IAnimationSystem &inAnimationSystem) const = 0;
+ virtual void ExecuteSlide(SSlideKey inKey, IAnimationSystem &inAnimationSystem,
+ ILogicSystem &inLogicManager) = 0;
+ virtual void RollbackSlide(SSlideKey inKey, IAnimationSystem &inAnimationSystem,
+ ILogicSystem &inLogicManager) = 0;
+ virtual QT3DSU8 FindSlide(element::SElement &inComponent,
+ const char8_t *inSlideName) const = 0;
+ virtual QT3DSU8 FindSlide(element::SElement &inComponent, QT3DSU32 inSlideHashName) const = 0;
+ virtual const char8_t *GetSlideName(SSlideKey inKey) const = 0;
+ virtual QT3DSU8 GetPlaythroughToSlideIndex(SSlideKey inKey) const = 0;
+
+ static ISlideSystem &CreateSlideSystem(NVFoundationBase &inFnd, IStringTable &inStrTable,
+ IElementAllocator &inElemAllocator);
+ };
+}
+}
diff --git a/src/runtime/Qt3DSTimePolicy.cpp b/src/runtime/Qt3DSTimePolicy.cpp
new file mode 100644
index 0000000..e1efb8e
--- /dev/null
+++ b/src/runtime/Qt3DSTimePolicy.cpp
@@ -0,0 +1,407 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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 "RuntimePrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSTimePolicy.h"
+#include "foundation/Qt3DSUnionCast.h"
+#include "foundation/Qt3DSIntrinsics.h"
+#include "Qt3DSComponentManager.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Constants
+//==============================================================================
+const TTimeUnit CTimePolicy::FOREVER = 0;
+
+SAlignedTimeUnit::SAlignedTimeUnit(const TTimeUnit &inUnit)
+{
+ StaticAssert<sizeof(SAlignedTimeUnit) == sizeof(TTimeUnit)>::valid_expression();
+ qt3ds::intrinsics::memCopy(this, &inUnit, sizeof(TTimeUnit));
+}
+
+SAlignedTimeUnit::operator TTimeUnit() const
+{
+ TTimeUnit retval;
+ qt3ds::intrinsics::memCopy(&retval, this, sizeof(TTimeUnit));
+ return retval;
+}
+
+void SAlignedTimeUnit::operator-=(const TTimeUnit &inTime)
+{
+ TTimeUnit theThis(*this);
+ theThis -= inTime;
+ *this = SAlignedTimeUnit(theThis);
+}
+
+void SAlignedTimeUnit::operator%=(const TTimeUnit &inTime)
+{
+ TTimeUnit theThis(*this);
+ theThis %= inTime;
+ *this = SAlignedTimeUnit(theThis);
+}
+
+//==============================================================================
+/**
+ * Initialization
+ */
+
+void CTimePolicy::Initialize(const TTimeUnit inLoopingDuration, TimePolicyModes::Enum inMode)
+{
+ m_LocalTime = 0;
+ m_LoopingDuration = (unsigned long)inLoopingDuration;
+ m_TimePolicyMode = inMode;
+ m_Offset = 0;
+ m_Paused = false;
+ m_OffsetInvalid = 1;
+ m_Backward = 0;
+ m_Rate = 1.0f;
+}
+
+void CTimePolicy::Initialize(const TTimeUnit inLoopingDuration /*= FOREVER*/,
+ UINT32 inRepetitions /*= 1*/, BOOL inPingPong /*= false*/)
+{
+ // UINT8 m_Repetitions : 2; ///< Number of traversal ( 0 = forever, 1 = stop at end, 2 =
+ // ping )
+ TimePolicyModes::Enum theMode = TimePolicyModes::StopAtEnd;
+ if (inRepetitions == 1)
+ theMode = TimePolicyModes::StopAtEnd;
+ else if (inRepetitions == 0) {
+ if (inPingPong)
+ theMode = TimePolicyModes::PingPong;
+ else
+ theMode = TimePolicyModes::Looping;
+ } else
+ theMode = TimePolicyModes::Ping;
+
+ Initialize(inLoopingDuration, theMode);
+}
+
+//==============================================================================
+/**
+ * Return the last computed time. This is subjected to playmodes.
+ * @return local time
+ */
+TTimeUnit CTimePolicy::InternalGetTime() const
+{
+ if (m_LocalTime < m_LoopingDuration)
+ return m_LocalTime;
+ else {
+ if (m_LoopingDuration == 0)
+ return m_LocalTime;
+
+ switch (m_TimePolicyMode) {
+ case TimePolicyModes::StopAtEnd:
+ return m_LoopingDuration;
+ case TimePolicyModes::Looping:
+ return m_LocalTime % m_LoopingDuration;
+ case TimePolicyModes::Ping: {
+ unsigned long interval = m_LocalTime / m_LoopingDuration;
+ unsigned long leftover = m_LocalTime % m_LoopingDuration;
+ if (interval == 1)
+ return m_LoopingDuration - leftover;
+ return 0;
+ }
+ case TimePolicyModes::PingPong: {
+ unsigned long interval = m_LocalTime / m_LoopingDuration;
+ unsigned long leftover = m_LocalTime % m_LoopingDuration;
+ if (interval % 2)
+ return m_LoopingDuration - leftover;
+ return leftover;
+ }
+ default:
+ QT3DS_ASSERT(false);
+ return 0;
+ break;
+ }
+ }
+}
+
+TTimeUnit CTimePolicy::GetTime() const
+{
+ TTimeUnit retval = InternalGetTime();
+ // Apply direction and rate.
+ bool isPlayingBack = m_Backward;
+ if (m_Rate < 0)
+ isPlayingBack = !isPlayingBack;
+
+ if (isPlayingBack)
+ retval = m_LoopingDuration - retval;
+ return retval;
+}
+
+static unsigned long clampTime(long inTime, unsigned long inDuration)
+{
+ if (inTime < 0)
+ return 0;
+ if (inTime > (long)inDuration)
+ return inDuration;
+ return (unsigned long)inTime;
+}
+
+//==============================================================================
+/**
+ * Trigger the policy to return inTime next GetTime is called.
+ * This is tricky math and the m_Offset field is being used both normally to
+ * simply change the time and here to calibrate itself next time ComputeTime
+ * is called.
+ * @param inTime time to be returned next ComputeTime
+ */
+void CTimePolicy::SetTime(TTimeUnit inTime)
+{
+
+ bool isPlayingBack = m_Backward;
+ if (m_Rate < 0)
+ isPlayingBack = !isPlayingBack;
+
+ if (isPlayingBack)
+ inTime = m_LoopingDuration - inTime;
+
+ m_OffsetInvalid = 1;
+
+ // Setup m_LocalTime such that it reflects intime.
+ if (m_LoopingDuration == 0) {
+ m_LocalTime = (unsigned long)inTime;
+ return;
+ }
+
+ switch (m_TimePolicyMode) {
+ case TimePolicyModes::StopAtEnd:
+ m_LocalTime = clampTime((long)inTime, m_LoopingDuration);
+ break;
+ case TimePolicyModes::Looping: {
+ long input = (long)inTime;
+ while (input < 0)
+ input += (long)m_LoopingDuration;
+ m_LocalTime = ((unsigned long)input) % m_LoopingDuration;
+ } break;
+ case TimePolicyModes::Ping: {
+ long input = (long)inTime;
+ if (input < 0)
+ input = 0;
+
+ long totalInterval = (long)(m_LoopingDuration * 2);
+ if (input > totalInterval)
+ m_LocalTime = (unsigned long)totalInterval;
+ else
+ m_LocalTime = (unsigned long)input;
+ } break;
+ case TimePolicyModes::PingPong: {
+ long totalInterval = (long)(m_LoopingDuration * 2);
+ long input = (long)inTime;
+ while (input < 0)
+ input += totalInterval;
+ m_LocalTime = (unsigned long)(input % totalInterval);
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ }
+}
+
+//==============================================================================
+/**
+ * Retrieves the looping duration.
+ * @see CTimePolicy::SetLoopingDuration( )
+ * @return TTimeUnit looping duration
+ */
+TTimeUnit CTimePolicy::GetLoopingDuration() const
+{
+ return m_LoopingDuration;
+}
+
+//==============================================================================
+/**
+ * Set the looping duration.
+ * Looping duration is defined as the time it takes before stopping, pinging or
+ * looping.
+ * @param inTime looping duration
+ */
+void CTimePolicy::SetLoopingDuration(const TTimeUnit inTime)
+{
+ m_LoopingDuration = (unsigned long)inTime;
+}
+
+//==============================================================================
+/**
+ * Return the paused state.
+ * @return BOOL pause state
+ */
+BOOL CTimePolicy::GetPaused() const
+{
+ return m_Paused;
+}
+
+BOOL CTimePolicy::GetPlayBackDirection() const
+{
+ bool reversePlaybackDirection = m_Backward;
+ if (m_Rate < 0)
+ reversePlaybackDirection = !reversePlaybackDirection;
+
+ bool retval = m_LocalTime < m_LoopingDuration;
+
+ if (reversePlaybackDirection)
+ retval = !retval;
+ return retval;
+}
+
+//==============================================================================
+/**
+ * Sets the paused state
+ * @param inPaused pause state
+ */
+void CTimePolicy::SetPaused(const BOOL inPaused)
+{
+ m_Paused = static_cast<UINT8>(inPaused);
+}
+
+BOOL CTimePolicy::UpdateTime(const TTimeUnit inTime)
+{
+ if (fabs(m_Rate) > .001f)
+ m_LocalTime =
+ static_cast<unsigned long>(((unsigned long)(inTime + m_Offset)) * fabs(m_Rate));
+ // else we are effectively stopped and we can't update the time.
+
+ if (m_LoopingDuration == 0)
+ return FALSE;
+
+ unsigned long maxUsefulDuration = m_LoopingDuration * 2;
+ BOOL retval = 0;
+ switch (m_TimePolicyMode) {
+ case TimePolicyModes::StopAtEnd:
+ if (m_LocalTime > m_LoopingDuration) {
+ retval = 1;
+ m_LocalTime = m_LoopingDuration;
+ }
+ break;
+ case TimePolicyModes::Looping:
+ m_LocalTime = m_LocalTime % m_LoopingDuration;
+ break;
+ case TimePolicyModes::Ping:
+ if (m_LocalTime > maxUsefulDuration) {
+ retval = 1;
+ m_LocalTime = maxUsefulDuration;
+ }
+ break;
+ case TimePolicyModes::PingPong:
+ m_LocalTime = m_LocalTime % maxUsefulDuration;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ return retval;
+}
+//==============================================================================
+/**
+ * Compute the local time based off the current global time and the settings
+ * such as duration, repetitions and ping-pong.
+ * @param inTime global time being fed
+ * @return BOOL true if policy has reached it's end time
+ */
+BOOL CTimePolicy::ComputeTime(const TTimeUnit inTime)
+{
+ // Return the last computed time if paused
+ if (m_Paused || m_OffsetInvalid) {
+ // m_LocalTime = inTime + m_Offset;
+ if (fabs(m_Rate) > .001)
+ m_Offset = (TTimeUnit)(m_LocalTime / fabs(m_Rate)) - inTime;
+ else // rate is zero, this should not happen.
+ m_Offset = (TTimeUnit)(m_LocalTime)-inTime;
+ m_OffsetInvalid = 0;
+ return false;
+ }
+ return UpdateTime(inTime);
+}
+
+eastl::pair<BOOL, TTimeUnit>
+SComponentTimePolicyOverride::ComputeLocalTime(CTimePolicy &inTimePolicy, TTimeUnit inGlobalTime)
+{
+ TTimeUnit retval = 0;
+ BOOL finished = FALSE;
+ if (inTimePolicy.m_Paused || inTimePolicy.m_OffsetInvalid) {
+ finished = inTimePolicy.ComputeTime(inGlobalTime);
+ } else {
+ Q3DStudio_ASSERT(m_EndTime <= inTimePolicy.GetLoopingDuration());
+ unsigned long newLocalTime = (unsigned long)(inGlobalTime + inTimePolicy.m_Offset);
+
+ float diff = static_cast<float>(newLocalTime - inTimePolicy.GetTime());
+ diff *= m_TimeMultiplier;
+ float updatedLocalTime = static_cast<float>(inTimePolicy.m_LocalTime) + diff;
+
+ if (updatedLocalTime < 0.0f)
+ updatedLocalTime = 0.0f;
+
+ inTimePolicy.m_LocalTime = static_cast<unsigned long>(updatedLocalTime);
+
+ bool runningBackwards = m_TimeMultiplier < 0;
+
+ if (runningBackwards) {
+ if (inTimePolicy.m_LocalTime <= m_EndTime) {
+ finished = TRUE;
+ inTimePolicy.m_LocalTime = (unsigned long)m_EndTime;
+ }
+ } else // running forwards
+ {
+ if (inTimePolicy.m_LocalTime >= m_EndTime) {
+ finished = TRUE;
+ inTimePolicy.m_LocalTime = (unsigned long)m_EndTime;
+ }
+ }
+ inTimePolicy.m_Offset = inTimePolicy.m_LocalTime - inGlobalTime;
+ }
+ retval = inTimePolicy.GetTime();
+ return eastl::make_pair(finished, retval);
+}
+
+void SComponentTimePolicyOverride::SetTime(CTimePolicy &inTimePolicy, TTimeUnit inLocalTime)
+{
+ if (inLocalTime < 0)
+ inLocalTime = 0;
+ if (m_TimeMultiplier < 0) {
+ if (inLocalTime <= m_EndTime) {
+ inLocalTime = m_EndTime;
+ }
+ } else // running forwards
+ {
+ if (inLocalTime >= m_EndTime) {
+ inLocalTime = m_EndTime;
+ }
+ }
+ inTimePolicy.SetTime(inLocalTime);
+}
+
+} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSTimePolicy.h b/src/runtime/Qt3DSTimePolicy.h
new file mode 100644
index 0000000..6a13719
--- /dev/null
+++ b/src/runtime/Qt3DSTimePolicy.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+#include "foundation/Qt3DSOption.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+struct TimePolicyModes
+{
+ enum Enum {
+ StopAtEnd = 0,
+ Looping,
+ PingPong,
+ Ping,
+ };
+};
+
+//==============================================================================
+/**
+ * Filter time based of parameters such as loop, duration, ping-pong etc.
+ *
+ * The time policy is an agnostic class that transforms time units based on
+ * internal settings and times given to it. Think of it as the minimal
+ * intelligence needed to support the time playback modes we use. It is simply
+ * a function that takes in a source time variable, adjusts it accordingly,
+ * and returns a filtered version.
+ */
+
+QT3DS_ALIGN_PREFIX(4) class CTimePolicy
+{
+ //==============================================================================
+ // Fields and Constants
+ //==============================================================================
+public:
+ const static TTimeUnit FOREVER; ///< Forever repetitions means loop forever, Forever duration
+ ///means infinite duration
+ friend struct SComponentTimePolicyOverride; ///< This class needs to mangle some of our internal
+ ///members to keep everything consistent
+
+protected:
+ // Time policy related ( 20 bytes )
+ unsigned long m_LocalTime; ///< Component current local time; transformed by playmodes
+ unsigned long m_LoopingDuration; ///< Looping duration
+ SAlignedTimeUnit m_Offset; ///< Subtraction modifier
+ FLOAT m_Rate; ///< Time policy rate
+ UINT8 m_TimePadding[12]; ///< Padding to keep the size of the time policy the same.
+
+ UINT8
+ m_TimePolicyMode : 2; ///< Number of durations before halting - FOREVER means infinite reps
+ UINT8 m_Paused : 1; ///< Paused or playing
+ UINT8 m_OffsetInvalid : 1; ///< True if we need to reset our offset
+ UINT8 m_Backward : 1; ///< True if are in 'reverse' mode
+
+ UINT8 m_Unused[3]; ///< (Padding 3 bytes)
+
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ void Initialize(const TTimeUnit inLoopingDuration = FOREVER,
+ TimePolicyModes::Enum inMode = TimePolicyModes::StopAtEnd);
+ void Initialize(const TTimeUnit inLoopingDuration, UINT32 inRepetitions, BOOL inPingPong);
+
+public: // Access Methods
+ FLOAT GetRate() const { return m_Rate; }
+ void SetRate(float inRate) { m_Rate = inRate; }
+
+ bool IsReverse() const { return m_Backward; }
+ void SetReverse(bool inIsReverse) { m_Backward = inIsReverse ? 1 : 0; }
+
+ TTimeUnit GetTime() const;
+ void SetTime(TTimeUnit inTime);
+
+ TTimeUnit GetLoopingDuration() const;
+ void SetLoopingDuration(const TTimeUnit inTime);
+
+ BOOL GetPaused() const;
+ void SetPaused(const BOOL inPaused);
+
+ BOOL GetPlayBackDirection() const;
+
+public: // Time update
+ // The addendum contains extra parameters used that can be specified in a goto-slide command
+ // in the uia file.
+ BOOL ComputeTime(const TTimeUnit inTime);
+
+private:
+ TTimeUnit InternalGetTime() const;
+
+ BOOL UpdateTime(const TTimeUnit inTime);
+} QT3DS_ALIGN_SUFFIX(4);
+
+} // namespace Q3DStudio
diff --git a/src/runtime/RuntimePrefix.h b/src/runtime/RuntimePrefix.h
new file mode 100644
index 0000000..bf729eb
--- /dev/null
+++ b/src/runtime/RuntimePrefix.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include "render/backends/gl/Qt3DSOpenGLPrefix.h"
+
+#ifdef _WIN32
+//==============================================================================
+// DISABLED WARNINGS
+//
+// Note that most of these warnings are tuned off by default by the compiler
+// even at warning level 4. Using option /Wall turns on all warnings and makes
+// even standard Microsoft include files cry. We had to turn off these or
+// turn off warnings individually for each standard include file which was
+// too much work. Point is that /Wall is like Warning Level 5 and this brings
+// it down to about Warning level 4.5, still way above /W4.
+//#pragma warning( disable : 4189 ) // local variable is initialized but not referenced
+#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
+#pragma warning(disable : 4511) // copy constructor could not be generated
+#pragma warning(disable : 4512) // assignment operator could not be generated
+#pragma warning(disable : 4514) // unreferenced inline function has been removed
+#pragma warning(disable : 4619) // #pragma warning : there is no warning number '4619' (in string.h)
+#pragma warning(disable : 4625) // copy constructor could not be generated because a base class copy
+ // constructor is inaccessible
+#pragma warning(disable : 4626) // assignment operator could not be generated because a base class
+ // assignment operator is inaccessible
+#pragma warning( \
+ disable : 4668) // not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
+#pragma warning(disable : 4826) // Conversion from 'const void *' to 'void *' is sign-extended
+#pragma warning(disable : 4996) // _snprintf' was declared deprecated
+#pragma warning(disable : 4711) // function selected for automatic inline expansion
+#pragma warning(disable : 4710) // function not inlined
+#pragma warning( \
+ disable : 4738) // storing 32-bit float result in memory, possible loss of performance
+
+#pragma warning(disable : 4640) // construction of local static object is not thread-safe
+#pragma warning(disable : 4127) // conditional expression is constant
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#endif //_WIN32
+
+//==============================================================================
+// STD INCLUDES - Standard includes MUST come first
+#include <stdlib.h> // Standard functions
+#include <stdio.h> // File and IO
+#ifndef _INTEGRITYPLATFORM
+#include <memory.h> // memset, memcpy
+#endif
+#include <string.h> // strlen
+
+#if defined(_LINUXPLATFORM) || defined(_INTEGRITYPLATFORM)
+#include <stdint.h>
+
+#define UNREFERENCED_PARAMETER(theParam) theParam;
+#endif
+
+//==============================================================================
+// ROOT INCLUDES FOR FRAMEWORK
+//==============================================================================
+#include "Qt3DSTypes.h"
+#include "Qt3DSPlatformSpecific.h"
+#include "Qt3DSAssert.h"
+#include "Qt3DSMacros.h"
+#include "Qt3DSConfig.h"
+#include "Qt3DSMemorySettings.h"
+#include "Qt3DSMemory.h"
+#include "Qt3DSFrameworkTypes.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSTimer.h"
+#include "Qt3DSIScene.h"
+#include "foundation/Qt3DSRefCounted.h" //scoped releasable auto ptr.
+#include "EASTL/hash_map.h"
+#include "EASTL/string.h"
diff --git a/src/runtime/q3dsmaterialdefinitionparser.cpp b/src/runtime/q3dsmaterialdefinitionparser.cpp
new file mode 100644
index 0000000..913a24e
--- /dev/null
+++ b/src/runtime/q3dsmaterialdefinitionparser.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "q3dsmaterialdefinitionparser.h"
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qxmlstream.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qdebug.h>
+
+namespace Q3DStudio {
+
+void Q3DSMaterialDefinitionParser::getMaterialInfo(const QString &materialDefinition,
+ const QString &projPath, const QString &presPath,
+ QString &outName, QMap<QString, QString> &outValues,
+ QMap<QString, QMap<QString, QString>> &outTextureValues)
+{
+ // materialDefinition can be the .materialdef file name or its content
+ QString matDef;
+ if (materialDefinition.endsWith(QLatin1String(".materialdef"), Qt::CaseInsensitive)) {
+ QFile file(materialDefinition);
+ if (!file.open(QFile::Text | QFile::ReadOnly)) {
+ qWarning() << __FUNCTION__ << "Failed to open material definition file:"
+ << file.errorString();
+ return;
+ }
+ QTextStream stream(&file);
+ matDef = stream.readAll();
+ } else {
+ matDef = materialDefinition;
+ }
+
+ QXmlStreamReader reader(matDef);
+ reader.setNamespaceProcessing(false);
+
+ const QDir docDir(presPath);
+ const QDir projDir(projPath);
+ QString textureName;
+ QString propertyName;
+ QString propertyType;
+ QString propertyValue;
+ QMap<QString, QString> textureProperties;
+
+ const QString matDataVersion = QStringLiteral("1.0");
+ const QString sourcePathStr = QStringLiteral("sourcepath");
+ const QString nameStr = QStringLiteral("name");
+ const QString typeStr = QStringLiteral("type");
+ const QString versionStr = QStringLiteral("version");
+
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.tokenType() == QXmlStreamReader::StartElement) {
+ if (reader.name() == QLatin1String("TextureData")) {
+ textureName = reader.attributes().value(nameStr).toString();
+ textureProperties.clear();
+ } else if (reader.name() == QLatin1String("Property")) {
+ propertyName = reader.attributes().value(nameStr).toString();
+ propertyType = reader.attributes().value(typeStr).toString();
+ propertyValue.clear();
+ } else if (reader.name() == QLatin1String("MaterialData")) {
+ QString version = reader.attributes().value(versionStr).toString();
+ if (version != matDataVersion) {
+ qWarning() << __FUNCTION__ << "Invalid MaterialData version; expected '"
+ << matDataVersion << "', got '" << version << "'";
+ outName.clear();
+ outValues.clear();
+ outTextureValues.clear();
+ return;
+ }
+ }
+ } else if (reader.tokenType() == QXmlStreamReader::EndElement) {
+ if (reader.name() == QLatin1String("TextureData")) {
+ outTextureValues[textureName] = textureProperties;
+ textureName.clear();
+ } else if (reader.name() == QLatin1String("Property")) {
+ if (textureName.isEmpty()) {
+ outValues[propertyName] = propertyValue;
+ if (propertyName == nameStr)
+ outName = propertyValue;
+ } else {
+ textureProperties[propertyName] = propertyValue;
+ }
+ propertyName.clear();
+ propertyType.clear();
+ }
+ } else if (reader.tokenType() == QXmlStreamReader::Characters) {
+ if (!propertyName.isEmpty()) {
+ propertyValue = reader.text().toString();
+ if (propertyName == sourcePathStr) {
+ // Check that the referenced file exists
+ const auto absSourcePath = projDir.absoluteFilePath(propertyValue);
+ if (!QFileInfo(absSourcePath).exists()) {
+ qWarning() << __FUNCTION__ << "Referenced file doesn't exist:"
+ << propertyValue;
+ outName.clear();
+ outValues.clear();
+ outTextureValues.clear();
+ return;
+ }
+ }
+ if (!propertyValue.isEmpty() && (propertyType == QLatin1String("Texture")
+ || propertyName == sourcePathStr)) {
+ propertyValue = docDir.relativeFilePath(
+ projDir.absoluteFilePath(propertyValue));
+ }
+ }
+ }
+ }
+}
+
+}
diff --git a/src/runtime/q3dsmaterialdefinitionparser.h b/src/runtime/q3dsmaterialdefinitionparser.h
new file mode 100644
index 0000000..f6d98bb
--- /dev/null
+++ b/src/runtime/q3dsmaterialdefinitionparser.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 Q3DSMATERIALDEFINITIONPARSER_H
+#define Q3DSMATERIALDEFINITIONPARSER_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qmap.h>
+
+namespace Q3DStudio {
+
+class Q3DSMaterialDefinitionParser
+{
+public:
+ static void getMaterialInfo(const QString &materialDefinition,
+ const QString &projPath, const QString &presPath,
+ QString &outName, QMap<QString, QString> &outValues,
+ QMap<QString, QMap<QString, QString>> &outTextureValues);
+};
+
+}
+
+#endif
diff --git a/src/runtime/q3dsqmlbehavior.cpp b/src/runtime/q3dsqmlbehavior.cpp
new file mode 100644
index 0000000..83b706e
--- /dev/null
+++ b/src/runtime/q3dsqmlbehavior.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include "q3dsqmlbehavior.h"
+#include "q3dsqmlscript.h"
+
+namespace Q3DStudio {
+
+Q3DSQmlBehavior::Q3DSQmlBehavior(QObject *parent)
+ : QObject(parent), m_script(nullptr)
+{
+
+}
+
+void Q3DSQmlBehavior::setScript(Q3DSQmlScript *script)
+{
+ m_script = script;
+}
+
+float Q3DSQmlBehavior::getDeltaTime()
+{
+ return m_script->getDeltaTime();
+}
+
+float Q3DSQmlBehavior::getAttribute(const QString &attribute)
+{
+ return m_script->getAttribute(attribute);
+}
+
+void Q3DSQmlBehavior::setAttribute(const QString &attribute, const QVariant &value)
+{
+ m_script->setAttribute(attribute, value);
+}
+
+void Q3DSQmlBehavior::setAttribute(const QString &handle, const QString &attribute,
+ const QVariant &value)
+{
+ m_script->setAttribute(handle, attribute, value);
+}
+
+void Q3DSQmlBehavior::fireEvent(const QString &event)
+{
+ m_script->fireEvent(event);
+}
+
+void Q3DSQmlBehavior::registerForEvent(const QString &event, const QJSValue &function)
+{
+ m_script->registerForEvent(event, function);
+}
+
+void Q3DSQmlBehavior::registerForEvent(const QString &handle, const QString &event,
+ const QJSValue &function)
+{
+ m_script->registerForEvent(handle, event, function);
+}
+
+void Q3DSQmlBehavior::unregisterForEvent(const QString &event)
+{
+ m_script->unregisterForEvent(event);
+}
+
+void Q3DSQmlBehavior::unregisterForEvent(const QString &handle, const QString &event)
+{
+ m_script->unregisterForEvent(handle, event);
+}
+
+QVector2D Q3DSQmlBehavior::getMousePosition()
+{
+ return m_script->getMousePosition();
+}
+
+QMatrix4x4 Q3DSQmlBehavior::calculateGlobalTransform(const QString &handle)
+{
+ return m_script->calculateGlobalTransform(handle);
+}
+
+QVector3D Q3DSQmlBehavior::lookAt(const QVector3D &target)
+{
+ return m_script->lookAt(target);
+}
+
+QVector3D Q3DSQmlBehavior::matrixToEuler(const QMatrix4x4 &matrix)
+{
+ return m_script->matrixToEuler(matrix);
+}
+
+QString Q3DSQmlBehavior::getParent(const QString &handle)
+{
+ return m_script->getParent(handle);
+}
+
+void Q3DSQmlBehavior::setDataInputValue(const QString &name, const QVariant &value)
+{
+ return m_script->setDataInputValue(name, value);
+}
+
+}
diff --git a/src/runtime/q3dsqmlbehavior.h b/src/runtime/q3dsqmlbehavior.h
new file mode 100644
index 0000000..76f4a4a
--- /dev/null
+++ b/src/runtime/q3dsqmlbehavior.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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 Q3DSQMLBEHAVIOR_H
+#define Q3DSQMLBEHAVIOR_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+#include <QtQml/qjsvalue.h>
+#include <QtGui/qvector2d.h>
+#include <QtGui/qvector3d.h>
+#include <QtGui/qmatrix4x4.h>
+
+namespace Q3DStudio {
+
+class Q3DSQmlScript;
+
+class Q3DSQmlBehavior : public QObject
+{
+ Q_OBJECT
+public:
+ Q3DSQmlBehavior(QObject *parent = nullptr);
+
+ void setScript(Q3DSQmlScript *script);
+
+ Q_INVOKABLE float getDeltaTime();
+ Q_INVOKABLE float getAttribute(const QString &attribute);
+ Q_INVOKABLE void setAttribute(const QString &attribute, const QVariant &value);
+ Q_INVOKABLE void setAttribute(const QString &handle, const QString &attribute,
+ const QVariant &value);
+ Q_INVOKABLE void fireEvent(const QString &event);
+ Q_INVOKABLE void registerForEvent(const QString &event, const QJSValue &function);
+ Q_INVOKABLE void registerForEvent(const QString &handle, const QString &event,
+ const QJSValue &function);
+ Q_INVOKABLE void unregisterForEvent(const QString &event);
+ Q_INVOKABLE void unregisterForEvent(const QString &handle, const QString &event);
+ Q_INVOKABLE QVector2D getMousePosition();
+ Q_INVOKABLE QMatrix4x4 calculateGlobalTransform(const QString &handle = QString());
+ Q_INVOKABLE QVector3D lookAt(const QVector3D &target);
+ Q_INVOKABLE QVector3D matrixToEuler(const QMatrix4x4 &matrix);
+ Q_INVOKABLE QString getParent(const QString &handle = QString());
+ Q_REVISION(1) Q_INVOKABLE void setDataInputValue(const QString &name, const QVariant &value);
+
+
+Q_SIGNALS:
+ void initialize();
+ void activate();
+ void update();
+ void deactivate();
+
+private:
+ Q3DSQmlScript *m_script;
+};
+
+}
+
+#endif
diff --git a/src/runtime/q3dsqmlscript.cpp b/src/runtime/q3dsqmlscript.cpp
new file mode 100644
index 0000000..725c752
--- /dev/null
+++ b/src/runtime/q3dsqmlscript.cpp
@@ -0,0 +1,742 @@
+/****************************************************************************
+**
+** 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 "q3dsqmlscript.h"
+
+#include "Qt3DSQmlEngine.h"
+#include "Qt3DSElementSystem.h"
+#include "Qt3DSApplication.h"
+#include "Qt3DSPresentation.h"
+#include "Qt3DSInputEngine.h"
+#include "Qt3DSInputFrame.h"
+#include "Qt3DSQmlElementHelper.h"
+#include "Qt3DSHash.h"
+#include "Qt3DSEulerAngles.h"
+#include "Qt3DSMathUtils.h"
+
+#include <QMetaProperty>
+
+using namespace Q3DStudio;
+using namespace qt3ds::runtime;
+
+QJSValue argToQJSValue(qt3ds::foundation::IStringTable &strTable,
+ Q3DStudio::UINT8 type, const UVariant &value) {
+ switch (type) {
+ case ATTRIBUTETYPE_INT32:
+ case ATTRIBUTETYPE_HASH:
+ return QJSValue(value.m_INT32);
+ case ATTRIBUTETYPE_FLOAT:
+ return QJSValue(value.m_FLOAT);
+ case ATTRIBUTETYPE_BOOL:
+ return QJSValue(value.m_INT32 != 0);
+ case ATTRIBUTETYPE_STRING:
+ return QJSValue(strTable.HandleToStr(value.m_StringHandle));
+ case ATTRIBUTETYPE_NONE:
+ return QJSValue();
+ default:
+ qCCritical(qt3ds::INVALID_OPERATION)
+ << "Pushed event argument value of unknown type: "<< type;
+ }
+ return QJSValue();
+}
+
+void eventCallback(void *contextData, SEventCommand &event)
+{
+ auto data = static_cast<Q3DSQmlScript::EventData *>(contextData);
+ qt3ds::foundation::IStringTable &strTable(
+ event.m_Target->GetBelongedPresentation()->GetStringTable());
+ QJSValue arg1 = argToQJSValue(strTable, event.m_Arg1Type, event.m_Arg1);
+ QJSValue arg2 = argToQJSValue(strTable, event.m_Arg2Type, event.m_Arg2);
+ data->function.call({arg1, arg2});
+}
+
+Q3DSQmlScript::Q3DSQmlScript(CQmlEngine &api, Q3DSQmlBehavior &object,
+ TElement &behavior, TElement &owner)
+ : QObject(nullptr)
+ , m_api(api)
+ , m_object(object)
+ , m_behavior(behavior)
+ , m_owner(owner)
+ , m_initialized(false)
+ , m_lastActivationState(false)
+ , m_deltaTime(0.0f)
+ , m_lastTime(0)
+{
+ qt3ds::foundation::IStringTable &strTable
+ = m_behavior.GetBelongedPresentation()->GetStringTable();
+ const QMetaObject *meta = object.metaObject();
+ for (int i = 0; i < meta->propertyCount(); ++i) {
+ QMetaProperty property = meta->property(i);
+ QVariant::Type t = property.type();
+ if (static_cast<QMetaType::Type>(t) == QMetaType::QVariant) {
+ // detect type from behavior property
+ auto nameHash = CHash::HashAttribute(property.name());
+ Option<element::TPropertyDescAndValuePtr> value = behavior.FindProperty(nameHash);
+ if (value.hasValue()) {
+ switch (value->first.m_Type) {
+ case ATTRIBUTETYPE_INT32:
+ case ATTRIBUTETYPE_HASH:
+ t = QVariant::Int;
+ break;
+ case ATTRIBUTETYPE_FLOAT:
+ t = QVariant::Double;
+ break;
+ case ATTRIBUTETYPE_BOOL:
+ t = QVariant::Bool;
+ break;
+ case ATTRIBUTETYPE_STRING:
+ t = QVariant::String;
+ break;
+ case ATTRIBUTETYPE_FLOAT4:
+ t = QVariant::Vector4D;
+ break;
+ case ATTRIBUTETYPE_FLOAT3:
+ t = QVariant::Vector3D;
+ break;
+ case ATTRIBUTETYPE_FLOAT2:
+ t = QVariant::Vector2D;
+ break;
+ default:
+ break;
+ }
+ } else {
+ // detect vectors
+ QByteArray name(property.name());
+ QByteArray xname = name + QByteArrayLiteral(".x");
+ Option<element::TPropertyDescAndValuePtr> xvalue
+ = behavior.FindProperty(CHash::HashAttribute(xname.data()));
+ QByteArray rname = name + QByteArrayLiteral(".r");
+ Option<element::TPropertyDescAndValuePtr> rvalue
+ = behavior.FindProperty(CHash::HashAttribute(rname.data()));
+ if (xvalue.hasValue()) {
+ QByteArray yname = name + QByteArrayLiteral(".y");
+ QByteArray zname = name + QByteArrayLiteral(".z");
+ QByteArray wname = name + QByteArrayLiteral(".w");
+
+ Option<element::TPropertyDescAndValuePtr> yvalue
+ = behavior.FindProperty(CHash::HashAttribute(yname.data()));
+ Option<element::TPropertyDescAndValuePtr> zvalue
+ = behavior.FindProperty(CHash::HashAttribute(zname.data()));
+ Option<element::TPropertyDescAndValuePtr> wvalue
+ = behavior.FindProperty(CHash::HashAttribute(wname.data()));
+ int count = 1;
+ count += yvalue.hasValue() ? 1 : 0;
+ count += zvalue.hasValue() ? 1 : 0;
+ count += wvalue.hasValue() ? 1 : 0;
+ if (count == 2)
+ t = QVariant::Vector2D;
+ else if (count == 3)
+ t = QVariant::Vector3D;
+ else if (count == 4)
+ t = QVariant::Vector4D;
+ } else if (rvalue.hasValue()) {
+ QByteArray gname = name + QByteArrayLiteral(".g");
+ QByteArray bname = name + QByteArrayLiteral(".b");
+ QByteArray aname = name + QByteArrayLiteral(".a");
+ Option<element::TPropertyDescAndValuePtr> gvalue
+ = behavior.FindProperty(CHash::HashAttribute(gname.data()));
+ Option<element::TPropertyDescAndValuePtr> bvalue
+ = behavior.FindProperty(CHash::HashAttribute(bname.data()));
+ Option<element::TPropertyDescAndValuePtr> avalue
+ = behavior.FindProperty(CHash::HashAttribute(aname.data()));
+ int count = 1;
+ count += gvalue.hasValue() ? 1 : 0;
+ count += bvalue.hasValue() ? 1 : 0;
+ count += avalue.hasValue() ? 1 : 0;
+ if (count == 2)
+ t = QVariant::Vector2D;
+ else if (count == 3)
+ t = QVariant::Vector3D;
+ else if (count == 4)
+ t = QVariant::Vector4D;
+ }
+ }
+ }
+ switch (t) {
+ case QVariant::Bool: {
+ const char *name = property.name();
+ auto nameHash = CHash::HashAttribute(name);
+ Option<element::TPropertyDescAndValuePtr> value = behavior.FindProperty(nameHash);
+ if (value.hasValue()) {
+ std::function<void()> mapper = [&object, property, value]() -> void {
+ Q3DStudio::UVariant *val = value->second;
+ property.write(&object, QVariant::fromValue<bool>(val->m_INT32 > 0));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ } break;
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::LongLong:
+ case QVariant::ULongLong: {
+ const char *name = property.name();
+ auto nameHash = CHash::HashAttribute(name);
+ Option<element::TPropertyDescAndValuePtr> value = behavior.FindProperty(nameHash);
+ if (value.hasValue()) {
+ std::function<void()> mapper = [&object, property, value]() {
+ Q3DStudio::UVariant *val = value->second;
+ property.write(&object, QVariant::fromValue(val->m_INT32));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ } break;
+ case QVariant::Double: {
+ const char *name = property.name();
+ auto nameHash = CHash::HashAttribute(name);
+ Option<element::TPropertyDescAndValuePtr> value = behavior.FindProperty(nameHash);
+ if (value.hasValue()) {
+ std::function<void()> mapper = [&object, property, value]() {
+ Q3DStudio::UVariant *val = value->second;
+ property.write(&object, QVariant::fromValue(val->m_FLOAT));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ } break;
+ case QVariant::String: {
+ const char *name = property.name();
+ auto nameHash = CHash::HashAttribute(name);
+ Option<element::TPropertyDescAndValuePtr> value = behavior.FindProperty(nameHash);
+ if (value.hasValue()) {
+ std::function<void()> mapper = [&object, property, value, &strTable]() {
+ Q3DStudio::UVariant *val = value->second;
+ property.write(&object, QVariant::fromValue(
+ QString::fromUtf8(strTable.HandleToStr(val->m_StringHandle).c_str())));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ } break;
+ case QVariant::Vector2D: {
+ std::function<void()> mapper;
+ auto nameHash = CHash::HashAttribute(property.name());
+ Option<element::TPropertyDescAndValuePtr> prop = behavior.FindProperty(nameHash);
+ if (prop.hasValue() && prop->first.m_Type == Q3DStudio::ATTRIBUTETYPE_FLOAT2) {
+ mapper = [&object, property, prop]() {
+ QVector2D vec;
+ Q3DStudio::UVariant *value = prop->second;
+ vec.setX(value->m_FLOAT3[0]);
+ vec.setY(value->m_FLOAT3[1]);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ } else {
+ QByteArray name(property.name());
+ QByteArray cname = name + QByteArrayLiteral(".x");
+ auto nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> xvalue = behavior.FindProperty(nameHash);
+ if (xvalue.hasValue()) {
+ cname = name + QByteArrayLiteral(".y");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> yvalue
+ = behavior.FindProperty(nameHash);
+
+ if (xvalue.hasValue() && yvalue.hasValue()) {
+ mapper = [&object, property, xvalue, yvalue]() {
+ QVector2D vec;
+ vec.setX(xvalue->second->m_FLOAT);
+ vec.setY(yvalue->second->m_FLOAT);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ }
+ }
+ } break;
+ case QVariant::Vector3D: {
+ std::function<void()> mapper;
+ auto nameHash = CHash::HashAttribute(property.name());
+ Option<element::TPropertyDescAndValuePtr> prop = behavior.FindProperty(nameHash);
+ if (prop.hasValue() && prop->first.m_Type == Q3DStudio::ATTRIBUTETYPE_FLOAT3) {
+ mapper = [&object, property, prop]() {
+ QVector3D vec;
+ Q3DStudio::UVariant *value = prop->second;
+ vec.setX(value->m_FLOAT3[0]);
+ vec.setY(value->m_FLOAT3[1]);
+ vec.setZ(value->m_FLOAT3[2]);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ } else {
+ QByteArray name(property.name());
+ QByteArray cname = name + QByteArrayLiteral(".x");
+ auto nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> xvalue = behavior.FindProperty(nameHash);
+ if (xvalue.hasValue()) {
+ cname = name + QByteArrayLiteral(".y");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> yvalue
+ = behavior.FindProperty(nameHash);
+ cname = name + QByteArrayLiteral(".z");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> zvalue
+ = behavior.FindProperty(nameHash);
+
+ if (xvalue.hasValue() && yvalue.hasValue() && zvalue.hasValue()) {
+ mapper = [&object, property, xvalue, yvalue, zvalue]() {
+ QVector3D vec;
+ vec.setX(xvalue->second->m_FLOAT);
+ vec.setY(yvalue->second->m_FLOAT);
+ vec.setZ(zvalue->second->m_FLOAT);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ }
+ }
+ } break;
+ case QVariant::Color:
+ case QVariant::Vector4D: {
+ std::function<void()> mapper;
+ auto nameHash = CHash::HashAttribute(property.name());
+ Option<element::TPropertyDescAndValuePtr> prop = behavior.FindProperty(nameHash);
+ if (prop.hasValue() && prop->first.m_Type == Q3DStudio::ATTRIBUTETYPE_FLOAT4) {
+ mapper = [&object, property, prop]() {
+ QVector4D vec;
+ Q3DStudio::UVariant *value = prop->second;
+ vec.setX(value->m_FLOAT4[0]);
+ vec.setY(value->m_FLOAT4[1]);
+ vec.setZ(value->m_FLOAT4[2]);
+ vec.setW(value->m_FLOAT4[3]);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ } else {
+ QByteArray name(property.name());
+ QByteArray cname = name + QByteArrayLiteral(".x");
+ auto nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> xvalue = behavior.FindProperty(nameHash);
+ if (xvalue.hasValue()) {
+ cname = name + QByteArrayLiteral(".y");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> yvalue
+ = behavior.FindProperty(nameHash);
+ cname = name + QByteArrayLiteral(".z");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> zvalue
+ = behavior.FindProperty(nameHash);
+ cname = name + QByteArrayLiteral(".w");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> wvalue
+ = behavior.FindProperty(nameHash);
+
+ if (xvalue.hasValue() && yvalue.hasValue() && zvalue.hasValue()
+ && wvalue.hasValue()) {
+ mapper = [&object, property, xvalue, yvalue, zvalue, wvalue]() {
+ QVector4D vec;
+ vec.setX(xvalue->second->m_FLOAT);
+ vec.setY(yvalue->second->m_FLOAT);
+ vec.setZ(zvalue->second->m_FLOAT);
+ vec.setW(wvalue->second->m_FLOAT);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ } else {
+ cname = name + QByteArrayLiteral(".r");
+ auto nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> rvalue
+ = behavior.FindProperty(nameHash);
+ if (rvalue.hasValue()) {
+ cname = name + QByteArrayLiteral(".g");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> gvalue
+ = behavior.FindProperty(nameHash);
+ cname = name + QByteArrayLiteral(".b");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> bvalue
+ = behavior.FindProperty(nameHash);
+ cname = name + QByteArrayLiteral(".a");
+ nameHash = CHash::HashAttribute(cname.data());
+ Option<element::TPropertyDescAndValuePtr> avalue
+ = behavior.FindProperty(nameHash);
+
+ if (rvalue.hasValue() && gvalue.hasValue() && bvalue.hasValue()
+ && avalue.hasValue()) {
+ mapper = [&object, property, rvalue, gvalue, bvalue, avalue]() {
+ QVector4D vec;
+ vec.setX(rvalue->second->m_FLOAT);
+ vec.setY(gvalue->second->m_FLOAT);
+ vec.setZ(bvalue->second->m_FLOAT);
+ vec.setW(avalue->second->m_FLOAT);
+ property.write(&object, QVariant::fromValue(vec));
+ };
+ m_mappedProperties.push_back(mapper);
+ }
+ }
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
+Q3DSQmlScript::~Q3DSQmlScript()
+{
+ for (const EventCallbackInfo &callback : m_eventCallbacks)
+ delete callback.data;
+}
+
+void Q3DSQmlScript::update()
+{
+ updateProperties();
+
+ bool active = m_behavior.GetActive();
+
+ if (active && !m_initialized) {
+ m_initialized = true;
+ Q_EMIT m_object.initialize();
+ }
+
+ TTimeUnit time = static_cast<CPresentation *>(m_behavior.GetBelongedPresentation())->GetTime();
+ m_deltaTime = (time - m_lastTime) / 1000.0f;
+ m_lastTime = time;
+
+ if (m_lastActivationState != active) {
+ if (active)
+ Q_EMIT m_object.activate();
+ else
+ Q_EMIT m_object.deactivate();
+ }
+ m_lastActivationState = active;
+ if (active)
+ Q_EMIT m_object.update();
+}
+
+void Q3DSQmlScript::call(const QString &function)
+{
+ const QMetaObject *meta = m_object.metaObject();
+ const QString normalized = function + "()";
+ if (meta->indexOfMethod(normalized.toUtf8().constData()) != -1)
+ QMetaObject::invokeMethod(&m_object, function.toUtf8().constData());
+}
+
+void Q3DSQmlScript::updateProperties()
+{
+ using namespace qt3ds::foundation;
+ using namespace qt3ds::runtime::element;
+
+ if (!m_behavior.GetActive() || !m_behavior.IsDirty())
+ return;
+
+ for (auto m : qAsConst(m_mappedProperties))
+ m();
+}
+
+bool Q3DSQmlScript::hasBehavior(const TElement *behavior)
+{
+ return &m_behavior == behavior;
+}
+
+float Q3DSQmlScript::getDeltaTime()
+{
+ return m_deltaTime;
+}
+
+float Q3DSQmlScript::getAttribute(const QString &attribute)
+{
+ if (!m_behavior.GetActive())
+ return 0;
+
+ float floatValue = 0;
+ m_api.GetAttribute(&m_owner,
+ attribute.toUtf8().constData(),
+ (char *)&floatValue);
+ return floatValue;
+}
+
+void Q3DSQmlScript::setAttribute(const QString &attribute, const QVariant &value)
+{
+ setAttribute("", attribute, value);
+}
+
+void Q3DSQmlScript::setAttribute(const QString &handle, const QString &attribute,
+ const QVariant &value)
+{
+ if (!m_behavior.GetActive())
+ return;
+
+ TElement *element = &m_owner;
+ if (!handle.isEmpty())
+ element = getElementByPath(handle);
+ if (!element)
+ return;
+
+ QByteArray valueStr;
+ float valueFloat;
+
+ const char *valuePtr = nullptr;
+ switch (static_cast<QMetaType::Type>(value.type())) {
+ case QMetaType::Bool:
+ case QMetaType::Int:
+ case QMetaType::Double:
+ case QMetaType::Float:
+ valueFloat = value.toFloat();
+ valuePtr = reinterpret_cast<const char *>(&valueFloat);
+ break;
+ case QMetaType::QVector2D: {
+ QVector2D vec = value.value<QVector2D>();
+ float val[2];
+ val[0] = vec.x();
+ val[1] = vec.y();
+ const QByteArray name = attribute.toUtf8();
+ QByteArray cname = name + QByteArrayLiteral(".x");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val));
+ cname = name + QByteArrayLiteral(".y");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val + 1));
+ return;
+ }
+ case QMetaType::QVector3D: {
+ QVector3D vec = value.value<QVector3D>();
+ float val[3];
+ val[0] = vec.x();
+ val[1] = vec.y();
+ val[2] = vec.z();
+ const QByteArray name = attribute.toUtf8();
+ QByteArray cname = name + QByteArrayLiteral(".x");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val));
+ cname = name + QByteArrayLiteral(".y");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val + 1));
+ cname = name + QByteArrayLiteral(".z");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val + 2));
+ return;
+ }
+ case QMetaType::QColor:
+ case QMetaType::QVector4D: {
+ QVector4D vec = value.value<QVector4D>();
+ float val[4];
+ val[0] = vec.x();
+ val[1] = vec.y();
+ val[2] = vec.z();
+ val[3] = vec.w();
+ const QByteArray name = attribute.toUtf8();
+ QByteArray cname = name + QByteArrayLiteral(".x");
+ if (m_api.GetAttribute(element, cname.constData(), reinterpret_cast<char *>(&valueFloat))) {
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val));
+ cname = name + QByteArrayLiteral(".y");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val + 1));
+ cname = name + QByteArrayLiteral(".z");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val + 2));
+ cname = name + QByteArrayLiteral(".w");
+ m_api.SetAttribute(element, cname.constData(), reinterpret_cast<const char *>(val + 3));
+ } else {
+ QByteArray cname = name + QByteArrayLiteral(".r");
+ if (m_api.GetAttribute(element, cname.constData(),
+ reinterpret_cast<char *>(&valueFloat))) {
+ m_api.SetAttribute(element, cname.constData(),
+ reinterpret_cast<const char *>(val));
+ cname = name + QByteArrayLiteral(".g");
+ m_api.SetAttribute(element, cname.constData(),
+ reinterpret_cast<const char *>(val + 1));
+ cname = name + QByteArrayLiteral(".b");
+ m_api.SetAttribute(element, cname.constData(),
+ reinterpret_cast<const char *>(val + 2));
+ cname = name + QByteArrayLiteral(".a");
+ m_api.SetAttribute(element, cname.constData(),
+ reinterpret_cast<const char *>(val + 3));
+ }
+ }
+ return;
+ }
+ case QMetaType::QString:
+ default:
+ valueStr = value.toString().toUtf8();
+ valuePtr = valueStr.constData();
+ break;
+ }
+
+ m_api.SetAttribute(element, attribute.toUtf8().constData(), valuePtr);
+}
+
+void Q3DSQmlScript::fireEvent(const QString &event)
+{
+ if (!m_behavior.GetActive())
+ return;
+ m_api.FireEvent(m_behavior.m_Path, event.toUtf8().constData());
+}
+
+void Q3DSQmlScript::registerForEvent(const QString &event, const QJSValue &function)
+{
+ registerForEvent("", event, function);
+}
+
+void Q3DSQmlScript::registerForEvent(const QString &handle, const QString &event,
+ const QJSValue &function)
+{
+ if (!m_behavior.GetActive())
+ return;
+
+ TElement *element = &m_owner;
+ if (!handle.isEmpty())
+ element = getElementByPath(handle);
+ if (!element)
+ return;
+
+ if (!function.isCallable())
+ return;
+
+ CPresentation *presentation
+ = static_cast<CPresentation *>(m_behavior.GetBelongedPresentation());
+ TEventCommandHash eventHash = CHash::HashEventCommand(event.toUtf8().constData());
+
+ for (auto &&callback : m_eventCallbacks) {
+ if (callback.element == element && callback.eventHash == eventHash)
+ return;
+ }
+
+ EventData *data = new EventData();
+ data->function = function;
+
+ m_eventCallbacks.push_back({element, eventHash, data});
+
+ presentation->RegisterEventCallback(element, eventHash, &eventCallback, data);
+}
+
+void Q3DSQmlScript::unregisterForEvent(const QString &event)
+{
+ unregisterForEvent("", event);
+}
+
+void Q3DSQmlScript::unregisterForEvent(const QString &handle, const QString &event)
+{
+ if (!m_behavior.GetActive())
+ return;
+
+ TElement *element = &m_owner;
+ if (!handle.isEmpty())
+ element = getElementByPath(handle);
+ if (!element)
+ return;
+
+ CPresentation *presentation
+ = static_cast<CPresentation *>(m_behavior.GetBelongedPresentation());
+ TEventCommandHash eventHash = CHash::HashEventCommand(event.toUtf8().constData());
+ EventData *data = nullptr;
+
+ for (int i = 0; i < m_eventCallbacks.size(); ++i) {
+ if (m_eventCallbacks[i].element == element && m_eventCallbacks[i].eventHash == eventHash) {
+ data = m_eventCallbacks[i].data;
+ m_eventCallbacks.erase(m_eventCallbacks.begin() + i);
+ break;
+ }
+ }
+
+ if (data) {
+ presentation->UnregisterEventCallback(element, eventHash, &eventCallback, data);
+ delete data;
+ }
+}
+
+QVector2D Q3DSQmlScript::getMousePosition()
+{
+ CPresentation *presentation
+ = static_cast<CPresentation *>(m_behavior.GetBelongedPresentation());
+ qt3ds::runtime::IApplication &app = presentation->GetApplication();
+
+ SInputFrame input = app.GetInputEngine().GetInputFrame();
+ if (app.GetPrimaryPresentation()) {
+ Q3DStudio::SMousePosition position =
+ app.GetPrimaryPresentation()->GetScene()->WindowToPresentation(
+ Q3DStudio::SMousePosition(static_cast<INT32>(input.m_PickX),
+ static_cast<INT32>(input.m_PickY)));
+
+ return QVector2D(position.m_X, position.m_Y);
+ }
+ return QVector2D(0, 0);
+}
+
+QMatrix4x4 Q3DSQmlScript::calculateGlobalTransform(const QString &handle)
+{
+ TElement *element = &m_owner;
+ if (!handle.isEmpty())
+ element = getElementByPath(handle);
+ if (!element)
+ return QMatrix4x4();
+
+ RuntimeMatrix transform;
+ IScene *scene = element->GetBelongedPresentation()->GetScene();
+ scene->CalculateGlobalTransform(element, transform);
+ transform.FlipCoordinateSystem();
+
+ return QMatrix4x4(&transform.m_Data[0][0]);
+}
+
+QVector3D Q3DSQmlScript::lookAt(const QVector3D &target)
+{
+ FLOAT theMag = ::sqrtf(target.x() * target.x() + target.z() * target.z());
+ FLOAT thePitch = -::atan2f(target.y(), theMag);
+ FLOAT theYaw = ::atan2f(target.x(), target.z());
+
+ return QVector3D(radToDeg(thePitch), radToDeg(theYaw), 0.0f);
+}
+
+QVector3D Q3DSQmlScript::matrixToEuler(const QMatrix4x4 &matrix)
+{
+ CEulerAngleConverter converter;
+ const float *qMatrix = matrix.constData();
+ HMatrix hHatrix;
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j)
+ hHatrix[i][j] = qMatrix[j * 4 + i];
+ }
+ EulerAngles eulerAngles = converter.Eul_FromHMatrix(hHatrix, EulOrdYXZs);
+ return QVector3D(-eulerAngles.y, -eulerAngles.x, -eulerAngles.z);
+}
+
+QString Q3DSQmlScript::getParent(const QString &handle)
+{
+ TElement *element = &m_owner;
+ if (!handle.isEmpty())
+ element = getElementByPath(handle);
+ if (!element)
+ return "";
+
+ TElement *parent = element->GetParent();
+ if (!parent)
+ return "";
+ return parent->m_Path.c_str();
+}
+
+void Q3DSQmlScript::setDataInputValue(const QString &name, const QVariant &value,
+ qt3ds::runtime::DataInputValueRole valueRole)
+{
+ m_api.SetDataInputValue(name, value, valueRole);
+}
+
+TElement *Q3DSQmlScript::getElementByPath(const QString &path)
+{
+ if (!m_api.GetApplication())
+ return nullptr;
+
+ TElement *element = CQmlElementHelper::GetElement(
+ *m_api.GetApplication(),
+ m_api.GetApplication()->GetPrimaryPresentation(),
+ path.toUtf8().constData(), &m_owner);
+ return element;
+}
diff --git a/src/runtime/q3dsqmlscript.h b/src/runtime/q3dsqmlscript.h
new file mode 100644
index 0000000..3ad1118
--- /dev/null
+++ b/src/runtime/q3dsqmlscript.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** 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 Q3DSQMLSCRIPT_H
+#define Q3DSQMLSCRIPT_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+#include <QtQml/qjsvalue.h>
+#include <QtGui/qvector2d.h>
+#include <QtGui/qvector3d.h>
+#include <QtGui/qmatrix4x4.h>
+
+#include "Qt3DSTypes.h"
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSEvent.h"
+#include "q3dsqmlbehavior.h"
+#include "Qt3DSApplication.h"
+
+namespace Q3DStudio {
+class CQmlEngine;
+
+class Q3DSQmlScript : public QObject
+{
+ Q_OBJECT
+public:
+ Q3DSQmlScript(CQmlEngine &api, Q3DSQmlBehavior &object, TElement &element, TElement &parent);
+ ~Q3DSQmlScript();
+
+ void update();
+ void call(const QString &function);
+ void updateProperties();
+ bool hasBehavior(const TElement *behavior);
+
+ float getDeltaTime();
+ float getAttribute(const QString &attribute);
+ void setAttribute(const QString &attribute, const QVariant &value);
+ void setAttribute(const QString &handle, const QString &attribute,
+ const QVariant &value);
+ void fireEvent(const QString &event);
+ void registerForEvent(const QString &event, const QJSValue &function);
+ void registerForEvent(const QString &handle, const QString &event,
+ const QJSValue &function);
+ void unregisterForEvent(const QString &event);
+ void unregisterForEvent(const QString &handle, const QString &event);
+ QVector2D getMousePosition();
+ QMatrix4x4 calculateGlobalTransform(const QString &handle);
+ QVector3D lookAt(const QVector3D &target);
+ QVector3D matrixToEuler(const QMatrix4x4 &matrix);
+ QString getParent(const QString &handle);
+ void setDataInputValue(const QString &name, const QVariant &value,
+ qt3ds::runtime::DataInputValueRole valueRole
+ = qt3ds::runtime::DataInputValueRole::Value);
+
+ struct EventData {
+ QJSValue function;
+ };
+
+private:
+ TElement *getElementByPath(const QString &path);
+
+ CQmlEngine &m_api;
+ Q3DSQmlBehavior &m_object;
+ TElement &m_behavior;
+ TElement &m_owner;
+
+ bool m_initialized;
+ bool m_lastActivationState;
+
+ float m_deltaTime;
+ TTimeUnit m_lastTime;
+ struct EventCallbackInfo {
+ TElement *element;
+ TEventCommandHash eventHash;
+ EventData *data;
+ };
+
+ QVector<std::function<void()>> m_mappedProperties;
+ QVector<EventCallbackInfo> m_eventCallbacks;
+};
+}
+
+#endif // Q3DSQMLSCRIPT_H
diff --git a/src/runtime/q3dsvariantconfig.cpp b/src/runtime/q3dsvariantconfig.cpp
new file mode 100644
index 0000000..e1fa04c
--- /dev/null
+++ b/src/runtime/q3dsvariantconfig.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://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 "q3dsvariantconfig_p.h"
+
+namespace qt3ds {
+
+Q3DSVariantConfig::Q3DSVariantConfig()
+{
+
+}
+
+Q3DSVariantConfig::~Q3DSVariantConfig()
+{
+ for (auto str : qAsConst(m_internalVariantList))
+ delete str;
+ m_internalVariantList.clear();
+}
+
+void Q3DSVariantConfig::setVariantList(const QStringList &variantList)
+{
+ // Store the QStringList to be returned from the API
+ m_variantList = variantList;
+
+ for (auto str : qAsConst(m_internalVariantList))
+ delete str;
+ m_internalVariantList.clear();
+
+ // Build a fixed (in mem location) list of the variant strings
+ for (auto tag : variantList)
+ m_internalVariantList.append(new QString(tag));
+
+ // Parse the variantGroup:variant list to map using the fixed list
+ m_variantFilter.clear();
+ for (auto tag : qAsConst(m_internalVariantList)) {
+ QStringRef refTag = QStringRef(tag);
+ int separatorIdx = refTag.indexOf(QLatin1Char(':'));
+ QStringRef group = refTag.left(separatorIdx);
+ QStringRef variant = refTag.mid(separatorIdx + 1);
+ m_variantFilter[group].append(variant);
+ }
+}
+
+bool Q3DSVariantConfig::isPartOfConfig(const QStringRef &variantAttributes) const
+{
+ // Variant filter is ignored when it's not defined
+ // or if the object has no variant attributes
+ if (m_variantFilter.isEmpty() || variantAttributes.isEmpty())
+ return true;
+
+ // Collect all variant tags per group from the element
+ QHash<QStringRef, QVector<QStringRef>> groupToVariants;
+ const QVector<QStringRef> variantTags = variantAttributes.split(
+ QLatin1Char(','),
+ QString::SkipEmptyParts);
+
+ for (auto tag : variantTags) {
+ // Break each variantGroup:value to group and value strings
+ int groupSeparatorIdx = tag.indexOf(QLatin1Char(':'));
+ QStringRef group = tag.left(groupSeparatorIdx);
+
+ // Only collect variant tags that are relevant to variant filtering
+ if (m_variantFilter.contains(group)) {
+ QStringRef variant = tag.mid(groupSeparatorIdx + 1);
+ groupToVariants[group].append(variant);
+ }
+ }
+
+ // If no relevant variant tags found in element, load the element
+ if (groupToVariants.isEmpty())
+ return true;
+
+ // Verify that the element matches the variant filtering per group.
+ // To match the element must either:
+ // - Have no variant tag defined for the group
+ // - Have a matching tag for the group
+ bool isLoaded = true;
+ const auto filteredGroups = m_variantFilter.keys();
+ for (auto group : filteredGroups) {
+ const QVector<QStringRef> variants = groupToVariants[group];
+ const QVector<QStringRef> filteredVariants = m_variantFilter[group];
+
+ if (variants.size() > 0) {
+ // Check if ANY of the variant values of the element matches ANY
+ // of the included variants in the filter for this variant group
+ bool matchesFilter = false;
+ for (auto variant : variants)
+ matchesFilter |= filteredVariants.contains(variant);
+
+ isLoaded &= matchesFilter;
+ }
+ }
+
+ return isLoaded;
+}
+
+}
diff --git a/src/runtime/q3dsvariantconfig_p.h b/src/runtime/q3dsvariantconfig_p.h
new file mode 100644
index 0000000..1905f44
--- /dev/null
+++ b/src/runtime/q3dsvariantconfig_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://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 Q3DSVARIANTCONFIG_P_H
+#define Q3DSVARIANTCONFIG_P_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvector.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+namespace qt3ds {
+
+class Q3DSVariantConfig
+{
+public:
+ Q3DSVariantConfig();
+ ~Q3DSVariantConfig();
+
+ inline bool operator==(const Q3DSVariantConfig &other) const
+ {
+ return (this->m_variantList == other.m_variantList);
+ }
+
+ inline bool operator!=(const Q3DSVariantConfig &other) const
+ {
+ return (this->m_variantList != other.m_variantList);
+ }
+
+ inline bool isEmpty() const { return m_variantList.isEmpty(); }
+
+ void setVariantList(const QStringList &variantList);
+
+ bool isPartOfConfig(const QStringRef &variantAttributes) const;
+
+ inline const QStringList &variantList() const { return m_variantList; }
+
+private:
+ QStringList m_variantList;
+ QList<QString *> m_internalVariantList;
+ QHash<QStringRef, QVector<QStringRef>> m_variantFilter;
+};
+
+};
+#endif // Q3DSVARIANTCONFIG_P_H