diff options
Diffstat (limited to 'src/runtime')
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 ¤tIndex, int &previousIndex, + QString ¤tName, 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 ¤tProps : 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 ¤tIndex, int &previousIndex, + QString ¤tName, 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 ¶metersManager + = 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 ¤tIndex, int &previousIndex, + QString ¤tName, 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 |