From 0a82b82de3ad7796cf91a355017a24ace2c830b4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 3 May 2019 17:32:41 +0300 Subject: Add material creation to runtime C++ API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Materials can only be created into the material container, animatable materials are not supported. Elements using new created materials should not be created before Q3DSPresentation::materialCreated signal is received. Task-number: QT3DS-3377 Change-Id: I782f09094d5f7151d7e1dab28b76fbbfdfcb6066 Reviewed-by: Mahmoud Badri Reviewed-by: Jari Karppinen Reviewed-by: Tomi Korpipää --- .../Client/Code/Core/Doc/DocumentEditor.cpp | 93 +--- .../Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro | 2 + src/Runtime/Source/engine/Qt3DSRuntimeView.cpp | 16 + src/Runtime/Source/engine/Qt3DSRuntimeView.h | 2 + src/Runtime/Source/runtime/Qt3DSApplication.cpp | 4 +- src/Runtime/Source/runtime/Qt3DSIScriptBridge.h | 6 + src/Runtime/Source/runtime/Qt3DSPresentation.cpp | 80 +-- src/Runtime/Source/runtime/Qt3DSPresentation.h | 58 +- src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp | 590 ++++++++++++++++++--- .../runtime/q3dsmaterialdefinitionparser.cpp | 138 +++++ .../Source/runtime/q3dsmaterialdefinitionparser.h | 49 ++ src/Runtime/Source/uipparser/Qt3DSIPresentation.h | 42 +- src/Runtime/Source/viewer/Qt3DSViewerApp.cpp | 10 + src/Runtime/Source/viewer/Qt3DSViewerApp.h | 2 + src/Viewer/qmlviewer/Qt3DSRenderer.cpp | 8 + src/Viewer/qmlviewer/Qt3DSRenderer.h | 2 + src/Viewer/qmlviewer/Qt3DSView.cpp | 4 + src/Viewer/studio3d/q3dscommandqueue.cpp | 1 + src/Viewer/studio3d/q3dscommandqueue_p.h | 1 + src/Viewer/studio3d/q3dspresentation.cpp | 40 +- src/Viewer/studio3d/q3dspresentation.h | 2 + tests/auto/viewer/tst_qt3dsviewer.cpp | 182 +++++-- tests/auto/viewer/tst_qt3dsviewer.h | 7 + tests/auto/viewer/tst_qt3dsviewer.qml | 2 +- tests/auto/viewer/viewer.qrc | 7 + .../simple_cube_animation/maps/QT-symbol.png | Bin 0 -> 4541 bytes .../maps/materials/shadow.png | Bin 0 -> 334 bytes .../maps/materials/spherical_checker.png | Bin 0 -> 11066 bytes .../materials/Basic Blue.materialdef | 25 + .../materials/Basic Green.materialdef | 6 +- .../materials/Basic Red.materialdef | 6 +- .../materials/Basic Texture.materialdef | 63 +++ .../materials/Copper.materialdef | 14 + .../simple_cube_animation/materials/copper.shader | 178 +++++++ .../presentations/simple_cube_animation.uip | 20 +- 35 files changed, 1330 insertions(+), 330 deletions(-) create mode 100644 src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.cpp create mode 100644 src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.h create mode 100644 tests/scenes/simple_cube_animation/maps/QT-symbol.png create mode 100644 tests/scenes/simple_cube_animation/maps/materials/shadow.png create mode 100644 tests/scenes/simple_cube_animation/maps/materials/spherical_checker.png create mode 100644 tests/scenes/simple_cube_animation/materials/Basic Blue.materialdef create mode 100644 tests/scenes/simple_cube_animation/materials/Basic Texture.materialdef create mode 100644 tests/scenes/simple_cube_animation/materials/Copper.materialdef create mode 100644 tests/scenes/simple_cube_animation/materials/copper.shader diff --git a/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp b/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp index b821701d..71dc1de1 100644 --- a/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp +++ b/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp @@ -86,6 +86,7 @@ #include #include #include "runtime/q3dsqmlbehavior.h" +#include "runtime/q3dsmaterialdefinitionparser.h" #include "Qt3DSFileToolsSeekableMeshBufIOStream.h" #include "IObjectReferenceHelper.h" #include "StudioProjectSettings.h" @@ -917,93 +918,13 @@ public: QString &outName, QMap &outValues, QMap> &outTextureValues) override { - if (!QFileInfo(inAbsoluteFilePath).exists()) - return; - - qt3ds::foundation::CFileSeekableIOStream theStream(inAbsoluteFilePath, - qt3ds::foundation::FileReadFlags()); - if (theStream.IsOpen()) { - const QDir docDir(m_Doc.GetDocumentDirectory().toQString()); - const QDir projDir = g_StudioApp.GetCore()->getProjectFile().getProjectPath(); + Q3DStudio::Q3DSMaterialDefinitionParser::getMaterialInfo( + inAbsoluteFilePath, g_StudioApp.GetCore()->getProjectFile().getProjectPath(), + m_Doc.GetDocumentDirectory().toQString(), outName, outValues, outTextureValues); - std::shared_ptr theFactory = - IDOMFactory::CreateDOMFactory(m_DataCore.GetStringTablePtr()); - SImportXmlErrorHandler theImportHandler(m_Doc.GetImportFailedHandler(), - Q3DStudio::CString::fromQString( - inAbsoluteFilePath)); - qt3dsdm::SDOMElement *theElem = - CDOMSerializer::Read(*theFactory, theStream, &theImportHandler); - if (theElem) { - outName = getMaterialNameFromFilePath(inAbsoluteFilePath); - std::shared_ptr theReader = IDOMReader::CreateDOMReader( - *theElem, m_DataCore.GetStringTablePtr(), theFactory); - - const QString sourcePath = QStringLiteral("sourcepath"); - QStringList convertPaths; - for (bool success = theReader->MoveToFirstChild("Property"); success; - success = theReader->MoveToNextSibling("Property")) { - const char8_t *name = ""; - const char8_t *value = ""; - const char8_t *type = ""; - theReader->Att("name", name); - theReader->Att("type", type); - theReader->Value(value); - const QString nameStr = QString::fromUtf8(name); - const QString valueStr = QString::fromUtf8(value); - if (nameStr == sourcePath) { - // Check if the custom material still exists - const auto absSourcePath = projDir.absoluteFilePath(valueStr); - if (!QFileInfo(absSourcePath).exists()) { - outValues.clear(); - outTextureValues.clear(); - return; - } - } - if (!valueStr.isEmpty() && (QString::fromUtf8(type) == QLatin1String("Texture") - || nameStr == sourcePath)) { - convertPaths.append(nameStr); - } - outValues[nameStr] = valueStr; - } - - for (const auto &prop : qAsConst(convertPaths)) { - // Change paths to be relative to the presentation - const QString origPath = outValues[prop]; - outValues[prop] = docDir.relativeFilePath(projDir.absoluteFilePath(origPath)); - } - - if (AreEqual(theReader->GetElementName(), L"Property")) - theReader->Leave(); - - for (bool texSuccess = theReader->MoveToFirstChild("TextureData"); texSuccess; - texSuccess = theReader->MoveToNextSibling("TextureData")) { - QMap texValues; - const char8_t *texName = ""; - theReader->Att("name", texName); - for (bool success = theReader->MoveToFirstChild("Property"); success; - success = theReader->MoveToNextSibling("Property")) { - const char8_t *name = ""; - const char8_t *value = ""; - theReader->Att("name", name); - theReader->Value(value); - texValues[name] = value; - } - - if (texValues.contains(sourcePath) && !texValues[sourcePath].isEmpty()) { - // Change path to be relative to the presentation - texValues[sourcePath] = docDir.relativeFilePath( - projDir.absoluteFilePath(texValues[sourcePath])); - } - - outTextureValues[texName] = texValues; - - if (AreEqual(theReader->GetElementName(), L"Property")) - theReader->Leave(); - } - - outValues[QStringLiteral("name")] = outName; - } - } + // Fix the outName to follow the file name (in case it has changed) + outName = getMaterialNameFromFilePath(inAbsoluteFilePath); + outValues[QStringLiteral("name")] = outName; } /////////////////////////////////////////////////////////////////// diff --git a/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro b/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro index 9d5d2a01..1a76455a 100644 --- a/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro +++ b/src/Runtime/Qt3DSRuntimeStatic/Qt3DSRuntimeStatic.pro @@ -57,6 +57,7 @@ SOURCES += \ ../Source/runtime/Qt3DSSlideSystem.cpp \ ../Source/runtime/Qt3DSTimePolicy.cpp \ ../Source/runtime/q3dsvariantconfig.cpp \ + ../Source/runtime/q3dsmaterialdefinitionparser.cpp \ ../Source/runtimerender/graphobjects/Qt3DSRenderCamera.cpp \ ../Source/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.cpp \ ../Source/runtimerender/graphobjects/Qt3DSRenderDynamicObject.cpp \ @@ -258,6 +259,7 @@ HEADERS += \ ../Source/runtime/Qt3DSIText.h \ ../Source/runtime/Qt3DSKernelTypes.h \ ../Source/runtime/q3dsvariantconfig_p.h \ + ../Source/runtime/q3dsmaterialdefinitionparser.h \ ../Source/runtimerender/graphobjects/Qt3DSRenderCamera.h \ ../Source/runtimerender/graphobjects/Qt3DSRenderCustomMaterial.h \ ../Source/runtimerender/graphobjects/Qt3DSRenderDefaultMaterial.h \ diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp index 5e6506b3..5d6b0a6c 100644 --- a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp +++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp @@ -218,6 +218,7 @@ public: void createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties) override; void deleteElement(const QString &elementPath) override; + void createMaterial(const QString &elementPath, const QString &materialDefinition) override; void SetAttribute(const char *elementPath, const char *attributeName, const char *value) override; bool GetAttribute(const char *elementPath, const char *attributeName, void *value) override; @@ -309,6 +310,8 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed signalProxy(), &QRuntimeViewSignalProxy::SigSlideExited); QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigCustomSignal, signalProxy(), &QRuntimeViewSignalProxy::SigCustomSignal); + QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigMaterialCreated, + signalProxy(), &QRuntimeViewSignalProxy::SigMaterialCreated); m_TimeProvider.Reset(); return true; @@ -636,6 +639,19 @@ void CRuntimeView::deleteElement(const QString &elementPath) } } +void CRuntimeView::createMaterial(const QString &elementPath, const QString &materialDefinition) +{ + if (m_Application) { + Q3DStudio::CQmlEngine &theBridgeEngine + = static_cast(m_RuntimeFactoryCore->GetScriptEngineQml()); + theBridgeEngine.createMaterial( + elementPath, materialDefinition, + &m_RuntimeFactory->GetQt3DSRenderContext().GetCustomMaterialSystem(), + &m_RuntimeFactory->GetQt3DSRenderContext().GetDynamicObjectSystem(), + &m_RuntimeFactory->GetQt3DSRenderContext().GetRenderer()); + } +} + void CRuntimeView::SetAttribute(const char *elementPath, const char *attributeName, const char *value) { diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.h b/src/Runtime/Source/engine/Qt3DSRuntimeView.h index 0bd63010..ec136525 100644 --- a/src/Runtime/Source/engine/Qt3DSRuntimeView.h +++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.h @@ -55,6 +55,7 @@ Q_SIGNALS: void SigSlideEntered(const QString &elementPath, unsigned int index, const QString &name); void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name); void SigCustomSignal(const QString &elementPath, const QString &name); + void SigMaterialCreated(const QString &name); }; namespace qt3ds { @@ -192,6 +193,7 @@ public: virtual void createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties) = 0; virtual void deleteElement(const QString &elementPath) = 0; + virtual void createMaterial(const QString &elementPath, const QString &materialDefinition) = 0; virtual void SetAttribute(const char *elementPath, const char *attributeName, const char *value) = 0; virtual bool GetAttribute(const char *elementPath, const char *attributeName, void *value) = 0; diff --git a/src/Runtime/Source/runtime/Qt3DSApplication.cpp b/src/Runtime/Source/runtime/Qt3DSApplication.cpp index 830f4573..07ca8e4c 100644 --- a/src/Runtime/Source/runtime/Qt3DSApplication.cpp +++ b/src/Runtime/Source/runtime/Qt3DSApplication.cpp @@ -1092,7 +1092,9 @@ struct SApp : public IApplication if (theStream) { theStream = NULL; CPresentation *thePresentation - = Q3DStudio_new(CPresentation) CPresentation(inAsset.m_Id.c_str(), this); + = Q3DStudio_new(CPresentation) CPresentation(inAsset.m_Id.c_str(), + GetProjectDirectory().c_str(), + this); inAsset.m_Presentation = thePresentation; thePresentation->SetFilePath(theFile.c_str()); NVScopedReleasable theUIPParser(IUIPParser::Create( diff --git a/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h b/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h index 8f8925d8..8edced36 100644 --- a/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h +++ b/src/Runtime/Source/runtime/Qt3DSIScriptBridge.h @@ -53,6 +53,8 @@ namespace qt3ds { namespace render { class IThreadPool; class IQt3DSRenderer; + class ICustomMaterialSystem; + class IDynamicObjectSystem; } } @@ -164,6 +166,10 @@ public: // Elements qt3ds::render::IQt3DSRenderer *renderer) = 0; virtual void deleteElement(const QString &elementPath, qt3ds::render::IQt3DSRenderer *renderer) = 0; + virtual void createMaterial(const QString &elementPath, const QString &materialDefinition, + qt3ds::render::ICustomMaterialSystem *customMaterialSystem, + qt3ds::render::IDynamicObjectSystem *dynamicObjectSystem, + qt3ds::render::IQt3DSRenderer *renderer) = 0; public: // Components virtual void GotoSlide(const char *component, const char *slideName, diff --git a/src/Runtime/Source/runtime/Qt3DSPresentation.cpp b/src/Runtime/Source/runtime/Qt3DSPresentation.cpp index a4302ad5..ea01878c 100644 --- a/src/Runtime/Source/runtime/Qt3DSPresentation.cpp +++ b/src/Runtime/Source/runtime/Qt3DSPresentation.cpp @@ -28,9 +28,6 @@ ** ****************************************************************************/ -//============================================================================== -// Includes -//============================================================================== #include "Qt3DSPresentation.h" #include "Qt3DSCommandEventTypes.h" #include "Qt3DSIScriptBridge.h" @@ -47,34 +44,28 @@ #include "Qt3DSLogicSystem.h" #include "Qt3DSParametersSystem.h" -//============================================================================== -// Namespace -//============================================================================== +#include + namespace Q3DStudio { -//============================================================================== -// Constants -//============================================================================== -/// Maximum number of Event/Command that can be queued in an Update cycle +// Maximum number of Event/Command that can be queued in an Update cycle const INT32 Q3DStudio_EVENTCOMMANDQUEUECAPACITY = 512; -/// Limit to prevent infinite loop during queue processing +// Limit to prevent infinite loop during queue processing const INT32 Q3DStudio_MAXEVENTCOMMANDLOOPCOUNT = Q3DStudio_EVENTCOMMANDQUEUECAPACITY * 10; #ifdef WIN32 #pragma warning(push) #pragma warning(disable : 4355) #endif -//============================================================================== -/** - * Constructor - */ -CPresentation::CPresentation(const CHAR *inName, qt3ds::runtime::IApplication *inApplication) + +CPresentation::CPresentation(const QString &inName, const QString &projectPath, + qt3ds::runtime::IApplication *inApplication) : m_Name(inName) , m_Application(inApplication) - , m_Scene(NULL) - , m_ActivityZone(NULL) - , m_RootElement(NULL) + , m_Scene(nullptr) + , m_ActivityZone(nullptr) + , m_RootElement(nullptr) , m_EventCommandQueue(Q3DStudio_EVENTCOMMANDQUEUECAPACITY, "EventCommandQueue") , m_IsProcessingEventCommandQueue(false) , m_ComponentManager(*this) @@ -85,6 +76,7 @@ CPresentation::CPresentation(const CHAR *inName, qt3ds::runtime::IApplication *i , m_OffsetInvalid(true) , m_Active(true) { + m_projectPath = QFileInfo(projectPath).absoluteFilePath(); m_Size.m_Width = 0; m_Size.m_Height = 0; m_Size.m_ScaleMode = SCALEMODE_UNKNOWN; @@ -103,15 +95,10 @@ CPresentation::CPresentation(const CHAR *inName, qt3ds::runtime::IApplication *i #pragma warning(pop) #endif -//============================================================================== -/** - * Destructor - */ CPresentation::~CPresentation() { } -//============================================================================== /** * Registers an element for notification when events fired on it. * @param inElement target element to monitor @@ -129,7 +116,6 @@ void CPresentation::RegisterEventCallback(TElement *inElement, const TEventComma inElement->SetFlag(ELEMENTFLAG_PICKENABLED, true); } -//============================================================================== /** * Unregisters a previously registered event callback. * @param inElement target element to monitor @@ -183,7 +169,6 @@ void CPresentation::PreUpdate(const TTimeUnit inGlobalTime) ProcessEventCommandQueue(); } -//============================================================================== /** * Update the presentation to the current time. This will start triggering the * various stages of the presentation frame rhythm @@ -230,7 +215,6 @@ void CPresentation::PostUpdate(const TTimeUnit inGlobalTime) m_PreviousGlobalTime = inGlobalTime; } -//============================================================================== /** * Process the Event/Command queue completely * PostEventCommand is the method that will add new Event/Command to the queue. @@ -288,7 +272,6 @@ BOOL CPresentation::ProcessEventCommandQueue() return theResult; } -//============================================================================== /** * Pass incoming event to event consumers immediately * @param ioEvent the incoming event @@ -318,7 +301,6 @@ void CPresentation::ProcessEvent(SEventCommand &ioEvent, INT32 &ioEventCount) } } -//============================================================================== /** * Handle event bubbling * @param ioEvent the incoming event @@ -330,7 +312,7 @@ void CPresentation::ProcessEventBubbling(SEventCommand &ioEvent, INT32 &ioEventC // Check for onGroupedMouseOver/Out // arg1 = the original onMouseOut model and arg2 = the original onMouseOver model if (ioEvent.m_Type == ON_MOUSEOUT) { - // If original onMouseOver model is NULL or not a descendent, fire onGroupedMouseOut + // If original onMouseOver model is nullptr or not a descendent, fire onGroupedMouseOut TElement *theMouseOverModel = static_cast(ioEvent.m_Arg2.m_VoidPointer); if (!theMouseOverModel || !ioEvent.m_Target->IsDescendent(*theMouseOverModel)) { SEventCommand theEvent = ioEvent; @@ -345,7 +327,7 @@ void CPresentation::ProcessEventBubbling(SEventCommand &ioEvent, INT32 &ioEventC ioEvent.m_Arg2.m_VoidPointer = ioEvent.m_Target; } } else if (ioEvent.m_Type == ON_MOUSEOVER) { - // If original onMouseOut model is NULL or not a descendent, fire onGroupedMouseOver + // If original onMouseOut model is nullptr or not a descendent, fire onGroupedMouseOver TElement *theMouseOutModel = static_cast(ioEvent.m_Arg1.m_VoidPointer); if (!theMouseOutModel || !ioEvent.m_Target->IsDescendent(*theMouseOutModel)) { SEventCommand theEvent = ioEvent; @@ -379,7 +361,6 @@ void CPresentation::ProcessEventBubbling(SEventCommand &ioEvent, INT32 &ioEventC } } -//============================================================================== /** * Execute command immediately * @param inCommand incoming command structure @@ -454,7 +435,6 @@ void CPresentation::ProcessCommand(const SEventCommand &inCommand) } } -//============================================================================== /** * Put an Event in the queue to be processed later during the Event/Command * processing stage in Update @@ -472,7 +452,6 @@ void CPresentation::FireEvent(const SEventCommand &inEvent) theEventCommand.m_IsEvent = true; } -//============================================================================== /** * Put an Event in the queue to be processed later during the Event/Command * processing stage in Update See ProcessEventCommandQueue for more @@ -508,7 +487,6 @@ void CPresentation::FireEvent(const TEventCommandHash inEventType, TElement *inT m_EventCommandQueue.NewEntry() = theEvent; } -//============================================================================== /** * Put a Command in the queue to be processed later during the Event/Command * processing stage in Update. See ProcessEventCommandQueue for more information @@ -564,7 +542,7 @@ void CPresentation::ProcessEvent(SEventCommand &inEvent) INT32 theEventProcessedCount = 0; ProcessEvent(inEvent, theEventProcessedCount); } -//============================================================================== + /** * This method is triggered after the presentation is streamed in. At this point, * all the stores will be loaded up. @@ -574,7 +552,6 @@ void CPresentation::OnPresentationLoaded() m_FrameData.Reserve(1000 /*m_ElementManager.GetElementCount( )*/); } -//============================================================================== /** * Set the full path for this presentation. This can be used by anyone who * knows the presentation to form relative paths. @@ -582,20 +559,26 @@ void CPresentation::OnPresentationLoaded() */ void CPresentation::SetFilePath(const CHAR *inPath) { - m_FilePath = inPath; + m_FilePath = QFileInfo(inPath).absoluteFilePath(); } -//============================================================================== /** * Gets the full file path for this presentation. This can be used by anyone who * knows the presentation to form relative correct paths. */ -QString CPresentation::GetFilePath() +QString CPresentation::GetFilePath() const { return m_FilePath; } -//============================================================================== +/** + * Gets the absolute file path for the project that owns this presentation. + */ +QString CPresentation::getProjectPath() const +{ + return m_projectPath; +} + /** * Gets the pause state * @return true if the presentation is paused, false if otherwise @@ -605,7 +588,6 @@ BOOL CPresentation::GetPause() const return m_Paused; } -//============================================================================== /** * Sets the pause state * @param inPause set true to pause, set false if otherwise @@ -615,7 +597,6 @@ void CPresentation::SetPause(const BOOL inPause) m_Paused = inPause ? true : false; } -//============================================================================== /** * Simple manager access: Scene */ @@ -645,7 +626,6 @@ bool CPresentation::GetActive() const return m_Active; } -//============================================================================== /** * Simple manager access: Scene */ @@ -654,7 +634,6 @@ IScene *CPresentation::GetScene() const return m_Scene; } -//============================================================================== /** * Simple manager access: Script Bridge Qml */ @@ -663,10 +642,9 @@ IScriptBridge *CPresentation::GetScriptBridgeQml() if (m_Application) return &m_Application->GetRuntimeFactoryCore().GetScriptEngineQml(); - return NULL; + return nullptr; } -//============================================================================== /** * Simple manager access: Component Manager */ @@ -675,7 +653,6 @@ IComponentManager &CPresentation::GetComponentManager() return m_ComponentManager; } -//============================================================================== /** * Simple manager access: Slide Manager */ @@ -684,7 +661,6 @@ ISlideSystem &CPresentation::GetSlideSystem() return *m_SlideSystem; } -//============================================================================== /** * Simple manager access: Animation Manager */ @@ -693,7 +669,6 @@ qt3ds::runtime::IAnimationSystem &CPresentation::GetAnimationSystem() return *m_AnimationSystem; } -//============================================================================== /** * Simple manager access: Logic Manager */ @@ -702,7 +677,6 @@ ILogicSystem &CPresentation::GetLogicSystem() return *m_LogicSystem; } -//============================================================================== /** * Simple manager access: Params Manager */ @@ -736,7 +710,6 @@ qt3ds::foundation::IStringTable &CPresentation::GetStringTable() return GetApplication().GetRuntimeFactoryCore().GetStringTable(); } -//============================================================================== /** * Current frame data stores traversal lists for use later */ @@ -750,7 +723,6 @@ void CPresentation::SetLoadedBuffer(qt3ds::render::ILoadedBuffer &inBuffer) m_LoadedBuffer = inBuffer; } -//============================================================================== /** * Retrieve the name of the presentation. This is actually the file path. * @return the name of this presentation @@ -760,7 +732,6 @@ const QByteArray CPresentation::GetName() const return m_Name.toLatin1(); } -//============================================================================== /** * Retrieve the size of the presentation in Studio * @return the size of this presentation @@ -770,7 +741,6 @@ SPresentationSize CPresentation::GetSize() const return m_Size; } -//============================================================================== /** * Set the size of the presentation as reflected in Studio * @param inSize size of presentation ( width, height, scale mode ) in Studio diff --git a/src/Runtime/Source/runtime/Qt3DSPresentation.h b/src/Runtime/Source/runtime/Qt3DSPresentation.h index d813b418..5e3145fd 100644 --- a/src/Runtime/Source/runtime/Qt3DSPresentation.h +++ b/src/Runtime/Source/runtime/Qt3DSPresentation.h @@ -30,9 +30,6 @@ #pragma once -//============================================================================== -// Includes -//============================================================================== #include "RuntimePrefix.h" #include "Qt3DSIPresentation.h" #include "Qt3DSPresentationFrameData.h" @@ -53,6 +50,7 @@ Q_SIGNALS: void SigSlideEntered(const QString &elementPath, unsigned int index, const QString &name); void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name); void SigCustomSignal(const QString &elementPath, const QString &name); + void SigMaterialCreated(const QString &name); }; namespace qt3ds { @@ -68,45 +66,40 @@ namespace render { } } -//============================================================================== -// Namespace -//============================================================================== namespace Q3DStudio { -//============================================================================== + /** * Intelligent representation of a Studio presentation. */ class CPresentation : public IPresentation { - //============================================================================== - // Fields - //============================================================================== protected: - QString m_Name; ///< Name of ths presentation - QString m_FilePath; ///< The full path from which this presentation was loaded - qt3ds::runtime::IApplication *m_Application; ///< Runtime object - IScene *m_Scene; ///< Connection to the associated scene (render) for this presentation - qt3ds::runtime::IActivityZone *m_ActivityZone; ///< Controls element active status. + QString m_Name; // Name of this presentation + QString m_FilePath; // Absolute path to this presentation + QString m_projectPath; // Absolute path to the project root + qt3ds::runtime::IApplication *m_Application; // Runtime object + IScene *m_Scene; // Connection to the associated scene (render) for this presentation + qt3ds::runtime::IActivityZone *m_ActivityZone; // Controls element active status TElement *m_RootElement; - CPresentationFrameData m_FrameData; ///< Storage of data of the current frame - CCircularArray m_EventCommandQueue; ///< The Event/Command integrated queue + CPresentationFrameData m_FrameData; // Storage of data of the current frame + CCircularArray m_EventCommandQueue; // The Event/Command integrated queue bool m_IsProcessingEventCommandQueue; - CEventCallbacks m_EventCallbacks; ///< Handles event callbacks on registered elements - SPresentationSize m_Size; ///< Native width, height and mode exported from Studio + CEventCallbacks m_EventCallbacks; // Handles event callbacks on registered elements + SPresentationSize m_Size; // Native width, height and mode exported from Studio qt3ds::foundation::NVScopedRefCounted - m_LoadedBuffer; ///< Reference to loaded data when loading from binary. + m_LoadedBuffer; // Reference to loaded data when loading from binary CComponentManager m_ComponentManager; qt3ds::foundation::NVScopedRefCounted - m_SlideSystem; ///< Container and factory of all logics + m_SlideSystem; // Container and factory of all slides qt3ds::foundation::NVScopedRefCounted - m_LogicSystem; ///< Container and factory of all logics + m_LogicSystem; // Container and factory of all logics qt3ds::foundation::NVScopedRefCounted - m_AnimationSystem; ///< Container and factory of all animation tracks + m_AnimationSystem; // Container and factory of all animation tracks qt3ds::foundation::NVScopedRefCounted - m_ParametersSystem; ///< Container and factory of all custom actions + m_ParametersSystem; // Container and factory of all custom actions TTimeUnit m_Offset; TTimeUnit m_LocalTime; @@ -118,12 +111,10 @@ protected: typedef eastl::hash_map TElemStringMap; TElemStringMap m_ElementPathMap; - //============================================================================== - // Methods - //============================================================================== public: // Construction - CPresentation(const CHAR *inName, qt3ds::runtime::IApplication *inRuntime); - virtual ~CPresentation(); + CPresentation(const QString &inName, const QString &projectPath, + qt3ds::runtime::IApplication *inRuntime); + virtual ~CPresentation() override; public: // Execution void Initialize(); @@ -153,11 +144,11 @@ public: // Bridge Control public: // Commands and Events void FireEvent(const SEventCommand &inEvent); void FireEvent(const TEventCommandHash inEventType, TElement *inTarget, - const UVariant *inArg1 = NULL, const UVariant *inArg2 = NULL, + const UVariant *inArg1 = nullptr, const UVariant *inArg2 = nullptr, const EAttributeType inType1 = ATTRIBUTETYPE_NONE, const EAttributeType inType2 = ATTRIBUTETYPE_NONE) override; void FireCommand(const TEventCommandHash inCommandType, TElement *inTarget, - const UVariant *inArg1 = NULL, const UVariant *inArg2 = NULL, + const UVariant *inArg1 = nullptr, const UVariant *inArg2 = nullptr, const EAttributeType inType1 = ATTRIBUTETYPE_NONE, const EAttributeType inType2 = ATTRIBUTETYPE_NONE) override; void FlushEventCommandQueue(void) override; @@ -213,9 +204,10 @@ public: // Configuration access void SetUpdateLock(const BOOL inLockUpdate); void SetVCAA(const BOOL inVCAA); -public: // Full file path +public: // Full file paths void SetFilePath(const CHAR *inPath) override; - QString GetFilePath() override; + QString GetFilePath() const override; + QString getProjectPath() const override; private: // Disabled Copy Construction CPresentation(CPresentation &); diff --git a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp index e4aed96e..4825c7ef 100644 --- a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp +++ b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp @@ -70,6 +70,13 @@ #include "Qt3DSRenderRuntimeBindingImpl.h" #include "Qt3DSRenderBufferManager.h" // TODO: Needed for adding meshes dynamically (QT3DS-3378) #include "Qt3DSRenderer.h" +#include "q3dsmaterialdefinitionparser.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderUIPLoader.h" +#include "Qt3DSDMMetaData.h" +#include "Qt3DSRenderUIPSharedTranslation.h" #include #include @@ -357,8 +364,10 @@ struct SEmitSignalData : public NVRefCounted class CQmlEngineImpl : public CQmlEngine { - typedef NVScopedRefCounted TEmitSignalPtr; - typedef eastl::list TEmitSignalQueue; + using TEmitSignalPtr = NVScopedRefCounted; + using TEmitSignalQueue = eastl::list; + using TPropertyDescAndValueList = eastl::vector; + using TPropertyDesc = qt3ds::runtime::element::SPropertyDesc; static const int MAX_ACTION_QUEUE_SIZE = 100; @@ -423,7 +432,10 @@ public: qt3ds::render::IQt3DSRenderer *renderer) override; void deleteElement(const QString &elementPath, qt3ds::render::IQt3DSRenderer *renderer) override; - //void createMaterial() override; // TODO (QT3DS-3377) + void createMaterial(const QString &elementPath, const QString &materialDefinition, + qt3ds::render::ICustomMaterialSystem *customMaterialSystem, + IDynamicObjectSystem *dynamicObjectSystem, + qt3ds::render::IQt3DSRenderer *renderer) override; //void createMesh() override; // TODO (QT3DS-3378) void GotoSlide(const char *component, const char *slideName, @@ -473,6 +485,39 @@ private: // find out which datainputs are used in the expression QVector resolveDependentDatainputs(const QString &expression, const QString &controllerName); + + // Methods to add element attributes to list for element creation + void addStringAttribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QString &inAttName, const QString &inValue); + void addIntAttribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QString &inAttName, int inValue); + void addBoolAttribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QString &inAttName, bool inValue); + void addFloatAttribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QString &inAttName, float inValue); + void addFloat2Attribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QStringList &inAttNames, const QVector2D &inValue); + void addFloat3Attribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QStringList &inAttNames, const QVector3D &inValue); + void addFloat4Attribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QStringList &inAttNames, const QVector4D &inValue); + void addElementRefAttribute(qt3ds::foundation::IStringTable &strTable, + TPropertyDescAndValueList &list, + const QString &inAttName, TElement *element); + template + void setDynamicObjectProperty(qt3ds::render::SDynamicObject &material, + const qt3ds::render::dynamic::SPropertyDefinition &propDesc, + const TDataType &propValue); + QVector2D parseFloat2Property(const QString &propValue); + QVector3D parseFloat3Property(const QString &propValue); + QVector4D parseFloat4Property(const QString &propValue); }; CQmlEngineImpl::CQmlEngineImpl(NVFoundationBase &fnd, ITimeProvider &) @@ -868,8 +913,7 @@ void CQmlEngineImpl::SetDataInputValue( } } -using TPropertyDescAndValueList = eastl::vector; -using TPropertyDesc = qt3ds::runtime::element::SPropertyDesc; +static int _idCounter = 0; void CQmlEngineImpl::createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties, @@ -898,8 +942,7 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri IPresentation *presentation = parentElement->GetBelongedPresentation(); - static int idCounter = 0; - ++idCounter; + ++_idCounter; // Resolve slide QByteArray theSlideName = slideName.toUtf8(); @@ -921,7 +964,7 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri refMatName = QStringLiteral("/") + refMatName; if (newElementName.isEmpty()) - newElementName = QStringLiteral("NewElement_%1").arg(idCounter); + newElementName = QStringLiteral("NewElement_%1").arg(_idCounter); QByteArray newElementNameBa = newElementName.toUtf8(); // Make sure the name is not duplicate @@ -941,52 +984,6 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri const CRegisteredString elementSubType; TPropertyDescAndValueList elementProperties; - // Set properties - auto addStringAttribute = [&strTable](TPropertyDescAndValueList &list, - const QString &inAttName, const QString &inValue) { - QByteArray valueBa = inValue.toUtf8(); - qt3ds::foundation::CStringHandle strHandle = strTable.GetHandle(valueBa.constData()); - UVariant theValue; - theValue.m_StringHandle = strHandle.handle(); - const CRegisteredString attStr = strTable.RegisterStr(inAttName); - list.push_back( - eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_STRING), theValue)); - }; - auto addIntAttribute = [&strTable](TPropertyDescAndValueList &list, const QString &inAttName, - int inValue) { - UVariant theValue; - theValue.m_INT32 = inValue; - const CRegisteredString attStr = strTable.RegisterStr(inAttName); - list.push_back( - eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_INT32), theValue)); - }; - auto addBoolAttribute = [&strTable](TPropertyDescAndValueList &list, - const QString &inAttName, bool inValue) { - UVariant theValue; - theValue.m_INT32 = int(inValue); - const CRegisteredString attStr = strTable.RegisterStr(inAttName); - list.push_back( - eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_BOOL), theValue)); - }; - auto addFloatAttribute = [&strTable](TPropertyDescAndValueList &list, const QString &inAttName, - float inValue) { - UVariant theValue; - theValue.m_FLOAT = inValue; - const CRegisteredString attStr = strTable.RegisterStr(inAttName); - list.push_back( - eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue)); - }; - auto addFloat3Attribute = [&strTable](TPropertyDescAndValueList &list, - const QStringList &inAttNames, const QVector3D &inValue) { - for (int i = 0; i < 3; ++i) { - UVariant theValue; - theValue.m_FLOAT = inValue[i]; - const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i)); - list.push_back( - eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue)); - } - }; - // Set default values for missing mandatory properties const QString sourcePathPropName = QStringLiteral("sourcepath"); const QString startTimePropName = QStringLiteral("starttime"); @@ -995,18 +992,20 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri Q3DStudio::UVariant attValue; bool eyeBall = true; theProperties.value(QStringLiteral("eyeball"), true).toBool(); - if (!theProperties.contains(sourcePathPropName)) - addStringAttribute(elementProperties, sourcePathPropName, QStringLiteral("#Cube")); + if (!theProperties.contains(sourcePathPropName)) { + addStringAttribute(strTable, elementProperties, sourcePathPropName, + QStringLiteral("#Cube")); + } if (!theProperties.contains(startTimePropName)) { parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_STARTTIME, attValue); - addIntAttribute(elementProperties, startTimePropName, int(attValue.m_INT32)); + addIntAttribute(strTable, elementProperties, startTimePropName, int(attValue.m_INT32)); } if (!theProperties.contains(endTimePropName)) { parentElement->GetAttribute(Q3DStudio::ATTRIBUTE_ENDTIME, attValue); - addIntAttribute(elementProperties, endTimePropName, int(attValue.m_INT32)); + addIntAttribute(strTable, elementProperties, endTimePropName, int(attValue.m_INT32)); } if (!theProperties.contains(eyeBallPropName)) - addBoolAttribute(elementProperties, eyeBallPropName, true); + addBoolAttribute(strTable, elementProperties, eyeBallPropName, true); else eyeBall = theProperties.value(QStringLiteral("eyeball")).toBool(); @@ -1015,16 +1014,16 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri it.next(); switch (it.value().type()) { case QVariant::Double: - addFloatAttribute(elementProperties, it.key(), it.value().toFloat()); + addFloatAttribute(strTable, elementProperties, it.key(), it.value().toFloat()); break; case QVariant::Bool: - addBoolAttribute(elementProperties, it.key(), it.value().toBool()); + addBoolAttribute(strTable, elementProperties, it.key(), it.value().toBool()); break; case QVariant::Int: - addIntAttribute(elementProperties, it.key(), it.value().toInt()); + addIntAttribute(strTable, elementProperties, it.key(), it.value().toInt()); break; case QVariant::String: - addStringAttribute(elementProperties, it.key(), it.value().toString()); + addStringAttribute(strTable, elementProperties, it.key(), it.value().toString()); break; case QVariant::Vector3D: { QVector3D vec = it.value().value(); @@ -1039,7 +1038,7 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri atts << (it.key() + QLatin1String(".x")) << (it.key() + QLatin1String(".y")) << (it.key() + QLatin1String(".z")); - addFloat3Attribute(elementProperties, atts, vec); + addFloat3Attribute(strTable, elementProperties, atts, vec); break; } default: @@ -1131,7 +1130,7 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri NVAllocatorCallback &allocator = presentation->GetScene()->allocator(); qt3ds::render::SModel *newObject = QT3DS_NEW(allocator, qt3ds::render::SModel)(); newObject->m_Id = strTable.RegisterStr((QByteArrayLiteral("_newObject_") - + QByteArray::number(idCounter)).constData()); + + QByteArray::number(_idCounter)).constData()); parentObject.AddChild(*newObject); qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newElem, *newObject, allocator); @@ -1140,7 +1139,7 @@ void CQmlEngineImpl::createElement(const QString &parentElementPath, const QStri qt3ds::render::SReferencedMaterial *newMaterial = QT3DS_NEW(allocator, qt3ds::render::SReferencedMaterial)(); newMaterial->m_Id = strTable.RegisterStr((QByteArrayLiteral("_newMaterial_") - + QByteArray::number(idCounter)).constData()); + + QByteArray::number(_idCounter)).constData()); newMaterial->m_ReferencedMaterial = referencedMaterial; newObject->AddMaterial(*newMaterial); @@ -1236,6 +1235,336 @@ void CQmlEngineImpl::deleteElement(const QString &elementPath, m_Application->GetElementAllocator().ReleaseElement(*element, true); } +/** + Creates material into material container of the presentation that owns the specified element. + The materialDefinition parameter can contain a .materialdef file path or + the entire material definition in the .materialdef format. +*/ +void CQmlEngineImpl::createMaterial(const QString &elementPath, + const QString &materialDefinition, + ICustomMaterialSystem *customMaterialSystem, + IDynamicObjectSystem *dynamicObjectSystem, + qt3ds::render::IQt3DSRenderer *renderer) +{ + QByteArray thePath = elementPath.toUtf8(); + TElement *element = getTarget(thePath.constData()); + + if (!element) { + qWarning() << __FUNCTION__ << "Invalid element:" << elementPath; + return; + } + + CPresentation *presentation = static_cast(element->GetBelongedPresentation()); + QString presPath = QFileInfo(presentation->GetFilePath()).absolutePath(); + QString projPath = presentation->getProjectPath(); + + QString materialName; + QMap materialProps; + QMap> textureProps; + + Q3DSMaterialDefinitionParser::getMaterialInfo(materialDefinition, + projPath, presPath, + materialName, materialProps, textureProps); + + // We don't care about the path parameter + const QString pathStr = QStringLiteral("path"); + if (materialProps.contains(pathStr)) + materialProps.remove(pathStr); + + // Find material container + auto &strTable = presentation->GetStringTable(); + NVAllocatorCallback &allocator = presentation->GetScene()->allocator(); + TElement *rootElement = presentation->GetRoot(); + const auto containerName = strTable.RegisterStr("__Container"); + TElement *container = rootElement->FindChild(CHash::HashString(containerName.c_str())); + if (container) { + // Check that the material doesn't already exist in container + TElement *firstChild = nullptr; + TElement *nextChild = container->GetChild(); + firstChild = nextChild; + while (nextChild) { + QString childName = QString::fromUtf8(nextChild->m_Name); + if (childName == materialName) { + qWarning() << __FUNCTION__ << "Material already exists in material container"; + return; + } + nextChild = nextChild->GetSibling(); + } + } else { + // TODO: Create a material container if it doesn't exist (QT3DS-3412) + qWarning() << __FUNCTION__ << "Presentation has no material container"; + return; + } + + // Create material element in the container based on the material definition + auto &metaData = m_Application->GetMetaData(); + const auto matName = strTable.RegisterStr(materialName); + const bool isCustomMaterial + = materialProps.value(QStringLiteral("type")) == QLatin1String("CustomMaterial"); + CRegisteredString matType; + CRegisteredString matClass; + QHash dynPropDefs; + + if (isCustomMaterial) { + CRegisteredString sourcePath + = strTable.RegisterStr(materialProps.value(QStringLiteral("sourcepath")).toUtf8()); + matType = strTable.RegisterStr("CustomMaterial"); + matClass = sourcePath; // Create just one class per shader + if (sourcePath.IsValid()) { + Option matMetaData = + metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str()); + if (!matMetaData.hasValue()) { + metaData.LoadMaterialXMLFile(matType.c_str(), matClass.c_str(), matName.c_str(), + sourcePath.c_str()); + matMetaData = metaData.GetMaterialMetaDataBySourcePath(sourcePath.c_str()); + } + if (matMetaData.hasValue()) { + qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( + matClass, m_Foundation, *customMaterialSystem, matMetaData, + strTable); + NVConstDataRef customProperties; + customProperties = dynamicObjectSystem->GetProperties(matClass); + for (QT3DSU32 i = 0, end = customProperties.size(); i < end; ++i) { + QString propName = QString::fromUtf8(customProperties[i].m_Name.c_str()); + if (materialProps.contains(propName)) + dynPropDefs.insert(propName, customProperties[i]); + } + } else { + qWarning() << __FUNCTION__ + << "Could not resolve material properties for CustomMaterial:" + << materialName; + } + } else { + qWarning() << __FUNCTION__ + << "Missing sourcepath from material definition of a CustomMaterial:" + << materialName; + } + } else { + matType = strTable.RegisterStr("Material"); + } + + auto createElementPropsFromDefProps = [&](const QMap &defProps, + TPropertyDescAndValueList &elementProps, + const CRegisteredString &elementType) -> bool { + QMapIterator propIter(defProps); + while (propIter.hasNext()) { + propIter.next(); + if (dynPropDefs.contains(propIter.key())) + continue; // Dynamic properties are added directly to graph objects later + + auto propName = strTable.RegisterStr(propIter.key()); + ERuntimeDataModelDataType dataType; + ERuntimeAdditionalMetaDataType additionalType; + dataType = metaData.GetPropertyType(elementType, propName); + additionalType = metaData.GetAdditionalType(elementType, propName); + + switch (dataType) { + case ERuntimeDataModelDataTypeLong: { + addIntAttribute(strTable, elementProps, propIter.key(), + propIter.value().toInt()); + break; + } + case ERuntimeDataModelDataTypeFloat: { + addFloatAttribute(strTable, elementProps, propIter.key(), + propIter.value().toFloat()); + break; + } + case ERuntimeDataModelDataTypeFloat2: { + QVector2D vec = parseFloat2Property(propIter.value()); + QStringList atts; + atts << (propIter.key() + QLatin1String(".x")) + << (propIter.key() + QLatin1String(".y")); + addFloat2Attribute(strTable, elementProps, atts, vec); + break; + } + case ERuntimeDataModelDataTypeFloat3: { + QVector3D vec = parseFloat3Property(propIter.value()); + if (additionalType == ERuntimeAdditionalMetaDataTypeRotation) { + vec.setX(qDegreesToRadians(vec.x())); + vec.setY(qDegreesToRadians(vec.y())); + vec.setZ(qDegreesToRadians(vec.z())); + } + QStringList atts; + atts << (propIter.key() + QLatin1String(".x")) + << (propIter.key() + QLatin1String(".y")) + << (propIter.key() + QLatin1String(".z")); + addFloat3Attribute(strTable, elementProps, atts, vec); + break; + } + case ERuntimeDataModelDataTypeFloat4: { + QVector4D vec = parseFloat4Property(propIter.value()); + QStringList atts; + if (additionalType == ERuntimeAdditionalMetaDataTypeColor) { + atts << (propIter.key() + QLatin1String(".r")) + << (propIter.key() + QLatin1String(".g")) + << (propIter.key() + QLatin1String(".b")) + << (propIter.key() + QLatin1String(".a")); + } else { + atts << (propIter.key() + QLatin1String(".x")) + << (propIter.key() + QLatin1String(".y")) + << (propIter.key() + QLatin1String(".z")) + << (propIter.key() + QLatin1String(".w")); + } + addFloat4Attribute(strTable, elementProps, atts, vec); + break; + } + case ERuntimeDataModelDataTypeBool: { + bool boolValue = propIter.value().compare(QLatin1String("true"), + Qt::CaseInsensitive) == 0; + addBoolAttribute(strTable, elementProps, propIter.key(), boolValue); + break; + } + case ERuntimeDataModelDataTypeStringRef: + case ERuntimeDataModelDataTypeString: { + addStringAttribute(strTable, elementProps, propIter.key(), propIter.value()); + break; + } + case ERuntimeDataModelDataTypeLong4: { + if (additionalType == ERuntimeAdditionalMetaDataTypeImage) { + // Insert placeholder for now, will be patched later + addElementRefAttribute(strTable, elementProps, propIter.key(), nullptr); + } + break; + } + default: + qWarning() << __FUNCTION__ + << "Unsupported material property type for property:" + << propIter.key(); + QT3DS_ASSERT(false); + break; + } + } + return true; + }; + + TPropertyDescAndValueList elementProps; + bool success = createElementPropsFromDefProps(materialProps, elementProps, matType); + if (!success) + return; // createElementPropsFromDefProps already prints a warning + + TElement &newMatElem = m_Application->GetElementAllocator().CreateElement( + matName, matType, matClass, + toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())), + presentation, container, false); + newMatElem.SetActive(true); + + // Create image elements + CRegisteredString imageType = strTable.RegisterStr("Image"); + QMapIterator> texIter(textureProps); + QHash imageElementMap; + while (texIter.hasNext()) { + texIter.next(); + elementProps.clear(); + success = createElementPropsFromDefProps(texIter.value(), elementProps, imageType); + if (!success) { + m_Application->GetElementAllocator().ReleaseElement(newMatElem, true); + return; // createElementPropsFromDefProps already prints a warning + } + CRegisteredString imageName = strTable.RegisterStr(texIter.key()); + + TElement &newImageElem = m_Application->GetElementAllocator().CreateElement( + imageName, imageType, CRegisteredString(), + toConstDataRef(elementProps.data(), QT3DSU32(elementProps.size())), + presentation, &newMatElem, false); + imageElementMap.insert(texIter.key(), &newImageElem); + newImageElem.SetActive(true); + } + + // Create render object for the material + qt3ds::render::SGraphObject *newMaterial = nullptr; + CRegisteredString newMatId = strTable.RegisterStr( + (QByteArrayLiteral("_newMaterial_") + QByteArray::number(++_idCounter)) + .constData()); + if (isCustomMaterial) { + newMaterial = customMaterialSystem->CreateCustomMaterial(matClass, allocator); + newMaterial->m_Id = newMatId; + auto dynObj = static_cast(newMaterial); + + QHashIterator + dynPropIter(dynPropDefs); + while (dynPropIter.hasNext()) { + dynPropIter.next(); + QByteArray propValStr = materialProps.value(dynPropIter.key()).toUtf8(); + const auto propDesc = dynPropIter.value(); + switch (propDesc.m_DataType) { + case qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool: { + bool boolValue = propValStr.compare(QByteArrayLiteral("true"), + Qt::CaseInsensitive) == 0; + setDynamicObjectProperty(*dynObj, propDesc, boolValue); + break; + } + case qt3ds::render::NVRenderShaderDataTypes::QT3DSF32: + setDynamicObjectProperty(*dynObj, propDesc, propValStr.toFloat()); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSI32: + if (!propDesc.m_IsEnumProperty) { + setDynamicObjectProperty(*dynObj, propDesc, propValStr.toInt()); + } else { + const NVConstDataRef &enumNames = propDesc.m_EnumValueNames; + for (QT3DSU32 i = 0, end = enumNames.size(); i < end; ++i) { + if (propValStr.compare(enumNames[i].c_str()) == 0) { + setDynamicObjectProperty(*dynObj, propDesc, i); + break; + } + } + } + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2: + setDynamicObjectProperty(*dynObj, propDesc, parseFloat2Property(propValStr)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3: + setDynamicObjectProperty(*dynObj, propDesc, parseFloat3Property(propValStr)); + break; + case qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4: + setDynamicObjectProperty(*dynObj, propDesc, parseFloat4Property(propValStr)); + break; + case qt3ds::render::NVRenderShaderDataTypes::NVRenderTexture2DPtr: + case qt3ds::render::NVRenderShaderDataTypes::NVRenderImage2DPtr: { + CRegisteredString regStr; + regStr = strTable.RegisterStr(propValStr); + setDynamicObjectProperty(*dynObj, propDesc, regStr); + break; + } + default: + qWarning() << __FUNCTION__ + << "Unsupported custom material property type '" << propDesc.m_DataType + << "' for property:" + << dynPropIter.key(); + QT3DS_ASSERT(false); + break; + } + } + } else { + newMaterial = QT3DS_NEW(allocator, qt3ds::render::SDefaultMaterial)(); + newMaterial->m_Id = newMatId; + + // Update element refs for image elements and create graph objects & translators + QHashIterator imageIter(imageElementMap); + while (imageIter.hasNext()) { + imageIter.next(); + TElement *imageElem = imageIter.value(); + UVariant *propValue = newMatElem.FindPropertyValue(imageElem->m_Name); + if (propValue) { + propValue->m_ElementHandle = imageIter.value()->GetHandle(); + + qt3ds::render::SImage *newImageObj = QT3DS_NEW(allocator, qt3ds::render::SImage)(); + newImageObj->m_Id + = strTable.RegisterStr((QByteArrayLiteral("_newImage_") + + QByteArray::number(++_idCounter)).constData()); + qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(*imageElem, + *newImageObj, allocator); + } + } + } + + qt3ds::render::Qt3DSTranslator::CreateTranslatorForElement(newMatElem, *newMaterial, allocator); + + // Notify presentation asynchronously to give renderer time to initialize the materials properly + QTimer::singleShot(0, [materialName, presentation]() { + presentation->signalProxy()->SigMaterialCreated(materialName); + }); +} + void CQmlEngineImpl::GotoSlide(const char *component, const char *slideName, const SScriptEngineGotoSlideArgs &inArgs) { @@ -1777,6 +2106,134 @@ QVector CQmlEngineImpl::resolveDependentDatainputs(const QString &expre return ret; } +void CQmlEngineImpl::addStringAttribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QString &inAttName, const QString &inValue) +{ + QByteArray valueBa = inValue.toUtf8(); + qt3ds::foundation::CStringHandle strHandle = strTable.GetHandle(valueBa.constData()); + UVariant theValue; + theValue.m_StringHandle = strHandle.handle(); + const CRegisteredString attStr = strTable.RegisterStr(inAttName); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_STRING), theValue)); +} + +void CQmlEngineImpl::addIntAttribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QString &inAttName, int inValue) +{ + UVariant theValue; + theValue.m_INT32 = inValue; + const CRegisteredString attStr = strTable.RegisterStr(inAttName); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_INT32), theValue)); +} + +void CQmlEngineImpl::addBoolAttribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QString &inAttName, bool inValue) +{ + UVariant theValue; + theValue.m_INT32 = int(inValue); + const CRegisteredString attStr = strTable.RegisterStr(inAttName); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_BOOL), theValue)); +} + +void CQmlEngineImpl::addFloatAttribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QString &inAttName, float inValue) +{ + UVariant theValue; + theValue.m_FLOAT = inValue; + const CRegisteredString attStr = strTable.RegisterStr(inAttName); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue)); +} + +void CQmlEngineImpl::addFloat2Attribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QStringList &inAttNames, const QVector2D &inValue) +{ + for (int i = 0; i < 2; ++i) { + UVariant theValue; + theValue.m_FLOAT = inValue[i]; + const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i)); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue)); + } +} + +void CQmlEngineImpl::addFloat3Attribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QStringList &inAttNames, const QVector3D &inValue) +{ + for (int i = 0; i < 3; ++i) { + UVariant theValue; + theValue.m_FLOAT = inValue[i]; + const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i)); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue)); + } +} + +void CQmlEngineImpl::addFloat4Attribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QStringList &inAttNames, const QVector4D &inValue) +{ + for (int i = 0; i < 4; ++i) { + UVariant theValue; + theValue.m_FLOAT = inValue[i]; + const CRegisteredString attStr = strTable.RegisterStr(inAttNames.at(i)); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_FLOAT), theValue)); + } +} + +void CQmlEngineImpl::addElementRefAttribute(IStringTable &strTable, + CQmlEngineImpl::TPropertyDescAndValueList &list, + const QString &inAttName, TElement *element) +{ + UVariant theValue; + if (element) { + theValue.m_ElementHandle = element->GetHandle(); + QT3DS_ASSERT(theValue.m_ElementHandle); + } else { + theValue.m_ElementHandle = 0; + } + const CRegisteredString attStr = strTable.RegisterStr(inAttName); + list.push_back(eastl::make_pair(TPropertyDesc(attStr, ATTRIBUTETYPE_ELEMENTREF), theValue)); +} + +QVector2D CQmlEngineImpl::parseFloat2Property(const QString &propValue) +{ + QVector values = propValue.splitRef(QLatin1Char(' ')); + QVector2D retVal; + for (int i = 0; i < values.size() && i < 2; ++i) + retVal[i] = values[i].toFloat(); + return retVal; +} + +QVector3D CQmlEngineImpl::parseFloat3Property(const QString &propValue) +{ + QVector values = propValue.splitRef(QLatin1Char(' ')); + QVector3D retVal; + for (int i = 0; i < values.size() && i < 3; ++i) + retVal[i] = values[i].toFloat(); + return retVal; +} + +QVector4D CQmlEngineImpl::parseFloat4Property(const QString &propValue) +{ + QVector values = propValue.splitRef(QLatin1Char(' ')); + QVector4D retVal; + for (int i = 0; i < values.size() && i < 4; ++i) + retVal[i] = values[i].toFloat(); + return retVal; +} + +template +void CQmlEngineImpl::setDynamicObjectProperty(qt3ds::render::SDynamicObject &material, + const dynamic::SPropertyDefinition &propDesc, + const TDataType &propValue) +{ + memCopy(material.GetDataSectionBegin() + propDesc.m_Offset, &propValue, sizeof(TDataType)); +} + /** * @brief Create QML engine * @@ -1789,4 +2246,5 @@ CQmlEngine *CQmlEngine::Create(qt3ds::NVFoundationBase &inFoundation, ITimeProvi { return QT3DS_NEW(inFoundation.getAllocator(), CQmlEngineImpl)(inFoundation, inTimeProvider); } + } diff --git a/src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.cpp b/src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.cpp new file mode 100644 index 00000000..913a24ec --- /dev/null +++ b/src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dsmaterialdefinitionparser.h" + +#include +#include +#include +#include +#include + +namespace Q3DStudio { + +void Q3DSMaterialDefinitionParser::getMaterialInfo(const QString &materialDefinition, + const QString &projPath, const QString &presPath, + QString &outName, QMap &outValues, + QMap> &outTextureValues) +{ + // materialDefinition can be the .materialdef file name or its content + QString matDef; + if (materialDefinition.endsWith(QLatin1String(".materialdef"), Qt::CaseInsensitive)) { + QFile file(materialDefinition); + if (!file.open(QFile::Text | QFile::ReadOnly)) { + qWarning() << __FUNCTION__ << "Failed to open material definition file:" + << file.errorString(); + return; + } + QTextStream stream(&file); + matDef = stream.readAll(); + } else { + matDef = materialDefinition; + } + + QXmlStreamReader reader(matDef); + reader.setNamespaceProcessing(false); + + const QDir docDir(presPath); + const QDir projDir(projPath); + QString textureName; + QString propertyName; + QString propertyType; + QString propertyValue; + QMap textureProperties; + + const QString matDataVersion = QStringLiteral("1.0"); + const QString sourcePathStr = QStringLiteral("sourcepath"); + const QString nameStr = QStringLiteral("name"); + const QString typeStr = QStringLiteral("type"); + const QString versionStr = QStringLiteral("version"); + + while (!reader.atEnd()) { + reader.readNext(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("TextureData")) { + textureName = reader.attributes().value(nameStr).toString(); + textureProperties.clear(); + } else if (reader.name() == QLatin1String("Property")) { + propertyName = reader.attributes().value(nameStr).toString(); + propertyType = reader.attributes().value(typeStr).toString(); + propertyValue.clear(); + } else if (reader.name() == QLatin1String("MaterialData")) { + QString version = reader.attributes().value(versionStr).toString(); + if (version != matDataVersion) { + qWarning() << __FUNCTION__ << "Invalid MaterialData version; expected '" + << matDataVersion << "', got '" << version << "'"; + outName.clear(); + outValues.clear(); + outTextureValues.clear(); + return; + } + } + } else if (reader.tokenType() == QXmlStreamReader::EndElement) { + if (reader.name() == QLatin1String("TextureData")) { + outTextureValues[textureName] = textureProperties; + textureName.clear(); + } else if (reader.name() == QLatin1String("Property")) { + if (textureName.isEmpty()) { + outValues[propertyName] = propertyValue; + if (propertyName == nameStr) + outName = propertyValue; + } else { + textureProperties[propertyName] = propertyValue; + } + propertyName.clear(); + propertyType.clear(); + } + } else if (reader.tokenType() == QXmlStreamReader::Characters) { + if (!propertyName.isEmpty()) { + propertyValue = reader.text().toString(); + if (propertyName == sourcePathStr) { + // Check that the referenced file exists + const auto absSourcePath = projDir.absoluteFilePath(propertyValue); + if (!QFileInfo(absSourcePath).exists()) { + qWarning() << __FUNCTION__ << "Referenced file doesn't exist:" + << propertyValue; + outName.clear(); + outValues.clear(); + outTextureValues.clear(); + return; + } + } + if (!propertyValue.isEmpty() && (propertyType == QLatin1String("Texture") + || propertyName == sourcePathStr)) { + propertyValue = docDir.relativeFilePath( + projDir.absoluteFilePath(propertyValue)); + } + } + } + } +} + +} diff --git a/src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.h b/src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.h new file mode 100644 index 00000000..f6d98bbf --- /dev/null +++ b/src/Runtime/Source/runtime/q3dsmaterialdefinitionparser.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DSMATERIALDEFINITIONPARSER_H +#define Q3DSMATERIALDEFINITIONPARSER_H + +#include +#include + +namespace Q3DStudio { + +class Q3DSMaterialDefinitionParser +{ +public: + static void getMaterialInfo(const QString &materialDefinition, + const QString &projPath, const QString &presPath, + QString &outName, QMap &outValues, + QMap> &outTextureValues); +}; + +} + +#endif diff --git a/src/Runtime/Source/uipparser/Qt3DSIPresentation.h b/src/Runtime/Source/uipparser/Qt3DSIPresentation.h index 00f3d15a..a9d36446 100644 --- a/src/Runtime/Source/uipparser/Qt3DSIPresentation.h +++ b/src/Runtime/Source/uipparser/Qt3DSIPresentation.h @@ -30,9 +30,6 @@ #pragma once -//============================================================================== -// Includes -//============================================================================== #include "Qt3DSEventCallbacks.h" namespace qt3ds { @@ -52,14 +49,8 @@ namespace foundation { } } -//============================================================================== -// Namespace -//============================================================================== namespace Q3DStudio { -//============================================================================== -// Forwards -//============================================================================== class IComponentManager; class IEventManager; class IScene; @@ -71,20 +62,14 @@ using qt3ds::runtime::IAnimationSystem; using qt3ds::runtime::ILogicSystem; using qt3ds::runtime::IParametersSystem; -//============================================================================== -// Enumeration -//============================================================================== -/// Method used to translate from presentation size to window size +// Method used to translate from presentation size to window size enum EScaleMode { - SCALEMODE_FREE, ///< Free scaling mode - SCALEMODE_EXACT, ///< Fixed presentation size - SCALEMODE_ASPECT, ///< Maintain aspect ratio - SCALEMODE_UNKNOWN, ///< ERROR! - Uninitialized scale mode + SCALEMODE_FREE, // Free scaling mode + SCALEMODE_EXACT, // Fixed presentation size + SCALEMODE_ASPECT, // Maintain aspect ratio + SCALEMODE_UNKNOWN, // ERROR! - Uninitialized scale mode }; -//============================================================================== -// Structs -//============================================================================== struct SPresentationSize { SPresentationSize() @@ -93,22 +78,18 @@ struct SPresentationSize , m_ScaleMode(0) { } - UINT16 m_Width; ///< Native width of the presentation - UINT16 m_Height; ///< Native height of the presentation - UINT8 m_ScaleMode; ///< Presentation to window scale method + UINT16 m_Width; // Native width of the presentation + UINT16 m_Height; // Native height of the presentation + UINT8 m_ScaleMode; // Presentation to window scale method UINT8 m_Padding[3]; }; -//============================================================================== /** * @interface IPresentation * Base interface of the presentation. */ class IPresentation { - //============================================================================== - // Methods - //============================================================================== public: // Construction IPresentation() {} virtual ~IPresentation() {} @@ -127,11 +108,11 @@ public: // Bridge Control public: // Commands and Events virtual void FireEvent(const TEventCommandHash inEventType, TElement *inTarget, - const UVariant *inArg1 = NULL, const UVariant *inArg2 = NULL, + const UVariant *inArg1 = nullptr, const UVariant *inArg2 = nullptr, const EAttributeType inType1 = ATTRIBUTETYPE_NONE, const EAttributeType inType2 = ATTRIBUTETYPE_NONE) = 0; virtual void FireCommand(const TEventCommandHash inEventType, TElement *inTarget, - const UVariant *inArg1 = NULL, const UVariant *inArg2 = NULL, + const UVariant *inArg1 = nullptr, const UVariant *inArg2 = nullptr, const EAttributeType inType1 = ATTRIBUTETYPE_NONE, const EAttributeType inType2 = ATTRIBUTETYPE_NONE) = 0; virtual void FlushEventCommandQueue(void) = 0; @@ -153,7 +134,8 @@ public: // Hooks and callbacks public: // Full file path virtual void SetFilePath(const CHAR *inPath) = 0; - virtual QString GetFilePath() = 0; + virtual QString GetFilePath() const = 0; + virtual QString getProjectPath() const = 0; virtual TElement *GetRoot() = 0; virtual void SetRoot(TElement &inRoot) = 0; diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp index 8bf7eb80..fadd680c 100644 --- a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp +++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp @@ -357,6 +357,8 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor &QRuntimeViewSignalProxy::SigSlideExited, this, &Q3DSViewerApp::SigSlideExited); connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigCustomSignal, this, &Q3DSViewerApp::SigCustomSignal); + connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigMaterialCreated, this, + &Q3DSViewerApp::SigMaterialCreated); Resize(winWidth, winHeight); @@ -811,6 +813,14 @@ void Q3DSViewerApp::deleteElement(const QString &elementPath) m_Impl.m_view->deleteElement(elementPath); } +void Q3DSViewerApp::createMaterial(const QString &elementPath, const QString &materialDefinition) +{ + if (!m_Impl.m_view) + return; + + m_Impl.m_view->createMaterial(elementPath, materialDefinition); +} + Q3DSViewerApp &Q3DSViewerApp::Create(void *glContext, Q3DStudio::IAudioPlayer *inAudioPlayer, QElapsedTimer *startupTimer) { diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.h b/src/Runtime/Source/viewer/Qt3DSViewerApp.h index d2d9b93e..5cd52bca 100644 --- a/src/Runtime/Source/viewer/Qt3DSViewerApp.h +++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.h @@ -362,6 +362,7 @@ public: void createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties); void deleteElement(const QString &elementPath); + void createMaterial(const QString &elementPath, const QString &materialDefinition); QString error(); @@ -403,6 +404,7 @@ Q_SIGNALS: void SigSlideEntered(const QString &elementPath, unsigned int index, const QString &name); void SigSlideExited(const QString &elementPath, unsigned int index, const QString &name); void SigCustomSignal(const QString &elementPath, const QString &name); + void SigMaterialCreated(const QString &name); void SigPresentationReady(); }; diff --git a/src/Viewer/qmlviewer/Qt3DSRenderer.cpp b/src/Viewer/qmlviewer/Qt3DSRenderer.cpp index 73e2f0f8..4122a645 100644 --- a/src/Viewer/qmlviewer/Qt3DSRenderer.cpp +++ b/src/Viewer/qmlviewer/Qt3DSRenderer.cpp @@ -207,6 +207,10 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo) this, &Q3DSRenderer::enterSlide); connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideExited, this, &Q3DSRenderer::exitSlide); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, + this, &Q3DSRenderer::customSignalEmitted); + connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMaterialCreated, + this, &Q3DSRenderer::materialCreated); return true; } @@ -342,6 +346,10 @@ void Q3DSRenderer::processCommands() m_runtime->deleteElement(cmd.m_elementPath); break; } + case CommandType_CreateMaterial: { + m_runtime->createMaterial(cmd.m_elementPath, cmd.m_stringValue); + break; + } case CommandType_RequestSlideInfo: { int current = 0; int previous = 0; diff --git a/src/Viewer/qmlviewer/Qt3DSRenderer.h b/src/Viewer/qmlviewer/Qt3DSRenderer.h index 86bb59c1..2d3b69db 100644 --- a/src/Viewer/qmlviewer/Qt3DSRenderer.h +++ b/src/Viewer/qmlviewer/Qt3DSRenderer.h @@ -61,6 +61,8 @@ Q_SIGNALS: void exitSlide(const QString &elementPath, unsigned int slide, const QString &slideName); void requestResponse(const QString &elementPath, CommandType commandType, void *requestData); void presentationReady(); + void customSignalEmitted(const QString &elementPath, const QString &name); + void materialCreated(const QString &name); protected: static void onInitHandler(void *userData); diff --git a/src/Viewer/qmlviewer/Qt3DSView.cpp b/src/Viewer/qmlviewer/Qt3DSView.cpp index f5abb3e1..a68bd667 100644 --- a/src/Viewer/qmlviewer/Qt3DSView.cpp +++ b/src/Viewer/qmlviewer/Qt3DSView.cpp @@ -205,6 +205,10 @@ QQuickFramebufferObject::Renderer *Q3DSView::createRenderer() const m_presentation->d_ptr, &Q3DSPresentationPrivate::handleSlideEntered); connect(renderer, &Q3DSRenderer::exitSlide, m_presentation, &Q3DSPresentation::slideExited); + connect(renderer, &Q3DSRenderer::customSignalEmitted, + m_presentation, &Q3DSPresentation::customSignalEmitted); + connect(renderer, &Q3DSRenderer::materialCreated, + m_presentation, &Q3DSPresentation::materialCreated); connect(renderer, &Q3DSRenderer::requestResponse, this, &Q3DSView::requestResponseHandler); connect(renderer, &Q3DSRenderer::presentationReady, diff --git a/src/Viewer/studio3d/q3dscommandqueue.cpp b/src/Viewer/studio3d/q3dscommandqueue.cpp index c6f636e3..ed0ad8b5 100644 --- a/src/Viewer/studio3d/q3dscommandqueue.cpp +++ b/src/Viewer/studio3d/q3dscommandqueue.cpp @@ -217,6 +217,7 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue) break; case CommandType_GoToSlideByName: case CommandType_FireEvent: + case CommandType_CreateMaterial: queueCommand(source.m_elementPath, source.m_commandType, source.m_stringValue); break; case CommandType_SetPresentationActive: diff --git a/src/Viewer/studio3d/q3dscommandqueue_p.h b/src/Viewer/studio3d/q3dscommandqueue_p.h index 43b7ef89..ff30829f 100644 --- a/src/Viewer/studio3d/q3dscommandqueue_p.h +++ b/src/Viewer/studio3d/q3dscommandqueue_p.h @@ -70,6 +70,7 @@ enum CommandType { CommandType_SetDataInputValue, CommandType_CreateElement, CommandType_DeleteElement, + CommandType_CreateMaterial, // Requests CommandType_RequestSlideInfo, diff --git a/src/Viewer/studio3d/q3dspresentation.cpp b/src/Viewer/studio3d/q3dspresentation.cpp index 6884e0ee..3739ae66 100644 --- a/src/Viewer/studio3d/q3dspresentation.cpp +++ b/src/Viewer/studio3d/q3dspresentation.cpp @@ -36,9 +36,7 @@ #include #include #include -#include -#include -#include +#include QT_BEGIN_NAMESPACE @@ -276,11 +274,11 @@ void Q3DSPresentation::setDataInputValue(const QString &name, const QVariant &va } /** - Adds a new child element for the element specified by parentElementPath to the slide specified with - slideName. Only model element creation is currently supported. - A referenced material element is also created for the new model element. The source material name - can be specified with custom "material" property in the properties hash. - The source material must exist in the material container of the presentation. + Adds a new child element for the element specified by parentElementPath to the slide specified + with slideName. Only model element creation is currently supported. + A referenced material element is also created for the new model element. The source material + name can be specified with custom "material" property in the properties hash. + The source material must exist in the material container of the presentation. */ void Q3DSPresentation::createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties) @@ -296,8 +294,8 @@ void Q3DSPresentation::createElement(const QString &parentElementPath, const QSt } /** - Removes an element added by createElement and all its child elements. - */ + Removes an element added by createElement and all its child elements. +*/ void Q3DSPresentation::deleteElement(const QString &elementPath) { if (d_ptr->m_viewerApp) @@ -306,6 +304,24 @@ void Q3DSPresentation::deleteElement(const QString &elementPath) d_ptr->m_commandQueue->queueCommand(elementPath, CommandType_DeleteElement); } +/** + Creates a material specified by the materialDefinition parameter into the material + container of the presentation that owns the element specified by the elementPath parameter. + After creation, the material can be used for new elements created via createElement. + The materialDefinition parameter can contain either the file path to a material definition + file or a material definition in the Qt 3D Studion material data format. +*/ +void Q3DSPresentation::createMaterial(const QString &elementPath, + const QString &materialDefinition) +{ + if (d_ptr->m_viewerApp) { + d_ptr->m_viewerApp->createMaterial(elementPath, materialDefinition); + } else if (d_ptr->m_commandQueue) { + d_ptr->m_commandQueue->queueCommand(elementPath, CommandType_CreateMaterial, + materialDefinition); + } +} + void Q3DSPresentation::mousePressEvent(QMouseEvent *e) { if (d_ptr->m_viewerApp) { @@ -448,6 +464,8 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool q_ptr, &Q3DSPresentation::slideExited); connect(app, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, q_ptr, &Q3DSPresentation::customSignalEmitted); + connect(app, &Q3DSViewer::Q3DSViewerApp::SigMaterialCreated, + q_ptr, &Q3DSPresentation::materialCreated); } if (oldApp) { disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigSlideEntered, @@ -456,6 +474,8 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool q_ptr, &Q3DSPresentation::slideExited); disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal, q_ptr, &Q3DSPresentation::customSignalEmitted); + disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigMaterialCreated, + q_ptr, &Q3DSPresentation::materialCreated); } } } diff --git a/src/Viewer/studio3d/q3dspresentation.h b/src/Viewer/studio3d/q3dspresentation.h index 97017008..b444f008 100644 --- a/src/Viewer/studio3d/q3dspresentation.h +++ b/src/Viewer/studio3d/q3dspresentation.h @@ -86,6 +86,7 @@ public: void createElement(const QString &parentElementPath, const QString &slideName, const QHash &properties); void deleteElement(const QString &elementPath); + void createMaterial(const QString &elementPath, const QString &materialDefinition); public Q_SLOTS: void setSource(const QUrl &source); @@ -112,6 +113,7 @@ Q_SIGNALS: void dataInputsReady(); void customSignalEmitted(const QString &elementPath, const QString &name); void delayedLoadingChanged(bool enable); + void materialCreated(const QString &name); private: Q_DISABLE_COPY(Q3DSPresentation) diff --git a/tests/auto/viewer/tst_qt3dsviewer.cpp b/tests/auto/viewer/tst_qt3dsviewer.cpp index f7d5e831..d3387678 100644 --- a/tests/auto/viewer/tst_qt3dsviewer.cpp +++ b/tests/auto/viewer/tst_qt3dsviewer.cpp @@ -34,6 +34,9 @@ #include #include #include +#include +#include +#include void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) @@ -98,6 +101,7 @@ void tst_qt3dsviewer::init() void tst_qt3dsviewer::cleanup() { + deleteCreatedElements(0); m_viewer.hide(); } @@ -165,7 +169,8 @@ void tst_qt3dsviewer::testSettings() void tst_qt3dsviewer::testCreateElement() { - // Currently this method is very bare bones on actual testing. + // Currently this method is very bare bones on actual testing due to lack of programmatic + // feedback on dynamic object creation. // It can be used to visually check if items are getting created. m_viewer.show(); @@ -187,7 +192,7 @@ void tst_qt3dsviewer::testCreateElement() QVariant::fromValue(QVector3D(200, 300, 200))); data.insert(QStringLiteral("opacity"), 20.0); - m_presentation->createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); // Elements can be registered before they are created Q3DSElement newCylinder(m_presentation, QStringLiteral("Scene.Layer.New Cylinder")); @@ -218,8 +223,8 @@ void tst_qt3dsviewer::testCreateElement() data.insert(QStringLiteral("position"), QVariant::fromValue(QVector3D(50, animValue, 50))); - m_presentation->createElement(QStringLiteral("Scene.Layer.New Cylinder"), - QStringLiteral("Slide1"), data); + createElement(QStringLiteral("Scene.Layer.New Cylinder"), + QStringLiteral("Slide1"), data); data.clear(); data.insert(QStringLiteral("name"), QStringLiteral("New Sphere")); @@ -230,8 +235,7 @@ void tst_qt3dsviewer::testCreateElement() data.insert(QStringLiteral("position"), QVariant::fromValue(QVector3D(animValue, 75, 0))); - m_presentation->createElement(QStringLiteral("Scene.Layer.Cube2"), - QStringLiteral("Slide2"), data); + createElement(QStringLiteral("Scene.Layer.Cube2"), QStringLiteral("Slide2"), data); data.clear(); data.insert(QStringLiteral("name"), QStringLiteral("Sphere To Delete")); @@ -242,8 +246,7 @@ void tst_qt3dsviewer::testCreateElement() data.insert(QStringLiteral("position"), QVariant::fromValue(QVector3D(-100, -100, 0))); - m_presentation->createElement(QStringLiteral("Scene.Layer"), - QStringLiteral("Slide2"), data); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), data); animationTimer.start(); }); @@ -254,6 +257,8 @@ void tst_qt3dsviewer::testCreateElement() // Remove dynamically added object QTimer::singleShot(3000, [&]() { m_presentation->deleteElement(QStringLiteral("Scene.Layer.Sphere To Delete")); + // Don't remove the deleted element from createdElements to test removing already deleted + // element later when everything is cleaned up. }); // Create objects to slides 1 and 2 while slide 2 is executing @@ -267,8 +272,7 @@ void tst_qt3dsviewer::testCreateElement() data.insert(QStringLiteral("position"), QVariant::fromValue(QVector3D(-100, -100, 0))); - m_presentation->createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), - data); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); data.clear(); data.insert(QStringLiteral("name"), QStringLiteral("New Sphere 2")); @@ -279,8 +283,7 @@ void tst_qt3dsviewer::testCreateElement() data.insert(QStringLiteral("position"), QVariant::fromValue(QVector3D(-100, 100, 0))); - m_presentation->createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), - data); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), data); }); // Switch to slide 1 @@ -290,16 +293,14 @@ void tst_qt3dsviewer::testCreateElement() createTimer.setInterval(0); static int elemCounter = 0; QRandomGenerator rnd; - QStringList massModels; - auto createModelsConnection = QObject::connect(&createTimer, &QTimer::timeout, [&]() { - // Create a bunch of models to slide 2 in small batches to avoid slowdown + auto createElementsConnection = QObject::connect(&createTimer, &QTimer::timeout, [&]() { + // Create a bunch of elements to slide 2 in small batches to avoid slowdown for (int i = 0; i < 5; ++i) { ++elemCounter; data.clear(); - QString modelName = QStringLiteral("MassModel_%1").arg(elemCounter); - massModels << QStringLiteral("Scene.Layer.") + modelName; - data.insert(QStringLiteral("name"), modelName); + QString elementName = QStringLiteral("MassElement_%1").arg(elemCounter); + data.insert(QStringLiteral("name"), elementName); data.insert(QStringLiteral("sourcepath"), elemCounter % 2 ? QStringLiteral("#Cube") : QStringLiteral("#Cone")); data.insert(QStringLiteral("material"), @@ -310,39 +311,152 @@ void tst_qt3dsviewer::testCreateElement() rnd.bounded(-600, 600), rnd.bounded(800, 1200)))); - m_presentation->createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), - data); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide2"), data); } if (elemCounter >= 1000) { - qDebug() << "Extra models created:" << elemCounter; + qDebug() << "Extra elements created:" << elemCounter; createTimer.stop(); } }); - qDebug() << "Start creating extra models"; + qDebug() << "Start creating extra elements"; createTimer.start(); // Switch to slide 2 QVERIFY(spyExited.wait(20000)); - QObject::disconnect(createModelsConnection); + QObject::disconnect(createElementsConnection); QTest::qWait(500); - elemCounter = massModels.size() - 1; - QObject::connect(&createTimer, &QTimer::timeout, [&]() { - // Delete all models we created previously + deleteCreatedElements(1); + + // Switch to slide 1 + QVERIFY(spyExited.wait(20000)); + QTest::qWait(1000); +} + +void tst_qt3dsviewer::testCreateMaterial() +{ + m_viewer.show(); + + m_settings->setShowRenderStats(true); + m_settings->setScaleMode(Q3DSViewerSettings::ScaleModeFill); + + QSignalSpy spyExited(m_presentation, + SIGNAL(slideExited(const QString &, unsigned int, const QString &))); + QSignalSpy spyMatCreated(m_presentation, SIGNAL(materialCreated(const QString &))); + + // Create material via .materialdef file in resources + m_presentation->createMaterial( + QStringLiteral("Scene"), + QStringLiteral( + ":/scenes/simple_cube_animation/materials/Basic Blue.materialdef")); + m_presentation->createMaterial( + QStringLiteral("Scene"), + QStringLiteral( + ":/scenes/simple_cube_animation/materials/Basic Texture.materialdef")); + + // Create material directly from materialdef content + auto loadMatDefFile = [&](const QString &fileName) -> QString { + QFile matDefFile(fileName); + if (!matDefFile.open(QIODevice::ReadOnly | QIODevice::Text)) + return {}; + + QTextStream in(&matDefFile); + return in.readAll(); + }; + QString matDef = loadMatDefFile( + QStringLiteral(":/scenes/simple_cube_animation/materials/Copper.materialdef")); + QVERIFY(!matDef.isEmpty()); + + m_presentation->createMaterial(QStringLiteral("Scene"), matDef); + + QObject::connect(m_presentation, &Q3DSPresentation::materialCreated, + [this](const QString &name) { + QHash data; + + if (name == QStringLiteral("materials/Basic Blue")) { + data.insert(QStringLiteral("name"), QStringLiteral("Blue Cylinder")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cylinder")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(200, 300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } + + if (name == QStringLiteral("materials/Basic Texture")) { + data.insert(QStringLiteral("name"), QStringLiteral("Textured Cone")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cone")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(-200, -300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } + + if (name == QStringLiteral("materials/Copper")) { + data.insert(QStringLiteral("name"), QStringLiteral("Copper Sphere")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Sphere")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(-200, 300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } + + if (name == QStringLiteral("materials/Just Yellow")) { + QHash data; + data.insert(QStringLiteral("name"), QStringLiteral("Yellow Cube")); + data.insert(QStringLiteral("sourcepath"), QStringLiteral("#Cube")); + data.insert(QStringLiteral("material"), name); + data.insert(QStringLiteral("position"), + QVariant::fromValue(QVector3D(200, -300, 200))); + createElement(QStringLiteral("Scene.Layer"), QStringLiteral("Slide1"), data); + } + }); + + // Create material after start + QTimer::singleShot(1000, [&]() { + QString md = loadMatDefFile(QStringLiteral( + ":/scenes/simple_cube_animation/materials/Basic Blue.materialdef")); + // Modify the diffuse color and material name so that we can be sure it is a new one + md.replace(QStringLiteral("Basic Blue"), QStringLiteral("Just Yellow")); + md.replace(QRegularExpression(QStringLiteral("\"diffuse\">.*<")), + QStringLiteral("\"diffuse\">1 1 0 1<")); + m_presentation->createMaterial(QStringLiteral("Scene"), md); + }); + + QVERIFY(spyExited.wait(20000)); + QCOMPARE(spyMatCreated.count(), 4); + QTest::qWait(200); // Extra wait to verify slide change visually +} + +void tst_qt3dsviewer::deleteCreatedElements(int interval) +{ + QTimer deleteTimer; + deleteTimer.setInterval(interval); + int elemCounter = m_createdElements.size() - 1; + QObject::connect(&deleteTimer, &QTimer::timeout, [&]() { + // Delete all elements we created previously if (elemCounter >= 0) { - m_presentation->deleteElement(massModels[elemCounter]); + m_presentation->deleteElement(m_createdElements[elemCounter]); --elemCounter; } else { - qDebug() << "Extra models deleted"; - createTimer.stop(); + qDebug() << "Extra elements deleted"; + deleteTimer.stop(); } }); - qDebug() << "Start deleting extra models"; - createTimer.setInterval(1); - createTimer.start(); + qDebug() << "Start deleting extra elements"; + deleteTimer.start(); - // Switch to slide 1 - QVERIFY(spyExited.wait(20000)); + while (deleteTimer.isActive()) + QTest::qWait(20); + + m_createdElements.clear(); +} + +void tst_qt3dsviewer::createElement(const QString &parentElementPath, const QString &slideName, + const QHash &properties) +{ + m_createdElements << parentElementPath + QLatin1Char('.') + + properties[QStringLiteral("name")].toString(); + m_presentation->createElement(parentElementPath, slideName, properties); } QTEST_MAIN(tst_qt3dsviewer) diff --git a/tests/auto/viewer/tst_qt3dsviewer.h b/tests/auto/viewer/tst_qt3dsviewer.h index f50bf935..bd5d0cd9 100644 --- a/tests/auto/viewer/tst_qt3dsviewer.h +++ b/tests/auto/viewer/tst_qt3dsviewer.h @@ -56,12 +56,19 @@ private Q_SLOTS: void testFrameUpdates(); void testSettings(); void testCreateElement(); + void testCreateMaterial(); private: + void deleteCreatedElements(int interval); + void createElement(const QString &parentElementPath, const QString &slideName, + const QHash &properties); + QQuickView m_viewer; QObject *m_studio3DItem = nullptr; Q3DSPresentation *m_presentation = nullptr; Q3DSViewerSettings *m_settings = nullptr; + QStringList m_createdElements; + }; #endif // TST_QT3DSVIEWER diff --git a/tests/auto/viewer/tst_qt3dsviewer.qml b/tests/auto/viewer/tst_qt3dsviewer.qml index 2368f039..e65a49db 100644 --- a/tests/auto/viewer/tst_qt3dsviewer.qml +++ b/tests/auto/viewer/tst_qt3dsviewer.qml @@ -55,7 +55,7 @@ Studio3D { width: 800 height: 800 Presentation { - source: "qrc:/../../scenes/simple_cube_animation/simple_cube_animation.uia" + source: "qrc:/scenes/simple_cube_animation/simple_cube_animation.uia" } ViewerSettings { } diff --git a/tests/auto/viewer/viewer.qrc b/tests/auto/viewer/viewer.qrc index d85ee786..dcdddbea 100644 --- a/tests/auto/viewer/viewer.qrc +++ b/tests/auto/viewer/viewer.qrc @@ -4,6 +4,13 @@ ../../scenes/simple_cube_animation/presentations/simple_cube_animation.uip ../../scenes/simple_cube_animation/materials/Basic Green.materialdef ../../scenes/simple_cube_animation/materials/Basic Red.materialdef + ../../scenes/simple_cube_animation/materials/Basic Blue.materialdef + ../../scenes/simple_cube_animation/materials/Basic Texture.materialdef + ../../scenes/simple_cube_animation/materials/Copper.materialdef + ../../scenes/simple_cube_animation/materials/copper.shader + ../../scenes/simple_cube_animation/maps/materials/shadow.png + ../../scenes/simple_cube_animation/maps/materials/spherical_checker.png + ../../scenes/simple_cube_animation/maps/QT-symbol.png tst_qt3dsviewer.qml diff --git a/tests/scenes/simple_cube_animation/maps/QT-symbol.png b/tests/scenes/simple_cube_animation/maps/QT-symbol.png new file mode 100644 index 00000000..752a2e2f Binary files /dev/null and b/tests/scenes/simple_cube_animation/maps/QT-symbol.png differ diff --git a/tests/scenes/simple_cube_animation/maps/materials/shadow.png b/tests/scenes/simple_cube_animation/maps/materials/shadow.png new file mode 100644 index 00000000..599b1ccc Binary files /dev/null and b/tests/scenes/simple_cube_animation/maps/materials/shadow.png differ diff --git a/tests/scenes/simple_cube_animation/maps/materials/spherical_checker.png b/tests/scenes/simple_cube_animation/maps/materials/spherical_checker.png new file mode 100644 index 00000000..e42394dd Binary files /dev/null and b/tests/scenes/simple_cube_animation/maps/materials/spherical_checker.png differ diff --git a/tests/scenes/simple_cube_animation/materials/Basic Blue.materialdef b/tests/scenes/simple_cube_animation/materials/Basic Blue.materialdef new file mode 100644 index 00000000..7fdda62b --- /dev/null +++ b/tests/scenes/simple_cube_animation/materials/Basic Blue.materialdef @@ -0,0 +1,25 @@ + + Pixel + Normal + 0 0.482353 1 1 + 0 + 0 + 100 + 1 1 1 1 + 0 + 0.5 + 20 + 1 + 0 + Default + 1 1 1 1 + 1.5 + 0 + False + + + + Material + + + \ No newline at end of file diff --git a/tests/scenes/simple_cube_animation/materials/Basic Green.materialdef b/tests/scenes/simple_cube_animation/materials/Basic Green.materialdef index 5dbd3d6a..59a0ae02 100644 --- a/tests/scenes/simple_cube_animation/materials/Basic Green.materialdef +++ b/tests/scenes/simple_cube_animation/materials/Basic Green.materialdef @@ -1,18 +1,18 @@ Pixel Normal - 0 1 0.0313726 + 0.180392 1 0 1 0 0 100 - 1 1 1 + 1 1 1 1 0 0.5 20 1 0 Default - 1 1 1 + 1 1 1 1 1.5 0 False diff --git a/tests/scenes/simple_cube_animation/materials/Basic Red.materialdef b/tests/scenes/simple_cube_animation/materials/Basic Red.materialdef index b5b43717..ecb9bb43 100644 --- a/tests/scenes/simple_cube_animation/materials/Basic Red.materialdef +++ b/tests/scenes/simple_cube_animation/materials/Basic Red.materialdef @@ -1,18 +1,18 @@ Pixel Normal - 1 0.0196078 0.0352941 + 1 0 0.0156863 1 0 0 100 - 1 1 1 + 1 1 1 1 0 0.5 20 1 0 Default - 1 1 1 + 1 1 1 1 1.5 0 False diff --git a/tests/scenes/simple_cube_animation/materials/Basic Texture.materialdef b/tests/scenes/simple_cube_animation/materials/Basic Texture.materialdef new file mode 100644 index 00000000..4defa98f --- /dev/null +++ b/tests/scenes/simple_cube_animation/materials/Basic Texture.materialdef @@ -0,0 +1,63 @@ + + Pixel + Normal + 0.752941 0.756863 1 1 + + + 0 + 0 + 100 + 1 1 1 1 + 0 + 0.5 + 20 + 1 + 0 + Default + 1 1 1 1 + 1.5 + 0 + False + + + + Material + + + + 1 + 1 + UV Mapping + Tiled + Tiled + 0 + 0 + 0 + 0 + 0 + + + + + Image + + + + 2 + 3 + UV Mapping + Tiled + Tiled + 0 + 0 + 0 + 0 + 0 + + + + + Image + + + \ No newline at end of file diff --git a/tests/scenes/simple_cube_animation/materials/Copper.materialdef b/tests/scenes/simple_cube_animation/materials/Copper.materialdef new file mode 100644 index 00000000..76880986 --- /dev/null +++ b/tests/scenes/simple_cube_animation/materials/Copper.materialdef @@ -0,0 +1,14 @@ + + + True + + False + 0 + 0.807843 0.45098 0.211765 1 + + + + CustomMaterial + + + \ No newline at end of file diff --git a/tests/scenes/simple_cube_animation/materials/copper.shader b/tests/scenes/simple_cube_animation/materials/copper.shader new file mode 100644 index 00000000..cf9e1ad4 --- /dev/null +++ b/tests/scenes/simple_cube_animation/materials/copper.shader @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + +// add enum defines +#define scatter_reflect 0 +#define scatter_transmit 1 +#define scatter_reflect_transmit 2 + +#define QT3DS_ENABLE_UV0 1 +#define QT3DS_ENABLE_WORLD_POSITION 1 +#define QT3DS_ENABLE_TEXTAN 1 +#define QT3DS_ENABLE_BINORMAL 0 + +#include "vertexFragmentBase.glsllib" + +// set shader output +out vec4 fragColor; + +// add structure defines +struct layer_result +{ + vec4 base; + vec4 layer; + mat3 tanFrame; +}; + + +// temporary declarations + vec4 tmpShadowTerm; + +layer_result layers[1]; + +#include "SSAOCustomMaterial.glsllib" +#include "sampleLight.glsllib" +#include "sampleProbe.glsllib" +#include "sampleArea.glsllib" +#include "square.glsllib" +#include "calculateRoughness.glsllib" +#include "evalBakedShadowMap.glsllib" +#include "evalEnvironmentMap.glsllib" +#include "luminance.glsllib" +#include "microfacetBSDF.glsllib" +#include "physGlossyBSDF.glsllib" +#include "simpleGlossyBSDF.glsllib" +#include "fresnelLayer.glsllib" + +bool evalTwoSided() +{ + return( false ); +} + +vec3 computeFrontMaterialEmissive() +{ + return( vec3( 0, 0, 0 ) ); +} + +void computeFrontLayerColor( in vec3 normal, in vec3 lightDir, in vec3 viewDir, in vec3 lightDiffuse, in vec3 lightSpecular, in float materialIOR, float aoFactor ) +{ +#if QT3DS_ENABLE_CG_LIGHTING + layers[0].base += tmpShadowTerm * vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += tmpShadowTerm * microfacetBSDF( layers[0].tanFrame, lightDir, viewDir, lightSpecular, materialIOR, roughness, roughness, scatter_reflect ); + +#endif +} + +void computeFrontAreaColor( in int lightIdx, in vec4 lightDiffuse, in vec4 lightSpecular ) +{ +#if QT3DS_ENABLE_CG_LIGHTING + layers[0].base += tmpShadowTerm * vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += tmpShadowTerm * lightSpecular * sampleAreaGlossy( layers[0].tanFrame, varWorldPos, lightIdx, viewDir, roughness, roughness ); + +#endif +} + +void computeFrontLayerEnvironment( in vec3 normal, in vec3 viewDir, float aoFactor ) +{ +#if !QT3DS_ENABLE_LIGHT_PROBE + layers[0].base += tmpShadowTerm * vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += tmpShadowTerm * microfacetSampledBSDF( layers[0].tanFrame, viewDir, roughness, roughness, scatter_reflect ); + +#else + layers[0].base += tmpShadowTerm * vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += tmpShadowTerm * sampleGlossyAniso( layers[0].tanFrame, viewDir, roughness, roughness ); + +#endif +} + +vec3 computeBackMaterialEmissive() +{ + return( vec3(0, 0, 0) ); +} + +void computeBackLayerColor( in vec3 normal, in vec3 lightDir, in vec3 viewDir, in vec3 lightDiffuse, in vec3 lightSpecular, in float materialIOR, float aoFactor ) +{ +#if QT3DS_ENABLE_CG_LIGHTING + layers[0].base += vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += vec4( 0.0, 0.0, 0.0, 1.0 ); +#endif +} + +void computeBackAreaColor( in int lightIdx, in vec4 lightDiffuse, in vec4 lightSpecular ) +{ +#if QT3DS_ENABLE_CG_LIGHTING + layers[0].base += vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += vec4( 0.0, 0.0, 0.0, 1.0 ); +#endif +} + +void computeBackLayerEnvironment( in vec3 normal, in vec3 viewDir, float aoFactor ) +{ +#if !QT3DS_ENABLE_LIGHT_PROBE + layers[0].base += vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += vec4( 0.0, 0.0, 0.0, 1.0 ); +#else + layers[0].base += vec4( 0.0, 0.0, 0.0, 1.0 ); + layers[0].layer += vec4( 0.0, 0.0, 0.0, 1.0 ); +#endif +} + +float computeIOR() +{ + return( false ? 1.0 : luminance( vec3( 1, 1, 1 ) ) ); +} + +float evalCutout() +{ + return( 1.000000 ); +} + +vec3 computeNormal() +{ + return( normal ); +} + +void computeTemporaries() +{ + tmpShadowTerm = evalBakedShadowMap( texCoord0 ); +} + +vec4 computeLayerWeights( in float alpha ) +{ + vec4 color; + color = fresnelLayer( normal, vec3( 25.65, 25.65, 25.65 ), 1.000000, metal_color.rgb, layers[0].layer, layers[0].base, alpha ); + return color; +} + + +void initializeLayerVariables(void) +{ + // clear layers + layers[0].base = vec4(0.0, 0.0, 0.0, 1.0); + layers[0].layer = vec4(0.0, 0.0, 0.0, 1.0); + layers[0].tanFrame = orthoNormalize( mat3( tangent, cross(normal, tangent), normal ) ); +} + + + + + + + + + + + diff --git a/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip b/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip index 7cabcbf0..bfedf1e1 100644 --- a/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip +++ b/tests/scenes/simple_cube_animation/presentations/simple_cube_animation.uip @@ -9,32 +9,36 @@ - + - + - + - + - + + 0 -341.785 100 100 5 269.972 100 100 + 0 0 100 100 5 0 100 100 + 0 0 100 100 5 0 100 100 + - - + + - + 0 0 100 100 1 0 100 100 4 0 100 100 5 90 100 100 7 90 100 100 10 0 100 100 0 0 100 100 1 0 100 100 4 90 100 100 5 0 100 100 7 0 100 100 10 0 100 100 0 0 100 100 1 -90 100 100 4 -90 100 100 5 -90 100 100 7 0 100 100 10 0 100 100 -- cgit v1.2.3