/**************************************************************************** ** ** Copyright (C) 1993-2009 NVIDIA Corporation. ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt 3D Studio. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "EnginePrefix.h" #include "Qt3DSRenderRuntimeBindingImpl.h" #include "Qt3DSSceneManager.h" #include "Qt3DSIScene.h" #include "Qt3DSRuntimeView.h" #include "Qt3DSQmlEngine.h" #include "Qt3DSRenderUIPLoader.h" #include "Qt3DSPresentationFrameData.h" #include "foundation/AutoDeallocatorAllocator.h" #include "Qt3DSRenderSubpresentation.h" #include "Qt3DSIScriptBridge.h" #include "Qt3DSFileStream.h" #include "Qt3DSDMPrefix.h" #include "foundation/IOStreams.h" #include "Qt3DSDMStringTable.h" #include "Qt3DSDMXML.h" #include "Qt3DSRenderBufferManager.h" #include "foundation/SerializationTypes.h" #include "Qt3DSRenderGraphObjectSerializer.h" #include "Qt3DSRenderShaderCache.h" #include "Qt3DSRenderImageBatchLoader.h" #include "Qt3DSPresentation.h" #include "Qt3DSDLLManager.h" #include "Qt3DSBasicPluginDLL.h" #include "Qt3DSPluginDLL.h" #include "Qt3DSRenderPlugin.h" #include "foundation/FileTools.h" #include "Qt3DSStateVisualBindingContextCommands.h" #include "Qt3DSStateScriptContext.h" #include "EventPollingSystem.h" #include "EventSystem.h" #include "Qt3DSApplication.h" #include "Qt3DSMatrix.h" #include "Qt3DSWindowSystem.h" #if !defined (Q_OS_MACOS) #include "Qt3DSEGLInfo.h" #endif #include "Qt3DSOffscreenRenderKey.h" #include "Qt3DSOldNBustedRenderPlugin.h" #include "Qt3DSRenderCustomMaterialSystem.h" #include "foundation/Qt3DSPerfTimer.h" #include "Qt3DSRenderBufferLoader.h" #include "Qt3DSRenderRenderList.h" #include "Qt3DSRenderPrefilterTexture.h" #include "foundation/PoolingAllocator.h" #include "q3dsqmlrender.h" #ifdef EA_PLATFORM_WINDOWS #pragma warning(disable : 4355) #endif using namespace qt3ds::render; using eastl::make_pair; using eastl::pair; using qt3ds::runtime::IApplication; #ifndef _WIN32 #define stricmp strcasecmp #endif namespace qt3ds { namespace render { qt3ds::foundation::MallocAllocator g_BaseAllocator; } } namespace { struct Qt3DSRenderScene; struct Qt3DSRenderSceneSubPresRenderer : public CSubPresentationRenderer { Qt3DSRenderScene &m_Scene; Qt3DSRenderSceneSubPresRenderer(Qt3DSRenderScene &inScene, IQt3DSRenderContext &inRenderContext, SPresentation &inPresentation) : CSubPresentationRenderer(inRenderContext, inPresentation) , m_Scene(inScene) { } QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_RenderContext.GetAllocator()) SOffscreenRenderFlags NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, QT3DSVec2 inPresScale, const SRenderInstanceId instanceId) override; void Render(const SOffscreenRendererEnvironment &inEnvironment, NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, const SRenderInstanceId instanceId) override; void RenderWithClear(const SOffscreenRendererEnvironment &inEnvironment, NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, QT3DSVec4 inClearColor, const SRenderInstanceId instanceId) override; }; struct SSceneLoadData { NVAllocatorCallback &m_Allocator; NVScopedRefCounted m_Data; SPresentation *m_Presentation; NVDataRef m_TranslatorData; NVDataRef m_SceneGraphData; eastl::vector m_Translators; SPoolingAllocator m_AutoAllocator; Q3DStudio::IPresentation *m_RuntimePresentation; QT3DSI32 mRefCount; SSceneLoadData(NVAllocatorCallback &alloc) : m_Allocator(alloc) , m_Presentation(nullptr) , m_AutoAllocator(alloc) , m_RuntimePresentation(nullptr) , mRefCount(0) { } QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator) }; struct Qt3DSRenderScene : public Q3DStudio::IScene { SBindingCore &m_BindingCore; NVScopedRefCounted m_Context; NVScopedRefCounted m_LoadData; // The scene graph gets allocated kind of on its own // So do the smaller translation objects that translate TElement properties // into the graph. SPresentation *m_Presentation; Q3DStudio::IPresentation *m_RuntimePresentation; void *m_UserData; TTranslatorDirytSet m_DirtySet; CRegisteredString m_OffscreenRendererId; IOffscreenRenderer *m_OffscreenRenderer; CRegisteredString m_SubPresentationType; nvvector m_GraphObjectList; bool m_LoggedPickLastFrame; NVRenderRect m_LastRenderViewport; CRegisteredString m_PathSubPathType; Qt3DSRenderScene(SBindingCore &inBindingCore, IQt3DSRenderContext &inContext, SSceneLoadData &inLoadData) : m_BindingCore(inBindingCore) , m_Context(inContext) , m_LoadData(inLoadData) , m_Presentation(inLoadData.m_Presentation) , m_RuntimePresentation(inLoadData.m_RuntimePresentation) , m_UserData(nullptr) , m_DirtySet(inContext.GetAllocator(), "Qt3DSRenderScene::m_DirtySet") , m_OffscreenRenderer(nullptr) , m_SubPresentationType( inContext.GetStringTable().RegisterStr(CSubPresentationRenderer::GetRendererName())) , m_GraphObjectList(inContext.GetAllocator(), "Qt3DSDSRenderScene::m_GraphObjectList") , m_LoggedPickLastFrame(false) { for (QT3DSU32 idx = 0, end = inLoadData.m_Translators.size(); idx < end; ++idx) { m_DirtySet.insert(*inLoadData.m_Translators[idx]); } m_PathSubPathType = inContext.GetStringTable().RegisterStr("PathAnchorPoint"); } virtual ~Qt3DSRenderScene() override { if (m_OffscreenRenderer) m_Context->GetOffscreenRenderManager().ReleaseOffscreenRenderer(m_OffscreenRendererId); m_OffscreenRenderer = nullptr; if (m_Presentation && m_Presentation->m_Scene) { for (SLayer *theLayer = m_Presentation->m_Scene->m_FirstChild; theLayer; theLayer = static_cast(theLayer->m_NextSibling)) { m_Context->GetRenderer().ReleaseLayerRenderResources(*theLayer, nullptr); } } } qt3ds::NVAllocatorCallback &allocator() override { return m_LoadData->m_AutoAllocator; } Q3DStudio::IPresentation &GetPresentation() override { return *m_RuntimePresentation; } bool preferKtx() const override { return m_Presentation->m_preferKTX; } // Update really just adds objects to the dirty set bool Update() { Q3DStudio::TElementList &theDirtyList = m_RuntimePresentation->GetFrameData().GetDirtyList(); for (int idx = 0, end = theDirtyList.GetCount(); idx < end; ++idx) { Q3DStudio::TElement &theElement = *theDirtyList[idx]; Qt3DSTranslator *theTranslator = reinterpret_cast(theElement.GetAssociation()); if (!theTranslator && theElement.GetType() == m_PathSubPathType) { Q3DStudio::TElement *theParent = theElement.GetParent(); if (theParent) { theTranslator = reinterpret_cast(theParent->GetAssociation()); // The path translator responds to anchor point changes as well as its own data // changes. if (theTranslator && theTranslator->RenderObject().m_Type != GraphObjectTypes::PathSubPath) theTranslator = nullptr; } } if (theTranslator) m_DirtySet.insert(*theTranslator); } return m_DirtySet.size() > 0; } void TransferDirtyProperties() { if (m_Presentation) { for (QT3DSU32 idx = 0, end = m_DirtySet.size(); idx < end; ++idx) { QT3DS_ASSERT(m_DirtySet.size() == end); m_DirtySet[idx]->OnElementChanged(*m_Presentation, *m_Context, *m_RuntimePresentation); } m_DirtySet.clear(); } } bool PrepareForRender() { TransferDirtyProperties(); m_LastRenderViewport = m_Context->GetRenderList().GetViewport(); if (m_Presentation && m_Presentation->m_Scene) { NVRenderRect theViewportSize(m_LastRenderViewport); return m_Presentation->m_Scene->PrepareForRender( QT3DSVec2(QT3DSF32(theViewportSize.m_Width), QT3DSF32(theViewportSize.m_Height)), *m_Context); } return false; } void Render() { if (m_Presentation && m_Presentation->m_Scene) { NVRenderRect theViewportSize(m_LastRenderViewport); m_Presentation->m_Scene->Render( QT3DSVec2(QT3DSF32(theViewportSize.m_Width), QT3DSF32(theViewportSize.m_Height)), *m_Context, SScene::DoNotClear); } } // Note that we do not need to call WindowToPresentation on the mouse coordinates because they // are specifically // supposed to be the return values from getMousePosition which applies that transformation. We // do, however need // to reverse part of this transformation; whatever part happens after // m_Context->GetMousePickMouseCoords Q3DStudio::TElement *UserPick(float mouseX, float mouseY) { // Note that the pick code below only calls GetMousePickMouseCoords // while windowToPresentation subtracts the window positional offset from // the mouse position. // Thus we have to add it back. QT3DSVec2 mousePos(mouseX + m_LastRenderViewport.m_X, mouseY + m_LastRenderViewport.m_Y); Qt3DSRenderPickResult thePickResult = m_Context->GetRenderer().Pick( *m_Presentation->m_Scene->m_FirstChild, m_Context->GetMousePickViewport() // GetMousePickMouseCoords is called by the renderer to setup the pick frame. // This is so that the presentation's lastMouseX and lastMouseY variables are correctly // setup. , mousePos, true, true); if (thePickResult.m_HitObject != nullptr) { SModel *theHitModel = static_cast(const_cast(thePickResult.m_HitObject)); return &Qt3DSTranslator::GetTranslatorFromGraphNode(*theHitModel)->Element(); } return nullptr; } virtual Option FacePosition(Q3DStudio::TElement &inElement, float mouseX, float mouseY, NVDataRef inMapperElements, Q3DStudio::FacePositionPlanes::Enum inPlane) { if (inElement.GetBelongedPresentation() != this->m_RuntimePresentation && inMapperElements.size() == 0) { return Empty(); } Qt3DSTranslator *theTranslator = reinterpret_cast(inElement.GetAssociation()); if (theTranslator == nullptr) return Empty(); bool isValidPickObject = GraphObjectTypes::IsNodeType(theTranslator->m_RenderObject->m_Type); if (isValidPickObject == false) return Empty(); SNode &theNode = static_cast(*theTranslator->m_RenderObject); NVBounds3 theBounds = GetNodeLocalBoundingBox(&inElement, false); // See commens in UserPick QT3DSVec2 mousePos(mouseX + m_LastRenderViewport.m_X, mouseY + m_LastRenderViewport.m_Y); eastl::vector theMapperObjects(inMapperElements.size()); for (QT3DSU32 idx = 0, end = inMapperElements.size(); idx < end; ++idx) { Qt3DSTranslator *theMapperTranslator = reinterpret_cast(inMapperElements[idx]->GetAssociation()); SGraphObject *theMapperObject = nullptr; if (theMapperTranslator) { theMapperObject = theMapperTranslator->m_RenderObject; } if (theMapperObject == nullptr) { QT3DS_ASSERT(false); } else { theMapperObjects[idx] = theMapperObject; } } qt3ds::render::SBasisPlanes::Enum thePlane(qt3ds::render::SBasisPlanes::XY); switch (inPlane) { case Q3DStudio::FacePositionPlanes::XY: thePlane = qt3ds::render::SBasisPlanes::XY; break; case Q3DStudio::FacePositionPlanes::YZ: thePlane = qt3ds::render::SBasisPlanes::YZ; break; case Q3DStudio::FacePositionPlanes::XZ: thePlane = qt3ds::render::SBasisPlanes::XZ; break; } if (theBounds.isEmpty() == false) { return m_Context->GetRenderer().FacePosition( theNode, theBounds, theNode.m_GlobalTransform, m_Context->GetMousePickViewport(), mousePos, NVDataRef(theMapperObjects.data(), QT3DSU32(theMapperObjects.size())), thePlane); } return Empty(); } void Pick(Q3DStudio::SPickFrame &ioPickFrame) { // Presentation's m_Hide can disable rendering bool wasPickLoggedLastFrame = m_LoggedPickLastFrame; m_LoggedPickLastFrame = false; if (ioPickFrame.m_InputFrame.m_PickValid) { // If we have not already found a valid pick on a previous layer if (!ioPickFrame.m_ResultValid && m_Presentation && m_Presentation->m_Scene && m_Presentation->m_Scene->m_FirstChild) { Qt3DSRenderPickResult thePickResult = m_Context->GetRenderer().Pick( *m_Presentation->m_Scene->m_FirstChild, m_Context->GetMousePickViewport() // GetMousePickMouseCoords is called by the renderer to setup the pick frame. // This is so that the presentation's lastMouseX and lastMouseY variables are // correctly setup. , m_Context->GetMousePickMouseCoords( QT3DSVec2(ioPickFrame.m_InputFrame.m_PickX, ioPickFrame.m_InputFrame.m_PickY)), true); if (thePickResult.m_HitObject != nullptr) { SModel *theHitModel = static_cast( const_cast(thePickResult.m_HitObject)); ioPickFrame.m_Model = &Qt3DSTranslator::GetTranslatorFromGraphNode(*theHitModel)->Element(); // I don't think local hit is used any more, but not sure. If they are used, // then the code below is probably wrong. ioPickFrame.m_LocalHit[0] = thePickResult.m_LocalUVCoords.x; ioPickFrame.m_LocalHit[1] = thePickResult.m_LocalUVCoords.y; ioPickFrame.m_SquaredDistance = thePickResult.m_CameraDistanceSq; ioPickFrame.m_ResultValid = true; if (wasPickLoggedLastFrame == false) { m_LoggedPickLastFrame = true; Q3DStudio::IPresentation *thePresentation = ioPickFrame.m_Model->GetBelongedPresentation(); IApplication &theRuntime = thePresentation->GetApplication(); // We are picking against the previous frame of information. qCInfo(TRACE_INFO, "Model Picked: %s on frame %d", theHitModel->m_Id.c_str(), theRuntime.GetFrameCount() - 1); } } else { // The scene is always picked if it is pickable; nothing else really makes // sense. Qt3DSTranslator *theTranslator = Qt3DSTranslator::GetTranslatorFromGraphNode(*m_Presentation->m_Scene); if (theTranslator) { ioPickFrame.m_Model = &theTranslator->Element(); // I don't think local hit is used any more, but not sure. If they are // used, then the code below is probably wrong. ioPickFrame.m_LocalHit[0] = ioPickFrame.m_InputFrame.m_PickX / m_Presentation->m_PresentationDimensions.x; ioPickFrame.m_LocalHit[1] = 1.0f - ioPickFrame.m_InputFrame.m_PickY / m_Presentation->m_PresentationDimensions.y; ioPickFrame.m_SquaredDistance = 0; ioPickFrame.m_ResultValid = true; } } } } } void SetUserData(void *inUserData) override { m_UserData = inUserData; } void *GetUserData() override { return m_UserData; } // Unfortunately, you should expect the node to be dirty at this point so we may need to force // an update void CalculateGlobalTransform(Q3DStudio::TElement *inElement, Q3DStudio::RuntimeMatrix &outTransform) override { if (inElement == nullptr) { QT3DS_ASSERT(false); return; } Qt3DSTranslator *theTranslator = reinterpret_cast(inElement->GetAssociation()); if (theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetUIPType())) { Update(); TransferDirtyProperties(); SNode *theNode = static_cast(&theTranslator->RenderObject()); // Ensure the node stays dirty bool wasDirty = theNode->m_Flags.IsDirty(); theNode->CalculateGlobalVariables(); theNode->m_Flags.SetDirty(wasDirty); memCopy(outTransform.m_Data, theNode->m_GlobalTransform.front(), sizeof(QT3DSMat44)); } else { qCCritical(INVALID_OPERATION, "Calculate global transform called on invalide object"); QT3DS_ASSERT(false); } } void SetLocalTransformMatrix(Q3DStudio::TElement *inElement, const Q3DStudio::RuntimeMatrix &inTransform) override { if (inElement == nullptr) { QT3DS_ASSERT(false); return; } Qt3DSTranslator *theTranslator = reinterpret_cast(inElement->GetAssociation()); if (theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetUIPType())) { SNode *theNode = static_cast(&theTranslator->RenderObject()); QT3DSMat44 transform; memCopy(transform.front(), inTransform.m_Data, sizeof(QT3DSMat44)); theNode->SetLocalTransformFromMatrix(transform); theNode->MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); } } void EnsureNodeIsUpToDate(SNode &inNode, bool inIncludeChildren) { bool wasDirty = inNode.m_Flags.IsDirty(); if (wasDirty) { inNode.CalculateGlobalVariables(); inNode.m_Flags.SetDirty(wasDirty); if (inIncludeChildren) { for (SNode *theChild = inNode.m_FirstChild; theChild; theChild = theChild->m_NextSibling) EnsureNodeIsUpToDate(*theChild, inIncludeChildren); } } } NVBounds3 GetNodeLocalBoundingBox(Q3DStudio::TElement *inElement, bool inSelfOnly) { NVBounds3 retval(NVBounds3::empty()); if (inElement == nullptr) { QT3DS_ASSERT(false); return retval; } Qt3DSTranslator *theTranslator = reinterpret_cast(inElement->GetAssociation()); if (theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetUIPType())) { Update(); TransferDirtyProperties(); SNode *theNode = static_cast(&theTranslator->RenderObject()); bool theIncludeChildren = !inSelfOnly; EnsureNodeIsUpToDate(*theNode, theIncludeChildren); IBufferManager &theBufferManager(m_Context->GetBufferManager()); return theNode->GetBounds(theBufferManager, m_Context->GetPathManager(), theIncludeChildren); } else { qCCritical(INVALID_OPERATION, "GetBoundingBox called on invalid object"); QT3DS_ASSERT(false); } return retval; } static void Assign(Q3DStudio::CBoundingBox &lhs, NVBounds3 &rhs) { lhs.m_Min.m_X = rhs.minimum.x; lhs.m_Min.m_Y = rhs.minimum.y; lhs.m_Min.m_Z = rhs.minimum.z; lhs.m_Max.m_X = rhs.maximum.x; lhs.m_Max.m_Y = rhs.maximum.y; lhs.m_Max.m_Z = rhs.maximum.z; } Q3DStudio::CBoundingBox GetBoundingBox(Q3DStudio::TElement *inElement, bool inSelfOnly) override { Q3DStudio::CBoundingBox retval; retval.SetEmpty(); NVBounds3 theLocalBox = GetNodeLocalBoundingBox(inElement, inSelfOnly); if (theLocalBox.isEmpty() == false) { Qt3DSTranslator *theTranslator = reinterpret_cast(inElement->GetAssociation()); if (theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetUIPType())) { SNode *theNode = static_cast(&theTranslator->RenderObject()); theLocalBox.transform(theNode->m_GlobalTransform); Assign(retval, theLocalBox); // Left handed nodes need to return values in their space, not in the global space // This is a hack fix due to a bug. if (theNode->m_Flags.IsLeftHanded()) { eastl::swap(retval.m_Min.m_Z, retval.m_Max.m_Z); retval.m_Min.m_Z *= -1; retval.m_Max.m_Z *= -1; } } } return retval; } Q3DStudio::CBoundingBox GetLocalBoundingBox(Q3DStudio::TElement *inElement, bool inSelfOnly) override { Q3DStudio::CBoundingBox retval; retval.SetEmpty(); if (inElement == nullptr) { QT3DS_ASSERT(false); return retval; } NVBounds3 theLocalBounds = GetNodeLocalBoundingBox(inElement, inSelfOnly); if (theLocalBounds.isEmpty() == false) Assign(retval, theLocalBounds); return retval; } Q3DStudio::SCameraRect GetCameraBounds(Q3DStudio::TElement &inElem) override { Qt3DSTranslator *theTranslator = reinterpret_cast(inElem.GetAssociation()); if (theTranslator && theTranslator->m_RenderObject) { Option theRectOpt = m_Context->GetRenderer().GetCameraBounds(*theTranslator->m_RenderObject); if (theRectOpt.hasValue()) { qt3ds::render::SCuboidRect theRect(*theRectOpt); return Q3DStudio::SCameraRect(theRect.m_Left, theRect.m_Top, theRect.m_Right, theRect.m_Bottom); } } return Q3DStudio::SCameraRect(); } void PositionToScreen(Q3DStudio::TElement &inElement, QT3DSVec3 &inPos, QT3DSVec3 &outScreen) override { Qt3DSTranslator *theTranslator = reinterpret_cast(inElement.GetAssociation()); if (theTranslator && theTranslator->m_RenderObject) { SNode *theNode = reinterpret_cast(theTranslator->m_RenderObject); QT3DSVec3 thePos = theNode->m_GlobalTransform.transform(inPos); outScreen = m_Context->GetRenderer().ProjectPosition(*theNode, thePos); } } void ScreenToPosition(Q3DStudio::TElement &inElement, QT3DSVec3 &inScreen, QT3DSVec3 &outPos) override { Qt3DSTranslator *theTranslator = reinterpret_cast(inElement.GetAssociation()); if (theTranslator && theTranslator->m_RenderObject) { SNode *theNode = reinterpret_cast(theTranslator->m_RenderObject); QT3DSVec3 objPos = theNode->GetGlobalPos(); outPos = m_Context->GetRenderer().UnprojectWithDepth(*(theNode), objPos, inScreen); } } static void GenerateBsdfMipmaps(SImage *theImage, const unsigned char *inBuffer, Q3DStudio::INT32 inBufferLength, Q3DStudio::INT32 inWidth, Q3DStudio::INT32 inHeight, qt3ds::render::NVRenderTextureFormats::Enum inFormat, IQt3DSRenderContext *theContext) { NVRenderTextureFormats::Enum destFormat = qt3ds::render::NVRenderTextureFormats::RGBA16F; Qt3DSRenderPrefilterTexture *theBSDFMipMap = theImage->m_TextureData.m_BSDFMipMap; if (theBSDFMipMap == nullptr) { theBSDFMipMap = Qt3DSRenderPrefilterTexture::Create( &theContext->GetRenderContext(), inWidth, inHeight, *theImage->m_TextureData.m_Texture, destFormat, theContext->GetFoundation()); theImage->m_TextureData.m_BSDFMipMap = theBSDFMipMap; } if (theBSDFMipMap) theBSDFMipMap->Build((void *)(inBuffer), inBufferLength, inFormat); } // This could cause some significant drama. void SetTextureData(Q3DStudio::TElement *inElement, const unsigned char *inBuffer, Q3DStudio::INT32 inBufferLength, Q3DStudio::INT32 inWidth, Q3DStudio::INT32 inHeight, qt3ds::render::NVRenderTextureFormats::Enum inFormat, Q3DStudio::INT32 inHasTransparency) override { Qt3DSTranslator *theTranslator = reinterpret_cast(inElement->GetAssociation()); if (theTranslator && theTranslator->GetUIPType() == GraphObjectTypes::Image) { SImage *theImage = static_cast(&theTranslator->RenderObject()); // Attempt to resolve the image's path if (!theImage->m_TextureData.m_Texture) { if (theImage->m_ImagePath.IsValid()) theImage->m_TextureData = m_Context->GetBufferManager().LoadRenderImage( theImage->m_ImagePath, false, theImage->m_MappingMode == ImageMappingModes::LightProbe); } // Here we go, updating the texture. if (theImage->m_TextureData.m_Texture) { if (theImage->m_MappingMode == ImageMappingModes::LightProbe) { // theImage->m_TextureData.m_Texture->GenerateMipmaps(); GenerateBsdfMipmaps(theImage, inBuffer, inBufferLength, inWidth, inHeight, inFormat, m_Context); } else theImage->m_TextureData.m_Texture->SetTextureData( NVDataRef((QT3DSU8 *)inBuffer, (QT3DSU32)inBufferLength), 0, inWidth, inHeight, inFormat); if (inHasTransparency >= 0) { bool hasTransparency = inHasTransparency ? true : false; theImage->m_TextureData.m_TextureFlags.SetHasTransparency(hasTransparency); m_Context->GetBufferManager().SetImageHasTransparency(theImage->m_ImagePath, hasTransparency); } theImage->m_Flags.SetDirty(true); } } else { qCCritical(INVALID_OPERATION, "SetTextureData called on object that is not an image"); QT3DS_ASSERT(false); } } bool CreateOrSetMeshData(const char *inPathStr, unsigned char *vertData, unsigned int numVerts, unsigned int vertStride, unsigned int *indexData, unsigned int numIndices, qt3ds::NVBounds3 &objBounds) override { SRenderMesh *theMesh = nullptr; if (inPathStr && vertData && indexData) { theMesh = m_Context->GetBufferManager().CreateMesh( inPathStr, vertData, numVerts, vertStride, indexData, numIndices, objBounds); } else { qCCritical(INVALID_OPERATION, "CreateOrSetMeshData was not supplied necessary buffers or object path"); } return (theMesh != nullptr); } Q3DStudio::STextSizes MeasureText(Q3DStudio::TElement *inElement, const char *inTextStr) override { Qt3DSTranslator *theTranslator = reinterpret_cast(inElement->GetAssociation()); Q3DStudio::STextSizes retval; if (theTranslator && theTranslator->GetUIPType() == GraphObjectTypes::Text) { if (inElement->IsDirty()) { theTranslator->OnElementChanged(*m_Presentation, *m_Context, *m_RuntimePresentation); } SText *theText = static_cast(&theTranslator->RenderObject()); if (theText) { STextDimensions theDimensions = m_Context->GetTextRenderer()->MeasureText(*theText, 1.0f, inTextStr); retval = Q3DStudio::STextSizes(Q3DStudio::INT32(theDimensions.m_TextWidth), Q3DStudio::INT32(theDimensions.m_TextHeight)); } } else { qCCritical(INVALID_OPERATION, "MeasureText called on object that is not text"); QT3DS_ASSERT(false); } return retval; } Q3DStudio::STextSizes GetPresentationDesignDimensions() override { if (m_Presentation) { return Q3DStudio::STextSizes( static_cast(m_Presentation->m_PresentationDimensions.x), static_cast(m_Presentation->m_PresentationDimensions.y)); } QT3DS_ASSERT(false); return Q3DStudio::STextSizes(); } virtual Q3DStudio::SMousePosition WindowToPresentation(const Q3DStudio::SMousePosition &inWindowCoords) override { // If there aren't any rotations, then account for the difference in width/height of the // presentation and the window QT3DSVec2 theCoords = m_Context->GetMousePickMouseCoords( QT3DSVec2(QT3DSF32(inWindowCoords.m_X), QT3DSF32(inWindowCoords.m_Y))); theCoords.x -= m_LastRenderViewport.m_X; // Note that the mouse Y is reversed. Thus a positive offset of the viewport will reduce // the mouse value. theCoords.y -= m_LastRenderViewport.m_Y; return Q3DStudio::SMousePosition(theCoords.x, theCoords.y); } qt3ds::foundation::CRegisteredString RegisterStr(const char *inStr) override { return m_Context->GetStringTable().RegisterStr(inStr); } void RegisterOffscreenRenderer(const char *inKey) override { m_OffscreenRenderer = QT3DS_NEW(m_Context->GetAllocator(), Qt3DSRenderSceneSubPresRenderer)( *this, *m_Context, *m_Presentation); m_OffscreenRendererId = m_Context->GetStringTable().RegisterStr(inKey); m_Context->GetOffscreenRenderManager().RegisterOffscreenRenderer(m_OffscreenRendererId, *m_OffscreenRenderer); } template void forAllObjects(nvvector &vec, GraphObjectTypes::Enum type, C callable) { nvvector::iterator it = vec.begin(); nvvector::iterator end = vec.end(); while (it != end) { if ((*it)->m_Type == type) callable(static_cast(*it)); ++it; } } void PostLoadStep() { IBufferManager &mgr = m_Context->GetBufferManager(); forAllObjects(m_GraphObjectList, GraphObjectTypes::Image, [&mgr](SImage *image){ if (image->m_ImagePath.IsValid() && qt3ds::runtime::isImagePath( image->m_ImagePath.c_str())) { const bool ibl = image->m_MappingMode == ImageMappingModes::LightProbe; image->m_LoadedTextureData = mgr.CreateReloadableImage(image->m_ImagePath, false, ibl); image->m_LoadedTextureData->m_callbacks.push_back(image); } }); } void Release() override { NVDelete(m_Context->GetAllocator(), this); } }; SOffscreenRenderFlags Qt3DSRenderSceneSubPresRenderer::NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, QT3DSVec2 inPresScale, const SRenderInstanceId instanceId) { m_Scene.TransferDirtyProperties(); return CSubPresentationRenderer::NeedsRender(inEnvironment, inPresScale, instanceId); } void Qt3DSRenderSceneSubPresRenderer::Render(const SOffscreenRendererEnvironment &inEnvironment, NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, const SRenderInstanceId instanceId) { CSubPresentationRenderer::Render(inEnvironment, inRenderContext, inPresScale, inClearBuffer, instanceId); } void Qt3DSRenderSceneSubPresRenderer::RenderWithClear( const SOffscreenRendererEnvironment &inEnvironment, NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, SScene::RenderClearCommand inClearBuffer, QT3DSVec4 inClearColor, const SRenderInstanceId id) { CSubPresentationRenderer::RenderWithClear(inEnvironment, inRenderContext, inPresScale, inClearBuffer, inClearColor, id); } ////////////////////////////////////////////////////////////////// // Scene Manager ////////////////////////////////////////////////////////////////// struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager, public Q3DStudio::ISceneBinaryLoader { typedef nvhash_map TStrBoolMap; NVScopedRefCounted m_Context; nvvector> m_Scenes; nvvector> m_RenderPlugins; Q3DStudio::INT32 m_ViewWidth; Q3DStudio::INT32 m_ViewHeight; Q3DStudio::SPickFrame m_PickFrame; NVDataRef m_StrTableData; // The boolean is to mark transparent images and ibl images nvvector>> m_SourcePaths; eastl::hash_set m_SourcePathSet; Qt3DSRenderScene *m_LastRenderedScene; Q3DStudio::IWindowSystem &m_WindowSystem; Mutex m_LoadingScenesMutex; eastl::vector> m_LoadingScenes; CRegisteredString m_ProjectDir; CRegisteredString m_BinaryDir; bool m_ProjectInitialized; QT3DSI32 mRefCount; Qt3DSRenderSceneManager(SBindingCore &ctx, Q3DStudio::IWindowSystem &inWindowSystem) : m_Context(ctx) , m_Scenes(ctx.GetAllocator(), "Qt3DSRenderSceneManager::m_Scenes") , m_RenderPlugins(ctx.GetAllocator(), "Qt3DSRenderSceneManager::m_RenderPlugins") , m_ViewWidth(0) , m_ViewHeight(0) , m_SourcePaths(ctx.GetAllocator(), "Qt3DSRenderSceneManager::m_SourcePaths") , m_LastRenderedScene(nullptr) , m_WindowSystem(inWindowSystem) , m_LoadingScenesMutex(ctx.GetAllocator()) , m_ProjectInitialized(false) , mRefCount(0) { memZero(&m_PickFrame, sizeof(m_PickFrame)); } virtual ~Qt3DSRenderSceneManager() override { for (QT3DSU32 idx = 0, end = m_Scenes.size(); idx < end; ++idx) m_Scenes[idx].second->Release(); m_Scenes.clear(); } QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Context->GetAllocator()) static QT3DSU32 GetFileTag() { const char *fileTag = "Qt3DSS"; const QT3DSU32 *theTagPtr = reinterpret_cast(fileTag); return *theTagPtr; } void FinalizeScene(Q3DStudio::IPresentation &inPresentation, Qt3DSRenderScene &inScene) { inPresentation.SetScene(&inScene); if (m_ProjectInitialized == false) { m_ProjectInitialized = true; // For QT3DS-3353 assume project fonts are in a subdirectory relative to presentation. QString projectFontDir = inPresentation.getProjectPath() + QStringLiteral("/fonts"); if (m_Context->m_Context->GetTextRenderer()) { m_Context->m_Context->GetTextRenderer()->AddProjectFontDirectory( projectFontDir.toUtf8().data()); } if (m_Context->m_Context->getDistanceFieldRenderer()) { m_Context->m_Context->getDistanceFieldRenderer()->AddProjectFontDirectory( projectFontDir.toUtf8().data()); } eastl::string theBinaryPath(inPresentation.GetFilePath().toLatin1().constData()); qt3ds::foundation::CFileTools::AppendDirectoryInPathToFile(theBinaryPath, "binary"); eastl::string theBinaryDir(theBinaryPath); qt3ds::foundation::CFileTools::GetDirectory(theBinaryDir); if (m_Context->m_WriteOutShaderCache) qt3ds::foundation::CFileTools::CreateDir(theBinaryDir.c_str()); } inScene.m_RuntimePresentation = &inPresentation; m_Scenes.push_back(make_pair(&inPresentation, &inScene)); Qt3DSTranslator::AssignUserData(inPresentation, *inScene.m_Presentation); } static const char *GetBinaryExtension() { return "uibsg"; } struct SPluginInstanceTableProvider : public Q3DStudio::IScriptTableProvider { IRenderPluginInstance &m_Instance; SPluginInstanceTableProvider(IRenderPluginInstance &ins) : m_Instance(ins) { } void CreateTable(script_State *inState) override { m_Instance.CreateScriptProxy(inState); } }; void InitializeTranslator(Qt3DSTranslator &inTranslator, Q3DStudio::IScriptBridge &inBridge) { if (inTranslator.m_RenderObject->m_Type == GraphObjectTypes::RenderPlugin) { SRenderPlugin &theRenderPlugin = static_cast(*inTranslator.m_RenderObject); IRenderPluginInstance *thePluginInstance = m_Context->m_Context->GetRenderPluginManager().GetOrCreateRenderPluginInstance( theRenderPlugin.m_PluginPath, &theRenderPlugin); if (thePluginInstance) { SPluginInstanceTableProvider theProvider(*thePluginInstance); inBridge.SetTableForElement(*inTranslator.m_Element, theProvider); } } } struct SResolver : public qt3ds::render::IUIPReferenceResolver { IStringTable &m_StringTable; Q3DStudio::IUIPParser &m_Parser; SResolver(IStringTable &strt, Q3DStudio::IUIPParser &p) : m_StringTable(strt) , m_Parser(p) { } CRegisteredString ResolveReference(CRegisteredString inStart, const char *inReference) override { eastl::string theResolvedId = m_Parser.ResolveReference(inStart, inReference); if (theResolvedId.size()) return m_StringTable.RegisterStr(theResolvedId.c_str()); return CRegisteredString(); } }; Q3DStudio::IScene *LoadScene(Q3DStudio::IPresentation *inPresentation, Q3DStudio::IUIPParser *inParser, Q3DStudio::IScriptBridge &inBridge, const qt3ds::Q3DSVariantConfig &variantConfig) override { // We have to initialize the tags late so that we can load flow data before adding anything // to the string table. Qt3DSTranslator::InitializePointerTags(m_Context->m_RenderContext->GetStringTable()); NVScopedRefCounted theScene = QT3DS_NEW(m_Context->GetAllocator(), SSceneLoadData)(m_Context->GetAllocator()); Qt3DSRenderScene *theIScene = nullptr; if (inParser) { QString thePath(inPresentation->GetFilePath()); QFileInfo fileInfo(thePath); TIdObjectMap theObjMap(m_Context->GetAllocator(), "LoadScene::theObjMap"); SResolver theResolver(m_Context->m_CoreContext->GetStringTable(), *inParser); theScene->m_Presentation = IUIPLoader::LoadUIPFile( inParser->GetDOMReader(), fileInfo.absoluteFilePath().toLatin1().constData(), inParser->GetMetaData(), m_Context->m_CoreContext->GetStringTable(), m_Context->m_RenderContext->GetFoundation(), theScene->m_AutoAllocator, theObjMap, m_Context->m_Context->GetBufferManager(), m_Context->m_Context->GetEffectSystem(), fileInfo.path().toLatin1().constData(), m_Context->m_Context->GetRenderPluginManager(), m_Context->m_Context->GetCustomMaterialSystem(), m_Context->m_Context->GetDynamicObjectSystem(), m_Context->m_Context->GetPathManager(), &theResolver, variantConfig, false); if (!theScene->m_Presentation) { QT3DS_ASSERT(false); return nullptr; } NVConstDataRef theSourcePathData(inParser->GetSourcePaths()); const QVector slideSourcePaths = inParser->GetSlideSourcePaths(); IBufferManager &theManager(m_Context->m_Context->GetBufferManager()); // List of image paths to be loaded in parallel at the end. eastl::vector theSourcePathList; eastl::vector iblList; for (QT3DSU32 idx = 0, end = theSourcePathData.size(); idx < end; ++idx) { const eastl::string &theValue = theSourcePathData[idx]; CRegisteredString theSourcePath = m_Context->m_CoreContext->GetStringTable().RegisterStr(theValue.c_str()); size_t theValueSize = theValue.size(); if (theValueSize > 3) { CRegisteredString theObjectPath = theSourcePath; if (qt3ds::runtime::isImagePath(theValue.c_str())) { // load only images not on any slide if (!theManager.isReloadableResourcesEnabled() || !slideSourcePaths.contains(QString::fromLatin1(theValue.c_str()))) { theManager.SetImageTransparencyToFalseIfNotSet(theObjectPath); bool ibl = inParser->isIblImage(theObjectPath.c_str()); bool transparent = theManager.GetImageHasTransparency(theObjectPath); if (m_SourcePathSet.insert(theSourcePath).second) { m_SourcePaths.push_back(eastl::make_pair(theSourcePath, eastl::make_pair(transparent, ibl))); } if (ibl) iblList.push_back(theObjectPath); else theSourcePathList.push_back(theObjectPath); } } else if (theValue.find(".mesh") != eastl::string::npos) { theManager.LoadMesh(theObjectPath); } } } // Fire off parallel loading of the source paths QT3DSU64 imageBatchId = m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( toConstDataRef(theSourcePathList.data(), theSourcePathList.size()), CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() .GetRenderContextType(), theScene->m_Presentation->m_preferKTX, false); QT3DSU64 iblImageBatchId = m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( toConstDataRef(iblList.data(), iblList.size()), CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() .GetRenderContextType(), theScene->m_Presentation->m_preferKTX, true); m_Context->m_Context->GetImageBatchLoader().BlockUntilLoaded( static_cast(imageBatchId)); m_Context->m_Context->GetImageBatchLoader().BlockUntilLoaded( static_cast(iblImageBatchId)); theIScene = QT3DS_NEW(m_Context->GetAllocator(), Qt3DSRenderScene)(*m_Context, *m_Context->m_Context, *theScene); // Now we need to associate the presentation with everything else. NVAllocatorCallback &translatorAllocator = theScene->m_AutoAllocator; for (TIdObjectMap::iterator iter = theObjMap.begin(), end = theObjMap.end(); iter != end; ++iter) { theIScene->m_GraphObjectList.push_back(iter->second); Q3DStudio::SElementAndType theElement = inParser->GetElementForID(iter->first.c_str()); if (theElement.m_Element && theElement.m_Type != Q3DStudio::UIPElementTypes::Unknown) { Qt3DSTranslator *theTranslator = Qt3DSTranslator::CreateTranslatorForElement( *theElement.m_Element, *iter->second, translatorAllocator); if (theTranslator) { theIScene->m_DirtySet.insert(*theTranslator); InitializeTranslator(*theTranslator, inBridge); } } } theIScene->PostLoadStep(); } else { // Binary load path is quite different than normal load path and // nothing else will load here. QT3DS_ASSERT(false); } if (inPresentation && theIScene) FinalizeScene(*inPresentation, *theIScene); return theIScene; } // threadsafe // Can be called from any thread bool GetBinaryLoadFileName(eastl::string &inPresentationFilename, eastl::string &outResult) override { eastl::string theBinaryPath(inPresentationFilename.c_str()); qt3ds::foundation::CFileTools::AppendDirectoryInPathToFile(theBinaryPath, "binary"); qt3ds::foundation::CFileTools::SetExtension( theBinaryPath, GetBinaryExtension()); // uibb: short for ui binary binding outResult = theBinaryPath; return true; } // threadsafe // returns a handle to the loaded object. Return value of zero means error. qt3ds::QT3DSU32 LoadSceneStage1(CRegisteredString inPresentationDirectory, qt3ds::render::ILoadedBuffer &inData) override { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load Scene Graph Stage 1"); NVDataRef theLoadedData(inData.Data()); SDataReader theReader(theLoadedData.begin(), theLoadedData.end()); QT3DSU32 theFileSig = theReader.LoadRef(); QT3DSU32 theBinaryVersion = theReader.LoadRef(); QT3DSU32 theDataSectionSize = QT3DSU32(theReader.m_EndPtr - theReader.m_CurrentPtr); if (theFileSig != GetFileTag() || theBinaryVersion != SGraphObject::GetSceneGraphBinaryVersion()) { QT3DS_ASSERT(false); return 0; } QT3DSU32 theLoadingSceneIndex = 0; SSceneLoadData *theScene; { Mutex::ScopedLock __locker(m_LoadingScenesMutex); theLoadingSceneIndex = QT3DSU32(m_LoadingScenes.size()) + 1; m_LoadingScenes.push_back( QT3DS_NEW(m_Context->GetAllocator(), SSceneLoadData)(m_Context->GetAllocator())); theScene = m_LoadingScenes.back(); } // preserve the data buffer because we run directly from it; there isn't a memcopy. theScene->m_Data = inData; QT3DSU32 theTranslatorOffset = theReader.LoadRef(); NVDataRef theSGData = NVDataRef(theReader.m_CurrentPtr, theTranslatorOffset); NVDataRef theTranslatorData = NVDataRef( theReader.m_CurrentPtr + theTranslatorOffset, theDataSectionSize - theTranslatorOffset); CStrTableOrDataRef theStrTableData(m_Context->m_CoreContext->GetStringTable()); if (m_StrTableData.size()) theStrTableData = CStrTableOrDataRef(m_StrTableData); theScene->m_Presentation = SGraphObjectSerializer::Load( theSGData, m_StrTableData, m_Context->m_CoreContext->GetDynamicObjectSystemCore(), m_Context->m_CoreContext->GetPathManagerCore(), m_Context->GetAllocator(), inPresentationDirectory); if (theScene->m_Presentation) theScene->m_Presentation->m_PresentationDirectory = inPresentationDirectory; theScene->m_TranslatorData = theTranslatorData; theScene->m_SceneGraphData = theSGData; { Mutex::ScopedLock __locker(m_LoadingScenesMutex); m_LoadingScenes[theLoadingSceneIndex - 1] = theScene; } return theLoadingSceneIndex; } // threadsafe // still does not require openGL context but has dependency on a few other things. void LoadSceneStage2(qt3ds::QT3DSU32 inSceneHandle, Q3DStudio::IPresentation &inPresentation, size_t inElementMemoryOffset, Q3DStudio::IScriptBridge &inBridge) override { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load Scene Graph Stage 2"); QT3DSU32 theSceneIndex = QT3DS_MAX_U32; SSceneLoadData *theScene; { Mutex::ScopedLock __locker(m_LoadingScenesMutex); QT3DSU32 numLoadingScenes = QT3DSU32(m_LoadingScenes.size()); if (inSceneHandle && inSceneHandle <= numLoadingScenes) { theSceneIndex = inSceneHandle - 1; theScene = m_LoadingScenes[theSceneIndex]; } else { QT3DS_ASSERT(false); return; } } SDataReader theReader(theScene->m_TranslatorData.begin(), theScene->m_TranslatorData.end()); QT3DSU32 theNumTranslators = theReader.LoadRef(); theScene->m_Translators.resize(theNumTranslators); theScene->m_RuntimePresentation = &inPresentation; for (QT3DSU32 idx = 0, end = theNumTranslators; idx < end; ++idx) { Qt3DSTranslator *theTranslator = Qt3DSTranslator::LoadTranslator( theReader, inElementMemoryOffset, theScene->m_SceneGraphData, theScene->m_AutoAllocator); if (theTranslator) { InitializeTranslator(*theTranslator, inBridge); } theScene->m_Translators[idx] = theTranslator; } } void OnGraphicsInitialized() { QT3DS_ASSERT(m_Context->m_Context.mPtr); // this means graphics have been initialized eastl::string theSourcePathStr; IBufferManager &theManager(m_Context->m_Context->GetBufferManager()); nvvector imagePathList(m_Context->GetAllocator(), "imagePathList"); nvvector iblImagePathList(m_Context->GetAllocator(), "iblImagePathList"); for (QT3DSU32 idx = 0, end = m_SourcePaths.size(); idx < end; ++idx) { theSourcePathStr.assign(m_SourcePaths[idx].first); bool hasTransparency = m_SourcePaths[idx].second.first; bool isIbl = m_SourcePaths[idx].second.second; if (theSourcePathStr.size() > 4) { CRegisteredString theObjectPath = m_SourcePaths[idx].first; if (qt3ds::runtime::isImagePath(theSourcePathStr.c_str())) { theManager.SetImageHasTransparency(theObjectPath, hasTransparency); if (isIbl) iblImagePathList.push_back(theObjectPath); else imagePathList.push_back(theObjectPath); } else { if (theSourcePathStr.find(".mesh") != eastl::string::npos) theManager.LoadMesh(theObjectPath); } } } bool pktx = false; for (unsigned int i = 0; i < m_Scenes.size(); ++i) { if (m_Scenes[i].second->m_Presentation->m_preferKTX) { pktx = true; break; } } { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Initial Batch Image Load"); m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( toConstDataRef(imagePathList.data(), imagePathList.size()), CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() .GetRenderContextType(), pktx, false); m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( toConstDataRef(iblImagePathList.data(), iblImagePathList.size()), CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() .GetRenderContextType(), pktx, true); } { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Initialize Scenes"); for (QT3DSU32 idx = 0, end = m_LoadingScenes.size(); idx < end; ++idx) { SSceneLoadData &theScene = *m_LoadingScenes[idx]; // m_Context->m_Foundation->error( QT3DS_WARN, "Finalizing scene %d", (int)idx+1 ); if (theScene.m_RuntimePresentation) { Qt3DSRenderScene *theIScene = QT3DS_NEW(m_Context->GetAllocator(), Qt3DSRenderScene)( *m_Context, *m_Context->m_Context, theScene); FinalizeScene(*theScene.m_RuntimePresentation, *theIScene); theIScene->PostLoadStep(); } else { qCWarning(WARNING, "Failed to finalize scene %d", int(idx + 1)); } } } } void LoadRenderPlugin(const char *inAssetIDString, const char *inPath, const char *inArgs) override { Q3DStudio::CDLLManager &theDLLManager = Q3DStudio::CDLLManager::GetDLLManager(); long theHandle = theDLLManager.LoadLibrary(inPath, EDLLTYPE_RENDERABLE_PLUGIN); if (theHandle >= 0) { qt3ds::render::IOffscreenRenderer *theOffscreenRenderer = QT3DS_NEW(m_Context->GetAllocator(), qt3ds::render::COldNBustedPluginRenderer)(*m_Context->m_Context, theHandle); qt3ds::foundation::CRegisteredString theAssetString = m_Context->m_CoreContext->GetStringTable().RegisterStr(inAssetIDString); m_Context->m_Context->GetOffscreenRenderManager().RegisterOffscreenRenderer( theAssetString, *theOffscreenRenderer); PROC_Initialize theInitializeProc = reinterpret_cast(theDLLManager.GetProc("Initialize", theHandle)); Q3DStudio_ASSERT(theInitializeProc); #if !defined (Q_OS_MACOS) PROC_SetEGLInfo theSetEGLInfoProc = reinterpret_cast(theDLLManager.GetProc("SetEGLInfo", theHandle)); // Set EGL parameters used for optional context creation if (theSetEGLInfoProc) { Q3DStudio::SEGLInfo *theInfo = m_WindowSystem.GetEGLInfo(); if (theInfo) theSetEGLInfoProc(theInfo->display, theInfo->context, theInfo->surface, theInfo->config); } #endif if (theInitializeProc && theInitializeProc(inArgs) == EDLLSTATUS_OK) m_RenderPlugins.push_back(make_pair(theAssetString, theHandle)); else qCWarning(qt3ds::INVALID_OPERATION) << "Unable to load plugin " << inAssetIDString; } else qCWarning(qt3ds::INVALID_OPERATION) << "Unable to load plugin " << inAssetIDString; return; } void LoadQmlStreamerPlugin(const char *inAssetIDString) override { qt3ds::render::IOffscreenRenderer *theOffscreenRenderer = QT3DS_NEW(m_Context->GetAllocator(), Q3DSQmlRender)(*m_Context->m_Context, inAssetIDString); if (theOffscreenRenderer) { qt3ds::foundation::CRegisteredString theAssetString = m_Context->m_CoreContext->GetStringTable().RegisterStr(inAssetIDString); m_Context->m_Context->GetOffscreenRenderManager().RegisterOffscreenRenderer( SOffscreenRendererKey(theAssetString), *theOffscreenRenderer); m_RenderPlugins.push_back(make_pair(theAssetString, 0)); } } void BinarySave(Q3DStudio::IScene &inScene) override { Qt3DSRenderScene &theScene = static_cast(inScene); qt3ds::render::SWriteBuffer theWriteBuffer(m_Context->GetAllocator(), "BinarySaveBuffer"); qt3ds::render::SPtrOffsetMap theSGOffsetMap(m_Context->GetAllocator(), "PointerOffsetMap"); // Start with some versioning and sanity checks. theWriteBuffer.write(GetFileTag()); theWriteBuffer.write(SGraphObject::GetSceneGraphBinaryVersion()); QT3DSU32 theTranslatorOffsetAddress = theWriteBuffer.size(); // Now the data section starts. Offsets should be relative to here, not the first // 8 bytes. theWriteBuffer.writeZeros(4); // offset where the translator data starts; QT3DSU32 theDataSectionStart = theWriteBuffer.size(); // These offsets are after we have read in the data section SGraphObjectSerializer::Save( m_Context->m_RenderContext->GetFoundation(), *theScene.m_Presentation, theWriteBuffer, m_Context->m_Context->GetDynamicObjectSystem(), m_Context->m_Context->GetPathManager(), theSGOffsetMap, m_Context->m_CoreContext->GetStringTable(), theScene.m_GraphObjectList); theWriteBuffer.align(sizeof(void *)); QT3DSU32 theTranslatorCountAddress = theWriteBuffer.size(); QT3DSU32 theTranslatorOffset = theTranslatorCountAddress - theDataSectionStart; theWriteBuffer.writeZeros(4); // Now write out the translators verbatim. We get an adjustment parameter on save that // allows a translation // from old element ptr->new element ptr. QT3DSU32 theTranslatorCount = 0; for (QT3DSU32 idx = 0, end = theScene.m_GraphObjectList.size(); idx < end; ++idx) { Qt3DSTranslator *theTranslator = Qt3DSTranslator::GetTranslatorFromGraphNode(*theScene.m_GraphObjectList[idx]); // Presentation nodes don't have translator if (theTranslator) { qt3ds::render::SPtrOffsetMap::iterator theIter = theSGOffsetMap.find(theScene.m_GraphObjectList[idx]); if (theIter != theSGOffsetMap.end()) { QT3DSU32 theOffset = theIter->second; theTranslator->Save(theWriteBuffer, theOffset); ++theTranslatorCount; } else { QT3DS_ASSERT(false); } } } QT3DSU32 *theTranslatorCountPtr = reinterpret_cast(theWriteBuffer.begin() + theTranslatorCountAddress); *theTranslatorCountPtr = theTranslatorCount; QT3DSU32 *theTranslatorOffsetPtr = reinterpret_cast(theWriteBuffer.begin() + theTranslatorOffsetAddress); *theTranslatorOffsetPtr = theTranslatorOffset; Q3DStudio::IPresentation &thePresentation = *theScene.m_RuntimePresentation; eastl::string theBinaryPath(thePresentation.GetFilePath().toLatin1().constData()); qt3ds::foundation::CFileTools::AppendDirectoryInPathToFile(theBinaryPath, "binary"); eastl::string theBinaryDir(theBinaryPath); qt3ds::foundation::CFileTools::GetDirectory(theBinaryDir); qt3ds::foundation::CFileTools::SetExtension( theBinaryPath, GetBinaryExtension()); // uibb: short for ui binary binding Q3DStudio::CFileStream theStream(theBinaryPath.c_str(), "wb"); if (theStream.IsOpen() == false) { QT3DS_ASSERT(false); } theStream.WriteRaw(theWriteBuffer.begin(), theWriteBuffer.size()); theStream.Close(); } // We save in the reverse order that we load because the effect system may add strings // to the string table when it is writing its data out, this the string table needs to come // last. // Loading, obviously, the string table needs to be the first object loaded. void BinarySaveManagerData(qt3ds::foundation::IOutStream &inStream, const char *inBinaryDir) override { qt3ds::render::SWriteBuffer theWriteBuffer(m_Context->GetAllocator(), "BinarySaveBuffer"); IStringTable &theStrTable = m_Context->m_CoreContext->GetStringTable(); eastl::string theProjectDir(inBinaryDir); qt3ds::foundation::CFileTools::GetDirectory(theProjectDir); // We save everything before the string table because often times saving something creates // new strings. theWriteBuffer.writeZeros(4); // Total data size // Dynamic object system theWriteBuffer.writeZeros(4); // Effect system offset // effect system theWriteBuffer.writeZeros(4); // Material system offset // material system theWriteBuffer.writeZeros(4); // Binary path offset // binary path data theWriteBuffer.writeZeros(4); // Plugin manager offset // plugin manager theWriteBuffer.writeZeros(4); // String system offset // string system last. QT3DSU32 theOffsetStart = theWriteBuffer.size(); m_Context->m_Context->GetDynamicObjectSystem().Save( theWriteBuffer, theStrTable.GetRemapMap(), theProjectDir.c_str()); theWriteBuffer.align(sizeof(void *)); QT3DSU32 theEffectSystemOffset = theWriteBuffer.size() - theOffsetStart; m_Context->m_Context->GetEffectSystem().Save(theWriteBuffer, theStrTable.GetRemapMap(), theProjectDir.c_str()); theWriteBuffer.align(sizeof(void *)); QT3DSU32 theMaterialSystemOffset = theWriteBuffer.size() - theOffsetStart; m_Context->m_Context->GetCustomMaterialSystem().Save( theWriteBuffer, theStrTable.GetRemapMap(), theProjectDir.c_str()); QT3DSU32 theBinaryPathOffset = theWriteBuffer.size() - theOffsetStart; theWriteBuffer.write(QT3DSU32(m_SourcePaths.size())); for (nvvector>>::iterator iter = m_SourcePaths.begin(), end = m_SourcePaths.end(); iter != end; ++iter) { CRegisteredString theStr(iter->first); theStr.Remap(theStrTable.GetRemapMap()); theWriteBuffer.write(size_t(theStr.c_str())); QT3DSU32 theSourcePathFlags = iter->second.first ? 1 : 0; theSourcePathFlags |= iter->second.second ? 2 : 0; theWriteBuffer.write(theSourcePathFlags); } QT3DSU32 thePluginManagerOffset = theWriteBuffer.size() - theOffsetStart; m_Context->m_Context->GetRenderPluginManager().Save( theWriteBuffer, theStrTable.GetRemapMap(), theProjectDir.c_str()); QT3DSU32 theStringTableOffset = theWriteBuffer.size() - theOffsetStart; theStrTable.Save(theWriteBuffer); QT3DSU32 *theSizePtr = reinterpret_cast(theWriteBuffer.begin()); theSizePtr[0] = theWriteBuffer.size() - 4; // overall size theSizePtr[1] = theEffectSystemOffset; theSizePtr[2] = theMaterialSystemOffset; theSizePtr[3] = theBinaryPathOffset; // thePathOffset theSizePtr[4] = thePluginManagerOffset; theSizePtr[5] = theStringTableOffset; inStream.Write(theWriteBuffer.begin(), theWriteBuffer.size()); } NVDataRef BinaryLoadManagerData(qt3ds::foundation::IInStream &inStream, const char *inBinaryDir) override { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - String Table + Render Objects"); QT3DS_ASSERT(m_Context->m_FlowData == nullptr); QT3DSU32 dataSize = 0; inStream.Read(dataSize); m_Context->m_FlowData = (QT3DSU8 *)m_Context->m_CoreContext->GetAllocator().allocate( dataSize, "SceneManager::BinaryFlowData", __FILE__, __LINE__); { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - Initial Data Load"); inStream.Read(m_Context->m_FlowData, dataSize); } SDataReader theReader(m_Context->m_FlowData, m_Context->m_FlowData + dataSize); QT3DSU32 theEffectSystemOffset = theReader.LoadRef(); QT3DSU32 theMaterialSystemOffset = theReader.LoadRef(); QT3DSU32 theBinaryPathOffset = theReader.LoadRef(); QT3DSU32 thePluginManagerOffset = theReader.LoadRef(); QT3DSU32 theStringTableOffset = theReader.LoadRef(); QT3DSU8 *theStartOffset = theReader.m_CurrentPtr; IStringTable &theStrTable = m_Context->m_CoreContext->GetStringTable(); // Load string table. { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - Load String Table"); m_StrTableData = toDataRef(theReader.m_CurrentPtr + theStringTableOffset, dataSize - theStringTableOffset); theStrTable.Load(m_StrTableData); } // Load source paths to preload heavy data theReader.m_CurrentPtr = theStartOffset + theBinaryPathOffset; QT3DSU32 theNumSourcePaths = theReader.LoadRef(); eastl::string theSourcePathStr; eastl::string theProjectDir(inBinaryDir); eastl::string theShaderCacheDir(inBinaryDir); // Up one moves to the project directory. qt3ds::foundation::CFileTools::GetDirectory(theProjectDir); const char8_t *theBasePath(theProjectDir.c_str()); m_ProjectDir = theStrTable.RegisterStr(theProjectDir.c_str()); m_BinaryDir = theStrTable.RegisterStr(theShaderCacheDir.c_str()); // Preload the heavy buffers m_SourcePaths.resize(theNumSourcePaths); for (QT3DSU32 idx = 0, end = theNumSourcePaths; idx < end; ++idx) { CRegisteredString thePath = theReader.LoadRef(); QT3DSU32 theFlags = theReader.LoadRef(); thePath.Remap(m_StrTableData); bool theBoolFlagValue = theFlags ? true : false; m_SourcePaths[idx] = eastl::make_pair(thePath, theBoolFlagValue); } { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - Base Dynamic System"); // Load effect system. NVDataRef theDynamicSystemData(theStartOffset, theEffectSystemOffset); m_Context->m_CoreContext->GetDynamicObjectSystemCore().Load( theDynamicSystemData, m_StrTableData, theBasePath); } { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - Effect System"); NVDataRef theEffectSystemData(theStartOffset + theEffectSystemOffset, theMaterialSystemOffset - theEffectSystemOffset); m_Context->m_CoreContext->GetEffectSystemCore().Load(theEffectSystemData, m_StrTableData, theBasePath); } { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - Material System"); NVDataRef theMaterialSystemData(theStartOffset + theMaterialSystemOffset, thePluginManagerOffset - theMaterialSystemOffset); m_Context->m_CoreContext->GetMaterialSystemCore().Load(theMaterialSystemData, m_StrTableData, theBasePath); } { SStackPerfTimer __perfTimer(m_Context->m_CoreContext->GetPerfTimer(), "Load UIAB - Plugin Manager Data"); NVDataRef thePluginManagerData(theStartOffset + thePluginManagerOffset, theStringTableOffset - thePluginManagerOffset); m_Context->m_CoreContext->GetRenderPluginCore().Load(thePluginManagerData, m_StrTableData, theBasePath); } return m_StrTableData; } virtual void DeleteScene(Q3DStudio::IPresentation *inPresentation) { QT3DSU32 idx; QT3DSU32 end; for (idx = 0, end = m_Scenes.size(); idx < end; ++idx) { if (m_Scenes[idx].first == inPresentation) break; } if (idx < m_Scenes.size()) { m_Scenes[idx].second->Release(); m_Scenes.erase(m_Scenes.begin() + idx); } } Q3DStudio::BOOL Update() override { bool theResult = false; long theSceneCount = m_Scenes.size(); for (size_t theSceneIndex = 0; theSceneIndex < theSceneCount; ++theSceneIndex) { Qt3DSRenderScene *theScene = m_Scenes[theSceneIndex].second; theResult |= theScene->Update(); } return theResult; } Q3DStudio::BOOL RenderPresentation(Q3DStudio::IPresentation *inPresentation, bool firstFrame) override { Qt3DSRenderScene *theFirstScene = nullptr; for (QT3DSU32 idx = 0, end = m_Scenes.size(); idx < end && theFirstScene == nullptr; ++idx) if (m_Scenes[idx].second->m_RuntimePresentation == inPresentation) theFirstScene = m_Scenes[idx].second; if (theFirstScene && theFirstScene->m_Presentation) { m_LastRenderedScene = theFirstScene; if (theFirstScene->m_Presentation->m_Scene && theFirstScene->m_Presentation->m_Scene->m_UseClearColor) { m_Context->m_Context->SetSceneColor( theFirstScene->m_Presentation->m_Scene->m_ClearColor); } else m_Context->m_Context->SetSceneColor(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); // Setup the render rotation *before* rendering so that the magic can happen on begin // render. if (m_Context->m_RenderRotationsEnabled) m_Context->m_Context->SetRenderRotation( theFirstScene->m_Presentation->m_PresentationRotation); else m_Context->m_Context->SetRenderRotation(RenderRotationValues::NoRotation); m_Context->m_Context->SetPresentationDimensions(QSize( int(theFirstScene->m_Presentation->m_PresentationDimensions.x), int(theFirstScene->m_Presentation->m_PresentationDimensions.y))); } m_Context->m_Context->BeginFrame(firstFrame); m_Context->m_RenderContext->ResetBlendState(); // How exactly does this work, I have no idea. // Should we only render the first scene and not every scene, perhaps? bool wasDirty = false; if (theFirstScene) wasDirty = theFirstScene->PrepareForRender(); else { m_Context->m_RenderContext->SetClearColor(QT3DSVec4(0, 0, 0, 0)); m_Context->m_RenderContext->Clear(qt3ds::render::NVRenderClearFlags( NVRenderClearValues::Color | NVRenderClearValues::Depth)); } m_Context->m_Context->RunRenderTasks(); if (theFirstScene) theFirstScene->Render(); m_Context->m_Context->EndFrame(); return wasDirty; } // I think render::check resize is called so this isn't necessary void OnViewResize(Q3DStudio::INT32 inViewWidth, Q3DStudio::INT32 inViewHeight) override { m_ViewWidth = inViewWidth; m_ViewHeight = inViewHeight; } void GetViewSize(Q3DStudio::INT32 &outWidth, Q3DStudio::INT32 &outHeight) override { outWidth = m_ViewWidth; outHeight = m_ViewHeight; } Q3DStudio::STextSizes GetDisplayDimensions(Q3DStudio::IPresentation *inPresentation) override { Qt3DSRenderScene *theFirstScene = nullptr; for (QT3DSU32 idx = 0, end = m_Scenes.size(); idx < end && theFirstScene == nullptr; ++idx) if (m_Scenes[idx].second->m_RuntimePresentation == inPresentation) theFirstScene = m_Scenes[idx].second; if (theFirstScene) { m_Context->m_Context->SetPresentationDimensions(QSize( int(theFirstScene->m_Presentation->m_PresentationDimensions.x), int(theFirstScene->m_Presentation->m_PresentationDimensions.y))); render::NVRenderRectF theDisplayViewport = m_Context->m_Context->GetDisplayViewport(); return Q3DStudio::STextSizes( static_cast(theDisplayViewport.m_Width), static_cast(theDisplayViewport.m_Height)); } return Q3DStudio::STextSizes(static_cast(0), static_cast(0)); } Q3DStudio::TElement *UserPick(float mouseX, float mouseY) override { if (m_LastRenderedScene) { return m_LastRenderedScene->UserPick(mouseX, mouseY); } return nullptr; } Option FacePosition(Q3DStudio::TElement &inElement, float mouseX, float mouseY, NVDataRef inElements, Q3DStudio::FacePositionPlanes::Enum inPlane) override { if (m_LastRenderedScene) { return m_LastRenderedScene->FacePosition(inElement, mouseX, mouseY, inElements, inPlane); } return Empty(); } Q3DStudio::SPickFrame AdvancePickFrame(const Q3DStudio::SInputFrame &inInputFrame) override { // We now have a new input frame, and our results are invalid but ready to be filled m_PickFrame.m_InputFrame = inInputFrame; m_PickFrame.m_Model = nullptr; m_PickFrame.m_ResultValid = false; if (m_LastRenderedScene) { if (m_PickFrame.m_InputFrame.m_PickValid) m_LastRenderedScene->Pick(m_PickFrame); } return m_PickFrame; } void Release() override { long theRenderPluginSize = m_RenderPlugins.size(); Q3DStudio::CDLLManager &theDLLManager = Q3DStudio::CDLLManager::GetDLLManager(); for (int theRenderPluginIndex = 0; theRenderPluginIndex < theRenderPluginSize; ++theRenderPluginIndex) { long theDLLHandle = m_RenderPlugins[theRenderPluginIndex].second; PROC_Uninitialize theUninitializeProc = reinterpret_cast( theDLLManager.GetProc("Uninitialize", theDLLHandle)); Q3DStudio_ASSERT(theUninitializeProc); theUninitializeProc &&theUninitializeProc(); theDLLManager.UnloadLibrary(theDLLHandle); } // Ensure the binding core doesn't get released until after we get released. NVScopedRefCounted theContext(m_Context); NVDelete(m_Context->GetAllocator(), this); } }; struct SRenderFactory; struct SRenderFactory : public IQt3DSRenderFactoryCore, public IQt3DSRenderFactory { NVScopedRefCounted m_Context; NVScopedRefCounted m_ScriptBridgeQml; NVScopedRefCounted m_SceneManager; NVScopedRefCounted m_EventSystem; qt3ds::runtime::IApplication *m_Application; QT3DSI32 m_RefCount; SRenderFactory(SBindingCore &inCore) : m_Context(inCore) , m_ScriptBridgeQml(nullptr) , m_SceneManager(nullptr) , m_Application(nullptr) , m_RefCount(0) { } ~SRenderFactory() { using namespace Q3DStudio; // Release the event system, it must be released before script engine m_EventSystem = nullptr; m_ScriptBridgeQml->Shutdown(*m_Context->m_Foundation); } void addRef() override { atomicIncrement(&m_RefCount); } void release() override { atomicDecrement(&m_RefCount); if (m_RefCount <= 0) { NVScopedRefCounted theContext(m_Context); NVDelete(m_Context->GetAllocator(), this); } } qt3ds::render::IQt3DSRenderContextCore &GetRenderContextCore() override { return *m_Context->m_CoreContext; } qt3ds::runtime::IApplication *GetApplicationCore() override { return m_Application; } void SetApplicationCore(qt3ds::runtime::IApplication *app) override { m_Application = app; } Q3DStudio::ISceneBinaryLoader &GetSceneLoader() override { if (m_SceneManager == nullptr) m_SceneManager = QT3DS_NEW(m_Context->GetAllocator(), Qt3DSRenderSceneManager)(*m_Context, m_Context->m_WindowSystem); return *m_SceneManager; } Q3DStudio::ITegraApplicationRenderEngine &CreateRenderEngine() override { return m_Context->CreateRenderer(); } Q3DStudio::ISceneManager &GetSceneManager() override { if (m_SceneManager == nullptr) m_SceneManager = QT3DS_NEW(m_Context->GetAllocator(), Qt3DSRenderSceneManager)(*m_Context, m_Context->m_WindowSystem); return *m_SceneManager; } Q3DStudio::IScriptBridge &GetScriptEngineQml() override { if (m_ScriptBridgeQml == nullptr) { m_ScriptBridgeQml = Q3DStudio::CQmlEngine::Create(*m_Context->m_Foundation, m_Context->m_TimeProvider); } return *m_ScriptBridgeQml; } qt3ds::render::IInputStreamFactory &GetInputStreamFactory() override { return m_Context->m_CoreContext->GetInputStreamFactory(); } qt3ds::render::IQt3DSRenderContext &GetQt3DSRenderContext() override { return *m_Context->m_Context; } qt3ds::evt::IEventSystem &GetEventSystem() override { if (!m_EventSystem) { m_EventSystem = qt3ds::evt::IEventSystem::Create(*m_Context->m_Foundation); } return *m_EventSystem; } Q3DStudio::ITimeProvider &GetTimeProvider() override { return m_Context->m_TimeProvider; } qt3ds::foundation::IStringTable &GetStringTable() override { return m_Context->m_CoreContext->GetStringTable(); } qt3ds::NVFoundationBase &GetFoundation() override { return *m_Context->m_Foundation.mPtr; } qt3ds::foundation::IPerfTimer &GetPerfTimer() override { return m_Context->m_CoreContext->GetPerfTimer(); } qt3ds::runtime::IApplication *GetApplication() override { return m_Application; } void SetApplication(qt3ds::runtime::IApplication *app) override { m_Application = app; if (app) { // QML engine GetScriptEngineQml(); m_ScriptBridgeQml->SetApplication(*app); m_ScriptBridgeQml->Initialize(); } } void SetDllDir(const char *dllDir) override { m_Context->m_CoreContext->GetRenderPluginCore().SetDllDir(dllDir); } void AddSearchPath(const char8_t *inFile) override { m_Context->m_CoreContext->GetInputStreamFactory().AddSearchDirectory(inFile); } virtual void Release() { NVDelete(m_Context->GetAllocator(), this); } struct SContextTypeRenderFactory : public IRuntimeFactoryRenderFactory { QSurfaceFormat format; SContextTypeRenderFactory(const QSurfaceFormat &fmt) : format(fmt) { } qt3ds::render::NVRenderContext *CreateRenderContext(qt3ds::NVFoundationBase &foundat, IStringTable &strt) override { #ifndef Qt3DS_NO_RENDER_SYMBOLS qt3ds::render::NVRenderContext &retval = NVRenderContext::CreateGL(foundat, strt, format); return &retval; #else qt3ds::render::NVRenderContext &retval = NVRenderContext::Createnullptr(foundat, strt); return &retval; #endif } }; IQt3DSRenderFactory &CreateRenderFactory(const QSurfaceFormat& format, bool delayedLoading) override { SContextTypeRenderFactory theContextFactory(format); m_Context->CreateRenderContext(theContextFactory, delayedLoading); GetSceneLoader(); { SStackPerfTimer __loadTimer(GetPerfTimer(), "SceneManager OnGraphicsInitialized"); m_SceneManager->OnGraphicsInitialized(); } return *this; } }; } IQt3DSRenderFactoryCore &IQt3DSRenderFactoryCore::CreateRenderFactoryCore( const char8_t *inApplicationDirectory, Q3DStudio::IWindowSystem &inWindowSystem, Q3DStudio::ITimeProvider &inTimeProvider) { SBindingCore *theCore = reinterpret_cast(malloc(sizeof(SBindingCore))); new (theCore) SBindingCore(inApplicationDirectory, inWindowSystem, inTimeProvider); return *QT3DS_NEW(theCore->GetAllocator(), SRenderFactory)(*theCore); }