summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/stateapplication
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source/stateapplication')
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSState.h108
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateApplication.cpp63
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateApplication.h46
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateContext.cpp202
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateContext.h95
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp332
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.h73
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateExecutionTypes.h392
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateIdValue.h173
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.cpp2057
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.h121
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateScriptContext.h128
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateSharedImpl.h117
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateSignalConnection.h49
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateTypes.h720
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.cpp626
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.h116
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextCommands.h348
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextValues.h253
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.cpp1948
-rw-r--r--src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.h112
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h111
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h375
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h190
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp345
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp81
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp534
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h102
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp302
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp312
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h386
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp322
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h925
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h540
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h88
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp574
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp222
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp1880
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h315
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h1772
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h81
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h406
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h599
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp239
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h195
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp813
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h467
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h271
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp2527
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h174
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h291
51 files changed, 23518 insertions, 0 deletions
diff --git a/src/Runtime/Source/stateapplication/Qt3DSState.h b/src/Runtime/Source/stateapplication/Qt3DSState.h
new file mode 100644
index 00000000..b5686c25
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSState.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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_STATE_H
+#define QT3DS_STATE_H
+
+namespace qt3ds {
+class NVAllocatorCallback;
+class NVFoundationBase;
+namespace foundation {
+ class CRegisteredString;
+ class IStringTable;
+ class IOutStream;
+ class IInStream;
+ class IDOMFactory;
+ struct SDOMAttribute;
+ struct SDOMElement;
+ struct SNamespacePairNode;
+ class SocketStream;
+}
+
+namespace intrinsics {
+}
+}
+
+namespace qt3ds {
+namespace state {
+
+ using namespace qt3ds;
+ using namespace qt3ds::foundation;
+ using namespace qt3ds::intrinsics;
+ using qt3ds::foundation::CRegisteredString;
+ using qt3ds::foundation::IStringTable;
+ class IStateContext;
+ class IStateInterpreter;
+ class IStateLogger;
+ class IExecutionContext;
+ class IScriptContext;
+ struct SSCXML;
+ struct SState;
+ struct STransition;
+ struct SParallel;
+ struct SFinal;
+ struct SHistory;
+ struct SOnEntry;
+ struct SOnExit;
+ struct SSend;
+ struct SRaise;
+ struct SIf;
+ struct SElseIf;
+ struct SElse;
+ struct SLog;
+ struct SAssign;
+ struct SScript;
+ struct SDataModel;
+ struct SData;
+ struct SStateNode;
+ struct SCancel;
+
+ namespace editor {
+ class IEditor;
+ class IEditorObject;
+ }
+
+ namespace debugger {
+ class IDebugOutStream;
+ struct STransitionId;
+ class IStateMachineListener;
+ class IStateMachineDebugInterface;
+ class IDebugger;
+ struct SDebugPropertyDeclaration;
+ class IDebuggedInterpreter;
+ class IDebuggerMasterListener;
+ class IDebuggedInterpreter;
+ struct SMicrostep;
+ class IScriptStateListener;
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateApplication.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateApplication.cpp
new file mode 100644
index 00000000..cd89d52f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateApplication.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateApplication.h"
+#include "foundation/FileTools.h"
+#include "EASTL/vector.h"
+#include "foundation/IOStreams.h"
+#include "foundation/XML.h"
+
+#include <QtCore/qstringlist.h>
+
+typedef eastl::string TAppStr;
+
+using namespace qt3ds::foundation;
+using namespace qt3ds::state;
+using namespace eastl;
+
+eastl::string IApplication::GetLaunchFile(const char *inFullUIPPath)
+{
+ eastl::string directory;
+ eastl::string filestem;
+ eastl::string extension;
+
+ CFileTools::Split(inFullUIPPath, directory, filestem, extension);
+ eastl::string uiaPath;
+
+ eastl::vector<eastl::string> dirFiles;
+ CFileTools::GetDirectoryEntries(directory, dirFiles);
+
+ for (qt3ds::QT3DSU32 idx = 0, end = dirFiles.size(); idx < end && uiaPath.empty(); ++idx) {
+ eastl::string fileExt;
+ CFileTools::GetExtension(dirFiles[idx].c_str(), fileExt);
+ if (fileExt.comparei("uia") == 0)
+ CFileTools::CombineBaseAndRelative(directory.c_str(), dirFiles[idx].c_str(), uiaPath);
+ }
+ return uiaPath.empty() == false ? uiaPath : eastl::string(inFullUIPPath);
+}
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateApplication.h b/src/Runtime/Source/stateapplication/Qt3DSStateApplication.h
new file mode 100644
index 00000000..3ebe0d4f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateApplication.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_APPLICATION_H
+#define QT3DS_STATE_APPLICATION_H
+#include "EASTL/string.h"
+#include <QtCore/qvector.h>
+
+namespace qt3ds {
+namespace state {
+ // Shared code for dealing with .uia files.
+ class IApplication
+ {
+ public:
+ static eastl::string GetLaunchFile(const char *inFullUIPPath);
+ };
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateContext.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateContext.cpp
new file mode 100644
index 00000000..60814ed5
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateContext.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateContext.h"
+#include "Qt3DSStateXMLIO.h"
+#include "foundation/XML.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+
+using namespace qt3ds::state;
+
+namespace {
+
+struct SStateContext : public IStateContext
+{
+ NVFoundationBase &m_Foundation;
+ TIDStateMap m_IDStateMap;
+ TSendList m_SendList;
+ SSCXML *m_Root;
+
+ // the factory needs to be here in order that we can easily just assign variables
+ // left and right and make things work.
+ NVScopedRefCounted<IDOMFactory> m_DOMFactory;
+ TPtrExtensionMap m_ExtensionInfo;
+ SNamespacePairNode *m_NamespacePairs;
+ QT3DSI32 mRefCount;
+
+ SStateContext(NVFoundationBase &alloc)
+ : m_Foundation(alloc)
+ , m_IDStateMap(alloc.getAllocator(), "SStateContext::m_IDStateMap")
+ , m_SendList(alloc.getAllocator(), "SStateContext::m_SendList")
+ , m_Root(NULL)
+ , m_DOMFactory(NULL)
+ , m_ExtensionInfo(alloc.getAllocator(), "SStateContext::m_ExtensionInfo")
+ , m_NamespacePairs(NULL)
+ , mRefCount(0)
+ {
+ }
+ virtual ~SStateContext() {}
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+ void SetDOMFactory(IDOMFactory *inFactory) override;
+
+ void SetRoot(SSCXML &inRoot) override
+ {
+ QT3DS_ASSERT(m_Root == NULL);
+ if (!m_Root)
+ m_Root = &inRoot;
+ }
+ SSCXML *GetRoot() override { return m_Root; }
+ void AddSendToList(SSend &inSend) override { m_SendList.push_back(&inSend); }
+ NVConstDataRef<SSend *> GetSendList() override
+ {
+ return toConstDataRef(m_SendList.data(), (QT3DSU32)m_SendList.size());
+ }
+ IDOMFactory *GetDOMFactory() override { return m_DOMFactory; }
+ NVConstDataRef<NVScopedRefCounted<IStateContext>> GetSubContexts() override
+ {
+ return NVConstDataRef<NVScopedRefCounted<IStateContext>>();
+ }
+
+ // UICStateInterpreter.cpp;
+ bool InsertId(const CRegisteredString &inId, const SIdValue &inValue) override;
+ void EraseId(const CRegisteredString &inId) override;
+ bool ContainsId(const CRegisteredString &inStr) override;
+ SStateNode *FindStateNode(const CRegisteredString &inStr) override;
+ SSend *FindSend(const CRegisteredString &inStr) override;
+
+ SItemExtensionInfo *GetExtensionInfo(void *inItem) override;
+ SItemExtensionInfo &GetOrCreateExtensionInfo(void *inItem) override;
+
+ void SetFirstNSNode(SNamespacePairNode &inNode) override { m_NamespacePairs = &inNode; }
+ SNamespacePairNode *GetFirstNSNode() override { return m_NamespacePairs; }
+
+ void Save(IOutStream &inOutStream, editor::IEditor *inEditor) override;
+};
+
+void SStateContext::SetDOMFactory(IDOMFactory *inFactory)
+{
+ m_DOMFactory = inFactory;
+}
+
+bool SStateContext::InsertId(const CRegisteredString &inId, const SIdValue &inValue)
+{
+ return m_IDStateMap.insert(eastl::make_pair(inId, inValue)).second;
+}
+
+void SStateContext::EraseId(const CRegisteredString &inId)
+{
+ m_IDStateMap.erase(inId);
+}
+
+bool SStateContext::ContainsId(const CRegisteredString &inStr)
+{
+ return m_IDStateMap.find(inStr) != m_IDStateMap.end();
+}
+
+SStateNode *SStateContext::FindStateNode(const CRegisteredString &inStr)
+{
+ TIDStateMap::iterator iter = m_IDStateMap.find(inStr);
+
+ if (iter != m_IDStateMap.end()) {
+ IdValueTypes::Enum typeEnum = iter->second.getType();
+ if (typeEnum == IdValueTypes::StateNode)
+ return iter->second.getData<SStateNode *>();
+ }
+ return NULL;
+}
+
+SSend *SStateContext::FindSend(const CRegisteredString &inStr)
+{
+ TIDStateMap::iterator iter = m_IDStateMap.find(inStr);
+ if (iter != m_IDStateMap.end() && iter->second.getType() == IdValueTypes::Send)
+ return iter->second.getData<SSend *>();
+ return NULL;
+}
+
+SItemExtensionInfo *SStateContext::GetExtensionInfo(void *inItem)
+{
+ TPtrExtensionMap::iterator iter = m_ExtensionInfo.find(inItem);
+ if (iter != m_ExtensionInfo.end())
+ return &iter->second;
+ return NULL;
+}
+
+SItemExtensionInfo &SStateContext::GetOrCreateExtensionInfo(void *inItem)
+{
+ return m_ExtensionInfo.insert(eastl::make_pair(inItem, SItemExtensionInfo(inItem)))
+ .first->second;
+}
+
+void SStateContext::Save(IOutStream &inOutStream, editor::IEditor *inEditor)
+{
+ CXMLIO::SaveSCXMLFile(*this, m_Foundation, *m_DOMFactory->GetStringTable(), inOutStream,
+ inEditor);
+}
+}
+
+IStateContext *IStateContext::Load(NVAllocatorCallback &inGraphAllocator,
+ NVFoundationBase &inFoundation, IInStream &inStream,
+ const char8_t *inFilename, IStringTable *inStrTable,
+ editor::IEditor *inEditor)
+{
+
+ NVScopedRefCounted<IStringTable> theStringTable = inStrTable;
+ if (!theStringTable)
+ theStringTable = IStringTable::CreateStringTable(inFoundation.getAllocator());
+ NVScopedRefCounted<IDOMFactory> theFactory =
+ IDOMFactory::CreateDOMFactory(inFoundation.getAllocator(), theStringTable);
+ eastl::pair<SNamespacePairNode *, SDOMElement *> readResult =
+ CDOMSerializer::Read(*theFactory, inStream);
+ SDOMElement *elem = readResult.second;
+ if (elem == NULL)
+ return NULL;
+
+ NVScopedRefCounted<IDOMReader> theReader = IDOMReader::CreateDOMReader(
+ inFoundation.getAllocator(), *elem, theStringTable, *theFactory);
+ IStateContext *retval = QT3DS_NEW(inFoundation.getAllocator(), SStateContext)(inFoundation);
+ retval->SetDOMFactory(theFactory.mPtr);
+ CXMLIO::LoadSCXMLFile(inGraphAllocator, inFoundation, *theReader, *theStringTable, inFilename,
+ *retval, inEditor);
+ if (readResult.first)
+ retval->SetFirstNSNode(*readResult.first);
+ return retval;
+}
+
+IStateContext *IStateContext::Create(NVFoundationBase &inFoundation)
+{
+ NVScopedRefCounted<IStringTable> theStringTable =
+ IStringTable::CreateStringTable(inFoundation.getAllocator());
+ NVScopedRefCounted<IDOMFactory> theFactory =
+ IDOMFactory::CreateDOMFactory(inFoundation.getAllocator(), theStringTable);
+
+ SStateContext *retval = QT3DS_NEW(inFoundation.getAllocator(), SStateContext)(inFoundation);
+ retval->SetDOMFactory(theFactory);
+ return retval;
+}
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateContext.h b/src/Runtime/Source/stateapplication/Qt3DSStateContext.h
new file mode 100644
index 00000000..31098938
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateContext.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_CONTEXT_H
+#define QT3DS_STATE_CONTEXT_H
+#include "Qt3DSState.h"
+#include "Qt3DSStateTypes.h"
+
+namespace qt3ds {
+namespace state {
+
+ // Thanks to the crazy world we live in, we have to store xml extension information
+ // on the state context.
+ struct SItemExtensionInfo
+ {
+ void *m_ItemPtr;
+ TDOMElementNodeList m_ExtensionNodes;
+ TDOMAttributeNodeList m_ExtensionAttributes;
+ SItemExtensionInfo(void *inItemPtr)
+ : m_ItemPtr(inItemPtr)
+ {
+ }
+ };
+ typedef nvhash_map<void *, SItemExtensionInfo> TPtrExtensionMap;
+ // Parsing produces a list of send objects that need to be added into the script
+ // context if they have their idlocation attribute set.
+ typedef nvvector<SSend *> TSendList;
+
+ class IStateContext : public NVRefCounted
+ {
+ public:
+ // Can only be done once.
+ virtual void SetRoot(SSCXML &inRoot) = 0;
+ virtual SSCXML *GetRoot() = 0;
+ virtual void AddSendToList(SSend &inSend) = 0;
+ virtual NVConstDataRef<SSend *> GetSendList() = 0;
+ virtual void SetDOMFactory(IDOMFactory *inFactory) = 0;
+ virtual IDOMFactory *GetDOMFactory() = 0;
+ virtual NVConstDataRef<NVScopedRefCounted<IStateContext>> GetSubContexts() = 0;
+ virtual bool ContainsId(const CRegisteredString &inId) = 0;
+ virtual bool InsertId(const CRegisteredString &inId, const SIdValue &inValue) = 0;
+ virtual void EraseId(const CRegisteredString &inId) = 0;
+ virtual SStateNode *FindStateNode(const CRegisteredString &inStr) = 0;
+ virtual SSend *FindSend(const CRegisteredString &inStr) = 0;
+
+ virtual SItemExtensionInfo *GetExtensionInfo(void *inItem) = 0;
+ virtual SItemExtensionInfo &GetOrCreateExtensionInfo(void *inItem) = 0;
+
+ virtual void SetFirstNSNode(SNamespacePairNode &inNode) = 0;
+ virtual SNamespacePairNode *GetFirstNSNode() = 0;
+
+ virtual void Save(IOutStream &inOutStream, editor::IEditor *inEditor = NULL) = 0;
+
+ // The graph allocator is expected to release anything allocated via it; the general
+ // allocator doesn't need to do this.
+ // Filename is stored on the context for debugging purposes, editor is used if during
+ // loading so we can load extra
+ // information that is associated with but not stored on the state graph.
+ // String table will be created if not passed in, editor is optional.
+ static IStateContext *Load(NVAllocatorCallback &inGraphAllocator,
+ NVFoundationBase &inFoundation, IInStream &inStream,
+ const char8_t *inFilename, IStringTable *inStrTable = NULL,
+ editor::IEditor *inEditor = NULL);
+
+ static IStateContext *Create(NVFoundationBase &inGeneralAlloc);
+ };
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp
new file mode 100644
index 00000000..4516c2f9
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.cpp
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateExecutionContext.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateInterpreter.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/StringConversionImpl.h"
+#include "Qt3DSStateScriptContext.h"
+#include "foundation/Utils.h"
+#include "EASTL/string.h"
+
+using namespace qt3ds::state;
+
+namespace {
+struct SExecContext : public IExecutionContext
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ // Referencing this here would create circular references
+ IStateInterpreter *m_Interpreter;
+ IStateLogger *m_DebugLogger;
+ NVScopedRefCounted<IStateLogger> m_Logger;
+ NVScopedRefCounted<IScriptContext> m_ScriptContext;
+ bool m_Error;
+ QT3DSI32 mRefCount;
+ eastl::string m_DelayStr;
+
+ SExecContext(NVFoundationBase &inFnd, IStringTable &inStrTable, IStateLogger &inLogger,
+ IScriptContext &inContext)
+ : m_Foundation(inFnd)
+ , m_StringTable(inStrTable)
+ , m_Interpreter(NULL)
+ , m_DebugLogger(NULL)
+ , m_Logger(inLogger)
+ , m_ScriptContext(inContext)
+ , m_Error(false)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ void SetInterpreter(IStateInterpreter &inInterpreter) override
+ {
+ m_Interpreter = &inInterpreter;
+ }
+
+ void SetMachineDebugLogger(IStateLogger &inDebugLogger) override
+ {
+ m_DebugLogger = &inDebugLogger;
+ }
+
+ void SignalError()
+ {
+ const char8_t *errorInfo = m_ScriptContext->GetErrorInfo();
+ if (errorInfo && *errorInfo) {
+ m_Interpreter->QueueEvent("error.execution", false);
+ }
+ m_Error = true;
+ }
+ void ExecuteContent(SExecutableContent &content)
+ {
+ switch (content.m_Type) {
+ case ExecutableContentTypes::Send: {
+ SSend &theSend = *content.CastTo<SSend>();
+ CRegisteredString theEvent;
+ if (!isTrivial(theSend.m_EventExpr)) {
+ SScriptExecutionResult theStr =
+ m_ScriptContext->ExecuteExpressionToString(theSend.m_EventExpr);
+ if (theStr.Valid())
+ theEvent = m_StringTable->RegisterStr(theStr.Result());
+ } else
+ theEvent = theSend.m_Event;
+
+ QT3DSU64 theDelay(0);
+ m_DelayStr.clear();
+ if (!isTrivial(theSend.m_DelayExpr)) {
+ SScriptExecutionResult theStr =
+ m_ScriptContext->ExecuteExpressionToString(theSend.m_DelayExpr);
+ if (theStr.Valid())
+ m_DelayStr.assign(nonNull(theStr.Result()));
+ } else
+ m_DelayStr.assign(nonNull(theSend.m_Delay));
+ if (m_DelayStr.size())
+ theDelay = ParseTimeStrToMilliseconds(m_DelayStr.c_str());
+
+ if (theEvent.IsValid()) {
+ TEventPtr theEventPtr;
+ if (theSend.m_Children.empty() == false) {
+ IScriptEvent *theNewEvent = m_ScriptContext->CreateScriptEvent(theEvent);
+ theEventPtr = theNewEvent;
+ for (TExecutableContentList::iterator iter = theSend.m_Children.begin(),
+ end = theSend.m_Children.end();
+ iter != end && m_Error == false; ++iter) {
+ if (iter->m_Type == ExecutableContentTypes::Param) {
+ SParam &theParam = static_cast<SParam &>(*iter);
+ if (theParam.m_Location.IsValid() == false) {
+ bool success =
+ theNewEvent->SetParam(theParam.m_Name, theParam.m_Expr);
+ if (!success)
+ SignalError();
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ } else if (iter->m_Type == ExecutableContentTypes::Content) {
+ SContent &theContent = static_cast<SContent &>(*iter);
+ if (!isTrivial(theContent.m_Expr)) {
+ bool success = theNewEvent->SetDataExpr(theContent.m_Expr);
+ if (!success)
+ SignalError();
+ } else if (!isTrivial(theContent.m_ContentValue)) {
+ bool success = theNewEvent->SetDataStr(theContent.m_ContentValue);
+ if (!success)
+ SignalError();
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ if (m_Error == false) {
+ bool isExternal = true;
+ if (AreEqual("#_internal", theSend.m_Target))
+ isExternal = false;
+ if (theEventPtr)
+ m_Interpreter->QueueEvent(theEventPtr, theDelay, theSend.m_Id, isExternal);
+ else
+ m_Interpreter->QueueEvent(theEvent, theDelay, theSend.m_Id, isExternal);
+ }
+ }
+ } break;
+ case ExecutableContentTypes::Cancel: {
+ SCancel &theCancel = *content.CastTo<SCancel>();
+ if (theCancel.m_Send) {
+ m_Interpreter->CancelEvent(theCancel.m_Send->m_Id);
+ } else if (!isTrivial(theCancel.m_IdExpression)) {
+ SScriptExecutionResult theStr =
+ m_ScriptContext->ExecuteExpressionToString(theCancel.m_IdExpression);
+ if (theStr.Valid()) {
+ const char8_t *theStrVal(theStr.Result());
+ if (!isTrivial(theStrVal))
+ m_Interpreter->CancelEvent(m_StringTable->RegisterStr(theStrVal));
+ }
+ }
+ } break;
+ case ExecutableContentTypes::Raise: {
+ SRaise &theRaise = *content.CastTo<SRaise>();
+ m_Interpreter->QueueEvent(theRaise.m_Event, false);
+ } break;
+ case ExecutableContentTypes::Log: {
+ SLog *theLog = content.CastTo<SLog>();
+ SScriptExecutionResult str =
+ m_ScriptContext->ExecuteExpressionToString(theLog->m_Expression);
+ if (str.Valid()) {
+ m_Logger->Log(theLog->m_Label, str.Result());
+ if (m_DebugLogger)
+ m_DebugLogger->Log(theLog->m_Label, str.Result());
+ } else
+ SignalError();
+ } break;
+ case ExecutableContentTypes::If: {
+ SIf &theIf = *content.CastTo<SIf>();
+ Option<bool> theCondResult = m_ScriptContext->ExecuteCondition(theIf.m_Cond);
+ if (theCondResult.hasValue()) {
+ bool currentConditionResult = *theCondResult;
+ for (TExecutableContentList::iterator ifIter = theIf.m_Children.begin(),
+ ifEnd = theIf.m_Children.end();
+ ifIter != ifEnd && m_Error == false; ++ifIter) {
+ if (currentConditionResult) {
+ switch (ifIter->m_Type) {
+ case ExecutableContentTypes::Else:
+ case ExecutableContentTypes::ElseIf:
+ return;
+ default:
+ ExecuteContent(*ifIter);
+ break;
+ }
+ } else {
+ switch (ifIter->m_Type) {
+ case ExecutableContentTypes::ElseIf: {
+ SElseIf &theElseIf = *ifIter->CastTo<SElseIf>();
+ theCondResult = m_ScriptContext->ExecuteCondition(theElseIf.m_Cond);
+ if (theCondResult.hasValue())
+ currentConditionResult = *theCondResult;
+ else {
+ SignalError();
+ return;
+ }
+ } break;
+ case ExecutableContentTypes::Else:
+ currentConditionResult = true;
+ break;
+ // Ignore all content that isn't if or else if we shouldn't be currently
+ // executing it.
+ default:
+ break;
+ }
+ }
+ }
+ } else
+ SignalError();
+ } break;
+ case ExecutableContentTypes::Foreach: {
+ SForeach &theItem = *content.CastTo<SForeach>();
+ Option<bool> success;
+ for (success = m_ScriptContext->BeginForeach(theItem.m_Array, theItem.m_Item,
+ theItem.m_Index);
+ success.hasValue() && *success && m_Error == false;
+ success = m_ScriptContext->NextForeach(theItem.m_Item, theItem.m_Index)) {
+ ExecuteContent(theItem.m_Children);
+ }
+ if (m_Error) {
+
+ } else if (success.hasValue() == false)
+ SignalError();
+ } break;
+ // We shouldn't get top level else or else if statements, they can only be inside an if
+ // statement.
+ case ExecutableContentTypes::Else:
+ case ExecutableContentTypes::ElseIf:
+ QT3DS_ASSERT(false);
+ break;
+
+ case ExecutableContentTypes::Assign: {
+ SAssign &theAssign = *content.CastTo<SAssign>();
+ bool success = m_ScriptContext->Assign(theAssign.m_Location, theAssign.m_Expression);
+ if (!success)
+ SignalError();
+ } break;
+ case ExecutableContentTypes::Script: {
+ SScript &theScript = *content.CastTo<SScript>();
+ if (!isTrivial(theScript.m_Data))
+ m_ScriptContext->ExecuteScript(theScript.m_Data);
+ } break;
+ default:
+ qCCritical(INTERNAL_ERROR, "Unimplemented executable content %s",
+ ExecutableContentTypes::ToString(content.m_Type));
+ }
+ }
+
+ void ExecuteContent(TExecutableContentList &inContent)
+ {
+ for (TExecutableContentList::iterator iter = inContent.begin(), end = inContent.end();
+ iter != end && m_Error == false; ++iter) {
+ ExecuteContent(*iter);
+ }
+ }
+
+ void Execute(STransition &inTransaction) override
+ {
+ m_Error = false;
+ ExecuteContent(inTransaction.m_ExecutableContent);
+ }
+
+ // These functions take the node as well as the list so a context can cache a fast
+ // execution path if necessary.
+ void Execute(SStateNode & /*inNode*/, TOnEntryList &inList) override
+ {
+ for (TOnEntryList::iterator iter = inList.begin(), end = inList.end(); iter != end;
+ ++iter) {
+ m_Error = false;
+ ExecuteContent(iter->m_ExecutableContent);
+ }
+ }
+
+ void Execute(SStateNode & /*inNode*/, TOnExitList &inList) override
+ {
+ for (TOnExitList::iterator iter = inList.begin(), end = inList.end(); iter != end; ++iter) {
+ m_Error = false;
+ ExecuteContent(iter->m_ExecutableContent);
+ }
+ }
+};
+}
+
+QT3DSU64 IExecutionContext::ParseTimeStrToMilliseconds(const char8_t *timeStr)
+{
+ if (isTrivial(timeStr))
+ return 0;
+
+ char *endPtr;
+ double theData = strtod(timeStr, &endPtr);
+ if (!isTrivial(endPtr)) {
+ if (AreEqual(endPtr, "s")) {
+ theData *= 1000;
+ } else if (AreEqual(endPtr, "ms")) {
+ // empty intentional
+ } else
+ theData = 0;
+ } else
+ theData = 0;
+ if (theData < 0)
+ theData = 0.0;
+ return static_cast<QT3DSU64>(theData);
+}
+
+IExecutionContext &IExecutionContext::Create(NVFoundationBase &inFoundation,
+ IStringTable &inStringTable, IStateLogger &inLogger,
+ IScriptContext &inScriptContext)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(), SExecContext)(inFoundation, inStringTable, inLogger,
+ inScriptContext);
+}
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.h b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.h
new file mode 100644
index 00000000..31ff0e22
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionContext.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EXECUTION_CONTEXT_H
+#define QT3DS_STATE_EXECUTION_CONTEXT_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateTypes.h"
+#include "foundation/Qt3DSRefCounted.h"
+
+namespace qt3ds {
+namespace state {
+
+ class IStateLogger : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateLogger() {}
+ public:
+ virtual void Log(const char8_t *inLabel, const char8_t *inExpression) = 0;
+ };
+ // Implementation of the execution context of the scxml state specification.
+ // Implementations if,send,raise,foreach,etc working closely with the scripting
+ // system.
+ class IExecutionContext : public NVRefCounted
+ {
+ protected:
+ virtual ~IExecutionContext() {}
+ public:
+ virtual void SetInterpreter(IStateInterpreter &inInterpreter) = 0;
+ virtual void SetMachineDebugLogger(IStateLogger &inDebugLogger) = 0;
+ virtual void Execute(STransition &inTransaction) = 0;
+ // These functions take the node as well as the list so a context can cache a fast
+ // execution path if necessary.
+ virtual void Execute(SStateNode &inNode, TOnEntryList &inList) = 0;
+ virtual void Execute(SStateNode &inNode, TOnExitList &inList) = 0;
+
+ // Returns the time string in milliseconds.
+ static QT3DSU64 ParseTimeStrToMilliseconds(const char8_t *timeStr);
+
+ static IExecutionContext &Create(NVFoundationBase &inFoundation,
+ IStringTable &inStringTable, IStateLogger &inLogger,
+ IScriptContext &inScriptContext);
+ };
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateExecutionTypes.h b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionTypes.h
new file mode 100644
index 00000000..6dc7674f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateExecutionTypes.h
@@ -0,0 +1,392 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EXECUTION_TYPES_H
+#define QT3DS_STATE_EXECUTION_TYPES_H
+#pragma once
+#include "Qt3DSStateTypes.h"
+
+namespace qt3ds {
+namespace state {
+
+ struct ExecutableContentTypes
+ {
+ enum Enum {
+ NoType = 0,
+ Raise,
+ If,
+ ElseIf,
+ Else,
+ Foreach,
+ Log,
+ Assign,
+ Script,
+ Send,
+ Cancel,
+ Param,
+ Content,
+ };
+ static const char8_t *ToString(Enum inVal)
+ {
+ switch (inVal) {
+ case Raise:
+ return "Raise";
+ case If:
+ return "If";
+ case ElseIf:
+ return "ElseIf";
+ case Else:
+ return "Else";
+ case Foreach:
+ return "Foreach";
+ case Log:
+ return "Log";
+ case Assign:
+ return "Assign";
+ case Script:
+ return "Script";
+ case Send:
+ return "Send";
+ case Cancel:
+ return "Cancel";
+ case Param:
+ return "Param";
+ case Content:
+ return "Content";
+ default:
+ return "Unknown execution type";
+ }
+ }
+ };
+
+ struct SRaise;
+ struct SIf;
+ struct SElseIf;
+ struct SElse;
+ struct SForeach;
+ struct SLog;
+ struct SAssign;
+ struct SScript;
+ struct SSend;
+ struct SCancel;
+ struct SParam;
+ struct SContent;
+
+ template <typename TDataType>
+ struct SExecutableContentTypeMap
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::NoType;
+ }
+ };
+
+ template <>
+ struct SExecutableContentTypeMap<SRaise>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Raise;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SIf>
+ {
+ static ExecutableContentTypes::Enum GetContentType() { return ExecutableContentTypes::If; }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SElseIf>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::ElseIf;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SElse>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Else;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SForeach>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Foreach;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SLog>
+ {
+ static ExecutableContentTypes::Enum GetContentType() { return ExecutableContentTypes::Log; }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SAssign>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Assign;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SScript>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Script;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SSend>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Send;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SCancel>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Cancel;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SParam>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Param;
+ }
+ };
+ template <>
+ struct SExecutableContentTypeMap<SContent>
+ {
+ static ExecutableContentTypes::Enum GetContentType()
+ {
+ return ExecutableContentTypes::Content;
+ }
+ };
+
+ // Defined by the execution context to speed up evaluation of executable data.
+ struct SExecutionData;
+
+ struct SExecutableContent
+ {
+ const ExecutableContentTypes::Enum m_Type;
+ SStateNode *m_StateNodeParent;
+ SExecutableContent *m_Parent;
+ SExecutableContent *m_NextSibling;
+ SExecutableContent *m_PreviousSibling;
+ TExecutableContentList m_Children;
+
+ SExecutableContent(ExecutableContentTypes::Enum inType)
+ : m_Type(inType)
+ , m_StateNodeParent(NULL)
+ , m_Parent(NULL)
+ , m_NextSibling(NULL)
+ , m_PreviousSibling(NULL)
+ {
+ }
+
+ template <typename TDataType>
+ TDataType *CastTo()
+ {
+ if (m_Type == SExecutableContentTypeMap<TDataType>::GetContentType())
+ return static_cast<TDataType *>(this);
+ return NULL;
+ }
+
+ template <typename TDataType>
+ const TDataType *CastTo() const
+ {
+ if (m_Type == SExecutableContentTypeMap<TDataType>::GetContentType())
+ return static_cast<const TDataType *>(this);
+ return NULL;
+ }
+ };
+
+ IMPLEMENT_INVASIVE_LIST(ExecutableContent, m_PreviousSibling, m_NextSibling);
+
+ struct SRaise : public SExecutableContent
+ {
+ CRegisteredString m_Event;
+ SRaise()
+ : SExecutableContent(ExecutableContentTypes::Raise)
+ {
+ }
+ };
+
+ struct SIf : public SExecutableContent
+ {
+ const char8_t *m_Cond;
+ SIf()
+ : SExecutableContent(ExecutableContentTypes::If)
+ , m_Cond(NULL)
+ {
+ }
+ };
+
+ struct SElseIf : public SExecutableContent
+ {
+ const char8_t *m_Cond;
+ SElseIf()
+ : SExecutableContent(ExecutableContentTypes::ElseIf)
+ , m_Cond(NULL)
+ {
+ }
+ };
+
+ struct SElse : public SExecutableContent
+ {
+ SElse()
+ : SExecutableContent(ExecutableContentTypes::Else)
+ {
+ }
+ };
+
+ struct SForeach : public SExecutableContent
+ {
+ CRegisteredString m_Array;
+ CRegisteredString m_Item;
+ CRegisteredString m_Index;
+ SForeach()
+ : SExecutableContent(ExecutableContentTypes::Foreach)
+ {
+ }
+ };
+
+ struct SLog : public SExecutableContent
+ {
+ CRegisteredString m_Label;
+ const char8_t *m_Expression;
+ SLog()
+ : SExecutableContent(ExecutableContentTypes::Log)
+ , m_Expression(NULL)
+ {
+ }
+ };
+
+ struct SAssign : public SExecutableContent
+ {
+ const char8_t *m_Location;
+ const char8_t *m_Expression;
+ SAssign()
+ : SExecutableContent(ExecutableContentTypes::Assign)
+ , m_Location(NULL)
+ , m_Expression(NULL)
+ {
+ }
+ };
+
+ struct SScript : public SExecutableContent
+ {
+ const char8_t *m_URL;
+ const char8_t *m_Data;
+ SScript()
+ : SExecutableContent(ExecutableContentTypes::Script)
+ , m_URL(NULL)
+ , m_Data(NULL)
+ {
+ }
+ };
+
+ struct SSend : public SExecutableContent
+ {
+ CRegisteredString m_Event;
+ const char8_t *m_EventExpr;
+ const char8_t *m_Target;
+ const char8_t *m_TargetExpr;
+ const char8_t *m_Type;
+ const char8_t *m_TypeExpr;
+ CRegisteredString m_Id;
+ const char8_t *m_IdLocation;
+ const char8_t *m_Delay;
+ const char8_t *m_DelayExpr;
+ const char8_t *m_NameList;
+
+ SSend()
+ : SExecutableContent(ExecutableContentTypes::Send)
+ , m_EventExpr(NULL)
+ , m_Target(NULL)
+ , m_TargetExpr(NULL)
+ , m_Type(NULL)
+ , m_TypeExpr(NULL)
+ , m_IdLocation(NULL)
+ , m_Delay(NULL)
+ , m_DelayExpr(NULL)
+ , m_NameList(NULL)
+ {
+ }
+ };
+ struct SCancel : public SExecutableContent
+ {
+ // If we have an id.
+ SSend *m_Send;
+ const char8_t *m_IdExpression;
+
+ SCancel()
+ : SExecutableContent(ExecutableContentTypes::Cancel)
+ , m_Send(NULL)
+ , m_IdExpression(NULL)
+ {
+ }
+ };
+
+ struct SParam : public SExecutableContent
+ {
+ CRegisteredString m_Name;
+ const char8_t *m_Expr;
+ CRegisteredString m_Location;
+ SParam()
+ : SExecutableContent(ExecutableContentTypes::Param)
+ , m_Expr(NULL)
+ {
+ }
+ };
+
+ struct SContent : public SExecutableContent
+ {
+ const char8_t *m_Expr;
+ const char8_t *m_ContentValue;
+ SContent()
+ : SExecutableContent(ExecutableContentTypes::Content)
+ , m_Expr(NULL)
+ , m_ContentValue(NULL)
+ {
+ }
+ };
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateIdValue.h b/src/Runtime/Source/stateapplication/Qt3DSStateIdValue.h
new file mode 100644
index 00000000..ed5beb13
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateIdValue.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** 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_STATE_ID_TYPE_VALUE_H
+#define QT3DS_STATE_ID_TYPE_VALUE_H
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+
+// Discriminated union to unify different things that may be identified by an id
+namespace qt3ds {
+namespace state {
+
+ struct IdValueTypes
+ {
+ enum Enum {
+ NoIdValue = 0,
+ StateNode,
+ Send,
+ };
+ };
+
+ template <typename TDataType>
+ struct SIdValueTypeMap
+ {
+ static IdValueTypes::Enum GetType() { return IdValueTypes::NoIdValue; }
+ };
+
+ template <>
+ struct SIdValueTypeMap<SStateNode *>
+ {
+ static IdValueTypes::Enum GetType() { return IdValueTypes::StateNode; }
+ };
+ template <>
+ struct SIdValueTypeMap<SSend *>
+ {
+ static IdValueTypes::Enum GetType() { return IdValueTypes::Send; }
+ };
+
+ struct SIdValueUnionTraits
+ {
+ typedef IdValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(void *),
+ };
+
+ static TIdType getNoDataId() { return IdValueTypes::NoIdValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SIdValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case IdValueTypes::StateNode:
+ return inVisitor(*NVUnionCast<SStateNode **>(inData));
+ case IdValueTypes::Send:
+ return inVisitor(*NVUnionCast<SSend **>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case IdValueTypes::NoIdValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case IdValueTypes::StateNode:
+ return inVisitor(*NVUnionCast<const SStateNode **>(inData));
+ case IdValueTypes::Send:
+ return inVisitor(*NVUnionCast<const SSend **>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case IdValueTypes::NoIdValue:
+ return inVisitor();
+ }
+ }
+ };
+}
+}
+
+// need some specializations in the original nv foundation namespace
+namespace qt3ds {
+namespace foundation {
+
+ template <>
+ struct DestructTraits<qt3ds::state::SStateNode *>
+ {
+ void destruct(qt3ds::state::SStateNode *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SSend *>
+ {
+ void destruct(qt3ds::state::SSend *) {}
+ };
+}
+}
+
+namespace qt3ds {
+namespace state {
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SIdValueUnionTraits,
+ SIdValueUnionTraits::TBufferSize>,
+ SIdValueUnionTraits::TBufferSize>
+ TIdUnionType;
+
+ struct SIdValue : public TIdUnionType
+ {
+ SIdValue() {}
+
+ SIdValue(const SIdValue &inOther)
+ : TIdUnionType(static_cast<const TIdUnionType &>(inOther))
+ {
+ }
+
+ SIdValue(SStateNode *inDt)
+ : TIdUnionType(inDt)
+ {
+ }
+ SIdValue(SSend *inDt)
+ : TIdUnionType(inDt)
+ {
+ }
+
+ SIdValue &operator=(const SIdValue &inOther)
+ {
+ TIdUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SIdValue &inOther) const { return TIdUnionType::operator==(inOther); }
+ bool operator!=(const SIdValue &inOther) const { return TIdUnionType::operator!=(inOther); }
+
+ bool empty() const { return getType() == IdValueTypes::NoIdValue; }
+ };
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.cpp
new file mode 100644
index 00000000..9fc72406
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.cpp
@@ -0,0 +1,2057 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateTypes.h"
+#include "Qt3DSStateInterpreter.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSIntrinsics.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Utils.h"
+#include "foundation/Qt3DSPool.h"
+#include "foundation/Qt3DSTime.h"
+#include "EASTL/string.h"
+#include "EASTL/set.h"
+#include "EASTL/sort.h"
+#include "EASTL/list.h"
+#include "Qt3DSStateExecutionContext.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateScriptContext.h"
+#include "Qt3DSStateSharedImpl.h"
+#include "foundation/XML.h"
+#include "Qt3DSStateIdValue.h"
+#include "Qt3DSStateDebugger.h"
+#include "Qt3DSStateXMLIO.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "Qt3DSStateContext.h"
+#include "foundation/FastAllocator.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+#ifdef _DEBUG
+#define QT3DS_LOG_ENTER_EXIT 1
+#define QT3DS_LOG_ACTIVE_EVENT 1
+#endif
+
+namespace qt3ds {
+namespace state {
+ struct SInterpreterData
+ {
+ };
+}
+}
+
+namespace {
+struct SSimpleEvent : public IEvent
+{
+ NVAllocatorCallback &m_Alloc;
+ volatile QT3DSI32 mRefCount;
+ CRegisteredString m_Name;
+
+ SSimpleEvent(NVAllocatorCallback &alloc, CRegisteredString nm)
+ : m_Alloc(alloc)
+ , mRefCount(0)
+ , m_Name(nm)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Alloc)
+
+ CRegisteredString GetName() const override { return m_Name; }
+};
+
+struct SStateNodeInterpreterData : public SInterpreterData
+{
+
+ QT3DSI32 m_DocumentOrder;
+ bool m_Entered;
+ SStateNode *m_TransitionSubgraphRoot;
+
+ SStateNodeInterpreterData(QT3DSI32 inDocOrder)
+ : m_DocumentOrder(inDocOrder)
+ , m_Entered(false)
+ , m_TransitionSubgraphRoot(NULL)
+ {
+ }
+};
+
+template <typename TDataType, typename THasher = eastl::hash<TDataType>,
+ typename TEqOp = eastl::equal_to<TDataType>>
+struct SOrderedSet
+{
+ typedef eastl::hash_set<TDataType, THasher, TEqOp, ForwardingAllocator> TSetType;
+ typedef nvvector<TDataType> TListType;
+
+ TSetType m_Set;
+ TListType m_List;
+ typedef typename TListType::iterator iterator;
+
+ SOrderedSet(NVAllocatorCallback &alloc, const char *inTypeName)
+ : m_Set(ForwardingAllocator(alloc, inTypeName))
+ , m_List(alloc, inTypeName)
+ {
+ }
+
+ bool insert(const TDataType &inDtype)
+ {
+ if (m_Set.insert(inDtype).second) {
+ m_List.push_back(inDtype);
+ return true;
+ }
+ return false;
+ }
+
+ void clear()
+ {
+ m_Set.clear();
+ m_List.clear();
+ }
+
+ iterator begin() { return m_List.begin(); }
+ iterator end() { return m_List.end(); }
+ QT3DSU32 size() const { return m_List.size(); }
+ bool contains(const TDataType &inType) const { return m_Set.find(inType) != m_Set.end(); }
+ TDataType operator[](int inIndex)
+ {
+ if (inIndex < (int)size())
+ return m_List[inIndex];
+ return TDataType();
+ }
+ void erase(const TDataType &inType)
+ {
+ typename TSetType::iterator iter = m_Set.find(inType);
+ if (iter != m_Set.end()) {
+ m_Set.erase(iter);
+ typename TListType::iterator iter = eastl::find(m_List.begin(), m_List.end(), inType);
+ if (iter != m_List.end())
+ m_List.erase(iter);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+};
+
+struct SStateNodeNode
+{
+ SStateNode *m_Node;
+ SStateNodeNode *m_NextSibling;
+ SStateNodeNode()
+ : m_Node(NULL)
+ , m_NextSibling(NULL)
+ {
+ }
+ SStateNodeNode(SStateNode *inNode)
+ : m_Node(inNode)
+ , m_NextSibling(NULL)
+ {
+ }
+};
+
+DEFINE_INVASIVE_SINGLE_LIST(StateNodeNode);
+IMPLEMENT_INVASIVE_SINGLE_LIST(StateNodeNode, m_NextSibling);
+
+struct SFutureEvent
+{
+ TEventPtr m_Event;
+ QT3DSU64 m_FireTime;
+ CRegisteredString m_CancelId;
+ bool m_IsExternal;
+ SFutureEvent(TEventPtr evt, QT3DSU64 ft, CRegisteredString id, bool inExternal)
+ : m_Event(evt)
+ , m_FireTime(ft)
+ , m_CancelId(id)
+ , m_IsExternal(inExternal)
+ {
+ }
+ SFutureEvent()
+ : m_FireTime((QT3DSU64)-1)
+ , m_IsExternal(false)
+ {
+ }
+ // We want the events sorted in *reverse* time order.
+ bool operator<(const SFutureEvent &inOther) const { return m_FireTime < inOther.m_FireTime; }
+};
+
+struct StateSystem;
+
+typedef SOrderedSet<SStateNode *> TStateNodeSet;
+typedef SOrderedSet<STransition *> TTransitionSet;
+typedef nvvector<STransition *> TTransitionList;
+
+struct SDebugInterface : public IStateMachineDebugInterface, public IStateLogger
+{
+ StateSystem &m_StateSystem;
+ // No listener means no events and no active breakpoints of course
+ NVScopedRefCounted<IStateMachineListener> m_Listener;
+ TDebugStr m_LogStr;
+ TDebugStr m_TempStr;
+ TDebugStrList m_EnterList;
+ TDebugStrList m_ExitList;
+ TTransitionIdList m_TransitionList;
+
+ SDebugInterface(StateSystem &ss)
+ : m_StateSystem(ss)
+ , m_Listener(NULL)
+ {
+ }
+ // ignored as this object is part of the state system.
+ void addRef() override;
+ void release() override;
+ bool valid() { return m_Listener.mPtr != NULL; }
+
+ void SetStateMachineListener(IStateMachineListener *inListener) override
+ {
+ if (m_Listener.mPtr != inListener) {
+ m_Listener = inListener;
+ if (m_Listener)
+ OnConnect();
+ }
+ }
+
+ // IStateLogger interface
+ void Log(const char8_t *inLabel, const char8_t *inExpression) override
+ {
+ if (m_Listener) {
+ m_LogStr.assign(nonNull(inLabel));
+ m_LogStr.append(" - ");
+ m_LogStr.append(nonNull(inExpression));
+ m_Listener->Log(m_LogStr);
+ }
+ }
+
+ IScriptContext &GetScriptContext() override;
+
+ // Functions implemented after StateSystem in order to take advantage of the StateSystem struct
+ // members directly.
+ void OnConnect();
+
+ void EventQueued(const char8_t *inEventName, bool inInternal)
+ {
+ m_Listener->EventQueued(inEventName, inInternal);
+ }
+ // Functions should not be called in there is not a valid listener
+ void BeginStep();
+ void BeginMicrostep();
+ void SetCurrentEvent(const char8_t *inEventName, bool inInternal);
+ void SetTransitionSet(const TTransitionList &inTransitions);
+ void SetExitSet(const TStateNodeSet &inSet);
+ void SetEnterSet(const TStateNodeSet &inSet);
+ // Log statements run through the debugger as well.
+ void EndMicrostep();
+ void EndStep();
+ void OnExternalBreak() override;
+};
+
+#define DEBUGGER_CALL(function) \
+ if (m_DebugInterface.valid()) \
+ m_DebugInterface.function();
+#define DEBUGGER_CALL1(function, arg) \
+ if (m_DebugInterface.valid()) \
+ m_DebugInterface.function(arg);
+#define DEBUGGER_CALL2(function, arg, arg2) \
+ if (m_DebugInterface.valid()) \
+ m_DebugInterface.function(arg, arg2);
+
+#ifdef _WIN32
+#pragma warning(disable : 4355)
+#endif
+struct SStateSignalSender : public IStateSignalConnection
+{
+ NVAllocatorCallback &m_Alloc;
+ StateSystem *m_System;
+ IStateInterpreterEventHandler &m_Handler;
+ volatile QT3DSI32 mRefCount;
+ SStateSignalSender(NVAllocatorCallback &inAlloc, StateSystem &inSystem,
+ IStateInterpreterEventHandler &inHandler)
+ : m_Alloc(inAlloc)
+ , m_System(&inSystem)
+ , m_Handler(inHandler)
+ , mRefCount(0)
+ {
+ }
+ virtual ~SStateSignalSender();
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Alloc)
+};
+
+// Note that we explicity don't addref the signal sender internally. This is by design.
+typedef nvvector<SStateSignalSender *> TSignalSenderList;
+
+struct StateSystem : public IStateInterpreter
+{
+ typedef eastl::basic_string<char8_t, ForwardingAllocator> TStrType;
+ typedef Pool<SStateNodeInterpreterData, ForwardingAllocator> TInterperterDataPool;
+ typedef eastl::list<TEventPtr, ForwardingAllocator> TEventQueue;
+ typedef nvhash_map<SHistory *, TStateNodeNodeList> THistoryMap;
+ typedef Pool<SStateNodeNode, ForwardingAllocator> TStateNodeNodePool;
+ typedef nvvector<SFutureEvent> TFutureEventList;
+ typedef nvhash_set<STransition *> TTransitionHashSet;
+ typedef Pool<STransition, ForwardingAllocator> TTransitionPool;
+
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<IStateContext> m_Context;
+ volatile QT3DSI32 mRefCount;
+ // Holds the leaves of the current state.
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ NVScopedRefCounted<IScriptContext> m_ScriptContext;
+ NVScopedRefCounted<IExecutionContext> m_ExecutionContext;
+ nvvector<SStateNode *> m_ParentComparisonLHS;
+ nvvector<SStateNode *> m_ParentComparisonRHS;
+ nvvector<SStateNode *> m_AncestorsList;
+ nvvector<SStateNode *> m_IDRefList;
+
+ THistoryMap m_HistoryMap;
+
+ SOrderedSet<SStateNode *> m_Configuration;
+ nvvector<SStateNode *> m_StatesToInvoke;
+ nvvector<SStateNode *> m_TransitionTargetList;
+ TTransitionList m_EnabledTransitions;
+
+ TEventQueue m_InternalQueue;
+ TEventQueue m_ExternalQueue;
+
+ SOrderedSet<STransition *> m_TransitionSet;
+
+ TStateNodeSet m_StatesToEnter;
+ TStateNodeSet m_StatesForDefaultEntry;
+ TStateNodeSet m_StatesToExit;
+
+ TInterperterDataPool m_InterpreterDataPool;
+ TStateNodeNodePool m_StateNodeNodePool;
+
+ TStrType m_Workspace;
+
+ TFutureEventList m_FutureEvents;
+
+ bool m_Initialized;
+ bool m_Running;
+
+ SDebugInterface m_DebugInterface;
+ TSignalSenderList m_SignalSenders;
+
+ TTransitionHashSet m_InvalidTransitions;
+
+ TTransitionPool m_TransitionPool;
+ nvvector<STransition *> m_TemporaryTransitions;
+ nvhash_map<SStateNode *, STransition *> m_StateInitialTransitionMap;
+ SFastAllocator<> m_TemporaryAllocator;
+ TStrType m_IdSplitter;
+ nvvector<SStateNode *> m_TempNodeList;
+
+ StateSystem(NVFoundationBase &inFnd, IStringTable &inStrTable, IScriptContext &inScriptContext,
+ IExecutionContext &inExecutionContext)
+ : m_Foundation(inFnd)
+ , mRefCount(0)
+ , m_StringTable(inStrTable)
+ , m_ScriptContext(inScriptContext)
+ , m_ExecutionContext(inExecutionContext)
+ , m_ParentComparisonLHS(inFnd.getAllocator(), "StateSystem::m_ParentComparisonLHS")
+ , m_ParentComparisonRHS(inFnd.getAllocator(), "StateSystem::m_ParentComparisonRHS")
+ , m_AncestorsList(inFnd.getAllocator(), "StateSystem::m_AncestorsList")
+ , m_IDRefList(inFnd.getAllocator(), "StateSystem::m_IDRefList")
+ , m_HistoryMap(inFnd.getAllocator(), "StateSystem::m_HistoryMap")
+ , m_Configuration(inFnd.getAllocator(), "StateSystem::m_Configuration")
+ , m_StatesToInvoke(inFnd.getAllocator(), "StateSystem::m_InvokeList")
+ , m_TransitionTargetList(inFnd.getAllocator(), "StateSystem::m_TransitionTargetList")
+ , m_EnabledTransitions(inFnd.getAllocator(), "StateSystem::m_TransitionList")
+ , m_InternalQueue(ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_InternalQueue"))
+ , m_ExternalQueue(ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_ExternalQueue"))
+ , m_TransitionSet(inFnd.getAllocator(), "StateSystem::m_TransitionSet")
+ , m_StatesToEnter(inFnd.getAllocator(), "StateSystem::m_StatesToEnter")
+ , m_StatesForDefaultEntry(inFnd.getAllocator(), "StateSystem::m_StatesForDefaultEntry")
+ , m_StatesToExit(inFnd.getAllocator(), "StateSystem::m_StatesToExit")
+ , m_InterpreterDataPool(
+ ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_InterpretDataPool"))
+ , m_StateNodeNodePool(
+ ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_StateNodeNodePool"))
+ , m_Workspace(ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_Workspace"))
+ , m_FutureEvents(inFnd.getAllocator(), "StateSystem::m_FutureEvents")
+ , m_Initialized(false)
+ , m_Running(false)
+ , m_DebugInterface(*this)
+ , m_SignalSenders(inFnd.getAllocator(), "StateSystem::m_SignalSenders")
+ , m_InvalidTransitions(inFnd.getAllocator(), "StateSystem::m_InvalidTransitions")
+ , m_TransitionPool(
+ ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_TransitionPool"))
+ , m_TemporaryTransitions(inFnd.getAllocator(), "StateSystem::m_TemporaryTransitions")
+ , m_StateInitialTransitionMap(inFnd.getAllocator(),
+ "StateSystem::m_StateInitialTempTransitionMap")
+ , m_TemporaryAllocator(inFnd.getAllocator(), "StateSystem::m_TemporaryAllocator")
+ , m_IdSplitter(ForwardingAllocator(inFnd.getAllocator(), "StateSystem::m_IdSplitter"))
+ , m_TempNodeList(inFnd.getAllocator(), "StateSystem::m_TempNodeList")
+ {
+ inExecutionContext.SetInterpreter(*this);
+ inExecutionContext.SetMachineDebugLogger(m_DebugInterface);
+ inScriptContext.SetInterpreter(*this);
+ }
+
+ ~StateSystem()
+ {
+ // Ensure the signallers will not attempt to communicate to this object.
+ for (TSignalSenderList::iterator iter = m_SignalSenders.begin(),
+ end = m_SignalSenders.end();
+ iter != end; ++iter)
+ (*iter)->m_System = NULL;
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ void InitializeDataStructures()
+ {
+ m_Configuration.clear();
+ m_StatesToInvoke.clear();
+ m_EnabledTransitions.clear();
+ m_Running = true;
+ }
+
+ STransition &CreateTemporaryTransition()
+ {
+ STransition *newTrans = m_TransitionPool.construct(__FILE__, __LINE__);
+ m_TemporaryTransitions.push_back(newTrans);
+ return *newTrans;
+ }
+
+ void ReleaseInitialAndTemporaryTransitions()
+ {
+ for (QT3DSU32 idx = 0, end = m_TemporaryTransitions.size(); idx < end; ++idx)
+ m_TransitionPool.deallocate(m_TemporaryTransitions[idx]);
+ m_TemporaryTransitions.clear();
+ m_StateInitialTransitionMap.clear();
+ m_TemporaryAllocator.reset();
+ }
+
+ void FreeHistoryData(SHistory &inHistory)
+ {
+ THistoryMap::iterator historyData = m_HistoryMap.find(&inHistory);
+ if (historyData != m_HistoryMap.end()) {
+ for (TStateNodeNodeList::iterator iter = historyData->second.begin(),
+ end = historyData->second.end();
+ iter != end; ++iter)
+ m_StateNodeNodePool.deallocate(&(*iter));
+ // Force clear the list.
+ historyData->second.m_Head = NULL;
+ }
+ }
+
+ TStateNodeNodeList *GetHistoryData(SHistory &inHistory)
+ {
+ THistoryMap::iterator historyData = m_HistoryMap.find(&inHistory);
+ if (historyData != m_HistoryMap.end())
+ return &historyData->second;
+ return NULL;
+ }
+
+ TStateNodeNodeList &GetOrCreateHistoryData(SHistory &inHistory)
+ {
+ THistoryMap::iterator historyData = m_HistoryMap.find(&inHistory);
+ if (historyData != m_HistoryMap.end())
+ return historyData->second;
+ return m_HistoryMap.insert(eastl::make_pair(&inHistory, TStateNodeNodeList()))
+ .first->second;
+ }
+
+ // Setup of the state system, you can add a set of roots to the state graph.
+ NVConstDataRef<SStateNode *> GetConfiguration() override { return m_Configuration.m_List; }
+
+ void GetPathToRoot(SStateNode &state, nvvector<SStateNode *> &inParents, bool inProper)
+ {
+ inParents.clear();
+ SStateNode *stateIter = &state;
+ if (inProper)
+ stateIter = state.m_Parent;
+ for (; stateIter; stateIter = stateIter->m_Parent)
+ inParents.push_back(stateIter);
+ }
+
+ // Given these two states, get the nearest parent they share in common.
+ // Return valid is the index taken from the end of the m_ParentComparison* lists.
+ // the index is valid in both of them.
+ QT3DSI32 GetSharedParentState(SStateNode &lhs, SStateNode &rhs, bool inProper)
+ {
+ GetPathToRoot(lhs, m_ParentComparisonLHS, inProper);
+ GetPathToRoot(rhs, m_ParentComparisonRHS, inProper);
+ if (m_ParentComparisonLHS.empty() || m_ParentComparisonRHS.empty()) {
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+ QT3DS_ASSERT(m_ParentComparisonLHS.back() == m_ParentComparisonRHS.back());
+ QT3DSI32 retval = 0;
+ for (nvvector<SStateNode *>::reverse_iterator lhsComp = m_ParentComparisonLHS.rbegin(),
+ rhsComp = m_ParentComparisonRHS.rbegin(),
+ lhsEnd = m_ParentComparisonLHS.rend(),
+ rhsEnd = m_ParentComparisonRHS.rend();
+ lhsComp != lhsEnd && rhsComp != rhsEnd && *lhsComp == *rhsComp;
+ ++lhsComp, ++rhsComp, ++retval) {
+ }
+ // Walk the path to the root backwards and note where it differs.
+ return retval - 1;
+ }
+
+ void CreateStateNodeInterpreterData(SStateNode &inNode, QT3DSI32 &inIdx)
+ {
+ inNode.m_InterpreterData = m_InterpreterDataPool.construct(inIdx, __FILE__, __LINE__);
+ ++inIdx;
+ if (inNode.m_Type == StateNodeTypes::SCXML) {
+ SSCXML *item = inNode.CastTo<SSCXML>();
+ if (item->m_Initial)
+ CreateStateNodeInterpreterData(*item->m_Initial, inIdx);
+ } else if (inNode.m_Type == StateNodeTypes::State) {
+ SState *item = inNode.CastTo<SState>();
+ if (item->m_Initial)
+ CreateStateNodeInterpreterData(*item->m_Initial, inIdx);
+ }
+ if (StateNodeTypes::CanHaveChildren(inNode.m_Type)) {
+ SStateParallelBase &theBase = static_cast<SStateParallelBase &>(inNode);
+ for (TStateNodeList::iterator iter = theBase.m_Children.begin(),
+ end = theBase.m_Children.end();
+ iter != end; ++iter)
+ CreateStateNodeInterpreterData(*iter, inIdx);
+ }
+ }
+
+ // Creates the interpreter data for the entire graph.
+ void CreateStateNodeInterpreterData()
+ {
+ if (m_Context->GetRoot() == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ QT3DSI32 nodeIdx = 1;
+ CreateStateNodeInterpreterData(*m_Context->GetRoot(), nodeIdx);
+ }
+
+ SStateNodeInterpreterData &GetOrCreateInterpreterData(SStateNode &inNode)
+ {
+ if (!inNode.m_InterpreterData)
+ CreateStateNodeInterpreterData();
+
+ return *static_cast<SStateNodeInterpreterData *>(inNode.m_InterpreterData);
+ }
+ struct SStateNodeSorter
+ {
+ StateSystem &m_System;
+ SStateNodeSorter(StateSystem &inS)
+ : m_System(inS)
+ {
+ }
+ bool operator()(SStateNode *lhs, SStateNode *rhs) const
+ {
+ return m_System.GetOrCreateInterpreterData(*lhs).m_DocumentOrder
+ < m_System.GetOrCreateInterpreterData(*rhs).m_DocumentOrder;
+ }
+ };
+
+ void SortByDocumentOrder(nvvector<SStateNode *> &inList)
+ {
+ eastl::sort(inList.begin(), inList.end(), SStateNodeSorter(*this));
+ }
+
+ void SortConfiguration() { SortByDocumentOrder(m_Configuration.m_List); }
+
+ void RunDataModel(SDataModel &inDM)
+ {
+ QT3DS_ASSERT(inDM.m_Source == NULL);
+ QT3DS_ASSERT(inDM.m_Expression == NULL);
+ for (TDataList::iterator iter = inDM.m_Data.begin(), end = inDM.m_Data.end(); iter != end;
+ ++iter) {
+ if (iter->m_Source == NULL)
+ m_ScriptContext->Assign(iter->m_Id.c_str(), iter->m_Expression);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ void RecursiveInitializeDataModel(SStateNode &inState)
+ {
+ SDataModel *theDataModel = inState.GetDataModel();
+ if (theDataModel)
+ RunDataModel(*theDataModel);
+
+ TStateNodeList *children = inState.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter)
+ RecursiveInitializeDataModel(*iter);
+ }
+ }
+
+ // Called during initialization for early binding of the datamodel
+ void RecursiveInitializeDataModel()
+ {
+ QT3DS_ASSERT(m_Context->GetRoot());
+ QT3DS_ASSERT(m_Context->GetRoot()->m_Flags.IsLateBinding() == false);
+ RecursiveInitializeDataModel(*m_Context->GetRoot());
+ }
+
+ void MarkTransitionAsInvalid(STransition &inTransition, SStateNode &inParent, int inIndex)
+ {
+ if (inIndex >= 0) {
+ qCCritical(INVALID_OPERATION, "Detected invalid transition %s:%d",
+ inParent.m_Id.c_str(), inIndex);
+ } else {
+ const char *transitionType = "initial";
+ if (inIndex == -1)
+ transitionType = "history";
+ qCCritical(INVALID_OPERATION, "Detected invalid %s transition on node %s:%d",
+ transitionType, inParent.m_Id.c_str(), inIndex);
+ }
+ m_InvalidTransitions.insert(&inTransition);
+ }
+
+ bool IsTransitionValid(STransition &inTransition)
+ {
+ return m_InvalidTransitions.find(&inTransition) == m_InvalidTransitions.end();
+ }
+
+ bool IsTransitionValid(STransition *inTransition)
+ {
+ if (inTransition)
+ return IsTransitionValid(*inTransition);
+
+ // NULL transitions are of course, invalid.
+ return false;
+ }
+
+ bool ValidateTransitionTargetList(STransition &inTransition, SStateNode &inParent, int inIndex)
+ {
+ for (QT3DSU32 idx = 0, end = inTransition.m_Target.size(); idx < end; ++idx) {
+ for (QT3DSU32 outerIdx = idx + 1; outerIdx < end; ++outerIdx) {
+ // We handle this case dynamically during transition activation
+ if (IsDescendant(*inTransition.m_Target[idx], *inTransition.m_Target[outerIdx])
+ || IsDescendant(*inTransition.m_Target[outerIdx], *inTransition.m_Target[idx]))
+ continue;
+
+ // If they aren't directly related they must be related indirectly via a parallel.
+ SStateNode *theLCA =
+ FindLCA(*inTransition.m_Target[idx], *inTransition.m_Target[outerIdx]);
+ if (theLCA && theLCA->m_Type != StateNodeTypes::Parallel) {
+ MarkTransitionAsInvalid(inTransition, inParent, inIndex);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ void ValidateInitialOrHistoryTransition(STransition *inTransition, SStateNode &inParent,
+ bool inIsInitial = false)
+ {
+ if (inTransition == NULL)
+ return;
+ int transIndex = inIsInitial ? -2 : -1;
+ if (inTransition->m_Target.size() == 0) {
+ MarkTransitionAsInvalid(*inTransition, inParent, transIndex);
+ } else {
+ ValidateTransitionTargetList(*inTransition, inParent, transIndex);
+ }
+ }
+
+ void ValidateGeneralTransition(STransition &inTransition, SStateNode &inParent, int inIndex)
+ {
+ // Three distinct ways a transition could be invalid.
+ // 1. targetless, eventless and conditionless
+ if (inTransition.m_Target.size() == 0 && inTransition.m_Event.IsValid() == false
+ && isTrivial(inTransition.m_Condition))
+ MarkTransitionAsInvalid(inTransition, inParent, inIndex);
+ else {
+ // Else eventless ,targetless, and points back to source state
+ if (inTransition.m_Target.size() == 1) {
+ if (inTransition.m_Target[0] == inTransition.m_Parent
+ && inTransition.m_Event.IsValid() == false
+ && isTrivial(inTransition.m_Condition)) {
+ MarkTransitionAsInvalid(inTransition, inParent, inIndex);
+ }
+ } else {
+ ValidateTransitionTargetList(inTransition, inParent, inIndex);
+ }
+ }
+ }
+
+ void RecursiveValidateTransitions(SStateNode &inNode, SStateNode *inParent, int inIndex)
+ {
+ switch (inNode.m_Type) {
+ case StateNodeTypes::SCXML:
+ ValidateInitialOrHistoryTransition(inNode.GetInitialTransition(), inNode);
+ break;
+ case StateNodeTypes::State:
+ ValidateInitialOrHistoryTransition(inNode.GetInitialTransition(), inNode);
+ break;
+ case StateNodeTypes::Parallel:
+ ValidateInitialOrHistoryTransition(inNode.GetInitialTransition(), inNode);
+ break;
+ case StateNodeTypes::History:
+ ValidateInitialOrHistoryTransition(static_cast<SHistory &>(inNode).m_Transition, inNode,
+ false);
+ break;
+ case StateNodeTypes::Transition:
+ if (inParent)
+ ValidateGeneralTransition(static_cast<STransition &>(inNode), *inParent, inIndex);
+ break;
+ default: // don't care
+ break;
+ }
+ RecursiveValidateTransitions(inNode, inNode.GetChildren());
+ }
+
+ void RecursiveValidateTransitions(SStateNode &inParent, TStateNodeList *inNodes)
+ {
+ if (inNodes) {
+ int idx = 0;
+ for (TStateNodeList::iterator iter = inNodes->begin(), end = inNodes->end();
+ iter != end; ++iter, ++idx)
+ RecursiveValidateTransitions(*iter, &inParent, idx);
+ }
+ }
+
+ // The state machine is pretty robust but there are is one category of input that can
+ // put the machine in a bad state. Transitions with either multiple targets or no target
+ // or a single target that points back to its owner may be bad depending on different criteria.
+ // For multiple targets, we invalidate a transition that puts us into a bad state.
+ // For no targets, in invalidate a transition that would cause the interpreter to loop
+ // infinitely
+ // For a single target, we invalidate a transition if it points back to its source but it has no
+ // condition
+ // or event. We don't attempt any more sophisticated analysis at this point although one could
+ // conceive of
+ // an analysis that could find loops depending on how long you wanted it to run.
+ void RecursiveValidateTransitions()
+ {
+ QT3DS_ASSERT(m_Context->GetRoot());
+ RecursiveValidateTransitions(*m_Context->GetRoot(), NULL, 0);
+ }
+
+ NVConstDataRef<SStateNode *> GetRefList(NVConstDataRef<SStateNode *> inIds) { return inIds; }
+
+ // unsorted, may contain duplicates.
+ NVDataRef<SStateNode *> GetRefList(NVConstDataRef<CRegisteredString> inIds)
+ {
+ m_IDRefList.clear();
+ for (QT3DSU32 idx = 0, end = inIds.size(); idx < end; ++idx) {
+ SStateNode *theNode = m_Context->FindStateNode(inIds[idx]);
+ if (theNode != NULL) {
+ m_IDRefList.push_back(theNode);
+ }
+ }
+ return m_IDRefList;
+ }
+
+ NVConstDataRef<SStateNode *> GetTargetStates(STransition *inTransition)
+ {
+ if (inTransition)
+ return inTransition->m_Target;
+ return NVDataRef<SStateNode *>();
+ }
+
+ static bool IsCompoundState(const SStateNode *inNode)
+ {
+ if (inNode == NULL)
+ return false;
+ return inNode->IsCompound();
+ }
+
+ static bool IsDescendant(SStateNode &inParent, SStateNode &inChild)
+ {
+ if (&inChild == &inParent)
+ return false;
+
+ for (SStateNode *theNode = &inChild; theNode; theNode = theNode->m_Parent)
+ if (theNode == &inParent)
+ return true;
+ return false;
+ }
+
+ static bool AllAreDescendants(SStateNode &inParent, NVConstDataRef<SStateNode *> inList)
+ {
+ for (QT3DSU32 idx = 0, end = inList.size(); idx < end; ++idx)
+ if (IsDescendant(inParent, *inList[idx]) == false)
+ return false;
+ return true;
+ }
+ struct SRefListPlusOne
+ {
+ NVConstDataRef<SStateNode *> m_List;
+ SStateNode *m_Extra;
+
+ SRefListPlusOne(NVConstDataRef<SStateNode *> l, SStateNode *e)
+ : m_List(l)
+ , m_Extra(e)
+ {
+ }
+
+ QT3DSU32 size() { return m_List.size() + (m_Extra ? 1 : 0); }
+
+ SStateNode *operator[](int idx)
+ {
+ if (idx < (int)m_List.size())
+ return m_List[idx];
+ if (idx == (int)m_List.size())
+ return m_Extra;
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+ };
+
+ SStateNode *FindLCA(SStateNode &lhs, SStateNode &rhs)
+ {
+ QT3DSU32 sharedIdx = GetSharedParentState(lhs, rhs, false);
+ return *(m_ParentComparisonLHS.rbegin() + sharedIdx);
+ }
+
+ SStateNode *FindLCCA(NVConstDataRef<SStateNode *> inList, SStateNode *inExtra = NULL)
+ {
+ SRefListPlusOne theList(inList, inExtra);
+ if (theList.size() == 0)
+ return NULL;
+ if (theList.size() == 1) {
+ for (SStateNode *item = theList[0]; item; item = item->m_Parent) {
+ if (item->IsCompound())
+ return item;
+ }
+ }
+ SStateNode *lhs = theList[0];
+ for (QT3DSU32 idx = 1, end = theList.size(); idx < end; ++idx) {
+ SStateNode *rhs = theList[idx];
+ QT3DSU32 sharedIdx = GetSharedParentState(*lhs, *rhs, false);
+ lhs = *(m_ParentComparisonLHS.rbegin() + sharedIdx);
+ }
+ for (SStateNode *item = lhs; item; item = item->m_Parent) {
+ if (item->IsCompound())
+ return item;
+ }
+ QT3DS_ASSERT(false);
+ return m_Context->GetRoot();
+ }
+ NVDataRef<SStateNode *> GetProperAncestors(SStateNode &inChild, SStateNode *inStop)
+ {
+ m_AncestorsList.clear();
+ for (SStateNode *parent = inChild.m_Parent; parent && parent != inStop;
+ parent = parent->m_Parent)
+ m_AncestorsList.push_back(parent);
+ return m_AncestorsList;
+ }
+
+ bool AddStateToEnterToSet(SStateNode &inNode)
+ {
+ if (m_Configuration.contains(&inNode) == false) {
+ m_StatesToEnter.insert(&inNode);
+ return true;
+ }
+ return false;
+ }
+
+ void AddStatesToEnter(SHistory &inState)
+ {
+ TStateNodeNodeList *history = GetHistoryData(inState);
+ if (history) {
+ for (TStateNodeNodeList::iterator iter = history->begin(), end = history->end();
+ iter != end; ++iter) {
+ AddStatesToEnter(*iter->m_Node);
+ NVDataRef<SStateNode *> ancestors =
+ GetProperAncestors(*iter->m_Node, inState.m_Parent);
+ for (QT3DSU32 ancIdx = 0, ancEnd = ancestors.size(); ancIdx < ancEnd; ++ancIdx)
+ AddStateToEnterToSet(*ancestors[ancIdx]);
+ }
+ } else {
+ if (IsTransitionValid(inState.m_Transition)) {
+ NVConstDataRef<SStateNode *> theList = GetRefList(inState.m_Transition->m_Target);
+ for (QT3DSU32 idx = 0, end = theList.size(); idx < end; ++idx) {
+ AddStatesToEnter(*theList[idx]);
+ NVDataRef<SStateNode *> ancestors =
+ GetProperAncestors(*theList[idx], inState.m_Parent);
+ for (QT3DSU32 ancIdx = 0, ancEnd = ancestors.size(); ancIdx < ancEnd; ++ancIdx)
+ AddStateToEnterToSet(*ancestors[ancIdx]);
+ }
+ } else {
+ qCCritical(INVALID_OPERATION,
+ "History node %s with no history, no transition, or invalid transition visited",
+ inState.m_Id.c_str());
+ SStateNode *theParent = inState.m_Parent;
+ if (theParent != NULL) {
+ if (theParent->m_Type != StateNodeTypes::Parallel) {
+ NVConstDataRef<SStateNode *> theDefaultInitial(
+ GetDefaultInitialState(*theParent));
+
+ QT3DS_ASSERT(theDefaultInitial.size() != 0);
+ for (QT3DSU32 idx = 0, end = theDefaultInitial.size(); idx < end; ++idx)
+ AddStatesToEnter(*theDefaultInitial[idx]);
+
+ EnterAncestors(theDefaultInitial, theParent);
+ } else {
+ SParallel *pstate = theParent->CastTo<SParallel>();
+ for (TStateNodeList::iterator iter = pstate->m_Children.begin(),
+ end = pstate->m_Children.end();
+ iter != end; ++iter) {
+ if (StateNodeTypes::IsStateType(iter->m_Type))
+ AddStatesToEnter(*iter);
+ }
+ }
+ } else {
+ QT3DS_ASSERT(false); // invalid configuration
+ }
+ }
+ }
+ }
+ NVConstDataRef<SStateNode *> GetDefaultInitialState(SStateNode &inState)
+ {
+ TStateNodeList *theChildren = inState.GetChildren();
+ NVConstDataRef<SStateNode *> retval;
+ if (theChildren) {
+ for (TStateNodeList::iterator iter = theChildren->begin(), end = theChildren->end();
+ iter != end && retval.size() == 0; ++iter) {
+ if (iter->m_Type == StateNodeTypes::State
+ || iter->m_Type == StateNodeTypes::Parallel
+ || iter->m_Type == StateNodeTypes::Final) {
+ SStateNode **newData =
+ reinterpret_cast<SStateNode **>(m_TemporaryAllocator.allocate(
+ sizeof(SStateNode *), "TempNode", __FILE__, __LINE__));
+ newData[0] = &(*iter);
+ retval = toDataRef(newData, 1);
+ }
+ }
+ }
+ return retval;
+ }
+
+ STransition *GetStateInitialTransition(SStateNode &inNode)
+ {
+ // Initialexpr, if it exists, takes precedence over the initial transition.
+ // they should not both exist but coding defensively, we have to take this into account.
+ eastl::pair<nvhash_map<SStateNode *, STransition *>::iterator, bool> inserter =
+ m_StateInitialTransitionMap.insert(eastl::make_pair(&inNode, (STransition *)NULL));
+ if (inserter.second) {
+ const char8_t *initialExpr = inNode.GetInitialExpression();
+ if (!isTrivial(initialExpr)) {
+ STransition *newTransition = &CreateTemporaryTransition();
+ newTransition->m_Parent = &inNode;
+ inserter.first->second = newTransition;
+ SScriptExecutionResult exprResultData =
+ m_ScriptContext->ExecuteExpressionToString(initialExpr);
+ if (exprResultData.Valid()) {
+ const char8_t *exprResult(exprResultData.Result());
+ // split this string into parts and extract ids.
+ m_IdSplitter.assign(nonNull(exprResult));
+ const char8_t *whitespaceData = " \n\t\r";
+ size_t charPos = m_IdSplitter.find_first_not_of(whitespaceData);
+ if (charPos == eastl::string::npos) {
+ m_IdSplitter.clear();
+ m_Workspace.clear();
+ }
+ if (charPos != 0)
+ m_IdSplitter.erase(m_IdSplitter.begin(), m_IdSplitter.begin() + charPos);
+ // Loop runs under assumption that idSplitter is empty or position 0 holds start
+ // of next id
+ while (m_IdSplitter.size()) {
+ // Trim to first character
+ eastl::string::size_type spacePos =
+ m_IdSplitter.find_first_of(whitespaceData);
+
+ if (spacePos != eastl::string::npos) {
+ charPos = m_IdSplitter.find_first_not_of(whitespaceData, spacePos);
+ m_Workspace = m_IdSplitter.c_str();
+ m_Workspace.resize(spacePos);
+ if (charPos != eastl::string::npos)
+ m_IdSplitter.erase(m_IdSplitter.begin(),
+ m_IdSplitter.begin() + charPos);
+ else
+ m_IdSplitter.clear();
+ } else {
+ m_Workspace = m_IdSplitter;
+ m_IdSplitter.clear();
+ }
+
+ if (m_Workspace.empty() == false) {
+ CRegisteredString stateId =
+ m_StringTable->RegisterStr(m_Workspace.c_str());
+ qt3ds::state::SStateNode *transitionNode =
+ m_Context->FindStateNode(stateId);
+ if (!transitionNode) {
+ m_TempNodeList.clear();
+ m_IdSplitter.clear();
+ qCCritical(INVALID_OPERATION,
+ "initialexpr=\"%s\" evaluated to \"%s\", but "
+ "this does not match the states in this "
+ "document. Using the default initial state "
+ "instead.",
+ nonNull(initialExpr), nonNull(exprResult));
+
+ eastl::string errorBuf;
+ IScriptEvent *newEvent = m_ScriptContext->CreateScriptEvent(
+ m_StringTable->RegisterStr("error.execution.initialexpr"));
+ newEvent->SetParamStr(m_StringTable->RegisterStr("expr"),
+ nonNull(initialExpr));
+ errorBuf.assign(nonNull(exprResult));
+ errorBuf.append(" does not match the states in this document.");
+ newEvent->SetParamStr(m_StringTable->RegisterStr("error"),
+ errorBuf.c_str());
+ QueueEvent(*newEvent, false);
+ } else
+ m_TempNodeList.push_back(transitionNode);
+ }
+ }
+ if (m_TempNodeList.empty() == false) {
+ QT3DSU32 allocSize = sizeof(SStateNode *) * m_TempNodeList.size();
+ SStateNode **nodeData =
+ reinterpret_cast<SStateNode **>(m_TemporaryAllocator.allocate(
+ allocSize, "TempNodes", __FILE__, __LINE__));
+ memCopy(nodeData, m_TempNodeList.data(), allocSize);
+ newTransition->m_Target = toDataRef(nodeData, m_TempNodeList.size());
+ m_TempNodeList.clear();
+ }
+ if (newTransition->m_Target.size() != 0) {
+ bool isTransValid = ValidateTransitionTargetList(*newTransition, inNode, 0);
+ if (!isTransValid) {
+ m_InvalidTransitions.erase(newTransition);
+ // Reset the transition so that we get just the default initial state
+ // below
+ newTransition->m_Target = NVConstDataRef<SStateNode *>();
+ // Create appropriate messages and events.
+ qCCritical(INVALID_OPERATION,
+ "initialexpr=\"%s\" evaluated to \"%s\", but this "
+ "results in an invalid transition. Using the "
+ "default initial state instead.",
+ nonNull(initialExpr), nonNull(exprResult));
+ eastl::string errorBuf;
+ IScriptEvent *newEvent = m_ScriptContext->CreateScriptEvent(
+ m_StringTable->RegisterStr("error.execution.initialexpr"));
+ newEvent->SetParamStr(m_StringTable->RegisterStr("expr"),
+ nonNull(initialExpr));
+ errorBuf.assign(nonNull(exprResult));
+ errorBuf.append(" results in invalid transition.");
+ newEvent->SetParamStr(m_StringTable->RegisterStr("error"),
+ errorBuf.c_str());
+ QueueEvent(*newEvent, false);
+ }
+ }
+ } // if script executed successfully
+ else {
+ const char8_t *runtimeError = exprResultData.Error();
+ IScriptEvent *newEvent = m_ScriptContext->CreateScriptEvent(
+ m_StringTable->RegisterStr("error.execution.initialexpr"));
+ newEvent->SetParamStr(m_StringTable->RegisterStr("expr"), nonNull(initialExpr));
+ newEvent->SetParamStr(m_StringTable->RegisterStr("error"),
+ nonNull(runtimeError));
+ QueueEvent(*newEvent, false);
+ }
+ if (newTransition->m_Target.size() == 0) {
+ newTransition->m_Target = GetDefaultInitialState(inNode);
+ }
+ }
+ if (inserter.first->second == NULL) {
+ // Assume already validated
+ inserter.first->second = inNode.GetInitialTransition();
+ }
+ }
+ return inserter.first->second;
+ }
+
+ void AddStatesToEnter(SStateNode &inState)
+ {
+ if (inState.m_Type == StateNodeTypes::History)
+ AddStatesToEnter(*inState.CastTo<SHistory>());
+ else {
+ AddStateToEnterToSet(inState);
+ if (inState.IsCompound()) {
+ m_StatesForDefaultEntry.insert(&inState);
+ NVConstDataRef<SStateNode *> targets;
+ STransition *initialTrans = GetStateInitialTransition(inState);
+ if (IsTransitionValid(initialTrans))
+ targets = GetTargetStates(initialTrans);
+
+ if (targets.size() == 0)
+ targets = GetDefaultInitialState(inState);
+
+ QT3DS_ASSERT(targets.size() != 0);
+ for (QT3DSU32 idx = 0, end = targets.size(); idx < end; ++idx)
+ AddStatesToEnter(*targets[idx]);
+
+ EnterAncestors(targets, &inState);
+ } else if (inState.m_Type == StateNodeTypes::Parallel) {
+ SParallel *pstate = inState.CastTo<SParallel>();
+ for (TStateNodeList::iterator iter = pstate->m_Children.begin(),
+ end = pstate->m_Children.end();
+ iter != end; ++iter) {
+ if (StateNodeTypes::IsStateType(iter->m_Type))
+ AddStatesToEnter(*iter);
+ }
+ }
+ }
+ }
+ bool AllChildrenInFinalStates(SStateNode &inNode)
+ {
+ TStateNodeList *children = inNode.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter)
+ if (!IsInFinalState(*iter))
+ return false;
+ return true;
+ }
+ return true;
+ }
+
+ void EraseState(nvvector<SStateNode *> &inList, SStateNode *inItem)
+ {
+ nvvector<SStateNode *>::iterator iter = eastl::find(inList.begin(), inList.end(), inItem);
+ if (iter != inList.end())
+ inList.erase(iter);
+ }
+
+ void ExitStates(NVDataRef<STransition *> inTransitions)
+ {
+ m_StatesToExit.clear();
+ for (QT3DSU32 idx = 0, end = inTransitions.size(); idx < end; ++idx) {
+ STransition &transition(*inTransitions[idx]);
+ if (transition.GetSource() == NULL) {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+ NVConstDataRef<SStateNode *> theList = GetRefList(transition.m_Target);
+ SStateNode *ancestor = GetTransitionSubgraphRoot(transition, theList);
+ if (theList.size() && transition.GetSource()) {
+ for (QT3DSU32 idx = 0, end = m_Configuration.size(); idx < end; ++idx)
+ if (IsDescendant(*ancestor, *m_Configuration[idx]))
+ m_StatesToExit.insert(m_Configuration[idx]);
+ }
+ }
+
+ /*
+ for s in m_StatesToExit:
+ statesToInvoke.delete(s)
+ */
+
+ SortByDocumentOrder(m_StatesToExit.m_List);
+ DEBUGGER_CALL1(SetExitSet, m_StatesToExit);
+ // Run through the list in reverse order.
+ for (nvvector<SStateNode *>::reverse_iterator iter = m_StatesToExit.m_List.rbegin(),
+ end = m_StatesToExit.m_List.rend();
+ iter != end; ++iter) {
+#ifdef QT3DS_LOG_ENTER_EXIT
+ qCInfo(TRACE_INFO, "Exiting state: %s", (*iter)->m_Id.c_str());
+#endif
+ TStateNodeList *children = (*iter)->GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (iter->m_Type == StateNodeTypes::History) {
+ SHistory &theHistory = *iter->CastTo<SHistory>();
+ FreeHistoryData(theHistory);
+ TStateNodeNodeList &theHistoryData = GetOrCreateHistoryData(theHistory);
+
+ if (theHistory.m_Parent == NULL) {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+
+ // If deep, then record the leaves
+ if (theHistory.m_Flags.IsDeep()) {
+ for (TStateNodeSet::iterator configIter = m_Configuration.begin(),
+ configEnd = m_Configuration.end();
+ configIter != configEnd; ++configIter) {
+ if ((*configIter)->IsAtomic()
+ && IsDescendant(*theHistory.m_Parent, **configIter))
+ theHistoryData.push_back(*m_StateNodeNodePool.construct(
+ *configIter, __FILE__, __LINE__));
+ }
+ }
+ // Else record what may be branches.
+ else {
+ for (TStateNodeSet::iterator configIter = m_Configuration.begin(),
+ configEnd = m_Configuration.end();
+ configIter != configEnd; ++configIter) {
+ if ((*configIter)->m_Parent == theHistory.m_Parent)
+ theHistoryData.push_back(*m_StateNodeNodePool.construct(
+ *configIter, __FILE__, __LINE__));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (nvvector<SStateNode *>::reverse_iterator iter = m_StatesToExit.m_List.rbegin(),
+ end = m_StatesToExit.m_List.rend();
+ iter != end; ++iter) {
+ SStateNode *theState = *iter;
+ TOnExitList *theExitList = theState->GetOnExitList();
+ if (theExitList)
+ m_ExecutionContext->Execute(*theState, *theExitList);
+ for (QT3DSU32 idx = 0, end = m_SignalSenders.size(); idx < end; ++idx)
+ m_SignalSenders[idx]->m_Handler.OnInterpreterEvent(InterpreterEventTypes::StateExit,
+ theState->m_Id);
+ EraseState(m_StatesToInvoke, theState);
+ m_Configuration.erase(theState);
+ }
+ }
+
+ void EnterAncestor(SStateNode &inNode)
+ {
+ m_StatesToEnter.insert(&inNode);
+ if (inNode.m_Type == StateNodeTypes::Parallel) {
+ SParallel *anc = inNode.CastTo<SParallel>();
+ for (TStateNodeList::iterator childIter = anc->m_Children.begin(),
+ endChild = anc->m_Children.end();
+ childIter != endChild; ++childIter) {
+ if (!StateNodeTypes::IsStateType(childIter->m_Type))
+ continue;
+ bool hasDescendent = false;
+ for (TStateNodeSet::iterator existingIter = m_StatesToEnter.begin(),
+ existingEnd = m_StatesToEnter.end();
+ existingIter != existingEnd && hasDescendent == false; ++existingIter) {
+ hasDescendent = IsDescendant(*childIter, *(*existingIter));
+ }
+ if (hasDescendent == false)
+ AddStatesToEnter(*childIter);
+ }
+ }
+ }
+
+ void EnterAncestors(NVConstDataRef<SStateNode *> inList, SStateNode *ancestor)
+ {
+ for (QT3DSU32 idx = 0, end = inList.size(); idx < end; ++idx) {
+ NVDataRef<SStateNode *> theAncestors = GetProperAncestors(*inList[idx], ancestor);
+ for (QT3DSU32 ancIdx = 0, ancEnd = theAncestors.size(); ancIdx < ancEnd; ++ancIdx) {
+ EnterAncestor(*theAncestors[ancIdx]);
+ // Break earlier if we have gone back and included the ancestor.
+ if (theAncestors[ancIdx] == ancestor)
+ break;
+ }
+ }
+ }
+
+ void EnterStates(NVDataRef<STransition *> inTransitions)
+ {
+ m_StatesToEnter.clear();
+ m_StatesForDefaultEntry.clear();
+
+ for (QT3DSU32 idx = 0, end = inTransitions.size(); idx < end; ++idx) {
+ STransition &transition = *inTransitions[idx];
+ NVConstDataRef<SStateNode *> theList = GetRefList(transition.m_Target);
+ if (transition.GetSource() == NULL) {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+
+ // Multi-target transitions present their own set of problems. One is that a perfectly
+ // valid
+ // multi-target transition may
+ if (theList.size() > 1) {
+ m_TransitionTargetList.clear();
+ // We have to ensure that if two targets are directly related, we take the most
+ // derived one.
+ for (QT3DSU32 targetIdx = 0, targetEnd = theList.size(); targetIdx < targetEnd;
+ ++targetIdx) {
+ SStateNode *nextTarget = theList[targetIdx];
+ for (QT3DSU32 takenTargetListIdx = 0,
+ takenTargetListEnd = m_TransitionTargetList.size();
+ takenTargetListIdx < takenTargetListEnd && nextTarget;
+ ++takenTargetListIdx) {
+ SStateNode *previousTarget = m_TransitionTargetList[takenTargetListIdx];
+ // If the previous target is more descendant than the original target
+ if (IsDescendant(*nextTarget, *previousTarget)) {
+ // Then we don't need to consider next target at all and we can
+ // continue.
+ nextTarget = NULL;
+ } else if (IsDescendant(*previousTarget, *nextTarget)) {
+ // If next target derives from previous target, then we remove previous
+ // target from
+ // the list.
+ m_TransitionTargetList.erase(m_TransitionTargetList.begin()
+ + takenTargetListIdx);
+ --takenTargetListIdx;
+ takenTargetListEnd = m_TransitionTargetList.size();
+ }
+ // Else we don't care, we will add next target to the list.
+ }
+ if (nextTarget != NULL)
+ m_TransitionTargetList.push_back(nextTarget);
+ }
+ theList = m_TransitionTargetList;
+ }
+
+ for (QT3DSU32 idx = 0, end = theList.size(); idx < end; ++idx)
+ AddStatesToEnter(*theList[idx]);
+ }
+
+ for (QT3DSU32 idx = 0, end = inTransitions.size(); idx < end; ++idx) {
+ STransition &transition = *inTransitions[idx];
+ NVConstDataRef<SStateNode *> theList = GetRefList(transition.m_Target);
+
+ if (transition.GetSource() == NULL) {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+
+ SStateNode *ancestor = GetTransitionSubgraphRoot(transition, theList);
+ EnterAncestors(theList, ancestor);
+ }
+ EnterStatesSecondHalf();
+ }
+
+ void EnterStatesSecondHalf()
+ {
+ nvvector<SStateNode *> &theEnterList(m_StatesToEnter.m_List);
+ SortByDocumentOrder(theEnterList);
+
+ DEBUGGER_CALL1(SetEnterSet, m_StatesToEnter);
+ for (QT3DSU32 idx = 0, end = theEnterList.size(); idx < end; ++idx) {
+ SStateNode *theEnterState(theEnterList[idx]);
+#ifdef QT3DS_LOG_ENTER_EXIT
+ qCInfo(TRACE_INFO, "Entering state: %s", theEnterState->m_Id.c_str());
+#endif
+ m_Configuration.insert(theEnterState);
+ m_StatesToInvoke.push_back(theEnterState);
+ SStateNodeInterpreterData &theData = GetOrCreateInterpreterData(*theEnterState);
+ if (theData.m_Entered == false) {
+ theData.m_Entered = true;
+ if (m_Context->GetRoot()->m_Flags.IsLateBinding()) {
+ SDataModel *theDataModel = theEnterState->GetDataModel();
+ if (theDataModel)
+ RunDataModel(*theDataModel);
+ }
+ }
+
+ TOnEntryList *theList = theEnterState->GetOnEntryList();
+ if (theList)
+ m_ExecutionContext->Execute(*theEnterState, *theList);
+ for (QT3DSU32 idx = 0, end = m_SignalSenders.size(); idx < end; ++idx)
+ m_SignalSenders[idx]->m_Handler.OnInterpreterEvent(
+ InterpreterEventTypes::StateEnter, theEnterState->m_Id);
+
+ if (m_StatesForDefaultEntry.contains(theEnterState)) {
+ SState *theState = theEnterState->CastTo<SState>();
+ if (theState && IsTransitionValid(theState->m_Initial))
+ m_ExecutionContext->Execute(*theState->m_Initial);
+ }
+ if (theEnterState->m_Type == StateNodeTypes::Final) {
+ SStateNode *parent = theEnterState->m_Parent;
+ SStateNode *grandparent = parent->m_Parent;
+ if (parent && grandparent) {
+ m_Workspace.assign("done.state.");
+ m_Workspace.append(parent->m_Id);
+
+ // TODO - donedata
+ QueueEvent(m_Workspace.c_str(), false);
+
+ if (grandparent && grandparent->m_Type == StateNodeTypes::Parallel
+ && AllChildrenInFinalStates(*grandparent)) {
+ m_Workspace.assign("done.state.");
+ m_Workspace.append(grandparent->m_Id);
+ QueueEvent(m_Workspace.c_str(), false);
+ }
+ }
+ }
+ }
+
+ if (IsInFinalState(m_Context->GetRoot()))
+ m_Running = false;
+ SortConfiguration();
+ }
+
+ bool InConfiguration(SStateNode &inState)
+ {
+ return eastl::find(m_Configuration.begin(), m_Configuration.end(), &inState)
+ != m_Configuration.end();
+ }
+
+ bool IsInFinalState(SStateNode &inState)
+ {
+ if (inState.m_Type == StateNodeTypes::State && inState.IsCompound()) {
+ SState *theState = inState.CastTo<SState>();
+ for (TStateNodeList::iterator childIter = theState->m_Children.begin(),
+ endIter = theState->m_Children.end();
+ childIter != endIter; ++childIter) {
+ if (IsInFinalState(*childIter))
+ return true;
+ }
+ } else if (inState.m_Type == StateNodeTypes::SCXML) {
+ SSCXML *theState = inState.CastTo<SSCXML>();
+ for (TStateNodeList::iterator childIter = theState->m_Children.begin(),
+ endIter = theState->m_Children.end();
+ childIter != endIter; ++childIter) {
+ if (childIter->m_Type == StateNodeTypes::Final && InConfiguration(*childIter))
+ return true;
+ }
+ } else if (inState.m_Type == StateNodeTypes::Parallel) {
+ SParallel *parallel = inState.CastTo<SParallel>();
+ for (TStateNodeList::iterator childIter = parallel->m_Children.begin(),
+ endIter = parallel->m_Children.end();
+ childIter != endIter; ++childIter) {
+ if (!IsInFinalState(*childIter))
+ return false;
+ }
+ return true;
+ } else if (inState.m_Type == StateNodeTypes::Final && InConfiguration(inState))
+ return true;
+
+ return false;
+ }
+
+ bool IsInFinalState(SStateNode *inState)
+ {
+ if (inState)
+ return IsInFinalState(*inState);
+ return false;
+ }
+
+ SStateNode *GetTransitionSubgraphRoot(STransition &inTransition,
+ NVConstDataRef<SStateNode *> inTargetList)
+ {
+ SStateNodeInterpreterData &data = GetOrCreateInterpreterData(inTransition);
+ if (data.m_TransitionSubgraphRoot != NULL)
+ return data.m_TransitionSubgraphRoot;
+ if (inTransition.GetSource() == NULL) {
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+ SStateNode &source = *inTransition.GetSource();
+ if (inTransition.m_Target.size() == 0)
+ data.m_TransitionSubgraphRoot = &source;
+ else {
+ if (inTransition.m_Flags.IsInternal() && source.IsCompound()
+ && AllAreDescendants(source, inTargetList))
+ data.m_TransitionSubgraphRoot = &source;
+ else
+ data.m_TransitionSubgraphRoot = FindLCCA(inTargetList, &source);
+ }
+
+ return data.m_TransitionSubgraphRoot;
+ }
+ SStateNode *GetTransitionSubgraphRoot(STransition &inTransition)
+ {
+ SStateNodeInterpreterData &data = GetOrCreateInterpreterData(inTransition);
+ if (data.m_TransitionSubgraphRoot != NULL)
+ return data.m_TransitionSubgraphRoot;
+
+ return GetTransitionSubgraphRoot(inTransition, GetRefList(inTransition.m_Target));
+ }
+ enum PreemptRule { AddNew, KeepExisting, ReplaceExisting };
+
+ PreemptRule IsPreempted(STransition &existing, STransition &nextTransition)
+ {
+ // Targetless transitions can get preempted and can preempt
+ SStateNode *existingRoot = GetTransitionSubgraphRoot(existing);
+ SStateNode *nextRoot = GetTransitionSubgraphRoot(nextTransition);
+ /*
+ http://www.w3.org/TR/scxml/#SelectingTransitions
+
+ A transition T is optimally enabled by event E in atomic state S if
+ a) T is enabled by E in S and
+ b) no transition that precedes T in document order in T's source state is enabled by E in S
+ and
+ c) no transition is enabled by E in S in any descendant of T's source state.
+ */
+
+ // static bool IsDescendant( SStateNode& inParent, SStateNode& inChild )
+
+ if (IsDescendant(*nextRoot, *existingRoot))
+ return KeepExisting;
+ if (IsDescendant(*existingRoot, *nextRoot))
+ return ReplaceExisting;
+
+ // Else these transactions are completely unrelated
+ return AddNew;
+ }
+
+ void FilterPreempted(TTransitionSet &inTransitionSet, TTransitionList &outTransitions)
+ {
+ outTransitions.clear();
+ for (TTransitionSet::iterator iter = inTransitionSet.begin(), end = inTransitionSet.end();
+ iter != end; ++iter) {
+ PreemptRule preempted = AddNew;
+ QT3DSU32 idx, endIdx;
+ for (idx = 0, endIdx = outTransitions.size(); idx < endIdx && preempted == AddNew;
+ ++idx)
+ preempted = IsPreempted(*outTransitions[idx], **iter);
+
+ switch (preempted) {
+ case KeepExisting: // Ignore the result.
+ break;
+ case ReplaceExisting:
+ // The iteration statement is evaluated before the exit test.
+ outTransitions[idx - 1] = *iter;
+ break;
+ case AddNew:
+ outTransitions.push_back(*iter);
+ break;
+ }
+ }
+ }
+
+ bool SelectEventlessTransitions(SStateNode &inNode, TTransitionSet &inSet)
+ {
+ TStateNodeList *theChildren = inNode.GetChildren();
+ if (theChildren == NULL) {
+ return false;
+ }
+ for (TStateNodeList::iterator iter = theChildren->begin(), end = theChildren->end();
+ iter != end; ++iter) {
+ if (iter->m_Type == StateNodeTypes::Transition) {
+ STransition &trans = *iter->CastTo<STransition>();
+ if (IsTransitionValid(&trans)) {
+ if (trans.m_Event.IsValid() == false) {
+ if (!isTrivial(trans.m_Condition)) {
+ Option<bool> condEval =
+ m_ScriptContext->ExecuteCondition(trans.m_Condition);
+ if (condEval.hasValue()) {
+ if (*condEval) {
+ inSet.insert(&trans);
+ return true;
+ }
+ } else {
+ QueueEvent("error.execution", false);
+ return false;
+ }
+ }
+ // Extension for running scxml documents with transitions with nothing but
+ // content
+ else {
+ inSet.insert(&trans);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ if (inNode.m_Parent)
+ SelectEventlessTransitions(*inNode.m_Parent, inSet);
+ return false;
+ }
+
+ // Precondition - m_Configuration is in document order.
+ // Postcondition - m_EnabledTransitions contains only the transitions selected
+ void SelectEventlessTransitions()
+ {
+ m_TransitionSet.clear();
+ static QT3DSU32 callCount = 0;
+ ++callCount;
+ for (QT3DSU32 idx = 0, end = m_Configuration.size(); idx < end; ++idx) {
+ if (idx) {
+ QT3DS_ASSERT(GetOrCreateInterpreterData(*m_Configuration[idx]).m_DocumentOrder
+ > GetOrCreateInterpreterData(*m_Configuration[idx - 1]).m_DocumentOrder);
+ }
+ if (m_Configuration[idx]->IsAtomic() == false)
+ continue;
+ SelectEventlessTransitions(*m_Configuration[idx], m_TransitionSet);
+ }
+ m_EnabledTransitions.clear();
+ FilterPreempted(m_TransitionSet, m_EnabledTransitions);
+ }
+ void SelectTransition(SStateNode &inNode, IEvent &inEvent, TTransitionSet &outTransitions)
+ {
+ TStateNodeList *theChildren = inNode.GetChildren();
+ if (theChildren) {
+ for (TStateNodeList::iterator iter = theChildren->begin(), end = theChildren->end();
+ iter != end; ++iter) {
+ if (iter->m_Type == StateNodeTypes::Transition) {
+ STransition &theTransition = *iter->CastTo<STransition>();
+ if (IsTransitionValid(theTransition)) {
+ if (theTransition.m_Event.IsValid()) {
+ if (qt3ds::state::impl::NameMatches(theTransition.m_Event.c_str(),
+ inEvent.GetName().c_str())) {
+ if (!isTrivial(theTransition.m_Condition)) {
+ Option<bool> condResult = m_ScriptContext->ExecuteCondition(
+ theTransition.m_Condition);
+ if (condResult.hasValue()) {
+ if (*condResult) {
+ outTransitions.insert(&theTransition);
+ return;
+ }
+ } else {
+ QueueEvent("error.execution", false);
+ return;
+ }
+ } else {
+ outTransitions.insert(&theTransition);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (inNode.m_Parent)
+ SelectTransition(*inNode.m_Parent, inEvent, outTransitions);
+ }
+
+ // Precondition is that m_Configuration is in document order.
+ void SelectTransitions(IEvent &inEvent)
+ {
+ m_TransitionSet.clear();
+ for (QT3DSU32 idx = 0, end = m_Configuration.size(); idx < end; ++idx) {
+ // Ensure that m_Configuration is in document order.
+ if (idx) {
+ QT3DS_ASSERT(GetOrCreateInterpreterData(*m_Configuration[idx]).m_DocumentOrder
+ > GetOrCreateInterpreterData(*m_Configuration[idx - 1]).m_DocumentOrder);
+ }
+
+ if (!m_Configuration[idx]->IsAtomic())
+ continue;
+ SelectTransition(*m_Configuration[idx], inEvent, m_TransitionSet);
+ }
+
+ FilterPreempted(m_TransitionSet, m_EnabledTransitions);
+ }
+
+ bool Initialize(IStateContext &inContext, bool inValidateTransitions) override
+ {
+ m_Initialized = false;
+ InitializeDataStructures();
+ m_Context = inContext;
+ QT3DS_ASSERT(m_Context->GetRoot());
+ if (m_Context->GetRoot() == NULL) {
+ QT3DS_ASSERT(false);
+ m_Running = false;
+ return false;
+ }
+
+ // Disable transitions that can put us into a bad state
+ if (inValidateTransitions)
+ RecursiveValidateTransitions();
+ m_Initialized = true;
+ return true;
+ }
+ NVConstDataRef<SStateNode *> Start() override
+ {
+ if (!m_Initialized) {
+ QT3DS_ASSERT(false);
+ return NVConstDataRef<SStateNode *>();
+ }
+ m_Running = true;
+ SSCXML &rootState = *m_Context->GetRoot();
+ if (!rootState.m_Flags.IsLateBinding())
+ RecursiveInitializeDataModel();
+ eastl::string theSendLocExpr;
+ NVConstDataRef<SSend *> theSendData = m_Context->GetSendList();
+ for (QT3DSU32 idx = 0, end = theSendData.size(); idx < end; ++idx) {
+ SSend &theSend(*theSendData[idx]);
+ if (!isTrivial(theSend.m_IdLocation)) {
+ QT3DS_ASSERT(theSend.m_Id.IsValid());
+ m_ScriptContext->AssignStr(theSend.m_IdLocation, theSend.m_Id.c_str());
+ }
+ }
+
+ STransition *initialTrans = this->GetStateInitialTransition(rootState);
+ if (IsTransitionValid(initialTrans)) {
+ m_Configuration.insert(&rootState);
+ m_ExecutionContext->Execute(*initialTrans);
+ AddStatesToEnter(rootState);
+ EnterAncestors(initialTrans->m_Target, &rootState);
+ QT3DS_ASSERT(m_EnabledTransitions.empty());
+ m_EnabledTransitions.clear();
+ m_EnabledTransitions.push_back(initialTrans);
+ DEBUGGER_CALL1(SetTransitionSet, m_EnabledTransitions);
+ m_EnabledTransitions.clear();
+ EnterStatesSecondHalf();
+ ReleaseInitialAndTemporaryTransitions();
+ DEBUGGER_CALL(EndMicrostep);
+ DEBUGGER_CALL(EndStep);
+ } else {
+ m_Running = false;
+ qFatal("Invalid state machine: root initial configuration is invalid");
+ }
+ return m_Configuration.m_List;
+ }
+
+ void Microstep(TTransitionList &inTransitions)
+ {
+ DEBUGGER_CALL1(SetTransitionSet, inTransitions);
+ ExitStates(inTransitions);
+ for (QT3DSU32 idx = 0, end = inTransitions.size(); idx < end; ++idx) {
+ m_ExecutionContext->Execute(*inTransitions[idx]);
+ for (QT3DSU32 sigIdx = 0, sigEnd = m_SignalSenders.size(); sigIdx < sigEnd; ++sigIdx)
+ m_SignalSenders[sigIdx]->m_Handler.OnInterpreterEvent(
+ InterpreterEventTypes::Transition, inTransitions[idx]->m_Id);
+ }
+
+ EnterStates(inTransitions);
+ // Allow them to be selected again at some point.
+ inTransitions.clear();
+ ReleaseInitialAndTemporaryTransitions();
+ DEBUGGER_CALL(EndMicrostep);
+ }
+
+ void CheckForDelayedEvents()
+ {
+ QT3DSU64 currentTime = Time::getCurrentTimeInTensOfNanoSeconds();
+ TFutureEventList::iterator removeIter = m_FutureEvents.begin();
+ TFutureEventList::iterator endIter = m_FutureEvents.end();
+ // Future events list is sorted so all we have to do is iterate forward until
+ // fire time is greater than current time.
+
+ for (; removeIter != endIter && removeIter->m_FireTime < currentTime; ++removeIter) {
+ }
+ // remove iter points either to the end or to the first event who's time has come.
+ for (TFutureEventList::iterator evtIter = m_FutureEvents.begin(); evtIter != removeIter;
+ ++evtIter) {
+ qCInfo(TRACE_INFO, "Sending delayed event: %s", evtIter->m_Event->GetName().c_str());
+ QT3DS_ASSERT(evtIter->m_FireTime <= currentTime);
+ QueueEvent(evtIter->m_Event, evtIter->m_IsExternal);
+ }
+ if (removeIter != m_FutureEvents.begin()) {
+ m_FutureEvents.erase(m_FutureEvents.begin(), removeIter);
+ }
+ }
+
+ bool CheckForStable()
+ {
+ CheckForDelayedEvents();
+ if (m_EnabledTransitions.empty()) {
+ m_ScriptContext->ClearCurrentEvent();
+ DEBUGGER_CALL(BeginMicrostep);
+ SelectEventlessTransitions();
+ }
+
+ return m_EnabledTransitions.empty() && m_InternalQueue.empty() && m_ExternalQueue.empty();
+ }
+
+ // Execute these events, return what state you are in.
+ // We process all internal and all external events before returning, so you are free to
+ // free or just deal with all userdata after this.
+ NVConstDataRef<SStateNode *> Execute() override
+ {
+ if (m_Running == false)
+ return m_Configuration.m_List;
+
+ DEBUGGER_CALL(BeginStep);
+ DEBUGGER_CALL(BeginMicrostep);
+
+ m_EnabledTransitions.clear();
+
+ QT3DSU32 iterationCount = 0;
+ QT3DSU32 MAX_ITERATION_COUNT = 1000;
+
+ // Here we handle eventless transitions and transitions
+ // triggered by internal events until machine is stable
+ while (iterationCount < MAX_ITERATION_COUNT && m_Running && !CheckForStable()) {
+ ++iterationCount;
+ if (m_EnabledTransitions.empty() == false) {
+ Microstep(m_EnabledTransitions);
+ DEBUGGER_CALL(BeginMicrostep);
+ } else if (m_InternalQueue.empty() == false) {
+ NVScopedRefCounted<IEvent> theEvent = m_InternalQueue.front();
+ m_ScriptContext->SetCurrentEvent(theEvent);
+ m_InternalQueue.pop_front();
+ DEBUGGER_CALL2(SetCurrentEvent, theEvent->GetName().c_str(), true);
+#ifdef QT3DS_LOG_ACTIVE_EVENT
+ qCInfo(TRACE_INFO, "Current event: %s", theEvent->GetName().c_str());
+#endif
+ SelectTransitions(*theEvent);
+ } else if (m_ExternalQueue.empty() == false) {
+ NVScopedRefCounted<IEvent> theEvent = m_ExternalQueue.front();
+ m_ScriptContext->SetCurrentEvent(theEvent);
+ m_ExternalQueue.pop_front();
+
+ DEBUGGER_CALL2(SetCurrentEvent, theEvent->GetName().c_str(), false);
+
+#ifdef QT3DS_LOG_ACTIVE_EVENT
+ qCInfo(TRACE_INFO, "Current event: %s", theEvent->GetName().c_str());
+#endif
+
+ /*
+ if isCancelEvent(externalEvent)
+ running = false
+ continue
+ */
+
+ // TODO datamodel
+ // datamodel["_event"] = theEvent;
+
+ // TODO invoke
+ /*
+ for state in configuration:
+ for inv in state.invoke:
+ if inv.invokeid == externalEvent.invokeid:
+ applyFinalize(inv, externalEvent)
+ if inv.autoforward:
+ send(inv.id, externalEvent)
+ */
+ SelectTransitions(*theEvent);
+ }
+ }
+ if (m_Running == false) {
+ for (nvvector<SStateNode *>::reverse_iterator iter = m_Configuration.m_List.rbegin(),
+ end = m_Configuration.m_List.rend();
+ iter != end; ++iter) {
+ TOnExitList *theExitList = (*iter)->GetOnExitList();
+ if (theExitList)
+ m_ExecutionContext->Execute(**iter, *theExitList);
+ /*
+ for inv in s.invoke:
+ cancelInvoke(inv)
+ */
+
+ // Set final done data.
+ /*
+ if isFinalState(s) and isScxmlState(s.parent):
+ returnDoneEvent(s.donedata)
+ */
+ }
+ }
+ DEBUGGER_CALL(EndStep);
+ return m_Configuration.m_List;
+ }
+
+ bool EventsPending() const override
+ {
+ return m_InternalQueue.empty() == false || m_ExternalQueue.empty() == false
+ || m_FutureEvents.empty() == false;
+ }
+
+ bool IsRunning() const override { return m_Running; }
+
+ void QueueEvent(const char8_t *inEventName, QT3DSU64 inDelay, CRegisteredString inCancelId,
+ bool inIsExternal) override
+ {
+ TEventPtr theEvent = QT3DS_NEW(m_Foundation.getAllocator(), SSimpleEvent)(
+ m_Foundation.getAllocator(), m_StringTable->RegisterStr(inEventName));
+ QueueEvent(theEvent, inDelay, inCancelId, inIsExternal);
+ }
+
+ void QueueEvent(TEventPtr inEvent, QT3DSU64 inDelay, CRegisteredString inCancelId,
+ bool inIsExternal) override
+ {
+ if (inDelay == 0) {
+ QueueEvent(inEvent, inIsExternal);
+ } else {
+ static QT3DSU64 sTensOfNanoSecondsInAMillisecond =
+ Time::sNumTensOfNanoSecondsInASecond / 1000;
+ QT3DSU64 fireTime = Time::getCurrentTimeInTensOfNanoSeconds()
+ + inDelay * sTensOfNanoSecondsInAMillisecond;
+ SFutureEvent theNewEvent(inEvent, fireTime, inCancelId, inIsExternal);
+ TFutureEventList::iterator iter =
+ eastl::upper_bound(m_FutureEvents.begin(), m_FutureEvents.end(), theNewEvent);
+ m_FutureEvents.insert(iter, theNewEvent);
+ }
+ }
+
+ void QueueEvent(TEventPtr inEvent, bool inIsExternal) override
+ {
+ if (inIsExternal)
+ m_ExternalQueue.push_back(inEvent);
+ else
+ m_InternalQueue.push_back(inEvent);
+ DEBUGGER_CALL2(EventQueued, inEvent->GetName().c_str(), inIsExternal);
+ }
+
+ void QueueEvent(const char8_t *inEventName, bool inIsExternal) override
+ {
+ TEventPtr theEvent = QT3DS_NEW(m_Foundation.getAllocator(), SSimpleEvent)(
+ m_Foundation.getAllocator(), m_StringTable->RegisterStr(inEventName));
+ QueueEvent(theEvent, inIsExternal);
+ }
+
+ void CancelEvent(CRegisteredString inCancelId) override
+ {
+ if (inCancelId.IsValid() == false)
+ return;
+ qCInfo(TRACE_INFO, "Cancel of id %s requested", inCancelId.c_str());
+ for (QT3DSU32 idx = 0; idx < m_FutureEvents.size(); ++idx) {
+ if (m_FutureEvents[idx].m_CancelId == inCancelId) {
+ TFutureEventList::iterator iter = m_FutureEvents.begin() + idx;
+ qCInfo(TRACE_INFO, "Cancelling event: %s, %s",
+ iter->m_Event->GetName().c_str(), iter->m_CancelId.c_str());
+ m_FutureEvents.erase(iter);
+ --idx;
+ }
+ }
+ }
+
+ TSignalConnectionPtr RegisterEventHandler(IStateInterpreterEventHandler &inHandler) override
+ {
+ SStateSignalSender *sender = QT3DS_NEW(m_Foundation.getAllocator(), SStateSignalSender)(
+ m_Foundation.getAllocator(), *this, inHandler);
+ m_SignalSenders.push_back(sender);
+ return sender;
+ }
+
+ IScriptContext &GetScriptContext() override { return *m_ScriptContext; }
+
+ IStateContext *GetStateContext() override { return m_Context; }
+
+ debugger::IStateMachineDebugInterface &GetDebugInterface() override { return m_DebugInterface; }
+
+ NVFoundationBase &GetFoundation() override { return m_Foundation; }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+// SDebugInterface implementation
+////////////////////////////////////////////////////////////////////////////////////
+
+void SDebugInterface::addRef()
+{
+ m_StateSystem.addRef();
+}
+
+void SDebugInterface::release()
+{
+ m_StateSystem.release();
+}
+
+struct SDebugStrOutStream : public IOutStream
+{
+ TDebugStr m_Str;
+ bool Write(NVConstDataRef<QT3DSU8> data) override
+ {
+ m_Str.append((char8_t *)data.begin(), (char8_t *)data.end());
+ return true;
+ }
+};
+
+NVConstDataRef<TDebugStr> ListToRef(const nvvector<SStateNode *> &inList, TDebugStrList &outData)
+{
+ outData.resize(inList.size());
+ for (QT3DSU32 idx = 0, end = inList.size(); idx < end; ++idx)
+ outData[idx].assign(nonNull(inList[idx]->m_Id.c_str()));
+ return toConstDataRef(outData.data(), outData.size());
+}
+
+IScriptContext &SDebugInterface::GetScriptContext()
+{
+ return *m_StateSystem.m_ScriptContext.mPtr;
+}
+
+// Functions implemented after StateSystem in order to take advantage of the StateSystem struct
+// members directly.
+void SDebugInterface::OnConnect()
+{
+ TDebugStr theFilename;
+ SDebugStrOutStream theOutStream;
+ if (m_StateSystem.m_Context->GetRoot())
+ theFilename.assign(nonNull(m_StateSystem.m_Context->GetRoot()->m_Filename));
+ CXMLIO::SaveSCXMLFile(*m_StateSystem.m_Context, m_StateSystem.m_Foundation,
+ *m_StateSystem.m_StringTable, theOutStream);
+ m_Listener->OnConnect(theFilename, theOutStream.m_Str,
+ ListToRef(m_StateSystem.m_Configuration.m_List, m_EnterList));
+}
+
+// Functions should not be called in there is not a valid listener
+void SDebugInterface::BeginStep()
+{
+ m_Listener->BeginStep();
+}
+
+void SDebugInterface::BeginMicrostep()
+{
+ m_Listener->BeginMicroStep();
+}
+
+void SDebugInterface::SetCurrentEvent(const char8_t *inEventName, bool inInternal)
+{
+ m_TempStr.assign(nonNull(inEventName));
+ m_Listener->SetEvent(m_TempStr, inInternal);
+}
+
+STransitionId TransitionToId(const STransition &inTransition)
+{
+ SStateNode *parent(inTransition.m_Parent);
+ STransitionId retval;
+ if (parent == NULL) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+
+ retval.m_StateId.assign(nonNull(parent->m_Id.c_str()));
+ if (&inTransition == parent->GetInitialTransition())
+ retval.m_TransitionIndex = -1;
+ else if (parent->m_Type == StateNodeTypes::History) {
+ SHistory &theHistory = static_cast<SHistory &>(*parent);
+ if (&inTransition == theHistory.m_Transition)
+ retval.m_TransitionIndex = -1;
+ }
+
+ if (retval.m_TransitionIndex == -2) {
+ TStateNodeList *childList = parent->GetChildren();
+ if (childList) {
+ QT3DSI32 index = 0;
+ for (TStateNodeList::iterator iter = childList->begin(), end = childList->end();
+ iter != end && retval.m_TransitionIndex == -2; ++iter, ++index) {
+ SStateNode &theNode(*iter);
+ if (theNode.m_Type == StateNodeTypes::Transition && &inTransition == (&theNode))
+ retval.m_TransitionIndex = index;
+ }
+ }
+ }
+
+ return retval;
+}
+
+void SDebugInterface::SetTransitionSet(const TTransitionList &inTransitions)
+{
+ m_TransitionList.resize(inTransitions.size());
+ for (QT3DSU32 idx = 0, end = inTransitions.size(); idx < end; ++idx)
+ m_TransitionList[idx] = TransitionToId(*inTransitions[idx]);
+ m_Listener->SetTransitionSet(toDataRef(m_TransitionList.data(), m_TransitionList.size()));
+}
+
+void SDebugInterface::SetExitSet(const TStateNodeSet &inSet)
+{
+ NVConstDataRef<TDebugStr> theSet(ListToRef(inSet.m_List, this->m_ExitList));
+ m_Listener->SetExitSet(theSet);
+}
+void SDebugInterface::SetEnterSet(const TStateNodeSet &inSet)
+{
+ NVConstDataRef<TDebugStr> theSet(ListToRef(inSet.m_List, this->m_EnterList));
+ m_Listener->SetEnterSet(theSet);
+}
+// Log statements run through the debugger as well.
+void SDebugInterface::EndMicrostep()
+{
+ m_Listener->EndMicroStep();
+}
+void SDebugInterface::EndStep()
+{
+ m_Listener->EndStep();
+}
+
+void SDebugInterface::OnExternalBreak()
+{
+ if (m_Listener)
+ m_Listener->OnExternalBreak();
+}
+
+SStateSignalSender::~SStateSignalSender()
+{
+ if (m_System) {
+ m_System->m_SignalSenders.erase(
+ eastl::find(m_System->m_SignalSenders.begin(), m_System->m_SignalSenders.end(), this));
+ }
+}
+}
+
+IStateInterpreter &IStateInterpreter::Create(NVFoundationBase &inFnd, IStringTable &inStrTable,
+ IScriptContext &inScriptContext,
+ IExecutionContext &inExecutionContext)
+{
+ return *QT3DS_NEW(inFnd.getAllocator(), StateSystem)(inFnd, inStrTable, inScriptContext,
+ inExecutionContext);
+}
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.h b/src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.h
new file mode 100644
index 00000000..d615d21f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateInterpreter.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_INTERPRETER_H
+#define QT3DS_STATE_INTERPRETER_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateTypes.h"
+#include "Qt3DSStateSignalConnection.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSDataRef.h"
+
+namespace qt3ds {
+namespace state {
+ struct InterpreterEventTypes
+ {
+ enum Enum {
+ UnknownInterpreterEvent = 0,
+ StateEnter,
+ StateExit,
+ Transition,
+ };
+ };
+
+ struct IStateInterpreterEventHandler
+ {
+ protected:
+ virtual ~IStateInterpreterEventHandler() {}
+
+ public:
+ virtual void OnInterpreterEvent(InterpreterEventTypes::Enum inEvent,
+ CRegisteredString inEventId) = 0;
+ };
+
+ class IStateInterpreter : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateInterpreter() {}
+ public:
+ // Setup of the state system, you can add a set of roots to the state graph.
+ virtual NVConstDataRef<SStateNode *> GetConfiguration() = 0;
+ // State context is referenced by this object.
+ // We can optionally validate the transitions to ensure that no transition can put us into
+ // an invalid state
+ // and so that simple loops don't exist. It is highly recommended to use this; it is done
+ // once via startup and
+ // shouldn't add appreciably to the initialization time but it makes the transition system a
+ // lot more robust.
+ virtual bool Initialize(IStateContext &inContext, bool inValidateTransitions = true) = 0;
+ // Bring the state machine to the internal state.
+ // Returns the list of states for the initial configuration
+ virtual NVConstDataRef<SStateNode *> Start() = 0;
+
+ // Execute transitions, internal events and external events until nothing is left to
+ // be done.
+ virtual NVConstDataRef<SStateNode *> Execute() = 0;
+
+ virtual bool IsRunning() const = 0;
+ virtual bool EventsPending() const = 0;
+
+ // Queue an event with an optional delay (0 means no delay).
+ // If the event has a delay then it can optionally be cancelled.
+ virtual void QueueEvent(const char8_t *inEventName, QT3DSU64 inDelay,
+ CRegisteredString inCancelId, bool inIsExternal = true) = 0;
+ virtual void QueueEvent(TEventPtr inEvent, QT3DSU64 inDelay, CRegisteredString inCancelId,
+ bool inIsExternal = true) = 0;
+ virtual void QueueEvent(TEventPtr inEvent, bool inIsExternal = true) = 0;
+ virtual void QueueEvent(const char8_t *inEventName, bool inIsExternal = true) = 0;
+
+ // Cancel an event with a particular id. Only cancels delayed events that have not fired
+ // yet.
+ virtual void CancelEvent(CRegisteredString inCancelId) = 0;
+
+ // When the connection gets destroyed, the hander will get no more events.
+ virtual TSignalConnectionPtr
+ RegisterEventHandler(IStateInterpreterEventHandler &inHandler) = 0;
+
+ virtual debugger::IStateMachineDebugInterface &GetDebugInterface() = 0;
+
+ virtual NVFoundationBase &GetFoundation() = 0;
+
+ virtual IScriptContext &GetScriptContext() = 0;
+
+ virtual IStateContext *GetStateContext() = 0;
+
+ // The only code that needs to happen in the state system is the code that executes a
+ // condition.
+ static IStateInterpreter &Create(NVFoundationBase &inFnd, IStringTable &inStrTable,
+ IScriptContext &inScriptContext,
+ IExecutionContext &inExecutionContext);
+ };
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateScriptContext.h b/src/Runtime/Source/stateapplication/Qt3DSStateScriptContext.h
new file mode 100644
index 00000000..110770a1
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateScriptContext.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_SCRIPT_CONTEXT_H
+#define QT3DS_STATE_SCRIPT_CONTEXT_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateTypes.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSOption.h"
+#include "foundation/StringTable.h"
+
+namespace qt3ds {
+namespace state {
+
+ class IScriptEvent : public IEvent
+ {
+ public:
+ // returns true on success, false if error.execution should be signaled.
+ virtual bool SetParam(CRegisteredString inName, const char8_t *inExpression) = 0;
+ // Sets the expression as a string
+ virtual bool SetParamStr(CRegisteredString inName, const char8_t *inStr) = 0;
+ virtual bool SetDataExpr(const char8_t *inExpression) = 0;
+ virtual bool SetDataStr(const char8_t *inStr) = 0;
+ };
+
+ struct SScriptExecutionResult
+ {
+ const char8_t *m_Result;
+ const char8_t *m_Error;
+ SScriptExecutionResult(const char8_t *inData, const char8_t *inError)
+ : m_Result(inData)
+ , m_Error(inError)
+ {
+ }
+ SScriptExecutionResult()
+ : m_Result("")
+ , m_Error("")
+ {
+ }
+ bool Valid() const { return m_Error == NULL || *m_Error == 0; }
+ const char8_t *Result() const
+ {
+ QT3DS_ASSERT(Valid());
+ return m_Result;
+ }
+ const char8_t *Error() const
+ {
+ QT3DS_ASSERT(!Valid());
+ return m_Error;
+ }
+ };
+
+ class IScriptContext : public NVRefCounted
+ {
+ protected:
+ virtual ~IScriptContext() {}
+ public:
+ // Should functions returning options return othing
+ virtual Option<bool> ExecuteCondition(const char8_t *inCond) = 0;
+ // Used for logging.
+ // Error and result are good until next call into the script context.
+ virtual SScriptExecutionResult ExecuteExpressionToString(const char8_t *inExpr) = 0;
+
+ // If return value is false, error is signaled with GetErrorInfo.
+ virtual bool Assign(const char8_t *inVariable, const char8_t *inExpr) = 0;
+ // Assign a string to this variable location.
+ virtual void AssignStr(const char8_t *inVariable, const char8_t *inStr) = 0;
+
+ // If return value is false, error is signaled via GetErrorInfo.
+ // The actual pointer and content used for inscript is expected to not change during
+ // execution of the system.
+ // it is legal for contexts to cache information based off the script pointer value.
+ virtual bool ExecuteScript(const char8_t *inScript) = 0;
+ // Always return 1 result
+ virtual int ExecuteStr(const char8_t *inScript, bool withRet) = 0;
+
+ // Return true on success, false on failure, and Empty on error.
+ virtual Option<bool> BeginForeach(const char8_t *inArray, const char8_t *inItem,
+ const char8_t *inIdxVar = "") = 0;
+ virtual Option<bool> NextForeach(const char8_t *inItem, const char8_t *inIdxVar = "") = 0;
+ virtual void CancelForeach() = 0;
+
+ virtual void SetCurrentEvent(TEventPtr inEvent) = 0;
+ virtual void ClearCurrentEvent() = 0;
+ // Create an event we can attach extra data do.
+ virtual IScriptEvent *CreateScriptEvent(CRegisteredString inName) = 0;
+
+ virtual const char8_t *GetErrorInfo() = 0;
+
+ virtual void SetInterpreter(IStateInterpreter &inInterpreter) = 0;
+
+ virtual CRegisteredString GetContextType() { return CRegisteredString(); }
+
+ // Dumps a differential state from the last time someone asked. This is a debug interface;
+ // not meant to be used
+ // by multiple listeners concurrently.
+ virtual void DumpState(debugger::IScriptStateListener &inListener) = 0;
+ };
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateSharedImpl.h b/src/Runtime/Source/stateapplication/Qt3DSStateSharedImpl.h
new file mode 100644
index 00000000..0b82cc3c
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateSharedImpl.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_SHARED_IMPL_H
+#define QT3DS_STATE_SHARED_IMPL_H
+#include "Qt3DSState.h"
+#include "foundation/Utils.h"
+
+namespace qt3ds {
+namespace state {
+ namespace impl {
+
+ inline bool NameMatchesInternal(const char8_t *inTransEvent, QT3DSU32 transLen,
+ const char8_t *inEventName, QT3DSU32 eventLen)
+ {
+ QT3DSU32 idx, end;
+ // Empty loop intentional to find first nonmatching character
+ for (idx = 0, end = NVMin(transLen, eventLen);
+ idx < end && inTransEvent[idx] == inEventName[idx]; ++idx) {
+ }
+ // Note that in this case we point to the first nonmatching character which may be off
+ // the end of either
+ // eventStr or transStr
+ bool match = false;
+ if (idx == transLen) {
+ if (idx == eventLen)
+ match = true;
+ else if (inEventName[idx] == '.')
+ match = true;
+ else if (inTransEvent[idx - 1] == '.')
+ match = true;
+ } else if (idx == eventLen) {
+ if ((transLen - idx) == 1)
+ match = inTransEvent[idx] == '*' || inTransEvent[idx] == '.';
+
+ else if ((transLen - idx) == 2)
+ match = inTransEvent[idx] == '.' && inTransEvent[idx + 1] == '*';
+ } else {
+ if (inTransEvent[idx] == '*')
+ match = true;
+ }
+ return match;
+ }
+
+ inline const char8_t *FindNextNonSpace(const char8_t *inPtr)
+ {
+ for (; *inPtr == ' '; ++inPtr) {
+ }
+ return inPtr;
+ }
+
+ inline const char8_t *FindNextSpaceOrNull(const char8_t *inPtr)
+ {
+ for (; *inPtr && *inPtr != ' '; ++inPtr) {
+ }
+ return inPtr;
+ }
+
+ inline bool NameMatches(const char8_t *inTransEvent, const char8_t *inEventName)
+ {
+ inTransEvent = nonNull(inTransEvent);
+ inEventName = nonNull(inEventName);
+
+ QT3DSU32 transLen = StrLen(inTransEvent);
+
+ QT3DSU32 eventLen = StrLen(inEventName);
+ if (transLen == 0) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ if (eventLen == 0) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+
+ for (inTransEvent = FindNextNonSpace(inTransEvent); inTransEvent && *inTransEvent;
+ inTransEvent = FindNextNonSpace(inTransEvent)) {
+ const char8_t *end = FindNextSpaceOrNull(inTransEvent);
+ QT3DSU32 len = (QT3DSU32)(end - inTransEvent);
+
+ if (len && NameMatchesInternal(inTransEvent, len, inEventName, eventLen))
+ return true;
+ inTransEvent = end;
+ }
+
+ return false;
+ }
+ }
+}
+}
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateSignalConnection.h b/src/Runtime/Source/stateapplication/Qt3DSStateSignalConnection.h
new file mode 100644
index 00000000..5bfa59ad
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateSignalConnection.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_SIGNAL_CONNECTION_H
+#define QT3DS_STATE_SIGNAL_CONNECTION_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSRefCounted.h"
+namespace qt3ds {
+namespace state {
+
+ class IStateSignalConnection : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateSignalConnection() {}
+ public:
+ };
+
+ typedef NVScopedRefCounted<IStateSignalConnection> TSignalConnectionPtr;
+}
+}
+
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateTypes.h b/src/Runtime/Source/stateapplication/Qt3DSStateTypes.h
new file mode 100644
index 00000000..41e759fb
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateTypes.h
@@ -0,0 +1,720 @@
+/****************************************************************************
+**
+** 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_STATE_TYPES_H
+#define QT3DS_STATE_TYPES_H
+#include "Qt3DSState.h"
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSFlags.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/StringTable.h"
+#include "foundation/TaggedPointer.h"
+#include "foundation/Qt3DSVec2.h"
+#include "foundation/Qt3DSVec3.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Utils.h"
+#include "Qt3DSStateIdValue.h"
+
+namespace qt3ds {
+namespace state {
+
+ // Link list definitions.
+ DEFINE_INVASIVE_LIST(StateNode);
+ DEFINE_INVASIVE_SINGLE_LIST(OnEntry);
+ DEFINE_INVASIVE_SINGLE_LIST(OnExit);
+ DEFINE_INVASIVE_SINGLE_LIST(DataModel);
+ DEFINE_INVASIVE_LIST(Invoke);
+
+ // Externally defined objects - These objects are defined by the implementation of the
+ // interpreter and the execution context. They are not used and cannot be manipulated by
+ // core state types.
+
+ // Standard content defined in another file so as to not clutter up the core state definitions.
+ struct SExecutableContent;
+ DEFINE_INVASIVE_LIST(ExecutableContent);
+ struct SScript;
+ struct SDataModel;
+
+ // Defined by the execution context; These objects can be defined by the implementation of the
+ // scripting
+ // system.
+ struct STransitionCondition;
+
+ // Defined by the interpreter. Really just a placeholder so the interpreter can cache
+ // item-specific data on an object.
+ struct SInterpreterData;
+
+ // We *have* to keep some subset of the data in document order because the algorithms
+ // defined in the specification rely on this.
+ struct StateNodeTypes
+ {
+ enum Enum {
+ UnknownType,
+ State,
+ Parallel,
+ Transition,
+ Final,
+ SCXML,
+ History,
+ };
+ static bool CanHaveChildren(StateNodeTypes::Enum val)
+ {
+ return val == State || val == Parallel || val == SCXML;
+ }
+ static bool CanHaveTransitions(StateNodeTypes::Enum val)
+ {
+ return val == State || val == Parallel;
+ }
+ static bool IsStateType(StateNodeTypes::Enum val)
+ {
+ return CanHaveChildren(val) || val == Final;
+ }
+ static bool IsTransitionType(StateNodeTypes::Enum val) { return val == Transition; }
+ static bool CanHaveInitializeNode(StateNodeTypes::Enum val) { return val == State; }
+ static bool IsAtomicType(StateNodeTypes::Enum val) { return val == State || val == Final; }
+ };
+
+ struct SState;
+ struct SParallel;
+ struct STransition;
+ struct SFinal;
+ struct SSCXML;
+ struct SHistory;
+
+ template <typename TDataType>
+ struct SStateNodeTypeMap
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::UnknownType; }
+ };
+
+ template <>
+ struct SStateNodeTypeMap<SState>
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::State; }
+ };
+ template <>
+ struct SStateNodeTypeMap<SParallel>
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::Parallel; }
+ };
+ template <>
+ struct SStateNodeTypeMap<STransition>
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::Transition; }
+ };
+ template <>
+ struct SStateNodeTypeMap<SFinal>
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::Final; }
+ };
+ template <>
+ struct SStateNodeTypeMap<SSCXML>
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::SCXML; }
+ };
+ template <>
+ struct SStateNodeTypeMap<SHistory>
+ {
+ static StateNodeTypes::Enum GetStateNodeType() { return StateNodeTypes::History; }
+ };
+ // Some editor info has to be contained on the state node. Description I am fine with eliding
+ // but
+ // most of the other information needs to sit on the nodes themselves.
+ struct StateNodeFlagValues
+ {
+ enum Enum {
+ HasNothing = 0,
+ HasPosition = 1,
+ HasDimension = 1 << 1,
+ HasColor = 1 << 2,
+ HasEndPosition = 1 << 3,
+ };
+ };
+
+ struct SStateNodeFlags : NVFlags<StateNodeFlagValues::Enum, QT3DSU32>
+ {
+ bool HasPosition() const { return this->operator&(StateNodeFlagValues::HasPosition); }
+ void SetHasPosition(bool val) { clearOrSet(val, StateNodeFlagValues::HasPosition); }
+ bool HasDimension() const { return this->operator&(StateNodeFlagValues::HasDimension); }
+ void SetHasDimension(bool val) { clearOrSet(val, StateNodeFlagValues::HasDimension); }
+ bool HasColor() const { return this->operator&(StateNodeFlagValues::HasColor); }
+ void SetHasColor(bool val) { clearOrSet(val, StateNodeFlagValues::HasColor); }
+ bool HasEndPosition() const { return this->operator&(StateNodeFlagValues::HasEndPosition); }
+ void SetHasEndPosition(bool val) { clearOrSet(val, StateNodeFlagValues::HasEndPosition); }
+ };
+
+ struct SStateNode
+ {
+ const StateNodeTypes::Enum m_Type;
+ CRegisteredString m_Id;
+ SStateNode *m_Parent;
+ SStateNode *m_NextSibling;
+ SStateNode *m_PreviousSibling;
+ SInterpreterData *m_InterpreterData;
+ SStateNodeFlags m_StateNodeFlags;
+ QT3DSVec2 m_Position;
+ QT3DSVec2 m_Dimension;
+ QT3DSVec3 m_Color;
+
+ SStateNode(StateNodeTypes::Enum inType)
+ : m_Type(inType)
+ , m_Parent(NULL)
+ , m_NextSibling(NULL)
+ , m_PreviousSibling(NULL)
+ , m_InterpreterData(NULL)
+ {
+ }
+
+ template <typename TDataType>
+ TDataType *CastTo()
+ {
+ if (m_Type == SStateNodeTypeMap<TDataType>::GetStateNodeType())
+ return static_cast<TDataType *>(this);
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+
+ template <typename TDataType>
+ const TDataType *CastTo() const
+ {
+ if (m_Type == SStateNodeTypeMap<TDataType>::GetStateNodeType())
+ return static_cast<const TDataType *>(this);
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+ // Helper functions that take care of the drama around getting the various
+ // shared properties
+ bool IsCompound() const;
+ bool IsAtomic() const;
+ void AppendChild(SStateNode &inChild);
+ TOnEntryList *GetOnEntryList();
+ TOnExitList *GetOnExitList();
+ STransition *GetInitialTransition();
+ const char8_t *GetInitialExpression();
+ TStateNodeList *GetChildren();
+ SDataModel *GetDataModel();
+ Option<QT3DSVec2> GetPosition() const
+ {
+ if (m_StateNodeFlags.HasPosition())
+ return m_Position;
+ return Empty();
+ }
+ void SetPosition(const Option<QT3DSVec2> &pos)
+ {
+ if (pos.hasValue()) {
+ m_Position = *pos;
+ }
+ m_StateNodeFlags.SetHasPosition(pos.hasValue());
+ }
+ Option<QT3DSVec2> GetDimension() const
+ {
+ if (m_StateNodeFlags.HasDimension())
+ return m_Dimension;
+ return Empty();
+ }
+ void SetDimension(const Option<QT3DSVec2> &pos)
+ {
+ if (pos.hasValue()) {
+ m_Dimension = *pos;
+ }
+ m_StateNodeFlags.SetHasDimension(pos.hasValue());
+ }
+ Option<QT3DSVec3> GetColor() const
+ {
+ if (m_StateNodeFlags.HasColor())
+ return m_Color;
+ return Empty();
+ }
+ void SetColor(const Option<QT3DSVec3> &pos)
+ {
+ if (pos.hasValue()) {
+ m_Color = *pos;
+ }
+ m_StateNodeFlags.SetHasColor(pos.hasValue());
+ }
+ };
+
+ struct SEntryExitBase : public SStateNode
+ {
+ TOnEntryList m_OnEntry;
+ TOnExitList m_OnExit;
+ SEntryExitBase(StateNodeTypes::Enum inType)
+ : SStateNode(inType)
+ {
+ }
+ };
+
+ // State and parallel objects can have states as children. Nothing aside from
+ // SCXML can have this.
+ struct SStateParallelBase : public SEntryExitBase
+ {
+ TStateNodeList m_Children;
+ SDataModel *m_DataModel;
+ TInvokeList m_InvokeList;
+
+ SStateParallelBase(StateNodeTypes::Enum inType)
+ : SEntryExitBase(inType)
+ , m_DataModel(NULL)
+ {
+ }
+
+ void RemoveChild(SStateNode &inChild)
+ {
+ QT3DS_ASSERT(inChild.m_Parent == this);
+ m_Children.remove(inChild);
+ inChild.m_Parent = NULL;
+ }
+
+ void AppendChild(SStateNode &inChild, SStateNode *inLoc = NULL)
+ {
+ if (inChild.m_Parent != NULL)
+ static_cast<SStateParallelBase *>(inChild.m_Parent)->RemoveChild(inChild);
+ if (inLoc)
+ m_Children.insert_after(*inLoc, inChild);
+ else
+ m_Children.push_back(inChild);
+ inChild.m_Parent = this;
+ }
+
+ void PrependChild(SStateNode &inChild, SStateNode *inLoc = NULL)
+ {
+ if (inChild.m_Parent != NULL)
+ static_cast<SStateParallelBase *>(inChild.m_Parent)->RemoveChild(inChild);
+ if (inLoc)
+ m_Children.insert_before(*inLoc, inChild);
+ else
+ m_Children.push_front(inChild);
+ inChild.m_Parent = this;
+ }
+
+ bool IsAtomic() const
+ {
+ for (TStateNodeList::const_iterator iter = m_Children.begin(), end = m_Children.end();
+ iter != end; ++iter) {
+ if (iter->m_Type != StateNodeTypes::Transition)
+ return false;
+ }
+ return true;
+ }
+ bool IsCompound() const { return !IsAtomic(); }
+ };
+
+ struct SCXMLFlagValues
+ {
+ enum Enum {
+ Late = 1,
+ };
+ };
+
+ struct SSCXMLFlags : public NVFlags<SCXMLFlagValues::Enum, QT3DSU32>
+ {
+ SSCXMLFlags() {}
+
+ void SetLateBinding(bool inValue) { clearOrSet(inValue, SCXMLFlagValues::Late); }
+ bool IsLateBinding() const { return this->operator&(SCXMLFlagValues::Late); }
+ };
+
+ // Begin standard object definitions
+
+ struct SSCXML : public SStateNode
+ {
+ STransition *m_Initial;
+ CRegisteredString m_Name;
+ TStateNodeList m_Children;
+ SScript *m_Script;
+ SSCXMLFlags m_Flags;
+ SDataModel *m_DataModel;
+ QT3DSI32 m_Version;
+ const char8_t *m_Filename;
+ const char8_t *m_InitialExpr;
+
+ static QT3DSI32 GetCurrentVersion() { return 1; }
+ SSCXML()
+ : SStateNode(StateNodeTypes::SCXML)
+ , m_Initial(NULL)
+ , m_Script(NULL)
+ , m_DataModel(NULL)
+ , m_Version(GetCurrentVersion())
+ , m_InitialExpr(NULL)
+ {
+ }
+
+ void RemoveChild(SStateNode &inChild)
+ {
+ QT3DS_ASSERT(inChild.m_Parent == this);
+ m_Children.remove(inChild);
+ inChild.m_Parent = NULL;
+ }
+
+ void AppendChild(SStateNode &inChild, SStateNode *inLoc = NULL)
+ {
+ if (inChild.m_Parent != NULL)
+ static_cast<SStateParallelBase *>(inChild.m_Parent)->RemoveChild(inChild);
+ if (inLoc)
+ m_Children.insert_after(*inLoc, inChild);
+ else
+ m_Children.push_back(inChild);
+ inChild.m_Parent = this;
+ }
+ };
+
+ struct SState : public SStateParallelBase
+ {
+ // transition, state, parallel, and final are all handed by SStateNode
+ STransition *m_Initial;
+ const char8_t *m_InitialExpr;
+
+ SState()
+ : SStateParallelBase(StateNodeTypes::State)
+ , m_Initial(NULL)
+ , m_InitialExpr(NULL)
+ {
+ }
+ };
+
+ struct SParallel : public SStateParallelBase
+ {
+ SParallel()
+ : SStateParallelBase(StateNodeTypes::Parallel)
+ {
+ }
+ };
+
+ struct TransitionFlagValues
+ {
+ enum Enum {
+ Internal = 1,
+ };
+ };
+
+ struct STransitionFlags : public NVFlags<TransitionFlagValues::Enum, QT3DSU32>
+ {
+ STransitionFlags() {}
+
+ void SetInternal(bool inValue) { clearOrSet(inValue, TransitionFlagValues::Internal); }
+ bool IsInternal() const { return this->operator&(TransitionFlagValues::Internal); }
+ };
+
+ struct STransition : public SStateNode
+ {
+ CRegisteredString m_Event;
+ const char8_t *m_Condition;
+ NVConstDataRef<SStateNode *> m_Target;
+ STransitionFlags m_Flags;
+ TExecutableContentList m_ExecutableContent;
+ NVConstDataRef<QT3DSVec2> m_Path;
+ // If we have multiple end targets, on order to lay them out we need a branch point
+ // along with the number of control points in each target. The first index points to the
+ // branch point in the path vector, then next index tells the number of control points
+ // for the first end point, etc.
+ NVConstDataRef<QT3DSU32> m_PathIndexes;
+ QT3DSVec2 m_EndPosition;
+ // m_Source of the transition is its parent
+
+ STransition()
+ : SStateNode(StateNodeTypes::Transition)
+ , m_Condition(NULL)
+ , m_EndPosition(0, 0)
+ {
+ }
+
+ SStateNode *GetSource() { return m_Parent; }
+
+ Option<QT3DSVec2> GetEndPosition() const
+ {
+ if (m_StateNodeFlags.HasEndPosition())
+ return m_EndPosition;
+ return Empty();
+ }
+ void SetEndPosition(const Option<QT3DSVec2> &pos)
+ {
+ if (pos.hasValue()) {
+ m_EndPosition = *pos;
+ }
+ m_StateNodeFlags.SetHasEndPosition(pos.hasValue());
+ }
+ };
+
+ // TODO: DoneData
+ struct SFinal : public SEntryExitBase
+ {
+ SFinal()
+ : SEntryExitBase(StateNodeTypes::Final)
+ {
+ }
+ };
+
+ struct HistoryFlagValues
+ {
+ enum Enum {
+ Deep = 1,
+ };
+ };
+
+ struct SHistoryFlags : public NVFlags<HistoryFlagValues::Enum, QT3DSU32>
+ {
+ SHistoryFlags() {}
+
+ void SetDeep(bool inValue) { clearOrSet(inValue, HistoryFlagValues::Deep); }
+ bool IsDeep() const { return this->operator&(HistoryFlagValues::Deep); }
+ };
+
+ struct SHistory : public SStateNode
+ {
+ SHistoryFlags m_Flags;
+ qt3ds::foundation::STaggedPointer m_UserData;
+ STransition *m_Transition;
+
+ SHistory()
+ : SStateNode(StateNodeTypes::History)
+ , m_Transition(NULL)
+ {
+ }
+ };
+
+ struct SOnEntry
+ {
+ SOnEntry *m_NextSibling;
+ TExecutableContentList m_ExecutableContent;
+ qt3ds::foundation::STaggedPointer m_UserData;
+ SOnEntry()
+ : m_NextSibling(NULL)
+ {
+ }
+ };
+
+ struct SOnExit
+ {
+ SOnExit *m_NextSibling;
+ TExecutableContentList m_ExecutableContent;
+ qt3ds::foundation::STaggedPointer m_UserData;
+ SOnExit()
+ : m_NextSibling(NULL)
+ {
+ }
+ };
+
+ struct SInvoke
+ {
+ SInvoke *m_NextSibling;
+ SInvoke *m_PreviousSibling;
+ // Will have either SCXML content or dom content but not both.
+ SSCXML *m_SCXMLContent;
+ SDOMElement *m_DOMContent;
+ SInvoke()
+ : m_NextSibling(NULL)
+ , m_PreviousSibling(NULL)
+ , m_SCXMLContent(NULL)
+ , m_DOMContent(NULL)
+ {
+ }
+ };
+
+ // Events, because they created both inside and outside
+ // the state system by various parties.
+ class IEvent : public NVRefCounted
+ {
+ protected:
+ virtual ~IEvent() {}
+ public:
+ virtual CRegisteredString GetName() const = 0;
+ // Optional type string for rtti. No string means this is an opaque event
+ // that cannot be safely cast to any specific event type.
+ virtual CRegisteredString GetEventType() const { return CRegisteredString(); }
+ };
+
+ typedef NVScopedRefCounted<IEvent> TEventPtr;
+
+ struct SIdValue;
+
+ typedef nvhash_map<CRegisteredString, SIdValue> TIDStateMap;
+
+ struct SDOMElementNode
+ {
+ SDOMElementNode *m_NextNode;
+ SDOMElement *m_Element;
+ SDOMElementNode(SDOMElement *elem = NULL)
+ : m_NextNode(NULL)
+ , m_Element(elem)
+ {
+ }
+ };
+
+ struct SDOMAttributeNode
+ {
+ SDOMAttributeNode *m_NextNode;
+ SDOMAttribute *m_Attribute;
+ SDOMAttributeNode(SDOMAttribute *inAtt = NULL)
+ : m_NextNode(NULL)
+ , m_Attribute(inAtt)
+ {
+ }
+ };
+ DEFINE_INVASIVE_SINGLE_LIST(DOMElementNode);
+ DEFINE_INVASIVE_SINGLE_LIST(DOMAttributeNode);
+
+ inline bool SStateNode::IsCompound() const
+ {
+ if (m_Type == StateNodeTypes::SCXML)
+ return true;
+ if (m_Type == StateNodeTypes::State)
+ return !IsAtomic();
+ return false;
+ }
+
+ inline bool SStateNode::IsAtomic() const
+ {
+ if (m_Type == StateNodeTypes::SCXML)
+ return false;
+
+ if (m_Type == StateNodeTypes::State) {
+ const SState *theState = CastTo<SState>();
+ for (TStateNodeList::iterator iter = theState->m_Children.begin(),
+ end = theState->m_Children.end();
+ iter != end; ++iter) {
+ if (iter->m_Type != StateNodeTypes::Transition
+ && iter->m_Type != StateNodeTypes::History)
+ return false;
+ }
+ return true;
+ } else if (m_Type == StateNodeTypes::Final)
+ return true;
+ return false;
+ }
+
+ inline void SStateNode::AppendChild(SStateNode &inChild)
+ {
+ if (m_Type == StateNodeTypes::State || m_Type == StateNodeTypes::Parallel) {
+ static_cast<SStateParallelBase *>(this)->AppendChild(inChild);
+ } else if (m_Type == StateNodeTypes::SCXML) {
+ static_cast<SSCXML *>(this)->AppendChild(inChild);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ inline TOnEntryList *SStateNode::GetOnEntryList()
+ {
+ if (m_Type == StateNodeTypes::State || m_Type == StateNodeTypes::Parallel)
+ return &static_cast<SStateParallelBase *>(this)->m_OnEntry;
+ if (m_Type == StateNodeTypes::Final)
+ return &CastTo<SFinal>()->m_OnEntry;
+ return NULL;
+ }
+
+ inline TOnExitList *SStateNode::GetOnExitList()
+ {
+ if (m_Type == StateNodeTypes::State || m_Type == StateNodeTypes::Parallel)
+ return &static_cast<SStateParallelBase *>(this)->m_OnExit;
+ if (m_Type == StateNodeTypes::Final)
+ return &CastTo<SFinal>()->m_OnExit;
+ return NULL;
+ }
+
+ inline STransition *SStateNode::GetInitialTransition()
+ {
+ if (m_Type == StateNodeTypes::State)
+ return static_cast<SState *>(this)->m_Initial;
+ if (m_Type == StateNodeTypes::SCXML)
+ return static_cast<SSCXML *>(this)->m_Initial;
+ return NULL;
+ }
+
+ inline const char8_t *SStateNode::GetInitialExpression()
+ {
+ if (m_Type == StateNodeTypes::State)
+ return nonNull(static_cast<SState *>(this)->m_InitialExpr);
+ if (m_Type == StateNodeTypes::SCXML)
+ return nonNull(static_cast<SSCXML *>(this)->m_InitialExpr);
+ return "";
+ }
+
+ inline TStateNodeList *SStateNode::GetChildren()
+ {
+ if (m_Type == StateNodeTypes::State || m_Type == StateNodeTypes::Parallel)
+ return &static_cast<SStateParallelBase *>(this)->m_Children;
+ if (m_Type == StateNodeTypes::SCXML)
+ return &static_cast<SSCXML *>(this)->m_Children;
+ return NULL;
+ }
+
+ inline SDataModel *SStateNode::GetDataModel()
+ {
+ if (m_Type == StateNodeTypes::State || m_Type == StateNodeTypes::Parallel)
+ return static_cast<SStateParallelBase *>(this)->m_DataModel;
+ if (m_Type == StateNodeTypes::SCXML)
+ return static_cast<SSCXML *>(this)->m_DataModel;
+ return NULL;
+ }
+
+ IMPLEMENT_INVASIVE_LIST(Invoke, m_PreviousSibling, m_NextSibling);
+ IMPLEMENT_INVASIVE_LIST(StateNode, m_PreviousSibling, m_NextSibling);
+ IMPLEMENT_INVASIVE_SINGLE_LIST(OnEntry, m_NextSibling);
+ IMPLEMENT_INVASIVE_SINGLE_LIST(OnExit, m_NextSibling);
+ IMPLEMENT_INVASIVE_SINGLE_LIST(DOMElementNode, m_NextNode);
+ IMPLEMENT_INVASIVE_SINGLE_LIST(DOMAttributeNode, m_NextNode);
+
+ DEFINE_INVASIVE_SINGLE_LIST(Data);
+
+ struct SDataModel
+ {
+ CRegisteredString m_Id;
+ const char8_t *m_Source;
+ const char8_t *m_Expression;
+ TDataList m_Data;
+
+ SDataModel()
+ : m_Source(NULL)
+ , m_Expression(NULL)
+ {
+ }
+ };
+
+ struct SData
+ {
+ CRegisteredString m_Id;
+ const char8_t *m_Source;
+ const char8_t *m_Expression;
+ SData *m_NextSibling;
+ SData()
+ : m_Source(NULL)
+ , m_Expression(NULL)
+ , m_NextSibling(NULL)
+ {
+ }
+ };
+
+ IMPLEMENT_INVASIVE_SINGLE_LIST(Data, m_NextSibling);
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.cpp
new file mode 100644
index 00000000..62413bae
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.cpp
@@ -0,0 +1,626 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateVisualBindingContext.h"
+
+#include "Qt3DSStateVisualBindingContextValues.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "EASTL/string.h"
+#include "EASTL/hash_map.h"
+#include "Qt3DSStateInterpreter.h"
+#include "Qt3DSStateContext.h"
+#include "foundation/XML.h"
+#include "foundation/FileTools.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/StringConversionImpl.h"
+
+using namespace qt3ds::state;
+
+namespace {
+
+struct SStateEventKey
+{
+ InterpreterEventTypes::Enum m_Event;
+ CRegisteredString m_Id;
+ SStateEventKey(InterpreterEventTypes::Enum inEvt, CRegisteredString inId)
+ : m_Event(inEvt)
+ , m_Id(inId)
+ {
+ }
+ bool operator==(const SStateEventKey &inOther) const
+ {
+ return m_Event == inOther.m_Event && m_Id == inOther.m_Id;
+ }
+};
+}
+namespace eastl {
+template <>
+struct hash<SStateEventKey>
+{
+ size_t operator()(const SStateEventKey &inKey) const
+ {
+ return hash<int>()((int)inKey.m_Event)
+ ^ hash<qt3ds::foundation::CRegisteredString>()(inKey.m_Id);
+ }
+};
+}
+
+namespace {
+struct SVisualStateCommandNode
+{
+ SVisualStateCommand m_Command;
+ SVisualStateCommandNode *m_NextSibling;
+ SVisualStateCommandNode(const SVisualStateCommand &inCommand = SVisualStateCommand())
+ : m_Command(inCommand)
+ , m_NextSibling(NULL)
+ {
+ }
+};
+
+DEFINE_INVASIVE_SINGLE_LIST(VisualStateCommandNode);
+IMPLEMENT_INVASIVE_SINGLE_LIST(VisualStateCommandNode, m_NextSibling);
+
+typedef TVisualStateCommandNodeList TCommandList;
+
+// Apparently eastl::hash_multimap isn't order preserving for items.
+typedef eastl::hash_map<SStateEventKey, TCommandList, eastl::hash<SStateEventKey>,
+ eastl::equal_to<SStateEventKey>, ForwardingAllocator>
+ TStateEventCommandMap;
+
+struct SStateMachineSystem : public IStateInterpreterEventHandler, public NVRefCounted
+{
+ NVAllocatorCallback &m_Allocator;
+ CRegisteredString m_Path;
+ CRegisteredString m_Id;
+ CRegisteredString m_DatamodelFunction;
+
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ NVScopedRefCounted<IStateInterpreter> m_Interpreter;
+ TStateEventCommandMap m_CommandMap;
+ QT3DSI32 mRefCount;
+ bool m_Error;
+ bool m_Running;
+
+ TSignalConnectionPtr m_InterpreterEventConnection;
+ NVScopedRefCounted<IVisualStateCommandHandler> m_CommandHandler;
+ NVScopedRefCounted<IVisualStateInterpreterFactory> m_InterpreterFactory;
+
+ SStateMachineSystem(const char8_t *inPath, const char8_t *inId, const char8_t *inFunction,
+ IStringTable &inStrTable, NVAllocatorCallback &inAlloc)
+ : m_Allocator(inAlloc)
+ , m_Path(inStrTable.RegisterStr(inPath))
+ , m_Id(inStrTable.RegisterStr(inId))
+ , m_DatamodelFunction(inStrTable.RegisterStr(inFunction))
+ , m_StringTable(inStrTable)
+ , m_CommandMap(ForwardingAllocator(inAlloc, "SStateMachineSystem::m_CommandMap"))
+ , mRefCount(0)
+ , m_Error(false)
+ , m_Running(false)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ void Initialize()
+ {
+ }
+
+ void Start()
+ {
+ if (m_Error || m_Running || !m_Interpreter)
+ return;
+ m_Running = true;
+ m_Interpreter->Start();
+ // Event handler must be replaced after interpreter started
+ m_InterpreterEventConnection = m_Interpreter->RegisterEventHandler(*this);
+ // Run throw the initial configuration and fire any enter events. This takes care of
+ // initial states.
+ NVConstDataRef<SStateNode *> initialConfig = m_Interpreter->GetConfiguration();
+ for (QT3DSU32 nodeIdx = 0, nodeEnd = initialConfig.size(); nodeIdx < nodeEnd; ++nodeIdx) {
+ SStateNode *theNode(initialConfig[nodeIdx]);
+ OnInterpreterEvent(InterpreterEventTypes::StateEnter, theNode->m_Id);
+ }
+ }
+
+ void Update()
+ {
+ if (m_Interpreter) {
+ m_Interpreter->Execute();
+ }
+ }
+
+ void OnInterpreterEvent(InterpreterEventTypes::Enum inEvent,
+ CRegisteredString inEventId) override
+ {
+ if (m_CommandHandler) {
+ CRegisteredString theId = m_StringTable->RegisterStr(inEventId.c_str());
+ SStateEventKey theKey(inEvent, theId);
+ TStateEventCommandMap::iterator theItem = m_CommandMap.find(theKey);
+ if (theItem != m_CommandMap.end()) {
+ for (TCommandList::iterator iter = theItem->second.begin(),
+ end = theItem->second.end();
+ iter != end; ++iter)
+ m_CommandHandler->Handle(iter->m_Command, m_Interpreter->GetScriptContext());
+ }
+ }
+ }
+};
+
+typedef eastl::vector<NVScopedRefCounted<SStateMachineSystem>> TStateMachineList;
+
+struct SVisualStateContext : public IVisualStateContext
+{
+ NVFoundationBase &m_Foundation;
+ SSAutoDeallocatorAllocator m_DataAllocator;
+ QT3DSI32 mRefCount;
+ TStateMachineList m_StateMachines;
+ NVScopedRefCounted<IVisualStateCommandHandler> m_CommandHandler;
+ NVScopedRefCounted<IVisualStateInterpreterFactory> m_InterpreterFactory;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ nvvector<SElementReference> m_PreparseResults;
+
+ SVisualStateContext(NVFoundationBase &fnd, IStringTable &inStrTable)
+ : m_Foundation(fnd)
+ , m_DataAllocator(fnd)
+ , mRefCount(0)
+ , m_StringTable(inStrTable)
+ , m_PreparseResults(fnd.getAllocator(), "SVisualStateContext::m_PreparseResults")
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ void LoadStateMachine(const char8_t *id, const char8_t *inRelativePath,
+ const char8_t *inDatamodelFunction) override
+ {
+ SStateMachineSystem *theSystem = QT3DS_NEW(m_Foundation.getAllocator(), SStateMachineSystem)(
+ inRelativePath, id, inDatamodelFunction, *m_StringTable, m_Foundation.getAllocator());
+ theSystem->m_CommandHandler = m_CommandHandler;
+ theSystem->m_InterpreterFactory = m_InterpreterFactory;
+ m_StateMachines.push_back(theSystem);
+ }
+
+ CRegisteredString ParseStrAtt(IDOMReader &inReader, const char8_t *attName)
+ {
+ const char8_t *temp = "";
+ if (inReader.UnregisteredAtt(attName, temp)) {
+ return m_StringTable->RegisterStr(temp);
+ }
+ return CRegisteredString();
+ }
+
+ void PreparseExecutableContent(IDOMReader &inReader)
+ {
+ for (bool setatt = inReader.MoveToFirstChild(); setatt;
+ setatt = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __commandScope(inReader);
+ const char8_t *elemName = inReader.GetElementName();
+
+ SElementReference theReference;
+ if (AreEqual(elemName, "goto-slide")) {
+ theReference = SElementReference(ParseStrAtt(inReader, "element"));
+ } else if (AreEqual(elemName, "call")) {
+ theReference = SElementReference(ParseStrAtt(inReader, "element"));
+ } else if (AreEqual(elemName, "set-attribute")) {
+ theReference = SElementReference(ParseStrAtt(inReader, "element"),
+ ParseStrAtt(inReader, "attribute"));
+ } else if (AreEqual(elemName, "fire-event")) {
+ theReference = SElementReference(ParseStrAtt(inReader, "element"));
+ } else if (AreEqual(elemName, "set-presentation")) {
+ } else {
+ qCCritical(INVALID_PARAMETER, "Unrecognized child in an enter/exit node: %s", elemName);
+ }
+ if (theReference.m_ElementPath.IsValid()) {
+ m_PreparseResults.push_back(theReference);
+ }
+ }
+ }
+
+ NVConstDataRef<SElementReference> PreParseDocument(IDOMReader &inReader) override
+ {
+ m_PreparseResults.clear();
+ for (bool success = inReader.MoveToFirstChild("statemachine"); success;
+ success = inReader.MoveToNextSibling("statemachine")) {
+ IDOMReader::Scope __readerScope(inReader);
+ if (inReader.MoveToFirstChild("visual-states")) {
+ IDOMReader::Scope __bindingsScope(inReader);
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __stateScope(inReader);
+ if (AreEqual(inReader.GetElementName().c_str(), "transition")) {
+ PreparseExecutableContent(inReader);
+ } else {
+ for (bool enterExitSuccess = inReader.MoveToFirstChild(); enterExitSuccess;
+ enterExitSuccess = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __enterExitScope(inReader);
+ PreparseExecutableContent(inReader);
+ }
+ }
+ }
+ }
+ }
+ return m_PreparseResults;
+ }
+
+ void ParseGotoSlideData(IDOMReader &inReader, SGotoSlideData &inData)
+ {
+ const char *tempData;
+ if (inReader.UnregisteredAtt("direction", tempData) && AreEqual(tempData, "reverse"))
+ inData.m_Reverse = true;
+
+ if (inReader.UnregisteredAtt("mode", tempData)) {
+ if (AreEqual(tempData, "stopatend")) {
+ inData.m_Mode = SlidePlaybackModes::StopAtEnd;
+ inData.m_PlaythroughTo = CRegisteredString();
+ } else if (AreEqual(tempData, "looping"))
+ inData.m_Mode = SlidePlaybackModes::Looping;
+ else if (AreEqual(tempData, "pingpong"))
+ inData.m_Mode = SlidePlaybackModes::PingPong;
+ else if (AreEqual(tempData, "ping"))
+ inData.m_Mode = SlidePlaybackModes::Ping;
+ else if (AreEqual(tempData, "playthrough")) {
+ if (!inReader.UnregisteredAtt("playthroughto", tempData) || isTrivial(tempData)) {
+ qCCritical(INVALID_OPERATION, "Goto slide command has playthough "
+ "mode but no playthroughto attribute; mode will be ignored");
+ } else {
+ inData.m_PlaythroughTo = m_StringTable->RegisterStr(tempData);
+ }
+
+ if (inData.m_PlaythroughTo.hasValue())
+ inData.m_Mode = SlidePlaybackModes::StopAtEnd;
+ }
+ }
+
+ if (inReader.UnregisteredAtt("state", tempData))
+ inData.m_Paused = AreEqual(tempData, "pause");
+
+ if (inReader.UnregisteredAtt("rate", tempData)) {
+ QT3DSF32 temp = 1.0f;
+ if (StringConversion<QT3DSF32>().StrTo(tempData, temp))
+ inData.m_Rate = temp;
+ }
+
+ if (inReader.UnregisteredAtt("time", tempData)) {
+ QT3DSF32 temp = 0.0f;
+ if (StringConversion<QT3DSF32>().StrTo(tempData, temp))
+ inData.m_StartTime = static_cast<QT3DSU32>(NVMax(0.0f, temp) * 1000.0f + .5f);
+ }
+ }
+
+ void ParseExecutableContent(IDOMReader &inReader, CRegisteredString &inStateId,
+ InterpreterEventTypes::Enum inEvent,
+ TStateEventCommandMap &inCommandMap)
+ {
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __itemScope(inReader);
+ const char8_t *elemName = inReader.GetElementName();
+ SVisualStateCommand theCommand;
+ if (AreEqual(elemName, "goto-slide")) {
+ const char8_t *rel;
+ if (inReader.UnregisteredAtt("rel", rel)) {
+ const char8_t *wrap;
+ inReader.UnregisteredAtt("wrap", wrap);
+ SGotoSlideRelative::Enum direction = SGotoSlideRelative::Error;
+ if (AreEqual(rel, "next"))
+ direction = SGotoSlideRelative::Next;
+ else if (AreEqual(rel, "previous"))
+ direction = SGotoSlideRelative::Previous;
+ else {
+ qCCritical(INVALID_OPERATION, "Goto slide relative has invalid "
+ "attribute (neither 'next' nor 'previous')");
+ }
+ bool doWrap = AreEqual(wrap, "true") ? true : false;
+
+ SGotoSlideRelative theCommandData(ParseStrAtt(inReader, "element"), direction,
+ doWrap);
+ ParseGotoSlideData(inReader, theCommandData.m_GotoSlideData);
+ theCommand = SVisualStateCommand(theCommandData);
+ } else {
+ SGotoSlide theCommandData(ParseStrAtt(inReader, "element"),
+ ParseStrAtt(inReader, "slide"));
+ ParseGotoSlideData(inReader, theCommandData.m_GotoSlideData);
+ theCommand = SVisualStateCommand(theCommandData);
+ }
+ } else if (AreEqual(elemName, "call")) {
+ theCommand = SVisualStateCommand(SCallFunction(ParseStrAtt(inReader, "element"),
+ ParseStrAtt(inReader, "handler"),
+ ParseStrAtt(inReader, "arguments")));
+ } else if (AreEqual(elemName, "set-attribute")) {
+ theCommand = SVisualStateCommand(SSetAttribute(ParseStrAtt(inReader, "element"),
+ ParseStrAtt(inReader, "attribute"),
+ ParseStrAtt(inReader, "value")));
+ } else if (AreEqual(elemName, "fire-event")) {
+ theCommand = SVisualStateCommand(
+ SFireEvent(ParseStrAtt(inReader, "element"), ParseStrAtt(inReader, "event")));
+ } else if (AreEqual(elemName, "set-presentation")) {
+ theCommand = SVisualStateCommand(SPresentationAttribute(
+ ParseStrAtt(inReader, "ref"), ParseStrAtt(inReader, "attribute"),
+ ParseStrAtt(inReader, "value")));
+ } else if (AreEqual(elemName, "play-sound")) {
+ theCommand = SVisualStateCommand(SPlaySound(ParseStrAtt(inReader, "file")));
+ } else {
+ qCCritical(INVALID_PARAMETER, "Unrecognized child in an enter/exit node: %s", elemName);
+ }
+ if (theCommand.getType() != VisualStateCommandTypes::NoVisualStateCommand) {
+ TCommandList &theList = inCommandMap
+ .insert(eastl::make_pair(
+ SStateEventKey(inEvent, inStateId), TCommandList()))
+ .first->second;
+ theList.push_back(*QT3DS_NEW(m_DataAllocator, SVisualStateCommandNode)(theCommand));
+ }
+ }
+ }
+
+ SStateMachineSystem *FindStateMachine(const char8_t *inId)
+ {
+ if (isTrivial(inId))
+ return NULL;
+ if (inId[0] == '#')
+ ++inId;
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx)
+ if (AreEqual(m_StateMachines[idx].mPtr->m_Id.c_str(), inId))
+ return m_StateMachines[idx].mPtr;
+ return NULL;
+ }
+
+ // We have to be careful of string table values coming from the state machine and from the
+ // reader
+ // because we don't necessarily share the same string table.
+ void LoadVisualStateMapping(IDOMReader &inReader) override
+ {
+ for (bool success = inReader.MoveToFirstChild("statemachine"); success;
+ success = inReader.MoveToNextSibling("statemachine")) {
+ IDOMReader::Scope __readerScope(inReader);
+ const char8_t *machineId = "";
+ if (inReader.UnregisteredAtt("ref", machineId)) {
+ SStateMachineSystem *theSystem = FindStateMachine(machineId + 1);
+ if (theSystem == NULL) {
+ qCCritical(INVALID_OPERATION, "Unknown state machine id: %s",
+ nonNull(machineId));
+ continue;
+ }
+
+ if (inReader.MoveToFirstChild("visual-states")) {
+ IDOMReader::Scope __bindingsScope(inReader);
+ for (bool bindingsSuccess = inReader.MoveToFirstChild(); bindingsSuccess;
+ bindingsSuccess = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __stateScope(inReader);
+ const char8_t *rawStateId = "";
+ inReader.UnregisteredAtt("ref", rawStateId);
+ if (isTrivial(rawStateId))
+ continue;
+
+ if (rawStateId[0] == '#')
+ ++rawStateId;
+ CRegisteredString elemName = m_StringTable->RegisterStr(rawStateId);
+
+ if (AreEqual(inReader.GetElementName().c_str(), "transition")) {
+ ParseExecutableContent(inReader, elemName,
+ InterpreterEventTypes::Transition,
+ theSystem->m_CommandMap);
+ } else {
+ for (bool stateSuccess = inReader.MoveToFirstChild(); stateSuccess;
+ stateSuccess = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __enterExitScope(inReader);
+ const char8_t *stateChildName = inReader.GetElementName();
+ if (AreEqual(stateChildName, "enter")) {
+ ParseExecutableContent(inReader, elemName,
+ InterpreterEventTypes::StateEnter,
+ theSystem->m_CommandMap);
+ } else if (AreEqual(stateChildName, "exit")) {
+ ParseExecutableContent(inReader, elemName,
+ InterpreterEventTypes::StateExit,
+ theSystem->m_CommandMap);
+ } else {
+ qCCritical(INVALID_PARAMETER,
+ "Unrecognized child in a visual state bindings state: %s",
+ stateChildName);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ qCCritical(INVALID_OPERATION, "visual-state element has no machine attribute");
+ }
+ }
+ }
+
+ void SetCommandHandler(IVisualStateCommandHandler *inHandler) override
+ {
+ m_CommandHandler = inHandler;
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx)
+ m_StateMachines[idx]->m_CommandHandler = inHandler;
+ }
+
+ void SetInterpreterFactory(IVisualStateInterpreterFactory *inHandler) override
+ {
+ m_InterpreterFactory = inHandler;
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx)
+ m_StateMachines[idx]->m_InterpreterFactory = inHandler;
+ }
+
+ void Initialize() override
+ {
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx)
+ m_StateMachines[idx]->Initialize();
+ }
+
+ void Start() override
+ {
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx)
+ m_StateMachines[idx]->Start();
+ }
+
+ void Update() override
+ {
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx)
+ m_StateMachines[idx]->Update();
+ }
+
+ QT3DSU32 CountItems(TCommandList &list)
+ {
+ QT3DSU32 retval = 0;
+ for (TCommandList::iterator iter = list.begin(), end = list.end(); iter != end; ++iter)
+ ++retval;
+ return retval;
+ }
+
+ struct SSaveVisitor
+ {
+ const SStrRemapMap &m_RemapMap;
+ SSaveVisitor(const SStrRemapMap &map)
+ : m_RemapMap(map)
+ {
+ }
+ template <typename TItemType>
+ SVisualStateCommand operator()(const TItemType &item)
+ {
+ TItemType newItem(item);
+ newItem.Remap(*this);
+ return newItem;
+ }
+ void Remap(CRegisteredString &inStr) { inStr.Remap(m_RemapMap); }
+ SVisualStateCommand operator()() { return SVisualStateCommand(); }
+ };
+
+ void BinarySave(IOutStream &stream) override
+ {
+ qt3ds::foundation::SWriteBuffer theWriteBuffer(m_Foundation.getAllocator(),
+ "BinarySave::writebuffer");
+ // Allocate space for overall size of the data section
+ theWriteBuffer.writeZeros(4);
+ theWriteBuffer.write((QT3DSU32)m_StateMachines.size());
+ const SStrRemapMap &theRemapMap(m_StringTable->GetRemapMap());
+ for (QT3DSU32 idx = 0, end = m_StateMachines.size(); idx < end; ++idx) {
+ SStateMachineSystem &theSystem = *m_StateMachines[idx];
+ CRegisteredString path(theSystem.m_Path);
+ CRegisteredString id(theSystem.m_Id);
+ CRegisteredString fn(theSystem.m_DatamodelFunction);
+ path.Remap(theRemapMap);
+ id.Remap(theRemapMap);
+ fn.Remap(theRemapMap);
+ theWriteBuffer.write(path);
+ theWriteBuffer.write(id);
+ theWriteBuffer.write(fn);
+ theWriteBuffer.write((QT3DSU32)theSystem.m_CommandMap.size());
+ for (TStateEventCommandMap::iterator iter = theSystem.m_CommandMap.begin(),
+ mapEnd = theSystem.m_CommandMap.end();
+ iter != mapEnd; ++iter) {
+ theWriteBuffer.write((QT3DSU32)iter->first.m_Event);
+ CRegisteredString stateId(iter->first.m_Id);
+ stateId.Remap(theRemapMap);
+ theWriteBuffer.write(stateId);
+
+ theWriteBuffer.write(CountItems(iter->second));
+ for (TCommandList::iterator cmdIter = iter->second.begin(),
+ cmdEnd = iter->second.end();
+ cmdIter != cmdEnd; ++cmdIter) {
+ SVisualStateCommand remapped =
+ cmdIter->m_Command.visit<SVisualStateCommand>(SSaveVisitor(theRemapMap));
+ SVisualStateCommandNode theNode(remapped);
+ theWriteBuffer.write(theNode);
+ }
+ }
+ }
+ QT3DSU32 totalSize = theWriteBuffer.size();
+ QT3DSU32 *data = (QT3DSU32 *)theWriteBuffer.begin();
+ data[0] = totalSize - 4;
+ stream.Write((QT3DSU8 *)data, totalSize);
+ }
+
+ struct SLoadVisitor
+ {
+ NVDataRef<QT3DSU8> m_RemapMap;
+ SLoadVisitor(const NVDataRef<QT3DSU8> map)
+ : m_RemapMap(map)
+ {
+ }
+ template <typename TItemType>
+ void operator()(TItemType &item)
+ {
+ item.Remap(*this);
+ }
+
+ void Remap(CRegisteredString &inStr) { inStr.Remap(m_RemapMap); }
+ void operator()() {}
+ };
+
+ void BinaryLoad(IInStream &stream, NVDataRef<QT3DSU8> inStringTableData) override
+ {
+ QT3DSU32 length;
+ stream.Read(length);
+ QT3DSU8 *data = (QT3DSU8 *)m_DataAllocator.allocate(length, "Binaryload", __FILE__, __LINE__);
+ stream.Read(data, length);
+
+ SDataReader theReader(data, data + length);
+ QT3DSU32 numMachines = theReader.LoadRef<QT3DSU32>();
+ m_StateMachines.clear();
+ m_StateMachines.reserve(numMachines);
+ for (QT3DSU32 idx = 0, end = numMachines; idx < end; ++idx) {
+ CRegisteredString path = theReader.LoadRef<CRegisteredString>();
+ CRegisteredString id = theReader.LoadRef<CRegisteredString>();
+ CRegisteredString fn = theReader.LoadRef<CRegisteredString>();
+ path.Remap(inStringTableData);
+ id.Remap(inStringTableData);
+ fn.Remap(inStringTableData);
+ LoadStateMachine(id, path, fn);
+ SStateMachineSystem &theSystem(*m_StateMachines.back());
+ QT3DSU32 mapSize = theReader.LoadRef<QT3DSU32>();
+ for (QT3DSU32 mapIdx = 0; mapIdx < mapSize; ++mapIdx) {
+ InterpreterEventTypes::Enum evt =
+ static_cast<InterpreterEventTypes::Enum>(theReader.LoadRef<QT3DSU32>());
+ CRegisteredString stateId = theReader.LoadRef<CRegisteredString>();
+ stateId.Remap(inStringTableData);
+ QT3DSU32 numCommands = theReader.LoadRef<QT3DSU32>();
+ TCommandList &theList =
+ theSystem.m_CommandMap
+ .insert(eastl::make_pair(SStateEventKey(evt, stateId), TCommandList()))
+ .first->second;
+ for (QT3DSU32 cmdIdx = 0, cmdEnd = numCommands; cmdIdx < cmdEnd; ++cmdIdx) {
+ SVisualStateCommandNode *nextNode = theReader.Load<SVisualStateCommandNode>();
+ nextNode->m_Command.visit<void>(SLoadVisitor(inStringTableData));
+ theList.push_back(*nextNode);
+ }
+ }
+ }
+ }
+};
+}
+
+IVisualStateContext &IVisualStateContext::Create(NVFoundationBase &inFoundation,
+ IStringTable &inStrTable)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(), SVisualStateContext)(inFoundation, inStrTable);
+}
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.h b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.h
new file mode 100644
index 00000000..192181e4
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContext.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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_STATE_VISUAL_BINDING_CONTEXT_H
+#define QT3DS_STATE_VISUAL_BINDING_CONTEXT_H
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSStateVisualBindingContextCommands.h"
+
+namespace qt3ds {
+namespace foundation {
+ class IDOMReader;
+ class CStrTableOrDataRef;
+}
+}
+
+namespace qt3ds {
+namespace state {
+
+ struct SVisualStateCommand;
+ struct SSetAttribute;
+
+ // Entity responsible for implementing the various visual state commands.
+ class IVisualStateCommandHandler : public NVRefCounted
+ {
+ protected:
+ virtual ~IVisualStateCommandHandler() {}
+ public:
+ virtual void Handle(const SVisualStateCommand &inCommand,
+ IScriptContext &inScriptContext) = 0;
+ };
+
+ class IVisualStateInterpreterFactory : public NVRefCounted
+ {
+ protected:
+ virtual ~IVisualStateInterpreterFactory() {}
+ };
+
+ // It is important that the visual state context list the elements it expects to find in a uip
+ // file
+ // so that during parse of the uip file, we can generate an error if some element isn't found.
+ struct SElementReference
+ {
+ CRegisteredString m_ElementPath;
+ CRegisteredString m_Attribute;
+ SElementReference(CRegisteredString elemPath = CRegisteredString(),
+ CRegisteredString att = CRegisteredString())
+ : m_ElementPath(elemPath)
+ , m_Attribute(att)
+ {
+ }
+ };
+
+ class IVisualStateContext : public NVRefCounted
+ {
+ protected:
+ virtual ~IVisualStateContext() {}
+ public:
+ // All machines are loaded execute is called on the first update call.
+ // made after LoadVisualStateMapping
+ virtual void LoadStateMachine(const char8_t *id, const char8_t *inRelativePath,
+ const char8_t *inDatamodelFunction) = 0;
+ virtual void LoadVisualStateMapping(IDOMReader &inReader) = 0;
+ // We have to pre-parse the xml so we can reference everything in the various presentations
+ // We run this pass, then during uip file parsing we output errors if things don't match up.
+ virtual NVConstDataRef<SElementReference> PreParseDocument(IDOMReader &inReader) = 0;
+ virtual void SetCommandHandler(IVisualStateCommandHandler *inHandler) = 0;
+ virtual void SetInterpreterFactory(IVisualStateInterpreterFactory *inHandler) = 0;
+
+ // Initialize the state machines. Machines are initialized in order of LoadStateMachine
+ // calls.
+ virtual void Initialize() = 0;
+ virtual void Start() = 0;
+
+ // Initialize the state machines. Machines are updated in order of LoadStateMachine calls.
+ virtual void Update() = 0;
+
+ // Save out to a format that allows very rapid loading.
+ virtual void BinarySave(IOutStream &stream) = 0;
+ virtual void BinaryLoad(IInStream &stream, NVDataRef<QT3DSU8> inStringTableData) = 0;
+
+ static IVisualStateContext &Create(NVFoundationBase &inFoundation,
+ IStringTable &inStrTable);
+ };
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextCommands.h b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextCommands.h
new file mode 100644
index 00000000..4d08f695
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextCommands.h
@@ -0,0 +1,348 @@
+/****************************************************************************
+**
+** 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_STATE_VISUAL_BINDING_CONTEXT_COMMANDS_H
+#define QT3DS_STATE_VISUAL_BINDING_CONTEXT_COMMANDS_H
+#include "Qt3DSState.h"
+#include "foundation/StringTable.h"
+
+namespace qt3ds {
+namespace state {
+
+ struct VisualStateCommandTypes
+ {
+ enum Enum {
+ NoVisualStateCommand = 0,
+ GotoSlide,
+ CallFunction,
+ SetAttribute,
+ GotoSlideRelative,
+ FireEvent,
+ PresentationAttribute,
+ PlaySound,
+ };
+ };
+
+ struct SlidePlaybackModes
+ {
+ enum Enum {
+ StopAtEnd = 0,
+ Looping,
+ PingPong,
+ Ping,
+ PlaythroughTo,
+ };
+ };
+
+ struct SGotoSlideData
+ {
+ Option<SlidePlaybackModes::Enum> m_Mode;
+ Option<CRegisteredString> m_PlaythroughTo;
+ Option<QT3DSU32> m_StartTime;
+ QT3DSF32 m_Rate;
+ bool m_Reverse;
+ Option<bool> m_Paused;
+ SGotoSlideData()
+ : m_Rate(1.0f)
+ , m_Reverse(false)
+ {
+ }
+ };
+
+ // All the element references in this file need to be absolute, meaning they
+ // must begin with a presentationid:. The is because the state machine exists at the
+ // application level where all presentations are pretty much equal and must be referenced
+ // by id.
+
+ // Go to a particular slide.
+
+ /*
+ a. If the slide attribute references a slide name not present on the time context an error must
+ be logged each time the executable would have been run, and the executable ignored.
+ b. If the time context is already on the slide referenced, no change is made to the time
+ context.
+ The slide does not restart. */
+ struct SGotoSlide
+ {
+ CRegisteredString m_Component;
+ CRegisteredString m_Slide;
+ SGotoSlideData m_GotoSlideData;
+ SGotoSlide(CRegisteredString cmd = CRegisteredString(),
+ CRegisteredString slide = CRegisteredString())
+ : m_Component(cmd)
+ , m_Slide(slide)
+ {
+ }
+ bool operator==(const SGotoSlide &inOther) const
+ {
+ return m_Component == inOther.m_Component && m_Slide == inOther.m_Slide;
+ }
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_Component);
+ inRemapper.Remap(m_Slide);
+ }
+ };
+
+ /*
+ 1. A <call.../> executable must have a element="..." attribute that references a behavior
+ element in one of the presentation assets for the application.
+ a. If the element attribute is missing, or references an element that cannot be found,
+ an error must be logged each time the executable would have been run, and the executable
+ ignored.
+ b. If the element attribute references an element that is not a behavior then an error
+ must be logged each time the executable would have been run, and the executable ignored.
+ 2. A <call.../> executable must have a handler="..." attribute that references a function value
+ in the table associated with the behavior element.
+ a. If the handler attribute is missing, or references a value that is not a function
+ value, an error must be logged each time the executable would have been run, and the executable
+ ignored.
+ 3. A <call.../> executable may have an args="..." attribute that specifies an expression to
+ evaluate.
+ a. If the result of evaluating this expression is a single value, it is passed as the
+ first argument to the behavior's function.
+ b. If the result of evaluating this expression is a table, it is treated as a list that
+ is unpack'd, the numeric properties sent as arguments in order.
+ c. If the result of evaluating this expression is an error, an error message must be
+ logged and the executable ignored.
+ The function is not invoked with no parameters.
+ */
+ struct SCallFunction
+ {
+ CRegisteredString m_Behavior;
+ CRegisteredString m_Handler;
+ CRegisteredString m_Arguments;
+ SCallFunction(CRegisteredString behavior = CRegisteredString(),
+ CRegisteredString hdler = CRegisteredString(),
+ CRegisteredString args = CRegisteredString())
+ : m_Behavior(behavior)
+ , m_Handler(hdler)
+ , m_Arguments(args)
+ {
+ }
+ bool operator==(const SCallFunction &inOther) const
+ {
+ return m_Behavior == inOther.m_Behavior && m_Handler == inOther.m_Handler
+ && m_Arguments == inOther.m_Arguments;
+ }
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_Behavior);
+ inRemapper.Remap(m_Handler);
+ inRemapper.Remap(m_Arguments);
+ }
+ };
+
+ /*
+ 1. A <set-attribute.../> executable must have an element="..." attribute that references an
+ element in one of the presentation assets for the application.
+ a. If the element attribute is missing, or references an element that cannot be found,
+ an error must be logged each time the executable would have been run, and the executable
+ ignored.
+ 2. A <set-attribute.../> executable must have an attribute="..." attribute that references an
+ attribute on the referenced element.
+ a. If the attribute attribute is missing, or references an attribute not present on the
+ element, an error must be logged each time the executable would have been run, and the
+ executable ignored.
+ 3. A <set-attribute.../> executable must have an value="..." attribute that describes the value
+ to set.
+ a. The contents of this attribute are evaluated as an expression and the result used
+ to set the attribute.
+ i. If the result of evaluating this expression does not match the type of the
+ attribute on the element then an error must be logged and the executable ignored.
+ 4. If a single visual state has both <goto-slide/> and <set-attribute/> executables, and the
+ slide change affects the same attribute as the set-attribute executable, then the set-attribute
+ executable must take effect (be applied after the slide change occurs).
+ In the future we may wish to have the order of this interaction controllable by the
+ ordering of the executables.
+ */
+ struct SSetAttribute
+ {
+ CRegisteredString m_Element;
+ CRegisteredString m_Attribute;
+ CRegisteredString m_Value;
+ SSetAttribute(CRegisteredString elem = CRegisteredString(),
+ CRegisteredString att = CRegisteredString(),
+ CRegisteredString val = CRegisteredString())
+ : m_Element(elem)
+ , m_Attribute(att)
+ , m_Value(val)
+ {
+ }
+ bool operator==(const SSetAttribute &inOther) const
+ {
+ return m_Element == inOther.m_Element && m_Attribute == inOther.m_Attribute
+ && m_Value == inOther.m_Value;
+ }
+
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_Element);
+ inRemapper.Remap(m_Attribute);
+ inRemapper.Remap(m_Value);
+ }
+ };
+
+ /*
+ 4. A rel="..." attribute must have either the value next or prev.
+ a. If the rel attribute has a different value an error must be logged each time the
+ executable would have been run, and the executable ignored.
+ b. A value of next causes the time context to go to the next slide.
+ i. If the time context is at the last slide, and there is no wrap attribute, or
+ the wrap attribute does not have a value of true, then no change occurs to the time context.
+ The slide does not restart.
+ ii. If the time context is at the last slide and there exists a wrap attribute
+ with a value of true then the time context is taken to the first slide.
+ c. A value of prev causes the time context to go to the previous slide.
+ i. If the time context is at the first slide, and there is no wrap attribute, or
+ the wrap attribute does not have a value of true, then no change occurs to the time context.
+ The slide does not restart.
+ ii. If the time context is at the last first and there exists a wrap attribute
+ with a value of true then the time context is taken to the last slide.
+ */
+ struct SGotoSlideRelative
+ {
+ CRegisteredString m_Component;
+ enum Enum {
+ Next = 0,
+ Previous,
+ Error,
+ };
+ Enum m_Direction;
+ bool m_Wrap;
+ SGotoSlideData m_GotoSlideData;
+ SGotoSlideRelative(CRegisteredString comp = CRegisteredString(), Enum dir = Next,
+ bool wrap = false)
+ : m_Component(comp)
+ , m_Direction(dir)
+ , m_Wrap(wrap)
+ {
+ }
+ bool operator==(const SGotoSlideRelative &inOther) const
+ {
+ return m_Component == inOther.m_Component && m_Direction == inOther.m_Direction
+ && m_Wrap == inOther.m_Wrap;
+ }
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_Component);
+ }
+ };
+
+ /*
+ 1. A <fire-event.../> executable must have an element="..." attribute that references an element
+ in one of the presentation assets for the application.
+ a. If the element attribute is missing, or references an element that cannot be found,
+ an error must be logged each time the executable would have been run, and the executable
+ ignored.
+ 2. A <fire-event.../> executable must have an event="..." attribute that describes the event
+ name to fire.
+ a. If the event attribute is missing an error must be logged each time the executable
+ would have been run, and the executable ignored.
+ */
+ struct SFireEvent
+ {
+ CRegisteredString m_Element;
+ CRegisteredString m_Event;
+ SFireEvent(CRegisteredString elem, CRegisteredString evt)
+ : m_Element(elem)
+ , m_Event(evt)
+ {
+ }
+ SFireEvent() {}
+ bool operator==(const SFireEvent &inOther) const
+ {
+ return m_Element == inOther.m_Element && m_Event == inOther.m_Event;
+ }
+
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_Element);
+ inRemapper.Remap(m_Event);
+ }
+ };
+
+ struct SPresentationAttribute
+ {
+ CRegisteredString m_Presentation;
+ CRegisteredString m_Attribute;
+ CRegisteredString m_Value;
+ SPresentationAttribute(CRegisteredString pres, CRegisteredString att, CRegisteredString val)
+ : m_Presentation(pres)
+ , m_Attribute(att)
+ , m_Value(val)
+ {
+ }
+ SPresentationAttribute() {}
+ bool operator==(const SPresentationAttribute &inOther) const
+ {
+ return m_Presentation == inOther.m_Presentation && m_Attribute == inOther.m_Attribute
+ && m_Value == inOther.m_Value;
+ }
+
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_Presentation);
+ inRemapper.Remap(m_Attribute);
+ inRemapper.Remap(m_Value);
+ }
+ };
+
+ struct SPlaySound
+ {
+ CRegisteredString m_SoundFilePath;
+ SPlaySound(CRegisteredString inSoundFilePath)
+ : m_SoundFilePath(inSoundFilePath)
+ {
+ }
+ SPlaySound() {}
+ bool operator==(const SPlaySound &inOther) const
+ {
+ return m_SoundFilePath == inOther.m_SoundFilePath;
+ }
+
+ template <typename TRemapper>
+ void Remap(TRemapper &inRemapper)
+ {
+ inRemapper.Remap(m_SoundFilePath);
+ }
+ };
+
+ // defined in UICStateVisualBindingContextValues.h
+ struct SVisualStateCommand;
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextValues.h b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextValues.h
new file mode 100644
index 00000000..7bad9b33
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateVisualBindingContextValues.h
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** 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_STATE_VISUAL_BINDING_CONTEXT_VALUES_H
+#define QT3DS_STATE_VISUAL_BINDING_CONTEXT_VALUES_H
+#include "Qt3DSStateVisualBindingContext.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "Qt3DSStateVisualBindingContextCommands.h"
+
+namespace qt3ds {
+namespace state {
+
+ template <typename TDataType>
+ struct SVisualStateCommandTypeMap
+ {
+ };
+
+ template <>
+ struct SVisualStateCommandTypeMap<SGotoSlide>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::GotoSlide;
+ }
+ };
+ template <>
+ struct SVisualStateCommandTypeMap<SCallFunction>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::CallFunction;
+ }
+ };
+ template <>
+ struct SVisualStateCommandTypeMap<SSetAttribute>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::SetAttribute;
+ }
+ };
+ template <>
+ struct SVisualStateCommandTypeMap<SGotoSlideRelative>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::GotoSlideRelative;
+ }
+ };
+ template <>
+ struct SVisualStateCommandTypeMap<SFireEvent>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::FireEvent;
+ }
+ };
+ template <>
+ struct SVisualStateCommandTypeMap<SPresentationAttribute>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::PresentationAttribute;
+ }
+ };
+ template <>
+ struct SVisualStateCommandTypeMap<SPlaySound>
+ {
+ static VisualStateCommandTypes::Enum GetType()
+ {
+ return VisualStateCommandTypes::PlaySound;
+ }
+ };
+
+ struct SVisualStateCommandUnionTraits
+ {
+ typedef VisualStateCommandTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(SGotoSlideRelative),
+ };
+
+ static TIdType getNoDataId() { return VisualStateCommandTypes::NoVisualStateCommand; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SVisualStateCommandTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case VisualStateCommandTypes::GotoSlide:
+ return inVisitor(*NVUnionCast<SGotoSlide *>(inData));
+ case VisualStateCommandTypes::CallFunction:
+ return inVisitor(*NVUnionCast<SCallFunction *>(inData));
+ case VisualStateCommandTypes::SetAttribute:
+ return inVisitor(*NVUnionCast<SSetAttribute *>(inData));
+ case VisualStateCommandTypes::GotoSlideRelative:
+ return inVisitor(*NVUnionCast<SGotoSlideRelative *>(inData));
+ case VisualStateCommandTypes::FireEvent:
+ return inVisitor(*NVUnionCast<SFireEvent *>(inData));
+ case VisualStateCommandTypes::PresentationAttribute:
+ return inVisitor(*NVUnionCast<SPresentationAttribute *>(inData));
+ case VisualStateCommandTypes::PlaySound:
+ return inVisitor(*NVUnionCast<SPlaySound *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case VisualStateCommandTypes::NoVisualStateCommand:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case VisualStateCommandTypes::GotoSlide:
+ return inVisitor(*NVUnionCast<const SGotoSlide *>(inData));
+ case VisualStateCommandTypes::CallFunction:
+ return inVisitor(*NVUnionCast<const SCallFunction *>(inData));
+ case VisualStateCommandTypes::SetAttribute:
+ return inVisitor(*NVUnionCast<const SSetAttribute *>(inData));
+ case VisualStateCommandTypes::GotoSlideRelative:
+ return inVisitor(*NVUnionCast<const SGotoSlideRelative *>(inData));
+ case VisualStateCommandTypes::FireEvent:
+ return inVisitor(*NVUnionCast<const SFireEvent *>(inData));
+ case VisualStateCommandTypes::PresentationAttribute:
+ return inVisitor(*NVUnionCast<const SPresentationAttribute *>(inData));
+ case VisualStateCommandTypes::PlaySound:
+ return inVisitor(*NVUnionCast<const SPlaySound *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case VisualStateCommandTypes::NoVisualStateCommand:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SVisualStateCommandUnionTraits,
+ SVisualStateCommandUnionTraits::
+ TBufferSize>,
+ SVisualStateCommandUnionTraits::TBufferSize>
+ TVisualStateCommandUnionType;
+
+ struct SVisualStateCommand : public TVisualStateCommandUnionType
+ {
+ SVisualStateCommand() {}
+
+ SVisualStateCommand(const SVisualStateCommand &inOther)
+ : TVisualStateCommandUnionType(
+ static_cast<const TVisualStateCommandUnionType &>(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SVisualStateCommand(const TDataType &inDt)
+ : TVisualStateCommandUnionType(inDt)
+ {
+ }
+
+ SVisualStateCommand &operator=(const SVisualStateCommand &inOther)
+ {
+ TVisualStateCommandUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SVisualStateCommand &inOther) const
+ {
+ return TVisualStateCommandUnionType::operator==(inOther);
+ }
+ bool operator!=(const SVisualStateCommand &inOther) const
+ {
+ return TVisualStateCommandUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == VisualStateCommandTypes::NoVisualStateCommand; }
+ };
+}
+}
+#ifndef _INTEGRITYPLATFORM
+namespace qt3ds {
+namespace foundation {
+
+ template <>
+ struct DestructTraits<qt3ds::state::SGotoSlide>
+ {
+ void destruct(qt3ds::state::SGotoSlide &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SCallFunction>
+ {
+ void destruct(qt3ds::state::SCallFunction &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SSetAttribute>
+ {
+ void destruct(qt3ds::state::SSetAttribute &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SGotoSlideRelative>
+ {
+ void destruct(qt3ds::state::SGotoSlideRelative &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SFireEvent>
+ {
+ void destruct(qt3ds::state::SFireEvent &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SPresentationAttribute>
+ {
+ void destruct(qt3ds::state::SPresentationAttribute &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::SPlaySound>
+ {
+ void destruct(qt3ds::state::SPlaySound &) {}
+ };
+}
+}
+#endif
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.cpp b/src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.cpp
new file mode 100644
index 00000000..44f3242f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.cpp
@@ -0,0 +1,1948 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateXMLIO.h"
+#include "foundation/XML.h"
+#include "Qt3DSStateTypes.h"
+#include "Qt3DSStateContext.h"
+#include "foundation/Utils.h"
+#include "foundation/StringConversionImpl.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "EASTL/hash_map.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorValue.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::editor;
+
+namespace {
+#define ITERATE_XML_ELEMENT_NAMES \
+ HANDLE_XML_ELEMENT_NAME(scxml) \
+ HANDLE_XML_ELEMENT_NAME(state) \
+ HANDLE_XML_ELEMENT_NAME(parallel) \
+ HANDLE_XML_ELEMENT_NAME(transition) \
+ HANDLE_XML_ELEMENT_NAME(initial) \
+ HANDLE_XML_ELEMENT_NAME(final) \
+ HANDLE_XML_ELEMENT_NAME(onentry) \
+ HANDLE_XML_ELEMENT_NAME(onexit) \
+ HANDLE_XML_ELEMENT_NAME(history) \
+ HANDLE_XML_ELEMENT_NAME(raise) \
+ HANDLE_XML_ELEMENT_NAME(if) \
+ HANDLE_XML_ELEMENT_NAME(elseif) \
+ HANDLE_XML_ELEMENT_NAME(else) \
+ HANDLE_XML_ELEMENT_NAME(foreach) \
+ HANDLE_XML_ELEMENT_NAME(log) \
+ HANDLE_XML_ELEMENT_NAME(param) \
+ HANDLE_XML_ELEMENT_NAME(assign) \
+ HANDLE_XML_ELEMENT_NAME(script) \
+ HANDLE_XML_ELEMENT_NAME(send) \
+ HANDLE_XML_ELEMENT_NAME(cancel) \
+ HANDLE_XML_ELEMENT_NAME(invoke) \
+ HANDLE_XML_ELEMENT_NAME(finalize) \
+ HANDLE_XML_ELEMENT_NAME(cond) \
+ HANDLE_XML_ELEMENT_NAME(event) \
+ HANDLE_XML_ELEMENT_NAME(datamodel) \
+ HANDLE_XML_ELEMENT_NAME(data) \
+ HANDLE_XML_ELEMENT_NAME(content) \
+ HANDLE_XML_ELEMENT_NAME(external_transition)
+
+struct SXMLName
+{
+ enum Enum {
+#define HANDLE_XML_ELEMENT_NAME(nm) e##nm,
+ ITERATE_XML_ELEMENT_NAMES
+#undef HANDLE_XML_ELEMENT_NAME
+ LastName,
+ };
+ static const char *GetNameForElemName(Enum inName)
+ {
+ switch (inName) {
+#define HANDLE_XML_ELEMENT_NAME(nm) \
+ case e##nm: \
+ return #nm;
+ ITERATE_XML_ELEMENT_NAMES
+#undef HANDLE_XML_ELEMENT_NAME
+ default:
+ break;
+ }
+ QT3DS_ASSERT(false);
+ return "unknown element";
+ }
+};
+
+const char8_t *GetSCXMLNamespace()
+{
+ return "http://www.w3.org/2005/07/scxml";
+}
+const char8_t *GetStudioStateNamespace()
+{
+ return "http://qt.io/qt3dstudio/uicstate";
+}
+
+typedef eastl::pair<const char8_t *, NVConstDataRef<SStateNode *> *> TIdListPtrPair;
+typedef eastl::pair<const char8_t *, SSend **> TIdSendPair;
+
+TEditorStr DoGenerateUniqueId(const char *inRoot, IStateContext &ioContext,
+ IStringTable &ioStringTable)
+{
+ if (isTrivial(inRoot))
+ inRoot = "id";
+ TEditorStr stem(inRoot);
+ TEditorStr idStr(stem);
+ // Make the stem a valid possible id according xml specifications
+ if (idStr.size()) {
+ // Replace all spaces with undre
+ // Check that the first item isn't a space or a number. We can't do a real check here
+ // because we don't have unicode tables of letters and such.
+ // replace spaces with underscores.
+ for (TEditorStr::size_type thePos = idStr.find(' '); thePos != TEditorStr::npos;
+ thePos = idStr.find(' ', thePos + 1))
+ idStr.replace(thePos, 1, "_");
+ if (idStr[0] >= '0' && idStr[0] <= '9')
+ idStr.insert(idStr.begin(), 1, ':');
+ }
+
+ QT3DSU32 idx = 0;
+
+ while (ioContext.ContainsId(ioStringTable.RegisterStr(idStr.c_str()))) {
+ ++idx;
+ char temp[64];
+ sprintf(temp, "%d", idx);
+ idStr.assign(stem);
+ idStr.append("_");
+ idStr.append(temp);
+ }
+
+ return idStr;
+}
+
+struct SParseContext
+{
+ typedef nvvector<eastl::pair<CRegisteredString, STransition *>> TExternalTransitionList;
+
+ NVAllocatorCallback &m_GraphAllocator;
+ NVFoundationBase &m_Foundation;
+ IDOMReader &m_Reader;
+ IStateContext &m_Context;
+ IStringTable &m_StrTable;
+ IEditor *m_Editor;
+ CRegisteredString m_Names[SXMLName::LastName];
+ MemoryBuffer<ForwardingAllocator> m_ParseBuffer;
+ nvvector<char8_t> m_TempBuffer;
+ // To be filled in on the second parse pass
+ nvvector<TIdListPtrPair> m_References;
+ nvvector<TIdSendPair> m_SendReferences;
+ CXMLIO::TIdRemapMap m_RemapMap;
+ nvvector<SIdValue> m_GenerateIdList;
+ TExternalTransitionList m_ExternalTransitions;
+ CRegisteredString m_SCXMLNamespace;
+ CRegisteredString m_StudioNamespace;
+ QT3DSI32 m_Version;
+
+ SParseContext(NVAllocatorCallback &inGraphAlloc, NVFoundationBase &inFnd, IDOMReader &inReader,
+ IStateContext &inCtx, IStringTable &inStrTable, IEditor *inEditor)
+ : m_GraphAllocator(inGraphAlloc)
+ , m_Foundation(inFnd)
+ , m_Reader(inReader)
+ , m_Context(inCtx)
+ , m_StrTable(inStrTable)
+ , m_Editor(inEditor)
+ , m_ParseBuffer(ForwardingAllocator(inFnd.getAllocator(), "ParseBuffer"))
+ , m_TempBuffer(inFnd.getAllocator(), "TempBuffer")
+ , m_References(inFnd.getAllocator(), "m_References")
+ , m_SendReferences(inFnd.getAllocator(), "m_StrReferences")
+ , m_GenerateIdList(inFnd.getAllocator(), "m_GenerateIdList")
+ , m_ExternalTransitions(inFnd.getAllocator(), "m_ExternalTransitions")
+ , m_Version(SSCXML::GetCurrentVersion())
+ {
+#define HANDLE_XML_ELEMENT_NAME(nm) m_Names[SXMLName::e##nm] = inStrTable.RegisterStr(#nm);
+ ITERATE_XML_ELEMENT_NAMES
+#undef HANDLE_XML_ELEMENT_NAME
+ m_SCXMLNamespace = inStrTable.RegisterStr(GetSCXMLNamespace());
+ m_StudioNamespace = inStrTable.RegisterStr(GetStudioStateNamespace());
+ }
+
+ inline bool eq(CRegisteredString lhs, SXMLName::Enum rhs) { return lhs == m_Names[rhs]; }
+ const char8_t *ToGraphStr(const char8_t *temp)
+ {
+ temp = nonNull(temp);
+ QT3DSU32 len = StrLen(temp) + 1;
+ char8_t *retval =
+ (char8_t *)m_GraphAllocator.allocate(len, "graph string", __FILE__, __LINE__);
+ memCopy(retval, temp, len);
+ // Force null termination regardless.
+ retval[len] = 0;
+ return retval;
+ }
+ const char8_t *ParseStrAtt(const char8_t *attName)
+ {
+ const char8_t *temp;
+ if (m_Reader.UnregisteredAtt(attName, temp) && temp && *temp)
+ return ToGraphStr(temp);
+ return NULL;
+ }
+ void ParseStrAtt(const char8_t *attName, const char8_t *&outVal)
+ {
+ outVal = ParseStrAtt(attName);
+ }
+ void ParseStrAtt(const char8_t *attName, CRegisteredString &outVal)
+ {
+ m_Reader.Att(attName, outVal);
+ }
+
+ SItemExtensionInfo &GetExtensionInfo(void *inItem)
+ {
+ return m_Context.GetOrCreateExtensionInfo(inItem);
+ }
+
+ // Extension attributes are indicated by their namespace. If they have a namespace
+ // and they aren't scxml namespace and they aren't studio namespace, they are extension.
+ void ParseExtensionAttributes(void *inItem)
+ {
+ for (SDOMAttribute *theAttribute = m_Reader.GetFirstAttribute(); theAttribute;
+ theAttribute = theAttribute->m_NextAttribute) {
+ if (theAttribute->m_Namespace != m_StudioNamespace
+ && theAttribute->m_Namespace != m_SCXMLNamespace
+ && theAttribute->m_Namespace.IsValid()) {
+ GetExtensionInfo(inItem).m_ExtensionAttributes.push_back(
+ *QT3DS_NEW(m_GraphAllocator, SDOMAttributeNode)(theAttribute));
+ }
+ }
+ }
+ bool ParseExtensionElement(void *inItem)
+ {
+ SDOMElement &theElement(*m_Reader.GetElement());
+
+ if (theElement.m_Namespace != m_StudioNamespace
+ && theElement.m_Namespace != m_SCXMLNamespace && theElement.m_Namespace.IsValid()) {
+ GetExtensionInfo(inItem).m_ExtensionNodes.push_back(
+ *QT3DS_NEW(m_GraphAllocator, SDOMElementNode)(&theElement));
+ return true;
+ }
+ return false;
+ }
+
+ void ParseExtensionElements(void *inItem)
+ {
+ IDOMReader::Scope _childElemScope(m_Reader);
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ ParseExtensionElement(inItem);
+ }
+ }
+
+ NVConstDataRef<QT3DSF32> ParseFloats(const char8_t *inData)
+ {
+ size_t len = StrLen(inData) + 1;
+ if (len == 1)
+ return NVConstDataRef<QT3DSF32>();
+ m_TempBuffer.resize((QT3DSU32)len);
+ memCopy(m_TempBuffer.data(), inData, (QT3DSU32)len);
+ m_ParseBuffer.clear();
+ Char8TReader theReader(m_TempBuffer.data(), m_ParseBuffer);
+ NVConstDataRef<QT3DSF32> retval;
+ theReader.ReadBuffer(retval);
+ return retval;
+ }
+ bool ParseVec2Att(const char8_t *attName, QT3DSVec2 &outVal)
+ {
+ const char8_t *tempVal;
+ if (m_Reader.UnregisteredAtt(attName, tempVal, GetStudioStateNamespace())) {
+ NVConstDataRef<QT3DSF32> floats = ParseFloats(tempVal);
+ if (floats.mSize >= 2) {
+ memCopy(&outVal.x, floats.mData, sizeof(outVal));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ParseVec3Att(const char8_t *attName, QT3DSVec3 &outVal)
+ {
+ const char8_t *tempVal;
+ if (m_Reader.UnregisteredAtt(attName, tempVal, GetStudioStateNamespace())) {
+ NVConstDataRef<QT3DSF32> floats = ParseFloats(tempVal);
+ if (floats.mSize >= 3) {
+ memCopy(&outVal.x, floats.mData, sizeof(outVal));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ CRegisteredString RemapStr(CRegisteredString inStr)
+ {
+ CXMLIO::TIdRemapMap::iterator iter = m_RemapMap.find(inStr.c_str());
+ if (iter != m_RemapMap.end())
+ inStr = m_StrTable.RegisterStr(iter->second.c_str());
+ return inStr;
+ }
+
+ SStateNode *FindStateNode(CRegisteredString inString)
+ {
+ return m_Context.FindStateNode(RemapStr(inString));
+ }
+
+ SSend *ParseSendIdSecondPass(const char8_t *inStr)
+ {
+ if (isTrivial(inStr))
+ return NULL;
+ return m_Context.FindSend(RemapStr(m_StrTable.RegisterStr(inStr)));
+ }
+
+ NVConstDataRef<SStateNode *> ParseIDRefSecondPass(const char8_t *inStr)
+ {
+ typedef eastl::basic_string<char8_t, ForwardingAllocator> TStrType;
+ TStrType workspace(ForwardingAllocator(m_Foundation.getAllocator(), "ParseIDRef"));
+ TStrType str(ForwardingAllocator(m_Foundation.getAllocator(), "ParseIDRef"));
+ nvvector<SStateNode *> tempVal(m_Foundation.getAllocator(), "ParseIDRef");
+
+ workspace.assign(inStr);
+ for (TStrType::size_type startPos = workspace.find_first_not_of(' '),
+ endPos = workspace.find_first_of(' ', startPos);
+ startPos != TStrType::npos; startPos = workspace.find_first_not_of(' ', endPos),
+ endPos = workspace.find_first_of(' ', startPos)) {
+ if (endPos == TStrType::npos)
+ endPos = workspace.size();
+ str = workspace.substr(startPos, endPos - startPos);
+ CRegisteredString theStr(m_StrTable.RegisterStr(str.c_str()));
+ SStateNode *theNode = FindStateNode(theStr);
+ if (theNode)
+ tempVal.push_back(theNode);
+ }
+ if (tempVal.size() == 0)
+ return NVConstDataRef<SStateNode *>();
+
+ SStateNode **dataPtr = (SStateNode **)m_GraphAllocator.allocate(
+ tempVal.size() * sizeof(SStateNode *), "IDRef", __FILE__, __LINE__);
+ memCopy(dataPtr, tempVal.data(), tempVal.size() * sizeof(SStateNode *));
+ return toDataRef(dataPtr, tempVal.size());
+ }
+
+ void ParseIDRef(const char8_t *inStr, NVConstDataRef<SStateNode *> &ioNodes)
+ {
+ if (inStr == NULL || *inStr == 0) {
+ ioNodes = NVConstDataRef<SStateNode *>();
+ return;
+ }
+ m_References.push_back(eastl::make_pair(inStr, &ioNodes));
+ }
+
+ static void AppendChild(SStateNode &inParent, TStateNodeList &outChildren, SStateNode &inChild)
+ {
+ inChild.m_Parent = &inParent;
+ outChildren.push_back(inChild);
+ }
+
+ static void AppendChild(SStateNode *inNodeParent, SExecutableContent *inParent,
+ TExecutableContentList &outChildren, SExecutableContent &inChild)
+ {
+ if (inNodeParent) {
+ QT3DS_ASSERT(inParent == NULL);
+ } else {
+ QT3DS_ASSERT(inParent);
+ }
+ inChild.m_StateNodeParent = inNodeParent;
+ inChild.m_Parent = inParent;
+ outChildren.push_back(inChild);
+ }
+
+ template <typename TStateType>
+ void ParseEditorAttributes(TStateType &inNode)
+ {
+ ParseExtensionAttributes(&inNode);
+ if (m_Editor) {
+ const char8_t *name;
+ if (m_Reader.UnregisteredAtt("id", name, GetStudioStateNamespace()))
+ m_Editor->GetOrCreate(inNode)->SetPropertyValue("id", eastl::string(nonNull(name)));
+
+ QT3DSVec2 temp;
+ if (ParseVec2Att("position", temp))
+ m_Editor->GetOrCreate(inNode)->SetPropertyValue("position", temp);
+
+ if (ParseVec2Att("dimension", temp))
+ m_Editor->GetOrCreate(inNode)->SetPropertyValue("dimension", temp);
+
+ QT3DSVec3 tempv3;
+ if (ParseVec3Att("color", tempv3))
+ m_Editor->GetOrCreate(inNode)->SetPropertyValue("dimension", tempv3);
+ }
+ }
+
+ void ParseStateNodeEditorAttributes(SStateNode &inNode)
+ {
+ ParseExtensionAttributes(&inNode);
+ CRegisteredString Id;
+ if (inNode.m_Id.IsValid() == false)
+ m_Reader.Att("id", inNode.m_Id);
+
+ QT3DSVec2 temp;
+ if (ParseVec2Att("position", temp))
+ inNode.SetPosition(temp);
+
+ if (ParseVec2Att("dimension", temp))
+ inNode.SetDimension(temp);
+
+ QT3DSVec3 tempv3;
+ if (ParseVec3Att("color", tempv3))
+ inNode.SetColor(tempv3);
+
+ if (m_Editor) {
+ const char8_t *desc;
+ if (m_Reader.UnregisteredAtt("description", desc, GetStudioStateNamespace()))
+ m_Editor->ToEditor(inNode)->SetPropertyValue("description",
+ eastl::string(nonNull(desc)));
+ }
+ }
+
+ // Parse the node id. IF it exists, then schedule the id for remapping.
+ template <typename TDataType>
+ void ParseNodeId(TDataType &inNode)
+ {
+ m_Reader.Att("id", inNode.m_Id);
+ if (inNode.m_Id.IsValid() == false || m_Context.ContainsId(inNode.m_Id)) {
+ m_GenerateIdList.push_back(&inNode);
+ } else {
+ bool success = m_Context.InsertId(inNode.m_Id, &inNode);
+ (void)success;
+ QT3DS_ASSERT(success);
+ }
+ }
+
+ SParam &ParseParam()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SParam *retval = QT3DS_NEW(m_GraphAllocator, SParam)();
+ ParseStrAtt("name", retval->m_Name);
+ ParseStrAtt("expr", retval->m_Expr);
+ ParseStrAtt("location", retval->m_Location);
+ ParseExtensionAttributes(retval);
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+
+ SContent &ParseContent()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SContent *retval = QT3DS_NEW(m_GraphAllocator, SContent)();
+ ParseStrAtt("expr", retval->m_Expr);
+ if (isTrivial(retval->m_Expr)) {
+ if (m_Reader.CountChildren() == 0) {
+ const char8_t *val = NULL;
+ m_Reader.Value(val);
+ if (!isTrivial(val))
+ retval->m_ContentValue = this->ToGraphStr(val);
+ }
+ // We don't implement any extensions, so this is most certainly going into the
+ // extensions bin for this content.
+ else {
+ }
+ }
+ ParseExtensionAttributes(retval);
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+
+ SExecutableContent &ParseSend()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SSend *retval = QT3DS_NEW(m_GraphAllocator, SSend)();
+ ParseStrAtt("event", retval->m_Event);
+ ParseStrAtt("eventexpr", retval->m_EventExpr);
+ ParseStrAtt("target", retval->m_Target);
+ ParseStrAtt("targetexpr", retval->m_TargetExpr);
+ ParseStrAtt("type", retval->m_Type);
+ ParseStrAtt("typeExpr", retval->m_TypeExpr);
+ ParseNodeId(*retval);
+ ParseStrAtt("idlocation", retval->m_IdLocation);
+ ParseStrAtt("delay", retval->m_Delay);
+ ParseStrAtt("delayexpr", retval->m_DelayExpr);
+ ParseStrAtt("namelist", retval->m_NameList);
+ ParseExtensionAttributes(retval);
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (eq(elemName, SXMLName::eparam))
+ AppendChild(NULL, retval, retval->m_Children, ParseParam());
+ else if (eq(elemName, SXMLName::econtent))
+ retval->m_Children.push_back(ParseContent());
+ else {
+ if (!ParseExtensionElement(retval)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse send child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ m_Context.AddSendToList(*retval);
+ return *retval;
+ }
+ SExecutableContent &ParseIf()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SIf *retval = QT3DS_NEW(m_GraphAllocator, SIf)();
+ ParseStrAtt("cond", retval->m_Cond);
+ ParseExtensionAttributes(retval);
+ ParseExecutableContent(NULL, retval, retval->m_Children, retval);
+ return *retval;
+ }
+
+ SExecutableContent &ParseElseIf()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SElseIf *retval = QT3DS_NEW(m_GraphAllocator, SElseIf)();
+ ParseStrAtt("cond", retval->m_Cond);
+ ParseExtensionAttributes(retval);
+ ParseExecutableContent(NULL, retval, retval->m_Children, retval);
+ return *retval;
+ }
+
+ SExecutableContent &ParseElse()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SElse *retval = QT3DS_NEW(m_GraphAllocator, SElse)();
+ ParseExtensionAttributes(retval);
+ ParseExecutableContent(NULL, retval, retval->m_Children, retval);
+ return *retval;
+ }
+ SExecutableContent &ParseForEach()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SForeach *retval = QT3DS_NEW(m_GraphAllocator, SForeach)();
+ ParseStrAtt("array", retval->m_Array);
+ ParseStrAtt("item", retval->m_Item);
+ ParseStrAtt("index", retval->m_Index);
+ ParseExtensionAttributes(retval);
+ ParseExecutableContent(NULL, retval, retval->m_Children, retval);
+ return *retval;
+ }
+
+ SExecutableContent &ParseRaise()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SRaise *retval = QT3DS_NEW(m_GraphAllocator, SRaise)();
+ ParseStrAtt("event", retval->m_Event);
+ ParseExtensionAttributes(retval);
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+
+ SExecutableContent &ParseLog()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SLog *retval = QT3DS_NEW(m_GraphAllocator, SLog)();
+ ParseStrAtt("label", retval->m_Label);
+ ParseStrAtt("expr", retval->m_Expression);
+ ParseExtensionAttributes(retval);
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+
+ SData &ParseData()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SData *retval = QT3DS_NEW(m_GraphAllocator, SData)();
+ ParseStrAtt("id", retval->m_Id);
+ ParseStrAtt("src", retval->m_Source);
+ ParseStrAtt("expr", retval->m_Expression);
+ ParseEditorAttributes(*retval);
+ ParseExtensionElements(retval);
+ const char8_t *value;
+ m_Reader.Value(value);
+ // Not handling arbitrary content under data right now.
+ if (isTrivial(retval->m_Expression) && !isTrivial(value)) {
+ retval->m_Expression = ToGraphStr(value);
+ }
+ return *retval;
+ }
+
+ SDataModel *ParseDataModel()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SDataModel *retval = QT3DS_NEW(m_GraphAllocator, SDataModel)();
+ ParseStrAtt("id", retval->m_Id);
+ ParseStrAtt("src", retval->m_Source);
+ ParseStrAtt("expr", retval->m_Expression);
+ ParseEditorAttributes(*retval);
+ for (bool success = m_Reader.MoveToFirstChild("data"); success;
+ success = m_Reader.MoveToNextSibling("data"))
+ retval->m_Data.push_back(ParseData());
+
+ ParseExtensionElements(retval);
+
+ return retval;
+ }
+
+ SExecutableContent &ParseAssign()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SAssign *retval = QT3DS_NEW(m_GraphAllocator, SAssign)();
+ ParseStrAtt("location", retval->m_Location);
+ ParseStrAtt("expr", retval->m_Expression);
+ ParseExtensionAttributes(retval);
+ ParseExtensionElements(retval);
+ // Not dealing with children.
+ return *retval;
+ }
+
+ SExecutableContent &ParseScript()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SScript *retval = QT3DS_NEW(m_GraphAllocator, SScript)();
+ ParseStrAtt("src", retval->m_URL);
+ ParseExtensionAttributes(retval);
+ if (m_Reader.Value(retval->m_Data))
+ retval->m_Data = ToGraphStr(retval->m_Data);
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+
+ SExecutableContent &ParseCancel()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SCancel *retval = QT3DS_NEW(m_GraphAllocator, SCancel)();
+ const char8_t *sendId;
+ if (m_Reader.UnregisteredAtt("sendid", sendId))
+ m_SendReferences.push_back(eastl::make_pair(sendId, &retval->m_Send));
+ ParseStrAtt("sendidexpr", retval->m_IdExpression);
+ ParseExtensionAttributes(retval);
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+ struct SExecutableContentParseBinding
+ {
+ SXMLName::Enum m_Name;
+ typedef SExecutableContent &(SParseContext::*TParseFunc)();
+ TParseFunc m_ParseFunc;
+ };
+
+ static NVDataRef<SExecutableContentParseBinding> GetParseBindings()
+ {
+ static SExecutableContentParseBinding s_ExecContentParseBindings[10] = {
+ { SXMLName::esend, &SParseContext::ParseSend },
+ { SXMLName::eif, &SParseContext::ParseIf },
+ { SXMLName::eelseif, &SParseContext::ParseElseIf },
+ { SXMLName::eelse, &SParseContext::ParseElse },
+ { SXMLName::eforeach, &SParseContext::ParseForEach },
+ { SXMLName::eraise, &SParseContext::ParseRaise },
+ { SXMLName::elog, &SParseContext::ParseLog },
+ { SXMLName::eassign, &SParseContext::ParseAssign },
+ { SXMLName::escript, &SParseContext::ParseScript },
+ { SXMLName::ecancel, &SParseContext::ParseCancel },
+ };
+
+ return toDataRef(s_ExecContentParseBindings, 10);
+ }
+
+ bool ParseExecutableContentItem(SStateNode *inNodeParent, SExecutableContent *inParent,
+ TExecutableContentList &outContent)
+ {
+ CRegisteredString elemName = m_Reader.GetElementName();
+ NVDataRef<SExecutableContentParseBinding> theBindingList(GetParseBindings());
+ for (QT3DSU32 idx = 0, end = theBindingList.size(); idx < end; ++idx) {
+ SExecutableContentParseBinding theBinding(theBindingList[idx]);
+ if (eq(elemName, theBinding.m_Name)) {
+ AppendChild(inNodeParent, inParent, outContent, (this->*theBinding.m_ParseFunc)());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void ParseExecutableContent(SStateNode *inStateNodeParent, SExecutableContent *inParent,
+ TExecutableContentList &outContent, void *inExtensionPtr)
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (!ParseExecutableContentItem(inStateNodeParent, inParent, outContent)) {
+ if (!ParseExtensionElement(inExtensionPtr)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse extension child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+
+ SOnEntry &ParseOnEntry(SStateNode &inParent)
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SOnEntry *retval = QT3DS_NEW(m_GraphAllocator, SOnEntry)();
+ ParseExecutableContent(&inParent, NULL, retval->m_ExecutableContent, retval);
+ return *retval;
+ }
+
+ SOnExit &ParseOnExit(SStateNode &inParent)
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SOnExit *retval = QT3DS_NEW(m_GraphAllocator, SOnExit)();
+ ParseExecutableContent(&inParent, NULL, retval->m_ExecutableContent, retval);
+ return *retval;
+ }
+
+ SHistory &ParseHistory()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SHistory *retval = QT3DS_NEW(m_GraphAllocator, SHistory)();
+ ParseNodeId(*retval);
+ const char8_t *temp;
+ if (m_Reader.UnregisteredAtt("type", temp) && AreEqual(temp, "deep"))
+ retval->m_Flags.SetDeep(true);
+ ParseStateNodeEditorAttributes(*retval);
+ if (m_Reader.MoveToFirstChild("transition")) {
+ retval->m_Transition = &ParseTransition();
+ retval->m_Transition->m_Parent = retval;
+ }
+ ParseExtensionElements(retval);
+ return *retval;
+ }
+
+ STransition &ParseTransition()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ STransition *retval = QT3DS_NEW(m_GraphAllocator, STransition)();
+ m_Reader.Att("event", retval->m_Event);
+ ParseNodeId(*retval);
+ const char8_t *temp;
+ if (m_Reader.UnregisteredAtt("target", temp)) {
+ ParseIDRef(temp, retval->m_Target);
+ }
+ if (m_Reader.UnregisteredAtt("type", temp) && AreEqual(temp, "internal"))
+ retval->m_Flags.SetInternal(true);
+ ParseStateNodeEditorAttributes(*retval);
+
+ retval->m_Condition = ParseStrAtt("cond");
+ // Position and such is only valid for transitions after UIC version 0.
+ if (m_Version > 0) {
+ QT3DSVec2 endPos;
+ if (ParseVec2Att("end_position", endPos))
+ retval->SetEndPosition(endPos);
+
+ if (m_Reader.UnregisteredAtt("path", temp)) {
+ NVConstDataRef<QT3DSF32> pathData = ParseFloats(temp);
+ QT3DS_ASSERT((pathData.size() % 2) == 0);
+ size_t newDataSize = pathData.size() * sizeof(QT3DSF32);
+ QT3DSVec2 *newData = (QT3DSVec2 *)m_GraphAllocator.allocate(
+ newDataSize, "STransition::m_Path", __FILE__, __LINE__);
+ memCopy(newData, pathData.begin(), (QT3DSU32)newDataSize);
+ retval->m_Path = toDataRef(newData, (QT3DSU32)(newDataSize / sizeof(QT3DSVec2)));
+ }
+ } else {
+ retval->SetPosition(Empty());
+ }
+
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (!ParseExecutableContentItem(retval, NULL, retval->m_ExecutableContent)) {
+ if (!ParseExtensionElement(retval)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse transition child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ return *retval;
+ }
+
+ SFinal &ParseFinal()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SFinal *retval = QT3DS_NEW(m_GraphAllocator, SFinal)();
+
+ ParseNodeId(*retval);
+ ParseStateNodeEditorAttributes(*retval);
+
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (eq(elemName, SXMLName::eonentry))
+ retval->m_OnEntry.push_back(ParseOnEntry(*retval));
+ else if (eq(elemName, SXMLName::eonexit))
+ retval->m_OnExit.push_back(ParseOnExit(*retval));
+ else if (!ParseExtensionElement(retval)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse final child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ return *retval;
+ }
+
+ bool ParseStateParallelChildren(SStateNode &inParent, CRegisteredString &inElemName,
+ TStateNodeList &outChildren, TOnEntryList &inOnEntry,
+ TOnExitList &inOnExit, SDataModel *&dmPtr)
+ {
+ if (eq(inElemName, SXMLName::estate))
+ AppendChild(inParent, outChildren, ParseState());
+ else if (eq(inElemName, SXMLName::etransition))
+ AppendChild(inParent, outChildren, ParseTransition());
+ else if (eq(inElemName, SXMLName::eparallel))
+ AppendChild(inParent, outChildren, ParseParallel());
+ else if (eq(inElemName, SXMLName::eonentry))
+ inOnEntry.push_back(ParseOnEntry(inParent));
+ else if (eq(inElemName, SXMLName::eonexit))
+ inOnExit.push_back(ParseOnExit(inParent));
+ else if (eq(inElemName, SXMLName::ehistory))
+ AppendChild(inParent, outChildren, ParseHistory());
+ else if (eq(inElemName, SXMLName::efinal))
+ AppendChild(inParent, outChildren, ParseFinal());
+ else if (eq(inElemName, SXMLName::edatamodel))
+ dmPtr = ParseDataModel();
+ else {
+ return false;
+ }
+ return true;
+ }
+
+ SStateNode &ParseParallel()
+ {
+ IDOMReader::Scope _itemScope(m_Reader);
+ SParallel *retval = QT3DS_NEW(m_GraphAllocator, SParallel)();
+ ParseNodeId(*retval);
+ ParseStateNodeEditorAttributes(*retval);
+
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (!ParseStateParallelChildren(*retval, elemName, retval->m_Children,
+ retval->m_OnEntry, retval->m_OnExit,
+ retval->m_DataModel)) {
+ if (!ParseExtensionElement(retval)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse parallel child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ return *retval;
+ }
+
+ void SetStateInitialTransition(STransition *&ioInitial, TStateNodeList &inChildren,
+ SStateNode &inSource)
+ {
+ if (ioInitial) {
+ ioInitial->m_Parent = &inSource;
+ return;
+ }
+ SStateNode *firstStateChild = NULL;
+ for (TStateNodeList::iterator iter = inChildren.begin();
+ iter != inChildren.end() && firstStateChild == NULL; ++iter) {
+ if (iter->m_Type == StateNodeTypes::State || iter->m_Type == StateNodeTypes::Parallel
+ || iter->m_Type == StateNodeTypes::Final) {
+ firstStateChild = &(*iter);
+ }
+ }
+
+ if (firstStateChild) {
+ STransition *theTransition = QT3DS_NEW(m_GraphAllocator, STransition)();
+ size_t byteSize = sizeof(SStateNode *);
+ SStateNode **theData = (SStateNode **)m_GraphAllocator.allocate(
+ byteSize, "InitialTransition", __FILE__, __LINE__);
+ *theData = firstStateChild;
+ theTransition->m_Target = toDataRef(theData, 1);
+ theTransition->m_Parent = &inSource;
+ ioInitial = theTransition;
+ }
+ }
+
+ STransition *ParseInitial()
+ {
+ if (m_Reader.MoveToFirstChild("transition"))
+ return &ParseTransition();
+ else {
+ QT3DS_ASSERT(false);
+ }
+ return NULL;
+ }
+
+ SStateNode &ParseState()
+ {
+ IDOMReader::Scope _stateScope(m_Reader);
+ SState *theNewState = QT3DS_NEW(m_GraphAllocator, SState)();
+ ParseNodeId(*theNewState);
+ const char8_t *initialAtt;
+ if (m_Reader.UnregisteredAtt("initialexpr", initialAtt, GetStudioStateNamespace())) {
+ theNewState->m_InitialExpr = ToGraphStr(initialAtt);
+ const char8_t *errorTest;
+ if (m_Reader.UnregisteredAtt("initial", errorTest)) {
+ qCCritical(INVALID_OPERATION, "Attribute initial=\"%s\" conflicts with "
+ "attribute initialexpr=\"%s\" on <state "
+ "id=\"%s\">; using initialexpr.",
+ nonNull(errorTest), nonNull(initialAtt),
+ nonNull(theNewState->m_Id.c_str()));
+ }
+ } else if (m_Reader.UnregisteredAtt("initial", initialAtt) && !isTrivial(initialAtt)) {
+ STransition *theTransition = QT3DS_NEW(m_GraphAllocator, STransition)();
+ ParseIDRef(initialAtt, theTransition->m_Target);
+ theTransition->m_Parent = theNewState;
+ theNewState->m_Initial = theTransition;
+ }
+
+ ParseStateNodeEditorAttributes(*theNewState);
+
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (!ParseStateParallelChildren(*theNewState, elemName, theNewState->m_Children,
+ theNewState->m_OnEntry, theNewState->m_OnExit,
+ theNewState->m_DataModel)) {
+ // InitialExpr takes precedence over initial transition
+ if (eq(elemName, SXMLName::einitial) && isTrivial(theNewState->m_InitialExpr)) {
+ if (!theNewState->m_Initial)
+ theNewState->m_Initial = ParseInitial();
+ } else {
+ if (!ParseExtensionElement(theNewState)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse state child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+ if (isTrivial(theNewState->m_InitialExpr))
+ SetStateInitialTransition(theNewState->m_Initial, theNewState->m_Children,
+ *theNewState);
+ return *theNewState;
+ }
+
+ static const char8_t *GetDefaultIdName(SStateNode &theNode)
+ {
+ const char8_t *theTemp = "id";
+ switch (theNode.m_Type) {
+ case StateNodeTypes::State:
+ theTemp = "state";
+ break;
+ case StateNodeTypes::Parallel:
+ theTemp = "parallel";
+ break;
+ case StateNodeTypes::Final:
+ theTemp = "final";
+ break;
+ case StateNodeTypes::History:
+ theTemp = "history";
+ break;
+ case StateNodeTypes::Transition:
+ theTemp = "transition";
+ break;
+ default:
+ break;
+ }
+ return theTemp;
+ }
+
+ static const char8_t *GetDefaultIdName(SSend &) { return "send"; }
+
+ template <typename TDataType>
+ void GenerateIdValue(TDataType &theNode)
+ {
+ if (theNode.m_Id.IsValid() == true) {
+ bool preexisting = m_Context.ContainsId(theNode.m_Id);
+ if (preexisting) {
+ CRegisteredString oldId = theNode.m_Id;
+ theNode.m_Id = CRegisteredString();
+ CXMLIO::GenerateUniqueId(theNode, oldId.c_str(), m_Context, m_StrTable);
+ bool success = m_RemapMap.insert(eastl::make_pair(oldId, theNode.m_Id)).second;
+ (void)success;
+ QT3DS_ASSERT(success);
+ }
+ } else {
+ CXMLIO::GenerateUniqueId(theNode, GetDefaultIdName(theNode), m_Context, m_StrTable);
+ }
+ }
+
+ void FinishParsing()
+ {
+ for (QT3DSU32 idx = 0, end = m_GenerateIdList.size(); idx < end; ++idx) {
+ SIdValue &theNode(m_GenerateIdList[idx]);
+ switch (theNode.getType()) {
+ case IdValueTypes::StateNode:
+ GenerateIdValue(*(theNode.getData<SStateNode *>()));
+ break;
+ case IdValueTypes::Send:
+ GenerateIdValue(*(theNode.getData<SSend *>()));
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ for (QT3DSU32 idx = 0, end = m_References.size(); idx < end; ++idx)
+ *(m_References[idx].second) = ParseIDRefSecondPass(m_References[idx].first);
+
+ for (QT3DSU32 idx = 0, end = m_SendReferences.size(); idx < end; ++idx)
+ *(m_SendReferences[idx].second) = ParseSendIdSecondPass(m_SendReferences[idx].first);
+
+ if (m_Editor) {
+ for (QT3DSU32 idx = 0, end = m_ExternalTransitions.size(); idx < end; ++idx) {
+ SStateNode *theNode = FindStateNode(m_ExternalTransitions[idx].first);
+ if (theNode) {
+ TObjPtr transObj = m_Editor->GetOrCreate(*m_ExternalTransitions[idx].second);
+ TObjPtr stateObj = m_Editor->ToEditor(*theNode);
+ stateObj->Append("children", transObj);
+ }
+ }
+ }
+ }
+ SSCXML &ParseSCXML()
+ {
+ IDOMReader::Scope _stateScope(m_Reader);
+ SSCXML *retval = QT3DS_NEW(m_GraphAllocator, SSCXML)();
+
+ if (m_Reader.Att("id", retval->m_Id))
+ m_Context.InsertId(retval->m_Id, retval);
+ const char8_t *initial;
+ if (m_Reader.UnregisteredAtt("initialexpr", initial, GetStudioStateNamespace())) {
+ retval->m_InitialExpr = ToGraphStr(initial);
+ } else if (m_Reader.UnregisteredAtt("initial", initial)) {
+ STransition *theTransition = QT3DS_NEW(m_GraphAllocator, STransition)();
+ ParseIDRef(initial, theTransition->m_Target);
+ theTransition->m_Parent = retval;
+ retval->m_Initial = theTransition;
+ }
+ ParseStrAtt("name", retval->m_Name);
+ const char8_t *uicVersion;
+ if (!m_Reader.UnregisteredAtt("version", uicVersion, GetStudioStateNamespace()))
+ retval->m_Version = 0;
+ else {
+ StringConversion<QT3DSI32>().StrTo(uicVersion, retval->m_Version);
+ }
+ m_Version = retval->m_Version;
+ const char8_t *desc;
+ if (m_Editor && m_Reader.UnregisteredAtt("description", desc))
+ m_Editor->GetOrCreate(*retval)->SetPropertyValue("description", desc);
+
+ const char8_t *temp;
+ if (m_Reader.UnregisteredAtt("binding", temp) && AreEqual(temp, "late"))
+ retval->m_Flags.SetLateBinding(true);
+
+ ParseExtensionAttributes(retval);
+
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (eq(elemName, SXMLName::estate))
+ AppendChild(*retval, retval->m_Children, ParseState());
+ else if (eq(elemName, SXMLName::eparallel))
+ AppendChild(*retval, retval->m_Children, ParseParallel());
+ else if (eq(elemName, SXMLName::etransition))
+ AppendChild(*retval, retval->m_Children, ParseTransition());
+ else if (eq(elemName, SXMLName::efinal))
+ AppendChild(*retval, retval->m_Children, ParseFinal());
+ else if (eq(elemName, SXMLName::einitial))
+ retval->m_Initial = ParseInitial();
+ else if (eq(elemName, SXMLName::edatamodel))
+ retval->m_DataModel = ParseDataModel();
+ else {
+ if (!ParseExtensionElement(retval)) {
+ qCCritical(INTERNAL_ERROR, "Failed to parse scxml child %s", elemName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ if (isTrivial(retval->m_InitialExpr)) {
+ SetStateInitialTransition(retval->m_Initial, retval->m_Children, *retval);
+ }
+ FinishParsing();
+ return *retval;
+ }
+ eastl::vector<SStateNode *> ParseFragment()
+ {
+ eastl::vector<SStateNode *> retval;
+
+ for (bool success = m_Reader.MoveToFirstChild(); success;
+ success = m_Reader.MoveToNextSibling()) {
+ IDOMReader::Scope _loopScope(m_Reader);
+ CRegisteredString elemName = m_Reader.GetElementName();
+ if (eq(elemName, SXMLName::estate))
+ retval.push_back(&ParseState());
+ else if (eq(elemName, SXMLName::eparallel))
+ retval.push_back(&ParseParallel());
+ else if (eq(elemName, SXMLName::efinal))
+ retval.push_back(&ParseFinal());
+ else if (eq(elemName, SXMLName::ehistory))
+ retval.push_back(&ParseHistory());
+ else if (eq(elemName, SXMLName::eexternal_transition)) {
+ CRegisteredString src;
+ m_Reader.Att("source", src);
+ if (src.IsValid())
+ m_ExternalTransitions.push_back(eastl::make_pair(src, &ParseTransition()));
+ } else {
+ qCCritical(INTERNAL_ERROR, "Failed to parse scxml child %s", elemName.c_str() );
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ FinishParsing();
+ return retval;
+ }
+};
+
+struct SWriteContext
+{
+ IDOMWriter &m_Writer;
+ IStateContext &m_Context;
+ IStringTable &m_StringTable;
+ const char8_t *m_CurrentNamespace;
+ const char8_t *m_Names[SXMLName::LastName];
+ eastl::string m_IdStr;
+ eastl::string m_RefListWorkspace;
+ IEditor *m_Editor;
+ MemoryBuffer<ForwardingAllocator> m_Buffer;
+ Option<QT3DSVec2> m_MousePos;
+ nvvector<SStateNode *> m_WrittenNodes;
+ nvvector<STransition *> m_ExternalTransitions;
+
+ SWriteContext(IDOMWriter &inWriter, IStateContext &inContext, IStringTable &inStrTable,
+ IEditor *inEditor, NVAllocatorCallback &inAlloc)
+ : m_Writer(inWriter)
+ , m_Context(inContext)
+ , m_StringTable(inStrTable)
+ , m_CurrentNamespace(GetSCXMLNamespace())
+ , m_Editor(inEditor)
+ , m_Buffer(ForwardingAllocator(inAlloc, "WriteBuffer"))
+ , m_WrittenNodes(inAlloc, "m_WrittenNodes")
+ , m_ExternalTransitions(inAlloc, "m_ExternalTransitions")
+ {
+#define HANDLE_XML_ELEMENT_NAME(nm) m_Names[SXMLName::e##nm] = #nm;
+ ITERATE_XML_ELEMENT_NAMES
+#undef HANDLE_XML_ELEMENT_NAME
+ }
+
+ void GenerateId(SStateNode &inNode, const char8_t *stem)
+ {
+ if (inNode.m_Id.IsValid() == false)
+ CXMLIO::GenerateUniqueId(inNode, stem, m_Context, m_StringTable);
+ }
+
+ void WriteExtensionElement(SDOMElement &elem)
+ {
+ IDOMWriter::Scope _elemScope(m_Writer, elem.m_Name.c_str(), elem.m_Namespace.c_str());
+ for (TAttributeList::iterator iter = elem.m_Attributes.begin(),
+ end = elem.m_Attributes.end();
+ iter != end; ++iter) {
+ SDOMAttribute &theAttribute(*iter);
+ m_Writer.Att(theAttribute.m_Name, theAttribute.m_Value,
+ theAttribute.m_Namespace.c_str());
+ }
+ for (SDOMElement::TElementChildList::iterator iter = elem.m_Children.begin(),
+ end = elem.m_Children.end();
+ iter != end; ++iter) {
+ WriteExtensionElement(elem);
+ }
+ if (!isTrivial(elem.m_Value)) {
+ m_Writer.Value(elem.m_Value);
+ }
+ }
+
+ void WriteExtensionData(void *inItem)
+ {
+ SItemExtensionInfo *infoPtr = m_Context.GetExtensionInfo(inItem);
+ if (infoPtr) {
+ SItemExtensionInfo &theInfo(*infoPtr);
+ for (TDOMAttributeNodeList::iterator iter = theInfo.m_ExtensionAttributes.begin(),
+ end = theInfo.m_ExtensionAttributes.end();
+ iter != end; ++iter) {
+ SDOMAttribute &theAttribute(*iter->m_Attribute);
+ m_Writer.Att(theAttribute.m_Name.c_str(), theAttribute.m_Value,
+ theAttribute.m_Namespace.c_str());
+ }
+ for (TDOMElementNodeList::iterator iter = theInfo.m_ExtensionNodes.begin(),
+ end = theInfo.m_ExtensionNodes.end();
+ iter != end; ++iter) {
+ WriteExtensionElement(*iter->m_Element);
+ }
+ }
+ }
+
+ void Att(const char8_t *inName, const char8_t *inData)
+ {
+ if (!isTrivial(inData))
+ m_Writer.Att(inName, inData, m_CurrentNamespace);
+ }
+
+ void Att(const char8_t *inName, CRegisteredString inData)
+ {
+ if (inData.IsValid())
+ m_Writer.Att(inName, inData.c_str(), m_CurrentNamespace);
+ }
+
+ void Att(const char8_t *inName, NVConstDataRef<CRegisteredString> inData)
+ {
+ m_RefListWorkspace.clear();
+ for (QT3DSU32 idx = 0, end = inData.size(); idx < end; ++idx) {
+ if (m_RefListWorkspace.size())
+ m_RefListWorkspace.append(" ");
+ m_RefListWorkspace.append(inData[idx].c_str());
+ }
+ if (m_RefListWorkspace.size())
+ Att(inName, m_RefListWorkspace.c_str());
+ }
+ void Att(const char8_t *inName, NVConstDataRef<SStateNode *> inData)
+ {
+ m_RefListWorkspace.clear();
+ for (QT3DSU32 idx = 0, end = inData.size(); idx < end; ++idx) {
+ if (m_RefListWorkspace.size())
+ m_RefListWorkspace.append(" ");
+ if (inData[idx])
+ m_RefListWorkspace.append(inData[idx]->m_Id.c_str());
+ }
+ if (m_RefListWorkspace.size())
+ Att(inName, m_RefListWorkspace.c_str());
+ }
+ void Att(const char8_t *inName, const QT3DSVec2 &inData)
+ {
+ m_Buffer.clear();
+ Char8TWriter writer(m_Buffer);
+ writer.Write(NVConstDataRef<QT3DSF32>(&inData.x, 2));
+ m_Buffer.writeZeros(1);
+ m_Writer.Att(inName, (const char8_t *)m_Buffer.begin(), GetStudioStateNamespace());
+ }
+
+ void Att(const char8_t *inName, const QT3DSVec3 &inData)
+ {
+ m_Buffer.clear();
+ Char8TWriter writer(m_Buffer);
+ writer.Write(NVConstDataRef<QT3DSF32>(&inData.x, 3));
+ m_Buffer.writeZeros(1);
+ m_Writer.Att(inName, (const char8_t *)m_Buffer.begin(), GetStudioStateNamespace());
+ }
+
+ void WriteEditorAttributes(void *inType, bool idId, bool inAdjustPos = false)
+ {
+ if (m_Editor) {
+ TObjPtr editorObj = m_Editor->GetEditor(inType);
+ if (editorObj != NULL) {
+ if (idId) {
+ eastl::string name = editorObj->GetId();
+ if (name.empty() == false)
+ m_Writer.Att("id", name.c_str(), GetStudioStateNamespace());
+ }
+ eastl::string description = editorObj->GetDescription();
+ if (description.empty() == false)
+ m_Writer.Att("description", description.c_str(), GetStudioStateNamespace());
+
+ Option<SValue> tempData = editorObj->GetPropertyValue("position");
+ if (tempData.hasValue()) {
+ QT3DSVec2 thePos(tempData->getData<QT3DSVec2>());
+ if (inAdjustPos && m_MousePos.hasValue()) {
+ // Get the global pos, not the local position.
+ for (TObjPtr parentPtr = editorObj->Parent(); parentPtr;
+ parentPtr = parentPtr->Parent()) {
+ Option<SValue> parentPos = parentPtr->GetPropertyValue("position");
+ if (parentPos.hasValue())
+ thePos += parentPos->getData<QT3DSVec2>();
+ }
+ // Store pos in global coords adjusted.
+ thePos -= *m_MousePos;
+ }
+ Att("position", thePos);
+ }
+
+ tempData = editorObj->GetPropertyValue("dimension");
+ if (tempData.hasValue())
+ Att("dimension", tempData->getData<QT3DSVec2>());
+
+ tempData = editorObj->GetPropertyValue("color");
+ if (tempData.hasValue())
+ Att("color", tempData->getData<QT3DSVec3>());
+ }
+ }
+ }
+
+ void WriteSend(SSend &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "send", m_CurrentNamespace);
+ Att("event", inContent.m_Event);
+ Att("eventexpr", inContent.m_EventExpr);
+ Att("target", inContent.m_Target);
+ Att("targetexpr", inContent.m_TargetExpr);
+ Att("type", inContent.m_Type);
+ Att("typeExpr", inContent.m_TypeExpr);
+ Att("id", inContent.m_Id);
+ Att("idlocation", inContent.m_IdLocation);
+ Att("delay", inContent.m_Delay);
+ Att("delayexpr", inContent.m_DelayExpr);
+ Att("namelist", inContent.m_NameList);
+ WriteExecutableContentList(inContent.m_Children);
+ WriteEditorAttributes(&inContent, false);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteParam(SParam &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "param", m_CurrentNamespace);
+ Att("name", inContent.m_Name);
+
+ if (!isTrivial(inContent.m_Expr))
+ Att("expr", inContent.m_Expr);
+ else if (!isTrivial(inContent.m_Location))
+ Att("location", inContent.m_Location);
+
+ WriteEditorAttributes(&inContent, false);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteContent(SContent &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "content", m_CurrentNamespace);
+ if (!isTrivial(inContent.m_Expr))
+ Att("expr", inContent.m_Expr);
+ else if (!isTrivial(inContent.m_ContentValue))
+ m_Writer.Value(inContent.m_ContentValue);
+
+ WriteEditorAttributes(&inContent, false);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteRaise(SRaise &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "raise", m_CurrentNamespace);
+ Att("event", inContent.m_Event);
+ WriteEditorAttributes(&inContent, false);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteAssign(SAssign &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "assign", m_CurrentNamespace);
+ Att("location", inContent.m_Location);
+ Att("expr", inContent.m_Expression);
+ WriteEditorAttributes(&inContent, false);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteIf(SIf &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "if", m_CurrentNamespace);
+ Att("cond", inContent.m_Cond);
+ WriteEditorAttributes(&inContent, false);
+ WriteExecutableContentList(inContent.m_Children);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteElseIf(SElseIf &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "elseif", m_CurrentNamespace);
+ WriteEditorAttributes(&inContent, false);
+ Att("cond", inContent.m_Cond);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteElse(SElse &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "else", m_CurrentNamespace);
+ WriteEditorAttributes(&inContent, false);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteLog(SLog &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "log", m_CurrentNamespace);
+ WriteEditorAttributes(&inContent, false);
+ Att("label", inContent.m_Label);
+ Att("expr", inContent.m_Expression);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteScript(SScript &inContent)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "script", m_CurrentNamespace);
+ WriteEditorAttributes(&inContent, false);
+ if (isTrivial(inContent.m_Data) && !isTrivial(inContent.m_URL))
+ Att("src", inContent.m_URL);
+ else
+ m_Writer.Value(inContent.m_Data);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteCancel(SCancel &inContent)
+ {
+ // Do not serialize invalid cancel items.
+ if (isTrivial(inContent.m_IdExpression) && inContent.m_Send == NULL)
+ return;
+
+ IDOMWriter::Scope _itemScope(m_Writer, "cancel", m_CurrentNamespace);
+ if (!inContent.m_Send && inContent.m_IdExpression)
+ Att("sendidexpr", inContent.m_IdExpression);
+ else
+ Att("sendid", inContent.m_Send->m_Id);
+
+ WriteEditorAttributes(&inContent, true);
+ WriteExtensionData(&inContent);
+ }
+
+ void WriteExecutableContent(SExecutableContent &inContent)
+ {
+ switch (inContent.m_Type) {
+ case ExecutableContentTypes::Send:
+ WriteSend(static_cast<SSend &>(inContent));
+ break;
+ case ExecutableContentTypes::Raise:
+ WriteRaise(static_cast<SRaise &>(inContent));
+ break;
+ case ExecutableContentTypes::Assign:
+ WriteAssign(static_cast<SAssign &>(inContent));
+ break;
+ case ExecutableContentTypes::If:
+ WriteIf(static_cast<SIf &>(inContent));
+ break;
+ case ExecutableContentTypes::ElseIf:
+ WriteElseIf(static_cast<SElseIf &>(inContent));
+ break;
+ case ExecutableContentTypes::Else:
+ WriteElse(static_cast<SElse &>(inContent));
+ break;
+ case ExecutableContentTypes::Log:
+ WriteLog(static_cast<SLog &>(inContent));
+ break;
+ case ExecutableContentTypes::Script:
+ WriteScript(static_cast<SScript &>(inContent));
+ break;
+ case ExecutableContentTypes::Cancel:
+ WriteCancel(static_cast<SCancel &>(inContent));
+ break;
+ case ExecutableContentTypes::Param:
+ WriteParam(static_cast<SParam &>(inContent));
+ break;
+ case ExecutableContentTypes::Content:
+ WriteContent(static_cast<SContent &>(inContent));
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ void WriteExecutableContentList(TExecutableContentList &inList)
+ {
+ for (TExecutableContentList::iterator contentIter = inList.begin(),
+ contentEnd = inList.end();
+ contentIter != contentEnd; ++contentIter)
+ WriteExecutableContent(*contentIter);
+ }
+
+ void WriteOnEntry(SOnEntry &inItem)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "onentry", m_CurrentNamespace);
+ WriteEditorAttributes(&inItem, true);
+ WriteExecutableContentList(inItem.m_ExecutableContent);
+ WriteExtensionData(&inItem);
+ }
+
+ void WriteOnEntryList(TOnEntryList &inList)
+ {
+ for (TOnEntryList::iterator iter = inList.begin(), end = inList.end(); iter != end; ++iter)
+ WriteOnEntry(*iter);
+ }
+
+ void WriteOnExit(SOnExit &inItem)
+ {
+ IDOMWriter::Scope _itemScope(m_Writer, "onexit", m_CurrentNamespace);
+ WriteEditorAttributes(&inItem, true);
+ WriteExecutableContentList(inItem.m_ExecutableContent);
+ WriteExtensionData(&inItem);
+ }
+
+ void WriteOnExitList(TOnExitList &inList)
+ {
+ for (TOnExitList::iterator iter = inList.begin(), end = inList.end(); iter != end; ++iter)
+ WriteOnExit(*iter);
+ }
+
+ void WriteDataModel(SDataModel &inDataModel)
+ {
+ IDOMWriter::Scope _transitionScope(m_Writer, "datamodel", m_CurrentNamespace);
+ WriteEditorAttributes(&inDataModel, true);
+ for (TDataList::iterator iter = inDataModel.m_Data.begin(), end = inDataModel.m_Data.end();
+ iter != end; ++iter) {
+ IDOMWriter::Scope _transitionScope(m_Writer, "data", m_CurrentNamespace);
+ Att("id", iter->m_Id);
+ Att("expr", iter->m_Expression);
+ WriteEditorAttributes(&(*iter), true);
+ WriteExtensionData(&(*iter));
+ }
+ WriteExtensionData(&inDataModel);
+ }
+
+ static inline SStateNode *FirstValidChild(SState &inNode)
+ {
+ for (TStateNodeList::iterator iter = inNode.m_Children.begin(),
+ end = inNode.m_Children.end();
+ iter != end; ++iter) {
+ switch (iter->m_Type) {
+ case StateNodeTypes::State:
+ case StateNodeTypes::Parallel:
+ case StateNodeTypes::Final:
+ return iter.m_Obj;
+ default:
+ break;
+ }
+ }
+ return NULL;
+ }
+
+ void WriteState(SState &inNode, bool inAdjustPos)
+ {
+ m_WrittenNodes.push_back(&inNode);
+ IDOMWriter::Scope _transitionScope(m_Writer, "state", m_CurrentNamespace);
+ GenerateId(inNode, "state");
+ Att("id", inNode.m_Id);
+ WriteEditorAttributes(&inNode, false, inAdjustPos);
+ if (!isTrivial(inNode.m_InitialExpr)) {
+ m_Writer.Att("initialexpr", inNode.m_InitialExpr, GetStudioStateNamespace());
+ } else if (inNode.m_Initial) {
+ // First check to see if this could be an attribute
+ if (inNode.m_Initial->m_ExecutableContent.empty()) {
+ // Now check if it has one child and if it has only one child and that child is the
+ // first valid possible child
+ // then we don't write out the attribute
+ bool canElideInitial = inNode.m_Initial->m_Target.size() == 0
+ || (inNode.m_Initial->m_Target.size() == 1
+ && inNode.m_Initial->m_Target[0] == FirstValidChild(inNode));
+ if (!canElideInitial)
+ Att("initial", inNode.m_Initial->m_Target);
+ } else {
+ IDOMWriter::Scope _transitionScope(m_Writer, "initial", m_CurrentNamespace);
+ WriteTransition(*inNode.m_Initial, false);
+ }
+ }
+ WriteOnEntryList(inNode.m_OnEntry);
+ WriteOnExitList(inNode.m_OnExit);
+ WriteStateNodeList(inNode.m_Children);
+ if (inNode.m_DataModel)
+ WriteDataModel(*inNode.m_DataModel);
+ WriteExtensionData(&inNode);
+ }
+
+ void WriteParallel(SParallel &inNode, bool inAdjustPos)
+ {
+ m_WrittenNodes.push_back(&inNode);
+ IDOMWriter::Scope _transitionScope(m_Writer, "parallel", m_CurrentNamespace);
+ GenerateId(inNode, "parallel");
+ Att("id", inNode.m_Id);
+ WriteEditorAttributes(&inNode, false, inAdjustPos);
+ WriteOnEntryList(inNode.m_OnEntry);
+ WriteOnExitList(inNode.m_OnExit);
+ WriteStateNodeList(inNode.m_Children);
+ if (inNode.m_DataModel)
+ WriteDataModel(*inNode.m_DataModel);
+ WriteExtensionData(&inNode);
+ }
+
+ void WriteHistory(SHistory &inNode, bool inAdjustPos)
+ {
+ m_WrittenNodes.push_back(&inNode);
+ IDOMWriter::Scope _transitionScope(m_Writer, "history", m_CurrentNamespace);
+ GenerateId(inNode, "history");
+ Att("id", inNode.m_Id);
+ if (inNode.m_Flags.IsDeep())
+ Att("type", "deep");
+ WriteEditorAttributes(&inNode, false, inAdjustPos);
+ if (inNode.m_Transition)
+ WriteTransition(*inNode.m_Transition, false);
+ WriteExtensionData(&inNode);
+ }
+ void WriteTransitionData(STransition &inNode)
+ {
+ Att("event", inNode.m_Event);
+ Att("cond", inNode.m_Condition);
+ Att("target", inNode.m_Target);
+ if (inNode.m_Flags.IsInternal())
+ Att("type", "internal");
+ WriteEditorAttributes(&inNode, true, false);
+ if (inNode.m_StateNodeFlags.HasEndPosition()) {
+ Att("end_position", inNode.m_EndPosition);
+ }
+ if (inNode.m_Path.mSize) {
+ m_Buffer.clear();
+ Char8TWriter writer(m_Buffer);
+ writer.Write(
+ NVConstDataRef<QT3DSF32>((const QT3DSF32 *)inNode.m_Path.mData, 2 * inNode.m_Path.mSize),
+ QT3DS_MAX_U32);
+ m_Buffer.writeZeros(1);
+ m_Writer.Att("path", (const char8_t *)m_Buffer.begin(), GetStudioStateNamespace());
+ }
+ WriteExecutableContentList(inNode.m_ExecutableContent);
+ WriteExtensionData(&inNode);
+ }
+ void WriteTransition(STransition &inNode, bool /*inAdjustPos*/)
+ {
+ IDOMWriter::Scope _transitionScope(m_Writer, "transition", m_CurrentNamespace);
+ WriteTransitionData(inNode);
+ }
+ void WriteExternalTransition(STransition &inNode)
+ {
+ if (inNode.m_Parent != NULL) {
+ IDOMWriter::Scope _transitionScope(m_Writer, "external_transition",
+ GetStudioStateNamespace());
+ m_Writer.Att("source", inNode.m_Parent->m_Id.c_str(), GetStudioStateNamespace());
+ WriteTransitionData(inNode);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ void WriteFinal(SFinal &inNode, bool inAdjustPos)
+ {
+ m_WrittenNodes.push_back(&inNode);
+ IDOMWriter::Scope _transitionScope(m_Writer, "final", m_CurrentNamespace);
+ GenerateId(inNode, "history");
+ Att("id", inNode.m_Id);
+ WriteEditorAttributes(&inNode, false, inAdjustPos);
+ WriteOnEntryList(inNode.m_OnEntry);
+ WriteOnExitList(inNode.m_OnExit);
+ WriteExtensionData(&inNode);
+ }
+
+ void Write(SStateNode &inNode, bool inAdjustPos)
+ {
+ switch (inNode.m_Type) {
+ case StateNodeTypes::State:
+ WriteState(static_cast<SState &>(inNode), inAdjustPos);
+ break;
+ case StateNodeTypes::Parallel:
+ WriteParallel(static_cast<SParallel &>(inNode), inAdjustPos);
+ break;
+ case StateNodeTypes::History:
+ WriteHistory(static_cast<SHistory &>(inNode), inAdjustPos);
+ break;
+ case StateNodeTypes::Transition:
+ WriteTransition(static_cast<STransition &>(inNode), inAdjustPos);
+ break;
+ case StateNodeTypes::Final:
+ WriteFinal(static_cast<SFinal &>(inNode), inAdjustPos);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ void WriteStateNodeList(TStateNodeList &inNodeList)
+ {
+ for (TStateNodeList::iterator iter = inNodeList.begin(), end = inNodeList.end();
+ iter != end; ++iter) {
+ Write((*iter), false);
+ }
+ }
+
+ void Write()
+ {
+ SSCXML &item = *m_Context.GetRoot();
+ GenerateId(item, "scxml");
+ Att("name", item.m_Name);
+ if (item.m_Flags.IsLateBinding())
+ Att("binding", "late");
+ Att("version", "1");
+ m_Writer.Att("version", SSCXML::GetCurrentVersion(), GetStudioStateNamespace());
+
+ if (!isTrivial(item.m_InitialExpr)) {
+ m_Writer.Att("initialexpr", item.m_InitialExpr, GetStudioStateNamespace());
+ } else if (item.m_Initial)
+ Att("initial", item.m_Initial->m_Target);
+
+ WriteStateNodeList(item.m_Children);
+ if (item.m_DataModel)
+ WriteDataModel(*item.m_DataModel);
+ WriteExtensionData(&item);
+ }
+
+ void CheckTransitionForWrittenNodesList(STransition *transition)
+ {
+ if (transition == NULL)
+ return;
+ for (QT3DSU32 targetIdx = 0, targetEnd = transition->m_Target.size(); targetIdx < targetEnd;
+ ++targetIdx) {
+ if (eastl::find(m_WrittenNodes.begin(), m_WrittenNodes.end(),
+ transition->m_Target[targetIdx])
+ != m_WrittenNodes.end()) {
+ m_ExternalTransitions.push_back(transition);
+ return;
+ }
+ }
+ }
+
+ // Not sure the right answer here. I know you can't just blindly work with initials and such
+ // because you can't create new transitions for them.
+ void CheckForExternalTransitions(SStateNode &inNode, const eastl::vector<SStateNode *> &inRoots)
+ {
+ switch (inNode.m_Type) {
+ case StateNodeTypes::SCXML: {
+ SSCXML &theNode(static_cast<SSCXML &>(inNode));
+ // CheckTransitionForWrittenNodesList( theNode.m_Initial );
+ CheckForExternalTransitions(theNode.m_Children, inRoots);
+ } break;
+ case StateNodeTypes::State: {
+ SState &theNode(static_cast<SState &>(inNode));
+ // CheckTransitionForWrittenNodesList( theNode.m_Initial );
+ CheckForExternalTransitions(theNode.m_Children, inRoots);
+ } break;
+ case StateNodeTypes::Parallel: {
+ SParallel &theNode(static_cast<SParallel &>(inNode));
+ CheckForExternalTransitions(theNode.m_Children, inRoots);
+ } break;
+ case StateNodeTypes::Final:
+ break;
+ case StateNodeTypes::History: {
+ // CheckTransitionForWrittenNodesList( theNode.m_Transition );
+ } break;
+ case StateNodeTypes::Transition: {
+ STransition &theNode(static_cast<STransition &>(inNode));
+ CheckTransitionForWrittenNodesList(&theNode);
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ void CheckForExternalTransitions(TStateNodeList &ioList,
+ const eastl::vector<SStateNode *> &inRoots)
+ {
+ for (TStateNodeList::iterator iter = ioList.begin(), end = ioList.end(); iter != end;
+ ++iter) {
+ if (eastl::find(inRoots.begin(), inRoots.end(), &(*iter)) == inRoots.end())
+ CheckForExternalTransitions(*iter, inRoots);
+ }
+ }
+
+ void WriteFragment(eastl::vector<SStateNode *> &inRoots, const QT3DSVec2 &inMousePos)
+ {
+ m_MousePos = inMousePos;
+ for (QT3DSU32 idx = 0, end = inRoots.size(); idx < end; ++idx) {
+ Write(*inRoots[idx], true);
+ }
+ if (m_Context.GetRoot())
+ CheckForExternalTransitions(*m_Context.GetRoot(), inRoots);
+ m_CurrentNamespace = GetStudioStateNamespace();
+ for (QT3DSU32 idx = 0, end = m_ExternalTransitions.size(); idx < end; ++idx) {
+ WriteExternalTransition(*m_ExternalTransitions[idx]);
+ }
+ }
+};
+
+// True if child is a descedent of parent. False otherwise.
+inline bool IsDescendent(SStateNode &parent, SStateNode &child)
+{
+ if (&parent == &child)
+ return false;
+ if (&parent == child.m_Parent)
+ return true;
+
+ if (child.m_Parent)
+ return IsDescendent(parent, *child.m_Parent);
+
+ return false;
+}
+
+template <typename TDataType>
+void GenerateUniqueIdT(TDataType &inNode, const char8_t *inStem, IStateContext &ioContext,
+ IStringTable &ioStringTable)
+{
+ QT3DS_ASSERT(inNode.m_Id.IsValid() == false);
+ inNode.m_Id =
+ ioStringTable.RegisterStr(DoGenerateUniqueId(inStem, ioContext, ioStringTable).c_str());
+ bool insertResult = ioContext.InsertId(inNode.m_Id, &inNode);
+ QT3DS_ASSERT(insertResult);
+ (void)insertResult;
+}
+}
+
+namespace qt3ds {
+namespace state {
+
+ void CXMLIO::GenerateUniqueId(SStateNode &inNode, const char8_t *inStem,
+ IStateContext &ioContext, IStringTable &ioStringTable)
+ {
+ GenerateUniqueIdT(inNode, inStem, ioContext, ioStringTable);
+ }
+
+ void CXMLIO::GenerateUniqueId(SSend &inNode, const char8_t *inStem, IStateContext &ioContext,
+ IStringTable &ioStringTable)
+ {
+ GenerateUniqueIdT(inNode, inStem, ioContext, ioStringTable);
+ }
+
+ bool CXMLIO::LoadSCXMLFile(NVAllocatorCallback &inGraphAllocator, NVFoundationBase &inFnd,
+ IDOMReader &inReader, IStringTable &inStringTable,
+ const char8_t *inFilename, IStateContext &outContext,
+ editor::IEditor *inEditor)
+ {
+ // the topmost scxml node is a state, so go from there.
+ if (AreEqual(inReader.GetElementName().c_str(), "scxml")) {
+ SParseContext theParseContext(inGraphAllocator, inFnd, inReader, outContext,
+ inStringTable, inEditor);
+ outContext.SetRoot(theParseContext.ParseSCXML());
+ if (outContext.GetRoot()) {
+ inFilename = nonNull(inFilename);
+ outContext.GetRoot()->m_Filename = theParseContext.ToGraphStr(inFilename);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ eastl::pair<eastl::vector<SStateNode *>, CXMLIO::TIdRemapMap>
+ CXMLIO::LoadSCXMLFragment(NVAllocatorCallback &inGraphAllocator, NVFoundationBase &inFnd,
+ IDOMReader &inReader, IStringTable &inStringTable,
+ IStateContext &ioContext, editor::IEditor &inEditor)
+ {
+ if (AreEqual(inReader.GetElementName().c_str(), "scxml_fragment")) {
+ SParseContext theParseContext(inGraphAllocator, inFnd, inReader, ioContext,
+ inStringTable, &inEditor);
+ eastl::vector<SStateNode *> retval = theParseContext.ParseFragment();
+ return eastl::make_pair(retval, theParseContext.m_RemapMap);
+ }
+ return eastl::make_pair(eastl::vector<SStateNode *>(), CXMLIO::TIdRemapMap());
+ }
+
+ void CXMLIO::SaveSCXMLFile(IStateContext &inContext, NVFoundationBase &inFnd,
+ IStringTable &inStringTable, IOutStream &outStream,
+ editor::IEditor *inEditor)
+ {
+ NVScopedRefCounted<IDOMFactory> theFactory =
+ IDOMFactory::CreateDOMFactory(inFnd.getAllocator(), inStringTable);
+ NVScopedRefCounted<IDOMWriter> theWriter =
+ IDOMWriter::CreateDOMWriter(inFnd.getAllocator(), "scxml", inStringTable,
+ GetSCXMLNamespace())
+ .first;
+ SWriteContext theWriteContext(*theWriter, inContext, inStringTable, inEditor,
+ inFnd.getAllocator());
+ theWriteContext.Write();
+
+ // Now we actually serialize the data.
+ eastl::vector<SNamespacePair> thePairs;
+ thePairs.push_back(
+ SNamespacePair(inStringTable.RegisterStr(GetSCXMLNamespace()), CRegisteredString()));
+ thePairs.push_back(SNamespacePair(inStringTable.RegisterStr(GetStudioStateNamespace()),
+ inStringTable.RegisterStr("Qt3DS")));
+
+ for (SNamespacePairNode *theNode = inContext.GetFirstNSNode(); theNode;
+ theNode = theNode->m_NextNode) {
+ if (AreEqual(theNode->m_Namespace.c_str(), GetSCXMLNamespace()) == false
+ && AreEqual(theNode->m_Namespace.c_str(), GetStudioStateNamespace()) == false
+ && theNode->m_Abbreviation.IsValid())
+ thePairs.push_back(*theNode);
+ }
+
+ CDOMSerializer::WriteXMLHeader(outStream);
+ CDOMSerializer::Write(inFnd.getAllocator(), *theWriter->GetTopElement(), outStream,
+ inStringTable, toDataRef(thePairs.data(), thePairs.size()), false);
+ }
+
+ void CXMLIO::FindRoots(NVConstDataRef<SStateNode *> inObjects,
+ eastl::vector<SStateNode *> &outRoots)
+ {
+ // Stupid but effective algorithm.
+ for (QT3DSU32 idx = 0, end = inObjects.size(); idx < end; ++idx) {
+ // Transitions never make it into the root, nor does the top scxml object
+ if (inObjects[idx]->m_Type == StateNodeTypes::Transition
+ || inObjects[idx]->m_Type == StateNodeTypes::SCXML)
+ continue;
+ SStateNode *newNode(inObjects[idx]);
+ // Note that re-querying size is important.
+ bool skip = false;
+ for (QT3DSU32 existingIdx = 0; existingIdx < outRoots.size() && skip == false;
+ ++existingIdx) {
+ SStateNode *existingNode(outRoots[existingIdx]);
+ if (newNode == existingNode || IsDescendent(*existingNode, *newNode))
+ skip = true;
+
+ // If the existing item is a descendent of new node,
+ // then get the existing item out of the list.
+ if (IsDescendent(*newNode, *existingNode)) {
+ // Get the existing item out of the list.
+ outRoots.erase(outRoots.begin() + existingIdx);
+ --existingIdx;
+ }
+ }
+ if (!skip)
+ outRoots.push_back(newNode);
+ }
+ }
+
+ eastl::vector<SStateNode *> CXMLIO::SaveSCXMLFragment(
+ IStateContext &inContext, NVFoundationBase &inFnd, IStringTable &inStringTable,
+ IDOMWriter &ioWriter, NVDataRef<SStateNode *> inObjects, editor::IEditor &inEditor,
+ const QT3DSVec2 &inMousePos, eastl::vector<SNamespacePair> &outNamespaces)
+ {
+ eastl::vector<SStateNode *> theRoots;
+ FindRoots(inObjects, theRoots);
+ NVScopedRefCounted<IDOMWriter> theWriter(ioWriter);
+ SWriteContext theWriteContext(*theWriter, inContext, inStringTable, &inEditor,
+ inFnd.getAllocator());
+ theWriteContext.WriteFragment(theRoots, inMousePos);
+
+ outNamespaces.push_back(
+ SNamespacePair(inStringTable.RegisterStr(GetSCXMLNamespace()), CRegisteredString()));
+ outNamespaces.push_back(SNamespacePair(inStringTable.RegisterStr(GetStudioStateNamespace()),
+ inStringTable.RegisterStr("Qt3DS")));
+ return theRoots;
+ }
+
+ void CXMLIO::ToEditableXml(IStateContext &inContext, NVFoundationBase &inFnd,
+ IStringTable &inStringTable, IDOMWriter &ioWriter,
+ SExecutableContent &inContent, editor::IEditor &inEditor)
+ {
+ SWriteContext theWriteContext(ioWriter, inContext, inStringTable, &inEditor,
+ inFnd.getAllocator());
+ theWriteContext.WriteExecutableContent(inContent);
+ }
+
+ SExecutableContent *
+ CXMLIO::FromEditableXML(IDOMReader &inReader, NVFoundationBase &inFnd, IStateContext &inContext,
+ IStringTable &inStringTable, NVAllocatorCallback &inGraphAllocator,
+ editor::IEditor &inEditor, SStateNode *inStateNodeParent,
+ SExecutableContent *inExecContentParent)
+ {
+ SParseContext theParseContext(inGraphAllocator, inFnd, inReader, inContext, inStringTable,
+ &inEditor);
+ TExecutableContentList theContentList;
+ theParseContext.ParseExecutableContentItem(inStateNodeParent, inExecContentParent,
+ theContentList);
+ return &(*theContentList.begin());
+ }
+
+ eastl::vector<eastl::string> CXMLIO::GetSupportedExecutableContentNames()
+ {
+ eastl::vector<eastl::string> retval;
+ NVConstDataRef<SParseContext::SExecutableContentParseBinding> theBindingList =
+ SParseContext::GetParseBindings();
+ for (QT3DSU32 idx = 0, end = theBindingList.size(); idx < end; ++idx)
+ retval.push_back(
+ eastl::string(SXMLName::GetNameForElemName(theBindingList[idx].m_Name)));
+ return retval;
+ }
+}
+}
diff --git a/src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.h b/src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.h
new file mode 100644
index 00000000..3a639845
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/Qt3DSStateXMLIO.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_XML_IO_H
+#define QT3DS_STATE_XML_IO_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/StringTable.h"
+#include "foundation/XML.h"
+#include "EASTL/map.h"
+#include "EASTL/string.h"
+
+namespace qt3ds {
+namespace foundation {
+ class IDOMReader;
+ class IDOMWriter;
+}
+}
+
+namespace qt3ds {
+namespace state {
+
+ struct SExecutableContent;
+ class CXMLIO
+ {
+ public:
+ typedef eastl::map<eastl::string, eastl::string> TIdRemapMap;
+
+ static void GenerateUniqueId(SStateNode &inNode, const char8_t *inStem,
+ IStateContext &ioContext, IStringTable &ioStringTable);
+ static void GenerateUniqueId(SSend &inNode, const char8_t *inStem, IStateContext &ioContext,
+ IStringTable &ioStringTable);
+ // Load an SCXML file and return the root states
+ // All states, transitions, and memory used in the graph is allocated using the graph
+ // allocator.
+ // This makes freeing the memory much easier because you can just free it by releasing the
+ // graph
+ // allocator and you don't have to go object by object.
+ // Filename is just used as another tag or name on the scxml object.
+ static bool LoadSCXMLFile(NVAllocatorCallback &inGraphAllocator, NVFoundationBase &inFnd,
+ IDOMReader &inReader, IStringTable &inStringTable,
+ const char8_t *inFilename, IStateContext &outContext,
+ editor::IEditor *inEditor = NULL);
+
+ // Loading fragments remaps their ids to avoid conflics. Returns the top nodes from the
+ // scxml graph.
+ static eastl::pair<eastl::vector<SStateNode *>, TIdRemapMap>
+ LoadSCXMLFragment(NVAllocatorCallback &inGraphAllocator, NVFoundationBase &inFnd,
+ IDOMReader &inReader, IStringTable &inStringTable,
+ IStateContext &ioContext, editor::IEditor &inEditor);
+
+ // We write all the way to file instead of a DOM writer because we have to add xml
+ // namespaces and those can only
+ // be added during the actual serialization process.
+ static void SaveSCXMLFile(IStateContext &inContext, NVFoundationBase &inFnd,
+ IStringTable &inStringTable, IOutStream &outStream,
+ editor::IEditor *inEditor = NULL);
+
+ static void FindRoots(NVConstDataRef<SStateNode *> inObjects,
+ eastl::vector<SStateNode *> &outRoots);
+
+ // Returns the roots of the copied list
+ static eastl::vector<SStateNode *>
+ SaveSCXMLFragment(IStateContext &inContext, NVFoundationBase &inFnd,
+ IStringTable &inStringTable, IDOMWriter &ioWriter,
+ NVDataRef<SStateNode *> inObjects, editor::IEditor &inEditor,
+ const QT3DSVec2 &inMousePos, eastl::vector<SNamespacePair> &outNamespaces);
+
+ static void ToEditableXml(IStateContext &inContext, NVFoundationBase &inFnd,
+ IStringTable &inStringTable, IDOMWriter &ioWriter,
+ SExecutableContent &inContent, editor::IEditor &inEditor);
+
+ static SExecutableContent *
+ FromEditableXML(IDOMReader &inReader, NVFoundationBase &inFnd, IStateContext &inContext,
+ IStringTable &inStringTable, NVAllocatorCallback &inGraphAllocator,
+ editor::IEditor &inEditor, SStateNode *inStateNodeParent,
+ SExecutableContent *inExecContentParent);
+
+ static eastl::vector<eastl::string> GetSupportedExecutableContentNames();
+ };
+}
+}
+
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h
new file mode 100644
index 00000000..38a0b74f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_SCENE_GRAPH_DEBUGGER_H
+#define QT3DS_SCENE_GRAPH_DEBUGGER_H
+#pragma once
+#include "Qt3DSStateDebugger.h"
+#include "Qt3DSUIADatamodel.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ struct SSGValue;
+
+ struct SSGPropertyChange;
+
+ struct SGElemIdMap
+ {
+ void *m_Elem;
+ const char *m_Id;
+ };
+
+ // Persistant item that sticks around and appends some information to the binary file.
+ // The runtime debugger must exist for the entire time the runtime is running, not just
+ // during connection because the mapping from element->id only exists at file loading time.
+ class ISceneGraphRuntimeDebugger : public NVRefCounted, public IDebugStreamListener
+ {
+ public:
+ static const char *GetProtocolName() { return "Scene Graph Debugger"; }
+ // Nothing is returned if the object isn't connected. The returned value may not be
+ // valid
+ // after next GetOrCreateCall, so don't hold on to it.
+ virtual void MapPresentationId(void *presentation, const char *id) = 0;
+ virtual void MapElementIds(void *presentation, NVConstDataRef<SGElemIdMap> inIds) = 0;
+ virtual void OnPropertyChanged(void *elem,
+ NVConstDataRef<SSGPropertyChange> changes) = 0;
+ virtual void OnConnection(IDebugOutStream &outStream) = 0;
+ virtual void BinarySave(IOutStream &stream) = 0;
+ // Load the main datastructures, although we know the ids are wrong
+ virtual void BinaryLoad(IInStream &stream, NVDataRef<QT3DSU8> inStringTableData) = 0;
+ // Remap the presentation element points using id to map old presentation ptr to new
+ // presentation ptr.
+ virtual void BinaryLoadPresentation(void *presentation, const char *id,
+ size_t inElemOffset) = 0;
+ virtual void EndFrame() = 0;
+ virtual bool IsConnected() = 0;
+
+ static ISceneGraphRuntimeDebugger &Create(NVFoundationBase &fnd,
+ IStringTable &strTable);
+ };
+
+ class ISceneGraphArchitectDebuggerListener
+ {
+ public:
+ virtual void OnItemsDirty(NVConstDataRef<app::SAppElement *> inDirtySet) = 0;
+ };
+
+ // The architect debugger only exists when debugging.
+ class ISceneGraphArchitectDebugger : public NVRefCounted, public IDebugStreamListener
+ {
+ public:
+ virtual void SetListener(ISceneGraphArchitectDebuggerListener *listener) = 0;
+ // Note that we wrap the att or arg list and the initial values to provide extra
+ // information.
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(app::SAppElement &elem) = 0;
+
+ // These may be empty, so don't expect them. Also they are all string, no registered
+ // strings
+ // regardless of the type.
+ virtual eastl::vector<app::SDatamodelValue>
+ GetElementAttributeValues(app::SAppElement &elem) = 0;
+ virtual app::IDatamodel &GetDatamodel() = 0;
+
+ virtual void AttachToStream(IDebugOutStream &inStream) = 0;
+
+ virtual void RefreshData(bool inNeedReloadData) = 0;
+
+ static ISceneGraphArchitectDebugger &Create(qt3ds::app::IDatamodel &inDatamodel);
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h
new file mode 100644
index 00000000..4fea4322
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_SCENE_GRAPH_DEBUGGER_PROTOCOL_H
+#define QT3DS_SCENE_GRAPH_DEBUGGER_PROTOCOL_H
+#include "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSSceneGraphDebuggerValue.h"
+#include "foundation/Qt3DSMemoryBuffer.h"
+#include "foundation/SerializationTypes.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ // These are the datatypes we will communicate information with
+ static QT3DSU32 GetSceneGraphProtocolVersion() { return 1; }
+
+ struct SValueUpdate
+ {
+ QT3DSI32 m_Hash;
+ SSGValue m_Value;
+ SValueUpdate(QT3DSI32 h, const SSGValue &v)
+ : m_Hash(h)
+ , m_Value(v)
+ {
+ }
+ SValueUpdate()
+ : m_Hash(0)
+ {
+ }
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Hash);
+ inListener.Handle(m_Value);
+ };
+ };
+
+ struct SElemUpdate
+ {
+ QT3DSU64 m_Elem;
+ NVDataRef<SValueUpdate> m_Updates;
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Elem);
+ inListener.HandleRef(m_Updates);
+ };
+ };
+
+ struct SElemMap
+ {
+ QT3DSU64 m_Elem;
+ CRegisteredString m_Id;
+ SElemMap()
+ : m_Elem(0)
+ {
+ }
+ SElemMap(QT3DSU64 ptr, CRegisteredString name)
+ : m_Elem(ptr)
+ , m_Id(name)
+ {
+ }
+
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Elem);
+ inListener.Handle(m_Id);
+ }
+ };
+
+ struct SIdUpdate
+ {
+ QT3DSU64 m_Presentation;
+ CRegisteredString m_PresentationId;
+ NVDataRef<SElemMap> m_IdUpdates;
+
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Presentation);
+ inListener.Handle(m_PresentationId);
+ inListener.HandleRef(m_IdUpdates);
+ }
+ };
+
+ struct SSGProtocolMessageTypes
+ {
+ enum Enum {
+ UnknownMessage = 0,
+ Initialization,
+ IdUpdate,
+ ElemUpdate,
+ Frame,
+ };
+ };
+
+ // Implemented on runtime side.
+ struct SSGProtocolWriter
+ {
+ IOutStream &m_Stream;
+ MemoryBuffer<> m_WriteBuffer;
+ QT3DSU32 m_HighWaterMark;
+
+ SSGProtocolWriter(IOutStream &s, NVAllocatorCallback &alloc, QT3DSU32 highWaterMark = 4096)
+ : m_Stream(s)
+ , m_WriteBuffer(ForwardingAllocator(alloc, "WriteBuffer"))
+ , m_HighWaterMark(highWaterMark)
+ {
+ }
+
+ void Handle(QT3DSU64 data) { m_WriteBuffer.write(data); }
+
+ void Handle(QT3DSI32 data) { m_WriteBuffer.write(data); }
+
+ void Handle(CRegisteredString str)
+ {
+ QT3DSU32 len = static_cast<QT3DSU32>(strlen(str.c_str()) + 1);
+ m_WriteBuffer.write(len);
+ m_WriteBuffer.write(str.c_str(), len);
+ }
+ void Handle(SSGValue &value)
+ {
+ QT3DSU32 valType = static_cast<QT3DSU32>(value.getType());
+ m_WriteBuffer.write(valType);
+ switch (value.getType()) {
+ case SGPropertyValueTypes::Float:
+ m_WriteBuffer.write(value.getData<float>());
+ break;
+ case SGPropertyValueTypes::I32:
+ m_WriteBuffer.write(value.getData<QT3DSI32>());
+ break;
+ case SGPropertyValueTypes::String:
+ Handle(value.getData<CRegisteredString>());
+ break;
+ case SGPropertyValueTypes::Elem:
+ Handle(value.getData<QT3DSU64>());
+ break;
+ case SGPropertyValueTypes::NoSGValue:
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ template <typename TDataType>
+ void HandleRef(NVDataRef<TDataType> &ref)
+ {
+ m_WriteBuffer.write(ref.size());
+ for (QT3DSU32 idx = 0, end = ref.size(); idx < end; ++idx)
+ Handle(ref[idx]);
+ }
+
+ template <typename TDataType>
+ void Handle(TDataType &dtype)
+ {
+ dtype.IterateProperties(*this);
+ }
+
+ void Flush()
+ {
+ if (m_WriteBuffer.size()) {
+ NVConstDataRef<QT3DSU8> writeData(m_WriteBuffer);
+ m_Stream.Write(writeData);
+ m_WriteBuffer.clear();
+ }
+ }
+
+ void CheckBuffer()
+ {
+ if (m_WriteBuffer.size() > m_HighWaterMark)
+ Flush();
+ }
+
+ void Write(SIdUpdate &inIdUpdate)
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::IdUpdate);
+ inIdUpdate.IterateProperties(*this);
+ Flush();
+ }
+
+ void Write(SElemUpdate &inIdUpdate)
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::ElemUpdate);
+ inIdUpdate.IterateProperties(*this);
+ CheckBuffer();
+ }
+
+ void WriteInitialization()
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::Initialization);
+ m_WriteBuffer.write(GetSceneGraphProtocolVersion());
+ Flush();
+ }
+ void WriteFrame()
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::Frame);
+ Flush();
+ }
+ };
+
+ struct SSGProtocolReader
+ {
+ NVConstDataRef<QT3DSU8> m_Message;
+ SDataReader m_Reader;
+ IStringTable &m_StringTable;
+ eastl::vector<QT3DSU8> m_DataBuffer;
+ eastl::string m_TempString;
+ QT3DSU32 m_Allocated;
+ bool m_RestartRead;
+ SSGProtocolReader(NVConstDataRef<QT3DSU8> msg, IStringTable &strTable)
+ : m_Message(msg)
+ , m_Reader(const_cast<QT3DSU8 *>(msg.begin()), const_cast<QT3DSU8 *>(msg.end()))
+ , m_StringTable(strTable)
+ , m_Allocated(0)
+ , m_RestartRead(false)
+ {
+ }
+
+ SSGProtocolMessageTypes::Enum MessageType()
+ {
+ QT3DSU32 data = m_Reader.LoadRef<QT3DSU32>();
+ return static_cast<SSGProtocolMessageTypes::Enum>(data);
+ }
+
+ template <typename TDataType>
+ Option<NVDataRef<TDataType>> AllocateData(size_t size)
+ {
+ if (m_RestartRead)
+ return Empty();
+ if (size == 0)
+ return NVDataRef<TDataType>();
+
+ QT3DSU32 current = m_Allocated;
+ QT3DSU32 newAlloc = (QT3DSU32)(size * sizeof(TDataType));
+ // 8 byte align
+ if (newAlloc % 8)
+ newAlloc += 8 - (newAlloc % 8);
+
+ QT3DSU32 required = current + newAlloc;
+
+ if (required > m_DataBuffer.size()) {
+ m_RestartRead = true;
+ m_DataBuffer.resize(required * 2);
+ return Empty();
+ }
+ TDataType *offset = reinterpret_cast<TDataType *>(&m_DataBuffer[current]);
+ m_Allocated += newAlloc;
+ return toDataRef(offset, (QT3DSU32)size);
+ }
+
+ void Handle(QT3DSU64 &data) { data = m_Reader.LoadRef<QT3DSU64>(); }
+
+ void Handle(QT3DSI32 &data) { data = m_Reader.LoadRef<QT3DSI32>(); }
+
+ void Handle(CRegisteredString &str)
+ {
+ QT3DSU32 len = m_Reader.LoadRef<QT3DSU32>();
+ m_TempString.clear();
+ if (len)
+ m_TempString.assign((const char *)m_Reader.m_CurrentPtr, (size_t)(len - 1));
+ m_Reader.m_CurrentPtr += len;
+ if (m_Reader.m_CurrentPtr > m_Reader.m_EndPtr)
+ m_Reader.m_CurrentPtr = m_Reader.m_EndPtr;
+
+ str = m_StringTable.RegisterStr(m_TempString.c_str());
+ }
+
+ void Handle(SSGValue &value)
+ {
+ QT3DSU32 valType = m_Reader.LoadRef<QT3DSU32>();
+ switch (valType) {
+ case SGPropertyValueTypes::Float:
+ value = SSGValue(m_Reader.LoadRef<float>());
+ break;
+ case SGPropertyValueTypes::I32:
+ value = SSGValue(m_Reader.LoadRef<QT3DSI32>());
+ break;
+ case SGPropertyValueTypes::String: {
+ CRegisteredString temp;
+ Handle(temp);
+ value = SSGValue(temp);
+ } break;
+ case SGPropertyValueTypes::Elem:
+ value = m_Reader.LoadRef<QT3DSU64>();
+ break;
+ case SGPropertyValueTypes::NoSGValue:
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ template <typename TDataType>
+ void HandleRef(NVDataRef<TDataType> &ref)
+ {
+ QT3DSU32 numItems = m_Reader.LoadRef<QT3DSU32>();
+ Option<NVDataRef<TDataType>> refOpt = AllocateData<TDataType>(numItems);
+ if (refOpt.hasValue()) {
+ ref = *refOpt;
+ for (QT3DSU32 idx = 0, end = ref.size(); idx < end && m_RestartRead == false;
+ ++idx)
+ Handle(ref[idx]);
+ }
+ }
+
+ template <typename TDataType>
+ void Handle(TDataType &dtype)
+ {
+ dtype.IterateProperties(*this);
+ }
+
+ template <typename TDataType>
+ void DoRead(TDataType &ioValue)
+ {
+ QT3DSU8 *startPtr = m_Reader.m_CurrentPtr;
+ QT3DSU32 restartCount = 0;
+ do {
+ m_RestartRead = false;
+ m_Allocated = 0;
+ m_Reader.m_CurrentPtr = startPtr;
+ ioValue.IterateProperties(*this);
+ ++restartCount;
+ } while (m_RestartRead);
+ }
+
+ SIdUpdate ReadIdUpdate()
+ {
+ SIdUpdate retval;
+ DoRead(retval);
+ return retval;
+ };
+
+ SElemUpdate ReadElemUpdate()
+ {
+ SElemUpdate retval;
+ DoRead(retval);
+ return retval;
+ };
+
+ QT3DSU32 ReadInitialization() { return m_Reader.LoadRef<QT3DSU32>(); }
+
+ bool Finished() { return m_Reader.m_CurrentPtr >= m_Reader.m_EndPtr; }
+ };
+ }
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h
new file mode 100644
index 00000000..3b6a4172
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_SCENE_GRAPH_DEBUGGER_VALUE_H
+#define QT3DS_SCENE_GRAPH_DEBUGGER_VALUE_H
+#include "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSUIADatamodel.h"
+#include "Qt3DSUIADatamodelValue.h"
+#include "Qt3DSStateEditorValue.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ struct SGPropertyValueTypes
+ {
+ enum Enum {
+ NoSGValue = 0,
+ Float,
+ I32,
+ String,
+ Elem,
+ };
+ };
+
+ template <typename dtype>
+ struct SGValueTypeMap
+ {
+ };
+
+ template <>
+ struct SGValueTypeMap<float>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::Float; }
+ };
+
+ template <>
+ struct SGValueTypeMap<QT3DSI32>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::I32; }
+ };
+
+ template <>
+ struct SGValueTypeMap<CRegisteredString>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::String; }
+ };
+
+ template <>
+ struct SGValueTypeMap<QT3DSU64>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::Elem; }
+ };
+
+ struct SSGValueUnionTraits
+ {
+ typedef SGPropertyValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(QT3DSU64),
+ };
+
+ static TIdType getNoDataId() { return SGPropertyValueTypes::NoSGValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SGValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case SGPropertyValueTypes::Float:
+ return inVisitor(*NVUnionCast<float *>(inData));
+ case SGPropertyValueTypes::I32:
+ return inVisitor(*NVUnionCast<QT3DSI32 *>(inData));
+ case SGPropertyValueTypes::String:
+ return inVisitor(*NVUnionCast<CRegisteredString *>(inData));
+ case SGPropertyValueTypes::Elem:
+ return inVisitor(*NVUnionCast<QT3DSU64 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case SGPropertyValueTypes::NoSGValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case SGPropertyValueTypes::Float:
+ return inVisitor(*NVUnionCast<const float *>(inData));
+ case SGPropertyValueTypes::I32:
+ return inVisitor(*NVUnionCast<const QT3DSI32 *>(inData));
+ case SGPropertyValueTypes::String:
+ return inVisitor(*NVUnionCast<const CRegisteredString *>(inData));
+ case SGPropertyValueTypes::Elem:
+ return inVisitor(*NVUnionCast<const QT3DSU64 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case SGPropertyValueTypes::NoSGValue:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SSGValueUnionTraits,
+ SSGValueUnionTraits::TBufferSize>,
+ SSGValueUnionTraits::TBufferSize>
+ TSGValueUnionType;
+
+ struct SSGValue : public TSGValueUnionType
+ {
+ SSGValue() {}
+ SSGValue(const SSGValue &other)
+ : TSGValueUnionType(static_cast<const TSGValueUnionType &>(other))
+ {
+ }
+ SSGValue(float val)
+ : TSGValueUnionType(val)
+ {
+ }
+ SSGValue(QT3DSI32 val)
+ : TSGValueUnionType(val)
+ {
+ }
+ SSGValue(CRegisteredString val)
+ : TSGValueUnionType(val)
+ {
+ }
+ SSGValue(QT3DSU64 val)
+ : TSGValueUnionType(val)
+ {
+ }
+
+ SSGValue &operator=(const SSGValue &other)
+ {
+ TSGValueUnionType::operator=(static_cast<const TSGValueUnionType &>(other));
+ return *this;
+ }
+ };
+
+ struct SSGPropertyChange
+ {
+ QT3DSI32 m_Hash;
+ SSGValue m_Value;
+ SSGPropertyChange(QT3DSI32 h, const SSGValue &v)
+ : m_Hash(h)
+ , m_Value(v)
+ {
+ }
+ SSGPropertyChange()
+ : m_Hash(0)
+ {
+ }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp
new file mode 100644
index 00000000..403fb580
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp
@@ -0,0 +1,345 @@
+/****************************************************************************
+**
+** 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 "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSSceneGraphDebuggerValue.h"
+#include "Qt3DSSceneGraphDebuggerProtocol.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "EASTL/sort.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+typedef eastl::pair<QT3DSU64, CStringHandle> TElemStringHandlePair;
+
+struct SRegisteredIDComparator
+{
+ bool operator()(const TElemStringHandlePair &lhs, const TElemStringHandlePair &rhs) const
+ {
+ return lhs.first < rhs.first;
+ }
+};
+
+struct SPresentationIdMap
+{
+ void *m_Presentation;
+ CRegisteredString m_PresentationId;
+ nvvector<TElemStringHandlePair> m_RegisteredIdBackingStore;
+ NVDataRef<TElemStringHandlePair> m_RegisteredIds;
+ SPresentationIdMap(NVAllocatorCallback &alloc)
+ : m_RegisteredIdBackingStore(alloc, "registered ids")
+ {
+ }
+};
+
+struct RuntimeDebuggerImpl : public ISceneGraphRuntimeDebugger
+{
+ NVFoundationBase &m_Foundation;
+ IStringTable &m_StringTable;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ nvvector<SElemMap> m_ElemMapBuffer;
+ nvvector<SPresentationIdMap *> m_PresentationIdMap;
+ nvvector<SValueUpdate> m_ValueUpdates;
+ // Filter mechanism so we don't send the same thing twice.
+ nvhash_map<void *, eastl::vector<SValueUpdate>> m_LastUpdates;
+ SSGProtocolWriter *m_Writer;
+ SSAutoDeallocatorAllocator m_DataAllocator;
+ nvhash_map<void *, CStringHandle> m_ElemToNameMap;
+ QT3DSI32 mRefCount;
+
+ RuntimeDebuggerImpl(NVFoundationBase &fnd, IStringTable &strt)
+ : m_Foundation(fnd)
+ , m_StringTable(strt)
+ , m_ElemMapBuffer(fnd.getAllocator(), "elem map buffer")
+ , m_PresentationIdMap(fnd.getAllocator(), "Presentations")
+ , m_ValueUpdates(fnd.getAllocator(), "Value updates")
+ , m_LastUpdates(fnd.getAllocator(), "Last updates")
+ , m_Writer(NULL)
+ , m_DataAllocator(fnd)
+ , m_ElemToNameMap(fnd.getAllocator(), "ElemToNameMap")
+ , mRefCount(0)
+ {
+ }
+
+ ~RuntimeDebuggerImpl()
+ {
+ Disconnect();
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx)
+ delete m_PresentationIdMap[idx];
+ // the auto-deallocator takes care of the load data.
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ void Disconnect()
+ {
+ if (m_Writer)
+ delete m_Writer;
+ m_Writer = NULL;
+ }
+
+ bool CheckConnection()
+ {
+ if (m_OutStream) {
+ bool connected = m_OutStream->Connected();
+ if (!connected)
+ Disconnect();
+
+ return connected;
+ }
+ return false;
+ }
+
+ void MapPresentationId(void *presentation, const char *id) override
+ {
+ CRegisteredString newId = m_StringTable.RegisterStr(nonNull(id));
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx) {
+ if (m_PresentationIdMap[idx]->m_Presentation == presentation) {
+ m_PresentationIdMap[idx]->m_PresentationId = newId;
+ return;
+ }
+ }
+
+ SPresentationIdMap *map = new SPresentationIdMap(m_Foundation.getAllocator());
+ map->m_Presentation = presentation;
+ map->m_PresentationId = newId;
+ m_PresentationIdMap.push_back(map);
+ }
+
+ void SendElemIdMap(SPresentationIdMap &map)
+ {
+ if (m_ElemMapBuffer.size() && CheckConnection()) {
+ SIdUpdate theUpdate;
+ theUpdate.m_Presentation = (QT3DSU64)map.m_Presentation;
+ theUpdate.m_PresentationId = map.m_PresentationId;
+ theUpdate.m_IdUpdates = m_ElemMapBuffer;
+ m_Writer->Write(theUpdate);
+ }
+ m_ElemMapBuffer.clear();
+ }
+
+ void MapElementIds(void *presentation, NVConstDataRef<SGElemIdMap> inIds) override
+ {
+ SPresentationIdMap *map = NULL;
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end && map == NULL; ++idx)
+ if (m_PresentationIdMap[idx]->m_Presentation == presentation)
+ map = m_PresentationIdMap[idx];
+ if (map == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ m_ElemMapBuffer.clear();
+ m_ElemToNameMap.clear();
+ for (QT3DSU32 idx = 0, end = inIds.size(); idx < end; ++idx) {
+ const SGElemIdMap &item(inIds[idx]);
+ SElemMap newMap;
+ newMap.m_Elem = (QT3DSU64)item.m_Elem;
+ CStringHandle idHandle = m_StringTable.GetHandle(nonNull(item.m_Id));
+ newMap.m_Id = m_StringTable.HandleToStr(idHandle);
+ m_ElemMapBuffer.push_back(newMap);
+ m_ElemToNameMap[item.m_Elem] = idHandle;
+ }
+ SendElemIdMap(*map);
+
+ // store them for later.
+ map->m_RegisteredIdBackingStore.reserve(m_ElemToNameMap.size());
+ for (nvhash_map<void *, CStringHandle>::iterator iter = m_ElemToNameMap.begin(),
+ end = m_ElemToNameMap.end();
+ iter != end; ++iter) {
+ map->m_RegisteredIdBackingStore.push_back(
+ eastl::make_pair((QT3DSU64)iter->first, iter->second));
+ }
+
+ eastl::sort(map->m_RegisteredIdBackingStore.begin(), map->m_RegisteredIdBackingStore.end(),
+ SRegisteredIDComparator());
+
+ map->m_RegisteredIds = NVDataRef<TElemStringHandlePair>(
+ map->m_RegisteredIdBackingStore.data(), (QT3DSU32)map->m_RegisteredIdBackingStore.size());
+ }
+
+ static bool Equals(const SValueUpdate &lhs, const SValueUpdate &rhs)
+ {
+ return lhs.m_Hash == rhs.m_Hash && lhs.m_Value == rhs.m_Value;
+ }
+
+ void OnPropertyChanged(void *elem, NVConstDataRef<SSGPropertyChange> changes) override
+ {
+ if (CheckConnection() == false)
+ return;
+ eastl::vector<SValueUpdate> &updates = m_LastUpdates[elem];
+ updates.resize(changes.size());
+ for (QT3DSU32 changeIdx = 0, changeEnd = changes.size(); changeIdx < changeEnd; ++changeIdx) {
+ SValueUpdate theUpdate;
+ theUpdate.m_Hash = changes[changeIdx].m_Hash;
+ theUpdate.m_Value = changes[changeIdx].m_Value;
+ if (Equals(theUpdate, updates[changeIdx]) == false) {
+ updates[changeIdx] = theUpdate;
+ m_ValueUpdates.push_back(theUpdate);
+ }
+ }
+ if (m_ValueUpdates.size()) {
+ SElemUpdate theUpdate;
+ theUpdate.m_Elem = (QT3DSU64)elem;
+ theUpdate.m_Updates = m_ValueUpdates;
+ m_Writer->Write(theUpdate);
+ m_ValueUpdates.clear();
+ }
+ }
+
+ void SendPresentation(SPresentationIdMap &pres)
+ {
+ if (m_OutStream) {
+ m_ElemMapBuffer.clear();
+ for (TElemStringHandlePair *iter = pres.m_RegisteredIds.begin(),
+ *end = pres.m_RegisteredIds.end();
+ iter != end; ++iter) {
+ SElemMap newMap;
+ newMap.m_Elem = (QT3DSU64)iter->first;
+ newMap.m_Id = m_StringTable.HandleToStr(iter->second);
+ m_ElemMapBuffer.push_back(newMap);
+ }
+ SendElemIdMap(pres);
+ }
+ }
+
+ void OnConnection(IDebugOutStream &outStream) override
+ {
+ Disconnect();
+ m_OutStream = outStream;
+ m_Writer = new SSGProtocolWriter(outStream, m_Foundation.getAllocator());
+ m_Writer->WriteInitialization();
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx) {
+ SPresentationIdMap *map = m_PresentationIdMap[idx];
+ SendPresentation(*map);
+ }
+ }
+
+ bool IsConnected() override { return CheckConnection(); }
+
+ void BinarySave(IOutStream &ioStream) override
+ {
+ qt3ds::foundation::SWriteBuffer theWriteBuffer(m_Foundation.getAllocator(),
+ "BinarySave::writebuffer");
+
+ theWriteBuffer.writeZeros(4); // Overall binary size
+ theWriteBuffer.write((QT3DSU32)m_PresentationIdMap.size());
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx) {
+ // There is no use to writing out the presentation pointer address.
+ /*
+ void*
+ m_Presentation;
+ CRegisteredString m_PresentationId;
+ nvhash_map<void*, CRegisteredString> m_RegisteredIds;
+ */
+
+ SPresentationIdMap *map = m_PresentationIdMap[idx];
+ CRegisteredString presId(map->m_PresentationId);
+ presId.Remap(m_StringTable.GetRemapMap());
+ theWriteBuffer.write(presId);
+ theWriteBuffer.write((QT3DSU32)map->m_RegisteredIds.size());
+ theWriteBuffer.write(map->m_RegisteredIds.begin(), map->m_RegisteredIds.size());
+ }
+
+ QT3DSU32 totalSize = theWriteBuffer.size();
+ QT3DSU32 *data = (QT3DSU32 *)theWriteBuffer.begin();
+ data[0] = totalSize - 4;
+ ioStream.Write((QT3DSU8 *)data, totalSize);
+ }
+
+ void BinaryLoad(IInStream &ioStream, NVDataRef<QT3DSU8> strTableData) override
+ {
+ QT3DSU32 length;
+ ioStream.Read(length);
+ QT3DSU8 *data = (QT3DSU8 *)m_DataAllocator.allocate(length, "Binaryload", __FILE__, __LINE__);
+ ioStream.Read(data, length);
+ SDataReader theReader(data, data + length);
+ QT3DSU32 numPresentations = theReader.LoadRef<QT3DSU32>();
+ QT3DS_ASSERT(m_PresentationIdMap.size() == 0);
+ m_PresentationIdMap.resize(numPresentations);
+ for (QT3DSU32 idx = 0, end = numPresentations; idx < end; ++idx) {
+ m_PresentationIdMap[idx] = new SPresentationIdMap(m_Foundation.getAllocator());
+ SPresentationIdMap *map = m_PresentationIdMap[idx];
+ map->m_PresentationId = theReader.LoadRef<CRegisteredString>();
+ map->m_PresentationId.Remap(strTableData);
+ QT3DSU32 numElems = theReader.LoadRef<QT3DSU32>();
+ map->m_RegisteredIds =
+ toDataRef((TElemStringHandlePair *)theReader.m_CurrentPtr, numElems);
+ theReader.m_CurrentPtr += numElems * sizeof(TElemStringHandlePair);
+ }
+ }
+
+ void BinaryLoadPresentation(void *presPtr, const char *id, size_t elemOffset) override
+ {
+ CRegisteredString presId(m_StringTable.RegisterStr(id));
+ SPresentationIdMap *foundPres = NULL;
+ for (QT3DSU32 idx = 0, end = (QT3DSU32)m_PresentationIdMap.size(); idx < end && foundPres == NULL;
+ ++idx) {
+ if (m_PresentationIdMap[idx]->m_PresentationId == presId)
+ foundPres = m_PresentationIdMap[idx];
+ }
+ if (foundPres == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ foundPres->m_Presentation = presPtr;
+ nvvector<eastl::pair<void *, CRegisteredString>> newElemIds(m_Foundation.getAllocator(),
+ "Temp load map");
+ newElemIds.reserve(foundPres->m_RegisteredIds.size());
+ for (TElemStringHandlePair *iter = foundPres->m_RegisteredIds.begin(),
+ *end = foundPres->m_RegisteredIds.end();
+ iter != end; ++iter) {
+ size_t oldId = (size_t)iter->first;
+ size_t newId = elemOffset + oldId;
+ iter->first = (QT3DSU64)newId;
+ }
+ SendPresentation(*foundPres);
+ }
+
+ void EndFrame() override
+ {
+ if (CheckConnection())
+ m_Writer->WriteFrame();
+ }
+
+ void OnMessageReceived(const SDebugStreamMessage &) override { QT3DS_ASSERT(false); }
+};
+}
+
+ISceneGraphRuntimeDebugger &ISceneGraphRuntimeDebugger::Create(NVFoundationBase &fnd,
+ IStringTable &strTable)
+{
+ return *QT3DS_NEW(fnd.getAllocator(), RuntimeDebuggerImpl)(fnd, strTable);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp
new file mode 100644
index 00000000..ced1397b
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateTest.h"
+#include "foundation/IOStreams.h"
+#include "EASTL/string.h"
+#include "foundation/Utils.h"
+#include "foundation/FileTools.h"
+#include "foundation/XML.h"
+#include "foundation/Qt3DSAllocator.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/TrackingAllocator.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSStateContext.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "Qt3DSStateExecutionContext.h"
+#include "Qt3DSStateInterpreter.h"
+
+using namespace qt3ds::state::test;
+using namespace qt3ds::state;
+
+namespace {
+
+struct XMLHandler : public qt3ds::foundation::CXmlErrorHandler
+{
+ IDataLogger &m_Logger;
+ const char8_t *m_File;
+ eastl::string m_ErrorString;
+ XMLHandler(IDataLogger &logger, const char8_t *fname)
+ : m_Logger(logger)
+ , m_File(fname)
+ {
+ }
+
+ void OnXmlError(qt3ds::foundation::TXMLCharPtr errorName, int line, int /*column*/) override
+ {
+ m_ErrorString.assign("Failed to parse test file: ");
+ m_ErrorString.append(m_File);
+ m_Logger.Log(LogType::Error, m_File, line, errorName);
+ }
+};
+
+Option<STestResults> RunTest(const char8_t *inFullPath, const char8_t *inRoot,
+ IDataLogger &inLogger)
+{
+ return STestResults(1, 1);
+}
+}
+
+Option<STestResults> IDataTest::RunFile(const char8_t *fname, const char8_t *inRootDir,
+ IDataLogger &inLogger)
+{
+ return RunTest(fname, inRootDir, inLogger);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp
new file mode 100644
index 00000000..707ea73e
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp
@@ -0,0 +1,534 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateDebugStreams.h"
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSFlags.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSSync.h"
+#include "foundation/Qt3DSMemoryBuffer.h"
+#include "EASTL/string.h"
+
+using namespace qt3ds;
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+struct MultiProtocolMessageTypes
+{
+ enum Enum {
+ UnknownMessageType = 0,
+ NewProtocol = 1,
+ ProtocolMessage = 1 << 2,
+ };
+};
+
+struct SMultiProtocolMessageFlags : public NVFlags<MultiProtocolMessageTypes::Enum, QT3DSU32>
+{
+ bool IsNewProtocol() { return this->operator&(MultiProtocolMessageTypes::NewProtocol); }
+ void SetNewProtocol(bool inValue)
+ {
+ this->clearOrSet(inValue, MultiProtocolMessageTypes::NewProtocol);
+ }
+
+ bool IsProtocolMessage() { return this->operator&(MultiProtocolMessageTypes::ProtocolMessage); }
+ void SetProtocolMessage(bool inValue)
+ {
+ this->clearOrSet(inValue, MultiProtocolMessageTypes::ProtocolMessage);
+ }
+};
+
+struct SMultiProtocolInitializer
+{
+ static QT3DSU16 GetCurrentMultiProtocolVersion() { return 1; }
+
+ QT3DSU64 m_TimeNumerator;
+ QT3DSU64 m_TimeDenominator;
+ QT3DSU32 m_ProtocolVersion;
+
+ SMultiProtocolInitializer()
+ : m_TimeNumerator(Time::sCounterFreq.mNumerator)
+ , m_TimeDenominator(Time::sCounterFreq.mDenominator)
+ , m_ProtocolVersion(GetCurrentMultiProtocolVersion())
+ {
+ }
+};
+
+struct SMultiProtocolMessageHeader
+{
+
+ SMultiProtocolMessageFlags m_Flags;
+ QT3DSU32 m_Size;
+ QT3DSU32 m_ProtocolId;
+ QT3DSU64 m_Timestamp;
+ SMultiProtocolMessageHeader(MultiProtocolMessageTypes::Enum inMessageType, QT3DSU32 size,
+ QT3DSU32 protocolId, QT3DSU64 timestamp)
+ : m_Size(size)
+ , m_ProtocolId(protocolId)
+ , m_Timestamp(timestamp)
+ {
+ m_Flags.clearOrSet(true, inMessageType);
+ }
+ SMultiProtocolMessageHeader() {}
+};
+
+struct IProtocolMessageHandler
+{
+protected:
+ virtual ~IProtocolMessageHandler() {}
+public:
+ virtual void OnMessageReceived(SDebugStreamMessage msgData) = 0;
+};
+
+struct IProtocolHandler
+{
+protected:
+ virtual ~IProtocolHandler() {}
+public:
+ virtual void OnNewProtocol(CRegisteredString inProtocolName) = 0;
+};
+
+struct SSharedStreamImpl : public NVRefCounted
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<SocketStream> m_Stream;
+ IOutStream &m_WriteStream;
+ SMultiProtocolInitializer m_Initializer;
+ eastl::hash_map<CRegisteredString, QT3DSU32> m_ProtocolIdMap;
+ eastl::hash_map<QT3DSU32, IProtocolMessageHandler *> m_MessageHandlers;
+ MemoryBuffer<> m_ReadBuffer;
+ IStringTable &m_StringTable;
+ IProtocolHandler *m_ProtocolHandler;
+ QT3DSU32 m_NextProtocolId;
+ QT3DSI32 mRefCount;
+
+ SSharedStreamImpl(NVFoundationBase &fnd, SocketStream &stream, IOutStream &writeStream,
+ IStringTable &strTable, IProtocolHandler &pHandler)
+ : m_Foundation(fnd)
+ , m_Stream(stream)
+ , m_WriteStream(writeStream)
+ , m_ReadBuffer(ForwardingAllocator(fnd.getAllocator(), "ReadBuffer"))
+ , m_StringTable(strTable)
+ , m_ProtocolHandler(&pHandler)
+ , m_NextProtocolId(1)
+ , mRefCount(0)
+ {
+ NVConstDataRef<QT3DSU8> msgData = toU8DataRef(m_Initializer);
+ bool streamValid = m_Stream->Write(msgData);
+ if (streamValid == false)
+ m_Stream = NULL;
+ }
+ ~SSharedStreamImpl() {}
+
+ bool Initialize()
+ {
+ if (m_Stream) {
+ NVDataRef<QT3DSU8> msgData = toU8DataRef(m_Initializer);
+ QT3DSU32 numBytes = m_Stream->Read(msgData);
+ if (numBytes != sizeof(SMultiProtocolInitializer)
+ || m_Initializer.m_ProtocolVersion
+ > SMultiProtocolInitializer::GetCurrentMultiProtocolVersion()) {
+ m_Stream = NULL;
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ QT3DSU32 GetIdForProtocol(CRegisteredString protocol)
+ {
+ if (protocol.IsValid() == false)
+ return 0;
+ eastl::pair<eastl::hash_map<CRegisteredString, QT3DSU32>::iterator, bool> inserter =
+ m_ProtocolIdMap.insert(eastl::make_pair(protocol, m_NextProtocolId));
+ if (inserter.second) {
+ QT3DSU32 newId = m_NextProtocolId;
+ ++m_NextProtocolId;
+ if (m_Stream) {
+ QT3DSU32 msgLen = (QT3DSU32)strlen(protocol) + 1;
+ NVConstDataRef<QT3DSU8> writeBuf(reinterpret_cast<const QT3DSU8 *>(protocol.c_str()),
+ msgLen);
+ WriteMessage(MultiProtocolMessageTypes::NewProtocol, writeBuf, newId);
+ }
+ }
+ return inserter.first->second;
+ }
+
+ CRegisteredString GetProtocolForId(QT3DSU32 id)
+ {
+ for (eastl::hash_map<CRegisteredString, QT3DSU32>::iterator iter = m_ProtocolIdMap.begin(),
+ end = m_ProtocolIdMap.end();
+ iter != end; ++iter) {
+ if (iter->second == id)
+ return iter->first;
+ }
+ return CRegisteredString();
+ }
+
+ void AddMessageHandler(CRegisteredString protocol, IProtocolMessageHandler &hdl)
+ {
+ m_MessageHandlers.insert(eastl::make_pair(GetIdForProtocol(protocol), &hdl));
+ }
+
+ void RemoveMessageHandler(CRegisteredString protocol)
+ {
+ m_MessageHandlers.erase(GetIdForProtocol(protocol));
+ }
+
+ IProtocolMessageHandler *GetMessageHandler(CRegisteredString protocol)
+ {
+ return GetMessageHandler(GetIdForProtocol(protocol));
+ }
+
+ IProtocolMessageHandler *GetMessageHandler(QT3DSU32 protocolId)
+ {
+ eastl::hash_map<QT3DSU32, IProtocolMessageHandler *>::iterator iter =
+ m_MessageHandlers.find(protocolId);
+ if (iter != m_MessageHandlers.end())
+ return iter->second;
+ return NULL;
+ }
+
+ void ProtocolHandlerLeaving() { m_ProtocolHandler = NULL; }
+
+ void DispatchMessage(SMultiProtocolMessageHeader inHeader, NVConstDataRef<QT3DSU8> msg)
+ {
+ if (inHeader.m_Flags.IsNewProtocol()) {
+ char *pId = reinterpret_cast<char *>(const_cast<QT3DSU8 *>(msg.begin()));
+ // Ensure null terminated, which should be done anyway but we don't know it will be.
+ pId[inHeader.m_Size] = 0;
+ CRegisteredString protocolName = m_StringTable.RegisterStr(pId);
+ eastl::pair<eastl::hash_map<CRegisteredString, QT3DSU32>::iterator, bool> inserter =
+ m_ProtocolIdMap.insert(eastl::make_pair(protocolName, inHeader.m_ProtocolId));
+ if (inserter.second == false) {
+ // remap id to higher id to reduce the chance of conflicts.
+ QT3DSU32 potentialNewId = NVMax(inserter.first->second, inHeader.m_ProtocolId);
+ if (potentialNewId != inserter.first->second) {
+ m_NextProtocolId = NVMax(m_NextProtocolId, potentialNewId + 1);
+ CRegisteredString existing = GetProtocolForId(potentialNewId);
+ if (existing.IsValid()) {
+ m_ProtocolIdMap.erase(protocolName);
+ GetIdForProtocol(protocolName);
+ return;
+ } else {
+ inserter.first->second = potentialNewId;
+ }
+ }
+ } else {
+ if (m_ProtocolHandler != NULL) {
+ m_ProtocolHandler->OnNewProtocol(protocolName);
+ }
+ }
+ } else {
+ IProtocolMessageHandler *handler = GetMessageHandler(inHeader.m_ProtocolId);
+ if (handler != NULL)
+ handler->OnMessageReceived(SDebugStreamMessage(inHeader.m_Timestamp, msg));
+ }
+ }
+
+ NVDataRef<QT3DSU8> ReadChunk(NVDataRef<QT3DSU8> target)
+ {
+ QT3DSU32 totalRead = 0;
+ do {
+ NVDataRef<QT3DSU8> nextBuf(target.begin() + totalRead, target.size() - totalRead);
+ QT3DSU32 readResult = m_Stream->Read(nextBuf);
+ totalRead += readResult;
+ if (totalRead < target.size()) {
+ totalRead = totalRead;
+ }
+ } while (connected() && totalRead < target.size());
+
+ return toDataRef(m_ReadBuffer.begin(), totalRead);
+ }
+
+ NVDataRef<QT3DSU8> ReadChunk(QT3DSU32 size)
+ {
+ m_ReadBuffer.reserve(size);
+ return ReadChunk(toDataRef(m_ReadBuffer.begin(), size));
+ }
+
+ virtual SDebugStreamMessage WaitForNextMessage(CRegisteredString protocol)
+ {
+ QT3DSU32 msgId = GetIdForProtocol(protocol);
+
+ while (m_Stream) {
+ SMultiProtocolMessageHeader header;
+ NVDataRef<QT3DSU8> buf = toU8DataRef(header);
+ buf = ReadChunk(buf);
+ if (buf.size() < sizeof(header)) {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+ } else {
+ NVDataRef<QT3DSU8> readResult = ReadChunk(header.m_Size);
+ if (readResult.mSize != header.m_Size) {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+ } else {
+ if (header.m_ProtocolId == msgId) {
+ SDebugStreamMessage message;
+ message.m_Timestamp = header.m_Timestamp;
+ message.m_Data = readResult;
+ return message;
+ } else
+ DispatchMessage(header, readResult);
+ }
+ }
+ }
+
+ return SDebugStreamMessage();
+ }
+
+ virtual void MessagePump()
+ {
+ if (m_Stream == NULL)
+ return;
+ bool lastMessage = true;
+ do {
+ SMultiProtocolMessageHeader header;
+ NVDataRef<QT3DSU8> buf = toU8DataRef(header);
+ QT3DSU32 amountRead = m_Stream->nonBlockingRead(buf);
+ if (amountRead == 0) {
+ if (m_Stream->connected() == false)
+ m_Stream = NULL;
+ lastMessage = false;
+ } else {
+ // read the rest of the header.
+ QT3DSU32 leftover = buf.size() - amountRead;
+ if (leftover) {
+ NVDataRef<QT3DSU8> nextPiece(buf.begin() + amountRead, leftover);
+ nextPiece = ReadChunk(nextPiece);
+ amountRead += nextPiece.size();
+ }
+
+ if (amountRead < sizeof(SMultiProtocolMessageHeader)) {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+
+ } else {
+ NVDataRef<QT3DSU8> msgData = ReadChunk(header.m_Size);
+ if (msgData.size() == header.m_Size) {
+ DispatchMessage(header, msgData);
+ } else {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ } while (lastMessage && m_Stream);
+ }
+
+ SMultiProtocolMessageHeader CreateHeader(MultiProtocolMessageTypes::Enum type, QT3DSU32 size,
+ QT3DSU32 protocolId)
+ {
+ SMultiProtocolMessageHeader retval;
+ retval.m_Flags.clearOrSet(true, type);
+ retval.m_ProtocolId = protocolId;
+ retval.m_Size = size;
+ retval.m_Timestamp = Time::getCurrentCounterValue();
+ return retval;
+ }
+
+ bool WriteMessage(MultiProtocolMessageTypes::Enum type, NVConstDataRef<QT3DSU8> data,
+ QT3DSU32 protocolId)
+ {
+ if (connected()) {
+ SMultiProtocolMessageHeader header(CreateHeader(type, data.size(), protocolId));
+ NVConstDataRef<QT3DSU8> writeBuf = toU8DataRef(header);
+ bool success = m_WriteStream.Write(writeBuf);
+ if (success) {
+ success = m_WriteStream.Write(data);
+ }
+ if (!success)
+ m_Stream = NULL;
+ return success;
+ }
+ return false;
+ }
+
+ virtual bool Write(CRegisteredString protocol, NVConstDataRef<QT3DSU8> data)
+ {
+ return WriteMessage(MultiProtocolMessageTypes::ProtocolMessage, data,
+ GetIdForProtocol(protocol));
+ }
+
+ bool connected() { return m_Stream != NULL && m_Stream->connected(); }
+};
+
+struct SMultiProtocolSocketStreamImpl : public IMultiProtocolSocketStream,
+ public IProtocolMessageHandler
+{
+ NVFoundationBase &m_Foundation;
+ CRegisteredString m_Protocol;
+ IDebugStreamListener *m_Listener;
+ NVScopedRefCounted<SSharedStreamImpl> m_SharedStream;
+ bool m_StreamValid;
+ QT3DSI32 mRefCount;
+
+ SMultiProtocolSocketStreamImpl(NVFoundationBase &fnd, CRegisteredString protocol,
+ IDebugStreamListener *listener, SSharedStreamImpl &stream)
+ : m_Foundation(fnd)
+ , m_Protocol(protocol)
+ , m_Listener(listener)
+ , m_SharedStream(stream)
+ , m_StreamValid(true)
+ , mRefCount(0)
+ {
+ m_SharedStream->AddMessageHandler(m_Protocol, *this);
+ }
+
+ ~SMultiProtocolSocketStreamImpl() { m_SharedStream->RemoveMessageHandler(m_Protocol); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ IDebugStreamListener *GetListener() override { return m_Listener; }
+ CRegisteredString GetProtocolName() override { return m_Protocol; }
+
+ void SetListener(IDebugStreamListener *listener) override { m_Listener = listener; }
+
+ bool Write(NVConstDataRef<QT3DSU8> data) override
+ {
+ if (m_StreamValid)
+ m_StreamValid = m_SharedStream->Write(m_Protocol, data);
+
+ return m_StreamValid;
+ }
+
+ SDebugStreamMessage WaitForNextMessage() override
+ {
+ if (m_StreamValid)
+ return m_SharedStream->WaitForNextMessage(m_Protocol);
+ return SDebugStreamMessage();
+ }
+
+ void OnMessageReceived(SDebugStreamMessage data) override
+ {
+ if (m_Listener)
+ m_Listener->OnMessageReceived(data);
+ }
+
+ bool Connected() override { return m_SharedStream->connected(); }
+};
+
+struct SMultiProtocolSocketImpl : public IMultiProtocolSocket, public IProtocolHandler
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<SSharedStreamImpl> m_SharedStream;
+ NVScopedRefCounted<IMultiProtocolSocketListener> m_ProtocolListener;
+ QT3DSI32 mRefCount;
+ SMultiProtocolSocketImpl(NVFoundationBase &fnd, SocketStream &inStream, IStringTable &strTable,
+ IMultiProtocolSocketListener *protocolListener)
+ : m_Foundation(fnd)
+ , m_ProtocolListener(protocolListener)
+ , mRefCount(0)
+ {
+ // At some point I may switch the writer to a buffered stream, at least on the client side.
+ m_SharedStream = QT3DS_NEW(m_Foundation.getAllocator(), SSharedStreamImpl)(
+ m_Foundation, inStream, inStream, strTable, *this);
+ }
+
+ ~SMultiProtocolSocketImpl() { m_SharedStream->ProtocolHandlerLeaving(); }
+
+ bool Initialize() override { return m_SharedStream->Initialize(); }
+
+ bool Connected() override { return m_SharedStream->connected(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ virtual NVScopedRefCounted<IMultiProtocolSocketStream>
+ CreateProtocol(const char *name, IDebugStreamListener *inListener) override
+ {
+ NVScopedRefCounted<IMultiProtocolSocketStream> retval = GetProtocol(name);
+ if (retval) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+ CRegisteredString protocolName = m_SharedStream->m_StringTable.RegisterStr(name);
+ if (protocolName.IsValid() == false) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+ SMultiProtocolSocketStreamImpl *newStream =
+ QT3DS_NEW(m_Foundation.getAllocator(), SMultiProtocolSocketStreamImpl)(
+ m_Foundation, protocolName, inListener, *m_SharedStream);
+ return newStream;
+ }
+
+ NVScopedRefCounted<IMultiProtocolSocketStream> GetProtocol(const char *name) override
+ {
+ CRegisteredString protocolName = m_SharedStream->m_StringTable.RegisterStr(name);
+ IProtocolMessageHandler *handler = m_SharedStream->GetMessageHandler(protocolName);
+ if (handler) {
+ SMultiProtocolSocketStreamImpl *theImpl =
+ static_cast<SMultiProtocolSocketStreamImpl *>(handler);
+ return theImpl;
+ }
+ return NVScopedRefCounted<IMultiProtocolSocketStream>();
+ }
+
+ void OnNewProtocol(CRegisteredString inProtocolName) override
+ {
+ if (m_ProtocolListener) {
+ // We can expect the user to call create protocol at this point.
+ IDebugStreamListener *handler = m_ProtocolListener->OnNewProtocol(inProtocolName);
+ if (handler) {
+ SMultiProtocolSocketStreamImpl *newStream =
+ QT3DS_NEW(m_Foundation.getAllocator(), SMultiProtocolSocketStreamImpl)(
+ m_Foundation, inProtocolName, handler, *m_SharedStream);
+ m_ProtocolListener->OnNewProtocolStream(inProtocolName, *newStream);
+ }
+ }
+ }
+
+ CounterFrequencyToTensOfNanos SourceConversion() override
+ {
+ return CounterFrequencyToTensOfNanos(m_SharedStream->m_Initializer.m_TimeNumerator,
+ m_SharedStream->m_Initializer.m_TimeDenominator);
+ }
+
+ void MessagePump() override { m_SharedStream->MessagePump(); }
+};
+}
+
+NVScopedRefCounted<IMultiProtocolSocket>
+IMultiProtocolSocket::CreateProtocolSocket(NVFoundationBase &fnd, SocketStream &inStream,
+ IStringTable &strTable,
+ IMultiProtocolSocketListener *protocolListener)
+{
+ return QT3DS_NEW(fnd.getAllocator(), SMultiProtocolSocketImpl)(fnd, inStream, strTable,
+ protocolListener);
+}
+
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h
new file mode 100644
index 00000000..5bbc0b26
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUG_STREAMS_H
+#define QT3DS_STATE_DEBUG_STREAMS_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Socket.h"
+#include "foundation/Qt3DSTime.h"
+#include "Qt3DSStateDebugger.h"
+
+struct script_State;
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ class IMultiProtocolSocketStream : public IDebugOutStream
+ {
+ public:
+ virtual CRegisteredString GetProtocolName() = 0;
+ };
+
+ class IMultiProtocolSocketListener : public NVRefCounted
+ {
+ public:
+ // If a listener is returned from on new protocol, the system creates a stream
+ // with the returned listener
+ virtual IDebugStreamListener *OnNewProtocol(CRegisteredString inProtocolName) = 0;
+ // Created with the listener returned from OnNewProtocol.
+ virtual void OnNewProtocolStream(CRegisteredString inProtocolName,
+ IMultiProtocolSocketStream &inStream) = 0;
+ };
+
+ // Create a system of multiplexing multipler unrelated protocols
+ // through a single network socket.
+ class IMultiProtocolSocket : public NVRefCounted
+ {
+ public:
+ virtual bool Initialize() = 0;
+ virtual NVScopedRefCounted<IMultiProtocolSocketStream>
+ CreateProtocol(const char *name, IDebugStreamListener *inListener) = 0;
+ virtual NVScopedRefCounted<IMultiProtocolSocketStream>
+ GetProtocol(const char *name) = 0;
+ virtual bool Connected() = 0;
+
+ // Upon connection the multi protocol system does a handshake where both sides send
+ // their nanosecond
+ // conversions across. Note that the times sent on the multi protocol packets are in a
+ // system-specific 64 bit quantity that
+ // needs conversion to actual nanoseconds to be useful (identical to
+ // QueryHighPerformanceFrequency, QueryHighPerformanceCounter
+ virtual CounterFrequencyToTensOfNanos SourceConversion() = 0;
+
+ // Manually do a nonblocking check on the network socket for any new information and
+ // call the various stream listeners
+ // with information packets if found.
+ virtual void MessagePump() = 0;
+
+ static NVScopedRefCounted<IMultiProtocolSocket>
+ CreateProtocolSocket(NVFoundationBase &fnd, SocketStream &inStream,
+ IStringTable &strTable,
+ IMultiProtocolSocketListener *protocolListener);
+ };
+
+ class CProtocolNames
+ {
+ public:
+ static const char *getMobdebugProtocolName() { return "mobdebug"; }
+ static const char *getSCXMLProtocolName() { return "scxml"; }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp
new file mode 100644
index 00000000..a64e8589
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp
@@ -0,0 +1,302 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "EASTL/map.h"
+#include "EASTL/set.h"
+#include "foundation/Qt3DSAtomic.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+// Horrible name, I know.
+struct SDebuggedInterpreter : public IDebuggedInterpreter
+{
+ typedef eastl::set<TDebugStr> TDebugStrSet;
+ typedef eastl::vector<SBreakpoint> TBreakpointList;
+ typedef eastl::map<TDebugStr, SDatamodelValue> TDataModelTable;
+ typedef eastl::hash_map<SDatamodelTable *, TDataModelTable> TTableMap;
+ typedef eastl::vector<STableEntry> TTableEntryList;
+ // Null is the datamodel table.
+
+ NVAllocatorCallback &m_Allocator;
+ NVScopedRefCounted<IDebugOutStream> m_Stream;
+ QT3DSI32 mRefCount;
+ TEditorPtr m_Editor;
+ TDebugStr m_Filename;
+ SMessageSerializer m_Serializer;
+ QT3DSI32 m_StreamId;
+ TBreakpointList m_Breakpoints;
+ TDebugStrList m_StrList;
+ TDebugStrSet m_Configuration;
+ bool m_BreakOnMicrostep;
+ bool m_IsBroken;
+ Option<SBreakpoint> m_BrokenBreakpoint;
+ IDebuggerMasterListener &m_Listener;
+ TTableMap m_DatamodelValues;
+ mutable TTableEntryList m_TempEntries;
+ bool m_MicrostepHasData;
+ SMicrostepData m_MicrostepData;
+ TDebugStr m_MicrostepEvent;
+ TDebugStrList m_ConfigurationList;
+ TDebugStrList m_PreviousConfiguration;
+
+ SDebuggedInterpreter(NVAllocatorCallback &inAlloc, IDebugOutStream &inStream,
+ TEditorPtr inEditor, const TDebugStr &inFname, QT3DSI32 inStreamId,
+ NVConstDataRef<TDebugStr> inConfiguration,
+ IDebuggerMasterListener &inListener)
+ : m_Allocator(inAlloc)
+ , m_Stream(inStream)
+ , mRefCount(0)
+ , m_Editor(inEditor)
+ , m_Filename(inFname)
+ , m_StreamId(inStreamId)
+ , m_BreakOnMicrostep(false)
+ , m_IsBroken(false)
+ , m_Listener(inListener)
+ , m_MicrostepHasData(false)
+ {
+ m_ConfigurationList.assign(inConfiguration.begin(), inConfiguration.end());
+ m_Configuration.insert(inConfiguration.begin(), inConfiguration.end());
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ // Get the editor that represents the state graph for this debugged interpreter.
+ TEditorPtr GetEditor() override { return m_Editor; }
+ TDebugStr GetFilename() const override { return m_Filename; }
+
+ template <typename TMessageType>
+ void SendMessage(const TMessageType &inMessage)
+ {
+ m_Serializer.Serialize(m_StreamId, inMessage, 0);
+ m_Stream->Write(m_Serializer.ToRawMessage());
+ }
+
+ // Used when connecting to a live session.
+ void SetBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ TBreakpointList::iterator iter =
+ eastl::find(m_Breakpoints.begin(), m_Breakpoints.end(), inBreakpoint);
+ if (iter == m_Breakpoints.end()) {
+ SendMessage(SSetBreakpoint(inBreakpoint));
+ m_Breakpoints.push_back(inBreakpoint);
+ }
+ }
+
+ void ClearBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ TBreakpointList::iterator iter =
+ eastl::find(m_Breakpoints.begin(), m_Breakpoints.end(), inBreakpoint);
+ if (iter != m_Breakpoints.end()) {
+ SendMessage(SClearBreakpoint(inBreakpoint));
+ m_Breakpoints.erase(iter);
+ }
+ }
+
+ NVConstDataRef<SBreakpoint> GetBreakpoints() override
+ {
+ return toDataRef(m_Breakpoints.data(), m_Breakpoints.size());
+ }
+
+ void ClearAllBreakpoints() override
+ {
+ SendMessage(SClearAllBreakpoints());
+ m_Breakpoints.clear();
+ }
+
+ // break at the *end* of the microstep so you can see the data of the microstep.
+ void SetBreakOnMicrostep(bool inBreak) override
+ {
+ if (m_BreakOnMicrostep != inBreak) {
+ m_BreakOnMicrostep = inBreak;
+ SendMessage(SBreakOnMicrostep(inBreak));
+ }
+ }
+ bool IsBreakOnMicrostep() const override { return m_BreakOnMicrostep; }
+
+ NVConstDataRef<STableEntry> TableToList(const TDataModelTable &inTable) const
+ {
+ m_TempEntries.resize(inTable.size());
+ QT3DSU32 idx = 0;
+ for (TDataModelTable::const_iterator iter = inTable.begin(), end = inTable.end();
+ iter != end; ++iter, ++idx)
+ m_TempEntries[idx] = STableEntry(iter->first, iter->second);
+ return toConstDataRef(m_TempEntries.data(), m_TempEntries.size());
+ }
+
+ NVConstDataRef<STableEntry> GetTableValues(SDatamodelTable *inTable) const override
+ {
+ TTableMap::const_iterator iter = m_DatamodelValues.find(inTable);
+ if (iter != m_DatamodelValues.end())
+ return TableToList(iter->second);
+ return NVConstDataRef<STableEntry>();
+ }
+
+ NVConstDataRef<TDebugStr> GetPreviousConfiguration() const override
+ {
+ return toConstDataRef(m_PreviousConfiguration.data(), m_PreviousConfiguration.size());
+ }
+ NVConstDataRef<TDebugStr> GetConfiguration() const override
+ {
+ return toConstDataRef(m_ConfigurationList.data(), m_ConfigurationList.size());
+ }
+ const TDebugStr &GetMicrostepEvent() const override { return m_MicrostepEvent; }
+ NVConstDataRef<STransitionId> GetMicrostepTransitions() const override
+ {
+ return toConstDataRef(m_MicrostepData.m_Transitions.data(),
+ m_MicrostepData.m_Transitions.size());
+ }
+ NVConstDataRef<TDebugStr> GetMicrostepEnterStates() const override
+ {
+ return toConstDataRef(m_MicrostepData.m_EnterStates.data(),
+ m_MicrostepData.m_EnterStates.size());
+ }
+ NVConstDataRef<TDebugStr> GetMicrostepExitStates() const override
+ {
+ return toConstDataRef(m_MicrostepData.m_ExitStates.data(),
+ m_MicrostepData.m_ExitStates.size());
+ }
+ void Continue() override
+ {
+ SendMessage(SContinue());
+ m_IsBroken = false;
+ m_BrokenBreakpoint = Option<SBreakpoint>();
+ }
+
+ void Disconnect() override
+ {
+ ClearAllBreakpoints();
+ SetBreakOnMicrostep(false);
+ if (m_IsBroken == true)
+ Continue();
+
+ SendMessage(SDisconnect());
+ }
+
+ void BreakpointHit(const SBreakpoint &inBreakpoint) override
+ {
+ m_BrokenBreakpoint = inBreakpoint;
+ m_IsBroken = true;
+ m_Listener.OnInterpreterBroken(*this, inBreakpoint);
+ }
+
+ void OnEventQueued(const SEventQueued &inMsg) override
+ {
+ m_Listener.OnEventQueued(inMsg.m_Event, inMsg.m_Internal);
+ }
+
+ void OnBeginStep(const SBeginStep &) override { m_Listener.OnBeginStep(*this); }
+
+ void OnBeginMicrostep(const SBeginMicrostep &) override
+ {
+ m_MicrostepEvent.clear();
+ m_MicrostepData.m_Transitions.clear();
+ m_MicrostepData.m_EnterStates.clear();
+ m_MicrostepData.m_ExitStates.clear();
+ m_MicrostepHasData = false;
+ m_Listener.OnBeginMicrostep(*this);
+ }
+
+ void OnMicrostepEvent(const SMicrostepEvent &inMsg) override
+ {
+ m_MicrostepEvent = inMsg.m_Event;
+ }
+
+ void OnMicrostepData(const SMicrostepData &inMsg) override
+ {
+ m_MicrostepData = inMsg;
+ m_MicrostepHasData = true;
+ }
+ void OnEndMicrostep(const SEndMicrostep & /*inMsg*/) override
+ {
+ if (m_MicrostepHasData == false && m_MicrostepEvent.empty() == false) {
+ m_Listener.OnEventProcessed(m_MicrostepEvent, false);
+ } else {
+ m_PreviousConfiguration = m_ConfigurationList;
+
+ for (TDebugStrList::const_iterator iter = m_MicrostepData.m_ExitStates.begin(),
+ end = m_MicrostepData.m_ExitStates.end();
+ iter != end; ++iter)
+ m_Configuration.erase(*iter);
+
+ m_Configuration.insert(m_MicrostepData.m_EnterStates.begin(),
+ m_MicrostepData.m_EnterStates.end());
+
+ m_ConfigurationList.clear();
+ m_ConfigurationList.insert(m_ConfigurationList.end(), m_Configuration.begin(),
+ m_Configuration.end());
+ if (m_BreakOnMicrostep) {
+ m_IsBroken = true;
+ m_Listener.OnInterpreterBroken(*this, Option<SBreakpoint>());
+ } else
+ m_Listener.OnMicrostep(*this);
+ }
+ }
+
+ void OnModifyTable(const SModifyTableValues &inValues) override
+ {
+ TTableMap::iterator iter =
+ m_DatamodelValues.insert(eastl::make_pair(inValues.m_TablePtr, TDataModelTable()))
+ .first;
+ for (QT3DSU32 idx = 0, end = inValues.m_Modifications.size(); idx < end; ++idx) {
+ const STableModification &theMod(inValues.m_Modifications[idx]);
+ switch (theMod.m_Type) {
+ case TableModificationType::RemoveKey:
+ iter->second.erase(theMod.m_Entry.m_Key);
+ break;
+ case TableModificationType::SetKey:
+ (iter->second)[theMod.m_Entry.m_Key] = theMod.m_Entry.m_Value;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ m_Listener.OnDatamodelChange(
+ *this, inValues.m_TablePtr,
+ toConstDataRef(inValues.m_Modifications.data(), inValues.m_Modifications.size()));
+ }
+
+ bool IsBroken() const override { return m_IsBroken; }
+};
+}
+
+IDebuggedInterpreter &IDebuggedInterpreter::Create(NVAllocatorCallback &inAlloc,
+ IDebugOutStream &inStream, TEditorPtr inEditor,
+ const TDebugStr &inFilename, QT3DSI32 inStreamId,
+ NVConstDataRef<TDebugStr> inConfiguration,
+ IDebuggerMasterListener &inListener)
+{
+ return *QT3DS_NEW(inAlloc, SDebuggedInterpreter)(inAlloc, inStream, inEditor, inFilename,
+ inStreamId, inConfiguration, inListener);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp
new file mode 100644
index 00000000..5917db60
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp
@@ -0,0 +1,312 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/IOStreams.h"
+
+using namespace qt3ds::state::debugger;
+using namespace qt3ds::state;
+
+namespace {
+
+static MallocAllocator g_MallocAlloc;
+
+struct SDebugger : public IDebugger
+{
+ typedef eastl::vector<NVScopedRefCounted<IStateMachineDebugInterface>> TDebugList;
+ typedef eastl::hash_map<IStateMachineDebugInterface *, QT3DSI32> TMachineIdMap;
+ typedef eastl::hash_map<QT3DSI32, eastl::pair<NVScopedRefCounted<IStateMachineDebugInterface>,
+ NVScopedRefCounted<IStateMachineListener>>>
+ TIdMachineMap;
+ QT3DSI32 mRefCount;
+ QT3DSI32 m_NextStateMachineId;
+ TDebugList m_StateMachineList;
+ TMachineIdMap m_StateMachines;
+ TIdMachineMap m_IdToStateMachines;
+ QT3DSU64 m_StartTime;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ SMessageParser<SDebugger> m_Parser;
+ SMessageSerializer m_MessageSerializer;
+ TDebugStr m_MessageStr;
+
+ SDebugger()
+ : mRefCount(0)
+ , m_NextStateMachineId(1)
+ , m_StartTime(Time::getCurrentCounterValue())
+ {
+ }
+
+ virtual ~SDebugger() { DisconnectFromServer(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(g_MallocAlloc)
+
+ void ConnectStateMachine(IStateMachineDebugInterface &inMachine)
+ {
+ TMachineIdMap::iterator theIter = m_StateMachines.find(&inMachine);
+ if (theIter != m_StateMachines.end())
+ return;
+ QT3DSI32 theId(m_NextStateMachineId);
+ ++m_NextStateMachineId;
+ IStateMachineListener *theListener =
+ &IStateMachineListener::Create(g_MallocAlloc, theId, *m_OutStream.mPtr, m_StartTime,
+ *this, inMachine.GetScriptContext());
+ inMachine.SetStateMachineListener(theListener);
+ m_StateMachines.insert(eastl::make_pair(&inMachine, theId));
+ m_IdToStateMachines.insert(
+ eastl::make_pair(theId, eastl::make_pair(&inMachine, theListener)));
+ }
+
+ void OnServerConnected(IDebugOutStream &inStream) override
+ {
+ m_OutStream = inStream;
+ inStream.SetListener(this);
+ m_MessageSerializer.Serialize(0, SInitialization(), m_StartTime);
+ m_OutStream->Write(m_MessageSerializer.ToRawMessage());
+ for (QT3DSU32 idx = 0, end = m_StateMachineList.size(); idx < end; ++idx)
+ ConnectStateMachine(*m_StateMachineList[idx]);
+ }
+
+ void Connect(IStateMachineDebugInterface &inMachine) override
+ {
+ TDebugList::iterator theFind =
+ eastl::find(m_StateMachineList.begin(), m_StateMachineList.end(), &inMachine);
+ if (theFind == m_StateMachineList.end()) {
+ m_StateMachineList.push_back(&inMachine);
+ if (m_OutStream)
+ ConnectStateMachine(inMachine);
+ }
+ }
+
+ void DisconnectStateMachine(IStateMachineDebugInterface &inMachine)
+ {
+ TMachineIdMap::iterator theIter = m_StateMachines.find(&inMachine);
+ if (theIter == m_StateMachines.end())
+ return;
+
+ QT3DSI32 theId = theIter->second;
+ m_MessageSerializer.Serialize(theId, SDisconnect(),
+ Time::getCurrentCounterValue() - m_StartTime);
+ m_OutStream->Write(m_MessageSerializer.ToRawMessage());
+ inMachine.SetStateMachineListener(NULL);
+ m_StateMachines.erase(&inMachine);
+ m_IdToStateMachines.erase(theId);
+ }
+ void Disconnect(IStateMachineDebugInterface &inMachine) override
+ {
+ TDebugList::iterator theFind =
+ eastl::find(m_StateMachineList.begin(), m_StateMachineList.end(), &inMachine);
+ if (theFind != m_StateMachineList.end()) {
+ DisconnectStateMachine(inMachine);
+ m_StateMachineList.erase(theFind);
+ }
+ }
+
+ void DisconnectFromServer() override
+ {
+ if (m_OutStream) {
+ m_MessageSerializer.Serialize(-1, SDisconnect(),
+ Time::getCurrentCounterValue() - m_StartTime);
+ m_OutStream->Write(m_MessageSerializer.ToRawMessage());
+ }
+ m_StateMachines.clear();
+ m_IdToStateMachines.clear();
+ m_StateMachineList.clear();
+ m_OutStream = NULL;
+ }
+
+ void OnMessageReceived(const SDebugStreamMessage &msg) override
+ {
+ if (msg.m_Data.size())
+ m_Parser.Parse(msg.m_Data, *this);
+ }
+
+// Set of ignored messages
+#define IGNORE_DEBUG_MESSAGE_TYPE(tname) \
+ void OnMessage(QT3DSI32, QT3DSU64, const S##tname &) { QT3DS_ASSERT(false); }
+
+ // this are outgoing messages; we shouldn't be receiving them.
+ IGNORE_DEBUG_MESSAGE_TYPE(Initialization)
+ IGNORE_DEBUG_MESSAGE_TYPE(Connect)
+ IGNORE_DEBUG_MESSAGE_TYPE(BreakpointHit)
+ IGNORE_DEBUG_MESSAGE_TYPE(DebugLog)
+ IGNORE_DEBUG_MESSAGE_TYPE(EventQueued)
+ IGNORE_DEBUG_MESSAGE_TYPE(BeginStep)
+ IGNORE_DEBUG_MESSAGE_TYPE(BeginMicrostep)
+ IGNORE_DEBUG_MESSAGE_TYPE(MicrostepEvent)
+ IGNORE_DEBUG_MESSAGE_TYPE(MicrostepData)
+ IGNORE_DEBUG_MESSAGE_TYPE(EndMicrostep)
+ IGNORE_DEBUG_MESSAGE_TYPE(ModifyTableValues)
+
+ IStateMachineDebugInterface *GetStateMachine(QT3DSI32 inStreamId)
+ {
+ TIdMachineMap::iterator theIter = m_IdToStateMachines.find(inStreamId);
+ if (theIter == m_IdToStateMachines.end())
+ return NULL;
+ return theIter->second.first.mPtr;
+ }
+
+ IStateMachineListener *GetStateMachineListener(QT3DSI32 inStreamId)
+ {
+ TIdMachineMap::iterator theIter = m_IdToStateMachines.find(inStreamId);
+ if (theIter == m_IdToStateMachines.end())
+ return NULL;
+ return theIter->second.second.mPtr;
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SSetBreakpoint &inBp)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->SetBreakpoint(inBp.m_Breakpoint);
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SClearBreakpoint &inBp)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->ClearBreakpoint(inBp.m_Breakpoint);
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SClearAllBreakpoints &)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->ClearAllBreakpoints();
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SBreakOnMicrostep &inCmd)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->SetBreakOnMicrostep(inCmd.m_Value);
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SContinue &)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->Continue();
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SDisconnect &)
+ {
+ if (sid != 0 && sid != (QT3DSI32)QT3DS_MAX_U32) {
+ IStateMachineDebugInterface *iface = GetStateMachine(sid);
+ if (iface)
+ Disconnect(*iface);
+ } else {
+ if (sid == (QT3DSI32)QT3DS_MAX_U32)
+ DisconnectFromServer();
+ }
+ }
+
+ void error(const char8_t *, const char8_t *) {}
+};
+
+static inline NVConstDataRef<QT3DSU8> ToRef(const TDebugStr &str)
+{
+ return NVConstDataRef<QT3DSU8>((const QT3DSU8 *)str.begin(), str.size());
+}
+
+bool TestBasicParseRobustness()
+{
+ typedef STestMessageHandler<SInitialization> THandlerType;
+ typedef SMessageParser<THandlerType> TParserType;
+ // Just ensure things don't crash.
+ SInitialization testType;
+ THandlerType theHandler(2, 5, testType);
+ TDebugStr theStr;
+ TParserType theParser;
+ theParser.Parse(ToRef(theStr), theHandler);
+
+ theStr = "Initialization";
+ theParser.Parse(ToRef(theStr), theHandler);
+
+ theStr = "Initialization ";
+ theParser.Parse(ToRef(theStr), theHandler);
+ return true;
+}
+
+template <typename TDataType>
+bool TestSerialization(const TDataType &testType, SMessageSerializer &ioSerializer,
+ QT3DSU64 inTimestamp)
+{
+ typedef STestMessageHandler<TDataType> THandlerType;
+ typedef SMessageParser<THandlerType> TParserType;
+
+ ioSerializer.Serialize(5, testType, inTimestamp);
+ THandlerType theHandler(5, inTimestamp, testType);
+ TParserType theParser;
+ theParser.Parse(ioSerializer.ToRawMessage(), theHandler);
+ return theHandler.m_Result;
+}
+}
+
+IDebugger &IDebugger::CreateDebugger()
+{
+ return *QT3DS_NEW(g_MallocAlloc, SDebugger)();
+}
+
+bool CDebuggerTests::TestStreamProtocol()
+{
+ // for each message type, fill on some values, serialize to string and back and see what happens
+ TDebugStrList theList;
+ theList.push_back("one");
+ theList.push_back("two");
+ TTransitionIdList theTransList;
+ theTransList.push_back(STransitionId("one", 2));
+ theTransList.push_back(STransitionId("two", -1));
+ SBreakpoint bp1(SStateEnterBreakpoint("one"));
+ SBreakpoint bp2(SStateExitBreakpoint("two"));
+ SBreakpoint bp3(STransitionId("three", 4));
+ SMessageSerializer theSerializer;
+ bool retval = TestBasicParseRobustness();
+ // breaking out the large && statement to make testing easier.
+ retval = retval && TestSerialization(SInitialization(), theSerializer, 5);
+ retval = retval && TestSerialization(SConnect("abe.scxml", "<one>hey you</one>", theList),
+ theSerializer, 6);
+ retval = retval
+ && TestSerialization(SDebugLog(TDebugStr("Hey joe, where you goin'")), theSerializer, 7);
+ retval = retval && TestSerialization(SBeginMicrostep(), theSerializer, 8);
+ retval = retval && TestSerialization(SMicrostepEvent("evt1", true), theSerializer, 9);
+ retval = retval
+ && TestSerialization(SMicrostepData(theTransList, theList, theList), theSerializer, 9);
+ retval = retval && TestSerialization(SEndMicrostep(), theSerializer, 10);
+ retval = retval && TestSerialization(SEventQueued("evt1", false), theSerializer, 11);
+ retval = retval && TestSerialization(SEventQueued("evt1", false), theSerializer, 12);
+ retval = retval && TestSerialization(SSetBreakpoint(bp1), theSerializer, 13);
+ retval = retval && TestSerialization(SSetBreakpoint(bp2), theSerializer, 14);
+ retval = retval && TestSerialization(SSetBreakpoint(bp3), theSerializer, 15);
+ retval = retval && TestSerialization(SClearBreakpoint(bp1), theSerializer, 16);
+ retval = retval && TestSerialization(SClearAllBreakpoints(), theSerializer, 17);
+ return retval;
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h
new file mode 100644
index 00000000..00814e42
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUGGER_H
+#define QT3DS_STATE_DEBUGGER_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/StringTable.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSStateEditor.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ using namespace editor;
+
+ typedef TEditorStr TDebugStr;
+ struct SDebugValue;
+ struct SBreakpoint;
+ struct SNil;
+ struct SDatamodelTable;
+ struct SDatamodelUserData;
+ struct SDatamodelFunction;
+ struct SDatamodelCFunction;
+ struct SDatamodelThread;
+
+ typedef eastl::vector<SBreakpoint> TBreakpointList;
+
+ struct BreakpointTypes
+ {
+ enum Enum {
+ UnknownBreakpointType = 0,
+ StateEnter,
+ StateExit,
+ Transition,
+ };
+ };
+
+ struct SDebugStreamMessage
+ {
+ // Timestamp needs to be converted using the SourceConversion on the protocol socket.
+ QT3DSU64 m_Timestamp;
+ NVConstDataRef<QT3DSU8> m_Data;
+ SDebugStreamMessage()
+ : m_Timestamp(0)
+ {
+ }
+ SDebugStreamMessage(QT3DSU64 ts, NVConstDataRef<QT3DSU8> msg)
+ : m_Timestamp(ts)
+ , m_Data(msg)
+ {
+ }
+ };
+
+ // Note the stream listeners are not ref counted.
+ // it is your job to ensure it lasts as long as debug stream.
+ class IDebugStreamListener
+ {
+ protected:
+ virtual ~IDebugStreamListener() {}
+ public:
+ // Async message interface. Clients must provide this. Only called on main thread
+ virtual void OnMessageReceived(const SDebugStreamMessage &inMessage) = 0;
+ };
+
+ class IDebugOutStream : public NVRefCounted, public IOutStream
+ {
+ protected:
+ virtual ~IDebugOutStream() {}
+ public:
+ virtual void SetListener(IDebugStreamListener *listener) = 0;
+ virtual IDebugStreamListener *GetListener() = 0;
+ // return true if the stream is still connected to an output, false if
+ // something caused the connection to fail or close.
+ virtual SDebugStreamMessage WaitForNextMessage() = 0;
+ virtual bool Connected() = 0;
+ };
+
+ struct STransitionId
+ {
+ TDebugStr m_StateId;
+ // Index in file order of the transition.
+ //-1 means initial or history::transition, depending on if the state is
+ // a normal state or history state.
+ QT3DSI32 m_TransitionIndex;
+ STransitionId(const TDebugStr &inId = TDebugStr(), QT3DSI32 inIdx = -2)
+ : m_StateId(inId)
+ , m_TransitionIndex(inIdx)
+ {
+ }
+ bool operator==(const STransitionId &inOther) const
+ {
+ return m_StateId == inOther.m_StateId
+ && m_TransitionIndex == inOther.m_TransitionIndex;
+ }
+ };
+
+ typedef eastl::vector<STransitionId> TTransitionIdList;
+
+ ///////////////////////////////////////////////////////////////////////
+ // Client (runtime) side types
+ ///////////////////////////////////////////////////////////////////////
+
+ struct DatamodelValueTypes
+ {
+ enum Enum {
+ UnknownType = 0,
+ Nil,
+ Boolean,
+ Number,
+ String,
+ Table,
+ UserData,
+ Function,
+ CFunction,
+ Thread,
+ };
+ };
+
+ struct SDatamodelValue;
+
+ struct SDatamodelTable;
+ struct SDatamodelUserData;
+ struct SDatamodelFunction;
+ struct SDatamodelCFunction;
+ struct SDatamodelThread;
+ struct STableEntry;
+
+ class IScriptStateListener
+ {
+ protected:
+ virtual ~IScriptStateListener() {}
+ public:
+ virtual void SetKey(void *inTable, const TDebugStr &inStr,
+ const SDatamodelValue &inValue) = 0;
+ virtual void RemoveKey(void *inTable, const TDebugStr &inStr) = 0;
+ };
+
+ class IDebugger;
+ // Information coming from the state machine
+ class IStateMachineListener : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateMachineListener() {}
+ public:
+ // Events coming from the interpreter and sent over the interface.
+ virtual void OnConnect(const TDebugStr &SCXMLFilename, const TDebugStr &SCXMLFileData,
+ NVConstDataRef<TDebugStr> inConfiguration) = 0;
+
+ virtual void Log(const TDebugStr &inStr) = 0;
+ virtual void EventQueued(const TDebugStr &inEventName, bool inInternal) = 0;
+ virtual void BeginStep() = 0;
+ virtual void BeginMicroStep() = 0;
+ virtual void SetEvent(const TDebugStr &inEventName, bool inInternal) = 0;
+ virtual void SetTransitionSet(NVConstDataRef<STransitionId> inTransitions) = 0;
+ virtual void SetExitSet(NVConstDataRef<TDebugStr> inSet) = 0;
+ virtual void SetEnterSet(NVConstDataRef<TDebugStr> inSet) = 0;
+ // Log statements run through the debugger as well.
+ virtual void EndMicroStep() = 0;
+ virtual void EndStep() = 0;
+
+ // So far the breakpoints have all been things that are internal to the
+ // state machine.
+ virtual void SetBreakOnMicrostep(bool inEnableStep) = 0;
+ virtual void SetBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual void ClearBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual void ClearAllBreakpoints() = 0;
+
+ // Called internally.
+ virtual void Continue() = 0;
+ virtual void OnExternalBreak() = 0;
+
+ static IStateMachineListener &Create(NVAllocatorCallback &inAlloc, QT3DSU32 inStreamId,
+ IDebugOutStream &inOutStr, QT3DSU64 inStartTime,
+ IDebugger &inDebugger,
+ IScriptContext &inScriptContext);
+ };
+
+ // Information coming from the network stream will call these iterfaces
+ class IStateMachineDebugInterface : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateMachineDebugInterface() {}
+ public:
+ virtual void SetStateMachineListener(IStateMachineListener *inListener) = 0;
+ virtual void OnExternalBreak() = 0;
+ virtual IScriptContext &GetScriptContext() = 0;
+ };
+
+ // The debugger element is the object that sits in the debug process interpreter and sends
+ // information.
+ // There should be only one of these per network connection.
+ class IDebugger : public IDebugStreamListener, public NVRefCounted
+ {
+ protected:
+ virtual ~IDebugger() {}
+ public:
+ virtual void OnServerConnected(IDebugOutStream &inStream) = 0;
+ virtual void Connect(IStateMachineDebugInterface &inMachine) = 0;
+ virtual void Disconnect(IStateMachineDebugInterface &inMachine) = 0;
+ // Release any references to any state machines and to the output stream
+ virtual void DisconnectFromServer() = 0;
+
+ static IDebugger &CreateDebugger();
+ };
+
+ ///////////////////////////////////////////////////////////////////////
+ // Server (Architect) side types
+ ///////////////////////////////////////////////////////////////////////
+
+ struct DebugValueTypes
+ {
+ enum Enum { NoDebugValue = 0, String, StringList, TransitionIdList };
+ };
+
+ typedef eastl::vector<TDebugStr> TDebugStrList;
+
+ struct STableModification;
+
+ class IDebuggedInterpreter;
+
+ class IDebuggerMasterListener : public NVRefCounted
+ {
+ protected:
+ virtual ~IDebuggerMasterListener() {}
+ public:
+ virtual void OnInterpreterConnected(IDebuggedInterpreter &inInterpreter) = 0;
+ // If no breakpoint then the interpreter was broken on microstep.
+ virtual void OnEventQueued(const TDebugStr &inEvent, bool inInternal) = 0;
+ virtual void OnInterpreterBroken(IDebuggedInterpreter &inInterpreter,
+ const Option<SBreakpoint> &inBreakpoint) = 0;
+ // Event processed but no microstep was taken.
+ virtual void OnEventProcessed(const TDebugStr &inEvent, bool inInternal) = 0;
+ virtual void OnBeginStep(IDebuggedInterpreter &inInterpreter) = 0;
+ virtual void OnBeginMicrostep(IDebuggedInterpreter &inInterpreter) = 0;
+ // Event processed and microstep was taken.
+ virtual void OnMicrostep(IDebuggedInterpreter &inInterpreter) = 0;
+ // if inmodifications is empty the table needs to be replaced.
+ virtual void OnDatamodelChange(IDebuggedInterpreter &inInterpreter,
+ SDatamodelTable *inTable,
+ NVConstDataRef<STableModification> inModifications) = 0;
+ virtual void OnInterpreterDisconnected(IDebuggedInterpreter &inInterpreter) = 0;
+ virtual void OnLog(IDebuggedInterpreter *inInterpreter, const TDebugStr &inMessage) = 0;
+ };
+
+ struct STableEntry;
+
+ typedef eastl::vector<STableEntry> TTableEntryList;
+
+ // defined in UICStateDebuggerProtocol.h"
+ struct SBeginStep;
+ struct SBeginMicrostep;
+ struct SMicrostepEvent;
+ struct SMicrostepData;
+ struct SEndMicrostep;
+ struct SEventQueued;
+ struct SModifyTableValues;
+ struct SDatamodelTable;
+
+ // Represents the data stream coming from a single interpreter
+ class IDebuggedInterpreter : public NVRefCounted
+ {
+ protected:
+ virtual ~IDebuggedInterpreter() {}
+ public:
+ /* Playback interface, leaving until basic functionality is working.
+ virtual QT3DSU32 GetStepCount() = 0;
+ virtual void SetCurrentStep( QT3DSU32 inStep ) = 0;
+ virtual QT3DSU32 GetMicrostepCount() = 0;
+ virtual QT3DSU32 SetCurrentMicroStep( QT3DSU32 inStep ) = 0;
+ */
+
+ // Get the editor that represents the state graph for this debugged interpreter.
+ virtual TEditorPtr GetEditor() = 0;
+ virtual TDebugStr GetFilename() const = 0;
+
+ // Used when connecting to a live session.
+ virtual void SetBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual void ClearBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual NVConstDataRef<SBreakpoint> GetBreakpoints() = 0;
+ virtual void ClearAllBreakpoints() = 0;
+ // break at the *end* of the microstep so you can see the data of the microstep.
+ virtual void SetBreakOnMicrostep(bool inBreak) = 0;
+ virtual bool IsBreakOnMicrostep() const = 0;
+
+ // Return values not valid after *next* call.
+ // Null means the root.
+ virtual NVConstDataRef<STableEntry>
+ GetTableValues(SDatamodelTable *inTable = NULL) const = 0;
+
+ virtual NVConstDataRef<TDebugStr> GetPreviousConfiguration() const = 0;
+ virtual NVConstDataRef<TDebugStr> GetConfiguration() const = 0;
+ virtual const TDebugStr &GetMicrostepEvent() const = 0;
+ virtual NVConstDataRef<STransitionId> GetMicrostepTransitions() const = 0;
+ virtual NVConstDataRef<TDebugStr> GetMicrostepEnterStates() const = 0;
+ virtual NVConstDataRef<TDebugStr> GetMicrostepExitStates() const = 0;
+
+ virtual bool IsBroken() const = 0;
+ virtual void Continue() = 0;
+ // Get the set of changed properties since the last time you asked. Obviously this is
+ // assuming there
+ // is only one 'you' asking.
+ virtual void Disconnect() = 0;
+
+ // Semi-advanced debugger functionality that we don't need for version 1.
+
+ // virtual void SetPropertyValue( const char8_t* inName, const SDebugValue& inValue ) =
+ // 0;
+ // Set a property value on the running state machine.
+ // virtual void SetPropertyValue( TObjPtr inEditorObj, const char8_t* inName, const
+ // SValueOpt& inValue ) = 0;
+
+ // Eval code in the current state machine context. Works when connected live, wouldn't
+ // work if you
+ // were not connected live.
+ // virtual TDebugStr EvalCode( const TDebugStr& inStr ) = 0;
+
+ // Information coming from the stream, clients should not call these functions
+ virtual void BreakpointHit(const SBreakpoint &inBreakpoint) = 0;
+ virtual void OnEventQueued(const SEventQueued &inMsg) = 0;
+ virtual void OnBeginStep(const SBeginStep &inMsg) = 0;
+ virtual void OnBeginMicrostep(const SBeginMicrostep &inMsg) = 0;
+ virtual void OnMicrostepEvent(const SMicrostepEvent &inMsg) = 0;
+ virtual void OnMicrostepData(const SMicrostepData &inMsg) = 0;
+ virtual void OnEndMicrostep(const SEndMicrostep &inMsg) = 0;
+ virtual void OnModifyTable(const SModifyTableValues &inValues) = 0;
+
+ static IDebuggedInterpreter &Create(NVAllocatorCallback &inAlloc,
+ IDebugOutStream &inStream, TEditorPtr inEditor,
+ const TDebugStr &inFilename, QT3DSI32 inStreamId,
+ NVConstDataRef<TDebugStr> inConfiguration,
+ IDebuggerMasterListener &inListener);
+ };
+
+ // Debugger sites in the master process (Architect process) and controls one or more
+ // debuggers
+ // for one or more state machines running in the debug process.
+ class IDebuggerMaster : public IDebugStreamListener, public NVRefCounted
+ {
+ protected:
+ virtual ~IDebuggerMaster() {}
+ public:
+ virtual void Disconnect() = 0;
+
+ static IDebuggerMaster &CreateMaster(IDebugOutStream &outStream,
+ IDebuggerMasterListener &inListener);
+ };
+
+ class CDebuggerTests
+ {
+ public:
+ static bool TestStreamProtocol();
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp
new file mode 100644
index 00000000..2045f84f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "Qt3DSStateScriptContext.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+// Component sits in the state machine and receives messages from the state machine
+// itself.
+struct SListener : public IStateMachineListener, public IScriptStateListener
+{
+ typedef eastl::hash_map<SDatamodelTable *, SModifyTableValues> TTableModificationMap;
+ NVAllocatorCallback &m_Allocator;
+ QT3DSI32 mRefCount;
+ QT3DSU32 m_StreamId;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ SMessageSerializer m_Serializer;
+ TDebugStrList m_DebugStrList;
+ TDebugStrList m_DebugStrAltList;
+ TTransitionIdList m_TransitionIdList;
+ QT3DSU64 m_StartTime;
+ bool m_BreakOnMicrostep;
+ bool m_Blocking;
+ IDebugger &m_Debugger;
+ TDebugStr m_BreakStr;
+ NVScopedRefCounted<IScriptContext> m_ScriptContext;
+ TTableModificationMap m_TableModifications;
+ TBreakpointList m_Breakpoints;
+ SMicrostepData m_MicrostepData;
+ bool m_MicrostepBeginSent;
+ bool m_StepSent;
+
+ SListener(NVAllocatorCallback &alloc, QT3DSU32 inStreamId, IDebugOutStream &inOutStr,
+ QT3DSU64 inStartTime, IDebugger &inDebugger, IScriptContext &inScriptContext)
+ : m_Allocator(alloc)
+ , mRefCount(0)
+ , m_StreamId(inStreamId)
+ , m_OutStream(inOutStr)
+ , m_StartTime(inStartTime)
+ , m_BreakOnMicrostep(false)
+ , m_Blocking(false)
+ , m_Debugger(inDebugger)
+ , m_ScriptContext(inScriptContext)
+ , m_MicrostepBeginSent(false)
+ , m_StepSent(false)
+ {
+ }
+
+ virtual ~SListener() { m_Blocking = false; }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ template <typename TDataType>
+ static const eastl::vector<TDataType> &RefToList(NVConstDataRef<TDataType> inRef,
+ eastl::vector<TDataType> &outList)
+ {
+ outList.assign(inRef.begin(), inRef.end());
+ return outList;
+ }
+
+ template <typename TDataType>
+ void Serialize(const TDataType &dtype, QT3DSU64 inTime)
+ {
+ m_Serializer.Serialize(m_StreamId, dtype, inTime);
+ m_OutStream->Write(m_Serializer.ToRawMessage());
+ }
+
+ template <typename TDataType>
+ void Serialize(const TDataType &dtype)
+ {
+ Serialize(dtype, CurrentTime());
+ }
+
+ void OnConnect(const TDebugStr &SCXMLFilename, const TDebugStr &SCXMLFileData,
+ NVConstDataRef<TDebugStr> inConfiguration) override
+ {
+ Serialize(
+ SConnect(SCXMLFilename, SCXMLFileData, RefToList(inConfiguration, m_DebugStrList)));
+ DumpScriptState();
+ }
+
+ QT3DSU64 CurrentTime() { return Time::getCurrentCounterValue() - m_StartTime; }
+
+ void DumpScriptState()
+ {
+ // Run through the global state and output any keys that have changed.
+ m_TableModifications.clear();
+ m_ScriptContext->DumpState(*this);
+ for (TTableModificationMap::iterator iter = m_TableModifications.begin(),
+ end = m_TableModifications.end();
+ iter != end; ++iter) {
+ Serialize(iter->second);
+ }
+ m_TableModifications.clear();
+ }
+ virtual void OnBreakpointHit(const SBreakpoint &inBreakpointId)
+ {
+ DumpScriptState();
+ Serialize(SBreakpointHit(inBreakpointId));
+ Break();
+ }
+
+ void Log(const TDebugStr &inStr) override { Serialize(SDebugLog(inStr)); }
+
+ void SetBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ m_Breakpoints.push_back(inBreakpoint);
+ }
+
+ void ClearBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ TBreakpointList::iterator removeIter =
+ eastl::remove(m_Breakpoints.begin(), m_Breakpoints.end(), inBreakpoint);
+ if (removeIter != m_Breakpoints.end())
+ m_Breakpoints.erase(removeIter, m_Breakpoints.end());
+ }
+ void ClearAllBreakpoints() override { m_Breakpoints.clear(); }
+ void EventQueued(const TDebugStr &inEventName, bool inInternal) override
+ {
+ Serialize(SEventQueued(inEventName, inInternal));
+ }
+ void SendMicrostepBegin()
+ {
+ if (m_MicrostepBeginSent == false) {
+ if (m_StepSent == false) {
+ m_StepSent = true;
+ Serialize(SBeginStep());
+ }
+
+ m_MicrostepBeginSent = true;
+ Serialize(SBeginMicrostep());
+ }
+ }
+
+ void BeginStep() override { m_StepSent = false; }
+
+ void BeginMicroStep() override
+ {
+ m_MicrostepBeginSent = false;
+ m_MicrostepData.m_Transitions.clear();
+ m_MicrostepData.m_EnterStates.clear();
+ m_MicrostepData.m_ExitStates.clear();
+ }
+
+ void SetEvent(const TDebugStr &inEvent, bool inInternal) override
+ {
+ if (!inEvent.empty()) {
+ SendMicrostepBegin();
+ Serialize(SMicrostepEvent(inEvent, inInternal));
+ }
+ }
+ void SetTransitionSet(NVConstDataRef<STransitionId> inTransitions) override
+ {
+ SendMicrostepBegin();
+ RefToList(inTransitions, m_MicrostepData.m_Transitions);
+ }
+ void SetExitSet(NVConstDataRef<TDebugStr> inSet) override
+ {
+ SendMicrostepBegin();
+ RefToList(inSet, m_MicrostepData.m_ExitStates);
+ }
+ void SetEnterSet(NVConstDataRef<TDebugStr> inSet) override
+ {
+ SendMicrostepBegin();
+ RefToList(inSet, m_MicrostepData.m_EnterStates);
+ }
+
+ static inline bool FindInList(const TDebugStr &inStr, const TDebugStrList &inStrList)
+ {
+ if (eastl::find(inStrList.begin(), inStrList.end(), inStr) != inStrList.end())
+ return true;
+ return false;
+ }
+
+ static inline bool FindInList(const STransitionId &inStr, const TTransitionIdList &inStrList)
+ {
+ if (eastl::find(inStrList.begin(), inStrList.end(), inStr) != inStrList.end())
+ return true;
+ return false;
+ }
+
+ const SBreakpoint *FindHitBreakpoint()
+ {
+ // Now check for breakpoints.
+ for (QT3DSU32 breakIdx = 0, breakEnd = m_Breakpoints.size(); breakIdx < breakEnd; ++breakIdx) {
+ const SBreakpoint &theBreakpoint = m_Breakpoints[breakIdx];
+ switch (theBreakpoint.getType()) {
+ case BreakpointTypes::StateEnter:
+ if (FindInList(theBreakpoint.getData<SStateEnterBreakpoint>().m_ObjectId,
+ m_MicrostepData.m_EnterStates))
+ return &theBreakpoint;
+ case BreakpointTypes::StateExit:
+ if (FindInList(theBreakpoint.getData<SStateExitBreakpoint>().m_ObjectId,
+ m_MicrostepData.m_ExitStates))
+ return &theBreakpoint;
+ case BreakpointTypes::Transition:
+ if (FindInList(theBreakpoint.getData<STransitionId>(),
+ m_MicrostepData.m_Transitions))
+ return &theBreakpoint;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ return NULL;
+ }
+
+ // Log statements run through the debugger as well.
+ void EndMicroStep() override
+ {
+ DumpScriptState();
+ if (m_MicrostepBeginSent) {
+ if (m_MicrostepData.m_Transitions.empty() == false)
+ Serialize(m_MicrostepData);
+
+ Serialize(SEndMicrostep());
+ const SBreakpoint *theHitBreakpoint = FindHitBreakpoint();
+ if (theHitBreakpoint)
+ OnBreakpointHit(*theHitBreakpoint);
+ else if (m_BreakOnMicrostep)
+ Break();
+ }
+ }
+
+ void EndStep() override { m_StepSent = false; }
+
+ void SetBreakOnMicrostep(bool inEnableStep) override { m_BreakOnMicrostep = inEnableStep; }
+
+ virtual void Break()
+ {
+ if (!m_Blocking) {
+ // We may get disconnected *while* we are breaking.
+ // We need to ensure we don't get nuked while we are breaking.
+ NVScopedRefCounted<SListener> tempVar(this);
+ m_Blocking = true;
+ while (m_Blocking) {
+ SDebugStreamMessage msg = m_OutStream->WaitForNextMessage();
+ // Some out streams will have a reference to the debugger and some will not.
+ // If they do, then we do not have to hand our string to the debugger, we can assume
+ // the underlying implementation will have done that. If not, then we need to
+ // pass the message to the debugger.
+ if (m_Blocking && msg.m_Data.size())
+ m_Debugger.OnMessageReceived(msg);
+
+ if (!m_OutStream->Connected()) {
+ m_Blocking = false;
+ m_Debugger.DisconnectFromServer();
+ }
+ }
+ }
+ }
+
+ void Continue() override { m_Blocking = false; }
+
+ void OnExternalBreak() override { DumpScriptState(); }
+
+ // IScriptStateListener
+ void SetKey(void *inTable, const TDebugStr &inStr, const SDatamodelValue &inValue) override
+ {
+ SDatamodelTable *theTable((SDatamodelTable *)inTable);
+ SModifyTableValues &theModification =
+ m_TableModifications.insert(eastl::make_pair(theTable, SModifyTableValues(theTable)))
+ .first->second;
+ theModification.m_Modifications.push_back(
+ STableModification(STableEntry(inStr, inValue), TableModificationType::SetKey));
+ }
+
+ void RemoveKey(void *inTable, const TDebugStr &inStr) override
+ {
+ SDatamodelTable *theTable((SDatamodelTable *)inTable);
+ SModifyTableValues &theModification =
+ m_TableModifications.insert(eastl::make_pair(theTable, SModifyTableValues(theTable)))
+ .first->second;
+ theModification.m_Modifications.push_back(
+ STableModification(STableEntry(inStr), TableModificationType::RemoveKey));
+ }
+};
+}
+
+IStateMachineListener &IStateMachineListener::Create(NVAllocatorCallback &inAlloc, QT3DSU32 inStreamId,
+ IDebugOutStream &inOutStr, QT3DSU64 inStartTime,
+ IDebugger &inDebugger,
+ IScriptContext &inScriptContext)
+{
+ return *QT3DS_NEW(inAlloc, SListener)(inAlloc, inStreamId, inOutStr, inStartTime, inDebugger,
+ inScriptContext);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h
new file mode 100644
index 00000000..03230a61
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h
@@ -0,0 +1,925 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUGGER_PROTOCOL_H
+#define QT3DS_STATE_DEBUGGER_PROTOCOL_H
+#include "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "foundation/StringConversion.h"
+#include "foundation/StringConversionImpl.h"
+#include "foundation/Qt3DSTime.h"
+
+// Stream protocol, regardless of binary vs. text.
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ struct SInitialization
+ {
+ QT3DSI32 m_Version;
+ QT3DSI32 m_Padding;
+ CounterFrequencyToTensOfNanos m_TimerFrequency;
+ static QT3DSI32 GetCurrentVersion() { return 1; }
+ SInitialization()
+ : m_Version(GetCurrentVersion())
+ , m_Padding(0)
+ , m_TimerFrequency(Time::sCounterFreq)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Version);
+ inVisitor.visit(m_TimerFrequency.mNumerator);
+ inVisitor.visit(m_TimerFrequency.mDenominator);
+ }
+ bool operator==(const SInitialization &inOther) const
+ {
+ return m_Version == inOther.m_Version
+ && m_TimerFrequency.mNumerator == inOther.m_TimerFrequency.mNumerator
+ && m_TimerFrequency.mDenominator == inOther.m_TimerFrequency.mDenominator;
+ }
+ };
+
+ // Listener interface. For on connect, we send the current timestamp for that state
+ // machine.
+ struct SConnect
+ {
+ TDebugStr m_Filename;
+ TDebugStr m_SCXMLData; // scxml file contents
+ TDebugStrList m_Configuration;
+ SConnect() {}
+ SConnect(const TDebugStr &fn, const TDebugStr &xmlData, const TDebugStrList &config)
+ : m_Filename(fn)
+ , m_SCXMLData(xmlData)
+ , m_Configuration(config)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.randomText(m_Filename);
+ inVisitor.randomText(m_SCXMLData);
+ inVisitor.visitIdList(m_Configuration);
+ }
+ bool operator==(const SConnect &inOther) const
+ {
+ return m_Filename == inOther.m_Filename && m_SCXMLData == inOther.m_SCXMLData
+ && m_Configuration == inOther.m_Configuration;
+ }
+ };
+
+ struct SBreakpointHit
+ {
+ SBreakpoint m_Breakpoint;
+ SBreakpointHit() {}
+ SBreakpointHit(const SBreakpoint &bp)
+ : m_Breakpoint(bp)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Breakpoint);
+ }
+ bool operator==(const SBreakpointHit &inOther) const
+ {
+ return m_Breakpoint == inOther.m_Breakpoint;
+ }
+ };
+
+ struct SDebugLog
+ {
+ TDebugStr m_Message;
+ SDebugLog() {}
+ SDebugLog(const TDebugStr &msg)
+ : m_Message(msg)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.randomText(m_Message);
+ }
+ bool operator==(const SDebugLog &inOther) const
+ {
+ return m_Message == inOther.m_Message;
+ }
+ };
+
+ struct SModifyTableValues
+ {
+ SDatamodelTable *m_TablePtr;
+ TTableModificationList m_Modifications;
+
+ SModifyTableValues(SDatamodelTable *inTable = NULL,
+ const TTableModificationList &inList = TTableModificationList())
+ : m_TablePtr(inTable)
+ , m_Modifications(inList)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_TablePtr);
+ inVisitor.visit(m_Modifications);
+ }
+
+ bool operator==(const SModifyTableValues &inOther) const
+ {
+ return m_TablePtr == inOther.m_TablePtr
+ && m_Modifications == inOther.m_Modifications;
+ }
+ };
+
+ struct SBeginStep
+ {
+ SBeginStep() {}
+ template <typename TVisitor>
+ void Visit(TVisitor &)
+ {
+ }
+
+ bool operator==(const SBeginStep &) const { return true; }
+ };
+
+ struct SBeginMicrostep
+ {
+ SBeginMicrostep() {}
+ template <typename TVisitor>
+ void Visit(TVisitor &)
+ {
+ }
+
+ bool operator==(const SBeginMicrostep &) const { return true; }
+ };
+
+ struct SMicrostepEvent
+ {
+ TDebugStr m_Event;
+ bool m_Internal;
+
+ SMicrostepEvent(const TDebugStr &inEvent = TDebugStr(), bool inIsInternal = false)
+ : m_Event(inEvent)
+ , m_Internal(inIsInternal)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visitId(m_Event);
+ inVisitor.visit(m_Internal);
+ }
+
+ bool operator==(const SMicrostepEvent &inOther) const
+ {
+ return m_Event == inOther.m_Event && m_Internal == inOther.m_Internal;
+ }
+ };
+
+ struct SMicrostepData
+ {
+ TTransitionIdList m_Transitions;
+ TDebugStrList m_ExitStates;
+ TDebugStrList m_EnterStates;
+
+ SMicrostepData() {}
+ SMicrostepData(const TTransitionIdList &inTransitions,
+ const TDebugStrList &inExitStates, const TDebugStrList &inEnterStates)
+ : m_Transitions(inTransitions)
+ , m_ExitStates(inExitStates)
+ , m_EnterStates(inEnterStates)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ // Eventless transitions cause us to need to always write the string length.
+ inVisitor.visit(m_Transitions);
+ inVisitor.visitIdList(m_ExitStates);
+ inVisitor.visitIdList(m_EnterStates);
+ }
+ bool operator==(const SMicrostepData &inOther) const
+ {
+ return m_Transitions == inOther.m_Transitions
+ && m_ExitStates == inOther.m_ExitStates
+ && m_EnterStates == inOther.m_EnterStates;
+ }
+ };
+
+ struct SEndMicrostep
+ {
+ SEndMicrostep() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+
+ bool operator==(const SEndMicrostep &) const { return true; }
+ };
+
+ struct SEventQueued
+ {
+ TDebugStr m_Event;
+ bool m_Internal;
+ SEventQueued()
+ : m_Internal(false)
+ {
+ }
+ SEventQueued(const TDebugStr &evt, bool intern)
+ : m_Event(evt)
+ , m_Internal(intern)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visitEventName(m_Event);
+ inVisitor.visit(m_Internal);
+ }
+ bool operator==(const SEventQueued &inOther) const
+ {
+ return m_Event == inOther.m_Event && m_Internal == inOther.m_Internal;
+ }
+ };
+
+ // Debug interface
+
+ struct SSetBreakpoint
+ {
+ SBreakpoint m_Breakpoint;
+ SSetBreakpoint() {}
+ SSetBreakpoint(const SBreakpoint &bp)
+ : m_Breakpoint(bp)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Breakpoint);
+ }
+ bool operator==(const SSetBreakpoint &inOther) const
+ {
+ return m_Breakpoint == inOther.m_Breakpoint;
+ }
+ };
+
+ struct SClearBreakpoint
+ {
+ SBreakpoint m_Breakpoint;
+ SClearBreakpoint() {}
+ SClearBreakpoint(const SBreakpoint &bp)
+ : m_Breakpoint(bp)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Breakpoint);
+ }
+ bool operator==(const SClearBreakpoint &inOther) const
+ {
+ return m_Breakpoint == inOther.m_Breakpoint;
+ }
+ };
+
+ struct SClearAllBreakpoints
+ {
+ SClearAllBreakpoints() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+ bool operator==(const SClearAllBreakpoints & /*inOther*/) const { return true; }
+ };
+
+ struct SBreakOnMicrostep
+ {
+ bool m_Value;
+ SBreakOnMicrostep(bool val = false)
+ : m_Value(val)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Value);
+ }
+ bool operator==(const SBreakOnMicrostep &inOther) const
+ {
+ return m_Value == inOther.m_Value;
+ }
+ };
+
+ struct SContinue
+ {
+ SContinue() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+ bool operator==(const SContinue & /*inOther*/) const { return true; }
+ };
+
+ struct SDisconnect
+ {
+ SDisconnect() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+ bool operator==(const SDisconnect & /*inOther*/) const { return true; }
+ };
+
+ // Visitors to handle the breakpoint subtypes.
+ // The empty top visitor will force a compile error if there is a breakpoint type we don't
+ // recognize.
+ template <typename TDataType>
+ struct SBreakpointSerializationVisitor
+ {
+ };
+
+ template <>
+ struct SBreakpointSerializationVisitor<SStateEnterBreakpoint>
+ {
+ enum { Type = BreakpointTypes::StateEnter };
+ template <typename TVisitor>
+ static void Visit(TVisitor &inVisitor, SStateEnterBreakpoint &inBp)
+ {
+ inVisitor.visitId(inBp.m_ObjectId);
+ }
+ };
+
+ template <>
+ struct SBreakpointSerializationVisitor<SStateExitBreakpoint>
+ {
+ enum { Type = BreakpointTypes::StateExit };
+ template <typename TVisitor>
+ static void Visit(TVisitor &inVisitor, SStateExitBreakpoint &inBp)
+ {
+ inVisitor.visitId(inBp.m_ObjectId);
+ }
+ };
+
+ template <>
+ struct SBreakpointSerializationVisitor<STransitionId>
+ {
+ enum { Type = BreakpointTypes::Transition };
+ template <typename TVisitor>
+ static void Visit(TVisitor &inVisitor, STransitionId &inBp)
+ {
+ inVisitor.visitId(inBp.m_StateId);
+ inVisitor.visit(inBp.m_TransitionIndex);
+ }
+ };
+
+#define ITERATE_BREAKPOINT_TYPES \
+ HANDLE_BREAKPOINT_TYPE(StateEnter, SStateEnterBreakpoint) \
+ HANDLE_BREAKPOINT_TYPE(StateExit, SStateExitBreakpoint) \
+ HANDLE_BREAKPOINT_TYPE(Transition, STransitionId)
+
+#define ITERATE_DEBUG_MESSAGE_TYPES \
+ HANDLE_DEBUG_MESSAGE_TYPE(Initialization) \
+ HANDLE_DEBUG_MESSAGE_TYPE(Connect) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BreakpointHit) \
+ HANDLE_DEBUG_MESSAGE_TYPE(DebugLog) \
+ HANDLE_DEBUG_MESSAGE_TYPE(ModifyTableValues) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BeginStep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BeginMicrostep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(MicrostepEvent) \
+ HANDLE_DEBUG_MESSAGE_TYPE(MicrostepData) \
+ HANDLE_DEBUG_MESSAGE_TYPE(EndMicrostep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(EventQueued) \
+ HANDLE_DEBUG_MESSAGE_TYPE(SetBreakpoint) \
+ HANDLE_DEBUG_MESSAGE_TYPE(ClearBreakpoint) \
+ HANDLE_DEBUG_MESSAGE_TYPE(ClearAllBreakpoints) \
+ HANDLE_DEBUG_MESSAGE_TYPE(Disconnect) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BreakOnMicrostep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(Continue)
+
+ template <typename TStructType>
+ struct STypeToName
+ {
+ };
+
+#define HANDLE_BREAKPOINT_TYPE(enumType, structType) \
+ template <> \
+ struct STypeToName<structType> \
+ { \
+ static const char8_t *GetName() { return #enumType; } \
+ };
+#define HANDLE_DEBUG_MESSAGE_TYPE(msgType) \
+ template <> \
+ struct STypeToName<S##msgType> \
+ { \
+ static const char8_t *GetName() { return #msgType; } \
+ };
+
+ ITERATE_BREAKPOINT_TYPES
+ ITERATE_DEBUG_MESSAGE_TYPES
+
+#undef HANDLE_BREAKPOINT_TYPE
+#undef HANDLE_DEBUG_MESSAGE_TYPE
+
+ template <typename TMessageHandler>
+ struct SMessageParser
+ {
+ TDebugStr m_MessageHeaderStr;
+ TDebugStr m_TimestampStr;
+ TDebugStr m_ParseStr;
+ TDebugStr m_BreakpointTypeStr;
+ TDebugStr m_TempStr[4];
+ TMessageHandler *m_CurrentHandler;
+ bool m_Valid;
+ static void Trim(TDebugStr &inStr)
+ {
+ if (inStr.empty())
+ return;
+ eastl::string::size_type nonSpace = inStr.find_first_not_of(' ');
+ eastl::string::size_type lastNonSpace = inStr.find_last_not_of(' ');
+ if (nonSpace == TDebugStr::npos || lastNonSpace == TDebugStr::npos) {
+ inStr.clear();
+ return;
+ }
+
+ eastl::string::size_type msgLen = lastNonSpace - nonSpace;
+ inStr.erase(inStr.begin(), inStr.begin() + nonSpace);
+ inStr.resize(msgLen + 1);
+ }
+
+ void TrimForward(eastl::string::size_type startPos)
+ {
+ eastl::string::size_type nextPos = m_ParseStr.find_first_not_of(' ', startPos);
+ if (nextPos != TDebugStr::npos)
+ m_ParseStr.erase(m_ParseStr.begin(), m_ParseStr.begin() + nextPos);
+ else
+ m_ParseStr.clear();
+ }
+
+ void TrimForward()
+ {
+ eastl::string::size_type nextSpace = m_ParseStr.find_first_of(' ');
+ TrimForward(nextSpace);
+ }
+
+ void visitId(TDebugStr &inStr)
+ {
+ eastl::string::size_type nextSpace = m_ParseStr.find_first_of(' ');
+ if (nextSpace == TDebugStr::npos)
+ nextSpace = m_ParseStr.size();
+ inStr.assign(m_ParseStr.begin(), m_ParseStr.begin() + nextSpace);
+ TrimForward(nextSpace);
+ }
+ void visit(TDebugStr &inStr) { randomText(inStr); }
+ void visit(QT3DSI32 &inData)
+ {
+ StringConversion<QT3DSI32>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+ void visit(QT3DSU32 &inData)
+ {
+ StringConversion<QT3DSU32>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+
+ void visit(QT3DSU64 &inData)
+ {
+ StringConversion<QT3DSU64>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+
+ void visit(float &inData)
+ {
+ StringConversion<QT3DSF32>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+
+ void visit(bool &inData)
+ {
+ if (CompareNChars(m_ParseStr.c_str(), "True", 4) == 0)
+ inData = true;
+ else
+ inData = false;
+
+ TrimForward();
+ }
+
+ template <typename TPtrType>
+ void visitOpaquePtr(TPtrType *&inPtr)
+ {
+ QT3DSU64 temp;
+ visit(temp);
+ inPtr = reinterpret_cast<TPtrType *>(static_cast<size_t>(temp));
+ }
+
+ void visit(SDatamodelTable *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelUserData *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelFunction *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelCFunction *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelThread *&inData) { visitOpaquePtr(inData); }
+ void visit(SNil &) {}
+
+ void visit(SDatamodelValue &inValue)
+ {
+ visitId(m_TempStr[0]);
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, dtype) \
+ if (AreEqual(m_TempStr[0].c_str(), #name)) { \
+ dtype theData; \
+ visit(theData); \
+ inValue = theData; \
+ } else
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ void visit(TTableModificationList &inModifications)
+ {
+ QT3DSU32 numMods = 0;
+ visit(numMods);
+ inModifications.resize(numMods);
+ for (QT3DSU32 idx = 0, end = inModifications.size(); idx < end; ++idx) {
+ STableModification &theModification(inModifications[idx]);
+ visitId(m_TempStr[0]);
+ if (AreEqual(m_TempStr[0].c_str(), "SetKey"))
+ theModification.m_Type = TableModificationType::SetKey;
+ else if (AreEqual(m_TempStr[0].c_str(), "RemoveKey"))
+ theModification.m_Type = TableModificationType::RemoveKey;
+ else {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+ visit(theModification.m_Entry.m_Key);
+ if (theModification.m_Type == TableModificationType::SetKey)
+ visit(theModification.m_Entry.m_Value);
+ }
+ }
+
+ void visit(TTableEntryList &inEntryList)
+ {
+ QT3DSU32 numMods = 0;
+ visit(numMods);
+ inEntryList.resize(numMods);
+ for (QT3DSU32 idx = 0, end = inEntryList.size(); idx < end; ++idx) {
+ STableEntry &theEntry(inEntryList[idx]);
+ visit(theEntry.m_Key);
+ visit(theEntry.m_Value);
+ }
+ }
+
+ void randomText(TDebugStr &outStr)
+ {
+ QT3DSU32 strLen = 0;
+ visit(strLen);
+ outStr.assign(m_ParseStr.begin(), m_ParseStr.begin() + strLen);
+ TrimForward(strLen);
+ }
+
+ void visitIdList(TDebugStrList &inList)
+ {
+ QT3DSU32 numItems = 0;
+ visit(numItems);
+ inList.resize(numItems);
+ for (QT3DSU32 idx = 0, end = numItems; idx < end; ++idx)
+ visitId(inList[idx]);
+ }
+
+ void visit(TTransitionIdList &inList)
+ {
+ QT3DSU32 numItems = 0;
+ visit(numItems);
+ inList.resize(numItems);
+ for (QT3DSU32 idx = 0, end = numItems; idx < end; ++idx)
+ inList[idx] = BreakpointTransition();
+ }
+
+ void visitEventName(TDebugStr &outStr) { visitId(outStr); }
+
+ SStateEnterBreakpoint BreakpointStateEnter()
+ {
+ SStateEnterBreakpoint retval;
+ SBreakpointSerializationVisitor<SStateEnterBreakpoint>::Visit(*this, retval);
+ return retval;
+ }
+
+ SStateExitBreakpoint BreakpointStateExit()
+ {
+ SStateExitBreakpoint retval;
+ SBreakpointSerializationVisitor<SStateExitBreakpoint>::Visit(*this, retval);
+ return retval;
+ }
+
+ STransitionId BreakpointTransition()
+ {
+ STransitionId retval;
+ SBreakpointSerializationVisitor<STransitionId>::Visit(*this, retval);
+ return retval;
+ }
+
+ void visit(SBreakpoint &breakpoint)
+ {
+ visitId(m_BreakpointTypeStr);
+#define HANDLE_BREAKPOINT_TYPE(enumType, bpType) \
+ if (AreEqual(STypeToName<bpType>::GetName(), m_BreakpointTypeStr.c_str())) { \
+ breakpoint = Breakpoint##enumType(); \
+ } else
+ ITERATE_BREAKPOINT_TYPES
+#undef HANDLE_BREAKPOINT_TYPE
+ // else is defined in the iterate breakpoints.
+ {
+ m_Valid = false;
+ m_CurrentHandler->error("Unrecognized breakpoint type: ",
+ m_BreakpointTypeStr.c_str());
+ }
+ }
+
+ template <typename TDataType>
+ void DoParseT(TDebugStr &inStr, QT3DSU64 inTimestamp, QT3DSI32 streamId,
+ TMessageHandler &inHandler)
+ {
+ TDataType theType;
+ m_ParseStr = inStr;
+ m_Valid = true;
+ m_CurrentHandler = &inHandler;
+ theType.Visit(*this);
+ if (m_Valid)
+ inHandler.OnMessage(streamId, inTimestamp, theType);
+ }
+
+ // destructive parsing.
+ void Parse(NVConstDataRef<QT3DSU8> data, TMessageHandler &inHandler)
+ {
+ m_ParseStr.assign((const char *)data.begin(), (const char *)data.end());
+ Trim(m_ParseStr);
+ size_t spacePos = m_ParseStr.find_first_of(' ');
+ if (spacePos == TDebugStr::npos) {
+ m_ParseStr.assign((const char *)data.begin(), (const char *)data.end());
+ inHandler.error("Failed to parse message: ", m_ParseStr.c_str());
+ return;
+ }
+ qt3ds::QT3DSI32 streamId = 0;
+ visitId(m_MessageHeaderStr);
+ visit(streamId);
+ qt3ds::QT3DSU64 timestamp = 0;
+ visit(timestamp);
+#define HANDLE_DEBUG_MESSAGE_TYPE(name) \
+ if (AreEqual(STypeToName<S##name>::GetName(), m_MessageHeaderStr.c_str())) { \
+ DoParseT<S##name>(m_ParseStr, timestamp, streamId, inHandler); \
+ } else
+ ITERATE_DEBUG_MESSAGE_TYPES
+#undef HANDLE_DEBUG_MESSAGE_TYPE
+ {
+ inHandler.error("Failed to parse message type: ", m_MessageHeaderStr.c_str());
+ }
+ }
+ };
+
+ struct SMessageSerializer
+ {
+ TDebugStr m_Message;
+ void appendSpace() { m_Message.append(1, ' '); }
+
+ void visitId(const TDebugStr &inStr)
+ {
+ m_Message.append(inStr);
+ appendSpace();
+ }
+ template <typename TDataType>
+ void rawTypeToStr(const TDataType &inType)
+ {
+ char8_t buf[256] = { 0 };
+ StringConversion<TDataType>().ToStr(inType, toDataRef(buf, 256));
+ m_Message.append(buf);
+ appendSpace();
+ }
+ void visit(QT3DSI32 inData) { rawTypeToStr(inData); }
+ void visit(QT3DSU32 inData) { rawTypeToStr(inData); }
+
+ void visit(QT3DSU64 inData) { rawTypeToStr(inData); }
+
+ void visit(bool inData) { rawTypeToStr(inData); }
+
+ void visit(const TDebugStr &inStr) { randomText(inStr); }
+
+ void visit(const SNil &) {}
+
+ void visit(const TTransitionIdList &inData)
+ {
+ QT3DSU32 len = (QT3DSU32)inData.size();
+ visit(len);
+ for (QT3DSU32 idx = 0; idx < len; ++idx)
+ Breakpoint(inData[idx]);
+ }
+
+ void randomText(const TDebugStr &outStr)
+ {
+ QT3DSU32 len = (QT3DSU32)outStr.size();
+ visit(len);
+ if (len)
+ m_Message.append(outStr);
+ appendSpace();
+ }
+
+ void visitIdList(const TDebugStrList &inList)
+ {
+ QT3DSU32 numItems = (QT3DSU32)inList.size();
+ visit(numItems);
+ for (QT3DSU32 idx = 0; idx < numItems; ++idx)
+ visitId(inList[idx]);
+ }
+
+ void visitEventName(const TDebugStr &outStr) { visitId(outStr); }
+ void visit(float inValue) { rawTypeToStr(inValue); }
+ template <typename TPtrType>
+ void visitOpaquePtr(const TPtrType *inPtr)
+ {
+ size_t ptrValue = reinterpret_cast<size_t>(inPtr);
+ QT3DSU64 fullValue = static_cast<QT3DSU64>(ptrValue);
+ visit(fullValue);
+ }
+
+ void visit(SDatamodelTable *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelUserData *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelFunction *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelCFunction *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelThread *inData) { visitOpaquePtr(inData); }
+
+ void visit(const SDatamodelValue &inValue)
+ {
+ switch (inValue.getType()) {
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, dtype) \
+ case DatamodelValueTypes::name: { \
+ m_Message.append(#name); \
+ appendSpace(); \
+ visit(inValue.getData<dtype>()); \
+ break; \
+ }
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ void visit(const TTableModificationList &inModifications)
+ {
+ QT3DSU32 numItems = (QT3DSU32)inModifications.size();
+ visit(numItems);
+ for (QT3DSU32 idx = 0, end = inModifications.size(); idx < end; ++idx) {
+ const STableModification &theMod(inModifications[idx]);
+ switch (theMod.m_Type) {
+ case TableModificationType::SetKey: {
+ m_Message.append("SetKey");
+ appendSpace();
+ visit(theMod.m_Entry.m_Key);
+ visit(theMod.m_Entry.m_Value);
+ break;
+ }
+ case TableModificationType::RemoveKey: {
+ m_Message.append("RemoveKey");
+ appendSpace();
+ visit(theMod.m_Entry.m_Key);
+ break;
+ }
+ default:
+ QT3DS_ASSERT(false);
+ m_Message.append("badvalue");
+ appendSpace();
+ continue;
+ }
+ }
+ }
+ void visit(TTableEntryList &inEntries)
+ {
+ QT3DSU32 numItems = (QT3DSU32)inEntries.size();
+ visit(numItems);
+ for (QT3DSU32 idx = 0, end = inEntries.size(); idx < end; ++idx) {
+ const STableEntry &theEntry(inEntries[idx]);
+ visit(theEntry.m_Key);
+ visit(theEntry.m_Value);
+ }
+ }
+
+ template <typename TBreakpointType>
+ void Breakpoint(const TBreakpointType &inBp)
+ {
+ SBreakpointSerializationVisitor<TBreakpointType>::Visit(
+ *this, const_cast<TBreakpointType &>(inBp));
+ }
+
+ void visit(const SBreakpoint &inBp)
+ {
+ switch (inBp.getType()) {
+#define HANDLE_BREAKPOINT_TYPE(enumName, typeName) \
+ case BreakpointTypes::enumName: \
+ m_Message.append(STypeToName<typeName>::GetName()); \
+ appendSpace(); \
+ Breakpoint(inBp.getData<typeName>()); \
+ break;
+ ITERATE_BREAKPOINT_TYPES
+#undef HANDLE_BREAKPOINT_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ template <typename TMsgType>
+ void Serialize(QT3DSI32 inStreamId, const TMsgType &inMsg, QT3DSU64 inTimestamp)
+ {
+ m_Message.clear();
+ m_Message.append(STypeToName<TMsgType>::GetName());
+ appendSpace();
+ visit(inStreamId);
+ visit(inTimestamp);
+ const_cast<TMsgType &>(inMsg).Visit(*this);
+ }
+
+ NVConstDataRef<QT3DSU8> ToRawMessage()
+ {
+ if (m_Message.size() == 0)
+ return NVConstDataRef<QT3DSU8>();
+ return NVConstDataRef<QT3DSU8>(reinterpret_cast<const QT3DSU8 *>(m_Message.c_str()),
+ (QT3DSU32)m_Message.size());
+ }
+ };
+
+ template <typename TLhs, typename TRhs>
+ struct SMsgComparer
+ {
+ static bool Compare(const TLhs &, const TRhs &) { return false; }
+ };
+#define HANDLE_DEBUG_MESSAGE_TYPE(typeName) \
+ template <> \
+ struct SMsgComparer<S##typeName, S##typeName> \
+ { \
+ static bool Compare(const S##typeName &lhs, const S##typeName &rhs) { return lhs == rhs; } \
+ };
+ ITERATE_DEBUG_MESSAGE_TYPES;
+#undef HANDLE_DEBUG_MESSAGE_TYPE
+
+ template <typename TMessageType>
+ struct STestMessageHandler
+ {
+ bool m_Result;
+ QT3DSU64 m_Timestamp;
+ QT3DSU32 m_StreamId;
+ const TMessageType &m_ExpectedResult;
+
+ STestMessageHandler(QT3DSU32 sid, QT3DSU64 ts, const TMessageType &res)
+ : m_Result(false)
+ , m_Timestamp(ts)
+ , m_StreamId(sid)
+ , m_ExpectedResult(res)
+ {
+ }
+
+ template <typename TCrapType>
+ void OnMessage(QT3DSU32 sid, QT3DSU64 ts, const TCrapType &msgType)
+ {
+ if (ts == m_Timestamp && m_StreamId == sid)
+ m_Result =
+ SMsgComparer<TCrapType, TMessageType>::Compare(msgType, m_ExpectedResult);
+ }
+
+ void error(const char8_t *, const char8_t *) {}
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h
new file mode 100644
index 00000000..340f46cb
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h
@@ -0,0 +1,540 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUGGER_VALUES_H
+#define QT3DS_STATE_DEBUGGER_VALUES_H
+#pragma once
+#include "Qt3DSStateDebugger.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ using namespace qt3ds::state::editor;
+
+ // Force compile error if unsupported datatype requested
+ template <typename TDataType>
+ struct SDebugValueTypeMap
+ {
+ };
+
+ template <>
+ struct SDebugValueTypeMap<TDebugStr>
+ {
+ static DebugValueTypes::Enum GetType() { return DebugValueTypes::String; }
+ };
+ template <>
+ struct SDebugValueTypeMap<TDebugStrList>
+ {
+ static DebugValueTypes::Enum GetType() { return DebugValueTypes::StringList; }
+ };
+ template <>
+ struct SDebugValueTypeMap<TTransitionIdList>
+ {
+ static DebugValueTypes::Enum GetType() { return DebugValueTypes::TransitionIdList; }
+ };
+
+ struct SDebugValueUnionTraits
+ {
+ typedef DebugValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(TDebugStrList),
+ };
+
+ static TIdType getNoDataId() { return DebugValueTypes::NoDebugValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SDebugValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case DebugValueTypes::String:
+ return inVisitor(*NVUnionCast<TDebugStr *>(inData));
+ case DebugValueTypes::StringList:
+ return inVisitor(*NVUnionCast<TDebugStrList *>(inData));
+ case DebugValueTypes::TransitionIdList:
+ return inVisitor(*NVUnionCast<TTransitionIdList *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case DebugValueTypes::NoDebugValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case DebugValueTypes::String:
+ return inVisitor(*NVUnionCast<const TDebugStr *>(inData));
+ case DebugValueTypes::StringList:
+ return inVisitor(*NVUnionCast<const TDebugStrList *>(inData));
+ case DebugValueTypes::TransitionIdList:
+ return inVisitor(*NVUnionCast<const TTransitionIdList *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case DebugValueTypes::NoDebugValue:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SDebugValueUnionTraits,
+ SDebugValueUnionTraits::
+ TBufferSize>,
+ SDebugValueUnionTraits::TBufferSize>
+ TDebugValueUnionType;
+
+ struct SDebugValue : public TDebugValueUnionType
+ {
+ SDebugValue() {}
+
+ SDebugValue(const SDebugValue &inOther)
+ : TDebugValueUnionType(static_cast<const TDebugValueUnionType &>(inOther))
+ {
+ }
+
+ SDebugValue(const char8_t *inOther)
+ : TDebugValueUnionType(TDebugStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SDebugValue(const TDataType &inDt)
+ : TDebugValueUnionType(inDt)
+ {
+ }
+
+ SDebugValue &operator=(const SDebugValue &inOther)
+ {
+ TDebugValueUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SDebugValue &inOther) const
+ {
+ return TDebugValueUnionType::operator==(inOther);
+ }
+ bool operator!=(const SDebugValue &inOther) const
+ {
+ return TDebugValueUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == DebugValueTypes::NoDebugValue; }
+ };
+
+ struct SDebugValueOpt : public Option<SDebugValue>
+ {
+ SDebugValueOpt(const SDebugValueOpt &inOther)
+ : Option<SDebugValue>(inOther)
+ {
+ }
+ SDebugValueOpt(const Empty &)
+ : Option<SDebugValue>()
+ {
+ }
+ SDebugValueOpt() {}
+ template <typename TDataType>
+ SDebugValueOpt(const TDataType &inOther)
+ : Option<SDebugValue>(SDebugValue(inOther))
+ {
+ }
+ SDebugValueOpt &operator=(const SDebugValueOpt &inOther)
+ {
+ Option<SDebugValue>::operator=(inOther);
+ return *this;
+ }
+ };
+
+ // Breakpoint subtypes
+
+ struct SStateEnterBreakpoint
+ {
+ TDebugStr m_ObjectId;
+ SStateEnterBreakpoint(const TDebugStr &inObj = TDebugStr())
+ : m_ObjectId(inObj)
+ {
+ }
+ bool operator==(const SStateEnterBreakpoint &inOther) const
+ {
+ return m_ObjectId == inOther.m_ObjectId;
+ }
+ };
+
+ struct SStateExitBreakpoint
+ {
+ TDebugStr m_ObjectId;
+ SStateExitBreakpoint(const TDebugStr &inObj = TDebugStr())
+ : m_ObjectId(inObj)
+ {
+ }
+ bool operator==(const SStateExitBreakpoint &inOther) const
+ {
+ return m_ObjectId == inOther.m_ObjectId;
+ }
+ };
+
+ template <typename TDataType>
+ struct SBreakpointTypeMap
+ {
+ static BreakpointTypes::Enum GetType()
+ {
+ return BreakpointTypes::UnknownBreakpointType;
+ }
+ };
+
+ template <>
+ struct SBreakpointTypeMap<SStateEnterBreakpoint>
+ {
+ static BreakpointTypes::Enum GetType() { return BreakpointTypes::StateEnter; }
+ };
+ template <>
+ struct SBreakpointTypeMap<SStateExitBreakpoint>
+ {
+ static BreakpointTypes::Enum GetType() { return BreakpointTypes::StateExit; }
+ };
+ template <>
+ struct SBreakpointTypeMap<STransitionId>
+ {
+ static BreakpointTypes::Enum GetType() { return BreakpointTypes::Transition; }
+ };
+
+ struct SBreakpointUnionTraits
+ {
+ typedef BreakpointTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(STransitionId),
+ };
+
+ static TIdType getNoDataId() { return BreakpointTypes::UnknownBreakpointType; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SBreakpointTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case BreakpointTypes::StateEnter:
+ return inVisitor(*NVUnionCast<SStateEnterBreakpoint *>(inData));
+ case BreakpointTypes::StateExit:
+ return inVisitor(*NVUnionCast<SStateExitBreakpoint *>(inData));
+ case BreakpointTypes::Transition:
+ return inVisitor(*NVUnionCast<STransitionId *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case BreakpointTypes::UnknownBreakpointType:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case BreakpointTypes::StateEnter:
+ return inVisitor(*NVUnionCast<const SStateEnterBreakpoint *>(inData));
+ case BreakpointTypes::StateExit:
+ return inVisitor(*NVUnionCast<const SStateExitBreakpoint *>(inData));
+ case BreakpointTypes::Transition:
+ return inVisitor(*NVUnionCast<const STransitionId *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case BreakpointTypes::UnknownBreakpointType:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SBreakpointUnionTraits,
+ SBreakpointUnionTraits::
+ TBufferSize>,
+ SBreakpointUnionTraits::TBufferSize>
+ TBreakpointUnionType;
+
+ struct SBreakpoint : public TBreakpointUnionType
+ {
+ SBreakpoint() {}
+
+ SBreakpoint(const SBreakpoint &inOther)
+ : TBreakpointUnionType(static_cast<const TBreakpointUnionType &>(inOther))
+ {
+ }
+
+ SBreakpoint(const char8_t *inOther)
+ : TBreakpointUnionType(TEditorStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SBreakpoint(const TDataType &inDt)
+ : TBreakpointUnionType(inDt)
+ {
+ }
+
+ SBreakpoint &operator=(const SBreakpoint &inOther)
+ {
+ TBreakpointUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SBreakpoint &inOther) const
+ {
+ return TBreakpointUnionType::operator==(inOther);
+ }
+ bool operator!=(const SBreakpoint &inOther) const
+ {
+ return TBreakpointUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == SBreakpointUnionTraits::getNoDataId(); }
+ };
+
+ struct SNil
+ {
+ bool operator==(const SNil &) const { return true; }
+ };
+
+#define ITERATE_DATAMODEL_VALUE_TYPES \
+ HANDLE_DATAMODEL_VALUE_TYPE(Number, float) \
+ HANDLE_DATAMODEL_VALUE_TYPE(String, TDebugStr) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Nil, SNil) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Boolean, bool) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Table, SDatamodelTable *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(UserData, SDatamodelUserData *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Function, SDatamodelFunction *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(CFunction, SDatamodelCFunction *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Thread, SDatamodelThread *)
+
+ template <typename TDataType>
+ struct SDatamodelValueTypeMap
+ {
+ };
+
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, type) \
+ template <> \
+ struct SDatamodelValueTypeMap<type> \
+ { \
+ static DatamodelValueTypes::Enum GetType() { return DatamodelValueTypes::name; } \
+ };
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+
+ struct SDatamodelValueUnionTraits
+ {
+ typedef DatamodelValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(TDebugStr),
+ };
+
+ static TIdType getNoDataId() { return DatamodelValueTypes::UnknownType; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SDatamodelValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, type) \
+ case DatamodelValueTypes::name: \
+ return inVisitor(*NVUnionCast<type *>(inData));
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ case DatamodelValueTypes::UnknownType:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, type) \
+ case DatamodelValueTypes::name: \
+ return inVisitor(*NVUnionCast<const type *>(inData));
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ case DatamodelValueTypes::UnknownType:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SDatamodelValueUnionTraits,
+ SDatamodelValueUnionTraits::
+ TBufferSize>,
+ SDatamodelValueUnionTraits::TBufferSize>
+ TDatamodelValueUnionType;
+
+ struct SDatamodelValue : public TDatamodelValueUnionType
+ {
+ SDatamodelValue() {}
+
+ SDatamodelValue(const SDatamodelValue &inOther)
+ : TDatamodelValueUnionType(static_cast<const TDatamodelValueUnionType &>(inOther))
+ {
+ }
+
+ SDatamodelValue(const char8_t *inOther)
+ : TDatamodelValueUnionType(TDebugStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SDatamodelValue(const TDataType &inDt)
+ : TDatamodelValueUnionType(inDt)
+ {
+ }
+
+ SDatamodelValue &operator=(const SDatamodelValue &inOther)
+ {
+ TDatamodelValueUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SDatamodelValue &inOther) const
+ {
+ return TDatamodelValueUnionType::operator==(inOther);
+ }
+ bool operator!=(const SDatamodelValue &inOther) const
+ {
+ return TDatamodelValueUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == SDatamodelValueUnionTraits::getNoDataId(); }
+ };
+
+ struct STableEntry
+ {
+ TDebugStr m_Key;
+ SDatamodelValue m_Value;
+ STableEntry(const TDebugStr &inKey = TDebugStr(),
+ const SDatamodelValue &inValue = SDatamodelValue())
+ : m_Key(inKey)
+ , m_Value(inValue)
+ {
+ }
+
+ bool operator==(const STableEntry &inOther) const
+ {
+ return m_Key == inOther.m_Key && m_Value == inOther.m_Value;
+ }
+ };
+
+ struct TableModificationType
+ {
+ enum Enum {
+ UnknownModification = 0,
+ SetKey = 1,
+ RemoveKey = 2,
+ };
+ };
+
+ struct STableModification
+ {
+ STableEntry m_Entry;
+ TableModificationType::Enum m_Type;
+ STableModification(
+ const STableEntry &inEntry = STableEntry(),
+ TableModificationType::Enum inEnum = TableModificationType::UnknownModification)
+ : m_Entry(inEntry)
+ , m_Type(inEnum)
+ {
+ }
+ bool operator==(const STableModification &inMod) const
+ {
+ return inMod.m_Entry == m_Entry && inMod.m_Type == m_Type;
+ }
+ };
+
+ typedef eastl::vector<STableModification> TTableModificationList;
+ }
+}
+}
+
+#ifndef _INTEGRITYPLATFORM
+namespace qt3ds {
+namespace foundation {
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SNil>
+ {
+ void destruct(qt3ds::state::debugger::SNil &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelTable *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelTable *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelUserData *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelUserData *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelFunction *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelFunction *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelCFunction *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelCFunction *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelThread *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelThread *) {}
+ };
+}
+}
+#endif
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h
new file mode 100644
index 00000000..6edbe366
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_TEST_H
+#define QT3DS_STATE_TEST_H
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSOption.h"
+
+namespace qt3ds {
+namespace state {
+ namespace test {
+
+ struct LogType
+ {
+ enum Enum {
+ Error = 0,
+ Info = 1,
+ };
+ };
+
+ struct STestResults
+ {
+ QT3DSU32 m_TotalTests;
+ QT3DSU32 m_PassingTests;
+ STestResults(QT3DSU32 totalTests = 0, QT3DSU32 testPassed = 0)
+ : m_TotalTests(totalTests)
+ , m_PassingTests(testPassed)
+ {
+ }
+ STestResults &operator+=(const STestResults &inOther)
+ {
+ m_TotalTests += inOther.m_TotalTests;
+ m_PassingTests += inOther.m_PassingTests;
+ return *this;
+ }
+
+ bool Failed() const { return m_TotalTests != m_PassingTests; }
+ bool Passed() const { return !Failed(); }
+ };
+
+ class IDataLogger
+ {
+ protected:
+ virtual ~IDataLogger() {}
+ public:
+ virtual void Log(LogType::Enum inLogType, const char8_t *file, int line,
+ const char8_t *message) = 0;
+ };
+
+ // Run a data test returning a true/false result for pass/fail.
+ class IDataTest
+ {
+ public:
+ static Option<STestResults> RunFile(const char8_t *fname, const char8_t *inRootDir,
+ IDataLogger &inLogger);
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp
new file mode 100644
index 00000000..e15f587d
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp
@@ -0,0 +1,574 @@
+/****************************************************************************
+**
+** 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 "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSSceneGraphDebuggerValue.h"
+#include "Qt3DSSceneGraphDebuggerProtocol.h"
+#include "Qt3DSUIADatamodel.h"
+#include "Qt3DSStateEditorValue.h"
+#include "Qt3DSUIADatamodelValue.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSContainers.h"
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSHash.h" //we need to duplicate the hash attribute calls
+#include "EASTL/set.h"
+
+using namespace qt3ds;
+using namespace qt3ds::state;
+using namespace qt3ds::app;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+struct SAppElemVectorSet
+{
+ nvvector<SAppElement *> m_Elements;
+ eastl::hash_set<SAppElement *, eastl::hash<SAppElement *>, eastl::equal_to<SAppElement *>,
+ ForwardingAllocator>
+ m_Set;
+ SAppElemVectorSet(NVAllocatorCallback &alloc)
+ : m_Elements(alloc, "AppElemVectorSet")
+ , m_Set(ForwardingAllocator(alloc, "AppElemVectorSet"))
+ {
+ }
+
+ void insert(SAppElement *elem)
+ {
+ if (m_Set.insert(elem).second)
+ m_Elements.push_back(elem);
+ }
+
+ void clear()
+ {
+ m_Elements.clear();
+ m_Set.clear();
+ }
+ NVConstDataRef<SAppElement *> ToDataRef() { return m_Elements; }
+};
+
+struct SValueIndex
+{
+ QT3DSU32 m_ValueIndex;
+ QT3DSU32 m_Component;
+ SValueIndex(QT3DSU32 vi, QT3DSU32 component)
+ : m_ValueIndex(vi)
+ , m_Component(component)
+ {
+ }
+};
+
+struct SAppElementEntry
+{
+ Q3DStudio::TAttOrArgList m_AttList;
+ eastl::hash_map<QT3DSI32, SValueIndex> m_HashToIndexMap;
+ eastl::vector<app::SDatamodelValue> m_RuntimeValues;
+};
+
+struct SPresentationEntry
+{
+ CRegisteredString m_Id;
+ eastl::hash_map<CRegisteredString, SAppElement *> m_ElementMap;
+};
+
+Option<float> ValueToFloat(const SSGValue &val)
+{
+ if (val.getType() == SGPropertyValueTypes::Float)
+ return val.getData<float>();
+ return Empty();
+}
+
+Option<QT3DSI32> ValueToInteger(const SSGValue &val)
+{
+ if (val.getType() == SGPropertyValueTypes::I32)
+ return val.getData<QT3DSI32>();
+ return Empty();
+}
+
+Option<CRegisteredString> ValueToString(const SSGValue &val)
+{
+ if (val.getType() == SGPropertyValueTypes::String)
+ return val.getData<CRegisteredString>();
+ return Empty();
+}
+
+template <typename TDtype>
+struct SValueSetter
+{
+};
+
+template <>
+struct SValueSetter<float>
+{
+ static void SetValue(const SSGValue &incoming, float &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<float> val = ValueToFloat(incoming);
+ if (val.hasValue())
+ outgoing = *val;
+ }
+};
+
+template <>
+struct SValueSetter<QT3DSVec2>
+{
+ static void SetValue(const SSGValue &incoming, QT3DSVec2 &outgoing, QT3DSU32 component)
+ {
+ Option<float> val = ValueToFloat(incoming);
+ if (val.hasValue()) {
+ switch (component) {
+ case 0:
+ outgoing.x = *val;
+ break;
+ case 1:
+ outgoing.y = *val;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ }
+};
+
+template <>
+struct SValueSetter<QT3DSVec3>
+{
+ static void SetValue(const SSGValue &incoming, QT3DSVec3 &outgoing, QT3DSU32 component)
+ {
+ Option<float> val = ValueToFloat(incoming);
+ if (val.hasValue()) {
+ switch (component) {
+ case 0:
+ outgoing.x = *val;
+ break;
+ case 1:
+ outgoing.y = *val;
+ break;
+ case 2:
+ outgoing.z = *val;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ }
+};
+
+template <>
+struct SValueSetter<QT3DSI32>
+{
+ static void SetValue(const SSGValue &incoming, QT3DSI32 &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<QT3DSI32> val = ValueToInteger(incoming);
+ if (val.hasValue())
+ outgoing = *val;
+ }
+};
+
+template <>
+struct SValueSetter<eastl::string>
+{
+ static void SetValue(const SSGValue &incoming, eastl::string &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<CRegisteredString> val = ValueToString(incoming);
+ if (val.hasValue())
+ outgoing = eastl::string(val->c_str());
+ }
+};
+
+template <>
+struct SValueSetter<bool>
+{
+ static void SetValue(const SSGValue &incoming, bool &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<QT3DSI32> val = ValueToInteger(incoming);
+ if (val.hasValue())
+ outgoing = (*val) != 0 ? true : false;
+ }
+};
+
+// Element ref. Not sure what to do about this right now
+template <>
+struct SValueSetter<SGuid>
+{
+ static void SetValue(const SSGValue & /*incoming*/, SGuid & /*outgoing*/, QT3DSU32 /*component*/)
+ {
+ }
+};
+
+template <>
+struct SValueSetter<CRegisteredString>
+{
+ static void SetValue(const SSGValue & /*incoming*/, CRegisteredString & /*outgoing*/,
+ QT3DSU32 /*component*/)
+ {
+ QT3DS_ASSERT(false);
+ }
+};
+
+template <>
+struct SValueSetter<SObjectRef>
+{
+ static void SetValue(const SSGValue & /*incoming*/, SObjectRef & /*outgoing*/,
+ QT3DSU32 /*component*/)
+ {
+ }
+};
+
+template <>
+struct SValueSetter<CStringOrInt>
+{
+ static void SetValue(const SSGValue & /*incoming*/, CStringOrInt & /*outgoing*/,
+ QT3DSU32 /*component*/)
+ {
+ }
+};
+
+struct SValueSetterVisitor
+{
+ const SSGValue &m_Incoming;
+ QT3DSU32 m_Component;
+ SValueSetterVisitor &operator=(const SValueSetterVisitor &);
+ SValueSetterVisitor(const SSGValue &i, QT3DSU32 c)
+ : m_Incoming(i)
+ , m_Component(c)
+ {
+ }
+ template <typename TDataType>
+ void operator()(TDataType &dt)
+ {
+ SValueSetter<TDataType>::SetValue(m_Incoming, dt, m_Component);
+ }
+ // object refs can generate this response.
+ void operator()() {}
+};
+
+void SetComponentValue(const SSGValue &incoming, app::SDatamodelValue &outgoing, QT3DSU32 component)
+{
+ SValueSetterVisitor visitor(incoming, component);
+ outgoing.visit<void>(visitor);
+}
+
+struct SArchitectDebuggerImpl : public ISceneGraphArchitectDebugger
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<IDatamodel> m_Datamodel;
+ NVScopedRefCounted<IDebugOutStream> m_Stream;
+ ISceneGraphArchitectDebuggerListener *m_Listener;
+ nvhash_map<QT3DSU64, SAppElement *> m_ElemAppElemMap;
+ nvhash_map<SAppElement *, SAppElementEntry> m_RuntimeValues;
+ SAppElemVectorSet m_DirtySet;
+ eastl::vector<SPresentationEntry> m_Presentations;
+ bool m_Valid;
+ QT3DSI32 mRefCount;
+
+ SArchitectDebuggerImpl(IDatamodel &dm)
+ : m_Foundation(dm.GetFoundation())
+ , m_Datamodel(dm)
+ , m_Stream(NULL)
+ , m_Listener(NULL)
+ , m_ElemAppElemMap(dm.GetFoundation().getAllocator(), "ElemAppMap")
+ , m_RuntimeValues(dm.GetFoundation().getAllocator(), "RuntimeValues")
+ , m_DirtySet(dm.GetFoundation().getAllocator())
+ , m_Valid(true)
+ , mRefCount(0)
+ {
+ }
+
+ void addRef() { atomicIncrement(&mRefCount); }
+ void release()
+ {
+ // ensure the datamodel sticks around until after we are destroyed
+ NVScopedRefCounted<IDatamodel> dm(m_Datamodel);
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ NVDelete(m_Foundation.getAllocator(), this);
+ }
+ }
+
+ virtual IDatamodel &GetDatamodel() { return *m_Datamodel; }
+
+ static QT3DSI32 GetHashValue(const char *name, const char *component, eastl::string &ioWorkspace)
+ {
+ ioWorkspace.assign(nonNull(name));
+ ioWorkspace.append(".");
+ ioWorkspace.append(component);
+ return Q3DStudio::CHash::HashAttribute(ioWorkspace.c_str());
+ }
+
+ void AppendAttribute(const char *name, const char *formalName, ERuntimeDataModelDataType dtype,
+ SAppElementEntry &theEntry)
+ {
+ SAttOrArg newValue;
+ newValue.m_Name = name;
+ newValue.m_FormalName = formalName;
+ newValue.m_DataType = dtype;
+ theEntry.m_AttList.push_back(newValue);
+ theEntry.m_RuntimeValues.push_back(qt3ds::app::SDatamodelValue());
+ }
+
+ void LoadAppElement(SAppElement &elem, SPresentationEntry &entry)
+ {
+ entry.m_ElementMap.insert(eastl::make_pair(
+ m_Datamodel->GetStringTable().RegisterStr(m_Datamodel->GetElementId(elem).c_str()),
+ &elem));
+ SAppElementEntry &theEntry = m_RuntimeValues[&elem];
+ theEntry.m_AttList = m_Datamodel->GetElementAttributes(elem);
+ theEntry.m_RuntimeValues = m_Datamodel->GetElementAttributeInitialValues(elem);
+ AppendAttribute("active", "Active", ERuntimeDataModelDataTypeBool, theEntry);
+
+ if (m_Datamodel->IsComponent(elem)) {
+ AppendAttribute("slide", "(Slide)", ERuntimeDataModelDataTypeLong, theEntry);
+ AppendAttribute("time", "(Time)", ERuntimeDataModelDataTypeLong, theEntry);
+ AppendAttribute("paused", "(Mode)", ERuntimeDataModelDataTypeBool, theEntry);
+ }
+ eastl::string hashTemp;
+ for (QT3DSU32 idx = 0, end = (QT3DSU32)theEntry.m_AttList.size(); idx < end; ++idx) {
+ // Build out the component hash names.
+ const Q3DStudio::SAttOrArg &theProp(theEntry.m_AttList[idx]);
+ switch (theProp.m_DataType) {
+ // one component, one hash, just hash the name
+ default:
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair((QT3DSI32)Q3DStudio::CHash::HashAttribute(theProp.m_Name.c_str()),
+ SValueIndex(idx, 0)));
+ break;
+ case ERuntimeDataModelDataTypeFloat2: {
+ theEntry.m_HashToIndexMap.insert(eastl::make_pair(
+ GetHashValue(theProp.m_Name.c_str(), "x", hashTemp), SValueIndex(idx, 0)));
+ theEntry.m_HashToIndexMap.insert(eastl::make_pair(
+ GetHashValue(theProp.m_Name.c_str(), "y", hashTemp), SValueIndex(idx, 1)));
+ } break;
+ case ERuntimeDataModelDataTypeFloat3: {
+ const char *compNames[3] = { "x", "y", "z" };
+ if (theProp.m_MetaType == ERuntimeAdditionalMetaDataTypeColor) {
+ compNames[0] = "r";
+ compNames[1] = "g";
+ compNames[2] = "b";
+ }
+
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair(GetHashValue(theProp.m_Name.c_str(), compNames[0], hashTemp),
+ SValueIndex(idx, 0)));
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair(GetHashValue(theProp.m_Name.c_str(), compNames[1], hashTemp),
+ SValueIndex(idx, 1)));
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair(GetHashValue(theProp.m_Name.c_str(), compNames[2], hashTemp),
+ SValueIndex(idx, 2)));
+ } break;
+ }
+ // Set a value
+ if (theEntry.m_RuntimeValues[idx].getType() == ERuntimeDataModelDataTypeNone) {
+ switch (theProp.m_DataType) {
+ case ERuntimeDataModelDataTypeFloat:
+ theEntry.m_RuntimeValues[idx] = 0.0f;
+ break;
+ case ERuntimeDataModelDataTypeFloat2:
+ theEntry.m_RuntimeValues[idx] = QT3DSVec2(0, 0);
+ break;
+ case ERuntimeDataModelDataTypeFloat3:
+ theEntry.m_RuntimeValues[idx] = QT3DSVec3(0, 0, 0);
+ break;
+ case ERuntimeDataModelDataTypeLong:
+ theEntry.m_RuntimeValues[idx] = (QT3DSI32)0;
+ break;
+ case ERuntimeDataModelDataTypeString:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ case ERuntimeDataModelDataTypeBool:
+ theEntry.m_RuntimeValues[idx] = false;
+ break;
+ case ERuntimeDataModelDataTypeStringRef:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ // object references are stored as string values.
+ case ERuntimeDataModelDataTypeObjectRef:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ case ERuntimeDataModelDataTypeStringOrInt:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ }
+ }
+ }
+ eastl::vector<SAppElement *> children = m_Datamodel->GetElementChildren(elem);
+ for (size_t childIdx = 0, childEnd = children.size(); childIdx < childEnd; ++childIdx) {
+ LoadAppElement(*children[childIdx], entry);
+ }
+ }
+
+ void LoadDatamodel()
+ {
+ eastl::vector<app::SPresentation> presentations = m_Datamodel->GetPresentations();
+ m_Presentations.clear();
+ m_RuntimeValues.clear();
+ m_ElemAppElemMap.clear();
+ m_Presentations.resize(presentations.size());
+ for (size_t idx = 0, end = presentations.size(); idx < end; ++idx) {
+ app::SPresentation &incoming(presentations[idx]);
+ SPresentationEntry &pres = m_Presentations[idx];
+ pres.m_Id = m_Datamodel->GetStringTable().RegisterStr(incoming.m_Id.c_str());
+ if (incoming.m_Scene)
+ LoadAppElement(*incoming.m_Scene, pres);
+ }
+ }
+
+ SAppElementEntry *GetRuntimeValues(app::SAppElement &elem)
+ {
+ nvhash_map<SAppElement *, SAppElementEntry>::iterator iter = m_RuntimeValues.find(&elem);
+ if (iter != m_RuntimeValues.end())
+ return &iter->second;
+ return NULL;
+ }
+
+ SPresentationEntry *FindPresentation(CRegisteredString &str)
+ {
+ for (size_t idx = 0, end = m_Presentations.size(); idx < end; ++idx)
+ if (m_Presentations[idx].m_Id == str)
+ return &m_Presentations[idx];
+ return NULL;
+ }
+
+ /*External interface for clients*/
+
+ void SetListener(ISceneGraphArchitectDebuggerListener *listener) { m_Listener = listener; }
+
+ Q3DStudio::TAttOrArgList GetElementAttributes(app::SAppElement &elem)
+ {
+ SAppElementEntry *entry = GetRuntimeValues(elem);
+ if (entry)
+ return entry->m_AttList;
+ return Q3DStudio::TAttOrArgList();
+ }
+
+ eastl::vector<app::SDatamodelValue> GetElementAttributeValues(app::SAppElement &elem)
+ {
+ SAppElementEntry *entry = GetRuntimeValues(elem);
+ if (entry)
+ return entry->m_RuntimeValues;
+ return eastl::vector<app::SDatamodelValue>();
+ }
+
+ void OnMessageReceived(const SDebugStreamMessage &inMessage)
+ {
+ if (!m_Valid)
+ return;
+ SSGProtocolReader theReader(inMessage.m_Data, m_Datamodel->GetStringTable());
+ while (theReader.Finished() == false && m_Valid) {
+ SSGProtocolMessageTypes::Enum theMessageType = theReader.MessageType();
+ switch (theMessageType) {
+ case SSGProtocolMessageTypes::Initialization: {
+ QT3DSU32 version = theReader.ReadInitialization();
+ if (version > GetSceneGraphProtocolVersion()) {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_INVALID_OPERATION,
+ "Invalid scene graph debugger protocol version");
+ m_Valid = false;
+ }
+ } break;
+ case SSGProtocolMessageTypes::IdUpdate: {
+ SIdUpdate theUpdate = theReader.ReadIdUpdate();
+ SPresentationEntry *theEntry = FindPresentation(theUpdate.m_PresentationId);
+ if (theEntry) {
+ for (size_t idx = 0, end = theUpdate.m_IdUpdates.size(); idx < end; ++idx) {
+ const SElemMap &elemMap(theUpdate.m_IdUpdates[idx]);
+ eastl::hash_map<CRegisteredString, SAppElement *>::iterator iter =
+ theEntry->m_ElementMap.find(elemMap.m_Id);
+ if (iter != theEntry->m_ElementMap.end())
+ m_ElemAppElemMap[elemMap.m_Elem] = iter->second;
+ else {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_WARN, "Failed to map element");
+ }
+ }
+ }
+ } break;
+ case SSGProtocolMessageTypes::ElemUpdate: {
+ SElemUpdate theUpdate = theReader.ReadElemUpdate();
+ nvhash_map<QT3DSU64, SAppElement *>::iterator ptrToElem =
+ m_ElemAppElemMap.find(theUpdate.m_Elem);
+ if (ptrToElem != m_ElemAppElemMap.end()) {
+ nvhash_map<SAppElement *, SAppElementEntry>::iterator elemToEntry =
+ m_RuntimeValues.find(ptrToElem->second);
+ if (elemToEntry != m_RuntimeValues.end()) {
+ SAppElementEntry &theEntry(elemToEntry->second);
+ m_DirtySet.insert(elemToEntry->first);
+ for (size_t idx = 0, end = theUpdate.m_Updates.size(); idx < end; ++idx) {
+ const SValueUpdate &theValue(theUpdate.m_Updates[idx]);
+ eastl::hash_map<QT3DSI32, SValueIndex>::iterator hashIndex =
+ theEntry.m_HashToIndexMap.find(theValue.m_Hash);
+ if (hashIndex != theEntry.m_HashToIndexMap.end()) {
+ const SValueIndex theIdx = hashIndex->second;
+ SetComponentValue(theValue.m_Value,
+ theEntry.m_RuntimeValues[theIdx.m_ValueIndex],
+ theIdx.m_Component);
+ }
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_WARN, "Failed to map element");
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_WARN, "Failed to map element");
+ }
+ } break;
+ case SSGProtocolMessageTypes::Frame: {
+ NVConstDataRef<SAppElement *> dirtyItems = m_DirtySet.ToDataRef();
+ if (dirtyItems.size() && m_Listener) {
+ m_Listener->OnItemsDirty(dirtyItems);
+ m_DirtySet.clear();
+ }
+ } break;
+ }
+ } // end while
+ }
+
+ void AttachToStream(IDebugOutStream &inStream) { m_Stream = &inStream; }
+
+ void RefreshData(bool inNeedReloadData)
+ {
+ if (inNeedReloadData)
+ m_Datamodel->RefreshFile();
+ LoadDatamodel();
+ }
+};
+}
+
+ISceneGraphArchitectDebugger &
+ISceneGraphArchitectDebugger::Create(qt3ds::app::IDatamodel &inDatamodel)
+{
+ SArchitectDebuggerImpl &retval =
+ *QT3DS_NEW(inDatamodel.GetFoundation().getAllocator(), SArchitectDebuggerImpl)(inDatamodel);
+ retval.LoadDatamodel();
+ return retval;
+}
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp
new file mode 100644
index 00000000..83fe016f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSAtomic.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+MallocAllocator g_MallocAlloc;
+
+struct SDebugStrInStream : public IInStream
+{
+ size_t m_Pos;
+ const TDebugStr &m_Str;
+ SDebugStrInStream(const TDebugStr &inStr)
+ : m_Pos(0)
+ , m_Str(inStr)
+ {
+ }
+ virtual QT3DSU32 Read(NVDataRef<QT3DSU8> data)
+ {
+ size_t available = NVMin((size_t)data.size(), m_Str.size() - m_Pos);
+ if (available) {
+ memCopy(data.begin(), m_Str.data() + m_Pos, (QT3DSU32)available);
+ m_Pos += available;
+ }
+ return (QT3DSU32)available;
+ }
+};
+
+struct SDebuggerMaster : public IDebuggerMaster
+{
+ typedef eastl::hash_map<QT3DSI32, NVScopedRefCounted<IDebuggedInterpreter>> TIdInterpreterMap;
+
+ QT3DSI32 mRefCount;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ TIdInterpreterMap m_Interpreters;
+ IDebuggerMasterListener &m_Listener;
+ SMessageSerializer m_Serializer;
+ SMessageParser<SDebuggerMaster> m_Parser;
+ TDebugStr m_LogStr;
+ TDebugStr m_MessageStr;
+ bool m_Invalid;
+
+ SDebuggerMaster(IDebugOutStream &inStream, IDebuggerMasterListener &listener)
+ : mRefCount(0)
+ , m_OutStream(inStream)
+ , m_Listener(listener)
+ , m_Invalid(false)
+ {
+ }
+
+ virtual ~SDebuggerMaster()
+ {
+ // Disconnect();
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(g_MallocAlloc);
+
+ template <typename TMessageType>
+ void SendMessage(QT3DSI32 inStreamId, const TMessageType &inMessage)
+ {
+ m_Serializer.Serialize(inStreamId, inMessage, 0);
+ m_OutStream->Write(m_Serializer.ToRawMessage());
+ }
+
+ virtual void OnMessageReceived(const SDebugStreamMessage &inMessage)
+ {
+ if (m_Invalid)
+ return;
+ m_Parser.Parse(inMessage.m_Data, *this);
+ }
+
+ void ReleaseAllInterpreters()
+ {
+ for (TIdInterpreterMap::iterator iter = m_Interpreters.begin(), end = m_Interpreters.end();
+ iter != end; ++iter) {
+ iter->second->Disconnect();
+ m_Listener.OnInterpreterDisconnected(*iter->second.mPtr);
+ }
+ m_Interpreters.clear();
+ }
+
+ virtual void Disconnect()
+ {
+ SendMessage(-1, SDisconnect());
+ ReleaseAllInterpreters();
+ }
+
+ IDebuggedInterpreter *FindInterpreter(QT3DSI32 inStreamId)
+ {
+ TIdInterpreterMap::iterator iter = m_Interpreters.find(inStreamId);
+ if (iter != m_Interpreters.end())
+ return iter->second.mPtr;
+ return NULL;
+ }
+
+// Set of ignored messages
+#define IGNORE_DEBUG_MESSAGE_TYPE(tname) \
+ void OnMessage(QT3DSU32, QT3DSU64, const S##tname &) { QT3DS_ASSERT(false); }
+
+ // this are outgoing messages; we shouldn't be receiving them.
+ IGNORE_DEBUG_MESSAGE_TYPE(SetBreakpoint)
+ IGNORE_DEBUG_MESSAGE_TYPE(ClearBreakpoint)
+ IGNORE_DEBUG_MESSAGE_TYPE(BreakOnMicrostep)
+ IGNORE_DEBUG_MESSAGE_TYPE(ClearAllBreakpoints)
+ IGNORE_DEBUG_MESSAGE_TYPE(Continue)
+
+ void OnMessage(QT3DSU32 sid, QT3DSU64, const SConnect &inMessage)
+ {
+ SDebugStrInStream theStream(inMessage.m_SCXMLData);
+ TEditorPtr theEditor(IEditor::CreateEditor(inMessage.m_Filename.c_str(), theStream));
+ if (theEditor) {
+ IDebuggedInterpreter &theInterpreter = IDebuggedInterpreter::Create(
+ g_MallocAlloc, *m_OutStream.mPtr, theEditor, inMessage.m_Filename, sid,
+ toConstDataRef(inMessage.m_Configuration.data(),
+ (QT3DSU32)inMessage.m_Configuration.size()),
+ m_Listener);
+ m_Interpreters.insert(eastl::make_pair(sid, &theInterpreter));
+ m_Listener.OnInterpreterConnected(theInterpreter);
+ } else {
+ m_LogStr.assign("Connection failed: ");
+ m_LogStr.append(inMessage.m_Filename);
+ // log the failure somehow.
+ m_Listener.OnLog(NULL, m_LogStr);
+ }
+ }
+
+ void OnMessage(QT3DSU32 sid, QT3DSU64, const SBreakpointHit &inMessage)
+ {
+ IDebuggedInterpreter *interp = FindInterpreter(sid);
+ if (interp)
+ interp->BreakpointHit(inMessage.m_Breakpoint);
+ }
+
+ void OnMessage(QT3DSU32, QT3DSU64, const SInitialization &inMessage)
+ {
+ if (inMessage.m_Version != SInitialization::GetCurrentVersion()) {
+ QT3DS_ASSERT(false);
+ m_Invalid = true;
+ }
+ }
+
+ void OnMessage(QT3DSU32 sid, QT3DSU64, const SDebugLog &inMessage)
+ {
+ IDebuggedInterpreter *interp = FindInterpreter(sid);
+ m_Listener.OnLog(interp, inMessage.m_Message);
+ }
+
+#define FORWARD_INTERPRETER_EVENT(evnType, interpFun) \
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const evnType &inMsg) \
+ { \
+ IDebuggedInterpreter *interp = FindInterpreter(sid); \
+ if (interp) \
+ interp->interpFun(inMsg); \
+ }
+
+ FORWARD_INTERPRETER_EVENT(SEventQueued, OnEventQueued);
+ FORWARD_INTERPRETER_EVENT(SBeginStep, OnBeginStep);
+ FORWARD_INTERPRETER_EVENT(SBeginMicrostep, OnBeginMicrostep);
+ FORWARD_INTERPRETER_EVENT(SMicrostepEvent, OnMicrostepEvent);
+ FORWARD_INTERPRETER_EVENT(SMicrostepData, OnMicrostepData);
+ FORWARD_INTERPRETER_EVENT(SEndMicrostep, OnEndMicrostep);
+ FORWARD_INTERPRETER_EVENT(SModifyTableValues, OnModifyTable);
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SDisconnect &)
+ {
+ if (sid > 0) {
+ IDebuggedInterpreter *interp = FindInterpreter(sid);
+ if (interp) {
+ m_Listener.OnInterpreterDisconnected(*interp);
+ m_Interpreters.erase(sid);
+ }
+ } else {
+ ReleaseAllInterpreters();
+ }
+ }
+
+ void error(const char8_t *inPrefix, const char8_t *inSuffix)
+ {
+ m_LogStr.assign(nonNull(inPrefix));
+ m_LogStr.append(nonNull(inSuffix));
+ m_Listener.OnLog(NULL, m_LogStr);
+ }
+};
+}
+
+IDebuggerMaster &IDebuggerMaster::CreateMaster(IDebugOutStream &outStream,
+ IDebuggerMasterListener &inListener)
+{
+ return *QT3DS_NEW(g_MallocAlloc, SDebuggerMaster)(outStream, inListener);
+} \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp
new file mode 100644
index 00000000..6891fab0
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp
@@ -0,0 +1,1880 @@
+/****************************************************************************
+**
+** 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 "Qt3DSState.h"
+#include "Qt3DSStateEditorEditorsImpl.h"
+#include "Qt3DSStateExecutionContext.h"
+#include "EASTL/sort.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::editor;
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+#pragma warning(disable : 4355)
+
+ SEditorImpl::SEditorImpl(TFoundationPtr inFoundation,
+ NVScopedRefCounted<IStringTable> inStringTable)
+ : m_EditorFoundation(inFoundation)
+ , m_StringTable(inStringTable)
+ , m_AutoAllocator(m_EditorFoundation->getFoundation())
+ , m_StateContext(IStateContext::Create(m_EditorFoundation->getFoundation()))
+ , m_Editors(m_EditorFoundation->getAllocator(), "SEditorImpl::m_Editors")
+ , mRefCount(0)
+ , m_TransactionManager(
+ QT3DS_NEW(inFoundation->getAllocator(), STransactionManagerImpl)(inFoundation))
+ , m_Accessors(m_EditorFoundation->getAllocator(), "SEditorImpl::m_Accessors")
+ , m_CopyPasteListener(NULL)
+ {
+ }
+
+ void SEditorImpl::addRef() { atomicIncrement(&mRefCount); }
+
+ void SEditorImpl::release()
+ {
+ QT3DSI32 count = atomicDecrement(&mRefCount);
+ if (count <= 0) {
+ TFoundationPtr theFoundation(m_EditorFoundation);
+ NVDelete(theFoundation->getAllocator(), this);
+ }
+ }
+
+ TObjPtr SEditorImpl::InsertEditor(void *inData, IEditorObject *inEditor)
+ {
+ if (inEditor) {
+ bool insertResult = m_Editors.insert(eastl::make_pair(inData, inEditor)).second;
+ QT3DS_ASSERT(insertResult);
+ (void)insertResult;
+ }
+ return inEditor;
+ }
+
+ template <typename TStateType>
+ TObjPtr SEditorImpl::ToEditor(TStateType &inItem)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(&inItem);
+ if (iter != m_Editors.end())
+ return iter->second;
+
+ TObjPtr retval = SStateEditorMap<TStateType>::CreateEditor(inItem, *this, m_Accessors);
+ if (retval)
+ InsertEditor(&inItem, retval.mPtr);
+ return retval;
+ }
+
+ template <typename TStateType>
+ TObjPtr SEditorImpl::ToEditor(TStateType *inItem)
+ {
+ if (inItem == NULL)
+ return TObjPtr();
+
+ return ToEditor(*inItem);
+ }
+
+ TObjPtr SEditorImpl::ToEditor(SStateNode &inItem)
+ {
+ switch (inItem.m_Type) {
+ case StateNodeTypes::State:
+ return ToEditor(static_cast<SState &>(inItem));
+ case StateNodeTypes::Parallel:
+ return ToEditor(static_cast<SParallel &>(inItem));
+ case StateNodeTypes::Transition:
+ return ToEditor(static_cast<STransition &>(inItem));
+ case StateNodeTypes::Final:
+ return ToEditor(static_cast<SFinal &>(inItem));
+ case StateNodeTypes::SCXML:
+ return ToEditor(static_cast<SSCXML &>(inItem));
+ case StateNodeTypes::History:
+ return ToEditor(static_cast<SHistory &>(inItem));
+ default:
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ }
+
+ TObjPtr SEditorImpl::ExecutableContentToEditor(SExecutableContent &inItem)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(&inItem);
+ if (iter != m_Editors.end())
+ return iter->second;
+
+ switch (inItem.m_Type) {
+ case ExecutableContentTypes::Send:
+ return ToEditor(static_cast<SSend &>(inItem));
+ case ExecutableContentTypes::Raise:
+ return ToEditor(static_cast<SRaise &>(inItem));
+ case ExecutableContentTypes::Log:
+ return ToEditor(static_cast<SLog &>(inItem));
+ case ExecutableContentTypes::Assign:
+ return ToEditor(static_cast<SAssign &>(inItem));
+ case ExecutableContentTypes::If:
+ return ToEditor(static_cast<SIf &>(inItem));
+ case ExecutableContentTypes::ElseIf:
+ return ToEditor(static_cast<SElseIf &>(inItem));
+ case ExecutableContentTypes::Else:
+ return ToEditor(static_cast<SElse &>(inItem));
+ case ExecutableContentTypes::Script:
+ return ToEditor(static_cast<SScript &>(inItem));
+ case ExecutableContentTypes::Cancel:
+ return ToEditor(static_cast<SCancel &>(inItem));
+ default:
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ }
+
+ template <typename TStateType>
+ TStateType *SEditorImpl::FromEditor(TObjPtr inPtr)
+ {
+ if (inPtr.mPtr == NULL)
+ return NULL;
+ const char8_t *typeName = inPtr->TypeName();
+ typedef typename SStateEditorMap<TStateType>::TEditorType TProspectiveType;
+ if (AreEqual(typeName, TProspectiveType::GetTypeStr())) {
+ return reinterpret_cast<TStateType *>(
+ &static_cast<TProspectiveType *>(inPtr.mPtr)->m_Data);
+ }
+ return NULL;
+ }
+
+ SStateNode *SEditorImpl::StateNodeFromEditor(TObjPtr inPtr)
+ {
+ if (inPtr.mPtr == NULL)
+ return NULL;
+ const char8_t *typeName = inPtr->TypeName();
+ if (AreEqual(typeName, SSCXMLEditor::GetTypeStr()))
+ return FromEditor<SSCXML>(inPtr);
+ if (AreEqual(typeName, SStateEditor::GetTypeStr()))
+ return FromEditor<SState>(inPtr);
+ if (AreEqual(typeName, STransitionEditor::GetTypeStr()))
+ return FromEditor<STransition>(inPtr);
+ if (AreEqual(typeName, SParallelEditor::GetTypeStr()))
+ return FromEditor<SParallel>(inPtr);
+ if (AreEqual(typeName, SFinalEditor::GetTypeStr()))
+ return FromEditor<SFinal>(inPtr);
+ if (AreEqual(typeName, SHistoryEditor::GetTypeStr()))
+ return FromEditor<SHistory>(inPtr);
+ return NULL;
+ }
+
+ SExecutableContent *SEditorImpl::ExecutableContentFromEditor(TObjPtr inPtr)
+ {
+ if (inPtr.mPtr == NULL)
+ return NULL;
+ const char8_t *typeName = inPtr->TypeName();
+ if (AreEqual(typeName, SSendEditor::GetTypeStr())) {
+ return FromEditor<SSend>(inPtr);
+ }
+ if (AreEqual(typeName, SRaiseEditor::GetTypeStr())) {
+ return FromEditor<SRaise>(inPtr);
+ }
+ if (AreEqual(typeName, SLogEditor::GetTypeStr())) {
+ return FromEditor<SLog>(inPtr);
+ }
+ if (AreEqual(typeName, SAssignEditor::GetTypeStr())) {
+ return FromEditor<SAssign>(inPtr);
+ }
+ if (AreEqual(typeName, SIfEditor::GetTypeStr())) {
+ return FromEditor<SIf>(inPtr);
+ }
+ if (AreEqual(typeName, SElseIfEditor::GetTypeStr())) {
+ return FromEditor<SElseIf>(inPtr);
+ }
+ if (AreEqual(typeName, SElseEditor::GetTypeStr())) {
+ return FromEditor<SElse>(inPtr);
+ }
+ if (AreEqual(typeName, SScriptEditor::GetTypeStr())) {
+ return FromEditor<SScript>(inPtr);
+ }
+ if (AreEqual(typeName, SCancelEditor::GetTypeStr())) {
+ return FromEditor<SCancel>(inPtr);
+ }
+
+ return NULL;
+ }
+
+ void SEditorImpl::GenerateUniqueId(SStateNode &inNode, const char8_t *inStem)
+ {
+ QT3DS_ASSERT(inNode.m_Id.IsValid() == false);
+ CXMLIO::GenerateUniqueId(inNode, inStem, *m_StateContext, *m_StringTable);
+ }
+
+ void SEditorImpl::GenerateUniqueId(SSend &inNode, const char8_t *inStem)
+ {
+ CXMLIO::GenerateUniqueId(inNode, inStem, *m_StateContext, *m_StringTable);
+ }
+
+ TObjPtr SEditorImpl::GetRoot() { return ToEditor(m_StateContext->GetRoot()); }
+
+ template <typename TStateType>
+ eastl::pair<TStateType *, TObjPtr> SEditorImpl::CreateEditorAndObject()
+ {
+ typedef typename SStateEditorMap<TStateType>::TEditorType TEditorType;
+ TObjPtr newEditor = SStateEditorMap<TStateType>::CreateEditor(*this, m_Accessors);
+ TStateType *theState = &static_cast<TEditorType *>(newEditor.mPtr)->m_Data;
+ eastl::pair<TStateType *, TObjPtr> retval = eastl::make_pair(theState, newEditor);
+ InsertEditor(retval.first, retval.second.mPtr);
+ return retval;
+ }
+
+ TObjPtr SEditorImpl::DoCreate(const char8_t *inTypeName, const char8_t *inId)
+ {
+ if (AreEqual(inTypeName, SSCXMLEditor::GetTypeStr())) {
+ QT3DS_ASSERT(m_StateContext->GetRoot() == NULL);
+ eastl::pair<SSCXML *, TObjPtr> theNewEditor = CreateEditorAndObject<SSCXML>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "scxml" : inId);
+ m_StateContext->SetRoot(*theNewEditor.first);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SStateEditor::GetTypeStr())) {
+ eastl::pair<SState *, TObjPtr> theNewEditor = CreateEditorAndObject<SState>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "state" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SParallelEditor::GetTypeStr())) {
+ eastl::pair<SParallel *, TObjPtr> theNewEditor = CreateEditorAndObject<SParallel>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "parallel" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SFinalEditor::GetTypeStr())) {
+ eastl::pair<SFinal *, TObjPtr> theNewEditor = CreateEditorAndObject<SFinal>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "final" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SHistoryEditor::GetTypeStr())) {
+ eastl::pair<SHistory *, TObjPtr> theNewEditor = CreateEditorAndObject<SHistory>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "history" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, STransitionEditor::GetTypeStr())) {
+ eastl::pair<STransition *, TObjPtr> theNewEditor =
+ CreateEditorAndObject<STransition>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "transition" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SDataModelEditor::GetTypeStr())) {
+ eastl::pair<SDataModel *, TObjPtr> theNewEditor =
+ CreateEditorAndObject<SDataModel>();
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SDataEditor::GetTypeStr())) {
+ eastl::pair<SData *, TObjPtr> theNewEditor = CreateEditorAndObject<SData>();
+ return theNewEditor.second;
+ }
+
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ TObjPtr SEditorImpl::Create(const char8_t *inTypeName, const char8_t *inId)
+ {
+ TObjPtr retval = DoCreate(inTypeName, inId);
+ m_TransactionManager->OnObjectCreated(retval);
+ return retval;
+ }
+
+ TObjPtr SEditorImpl::GetOrCreate(SSCXML &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SState &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(STransition &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SParallel &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SFinal &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SHistory &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SDataModel &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SData &inData) { return ToEditor(inData); }
+
+ TObjPtr SEditorImpl::GetObjectById(const char8_t *inId)
+ {
+ if (isTrivial(inId))
+ return TObjPtr();
+ SStateNode *theNode = m_StateContext->FindStateNode(RegisterStr(inId));
+ if (theNode)
+ return ToEditor(*theNode);
+ return TObjPtr();
+ }
+
+ TObjPtr SEditorImpl::GetEditor(void *inGraphData)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(inGraphData);
+ if (iter != m_Editors.end())
+ return iter->second;
+ return TObjPtr();
+ }
+
+ TSignalConnectionPtr SEditorImpl::AddChangeListener(IEditorChangeListener &inListener)
+ {
+ return m_TransactionManager->AddChangeListener(inListener);
+ }
+
+ TTransactionPtr SEditorImpl::BeginTransaction(const TEditorStr &inName)
+ {
+ return m_TransactionManager->BeginTransaction(inName);
+ }
+
+ TTransactionPtr SEditorImpl::GetOpenTransaction()
+ {
+ return m_TransactionManager->GetOpenTransaction();
+ }
+
+ void SEditorImpl::RollbackTransaction() { m_TransactionManager->RollbackTransaction(); }
+
+ void SEditorImpl::EndTransaction() { m_TransactionManager->EndTransaction(); }
+
+ TEditorStr SEditorImpl::Copy(TObjList inObjects, const QT3DSVec2 &inMousePos)
+ {
+ eastl::vector<SStateNode *> theNodeList;
+ for (size_t idx = 0, end = inObjects.size(); idx < end; ++idx) {
+ SStateNode *theNode = StateNodeFromEditor(inObjects[idx]);
+ if (theNode && theNode->m_Type != StateNodeTypes::Transition
+ && theNode->m_Type != StateNodeTypes::SCXML) {
+ theNodeList.push_back(theNode);
+ }
+ }
+ eastl::vector<SNamespacePair> theNamespaces;
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable));
+ NVScopedRefCounted<IDOMWriter> theWriter(
+ IDOMWriter::CreateDOMWriter(m_EditorFoundation->getAllocator(), "scxml_fragment",
+ m_StringTable)
+ .first);
+
+ eastl::vector<SStateNode *> rootNodes = CXMLIO::SaveSCXMLFragment(
+ *m_StateContext, *m_EditorFoundation->m_Foundation, *m_StringTable, *theWriter,
+ toDataRef(theNodeList.data(), theNodeList.size()), *this, inMousePos,
+ theNamespaces);
+
+ if (m_CopyPasteListener)
+ m_CopyPasteListener->OnCopy(this, rootNodes, *theWriter, theNamespaces);
+
+ SEditorImplStrIOStream theStream;
+ CDOMSerializer::WriteXMLHeader(theStream);
+ CDOMSerializer::Write(m_EditorFoundation->getAllocator(), *theWriter->GetTopElement(),
+ theStream, *m_StringTable,
+ toDataRef(theNamespaces.data(), theNamespaces.size()), false);
+ return theStream.m_Str;
+ }
+
+ bool SEditorImpl::CanPaste(TObjPtr inTarget)
+ {
+ SStateNode *theNode = StateNodeFromEditor(inTarget);
+ if (theNode) {
+ return theNode->m_Type != StateNodeTypes::Transition
+ && theNode->m_Type != StateNodeTypes::History
+ && theNode->m_Type != StateNodeTypes::Final;
+ }
+
+ return false;
+ }
+
+ void SEditorImpl::AddNewPasteObjectToTransaction(SStateNode &inNode)
+ {
+ TObjPtr editorPtr = ToEditor(inNode);
+ if (editorPtr) {
+ m_TransactionManager->m_Transaction->m_Changes.push_back(new SChange(*editorPtr));
+ m_TransactionManager->OnObjectCreated(editorPtr);
+ Option<SValue> children = editorPtr->GetPropertyValue("children");
+ if (children.hasValue()) {
+ TObjList childList = children->getData<TObjList>();
+ for (size_t idx = 0, end = childList.size(); idx < end; ++idx) {
+ SStateNode *childNode = StateNodeFromEditor(childList[idx]);
+ if (childNode)
+ AddNewPasteObjectToTransaction(*childNode);
+ }
+ }
+ }
+ }
+
+ void RecurseAndCheckForValidHistoryAfterPaste(TObjPtr inNode, SEditorImpl &inEditor)
+ {
+ if (AreEqual(inNode->TypeName(), "history")) {
+ inEditor.CheckAndSetValidHistoryDefault(inNode);
+ } else {
+ Option<SValue> childrenOpt = inNode->GetPropertyValue("children");
+ if (childrenOpt.hasValue() == false) {
+ return;
+ }
+ TObjList children = childrenOpt->getData<TObjList>();
+ for (size_t childIdx = 0, childEnd = children.size(); childIdx < childEnd;
+ ++childIdx) {
+ RecurseAndCheckForValidHistoryAfterPaste(children[childIdx], inEditor);
+ }
+ }
+ }
+
+ void SEditorImpl::Paste(const TEditorStr &inCopiedObjects, TObjPtr inTarget,
+ const QT3DSVec2 &inMousePos)
+ {
+ Option<SValue> childrenOpt = inTarget->GetPropertyValue("children");
+ if (childrenOpt.hasValue() == false) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ TObjList children = childrenOpt->getData<TObjList>();
+
+ SEditorImplStrInStream inStream(inCopiedObjects);
+ NVScopedRefCounted<IDOMFactory> theFactory =
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable);
+ eastl::pair<SNamespacePairNode *, SDOMElement *> readResult =
+ CDOMSerializer::Read(*theFactory, inStream);
+ SDOMElement *elem = readResult.second;
+ if (elem == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ NVScopedRefCounted<IDOMReader> theReader = IDOMReader::CreateDOMReader(
+ m_EditorFoundation->getAllocator(), *elem, m_StringTable, *theFactory);
+ eastl::pair<eastl::vector<SStateNode *>, CXMLIO::TIdRemapMap> theRootsPair;
+ {
+ IDOMReader::Scope __readerScope(*theReader);
+ theRootsPair =
+ CXMLIO::LoadSCXMLFragment(m_AutoAllocator, *m_EditorFoundation->m_Foundation,
+ *theReader, *m_StringTable, *m_StateContext, *this);
+ }
+ eastl::vector<SStateNode *> &theObjects(theRootsPair.first);
+ if (m_CopyPasteListener)
+ m_CopyPasteListener->OnPaste(this, *theReader, theRootsPair.second);
+ // Merge any namespaces into the context's namespace list.
+ if (m_StateContext->GetDOMFactory()) {
+ for (SNamespacePairNode *fragNode = readResult.first; fragNode;
+ fragNode = fragNode->m_NextNode) {
+ bool found = false;
+ for (SNamespacePairNode *ctxNode = m_StateContext->GetFirstNSNode();
+ ctxNode && !found; ctxNode = ctxNode->m_NextNode) {
+ if (ctxNode->m_Namespace == fragNode->m_Namespace)
+ found = true;
+ }
+ if (!found) {
+ SNamespacePairNode *newNode =
+ m_StateContext->GetDOMFactory()->NextNSPairNode(
+ fragNode->m_Namespace, fragNode->m_Abbreviation);
+ newNode->m_NextNode = m_StateContext->GetFirstNSNode();
+ m_StateContext->SetFirstNSNode(*newNode);
+ }
+ }
+ }
+ QT3DSVec2 globalPos(0, 0);
+ if (inTarget) {
+ for (TObjPtr posObj = inTarget; posObj; posObj = posObj->Parent()) {
+ Option<SValue> theData = posObj->GetPropertyValue("position");
+ if (theData.hasValue())
+ globalPos += theData->getData<QT3DSVec2>();
+ }
+ }
+ QT3DSVec2 theMousePos = inMousePos - globalPos;
+ for (size_t idx = 0, end = theObjects.size(); idx < end; ++idx) {
+ SStateNode *currentObject = theObjects[idx];
+ QT3DSVec2 objPos(0, 0);
+ if (currentObject->m_StateNodeFlags.HasPosition())
+ objPos = currentObject->m_Position;
+ currentObject->SetPosition(objPos + theMousePos);
+ if (m_TransactionManager->m_Transaction)
+ AddNewPasteObjectToTransaction(*currentObject);
+ TObjPtr editorPtr = ToEditor(*currentObject);
+ if (editorPtr.mPtr)
+ children.push_back(editorPtr);
+ }
+ // This sets up the valid parents. Without that, checking if a history node is valid
+ // is meaningless.
+ inTarget->SetPropertyValue("children", children);
+ for (size_t idx = 0, end = children.size(); idx < end; ++idx) {
+ RecurseAndCheckForValidHistoryAfterPaste(children[idx], *this);
+ }
+ }
+
+ bool SEditorImpl::IsDerivedFrom(SStateNode &child, SStateNode &parent)
+ {
+ if (&child == &parent)
+ return true;
+ if (child.m_Parent)
+ return IsDerivedFrom(*child.m_Parent, parent);
+ return false;
+ }
+
+ // This method should always return a value because the scxml root item
+ // is the parent of everyone else.
+ SStateNode &SEditorImpl::GetLeastCommonAncestor(SStateNode &lhs, SStateNode &rhs)
+ {
+ // If lhs is derived from rhs, return rhs
+ if (IsDerivedFrom(lhs, rhs))
+ return rhs;
+ // vice versa
+ else if (IsDerivedFrom(rhs, lhs))
+ return lhs;
+
+ // Else wander up the tree till we find a common ancestor.
+ QT3DS_ASSERT(lhs.m_Parent != NULL);
+ if (lhs.m_Parent != NULL)
+ return GetLeastCommonAncestor(*lhs.m_Parent, rhs);
+
+ return lhs;
+ }
+
+ TObjPtr SEditorImpl::GetLeastCommonAncestor(NVConstDataRef<TObjPtr> inObjects)
+ {
+ SStateNode *lastAncestor = NULL;
+ if (inObjects.size() == 0)
+ return TObjPtr();
+
+ for (QT3DSU32 idx = 0, end = inObjects.size(); idx < end; ++idx) {
+ if (lastAncestor == NULL) {
+ lastAncestor = StateNodeFromEditor(inObjects[idx]);
+ } else {
+ SStateNode *nextNode = StateNodeFromEditor(inObjects[idx]);
+ if (nextNode != NULL) {
+ lastAncestor = &GetLeastCommonAncestor(*lastAncestor, *nextNode);
+ }
+ }
+ }
+
+ if (lastAncestor)
+ return ToEditor(*lastAncestor);
+ else
+ return TObjPtr();
+ }
+
+ void GetCancelableSendIds(TExecutableContentList &inList, TObjList &outList,
+ SEditorImpl &inEditor);
+
+ void RecursiveGetCancelableSendIds(SExecutableContent &inNode, TObjList &outList,
+ SEditorImpl &inEditor)
+ {
+ if (inNode.m_Type == ExecutableContentTypes::Send) {
+ SSend &theSend = static_cast<SSend &>(inNode);
+ if (IExecutionContext::ParseTimeStrToMilliseconds(theSend.m_Delay)
+ || !isTrivial(theSend.m_DelayExpr)) {
+ outList.push_back(inEditor.ExecutableContentToEditor(theSend));
+ }
+ }
+ GetCancelableSendIds(inNode.m_Children, outList, inEditor);
+ }
+
+ void GetCancelableSendIds(TExecutableContentList &inList, TObjList &outList,
+ SEditorImpl &inEditor)
+ {
+ for (TExecutableContentList::iterator iter = inList.begin(), end = inList.end();
+ iter != end; ++iter)
+ RecursiveGetCancelableSendIds(*iter, outList, inEditor);
+ }
+
+ void RecursiveGetCancelableSendIds(SStateNode &inNode, TObjList &outList,
+ SEditorImpl &inEditor)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition) {
+ STransition &transition = static_cast<STransition &>(inNode);
+ GetCancelableSendIds(transition.m_ExecutableContent, outList, inEditor);
+ } else {
+ TOnEntryList *entryList = inNode.GetOnEntryList();
+ TOnExitList *exitList = inNode.GetOnExitList();
+ if (entryList) {
+ for (TOnEntryList::iterator iter = entryList->begin(), end = entryList->end();
+ iter != end; ++iter)
+ GetCancelableSendIds(iter->m_ExecutableContent, outList, inEditor);
+ }
+ if (exitList) {
+ for (TOnExitList::iterator iter = exitList->begin(), end = exitList->end();
+ iter != end; ++iter)
+ GetCancelableSendIds(iter->m_ExecutableContent, outList, inEditor);
+ }
+ }
+ TStateNodeList *childList = inNode.GetChildren();
+ if (childList) {
+ for (TStateNodeList::iterator childIter = childList->begin(),
+ childEnd = childList->end();
+ childIter != childEnd; ++childIter)
+ RecursiveGetCancelableSendIds(*childIter, outList, inEditor);
+ }
+ }
+
+ // TODO - think about a fast and easy way to cache things (and keep the cache correct).
+ TObjList SEditorImpl::GetCancelableSendIds()
+ {
+ TObjList retval;
+ if (m_StateContext->GetRoot())
+ RecursiveGetCancelableSendIds(*m_StateContext->GetRoot(), retval, *this);
+ return retval;
+ }
+
+ static const char8_t *whitespace = "\n\r\t ";
+
+ void StripIfLastChar(TEditorStr &str, char data)
+ {
+ if (str.size() && str.back() == data)
+ str.resize(str.size() - 1);
+ }
+
+ void AddUnique(TEditorStrList &list, const TEditorStr &str)
+ {
+ if (eastl::find(list.begin(), list.end(), str) == list.end())
+ list.push_back(str);
+ }
+
+ void RecursiveScanExecutableContentForEvents(TExecutableContentList &contentList,
+ TEditorStrList &ioStrList)
+ {
+ for (TExecutableContentList::iterator iter = contentList.begin(),
+ end = contentList.end();
+ iter != end; ++iter) {
+ if (iter->m_Type == ExecutableContentTypes::Send) {
+ SSend &theSend = static_cast<SSend &>(*iter);
+ if (!isTrivial(theSend.m_Event)) {
+ AddUnique(ioStrList, theSend.m_Event.c_str());
+ }
+ } else if (iter->m_Type == ExecutableContentTypes::Raise) {
+ SRaise &theRaise = static_cast<SRaise &>(*iter);
+ if (!isTrivial(theRaise.m_Event)) {
+ AddUnique(ioStrList, theRaise.m_Event.c_str());
+ }
+ }
+ RecursiveScanExecutableContentForEvents(iter->m_Children, ioStrList);
+ }
+ }
+
+ template <typename TListType>
+ void ScanItemListForEvents(TListType *itemList, TEditorStrList &ioEvents)
+ {
+ if (itemList) {
+ typedef typename TListType::iterator iterator;
+ for (iterator iter = itemList->begin(), end = itemList->end(); iter != end;
+ ++iter) {
+ RecursiveScanExecutableContentForEvents(iter->m_ExecutableContent, ioEvents);
+ }
+ }
+ }
+
+ void RecursiveGetStateMachineEvents(SStateNode &inNode, TEditorStrList &ioList)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition) {
+ STransition &theTransition = static_cast<STransition &>(inNode);
+ TEditorStr transStr(theTransition.m_Event);
+ for (size_t pos = transStr.find_first_not_of(whitespace); pos != TEditorStr::npos;
+ pos = transStr.find_first_not_of(whitespace, pos)) {
+ size_t end = transStr.find_first_of(whitespace, pos);
+ if (end == TEditorStr::npos)
+ end = transStr.size();
+ TEditorStr subStr = transStr.substr(pos, end - pos);
+ pos = end;
+ StripIfLastChar(subStr, '*');
+ StripIfLastChar(subStr, '.');
+ AddUnique(ioList, subStr);
+ }
+ RecursiveScanExecutableContentForEvents(theTransition.m_ExecutableContent, ioList);
+ } else {
+ ScanItemListForEvents(inNode.GetOnEntryList(), ioList);
+ ScanItemListForEvents(inNode.GetOnExitList(), ioList);
+ }
+ TStateNodeList *children = inNode.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter)
+ RecursiveGetStateMachineEvents(*iter, ioList);
+ }
+ }
+
+ TEditorStrList SEditorImpl::GetStateMachineEvents()
+ {
+ TEditorStrList retval;
+ if (m_StateContext->GetRoot())
+ RecursiveGetStateMachineEvents(*m_StateContext->GetRoot(), retval);
+ eastl::sort(retval.begin(), retval.end());
+ return retval;
+ }
+
+ eastl::pair<SExecutableContent *, TObjPtr>
+ SEditorImpl::CreateExecutableContent(const char8_t *inTypeName)
+ {
+ SExecutableContent *newExecutable = NULL;
+ TObjPtr newObj;
+ if (AreEqual(inTypeName, SSendEditor::GetTypeStr())) {
+ eastl::pair<SSend *, TObjPtr> theNewEditor = CreateEditorAndObject<SSend>();
+ GenerateUniqueId(*theNewEditor.first, "send");
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SScriptEditor::GetTypeStr())) {
+ eastl::pair<SScript *, TObjPtr> theNewEditor = CreateEditorAndObject<SScript>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SCancelEditor::GetTypeStr())) {
+ eastl::pair<SCancel *, TObjPtr> theNewEditor = CreateEditorAndObject<SCancel>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ return eastl::make_pair(newExecutable, newObj);
+ }
+
+ struct SExecListAddChange : public IChange
+ {
+ TExecutableContentList &parentList;
+ SExecutableContent *prevSibling;
+ SExecutableContent &item;
+ TObjPtr editorObj;
+ QT3DSI32 mRefCount;
+ bool m_AddOnDo;
+ SExecListAddChange(TExecutableContentList &plist, SExecutableContent *prev,
+ SExecutableContent &_item, TObjPtr objPtr, bool addOnDo)
+ : parentList(plist)
+ , prevSibling(prev)
+ , item(_item)
+ , editorObj(objPtr)
+ , mRefCount(0)
+ , m_AddOnDo(addOnDo)
+ {
+ }
+
+ void Add()
+ {
+ if (prevSibling)
+ parentList.insert_after(*prevSibling, item);
+ else
+ parentList.push_front(item);
+ }
+ void Remove() { parentList.remove(item); }
+
+ virtual void Do()
+ {
+ if (m_AddOnDo)
+ Add();
+ else
+ Remove();
+ }
+
+ virtual void Undo()
+ {
+ if (m_AddOnDo)
+ Remove();
+ else
+ Add();
+ }
+
+ virtual TObjPtr GetEditor() { return editorObj; }
+
+ virtual void addRef() { ++mRefCount; }
+
+ virtual void release()
+ {
+ --mRefCount;
+ if (mRefCount <= 0)
+ delete this;
+ }
+ };
+
+ void SEditorImpl::ReplaceExecutableContent(SExecutableContent &oldContent,
+ SExecutableContent &newContent)
+ {
+ SStateNode *stateNodeParent = oldContent.m_StateNodeParent;
+ SExecutableContent *execContentParent = oldContent.m_Parent;
+ SExecutableContentParentInfo parentInfo(oldContent, *this);
+ SExecutableContent *prevSibling = oldContent.m_PreviousSibling;
+
+ // Can't use remove object from graph here because it is too aggressive.
+ NVScopedRefCounted<SExecListAddChange> oldChange = new SExecListAddChange(
+ *parentInfo.parentList, prevSibling, oldContent, parentInfo.editorObj, false);
+ oldChange->Do();
+ newContent.m_StateNodeParent = stateNodeParent;
+ newContent.m_Parent = execContentParent;
+ NVScopedRefCounted<SExecListAddChange> newChange = new SExecListAddChange(
+ *parentInfo.parentList, prevSibling, newContent, parentInfo.editorObj, true);
+ newChange->Do();
+ if (GetOpenTransactionImpl()) {
+ GetOpenTransactionImpl()->m_Changes.push_back(oldChange.mPtr);
+ GetOpenTransactionImpl()->m_Changes.push_back(newChange.mPtr);
+ }
+ }
+
+ TObjPtr SEditorImpl::ChangeExecutableContentType(TObjPtr inContent, const char8_t *newType)
+ {
+ SExecutableContent *theContent = ExecutableContentFromEditor(inContent);
+ if (!theContent) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ SExecutableContentParentInfo parentInfo(*theContent, *this);
+ // find the index of the item in the parent list.
+ if (parentInfo.parentList == NULL) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ eastl::pair<SExecutableContent *, TObjPtr> theNewContent =
+ CreateExecutableContent(newType);
+ if (!theNewContent.first) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ ReplaceExecutableContent(*theContent, *theNewContent.first);
+ return theNewContent.second;
+ }
+
+ TEditorStr SEditorImpl::ToXML(TObjPtr inContent)
+ {
+ SExecutableContent *theContent = ExecutableContentFromEditor(inContent);
+ if (!theContent) {
+ QT3DS_ASSERT(false);
+ return TEditorStr();
+ }
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable));
+ NVScopedRefCounted<IDOMWriter> theWriter(
+ IDOMWriter::CreateDOMWriter(m_EditorFoundation->getAllocator(), "innerXML",
+ m_StringTable, 0)
+ .first);
+ CXMLIO::ToEditableXml(*m_StateContext, m_EditorFoundation->getFoundation(),
+ *m_StringTable, *theWriter, *theContent, *this);
+ SDOMElement *theTopElem = theWriter->GetTopElement();
+ SDOMElement *xmlElem = &theTopElem->m_Children.front();
+ SEditorImplStrIOStream theStream;
+ if (xmlElem) {
+
+ eastl::vector<SNamespacePair> theNamespaces;
+ for (SNamespacePairNode *theNode = m_StateContext->GetFirstNSNode(); theNode;
+ theNode = theNode->m_NextNode)
+ theNamespaces.push_back(*theNode);
+ if (theNamespaces.empty()) {
+ theNamespaces.push_back(SNamespacePair(
+ m_StringTable->RegisterStr("http://www.w3.org/2005/07/scxml")));
+ theNamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr("http://qt.io/qt3dstudio/uicstate"),
+ m_StringTable->RegisterStr("Qt3DS")));
+ }
+ CDOMSerializer::Write(
+ m_EditorFoundation->getAllocator(), *xmlElem, theStream, *m_StringTable,
+ toDataRef(theNamespaces.data(), theNamespaces.size()), false, 0, false);
+ }
+ return theStream.m_Str;
+ }
+
+ struct SStrXMLErrorHandler : public CXmlErrorHandler
+ {
+ TEditorStr m_Error;
+ virtual void OnXmlError(TXMLCharPtr errorName, int line, int /*column*/)
+ {
+ char buf[256];
+ sprintf(buf, "Error on line %d: ", line);
+ m_Error.assign(buf);
+ m_Error.append(nonNull(errorName));
+ }
+ };
+
+ eastl::pair<TEditorStr, TObjPtr> SEditorImpl::FromXML(TObjPtr inContent,
+ const TEditorStr &ioEditedXML)
+ {
+ TEditorStr wrappedXML("<snippet ");
+ eastl::vector<SNamespacePair> theNamespaces;
+ for (SNamespacePairNode *theNode = m_StateContext->GetFirstNSNode(); theNode;
+ theNode = theNode->m_NextNode)
+ theNamespaces.push_back(*theNode);
+ if (theNamespaces.empty()) {
+ theNamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr("http://www.w3.org/2005/07/scxml")));
+ theNamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr("http://qt.io/qt3dstudio/uicstate"),
+ m_StringTable->RegisterStr("Qt3DS")));
+ }
+ for (size_t idx = 0, end = theNamespaces.size(); idx < end; ++idx) {
+ SNamespacePair &theNode(theNamespaces[idx]);
+ wrappedXML.append(" xmlns");
+ if (theNode.m_Abbreviation.IsValid()) {
+ wrappedXML.append(":");
+ wrappedXML.append(theNode.m_Abbreviation.c_str());
+ }
+ wrappedXML.append("=\"");
+ wrappedXML.append(theNode.m_Namespace.c_str());
+ wrappedXML.append("\"");
+ }
+ wrappedXML.append(">");
+ wrappedXML.append(ioEditedXML);
+ wrappedXML.append("</snippet>");
+ SEditorImplStrInStream theStream(wrappedXML);
+
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable));
+ SStrXMLErrorHandler errorHandler;
+ SDOMElement *theElem =
+ CDOMSerializer::Read(*theFactory, theStream, &errorHandler).second;
+ TObjPtr retval;
+ if (theElem) {
+ NVScopedRefCounted<IDOMReader> theReader(IDOMReader::CreateDOMReader(
+ m_EditorFoundation->getAllocator(), theElem->m_Children.front(), m_StringTable,
+ theFactory));
+ SExecutableContent *oldContent = ExecutableContentFromEditor(inContent);
+ SExecutableContent *theContent =
+ CXMLIO::FromEditableXML(*theReader, m_EditorFoundation->getFoundation(),
+ *m_StateContext, *m_StringTable, m_AutoAllocator, *this,
+ oldContent->m_StateNodeParent, oldContent->m_Parent);
+ if (theContent) {
+ ReplaceExecutableContent(*oldContent, *theContent);
+ retval = ExecutableContentToEditor(*theContent);
+ } else {
+ errorHandler.m_Error.assign("The \"");
+ errorHandler.m_Error.append(theReader->GetElementName().c_str());
+ errorHandler.m_Error.append("\" action is not recognized. It must be one of:");
+ eastl::vector<eastl::string> contentNames =
+ CXMLIO::GetSupportedExecutableContentNames();
+ for (size_t idx = 0, end = contentNames.size(); idx < end; ++idx) {
+ if (idx != 0)
+ errorHandler.m_Error.append(",");
+
+ errorHandler.m_Error.append(" \"");
+ errorHandler.m_Error.append(contentNames[idx].c_str());
+ errorHandler.m_Error.append("\"");
+ }
+ errorHandler.m_Error.append(".");
+ }
+ }
+
+ return eastl::make_pair(errorHandler.m_Error, retval);
+ }
+
+ void SEditorImpl::ReleaseEditor(void *inData)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(inData);
+ if (iter != m_Editors.end())
+ m_Editors.erase(iter);
+ }
+
+ bool SEditorImpl::Save(const char8_t *inFileName)
+ {
+ CFileSeekableIOStream theFile(inFileName, FileWriteFlags());
+ if (theFile.IsOpen() == false) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+
+ Save(theFile);
+ return true;
+ }
+
+ void SEditorImpl::Save(IOutStream &inStream) { m_StateContext->Save(inStream, this); }
+
+ bool SEditorImpl::Load(IInStream &inStream, const char8_t *inFilename)
+ {
+ bool theRetval = false;
+ m_StateContext = IStateContext::Load(m_AutoAllocator, *m_EditorFoundation->m_Foundation,
+ inStream, inFilename, m_StringTable.mPtr, this);
+ if (m_StateContext.mPtr == 0)
+ m_StateContext = IStateContext::Create(*m_EditorFoundation->m_Foundation);
+ else
+ theRetval = true;
+ return theRetval;
+ }
+
+ STransaction *SEditorImpl::GetOpenTransactionImpl()
+ {
+ return m_TransactionManager->m_Transaction.mPtr;
+ }
+
+ void SEditorImpl::SetIdProperty(SStateNode &inNode, const SValue &inData,
+ CRegisteredString & /*inTarget*/)
+ {
+ IStateContext &theContext = *m_StateContext;
+ theContext.EraseId(inNode.m_Id);
+ TEditorStr potentialId = inData.getData<TEditorStr>();
+ if (potentialId.size() == 0) {
+ switch (inNode.m_Type) {
+ case StateNodeTypes::State:
+ potentialId = "state";
+ break;
+ case StateNodeTypes::Parallel:
+ potentialId = "parallel";
+ break;
+ case StateNodeTypes::Transition:
+ potentialId = "transition";
+ break;
+ case StateNodeTypes::Final:
+ potentialId = "final";
+ break;
+ case StateNodeTypes::History:
+ potentialId = "history";
+ break;
+ case StateNodeTypes::SCXML:
+ potentialId = "scxml";
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ potentialId = "id";
+ break;
+ }
+ }
+ CRegisteredString oldId = inNode.m_Id;
+ inNode.m_Id = CRegisteredString();
+ GenerateUniqueId(inNode, potentialId.c_str());
+ // This is a bad hack to get around:
+ // 1. user changes id calling this function
+ if (m_CopyPasteListener) {
+ m_CopyPasteListener->OnIDChange(this, inNode, oldId);
+ }
+ }
+
+ void SEditorImpl::SetIdProperty(SSend &inNode, const SValue &inData,
+ CRegisteredString & /*inTarget*/)
+ {
+ IStateContext &theContext = *m_StateContext;
+ theContext.EraseId(inNode.m_Id);
+ TEditorStr potentialId = inData.getData<TEditorStr>();
+ if (potentialId.size() == 0)
+ potentialId = "send";
+
+ inNode.m_Id = CRegisteredString();
+ GenerateUniqueId(inNode, potentialId.c_str());
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, NVConstDataRef<SStateNode *> &inTarget)
+ {
+ if (inTarget.mData != NULL) {
+ m_AutoAllocator.deallocate((void *)inTarget.mData);
+ inTarget.mData = NULL;
+ inTarget.mSize = 0;
+ }
+ if (inData.hasValue()) {
+ TObjList inList(inData->getData<TObjList>());
+ eastl::vector<SStateNode *> validObjects;
+ for (size_t idx = 0, end = inList.size(); idx < end; ++idx) {
+ TObjPtr theObj = inList[idx];
+ SStateNode *theNode = StateNodeFromEditor(theObj);
+ if (theNode) {
+ validObjects.push_back(theNode);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ if (validObjects.empty() == false) {
+ NVConstDataRef<SStateNode *> &outList(inTarget);
+ size_t byteCount = validObjects.size() * sizeof(SStateNode *);
+ outList.mData = (SStateNode * const *)m_AutoAllocator.allocate(
+ byteCount, "graph ref list data", __FILE__, __LINE__);
+ memCopy((void *)outList.mData, validObjects.data(), byteCount);
+ outList.mSize = validObjects.size();
+ }
+ }
+ }
+
+ SValue SEditorImpl::Get(NVConstDataRef<SStateNode *> &inTarget)
+ {
+ TObjList retval;
+ NVConstDataRef<SStateNode *> inRefList(inTarget);
+ for (QT3DSU32 idx = 0, end = inRefList.size(); idx < end; ++idx) {
+ TObjPtr obj = ToEditor(inRefList[idx]);
+ if (obj)
+ retval.push_back(obj);
+ else {
+ // This shouldn't ever happen, but it might when loading
+ // an invalid file.
+ QT3DS_ASSERT(false);
+ }
+ }
+ return retval;
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, STransition *&inTarget)
+ {
+ inTarget = NULL;
+ if (inData.hasValue()) {
+ TObjPtr theData(inData->getData<TObjPtr>());
+ inTarget = FromEditor<STransition>(theData);
+ }
+ }
+
+ Option<SValue> SEditorImpl::Get(STransition *&inTarget)
+ {
+ return SValue(ToEditor(inTarget));
+ }
+
+ void SEditorImpl::SetInitial(const TObjList &inList, STransition *&outTransitionPtr)
+ {
+ if (outTransitionPtr) {
+ Set(SValue(inList), outTransitionPtr->m_Target);
+ if (outTransitionPtr->m_Target.mSize == 0
+ && outTransitionPtr->m_ExecutableContent.empty())
+ outTransitionPtr = NULL;
+ } else {
+ NVConstDataRef<SStateNode *> theTemp;
+ Set(SValue(inList), theTemp);
+ if (theTemp.mSize) {
+ outTransitionPtr = QT3DS_NEW(m_AutoAllocator, STransition)();
+ outTransitionPtr->m_Target = theTemp;
+ }
+ }
+ }
+
+ SValue SEditorImpl::GetInitial(STransition *inInitialTrans)
+ {
+ if (inInitialTrans)
+ return Get(inInitialTrans->m_Target);
+
+ return TObjList();
+ }
+ static inline STransition **GetInitialTransitionPtr(SStateNode &inParent)
+ {
+ StateNodeTypes::Enum typeEnum(inParent.m_Type);
+ STransition **theParentTransPtr = NULL;
+ if (typeEnum == StateNodeTypes::SCXML)
+ theParentTransPtr = &static_cast<SSCXML &>(inParent).m_Initial;
+ else if (typeEnum == StateNodeTypes::State)
+ theParentTransPtr = &static_cast<SState &>(inParent).m_Initial;
+ return theParentTransPtr;
+ }
+
+ static inline bool IsFirstChild(SStateNode &inChild, SStateNode &inParent)
+ {
+ TStateNodeList *childList = inParent.GetChildren();
+ SStateNode *firstViableChild = NULL;
+ if (childList) {
+ for (TStateNodeList::iterator iter = childList->begin(), end = childList->end();
+ iter != end && firstViableChild == NULL; ++iter) {
+ StateNodeTypes::Enum typeEnum = iter->m_Type;
+ if (typeEnum == StateNodeTypes::State || typeEnum == StateNodeTypes::Parallel
+ || typeEnum == StateNodeTypes::Final) {
+ firstViableChild = &(*iter);
+ }
+ }
+ }
+ return firstViableChild == &inChild;
+ }
+
+ struct SInitialTargetChange : public IChange
+ {
+ TObjPtr m_EditorObj;
+ SStateNode &m_Parent;
+ STransition *m_InitialTransition;
+ STransition *m_FinalTransition;
+ QT3DSI32 m_RefCount;
+ // Implicitly setting c to be the initial target.
+ SInitialTargetChange(TObjPtr editorObj, SStateNode &p, SStateNode &c,
+ SEditorImpl &editor)
+ : m_EditorObj(editorObj)
+ , m_Parent(p)
+ , m_InitialTransition(NULL)
+ , m_FinalTransition(NULL)
+ , m_RefCount(0)
+ {
+ STransition **theTransitionPtr = GetInitialTransitionPtr(m_Parent);
+ QT3DS_ASSERT(theTransitionPtr);
+ if (theTransitionPtr) {
+ m_InitialTransition = *theTransitionPtr;
+ theTransitionPtr[0] = NULL;
+ bool firstChild = IsFirstChild(c, p);
+ if (!firstChild) {
+ TObjPtr editorObj = editor.Create("transition", "");
+ TObjList targets;
+ targets.push_back(editor.ToEditor(c));
+ editorObj->SetPropertyValue("target", targets);
+ SStateNode *stateNode = editor.StateNodeFromEditor(editorObj);
+ m_FinalTransition = static_cast<STransition *>(stateNode);
+ m_FinalTransition->m_Parent = &p;
+ *theTransitionPtr = m_FinalTransition;
+ }
+ }
+ }
+ virtual void addRef() { ++m_RefCount; }
+ virtual void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObj; }
+
+ template <typename TNodeType>
+ void Apply(STransition *trans, TNodeType &node)
+ {
+ node.m_Initial = trans;
+ }
+
+ virtual void Do()
+ {
+ if (m_Parent.m_Type == StateNodeTypes::SCXML)
+ Apply(m_FinalTransition, static_cast<SSCXML &>(m_Parent));
+ else if (m_Parent.m_Type == StateNodeTypes::State)
+ Apply(m_FinalTransition, static_cast<SState &>(m_Parent));
+ }
+
+ virtual void Undo()
+ {
+ if (m_Parent.m_Type == StateNodeTypes::SCXML)
+ Apply(m_InitialTransition, static_cast<SSCXML &>(m_Parent));
+ else if (m_Parent.m_Type == StateNodeTypes::State)
+ Apply(m_InitialTransition, static_cast<SState &>(m_Parent));
+ }
+ };
+
+ void SEditorImpl::SetInitialTarget(const Option<SValue> &inData, SStateNode &inNode)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition
+ /**|| inNode.m_Type == StateNodeTypes::History **/) {
+ QT3DS_ASSERT(false);
+ }
+ SStateNode *theParent = inNode.m_Parent;
+ if (theParent) {
+ STransition **theParentTransPtr = GetInitialTransitionPtr(*theParent);
+
+ if (theParentTransPtr) {
+ bool newValue = false;
+ if (inData.hasValue())
+ newValue = inData->getData<bool>();
+
+ NVScopedRefCounted<SInitialTargetChange> theChange =
+ new SInitialTargetChange(ToEditor(*theParent), *theParent, inNode, *this);
+ theChange->Do();
+ STransaction *theTransaction = GetOpenTransactionImpl();
+ if (theTransaction) {
+ theTransaction->m_Changes.push_back(theChange.mPtr);
+ TStateNodeList *children = theParent->GetChildren();
+ for (TStateNodeList::iterator iter = children->begin(),
+ end = children->end();
+ iter != end; ++iter) {
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*iter)));
+ }
+ }
+ }
+ }
+ }
+
+ SValue SEditorImpl::IsInitialTarget(SStateNode &inNode)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition
+ /** || inNode.m_Type == StateNodeTypes::History **/)
+ return SValue(false);
+
+ SStateNode *theParent(inNode.m_Parent);
+ if (theParent) {
+ if (!isTrivial(theParent->GetInitialExpression()))
+ return false;
+
+ STransition *theTransition = theParent->GetInitialTransition();
+ if (theTransition) {
+ for (QT3DSU32 idx = 0, end = theTransition->m_Target.size(); idx < end; ++idx)
+ if (&inNode == theTransition->m_Target[idx])
+ return SValue(true);
+ } else {
+ if (GetInitialTransitionPtr(*theParent))
+ return SValue(IsFirstChild(inNode, *theParent));
+ }
+ }
+ return SValue(false);
+ }
+
+ TEditorStr SEditorImpl::GetDefaultInitialValue(SStateNode &inNode)
+ {
+ TStateNodeList *children = inNode.GetChildren();
+ if (children == NULL)
+ return TEditorStr();
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (iter->m_Type != StateNodeTypes::History
+ && iter->m_Type != StateNodeTypes::Transition)
+ return TEditorStr(iter->m_Id.c_str());
+ }
+
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (iter->m_Type != StateNodeTypes::Transition)
+ return TEditorStr(iter->m_Id.c_str());
+ }
+
+ return TEditorStr();
+ }
+
+ void SEditorImpl::GetLegalInitialValues(SStateNode &inNode,
+ eastl::vector<CRegisteredString> &outValues)
+ {
+ TStateNodeList *children = inNode.GetChildren();
+ if (children == NULL)
+ return;
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (/** iter->m_Type != StateNodeTypes::History && **/ iter->m_Type
+ != StateNodeTypes::Transition)
+ outValues.push_back(iter->m_Id);
+ }
+ }
+
+ struct SInitialComboChange : public IChange
+ {
+ SStateNode &m_Node;
+ TObjPtr m_EditorObj;
+ const TEditorStr m_PreEditorComboValue;
+ const TEditorStr m_PostEditorComboValue;
+ const char8_t *m_PreInitialExpression;
+ const char8_t *m_PostInitialExpression;
+ STransition *m_PreComboTransition;
+ STransition *m_PostComboTransition;
+ QT3DSI32 m_RefCount;
+ SInitialComboChange(SStateNode &node, TObjPtr editObj, const char8_t *preExpr,
+ const char8_t *postExpr, STransition *preCombo,
+ STransition *postCombo, const TEditorStr &preEditorComboValue,
+ const TEditorStr &postEditorComboValue)
+ : m_Node(node)
+ , m_EditorObj(editObj)
+ , m_RefCount(0)
+ , m_PreInitialExpression(preExpr)
+ , m_PostInitialExpression(postExpr)
+ , m_PreComboTransition(preCombo)
+ , m_PostComboTransition(postCombo)
+ , m_PreEditorComboValue(preEditorComboValue)
+ , m_PostEditorComboValue(postEditorComboValue)
+ {
+ }
+
+ virtual void addRef() { ++m_RefCount; }
+ virtual void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObj; }
+
+ template <typename TNodeType, typename TEditorType>
+ void Apply(const char8_t *expr, STransition *trans, const TEditorStr &comboValue,
+ TNodeType &node, TEditorType &editor)
+ {
+ node.m_Initial = trans;
+ node.m_InitialExpr = expr;
+ editor.ApplyInitial(comboValue);
+ // editor.m_InitialComboValue = comboValue;
+ }
+
+ virtual void Do()
+ {
+ if (m_Node.m_Type == StateNodeTypes::SCXML)
+ Apply(m_PostInitialExpression, m_PostComboTransition, m_PostEditorComboValue,
+ static_cast<SSCXML &>(m_Node), static_cast<SSCXMLEditor &>(*m_EditorObj));
+ else if (m_Node.m_Type == StateNodeTypes::State)
+ Apply(m_PostInitialExpression, m_PostComboTransition, m_PostEditorComboValue,
+ static_cast<SState &>(m_Node), static_cast<SStateEditor &>(*m_EditorObj));
+ }
+
+ virtual void Undo()
+ {
+ if (m_Node.m_Type == StateNodeTypes::SCXML)
+ Apply(m_PreInitialExpression, m_PreComboTransition, m_PreEditorComboValue,
+ static_cast<SSCXML &>(m_Node), static_cast<SSCXMLEditor &>(*m_EditorObj));
+ else if (m_Node.m_Type == StateNodeTypes::State)
+ Apply(m_PreInitialExpression, m_PreComboTransition, m_PreEditorComboValue,
+ static_cast<SState &>(m_Node), static_cast<SStateEditor &>(*m_EditorObj));
+ }
+ };
+
+ void SEditorImpl::SetInitialAttribute(const Option<SValue> &inData, SStateNode &inNode)
+ {
+ TObjPtr theEditor = ToEditor(inNode);
+ STransition **targetPtr = NULL;
+ const char8_t **exprPtr = NULL;
+ TEditorStr comboValue;
+ if (inNode.m_Type == StateNodeTypes::State) {
+ targetPtr = &static_cast<SState &>(inNode).m_Initial;
+ exprPtr = &static_cast<SState &>(inNode).m_InitialExpr;
+ comboValue = static_cast<SStateEditor &>(*theEditor).m_InitialComboValue;
+ } else if (inNode.m_Type == StateNodeTypes::SCXML) {
+ targetPtr = &static_cast<SSCXML &>(inNode).m_Initial;
+ exprPtr = &static_cast<SSCXML &>(inNode).m_InitialExpr;
+ comboValue = static_cast<SSCXMLEditor &>(*theEditor).m_InitialComboValue;
+ }
+
+ if (targetPtr == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ STransition *initialTrans = *targetPtr;
+ const char8_t *initialExpr = *exprPtr;
+ TEditorStr initialComboValue = comboValue;
+
+ if (inData.isEmpty()) {
+ *targetPtr = NULL;
+ comboValue = "(script expression)";
+ } else {
+ TEditorStr stateId = inData->getData<TEditorStr>();
+ SStateNode *theNode =
+ this->m_StateContext->FindStateNode(RegisterStr(stateId.c_str()));
+ if (theNode) {
+ *targetPtr = QT3DS_NEW(this->m_AutoAllocator, STransition)();
+ SStateNode **newNode =
+ reinterpret_cast<SStateNode **>(this->m_AutoAllocator.allocate(
+ sizeof(SStateNode *), "TargetList", __FILE__, __LINE__));
+ targetPtr[0]->m_Target = toDataRef(newNode, 1);
+ *const_cast<SStateNode **>(targetPtr[0]->m_Target.begin()) = theNode;
+ targetPtr[0]->m_Target.mSize = 1;
+ comboValue = stateId;
+ } else {
+ *targetPtr = NULL;
+ comboValue = "";
+ }
+ *exprPtr = "";
+ }
+ STransition *postTrans = *targetPtr;
+ const char8_t *postExpr = *exprPtr;
+ TEditorStr postComboValue = comboValue;
+ STransaction *theTransaction = GetOpenTransactionImpl();
+ if (theTransaction) {
+ SInitialComboChange *theChange = new SInitialComboChange(
+ inNode, ToEditor(inNode), initialExpr, postExpr, initialTrans, postTrans,
+ initialComboValue, postComboValue);
+ theChange->Do();
+ theTransaction->m_Changes.push_back(theChange);
+ TStateNodeList *children = inNode.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*iter)));
+ }
+ }
+ }
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, SDataModel *&inTarget)
+ {
+ if (inData.hasValue()) {
+ TObjPtr thePtr = inData->getData<TObjPtr>();
+ if (thePtr)
+ inTarget = FromEditor<SDataModel>(thePtr);
+ else
+ inTarget = NULL;
+ } else
+ inTarget = NULL;
+ }
+
+ SValue SEditorImpl::Get(SDataModel *inTarget) { return ToEditor(inTarget); }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, TDataList &inTarget)
+ {
+ while (inTarget.empty() == false)
+ inTarget.remove(inTarget.front());
+ if (inData.hasValue()) {
+ TObjList newData = inData->getData<TObjList>();
+ for (TObjList::iterator iter = newData.begin(), end = newData.end(); iter != end;
+ ++iter) {
+ SData *entry = FromEditor<SData>(*iter);
+ if (entry)
+ inTarget.push_back(*entry);
+ }
+ }
+ }
+
+ SValue SEditorImpl::Get(TDataList &inTarget)
+ {
+ TObjList retval;
+ for (TDataList::iterator iter = inTarget.begin(), end = inTarget.end(); iter != end;
+ ++iter) {
+ TObjPtr item = ToEditor(*iter);
+ if (item)
+ retval.push_back(item);
+ }
+ return retval;
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, NVConstDataRef<QT3DSVec2> &inTarget)
+ {
+ if (inTarget.size()) {
+ m_AutoAllocator.deallocate((void *)inTarget.mData);
+ inTarget.mData = NULL;
+ inTarget.mSize = 0;
+ }
+ if (inData.hasValue()) {
+ TVec2List newData(inData->getData<TVec2List>());
+ if (newData.size()) {
+ size_t newDataSize = newData.size() * sizeof(QT3DSVec2);
+ QT3DSVec2 *newTargetData = (QT3DSVec2 *)m_AutoAllocator.allocate(
+ newDataSize, "Transition::m_Path", __FILE__, __LINE__);
+ memCopy(newTargetData, newData.data(), newDataSize);
+ inTarget = toDataRef(newTargetData, newData.size());
+ }
+ }
+ }
+
+ SValue SEditorImpl::Get(const NVConstDataRef<QT3DSVec2> &inTarget)
+ {
+ TVec2List retval;
+ if (inTarget.mSize)
+ retval.insert(retval.end(), inTarget.begin(), inTarget.end());
+ return retval;
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, SSend *&inTarget)
+ {
+ inTarget = NULL;
+ if (inData.hasValue() && inData->getType() == ValueTypes::ObjPtr) {
+ TObjPtr object = inData->getData<TObjPtr>();
+ SSend *newPtr = FromEditor<SSend>(object);
+ inTarget = newPtr;
+ }
+ }
+
+ SValue SEditorImpl::Get(SSend *inTarget)
+ {
+ if (!inTarget)
+ return SValue(TObjPtr());
+ return ToEditor(inTarget);
+ }
+
+ TObjPtr SEditorImpl::CreateExecutableContent(SStateNode &inParent,
+ const char8_t *inTypeName)
+ {
+ SExecutableContent *newExecutable = NULL;
+ TObjPtr newObj;
+ if (AreEqual(inTypeName, SSendEditor::GetTypeStr())) {
+ eastl::pair<SSend *, TObjPtr> theNewEditor = CreateEditorAndObject<SSend>();
+ GenerateUniqueId(*theNewEditor.first, "send");
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SScriptEditor::GetTypeStr())) {
+ eastl::pair<SScript *, TObjPtr> theNewEditor = CreateEditorAndObject<SScript>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SCancelEditor::GetTypeStr())) {
+ eastl::pair<SCancel *, TObjPtr> theNewEditor = CreateEditorAndObject<SCancel>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ if (newExecutable)
+ newExecutable->m_StateNodeParent = &inParent;
+ return newObj;
+ }
+
+ SValue SEditorImpl::GetSendId(const char8_t *expression)
+ {
+ if (isTrivial(expression))
+ return SValue((QT3DSU32)0);
+ return static_cast<QT3DSU32>(IExecutionContext::ParseTimeStrToMilliseconds(expression));
+ }
+
+ void SEditorImpl::SetSendId(const Option<SValue> &inData, const char8_t *&outExpression)
+ {
+ if (!isTrivial(outExpression))
+ m_AutoAllocator.deallocate(const_cast<char8_t *>(outExpression));
+
+ outExpression = "";
+
+ if (inData.hasValue() && inData->getType() == ValueTypes::U32) {
+ QT3DSU32 expr = inData->getData<QT3DSU32>();
+ char buffer[64];
+ sprintf(buffer, "%u", expr);
+ int len = strlen(buffer);
+ if (len < 61) {
+ buffer[len] = 'm';
+ buffer[len + 1] = 's';
+ buffer[len + 2] = 0;
+ }
+ outExpression = ToGraphStr(buffer);
+ }
+ }
+
+ IStateContext &SEditorImpl::GetStateContext() { return *m_StateContext; }
+
+ void SEditorImpl::SetTransactionManager(STransactionManagerImpl &manager)
+ {
+ m_TransactionManager = &manager;
+ }
+
+ bool IsLegalHistoryTarget(StateNodeTypes::Enum inType)
+ {
+ return inType == StateNodeTypes::State || inType == StateNodeTypes::Final
+ || inType == StateNodeTypes::Parallel;
+ }
+
+ void RecurseGetChildrenForHistoryTarget(SState &inNode,
+ eastl::vector<CRegisteredString> &retval)
+ {
+ for (TStateNodeList::iterator iter = inNode.m_Children.begin(),
+ end = inNode.m_Children.end();
+ iter != end; ++iter) {
+ if (IsLegalHistoryTarget(iter->m_Type)) {
+ retval.push_back(iter->m_Id);
+ if (iter->m_Type == StateNodeTypes::State)
+ RecurseGetChildrenForHistoryTarget(static_cast<SState &>(*iter), retval);
+ }
+ }
+ }
+
+ eastl::vector<CRegisteredString> SEditorImpl::GetLegalHistoryDefaultValues(SHistory &inData)
+ {
+ eastl::vector<CRegisteredString> retval;
+ if (inData.m_Parent && inData.m_Parent->m_Type == StateNodeTypes::State) {
+ SState &theState = static_cast<SState &>(*inData.m_Parent);
+ for (TStateNodeList::iterator iter = theState.m_Children.begin(),
+ end = theState.m_Children.end();
+ iter != end; ++iter) {
+ if (IsLegalHistoryTarget(iter->m_Type)) {
+ retval.push_back(iter->m_Id);
+ if (inData.m_Flags.IsDeep()) {
+ if (iter->m_Type == StateNodeTypes::State)
+ RecurseGetChildrenForHistoryTarget(static_cast<SState &>(*iter),
+ retval);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ void RecurseGetLegalParents(eastl::vector<CRegisteredString> &outResult,
+ SStateNode &inTarget, SStateNode &inCurrent)
+ {
+ // Basic checks
+ if (inCurrent.m_Type == StateNodeTypes::History
+ || inCurrent.m_Type == StateNodeTypes::Final || &inCurrent == &inTarget)
+ return;
+
+ // Parallel's cannot have finals, but their children could
+ bool isIllegalChild = (inTarget.m_Type == StateNodeTypes::Final
+ && inCurrent.m_Type == StateNodeTypes::Parallel)
+ || inCurrent.m_Type == StateNodeTypes::SCXML;
+
+ if (isIllegalChild == false) {
+ outResult.push_back(inCurrent.m_Id);
+ }
+ TStateNodeList *children = inCurrent.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ RecurseGetLegalParents(outResult, inTarget, *iter);
+ }
+ }
+ }
+
+ static bool lexi(CRegisteredString lhs, CRegisteredString rhs)
+ {
+ return strcmp(lhs.c_str(), rhs.c_str()) < 0;
+ }
+
+ eastl::vector<CRegisteredString> SEditorImpl::GetLegalParentIds(SStateNode &inNode)
+ {
+ // Do a depth first search and just do not include this node or any history or final
+ // nodes.
+ eastl::vector<CRegisteredString> retval;
+ RecurseGetLegalParents(retval, inNode, *m_StateContext->GetRoot());
+ eastl::sort(retval.begin(), retval.end(), lexi);
+ return retval;
+ }
+
+ struct SParentChange : public IChange
+ {
+ TObjPtr m_ChildObj;
+ TStateNodeList *m_OldList;
+ TStateNodeList &m_NewList;
+ SStateNode *m_OldParent;
+ SStateNode &m_NewParent;
+ SStateNode &m_Child;
+ QT3DSI32 m_RefCount;
+ SParentChange(TObjPtr childObj, TStateNodeList *oldL, TStateNodeList &newL,
+ SStateNode *oldp, SStateNode &newp, SStateNode &c)
+ : m_ChildObj(childObj)
+ , m_OldList(oldL)
+ , m_NewList(newL)
+ , m_OldParent(oldp)
+ , m_NewParent(newp)
+ , m_Child(c)
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+
+ virtual void Do()
+ {
+ if (m_OldList)
+ m_OldList->remove(m_Child);
+ m_NewList.push_back(m_Child);
+ m_Child.m_Parent = &m_NewParent;
+ }
+ virtual void Undo()
+ {
+ m_NewList.remove(m_Child);
+ if (m_OldList)
+ m_OldList->push_back(m_Child);
+ m_Child.m_Parent = m_OldParent;
+ }
+
+ virtual TObjPtr GetEditor() { return m_ChildObj; }
+ };
+
+ void SEditorImpl::SetParent(SStateNode &inNode, const Option<SValue> &inValue)
+ {
+ STransaction *theTransaction = m_TransactionManager->GetOpenTransactionImpl();
+ SStateNode *oldParent = inNode.m_Parent;
+ TEditorStr newParentId;
+ if (inValue.hasValue())
+ newParentId = inValue->getData<TEditorStr>();
+
+ SStateNode *newParent =
+ m_StateContext->FindStateNode(m_StringTable->RegisterStr(newParentId.c_str()));
+ if (newParent == NULL)
+ newParent = m_StateContext->GetRoot();
+ TStateNodeList *newList = newParent->GetChildren();
+ if (newList == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ SParentChange *theChange =
+ new SParentChange(ToEditor(inNode), oldParent ? oldParent->GetChildren() : NULL,
+ *newList, oldParent, *newParent, inNode);
+ theChange->Do();
+ if (theTransaction) {
+ theTransaction->m_Changes.push_back(theChange);
+ if (oldParent)
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*oldParent)));
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*newParent)));
+ }
+ CheckAndSetValidHistoryDefault(ToEditor(inNode));
+ // Fix the initial state of the old parent if it exists
+ if (oldParent) {
+ if (isTrivial(oldParent->GetInitialExpression())
+ && oldParent->GetInitialTransition()) {
+ STransition *theTransition = oldParent->GetInitialTransition();
+ if (theTransition != NULL && theTransition->m_Target.size()) {
+ bool foundItem = false;
+ for (QT3DSU32 idx = 0, end = theTransition->m_Target.size();
+ idx < end && foundItem == false; ++idx) {
+ if (theTransition->m_Target[idx] == &inNode)
+ foundItem = true;
+ }
+ if (foundItem) {
+ SetInitialAttribute(SValue(TEditorStr()), *oldParent);
+ }
+ }
+ }
+ // fix all history nodes
+ TStateNodeList *parentChildren = oldParent->GetChildren();
+ if (parentChildren) {
+ for (TStateNodeList::iterator iter = parentChildren->begin(),
+ end = parentChildren->end();
+ iter != end; ++iter) {
+ if (iter->m_Type == StateNodeTypes::History) {
+ CheckAndSetValidHistoryDefault(ToEditor(*iter));
+ }
+ }
+ }
+ }
+ // Set the new object inside the new parent.
+ ToEditor(inNode)->SetPropertyValue("position", QT3DSVec2(0, 0));
+ }
+
+ void SEditorImpl::CheckAndSetValidHistoryDefault(TObjPtr inHistoryNode)
+ {
+ if (inHistoryNode && AreEqual(inHistoryNode->TypeName(), "history")) {
+ eastl::vector<CRegisteredString> validChildren =
+ inHistoryNode->GetLegalValues("default");
+ TObjList existing = inHistoryNode->GetPropertyValue("default")->getData<TObjList>();
+ if (existing.size()) {
+ // Ensure all of the existing nodes are in the valid children list.
+ bool allValid = true;
+ for (size_t existingIdx = 0, existingEnd = existing.size();
+ existingIdx < existingEnd && allValid; ++existingIdx) {
+ CRegisteredString idStr =
+ m_StringTable->RegisterStr(existing[existingIdx]->GetId().c_str());
+ if (eastl::find(validChildren.begin(), validChildren.end(), idStr)
+ == validChildren.end())
+ allValid = false;
+ }
+ if (allValid)
+ return;
+ }
+ SetValidHistoryDefault(inHistoryNode);
+ }
+ }
+
+ void SEditorImpl::SetValidHistoryDefault(TObjPtr inHistoryNode)
+ {
+ if (inHistoryNode && AreEqual(inHistoryNode->TypeName(), "history")) {
+ eastl::vector<CRegisteredString> legalValues =
+ inHistoryNode->GetLegalValues("default");
+ if (legalValues.size()) {
+ TObjPtr firstLegal = GetObjectById(legalValues[0]);
+ TObjList propValue;
+ propValue.push_back(firstLegal);
+ inHistoryNode->SetPropertyValue("default", propValue);
+ }
+ }
+ }
+
+ MallocAllocator SBaseEditorFoundation::g_BaseAlloc;
+
+ bool SPropertyDecNameFinder::operator()(const SPropertyDeclaration &dec) const
+ {
+ return AreEqual(m_NameStr, dec.m_Name.c_str());
+ }
+
+ void IEditorObject::Append(const char8_t *inPropName, TObjPtr inObj)
+ {
+ Option<SValue> theProp = GetPropertyValue(inPropName);
+ if (theProp.hasValue() && theProp->getType() == ValueTypes::ObjPtrList
+ && inObj != NULL) {
+ TObjList theData = theProp->getData<TObjList>();
+ theData.push_back(inObj);
+ SetPropertyValue(inPropName, theData);
+ }
+ }
+
+ void IEditorObject::Remove(const char8_t *inPropName, TObjPtr inObj)
+ {
+ Option<SValue> theProp = GetPropertyValue(inPropName);
+ if (theProp.hasValue() && theProp->getType() == ValueTypes::ObjPtrList
+ && inObj != NULL) {
+ TObjList theData = theProp->getData<TObjList>();
+ TObjList::iterator theFind = eastl::find(theData.begin(), theData.end(), inObj);
+ if (theFind != theData.end())
+ theData.erase(theFind);
+ SetPropertyValue(inPropName, theData);
+ }
+ }
+
+ IEditor &IEditor::CreateEditor()
+ {
+ TFoundationPtr theFoundation = SBaseEditorFoundation::Create();
+ return *QT3DS_NEW(theFoundation->getAllocator(), SEditorImpl)(
+ theFoundation, IStringTable::CreateStringTable(theFoundation->getAllocator()));
+ }
+
+ IEditor *IEditor::CreateEditor(const char8_t *inFname)
+ {
+ CFileSeekableIOStream theFile(inFname, FileReadFlags());
+ if (theFile.IsOpen() == false)
+ return NULL;
+
+ return CreateEditor(inFname, theFile);
+ }
+
+ IEditor *IEditor::CreateEditor(const char8_t *inFname, IInStream &inStream)
+ {
+ IEditor &theEditor(CreateEditor());
+ if (static_cast<SEditorImpl &>(theEditor).Load(inStream, inFname))
+ return &theEditor;
+
+ theEditor.release();
+ return NULL;
+ }
+ }
+}
+}
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h
new file mode 100644
index 00000000..31c83afe
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_H
+#define QT3DS_STATE_EDITOR_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateSignalConnection.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/TaggedPointer.h"
+#include "EASTL/vector.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSVec2.h"
+#include "foundation/Qt3DSVec3.h"
+#include "Qt3DSStateInterpreter.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ class IEditorObject;
+ typedef NVScopedRefCounted<IEditorObject> TObjPtr;
+ typedef eastl::vector<TObjPtr> TObjList;
+
+ class IEditorChangeListener
+ {
+ protected:
+ virtual ~IEditorChangeListener() {}
+ public:
+ // The same object may be represented multiple times in this list.
+ virtual void OnDataChange(const TObjList &inChangedObjects,
+ const TObjList &inRemovedObjects) = 0;
+ };
+
+ struct EditorPropertyTypes
+ {
+ enum Enum {
+ NoProperty = 0,
+ Event, // string
+ Id, // string
+ Object, // IEditorObject
+ ObjectList, // IEditorObject list
+ // A string, but one from a set of strings.
+ StringSet, // string
+ String, // string, single line
+ BigString, // string than may be multiple lines long
+ Position, // vec2
+ Dimension, // vec2
+ PositionList, // vector<QT3DSVec2>
+ Script, // string
+ Color,
+ Boolean,
+ U32,
+ };
+ };
+
+ struct SPropertyDeclaration
+ {
+ CRegisteredString m_Name;
+ EditorPropertyTypes::Enum m_Type;
+ // for enumerations, the list of enumerations
+ SPropertyDeclaration() {}
+ SPropertyDeclaration(CRegisteredString inName, EditorPropertyTypes::Enum inType)
+ : m_Name(inName)
+ , m_Type(inType)
+ {
+ }
+ };
+
+ typedef eastl::vector<SPropertyDeclaration> TPropertyDeclarationList;
+
+ struct SPropertyDecNameFinder
+ {
+ const char8_t *m_NameStr;
+ SPropertyDecNameFinder(const char8_t *nmStr)
+ : m_NameStr(nmStr)
+ {
+ }
+ // Implemented in UICStateEditor.cpp to include "foundation/Utils.h"
+ bool operator()(const SPropertyDeclaration &dec) const;
+ };
+
+ typedef eastl::vector<QT3DSVec2> TVec2List;
+ typedef eastl::string TEditorStr;
+ typedef eastl::vector<TEditorStr> TEditorStrList;
+
+ // Our generic discriminated union type for all property values.
+ struct SValue;
+ struct SValueOpt;
+
+ class IEditor;
+
+ class IEditorObject : public NVRefCounted
+ {
+ protected:
+ virtual ~IEditorObject() {}
+ const char8_t *m_TypeName;
+
+ public:
+ IEditorObject(const char8_t *tn)
+ : m_TypeName(tn)
+ {
+ }
+ const char8_t *TypeName() const { return m_TypeName; }
+ // implemented in UICStateEditor.cpp using getpropertyvalue.
+ virtual TEditorStr GetId() = 0;
+ virtual TEditorStr GetDescription() = 0;
+ virtual void SetId(const TEditorStr &inId) = 0;
+ virtual void SetDescription(const TEditorStr &inName) = 0;
+ // Things only have one canonical parent.
+ virtual TObjPtr Parent() = 0;
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties) = 0;
+ virtual Option<SPropertyDeclaration> FindProperty(const char8_t *propName) = 0;
+ // The legal values for a property tend to be something that have to be calculated
+ // dynamically.
+ virtual eastl::vector<CRegisteredString> GetLegalValues(const char8_t *propName) = 0;
+
+ virtual Option<SValue> GetPropertyValue(const char8_t *inPropName) = 0;
+ virtual void SetPropertyValue(const char8_t *inPropName, const SValueOpt &inValue) = 0;
+ virtual bool endEdit() { return false; } // should SetPropertyValue end property edit
+
+ // Utility function implemented in UICStateEditor.cpp
+ virtual void Append(const char8_t *inPropName, TObjPtr inObj);
+ virtual void Remove(const char8_t *inPropName, TObjPtr inObj);
+
+ // Remove this from any parents. Remove any transitions pointing to this.
+ // Only works for state-node derived types.
+ // Executable content, onEntry onExit and such will not work with this.
+ // Also removes this item id from the id map. This is what is used to delete objects.
+ // Do not reattach yourself; callers should release all references to this object after
+ // calling
+ // this.
+ virtual void RemoveObjectFromGraph() = 0;
+
+ // Internal calls, don't call externally
+ virtual void RemoveIdFromContext() = 0;
+ virtual void AddIdToContext() = 0;
+
+ // Most things do not have any executable content.
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ return NVConstDataRef<InterpreterEventTypes::Enum>();
+ }
+
+ // Be aware that there are more types of executable content than indicated in the store;
+ // a lot more.
+ // So just ignore the types the story ignores.
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum /*inType*/)
+ {
+ return TObjList();
+ }
+
+ // inName can be an executable content:
+ //'script', 'send', 'cancel'
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum /*inType*/,
+ const char8_t * /*inName*/)
+ {
+ return TObjPtr();
+ }
+
+ virtual IEditor &GetEditor() = 0;
+ };
+
+ class ITransaction : public NVRefCounted
+ {
+ protected:
+ virtual ~ITransaction() {}
+ TEditorStr m_Name;
+
+ public:
+ ITransaction(const TEditorStr inName = TEditorStr())
+ : m_Name(inName)
+ {
+ }
+ TEditorStr GetName() const { return m_Name; }
+ // Send the signals collected during the creation of this transaction
+ virtual void SendDoSignals() = 0;
+ virtual TObjList GetEditedObjects() = 0;
+ virtual void Do() = 0;
+ virtual void Undo() = 0;
+ virtual bool Empty() = 0;
+ };
+
+ typedef NVScopedRefCounted<ITransaction> TTransactionPtr;
+
+ class ITransactionManager : public NVRefCounted
+ {
+ protected:
+ virtual ~ITransactionManager() {}
+ public:
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener) = 0;
+ // Undo/redo is supported via a transparent transaction system.
+ // calls are reentrant but last call will close the transaction object.
+ // Any changes to any editor objects will go through this transaction when they happen.
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName = TEditorStr()) = 0;
+ virtual TTransactionPtr GetOpenTransaction() = 0;
+ virtual void RollbackTransaction() = 0;
+ virtual void EndTransaction() = 0;
+ };
+
+ // Editor interface to a UICState dataset
+ class IEditor : public ITransactionManager
+ {
+ protected:
+ virtual ~IEditor() {}
+ public:
+ // Traditional editor interface.
+ virtual TObjPtr GetRoot() = 0;
+ // You can force the id to be a particular ID in some cases, this is useful for testing.
+ // ID is ignored for objects that don't have an id.
+ virtual TObjPtr Create(const char8_t *inTypeName, const char8_t *inId = 0) = 0;
+ virtual TObjPtr GetOrCreate(SSCXML &inData) = 0;
+ virtual TObjPtr GetOrCreate(SState &inData) = 0;
+ virtual TObjPtr GetOrCreate(STransition &inData) = 0;
+ virtual TObjPtr GetOrCreate(SParallel &inData) = 0;
+ virtual TObjPtr GetOrCreate(SHistory &inData) = 0;
+ virtual TObjPtr GetOrCreate(SFinal &inData) = 0;
+ virtual TObjPtr GetOrCreate(SDataModel &inData) = 0;
+ virtual TObjPtr GetOrCreate(SData &inData) = 0;
+ virtual TObjPtr ToEditor(SStateNode &inItem) = 0;
+
+ // Get a particular object by id. Useful in testing scenarios.
+ virtual TObjPtr GetObjectById(const char8_t *inId) = 0;
+
+ // Get an editor, if it already has been created, for this piece of graph data.
+ // Note that if it has not been created, this function couldn't possibly create it
+ // due to lack of type information.
+ virtual TObjPtr GetEditor(void *inGraphData) = 0;
+
+ // Copy save a subgraph to a string. Converts all top level positions to be relative to
+ // mouse pos.
+ virtual TEditorStr Copy(TObjList inObjects, const QT3DSVec2 &inMousePos) = 0;
+ virtual bool CanPaste(TObjPtr inTarget) = 0;
+ // Paste in, adding relative pos to all top level positions.
+ virtual void Paste(const TEditorStr &inCopiedObjects, TObjPtr inTarget,
+ const QT3DSVec2 &inRelativePos) = 0;
+
+ virtual TObjPtr GetLeastCommonAncestor(NVConstDataRef<TObjPtr> inObjects) = 0;
+
+ // Returns the list of send ids that have a delay on them.
+ virtual TObjList GetCancelableSendIds() = 0;
+
+ // Return the set of events in use in the state machine.
+ virtual TEditorStrList GetStateMachineEvents() = 0;
+
+ // Change content from one type to another.
+ virtual TObjPtr ChangeExecutableContentType(TObjPtr inContent,
+ const char8_t *newType) = 0;
+
+ virtual TEditorStr ToXML(TObjPtr inContent) = 0;
+ // Returns the error from parsing the xml or if everything went correctly replaces
+ // inContent
+ // with the result of parsing the xml and returns the new content.
+ virtual eastl::pair<TEditorStr, TObjPtr> FromXML(TObjPtr inContent,
+ const TEditorStr &ioEditedXML) = 0;
+
+ virtual bool Save(const char8_t *inFname) = 0;
+ virtual void Save(IOutStream &inStream) = 0;
+
+ // This must be called on history creation once it has been added to the proper parent.
+ virtual void SetValidHistoryDefault(TObjPtr inHistoryNode) = 0;
+
+ static IEditor &CreateEditor();
+
+ static IEditor *CreateEditor(const char8_t *inFname);
+ static IEditor *CreateEditor(const char8_t *inFname, IInStream &inStream);
+ };
+
+ typedef NVScopedRefCounted<IEditor> TEditorPtr;
+
+ struct SEditorImplTransactionScope
+ {
+ ITransactionManager &m_Editor;
+ TTransactionPtr m_Transaction;
+ SEditorImplTransactionScope(ITransactionManager &inEditor)
+ : m_Editor(inEditor)
+ , m_Transaction(inEditor.BeginTransaction())
+ {
+ }
+ ~SEditorImplTransactionScope() { m_Editor.EndTransaction(); }
+
+ TTransactionPtr operator->() { return m_Transaction; }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h
new file mode 100644
index 00000000..fc9411d2
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h
@@ -0,0 +1,1772 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_EDITORS_IMPL_H
+#define QT3DS_STATE_EDITOR_EDITORS_IMPL_H
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorValue.h"
+#include "Qt3DSStateEditorTransactionImpl.h"
+#include "Qt3DSStateTypes.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateEditorProperties.h"
+#include "foundation/Utils.h"
+#include "foundation/XML.h"
+#include "Qt3DSStateXMLIO.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateContext.h"
+#include "Qt3DSStateEditorImpl.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct EditorTypes
+ {
+ enum Enum {
+ UnknownEditor = 0,
+ SCXML,
+ State,
+ Parallel,
+ Transition,
+ History,
+ Final,
+ ExecutableContent,
+ OnEntry,
+ OnExit,
+ Send,
+ Raise,
+ If,
+ Else,
+ ElseIf,
+ Log,
+ Assign,
+ Script,
+ DataModel,
+ Data,
+ Cancel,
+ };
+ };
+
+ struct SEditorImplObject : public IEditorObject
+ {
+ TFoundationPtr m_Foundation;
+ SEditorImpl &m_Editor;
+ volatile QT3DSI32 mRefCount;
+ eastl::string m_Id;
+ eastl::string m_Description;
+ Option<QT3DSVec3> m_Color;
+ TPropertyAccessorList m_PropertyAccessors;
+
+ static void CreateAccessors(SEditorImpl &inData, TPropertyAccessorList &outAccessors,
+ bool includeId, bool includeColor)
+ {
+ if (includeId)
+ outAccessors.push_back(CreateEditorAccessor(inData, &SEditorImplObject::m_Id,
+ "id", EditorPropertyTypes::String));
+
+ outAccessors.push_back(
+ CreateEditorAccessor(inData, &SEditorImplObject::m_Description, "description",
+ EditorPropertyTypes::String));
+ if (includeColor)
+ outAccessors.push_back(CreateEditorAccessor(
+ inData, &SEditorImplObject::m_Color, "color", EditorPropertyTypes::Color));
+ }
+
+ SEditorImplObject(const char8_t *tn, SEditorImpl &inData,
+ const TPropertyAccessorList &inAccessors)
+ : IEditorObject(tn)
+ , m_Foundation(inData.m_EditorFoundation)
+ , m_Editor(inData)
+ , mRefCount(0)
+ , m_PropertyAccessors(inAccessors)
+ {
+ }
+
+ IPropertyAccessor *FindPropertyAccessor(const char8_t *inName)
+ {
+ CRegisteredString nameStr = m_Editor.m_StringTable->RegisterStr(inName);
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr)
+ return m_PropertyAccessors[idx].mPtr;
+ return NULL;
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ }
+
+ virtual Option<SPropertyDeclaration> FindProperty(const char8_t *propName)
+ {
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (AreEqual(m_PropertyAccessors[idx]->m_Declaration.m_Name.c_str(), propName))
+ return m_PropertyAccessors[idx]->m_Declaration;
+ }
+ return Empty();
+ }
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(const char8_t *propName)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(propName);
+ if (accessor)
+ return accessor->GetLegalValues(*this);
+ return eastl::vector<CRegisteredString>();
+ }
+
+ virtual Option<SValue> GetPropertyValue(const char8_t *inPropName)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ return accessor->Get(*this);
+ }
+ return Empty();
+ }
+ virtual void SetPropertyValue(const char8_t *inPropName, const SValueOpt &inValue)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ if (accessor->HandlesTransaction() == false) {
+ STransaction *theTransaction = m_Editor.GetOpenTransactionImpl();
+ if (theTransaction) {
+ Option<SValue> existing = accessor->Get(*this);
+ theTransaction->m_Changes.push_back(
+ new SChange(existing, inValue, *accessor, *this));
+ }
+ }
+ accessor->Set(*this, inValue);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ virtual TEditorStr GetId()
+ {
+ Option<SValue> propValue = GetPropertyValue("id");
+ if (propValue.hasValue())
+ return propValue->getData<TEditorStr>();
+ return TEditorStr();
+ }
+ virtual TEditorStr GetDescription() { return m_Description; }
+ // We have to go through the SPV interface to take transactions into account.
+
+ virtual void SetId(const TEditorStr &inName) { SetPropertyValue("id", SValue(inName)); }
+ virtual void SetDescription(const TEditorStr &inName)
+ {
+ SetPropertyValue("description", SValue(inName));
+ }
+
+ virtual TObjPtr Parent() { return TObjPtr(); }
+ virtual void RemoveObjectFromGraph() { QT3DS_ASSERT(false); }
+
+ virtual IEditor &GetEditor() { return m_Editor; }
+ };
+
+#define QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE \
+ void addRef() { atomicIncrement(&mRefCount); } \
+ void release() \
+ { \
+ TFoundationPtr fnd(m_Foundation); \
+ /*Ensure the editor sticks around till *after* we are gone. */ \
+ /*This is because our objects will keep several bits of the editor around */ \
+ QT3DS_IMPLEMENT_REF_COUNT_RELEASE(m_Foundation->getAllocator()); \
+ }
+
+ struct SSCXMLEditor : public SEditorImplObject
+ {
+ typedef SSCXML TStateType;
+ enum { EditorType = EditorTypes::SCXML };
+
+ SSCXML &m_Data;
+ eastl::string m_InitialComboValue;
+ bool m_endEdit;
+
+ static const char8_t *GetTypeStr() { return "scxml"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<SSCXMLEditor, SSCXML, STransition *> TInitialProp;
+ typedef SFlagBooleanProperty<SSCXMLEditor, SSCXMLFlags> TBindingProp;
+ TPropertyAccessorList &retval(inList);
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ typedef SDataProp<SSCXMLEditor, SSCXML, CRegisteredString> TNameProp;
+ typedef SInitialComboProp<SSCXMLEditor, SSCXML> TInitialComboProp;
+ typedef SDataProp<SSCXMLEditor, SSCXML, const char8_t *> TCharProp;
+ IPropertyAccessor *newAccessor =
+ QT3DS_NEW(alloc, TNameProp)(inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("name"),
+ EditorPropertyTypes::String),
+ &SSCXML::m_Name);
+ retval.push_back(newAccessor);
+ retval.push_back(CreateEditorAccessor(inEditorData,
+ &SEditorImplObject::m_Description,
+ "description", EditorPropertyTypes::String));
+ newAccessor = QT3DS_NEW(alloc, TBindingProp)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable, "binding",
+ "early", "late", &SSCXMLFlags::IsLateBinding, &SSCXMLFlags::SetLateBinding);
+ retval.push_back(newAccessor);
+
+ TInitialComboProp *theInitialCombo = QT3DS_NEW(alloc, TInitialComboProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initial"),
+ EditorPropertyTypes::StringSet));
+ retval.push_back(theInitialCombo);
+
+ TCharProp *theInitialExprProp = QT3DS_NEW(alloc, TCharProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initialexpr"),
+ EditorPropertyTypes::BigString),
+ &SSCXML::m_InitialExpr);
+ retval.push_back(theInitialExprProp);
+
+ retval.push_back(CreateDataAccessor<SSCXMLEditor>(
+ inEditorData, &SSCXML::m_DataModel, "datamodel", EditorPropertyTypes::Object));
+ newAccessor = QT3DS_NEW(alloc, SChildrenProperty<SSCXMLEditor>)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable);
+ retval.push_back(newAccessor);
+ // Replace the name property with one that writes to our data
+ return retval;
+ }
+
+ SSCXMLEditor(SSCXML &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ , m_endEdit(false)
+ {
+ }
+
+ void ApplyInitial(const TEditorStr &comboValue)
+ {
+ m_endEdit = false;
+
+ if (m_InitialComboValue == "(script expression)"
+ || comboValue == "(script expression)")
+ m_endEdit = true;
+
+ m_InitialComboValue = comboValue;
+ }
+
+ virtual bool endEdit()
+ {
+ bool bTemp = m_endEdit;
+ m_endEdit = false;
+ return bTemp;
+ }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual void RemoveObjectFromGraph() { QT3DS_ASSERT(false); }
+
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ eastl::vector<CRegisteredString> temp;
+ m_Editor.GetLegalInitialValues(m_Data, temp);
+ bool hasInitialState = temp.size() > 1;
+ bool hasInitialExpr = false;
+ CRegisteredString nameStr = m_Editor.RegisterStr("initial");
+ CRegisteredString initialExprStr = m_Editor.RegisterStr("initialexpr");
+
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ if (hasInitialState)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else if (m_PropertyAccessors[idx]->m_Declaration.m_Name == initialExprStr) {
+ if (hasInitialExpr)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else {
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ }
+
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ TEditorStr initialValue =
+ m_PropertyAccessors[idx]->Get(*this)->getData<TEditorStr>();
+ if (initialValue == "(script expression)")
+ hasInitialExpr = true;
+ }
+ }
+ }
+ };
+
+ struct SPositionalEditor : public SEditorImplObject
+ {
+ SPositionalEditor(const char8_t *inTypeName, SEditorImpl &inData,
+ const TPropertyAccessorList &inAccessors)
+ : SEditorImplObject(inTypeName, inData, inAccessors)
+ {
+ }
+ };
+
+ template <typename TListType, typename TListItem>
+ struct TListInsertDeleteChange : public IChange
+ {
+ TObjPtr m_EditorObject;
+ QT3DSI32 mRefCount;
+ TListType *m_ParentList;
+ TListItem *m_TargetContent;
+ QT3DSI32 m_ContentIdx;
+ bool m_AddOnDo;
+ // The item must be in the list for this to work.
+ TListInsertDeleteChange(TObjPtr inEditor, TListType *plist, TListItem *tc, bool addOnDo)
+ : m_EditorObject(inEditor)
+ , mRefCount(0)
+ , m_ParentList(plist)
+ , m_TargetContent(tc)
+ , m_ContentIdx(-1)
+ , m_AddOnDo(addOnDo)
+ {
+ for (typename TListType::iterator iter = m_ParentList->begin(),
+ end = m_ParentList->end();
+ iter != end; ++iter) {
+ if (&(*iter) == m_TargetContent)
+ break;
+ // setup content idx to point to the item just our target item.
+ ++m_ContentIdx;
+ }
+ }
+ virtual void addRef() { atomicIncrement(&mRefCount); }
+ virtual void release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ delete this;
+ }
+ }
+ void add()
+ {
+ if (m_ContentIdx > -1) {
+ QT3DSI32 idx = m_ContentIdx - 1;
+ typename TListType::iterator iter = m_ParentList->begin();
+
+ for (typename TListType::iterator end = m_ParentList->end();
+ iter != end && idx > -1; ++iter, --idx) {
+ };
+
+ if (iter != m_ParentList->end())
+ m_ParentList->insert_after(*iter, *m_TargetContent);
+ else
+ m_ParentList->push_back(*m_TargetContent);
+ } else
+ m_ParentList->push_front(*m_TargetContent);
+ }
+ void remove() { m_ParentList->remove(*m_TargetContent); }
+
+ virtual void Do()
+ {
+ if (m_AddOnDo)
+ add();
+ else
+ remove();
+ }
+ virtual void Undo()
+ {
+ if (m_AddOnDo)
+ remove();
+ else
+ add();
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObject; }
+ };
+
+ struct SExecutableContentChange
+ : public TListInsertDeleteChange<TExecutableContentList, SExecutableContent>
+ {
+ typedef TListInsertDeleteChange<TExecutableContentList, SExecutableContent> TBase;
+ SExecutableContentChange(TObjPtr inEditor, TExecutableContentList *plist,
+ SExecutableContent *tc, bool addOnDo)
+ : TBase(inEditor, plist, tc, addOnDo)
+ {
+ }
+ };
+
+ struct SStateEditor : public SPositionalEditor
+ {
+ typedef SState TStateType;
+ enum { EditorType = EditorTypes::State };
+ SState &m_Data;
+ eastl::string m_InitialComboValue;
+ bool m_endEdit;
+ static const char8_t *GetTypeStr() { return "state"; }
+
+ template <typename TEditorType>
+ static void CreateStateNodeChildren(SEditorImpl &inEditorData,
+ TPropertyAccessorList &retval)
+ {
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ IPropertyAccessor *newAccessor = QT3DS_NEW(alloc, SChildrenProperty<TEditorType>)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable);
+ retval.push_back(newAccessor);
+ }
+
+ template <typename TEditorType>
+ static void CreatePositionColorAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &retval)
+ {
+ typedef SOptionAccessorProp<TEditorType, SStateNode, QT3DSVec2> TVec2Access;
+ typedef SOptionAccessorProp<TEditorType, SStateNode, QT3DSVec3> TVec3Access;
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("position"),
+ EditorPropertyTypes::Position),
+ &SStateNode::GetPosition, &SStateNode::SetPosition));
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("dimension"),
+ EditorPropertyTypes::Position),
+ &SStateNode::GetDimension, &SStateNode::SetDimension));
+
+ retval.push_back(QT3DS_NEW(alloc, TVec3Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("color"),
+ EditorPropertyTypes::Color),
+ &SStateNode::GetColor, &SStateNode::SetColor));
+ }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<SStateEditor, SState, STransition *> TInitialProp;
+ typedef SDataIdProp<SStateEditor, SState> TIdPropType;
+ typedef SInitialTargetProp<SStateEditor, SState> TInitialTargetProp;
+ typedef SInitialComboProp<SStateEditor, SState> TInitialComboProp;
+ typedef SDataProp<SStateEditor, SState, const char8_t *> TCharProp;
+
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SState::m_Id));
+
+ typedef SParentProp<SStateEditor> TParentPropType;
+ retval.push_back(QT3DS_NEW(alloc, TParentPropType)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+
+ TInitialComboProp *theInitialCombo = QT3DS_NEW(alloc, TInitialComboProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initial"),
+ EditorPropertyTypes::StringSet));
+ retval.push_back(theInitialCombo);
+
+ TCharProp *theInitialExprProp = QT3DS_NEW(alloc, TCharProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initialexpr"),
+ EditorPropertyTypes::BigString),
+ &SState::m_InitialExpr);
+ retval.push_back(theInitialExprProp);
+ retval.push_back(CreateDataAccessor<SStateEditor>(
+ inEditorData, &SState::m_DataModel, "datamodel", EditorPropertyTypes::Object));
+ CreateStateNodeChildren<SStateEditor>(inEditorData, retval);
+ CreatePositionColorAccessors<SStateEditor>(inEditorData, retval);
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+ // Replace the name property with one that writes to our data
+ return retval;
+ }
+ SStateEditor(SState &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ , m_endEdit(false)
+ {
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ eastl::vector<CRegisteredString> temp;
+ m_Editor.GetLegalInitialValues(m_Data, temp);
+ bool hasInitialState = temp.size() > 1;
+ bool hasInitialExpr = false;
+ CRegisteredString nameStr = m_Editor.RegisterStr("initial");
+ CRegisteredString initialExprStr = m_Editor.RegisterStr("initialexpr");
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ if (hasInitialState)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else if (m_PropertyAccessors[idx]->m_Declaration.m_Name == initialExprStr) {
+ if (hasInitialExpr)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+
+ if (hasInitialState
+ && m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ TEditorStr initialValue =
+ m_PropertyAccessors[idx]->Get(*this)->getData<TEditorStr>();
+ if (initialValue == "(script expression)")
+ hasInitialExpr = true;
+ }
+ }
+ }
+
+ void ApplyInitial(const TEditorStr &comboValue)
+ {
+ m_endEdit = false;
+
+ if (m_InitialComboValue == "(script expression)"
+ || comboValue == "(script expression)")
+ m_endEdit = true;
+
+ m_InitialComboValue = comboValue;
+ }
+
+ virtual bool endEdit()
+ {
+ bool bTemp = m_endEdit;
+ m_endEdit = false;
+ return bTemp;
+ }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ struct SObjListContains
+ {
+ const TObjList &m_Targets;
+ SObjListContains(const TObjList &t)
+ : m_Targets(t)
+ {
+ }
+ bool operator()(const TObjPtr &inObj) const
+ {
+ return eastl::find(m_Targets.begin(), m_Targets.end(), inObj.mPtr)
+ != m_Targets.end();
+ }
+ };
+
+ static void CheckAndRemoveStateFromTransitionProperty(TObjList &removedItems,
+ IEditorObject &inEditorObj,
+ const char8_t *propName)
+ {
+ Option<SValue> propVal = inEditorObj.GetPropertyValue(propName);
+ if (propVal.hasValue()) {
+ if (propVal->getType() == ValueTypes::ObjPtr) {
+ TObjPtr transProp = propVal->getData<TObjPtr>();
+ if (transProp) {
+ TObjList propValValues =
+ transProp->GetPropertyValue("target")->getData<TObjList>();
+ TObjList::iterator lastIter =
+ eastl::remove_if(propValValues.begin(), propValValues.end(),
+ SObjListContains(removedItems));
+ if (lastIter != propValValues.end()) {
+ propValValues.erase(lastIter, propValValues.end());
+ if (propValValues.empty())
+ inEditorObj.SetPropertyValue(propName, TObjPtr());
+ else
+ transProp->SetPropertyValue("target", propValValues);
+ }
+ }
+ } else if (propVal->getType() == ValueTypes::String) {
+ TEditorStr theIdStr = propVal->getData<TEditorStr>();
+ for (size_t idx = 0, end = removedItems.size(); idx < end; ++idx) {
+ if (removedItems[idx]->GetId() == theIdStr) {
+ inEditorObj.SetPropertyValue(propName, TEditorStr());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ static void RemoveTransitionsPointingTo(TObjList &removedItems,
+ IEditorObject &inEditorObj)
+ {
+ if (AreEqual(inEditorObj.TypeName(), "transition")) {
+ TObjList targets = inEditorObj.GetPropertyValue("target")->getData<TObjList>();
+ TObjList::iterator lastIter = eastl::remove_if(targets.begin(), targets.end(),
+ SObjListContains(removedItems));
+ if (lastIter != targets.end()) {
+ targets.erase(lastIter, targets.end());
+ inEditorObj.SetPropertyValue("target", targets);
+ if (targets.size() == 0)
+ inEditorObj.RemoveObjectFromGraph();
+ }
+ } else {
+ // state and parallel
+ CheckAndRemoveStateFromTransitionProperty(removedItems, inEditorObj, "initial");
+ // history
+ CheckAndRemoveStateFromTransitionProperty(removedItems, inEditorObj,
+ "transition");
+ }
+ Option<SValue> childListOpt = inEditorObj.GetPropertyValue("children");
+ if (childListOpt.hasValue()) {
+ TObjList childList = childListOpt->getData<TObjList>();
+ for (size_t idx = 0, end = childList.size(); idx < end; ++idx)
+ RemoveTransitionsPointingTo(removedItems, *childList[idx]);
+ }
+ }
+ static void RemoveObjectFromGraph(IEditorObject &inEditorObj,
+ TObjList &outRemovedObjects)
+ {
+ TObjPtr parentPtr = inEditorObj.Parent();
+ outRemovedObjects.push_back(&inEditorObj);
+ if (parentPtr)
+ parentPtr->Remove("children", &inEditorObj);
+
+ Option<SValue> nodeChildrenOpt = inEditorObj.GetPropertyValue("children");
+ if (nodeChildrenOpt.hasValue()) {
+ TObjList nodeChildren(nodeChildrenOpt->getData<TObjList>());
+ for (size_t idx = 0, end = nodeChildren.size(); idx < end; ++idx)
+ RemoveObjectFromGraph(*nodeChildren[idx], outRemovedObjects);
+ }
+ }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ // Simple and slow as hell.
+ static void RemoveObjectFromGraph(SEditorImplObject &inEditorObj)
+ {
+ TObjList removedItems;
+ TObjPtr oldParent = inEditorObj.Parent();
+ RemoveObjectFromGraph(inEditorObj, removedItems);
+ RemoveTransitionsPointingTo(removedItems, *inEditorObj.m_Editor.GetRoot());
+ for (size_t idx = 0, end = removedItems.size(); idx < end; ++idx) {
+ inEditorObj.m_Editor.m_TransactionManager->OnObjectDeleted(removedItems[idx]);
+ inEditorObj.RemoveIdFromContext();
+ }
+ if (oldParent) {
+ TObjList children =
+ oldParent->GetPropertyValue("children")->getData<TObjList>();
+ for (size_t childIdx = 0, childEnd = children.size(); childIdx < childEnd;
+ ++childIdx) {
+ inEditorObj.m_Editor.CheckAndSetValidHistoryDefault(children[childIdx]);
+ }
+ }
+ }
+
+ virtual void RemoveObjectFromGraph() { RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum retval[] = { InterpreterEventTypes::StateEnter,
+ InterpreterEventTypes::StateExit };
+ return toConstDataRef(retval, 2);
+ }
+
+ template <typename TListType>
+ static TObjList ListDataToEditor(TListType &inList, SEditorImplObject &ioObj)
+ {
+ TObjList retval;
+ typename TListType::iterator iter = inList.begin();
+ // Take the first one.
+ if (iter != inList.end()) {
+ for (TExecutableContentList::iterator
+ contentIter = iter->m_ExecutableContent.begin(),
+ contentEnd = iter->m_ExecutableContent.end();
+ contentIter != contentEnd; ++contentIter) {
+ TObjPtr editorData = ioObj.m_Editor.ExecutableContentToEditor(*contentIter);
+ retval.push_back(editorData);
+ }
+ }
+ return retval;
+ }
+
+ static TObjList ListDataToEditor(InterpreterEventTypes::Enum inType,
+ SEntryExitBase &inItem, SEditorImplObject &ioObj)
+ {
+ switch (inType) {
+ case InterpreterEventTypes::StateEnter:
+ return ListDataToEditor(inItem.m_OnEntry, ioObj);
+ case InterpreterEventTypes::StateExit:
+ return ListDataToEditor(inItem.m_OnExit, ioObj);
+ default:
+ break;
+ }
+ QT3DS_ASSERT(false);
+ return TObjList();
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ return SStateEditor::ListDataToEditor(inType, m_Data, *this);
+ }
+
+ template <typename TListItemType, typename TListType>
+ static TObjPtr DoCreateAppendExecutableContent(SEditorImplObject &inObj,
+ TListType &inList, const char8_t *inName,
+ SStateNode &inNode)
+ {
+ NVScopedRefCounted<IChange> theNewListItemChange;
+ if (inList.empty()) {
+ TListItemType *newType =
+ QT3DS_NEW(inObj.m_Editor.m_AutoAllocator, TListItemType)();
+ inList.push_back(*newType);
+ theNewListItemChange = new TListInsertDeleteChange<TListType, TListItemType>(
+ inObj, &inList, newType, true);
+ }
+ TListItemType &theFront = *inList.begin();
+ TObjPtr retval = inObj.m_Editor.CreateExecutableContent(inNode, inName);
+ SExecutableContent *theContent = inObj.m_Editor.ExecutableContentFromEditor(retval);
+ theFront.m_ExecutableContent.push_back(*theContent);
+ if (inObj.m_Editor.GetOpenTransactionImpl()) {
+ if (theNewListItemChange)
+ inObj.m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(
+ theNewListItemChange);
+
+ NVScopedRefCounted<SExecutableContentChange> newChange =
+ new SExecutableContentChange(inObj, &theFront.m_ExecutableContent,
+ theContent, true);
+ inObj.m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(newChange.mPtr);
+ }
+ return retval;
+ }
+
+ static TObjPtr DoCreateAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName,
+ SEditorImplObject &inObj,
+ SEntryExitBase &inItem)
+ {
+ switch (inType) {
+ case InterpreterEventTypes::StateEnter:
+ return DoCreateAppendExecutableContent<SOnEntry>(inObj, inItem.m_OnEntry,
+ inName, inItem);
+ case InterpreterEventTypes::StateExit:
+ return DoCreateAppendExecutableContent<SOnExit>(inObj, inItem.m_OnExit, inName,
+ inItem);
+ default:
+ break;
+ }
+
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ return SStateEditor::DoCreateAppendExecutableContent(inType, inName, *this, m_Data);
+ }
+ };
+
+ struct SParallelEditor : public SPositionalEditor
+ {
+ typedef SParallel TStateType;
+ enum { EditorType = EditorTypes::Parallel };
+ SParallel &m_Data;
+ static const char8_t *GetTypeStr() { return "parallel"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+
+ typedef SInitialTargetProp<SParallelEditor, SParallel> TInitialTargetProp;
+ TPropertyAccessorList &retval(inList);
+ typedef SDataIdProp<SParallelEditor, SParallel> TIdPropType;
+ typedef SParentProp<SParallelEditor> TParallelParentProp;
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SParallel::m_Id));
+
+ retval.push_back(QT3DS_NEW(alloc, TParallelParentProp)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ retval.push_back(CreateDataAccessor<SParallelEditor>(
+ inEditorData, &SState::m_DataModel, "datamodel", EditorPropertyTypes::Object));
+ SStateEditor::CreateStateNodeChildren<SParallelEditor>(inEditorData, retval);
+ SStateEditor::CreatePositionColorAccessors<SParallelEditor>(inEditorData, retval);
+ // Replace the name property with one that writes to our data
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+ return retval;
+ }
+
+ SParallelEditor(SParallel &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum retval[] = { InterpreterEventTypes::StateEnter,
+ InterpreterEventTypes::StateExit };
+ return toConstDataRef(retval, 2);
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ return SStateEditor::ListDataToEditor(inType, m_Data, *this);
+ }
+
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ return SStateEditor::DoCreateAppendExecutableContent(inType, inName, *this, m_Data);
+ }
+ };
+
+ struct SFinalEditor : public SPositionalEditor
+ {
+ typedef SFinal TStateType;
+ enum { EditorType = EditorTypes::Final };
+
+ SFinal &m_Data;
+ static const char8_t *GetTypeStr() { return "final"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ typedef SDataIdProp<SFinalEditor, SFinal> TIdPropType;
+ typedef SInitialTargetProp<SFinalEditor, SFinal> TInitialTargetProp;
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ typedef SParentProp<SFinalEditor> TFinalParentProp;
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SFinal::m_Id));
+ retval.push_back(QT3DS_NEW(alloc, TFinalParentProp)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ SStateEditor::CreatePositionColorAccessors<SFinalEditor>(inEditorData, retval);
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+ // Replace the name property with one that writes to our data
+ return retval;
+ }
+
+ SFinalEditor(SFinal &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum retval[] = { InterpreterEventTypes::StateEnter,
+ InterpreterEventTypes::StateExit };
+ return toConstDataRef(retval, 2);
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ return SStateEditor::ListDataToEditor(inType, m_Data, *this);
+ }
+
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ return SStateEditor::DoCreateAppendExecutableContent(inType, inName, *this, m_Data);
+ }
+ };
+
+ struct SHistoryEditor : public SPositionalEditor
+ {
+ typedef SHistory TStateType;
+ enum { EditorType = EditorTypes::History };
+ SHistory &m_Data;
+ static const char8_t *GetTypeStr() { return "history"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<SHistoryEditor, SHistory, STransition *> TTransitionProp;
+
+ typedef SFlagBooleanProperty<SHistoryEditor, SHistoryFlags> THistoryFlagsProp;
+ typedef SDataIdProp<SHistoryEditor, SHistory> TIdPropType;
+ typedef SHistoryTransitionProp<SHistoryEditor> THistoryTransitionProp;
+ typedef SParentProp<SHistoryEditor> THistoryParentProp;
+ typedef SInitialTargetProp<SHistoryEditor, SHistory> TInitialTargetProp;
+
+ TPropertyAccessorList &retval(inList);
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SHistory::m_Id));
+ retval.push_back(QT3DS_NEW(alloc, THistoryParentProp)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ SStateEditor::CreatePositionColorAccessors<SHistoryEditor>(inEditorData, retval);
+ retval.push_back(QT3DS_NEW(
+ alloc, THistoryFlagsProp(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable, "type", "shallow", "deep",
+ &SHistoryFlags::IsDeep, &SHistoryFlags::SetDeep)));
+
+ retval.push_back(QT3DS_NEW(alloc, TTransitionProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("transition"),
+ EditorPropertyTypes::ObjectList),
+ &SHistory::m_Transition));
+
+ retval.push_back(QT3DS_NEW(alloc, THistoryTransitionProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("default"),
+ EditorPropertyTypes::ObjectList)));
+
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+
+ return retval;
+ }
+
+ SHistoryEditor(SHistory &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+ };
+
+ struct STransitionEditor : public SEditorImplObject
+ {
+ typedef STransition TStateType;
+ STransition &m_Data;
+ enum { EditorType = EditorTypes::Transition };
+ static const char8_t *GetTypeStr() { return "transition"; }
+ TVec2List m_PathList;
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<STransitionEditor, STransition, const char8_t *> TTransCharProp;
+ typedef SDataProp<STransitionEditor, STransition, CRegisteredString>
+ TTransRegStrProp;
+ typedef SDataProp<STransitionEditor, STransition, NVConstDataRef<SStateNode *>>
+ TTransTargetProp;
+ typedef SDataProp<STransitionEditor, STransition, NVConstDataRef<QT3DSVec2>>
+ TTransPathProp;
+ typedef SFlagBooleanProperty<STransitionEditor, STransitionFlags>
+ TTransitionFlagsProp;
+ typedef SDataIdProp<STransitionEditor, STransition> TIdPropType;
+ typedef SOptionAccessorProp<STransitionEditor, STransition, QT3DSVec2> TVec2Access;
+ typedef SOptionAccessorProp<STransitionEditor, STransition, QT3DSVec3> TVec3Access;
+ typedef SDataIdProp<STransitionEditor, STransition> TIdPropType;
+
+ TPropertyAccessorList &retval(inList);
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+
+ retval.push_back(
+ QT3DS_NEW(alloc, TIdPropType)(inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"),
+ EditorPropertyTypes::String),
+ &STransition::m_Id));
+
+ retval.push_back(QT3DS_NEW(alloc, TTransRegStrProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("event"),
+ EditorPropertyTypes::String),
+ &STransition::m_Event));
+
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ IPropertyAccessor *accessor = QT3DS_NEW(alloc, TTransCharProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("cond"),
+ EditorPropertyTypes::BigString),
+ &STransition::m_Condition);
+
+ retval.push_back(accessor);
+ accessor = QT3DS_NEW(alloc, TTransTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("target"),
+ EditorPropertyTypes::ObjectList),
+ &STransition::m_Target);
+ retval.push_back(accessor);
+ retval.push_back(QT3DS_NEW(alloc, TTransitionFlagsProp)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable, "type",
+ "external", "internal", &STransitionFlags::IsInternal,
+ &STransitionFlags::SetInternal));
+
+ accessor = QT3DS_NEW(alloc, TTransPathProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("path"),
+ EditorPropertyTypes::PositionList),
+ &STransition::m_Path);
+ retval.push_back(accessor);
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("position"),
+ EditorPropertyTypes::PositionList),
+ &STransition::GetPosition, &STransition::SetPosition));
+
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("end position"),
+ EditorPropertyTypes::PositionList),
+ &STransition::GetEndPosition, &STransition::SetEndPosition));
+
+ return retval;
+ }
+
+ STransitionEditor(STransition &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum data[] = { InterpreterEventTypes::Transition };
+ return toConstDataRef(data, 1);
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ TObjList retval;
+ if (inType == InterpreterEventTypes::Transition) {
+ for (TExecutableContentList::iterator iter = m_Data.m_ExecutableContent.begin(),
+ end = m_Data.m_ExecutableContent.end();
+ iter != end; ++iter) {
+ retval.push_back(m_Editor.ExecutableContentToEditor(*iter));
+ }
+ }
+ return retval;
+ }
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ TObjPtr retval;
+ if (inType == InterpreterEventTypes::Transition) {
+ retval = m_Editor.CreateExecutableContent(m_Data, inName);
+ SExecutableContent *theContent = m_Editor.ExecutableContentFromEditor(retval);
+ m_Data.m_ExecutableContent.push_back(*theContent);
+ if (m_Editor.GetOpenTransactionImpl()) {
+ NVScopedRefCounted<SExecutableContentChange> newChange =
+ new SExecutableContentChange(*this, &m_Data.m_ExecutableContent,
+ theContent, true);
+ m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(newChange.mPtr);
+ }
+ }
+ return retval;
+ }
+ };
+
+ struct SExecutableContentParentInfo
+ {
+ SExecutableContent &content;
+ SEditorImpl &m_Editor;
+ TExecutableContentList *parentList;
+ TObjPtr editorObj;
+ TOnEntryList *entryList;
+ SOnEntry *entryItem;
+ TOnExitList *exitList;
+ SOnExit *exitItem;
+ SExecutableContentParentInfo(SExecutableContent &c, SEditorImpl &e)
+ : content(c)
+ , m_Editor(e)
+ , parentList(NULL)
+ , entryList(NULL)
+ , entryItem(NULL)
+ , exitList(NULL)
+ , exitItem(NULL)
+ {
+ if (GetContent().m_StateNodeParent) {
+ editorObj = m_Editor.ToEditor(*GetContent().m_StateNodeParent);
+ if (GetContent().m_StateNodeParent->m_Type == StateNodeTypes::Transition)
+ parentList = &static_cast<STransition *>(GetContent().m_StateNodeParent)
+ ->m_ExecutableContent;
+ else {
+ SExecutableContent *targetContent = &GetContent();
+ entryList = GetContent().m_StateNodeParent->GetOnEntryList();
+ exitList = GetContent().m_StateNodeParent->GetOnExitList();
+ if (entryList) {
+ for (TOnEntryList::iterator iter = entryList->begin(),
+ end = entryList->end();
+ iter != end && parentList == NULL; ++iter) {
+ for (TExecutableContentList::iterator
+ contentIter = iter->m_ExecutableContent.begin(),
+ contentEnd = iter->m_ExecutableContent.end();
+ contentIter != contentEnd && parentList == NULL;
+ ++contentIter) {
+ if (&(*contentIter) == targetContent) {
+ parentList = &iter->m_ExecutableContent;
+ exitList = NULL;
+ entryItem = &(*iter);
+ }
+ }
+ }
+ }
+ if (parentList == NULL && exitList != NULL) {
+ for (TOnExitList::iterator iter = exitList->begin(),
+ end = exitList->end();
+ iter != end && parentList == NULL; ++iter) {
+ for (TExecutableContentList::iterator
+ contentIter = iter->m_ExecutableContent.begin(),
+ contentEnd = iter->m_ExecutableContent.end();
+ contentIter != contentEnd && parentList == NULL;
+ ++contentIter) {
+ if (&(*contentIter) == targetContent) {
+ parentList = &iter->m_ExecutableContent;
+ entryList = NULL;
+ exitItem = &(*iter);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ editorObj = m_Editor.ToEditor(GetContent().m_Parent);
+ parentList = &GetContent().m_Parent->m_Children;
+ }
+ }
+ SExecutableContent &GetContent() { return content; }
+ };
+
+ struct SExecutableContentEditor : public SEditorImplObject
+ {
+ SExecutableContentEditor(const char8_t *inTypeStr, SEditorImpl &inEditorData,
+ const TPropertyAccessorList &inList)
+ : SEditorImplObject(inTypeStr, inEditorData, inList)
+ {
+ }
+
+ virtual SExecutableContent &GetContent() = 0;
+
+ virtual void RemoveObjectFromGraph()
+ {
+ SExecutableContentParentInfo parentInfo(GetContent(), m_Editor);
+ TExecutableContentList *parentList = parentInfo.parentList;
+ TObjPtr editorObj = parentInfo.editorObj;
+ TOnEntryList *entryList = parentInfo.entryList;
+ SOnEntry *entryItem = parentInfo.entryItem;
+ TOnExitList *exitList = parentInfo.exitList;
+ SOnExit *exitItem = parentInfo.exitItem;
+
+ if (parentList) {
+ NVScopedRefCounted<SExecutableContentChange> change =
+ new SExecutableContentChange(editorObj, parentList, &GetContent(), false);
+ change->Do();
+ if (m_Editor.GetOpenTransactionImpl())
+ m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(change.mPtr);
+ // Perhaps remove the item itself
+ if (parentList->empty()) {
+ NVScopedRefCounted<IChange> newChange;
+ if (entryItem)
+ newChange = new TListInsertDeleteChange<TOnEntryList, SOnEntry>(
+ editorObj, entryList, entryItem, false);
+ else if (exitItem)
+ newChange = new TListInsertDeleteChange<TOnExitList, SOnExit>(
+ editorObj, exitList, exitItem, false);
+
+ if (newChange) {
+ newChange->Do();
+ if (m_Editor.GetOpenTransactionImpl())
+ m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(newChange);
+ }
+ }
+ }
+ }
+ };
+
+ struct SSendEditor : public SExecutableContentEditor
+ {
+ typedef SSend TStateType;
+ enum { EditorType = EditorTypes::Send };
+ SSend &m_Data;
+ static const char8_t *GetTypeStr() { return "send"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ typedef SDataIdProp<SSendEditor, SSend> TIdPropType;
+
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SSendEditor>(inData, &SSend::m_Event, "event",
+ EditorPropertyTypes::String));
+ NVFoundationBase &fnd = inData.m_EditorFoundation->getFoundation();
+
+ retval.push_back(QT3DS_NEW(fnd.getAllocator(), TIdPropType)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SSend::m_Id));
+
+ retval.push_back(QT3DS_NEW(fnd.getAllocator(), SDelayProp<SSendEditor>)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr("delay"), EditorPropertyTypes::U32)));
+ return retval;
+ }
+ SSendEditor(SSend &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SRaiseEditor : public SExecutableContentEditor
+ {
+ typedef SRaise TStateType;
+ enum { EditorType = EditorTypes::Raise };
+ SRaise &m_Data;
+ static const char8_t *GetTypeStr() { return "raise"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SRaiseEditor>(inData, &SRaise::m_Event, "event",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+ SRaiseEditor(SRaise &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SLogEditor : public SExecutableContentEditor
+ {
+ typedef SLog TStateType;
+ enum { EditorType = EditorTypes::Log };
+ SLog &m_Data;
+ static const char8_t *GetTypeStr() { return "log"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SLogEditor>(inData, &SLog::m_Label, "label",
+ EditorPropertyTypes::String));
+ retval.push_back(CreateDataAccessor<SLogEditor>(inData, &SLog::m_Expression, "expr",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+ SLogEditor(SLog &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SAssignEditor : public SExecutableContentEditor
+ {
+ typedef SAssign TStateType;
+ enum { EditorType = EditorTypes::Assign };
+ SAssign &m_Data;
+ static const char8_t *GetTypeStr() { return "assign"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SAssignEditor>(
+ inData, &SAssign::m_Location, "location", EditorPropertyTypes::String));
+ retval.push_back(CreateDataAccessor<SAssignEditor>(
+ inData, &SAssign::m_Expression, "expr", EditorPropertyTypes::String));
+ return retval;
+ }
+ SAssignEditor(SAssign &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SIfEditor : public SExecutableContentEditor
+ {
+ typedef SIf TStateType;
+ enum { EditorType = EditorTypes::If };
+ SIf &m_Data;
+ static const char8_t *GetTypeStr() { return "if"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SIfEditor>(inData, &SIf::m_Cond, "cond",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SIfEditor(SIf &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SElseIfEditor : public SExecutableContentEditor
+ {
+ typedef SElseIf TStateType;
+ enum { EditorType = EditorTypes::ElseIf };
+ SElseIf &m_Data;
+ static const char8_t *GetTypeStr() { return "elseif"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SElseIfEditor>(inData, &SElseIf::m_Cond, "cond",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SElseIfEditor(SElseIf &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SElseEditor : public SExecutableContentEditor
+ {
+ typedef SElse TStateType;
+ enum { EditorType = EditorTypes::Else };
+ SElse &m_Data;
+ static const char8_t *GetTypeStr() { return "else"; }
+
+ SElseEditor(SElse &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData, inList)
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SScriptEditor : public SExecutableContentEditor
+ {
+ typedef SScript TStateType;
+ enum { EditorType = EditorTypes::Script };
+ SScript &m_Data;
+ static const char8_t *GetTypeStr() { return "script"; }
+
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SScriptEditor>(
+ inData, &SScript::m_Data, "content", EditorPropertyTypes::BigString));
+ return retval;
+ }
+
+ SScriptEditor(SScript &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SCancelEditor : public SExecutableContentEditor
+ {
+ typedef SData TStateType;
+ enum { EditorType = EditorTypes::Cancel };
+ SCancel &m_Data;
+ static const char8_t *GetTypeStr() { return "cancel"; }
+
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList retval;
+ retval.push_back(CreateDataAccessor<SCancelEditor>(
+ inData, &SCancel::m_Send, "sendid", EditorPropertyTypes::Object));
+ retval.push_back(CreateDataAccessor<SCancelEditor>(
+ inData, &SCancel::m_IdExpression, "sendidexpr", EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SCancelEditor(SCancel &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SDataModelEditor : public SEditorImplObject
+ {
+ typedef SDataModel TStateType;
+ enum { EditorType = EditorTypes::DataModel };
+ SDataModel &m_Data;
+ static const char8_t *GetTypeStr() { return "datamodel"; }
+
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SDataModelEditor>(
+ inData, &SDataModel::m_Data, "data", EditorPropertyTypes::ObjectList));
+ return retval;
+ }
+
+ SDataModelEditor(SDataModel &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SDataEditor : public SEditorImplObject
+ {
+ typedef SData TStateType;
+ enum { EditorType = EditorTypes::Data };
+ SData &m_Data;
+ static const char8_t *GetTypeStr() { return "data"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList retval;
+ retval.push_back(CreateDataAccessor<SDataEditor>(inData, &SData::m_Id, "id",
+ EditorPropertyTypes::String));
+ retval.push_back(CreateDataAccessor<SDataEditor>(
+ inData, &SData::m_Expression, "expr", EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SDataEditor(SData &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SNullEditor
+ {
+ int m_Data;
+ static const char8_t *GetTypeStr() { return ""; }
+ };
+
+ template <typename TStateType>
+ struct SStateEditorMap
+ {
+ typedef SNullEditor TEditorType;
+ template <typename TIgnored>
+ static TObjPtr CreateEditor(TIgnored &, SEditorImpl &, TAccessorMap &)
+ {
+ return TObjPtr();
+ }
+ };
+
+ template <typename TEditorType>
+ struct SEditorImplStateMap
+ {
+ typedef int TStateType;
+ };
+
+ static inline TPropertyAccessorList &GetAccessorList(TAccessorMap &inMap, int inAccessor)
+ {
+ return inMap.insert(eastl::make_pair(inAccessor, TPropertyAccessorList()))
+ .first->second;
+ }
+
+ template <>
+ struct SStateEditorMap<STransition>
+ {
+ typedef STransition stateType;
+ typedef STransitionEditor TEditorType;
+ typedef STransitionEditor editorType;
+ static TObjPtr CreateEditor(stateType &inData, SEditorImpl &inEditorData,
+ TAccessorMap &inAccessors)
+ {
+ return QT3DS_NEW(inEditorData.m_EditorFoundation->getAllocator(), TEditorType)(
+ inData, inEditorData,
+ GetAccessorList(inAccessors, (int)editorType::EditorType));
+ }
+ static TObjPtr CreateEditor(SEditorImpl &inEditorData, TAccessorMap &inAccessors)
+ {
+ STransition *newTransition = QT3DS_NEW(inEditorData.m_AutoAllocator, stateType)();
+ newTransition->m_Flags.SetInternal(true);
+ return CreateEditor(*newTransition, inEditorData, inAccessors);
+ }
+ };
+ template <>
+ struct SEditorImplStateMap<STransitionEditor>
+ {
+ typedef STransition TStateType;
+ };
+
+#define DEFINE_STATE_EDITOR_TYPE_MAP(stateType, editorType) \
+ template <> \
+ struct SStateEditorMap<stateType> \
+ { \
+ typedef editorType TEditorType; \
+ static TObjPtr CreateEditor(stateType &inData, SEditorImpl &inEditorData, \
+ TAccessorMap &inAccessors) \
+ { \
+ return QT3DS_NEW(inEditorData.m_EditorFoundation->getAllocator(), TEditorType)( \
+ inData, inEditorData, GetAccessorList(inAccessors, (int)editorType::EditorType)); \
+ } \
+ static TObjPtr CreateEditor(SEditorImpl &inEditorData, TAccessorMap &inAccessors) \
+ { \
+ return CreateEditor(*QT3DS_NEW(inEditorData.m_AutoAllocator, stateType)(), inEditorData, \
+ inAccessors); \
+ } \
+ }; \
+ template <> \
+ struct SEditorImplStateMap<editorType> \
+ { \
+ typedef stateType TStateType; \
+ };
+
+ DEFINE_STATE_EDITOR_TYPE_MAP(SSCXML, SSCXMLEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SState, SStateEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SParallel, SParallelEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SHistory, SHistoryEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SFinal, SFinalEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SSend, SSendEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SRaise, SRaiseEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SLog, SLogEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SAssign, SAssignEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SIf, SIfEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SElseIf, SElseIfEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SElse, SElseEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SScript, SScriptEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SDataModel, SDataModelEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SData, SDataEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SCancel, SCancelEditor);
+
+#undef DEFINE_STATE_EDITOR_TYPE_MAP
+ }
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h
new file mode 100644
index 00000000..edb55ee3
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_FOUNDATION_H
+#define QT3DS_STATE_EDITOR_FOUNDATION_H
+#pragma once
+
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "foundation/TrackingAllocator.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/IOStreams.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct SBaseEditorFoundation
+ {
+ static MallocAllocator g_BaseAlloc;
+ QT3DSI32 mRefCount;
+ CAllocator m_BaseAllocator;
+ NVScopedRefCounted<NVFoundation> m_Foundation;
+ SBaseEditorFoundation()
+ : mRefCount(0)
+ , m_BaseAllocator()
+ , m_Foundation(NVCreateFoundation(QT3DS_FOUNDATION_VERSION, m_BaseAllocator))
+ {
+ }
+
+ ~SBaseEditorFoundation() {}
+
+ NVFoundationBase &getFoundation() { return *m_Foundation; }
+ NVAllocatorCallback &getAllocator() { return m_Foundation->getAllocator(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(g_BaseAlloc);
+
+ static SBaseEditorFoundation &Create()
+ {
+ SBaseEditorFoundation *fndPtr =
+ (SBaseEditorFoundation *)malloc(sizeof(SBaseEditorFoundation));
+ new (fndPtr) SBaseEditorFoundation();
+ return *fndPtr;
+ }
+ };
+
+ typedef NVScopedRefCounted<SBaseEditorFoundation> TFoundationPtr;
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h
new file mode 100644
index 00000000..c1a691ee
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h
@@ -0,0 +1,406 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_IMPL_H
+#define QT3DS_STATE_EDITOR_IMPL_H
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorValue.h"
+#include "Qt3DSStateTypes.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "foundation/Utils.h"
+#include "foundation/XML.h"
+#include "Qt3DSStateXMLIO.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateContext.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct SEditorImpl;
+
+ typedef nvhash_map<void *, TObjPtr> TStateEditorMap;
+
+ class IPropertyAccessor : public NVRefCounted
+ {
+ protected:
+ virtual ~IPropertyAccessor() {}
+ public:
+ SPropertyDeclaration m_Declaration;
+ IPropertyAccessor(const SPropertyDeclaration &inDec)
+ : m_Declaration(inDec)
+ {
+ }
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject & /*inObj*/)
+ {
+ return eastl::vector<CRegisteredString>();
+ }
+
+ virtual Option<SValue> Get(IEditorObject &inObj) = 0;
+ virtual void Set(IEditorObject &inObj, const Option<SValue> &inValue) = 0;
+ // Return true if this access handles the transaction code itself.
+ // Currently only used for the is initial target property.
+ virtual bool HandlesTransaction() { return false; }
+ };
+
+ typedef NVScopedRefCounted<IPropertyAccessor> TPropertyAccessorPtr;
+ typedef eastl::vector<TPropertyAccessorPtr> TPropertyAccessorList;
+ typedef nvhash_map<int, TPropertyAccessorList> TAccessorMap;
+
+#define QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(foundationPtr, refcountVar) \
+ void addRef() { atomicIncrement(&(refcountVar)); } \
+ void release() \
+ { \
+ TFoundationPtr temp(foundationPtr); \
+ QT3DSI32 value = atomicDecrement(&(refcountVar)); \
+ if (value <= 0) \
+ NVDelete(foundationPtr->getAllocator(), this); \
+ }
+
+ struct SEditorImplStrIOStream : public IOutStream
+ {
+ TEditorStr m_Str;
+ bool Write(NVConstDataRef<QT3DSU8> data)
+ {
+ if (data.size())
+ m_Str.append((const char8_t *)data.begin(), (const char8_t *)data.end());
+ return true;
+ }
+ };
+
+ struct SEditorImplStrInStream : public IInStream
+ {
+ const char8_t *m_Pos;
+ const char8_t *m_End;
+ SEditorImplStrInStream(const TEditorStr &inStr)
+ : m_Pos(inStr.data())
+ , m_End(inStr.data() + inStr.size())
+ {
+ }
+
+ virtual QT3DSU32 Read(NVDataRef<QT3DSU8> data)
+ {
+ QT3DSU32 amountLeft = (QT3DSU32)(m_End - m_Pos);
+ QT3DSU32 amountToRead = NVMin(amountLeft, data.size());
+ memCopy(data.mData, m_Pos, amountToRead);
+ m_Pos += amountToRead;
+ return amountToRead;
+ }
+ };
+
+ struct STransaction;
+
+ typedef eastl::vector<IEditorChangeListener *> TChangeListenerList;
+ struct STransactionManagerImpl;
+
+ class IEditorCopyPasteListener
+ {
+ protected:
+ virtual ~IEditorCopyPasteListener() {}
+
+ public:
+ virtual void OnCopy(TEditorPtr inEditor, eastl::vector<SStateNode *> &ioCopiedRoots,
+ IDOMWriter &ioWriter,
+ eastl::vector<SNamespacePair> &ioNamespaces) = 0;
+ virtual void OnPaste(TEditorPtr inEditor, IDOMReader &ioReader,
+ CXMLIO::TIdRemapMap &inStateIdRemapMap) = 0;
+ virtual void OnIDChange(TEditorPtr inEditor, SStateNode &inNode,
+ const char8_t *inOldId) = 0;
+ };
+
+ // Forward declaration of the editor interface that the properties and the editor objects
+ // can all use. Implementation in UICStateEditor.cpp
+ struct SEditorImpl : public IEditor
+ {
+ // Note that *we* keep references to our editors
+ // but our editor objects *cannot* keep a reference to us.
+ // This means that our editor objects need to be able to function (not crash) if we
+ // ourselves go out of scope.
+ TFoundationPtr m_EditorFoundation;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ SSAutoDeallocatorAllocator m_AutoAllocator;
+ NVScopedRefCounted<IStateContext> m_StateContext;
+ TStateEditorMap m_Editors;
+ volatile QT3DSI32 mRefCount;
+ NVScopedRefCounted<STransactionManagerImpl> m_TransactionManager;
+ TAccessorMap m_Accessors;
+ IEditorCopyPasteListener *m_CopyPasteListener;
+
+ SEditorImpl(TFoundationPtr inFoundation,
+ NVScopedRefCounted<IStringTable> inStringTable);
+ void addRef();
+ void release();
+
+ TObjPtr InsertEditor(void *inData, IEditorObject *inEditor);
+
+ template <typename TStateType>
+ TObjPtr ToEditor(TStateType &inItem);
+
+ template <typename TStateType>
+ TObjPtr ToEditor(TStateType *inItem);
+ TObjPtr ToEditor(SStateNode &inItem);
+ TObjPtr ExecutableContentToEditor(SExecutableContent &inItem);
+
+ template <typename TStateType>
+ TStateType *FromEditor(TObjPtr inPtr);
+
+ SStateNode *StateNodeFromEditor(TObjPtr inPtr);
+
+ SExecutableContent *ExecutableContentFromEditor(TObjPtr inPtr);
+ void GenerateUniqueId(SStateNode &inNode, const char8_t *inStem);
+ void GenerateUniqueId(SSend &inNode, const char8_t *inStem);
+
+ virtual TObjPtr GetRoot();
+
+ template <typename TStateType>
+ eastl::pair<TStateType *, TObjPtr> CreateEditorAndObject();
+
+ TObjPtr DoCreate(const char8_t *inTypeName, const char8_t *inId);
+
+ virtual TObjPtr Create(const char8_t *inTypeName, const char8_t *inId);
+
+ virtual TObjPtr GetOrCreate(SSCXML &inData);
+ virtual TObjPtr GetOrCreate(SState &inData);
+ virtual TObjPtr GetOrCreate(STransition &inData);
+ virtual TObjPtr GetOrCreate(SParallel &inData);
+ virtual TObjPtr GetOrCreate(SFinal &inData);
+ virtual TObjPtr GetOrCreate(SHistory &inData);
+ virtual TObjPtr GetOrCreate(SDataModel &inData);
+ virtual TObjPtr GetOrCreate(SData &inData);
+
+ virtual TObjPtr GetObjectById(const char8_t *inId);
+
+ virtual TObjPtr GetEditor(void *inGraphData);
+
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener);
+
+ void RemoveChangeListener(IEditorChangeListener &inListener);
+
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName);
+
+ virtual TTransactionPtr GetOpenTransaction();
+
+ STransaction *GetOpenTransactionImpl();
+
+ virtual void RollbackTransaction();
+
+ virtual void EndTransaction();
+
+ virtual TEditorStr Copy(TObjList inObjects, const QT3DSVec2 &inMousePos);
+
+ virtual bool CanPaste(TObjPtr inTarget);
+
+ void AddNewPasteObjectToTransaction(SStateNode &inNode);
+
+ virtual void Paste(const TEditorStr &inCopiedObjects, TObjPtr inTarget,
+ const QT3DSVec2 &inMousePos);
+
+ static bool IsDerivedFrom(SStateNode &child, SStateNode &parent);
+
+ // This method should always return a value because the scxml root item
+ // is the parent of everyone else.
+ static SStateNode &GetLeastCommonAncestor(SStateNode &lhs, SStateNode &rhs);
+
+ TObjPtr GetLeastCommonAncestor(NVConstDataRef<TObjPtr> inObjects);
+
+ TObjList GetCancelableSendIds();
+
+ TObjPtr ChangeExecutableContentType(TObjPtr inContent, const char8_t *newType);
+
+ TEditorStr ToXML(TObjPtr inContent);
+
+ eastl::pair<TEditorStr, TObjPtr> FromXML(TObjPtr inContent,
+ const TEditorStr &ioEditedXML);
+
+ // Return the set of events in use in the state machine.
+ TEditorStrList GetStateMachineEvents();
+
+ void ReleaseEditor(void *inData);
+
+ virtual bool Save(const char8_t *inFileName);
+ virtual void Save(IOutStream &inStream);
+
+ bool Load(IInStream &inStream, const char8_t *inFilename);
+
+ /////////////////////////////////////////////////////////////////////
+ // Property access helpers
+ /////////////////////////////////////////////////////////////////////
+ void SetIdProperty(SStateNode &inNode, const SValue &inData,
+ CRegisteredString &inTarget);
+ void SetIdProperty(SSend &inNode, const SValue &inData, CRegisteredString &inTarget);
+
+ void Set(const SValue &inData, CRegisteredString &inTarget)
+ {
+ TEditorStr theStr(inData.getData<TEditorStr>());
+ inTarget = m_StringTable->RegisterStr(theStr.c_str());
+ }
+
+ void Set(const SValue &inData, const char8_t *&inTarget)
+ {
+ TEditorStr theStr(inData.getData<TEditorStr>());
+ const char8_t *theData(theStr.c_str());
+
+ if (inTarget && *inTarget != 0)
+ m_AutoAllocator.deallocate(const_cast<char8_t *>(inTarget));
+
+ size_t len = StrLen(theData);
+ if (len == 0)
+ inTarget = NULL;
+
+ ++len; // account for null terminate
+
+ char8_t *newTarget = (char8_t *)m_AutoAllocator.allocate(len + 1, "graph string",
+ __FILE__, __LINE__);
+ memCopy(newTarget, theData, len);
+ inTarget = newTarget;
+ }
+ SValue Get(CRegisteredString &inTarget) { return inTarget.c_str(); }
+ SValue Get(const char8_t *inItem) { return nonNull(inItem); }
+
+ void Set(const Option<SValue> &inData, NVConstDataRef<SStateNode *> &inTarget);
+ SValue Get(NVConstDataRef<SStateNode *> &inTarget);
+
+ void Set(const Option<SValue> &inData, STransition *&inTarget);
+ Option<SValue> Get(STransition *&inTarget);
+
+ void SetInitial(const TObjList &inList, STransition *&outInitialTransition);
+ SValue GetInitial(STransition *inInitialTransition);
+
+ void SetInitialTarget(const Option<SValue> &inData, SStateNode &inNode);
+ SValue IsInitialTarget(SStateNode &inNode);
+
+ void Set(const Option<SValue> &inData, SDataModel *&inTarget);
+ SValue Get(SDataModel *inTarget);
+
+ void Set(const Option<SValue> &inData, TDataList &inTarget);
+ SValue Get(TDataList &inTarget);
+
+ void Set(const Option<SValue> &inData, NVConstDataRef<QT3DSVec2> &inTarget);
+ SValue Get(const NVConstDataRef<QT3DSVec2> &inTarget);
+
+ void Set(const Option<SValue> &inData, SSend *&inTarget);
+ SValue Get(SSend *inTarget);
+
+ void Set(const Option<SValue> &inData, TEditorStr &inTarget)
+ {
+ inTarget.clear();
+ if (inData.hasValue())
+ inTarget = inData->getData<TEditorStr>();
+ }
+
+ SValue Get(const TEditorStr &inTarget) { return inTarget; }
+
+ void Set(const Option<SValue> &inData, Option<QT3DSVec2> &inTarget)
+ {
+ if (inData.hasValue()) {
+ inTarget = inData->getData<QT3DSVec2>();
+ } else {
+ inTarget = Empty();
+ }
+ }
+ Option<SValue> Get(const Option<QT3DSVec2> &inTarget)
+ {
+ Option<SValue> retval;
+ if (inTarget.hasValue())
+ retval = SValue(inTarget.getValue());
+ return retval;
+ }
+
+ void Set(const Option<SValue> &inData, TVec2List &inTarget)
+ {
+ if (inData.hasValue()) {
+ inTarget = inData->getData<TVec2List>();
+ } else {
+ inTarget.clear();
+ }
+ }
+ SValue Get(const TVec2List &inTarget) { return inTarget; }
+
+ void Set(const Option<SValue> &inData, Option<QT3DSVec3> &inTarget)
+ {
+ if (inData.hasValue()) {
+ inTarget = inData->getData<QT3DSVec3>();
+ } else {
+ inTarget = Empty();
+ }
+ }
+ const Option<SValue> Get(const Option<QT3DSVec3> &inTarget)
+ {
+ Option<SValue> retval;
+ if (inTarget.hasValue())
+ retval = SValue(inTarget.getValue());
+ return retval;
+ }
+
+ const char8_t *ToGraphStr(const char8_t *inStr)
+ {
+ if (isTrivial(inStr))
+ return "";
+ QT3DSU32 len = StrLen(inStr) + 1;
+ char8_t *retval =
+ (char8_t *)m_AutoAllocator.allocate(len, "GraphStr", __FILE__, __LINE__);
+ memCopy(retval, inStr, len);
+ return retval;
+ }
+
+ SValue GetSendId(const char8_t *expression);
+ void SetSendId(const Option<SValue> &inData, const char8_t *&outExpression);
+
+ TObjPtr CreateExecutableContent(SStateNode &inParent, const char8_t *inTypeName);
+ eastl::pair<SExecutableContent *, TObjPtr>
+ CreateExecutableContent(const char8_t *inTypeName);
+
+ CRegisteredString RegisterStr(const char8_t *str)
+ {
+ return m_StringTable->RegisterStr(str);
+ }
+ IStateContext &GetStateContext();
+
+ TEditorStr GetDefaultInitialValue(SStateNode &inNode);
+ void GetLegalInitialValues(SStateNode &inNode,
+ eastl::vector<CRegisteredString> &outValues);
+ void SetInitialAttribute(const Option<SValue> &inData, SStateNode &inNode);
+
+ void SetTransactionManager(STransactionManagerImpl &trans);
+
+ void ReplaceExecutableContent(SExecutableContent &oldContent,
+ SExecutableContent &newContent);
+
+ eastl::vector<CRegisteredString> GetLegalHistoryDefaultValues(SHistory &inData);
+
+ eastl::vector<CRegisteredString> GetLegalParentIds(SStateNode &inNode);
+ void SetParent(SStateNode &inNode, const Option<SValue> &inValue);
+ void CheckAndSetValidHistoryDefault(TObjPtr inHistoryNode);
+ void SetValidHistoryDefault(TObjPtr inHistoryNode);
+ };
+ }
+}
+}
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h
new file mode 100644
index 00000000..5f06fbdb
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_PROPERTIES_H
+#define QT3DS_STATE_EDITOR_PROPERTIES_H
+#pragma once
+
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateEditorImpl.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ template <typename TEditorType>
+ struct SPropertyAccessorBase : public IPropertyAccessor
+ {
+ TFoundationPtr m_Allocator;
+ volatile QT3DSI32 mRefCount;
+
+ SPropertyAccessorBase(TFoundationPtr alloc, const SPropertyDeclaration &inDec)
+ : IPropertyAccessor(inDec)
+ , m_Allocator(alloc)
+ , mRefCount(0)
+ {
+ }
+
+ virtual Option<SValue> Get(IEditorObject &inObj)
+ {
+ return DoGet(static_cast<TEditorType &>(inObj));
+ }
+ virtual void Set(IEditorObject &inObj, const Option<SValue> &inValue)
+ {
+ DoSet(static_cast<TEditorType &>(inObj), inValue);
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj) = 0;
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue) = 0;
+ };
+
+ template <typename TEditorType>
+ struct SInitialProperty : SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ static SPropertyDeclaration CreatePropertyDeclaration(IStringTable &inStrTable)
+ {
+ SPropertyDeclaration theDeclaration;
+ theDeclaration.m_Name = inStrTable.RegisterStr("initial");
+ theDeclaration.m_Type = EditorPropertyTypes::Object;
+ return theDeclaration;
+ }
+ SInitialProperty(TFoundationPtr inAlloc, IStringTable &inStrTable)
+ : TBaseType(inAlloc, CreatePropertyDeclaration(inStrTable))
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return SValue(inObj.Get(inObj.m_Data.m_Initial));
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Initial =
+ inObj.template FromEditor<STransition>(inValue->getData<TObjPtr>());
+ }
+ };
+
+ template <typename TEditorType, typename TFlagsType>
+ struct SFlagBooleanProperty : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef bool (TFlagsType::*TGetPropPtr)() const;
+ typedef void (TFlagsType::*TSetPropPtr)(bool);
+
+ TGetPropPtr m_GetProp;
+ TSetPropPtr m_SetProp;
+ eastl::vector<CRegisteredString> m_LegalValues;
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject & /*inObj*/)
+ {
+ return m_LegalValues;
+ }
+
+ static SPropertyDeclaration CreatePropertyDeclaration(IStringTable &inStrTable,
+ const char8_t *propName)
+ {
+ SPropertyDeclaration theDeclaration;
+ theDeclaration.m_Name = inStrTable.RegisterStr(propName);
+ theDeclaration.m_Type = EditorPropertyTypes::StringSet;
+ return theDeclaration;
+ }
+ SFlagBooleanProperty(TFoundationPtr inFnd, IStringTable &inStrTable,
+ const char8_t *inPropName, const char8_t *falseName,
+ const char8_t *trueName, TGetPropPtr inGetProp,
+ TSetPropPtr inSetProp)
+ : TBaseType(inFnd, CreatePropertyDeclaration(inStrTable, inPropName))
+ , m_GetProp(inGetProp)
+ , m_SetProp(inSetProp)
+ {
+ m_LegalValues.push_back(inStrTable.RegisterStr(falseName));
+ m_LegalValues.push_back(inStrTable.RegisterStr(trueName));
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ bool boolVal = (inObj.m_Data.m_Flags.*m_GetProp)();
+ TEditorStr retval = boolVal ? TEditorStr(m_LegalValues[1].c_str())
+ : TEditorStr(m_LegalValues[0].c_str());
+ return SValue(retval);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValueOpt)
+ {
+ if (inValueOpt.hasValue()) {
+ TEditorStr data = inValueOpt->getData<TEditorStr>();
+
+ if (AreEqual(data.c_str(), m_LegalValues[1].c_str()))
+ (inObj.m_Data.m_Flags.*m_SetProp)(true);
+
+ else if (AreEqual(data.c_str(), m_LegalValues[0].c_str()))
+ (inObj.m_Data.m_Flags.*m_SetProp)(false);
+
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ };
+
+ template <typename TEditorType>
+ struct SChildrenProperty : SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ static SPropertyDeclaration CreatePropertyDeclaration(IStringTable &inStrTable)
+ {
+ SPropertyDeclaration theDeclaration;
+ theDeclaration.m_Name = inStrTable.RegisterStr("children");
+ theDeclaration.m_Type = EditorPropertyTypes::ObjectList;
+ return theDeclaration;
+ }
+
+ SChildrenProperty(TFoundationPtr inAlloc, IStringTable &inStrTable)
+ : TBaseType(inAlloc, CreatePropertyDeclaration(inStrTable))
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TObjList retval;
+ for (TStateNodeList::iterator iter = inObj.m_Data.m_Children.begin(),
+ end = inObj.m_Data.m_Children.end();
+ iter != end; ++iter)
+ retval.push_back(inObj.m_Editor.ToEditor(*iter));
+ return SValue(retval);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ if (inValue.hasValue()) {
+ const TObjList &data = inValue->getData<TObjList>();
+ TStateNodeList &theChildren(inObj.m_Data.m_Children);
+ // De-set all children.
+ while (theChildren.empty() == false)
+ inObj.m_Data.RemoveChild(theChildren.front());
+
+ for (TObjList::const_iterator iter = data.begin(), end = data.end();
+ iter != end; ++iter) {
+ SStateNode *theNode = inObj.m_Editor.StateNodeFromEditor(*iter);
+ if (theNode)
+ inObj.m_Data.AppendChild(*theNode);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+ };
+
+ // Property that is represented by a data item's member.
+ template <typename TEditorType, typename TStateType, typename TDataType>
+ struct SDataProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef TDataType TStateType::*TPropertyPtr;
+ TPropertyPtr m_Ptr;
+ eastl::vector<CRegisteredString> m_LegalValues;
+
+ SDataProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec, TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Ptr(inPtr)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject & /*inObj*/)
+ {
+ return m_LegalValues;
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.m_Editor.Get(inObj.m_Data.*(this->m_Ptr));
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.Set(inValue, inObj.m_Data.*(this->m_Ptr));
+ }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SInitialTargetProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SInitialTargetProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.m_Editor.IsInitialTarget(inObj.m_Data);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.SetInitialTarget(inValue, inObj.m_Data);
+ }
+
+ virtual bool HandlesTransaction() { return true; }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SInitialComboProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SInitialComboProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject &inEditor)
+ {
+ eastl::vector<CRegisteredString> retval;
+ TEditorType &theEditor = static_cast<TEditorType &>(inEditor);
+ retval.push_back(theEditor.m_Editor.RegisterStr("(script expression)"));
+ theEditor.m_Editor.GetLegalInitialValues(theEditor.m_Data, retval);
+ return retval;
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TEditorType &theEditor = inObj;
+ if (theEditor.m_InitialComboValue.size() == 0) {
+ if (!isTrivial(inObj.m_Data.GetInitialExpression()))
+ theEditor.m_InitialComboValue = "(script expression)";
+ else if (inObj.m_Data.GetInitialTransition()
+ && inObj.m_Data.GetInitialTransition()->m_Target.size())
+ theEditor.m_InitialComboValue.assign(
+ inObj.m_Data.GetInitialTransition()->m_Target[0]->m_Id.c_str());
+ else
+ theEditor.m_InitialComboValue =
+ theEditor.m_Editor.GetDefaultInitialValue(theEditor.m_Data);
+ }
+ return theEditor.m_InitialComboValue;
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ if (inValue->getData<TEditorStr>() != "(script expression)") {
+ inObj.m_Editor.SetInitialAttribute(inValue, inObj.m_Data);
+ } else {
+ inObj.m_Editor.SetInitialAttribute(Option<SValue>(), inObj.m_Data);
+ }
+ }
+ virtual bool HandlesTransaction() { return true; }
+ };
+
+ template <typename TEditorType, typename TStateType, typename TDataType>
+ struct SOptionAccessorProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef Option<TDataType> TOptType;
+ typedef TOptType (TStateType::*TGetPtr)() const;
+ typedef void (TStateType::*TSetPtr)(const TOptType &inOpt);
+ TGetPtr m_Getter;
+ TSetPtr m_Setter;
+
+ SOptionAccessorProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TGetPtr inPtr, TSetPtr inSetPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Getter(inPtr)
+ , m_Setter(inSetPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TOptType theOpt = (inObj.m_Data.*m_Getter)();
+ if (theOpt.hasValue())
+ return SValue(*theOpt);
+ return Empty();
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TOptType theNewValue;
+ if (inValue.hasValue())
+ theNewValue = TOptType(inValue->getData<TDataType>());
+ (inObj.m_Data.*m_Setter)(theNewValue);
+ }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SDataIdProp : SDataProp<TEditorType, TStateType, CRegisteredString>
+ {
+ typedef SDataProp<TEditorType, TStateType, CRegisteredString> TBaseType;
+ typedef CRegisteredString TStateType::*TPropertyPtr;
+
+ SDataIdProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec, inPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ // This may mangle the id in order to find a unique id.
+ inObj.m_Editor.SetIdProperty(inObj.m_Data, inValue, inObj.m_Data.*(this->m_Ptr));
+ }
+ };
+
+ template <typename TEditorObject, typename TStateType, typename TDataType>
+ IPropertyAccessor *CreateDataAccessor(SEditorImpl &inData, TDataType TStateType::*inDataPtr,
+ const char8_t *inName,
+ EditorPropertyTypes::Enum inPropType)
+ {
+ typedef SDataProp<TEditorObject, TStateType, TDataType> TPropType;
+ return QT3DS_NEW(inData.m_EditorFoundation->getAllocator(), TPropType)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr(inName), inPropType), inDataPtr);
+ }
+
+ template <typename TEditorType, typename TDataType>
+ struct SEditorImplProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef TDataType TEditorType::*TPropertyPtr;
+ TPropertyPtr m_Ptr;
+
+ SEditorImplProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Ptr(inPtr)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.m_Editor.Get(inObj.*(this->m_Ptr));
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.Set(inValue, inObj.*(this->m_Ptr));
+ }
+ };
+
+ template <typename TEditorObject, typename TDataType>
+ IPropertyAccessor *
+ CreateEditorAccessor(SEditorImpl &inData, TDataType TEditorObject::*inDataPtr,
+ const char8_t *inName, EditorPropertyTypes::Enum inPropType)
+ {
+ typedef SEditorImplProp<TEditorObject, TDataType> TPropType;
+ return QT3DS_NEW(inData.m_EditorFoundation->getAllocator(), TPropType)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr(inName), inPropType), inDataPtr);
+ }
+
+ // General property for initial and history transition properties
+ template <typename TEditorType, typename TStateType>
+ struct SSCXMLInitialPtr : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef STransition *TStateType::*TPropertyType;
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ TPropertyType m_PropPtr;
+
+ SSCXMLInitialPtr(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyType inPropPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_PropPtr(inPropPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.GetInitial(inObj.m_Data.*m_PropPtr);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.SetInitial(inValue->getData<TObjList>(), inObj.m_Data.*m_PropPtr);
+ }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SSCXMLInitialContent : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef STransition *TStateType::*TPropertyType;
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ TPropertyType m_PropPtr;
+
+ SSCXMLInitialContent(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyType inPropPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_PropPtr(inPropPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.GetInitialContent(inObj.m_Data.*m_PropPtr);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ if (inValue.hasValue()) {
+ inObj.SetInitialContent(inValue->getData<TObjList>(), inObj.m_Data.*m_PropPtr);
+ } else {
+ inObj.SetInitialContent(TObjList(), inObj.m_Data.*m_PropPtr);
+ }
+ }
+ };
+
+ template <typename TEditorType>
+ struct SDelayProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SDelayProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ if (!isTrivial(inObj.m_Data.m_DelayExpr))
+ return Empty();
+
+ return inObj.m_Editor.GetSendId(inObj.m_Data.m_Delay);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Data.m_DelayExpr = "";
+ inObj.m_Editor.SetSendId(inValue, inObj.m_Data.m_Delay);
+ }
+ };
+
+ template <typename TEditorType>
+ struct SHistoryTransitionProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SHistoryTransitionProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject &inEditor)
+ {
+ TEditorType &theEditor = static_cast<TEditorType &>(inEditor);
+ return theEditor.m_Editor.GetLegalHistoryDefaultValues(theEditor.m_Data);
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ if (inObj.m_Data.m_Transition)
+ return inObj.m_Editor.Get(inObj.m_Data.m_Transition->m_Target);
+
+ return SValue(TObjList());
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TObjList newObjects;
+
+ if (inValue.hasValue())
+ newObjects = inValue->getData<TObjList>();
+
+ if (newObjects.size()) {
+ if (!inObj.m_Data.m_Transition) {
+ inObj.m_Data.m_Transition =
+ (STransition *)inObj.m_Editor.m_AutoAllocator.allocate(
+ sizeof(STransition), "transition", __FILE__, __LINE__);
+ new (inObj.m_Data.m_Transition) STransition();
+ inObj.m_Data.m_Transition->m_Parent = &inObj.m_Data;
+ }
+ inObj.m_Editor.Set(inValue, inObj.m_Data.m_Transition->m_Target);
+ } else
+ inObj.m_Data.m_Transition = NULL;
+ }
+ };
+
+ template <typename TEditorType>
+ struct SParentProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SParentProp(TFoundationPtr inAlloc, IStringTable &inStrTable)
+ : TBaseType(inAlloc, SPropertyDeclaration(inStrTable.RegisterStr("parent"),
+ EditorPropertyTypes::StringSet))
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ static const char *SCXMLParentName() { return "(none)"; }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TObjPtr current = inObj.Parent();
+ if (current && AreEqual(current->TypeName(), "scxml") == false)
+ return SValue(current->GetId());
+ return Option<SValue>(SValue(eastl::string(SCXMLParentName())));
+ }
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject &inEditor)
+ {
+ TEditorType &theEditor = static_cast<TEditorType &>(inEditor);
+ eastl::vector<CRegisteredString> retval =
+ theEditor.m_Editor.GetLegalParentIds(theEditor.m_Data);
+ CRegisteredString parentName(theEditor.m_Editor.RegisterStr(SCXMLParentName()));
+ retval.insert(retval.begin(), parentName);
+ return retval;
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.SetParent(inObj.m_Data, inValue);
+ }
+
+ virtual bool HandlesTransaction() { return true; }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp
new file mode 100644
index 00000000..1bc0faf7
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateEditorTransactionImpl.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ // Some things have to be implemented below so that we can take advantage of the full
+ // SEditorImpl definition.
+
+ STransaction::STransaction(TFoundationPtr alloc, STransactionManagerImpl &inEditor,
+ const TEditorStr &inName)
+ : ITransaction(inName)
+ , m_Alloc(alloc)
+ , mRefCount(0)
+ , m_Editor(inEditor)
+ {
+ }
+
+ STransaction::~STransaction() {}
+
+ void STransaction::addRef() { atomicIncrement(&mRefCount); }
+
+ void STransaction::release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ TFoundationPtr fnd(m_Alloc);
+ NVDelete(fnd->getAllocator(), this);
+ }
+ }
+
+ void STransaction::ChangesToEditors()
+ {
+ m_EditorsList.clear();
+ m_EditorsList.resize(m_Changes.size());
+ eastl::transform(m_Changes.begin(), m_Changes.end(), m_EditorsList.begin(),
+ SChangeToEditor());
+ }
+
+ void STransaction::SendDoSignals()
+ {
+ ChangesToEditors();
+ CreateRemovedEditorsList(true);
+ for (TChangeListenerList::iterator listenerIter = m_Editor->m_ChangeListeners.begin(),
+ listenerEnd = m_Editor->m_ChangeListeners.end();
+ listenerIter != listenerEnd; ++listenerIter)
+ (*listenerIter)->OnDataChange(m_EditorsList, m_RemovedEditorsList);
+ }
+
+ void STransaction::Do()
+ {
+ for (TChangeList::iterator iter = m_Changes.begin(), end = m_Changes.end(); iter != end;
+ ++iter)
+ (*iter)->Do();
+
+ for (TRemovePairList::iterator iter = m_RemovedPairs.begin(),
+ end = m_RemovedPairs.end();
+ iter != end; ++iter) {
+ if (iter->first)
+ iter->second->RemoveIdFromContext();
+ else
+ iter->second->AddIdToContext();
+ }
+
+ SendDoSignals();
+ }
+
+ void STransaction::SilentUndo()
+ {
+ for (TChangeList::reverse_iterator iter = m_Changes.rbegin(), end = m_Changes.rend();
+ iter != end; ++iter)
+ (*iter)->Undo();
+
+ for (TRemovePairList::iterator iter = m_RemovedPairs.begin(),
+ end = m_RemovedPairs.end();
+ iter != end; ++iter) {
+ if (iter->first)
+ iter->second->AddIdToContext();
+ else
+ iter->second->RemoveIdFromContext();
+ }
+ }
+
+ void STransaction::Undo()
+ {
+ SilentUndo();
+ m_EditorsList.clear();
+ m_EditorsList.resize(m_Changes.size());
+ eastl::transform(m_Changes.rbegin(), m_Changes.rend(), m_EditorsList.begin(),
+ SChangeToEditor());
+ CreateRemovedEditorsList(false);
+ for (TChangeListenerList::iterator listenerIter = m_Editor->m_ChangeListeners.begin(),
+ listenerEnd = m_Editor->m_ChangeListeners.end();
+ listenerIter != listenerEnd; ++listenerIter)
+ (*listenerIter)->OnDataChange(m_EditorsList, m_RemovedEditorsList);
+ }
+
+ struct STransactionImplConnection : public IStateSignalConnection
+ {
+ TFoundationPtr &m_Alloc;
+ QT3DSI32 mRefCount;
+ STransactionManagerImpl &m_Editor;
+ IEditorChangeListener &m_ChangeListener;
+ STransactionImplConnection(TFoundationPtr &alloc, STransactionManagerImpl &e,
+ IEditorChangeListener &listener)
+ : m_Alloc(alloc)
+ , mRefCount(0)
+ , m_Editor(e)
+ , m_ChangeListener(listener)
+ {
+ }
+ // Implemented below editor to use editor's interfaces
+ virtual ~STransactionImplConnection()
+ {
+ m_Editor.RemoveChangeListener(m_ChangeListener);
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Alloc, mRefCount);
+ };
+
+ void STransactionManagerImpl::addRef() { atomicIncrement(&mRefCount); }
+ void STransactionManagerImpl::release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ TFoundationPtr fnd(m_EditorFoundation);
+ NVDelete(fnd->getAllocator(), this);
+ }
+ }
+
+ TSignalConnectionPtr
+ STransactionManagerImpl::AddChangeListener(IEditorChangeListener &inListener)
+ {
+ m_ChangeListeners.push_back(&inListener);
+ // If we have any listeners, then we can't be destroyed as their connections will still
+ // need
+ // to talk to us to release its stuff.
+ if (m_ChangeListeners.size() == 1)
+ addRef();
+ return QT3DS_NEW(m_EditorFoundation->getAllocator(),
+ STransactionImplConnection)(m_EditorFoundation, *this, inListener);
+ }
+
+ void STransactionManagerImpl::RemoveChangeListener(IEditorChangeListener &inListener)
+ {
+ TChangeListenerList::iterator iter =
+ eastl::find(m_ChangeListeners.begin(), m_ChangeListeners.end(), &inListener);
+ if (iter != m_ChangeListeners.end())
+ m_ChangeListeners.erase(iter);
+ if (m_ChangeListeners.size() == 0)
+ release();
+ }
+ TTransactionPtr STransactionManagerImpl::BeginTransaction(const TEditorStr &inName)
+ {
+ if (!m_Transaction) {
+ QT3DS_ASSERT(m_OpenCount == 0);
+ m_Transaction = QT3DS_NEW(m_EditorFoundation->getAllocator(),
+ STransaction)(m_EditorFoundation, *this, inName);
+ }
+ ++m_OpenCount;
+ return m_Transaction.mPtr;
+ }
+
+ TTransactionPtr STransactionManagerImpl::GetOpenTransaction() { return m_Transaction.mPtr; }
+ STransaction *STransactionManagerImpl::GetOpenTransactionImpl()
+ {
+ return m_Transaction.mPtr;
+ }
+
+ void STransactionManagerImpl::RollbackTransaction()
+ {
+ --m_OpenCount;
+ if (m_OpenCount <= 0) {
+ if (m_Transaction) {
+ m_Transaction->Undo();
+ m_Transaction = NULL;
+ }
+ }
+ QT3DS_ASSERT(m_OpenCount == 0);
+ m_OpenCount = 0;
+ }
+
+ void STransactionManagerImpl::EndTransaction()
+ {
+ TTransactionPtr retval = m_Transaction.mPtr;
+ --m_OpenCount;
+ if (m_OpenCount <= 0)
+ m_Transaction = NULL;
+ QT3DS_ASSERT(m_OpenCount >= 0);
+ m_OpenCount = 0;
+ }
+
+ void STransactionManagerImpl::OnObjectDeleted(TObjPtr inObj)
+ {
+ if (m_Transaction)
+ m_Transaction->m_RemovedPairs.push_back(eastl::make_pair(true, inObj));
+
+ if (m_ObjListener)
+ m_ObjListener->OnObjectDeleted(inObj);
+ }
+ void STransactionManagerImpl::OnObjectCreated(TObjPtr inObj)
+ {
+ if (m_Transaction)
+ m_Transaction->m_RemovedPairs.push_back(eastl::make_pair(false, inObj));
+
+ if (m_ObjListener)
+ m_ObjListener->OnObjectCreated(inObj);
+ }
+ }
+}
+} \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h
new file mode 100644
index 00000000..9996c351
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_TRANSACTION_IMPL_H
+#define QT3DS_STATE_EDITOR_TRANSACTION_IMPL_H
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateEditorProperties.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ class IChange : public NVRefCounted
+ {
+ protected:
+ virtual ~IChange() {}
+ public:
+ virtual void Do() = 0;
+ virtual void Undo() = 0;
+ virtual TObjPtr GetEditor() = 0;
+ };
+
+ typedef eastl::vector<NVScopedRefCounted<IChange>> TChangeList;
+
+ struct SChange : public IChange
+ {
+ Option<SValue> m_OldVal;
+ Option<SValue> m_NewVal;
+ TPropertyAccessorPtr m_Accessor;
+ TObjPtr m_Editor;
+ QT3DSI32 mRefCount;
+
+ SChange(const Option<SValue> &inOldVal, const Option<SValue> &inNewVal,
+ IPropertyAccessor &inAccessor, IEditorObject &inEditor)
+ : m_OldVal(inOldVal)
+ , m_NewVal(inNewVal)
+ , m_Accessor(inAccessor)
+ , m_Editor(inEditor)
+ , mRefCount(0)
+ {
+ }
+ // Sometimes we need to create a change just to signal a new object.
+ // In this case there isn't an accessor and there aren't old values and
+ // new values.
+ SChange(IEditorObject &inEditor)
+ : m_Editor(inEditor)
+ , mRefCount(0)
+ {
+ }
+ SChange()
+ : mRefCount(0)
+ {
+ }
+ void Do()
+ {
+ if (m_Accessor)
+ m_Accessor->Set(*m_Editor, m_NewVal);
+ }
+
+ void Undo()
+ {
+ if (m_Accessor)
+ m_Accessor->Set(*m_Editor, m_OldVal);
+ }
+ void addRef() { atomicIncrement(&mRefCount); }
+ void release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0)
+ delete this;
+ }
+
+ TObjPtr GetEditor() { return m_Editor; }
+ };
+
+ // true if removed on do, false if removed on false;
+ typedef eastl::pair<bool, TObjPtr> TRemovePair;
+ typedef eastl::vector<TRemovePair> TRemovePairList;
+
+ struct STransactionManagerImpl;
+
+ struct STransaction : public ITransaction
+ {
+ TFoundationPtr m_Alloc;
+ TChangeList m_Changes;
+ volatile QT3DSI32 mRefCount;
+ // We have to keep a refcount to the editor ourselves.
+ NVScopedRefCounted<STransactionManagerImpl> m_Editor;
+ TObjList m_EditorsList;
+ TObjList m_RemovedEditorsList;
+ TRemovePairList m_RemovedPairs;
+
+ STransaction(TFoundationPtr alloc, STransactionManagerImpl &inEditor,
+ const TEditorStr &inName);
+ virtual ~STransaction();
+ virtual void addRef();
+ virtual void release();
+
+ void CreateRemovedEditorsList(bool inDo)
+ {
+ m_RemovedEditorsList.clear();
+ for (TRemovePairList::iterator iter = m_RemovedPairs.begin(),
+ end = m_RemovedPairs.end();
+ iter != end; ++iter)
+ if (iter->first == inDo)
+ m_RemovedEditorsList.push_back(iter->second);
+ }
+
+ struct SChangeToEditor
+ {
+ TObjPtr operator()(IChange *inChange) const { return inChange->GetEditor(); }
+ };
+ virtual void SendDoSignals();
+ virtual void Do();
+ virtual void SilentUndo();
+ virtual void Undo();
+ virtual bool Empty() { return m_Changes.empty() && m_RemovedPairs.empty(); }
+ virtual TObjList GetEditedObjects()
+ {
+ ChangesToEditors();
+ return m_EditorsList;
+ }
+ void ChangesToEditors();
+ };
+
+ class ITransManagerImplListener
+ {
+ protected:
+ virtual ~ITransManagerImplListener() {}
+ public:
+ virtual void OnObjectDeleted(TObjPtr inObj) = 0;
+ virtual void OnObjectCreated(TObjPtr inObj) = 0;
+ };
+
+ struct STransactionManagerImpl : public ITransactionManager
+ {
+ TFoundationPtr m_EditorFoundation;
+ volatile QT3DSI32 mRefCount;
+ TChangeListenerList m_ChangeListeners;
+ NVScopedRefCounted<STransaction> m_Transaction;
+ QT3DSI32 m_OpenCount;
+ ITransManagerImplListener *m_ObjListener;
+
+ STransactionManagerImpl(TFoundationPtr fnd)
+ : m_EditorFoundation(fnd)
+ , mRefCount(0)
+ , m_OpenCount(0)
+ , m_ObjListener(NULL)
+ {
+ }
+ virtual void addRef();
+ virtual void release();
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener);
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName = TEditorStr());
+ virtual TTransactionPtr GetOpenTransaction();
+ STransaction *GetOpenTransactionImpl();
+ virtual void RollbackTransaction();
+ virtual void EndTransaction();
+
+ void OnObjectDeleted(TObjPtr inObj);
+ void OnObjectCreated(TObjPtr inObj);
+
+ void RemoveChangeListener(IEditorChangeListener &inListener);
+ };
+ }
+}
+}
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp
new file mode 100644
index 00000000..7fb4ab9f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp
@@ -0,0 +1,813 @@
+/****************************************************************************
+**
+** 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 "Qt3DSStateEditorTransitionPath.h"
+
+namespace {
+
+using namespace qt3ds::state::editor;
+using namespace qt3ds::state;
+
+bool inBounds(QT3DSF32 item, QT3DSF32 lower, QT3DSF32 upper)
+{
+ if (item >= lower && item <= upper)
+ return true;
+ return false;
+}
+
+DirectionTypes::Enum EdgeTypeToDirectionType(EdgeTypes::Enum inEdgeType)
+{
+ switch (inEdgeType) {
+ case EdgeTypes::Bottom:
+ case EdgeTypes::Top:
+ return DirectionTypes::Vertical;
+ default:
+ return DirectionTypes::Horizontal;
+ }
+}
+
+static inline SEndPoint DoGetActualEndPoint(const SEndPoint &inPoint, const SRect &inMyRect,
+ const QT3DSVec2 &inOtherCenter)
+{
+ SEndPoint theEndPoint = inPoint;
+ if (inPoint.m_EdgeType == EdgeTypes::UnsetEdgeType)
+ theEndPoint = CEditorTransitionPath::CalculateDefaultEndPoint(inMyRect, inOtherCenter);
+ return theEndPoint;
+}
+
+static inline eastl::pair<QT3DSVec2, EdgeTypes::Enum>
+GetEndpointPoint(const SEndPoint &inPoint, const SRect &inMyRect, const QT3DSVec2 &inOtherCenter)
+{
+ SEndPoint theEndPoint = DoGetActualEndPoint(inPoint, inMyRect, inOtherCenter);
+ SLine theRectLine;
+ switch (theEndPoint.m_EdgeType) {
+ case EdgeTypes::Top:
+ theRectLine = inMyRect.topEdge();
+ break;
+ case EdgeTypes::Bottom:
+ theRectLine = inMyRect.bottomEdge();
+ break;
+ case EdgeTypes::Left:
+ theRectLine = inMyRect.leftEdge();
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ // fallthrough intentional
+ case EdgeTypes::Right:
+ theRectLine = inMyRect.rightEdge();
+ break;
+ }
+
+ return eastl::make_pair(theRectLine.toPoint(theEndPoint.m_Interp), theEndPoint.m_EdgeType);
+}
+
+inline bool RectDiffers(const Option<SRect> &inLhs, const SRect &inRhs)
+{
+ if (inLhs.isEmpty())
+ return true;
+ SRect lhs = *inLhs;
+ return CEditorTransitionPath::AreAlmostEqual(lhs.m_TopLeft, inRhs.m_TopLeft) == false
+ || CEditorTransitionPath::AreAlmostEqual(lhs.m_WidthHeight, inRhs.m_WidthHeight) == false;
+}
+}
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ bool SRect::contains(const QT3DSVec2 &inPoint) const
+ {
+ if (inPoint.x >= left() && inPoint.x <= right() && inPoint.y >= top()
+ && inPoint.y <= bottom())
+ return true;
+ return false;
+ }
+
+ void CEditorTransitionPath::SetPathType(TransitionPathTypes::Enum inType)
+ {
+ m_TransitionPathType = inType;
+ if (m_TransitionPathType == TransitionPathTypes::BeginToBegin
+ || m_TransitionPathType == TransitionPathTypes::Targetless)
+ m_ControlPoints.clear();
+ MarkDirty();
+ }
+
+ bool CEditorTransitionPath::UpdateBeginEndRects(const SRect &inStartRect,
+ const SRect &inEndRect)
+ {
+ bool dataChanged = false;
+
+ if (m_BeginRect.hasValue() && m_EndRect.hasValue()) {
+ QT3DSVec2 beginDiff = inStartRect.center() - m_BeginRect->center();
+ QT3DSVec2 endDiff = inEndRect.center() - m_EndRect->center();
+
+ if (AreAlmostEqual(beginDiff, QT3DSVec2(0, 0)) == false
+ && AreAlmostEqual(beginDiff, endDiff, 1)) {
+ dataChanged = m_ControlPoints.empty() == false;
+ // Move all the control points.
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx) {
+ SControlPoint &thePoint(m_ControlPoints[idx]);
+ QT3DSF32 diffComponent =
+ SControlPoint::GetComponent(beginDiff, thePoint.m_Direction);
+ thePoint.m_Position += diffComponent;
+ }
+ }
+ }
+ bool rectDiffers =
+ RectDiffers(m_BeginRect, inStartRect) || RectDiffers(m_EndRect, inEndRect);
+ m_BeginRect = inStartRect;
+ m_EndRect = inEndRect;
+ if (rectDiffers)
+ MarkDirty();
+ m_TransitionPathType = TransitionPathTypes::BeginToEnd;
+ return dataChanged;
+ }
+
+ bool CEditorTransitionPath::UpdateBeginEndRects(const SRect &inStartRect,
+ TransitionPathTypes::Enum inPathType)
+ {
+ bool dataChanged = m_ControlPoints.empty() == false;
+ QT3DS_ASSERT(inPathType == TransitionPathTypes::BeginToBegin
+ || inPathType == TransitionPathTypes::Targetless);
+ bool rectDiffers = m_EndRect.hasValue() || RectDiffers(m_BeginRect, inStartRect);
+ m_BeginRect = inStartRect;
+ m_EndRect = Empty();
+ if (rectDiffers || dataChanged)
+ MarkDirty();
+ m_TransitionPathType = inPathType;
+ return dataChanged;
+ }
+
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> CEditorTransitionPath::GetBeginPointAndEdge() const
+ {
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint(QT3DSVec2(0, 0),
+ EdgeTypes::UnsetEdgeType);
+ if (m_BeginRect.hasValue()) {
+ QT3DSVec2 center(m_BeginRect->center());
+ center.x += 1;
+ if (m_EndRect.hasValue())
+ center = m_EndRect->center();
+ theStartPoint = GetEndpointPoint(m_Begin, *m_BeginRect, center);
+ }
+ return theStartPoint;
+ }
+
+ eastl::pair<QT3DSVec2, QT3DSVec2> CEditorTransitionPath::GetBeginEndPoints() const
+ {
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint(GetBeginPointAndEdge());
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theEndPoint(QT3DSVec2(0, 0),
+ EdgeTypes::UnsetEdgeType);
+ if (m_EndRect.hasValue())
+ theEndPoint = GetEndpointPoint(m_End, *m_EndRect, m_BeginRect->center());
+
+ return eastl::make_pair(theStartPoint.first, theEndPoint.first);
+ }
+
+ SEndPoint CEditorTransitionPath::CalculateEndPoint(const SRect &inRect,
+ const QT3DSVec2 &inPoint,
+ QT3DSF32 inEdgeBoundary)
+ {
+ if (inRect.width() == 0 || inRect.height() == 0) {
+ QT3DS_ASSERT(false);
+ return SEndPoint();
+ }
+
+ SLine centerToPoint = SLine(inRect.center(), inPoint);
+ SLine leftOrRight;
+ SLine topOrBottom;
+ Option<QT3DSF32> isect;
+ EdgeTypes::Enum theEdge = EdgeTypes::UnsetEdgeType;
+ // If line runs right, test against right edge
+ QT3DSF32 distance = 0;
+ SLine theRectLine;
+ if (centerToPoint.dx() > 0) {
+ theRectLine = inRect.rightEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ // If we are out of range for the right edge
+ if (isect.hasValue() && inBounds(*isect, 0.0f, 1.0f)) {
+ distance = theRectLine.dy();
+ theEdge = EdgeTypes::Right;
+ }
+ } else {
+ theRectLine = inRect.leftEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ if (isect.hasValue() && inBounds(*isect, 0.0f, 1.0f)) {
+ distance = theRectLine.dy();
+ theEdge = EdgeTypes::Left;
+ }
+ }
+ // If we haven't resolved the edge type
+ if (theEdge == EdgeTypes::UnsetEdgeType) {
+ if (centerToPoint.dy() < 0) {
+ theRectLine = inRect.topEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ theEdge = EdgeTypes::Top;
+ distance = theRectLine.dx();
+ } else {
+ theRectLine = inRect.bottomEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ theEdge = EdgeTypes::Bottom;
+ distance = theRectLine.dx();
+ }
+ }
+ // Now drop a perpendicular from the point to the rect line.
+ SLine normalLine(inPoint, inPoint + QT3DSVec2(theRectLine.dy(), -theRectLine.dx()));
+ isect = theRectLine.intersect(normalLine);
+ if (isect.isEmpty()) {
+ theEdge = EdgeTypes::Right;
+ isect = .5f;
+ }
+ SEndPoint retval(theEdge, *isect);
+ QT3DSF32 normalizedBoundary =
+ NVMax(0.0f, inEdgeBoundary / static_cast<float>(fabs(distance)));
+
+ QT3DSF32 edgeLowerBound = NVMax(0.0f, normalizedBoundary);
+ QT3DSF32 edgeUpperBound = NVMin(1.0f, 1.0f - normalizedBoundary);
+ retval.m_Interp = NVMax(edgeLowerBound, retval.m_Interp);
+ retval.m_Interp = NVMin(edgeUpperBound, retval.m_Interp);
+ return retval;
+ }
+
+ // Default end points always are in the middle of the rect edge
+ SEndPoint CEditorTransitionPath::CalculateDefaultEndPoint(const SRect &inRect,
+ const QT3DSVec2 &inPoint)
+ {
+ SEndPoint ep = CalculateEndPoint(inRect, inPoint, 0.0f);
+ return SEndPoint(ep.m_EdgeType, .5f);
+ }
+
+ void CEditorTransitionPath::SetEndPoint(const QT3DSVec2 &inWorldPoint)
+ {
+ QT3DS_ASSERT(m_EndRect.hasValue());
+ m_End = CalculateEndPoint(*m_EndRect, inWorldPoint, m_StateEdgeBuffer);
+ MarkDirty();
+ }
+
+ void CEditorTransitionPath::SetBeginPoint(const QT3DSVec2 &inWorldPoint)
+ {
+ QT3DS_ASSERT(m_BeginRect.hasValue());
+ m_Begin = CalculateEndPoint(*m_BeginRect, inWorldPoint, m_StateEdgeBuffer);
+ MarkDirty();
+ }
+
+ SEndPoint CEditorTransitionPath::GetActualBeginPoint() const
+ {
+ return DoGetActualEndPoint(m_Begin, *m_BeginRect, m_EndRect->center());
+ }
+
+ SEndPoint CEditorTransitionPath::GetActualEndPoint() const
+ {
+ return DoGetActualEndPoint(m_End, *m_EndRect, m_BeginRect->center());
+ }
+
+ void CEditorTransitionPath::SetControlPoint(QT3DSI32 idx, const QT3DSVec2 &inPosition,
+ bool inMoveAdjacentEndPoint)
+ {
+ if (idx < 0 || idx >= (QT3DSI32)m_ControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ m_ControlPoints[idx].Set(inPosition);
+ if (inMoveAdjacentEndPoint) {
+ // Move the end points adjacent to the handle.
+ QT3DSI32 possiblePoint = MapControlPointToPossibleControlPoint(idx);
+ size_t numPossible = m_PossibleControlPoints.size();
+ if (possiblePoint == 0)
+ SetBeginPoint(inPosition);
+ if (possiblePoint == ((QT3DSI32)numPossible - 1))
+ SetEndPoint(inPosition);
+ }
+
+ MarkDirty();
+ }
+
+ void CEditorTransitionPath::SetControlPoints(const TPointList &inList)
+ {
+ QT3DS_ASSERT(inList.size() == m_Path.size());
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx) {
+ SControlPoint &theControlPoint(m_ControlPoints[idx]);
+ if (theControlPoint.m_PathIndex >= 0
+ && theControlPoint.m_PathIndex < (QT3DSI32)inList.size())
+ theControlPoint.Set(inList[theControlPoint.m_PathIndex]);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ TPointList CEditorTransitionPath::GetPath() const
+ {
+ MaybeRegeneratePath();
+ return m_Path;
+ }
+
+ TControlPointList CEditorTransitionPath::GetPossibleControlPoints() const
+ {
+ MaybeRegeneratePath();
+ return m_PossibleControlPoints;
+ }
+
+ SControlPoint CEditorTransitionPath::GetPossibleControlPoint(QT3DSI32 inIdx) const
+ {
+ MaybeRegeneratePath();
+ if (inIdx >= 0 && inIdx < (QT3DSI32)m_PossibleControlPoints.size())
+ return m_PossibleControlPoints[inIdx];
+ QT3DS_ASSERT(false);
+ return SControlPoint();
+ }
+
+ // This may create a new control point thus invalidating the path and possible control
+ // points.
+ QT3DSI32
+ CEditorTransitionPath::MapPossibleControlPointToControlPoint(QT3DSI32 inPossiblePointIndex)
+ {
+ if (inPossiblePointIndex < 0
+ || inPossiblePointIndex >= (QT3DSI32)m_PossibleControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+ const SControlPoint &thePossiblePoint(m_PossibleControlPoints[inPossiblePointIndex]);
+ TControlPointList::iterator iter = eastl::lower_bound(
+ m_ControlPoints.begin(), m_ControlPoints.end(), thePossiblePoint);
+ QT3DSI32 retval = (QT3DSI32)m_ControlPoints.size();
+ if (iter != m_ControlPoints.end()) {
+ retval = (QT3DSI32)(iter - m_ControlPoints.begin());
+ if (iter->m_PathIndex == thePossiblePoint.m_PathIndex)
+ return retval;
+ }
+
+ m_ControlPoints.insert(iter, thePossiblePoint);
+ return retval;
+ }
+
+ QT3DSI32 CEditorTransitionPath::MapControlPointToPossibleControlPoint(QT3DSI32 inPointIndex)
+ {
+ if (inPointIndex < 0 || inPointIndex >= (QT3DSI32)m_ControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+ const SControlPoint &theControlPoint(m_ControlPoints[inPointIndex]);
+ TControlPointList::iterator iter = eastl::lower_bound(
+ m_PossibleControlPoints.begin(), m_PossibleControlPoints.end(), theControlPoint);
+ if (iter != m_PossibleControlPoints.end()) {
+ QT3DSI32 retval = (QT3DSI32)(iter - m_PossibleControlPoints.begin());
+ if (iter->m_PathIndex == theControlPoint.m_PathIndex)
+ return retval;
+ }
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+
+ bool CEditorTransitionPath::DoesControlPointExist(QT3DSI32 inPossiblePointIndex) const
+ {
+ if (inPossiblePointIndex < 0
+ || inPossiblePointIndex >= (QT3DSI32)m_PossibleControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ const SControlPoint &thePossiblePoint(m_PossibleControlPoints[inPossiblePointIndex]);
+ TControlPointList::const_iterator iter = eastl::lower_bound(
+ m_ControlPoints.begin(), m_ControlPoints.end(), thePossiblePoint);
+ if (iter != m_ControlPoints.end() && iter->m_PathIndex == thePossiblePoint.m_PathIndex)
+ return true;
+ return false;
+ }
+
+ // Run through the control point list and if you find another control point on the line,
+ // return it's index. Else
+ // bail.
+ // If you are finding the remove algorithm is too specific or hard to use increase the 2.0f
+ // numbers below.
+ inline Option<size_t> NextParallelControlPointOnLine(const SControlPoint &inItem,
+ const SControlPoint &inEndPoint,
+ const TControlPointList &inList,
+ size_t inStartIdx)
+ {
+ for (size_t idx = inStartIdx, end = inList.size(); idx < end; ++idx) {
+ const SControlPoint &theTestPoint(inList[idx]);
+ if (theTestPoint.m_Direction == inItem.m_Direction) {
+ if (CEditorTransitionPath::AreAlmostEqual(inItem.m_Position,
+ theTestPoint.m_Position, 2.0f))
+ return idx + 1;
+ else
+ return Empty();
+ }
+ }
+
+ // Check if beginning and end lie in the same path, or within just a couple pixels.
+ if (inItem.m_Direction == inEndPoint.m_Direction
+ && CEditorTransitionPath::AreAlmostEqual(inItem.m_Position, inEndPoint.m_Position,
+ 2.0f))
+ return inList.size();
+
+ return Empty();
+ }
+
+ // We try to find control point that point the same direction and lie on the same line with
+ // only control points with
+ // orthogonal directions in between. If we find points that fullfill this criteria, we know
+ // we can remove all intermediate
+ // points because the transition path will end up making a straight line.
+ bool CEditorTransitionPath::RemoveRedundantControlPoints()
+ {
+ if (m_ControlPoints.empty())
+ return false;
+
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint = GetBeginPointAndEdge();
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theEndPoint;
+ if (m_EndRect.hasValue())
+ theEndPoint = GetEndpointPoint(m_End, *m_EndRect, m_BeginRect->center());
+ else
+ theEndPoint = theStartPoint;
+ // Find runs of control points in the same line. Remove the points in the middle of the
+ // line.
+ SControlPoint theLastControlPoint(EdgeTypeToDirectionType(theStartPoint.second));
+ theLastControlPoint.Set(theStartPoint.first);
+ SControlPoint theEndControlPoint(EdgeTypeToDirectionType(theEndPoint.second));
+ theEndControlPoint.Set(theEndPoint.first);
+ size_t numControlPoints(m_ControlPoints.size());
+ for (size_t idx = 0, end = numControlPoints; idx < end; ++idx) {
+ Option<size_t> removeEnd = NextParallelControlPointOnLine(
+ theLastControlPoint, theEndControlPoint, m_ControlPoints, idx);
+ if (removeEnd.isEmpty() == false) {
+ size_t lastItem = *removeEnd;
+ m_ControlPoints.erase(m_ControlPoints.begin() + idx,
+ m_ControlPoints.begin() + lastItem);
+ --idx;
+ end = m_ControlPoints.size();
+ } else
+ theLastControlPoint = m_ControlPoints[idx];
+ }
+ if (m_ControlPoints.size() != numControlPoints) {
+ MarkDirty();
+ return true;
+ }
+ return false;
+ }
+
+ void CEditorTransitionPath::RestoreAutoTransition()
+ {
+ m_ControlPoints.clear();
+ m_Begin = SEndPoint();
+ m_End = SEndPoint();
+ MarkDirty();
+ }
+
+ bool CEditorTransitionPath::IsManualMode() const
+ {
+ return m_Begin.m_EdgeType != EdgeTypes::UnsetEdgeType
+ || m_End.m_EdgeType != EdgeTypes::UnsetEdgeType || m_ControlPoints.empty() == false;
+ }
+
+ SPointQueryResult CEditorTransitionPath::Pick(QT3DSVec2 inPoint, QT3DSVec2 inControlBoxDims,
+ QT3DSVec2 inEndBoxDims)
+ {
+ MaybeRegeneratePath();
+
+ if (inEndBoxDims.x && inEndBoxDims.y) {
+ SRect endBox(QT3DSVec2(-1.0f * inEndBoxDims.x / 2, -1.0f * inEndBoxDims.y / 2),
+ inEndBoxDims);
+ SRect testRect(endBox);
+ testRect.translate(m_Path.front());
+ if (testRect.contains(inPoint))
+ return SPointQueryResult(PointQueryResultType::Begin);
+ testRect = SRect(endBox);
+ testRect.translate(m_Path.back());
+ if (testRect.contains(inPoint))
+ return SPointQueryResult(PointQueryResultType::End);
+ }
+ if (inControlBoxDims.x && inControlBoxDims.y) {
+ SRect theControlBox(
+ QT3DSVec2(-1.0f * inControlBoxDims.x / 2.0f, -1.0f * inControlBoxDims.y / 2.0f),
+ inControlBoxDims);
+ for (size_t idx = 0, end = m_PossibleControlPoints.size(); idx < end; ++idx) {
+ const SControlPoint &thePoint(m_PossibleControlPoints[idx]);
+ QT3DSVec2 startPoint = m_Path[thePoint.m_PathIndex];
+ QT3DSVec2 endPoint = m_Path[thePoint.m_PathIndex + 1];
+ // We stretch the rect to contain the entire line, not just where we display the
+ // point.
+ QT3DSVec2 lineDims = endPoint - startPoint;
+ QT3DSF32 lineRectHeight = SControlPoint::GetComponent(theControlBox.m_WidthHeight,
+ thePoint.m_Direction);
+ QT3DSF32 lineRectLength =
+ fabs(SControlPoint::GetOrthogonalComponent(lineDims, thePoint.m_Direction));
+ QT3DSVec2 rectDims = SControlPoint::FromComponentToVector(
+ lineRectHeight, lineRectLength, thePoint.m_Direction);
+ QT3DSVec2 rectTopLeft =
+ QT3DSVec2(NVMin(startPoint.x, endPoint.x), NVMin(startPoint.y, endPoint.y));
+ QT3DSF32 rectComponent =
+ SControlPoint::GetComponent(rectTopLeft, thePoint.m_Direction);
+ rectComponent -= lineRectHeight / 2.0f; // Center the box about the line.
+ rectTopLeft = SControlPoint::SetComponent(rectTopLeft, rectComponent,
+ thePoint.m_Direction);
+ SRect testRect(rectTopLeft, rectDims);
+ if (testRect.contains(inPoint))
+ return SPointQueryResult(PointQueryResultType::Control, (QT3DSI32)idx);
+ }
+ }
+ return PointQueryResultType::NoPoint;
+ }
+
+ SPointQueryResult
+ CEditorTransitionPath::PickClosestControlPoint(QT3DSVec2 inPoint,
+ DirectionTypes::Enum inDirectionType)
+ {
+ MaybeRegeneratePath();
+ QT3DSI32 closestIdx = -1;
+ QT3DSF32 minDistance = QT3DS_MAX_F32;
+
+ for (size_t idx = 0, end = m_PossibleControlPoints.size(); idx < end; ++idx) {
+ const SControlPoint &thePoint(m_PossibleControlPoints[idx]);
+ QT3DSVec2 startPoint = m_Path[thePoint.m_PathIndex];
+ QT3DSVec2 endPoint = m_Path[thePoint.m_PathIndex + 1];
+ SLine theLine(startPoint, endPoint);
+ QT3DSF32 distance = theLine.distance(inPoint);
+
+ if (distance < minDistance && thePoint.m_Direction == inDirectionType) {
+ closestIdx = idx;
+ minDistance = distance;
+ }
+ }
+ if (closestIdx == -1)
+ return SPointQueryResult();
+ return SPointQueryResult(PointQueryResultType::Control, closestIdx);
+ }
+
+ // The output functions are both setup under these assumptions:
+ // 1. The current point does *not* have representation in the possible control points list.
+ // 2. The last point *does* have representation in the possible control points list.
+ // 3. The algorithm should output all path elements up and to but not including the
+ // current point.
+ // So, given straight line do not output any possible points.
+ // Given zig-zag, output two points.
+ SControlPoint
+ CEditorTransitionPath::OutputParallelPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint,
+ QT3DSF32 runWidth) const
+ {
+ const SControlPoint &theRunPoint(inCurrentPoint);
+ const SControlPoint theLastControlPoint(inLastPoint);
+ DirectionTypes::Enum runDirection(inLastPoint.m_Direction);
+ if (AreAlmostEqual(theRunPoint.m_Position, theLastControlPoint.m_Position, 1.0)) {
+ // Straigh line. Perhaps remove this point?
+ theRunPoint.m_PathIndex = theLastControlPoint.m_PathIndex;
+ return theRunPoint;
+ } else {
+ // First, output a possible control point inline with inLastPoint
+ SControlPoint possiblePoint(inLastPoint);
+ possiblePoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+ m_PossibleControlPoints.push_back(possiblePoint);
+ // Output zig-zag, we zig zag from last control point to theRunPoint. We need to
+ // push two points and two control points.
+ QT3DSVec2 startPos(m_Path.back());
+ QT3DSF32 startComponent = SControlPoint::GetComponent(startPos, runDirection);
+ QT3DSF32 orthoStartComponent =
+ SControlPoint::GetOrthogonalComponent(startPos, runDirection);
+ QT3DSF32 endComponent = theRunPoint.m_Position;
+ QT3DSF32 orthoEndComponent = orthoStartComponent + runWidth;
+ QT3DSVec2 endPos = SControlPoint::FromComponentToVector(
+ endComponent, orthoEndComponent, runDirection);
+ QT3DSF32 zigZagOrthoPos = orthoStartComponent + runWidth / 2;
+ QT3DSI32 crossbarIndex = (QT3DSI32)m_Path.size();
+ QT3DSVec2 crossbarStart = SControlPoint::FromComponentToVector(
+ startComponent, zigZagOrthoPos, runDirection);
+ m_Path.push_back(crossbarStart);
+ m_PossibleControlPoints.push_back(
+ SControlPoint(SControlPoint::OppositeDirection(theRunPoint.m_Direction),
+ orthoStartComponent, crossbarIndex));
+ QT3DSVec2 crossbarEnd = SControlPoint::FromComponentToVector(
+ endComponent, zigZagOrthoPos, theRunPoint.m_Direction);
+ m_Path.push_back(crossbarEnd);
+ theRunPoint.m_PathIndex = crossbarIndex + 1;
+ // Do not, however, output the run point. This will happen in the next step.
+ return inCurrentPoint;
+ }
+ }
+
+ // Given right angle, output 1 point.
+ SControlPoint
+ CEditorTransitionPath::OutputOrthogonalPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint) const
+ {
+ SLine lastLine = inLastPoint.ToLine();
+ SLine currentLine = inCurrentPoint.ToLine();
+ Option<QT3DSF32> isect = lastLine.intersect(currentLine);
+ QT3DS_ASSERT(isect.hasValue());
+ inLastPoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+ m_PossibleControlPoints.push_back(inLastPoint);
+ if (isect.hasValue()) {
+ QT3DSVec2 theIsectPoint = lastLine.toPoint(*isect);
+ m_Path.push_back(theIsectPoint);
+ }
+ inCurrentPoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+ return inCurrentPoint;
+ }
+
+ void CEditorTransitionPath::MaybeRegeneratePath() const
+ {
+ if (IsDirty() == false)
+ return;
+
+ if (m_TransitionPathType == TransitionPathTypes::BeginToEnd) {
+ // Ensure intermediate information is cleared.
+ const_cast<CEditorTransitionPath *>(this)->MarkDirty();
+ // We don't have the begin and end states.
+ if (m_BeginRect.isEmpty() || m_EndRect.isEmpty()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ // Find the start and end points.
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint =
+ GetEndpointPoint(m_Begin, *m_BeginRect, m_EndRect->center());
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theEndPoint =
+ GetEndpointPoint(m_End, *m_EndRect, m_BeginRect->center());
+ m_Path.push_back(theStartPoint.first);
+ SControlPoint theLastControlPoint(EdgeTypeToDirectionType(theStartPoint.second));
+ theLastControlPoint.Set(theStartPoint.first);
+ theLastControlPoint.m_PathIndex = 0;
+ SControlPoint theEndControlPoint(EdgeTypeToDirectionType(theEndPoint.second));
+ theEndControlPoint.Set(theEndPoint.first);
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx) {
+ const SControlPoint &thePoint(m_ControlPoints[idx]);
+ if (thePoint.m_Direction == theLastControlPoint.m_Direction) {
+ // zig zag. Requires us to find the first point that *isn't a zig zag in
+ // order to
+ // calculate where the zig zag should be. We could have a section composed
+ // of only
+ // parallel directions and we can't lay then out until we find how much
+ // distance we have
+ // to space each on out.
+ // Image you get to this point and you find you have a set of control points
+ // with vertical direction.
+ // Their positions will tell us how far left each one should sit. But we
+ // don't have enough information
+ // to lay them out without knowing how much vertical space this section
+ // should fill. So we would have to
+ // search forward until we can figure this out.
+ QT3DSVec2 runStart = m_Path.back();
+ // Search forward until either we run out of points or until we find a point
+ // who's direction
+ // does not match the current direction. We call a contiguous set of
+ // control points who all
+ // have the same direction a 'run'.
+ size_t runEnd;
+ size_t zigzagCount = 1;
+ DirectionTypes::Enum runDirection = theLastControlPoint.m_Direction;
+ // Search forward till we find a point that is different.
+ for (runEnd = idx + 1; runEnd < end
+ && m_ControlPoints[runEnd].m_Direction == thePoint.m_Direction;
+ ++runEnd) {
+ // Skip items that are in line. They shouldn't be counted towards our
+ // zigzag count.
+ if (AreAlmostEqual(m_ControlPoints[runEnd].m_Position,
+ m_ControlPoints[runEnd - 1].m_Position)
+ == false)
+ ++zigzagCount;
+ }
+ // Two possible cases. Either we find a control point that has a different
+ // direction in which case we then figure out
+ // how much space we need overall *or* we ran out of control points in which
+ // case we use the end point.
+ QT3DSVec2 runEndPoint(0, 0);
+ if (runEnd == end) {
+ // check if the end point direction is the same. This could be the
+ // final zig zag. Else it will be a righthand turn
+ if (EdgeTypeToDirectionType(theEndPoint.second) == runDirection
+ && AreAlmostEqual(theEndControlPoint.m_Position,
+ thePoint.m_Position)
+ == false)
+ ++zigzagCount;
+
+ runEndPoint = theEndPoint.first;
+ } else {
+ SLine thePointLine(thePoint.ToLine());
+ Option<QT3DSF32> isect =
+ thePointLine.intersect(m_ControlPoints[runEnd].ToLine());
+ if (isect.hasValue())
+ runEndPoint = thePointLine.toPoint(*isect);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ QT3DSF32 runOrthoStart =
+ SControlPoint::GetOrthogonalComponent(runStart, runDirection);
+ QT3DSF32 runOrthoEnd =
+ SControlPoint::GetOrthogonalComponent(runEndPoint, runDirection);
+ QT3DSF32 runRange = runOrthoEnd - runOrthoStart;
+ QT3DSF32 runWidth = runRange / (QT3DSF32)zigzagCount;
+ // Now we iterate through the run itself and output path elements.
+ for (; idx < runEnd; ++idx) {
+ theLastControlPoint = OutputParallelPoints(
+ theLastControlPoint, m_ControlPoints[idx], runWidth);
+ }
+ // Subtract one to account for the loop upate that happens next
+ --idx;
+ } else // right angle
+ {
+ theLastControlPoint =
+ OutputOrthogonalPoints(theLastControlPoint, m_ControlPoints[idx]);
+ }
+ }
+ // Finished iterating through the control points. Now we have the sticky situation
+ // of the very last point
+ // and how it joins with the end point.
+ QT3DSVec2 lastPoint(m_Path.back());
+ if (theEndControlPoint.m_Direction == theLastControlPoint.m_Direction) {
+ QT3DSF32 lastPointOrthoComponent = SControlPoint::GetOrthogonalComponent(
+ lastPoint, theLastControlPoint.m_Direction);
+ QT3DSF32 endOrthoComponent = SControlPoint::GetOrthogonalComponent(
+ theEndPoint.first, theLastControlPoint.m_Direction);
+ QT3DSF32 runWidth = endOrthoComponent - lastPointOrthoComponent;
+ OutputParallelPoints(theLastControlPoint, theEndControlPoint, runWidth);
+ } else {
+ theLastControlPoint =
+ OutputOrthogonalPoints(theLastControlPoint, theEndControlPoint);
+ }
+ // Now output the last possible point which matches the end control point's
+ // direction and such.
+ theEndControlPoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+#ifdef _DEBUG
+ // The directly previous possible point in the list should not match this point. In
+ // fact, it really should be orthogonal.
+ if (m_PossibleControlPoints.size()) {
+ QT3DS_ASSERT(m_PossibleControlPoints.back().m_Direction
+ != theEndControlPoint.m_Direction
+ || AreAlmostEqual(m_PossibleControlPoints.back().m_Position,
+ theEndControlPoint.m_Position)
+ == false);
+ }
+#endif
+ m_PossibleControlPoints.push_back(theEndControlPoint);
+ // Finally push back the last item.
+ m_Path.push_back(theEndPoint.first);
+ } else if (m_TransitionPathType == TransitionPathTypes::BeginToBegin
+ || m_TransitionPathType == TransitionPathTypes::Targetless) {
+ QT3DSVec2 beginCenter(m_BeginRect->center());
+ beginCenter.x += 1;
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint =
+ GetEndpointPoint(m_Begin, *m_BeginRect, beginCenter);
+ QT3DSVec2 lineDir;
+ m_Path.push_back(theStartPoint.first);
+ if (m_TransitionPathType == TransitionPathTypes::BeginToBegin) {
+ switch (theStartPoint.second) {
+ case EdgeTypes::Top:
+ lineDir = QT3DSVec2(0, -1);
+ break;
+ case EdgeTypes::Bottom:
+ lineDir = QT3DSVec2(0, 1);
+ break;
+ case EdgeTypes::Left:
+ lineDir = QT3DSVec2(-1, 0);
+ break;
+ case EdgeTypes::Right:
+ lineDir = QT3DSVec2(1, 0);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ QT3DSF32 squareDiagLen = 30;
+ QT3DSF32 halfDiag = squareDiagLen / 2.0f;
+ QT3DSVec2 theOppPoint = theStartPoint.first + lineDir * squareDiagLen;
+ QT3DSVec2 middle = (theStartPoint.first + theOppPoint) / 2.0f;
+ QT3DSVec2 orthoLineDir = QT3DSVec2(lineDir.y, lineDir.x);
+ QT3DSVec2 loopTop = middle + orthoLineDir * halfDiag;
+ QT3DSVec2 loopBottom = middle - orthoLineDir * halfDiag;
+ m_Path.push_back(loopTop);
+ m_Path.push_back(theOppPoint);
+ m_Path.push_back(loopBottom);
+ m_Path.push_back(theStartPoint.first);
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+}
+} \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h
new file mode 100644
index 00000000..bacf60f2
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h
@@ -0,0 +1,467 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_TRANSITION_PATH_H
+#define QT3DS_STATE_EDITOR_TRANSITION_PATH_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSVec2.h"
+#include "EASTL/vector.h"
+#include "foundation/Qt3DSOption.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct EdgeTypes
+ {
+ enum Enum {
+ UnsetEdgeType = 0,
+ Left,
+ Right,
+ Top,
+ Bottom,
+ };
+ };
+
+ struct SEndPoint
+ {
+ // Which edge are we on
+ EdgeTypes::Enum m_EdgeType;
+ // How far from the start of the edge are we.
+ QT3DSF32 m_Interp;
+ SEndPoint(EdgeTypes::Enum inType = EdgeTypes::UnsetEdgeType, QT3DSF32 interp = .5f)
+ : m_EdgeType(inType)
+ , m_Interp(interp)
+ {
+ }
+ };
+
+ struct DirectionTypes
+ {
+ enum Enum {
+ UnknownDirection = 0,
+ Horizontal,
+ Vertical,
+ };
+ };
+
+ struct SLine
+ {
+ QT3DSVec2 m_Begin;
+ QT3DSVec2 m_End;
+
+ SLine(const QT3DSVec2 &beg = QT3DSVec2(0, 0), const QT3DSVec2 &end = QT3DSVec2(0, 0))
+ : m_Begin(beg)
+ , m_End(end)
+ {
+ }
+
+ QT3DSF32 dx() const { return m_End.x - m_Begin.x; }
+ QT3DSF32 dy() const { return m_End.y - m_Begin.y; }
+ QT3DSVec2 begin() const { return m_Begin; }
+ QT3DSVec2 end() const { return m_End; }
+ QT3DSVec2 toPoint(QT3DSF32 interp) { return m_Begin + QT3DSVec2(dx() * interp, dy() * interp); }
+ Option<QT3DSF32> slope() const
+ {
+ QT3DSF32 run = dx();
+ if (fabs(run) > .0001f)
+ return dy() / dx();
+ return Empty();
+ }
+ // If this function returns a value, it returns an interpolation value such that if you
+ // call toPoint on the return value it gives you the intersection point.
+ // http://tog.acm.org/resources/GraphicsGems/gemsiii/insectc.c
+ // Simplifications taken from Qt's line intersect
+ Option<QT3DSF32> intersect(const SLine &other) const
+ {
+ // ipmlementation is based on Graphics Gems III's "Faster Line Segment Intersection"
+ QT3DSVec2 a = m_End - m_Begin;
+ QT3DSVec2 b = other.m_End - other.m_Begin;
+ QT3DSVec2 c = m_Begin - other.m_Begin;
+
+ QT3DSF32 denominator = a.y * b.x - a.x * b.y;
+
+ if (denominator == 0 || fabs(denominator) < .0001f)
+ return Empty();
+
+ QT3DSF32 reciprocal = 1.0f / denominator;
+ return (b.y * c.x - b.x * c.y) * reciprocal;
+ }
+
+ // Caculates the top half of the distance to line equation with the fabs or sqrt.
+ QT3DSF32 distanceNumerator(const QT3DSVec2 &inPoint) const
+ {
+ QT3DSF32 x2 = inPoint.x;
+ QT3DSF32 y2 = inPoint.y;
+
+ QT3DSF32 x1 = m_End.x;
+ QT3DSF32 y1 = m_End.y;
+
+ QT3DSF32 x0 = m_Begin.x;
+ QT3DSF32 y0 = m_Begin.y;
+ return ((x2 - x1) * (y1 - y0)) - ((x1 - x0) * (y2 - y1));
+ }
+
+ QT3DSF32 distance(const QT3DSVec2 &inPoint) const
+ {
+ QT3DSF32 theDx = dx();
+ QT3DSF32 theDy = dy();
+ return fabs(distanceNumerator(inPoint)) / sqrtf(theDx * theDx + theDy * theDy);
+ }
+ };
+
+ // Rect in same coordinate space as QT.
+ struct SRect
+ {
+ QT3DSVec2 m_TopLeft;
+ QT3DSVec2 m_WidthHeight;
+ SRect(const QT3DSVec2 &tl = QT3DSVec2(0, 0), const QT3DSVec2 &wh = QT3DSVec2(0, 0))
+ : m_TopLeft(tl)
+ , m_WidthHeight(wh)
+ {
+ }
+ QT3DSF32 left() const { return m_TopLeft.x; }
+ QT3DSF32 top() const { return m_TopLeft.y; }
+ QT3DSF32 right() const { return m_TopLeft.x + width(); }
+ QT3DSF32 bottom() const { return m_TopLeft.y + height(); }
+ QT3DSF32 width() const { return m_WidthHeight.x; }
+ QT3DSF32 height() const { return m_WidthHeight.y; }
+ QT3DSVec2 topLeft() const { return m_TopLeft; }
+ QT3DSVec2 bottomLeft() const { return QT3DSVec2(left(), bottom()); }
+ QT3DSVec2 bottomRight() const { return QT3DSVec2(right(), bottom()); }
+ QT3DSVec2 topRight() const { return QT3DSVec2(right(), top()); }
+ QT3DSVec2 center() const
+ {
+ return QT3DSVec2(left() + width() / 2.0f, top() + height() / 2.0f);
+ }
+ SLine leftEdge() const { return SLine(topLeft(), bottomLeft()); }
+ SLine rightEdge() const { return SLine(topRight(), bottomRight()); }
+ SLine topEdge() const { return SLine(topLeft(), topRight()); }
+ SLine bottomEdge() const { return SLine(bottomLeft(), bottomRight()); }
+ void translate(QT3DSF32 x, QT3DSF32 y)
+ {
+ m_TopLeft.x += x;
+ m_TopLeft.y += y;
+ }
+ void translate(const QT3DSVec2 &vec)
+ {
+ m_TopLeft.x += vec.x;
+ m_TopLeft.y += vec.y;
+ }
+ bool contains(const QT3DSVec2 &inPoint) const;
+ };
+
+ struct SControlPoint
+ {
+ DirectionTypes::Enum m_Direction;
+ // World space position
+ QT3DSF32 m_Position;
+ // This is a calculated value. Values set will be ignored in favor of recaculating by
+ // re-deriving
+ // the transition path.
+ mutable QT3DSI32 m_PathIndex;
+ SControlPoint(DirectionTypes::Enum inDir = DirectionTypes::UnknownDirection,
+ QT3DSF32 pos = 0.0f, QT3DSI32 idx = -1)
+ : m_Direction(inDir)
+ , m_Position(pos)
+ , m_PathIndex(idx)
+ {
+ }
+ bool operator<(const SControlPoint &inOther) const
+ {
+ return m_PathIndex < inOther.m_PathIndex;
+ }
+ static QT3DSF32 GetComponent(const QT3DSVec2 &inPoint, DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return inPoint.y;
+ break;
+ case DirectionTypes::Vertical:
+ return inPoint.x;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ return 0;
+ }
+ static DirectionTypes::Enum OppositeDirection(DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return DirectionTypes::Vertical;
+ default:
+ return DirectionTypes::Horizontal;
+ }
+ }
+ static QT3DSF32 GetOrthogonalComponent(const QT3DSVec2 &inPoint, DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return inPoint.x;
+ default:
+ return inPoint.y;
+ }
+ }
+ static QT3DSVec2 FromComponentToVector(QT3DSF32 inComponent, QT3DSF32 orthoComponent,
+ DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return QT3DSVec2(orthoComponent, inComponent);
+ default:
+ return QT3DSVec2(inComponent, orthoComponent);
+ }
+ }
+ static QT3DSVec2 SetComponent(const QT3DSVec2 &inPoint, QT3DSF32 inValue,
+ DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return QT3DSVec2(inPoint.x, inValue);
+ default:
+ return QT3DSVec2(inValue, inPoint.y);
+ }
+ }
+ void Set(const QT3DSVec2 &inPoint) { m_Position = GetComponent(inPoint, m_Direction); }
+ SLine ToLine() const
+ {
+ switch (m_Direction) {
+ case DirectionTypes::Horizontal:
+ return SLine(QT3DSVec2(0, m_Position), QT3DSVec2(1, m_Position));
+ default:
+ return SLine(QT3DSVec2(m_Position, 0), QT3DSVec2(m_Position, 1));
+ }
+ }
+ };
+
+ typedef eastl::vector<SControlPoint> TControlPointList;
+ typedef eastl::vector<QT3DSVec2> TPointList;
+
+ struct PointQueryResultType
+ {
+ enum Enum {
+ NoPoint = 0,
+ Begin,
+ End,
+ Control,
+ };
+ };
+
+ struct SPointQueryResult
+ {
+ PointQueryResultType::Enum m_QueryType;
+ QT3DSI32 m_Index;
+ SPointQueryResult(
+ PointQueryResultType::Enum inResultType = PointQueryResultType::NoPoint,
+ QT3DSI32 inIndex = -1)
+ : m_QueryType(inResultType)
+ , m_Index(inIndex)
+ {
+ }
+ };
+
+ struct TransitionPathTypes
+ {
+ enum Enum {
+ UnknownPathType = 0,
+ BeginToEnd = 1,
+ BeginToBegin = 2,
+ Targetless = 3,
+ };
+ };
+
+ class CEditorTransitionPath
+ {
+ QT3DSF32 m_StateEdgeBuffer;
+ Option<SRect> m_BeginRect;
+ Option<SRect> m_EndRect;
+ SEndPoint m_Begin;
+ SEndPoint m_End;
+ // Control points that the user has edited.
+ TControlPointList m_ControlPoints;
+ TransitionPathTypes::Enum m_TransitionPathType;
+
+ // ephemeral data, can be derived from start,end points (along with start/end state) and
+ // the
+ // user control points.
+ mutable TPointList m_Path;
+ // The full set of possible control points can be derived from the path.
+ mutable TControlPointList m_PossibleControlPoints;
+
+ public:
+ CEditorTransitionPath(QT3DSF32 inStateEdgeBuffer = 10.0f)
+ : m_StateEdgeBuffer(inStateEdgeBuffer)
+ , m_TransitionPathType(TransitionPathTypes::BeginToEnd)
+ {
+ }
+ Option<SRect> GetBeginRect() const { return m_BeginRect; }
+ void SetBeginRect(const Option<SRect> &inRect)
+ {
+ m_BeginRect = inRect;
+ MarkDirty();
+ }
+
+ Option<SRect> GetEndRect() const { return m_EndRect; }
+ void SetEndRect(const Option<SRect> &inRect)
+ {
+ m_EndRect = inRect;
+ MarkDirty();
+ }
+
+ TransitionPathTypes::Enum GetPathType() const { return m_TransitionPathType; }
+ // May delete all the control points.
+ void SetPathType(TransitionPathTypes::Enum inType);
+ // This may move control points if instart rect and inendrect have shifted by similar
+ // amounts
+ // Returns true if persistent data changed, false otherwise.
+ bool UpdateBeginEndRects(const SRect &inStartRect, const SRect &inEndRect);
+ bool UpdateBeginEndRects(const SRect &inStartRect,
+ TransitionPathTypes::Enum inPathType);
+ eastl::pair<QT3DSVec2, QT3DSVec2> GetBeginEndPoints() const;
+
+ SEndPoint GetEndPoint() const { return m_End; }
+ void SetEndPoint(const SEndPoint &pt)
+ {
+ m_End = pt;
+ MarkDirty();
+ }
+ void SetEndPoint(const QT3DSVec2 &inWorldPoint);
+
+ SEndPoint GetBeginPoint() const { return m_Begin; }
+ void SetBeginPoint(const SEndPoint &pt)
+ {
+ m_Begin = pt;
+ MarkDirty();
+ }
+ void SetBeginPoint(const QT3DSVec2 &inWorldPoint);
+ // Return where the end point will be. Will not return an invalid in point
+ // or an end point where information is not set.
+ SEndPoint GetActualBeginPoint() const;
+ SEndPoint GetActualEndPoint() const;
+
+ TControlPointList GetControlPoints() const { return m_ControlPoints; }
+ SControlPoint GetControlPoint(QT3DSI32 inIndex) const
+ {
+ if (inIndex < (QT3DSI32)m_ControlPoints.size())
+ return m_ControlPoints[inIndex];
+ return SControlPoint();
+ }
+ void SetControlPoints(const TControlPointList &list)
+ {
+ m_ControlPoints = list;
+ MarkDirty();
+ }
+ void SetControlPoint(QT3DSI32 inControlPointIndex, const QT3DSVec2 &inPosition,
+ bool inMoveAdjacentEndPoint);
+
+ // Set the control points by updating the individual items in the path list.
+ // This works if you do not add/remove points from the list.
+ //
+ // list = GetPath
+ // move points, don't add,delete
+ // setControlPoints(list)
+ void SetControlPoints(const TPointList &inList);
+ TPointList GetPath() const;
+ TControlPointList GetPossibleControlPoints() const;
+ SControlPoint GetPossibleControlPoint(QT3DSI32 inIdx) const;
+
+ // This may create a new control point thus invalidating the path and possible control
+ // points.
+ QT3DSI32 MapPossibleControlPointToControlPoint(QT3DSI32 inPossiblePointIndex);
+ QT3DSI32 MapControlPointToPossibleControlPoint(QT3DSI32 inPointIndex);
+ // Returns true if MapPossibleControlPointToControlPoint will *not* create a new point
+ // false otherwise.
+ bool DoesControlPointExist(QT3DSI32 inPossiblePointIndex) const;
+
+ // Returns true if any points were removed.
+ bool RemoveRedundantControlPoints();
+
+ void RestoreAutoTransition();
+
+ bool IsManualMode() const;
+
+ // Query against the dataset to see what got picked. Includes the endoints.
+ // The rects should be centered about the original and just describe the picking
+ // rect to hit against. Empty rects will not get hit.
+ // Note that the result is the index into the possible point list where the hit
+ // happened.
+ // If you intend to manipulate the point, you need to call
+ // MapPossibleControlPointToControlPoint,
+ // Which may add a control point which you then can manipulate.
+ SPointQueryResult Pick(QT3DSVec2 inPoint, QT3DSVec2 inControlBoxDims,
+ QT3DSVec2 inEndBoxDims = QT3DSVec2(0, 0));
+ SPointQueryResult PickClosestControlPoint(QT3DSVec2 inPoint,
+ DirectionTypes::Enum inDirectionType);
+
+ void MarkDirty()
+ {
+ m_Path.clear();
+ m_PossibleControlPoints.clear();
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx)
+ m_ControlPoints[idx].m_PathIndex = -1;
+ }
+
+ bool IsDirty() const { return m_Path.empty(); }
+
+ static SEndPoint CalculateEndPoint(const SRect &inRect, const QT3DSVec2 &inPoint,
+ QT3DSF32 inEdgeBoundary);
+ static SEndPoint CalculateDefaultEndPoint(const SRect &inRect, const QT3DSVec2 &inPoint);
+ static bool AreAlmostEqual(QT3DSF32 lhs, QT3DSF32 rhs, QT3DSF32 error = .001f)
+ {
+ if (fabs(lhs - rhs) < error)
+ return true;
+ return false;
+ }
+
+ static bool AreAlmostEqual(const QT3DSVec2 &lhs, const QT3DSVec2 &rhs, QT3DSF32 error = .001f)
+ {
+ return AreAlmostEqual(lhs.x, rhs.x, error) && AreAlmostEqual(lhs.y, rhs.y, error);
+ }
+
+ // Regenerates the path if this object is dirty.
+ void MaybeRegeneratePath() const;
+
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> GetBeginPointAndEdge() const;
+
+ protected:
+ // Output the path between points that have the same direction.
+ SControlPoint OutputParallelPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint,
+ QT3DSF32 inRunWidth) const;
+ SControlPoint OutputOrthogonalPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint) const;
+ };
+ }
+}
+}
+
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h
new file mode 100644
index 00000000..d47e6c99
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_VALUE_H
+#define QT3DS_STATE_EDITOR_VALUE_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditor.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "foundation/Qt3DSVec3.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct ValueTypes
+ {
+ enum Enum {
+ NoEditorValue = 0,
+ String,
+ ObjPtr,
+ ObjPtrList,
+ Vec2,
+ Vec2List,
+ Vec3,
+ Boolean,
+ U32,
+ };
+ };
+
+ template <typename TDataType>
+ struct SValueTypeMap
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::NoEditorValue; }
+ };
+
+ template <>
+ struct SValueTypeMap<TEditorStr>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::String; }
+ };
+ template <>
+ struct SValueTypeMap<TObjPtr>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::ObjPtr; }
+ };
+ template <>
+ struct SValueTypeMap<TObjList>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::ObjPtrList; }
+ };
+ template <>
+ struct SValueTypeMap<QT3DSVec2>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Vec2; }
+ };
+ template <>
+ struct SValueTypeMap<TVec2List>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Vec2List; }
+ };
+ template <>
+ struct SValueTypeMap<QT3DSVec3>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Vec3; }
+ };
+ template <>
+ struct SValueTypeMap<bool>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Boolean; }
+ };
+ template <>
+ struct SValueTypeMap<QT3DSU32>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::U32; }
+ };
+
+ struct SValueUnionTraits
+ {
+ typedef ValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(TVec2List),
+ };
+
+ static TIdType getNoDataId() { return ValueTypes::NoEditorValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ValueTypes::String:
+ return inVisitor(*NVUnionCast<TEditorStr *>(inData));
+ case ValueTypes::ObjPtr:
+ return inVisitor(*NVUnionCast<TObjPtr *>(inData));
+ case ValueTypes::ObjPtrList:
+ return inVisitor(*NVUnionCast<TObjList *>(inData));
+ case ValueTypes::Vec2:
+ return inVisitor(*NVUnionCast<QT3DSVec2 *>(inData));
+ case ValueTypes::Vec2List:
+ return inVisitor(*NVUnionCast<TVec2List *>(inData));
+ case ValueTypes::Vec3:
+ return inVisitor(*NVUnionCast<QT3DSVec3 *>(inData));
+ case ValueTypes::Boolean:
+ return inVisitor(*NVUnionCast<bool *>(inData));
+ case ValueTypes::U32:
+ return inVisitor(*NVUnionCast<QT3DSU32 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ValueTypes::NoEditorValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ValueTypes::String:
+ return inVisitor(*NVUnionCast<const TEditorStr *>(inData));
+ case ValueTypes::ObjPtr:
+ return inVisitor(*NVUnionCast<const TObjPtr *>(inData));
+ case ValueTypes::ObjPtrList:
+ return inVisitor(*NVUnionCast<const TObjList *>(inData));
+ case ValueTypes::Vec2:
+ return inVisitor(*NVUnionCast<const QT3DSVec2 *>(inData));
+ case ValueTypes::Vec2List:
+ return inVisitor(*NVUnionCast<const TVec2List *>(inData));
+ case ValueTypes::Vec3:
+ return inVisitor(*NVUnionCast<const QT3DSVec3 *>(inData));
+ case ValueTypes::Boolean:
+ return inVisitor(*NVUnionCast<const bool *>(inData));
+ case ValueTypes::U32:
+ return inVisitor(*NVUnionCast<const QT3DSU32 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ValueTypes::NoEditorValue:
+ return inVisitor();
+ }
+ }
+ };
+ }
+}
+}
+
+#ifndef _INTEGRITYPLATFORM
+// need some specializations in the original nv foundation namespace
+namespace qt3ds {
+namespace foundation {
+
+ template <>
+ struct DestructTraits<QT3DSVec2>
+ {
+ void destruct(QT3DSVec2 &) {}
+ };
+ template <>
+ struct DestructTraits<QT3DSVec3>
+ {
+ void destruct(QT3DSVec3 &) {}
+ };
+}
+}
+#endif
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SValueUnionTraits,
+ SValueUnionTraits::TBufferSize>,
+ SValueUnionTraits::TBufferSize>
+ TEditorUnionType;
+
+ struct SValue : public TEditorUnionType
+ {
+ SValue() {}
+
+ SValue(const SValue &inOther)
+ : TEditorUnionType(static_cast<const TEditorUnionType &>(inOther))
+ {
+ }
+
+ SValue(const char8_t *inOther)
+ : TEditorUnionType(TEditorStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SValue(const TDataType &inDt)
+ : TEditorUnionType(inDt)
+ {
+ }
+
+ SValue &operator=(const SValue &inOther)
+ {
+ TEditorUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SValue &inOther) const
+ {
+ return TEditorUnionType::operator==(inOther);
+ }
+ bool operator!=(const SValue &inOther) const
+ {
+ return TEditorUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == ValueTypes::NoEditorValue; }
+ };
+
+ struct SValueOpt : public Option<SValue>
+ {
+ SValueOpt(const SValueOpt &inOther)
+ : Option<SValue>(inOther)
+ {
+ }
+ SValueOpt(const Empty &)
+ : Option<SValue>()
+ {
+ }
+ SValueOpt() {}
+ template <typename TDataType>
+ SValueOpt(const TDataType &inOther)
+ : Option<SValue>(SValue(inOther))
+ {
+ }
+ SValueOpt &operator=(const SValueOpt &inOther)
+ {
+ Option<SValue>::operator=(inOther);
+ return *this;
+ }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp
new file mode 100644
index 00000000..f21fb27b
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp
@@ -0,0 +1,2527 @@
+/****************************************************************************
+**
+** 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 "Qt3DSUIADatamodel.h"
+#include "foundation/IOStreams.h"
+#include "foundation/XML.h"
+#include "foundation/FileTools.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateApplication.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "EASTL/map.h"
+#include "EASTL/sort.h"
+#include "Qt3DSStateEditorTransactionImpl.h"
+#include "Qt3DSUIADatamodelValue.h"
+#include "foundation/StringConversion.h"
+#include "foundation/StringConversionImpl.h"
+#include "Qt3DSDMStringTable.h"
+
+using namespace qt3ds::app;
+using qt3ds::render::IInputStreamFactory;
+
+namespace qt3ds {
+namespace app {
+
+ struct ElementSubTypes
+ {
+ enum Enum {
+ NoSubType = 0,
+ Component = 1,
+ Behavior = 2,
+ };
+ };
+
+ struct SAppElement
+ {
+ TEditorStr m_Path;
+ TEditorStr m_Type;
+ TEditorStr m_Name;
+ TEditorStr m_Id;
+ Q3DStudio::TAttOrArgList m_Attributes;
+ Q3DStudio::TVisualEventList m_VisualEvents;
+ eastl::vector<NVScopedRefCounted<SAppElement>> m_Children;
+ eastl::vector<SDatamodelValue> m_InitialValues;
+
+ private:
+ QT3DSI32 m_RefCount;
+
+ public:
+ SAppElement()
+ : m_RefCount(0)
+ {
+ }
+
+ virtual ~SAppElement() {}
+
+ void addRef() { ++m_RefCount; }
+
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+
+ virtual ElementSubTypes::Enum GetSubType() { return ElementSubTypes::NoSubType; }
+ };
+}
+}
+
+namespace {
+
+typedef eastl::basic_string<char8_t> TStrType;
+struct IPropertyParser
+{
+ virtual ~IPropertyParser() {}
+ virtual Option<TStrType> ParseStr(const char8_t *inName) = 0;
+ virtual Option<QT3DSF32> ParseFloat(const char8_t *inName) = 0;
+ virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName) = 0;
+ virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName) = 0;
+ virtual Option<bool> ParseBool(const char8_t *inName) = 0;
+ virtual Option<QT3DSU32> ParseU32(const char8_t *inName) = 0;
+ virtual Option<QT3DSI32> ParseI32(const char8_t *inName) = 0;
+};
+
+struct SMetaPropertyParser : public IPropertyParser
+{
+ Q3DStudio::IRuntimeMetaData &m_MetaData;
+ IStringTable &m_StringTable;
+ TStrType m_TempStr;
+ qt3ds::foundation::CRegisteredString m_Type;
+ qt3ds::foundation::CRegisteredString m_ClassId;
+
+ SMetaPropertyParser(const char8_t *inType, const char8_t *inClass,
+ Q3DStudio::IRuntimeMetaData &inMeta, IStringTable &stringTable)
+ : m_MetaData(inMeta)
+ , m_StringTable(stringTable)
+ , m_Type(stringTable.RegisterStr(inType))
+ , m_ClassId(stringTable.RegisterStr(inClass))
+ {
+ }
+
+ qt3ds::foundation::CRegisteredString Register(const char8_t *inName)
+ {
+ return m_StringTable.RegisterStr(inName);
+ }
+
+ virtual Option<TStrType> ParseStr(const char8_t *inName)
+ {
+ qt3ds::foundation::CRegisteredString theName(Register(inName));
+ Q3DStudio::ERuntimeDataModelDataType theType(
+ m_MetaData.GetPropertyType(m_Type, theName, m_ClassId));
+ if (theType != Q3DStudio::ERuntimeDataModelDataTypeObjectRef
+ && theType != Q3DStudio::ERuntimeDataModelDataTypeLong4) {
+ return m_MetaData.GetPropertyValueString(m_Type, theName, m_ClassId);
+ }
+ return Empty();
+ }
+ virtual Option<QT3DSF32> ParseFloat(const char8_t *inName)
+ {
+ return m_MetaData.GetPropertyValueFloat(m_Type, Register(inName), m_ClassId);
+ }
+ virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName)
+ {
+ Option<qt3ds::QT3DSVec3> theProperty =
+ m_MetaData.GetPropertyValueVector2(m_Type, Register(inName), m_ClassId);
+ if (theProperty.hasValue()) {
+ return QT3DSVec2(theProperty->x, theProperty->y);
+ }
+ return Empty();
+ }
+ virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName)
+ {
+ Option<qt3ds::QT3DSVec3> theProperty =
+ m_MetaData.GetPropertyValueVector3(m_Type, Register(inName), m_ClassId);
+ if (theProperty.hasValue()) {
+ return *theProperty;
+ }
+ return Empty();
+ }
+ virtual Option<bool> ParseBool(const char8_t *inName)
+ {
+ return m_MetaData.GetPropertyValueBool(m_Type, Register(inName), m_ClassId);
+ }
+
+ virtual Option<QT3DSU32> ParseU32(const char8_t *inName)
+ {
+ Option<long> retval = m_MetaData.GetPropertyValueLong(m_Type, Register(inName), m_ClassId);
+ if (retval.hasValue())
+ return (QT3DSU32)retval.getValue();
+ return Empty();
+ }
+
+ virtual Option<QT3DSI32> ParseI32(const char8_t *inName)
+ {
+ Option<long> retval = m_MetaData.GetPropertyValueLong(m_Type, Register(inName), m_ClassId);
+ if (retval.hasValue())
+ return (QT3DSI32)retval.getValue();
+ return Empty();
+ }
+};
+
+struct SDomReaderPropertyParser : public IPropertyParser
+{
+ IDOMReader &m_Reader;
+ nvvector<char8_t> &m_TempBuf;
+ MemoryBuffer<ForwardingAllocator> &m_ReadBuffer;
+
+ SDomReaderPropertyParser(IDOMReader &reader, nvvector<char8_t> &inTempBuf,
+ MemoryBuffer<ForwardingAllocator> &readBuf)
+ : m_Reader(reader)
+ , m_TempBuf(inTempBuf)
+ , m_ReadBuffer(readBuf)
+ {
+ }
+ virtual Option<TStrType> ParseStr(const char8_t *inName)
+ {
+ const char8_t *retval;
+ if (m_Reader.UnregisteredAtt(inName, retval))
+ return TStrType(retval);
+ return Empty();
+ }
+ virtual Option<QT3DSF32> ParseFloat(const char8_t *inName)
+ {
+ QT3DSF32 retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+ virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName)
+ {
+ QT3DSVec2 retval;
+ const char8_t *tempVal;
+
+ if (m_Reader.UnregisteredAtt(inName, tempVal)) {
+ eastl::string tempBuffer(tempVal);
+ Char8TReader theReader(const_cast<char *>(tempBuffer.c_str()), m_ReadBuffer);
+ NVDataRef<QT3DSF32> theDataRef(toDataRef(&retval.x, 2));
+ theReader.ReadRef(theDataRef);
+ return retval;
+ }
+ return Empty();
+ }
+ virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName)
+ {
+ QT3DSVec3 retval;
+ const char8_t *tempVal;
+ if (m_Reader.UnregisteredAtt(inName, tempVal)) {
+ eastl::string tempBuffer(tempVal);
+ Char8TReader theReader(const_cast<char *>(tempBuffer.c_str()), m_ReadBuffer);
+ NVDataRef<QT3DSF32> theDataRef(toDataRef(&retval.x, 3));
+ theReader.ReadRef(theDataRef);
+ return retval;
+ }
+ return Empty();
+ }
+ virtual Option<bool> ParseBool(const char8_t *inName)
+ {
+ bool retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+
+ virtual Option<QT3DSU32> ParseU32(const char8_t *inName)
+ {
+ QT3DSU32 retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+
+ virtual Option<QT3DSI32> ParseI32(const char8_t *inName)
+ {
+ QT3DSI32 retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+};
+
+template <typename TDataType>
+struct SParserHelper
+{
+};
+template <>
+struct SParserHelper<TStrType>
+{
+ static Option<TStrType> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseStr(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSF32>
+{
+ static Option<QT3DSF32> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseFloat(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSVec2>
+{
+ static Option<QT3DSVec2> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseVec2(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSVec3>
+{
+ static Option<QT3DSVec3> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseVec3(inName);
+ }
+};
+template <>
+struct SParserHelper<bool>
+{
+ static Option<bool> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseBool(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSU32>
+{
+ static Option<QT3DSU32> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseU32(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSI32>
+{
+ static Option<QT3DSI32> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseI32(inName);
+ }
+};
+
+struct SSlideInfo
+{
+ TEditorStr m_Name;
+};
+
+struct SAppComponent : public SAppElement
+{
+ TEditorStrList m_Slides;
+ virtual ElementSubTypes::Enum GetSubType() { return ElementSubTypes::Component; }
+};
+
+struct SAppBehavior : public SAppElement
+{
+ Q3DStudio::THandlerList m_Handlers;
+ virtual ElementSubTypes::Enum GetSubType() { return ElementSubTypes::Behavior; }
+};
+
+struct SVSEditorObject;
+
+struct SVSEditor
+{
+ TFoundationPtr m_Foundation;
+ NVScopedRefCounted<STransactionManagerImpl> m_TransactionManager;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ SVSEditor(TFoundationPtr fnd, IStringTable &inStringTable)
+ : m_Foundation(fnd)
+ , m_TransactionManager(QT3DS_NEW(m_Foundation->getAllocator(), STransactionManagerImpl)(fnd))
+ , m_StringTable(inStringTable)
+ {
+ }
+
+ virtual void RemoveObjectFromGraph(SVSEditorObject &inObj) = 0;
+ STransaction *GetOpenTransactionImpl()
+ {
+ return m_TransactionManager->GetOpenTransactionImpl();
+ }
+};
+
+struct SVSEditorObject : public IEditorObject
+{
+ TFoundationPtr m_Foundation;
+ SVSEditor &m_Editor;
+ TObjPtr m_ParentObject;
+ TPropertyAccessorList m_PropertyAccessors;
+ QT3DSI32 mRefCount;
+ SVSEditorObject(SVSEditor &editor, TObjPtr parent, const char8_t *typeName,
+ const TPropertyAccessorList &ioList)
+ : IEditorObject(typeName)
+ , m_Foundation(editor.m_Foundation)
+ , m_Editor(editor)
+ , m_ParentObject(parent)
+ , m_PropertyAccessors(ioList)
+ , mRefCount(0)
+ {
+ }
+
+ virtual TEditorStr GetId() { return TEditorStr(); }
+ virtual TEditorStr GetDescription() { return TEditorStr(); }
+ virtual void SetId(const TEditorStr &) { QT3DS_ASSERT(false); }
+ virtual void SetDescription(const TEditorStr &) { QT3DS_ASSERT(false); }
+
+ virtual TObjPtr Parent() { return m_ParentObject; }
+
+ IPropertyAccessor *FindPropertyAccessor(const char8_t *inName)
+ {
+ CRegisteredString nameStr = m_Editor.m_StringTable->RegisterStr(inName);
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr)
+ return m_PropertyAccessors[idx].mPtr;
+ return NULL;
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ }
+
+ virtual Option<SPropertyDeclaration> FindProperty(const char8_t *propName)
+ {
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (AreEqual(m_PropertyAccessors[idx]->m_Declaration.m_Name.c_str(), propName))
+ return m_PropertyAccessors[idx]->m_Declaration;
+ }
+ return Empty();
+ }
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(const char8_t *)
+ {
+ return eastl::vector<CRegisteredString>();
+ }
+
+ virtual Option<SValue> GetPropertyValue(const char8_t *inPropName)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ return accessor->Get(*this);
+ }
+ return Empty();
+ }
+ virtual void SetPropertyValue(const char8_t *inPropName, const SValueOpt &inValue)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ STransaction *theTransaction = m_Editor.GetOpenTransactionImpl();
+ if (theTransaction) {
+ Option<SValue> existing = accessor->Get(*this);
+ theTransaction->m_Changes.push_back(
+ new SChange(existing, inValue, *accessor, *this));
+ }
+ accessor->Set(*this, inValue);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ virtual void RemoveObjectFromGraph() { m_Editor.RemoveObjectFromGraph(*this); }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ virtual IEditor &GetEditor() { return m_ParentObject->GetEditor(); }
+};
+
+template <typename TEditorType>
+struct SGenericPropertyStringAccessor : public SPropertyAccessorBase<TEditorType>
+{
+ typedef TEditorStr TDataType;
+ typedef TDataType TEditorType::*TPropertyPtr;
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ TPropertyPtr m_Property;
+
+ SGenericPropertyStringAccessor(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Property(inPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TDataType &dtype = inObj.*(this->m_Property);
+ return SValue(dtype);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TDataType &dtype = inObj.*(this->m_Property);
+ dtype.clear();
+ if (inValue.hasValue() && inValue->getType() == ValueTypes::String)
+ dtype = inValue->getData<TEditorStr>();
+ }
+};
+
+template <typename TEditorType>
+struct SGenericPropertyStringFunctionAccessor : public SPropertyAccessorBase<TEditorType>
+{
+ typedef TEditorStr TDataType;
+ typedef TDataType (TEditorType::*TGetter)() const;
+ typedef void (TEditorType::*TSetter)(const TDataType &);
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ TGetter m_Getter;
+ TSetter m_Setter;
+
+ SGenericPropertyStringFunctionAccessor(TFoundationPtr inAlloc,
+ const SPropertyDeclaration &inDec, TGetter inGetter,
+ TSetter inSetter)
+ : TBaseType(inAlloc, inDec)
+ , m_Getter(inGetter)
+ , m_Setter(inSetter)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TDataType dtype = (inObj.*m_Getter)();
+ return SValue(dtype);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TDataType dtype;
+ dtype.clear();
+ if (inValue.hasValue() && inValue->getType() == ValueTypes::String)
+ dtype = inValue->getData<TEditorStr>();
+
+ (inObj.*m_Setter)(dtype);
+ }
+};
+
+struct SGotoSlideEditor : public SVSEditorObject
+{
+ TEditorStr m_Component;
+ TEditorStr m_Slide;
+ TEditorStr m_Rel;
+ TEditorStr m_Wrap;
+ TEditorStr m_Direction;
+ TEditorStr m_State;
+ TEditorStr m_Mode;
+ TEditorStr m_PlayThroughTo;
+ TEditorStr m_Rate;
+ TEditorStr m_Time;
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SGotoSlideEditor> TStrProp;
+ typedef SGenericPropertyStringFunctionAccessor<SGotoSlideEditor> TStrFunProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Component));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrFunProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("slide"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::GetSlide, &SGotoSlideEditor::SetSlide));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrFunProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("rel"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::GetRel, &SGotoSlideEditor::SetRel));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("wrap"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Wrap));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("direction"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Direction));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("state"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_State));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("mode"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Mode));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("playthroughto"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_PlayThroughTo));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("rate"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Rate));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("time"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Time));
+ }
+
+ SGotoSlideEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+
+ static const char8_t *ElementName() { return "goto-slide"; }
+
+ void SetSlide(const TEditorStr &inSlide)
+ {
+ if (inSlide.empty() == false) {
+ if (m_Editor.GetOpenTransactionImpl())
+ SetPropertyValue("rel", SValueOpt());
+ else
+ m_Rel.clear();
+ }
+ m_Slide = inSlide;
+ }
+
+ TEditorStr GetSlide() const { return m_Slide; }
+
+ void SetRel(const TEditorStr &inRel)
+ {
+ if (inRel.empty() == false) {
+ if (m_Editor.GetOpenTransactionImpl())
+ SetPropertyValue("slide", SValueOpt());
+ else
+ m_Slide.clear();
+ }
+ m_Rel = inRel;
+ }
+
+ TEditorStr GetRel() const { return m_Rel; }
+};
+
+struct SRunHandlerEditor : public SVSEditorObject
+{
+ TEditorStr m_Behavior;
+ TEditorStr m_Handler;
+ TEditorStr m_ArgumentStr;
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SRunHandlerEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SRunHandlerEditor::m_Behavior));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("handler"),
+ EditorPropertyTypes::String),
+ &SRunHandlerEditor::m_Handler));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("arguments"),
+ EditorPropertyTypes::String),
+ &SRunHandlerEditor::m_ArgumentStr));
+ }
+ SRunHandlerEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "call"; }
+};
+
+struct SSetAttributeEditor : public SVSEditorObject
+{
+ TEditorStr m_Element;
+ TEditorStr m_Attribute;
+ TEditorStr m_Value;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SSetAttributeEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SSetAttributeEditor::m_Element));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("attribute"),
+ EditorPropertyTypes::String),
+ &SSetAttributeEditor::m_Attribute));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("value"),
+ EditorPropertyTypes::String),
+ &SSetAttributeEditor::m_Value));
+ }
+ SSetAttributeEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "set-attribute"; }
+};
+
+struct SFireEventEditor : public SVSEditorObject
+{
+ TEditorStr m_Element;
+ TEditorStr m_Event;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SFireEventEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SFireEventEditor::m_Element));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("event"),
+ EditorPropertyTypes::String),
+ &SFireEventEditor::m_Event));
+ }
+ SFireEventEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "fire-event"; }
+};
+
+struct SSetPresentationEditor : public SVSEditorObject
+{
+ TEditorStr m_Ref;
+ TEditorStr m_Attribute;
+ TEditorStr m_Value;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SSetPresentationEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("ref"),
+ EditorPropertyTypes::String),
+ &SSetPresentationEditor::m_Ref));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("attribute"),
+ EditorPropertyTypes::String),
+ &SSetPresentationEditor::m_Attribute));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("value"),
+ EditorPropertyTypes::String),
+ &SSetPresentationEditor::m_Value));
+ }
+ SSetPresentationEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "set-presentation"; }
+};
+
+struct SPlaySoundEditor : public SVSEditorObject
+{
+ TEditorStr m_FilePath;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SPlaySoundEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("file"),
+ EditorPropertyTypes::String),
+ &SPlaySoundEditor::m_FilePath));
+ }
+ SPlaySoundEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "play-sound"; }
+};
+
+struct SVSEntry
+{
+ QT3DSI32 mRefCount;
+ InterpreterEventTypes::Enum m_EventType;
+ TObjList m_Editors;
+
+ SVSEntry(InterpreterEventTypes::Enum evtType = InterpreterEventTypes::UnknownInterpreterEvent)
+ : mRefCount(0)
+ , m_EventType(evtType)
+ {
+ }
+ void addRef() { ++mRefCount; }
+ void release()
+ {
+ --mRefCount;
+ if (mRefCount <= 0)
+ delete this;
+ }
+};
+
+struct SVSEntryListChange : public IChange
+{
+ TObjPtr m_Object;
+ NVScopedRefCounted<SVSEntry> m_Entry;
+ QT3DSI32 m_Index;
+ bool m_AddOnDo;
+ TObjPtr m_EditorObj;
+ QT3DSI32 m_RefCount;
+
+ SVSEntryListChange(TObjPtr inObj, TObjPtr inPositionObj, SVSEntry &entry, bool addOnDo,
+ TObjPtr inEditorObj)
+ : m_Object(inObj)
+ , m_Entry(entry)
+ , m_AddOnDo(addOnDo)
+ , m_EditorObj(inEditorObj)
+ , m_RefCount(0)
+ {
+ TObjList::iterator iter =
+ eastl::find(entry.m_Editors.begin(), entry.m_Editors.end(), inPositionObj);
+ m_Index = iter - entry.m_Editors.begin();
+ }
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ void add() { m_Entry->m_Editors.insert(m_Entry->m_Editors.begin() + m_Index, m_Object); }
+ void remove()
+ {
+ TObjList::iterator iter =
+ eastl::find(m_Entry->m_Editors.begin(), m_Entry->m_Editors.end(), m_Object);
+ if (iter != m_Entry->m_Editors.end())
+ m_Entry->m_Editors.erase(iter);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ virtual void Do()
+ {
+ if (m_AddOnDo)
+ add();
+ else
+ remove();
+ }
+ virtual void Undo()
+ {
+ if (m_AddOnDo)
+ remove();
+ else
+ add();
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObj; }
+};
+
+typedef eastl::pair<NVScopedRefCounted<SVSEntry>, NVScopedRefCounted<SVSEntry>> TEntryExitPair;
+typedef eastl::map<TEditorStr, TEntryExitPair> TIdEntryExitMap;
+typedef eastl::map<TEditorStr, TIdEntryExitMap> TIdStateMapMap;
+
+struct SIdEntryExitDeleteChange : public IChange
+{
+ TIdEntryExitMap &m_Map;
+ TEditorStr m_Id;
+ TEntryExitPair m_Data;
+ QT3DSI32 m_RefCount;
+ TObjPtr m_EditorObject;
+ bool m_RemoveOnDo;
+ SIdEntryExitDeleteChange(TIdEntryExitMap &inMap, const TEditorStr &inId, TObjPtr inObj,
+ bool removeOnDo = true)
+ : m_Map(inMap)
+ , m_Id(inId)
+ , m_RefCount(0)
+ , m_EditorObject(inObj)
+ , m_RemoveOnDo(removeOnDo)
+ {
+ m_Data = m_Map[m_Id];
+ }
+ virtual void addRef() { ++m_RefCount; }
+ virtual void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ void remove()
+ {
+ TIdEntryExitMap::iterator iter = m_Map.find(m_Id);
+ if (iter != m_Map.end())
+ m_Map.erase(iter);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ void insert() { m_Map[m_Id] = m_Data; }
+ virtual void Do()
+ {
+ if (m_RemoveOnDo)
+ remove();
+ else
+ insert();
+ }
+ virtual void Undo()
+ {
+ if (m_RemoveOnDo)
+ insert();
+ else
+ remove();
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObject; }
+};
+
+struct SEditorEntry
+{
+ TEditorPtr m_Editor;
+ TEditorStr m_Id;
+ TEditorStr m_FilePath;
+};
+
+template <typename TDataType>
+SDatamodelValue ParseInitialProperty(const char8_t *inName, IPropertyParser &metaParser,
+ IPropertyParser &runtimeParser)
+{
+ Option<TDataType> val = SParserHelper<TDataType>::Parse(inName, runtimeParser);
+ if (val.isEmpty())
+ val = SParserHelper<TDataType>::Parse(inName, metaParser);
+ if (val.hasValue())
+ return SDatamodelValue(*val);
+ return SDatamodelValue();
+}
+
+template <typename TDataType>
+SDatamodelValue ParseSlideProperty(const char8_t *inName, IPropertyParser &runtimeParser)
+{
+ Option<TDataType> val = SParserHelper<TDataType>::Parse(inName, runtimeParser);
+ if (val.hasValue())
+ return *val;
+ return SDatamodelValue();
+}
+
+struct DatamodelImpl : public IDatamodel,
+ public ITransManagerImplListener,
+ public SVSEditor,
+ public IEditorCopyPasteListener
+{
+ TEditorStr m_FilePath;
+ QT3DSI32 mRefCount;
+ NVScopedRefCounted<IInputStreamFactory> m_InputStreamFactory;
+ eastl::pair<NVScopedRefCounted<IDOMWriter>, NVScopedRefCounted<IDOMReader>> m_UIADocument;
+ nvvector<SNamespacePair> m_UIANamespaces;
+ bool m_Dirty;
+ nvvector<NVScopedRefCounted<SAppElement>> m_Elements;
+ NVScopedRefCounted<SAppElement> m_FinderElement;
+ nvvector<SEditorEntry> m_Editors;
+ TIdStateMapMap m_IdToStateMaps;
+ eastl::vector<SPresentation> m_Presentations;
+ nvvector<char8_t> m_ParseBuf;
+ Q3DStudio::TAttOrArgList m_SlideProperties;
+ eastl::string m_LoadingErrorString;
+ IStateMachineEditorManager &m_StateMachineEditorManager;
+
+ DatamodelImpl(TFoundationPtr inFoundation, const TEditorStr &inPath, const TEditorStr &inAppDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IStringTable &inStringTable)
+ : SVSEditor(inFoundation, inStringTable)
+ , m_FilePath(inPath)
+ , mRefCount(0)
+ , m_InputStreamFactory(IInputStreamFactory::Create(m_Foundation->getFoundation()))
+ , m_UIANamespaces(m_Foundation->getAllocator(), "m_UIANamespaces")
+ , m_Dirty(false)
+ , m_Elements(m_Foundation->getAllocator(), "m_Elements")
+ , m_Editors(m_Foundation->getAllocator(), "m_Editors")
+ , m_ParseBuf(inFoundation->getAllocator(), "tempbuf")
+ , m_StateMachineEditorManager(inStateMachineEditorManager)
+ {
+ TEditorStr ext;
+ CFileTools::GetExtension(m_FilePath.c_str(), ext);
+ if (ext.comparei("uia") != 0) {
+ m_FilePath = IApplication::GetLaunchFile(inPath.c_str());
+ }
+ // Check extension, if it isn't what we expect then just set it to uia
+ ext.clear();
+ CFileTools::GetExtension(m_FilePath.c_str(), ext);
+ if (ext.comparei("uia") != 0)
+ CFileTools::SetExtension(m_FilePath, "uia");
+
+ m_InputStreamFactory->AddSearchDirectory(inAppDir.c_str());
+
+ m_TransactionManager->m_ObjListener = this;
+ }
+
+ ~DatamodelImpl() {}
+
+ void addRef() { atomicIncrement(&mRefCount); }
+
+ void release()
+ {
+ TFoundationPtr tempFoundation(m_Foundation);
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ NVDelete(m_Foundation->getAllocator(), this);
+ }
+ }
+ typedef eastl::map<TEditorStr, Q3DStudio::THandlerList> TIdHandlerMap;
+ typedef eastl::map<TEditorStr, Q3DStudio::TVisualEventList> TIdVisualEventMap;
+ typedef eastl::map<TEditorStr, NVScopedRefCounted<SAppElement>> TIdElemMap;
+
+ SAppElement &ParseElement(IDOMReader &inReader, Q3DStudio::IRuntimeMetaData &inMetaData,
+ eastl::vector<NVScopedRefCounted<SAppElement>> &ioChildList,
+ TIdElemMap &ioElemMap)
+ {
+ IDOMReader::Scope __elemScope(inReader);
+ const char8_t *className = inReader.GetElementName();
+ const char8_t *classRef;
+ inReader.UnregisteredAtt("class", classRef);
+ const char8_t *id;
+ inReader.UnregisteredAtt("id", id);
+ Q3DStudio::SElementInfo elemInfo = inMetaData.LoadElement(className, classRef, id);
+ NVScopedRefCounted<SAppElement> newElem;
+ if (elemInfo.m_ClassName.compare("Behavior") == 0) {
+ SAppBehavior *behavior = new SAppBehavior();
+ behavior->m_Handlers = inMetaData.GetCustomHandlers(id);
+ newElem = behavior;
+ } else if (elemInfo.m_IsComponent) {
+ SAppComponent *component = new SAppComponent();
+ newElem = component;
+ } else {
+ newElem = new SAppElement();
+ }
+ newElem->m_Attributes = elemInfo.m_Attributes;
+ newElem->m_VisualEvents = inMetaData.GetVisualEvents(id);
+ newElem->m_Type = elemInfo.m_ClassName;
+ newElem->m_Id = id;
+ const char8_t *elemName;
+ if (inReader.UnregisteredAtt("name", elemName))
+ newElem->m_Name.assign(elemName);
+ else {
+ Q3DStudio::TRuntimeMetaDataStrType str = inMetaData.GetPropertyValueString(
+ inMetaData.Register(className), inMetaData.Register("name"),
+ inMetaData.Register(id));
+ newElem->m_Name = str;
+ }
+
+ ioChildList.push_back(newElem);
+ m_Elements.push_back(newElem);
+ ioElemMap.insert(eastl::make_pair(TEditorStr(id), newElem));
+ {
+ SMetaPropertyParser theMetaParser(newElem->m_Type.c_str(), classRef, inMetaData,
+ inMetaData.GetStringTable()->GetRenderStringTable());
+ SDomReaderPropertyParser theDOMParser(inReader, m_ParseBuf, inReader.m_TempBuf);
+ for (size_t idx = 0, end = newElem->m_Attributes.size(); idx < end; ++idx) {
+ const SAttOrArg &theAtt(newElem->m_Attributes[idx]);
+ SDatamodelValue newValue;
+ switch (theAtt.m_DataType) {
+ case ERuntimeDataModelDataTypeFloat:
+ newValue = ParseInitialProperty<float>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat2:
+ newValue = ParseInitialProperty<QT3DSVec2>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat3:
+ newValue = ParseInitialProperty<QT3DSVec3>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeLong:
+ newValue = ParseInitialProperty<QT3DSI32>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeString:
+ newValue = ParseInitialProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theMetaParser, theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeBool:
+ newValue = ParseInitialProperty<bool>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeStringRef:
+ newValue = ParseInitialProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theMetaParser, theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeObjectRef:
+ newValue =
+ ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ default:
+ break;
+ }
+ newElem->m_InitialValues.push_back(newValue);
+ }
+ }
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling())
+ ParseElement(inReader, inMetaData, newElem->m_Children, ioElemMap);
+
+ return *newElem;
+ }
+
+ // Get the item names if not overridden
+ // Pull the names off so we can construct the item path.
+ void LoadSlide(IDOMReader &inReader, TIdElemMap &inMap)
+ {
+ IDOMReader::Scope __topScope(inReader);
+ for (bool commandSuccess = inReader.MoveToFirstChild(); commandSuccess;
+ commandSuccess = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __commandScope(inReader);
+ const char8_t *elemName = inReader.GetElementName();
+ if (AreEqual(elemName, "Set") || AreEqual(elemName, "Add")) {
+ const char8_t *name;
+ TIdElemMap::iterator iter = inMap.end();
+ const char8_t *itemRef;
+ inReader.UnregisteredAtt("ref", itemRef);
+ if (!isTrivial(itemRef)) {
+ if (itemRef[0] == '#')
+ ++itemRef;
+ iter = inMap.find(TEditorStr(itemRef));
+ }
+
+ if (inReader.UnregisteredAtt("name", name)) {
+ if (iter != inMap.end()) {
+ iter->second->m_Name.assign(name);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ if (iter != inMap.end() && AreEqual(elemName, "Add")) {
+ nvvector<char8_t> theTempBuf(this->m_Foundation->getAllocator(), "tempbuf");
+ SDomReaderPropertyParser theDOMParser(inReader, m_ParseBuf, inReader.m_TempBuf);
+ SAppElement *newElem = iter->second.mPtr;
+ for (size_t idx = 0, end = newElem->m_Attributes.size(); idx < end; ++idx) {
+ const SAttOrArg &theAtt(newElem->m_Attributes[idx]);
+ SDatamodelValue newValue;
+ switch (theAtt.m_DataType) {
+ case ERuntimeDataModelDataTypeFloat:
+ newValue =
+ ParseSlideProperty<float>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat2:
+ newValue =
+ ParseSlideProperty<QT3DSVec2>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat3:
+ newValue =
+ ParseSlideProperty<QT3DSVec3>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeLong:
+ newValue =
+ ParseSlideProperty<QT3DSI32>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeString:
+ newValue = ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeBool:
+ newValue =
+ ParseSlideProperty<bool>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeStringRef:
+ newValue = ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeObjectRef:
+ newValue = ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theDOMParser);
+ break;
+ default:
+ break;
+ }
+ if (newValue.getType() != ERuntimeDataModelDataTypeNone)
+ newElem->m_InitialValues[idx] = newValue;
+ }
+ }
+ }
+ }
+ }
+
+ void ResolveElemPath(SAppElement &inElem, const eastl::string &parentId)
+ {
+ inElem.m_Path = parentId;
+ if (inElem.m_Path.back() != ':')
+ inElem.m_Path.append(1, '.');
+ inElem.m_Path.append(inElem.m_Name);
+ for (size_t idx = 0, end = inElem.m_Children.size(); idx < end; ++idx)
+ ResolveElemPath(*inElem.m_Children[idx], inElem.m_Path);
+ }
+
+ void LoadUIPFile(const char *inRelativePath, const char *inUIPId,
+ Q3DStudio::IRuntimeMetaData &inMetaData)
+ {
+ eastl::string appDir(m_FilePath);
+ CFileTools::GetDirectory(appDir);
+ eastl::string uipPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), inRelativePath, uipPath);
+ CFileSeekableIOStream uipStream(uipPath.c_str(), FileReadFlags());
+ if (!uipStream.IsOpen()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_Foundation->getAllocator(), m_StringTable));
+ SDOMElement *domElem = CDOMSerializer::Read(*theFactory, uipStream).second;
+ if (!domElem) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ NVScopedRefCounted<IDOMReader> theReader(IDOMReader::CreateDOMReader(
+ m_Foundation->getAllocator(), *domElem, m_StringTable, theFactory));
+ if (!theReader->MoveToFirstChild("Project")) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ Q3DStudio::IRuntimeMetaData &theMetaData(inMetaData);
+ theMetaData.ClearPerProjectData();
+
+ TEditorStr rootPath(inUIPId);
+ rootPath.append(1, ':');
+ eastl::vector<NVScopedRefCounted<SAppElement>> topElements;
+ TIdElemMap idElemMap;
+ m_Presentations.push_back(SPresentation());
+ SPresentation &currentPresentation(m_Presentations.back());
+ currentPresentation.m_Id.assign(inUIPId);
+ currentPresentation.m_SrcPath.assign(inRelativePath);
+ {
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("ProjectSettings")) {
+ const char8_t *temp;
+ theReader->UnregisteredAtt("author", temp);
+ currentPresentation.m_Author.assign(nonNull(temp));
+ theReader->UnregisteredAtt("company", temp);
+ currentPresentation.m_Company.assign(nonNull(temp));
+ theReader->Att("presentationWidth", currentPresentation.m_Width);
+ theReader->Att("presentationHeight", currentPresentation.m_Height);
+ }
+ // Presentation width and height as specified in the file.
+ }
+ // Classes/handlers
+ {
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("Classes")) {
+ // Load each class system into the meta data and then pull back the handlers
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ const char8_t *id, *srcPath, *name;
+ theReader->UnregisteredAtt("id", id);
+ theReader->UnregisteredAtt("sourcepath", srcPath);
+ theReader->UnregisteredAtt("name", name);
+ eastl::string classItemPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), srcPath, classItemPath);
+
+ if (AreEqual(theReader->GetElementName().c_str(), "Behavior")) {
+ bool theScriptFile =
+ theMetaData.LoadScriptFile("Behavior", id, name, classItemPath.c_str());
+ QT3DS_ASSERT(theScriptFile);
+ (void)theScriptFile;
+ } else if (AreEqual(theReader->GetElementName().c_str(), "Effect")) {
+ bool theEffectFile = theMetaData.LoadEffectXMLFile("Effect", id, name,
+ classItemPath.c_str());
+ QT3DS_ASSERT(theEffectFile);
+ (void)theEffectFile;
+ } else if (AreEqual(theReader->GetElementName().c_str(), "RenderPlugin")) {
+ theMetaData.LoadPluginXMLFile("RenderPlugin", id, name,
+ classItemPath.c_str());
+ } else if (AreEqual(theReader->GetElementName().c_str(), "CustomMaterial")) {
+ theMetaData.LoadMaterialXMLFile("CustomMaterial", id, name,
+ classItemPath.c_str());
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+
+ // Graph
+ {
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("Graph")) {
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ currentPresentation.m_Scene =
+ &ParseElement(*theReader, theMetaData, topElements, idElemMap);
+ }
+ }
+ }
+ // States/Slides
+ {
+ // This is where the name *may* be set,
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("Logic")) {
+ // Slides are just slightly hierarchical, master and then nonmaster children.
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ IDOMReader::Scope __masterScope(*theReader);
+ LoadSlide(*theReader, idElemMap);
+ const char8_t *component;
+ theReader->UnregisteredAtt("component", component);
+ NVScopedRefCounted<SAppComponent> theComponent;
+ if (!isTrivial(component)) {
+ if (component[0] == '#')
+ ++component;
+ TIdElemMap::iterator iter = idElemMap.find(TEditorStr(component));
+ if (iter != idElemMap.end()) {
+ if (iter->second->GetSubType() == ElementSubTypes::Component)
+ theComponent = static_cast<SAppComponent *>(iter->second.mPtr);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ for (bool childSuccess = theReader->MoveToFirstChild("State"); childSuccess;
+ childSuccess = theReader->MoveToNextSibling("State")) {
+ IDOMReader::Scope __slideScope(*theReader);
+ const char8_t *slideName;
+ if (theReader->UnregisteredAtt("name", slideName))
+ theComponent->m_Slides.push_back(TEditorStr(slideName));
+ LoadSlide(*theReader, idElemMap);
+ }
+ }
+ }
+ }
+ // Now resolve all the names to create full paths.
+ for (size_t idx = 0, end = topElements.size(); idx < end; ++idx) {
+ ResolveElemPath(*topElements[idx], rootPath);
+ }
+ }
+
+ struct SElemLessThan
+ {
+ bool operator()(NVScopedRefCounted<SAppElement> lhs,
+ NVScopedRefCounted<SAppElement> rhs) const
+ {
+ return lhs->m_Path < rhs->m_Path;
+ }
+ };
+
+ static const char *GetNamespace() { return "http://qt.io/qt3dstudio/uia"; }
+ static const char *GetOldNamespace() { return "http://qt.io/qt3dstudio/uicomposer"; }
+
+ void LoadUIADatabase()
+ {
+ CFileSeekableIOStream theStream(m_FilePath.c_str(), FileReadFlags());
+ if (theStream.IsOpen()) {
+ LoadUIADatabaseFromStream(theStream);
+ }
+ }
+
+ struct SXmlErrorHandler : public CXmlErrorHandler
+ {
+ SXmlErrorHandler(const eastl::string &inFilePath, eastl::string &inErrorString)
+ : m_FilePath(inFilePath)
+ , m_ErrorString(inErrorString)
+ {
+ }
+ virtual ~SXmlErrorHandler() {}
+ virtual void OnXmlError(TXMLCharPtr errorName, int line, int column)
+ {
+ if (m_ErrorString.empty())
+ m_ErrorString.sprintf("%s(%d, %d): %s", m_FilePath.c_str(), line, column,
+ errorName);
+ }
+ eastl::string m_FilePath;
+ eastl::string &m_ErrorString;
+ };
+
+ void LoadUIADatabaseFromStream(IInStream &inStream)
+ {
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_Foundation->getAllocator(), m_StringTable));
+ m_UIADocument.first = NULL;
+ m_UIADocument.second = NULL;
+ m_UIANamespaces.clear();
+ m_Elements.clear();
+ m_IdToStateMaps.clear();
+ m_Editors.clear();
+ m_Presentations.clear();
+ m_SlideProperties.clear();
+ m_LoadingErrorString.clear();
+ eastl::string appDir(m_FilePath);
+ CFileTools::GetDirectory(appDir);
+
+ SXmlErrorHandler theXmlErrorWriter(m_FilePath, m_LoadingErrorString);
+ eastl::pair<SNamespacePairNode *, SDOMElement *> parseResult =
+ CDOMSerializer::Read(*theFactory, inStream, &theXmlErrorWriter);
+ if (parseResult.second != NULL) {
+ qt3ds::foundation::CRegisteredString theRegisteredOldNamespace =
+ m_StringTable->RegisterStr(GetOldNamespace());
+ qt3ds::foundation::CRegisteredString theRegisteredNamespace =
+ m_StringTable->RegisterStr(GetNamespace());
+
+ ReplaceDOMNamespace(*parseResult.second, theRegisteredOldNamespace,
+ theRegisteredNamespace);
+
+ m_UIADocument =
+ IDOMWriter::CreateDOMWriter(theFactory, *parseResult.second, m_StringTable);
+ for (SNamespacePairNode *nodePtr = parseResult.first; nodePtr;
+ nodePtr = nodePtr->m_NextNode) {
+ if (nodePtr->m_Namespace == theRegisteredOldNamespace)
+ nodePtr->m_Namespace = theRegisteredNamespace;
+ m_UIANamespaces.push_back(*nodePtr);
+ }
+ }
+
+ if (m_UIADocument.first.mPtr == NULL) {
+ m_UIADocument = IDOMWriter::CreateDOMWriter(m_Foundation->getAllocator(), "application",
+ m_StringTable, GetNamespace());
+ m_Dirty = true;
+ }
+ if (m_UIANamespaces.empty())
+ m_UIANamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr(GetNamespace())));
+
+ {
+ NVScopedReleasable<Q3DStudio::IRuntimeMetaData> theMetaData(
+ Q3DStudio::IRuntimeMetaData::Create(*m_InputStreamFactory));
+
+ if (theMetaData) {
+ m_SlideProperties = theMetaData->GetSlideAttributes();
+
+ eastl::vector<eastl::pair<TEditorStr, TEditorStr>> machinePathsAndIds;
+ IDOMReader::Scope __appScope(m_UIADocument.second);
+
+ if (m_UIADocument.second->MoveToFirstChild("assets")) {
+ IDOMReader::Scope __assetsScope(m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild(); success;
+ success = m_UIADocument.second->MoveToNextSibling()) {
+ IDOMReader::Scope __assetScope(m_UIADocument.second);
+ const char8_t *elemName = m_UIADocument.second->GetElementName();
+ if (AreEqual(elemName, "presentation")) {
+ const char8_t *id, *relativePath;
+ m_UIADocument.second->UnregisteredAtt("id", id);
+ m_UIADocument.second->UnregisteredAtt("src", relativePath);
+ LoadUIPFile(relativePath, id, *theMetaData);
+ } else if (AreEqual(elemName, "statemachine")) {
+ const char8_t *id, *relativePath;
+ m_UIADocument.second->UnregisteredAtt("id", id);
+ m_UIADocument.second->UnregisteredAtt("src", relativePath);
+ TEditorStr fullPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), relativePath,
+ fullPath);
+ CreateSCXMLEditor(fullPath, nonNull(id));
+ }
+ }
+ }
+
+ // Now sort our list of elements by path after we have loaded all uip files.
+ eastl::sort(m_Elements.begin(), m_Elements.end(), SElemLessThan());
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ {
+ IDOMReader::Scope __appScope(m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild("statemachine"); success;
+ success = m_UIADocument.second->MoveToNextSibling("statemachine")) {
+ IDOMReader::Scope __machineScope(m_UIADocument.second);
+ const char8_t *idref;
+ m_UIADocument.second->UnregisteredAtt("ref", idref);
+ if (!isTrivial(idref)) {
+ if (idref[0] == '#')
+ ++idref;
+ for (size_t idx = 0, end = m_Editors.size(); idx < end; ++idx) {
+ if (m_Editors[idx].m_Id.compare(idref) == 0)
+ ParseStateMachine(m_Editors[idx].m_Id, m_Editors[idx].m_Editor);
+ }
+ }
+ }
+ }
+ }
+
+ virtual bool IsDirty() const { return m_Dirty; }
+ // General queries of the dataset defined by the uia file and all uip files and scxml files it
+ // includes.
+
+ SAppElement *FindElementByPath(const TEditorStr &inPath)
+ {
+ if (!m_FinderElement)
+ m_FinderElement = new SAppElement();
+
+ m_FinderElement->m_Path = inPath;
+
+ nvvector<NVScopedRefCounted<SAppElement>>::iterator iter = eastl::lower_bound(
+ m_Elements.begin(), m_Elements.end(), m_FinderElement, SElemLessThan());
+ if (iter != m_Elements.end() && (*iter)->m_Path == m_FinderElement->m_Path) {
+ return iter->mPtr;
+ }
+ return NVScopedRefCounted<SAppElement>();
+ }
+
+ virtual TEditorStrList GetComponents()
+ {
+ TEditorStrList retval;
+ for (size_t idx = 0, end = m_Elements.size(); idx < end; ++idx) {
+ if (m_Elements[idx]->GetSubType() == ElementSubTypes::Component)
+ retval.push_back(m_Elements[idx]->m_Path);
+ }
+ return retval;
+ }
+
+ virtual TEditorStrList GetComponentSlides(const TEditorStr &inComponent)
+ {
+ SAppElement *elem = FindElementByPath(inComponent);
+ if (elem != NULL && elem->GetSubType() == ElementSubTypes::Component)
+ return static_cast<SAppComponent *>(elem)->m_Slides;
+ return TEditorStrList();
+ }
+
+ virtual TEditorStrList GetBehaviors()
+ {
+ TEditorStrList retval;
+ for (size_t idx = 0, end = m_Elements.size(); idx < end; ++idx) {
+ if (m_Elements[idx]->GetSubType() == ElementSubTypes::Behavior)
+ retval.push_back(m_Elements[idx]->m_Path);
+ }
+ return retval;
+ }
+
+ virtual Q3DStudio::THandlerList GetHandlers(const TEditorStr &inBehavior)
+ {
+ SAppElement *elem = FindElementByPath(inBehavior);
+ if (elem != NULL && elem->GetSubType() == ElementSubTypes::Behavior)
+ return static_cast<SAppBehavior *>(elem)->m_Handlers;
+ return Q3DStudio::THandlerList();
+ }
+
+ virtual Q3DStudio::TVisualEventList GetVisualEvents(const TEditorStr &inElement)
+ {
+ SAppElement *elem = FindElementByPath(inElement);
+ if (elem)
+ return elem->m_VisualEvents;
+ return Q3DStudio::TVisualEventList();
+ }
+
+ virtual TEditorStrList GetElements()
+ {
+ TEditorStrList retval;
+ for (size_t idx = 0, end = m_Elements.size(); idx < end; ++idx)
+ retval.push_back(m_Elements[idx]->m_Path);
+ return retval;
+ }
+
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(const TEditorStr &inElement)
+ {
+ SAppElement *elem = FindElementByPath(inElement);
+ if (elem)
+ return elem->m_Attributes;
+ return Q3DStudio::TAttOrArgList();
+ }
+
+ TEditorPtr CreateSCXMLEditor(const TEditorStr &inPath, const TEditorStr &inId)
+ {
+ TEditorPtr newEditor = m_StateMachineEditorManager.GetOrCreateEditor(inPath, 0);
+ if (newEditor) {
+ SEditorEntry theEntry;
+ theEntry.m_Editor = newEditor.mPtr;
+ theEntry.m_FilePath = inPath;
+ theEntry.m_Id = inId;
+ m_Editors.push_back(theEntry);
+ }
+ return newEditor.mPtr;
+ }
+
+ virtual TEditorPtr GetOrCreateEditor(const TEditorStr &inFullPath, bool *outLoadStatus)
+ {
+ (void)outLoadStatus;
+ TEditorStr normalizedPath(inFullPath);
+ CFileTools::NormalizePath(normalizedPath);
+ for (size_t idx = 0, end = m_Editors.size(); idx < end; ++idx) {
+ if (m_Editors[idx].m_FilePath == normalizedPath)
+ return m_Editors[idx].m_Editor;
+ }
+
+ // Is file full path under our application directory;
+ TEditorStr appDir(m_FilePath);
+ CFileTools::GetDirectory(appDir);
+ TEditorStr editorDir(inFullPath);
+ CFileTools::GetDirectory(editorDir);
+
+ // For scxml files outside our app dir, let the user create an editor manually.
+ // We will have nothing to do with it.
+ if (editorDir.find(appDir) == eastl::string::npos)
+ return TEditorPtr();
+
+ // Get an ID for the editor
+ IDOMReader::Scope __appScope(*m_UIADocument.second);
+ if (!m_UIADocument.second->MoveToFirstChild("assets")) {
+ m_UIADocument.first->Begin("assets", GetNamespace());
+ }
+
+ {
+ IDOMReader::Scope __assetsScope(*m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild("statemachine"); success;
+ success = m_UIADocument.second->MoveToNextSibling("statemachine")) {
+ const char8_t *srcPath;
+ const char8_t *id;
+ m_UIADocument.second->UnregisteredAtt("src", srcPath);
+ m_UIADocument.second->UnregisteredAtt("id", id);
+ TEditorStr docPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), srcPath, docPath);
+ CFileTools::NormalizePath(docPath);
+ if (docPath == normalizedPath) {
+ return CreateSCXMLEditor(normalizedPath, nonNull(id));
+ }
+ }
+ }
+
+ eastl::string dirname, fname, extension;
+ CFileTools::Split(normalizedPath.c_str(), dirname, fname, extension);
+ m_UIADocument.first->Begin("statemachine", GetNamespace());
+ m_UIADocument.first->Att("id", fname.c_str(), GetNamespace());
+ eastl::string relativePath;
+ CFileTools::GetRelativeFromBase(appDir, normalizedPath, relativePath);
+ m_UIADocument.first->Att("src", relativePath.c_str());
+ m_Dirty = true;
+ return CreateSCXMLEditor(normalizedPath, nonNull(fname.c_str()));
+ }
+
+ static bool CouldHaveVisualStateExecutableContent(TObjPtr inObject)
+ {
+ return inObject->GetExecutableContentTypes().size() > 0;
+ }
+ template <typename TEditorType>
+ TEditorType &CreateEditor(TObjPtr inParent)
+ {
+ TPropertyAccessorList accessors;
+ TEditorType::CreateProperties(accessors, *this);
+ return *QT3DS_NEW(m_Foundation->getAllocator(), TEditorType)(*this, inParent, accessors);
+ }
+
+ SVSEntry *GetEntryForPair(TEntryExitPair &inPair, InterpreterEventTypes::Enum inType)
+ {
+ SVSEntry *theEntry;
+ if (inType == InterpreterEventTypes::StateEnter
+ || inType == InterpreterEventTypes::Transition) {
+ if (inPair.first == NULL)
+ inPair.first = new SVSEntry(inType);
+ theEntry = inPair.first.mPtr;
+ } else {
+ if (inPair.second == NULL)
+ inPair.second = new SVSEntry(inType);
+ theEntry = inPair.second.mPtr;
+ }
+ return theEntry;
+ }
+
+ void ParseExecutableContent(IDOMReader &inReader, const TEditorStr &inId,
+ TIdEntryExitMap &ioMap, InterpreterEventTypes::Enum inType,
+ TObjPtr parentPtr)
+ {
+ IDOMReader::Scope __contentScope(inReader);
+ {
+ IDOMReader::Scope __testScope(inReader);
+ // See if there is any executable content to begin with.
+ if (inReader.MoveToFirstChild() == false)
+ return;
+ }
+
+ TEntryExitPair &thePair =
+ ioMap.insert(eastl::make_pair(inId, TEntryExitPair())).first->second;
+ SVSEntry *theEntry = GetEntryForPair(thePair, inType);
+ QT3DS_ASSERT(theEntry->m_EventType == inType);
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __elemScope(inReader);
+ const char8_t *elemName(inReader.GetElementName());
+ if (AreEqual(elemName, SGotoSlideEditor::ElementName())) {
+ const char8_t *component, *slide, *rel, *wrap, *direction, *state, *mode,
+ *playthroughto, *rate, *time;
+ inReader.UnregisteredAtt("element", component);
+ if (inReader.UnregisteredAtt("slide", slide) == false)
+ inReader.UnregisteredAtt("rel", rel);
+ else
+ rel = "";
+ inReader.UnregisteredAtt("wrap", wrap);
+ inReader.UnregisteredAtt("direction", direction);
+ inReader.UnregisteredAtt("state", state);
+ inReader.UnregisteredAtt("mode", mode);
+ inReader.UnregisteredAtt("playthroughto", playthroughto);
+ inReader.UnregisteredAtt("rate", rate);
+ inReader.UnregisteredAtt("time", time);
+ SGotoSlideEditor &theEditor = CreateEditor<SGotoSlideEditor>(parentPtr);
+ theEditor.m_Component.assign(nonNull(component));
+ theEditor.m_Slide.assign(nonNull(slide));
+ theEditor.m_Rel.assign(nonNull(rel));
+ theEditor.m_Wrap.assign(nonNull(wrap));
+ theEditor.m_Direction.assign(nonNull(direction));
+ theEditor.m_State.assign(nonNull(state));
+ theEditor.m_Mode.assign(nonNull(mode));
+ theEditor.m_PlayThroughTo.assign(nonNull(playthroughto));
+ theEditor.m_Rate.assign(nonNull(rate));
+ theEditor.m_Time.assign(nonNull(time));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SSetAttributeEditor::ElementName())) {
+ const char8_t *element, *att, *val;
+ inReader.UnregisteredAtt("element", element);
+ inReader.UnregisteredAtt("attribute", att);
+ inReader.UnregisteredAtt("value", val);
+ SSetAttributeEditor &theEditor = CreateEditor<SSetAttributeEditor>(parentPtr);
+ theEditor.m_Element.assign(nonNull(element));
+ theEditor.m_Attribute.assign(nonNull(att));
+ theEditor.m_Value.assign(nonNull(val));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SFireEventEditor::ElementName())) {
+ const char8_t *element, *evt;
+ inReader.UnregisteredAtt("element", element);
+ inReader.UnregisteredAtt("event", evt);
+ SFireEventEditor &theEditor = CreateEditor<SFireEventEditor>(parentPtr);
+ theEditor.m_Element.assign(nonNull(element));
+ theEditor.m_Event.assign(nonNull(evt));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SRunHandlerEditor::ElementName())) {
+ const char8_t *element, *handler, *args;
+ inReader.UnregisteredAtt("element", element);
+ inReader.UnregisteredAtt("handler", handler);
+ inReader.UnregisteredAtt("arguments", args);
+ SRunHandlerEditor &theEditor = CreateEditor<SRunHandlerEditor>(parentPtr);
+ theEditor.m_Behavior.assign(nonNull(element));
+ theEditor.m_Handler.assign(nonNull(handler));
+ theEditor.m_ArgumentStr.assign(nonNull(args));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SSetPresentationEditor::ElementName())) {
+ const char8_t *ref, *attribute, *value;
+ inReader.UnregisteredAtt("ref", ref);
+ inReader.UnregisteredAtt("attribute", attribute);
+ inReader.UnregisteredAtt("value", value);
+ SSetPresentationEditor &theEditor = CreateEditor<SSetPresentationEditor>(parentPtr);
+ theEditor.m_Ref.assign(nonNull(ref));
+ theEditor.m_Attribute.assign(nonNull(attribute));
+ theEditor.m_Value.assign(nonNull(value));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SPlaySoundEditor::ElementName())) {
+ const char8_t *file;
+ inReader.UnregisteredAtt("file", file);
+ SPlaySoundEditor &theEditor = CreateEditor<SPlaySoundEditor>(parentPtr);
+ theEditor.m_FilePath.assign(nonNull(file));
+ theEntry->m_Editors.push_back(theEditor);
+ }
+ }
+ }
+
+ void ParseVisualStateEntry(IDOMReader &inReader, TEditorPtr inEditor, TIdEntryExitMap &retval,
+ const char8_t *stateId)
+ {
+ TObjPtr parentPtr = inEditor->GetObjectById(stateId);
+ if (parentPtr) {
+ if (AreEqual(inReader.GetElementName().c_str(), "state")) {
+ for (bool entryExitSucces = inReader.MoveToFirstChild(); entryExitSucces;
+ entryExitSucces = inReader.MoveToNextSibling()) {
+ const char8_t *signalName(inReader.GetElementName());
+ if (AreEqual(signalName, "enter")) {
+ ParseExecutableContent(inReader, stateId, retval,
+ InterpreterEventTypes::StateEnter, parentPtr);
+ } else if (AreEqual(signalName, "exit")) {
+ ParseExecutableContent(inReader, stateId, retval,
+ InterpreterEventTypes::StateExit, parentPtr);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ } else if (AreEqual(inReader.GetElementName().c_str(), "transition")) {
+ ParseExecutableContent(inReader, stateId, retval, InterpreterEventTypes::Transition,
+ parentPtr);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ TIdEntryExitMap &ParseStateMachine(const TEditorStr &inId, TEditorPtr inEditor)
+ {
+ TIdEntryExitMap &retval =
+ m_IdToStateMaps.insert(eastl::make_pair(inId, TIdEntryExitMap())).first->second;
+ if (m_UIADocument.second->MoveToFirstChild("visual-states")) {
+ for (bool stateSuccess = m_UIADocument.second->MoveToFirstChild(); stateSuccess;
+ stateSuccess = m_UIADocument.second->MoveToNextSibling()) {
+ IDOMReader::Scope __stateScope(*m_UIADocument.second);
+ const char8_t *stateId;
+ m_UIADocument.second->UnregisteredAtt("ref", stateId);
+ if (!isTrivial(stateId)) {
+ if (stateId[0] == '#')
+ ++stateId;
+
+ ParseVisualStateEntry(*m_UIADocument.second, inEditor, retval, stateId);
+ }
+ }
+ }
+ return retval; // found a statemachine with the right id.
+ }
+
+ TIdEntryExitMap &GetOrCreateStateMap(const TEditorStr &inId, TEditorPtr inEditor)
+ {
+ TIdEntryExitMap &retval =
+ m_IdToStateMaps.insert(eastl::make_pair(inId, TIdEntryExitMap())).first->second;
+
+ if (retval.empty()) {
+ IDOMReader::Scope __appScope(*m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild("statemachine");
+ success && retval.empty();
+ success = m_UIADocument.second->MoveToNextSibling("statemachine")) {
+ IDOMReader::Scope __machineScope(*m_UIADocument.second);
+ const char8_t *idref;
+ m_UIADocument.second->UnregisteredAtt("ref", idref);
+ if (!isTrivial(idref)) {
+ if (idref[0] == '#')
+ ++idref;
+ if (inId.compare(idref) == 0) {
+ return ParseStateMachine(inId, inEditor);
+ }
+ }
+ } // for (statemachines)
+ }
+
+ return retval;
+ }
+
+ TIdEntryExitMap *GetStateMapForObject(TObjPtr inObject)
+ {
+ if (!CouldHaveVisualStateExecutableContent(inObject))
+ return NULL;
+
+ TEditorPtr theEditor = inObject->GetEditor();
+ // Find the id for the editor
+ TIdEntryExitMap *stateMap(NULL);
+
+ for (size_t idx = 0, end = m_Editors.size(); idx < end && stateMap == NULL; ++idx) {
+ if (m_Editors[idx].m_Editor == theEditor)
+ return &GetOrCreateStateMap(m_Editors[idx].m_Id, m_Editors[idx].m_Editor);
+ }
+ return NULL;
+ }
+
+ // The editor obj has a remove from graph function that really is delete
+ virtual TObjList GetVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType)
+ {
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return TObjList();
+
+ TEditorStr objId(inObject->GetId());
+ TIdEntryExitMap::iterator iter = stateMap->find(objId);
+ for (TIdEntryExitMap::iterator temp = stateMap->begin(), end = stateMap->end(); temp != end;
+ ++temp) {
+ objId = temp->first;
+ objId.clear();
+ }
+ if (iter != stateMap->end()) {
+ TEntryExitPair thePair = iter->second;
+ if (thePair.first && thePair.first->m_EventType == inEventType)
+ return thePair.first->m_Editors;
+ else if (thePair.second && thePair.second->m_EventType == inEventType)
+ return thePair.second->m_Editors;
+ }
+
+ return TObjList();
+ }
+
+ // Type name is the element name, so set-attribute, goto-slide, or fire-event
+ virtual TObjPtr AppendVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType,
+ const char8_t *inElementName)
+ {
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return TObjPtr();
+
+ NVConstDataRef<InterpreterEventTypes::Enum> supportedTypes =
+ inObject->GetExecutableContentTypes();
+ bool foundEventType = false;
+ for (size_t idx = 0, end = supportedTypes.size(); idx < end && foundEventType == false;
+ ++idx)
+ if (inEventType == supportedTypes[idx])
+ foundEventType = true;
+
+ if (foundEventType == false) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ TEntryExitPair &thePair =
+ stateMap->insert(eastl::make_pair(inObject->GetId(), TEntryExitPair())).first->second;
+ SVSEntry *entry = GetEntryForPair(thePair, inEventType);
+ if (!entry) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ if (AreEqual(inElementName, SGotoSlideEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SGotoSlideEditor>(inObject));
+ } else if (AreEqual(inElementName, SRunHandlerEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SRunHandlerEditor>(inObject));
+ } else if (AreEqual(inElementName, SSetAttributeEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SSetAttributeEditor>(inObject));
+ } else if (AreEqual(inElementName, SFireEventEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SFireEventEditor>(inObject));
+ } else if (AreEqual(inElementName, SSetPresentationEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SSetPresentationEditor>(inObject));
+ } else if (AreEqual(inElementName, SPlaySoundEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SPlaySoundEditor>(inObject));
+ } else {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ TObjPtr retval = entry->m_Editors.back();
+ STransaction *theTrans = GetOpenTransactionImpl();
+ if (theTrans) {
+ SVSEntryListChange *theChange =
+ new SVSEntryListChange(TObjPtr(), retval, *entry, true, inObject);
+ theTrans->m_Changes.push_back(theChange);
+ }
+ return retval;
+ }
+
+ virtual TObjPtr ChangeVisualStateExecutableContentName(TObjPtr inContent,
+ const char8_t *inElementName)
+ {
+ TObjPtr theParent = inContent->Parent();
+ if (!CouldHaveVisualStateExecutableContent(theParent)) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ TIdEntryExitMap *stateMap = GetStateMapForObject(theParent);
+ if (stateMap == NULL) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ TIdEntryExitMap::iterator iter = stateMap->find(theParent->GetId());
+ if (iter == stateMap->end()) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ TEntryExitPair thePair = iter->second;
+ NVScopedRefCounted<SVSEntry> theEntryFound;
+ if (!theEntryFound) {
+ NVScopedRefCounted<SVSEntry> theEntry = thePair.first;
+ TObjPtr theObj = inContent;
+ if (theEntry) {
+ TObjList::iterator iter =
+ eastl::find(theEntry->m_Editors.begin(), theEntry->m_Editors.end(), theObj);
+ if (iter != theEntry->m_Editors.end()) {
+ theEntryFound = theEntry;
+ }
+ }
+ }
+ if (!theEntryFound) {
+ NVScopedRefCounted<SVSEntry> theEntry = thePair.second;
+ TObjPtr theObj = inContent;
+ if (theEntry) {
+ TObjList::iterator iter =
+ eastl::find(theEntry->m_Editors.begin(), theEntry->m_Editors.end(), theObj);
+ if (iter != theEntry->m_Editors.end()) {
+ theEntryFound = theEntry;
+ }
+ }
+ }
+ if (!theEntryFound)
+ return TObjPtr();
+
+ TObjPtr theRetval;
+ if (AreEqual(inElementName, SGotoSlideEditor::ElementName())) {
+ theRetval = CreateEditor<SGotoSlideEditor>(theParent);
+ } else if (AreEqual(inElementName, SRunHandlerEditor::ElementName())) {
+ theRetval = CreateEditor<SRunHandlerEditor>(theParent);
+ } else if (AreEqual(inElementName, SSetAttributeEditor::ElementName())) {
+ theRetval = CreateEditor<SSetAttributeEditor>(theParent);
+ } else if (AreEqual(inElementName, SFireEventEditor::ElementName())) {
+ theRetval = CreateEditor<SFireEventEditor>(theParent);
+ } else if (AreEqual(inElementName, SSetPresentationEditor::ElementName())) {
+ theRetval = CreateEditor<SSetPresentationEditor>(theParent);
+ } else if (AreEqual(inElementName, SPlaySoundEditor::ElementName())) {
+ theRetval = CreateEditor<SPlaySoundEditor>(theParent);
+ } else {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ NVScopedRefCounted<SVSEntryListChange> theOldChange =
+ new SVSEntryListChange(inContent, inContent, *theEntryFound, false, theParent);
+ NVScopedRefCounted<SVSEntryListChange> theNewChange =
+ new SVSEntryListChange(theRetval, inContent, *theEntryFound, true, theParent);
+ theOldChange->Do();
+ theNewChange->Do();
+ if (GetOpenTransactionImpl()) {
+ GetOpenTransactionImpl()->m_Changes.push_back(theOldChange.mPtr);
+ GetOpenTransactionImpl()->m_Changes.push_back(theNewChange.mPtr);
+ }
+
+ return theRetval;
+ }
+
+ // Called when the source uia changes.
+ virtual void RefreshFile() { LoadUIADatabase(); }
+
+ virtual void RefreshFromStream(qt3ds::foundation::IInStream &inStream)
+ {
+ LoadUIADatabaseFromStream(inStream);
+ }
+
+ // Returns the path that was passed in on create.
+ virtual TEditorStr GetFilePath() { return m_FilePath; }
+
+ void CopyDOM(IDOMReader &inReader, IDOMWriter &inWriter)
+ {
+ IDOMReader::Scope __itemScope(inReader);
+ const SDOMElement &theElement(*inReader.GetElement());
+ IDOMWriter::Scope __writeScope(inWriter, theElement.m_Name, theElement.m_Namespace);
+ if (theElement.m_Attributes.empty() && theElement.m_Children.empty()) {
+ if (!isTrivial(theElement.m_Value))
+ inWriter.Value(theElement.m_Value);
+ } else {
+ for (TAttributeList::iterator iter = theElement.m_Attributes.begin(),
+ end = theElement.m_Attributes.end();
+ iter != end; ++iter) {
+ inWriter.Att(iter->m_Name, iter->m_Value, iter->m_Namespace);
+ }
+
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __loopScope(inReader);
+ CopyDOM(inReader, inWriter);
+ }
+ }
+ }
+ void ReplaceDOMNamespace(SDOMElement &inDom,
+ qt3ds::foundation::CRegisteredString &inNamespaceToReplace,
+ qt3ds::foundation::CRegisteredString &inNewNamespace)
+ {
+ if (!inNamespaceToReplace.IsValid() || !inNewNamespace.IsValid())
+ return;
+ if (inDom.m_Namespace == inNamespaceToReplace)
+ inDom.m_Namespace = inNewNamespace;
+ // Set attributes namespace
+ for (qt3ds::foundation::TAttributeList::iterator theIter = inDom.m_Attributes.begin(),
+ theEnd = inDom.m_Attributes.end();
+ theIter != theEnd; ++theIter) {
+ if (theIter->m_Namespace == inNamespaceToReplace)
+ theIter->m_Namespace = inNewNamespace;
+ }
+ // Recursive
+ for (qt3ds::foundation::SDOMElement::TElementChildList::iterator
+ theIter = inDom.m_Children.begin(),
+ theEnd = inDom.m_Children.end();
+ theIter != theEnd; ++theIter) {
+ ReplaceDOMNamespace(*theIter, inNamespaceToReplace, inNewNamespace);
+ }
+ }
+ struct SEditorIDFinder
+ {
+ TEditorStr m_Id;
+ SEditorIDFinder(const TEditorStr &id)
+ : m_Id(id)
+ {
+ }
+ bool operator()(const SEditorEntry &entry) const { return m_Id == entry.m_Id; }
+ };
+
+ void WriteExecutableContent(IDOMWriter &inWriter, NVScopedRefCounted<SVSEntry> inEntry)
+ {
+ if (inEntry == NULL)
+ return;
+ const char *elemName = "";
+ switch (inEntry->m_EventType) {
+ case InterpreterEventTypes::Transition:
+ break;
+ case InterpreterEventTypes::StateEnter:
+ elemName = "enter";
+ break;
+ case InterpreterEventTypes::StateExit:
+ elemName = "exit";
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ if (!isTrivial(elemName))
+ inWriter.Begin(elemName, GetNamespace());
+
+ eastl::vector<SPropertyDeclaration> properties;
+ for (size_t idx = 0, end = inEntry->m_Editors.size(); idx < end; ++idx) {
+ TObjPtr editor(inEntry->m_Editors[idx]);
+ IDOMWriter::Scope __contentScope(inWriter, editor->TypeName(), GetNamespace());
+ editor->GetProperties(properties);
+ bool theNoProperty = true;
+ for (size_t idx = 0, end = properties.size(); idx < end; ++idx) {
+ TEditorStr theProp =
+ editor->GetPropertyValue(properties[idx].m_Name)->getData<TEditorStr>();
+ if (theProp.empty() == false) {
+ inWriter.Att(properties[idx].m_Name.c_str(), theProp.c_str(),
+ GetNamespace());
+ theNoProperty = false;
+ }
+ }
+ if (theNoProperty && properties.size() > 0)
+ inWriter.Att(properties[0].m_Name.c_str(), "", GetNamespace());
+ }
+
+ if (!isTrivial(elemName))
+ inWriter.End();
+ }
+
+ void WriteStateEntry(TIdEntryExitMap::const_iterator stateIter, IDOMWriter &writer,
+ const char *typeName, const char *id)
+ {
+ const char8_t *stateName = "state";
+ if (AreEqual(typeName, "transition"))
+ stateName = "transition";
+ eastl::string tempAtt;
+ IDOMWriter::Scope __stateScope(writer, stateName, GetNamespace());
+ tempAtt.assign("#");
+ tempAtt.append(id);
+ writer.Att("ref", tempAtt.c_str());
+ WriteExecutableContent(writer, stateIter->second.first);
+ WriteExecutableContent(writer, stateIter->second.second);
+ }
+
+ // Returns false if unable to save, ask users to check the file out.
+ bool SaveInner(qt3ds::foundation::IOutStream &inStream)
+ {
+ IDOMReader::Scope __saveScope(m_UIADocument.second);
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_Foundation->getAllocator(), m_StringTable));
+ NVScopedRefCounted<IDOMWriter> outgoingDoc =
+ IDOMWriter::CreateDOMWriter(m_Foundation->getAllocator(), "application", m_StringTable,
+ GetNamespace())
+ .first;
+ {
+ m_UIADocument.second->MoveToFirstChild("application");
+ for (SDOMAttribute *theAtt = m_UIADocument.second->GetFirstAttribute(); theAtt;
+ theAtt = m_UIADocument.second->GetNextAttribute()) {
+ outgoingDoc->Att(theAtt->m_Name, theAtt->m_Value, theAtt->m_Namespace);
+ }
+ }
+
+ {
+ IDOMReader::Scope __appScope(m_UIADocument.second);
+ if (m_UIADocument.second->MoveToFirstChild("assets")) {
+ CopyDOM(*m_UIADocument.second, *outgoingDoc);
+ }
+ }
+ {
+ eastl::string tempAtt;
+ for (TIdStateMapMap::const_iterator iter = m_IdToStateMaps.begin(),
+ end = m_IdToStateMaps.end();
+ iter != end; ++iter) {
+ nvvector<SEditorEntry>::iterator editorEntry = eastl::find_if(
+ m_Editors.begin(), m_Editors.end(), SEditorIDFinder(iter->first));
+ if (editorEntry == m_Editors.end()) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ IDOMWriter::Scope __machineScope(*outgoingDoc, "statemachine", GetNamespace());
+ tempAtt.assign("#");
+ tempAtt.append(iter->first);
+ outgoingDoc->Att("ref", tempAtt.c_str());
+ IDOMWriter::Scope __vsScope(*outgoingDoc, "visual-states", GetNamespace());
+ const TIdEntryExitMap &itemMap = iter->second;
+ for (TIdEntryExitMap::const_iterator stateIter = itemMap.begin(),
+ stateEnd = itemMap.end();
+ stateIter != stateEnd; ++stateIter) {
+ TObjPtr editorObj =
+ editorEntry->m_Editor->GetObjectById(stateIter->first.c_str());
+ if (!editorObj) {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+ WriteStateEntry(stateIter, *outgoingDoc, editorObj->TypeName(),
+ stateIter->first.c_str());
+ }
+ }
+ }
+
+ SDOMElement *topElem = outgoingDoc->GetTopElement();
+ CDOMSerializer::WriteXMLHeader(inStream);
+ CDOMSerializer::Write(m_Foundation->getAllocator(), *topElem, inStream, *m_StringTable,
+ m_UIANamespaces);
+ m_Dirty = false;
+ return true;
+ }
+
+ virtual bool Save()
+ {
+ CFileSeekableIOStream theStream(m_FilePath.c_str(), FileWriteFlags());
+ if (!theStream.IsOpen())
+ return false;
+ return SaveInner(theStream);
+ }
+
+ virtual bool Save(qt3ds::foundation::IOutStream &inStream) { return SaveInner(inStream); }
+
+ virtual eastl::vector<SPresentation> GetPresentations() { return m_Presentations; }
+ virtual eastl::string GetElementType(SAppElement &elem) { return elem.m_Type; }
+
+ virtual eastl::string GetElementId(SAppElement &elem) { return elem.m_Id; }
+ virtual bool IsComponent(SAppElement &elem)
+ {
+ return elem.GetSubType() == ElementSubTypes::Component;
+ }
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(SAppElement &elem)
+ {
+ return elem.m_Attributes;
+ }
+ virtual Q3DStudio::TAttOrArgList GetSlideAttributes() { return m_SlideProperties; }
+ virtual eastl::vector<SDatamodelValue> GetElementAttributeInitialValues(SAppElement &elem)
+ {
+ return elem.m_InitialValues;
+ }
+ virtual eastl::vector<SAppElement *> GetElementChildren(SAppElement &elem)
+ {
+ eastl::vector<SAppElement *> retval;
+ retval.resize(elem.m_Children.size());
+ for (size_t idx = 0, end = elem.m_Children.size(); idx < end; ++idx)
+ retval[idx] = elem.m_Children[idx].mPtr;
+ return retval;
+ }
+ virtual eastl::string GetLastLoadingErrorString() { return m_LoadingErrorString; }
+ // The file may either exist or not. You can pass in a uip file as well as a uia file.
+
+ virtual NVFoundationBase &GetFoundation() { return m_Foundation->getFoundation(); }
+
+ virtual IStringTable &GetStringTable() { return *m_StringTable; }
+
+ // ITransactionManager
+ // Undo/redo is supported via a transparent transaction system.
+ // calls are reentrant but last call will close the transaction object.
+ // Any changes to any editor objects will go through this transaction when they happen.
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener)
+ {
+ return m_TransactionManager->AddChangeListener(inListener);
+ }
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName)
+ {
+ return m_TransactionManager->BeginTransaction(inName);
+ }
+
+ virtual TTransactionPtr GetOpenTransaction()
+ {
+ return m_TransactionManager->GetOpenTransaction();
+ }
+
+ virtual void RollbackTransaction() { m_TransactionManager->RollbackTransaction(); }
+ virtual void EndTransaction() { m_TransactionManager->EndTransaction(); }
+
+ virtual void OnObjectCreated(TObjPtr) {}
+
+ virtual void OnObjectDeleted(TObjPtr inObject)
+ {
+ // don't care.
+ if (!CouldHaveVisualStateExecutableContent(inObject))
+ return;
+
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return;
+
+ TIdEntryExitMap::iterator iter = stateMap->find(inObject->GetId());
+ if (iter == stateMap->end())
+ return;
+ NVScopedRefCounted<SIdEntryExitDeleteChange> theChange =
+ new SIdEntryExitDeleteChange(*stateMap, inObject->GetId(), inObject);
+ theChange->Do();
+ if (GetOpenTransactionImpl())
+ GetOpenTransactionImpl()->m_Changes.push_back(theChange.mPtr);
+ }
+ bool RemoveObjectFromEntry(NVScopedRefCounted<SVSEntry> inEntry, SVSEditorObject &inObj)
+ {
+ if (inEntry) {
+ TObjList::iterator iter =
+ eastl::find(inEntry->m_Editors.begin(), inEntry->m_Editors.end(), TObjPtr(inObj));
+ if (iter != inEntry->m_Editors.end()) {
+ NVScopedRefCounted<SVSEntryListChange> theChange = new SVSEntryListChange(
+ TObjPtr(inObj), TObjPtr(inObj), *inEntry, false, inObj.m_ParentObject);
+ theChange->Do();
+ if (GetOpenTransactionImpl())
+ GetOpenTransactionImpl()->m_Changes.push_back(theChange.mPtr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual void RemoveObjectFromGraph(SVSEditorObject &inObj)
+ {
+ TObjPtr parentPtr = inObj.m_ParentObject;
+ if (!CouldHaveVisualStateExecutableContent(parentPtr)) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ TIdEntryExitMap *stateMap = GetStateMapForObject(parentPtr);
+ if (stateMap == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ TIdEntryExitMap::iterator iter = stateMap->find(parentPtr->GetId());
+ if (iter == stateMap->end()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ TEntryExitPair thePair = iter->second;
+ if (!RemoveObjectFromEntry(thePair.first, inObj)) {
+ RemoveObjectFromEntry(thePair.second, inObj);
+ }
+ }
+
+ struct SEditorFinder
+ {
+ TEditorPtr m_Editor;
+ SEditorFinder(TEditorPtr editor)
+ : m_Editor(editor)
+ {
+ }
+ bool operator()(const SEditorEntry &entry) const { return entry.m_Editor == m_Editor; }
+ };
+
+ void CopyStateNode(SStateNode &inNode, TIdEntryExitMap &idMap, IDOMWriter &writer)
+ {
+ TIdEntryExitMap::iterator iter = idMap.find(inNode.m_Id.c_str());
+ if (iter != idMap.end()) {
+ const char *typeName = "state";
+ if (inNode.m_Type == StateNodeTypes::Transition)
+ typeName = "transition";
+ WriteStateEntry(iter, writer, typeName, inNode.m_Id.c_str());
+ }
+ TStateNodeList *childList = inNode.GetChildren();
+ if (childList) {
+ for (TStateNodeList::iterator iter = childList->begin(), end = childList->end();
+ iter != end; ++iter) {
+ CopyStateNode(*iter, idMap, writer);
+ }
+ }
+ }
+
+ virtual void OnCopy(TEditorPtr inEditor, eastl::vector<SStateNode *> &ioCopiedRoots,
+ IDOMWriter &ioWriter, eastl::vector<SNamespacePair> &ioNamespaces)
+ {
+ ioNamespaces.push_back(SNamespacePair(m_StringTable->RegisterStr(GetNamespace()),
+ m_StringTable->RegisterStr("uia")));
+ eastl::vector<SEditorEntry>::iterator entry =
+ eastl::find_if(m_Editors.begin(), m_Editors.end(), SEditorFinder(inEditor));
+ if (entry == m_Editors.end())
+ return;
+ TIdStateMapMap::iterator mapEntry = m_IdToStateMaps.find(entry->m_Id);
+ if (mapEntry == m_IdToStateMaps.end())
+ return;
+ IDOMWriter::Scope __modelScope(ioWriter, "datamodel_fragment", GetNamespace());
+ for (size_t idx = 0, end = ioCopiedRoots.size(); idx < end; ++idx) {
+ CopyStateNode(*ioCopiedRoots[idx], mapEntry->second, ioWriter);
+ }
+ }
+
+ virtual void OnPaste(TEditorPtr inEditor, IDOMReader &ioReader,
+ CXMLIO::TIdRemapMap &inStateIdRemapMap)
+ {
+ eastl::vector<SEditorEntry>::iterator entry =
+ eastl::find_if(m_Editors.begin(), m_Editors.end(), SEditorFinder(inEditor));
+ if (entry == m_Editors.end())
+ return;
+
+ IDOMReader::Scope __fragmentScope(ioReader);
+ if (ioReader.MoveToFirstChild("datamodel_fragment")) {
+ TIdEntryExitMap &stateMap =
+ m_IdToStateMaps.insert(eastl::make_pair(entry->m_Id, TIdEntryExitMap()))
+ .first->second;
+ for (bool success = ioReader.MoveToFirstChild(); success;
+ success = ioReader.MoveToNextSibling()) {
+ IDOMReader::Scope __childScope(ioReader);
+ const char8_t *idRef;
+ ioReader.UnregisteredAtt("ref", idRef);
+ if (isTrivial(idRef))
+ continue;
+ if (idRef[0] == '#')
+ ++idRef;
+
+ CXMLIO::TIdRemapMap::iterator finder = inStateIdRemapMap.find(idRef);
+ if (finder != inStateIdRemapMap.end())
+ idRef = finder->second.c_str();
+
+ TEditorStr idStr(idRef);
+ TObjPtr parentObj = inEditor->GetObjectById(idRef);
+ if (parentObj) {
+ ParseVisualStateEntry(ioReader, inEditor, stateMap, idRef);
+ if (stateMap.find(idStr) != stateMap.end()
+ && m_TransactionManager->GetOpenTransactionImpl()) {
+ m_TransactionManager->GetOpenTransactionImpl()->m_Changes.push_back(
+ new SIdEntryExitDeleteChange(stateMap, idStr, parentObj, false));
+ }
+ }
+ }
+ }
+ }
+
+ virtual void OnIDChange(TEditorPtr inEditor, SStateNode &inNode, const char8_t *inOldId)
+ {
+ eastl::vector<SEditorEntry>::iterator entry =
+ eastl::find_if(m_Editors.begin(), m_Editors.end(), SEditorFinder(inEditor));
+ if (entry == m_Editors.end())
+ return;
+
+ TIdEntryExitMap &stateMap =
+ m_IdToStateMaps.insert(eastl::make_pair(entry->m_Id, TIdEntryExitMap())).first->second;
+ TEditorStr oldIdStr(inOldId);
+ TEditorStr newIdStr(inNode.m_Id.c_str());
+ TIdEntryExitMap::iterator iter = stateMap.find(oldIdStr);
+ if (iter != stateMap.end()) {
+ TEntryExitPair thePair(iter->second);
+ stateMap.erase(iter);
+ stateMap.insert(eastl::make_pair(newIdStr, thePair));
+ }
+ }
+ virtual bool OnDeleteState(TObjPtr inObject)
+ {
+ // don't care.
+ if (!CouldHaveVisualStateExecutableContent(inObject))
+ return false;
+
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return false;
+
+ TIdEntryExitMap::iterator iter = stateMap->find(inObject->GetId());
+ if (iter == stateMap->end())
+ return false;
+ NVScopedRefCounted<SIdEntryExitDeleteChange> theChange =
+ new SIdEntryExitDeleteChange(*stateMap, inObject->GetId(), inObject);
+ theChange->Do();
+ if (GetOpenTransactionImpl())
+ GetOpenTransactionImpl()->m_Changes.push_back(theChange.mPtr);
+ return true;
+ }
+ virtual bool OnReloadStateMachine(TEditorPtr inStateMachineEditor)
+ {
+ bool theRetval = false;
+ TEditorPtr theEditor = inStateMachineEditor;
+
+ for (size_t idx = 0, end = m_Editors.size(); idx < end; ++idx) {
+ if (m_Editors[idx].m_Editor == theEditor) {
+ TIdStateMapMap::iterator theFind = m_IdToStateMaps.find(m_Editors[idx].m_Id);
+ if (theFind != m_IdToStateMaps.end()) {
+ TIdEntryExitMap &theStateMap = theFind->second;
+ for (TIdEntryExitMap::iterator theIter = theStateMap.begin(),
+ theEnd = theStateMap.end();
+ theIter != theEnd;) {
+ TEditorStr theStateId = theIter->first;
+ TObjPtr theState = inStateMachineEditor->GetObjectById(theStateId.c_str());
+ if (theState) {
+ {
+ NVScopedRefCounted<SVSEntry> theEntry = theIter->second.first;
+ if (theEntry) {
+ for (TObjList::iterator theIter = theEntry->m_Editors.begin(),
+ theEnd = theEntry->m_Editors.end();
+ theIter != theEnd; ++theIter) {
+ SVSEditorObject *theExecutableContent =
+ static_cast<SVSEditorObject *>(theIter->mPtr);
+ if (theExecutableContent) {
+ theExecutableContent->m_ParentObject = theState;
+ }
+ }
+ }
+ }
+ {
+ NVScopedRefCounted<SVSEntry> theEntry = theIter->second.second;
+ if (theEntry) {
+ for (TObjList::iterator theIter = theEntry->m_Editors.begin(),
+ theEnd = theEntry->m_Editors.end();
+ theIter != theEnd; ++theIter) {
+ SVSEditorObject *theExecutableContent =
+ static_cast<SVSEditorObject *>(theIter->mPtr);
+ if (theExecutableContent) {
+ theExecutableContent->m_ParentObject = theState;
+ }
+ }
+ }
+ }
+ ++theIter;
+ } else {
+ theStateMap.erase(theIter++);
+ theRetval = true;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return theRetval;
+ }
+ virtual void RegisterChangeListener(IStateMachineChangeListener &) {}
+ virtual void UnregisterChangeListener(IStateMachineChangeListener &) {}
+};
+}
+
+IDatamodel &IDatamodel::Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inAppDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IInStream *inStream)
+{
+ return IDatamodel::Create(inFoundation, inPath, inAppDir, inStateMachineEditorManager,
+ IStringTable::CreateStringTable(inFoundation->getAllocator()),
+ inStream);
+}
+
+IDatamodel &IDatamodel::Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inAppDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IStringTable &inStringTable, IInStream *inStream)
+{
+ DatamodelImpl &theDatamodel = *QT3DS_NEW(inFoundation->getAllocator(), DatamodelImpl)(
+ inFoundation, inPath, inAppDir, inStateMachineEditorManager, inStringTable);
+ if (inStream)
+ theDatamodel.LoadUIADatabaseFromStream(*inStream);
+ else
+ theDatamodel.LoadUIADatabase();
+ return theDatamodel;
+}
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h
new file mode 100644
index 00000000..e064529a
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef UIA_DATAMODEL_H
+#define UIA_DATAMODEL_H
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSMetadata.h"
+#include "Qt3DSStateInterpreter.h"
+#include "Qt3DSStateEditorFoundation.h"
+
+namespace qt3ds {
+namespace app {
+ using namespace qt3ds::state;
+ using namespace qt3ds::state::editor;
+
+ typedef eastl::pair<Q3DStudio::ERuntimeDataModelDataType,
+ Q3DStudio::ERuntimeAdditionalMetaDataType>
+ TDataType;
+
+ struct SDatamodelValue;
+
+ struct SAppElement;
+
+ struct SPresentation
+ {
+ eastl::string m_Id;
+ eastl::string m_SrcPath;
+ eastl::string m_Author;
+ eastl::string m_Company;
+ QT3DSU32 m_Width;
+ QT3DSU32 m_Height;
+ SAppElement *m_Scene;
+ SPresentation()
+ : m_Width(800)
+ , m_Height(480)
+ , m_Scene(NULL)
+ {
+ }
+ };
+
+ class IStateMachineChangeListener
+ {
+ public:
+ virtual ~IStateMachineChangeListener() {}
+ virtual bool OnDeleteState(TObjPtr inObject) = 0;
+ virtual bool OnReloadStateMachine(TEditorPtr inStateMachineEditor) = 0;
+ };
+
+ class IStateMachineEditorManager
+ {
+ public:
+ virtual ~IStateMachineEditorManager() {}
+ virtual TEditorPtr GetOrCreateEditor(const TEditorStr &inFullPath, bool *outLoadStatus) = 0;
+ virtual void RegisterChangeListener(IStateMachineChangeListener &inListener) = 0;
+ virtual void UnregisterChangeListener(IStateMachineChangeListener &inListener) = 0;
+ };
+
+ class IDatamodel : public ITransactionManager,
+ public IStateMachineEditorManager,
+ public IStateMachineChangeListener
+ {
+ protected:
+ virtual ~IDatamodel() {}
+ public:
+ // Returns true if we had to create the file. Users should always keep track of the
+ // transaction stack also
+ // this does not include that.
+ virtual bool IsDirty() const = 0;
+ // General queries of the dataset defined by the uia file and all uip files and scxml files
+ // it includes.
+
+ virtual TEditorStrList GetComponents() = 0;
+ virtual TEditorStrList GetComponentSlides(const TEditorStr &inComponent) = 0;
+
+ virtual TEditorStrList GetBehaviors() = 0;
+
+ virtual Q3DStudio::THandlerList GetHandlers(const TEditorStr &inBehavior) = 0;
+
+ virtual Q3DStudio::TVisualEventList GetVisualEvents(const TEditorStr &inElement) = 0;
+
+ virtual TEditorStrList GetElements() = 0;
+
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(const TEditorStr &inElement) = 0;
+
+ // Necessary to share transaction info and trigger deletes of related information but not
+ // going to get
+ // done right now.
+ // virtual TEditorPtr GetOrCreateEditor( const TEditorStr& inFullPath ) = 0;
+
+ // The editor obj has a remove from graph function that really is delete
+ virtual TObjList
+ GetVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType) = 0;
+ // Type name is the element name, so set-attribute, goto-slide, or fire-event
+ virtual TObjPtr AppendVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType,
+ const char8_t *inElementName) = 0;
+ virtual TObjPtr ChangeVisualStateExecutableContentName(TObjPtr inContent,
+ const char8_t *inElementName) = 0;
+
+ // Called when the source uia changes.
+ virtual void RefreshFile() = 0;
+ virtual void RefreshFromStream(qt3ds::foundation::IInStream &inStream) = 0;
+ // Returns the path that was passed in on create.
+ virtual TEditorStr GetFilePath() = 0;
+
+ // Returns false if unable to save, ask users to check the file out.
+ virtual bool Save() = 0;
+ virtual bool Save(qt3ds::foundation::IOutStream &inStream) = 0;
+
+ // The section below allows someone to build an initial scene graph.
+ // Note that components have additional properties
+ // Get a list of presentations found while parsing uia file.
+ virtual eastl::vector<SPresentation> GetPresentations() = 0;
+ virtual eastl::string GetElementType(SAppElement &elem) = 0;
+ virtual eastl::string GetElementId(SAppElement &elem) = 0;
+ virtual bool IsComponent(SAppElement &elem) = 0;
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(SAppElement &elem) = 0;
+ // These are found either on the slide or on the component depending on if you are working
+ // in uip space or runtime space.
+ virtual Q3DStudio::TAttOrArgList GetSlideAttributes() = 0;
+ virtual eastl::vector<SDatamodelValue>
+ GetElementAttributeInitialValues(SAppElement &elem) = 0;
+ virtual eastl::vector<SAppElement *> GetElementChildren(SAppElement &elem) = 0;
+
+ virtual eastl::string GetLastLoadingErrorString() = 0;
+
+ virtual NVFoundationBase &GetFoundation() = 0;
+ virtual IStringTable &GetStringTable() = 0;
+
+ // inPath may either exist or not. You can pass in a uip file as well as a uia file.
+ // application dir is so we can find the meta data.
+ // inStream provides a way to load from memory, it will be used if it's not NULL.
+ static IDatamodel &Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inApplicationDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IInStream *inStream = 0);
+ static IDatamodel &Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inApplicationDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IStringTable &inStringTable, IInStream *inStream = 0);
+ };
+
+ typedef NVScopedRefCounted<IDatamodel> TDatamodelPtr;
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h
new file mode 100644
index 00000000..7698809c
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef UIA_DATAMODEL_VALUE_H
+#define UIA_DATAMODEL_VALUE_H
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "Qt3DSUIADatamodel.h"
+
+namespace qt3ds {
+namespace app {
+
+ using namespace Q3DStudio;
+
+ template <typename TDatatype>
+ struct SDatamodelValueTypeMap
+ {
+ };
+
+#define QT3DS_UIA_DATAMODEL_TYPE_MAP(type, enumname) \
+ template <> \
+ struct SDatamodelValueTypeMap<type> \
+ { \
+ static Q3DStudio::ERuntimeDataModelDataType GetType() { return Q3DStudio::enumname; } \
+ };
+
+ struct SGuid
+ {
+ long m_Data[4];
+ };
+
+ struct SObjectRef
+ {
+ CRegisteredString m_Presentation;
+ CRegisteredString m_Id;
+ };
+
+ class CStringOrInt
+ {
+ bool m_IsString;
+ CRegisteredString m_String;
+ int m_IntValue;
+
+ public:
+ CStringOrInt(CRegisteredString val = CRegisteredString())
+ : m_IsString(true)
+ , m_String(val)
+ , m_IntValue(0)
+ {
+ }
+ CStringOrInt(int val)
+ : m_IsString(false)
+ , m_IntValue(val)
+ {
+ }
+ CStringOrInt(const CStringOrInt &other)
+ : m_IsString(other.m_IsString)
+ , m_String(other.m_String)
+ , m_IntValue(other.m_IntValue)
+ {
+ }
+ CStringOrInt &operator=(const CStringOrInt &other)
+ {
+ m_IsString = other.m_IsString;
+ m_String = other.m_String;
+ m_IntValue = other.m_IntValue;
+ return *this;
+ }
+ bool IsString() const { return m_IsString; }
+ CRegisteredString StringValue() const
+ {
+ QT3DS_ASSERT(m_IsString);
+ return m_String;
+ }
+ int IntValue() const
+ {
+ QT3DS_ASSERT(m_IsString == false);
+ return m_IntValue;
+ }
+ };
+
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(float, ERuntimeDataModelDataTypeFloat);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(QT3DSVec2, ERuntimeDataModelDataTypeFloat2);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(QT3DSVec3, ERuntimeDataModelDataTypeFloat3);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(QT3DSI32, ERuntimeDataModelDataTypeLong);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(eastl::string, ERuntimeDataModelDataTypeString);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(bool, ERuntimeDataModelDataTypeBool);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(SGuid, ERuntimeDataModelDataTypeLong4);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(CRegisteredString, ERuntimeDataModelDataTypeStringRef);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(SObjectRef, ERuntimeDataModelDataTypeObjectRef);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(CStringOrInt, ERuntimeDataModelDataTypeStringOrInt);
+
+ struct SDatamodelValueUnionTraits
+ {
+ typedef ERuntimeDataModelDataType TIdType;
+ enum {
+ TBufferSize = sizeof(eastl::string),
+ };
+
+ static TIdType getNoDataId() { return ERuntimeDataModelDataTypeNone; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SDatamodelValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ERuntimeDataModelDataTypeFloat:
+ return inVisitor(*NVUnionCast<float *>(inData));
+ case ERuntimeDataModelDataTypeFloat2:
+ return inVisitor(*NVUnionCast<QT3DSVec2 *>(inData));
+ case ERuntimeDataModelDataTypeFloat3:
+ return inVisitor(*NVUnionCast<QT3DSVec3 *>(inData));
+ case ERuntimeDataModelDataTypeLong:
+ return inVisitor(*NVUnionCast<QT3DSI32 *>(inData));
+ case ERuntimeDataModelDataTypeString:
+ return inVisitor(*NVUnionCast<eastl::string *>(inData));
+ case ERuntimeDataModelDataTypeBool:
+ return inVisitor(*NVUnionCast<bool *>(inData));
+ case ERuntimeDataModelDataTypeLong4:
+ return inVisitor(*NVUnionCast<SGuid *>(inData));
+ case ERuntimeDataModelDataTypeStringRef:
+ return inVisitor(*NVUnionCast<CRegisteredString *>(inData));
+ case ERuntimeDataModelDataTypeObjectRef:
+ return inVisitor(*NVUnionCast<SObjectRef *>(inData));
+ case ERuntimeDataModelDataTypeStringOrInt:
+ return inVisitor(*NVUnionCast<CStringOrInt *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ERuntimeDataModelDataTypeNone:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ERuntimeDataModelDataTypeFloat:
+ return inVisitor(*NVUnionCast<const float *>(inData));
+ case ERuntimeDataModelDataTypeFloat2:
+ return inVisitor(*NVUnionCast<const QT3DSVec2 *>(inData));
+ case ERuntimeDataModelDataTypeFloat3:
+ return inVisitor(*NVUnionCast<const QT3DSVec3 *>(inData));
+ case ERuntimeDataModelDataTypeLong:
+ return inVisitor(*NVUnionCast<const QT3DSI32 *>(inData));
+ case ERuntimeDataModelDataTypeString:
+ return inVisitor(*NVUnionCast<const eastl::string *>(inData));
+ case ERuntimeDataModelDataTypeBool:
+ return inVisitor(*NVUnionCast<const bool *>(inData));
+ case ERuntimeDataModelDataTypeLong4:
+ return inVisitor(*NVUnionCast<const SGuid *>(inData));
+ case ERuntimeDataModelDataTypeStringRef:
+ return inVisitor(*NVUnionCast<const CRegisteredString *>(inData));
+ case ERuntimeDataModelDataTypeObjectRef:
+ return inVisitor(*NVUnionCast<const SObjectRef *>(inData));
+ case ERuntimeDataModelDataTypeStringOrInt:
+ return inVisitor(*NVUnionCast<const CStringOrInt *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ERuntimeDataModelDataTypeNone:
+ return inVisitor();
+ }
+ }
+ };
+}
+}
+
+// need some specializations in the original nv foundation namespace
+namespace qt3ds {
+namespace foundation {
+
+ template <>
+ struct DestructTraits<qt3ds::app::SGuid>
+ {
+ void destruct(qt3ds::app::SGuid &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::app::SObjectRef>
+ {
+ void destruct(qt3ds::app::SObjectRef &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::app::CStringOrInt>
+ {
+ void destruct(qt3ds::app::CStringOrInt &) {}
+ };
+ template <>
+ struct DestructTraits<CRegisteredString>
+ {
+ void destruct(CRegisteredString &) {}
+ };
+}
+}
+
+namespace qt3ds {
+namespace app {
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SDatamodelValueUnionTraits,
+ SDatamodelValueUnionTraits::
+ TBufferSize>,
+ SDatamodelValueUnionTraits::TBufferSize>
+ TDatamodelUnionType;
+
+ struct SDatamodelValue : public TDatamodelUnionType
+ {
+ SDatamodelValue() {}
+ SDatamodelValue(const SDatamodelValue &other)
+ : TDatamodelUnionType(static_cast<const TDatamodelUnionType &>(other))
+ {
+ }
+ SDatamodelValue(float other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(QT3DSVec2 other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(QT3DSVec3 other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(QT3DSI32 other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(const eastl::string &other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(bool other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(SGuid other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(CRegisteredString other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(SObjectRef other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(CStringOrInt other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue &operator=(const SDatamodelValue &other)
+ {
+ TDatamodelUnionType::operator=(other);
+ return *this;
+ }
+ };
+}
+}
+#endif