diff options
Diffstat (limited to 'src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp')
-rw-r--r-- | src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp | 1477 |
1 files changed, 1477 insertions, 0 deletions
diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp new file mode 100644 index 0000000..8afa3c0 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp @@ -0,0 +1,1477 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 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 "Qt3DSRender.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRendererImpl.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderEffect.h" +#include "EASTL/sort.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderPresentation.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderResourceManager.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderEffectSystem.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "render/Qt3DSRenderRenderBuffer.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "Qt3DSRenderPlugin.h" +#include "Qt3DSRenderPluginGraphObject.h" +#include "Qt3DSRenderResourceBufferObjects.h" +#include "foundation/Qt3DSPerfTimer.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderBufferManager.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathManager.h" + +#ifdef _WIN32 +#pragma warning(disable : 4355) +#endif +namespace qt3ds { +namespace render { + using eastl::reverse; + using eastl::stable_sort; + using qt3ds::render::NVRenderContextScopedProperty; + using qt3ds::QT3DSVec2; + + namespace { + void MaybeQueueNodeForRender(SNode &inNode, nvvector<SRenderableNodeEntry> &outRenderables, + nvvector<SNode *> &outCamerasAndLights, QT3DSU32 &ioDFSIndex) + { + ++ioDFSIndex; + inNode.m_DFSIndex = ioDFSIndex; + if (GraphObjectTypes::IsRenderableType(inNode.m_Type)) + outRenderables.push_back(inNode); + else if (GraphObjectTypes::IsLightCameraType(inNode.m_Type)) + outCamerasAndLights.push_back(&inNode); + + for (SNode *theChild = inNode.m_FirstChild; theChild != NULL; + theChild = theChild->m_NextSibling) + MaybeQueueNodeForRender(*theChild, outRenderables, outCamerasAndLights, ioDFSIndex); + } + + bool HasValidLightProbe(SImage *inLightProbeImage) + { + return inLightProbeImage && inLightProbeImage->m_TextureData.m_Texture; + } + } + + SDefaultMaterialPreparationResult::SDefaultMaterialPreparationResult( + SShaderDefaultMaterialKey inKey) + : m_FirstImage(NULL) + , m_Opacity(1.0f) + , m_MaterialKey(inKey) + , m_Dirty(false) + { + } + +#define MAX_AA_LEVELS 8 + + SLayerRenderPreparationData::SLayerRenderPreparationData(SLayer &inLayer, + Qt3DSRendererImpl &inRenderer) + : m_Layer(inLayer) + , m_Renderer(inRenderer) + , m_Allocator(inRenderer.GetContext().GetAllocator()) + , m_RenderableNodeLightEntryPool( + ForwardingAllocator(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderableNodes")) + , m_RenderableNodes(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderableNodes") + , m_LightToNodeMap(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_LightToNodeMap") + , m_CamerasAndLights(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_CamerasAndLights") + , m_Camera(NULL) + , m_Lights(inRenderer.GetContext().GetAllocator(), "SLayerRenderPreparationData::m_Lights") + , m_OpaqueObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_OpaqueObjects") + , m_TransparentObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_TransparentObjects") + , m_RenderedOpaqueObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderedOpaqueObjects") + , m_RenderedTransparentObjects(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_RenderedTransparentObjects") + , m_IRenderWidgets(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_IRenderWidgets") + , m_SourceLightDirections(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_SourceLightDirections") + , m_LightDirections(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_LightDirections") + , m_ModelContexts(inRenderer.GetContext().GetAllocator(), + "SLayerRenderPreparationData::m_ModelContexts") + , m_CGLightingFeatureName( + inRenderer.GetContext().GetStringTable().RegisterStr("QT3DS_ENABLE_CG_LIGHTING")) + , m_FeaturesDirty(true) + , m_FeatureSetHash(0) + , m_TooManyLightsError(false) + { + } + + SLayerRenderPreparationData::~SLayerRenderPreparationData() {} + + bool SLayerRenderPreparationData::NeedsWidgetTexture() const + { + return m_IRenderWidgets.size() > 0; + } + + void SLayerRenderPreparationData::SetShaderFeature(CRegisteredString theStr, bool inValue) + { + SShaderPreprocessorFeature item(theStr, inValue); + eastl::vector<SShaderPreprocessorFeature>::iterator iter = m_Features.begin(), + end = m_Features.end(); + + // empty loop intentional. + for (; iter != end && ((iter->m_Name == theStr) == false); ++iter) + ; + + if (iter != end) { + if (iter->m_Enabled != inValue) { + iter->m_Enabled = inValue; + m_FeaturesDirty = true; + m_FeatureSetHash = 0; + } + } else { + m_Features.push_back(item); + m_FeaturesDirty = true; + m_FeatureSetHash = 0; + } + } + + void SLayerRenderPreparationData::SetShaderFeature(const char *inName, bool inValue) + { + CRegisteredString theStr(m_Renderer.GetQt3DSContext().GetStringTable().RegisterStr(inName)); + SetShaderFeature(theStr, inValue); + } + + NVConstDataRef<SShaderPreprocessorFeature> SLayerRenderPreparationData::GetShaderFeatureSet() + { + if (m_FeaturesDirty) { + eastl::sort(m_Features.begin(), m_Features.end()); + m_FeaturesDirty = false; + } + return toConstDataRef(m_Features.data(), (QT3DSU32)m_Features.size()); + } + + size_t SLayerRenderPreparationData::GetShaderFeatureSetHash() + { + if (!m_FeatureSetHash) + m_FeatureSetHash = HashShaderFeatureSet(GetShaderFeatureSet()); + return m_FeatureSetHash; + } + + bool SLayerRenderPreparationData::GetShadowMapManager() + { + if (m_ShadowMapManager.mPtr) + return true; + + m_ShadowMapManager.mPtr = Qt3DSShadowMap::Create(m_Renderer.GetQt3DSContext()); + + return m_ShadowMapManager.mPtr != NULL; + } + + bool SLayerRenderPreparationData::GetOffscreenRenderer() + { + if (m_LastFrameOffscreenRenderer.mPtr) + return true; + + if (m_Layer.m_RenderPlugin && m_Layer.m_RenderPlugin->m_Flags.IsActive()) { + IRenderPluginInstance *theInstance = + m_Renderer.GetQt3DSContext().GetRenderPluginManager().GetOrCreateRenderPluginInstance( + m_Layer.m_RenderPlugin->m_PluginPath, m_Layer.m_RenderPlugin); + if (theInstance) { + m_Renderer.GetQt3DSContext() + .GetOffscreenRenderManager() + .MaybeRegisterOffscreenRenderer(&theInstance, *theInstance); + m_LastFrameOffscreenRenderer = theInstance; + } + } + if (m_LastFrameOffscreenRenderer.mPtr == NULL) + m_LastFrameOffscreenRenderer = + m_Renderer.GetQt3DSContext().GetOffscreenRenderManager().GetOffscreenRenderer( + m_Layer.m_TexturePath); + return m_LastFrameOffscreenRenderer.mPtr != NULL; + } + + QT3DSVec3 SLayerRenderPreparationData::GetCameraDirection() + { + if (m_CameraDirection.hasValue() == false) { + if (m_Camera) + m_CameraDirection = m_Camera->GetScalingCorrectDirection(); + else + m_CameraDirection = QT3DSVec3(0, 0, -1); + } + return *m_CameraDirection; + } + + // Per-frame cache of renderable objects post-sort. + NVDataRef<SRenderableObject *> SLayerRenderPreparationData::GetOpaqueRenderableObjects() + { + if (m_RenderedOpaqueObjects.empty() == false || m_Camera == NULL) + return m_RenderedOpaqueObjects; + if (m_Layer.m_Flags.IsLayerEnableDepthTest() && m_OpaqueObjects.empty() == false) { + QT3DSVec3 theCameraDirection(GetCameraDirection()); + QT3DSVec3 theCameraPosition = m_Camera->GetGlobalPos(); + m_RenderedOpaqueObjects.assign(m_OpaqueObjects.begin(), m_OpaqueObjects.end()); + // Setup the object's sorting information + for (QT3DSU32 idx = 0, end = m_RenderedOpaqueObjects.size(); idx < end; ++idx) { + SRenderableObject &theInfo = *m_RenderedOpaqueObjects[idx]; + QT3DSVec3 difference = theInfo.m_WorldCenterPoint - theCameraPosition; + theInfo.m_CameraDistanceSq = difference.dot(theCameraDirection); + } + + ForwardingAllocator alloc(m_Renderer.GetPerFrameAllocator(), "SortAllocations"); + // Render nearest to furthest objects + eastl::merge_sort(m_RenderedOpaqueObjects.begin(), m_RenderedOpaqueObjects.end(), alloc, + ISRenderObjectPtrLessThan); + } + return m_RenderedOpaqueObjects; + } + + // If layer depth test is false, this may also contain opaque objects. + NVDataRef<SRenderableObject *> SLayerRenderPreparationData::GetTransparentRenderableObjects() + { + if (m_RenderedTransparentObjects.empty() == false || m_Camera == NULL) + return m_RenderedTransparentObjects; + + m_RenderedTransparentObjects.assign(m_TransparentObjects.begin(), + m_TransparentObjects.end()); + + if (m_Layer.m_Flags.IsLayerEnableDepthTest() == false) + m_RenderedTransparentObjects.insert(m_RenderedTransparentObjects.end(), + m_OpaqueObjects.begin(), m_OpaqueObjects.end()); + + if (m_RenderedTransparentObjects.empty() == false) { + QT3DSVec3 theCameraDirection(GetCameraDirection()); + QT3DSVec3 theCameraPosition = m_Camera->GetGlobalPos(); + + // Setup the object's sorting information + for (QT3DSU32 idx = 0, end = m_RenderedTransparentObjects.size(); idx < end; ++idx) { + SRenderableObject &theInfo = *m_RenderedTransparentObjects[idx]; + QT3DSVec3 difference = theInfo.m_WorldCenterPoint - theCameraPosition; + theInfo.m_CameraDistanceSq = difference.dot(theCameraDirection); + } + ForwardingAllocator alloc(m_Renderer.GetPerFrameAllocator(), "SortAllocations"); + // render furthest to nearest. + eastl::merge_sort(m_RenderedTransparentObjects.begin(), + m_RenderedTransparentObjects.end(), alloc, + ISRenderObjectPtrGreatThan); + } + + return m_RenderedTransparentObjects; + } + +#define MAX_LAYER_WIDGETS 200 + + void SLayerRenderPreparationData::AddRenderWidget(IRenderWidget &inWidget) + { + // The if the layer is not active then the widget can't be displayed. + // Furthermore ResetForFrame won't be called below which leads to stale + // widgets in the m_IRenderWidgets array. These stale widgets would get rendered + // the next time the layer was active potentially causing a crash. + if (!m_Layer.m_Flags.IsActive()) + return; + + // Ensure we clear the widget layer always + m_Renderer.LayerNeedsFrameClear(*static_cast<SLayerRenderData *>(this)); + + if (m_IRenderWidgets.size() < MAX_LAYER_WIDGETS) + m_IRenderWidgets.push_back(&inWidget); + } + +#define RENDER_FRAME_NEW(type) QT3DS_NEW(m_Renderer.GetPerFrameAllocator(), type) + +#define QT3DS_RENDER_MINIMUM_RENDER_OPACITY .01f + + SShaderDefaultMaterialKey + SLayerRenderPreparationData::GenerateLightingKey(DefaultMaterialLighting::Enum inLightingType) + { + SShaderDefaultMaterialKey theGeneratedKey(GetShaderFeatureSetHash()); + const bool lighting = inLightingType != DefaultMaterialLighting::NoLighting; + m_Renderer.DefaultMaterialShaderKeyProperties().m_HasLighting.SetValue(theGeneratedKey, + lighting); + if (lighting) { + const bool lightProbe = m_Layer.m_LightProbe + && m_Layer.m_LightProbe->m_TextureData.m_Texture; + m_Renderer.DefaultMaterialShaderKeyProperties().m_HasIbl.SetValue(theGeneratedKey, + lightProbe); + + QT3DSU32 numLights = (QT3DSU32)m_Lights.size(); + if (numLights > SShaderDefaultMaterialKeyProperties::LightCount + && m_TooManyLightsError == false) { + m_TooManyLightsError = true; + numLights = SShaderDefaultMaterialKeyProperties::LightCount; + qCCritical(INVALID_OPERATION, "Too many lights on layer, max is 7"); + QT3DS_ASSERT(false); + } + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightCount.SetValue(theGeneratedKey, + numLights); + + for (QT3DSU32 lightIdx = 0, lightEnd = m_Lights.size(); + lightIdx < lightEnd; ++lightIdx) { + SLight *theLight(m_Lights[lightIdx]); + const bool isDirectional = theLight->m_LightType == RenderLightTypes::Directional; + const bool isArea = theLight->m_LightType == RenderLightTypes::Area; + const bool castShadowsArea = (theLight->m_LightType != RenderLightTypes::Area) + && (theLight->m_CastShadow); + + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightFlags[lightIdx] + .SetValue(theGeneratedKey, !isDirectional); + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightAreaFlags[lightIdx] + .SetValue(theGeneratedKey, isArea); + m_Renderer.DefaultMaterialShaderKeyProperties().m_LightShadowFlags[lightIdx] + .SetValue(theGeneratedKey, castShadowsArea); + } + } + return theGeneratedKey; + } + + bool SLayerRenderPreparationData::PrepareTextForRender( + SText &inText, const QT3DSMat44 &inViewProjection, + QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags) + { + ITextTextureCache *theTextRenderer = m_Renderer.GetQt3DSContext().GetTextureCache(); + if (theTextRenderer == NULL) + return false; + + SRenderableObjectFlags theFlags; + theFlags.SetHasTransparency(true); + theFlags.SetCompletelyTransparent(inText.m_GlobalOpacity < .01f); + theFlags.SetPickable(true); + bool retval = false; + + if (theFlags.IsCompletelyTransparent() == false) { + retval = inText.m_Flags.IsDirty() || inText.m_Flags.IsTextDirty(); + inText.m_Flags.SetTextDirty(false); + QT3DSMat44 theMVP; + QT3DSMat33 theNormalMatrix; + inText.CalculateMVPAndNormalMatrix(inViewProjection, theMVP, theNormalMatrix); + + SRenderableObject *theRenderable = nullptr; + +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + Q3DSDistanceFieldRenderer *distanceFieldText + = static_cast<Q3DSDistanceFieldRenderer *>( + m_Renderer.GetQt3DSContext().getDistanceFieldRenderer()); + theRenderable = RENDER_FRAME_NEW(SDistanceFieldRenderable)( + theFlags, inText.GetGlobalPos(), inText, inText.m_Bounds, theMVP, + *distanceFieldText); +#else + TTPathObjectAndTexture theResult + = theTextRenderer->RenderText(inText, inTextScaleFactor); + inText.m_TextTexture = theResult.second.second.mPtr; + inText.m_TextTextureDetails = theResult.second.first; + inText.m_PathFontItem = theResult.first.second; + inText.m_PathFontDetails = theResult.first.first; + STextScaleAndOffset theScaleAndOffset(*inText.m_TextTexture, + inText.m_TextTextureDetails, inText); + QT3DSVec2 theTextScale(theScaleAndOffset.m_TextScale); + QT3DSVec2 theTextOffset(theScaleAndOffset.m_TextOffset); + QT3DSVec3 minimum(theTextOffset[0] - theTextScale[0], + theTextOffset[1] - theTextScale[1], 0); + QT3DSVec3 maximum(theTextOffset[0] + theTextScale[0], + theTextOffset[1] + theTextScale[1], 0); + inText.m_Bounds = NVBounds3(minimum, maximum); + + if (inText.m_PathFontDetails) + ioFlags.SetRequiresStencilBuffer(true); + + theRenderable = RENDER_FRAME_NEW(STextRenderable)( + theFlags, inText.GetGlobalPos(), m_Renderer, inText, inText.m_Bounds, theMVP, + inViewProjection, *inText.m_TextTexture, theTextOffset, theTextScale); +#endif + // After preparation, do not push object back to queue if it is not + // active, because we prepare text elements regardless of their + // visibility (=active status). + if (inText.m_Flags.IsGloballyActive()) + m_TransparentObjects.push_back(theRenderable); + } + return retval; + } + + eastl::pair<bool, SGraphObject *> + SLayerRenderPreparationData::ResolveReferenceMaterial(SGraphObject *inMaterial) + { + bool subsetDirty = false; + bool badIdea = false; + SGraphObject *theSourceMaterialObject(inMaterial); + SGraphObject *theMaterialObject(inMaterial); + while (theMaterialObject + && theMaterialObject->m_Type == GraphObjectTypes::ReferencedMaterial && !badIdea) { + SReferencedMaterial *theRefMaterial = + static_cast<SReferencedMaterial *>(theMaterialObject); + theMaterialObject = theRefMaterial->m_ReferencedMaterial; + if (theMaterialObject == theSourceMaterialObject) { + badIdea = true; + } + + if (theRefMaterial == theSourceMaterialObject) { + theRefMaterial->m_Dirty.UpdateDirtyForFrame(); + } + subsetDirty = subsetDirty | theRefMaterial->m_Dirty.IsDirty(); + } + if (badIdea) { + theMaterialObject = NULL; + } + return eastl::make_pair(subsetDirty, theMaterialObject); + } + + bool SLayerRenderPreparationData::PreparePathForRender( + SPath &inPath, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, SLayerRenderPreparationResultFlags &ioFlags) + { + SRenderableObjectFlags theSharedFlags; + theSharedFlags.SetPickable(true); + QT3DSF32 subsetOpacity = inPath.m_GlobalOpacity; + bool retval = inPath.m_Flags.IsDirty(); + inPath.m_Flags.SetDirty(false); + QT3DSMat44 theMVP; + QT3DSMat33 theNormalMatrix; + + inPath.CalculateMVPAndNormalMatrix(inViewProjection, theMVP, theNormalMatrix); + NVBounds3 theBounds(this->m_Renderer.GetQt3DSContext().GetPathManager().GetBounds(inPath)); + + if (inPath.m_GlobalOpacity >= QT3DS_RENDER_MINIMUM_RENDER_OPACITY + && inClipFrustum.hasValue()) { + // Check bounding box against the clipping planes + NVBounds3 theGlobalBounds = theBounds; + theGlobalBounds.transform(inPath.m_GlobalTransform); + if (inClipFrustum->intersectsWith(theGlobalBounds) == false) + subsetOpacity = 0.0f; + } + + SGraphObject *theMaterials[2] = { inPath.m_Material, inPath.m_SecondMaterial }; + + if (inPath.m_PathType == PathTypes::Geometry + || inPath.m_PaintStyle != PathPaintStyles::FilledAndStroked) + theMaterials[1] = NULL; + + // We need to fill material to be the first one rendered so the stroke goes on top. + // In the timeline, however, this is reversed. + + if (theMaterials[1]) + eastl::swap(theMaterials[1], theMaterials[0]); + + for (QT3DSU32 idx = 0, end = 2; idx < end; ++idx) { + if (theMaterials[idx] == NULL) + continue; + + SRenderableObjectFlags theFlags = theSharedFlags; + + eastl::pair<bool, SGraphObject *> theMaterialAndDirty( + ResolveReferenceMaterial(theMaterials[idx])); + SGraphObject *theMaterial(theMaterialAndDirty.second); + retval = retval || theMaterialAndDirty.first; + + if (theMaterial != NULL && theMaterial->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial *theDefaultMaterial = static_cast<SDefaultMaterial *>(theMaterial); + // Don't clear dirty flags if the material was referenced. + bool clearMaterialFlags = theMaterial == inPath.m_Material; + SDefaultMaterialPreparationResult prepResult(PrepareDefaultMaterialForRender( + *theDefaultMaterial, theFlags, subsetOpacity, clearMaterialFlags)); + + theFlags = prepResult.m_RenderableFlags; + if (inPath.m_PathType == PathTypes::Geometry) { + if ((inPath.m_BeginCapping != PathCapping::Noner + && inPath.m_BeginCapOpacity < 1.0f) + || (inPath.m_EndCapping != PathCapping::Noner + && inPath.m_EndCapOpacity < 1.0f)) + theFlags.SetHasTransparency(true); + } else { + ioFlags.SetRequiresStencilBuffer(true); + } + retval = retval || prepResult.m_Dirty; + bool isStroke = true; + if (idx == 0 && inPath.m_PathType == PathTypes::Painted) { + if (inPath.m_PaintStyle == PathPaintStyles::Filled + || inPath.m_PaintStyle == PathPaintStyles::FilledAndStroked) + isStroke = false; + } + + SPathRenderable *theRenderable = RENDER_FRAME_NEW(SPathRenderable)( + theFlags, inPath.GetGlobalPos(), m_Renderer, inPath.m_GlobalTransform, + theBounds, inPath, theMVP, theNormalMatrix, *theMaterial, prepResult.m_Opacity, + prepResult.m_MaterialKey, isStroke); + theRenderable->m_FirstImage = prepResult.m_FirstImage; + + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IPathManager &thePathManager = qt3dsContext.GetPathManager(); + retval = thePathManager.PrepareForRender(inPath) || retval; + retval |= (inPath.m_WireframeMode != qt3dsContext.GetWireframeMode()); + inPath.m_WireframeMode = qt3dsContext.GetWireframeMode(); + + if (theFlags.HasTransparency()) + m_TransparentObjects.push_back(theRenderable); + else + m_OpaqueObjects.push_back(theRenderable); + } else if (theMaterial != NULL + && theMaterial->m_Type == GraphObjectTypes::CustomMaterial) { + SCustomMaterial *theCustomMaterial = static_cast<SCustomMaterial *>(theMaterial); + // Don't clear dirty flags if the material was referenced. + // bool clearMaterialFlags = theMaterial == inPath.m_Material; + SDefaultMaterialPreparationResult prepResult( + PrepareCustomMaterialForRender(*theCustomMaterial, theFlags, subsetOpacity)); + + theFlags = prepResult.m_RenderableFlags; + if (inPath.m_PathType == PathTypes::Geometry) { + if ((inPath.m_BeginCapping != PathCapping::Noner + && inPath.m_BeginCapOpacity < 1.0f) + || (inPath.m_EndCapping != PathCapping::Noner + && inPath.m_EndCapOpacity < 1.0f)) + theFlags.SetHasTransparency(true); + } else { + ioFlags.SetRequiresStencilBuffer(true); + } + + retval = retval || prepResult.m_Dirty; + bool isStroke = true; + if (idx == 0 && inPath.m_PathType == PathTypes::Painted) { + if (inPath.m_PaintStyle == PathPaintStyles::Filled + || inPath.m_PaintStyle == PathPaintStyles::FilledAndStroked) + isStroke = false; + } + + SPathRenderable *theRenderable = RENDER_FRAME_NEW(SPathRenderable)( + theFlags, inPath.GetGlobalPos(), m_Renderer, inPath.m_GlobalTransform, + theBounds, inPath, theMVP, theNormalMatrix, *theMaterial, prepResult.m_Opacity, + prepResult.m_MaterialKey, isStroke); + theRenderable->m_FirstImage = prepResult.m_FirstImage; + + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IPathManager &thePathManager = qt3dsContext.GetPathManager(); + retval = thePathManager.PrepareForRender(inPath) || retval; + retval |= (inPath.m_WireframeMode != qt3dsContext.GetWireframeMode()); + inPath.m_WireframeMode = qt3dsContext.GetWireframeMode(); + + if (theFlags.HasTransparency()) + m_TransparentObjects.push_back(theRenderable); + else + m_OpaqueObjects.push_back(theRenderable); + } + } + return retval; + } + + void SLayerRenderPreparationData::PrepareImageForRender( + SImage &inImage, ImageMapTypes::Enum inMapType, SRenderableImage *&ioFirstImage, + SRenderableImage *&ioNextImage, SRenderableObjectFlags &ioFlags, + SShaderDefaultMaterialKey &inShaderKey, QT3DSU32 inImageIndex) + { + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IBufferManager &bufferManager = qt3dsContext.GetBufferManager(); + IOffscreenRenderManager &theOffscreenRenderManager( + qt3dsContext.GetOffscreenRenderManager()); + IRenderPluginManager &theRenderPluginManager(qt3dsContext.GetRenderPluginManager()); + if (inImage.ClearDirty(bufferManager, theOffscreenRenderManager, theRenderPluginManager)) + ioFlags |= RenderPreparationResultFlagValues::Dirty; + + // All objects with offscreen renderers are pickable so we can pass the pick through to the + // offscreen renderer and let it deal with the pick. + if (inImage.m_LastFrameOffscreenRenderer != NULL) { + ioFlags.SetPickable(true); + ioFlags |= RenderPreparationResultFlagValues::HasTransparency; + } + + if (inImage.m_TextureData.m_Texture) { + if (inImage.m_TextureData.m_TextureFlags.HasTransparency() + && (inMapType == ImageMapTypes::Diffuse + || inMapType == ImageMapTypes::Opacity + || inMapType == ImageMapTypes::Translucency)) { + ioFlags |= RenderPreparationResultFlagValues::HasTransparency; + } + // Textures used in general have linear characteristics. + // PKC -- The filters are properly set already. Setting them here only overrides what + // would + // otherwise be a correct setting. + // inImage.m_TextureData.m_Texture->SetMinFilter( NVRenderTextureMinifyingOp::Linear ); + // inImage.m_TextureData.m_Texture->SetMagFilter( NVRenderTextureMagnifyingOp::Linear ); + + SRenderableImage *theImage = RENDER_FRAME_NEW(SRenderableImage)(inMapType, inImage); + SShaderKeyImageMap &theKeyProp = + m_Renderer.DefaultMaterialShaderKeyProperties().m_ImageMaps[inImageIndex]; + + theKeyProp.SetEnabled(inShaderKey, true); + switch (inImage.m_MappingMode) { + default: + QT3DS_ASSERT(false); + // fallthrough intentional + case ImageMappingModes::Normal: + break; + case ImageMappingModes::Environment: + theKeyProp.SetEnvMap(inShaderKey, true); + break; + case ImageMappingModes::LightProbe: + theKeyProp.SetLightProbe(inShaderKey, true); + break; + } + + if (inImage.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + theKeyProp.SetInvertUVMap(inShaderKey, true); + if (ioFirstImage == NULL) + ioFirstImage = theImage; + else + ioNextImage->m_NextImage = theImage; + + if (inImage.m_TextureData.m_TextureFlags.IsPreMultiplied()) + theKeyProp.SetPremultiplied(inShaderKey, true); + + SShaderKeyTextureSwizzle &theSwizzleKeyProp = + m_Renderer.DefaultMaterialShaderKeyProperties().m_TextureSwizzle[inImageIndex]; + theSwizzleKeyProp.SetSwizzleMode( + inShaderKey, inImage.m_TextureData.m_Texture->GetTextureSwizzleMode(), true); + + ioNextImage = theImage; + } + } + + SDefaultMaterialPreparationResult SLayerRenderPreparationData::PrepareDefaultMaterialForRender( + SDefaultMaterial &inMaterial, SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity, + bool inClearDirtyFlags) + { + SDefaultMaterial *theMaterial = &inMaterial; + SDefaultMaterialPreparationResult retval(GenerateLightingKey(theMaterial->m_Lighting)); + retval.m_RenderableFlags = inExistingFlags; + SRenderableObjectFlags &renderableFlags(retval.m_RenderableFlags); + SShaderDefaultMaterialKey &theGeneratedKey(retval.m_MaterialKey); + retval.m_Opacity = inOpacity; + QT3DSF32 &subsetOpacity(retval.m_Opacity); + + if (theMaterial->m_Dirty.IsDirty()) { + renderableFlags |= RenderPreparationResultFlagValues::Dirty; + } + subsetOpacity *= theMaterial->m_Opacity; + if (inClearDirtyFlags) + theMaterial->m_Dirty.UpdateDirtyForFrame(); + + SRenderableImage *firstImage = NULL; + + // set wireframe mode + m_Renderer.DefaultMaterialShaderKeyProperties().m_WireframeMode.SetValue( + theGeneratedKey, m_Renderer.GetQt3DSContext().GetWireframeMode()); + + if (theMaterial->m_IblProbe && CheckLightProbeDirty(*theMaterial->m_IblProbe)) { + m_Renderer.PrepareImageForIbl(*theMaterial->m_IblProbe); + } + + if (!m_Renderer.DefaultMaterialShaderKeyProperties().m_HasIbl.GetValue(theGeneratedKey)) { + bool lightProbeValid = HasValidLightProbe(theMaterial->m_IblProbe); + SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", lightProbeValid); + m_Renderer.DefaultMaterialShaderKeyProperties().m_HasIbl.SetValue(theGeneratedKey, + lightProbeValid); + // SetShaderFeature( "QT3DS_ENABLE_IBL_FOV", + // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f ); + } + + if (subsetOpacity >= QT3DS_RENDER_MINIMUM_RENDER_OPACITY) { + + if (theMaterial->m_BlendMode != DefaultMaterialBlendMode::Normal + || theMaterial->m_OpacityMap) { + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + } + + bool specularEnabled = theMaterial->IsSpecularEnabled(); + m_Renderer.DefaultMaterialShaderKeyProperties().m_SpecularEnabled.SetValue( + theGeneratedKey, specularEnabled); + if (specularEnabled) { + m_Renderer.DefaultMaterialShaderKeyProperties().m_SpecularModel.SetSpecularModel( + theGeneratedKey, theMaterial->m_SpecularModel); + } + + m_Renderer.DefaultMaterialShaderKeyProperties().m_FresnelEnabled.SetValue( + theGeneratedKey, theMaterial->IsFresnelEnabled()); + + m_Renderer.DefaultMaterialShaderKeyProperties().m_VertexColorsEnabled.SetValue( + theGeneratedKey, theMaterial->IsVertexColorsEnabled()); + + // Run through the material's images and prepare them for render. + // this may in fact set pickable on the renderable flags if one of the images + // links to a sub presentation or any offscreen rendered object. + SRenderableImage *nextImage = NULL; +#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \ + if ((img)) \ + PrepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \ + theGeneratedKey, shadercomponent); + + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DiffuseMaps[0], ImageMapTypes::Diffuse, + SShaderDefaultMaterialKeyProperties::DiffuseMap0); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DiffuseMaps[1], ImageMapTypes::Diffuse, + SShaderDefaultMaterialKeyProperties::DiffuseMap1); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DiffuseMaps[2], ImageMapTypes::Diffuse, + SShaderDefaultMaterialKeyProperties::DiffuseMap2); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_EmissiveMap, ImageMapTypes::Emissive, + SShaderDefaultMaterialKeyProperties::EmissiveMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_EmissiveMap2, ImageMapTypes::Emissive, + SShaderDefaultMaterialKeyProperties::EmissiveMap2); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_SpecularReflection, ImageMapTypes::Specular, + SShaderDefaultMaterialKeyProperties::SpecularMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_RoughnessMap, ImageMapTypes::Roughness, + SShaderDefaultMaterialKeyProperties::RoughnessMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_OpacityMap, ImageMapTypes::Opacity, + SShaderDefaultMaterialKeyProperties::OpacityMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_BumpMap, ImageMapTypes::Bump, + SShaderDefaultMaterialKeyProperties::BumpMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_SpecularMap, ImageMapTypes::SpecularAmountMap, + SShaderDefaultMaterialKeyProperties::SpecularAmountMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_NormalMap, ImageMapTypes::Normal, + SShaderDefaultMaterialKeyProperties::NormalMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_DisplacementMap, ImageMapTypes::Displacement, + SShaderDefaultMaterialKeyProperties::DisplacementMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_TranslucencyMap, ImageMapTypes::Translucency, + SShaderDefaultMaterialKeyProperties::TranslucencyMap); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_Lightmaps.m_LightmapIndirect, + ImageMapTypes::LightmapIndirect, + SShaderDefaultMaterialKeyProperties::LightmapIndirect); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_Lightmaps.m_LightmapRadiosity, + ImageMapTypes::LightmapRadiosity, + SShaderDefaultMaterialKeyProperties::LightmapRadiosity); + CHECK_IMAGE_AND_PREPARE(theMaterial->m_Lightmaps.m_LightmapShadow, + ImageMapTypes::LightmapShadow, + SShaderDefaultMaterialKeyProperties::LightmapShadow); + } +#undef CHECK_IMAGE_AND_PREPARE + + if (subsetOpacity < QT3DS_RENDER_MINIMUM_RENDER_OPACITY) { + subsetOpacity = 0.0f; + // You can still pick against completely transparent objects(or rather their bounding + // box) + // you just don't render them. + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + renderableFlags |= RenderPreparationResultFlagValues::CompletelyTransparent; + } + + if (IsNotOne(subsetOpacity)) + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + + retval.m_FirstImage = firstImage; + if (retval.m_RenderableFlags.IsDirty()) + retval.m_Dirty = true; + return retval; + } + + SDefaultMaterialPreparationResult SLayerRenderPreparationData::PrepareCustomMaterialForRender( + SCustomMaterial &inMaterial, SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity) + { + SDefaultMaterialPreparationResult retval(GenerateLightingKey( + DefaultMaterialLighting::FragmentLighting)); // always fragment lighting + retval.m_RenderableFlags = inExistingFlags; + SRenderableObjectFlags &renderableFlags(retval.m_RenderableFlags); + SShaderDefaultMaterialKey &theGeneratedKey(retval.m_MaterialKey); + retval.m_Opacity = inOpacity; + QT3DSF32 &subsetOpacity(retval.m_Opacity); + + // If the custom material uses subpresentations, those have to be rendered before + // the custom material itself + m_Renderer.GetQt3DSContext().GetCustomMaterialSystem().renderSubpresentations(inMaterial); + + // set wireframe mode + m_Renderer.DefaultMaterialShaderKeyProperties().m_WireframeMode.SetValue( + theGeneratedKey, m_Renderer.GetQt3DSContext().GetWireframeMode()); + + if (subsetOpacity < QT3DS_RENDER_MINIMUM_RENDER_OPACITY) { + subsetOpacity = 0.0f; + // You can still pick against completely transparent objects(or rather their bounding + // box) + // you just don't render them. + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + renderableFlags |= RenderPreparationResultFlagValues::CompletelyTransparent; + } + + if (IsNotOne(subsetOpacity)) + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + + SRenderableImage *firstImage = NULL; + SRenderableImage *nextImage = NULL; + +#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \ + if ((img)) \ + PrepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \ + theGeneratedKey, shadercomponent); + + CHECK_IMAGE_AND_PREPARE(inMaterial.m_DisplacementMap, ImageMapTypes::Displacement, + SShaderDefaultMaterialKeyProperties::DisplacementMap); + CHECK_IMAGE_AND_PREPARE(inMaterial.m_Lightmaps.m_LightmapIndirect, + ImageMapTypes::LightmapIndirect, + SShaderDefaultMaterialKeyProperties::LightmapIndirect); + CHECK_IMAGE_AND_PREPARE(inMaterial.m_Lightmaps.m_LightmapRadiosity, + ImageMapTypes::LightmapRadiosity, + SShaderDefaultMaterialKeyProperties::LightmapRadiosity); + CHECK_IMAGE_AND_PREPARE(inMaterial.m_Lightmaps.m_LightmapShadow, + ImageMapTypes::LightmapShadow, + SShaderDefaultMaterialKeyProperties::LightmapShadow); +#undef CHECK_IMAGE_AND_PREPARE + + retval.m_FirstImage = firstImage; + return retval; + } + + bool SLayerRenderPreparationData::PrepareModelForRender( + SModel &inModel, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, TNodeLightEntryList &inScopedLights) + { + IQt3DSRenderContext &qt3dsContext(m_Renderer.GetQt3DSContext()); + IBufferManager &bufferManager = qt3dsContext.GetBufferManager(); + SRenderMesh *theMesh = bufferManager.LoadMesh(inModel.m_MeshPath); + if (theMesh == NULL) + return false; + + SGraphObject *theSourceMaterialObject = inModel.m_FirstMaterial; + SModelContext &theModelContext = + *RENDER_FRAME_NEW(SModelContext)(inModel, inViewProjection); + m_ModelContexts.push_back(&theModelContext); + + bool subsetDirty = false; + + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, m_SourceLightDirections, + inScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + for (QT3DSU32 idx = 0, end = theMesh->m_Subsets.size(); idx < end && theSourceMaterialObject; + ++idx, theSourceMaterialObject = GetNextMaterialSibling(theSourceMaterialObject)) { + SRenderSubset &theOuterSubset(theMesh->m_Subsets[idx]); + { + SRenderSubset &theSubset(theOuterSubset); + SRenderableObjectFlags renderableFlags; + renderableFlags.SetPickable(false); + renderableFlags.SetShadowCaster(inModel.m_ShadowCaster); + QT3DSF32 subsetOpacity = inModel.m_GlobalOpacity; + QT3DSVec3 theModelCenter(theSubset.m_Bounds.getCenter()); + theModelCenter = inModel.m_GlobalTransform.transform(theModelCenter); + + if (subsetOpacity >= QT3DS_RENDER_MINIMUM_RENDER_OPACITY + && inClipFrustum.hasValue()) { + // Check bounding box against the clipping planes + NVBounds3 theGlobalBounds = theSubset.m_Bounds; + theGlobalBounds.transform(theModelContext.m_Model.m_GlobalTransform); + if (inClipFrustum->intersectsWith(theGlobalBounds) == false) + subsetOpacity = 0.0f; + } + + // For now everything is pickable. Eventually we want to have localPickable and + // globalPickable set on the node during + // updates and have the runtime tell us what is pickable and what is not pickable. + // Completely transparent models cannot be pickable. But models with completely + // transparent materials + // still are. This allows the artist to control pickability in a somewhat + // fine-grained style. + bool canModelBePickable = inModel.m_GlobalOpacity > .01f; + renderableFlags.SetPickable(canModelBePickable + && (theModelContext.m_Model.m_Flags.IsGloballyPickable() + || renderableFlags.GetPickable())); + SRenderableObject *theRenderableObject(NULL); + eastl::pair<bool, SGraphObject *> theMaterialObjectAndDirty = + ResolveReferenceMaterial(theSourceMaterialObject); + SGraphObject *theMaterialObject = theMaterialObjectAndDirty.second; + subsetDirty = subsetDirty || theMaterialObjectAndDirty.first; + if (theMaterialObject == NULL) + continue; + + // set tessellation + if (inModel.m_TessellationMode != TessModeValues::NoTess) { + theSubset.m_PrimitiveType = NVRenderDrawMode::Patches; + // set tessellation factor + theSubset.m_EdgeTessFactor = inModel.m_EdgeTess; + theSubset.m_InnerTessFactor = inModel.m_InnerTess; + // update the vertex ver patch count in the input assembler + // currently we only support triangle patches so count is always 3 + theSubset.m_InputAssembler->SetPatchVertexCount(3); + theSubset.m_InputAssemblerDepth->SetPatchVertexCount(3); + // check wireframe mode + theSubset.m_WireframeMode = qt3dsContext.GetWireframeMode(); + + subsetDirty = + subsetDirty | (theSubset.m_WireframeMode != inModel.m_WireframeMode); + inModel.m_WireframeMode = qt3dsContext.GetWireframeMode(); + } else { + theSubset.m_PrimitiveType = theSubset.m_InputAssembler->GetPrimitiveType(); + theSubset.m_InputAssembler->SetPatchVertexCount(1); + theSubset.m_InputAssemblerDepth->SetPatchVertexCount(1); + // currently we allow wirframe mode only if tessellation is on + theSubset.m_WireframeMode = false; + + subsetDirty = + subsetDirty | (theSubset.m_WireframeMode != inModel.m_WireframeMode); + inModel.m_WireframeMode = false; + } + // Only clear flags on the materials in this direct hierarchy. Do not clear them of + // this + // references materials in another hierarchy. + bool clearMaterialDirtyFlags = theMaterialObject == theSourceMaterialObject; + + if (theMaterialObject == NULL) + continue; + + if (theMaterialObject->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial &theMaterial( + static_cast<SDefaultMaterial &>(*theMaterialObject)); + SDefaultMaterialPreparationResult theMaterialPrepResult( + PrepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity, + clearMaterialDirtyFlags)); + SShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.m_MaterialKey; + subsetOpacity = theMaterialPrepResult.m_Opacity; + SRenderableImage *firstImage(theMaterialPrepResult.m_FirstImage); + subsetDirty |= theMaterialPrepResult.m_Dirty; + renderableFlags = theMaterialPrepResult.m_RenderableFlags; + + m_Renderer.DefaultMaterialShaderKeyProperties() + .m_TessellationMode.SetTessellationMode(theGeneratedKey, + inModel.m_TessellationMode, true); + + NVConstDataRef<QT3DSMat44> boneGlobals; + if (theSubset.m_Joints.size()) { + QT3DS_ASSERT(false); + } + + theRenderableObject = RENDER_FRAME_NEW(SSubsetRenderable)( + renderableFlags, theModelCenter, m_Renderer, theSubset, theMaterial, + theModelContext, subsetOpacity, firstImage, theGeneratedKey, boneGlobals); + subsetDirty = subsetDirty || renderableFlags.IsDirty(); + + } // if type == DefaultMaterial + else if (theMaterialObject->m_Type == GraphObjectTypes::CustomMaterial) { + SCustomMaterial &theMaterial( + static_cast<SCustomMaterial &>(*theMaterialObject)); + + ICustomMaterialSystem &theMaterialSystem( + qt3dsContext.GetCustomMaterialSystem()); + subsetDirty |= theMaterialSystem.PrepareForRender( + theModelContext.m_Model, theSubset, theMaterial, clearMaterialDirtyFlags); + + SDefaultMaterialPreparationResult theMaterialPrepResult( + PrepareCustomMaterialForRender(theMaterial, renderableFlags, + subsetOpacity)); + SShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.m_MaterialKey; + subsetOpacity = theMaterialPrepResult.m_Opacity; + SRenderableImage *firstImage(theMaterialPrepResult.m_FirstImage); + renderableFlags = theMaterialPrepResult.m_RenderableFlags; + + // prepare for render tells us if the object is transparent + if (theMaterial.m_hasTransparency) + renderableFlags |= RenderPreparationResultFlagValues::HasTransparency; + // prepare for render tells us if the object is transparent + if (theMaterial.m_hasRefraction) + renderableFlags |= RenderPreparationResultFlagValues::HasRefraction; + + m_Renderer.DefaultMaterialShaderKeyProperties() + .m_TessellationMode.SetTessellationMode(theGeneratedKey, + inModel.m_TessellationMode, true); + + if (theMaterial.m_IblProbe && CheckLightProbeDirty(*theMaterial.m_IblProbe)) { + m_Renderer.PrepareImageForIbl(*theMaterial.m_IblProbe); + } + + theRenderableObject = RENDER_FRAME_NEW(SCustomMaterialRenderable)( + renderableFlags, theModelCenter, m_Renderer, theSubset, theMaterial, + theModelContext, subsetOpacity, firstImage, theGeneratedKey); + } + if (theRenderableObject) { + theRenderableObject->m_ScopedLights = inScopedLights; + // set tessellation + theRenderableObject->m_TessellationMode = inModel.m_TessellationMode; + + if (theRenderableObject->m_RenderableFlags.HasTransparency() + || theRenderableObject->m_RenderableFlags.HasRefraction()) { + m_TransparentObjects.push_back(theRenderableObject); + } else { + m_OpaqueObjects.push_back(theRenderableObject); + } + } + } + } + return subsetDirty; + } + + bool SLayerRenderPreparationData::PrepareRenderablesForRender( + const QT3DSMat44 &inViewProjection, const Option<SClippingFrustum> &inClipFrustum, + QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags) + { + SStackPerfTimer __timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::PrepareRenderablesForRender"); + m_ViewProjection = inViewProjection; + QT3DSF32 theTextScaleFactor = inTextScaleFactor; + bool wasDataDirty = false; + bool hasTextRenderer = m_Renderer.GetQt3DSContext().GetTextRenderer() != NULL; + for (QT3DSU32 idx = 0, end = m_RenderableNodes.size(); idx < end; ++idx) { + SRenderableNodeEntry &theNodeEntry(m_RenderableNodes[idx]); + SNode *theNode = theNodeEntry.m_Node; + wasDataDirty = wasDataDirty || theNode->m_Flags.IsDirty(); + switch (theNode->m_Type) { + case GraphObjectTypes::Model: { + SModel *theModel = static_cast<SModel *>(theNode); + theModel->CalculateGlobalVariables(); + if (theModel->m_Flags.IsGloballyActive()) { + bool wasModelDirty = PrepareModelForRender( + *theModel, inViewProjection, inClipFrustum, theNodeEntry.m_Lights); + wasDataDirty = wasDataDirty || wasModelDirty; + } + } break; + case GraphObjectTypes::Text: { + if (hasTextRenderer) { + SText *theText = static_cast<SText *>(theNode); + theText->CalculateGlobalVariables(); + // Omit check for global active flag intentionally and force + // render preparation for all Text items. This eliminates + // large delay for distance field text items becoming active + // mid-animation. + bool wasTextDirty = PrepareTextForRender(*theText, inViewProjection, + theTextScaleFactor, ioFlags); + wasDataDirty = wasDataDirty || wasTextDirty; + + } + } break; + case GraphObjectTypes::Path: { + SPath *thePath = static_cast<SPath *>(theNode); + thePath->CalculateGlobalVariables(); + if (thePath->m_Flags.IsGloballyActive()) { + bool wasPathDirty = + PreparePathForRender(*thePath, inViewProjection, inClipFrustum, ioFlags); + wasDataDirty = wasDataDirty || wasPathDirty; + } + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + return wasDataDirty; + } + + bool SLayerRenderPreparationData::CheckLightProbeDirty(SImage &inLightProbe) + { + IQt3DSRenderContext &theContext(m_Renderer.GetQt3DSContext()); + return inLightProbe.ClearDirty(theContext.GetBufferManager(), + theContext.GetOffscreenRenderManager(), + theContext.GetRenderPluginManager(), true); + } + + struct SLightNodeMarker + { + SLight *m_Light; + QT3DSU32 m_LightIndex; + QT3DSU32 m_FirstValidIndex; + QT3DSU32 m_JustPastLastValidIndex; + bool m_AddOrRemove; + SLightNodeMarker() + : m_Light(NULL) + , m_FirstValidIndex(0) + , m_JustPastLastValidIndex(0) + , m_AddOrRemove(false) + { + } + SLightNodeMarker(SLight &inLight, QT3DSU32 inLightIndex, SNode &inNode, bool aorm) + : m_Light(&inLight) + , m_LightIndex(inLightIndex) + , m_AddOrRemove(aorm) + { + if (inNode.m_Type == GraphObjectTypes::Layer) { + m_FirstValidIndex = 0; + m_JustPastLastValidIndex = QT3DS_MAX_U32; + } else { + m_FirstValidIndex = inNode.m_DFSIndex; + SNode *lastChild = NULL; + SNode *firstChild = inNode.m_FirstChild; + // find deepest last child + while (firstChild) { + for (SNode *childNode = firstChild; childNode; + childNode = childNode->m_NextSibling) + lastChild = childNode; + + if (lastChild) + firstChild = lastChild->m_FirstChild; + else + firstChild = NULL; + } + if (lastChild) + // last valid index would be the last child index + 1 + m_JustPastLastValidIndex = lastChild->m_DFSIndex + 1; + else // no children. + m_JustPastLastValidIndex = m_FirstValidIndex + 1; + } + } + }; + + // m_Layer.m_Camera->CalculateViewProjectionMatrix(m_ViewProjection); + void + SLayerRenderPreparationData::PrepareForRender(const QSize &inViewportDimensions) + { + SStackPerfTimer __timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::PrepareForRender"); + if (m_LayerPrepResult.hasValue()) + return; + + m_Features.clear(); + m_FeatureSetHash = 0; + QT3DSVec2 thePresentationDimensions((QT3DSF32)inViewportDimensions.width(), + (QT3DSF32)inViewportDimensions.height()); + IRenderList &theGraph(m_Renderer.GetQt3DSContext().GetRenderList()); + NVRenderRect theViewport(theGraph.GetViewport()); + NVRenderRect theScissor(theGraph.GetViewport()); + if (theGraph.IsScissorTestEnabled()) + theScissor = m_Renderer.GetContext().GetScissorRect(); + bool wasDirty = false; + bool wasDataDirty = false; + wasDirty = m_Layer.m_Flags.IsDirty(); + // The first pass is just to render the data. + QT3DSU32 maxNumAAPasses = m_Layer.m_ProgressiveAAMode == AAModeValues::NoAA + ? (QT3DSU32)0 + : (QT3DSU32)(m_Layer.m_ProgressiveAAMode) + 1; + maxNumAAPasses = NVMin((QT3DSU32)(MAX_AA_LEVELS + 1), maxNumAAPasses); + SEffect *theLastEffect = NULL; + // Uncomment the line below to disable all progressive AA. + // maxNumAAPasses = 0; + + SLayerRenderPreparationResult thePrepResult; + bool hasOffscreenRenderer = GetOffscreenRenderer(); + + bool SSAOEnabled = (m_Layer.m_AoStrength > 0.0f && m_Layer.m_AoDistance > 0.0f); + bool SSDOEnabled = (m_Layer.m_ShadowStrength > 0.0f && m_Layer.m_ShadowDist > 0.0f); + SetShaderFeature("QT3DS_ENABLE_SSAO", SSAOEnabled); + SetShaderFeature("QT3DS_ENABLE_SSDO", SSDOEnabled); + bool requiresDepthPrepass = (hasOffscreenRenderer == false) && (SSAOEnabled || SSDOEnabled); + SetShaderFeature("QT3DS_ENABLE_SSM", false); // by default no shadow map generation + + if (m_Layer.m_Flags.IsActive()) { + // Get the layer's width and height. + IEffectSystem &theEffectSystem(m_Renderer.GetQt3DSContext().GetEffectSystem()); + for (SEffect *theEffect = m_Layer.m_FirstEffect; theEffect; + theEffect = theEffect->m_NextEffect) { + if (theEffect->m_Flags.IsDirty()) { + wasDirty = true; + theEffect->m_Flags.SetDirty(false); + } + if (theEffect->m_Flags.IsActive()) { + // If the effect uses subpresentations, those have to be rendered before + // the effect itself + theEffectSystem.renderSubpresentations(*theEffect); + theLastEffect = theEffect; + if (hasOffscreenRenderer == false + && theEffectSystem.DoesEffectRequireDepthTexture(theEffect->m_ClassName)) + requiresDepthPrepass = true; + } + } + if (m_Layer.m_Flags.IsDirty()) { + wasDirty = true; + m_Layer.CalculateGlobalVariables(); + } + + bool shouldRenderToTexture = true; + + if (hasOffscreenRenderer) { + // We don't render to texture with offscreen renderers, we just render them to the + // viewport. + shouldRenderToTexture = false; + // Progaa disabled when using offscreen rendering. + maxNumAAPasses = 0; + } + + thePrepResult = SLayerRenderPreparationResult(SLayerRenderHelper( + theViewport, theScissor, m_Layer.m_Scene->m_Presentation->m_PresentationDimensions, + m_Layer, shouldRenderToTexture, m_Renderer.GetQt3DSContext().GetScaleMode(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor())); + thePrepResult.m_LastEffect = theLastEffect; + thePrepResult.m_MaxAAPassIndex = maxNumAAPasses; + thePrepResult.m_Flags.SetRequiresDepthTexture(requiresDepthPrepass + || NeedsWidgetTexture()); + thePrepResult.m_Flags.SetShouldRenderToTexture(shouldRenderToTexture); + if (m_Renderer.GetContext().GetRenderContextType() != NVRenderContextValues::GLES2) + thePrepResult.m_Flags.SetRequiresSsaoPass(SSAOEnabled); + + if (thePrepResult.IsLayerVisible()) { + if (shouldRenderToTexture) { + m_Renderer.GetQt3DSContext().GetRenderList().AddRenderTask( + CreateRenderToTextureRunnable()); + } + if (m_Layer.m_LightProbe && CheckLightProbeDirty(*m_Layer.m_LightProbe)) { + m_Renderer.PrepareImageForIbl(*m_Layer.m_LightProbe); + wasDataDirty = true; + } + + bool lightProbeValid = HasValidLightProbe(m_Layer.m_LightProbe); + + SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", lightProbeValid); + SetShaderFeature("QT3DS_ENABLE_IBL_FOV", m_Layer.m_ProbeFov < 180.0f); + + if (lightProbeValid && m_Layer.m_LightProbe2 + && CheckLightProbeDirty(*m_Layer.m_LightProbe2)) { + m_Renderer.PrepareImageForIbl(*m_Layer.m_LightProbe2); + wasDataDirty = true; + } + + SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE_2", + lightProbeValid && HasValidLightProbe(m_Layer.m_LightProbe2)); + + // Push nodes in reverse depth first order + if (m_RenderableNodes.empty()) { + m_CamerasAndLights.clear(); + QT3DSU32 dfsIndex = 0; + for (SNode *theChild = m_Layer.m_FirstChild; theChild; + theChild = theChild->m_NextSibling) + MaybeQueueNodeForRender(*theChild, m_RenderableNodes, m_CamerasAndLights, + dfsIndex); + reverse(m_CamerasAndLights.begin(), m_CamerasAndLights.end()); + reverse(m_RenderableNodes.begin(), m_RenderableNodes.end()); + m_LightToNodeMap.clear(); + } + m_Camera = NULL; + m_Lights.clear(); + m_OpaqueObjects.clear(); + m_TransparentObjects.clear(); + nvvector<SLightNodeMarker> theLightNodeMarkers(m_Renderer.GetPerFrameAllocator(), + "LightNodeMarkers"); + m_SourceLightDirections.clear(); + + for (QT3DSU32 idx = 0, end = m_CamerasAndLights.size(); idx < end; ++idx) { + SNode *theNode(m_CamerasAndLights[idx]); + wasDataDirty = wasDataDirty || theNode->m_Flags.IsDirty(); + switch (theNode->m_Type) { + case GraphObjectTypes::Camera: { + SCamera *theCamera = static_cast<SCamera *>(theNode); + if (theCamera->m_Flags.IsActive()) { + // Only proceed with camera nodes which are currently active. + // SetupCameraForRender() sets the camera used for picking and + // updates global state e.g. IsGloballyActive() + SCameraGlobalCalculationResult theResult = + thePrepResult.SetupCameraForRender(*theCamera); + wasDataDirty = wasDataDirty || theResult.m_WasDirty; + if (theCamera->m_Flags.IsGloballyActive()) + m_Camera = theCamera; + if (theResult.m_ComputeFrustumSucceeded == false) { + qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum"); + } + } + } break; + case GraphObjectTypes::Light: { + SLight *theLight = static_cast<SLight *>(theNode); + bool lightResult = theLight->CalculateGlobalVariables(); + wasDataDirty = lightResult || wasDataDirty; + // Note we setup the light index such that it is completely invariant of if + // the + // light is active or scoped. + QT3DSU32 lightIndex = (QT3DSU32)m_SourceLightDirections.size(); + m_SourceLightDirections.push_back(QT3DSVec3(0.0f)); + // Note we still need a light check when building the renderable light list. + // We also cannot cache shader-light bindings based on layers any more + // because + // the number of lights for a given renderable does not depend on the layer + // as it used to but + // additional perhaps on the light's scoping rules. + if (theLight->m_Flags.IsGloballyActive()) { + if (theLight->m_Scope == NULL) { + m_Lights.push_back(theLight); + if (m_Renderer.GetContext().GetRenderContextType() + != NVRenderContextValues::GLES2 + && theLight->m_CastShadow && GetShadowMapManager()) { + // PKC -- use of "res" as an exponent of two is an annoying + // artifact of the XML interface + // I'll change this with an enum interface later on, but that's + // less important right now. + QT3DSU32 mapSize = 1 << theLight->m_ShadowMapRes; + ShadowMapModes::Enum mapMode = + (theLight->m_LightType != RenderLightTypes::Directional) + ? ShadowMapModes::CUBE + : ShadowMapModes::VSM; + m_ShadowMapManager->AddShadowMapEntry( + m_Lights.size() - 1, mapSize, mapSize, + NVRenderTextureFormats::R16F, 1, mapMode, + ShadowFilterValues::NONE); + thePrepResult.m_Flags.SetRequiresShadowMapPass(true); + SetShaderFeature("QT3DS_ENABLE_SSM", true); + } + } + TLightToNodeMap::iterator iter = + m_LightToNodeMap.insert(eastl::make_pair(theLight, (SNode *)NULL)) + .first; + SNode *oldLightScope = iter->second; + SNode *newLightScope = theLight->m_Scope; + + if (oldLightScope != newLightScope) { + iter->second = newLightScope; + if (oldLightScope) + theLightNodeMarkers.push_back(SLightNodeMarker( + *theLight, lightIndex, *oldLightScope, false)); + if (newLightScope) + theLightNodeMarkers.push_back(SLightNodeMarker( + *theLight, lightIndex, *newLightScope, true)); + } + if (newLightScope) { + m_SourceLightDirections.back() = + theLight->GetScalingCorrectDirection(); + } + } + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + + if (theLightNodeMarkers.empty() == false) { + for (QT3DSU32 idx = 0, end = m_RenderableNodes.size(); idx < end; ++idx) { + SRenderableNodeEntry &theNodeEntry(m_RenderableNodes[idx]); + QT3DSU32 nodeDFSIndex = theNodeEntry.m_Node->m_DFSIndex; + for (QT3DSU32 markerIdx = 0, markerEnd = theLightNodeMarkers.size(); + markerIdx < markerEnd; ++markerIdx) { + SLightNodeMarker &theMarker = theLightNodeMarkers[markerIdx]; + if (nodeDFSIndex >= theMarker.m_FirstValidIndex + && nodeDFSIndex < theMarker.m_JustPastLastValidIndex) { + if (theMarker.m_AddOrRemove) { + SNodeLightEntry *theNewEntry = + m_RenderableNodeLightEntryPool.construct( + theMarker.m_Light, theMarker.m_LightIndex, __FILE__, + __LINE__); + theNodeEntry.m_Lights.push_back(*theNewEntry); + } else { + for (TNodeLightEntryList::iterator + lightIter = theNodeEntry.m_Lights.begin(), + lightEnd = theNodeEntry.m_Lights.end(); + lightIter != lightEnd; ++lightIter) { + if (lightIter->m_Light == theMarker.m_Light) { + SNodeLightEntry &theEntry = *lightIter; + theNodeEntry.m_Lights.remove(theEntry); + m_RenderableNodeLightEntryPool.deallocate(&theEntry); + break; + } + } + } + } + } + } + } + + QT3DSF32 theTextScaleFactor = 1.0f; + if (m_Camera) { + m_Camera->CalculateViewProjectionMatrix(m_ViewProjection); + theTextScaleFactor = m_Camera->GetTextScaleFactor( + thePrepResult.GetLayerToPresentationViewport(), + thePrepResult.GetPresentationDesignDimensions()); + SClipPlane nearPlane; + QT3DSMat33 theUpper33(m_Camera->m_GlobalTransform.getUpper3x3InverseTranspose()); + + QT3DSVec3 dir(theUpper33.transform(QT3DSVec3(0, 0, -1))); + dir.normalize(); + nearPlane.normal = dir; + QT3DSVec3 theGlobalPos = m_Camera->GetGlobalPos() + m_Camera->m_ClipNear * dir; + nearPlane.d = -(dir.dot(theGlobalPos)); + // the near plane's bbox edges are calculated in the clipping frustum's + // constructor. + m_ClippingFrustum = SClippingFrustum(m_ViewProjection, nearPlane); + } else { + m_ViewProjection = QT3DSMat44::createIdentity(); + } + + // Setup the light directions here. + + for (QT3DSU32 lightIdx = 0, lightEnd = m_Lights.size(); lightIdx < lightEnd; + ++lightIdx) { + m_LightDirections.push_back(m_Lights[lightIdx]->GetScalingCorrectDirection()); + } + + m_ModelContexts.clear(); + if (GetOffscreenRenderer() == false) { + bool renderablesDirty = + PrepareRenderablesForRender(m_ViewProjection, + m_ClippingFrustum, + theTextScaleFactor, thePrepResult.m_Flags); + wasDataDirty = wasDataDirty || renderablesDirty; + if (thePrepResult.m_Flags.RequiresStencilBuffer()) + thePrepResult.m_Flags.SetShouldRenderToTexture(true); + } else { + NVRenderRect theViewport = + thePrepResult.GetLayerToPresentationViewport().ToIntegerRect(); + bool theScissor = true; + NVRenderRect theScissorRect = + thePrepResult.GetLayerToPresentationScissorRect().ToIntegerRect(); + // This happens here because if there are any fancy render steps + IRenderList &theRenderList(m_Renderer.GetQt3DSContext().GetRenderList()); + NVRenderContext &theContext(m_Renderer.GetContext()); + SRenderListScopedProperty<bool> _listScissorEnabled( + theRenderList, &IRenderList::IsScissorTestEnabled, + &IRenderList::SetScissorTestEnabled, theScissor); + SRenderListScopedProperty<NVRenderRect> _listViewport( + theRenderList, &IRenderList::GetViewport, &IRenderList::SetViewport, + theViewport); + SRenderListScopedProperty<NVRenderRect> _listScissor( + theRenderList, &IRenderList::GetScissor, &IRenderList::SetScissorRect, + theScissorRect); + // Some plugins don't use the render list so they need the actual gl context + // setup. + qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, true); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __scissorRect( + theContext, &NVRenderContext::GetScissorRect, + &NVRenderContext::SetScissorRect, theScissorRect); + qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __viewportRect( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport, + theViewport); + SOffscreenRenderFlags theResult = m_LastFrameOffscreenRenderer->NeedsRender( + CreateOffscreenRenderEnvironment(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor(), &m_Layer); + wasDataDirty = wasDataDirty || theResult.m_HasChangedSinceLastFrame; + } + } + } + wasDirty = wasDirty || wasDataDirty; + thePrepResult.m_Flags.SetWasDirty(wasDirty); + thePrepResult.m_Flags.SetLayerDataDirty(wasDataDirty); + + m_LayerPrepResult = thePrepResult; + + // Per-frame cache of renderable objects post-sort. + GetOpaqueRenderableObjects(); + // If layer depth test is false, this may also contain opaque objects. + GetTransparentRenderableObjects(); + + GetCameraDirection(); + } + + void SLayerRenderPreparationData::ResetForFrame() + { + m_TransparentObjects.clear_unsafe(); + m_OpaqueObjects.clear_unsafe(); + m_LayerPrepResult.setEmpty(); + // The check for if the camera is or is not null is used + // to figure out if this layer was rendered at all. + m_Camera = NULL; + m_LastFrameOffscreenRenderer = NULL; + m_IRenderWidgets.clear_unsafe(); + m_CameraDirection.setEmpty(); + m_LightDirections.clear_unsafe(); + m_RenderedOpaqueObjects.clear_unsafe(); + m_RenderedTransparentObjects.clear_unsafe(); + } +} +} |