diff options
Diffstat (limited to 'src/runtimerender/rendererimpl')
13 files changed, 12159 insertions, 0 deletions
diff --git a/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp new file mode 100644 index 0000000..fff7e32 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** 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 "Qt3DSRenderableObjects.h" +#include "Qt3DSRendererImpl.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderCustomMaterialRenderContext.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderPathManager.h" +#include "Qt3DSRenderPathRenderContext.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" + +using qt3ds::foundation::CRegisteredString; + +namespace qt3ds { +namespace render { + struct SRenderableImage; + struct SShaderGeneratorGeneratedShader; + struct SSubsetRenderable; + using eastl::make_pair; + using eastl::reverse; + + STextScaleAndOffset::STextScaleAndOffset(NVRenderTexture2D &inTexture, + const STextTextureDetails &inTextDetails, + const STextRenderInfo &inInfo) + : m_TextOffset(0, 0) + , m_TextScale(1, 1) + + { + NVRenderTexture2D &theTexture = inTexture; + STextureDetails theDetails(theTexture.GetTextureDetails()); + QT3DSVec2 textDimensions(inTextDetails.m_TextWidth / 2.0f, inTextDetails.m_TextHeight / 2.0f); + textDimensions.x /= inTextDetails.m_ScaleFactor.x; + textDimensions.y /= inTextDetails.m_ScaleFactor.y; + QT3DSVec2 theTextScale(textDimensions.x, textDimensions.y); + QT3DSVec2 theTextOffset(0, 0); + + // Set the offsets to use after scaling the rect coordinates. + switch (inInfo.m_HorizontalAlignment) { + case TextHorizontalAlignment::Left: + theTextOffset[0] = theTextScale[0]; + break; + case TextHorizontalAlignment::Center: + break; + case TextHorizontalAlignment::Right: + theTextOffset[0] = -theTextScale[0]; + break; + default: + break; + } + + switch (inInfo.m_VerticalAlignment) { + case TextVerticalAlignment::Top: + theTextOffset[1] = -theTextScale[1]; + break; + case TextVerticalAlignment::Middle: + break; + case TextVerticalAlignment::Bottom: + theTextOffset[1] = theTextScale[1]; + break; + default: + break; + } + m_TextScale = theTextScale; + m_TextOffset = theTextOffset; + } + + void SSubsetRenderableBase::RenderShadowMapPass(const QT3DSVec2 &inCameraVec, + const SLight *inLight, const SCamera &inCamera, + SShadowMapEntry *inShadowMapEntry) + { + NVRenderContext &context(m_Generator.GetContext()); + SRenderableDepthPrepassShader *shader = NULL; + NVRenderInputAssembler *pIA = NULL; + + /* + if ( inLight->m_LightType == RenderLightTypes::Area ) + shader = m_Generator.GetParaboloidDepthShader( m_TessellationMode ); + else if ( inLight->m_LightType == RenderLightTypes::Directional ) + shader = m_Generator.GetOrthographicDepthShader( m_TessellationMode ); + else if ( inLight->m_LightType == RenderLightTypes::Point ) + shader = m_Generator.GetCubeShadowDepthShader( m_TessellationMode ); // This + will change to include a geometry shader pass. + */ + + if (inLight->m_LightType == RenderLightTypes::Directional) + shader = m_Generator.GetOrthographicDepthShader(m_TessellationMode); + else + shader = m_Generator.GetCubeShadowDepthShader(m_TessellationMode); + + if (shader == NULL || inShadowMapEntry == NULL) + return; + + // for phong and npatch tesselleation we need the normals too + if (m_TessellationMode == TessModeValues::NoTess + || m_TessellationMode == TessModeValues::TessLinear) + pIA = m_Subset.m_InputAssemblerDepth; + else + pIA = m_Subset.m_InputAssembler; + + QT3DSMat44 theModelViewProjection = inShadowMapEntry->m_LightVP * m_GlobalTransform; + // QT3DSMat44 theModelView = inLight->m_GlobalTransform.getInverse() * m_GlobalTransform; + + context.SetActiveShader(&shader->m_Shader); + shader->m_MVP.Set(theModelViewProjection); + shader->m_CameraPosition.Set(inCamera.m_Position); + shader->m_GlobalTransform.Set(m_GlobalTransform); + shader->m_CameraProperties.Set(inCameraVec); + /* + shader->m_CameraDirection.Set( inCamera.GetDirection() ); + + shader->m_ShadowMV[0].Set( inShadowMapEntry->m_LightCubeView[0] * m_GlobalTransform ); + shader->m_ShadowMV[1].Set( inShadowMapEntry->m_LightCubeView[1] * m_GlobalTransform ); + shader->m_ShadowMV[2].Set( inShadowMapEntry->m_LightCubeView[2] * m_GlobalTransform ); + shader->m_ShadowMV[3].Set( inShadowMapEntry->m_LightCubeView[3] * m_GlobalTransform ); + shader->m_ShadowMV[4].Set( inShadowMapEntry->m_LightCubeView[4] * m_GlobalTransform ); + shader->m_ShadowMV[5].Set( inShadowMapEntry->m_LightCubeView[5] * m_GlobalTransform ); + shader->m_Projection.Set( inCamera.m_Projection ); + */ + + // tesselation + if (m_TessellationMode != TessModeValues::NoTess) { + // set uniforms we need + shader->m_Tessellation.m_EdgeTessLevel.Set(m_Subset.m_EdgeTessFactor); + shader->m_Tessellation.m_InsideTessLevel.Set(m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + shader->m_Tessellation.m_PhongBlend.Set(0.75); + // set distance range value + shader->m_Tessellation.m_DistanceRange.Set(inCameraVec); + // disable culling + shader->m_Tessellation.m_DisableCulling.Set(1.0); + } + + context.SetInputAssembler(pIA); + context.Draw(m_Subset.m_PrimitiveType, m_Subset.m_Count, m_Subset.m_Offset); + } + + void SSubsetRenderableBase::RenderDepthPass(const QT3DSVec2 &inCameraVec, + SRenderableImage *inDisplacementImage, + float inDisplacementAmount) + { + NVRenderContext &context(m_Generator.GetContext()); + SRenderableDepthPrepassShader *shader = NULL; + NVRenderInputAssembler *pIA = NULL; + SRenderableImage *displacementImage = inDisplacementImage; + + if (m_Subset.m_PrimitiveType != NVRenderDrawMode::Patches) + shader = m_Generator.GetDepthPrepassShader(displacementImage != NULL); + else + shader = m_Generator.GetDepthTessPrepassShader(m_TessellationMode, + displacementImage != NULL); + + if (shader == NULL) + return; + + // for phong and npatch tesselleation or displacement mapping we need the normals (and uv's) + // too + if ((m_TessellationMode == TessModeValues::NoTess + || m_TessellationMode == TessModeValues::TessLinear) + && !displacementImage) + pIA = m_Subset.m_InputAssemblerDepth; + else + pIA = m_Subset.m_InputAssembler; + + context.SetActiveShader(&shader->m_Shader); + context.SetCullingEnabled(true); + + shader->m_MVP.Set(m_ModelContext.m_ModelViewProjection); + + if (displacementImage) { + // setup image transform + const QT3DSMat44 &textureTransform = displacementImage->m_Image.m_TextureTransform; + const QT3DSF32 *dataPtr(textureTransform.front()); + QT3DSVec3 offsets(dataPtr[12], dataPtr[13], + displacementImage->m_Image.m_TextureData.m_TextureFlags.IsPreMultiplied() + ? 1.0f + : 0.0f); + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + displacementImage->m_Image.m_TextureData.m_Texture->SetTextureWrapS( + displacementImage->m_Image.m_HorizontalTilingMode); + displacementImage->m_Image.m_TextureData.m_Texture->SetTextureWrapT( + displacementImage->m_Image.m_VerticalTilingMode); + + shader->m_DisplaceAmount.Set(inDisplacementAmount); + shader->m_DisplacementProps.m_Offsets.Set(offsets); + shader->m_DisplacementProps.m_Rotations.Set(rotations); + shader->m_DisplacementProps.m_Sampler.Set( + displacementImage->m_Image.m_TextureData.m_Texture); + } + + // tesselation + if (m_TessellationMode != TessModeValues::NoTess) { + // set uniforms we need + shader->m_GlobalTransform.Set(m_GlobalTransform); + + if (m_Generator.GetLayerRenderData() && m_Generator.GetLayerRenderData()->m_Camera) + shader->m_CameraPosition.Set( + m_Generator.GetLayerRenderData()->m_Camera->GetGlobalPos()); + else if (m_Generator.GetLayerRenderData()->m_Camera) + shader->m_CameraPosition.Set(QT3DSVec3(0.0, 0.0, 1.0)); + + shader->m_Tessellation.m_EdgeTessLevel.Set(m_Subset.m_EdgeTessFactor); + shader->m_Tessellation.m_InsideTessLevel.Set(m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + shader->m_Tessellation.m_PhongBlend.Set(0.75); + // set distance range value + shader->m_Tessellation.m_DistanceRange.Set(inCameraVec); + // enable culling + shader->m_Tessellation.m_DisableCulling.Set(0.0); + } + + context.SetInputAssembler(pIA); + context.Draw(m_Subset.m_PrimitiveType, m_Subset.m_Count, m_Subset.m_Offset); + } + + // An interface to the shader generator that is available to the renderables + + void SSubsetRenderable::Render(const QT3DSVec2 &inCameraVec, TShaderFeatureSet inFeatureSet) + { + NVRenderContext &context(m_Generator.GetContext()); + + SShaderGeneratorGeneratedShader *shader = m_Generator.GetShader(*this, inFeatureSet); + if (shader == NULL) + return; + + context.SetActiveShader(&shader->m_Shader); + + m_Generator.GetQt3DSContext().GetDefaultMaterialShaderGenerator().SetMaterialProperties( + shader->m_Shader, m_Material, inCameraVec, m_ModelContext.m_ModelViewProjection, + m_ModelContext.m_NormalMatrix, m_ModelContext.m_Model.m_GlobalTransform, m_FirstImage, + m_Opacity, m_Generator.GetLayerGlobalRenderProperties()); + + // tesselation + if (m_Subset.m_PrimitiveType == NVRenderDrawMode::Patches) { + shader->m_Tessellation.m_EdgeTessLevel.Set(m_Subset.m_EdgeTessFactor); + shader->m_Tessellation.m_InsideTessLevel.Set(m_Subset.m_InnerTessFactor); + // the blend value is hardcoded + shader->m_Tessellation.m_PhongBlend.Set(0.75); + // this should finally be based on some user input + shader->m_Tessellation.m_DistanceRange.Set(inCameraVec); + // enable culling + shader->m_Tessellation.m_DisableCulling.Set(0.0); + + if (m_Subset.m_WireframeMode) { + // we need the viewport matrix + NVRenderRect theViewport(context.GetViewport()); + QT3DSMat44 vpMatrix; + vpMatrix.column0 = QT3DSVec4((float)theViewport.m_Width / 2.0f, 0.0, 0.0, 0.0); + vpMatrix.column1 = QT3DSVec4(0.0, (float)theViewport.m_Height / 2.0f, 0.0, 0.0); + vpMatrix.column2 = QT3DSVec4(0.0, 0.0, 1.0, 0.0); + vpMatrix.column3 = + QT3DSVec4((float)theViewport.m_Width / 2.0f + (float)theViewport.m_X, + (float)theViewport.m_Height / 2.0f + (float)theViewport.m_Y, 0.0, 1.0); + + shader->m_ViewportMatrix.Set(vpMatrix); + } + } + + context.SetCullingEnabled(true); + context.SetInputAssembler(m_Subset.m_InputAssembler); + context.Draw(m_Subset.m_PrimitiveType, m_Subset.m_Count, m_Subset.m_Offset); + } + + void SSubsetRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec) + { + SRenderableImage *displacementImage = NULL; + for (SRenderableImage *theImage = m_FirstImage; + theImage != NULL && displacementImage == NULL; theImage = theImage->m_NextImage) { + if (theImage->m_MapType == ImageMapTypes::Displacement) + displacementImage = theImage; + } + SSubsetRenderableBase::RenderDepthPass(inCameraVec, displacementImage, + m_Material.m_DisplaceAmount); + } + + void STextRenderable::Render(const QT3DSVec2 &inCameraVec) + { + NVRenderContext &context(m_Generator.GetContext()); + + if (!m_Text.m_PathFontDetails) { + + STextRenderHelper theInfo = m_Generator.GetShader(*this, false); + if (theInfo.m_Shader == NULL) + return; + // All of our shaders produce premultiplied values. + qt3ds::render::NVRenderBlendFunctionArgument blendFunc( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + context.SetBlendFunction(blendFunc); + context.SetBlendEquation(blendEqu); + QT3DSVec4 theColor(m_Text.m_TextColor.x, m_Text.m_TextColor.y, m_Text.m_TextColor.z, + m_Text.m_GlobalOpacity); + + STextShader &shader(*theInfo.m_Shader); + shader.Render(*m_Text.m_TextTexture, *this, theColor, m_ModelViewProjection, + inCameraVec, context, theInfo.m_QuadInputAssembler, + theInfo.m_QuadInputAssembler.GetIndexCount(), m_Text.m_TextTextureDetails, + QT3DSVec3(0, 0, 0)); + } else { + QT3DS_ASSERT(context.IsPathRenderingSupported() && context.IsProgramPipelineSupported()); + + STextRenderHelper theInfo = m_Generator.GetShader(*this, true); + if (theInfo.m_Shader == NULL) + return; + + // All of our shaders produce premultiplied values. + qt3ds::render::NVRenderBlendFunctionArgument blendFunc( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + context.SetBlendFunction(blendFunc); + context.SetBlendEquation(blendEqu); + QT3DSVec4 theColor(m_Text.m_TextColor.x, m_Text.m_TextColor.y, m_Text.m_TextColor.z, + m_Text.m_GlobalOpacity); + STextShader &shader(*theInfo.m_Shader); + + shader.RenderPath(*m_Text.m_PathFontItem, *m_Text.m_PathFontDetails, *this, theColor, + m_ViewProjection, m_GlobalTransform, inCameraVec, context, + m_Text.m_TextTextureDetails, QT3DSVec3(0, 0, 0)); + } + } + + void STextRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec) + { + NVRenderContext &context(m_Generator.GetContext()); + STextDepthShader *theDepthShader = m_Generator.GetTextDepthShader(); + if (theDepthShader == NULL) + return; + + if (!m_Text.m_PathFontDetails) { + // we may change stencil test state + qt3ds::render::NVRenderContextScopedProperty<bool> __stencilTest( + context, &NVRenderContext::IsStencilTestEnabled, + &NVRenderContext::SetStencilTestEnabled, true); + + NVRenderShaderProgram &theShader(theDepthShader->m_Shader); + context.SetCullingEnabled(false); + context.SetActiveShader(&theShader); + theDepthShader->m_MVP.Set(m_ModelViewProjection); + theDepthShader->m_Sampler.Set(m_Text.m_TextTexture); + const STextScaleAndOffset &theScaleAndOffset(*this); + theDepthShader->m_Dimensions.Set( + QT3DSVec4(theScaleAndOffset.m_TextScale.x, theScaleAndOffset.m_TextScale.y, + theScaleAndOffset.m_TextOffset.x, theScaleAndOffset.m_TextOffset.y)); + theDepthShader->m_CameraProperties.Set(inCameraVec); + + STextureDetails theTextureDetails = m_Text.m_TextTexture->GetTextureDetails(); + const STextTextureDetails &theTextTextureDetails(m_Text.m_TextTextureDetails); + QT3DSF32 theWidthScale = + (QT3DSF32)theTextTextureDetails.m_TextWidth / (QT3DSF32)theTextureDetails.m_Width; + QT3DSF32 theHeightScale = + (QT3DSF32)theTextTextureDetails.m_TextHeight / (QT3DSF32)theTextureDetails.m_Height; + theDepthShader->m_TextDimensions.Set( + QT3DSVec3(theWidthScale, theHeightScale, theTextTextureDetails.m_FlipY ? 1.0f : 0.0f)); + context.SetInputAssembler(&theDepthShader->m_QuadInputAssembler); + context.Draw(NVRenderDrawMode::Triangles, + theDepthShader->m_QuadInputAssembler.GetIndexCount(), 0); + } else { + qt3ds::render::NVRenderBoolOp::Enum theDepthFunction = context.GetDepthFunction(); + bool isDepthEnabled = context.IsDepthTestEnabled(); + bool isStencilEnabled = context.IsStencilTestEnabled(); + bool isDepthWriteEnabled = context.IsDepthWriteEnabled(); + qt3ds::render::NVRenderStencilFunctionArgument theArg(qt3ds::render::NVRenderBoolOp::NotEqual, + 0, 0xFF); + qt3ds::render::NVRenderStencilOperationArgument theOpArg( + qt3ds::render::NVRenderStencilOp::Keep, qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Zero); + NVScopedRefCounted<NVRenderDepthStencilState> depthStencilState = + context.CreateDepthStencilState(isDepthEnabled, isDepthWriteEnabled, + theDepthFunction, false, theArg, theArg, theOpArg, + theOpArg); + + context.SetActiveShader(NULL); + context.SetCullingEnabled(false); + + context.SetDepthStencilState(depthStencilState); + + // setup transform + QT3DSMat44 offsetMatrix = QT3DSMat44::createIdentity(); + offsetMatrix.setPosition(QT3DSVec3( + m_TextOffset.x - (QT3DSF32)m_Text.m_TextTextureDetails.m_TextWidth / 2.0f, + m_TextOffset.y - (QT3DSF32)m_Text.m_TextTextureDetails.m_TextHeight / 2.0f, 0.0)); + + QT3DSMat44 pathMatrix = m_Text.m_PathFontItem->GetTransform(); + + context.SetPathProjectionMatrix(m_ViewProjection); + context.SetPathModelViewMatrix(m_GlobalTransform * offsetMatrix * pathMatrix); + + // first pass + m_Text.m_PathFontDetails->StencilFillPathInstanced(*m_Text.m_PathFontItem); + + // second pass + context.SetStencilTestEnabled(true); + m_Text.m_PathFontDetails->CoverFillPathInstanced(*m_Text.m_PathFontItem); + + context.SetStencilTestEnabled(isStencilEnabled); + context.SetDepthFunction(theDepthFunction); + } + } + +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + void SDistanceFieldRenderable::Render(const QT3DSVec2 &inCameraVec) + { + m_distanceFieldText.renderText(m_text, m_mvp); + } + + void SDistanceFieldRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec) + { + m_distanceFieldText.renderTextDepth(m_text, m_mvp); + } +#endif + + void SCustomMaterialRenderable::Render(const QT3DSVec2 & /*inCameraVec*/, + const SLayerRenderData &inLayerData, + const SLayer &inLayer, NVDataRef<SLight *> inLights, + const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture, + const NVRenderTexture2D *inSsaoTexture, + TShaderFeatureSet inFeatureSet) + { + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + SCustomMaterialRenderContext theRenderContext( + inLayer, inLayerData, inLights, inCamera, m_ModelContext.m_Model, m_Subset, + m_ModelContext.m_ModelViewProjection, m_GlobalTransform, m_ModelContext.m_NormalMatrix, + m_Material, inDepthTexture, inSsaoTexture, m_ShaderDescription, m_FirstImage, + m_Opacity); + + qt3dsContext.GetCustomMaterialSystem().RenderSubset(theRenderContext, inFeatureSet); + } + + void SCustomMaterialRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec, + const SLayer & /*inLayer*/, + NVConstDataRef<SLight *> /*inLights*/ + , + const SCamera & /*inCamera*/, + const NVRenderTexture2D * /*inDepthTexture*/) + { + + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + if (!qt3dsContext.GetCustomMaterialSystem().RenderDepthPrepass( + m_ModelContext.m_ModelViewProjection, m_Material, m_Subset)) { + SRenderableImage *displacementImage = NULL; + for (SRenderableImage *theImage = m_FirstImage; + theImage != NULL && displacementImage == NULL; theImage = theImage->m_NextImage) { + if (theImage->m_MapType == ImageMapTypes::Displacement) + displacementImage = theImage; + } + + SSubsetRenderableBase::RenderDepthPass(inCameraVec, displacementImage, + m_Material.m_DisplaceAmount); + } + } + + void SPathRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec, const SLayer & /*inLayer*/, + NVConstDataRef<SLight *> inLights, + const SCamera &inCamera, + const NVRenderTexture2D * /*inDepthTexture*/) + { + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + SPathRenderContext theRenderContext( + inLights, inCamera, m_Path, m_ModelViewProjection, m_GlobalTransform, m_NormalMatrix, + m_Opacity, m_Material, m_ShaderDescription, m_FirstImage, qt3dsContext.GetWireframeMode(), + inCameraVec, false, m_IsStroke); + + qt3dsContext.GetPathManager().RenderDepthPrepass( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), TShaderFeatureSet()); + } + + void SPathRenderable::Render(const QT3DSVec2 &inCameraVec, const SLayer & /*inLayer*/, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D * /*inDepthTexture*/ + , + const NVRenderTexture2D * /*inSsaoTexture*/ + , + TShaderFeatureSet inFeatureSet) + { + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + SPathRenderContext theRenderContext( + inLights, inCamera, m_Path, m_ModelViewProjection, m_GlobalTransform, m_NormalMatrix, + m_Opacity, m_Material, m_ShaderDescription, m_FirstImage, qt3dsContext.GetWireframeMode(), + inCameraVec, m_RenderableFlags.HasTransparency(), m_IsStroke); + + qt3dsContext.GetPathManager().RenderPath( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), inFeatureSet); + } + + void SPathRenderable::RenderShadowMapPass(const QT3DSVec2 &inCameraVec, const SLight *inLight, + const SCamera &inCamera, + SShadowMapEntry *inShadowMapEntry) + { + NVConstDataRef<SLight *> theLights; + IQt3DSRenderContext &qt3dsContext(m_Generator.GetQt3DSContext()); + + QT3DSMat44 theModelViewProjection = inShadowMapEntry->m_LightVP * m_GlobalTransform; + SPathRenderContext theRenderContext( + theLights, inCamera, m_Path, theModelViewProjection, m_GlobalTransform, m_NormalMatrix, + m_Opacity, m_Material, m_ShaderDescription, m_FirstImage, qt3dsContext.GetWireframeMode(), + inCameraVec, false, m_IsStroke); + + if (inLight->m_LightType != RenderLightTypes::Directional) { + qt3dsContext.GetPathManager().RenderCubeFaceShadowPass( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), + TShaderFeatureSet()); + } else + qt3dsContext.GetPathManager().RenderShadowMapPass( + theRenderContext, m_Generator.GetLayerGlobalRenderProperties(), + TShaderFeatureSet()); + } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h new file mode 100644 index 0000000..a369f7a --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRenderableObjects.h @@ -0,0 +1,472 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_IMPL_RENDERABLE_OBJECTS_H +#define QT3DS_RENDER_IMPL_RENDERABLE_OBJECTS_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSFlags.h" +#include "Qt3DSRenderModel.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSRenderMesh.h" +#include "Qt3DSRenderShaderKeys.h" +#include "Qt3DSRenderShaderCache.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" +#include "Qt3DSRenderableImage.h" +#include "Qt3DSDistanceFieldRenderer.h" + +namespace qt3ds { +namespace render { + + struct RenderPreparationResultFlagValues + { + enum Enum { + HasTransparency = 1 << 0, + CompletelyTransparent = 1 << 1, + Dirty = 1 << 2, + Pickable = 1 << 3, + DefaultMaterialMeshSubset = 1 << 4, + Text = 1 << 5, + Custom = 1 << 6, + CustomMaterialMeshSubset = 1 << 7, + HasRefraction = 1 << 8, + Path = 1 << 9, + ShadowCaster = 1 << 10, + DistanceField = 1 << 11, + }; + }; + + struct SRenderableObjectFlags : public NVFlags<RenderPreparationResultFlagValues::Enum, QT3DSU32> + { + void ClearOrSet(bool value, RenderPreparationResultFlagValues::Enum enumVal) + { + if (value) + this->operator|=(enumVal); + else + clear(enumVal); + } + + void SetHasTransparency(bool inHasTransparency) + { + ClearOrSet(inHasTransparency, RenderPreparationResultFlagValues::HasTransparency); + } + bool HasTransparency() const + { + return this->operator&(RenderPreparationResultFlagValues::HasTransparency); + } + bool HasRefraction() const + { + return this->operator&(RenderPreparationResultFlagValues::HasRefraction); + } + void SetCompletelyTransparent(bool inTransparent) + { + ClearOrSet(inTransparent, RenderPreparationResultFlagValues::CompletelyTransparent); + } + bool IsCompletelyTransparent() const + { + return this->operator&(RenderPreparationResultFlagValues::CompletelyTransparent); + } + void SetDirty(bool inDirty) + { + ClearOrSet(inDirty, RenderPreparationResultFlagValues::Dirty); + } + bool IsDirty() const { return this->operator&(RenderPreparationResultFlagValues::Dirty); } + void SetPickable(bool inPickable) + { + ClearOrSet(inPickable, RenderPreparationResultFlagValues::Pickable); + } + bool GetPickable() const + { + return this->operator&(RenderPreparationResultFlagValues::Pickable); + } + + // Mutually exclusive values + void SetDefaultMaterialMeshSubset(bool inMeshSubset) + { + ClearOrSet(inMeshSubset, RenderPreparationResultFlagValues::DefaultMaterialMeshSubset); + } + bool IsDefaultMaterialMeshSubset() const + { + return this->operator&(RenderPreparationResultFlagValues::DefaultMaterialMeshSubset); + } + + void SetCustomMaterialMeshSubset(bool inMeshSubset) + { + ClearOrSet(inMeshSubset, RenderPreparationResultFlagValues::CustomMaterialMeshSubset); + } + bool IsCustomMaterialMeshSubset() const + { + return this->operator&(RenderPreparationResultFlagValues::CustomMaterialMeshSubset); + } + + void SetText(bool inText) { ClearOrSet(inText, RenderPreparationResultFlagValues::Text); } + bool IsText() const { return this->operator&(RenderPreparationResultFlagValues::Text); } + + void setDistanceField(bool inText) + { + ClearOrSet(inText, RenderPreparationResultFlagValues::DistanceField); + } + + bool isDistanceField() const + { + return this->operator&(RenderPreparationResultFlagValues::DistanceField); + } + + void SetCustom(bool inCustom) + { + ClearOrSet(inCustom, RenderPreparationResultFlagValues::Custom); + } + bool IsCustom() const { return this->operator&(RenderPreparationResultFlagValues::Custom); } + + void SetPath(bool inPath) { ClearOrSet(inPath, RenderPreparationResultFlagValues::Path); } + bool IsPath() const { return this->operator&(RenderPreparationResultFlagValues::Path); } + + void SetShadowCaster(bool inCaster) + { + ClearOrSet(inCaster, RenderPreparationResultFlagValues::ShadowCaster); + } + bool IsShadowCaster() const + { + return this->operator&(RenderPreparationResultFlagValues::ShadowCaster); + } + }; + + struct SNodeLightEntry + { + SLight *m_Light; + QT3DSU32 m_LightIndex; + SNodeLightEntry *m_NextNode; + SNodeLightEntry() + : m_Light(NULL) + , m_NextNode(NULL) + { + } + SNodeLightEntry(SLight *inLight, QT3DSU32 inLightIndex) + : m_Light(inLight) + , m_LightIndex(inLightIndex) + , m_NextNode(NULL) + { + } + }; + + DEFINE_INVASIVE_SINGLE_LIST(NodeLightEntry); + + IMPLEMENT_INVASIVE_SINGLE_LIST(NodeLightEntry, m_NextNode); + + struct SRenderableObject; + + typedef void (*TRenderFunction)(SRenderableObject &inObject, const QT3DSVec2 &inCameraProperties); + + struct SRenderableObject + { + // Variables used for picking + const QT3DSMat44 &m_GlobalTransform; + const NVBounds3 &m_Bounds; + SRenderableObjectFlags m_RenderableFlags; + // For rough sorting for transparency and for depth + QT3DSVec3 m_WorldCenterPoint; + QT3DSF32 m_CameraDistanceSq; + TessModeValues::Enum m_TessellationMode; + bool m_ShadowCaster; + // For custom renderable objects the render function must be defined + TRenderFunction m_RenderFunction; + TNodeLightEntryList m_ScopedLights; + SRenderableObject(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + const QT3DSMat44 &inGlobalTransform, const NVBounds3 &inBounds, + TessModeValues::Enum inTessMode = TessModeValues::NoTess, + bool inShadowCaster = true, TRenderFunction inFunction = nullptr) + + : m_GlobalTransform(inGlobalTransform) + , m_Bounds(inBounds) + , m_RenderableFlags(inFlags) + , m_WorldCenterPoint(inWorldCenterPt) + , m_CameraDistanceSq(0) + , m_TessellationMode(inTessMode) + , m_ShadowCaster(inShadowCaster) + , m_RenderFunction(inFunction) + { + } + bool operator<(SRenderableObject *inOther) const + { + return m_CameraDistanceSq < inOther->m_CameraDistanceSq; + } + }; + + typedef nvvector<SRenderableObject *> TRenderableObjectList; + + // Different subsets from the same model will get the same + // model context so we can generate the MVP and normal matrix once + // and only once per subset. + struct SModelContext + { + const SModel &m_Model; + QT3DSMat44 m_ModelViewProjection; + QT3DSMat33 m_NormalMatrix; + + SModelContext(const SModel &inModel, const QT3DSMat44 &inViewProjection) + : m_Model(inModel) + { + m_Model.CalculateMVPAndNormalMatrix(inViewProjection, m_ModelViewProjection, + m_NormalMatrix); + } + SModelContext(const SModelContext &inOther) + : m_Model(inOther.m_Model) + { + // The default copy constructor for these objects is pretty darn slow. + memCopy(&m_ModelViewProjection, &inOther.m_ModelViewProjection, + sizeof(m_ModelViewProjection)); + memCopy(&m_NormalMatrix, &inOther.m_NormalMatrix, sizeof(m_NormalMatrix)); + } + }; + + typedef nvvector<SModelContext *> TModelContextPtrList; + + class Qt3DSRendererImpl; + struct SLayerRenderData; + struct SShadowMapEntry; + + struct SSubsetRenderableBase : public SRenderableObject + { + Qt3DSRendererImpl &m_Generator; + const SModelContext &m_ModelContext; + SRenderSubset m_Subset; + QT3DSF32 m_Opacity; + + SSubsetRenderableBase(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SRenderSubset &subset, + const SModelContext &modelContext, QT3DSF32 inOpacity) + + : SRenderableObject(inFlags, inWorldCenterPt, modelContext.m_Model.m_GlobalTransform, + m_Subset.m_Bounds) + , m_Generator(gen) + , m_ModelContext(modelContext) + , m_Subset(subset) + , m_Opacity(inOpacity) + { + } + void RenderShadowMapPass(const QT3DSVec2 &inCameraVec, const SLight *inLight, + const SCamera &inCamera, SShadowMapEntry *inShadowMapEntry); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec, SRenderableImage *inDisplacementImage, + float inDisplacementAmount); + }; + + /** + * A renderable that corresponds to a subset (a part of a model). + * These are created per subset per layer and are responsible for actually + * rendering this type of object. + */ + struct SSubsetRenderable : public SSubsetRenderableBase + { + const SDefaultMaterial &m_Material; + SRenderableImage *m_FirstImage; + SShaderDefaultMaterialKey m_ShaderDescription; + NVConstDataRef<QT3DSMat44> m_Bones; + + SSubsetRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SRenderSubset &subset, + const SDefaultMaterial &mat, const SModelContext &modelContext, + QT3DSF32 inOpacity, SRenderableImage *inFirstImage, + SShaderDefaultMaterialKey inShaderKey, + NVConstDataRef<QT3DSMat44> inBoneGlobals) + + : SSubsetRenderableBase(inFlags, inWorldCenterPt, gen, subset, modelContext, inOpacity) + , m_Material(mat) + , m_FirstImage(inFirstImage) + , m_ShaderDescription(inShaderKey) + , m_Bones(inBoneGlobals) + { + m_RenderableFlags.SetDefaultMaterialMeshSubset(true); + m_RenderableFlags.SetCustom(false); + m_RenderableFlags.SetText(false); + m_RenderableFlags.setDistanceField(false); + } + + void Render(const QT3DSVec2 &inCameraVec, TShaderFeatureSet inFeatureSet); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec); + + DefaultMaterialBlendMode::Enum getBlendingMode() + { + return m_Material.m_BlendMode; + } + }; + + struct SCustomMaterialRenderable : public SSubsetRenderableBase + { + const SCustomMaterial &m_Material; + SRenderableImage *m_FirstImage; + SShaderDefaultMaterialKey m_ShaderDescription; + + SCustomMaterialRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SRenderSubset &subset, + const SCustomMaterial &mat, const SModelContext &modelContext, + QT3DSF32 inOpacity, SRenderableImage *inFirstImage, + SShaderDefaultMaterialKey inShaderKey) + : SSubsetRenderableBase(inFlags, inWorldCenterPt, gen, subset, modelContext, inOpacity) + , m_Material(mat) + , m_FirstImage(inFirstImage) + , m_ShaderDescription(inShaderKey) + { + m_RenderableFlags.SetCustomMaterialMeshSubset(true); + } + + void Render(const QT3DSVec2 &inCameraVec, const SLayerRenderData &inLayerData, + const SLayer &inLayer, NVDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture, const NVRenderTexture2D *inSsaoTexture, + TShaderFeatureSet inFeatureSet); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec, const SLayer &inLayer, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture); + }; + + struct STextScaleAndOffset + { + QT3DSVec2 m_TextOffset; + QT3DSVec2 m_TextScale; + STextScaleAndOffset(const QT3DSVec2 &inTextOffset, const QT3DSVec2 &inTextScale) + : m_TextOffset(inTextOffset) + , m_TextScale(inTextScale) + { + } + STextScaleAndOffset(NVRenderTexture2D &inTexture, const STextTextureDetails &inTextDetails, + const STextRenderInfo &inInfo); + }; + + struct STextRenderable : public SRenderableObject, public STextScaleAndOffset + { + Qt3DSRendererImpl &m_Generator; + const SText &m_Text; + NVRenderTexture2D &m_Texture; + QT3DSMat44 m_ModelViewProjection; + QT3DSMat44 m_ViewProjection; + + STextRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const SText &inText, const NVBounds3 &inBounds, + const QT3DSMat44 &inModelViewProjection, const QT3DSMat44 &inViewProjection, + NVRenderTexture2D &inTextTexture, const QT3DSVec2 &inTextOffset, + const QT3DSVec2 &inTextScale) + : SRenderableObject(inFlags, inWorldCenterPt, inText.m_GlobalTransform, inBounds) + , STextScaleAndOffset(inTextOffset, inTextScale) + , m_Generator(gen) + , m_Text(inText) + , m_Texture(inTextTexture) + , m_ModelViewProjection(inModelViewProjection) + , m_ViewProjection(inViewProjection) + { + m_RenderableFlags.SetDefaultMaterialMeshSubset(false); + m_RenderableFlags.SetCustom(false); + m_RenderableFlags.SetText(true); + m_RenderableFlags.setDistanceField(false); + } + + void Render(const QT3DSVec2 &inCameraVec); + void RenderDepthPass(const QT3DSVec2 &inCameraVec); + }; + +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + struct SDistanceFieldRenderable : public SRenderableObject + { + Q3DSDistanceFieldRenderer &m_distanceFieldText; + QT3DSMat44 m_mvp; + SText &m_text; + + SDistanceFieldRenderable(SRenderableObjectFlags flags, QT3DSVec3 worldCenterPt, + SText &text, const NVBounds3 &bounds, const QT3DSMat44 &mvp, + Q3DSDistanceFieldRenderer &distanceFieldText) + : SRenderableObject(flags, worldCenterPt, text.m_GlobalTransform, bounds) + , m_distanceFieldText(distanceFieldText) + , m_mvp(mvp) + , m_text(text) + { + m_RenderableFlags.SetDefaultMaterialMeshSubset(false); + m_RenderableFlags.SetCustom(false); + m_RenderableFlags.SetText(false); + m_RenderableFlags.setDistanceField(true); + m_distanceFieldText.checkAndBuildGlyphs(text); + } + + void Render(const QT3DSVec2 &inCameraVec); + void RenderDepthPass(const QT3DSVec2 &inCameraVec); + }; +#endif + + struct SPathRenderable : public SRenderableObject + { + Qt3DSRendererImpl &m_Generator; + SPath &m_Path; + NVBounds3 m_Bounds; + QT3DSMat44 m_ModelViewProjection; + QT3DSMat33 m_NormalMatrix; + const SGraphObject &m_Material; + QT3DSF32 m_Opacity; + SRenderableImage *m_FirstImage; + SShaderDefaultMaterialKey m_ShaderDescription; + bool m_IsStroke; + + SPathRenderable(SRenderableObjectFlags inFlags, QT3DSVec3 inWorldCenterPt, + Qt3DSRendererImpl &gen, const QT3DSMat44 &inGlobalTransform, + NVBounds3 &inBounds, SPath &inPath, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 inNormalMat, const SGraphObject &inMaterial, QT3DSF32 inOpacity, + SShaderDefaultMaterialKey inShaderKey, bool inIsStroke) + + : SRenderableObject(inFlags, inWorldCenterPt, inGlobalTransform, m_Bounds) + , m_Generator(gen) + , m_Path(inPath) + , m_Bounds(inBounds) + , m_ModelViewProjection(inModelViewProjection) + , m_NormalMatrix(inNormalMat) + , m_Material(inMaterial) + , m_Opacity(inOpacity) + , m_FirstImage(NULL) + , m_ShaderDescription(inShaderKey) + , m_IsStroke(inIsStroke) + { + m_RenderableFlags.SetPath(true); + } + void Render(const QT3DSVec2 &inCameraVec, const SLayer &inLayer, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture, const NVRenderTexture2D *inSsaoTexture, + TShaderFeatureSet inFeatureSet); + + void RenderDepthPass(const QT3DSVec2 &inCameraVec, const SLayer &inLayer, + NVConstDataRef<SLight *> inLights, const SCamera &inCamera, + const NVRenderTexture2D *inDepthTexture); + + void RenderShadowMapPass(const QT3DSVec2 &inCameraVec, const SLight *inLight, + const SCamera &inCamera, SShadowMapEntry *inShadowMapEntry); + }; +} +} + +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp new file mode 100644 index 0000000..0b3f665 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp @@ -0,0 +1,2051 @@ +/**************************************************************************** +** +** 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 "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderBufferManager.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "EASTL/sort.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSTextRenderer.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderPresentation.h" +#include "Qt3DSRenderEffect.h" +#include "Qt3DSRenderEffectSystem.h" +#include "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "Qt3DSRenderTextTextureCache.h" +#include "Qt3DSRenderTextTextureAtlas.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderRenderList.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include <stdlib.h> + +#ifdef _WIN32 +#pragma warning(disable : 4355) +#endif + +// Quick tests you can run to find performance problems + +//#define QT3DS_RENDER_DISABLE_HARDWARE_BLENDING 1 +//#define QT3DS_RENDER_DISABLE_LIGHTING 1 +//#define QT3DS_RENDER_DISABLE_TEXTURING 1 +//#define QT3DS_RENDER_DISABLE_TRANSPARENCY 1 +//#define QT3DS_RENDER_DISABLE_FRUSTUM_CULLING 1 + +// If you are fillrate bound then sorting opaque objects can help in some circumstances +//#define QT3DS_RENDER_DISABLE_OPAQUE_SORT 1 + +using qt3ds::foundation::CRegisteredString; + +namespace qt3ds { +namespace render { + + struct SRenderableImage; + struct SShaderGeneratorGeneratedShader; + struct SSubsetRenderable; + using eastl::make_pair; + using eastl::reverse; + using eastl::stable_sort; + + SEndlType Endl; + + static SRenderInstanceId combineLayerAndId(const SLayer *layer, const SRenderInstanceId id) + { + uint64_t x = (uint64_t)layer; + x += 31u * (uint64_t)id; + return (SRenderInstanceId)x; + } + + Qt3DSRendererImpl::Qt3DSRendererImpl(IQt3DSRenderContext &ctx) + : m_qt3dsContext(ctx) + , m_Context(ctx.GetRenderContext()) + , m_BufferManager(ctx.GetBufferManager()) + , m_OffscreenRenderManager(ctx.GetOffscreenRenderManager()) + , m_StringTable(IStringTable::CreateStringTable(ctx.GetAllocator())) + , m_LayerShaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LayerShaders") + , m_Shaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_Shaders") + , m_ConstantBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_ConstantBuffers") + , m_TextShader(ctx.GetAllocator()) + , m_TextPathShader(ctx.GetAllocator()) + , m_TextWidgetShader(ctx.GetAllocator()) + , m_TextOnscreenShader(ctx.GetAllocator()) +#ifdef ADVANCED_BLEND_SW_FALLBACK + , m_LayerBlendTexture(ctx.GetResourceManager()) + , m_BlendFB(NULL) +#endif + , m_InstanceRenderMap(ctx.GetAllocator(), "Qt3DSRendererImpl::m_InstanceRenderMap") + , m_LastFrameLayers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LastFrameLayers") + , mRefCount(0) + , m_LastPickResults(ctx.GetAllocator(), "Qt3DSRendererImpl::m_LastPickResults") + , m_CurrentLayer(NULL) + , m_WidgetVertexBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetVertexBuffers") + , m_WidgetIndexBuffers(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetIndexBuffers") + , m_WidgetShaders(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetShaders") + , m_WidgetInputAssembler(ctx.GetAllocator(), "Qt3DSRendererImpl::m_WidgetInputAssembler") + , m_BoneIdNodeMap(ctx.GetAllocator(), "Qt3DSRendererImpl::m_BoneIdNodeMap") + , m_PickRenderPlugins(true) + , m_LayerCachingEnabled(true) + , m_LayerGPuProfilingEnabled(false) + { + } + Qt3DSRendererImpl::~Qt3DSRendererImpl() + { + m_LayerShaders.clear(); + for (TShaderMap::iterator iter = m_Shaders.begin(), end = m_Shaders.end(); iter != end; + ++iter) + NVDelete(m_Context->GetAllocator(), iter->second); + + m_Shaders.clear(); + m_InstanceRenderMap.clear(); + m_ConstantBuffers.clear(); + } + + void Qt3DSRendererImpl::addRef() { atomicIncrement(&mRefCount); } + + void Qt3DSRendererImpl::release() { QT3DS_IMPLEMENT_REF_COUNT_RELEASE(m_Context->GetAllocator()); } + + void Qt3DSRendererImpl::ChildrenUpdated(SNode &inParent) + { + if (inParent.m_Type == GraphObjectTypes::Layer) { + TInstanceRenderMap::iterator theIter + = m_InstanceRenderMap.find(static_cast<SRenderInstanceId>(&inParent)); + if (theIter == m_InstanceRenderMap.end()) { + // The layer is not in main presentation, but it might be in subpresentation + theIter = m_InstanceRenderMap.begin(); + while (theIter != m_InstanceRenderMap.end()) { + if (static_cast<SNode *>(&theIter->second.mPtr->m_Layer) == &inParent) + break; + theIter++; + } + } + if (theIter != m_InstanceRenderMap.end()) { + theIter->second->m_CamerasAndLights.clear(); + theIter->second->m_RenderableNodes.clear(); + } + } else if (inParent.m_Parent) + ChildrenUpdated(*inParent.m_Parent); + } + + QT3DSF32 Qt3DSRendererImpl::GetTextScale(const SText &inText) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inText); + if (theData) + return theData->m_TextScale; + return 1.0f; + } + + static inline SLayer *GetNextLayer(SLayer &inLayer) + { + if (inLayer.m_NextSibling && inLayer.m_NextSibling->m_Type == GraphObjectTypes::Layer) + return static_cast<SLayer *>(inLayer.m_NextSibling); + return NULL; + } + + static inline void MaybePushLayer(SLayer &inLayer, nvvector<SLayer *> &outLayerList) + { + inLayer.CalculateGlobalVariables(); + if (inLayer.m_Flags.IsGloballyActive() && inLayer.m_Flags.IsLayerRenderToTarget()) + outLayerList.push_back(&inLayer); + } + static void BuildRenderableLayers(SLayer &inLayer, nvvector<SLayer *> &renderableLayers, + bool inRenderSiblings) + { + MaybePushLayer(inLayer, renderableLayers); + if (inRenderSiblings) { + for (SLayer *theNextLayer = GetNextLayer(inLayer); theNextLayer; + theNextLayer = GetNextLayer(*theNextLayer)) + MaybePushLayer(*theNextLayer, renderableLayers); + } + } + + void Qt3DSRendererImpl::EnableLayerGpuProfiling(bool inEnabled) + { + if (m_LayerGPuProfilingEnabled != inEnabled) { + TInstanceRenderMap::iterator theIter; + for (theIter = m_InstanceRenderMap.begin(); theIter != m_InstanceRenderMap.end(); + theIter++) { + SLayerRenderData *data = theIter->second; + if (!inEnabled) + data->m_LayerProfilerGpu = nullptr; + else + data->CreateGpuProfiler(); + } + } + m_LayerGPuProfilingEnabled = inEnabled; + } + + bool Qt3DSRendererImpl::PrepareLayerForRender(SLayer &inLayer, + const QT3DSVec2 &inViewportDimensions, + bool inRenderSiblings, + const SRenderInstanceId id) + { + (void)inViewportDimensions; + nvvector<SLayer *> renderableLayers(m_qt3dsContext.GetPerFrameAllocator(), "LayerVector"); + // Found by fair roll of the dice. + renderableLayers.reserve(4); + + BuildRenderableLayers(inLayer, renderableLayers, inRenderSiblings); + + bool retval = false; + + for (nvvector<SLayer *>::reverse_iterator iter = renderableLayers.rbegin(), + end = renderableLayers.rend(); + iter != end; ++iter) { + // Store the previous state of if we were rendering a layer. + SLayer *theLayer = *iter; + SLayerRenderData *theRenderData = GetOrCreateLayerRenderDataForNode(*theLayer, id); + + if (theRenderData) { + theRenderData->PrepareForRender(); + retval = retval || theRenderData->m_LayerPrepResult->m_Flags.WasDirty(); + } else { + QT3DS_ASSERT(false); + } + } + + return retval; + } + + void Qt3DSRendererImpl::RenderLayer(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool clear, QT3DSVec4 clearColor, bool inRenderSiblings, + const SRenderInstanceId id) + { + (void)inViewportDimensions; + nvvector<SLayer *> renderableLayers(m_qt3dsContext.GetPerFrameAllocator(), "LayerVector"); + // Found by fair roll of the dice. + renderableLayers.reserve(4); + + BuildRenderableLayers(inLayer, renderableLayers, inRenderSiblings); + + NVRenderContext &theRenderContext(m_qt3dsContext.GetRenderContext()); + qt3ds::render::NVRenderFrameBuffer *theFB = theRenderContext.GetRenderTarget(); + for (nvvector<SLayer *>::reverse_iterator iter = renderableLayers.rbegin(), + end = renderableLayers.rend(); + iter != end; ++iter) { + SLayer *theLayer = *iter; + SLayerRenderData *theRenderData = GetOrCreateLayerRenderDataForNode(*theLayer, id); + SLayerRenderPreparationResult &prepRes(*theRenderData->m_LayerPrepResult); + LayerBlendTypes::Enum layerBlend = prepRes.GetLayer()->GetLayerBlend(); +#ifdef ADVANCED_BLEND_SW_FALLBACK + if ((layerBlend == LayerBlendTypes::Overlay || + layerBlend == LayerBlendTypes::ColorBurn || + layerBlend == LayerBlendTypes::ColorDodge) && + !theRenderContext.IsAdvancedBlendHwSupported() && + !theRenderContext.IsAdvancedBlendHwSupportedKHR()) { + // Create and set up FBO and texture for advanced blending SW fallback + NVRenderRect viewport = theRenderContext.GetViewport(); + m_LayerBlendTexture.EnsureTexture(viewport.m_Width + viewport.m_X, + viewport.m_Height + viewport.m_Y, + NVRenderTextureFormats::RGBA8); + if (m_BlendFB == NULL) + m_BlendFB = theRenderContext.CreateFrameBuffer(); + m_BlendFB->Attach(NVRenderFrameBufferAttachments::Color0, *m_LayerBlendTexture); + theRenderContext.SetRenderTarget(m_BlendFB); + theRenderContext.SetScissorTestEnabled(false); + QT3DSVec4 color(0.0f); + if (clear) + color = clearColor; + + QT3DSVec4 origColor = theRenderContext.GetClearColor(); + theRenderContext.SetClearColor(color); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + theRenderContext.SetClearColor(origColor); + theRenderContext.SetRenderTarget(theFB); + break; + } else { + m_LayerBlendTexture.ReleaseTexture(); + } +#endif + } + for (nvvector<SLayer *>::reverse_iterator iter = renderableLayers.rbegin(), + end = renderableLayers.rend(); + iter != end; ++iter) { + // Store the previous state of if we were rendering a layer. + SLayer *theLayer = *iter; + SLayerRenderData *theRenderData = GetOrCreateLayerRenderDataForNode(*theLayer, id); + + if (theRenderData) { + if (theRenderData->m_LayerPrepResult->IsLayerVisible()) + theRenderData->RunnableRenderToViewport(theFB); + } else { + QT3DS_ASSERT(false); + } + } + } + + SLayer *Qt3DSRendererImpl::GetLayerForNode(const SNode &inNode) const + { + if (inNode.m_Type == GraphObjectTypes::Layer) { + return &const_cast<SLayer &>(static_cast<const SLayer &>(inNode)); + } + if (inNode.m_Parent) + return GetLayerForNode(*inNode.m_Parent); + return NULL; + } + + SLayerRenderData *Qt3DSRendererImpl::GetOrCreateLayerRenderDataForNode(const SNode &inNode, + const SRenderInstanceId id) + { + const SLayer *theLayer = GetLayerForNode(inNode); + if (theLayer) { + TInstanceRenderMap::const_iterator theIter + = m_InstanceRenderMap.find(combineLayerAndId(theLayer, id)); + if (theIter != m_InstanceRenderMap.end()) + return const_cast<SLayerRenderData *>(theIter->second.mPtr); + + SLayerRenderData *theRenderData = QT3DS_NEW(m_Context->GetAllocator(), SLayerRenderData)( + const_cast<SLayer &>(*theLayer), *this); + m_InstanceRenderMap.insert(make_pair(combineLayerAndId(theLayer, id), theRenderData)); + + // create a profiler if enabled + if (IsLayerGpuProfilingEnabled() && theRenderData) + theRenderData->CreateGpuProfiler(); + + return theRenderData; + } + return NULL; + } + + SCamera *Qt3DSRendererImpl::GetCameraForNode(const SNode &inNode) const + { + SLayerRenderData *theLayer = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theLayer) + return theLayer->m_Camera; + return NULL; + } + + Option<SCuboidRect> Qt3DSRendererImpl::GetCameraBounds(const SGraphObject &inObject) + { + if (GraphObjectTypes::IsNodeType(inObject.m_Type)) { + const SNode &theNode = static_cast<const SNode &>(inObject); + SLayerRenderData *theLayer = GetOrCreateLayerRenderDataForNode(theNode); + if (theLayer->GetOffscreenRenderer() == false) { + SCamera *theCamera = theLayer->m_Camera; + if (theCamera) + return theCamera->GetCameraBounds( + theLayer->m_LayerPrepResult->GetLayerToPresentationViewport(), + theLayer->m_LayerPrepResult->GetPresentationDesignDimensions()); + } + } + return Option<SCuboidRect>(); + } + + void Qt3DSRendererImpl::DrawScreenRect(NVRenderRectF inRect, const QT3DSVec3 &inColor) + { + SCamera theScreenCamera; + theScreenCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + NVRenderRectF theViewport(m_Context->GetViewport()); + theScreenCamera.m_Flags.SetOrthographic(true); + theScreenCamera.CalculateGlobalVariables(theViewport, + QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + GenerateXYQuad(); + if (!m_ScreenRectShader) { + IShaderProgramGenerator &theGenerator(GetProgramGenerator()); + theGenerator.BeginProgram(); + IShaderStageGenerator &vertexGenerator( + *theGenerator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *theGenerator.GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddUniform("rectangle_dims", "vec3"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos * rectangle_dims, 1.0);"); + vertexGenerator.Append("}"); + fragmentGenerator.AddUniform("output_color", "vec3"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tgl_FragColor.rgb = output_color;"); + fragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + fragmentGenerator.Append("}"); + // No flags enabled + m_ScreenRectShader = theGenerator.CompileGeneratedShader( + "DrawScreenRect", SShaderCacheProgramFlags(), TShaderFeatureSet()); + } + if (m_ScreenRectShader) { + // Fudge the rect by one pixel to ensure we see all the corners. + if (inRect.m_Width > 1) + inRect.m_Width -= 1; + if (inRect.m_Height > 1) + inRect.m_Height -= 1; + inRect.m_X += 1; + inRect.m_Y += 1; + // Figure out the rect center. + SNode theNode; + + QT3DSVec2 rectGlobalCenter = inRect.Center(); + QT3DSVec2 rectCenter(theViewport.ToNormalizedRectRelative(rectGlobalCenter)); + theNode.m_Position.x = rectCenter.x; + theNode.m_Position.y = rectCenter.y; + theNode.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theNode.CalculateGlobalVariables(); + QT3DSMat44 theViewProjection; + theScreenCamera.CalculateViewProjectionMatrix(theViewProjection); + QT3DSMat44 theMVP; + QT3DSMat33 theNormal; + theNode.CalculateMVPAndNormalMatrix(theViewProjection, theMVP, theNormal); + m_Context->SetBlendingEnabled(false); + m_Context->SetDepthWriteEnabled(false); + m_Context->SetDepthTestEnabled(false); + m_Context->SetCullingEnabled(false); + m_Context->SetActiveShader(m_ScreenRectShader); + m_ScreenRectShader->SetPropertyValue("model_view_projection", theMVP); + m_ScreenRectShader->SetPropertyValue("output_color", inColor); + m_ScreenRectShader->SetPropertyValue( + "rectangle_dims", QT3DSVec3(inRect.m_Width / 2.0f, inRect.m_Height / 2.0f, 0.0f)); + } + if (!m_RectInputAssembler) { + QT3DS_ASSERT(m_QuadVertexBuffer); + QT3DSU8 indexData[] = { 0, 1, 1, 2, 2, 3, 3, 0 }; + + m_RectIndexBuffer = m_Context->CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, + qt3ds::render::NVRenderComponentTypes::QT3DSU8, sizeof(indexData), + toConstDataRef(indexData, sizeof(indexData))); + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + }; + + // create our attribute layout + m_RectAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 1)); + + QT3DSU32 strides = m_QuadVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_RectInputAssembler = m_Context->CreateInputAssembler( + m_RectAttribLayout, toConstDataRef(&m_QuadVertexBuffer.mPtr, 1), m_RectIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + m_Context->SetInputAssembler(m_RectInputAssembler); + m_Context->Draw(NVRenderDrawMode::Lines, m_RectIndexBuffer->GetNumIndices(), 0); + } + + void Qt3DSRendererImpl::SetupWidgetLayer() + { + NVRenderContext &theContext = m_qt3dsContext.GetRenderContext(); + + if (!m_WidgetTexture) { + IResourceManager &theManager = m_qt3dsContext.GetResourceManager(); + m_WidgetTexture = theManager.AllocateTexture2D(m_BeginFrameViewport.m_Width, + m_BeginFrameViewport.m_Height, + NVRenderTextureFormats::RGBA8); + m_WidgetFBO = theManager.AllocateFrameBuffer(); + m_WidgetFBO->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*m_WidgetTexture)); + theContext.SetRenderTarget(m_WidgetFBO); + + // NVRenderRect theScissorRect( 0, 0, m_BeginFrameViewport.m_Width, + // m_BeginFrameViewport.m_Height ); + // NVRenderContextScopedProperty<NVRenderRect> __scissorRect( theContext, + // &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect, theScissorRect ); + qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + m_Context->SetClearColor(QT3DSVec4(0, 0, 0, 0)); + m_Context->Clear(NVRenderClearValues::Color); + + } else + theContext.SetRenderTarget(m_WidgetFBO); + } + + void Qt3DSRendererImpl::BeginFrame() + { + for (QT3DSU32 idx = 0, end = m_LastFrameLayers.size(); idx < end; ++idx) + m_LastFrameLayers[idx]->ResetForFrame(); + m_LastFrameLayers.clear(); + m_BeginFrameViewport = m_qt3dsContext.GetRenderList().GetViewport(); + } + void Qt3DSRendererImpl::EndFrame() + { + if (m_WidgetTexture) { + using qt3ds::render::NVRenderContextScopedProperty; + // Releasing the widget FBO can set it as the active frame buffer. + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __fbo( + *m_Context, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + STextureDetails theDetails = m_WidgetTexture->GetTextureDetails(); + m_Context->SetBlendingEnabled(true); + // Colors are expected to be non-premultiplied, so we premultiply alpha into them at + // this point. + m_Context->SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha)); + m_Context->SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + + m_Context->SetDepthTestEnabled(false); + m_Context->SetScissorTestEnabled(false); + m_Context->SetViewport(m_BeginFrameViewport); + SCamera theCamera; + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + QT3DSVec2 theTextureDims((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theDetails.m_Width, theDetails.m_Height), theTextureDims); + QT3DSMat44 theViewProj; + theCamera.CalculateViewProjectionMatrix(theViewProj); + RenderQuad(theTextureDims, theViewProj, *m_WidgetTexture); + + IResourceManager &theManager(m_qt3dsContext.GetResourceManager()); + theManager.Release(*m_WidgetFBO); + theManager.Release(*m_WidgetTexture); + m_WidgetTexture = NULL; + m_WidgetFBO = NULL; + } + } + + inline bool PickResultLessThan(const Qt3DSRenderPickResult &lhs, const Qt3DSRenderPickResult &rhs) + { + return FloatLessThan(lhs.m_CameraDistanceSq, rhs.m_CameraDistanceSq); + } + + inline QT3DSF32 ClampUVCoord(QT3DSF32 inUVCoord, NVRenderTextureCoordOp::Enum inCoordOp) + { + if (inUVCoord > 1.0f || inUVCoord < 0.0f) { + switch (inCoordOp) { + default: + QT3DS_ASSERT(false); + break; + case NVRenderTextureCoordOp::ClampToEdge: + inUVCoord = NVMin(inUVCoord, 1.0f); + inUVCoord = NVMax(inUVCoord, 0.0f); + break; + case NVRenderTextureCoordOp::Repeat: { + QT3DSF32 multiplier = inUVCoord > 0.0f ? 1.0f : -1.0f; + QT3DSF32 clamp = fabs(inUVCoord); + clamp = clamp - floor(clamp); + if (multiplier < 0) + inUVCoord = 1.0f - clamp; + else + inUVCoord = clamp; + } break; + case NVRenderTextureCoordOp::MirroredRepeat: { + QT3DSF32 multiplier = inUVCoord > 0.0f ? 1.0f : -1.0f; + QT3DSF32 clamp = fabs(inUVCoord); + if (multiplier > 0.0f) + clamp -= 1.0f; + QT3DSU32 isMirrored = ((QT3DSU32)clamp) % 2 == 0; + QT3DSF32 remainder = clamp - floor(clamp); + inUVCoord = remainder; + if (isMirrored) { + if (multiplier > 0.0f) + inUVCoord = 1.0f - inUVCoord; + } else { + if (multiplier < 0.0f) + inUVCoord = 1.0f - remainder; + } + } break; + } + } + return inUVCoord; + } + + static eastl::pair<QT3DSVec2, QT3DSVec2> + GetMouseCoordsAndViewportFromSubObject(QT3DSVec2 inLocalHitUVSpace, + Qt3DSRenderPickSubResult &inSubResult) + { + QT3DSMat44 theTextureMatrix(inSubResult.m_TextureMatrix); + QT3DSVec3 theNewUVCoords( + theTextureMatrix.transform(QT3DSVec3(inLocalHitUVSpace.x, inLocalHitUVSpace.y, 0))); + theNewUVCoords.x = ClampUVCoord(theNewUVCoords.x, inSubResult.m_HorizontalTilingMode); + theNewUVCoords.y = ClampUVCoord(theNewUVCoords.y, inSubResult.m_VerticalTilingMode); + QT3DSVec2 theViewportDimensions = + QT3DSVec2((QT3DSF32)inSubResult.m_ViewportWidth, (QT3DSF32)inSubResult.m_ViewportHeight); + QT3DSVec2 theMouseCoords(theNewUVCoords.x * theViewportDimensions.x, + (1.0f - theNewUVCoords.y) * theViewportDimensions.y); + + return eastl::make_pair(theMouseCoords, theViewportDimensions); + } + + SPickResultProcessResult Qt3DSRendererImpl::ProcessPickResultList(bool inPickEverything) + { + if (m_LastPickResults.empty()) + return SPickResultProcessResult(); + // Things are rendered in a particular order and we need to respect that ordering. + eastl::stable_sort(m_LastPickResults.begin(), m_LastPickResults.end(), PickResultLessThan); + + // We need to pick against sub objects basically somewhat recursively + // but if we don't hit any sub objects and the parent isn't pickable then + // we need to move onto the next item in the list. + // We need to keep in mind that theQuery->Pick will enter this method in a later + // stack frame so *if* we get to sub objects we need to pick against them but if the pick + // completely misses *and* the parent object locally pickable is false then we need to move + // onto the next object. + + QT3DSU32 maxPerFrameAllocationPickResultCount = + SFastAllocator<>::SlabSize / sizeof(Qt3DSRenderPickResult); + QT3DSU32 numToCopy = + NVMin(maxPerFrameAllocationPickResultCount, (QT3DSU32)m_LastPickResults.size()); + QT3DSU32 numCopyBytes = numToCopy * sizeof(Qt3DSRenderPickResult); + Qt3DSRenderPickResult *thePickResults = reinterpret_cast<Qt3DSRenderPickResult *>( + GetPerFrameAllocator().allocate(numCopyBytes, "tempPickData", __FILE__, __LINE__)); + memCopy(thePickResults, m_LastPickResults.data(), numCopyBytes); + m_LastPickResults.clear(); + bool foundValidResult = false; + SPickResultProcessResult thePickResult(thePickResults[0]); + for (size_t idx = 0; idx < numToCopy && foundValidResult == false; ++idx) { + thePickResult = thePickResults[idx]; + // Here we do a hierarchy. Picking against sub objects takes precedence. + // If picking against the sub object doesn't return a valid result *and* + // the current object isn't globally pickable then we move onto the next object returned + // by the pick query. + if (thePickResult.m_HitObject != NULL && thePickResult.m_FirstSubObject != NULL + && m_PickRenderPlugins) { + QT3DSVec2 theUVCoords(thePickResult.m_LocalUVCoords.x, + thePickResult.m_LocalUVCoords.y); + IOffscreenRenderer *theSubRenderer(thePickResult.m_FirstSubObject->m_SubRenderer); + eastl::pair<QT3DSVec2, QT3DSVec2> mouseAndViewport = + GetMouseCoordsAndViewportFromSubObject(theUVCoords, + *thePickResult.m_FirstSubObject); + QT3DSVec2 theMouseCoords = mouseAndViewport.first; + QT3DSVec2 theViewportDimensions = mouseAndViewport.second; + IGraphObjectPickQuery *theQuery = theSubRenderer->GetGraphObjectPickQuery(this); + if (theQuery) { + Qt3DSRenderPickResult theInnerPickResult = + theQuery->Pick(theMouseCoords, theViewportDimensions, inPickEverything); + if (theInnerPickResult.m_HitObject) { + thePickResult = theInnerPickResult; + thePickResult.m_OffscreenRenderer = theSubRenderer; + foundValidResult = true; + thePickResult.m_WasPickConsumed = true; + } else if (GraphObjectTypes::IsNodeType(thePickResult.m_HitObject->m_Type)) { + const SNode *theNode = + static_cast<const SNode *>(thePickResult.m_HitObject); + if (theNode->m_Flags.IsGloballyPickable() == true) { + foundValidResult = true; + thePickResult.m_WasPickConsumed = true; + } + } + } else { + // If the sub renderer doesn't consume the pick then we return the picked object + // itself. So no matter what, if we get to here the pick was consumed. + thePickResult.m_WasPickConsumed = true; + bool wasPickConsumed = + theSubRenderer->Pick(theMouseCoords, theViewportDimensions, this); + if (wasPickConsumed) { + thePickResult.m_HitObject = NULL; + foundValidResult = true; + } + } + } else { + foundValidResult = true; + thePickResult.m_WasPickConsumed = true; + } + } + return thePickResult; + } + + Qt3DSRenderPickResult Qt3DSRendererImpl::Pick(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickSiblings, + bool inPickEverything, const SRenderInstanceId id) + { + m_LastPickResults.clear(); + + SLayer *theLayer = &inLayer; + // Stepping through how the original runtime did picking it picked layers in order + // stopping at the first hit. So objects on the top layer had first crack at the pick + // vector itself. + do { + if (theLayer->m_Flags.IsActive()) { + TInstanceRenderMap::iterator theIter + = m_InstanceRenderMap.find(combineLayerAndId(theLayer, id)); + if (theIter != m_InstanceRenderMap.end()) { + m_LastPickResults.clear(); + GetLayerHitObjectList(*theIter->second, inViewportDimensions, inMouseCoords, + inPickEverything, m_LastPickResults, + GetPerFrameAllocator()); + SPickResultProcessResult retval(ProcessPickResultList(inPickEverything)); + if (retval.m_WasPickConsumed) + return retval; + } else { + // QT3DS_ASSERT( false ); + } + } + + if (inPickSiblings) + theLayer = GetNextLayer(*theLayer); + else + theLayer = NULL; + } while (theLayer != NULL); + + return Qt3DSRenderPickResult(); + } + + static inline Option<QT3DSVec2> IntersectRayWithNode(const SNode &inNode, + SRenderableObject &inRenderableObject, + const SRay &inPickRay) + { + if (inRenderableObject.m_RenderableFlags.IsText()) { + STextRenderable &theRenderable = static_cast<STextRenderable &>(inRenderableObject); + if (&theRenderable.m_Text == &inNode) { + return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform, + inRenderableObject.m_Bounds); + } +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + } else if (inRenderableObject.m_RenderableFlags.isDistanceField()) { + SDistanceFieldRenderable &theRenderable = static_cast<SDistanceFieldRenderable &>( + inRenderableObject); + if (&theRenderable.m_text == &inNode) { + return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform, + inRenderableObject.m_Bounds); + } +#endif + } else if (inRenderableObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) { + SSubsetRenderable &theRenderable = static_cast<SSubsetRenderable &>(inRenderableObject); + if (&theRenderable.m_ModelContext.m_Model == &inNode) + return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform, + inRenderableObject.m_Bounds); + } else if (inRenderableObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) { + SCustomMaterialRenderable &theRenderable = + static_cast<SCustomMaterialRenderable &>(inRenderableObject); + if (&theRenderable.m_ModelContext.m_Model == &inNode) + return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform, + inRenderableObject.m_Bounds); + } else { + QT3DS_ASSERT(false); + } + return Empty(); + } + + static inline Qt3DSRenderPickSubResult ConstructSubResult(SImage &inImage) + { + STextureDetails theDetails = inImage.m_TextureData.m_Texture->GetTextureDetails(); + return Qt3DSRenderPickSubResult(*inImage.m_LastFrameOffscreenRenderer, + inImage.m_TextureTransform, inImage.m_HorizontalTilingMode, + inImage.m_VerticalTilingMode, theDetails.m_Width, + theDetails.m_Height); + } + + Option<QT3DSVec2> Qt3DSRendererImpl::FacePosition(SNode &inNode, NVBounds3 inBounds, + const QT3DSMat44 &inGlobalTransform, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, + NVDataRef<SGraphObject *> inMapperObjects, + SBasisPlanes::Enum inPlane) + { + SLayerRenderData *theLayerData = GetOrCreateLayerRenderDataForNode(inNode); + if (theLayerData == NULL) + return Empty(); + // This function assumes the layer was rendered to the scene itself. There is another + // function + // for completely offscreen layers that don't get rendered to the scene. + bool wasRenderToTarget(theLayerData->m_Layer.m_Flags.IsLayerRenderToTarget()); + if (wasRenderToTarget == false || theLayerData->m_Camera == NULL + || theLayerData->m_LayerPrepResult.hasValue() == false + || theLayerData->m_LastFrameOffscreenRenderer.mPtr != NULL) + return Empty(); + + QT3DSVec2 theMouseCoords(inMouseCoords); + QT3DSVec2 theViewportDimensions(inViewportDimensions); + + for (QT3DSU32 idx = 0, end = inMapperObjects.size(); idx < end; ++idx) { + SGraphObject ¤tObject = *inMapperObjects[idx]; + if (currentObject.m_Type == GraphObjectTypes::Layer) { + // The layer knows its viewport so it can take the information directly. + // This is extremely counter intuitive but a good sign. + } else if (currentObject.m_Type == GraphObjectTypes::Image) { + SImage &theImage = static_cast<SImage &>(currentObject); + SModel *theParentModel = NULL; + if (theImage.m_Parent + && theImage.m_Parent->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial *theMaterial = + static_cast<SDefaultMaterial *>(theImage.m_Parent); + if (theMaterial) { + theParentModel = theMaterial->m_Parent; + } + } + if (theParentModel == NULL) { + QT3DS_ASSERT(false); + return Empty(); + } + NVBounds3 theModelBounds = theParentModel->GetBounds( + GetQt3DSContext().GetBufferManager(), GetQt3DSContext().GetPathManager(), false); + + if (theModelBounds.isEmpty()) { + QT3DS_ASSERT(false); + return Empty(); + } + Option<QT3DSVec2> relativeHit = + FacePosition(*theParentModel, theModelBounds, theParentModel->m_GlobalTransform, + theViewportDimensions, theMouseCoords, NVDataRef<SGraphObject *>(), + SBasisPlanes::XY); + if (relativeHit.isEmpty()) { + return Empty(); + } + Qt3DSRenderPickSubResult theResult = ConstructSubResult(theImage); + QT3DSVec2 hitInUVSpace = (*relativeHit) + QT3DSVec2(.5f, .5f); + eastl::pair<QT3DSVec2, QT3DSVec2> mouseAndViewport = + GetMouseCoordsAndViewportFromSubObject(hitInUVSpace, theResult); + theMouseCoords = mouseAndViewport.first; + theViewportDimensions = mouseAndViewport.second; + } + } + + Option<SRay> theHitRay = theLayerData->m_LayerPrepResult->GetPickRay( + theMouseCoords, theViewportDimensions, false); + if (theHitRay.hasValue() == false) + return Empty(); + + // Scale the mouse coords to change them into the camera's numerical space. + SRay thePickRay = *theHitRay; + Option<QT3DSVec2> newValue = thePickRay.GetRelative(inGlobalTransform, inBounds, inPlane); + return newValue; + } + + Qt3DSRenderPickResult + Qt3DSRendererImpl::PickOffscreenLayer(SLayer &/*inLayer*/, const QT3DSVec2 & /*inViewportDimensions*/ + , + const QT3DSVec2 & /*inMouseCoords*/ + , + bool /*inPickEverything*/) + { + return Qt3DSRenderPickResult(); + } + + QT3DSVec3 Qt3DSRendererImpl::UnprojectToPosition(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec2 &inMouseVec) const + { + // Translate mouse into layer's coordinates + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theData == NULL || theData->m_Camera == NULL) { + return QT3DSVec3(0, 0, 0); + } // QT3DS_ASSERT( false ); return QT3DSVec3(0,0,0); } + + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + QT3DSVec2 theDims((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()); + + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + SRay theRay = thePrepResult.GetPickRay(inMouseVec, theDims, true); + + return theData->m_Camera->UnprojectToPosition(inPosition, theRay); + } + + QT3DSVec3 Qt3DSRendererImpl::UnprojectWithDepth(SNode &inNode, QT3DSVec3 &, + const QT3DSVec3 &inMouseVec) const + { + // Translate mouse into layer's coordinates + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theData == NULL || theData->m_Camera == NULL) { + return QT3DSVec3(0, 0, 0); + } // QT3DS_ASSERT( false ); return QT3DSVec3(0,0,0); } + + // Flip the y into gl coordinates from window coordinates. + QT3DSVec2 theMouse(inMouseVec.x, inMouseVec.y); + NVReal theDepth = inMouseVec.z; + + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + SRay theRay = thePrepResult.GetPickRay( + theMouse, QT3DSVec2((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()), true); + QT3DSVec3 theTargetPosition = theRay.m_Origin + theRay.m_Direction * theDepth; + if (inNode.m_Parent != NULL && inNode.m_Parent->m_Type != GraphObjectTypes::Layer) + theTargetPosition = + inNode.m_Parent->m_GlobalTransform.getInverse().transform(theTargetPosition); + // Our default global space is right handed, so if you are left handed z means something + // opposite. + if (inNode.m_Flags.IsLeftHanded()) + theTargetPosition.z *= -1; + return theTargetPosition; + } + + QT3DSVec3 Qt3DSRendererImpl::ProjectPosition(SNode &inNode, const QT3DSVec3 &inPosition) const + { + // Translate mouse into layer's coordinates + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theData == NULL || theData->m_Camera == NULL) { + return QT3DSVec3(0, 0, 0); + } + + QT3DSMat44 viewProj; + theData->m_Camera->CalculateViewProjectionMatrix(viewProj); + QT3DSVec4 projPos = viewProj.transform(QT3DSVec4(inPosition, 1.0f)); + projPos.x /= projPos.w; + projPos.y /= projPos.w; + + NVRenderRectF theViewport = theData->m_LayerPrepResult->GetLayerToPresentationViewport(); + QT3DSVec2 theDims((QT3DSF32)theViewport.m_Width, (QT3DSF32)theViewport.m_Height); + projPos.x += 1.0; + projPos.y += 1.0; + projPos.x *= 0.5; + projPos.y *= 0.5; + QT3DSVec3 cameraToObject = theData->m_Camera->GetGlobalPos() - inPosition; + projPos.z = sqrtf(cameraToObject.dot(cameraToObject)); + QT3DSVec3 mouseVec = QT3DSVec3(projPos.x, projPos.y, projPos.z); + mouseVec.x *= theDims.x; + mouseVec.y *= theDims.y; + + mouseVec.x += theViewport.m_X; + mouseVec.y += theViewport.m_Y; + + // Flip the y into window coordinates so it matches the mouse. + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + mouseVec.y = theWindow.height() - mouseVec.y; + + return mouseVec; + } + + Option<SLayerPickSetup> Qt3DSRendererImpl::GetLayerPickSetup(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QSize &inPickDims) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return Empty(); + } + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + QT3DSVec2 theDims((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()); + // The mouse is relative to the layer + Option<QT3DSVec2> theLocalMouse = GetLayerMouseCoords(*theData, inMouseCoords, theDims, false); + if (theLocalMouse.hasValue() == false) { + return Empty(); + } + + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + if (thePrepResult.GetCamera() == NULL) { + return Empty(); + } + // Perform gluPickMatrix and pre-multiply it into the view projection + QT3DSMat44 theTransScale(QT3DSMat44::createIdentity()); + SCamera &theCamera(*thePrepResult.GetCamera()); + + NVRenderRectF layerToPresentation = thePrepResult.GetLayerToPresentationViewport(); + // Offsetting is already taken care of in the camera's projection. + // All we need to do is to scale and translate the image. + layerToPresentation.m_X = 0; + layerToPresentation.m_Y = 0; + QT3DSVec2 theMouse(*theLocalMouse); + // The viewport will need to center at this location + QT3DSVec2 viewportDims((QT3DSF32)inPickDims.width(), (QT3DSF32)inPickDims.height()); + QT3DSVec2 bottomLeft = + QT3DSVec2(theMouse.x - viewportDims.x / 2.0f, theMouse.y - viewportDims.y / 2.0f); + // For some reason, and I haven't figured out why yet, the offsets need to be backwards for + // this to work. + // bottomLeft.x = layerToPresentation.m_Width - bottomLeft.x; + // bottomLeft.y = layerToPresentation.m_Height - bottomLeft.y; + // Virtual rect is relative to the layer. + NVRenderRectF thePickRect(bottomLeft.x, bottomLeft.y, viewportDims.x, viewportDims.y); + QT3DSMat44 projectionPremult(QT3DSMat44::createIdentity()); + projectionPremult = render::NVRenderContext::ApplyVirtualViewportToProjectionMatrix( + projectionPremult, layerToPresentation, thePickRect); + projectionPremult = projectionPremult.getInverse(); + + QT3DSMat44 globalInverse = theCamera.m_GlobalTransform.getInverse(); + QT3DSMat44 theVP = theCamera.m_Projection * globalInverse; + // For now we won't setup the scissor, so we may be off by inPickDims at most because + // GetLayerMouseCoords will return + // false if the mouse is too far off the layer. + return SLayerPickSetup(projectionPremult, theVP, + NVRenderRect(0, 0, (QT3DSU32)layerToPresentation.m_Width, + (QT3DSU32)layerToPresentation.m_Height)); + } + + Option<NVRenderRectF> Qt3DSRendererImpl::GetLayerRect(SLayer &inLayer) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return Empty(); + } + SLayerRenderPreparationResult &thePrepResult(*theData->m_LayerPrepResult); + return thePrepResult.GetLayerToPresentationViewport(); + } + + // This doesn't have to be cheap. + void Qt3DSRendererImpl::RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return; + } + theData->PrepareAndRender(inViewProjection); + } + + void Qt3DSRendererImpl::AddRenderWidget(IRenderWidget &inWidget) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inWidget.GetNode()); + if (theData) + theData->AddRenderWidget(inWidget); + } + + void Qt3DSRendererImpl::RenderLayerRect(SLayer &inLayer, const QT3DSVec3 &inColor) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData) + theData->m_BoundingRectColor = inColor; + } + + SScaleAndPosition Qt3DSRendererImpl::GetWorldToPixelScaleFactor(const SCamera &inCamera, + const QT3DSVec3 &inWorldPoint, + SLayerRenderData &inRenderData) + { + if (inCamera.m_Flags.IsOrthographic() == true) { + // There are situations where the camera can scale. + return SScaleAndPosition( + inWorldPoint, + inCamera.GetOrthographicScaleFactor( + inRenderData.m_LayerPrepResult->GetLayerToPresentationViewport(), + inRenderData.m_LayerPrepResult->GetPresentationDesignDimensions())); + } else { + QT3DSVec3 theCameraPos(0, 0, 0); + QT3DSVec3 theCameraDir(0, 0, -1); + SRay theRay(theCameraPos, inWorldPoint - theCameraPos); + NVPlane thePlane(theCameraDir, -600); + QT3DSVec3 theItemPosition(inWorldPoint); + Option<QT3DSVec3> theIntersection = theRay.Intersect(thePlane); + if (theIntersection.hasValue()) + theItemPosition = *theIntersection; + // The special number comes in from physically measuring how off we are on the screen. + QT3DSF32 theScaleFactor = (1.0f / inCamera.m_Projection.column1[1]); + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inCamera); + QT3DSU32 theHeight = theData->m_LayerPrepResult->GetTextureDimensions().height(); + QT3DSF32 theScaleMultiplier = 600.0f / ((QT3DSF32)theHeight / 2.0f); + theScaleFactor *= theScaleMultiplier; + + return SScaleAndPosition(theItemPosition, theScaleFactor); + } + } + + SScaleAndPosition Qt3DSRendererImpl::GetWorldToPixelScaleFactor(SLayer &inLayer, + const QT3DSVec3 &inWorldPoint) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer); + if (theData == NULL || theData->m_Camera == NULL) { + QT3DS_ASSERT(false); + return SScaleAndPosition(); + } + return GetWorldToPixelScaleFactor(*theData->m_Camera, inWorldPoint, *theData); + } + + void Qt3DSRendererImpl::ReleaseLayerRenderResources(SLayer &inLayer, const SRenderInstanceId id) + { + TInstanceRenderMap::iterator theIter + = m_InstanceRenderMap.find(combineLayerAndId(&inLayer, id)); + if (theIter != m_InstanceRenderMap.end()) { + TLayerRenderList::iterator theLastFrm = eastl::find( + m_LastFrameLayers.begin(), m_LastFrameLayers.end(), theIter->second.mPtr); + if (theLastFrm != m_LastFrameLayers.end()) { + theIter->second->ResetForFrame(); + m_LastFrameLayers.erase(theLastFrm); + } + m_InstanceRenderMap.erase(theIter); + } + } + + void Qt3DSRendererImpl::RenderQuad(const QT3DSVec2 inDimensions, const QT3DSMat44 &inMVP, + NVRenderTexture2D &inQuadTexture) + { + m_Context->SetCullingEnabled(false); + SLayerSceneShader *theShader = GetSceneLayerShader(); + NVRenderContext &theContext(*m_Context); + theContext.SetActiveShader(&theShader->m_Shader); + theShader->m_MVP.Set(inMVP); + theShader->m_Dimensions.Set(inDimensions); + theShader->m_Sampler.Set(&inQuadTexture); + + GenerateXYQuad(); + theContext.SetInputAssembler(m_QuadInputAssembler); + theContext.Draw(NVRenderDrawMode::Triangles, m_QuadIndexBuffer->GetNumIndices(), 0); + } + + void Qt3DSRendererImpl::RenderQuad() + { + m_Context->SetCullingEnabled(false); + GenerateXYQuad(); + m_Context->SetInputAssembler(m_QuadInputAssembler); + m_Context->Draw(NVRenderDrawMode::Triangles, m_QuadIndexBuffer->GetNumIndices(), 0); + } + + void Qt3DSRendererImpl::RenderPointsIndirect() + { + m_Context->SetCullingEnabled(false); + GenerateXYZPoint(); + m_Context->SetInputAssembler(m_PointInputAssembler); + m_Context->DrawIndirect(NVRenderDrawMode::Points, 0); + } + + void Qt3DSRendererImpl::LayerNeedsFrameClear(SLayerRenderData &inLayer) + { + m_LastFrameLayers.push_back(&inLayer); + } + + void Qt3DSRendererImpl::BeginLayerDepthPassRender(SLayerRenderData &inLayer) + { + m_CurrentLayer = &inLayer; + } + + void Qt3DSRendererImpl::EndLayerDepthPassRender() { m_CurrentLayer = NULL; } + + void Qt3DSRendererImpl::BeginLayerRender(SLayerRenderData &inLayer) + { + m_CurrentLayer = &inLayer; + // Remove all of the shaders from the layer shader set + // so that we can only apply the camera and lighting properties to + // shaders that are in the layer. + m_LayerShaders.clear(); + } + void Qt3DSRendererImpl::EndLayerRender() { m_CurrentLayer = NULL; } + +// Allocate an object that lasts only this frame. +#define RENDER_FRAME_NEW(type) \ + new (m_PerFrameAllocator.m_FastAllocator.allocate(sizeof(type), __FILE__, __LINE__)) type + + void Qt3DSRendererImpl::PrepareImageForIbl(SImage &inImage) + { + if (inImage.m_TextureData.m_Texture && inImage.m_TextureData.m_Texture->GetNumMipmaps() < 1) + inImage.m_TextureData.m_Texture->GenerateMipmaps(); + } + + bool NodeContainsBoneRoot(SNode &childNode, QT3DSI32 rootID) + { + for (SNode *childChild = childNode.m_FirstChild; childChild != NULL; + childChild = childChild->m_NextSibling) { + if (childChild->m_SkeletonId == rootID) + return true; + } + + return false; + } + + void FillBoneIdNodeMap(SNode &childNode, nvhash_map<long, SNode *> &ioMap) + { + if (childNode.m_SkeletonId >= 0) + ioMap[childNode.m_SkeletonId] = &childNode; + for (SNode *childChild = childNode.m_FirstChild; childChild != NULL; + childChild = childChild->m_NextSibling) + FillBoneIdNodeMap(*childChild, ioMap); + } + + bool Qt3DSRendererImpl::PrepareTextureAtlasForRender() + { + ITextTextureAtlas *theTextureAtlas = m_qt3dsContext.GetTextureAtlas(); + if (theTextureAtlas == NULL) + return false; + + // this is a one time creation + if (!theTextureAtlas->IsInitialized()) { + NVRenderContext &theContext(*m_Context); + NVScopedRefCounted<NVRenderVertexBuffer> mVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> mInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> mAttribLayout; + // temporay FB + using qt3ds::render::NVRenderContextScopedProperty; + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __fbo( + *m_Context, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + + ITextRenderer &theTextRenderer(*m_qt3dsContext.GetOnscreenTextRenderer()); + TTextTextureAtlasDetailsAndTexture theResult = theTextureAtlas->PrepareTextureAtlas(); + if (!theResult.first.m_EntryCount) { + QT3DS_ASSERT(theResult.first.m_EntryCount); + return false; + } + + // generate the index buffer we need + GenerateXYQuad(); + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_uv", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + // create our attribute layout + mAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + NVRenderFrameBuffer *theAtlasFB( + m_qt3dsContext.GetResourceManager().AllocateFrameBuffer()); + theAtlasFB->Attach(NVRenderFrameBufferAttachments::Color0, *theResult.second); + m_qt3dsContext.GetRenderContext().SetRenderTarget(theAtlasFB); + + // this texture contains our single entries + NVRenderTexture2D *theTexture = nullptr; + if (m_Context->GetRenderContextType() == NVRenderContextValues::GLES2) { + theTexture = m_qt3dsContext.GetResourceManager() + .AllocateTexture2D(32, 32, NVRenderTextureFormats::RGBA8); + } else { + theTexture = m_qt3dsContext.GetResourceManager() + .AllocateTexture2D(32, 32, NVRenderTextureFormats::Alpha8); + } + m_Context->SetClearColor(QT3DSVec4(0, 0, 0, 0)); + m_Context->Clear(NVRenderClearValues::Color); + m_Context->SetDepthTestEnabled(false); + m_Context->SetScissorTestEnabled(false); + m_Context->SetCullingEnabled(false); + m_Context->SetBlendingEnabled(false); + m_Context->SetViewport( + NVRenderRect(0, 0, theResult.first.m_TextWidth, theResult.first.m_TextHeight)); + + SCamera theCamera; + theCamera.m_ClipNear = -1.0; + theCamera.m_ClipFar = 1.0; + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + QT3DSVec2 theTextureDims((QT3DSF32)theResult.first.m_TextWidth, + (QT3DSF32)theResult.first.m_TextHeight); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theResult.first.m_TextWidth, theResult.first.m_TextHeight), + theTextureDims); + // We want a 2D lower left projection + QT3DSF32 *writePtr(theCamera.m_Projection.front()); + writePtr[12] = -1; + writePtr[13] = -1; + + // generate render stuff + // We dynamicall update the vertex buffer + QT3DSF32 tempBuf[20]; + QT3DSF32 *bufPtr = tempBuf; + QT3DSU32 bufSize = 20 * sizeof(QT3DSF32); // 4 vertices 3 pos 2 tex + NVDataRef<QT3DSU8> vertData((QT3DSU8 *)bufPtr, bufSize); + mVertexBuffer = theContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Dynamic, 20 * sizeof(QT3DSF32), + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32), vertData); + QT3DSU32 strides = mVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + mInputAssembler = theContext.CreateInputAssembler( + mAttribLayout, toConstDataRef(&mVertexBuffer.mPtr, 1), m_QuadIndexBuffer.mPtr, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + + NVRenderShaderProgram *theShader = GetTextAtlasEntryShader(); + STextShader theTextShader(*theShader); + + if (theShader) { + theContext.SetActiveShader(theShader); + theTextShader.m_MVP.Set(theCamera.m_Projection); + + // we are going through all entries and render to the FBO + for (QT3DSU32 i = 0; i < theResult.first.m_EntryCount; i++) { + STextTextureAtlasEntryDetails theDetails = + theTextRenderer.RenderAtlasEntry(i, *theTexture); + // update vbo + // we need to mirror coordinates + QT3DSF32 x1 = (QT3DSF32)theDetails.m_X; + QT3DSF32 x2 = (QT3DSF32)theDetails.m_X + theDetails.m_TextWidth; + QT3DSF32 y1 = (QT3DSF32)theDetails.m_Y; + QT3DSF32 y2 = (QT3DSF32)theDetails.m_Y + theDetails.m_TextHeight; + + QT3DSF32 box[4][5] = { + { x1, y1, 0, 0, 1 }, + { x1, y2, 0, 0, 0 }, + { x2, y2, 0, 1, 0 }, + { x2, y1, 0, 1, 1 }, + }; + + NVDataRef<QT3DSU8> vertData((QT3DSU8 *)box, bufSize); + mVertexBuffer->UpdateBuffer(vertData, false); + + theTextShader.m_Sampler.Set(theTexture); + + theContext.SetInputAssembler(mInputAssembler); + theContext.Draw(NVRenderDrawMode::Triangles, m_QuadIndexBuffer->GetNumIndices(), + 0); + } + } + + m_qt3dsContext.GetResourceManager().Release(*theTexture); + m_qt3dsContext.GetResourceManager().Release(*theAtlasFB); + + return true; + } + + return theTextureAtlas->IsInitialized(); + } + + Option<QT3DSVec2> Qt3DSRendererImpl::GetLayerMouseCoords(SLayerRenderData &inLayerRenderData, + const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect) const + { + if (inLayerRenderData.m_LayerPrepResult.hasValue()) + return inLayerRenderData.m_LayerPrepResult->GetLayerMouseCoords( + inMouseCoords, inViewportDimensions, forceImageIntersect); + return Empty(); + } + + void Qt3DSRendererImpl::GetLayerHitObjectList(SLayerRenderData &inLayerRenderData, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inPresCoords, bool inPickEverything, + TPickResultArray &outIntersectionResult, + NVAllocatorCallback &inTempAllocator) + { + // This function assumes the layer was rendered to the scene itself. There is another + // function + // for completely offscreen layers that don't get rendered to the scene. + bool wasRenderToTarget(inLayerRenderData.m_Layer.m_Flags.IsLayerRenderToTarget()); + if (wasRenderToTarget && inLayerRenderData.m_Camera != NULL) { + Option<SRay> theHitRay; + if (inLayerRenderData.m_LayerPrepResult.hasValue()) { + theHitRay = inLayerRenderData.m_LayerPrepResult->GetPickRay( + inPresCoords, inViewportDimensions, false, m_Context->isSceneCameraView()); + } + if (inLayerRenderData.m_LastFrameOffscreenRenderer.mPtr == NULL) { + if (theHitRay.hasValue()) { + // Scale the mouse coords to change them into the camera's numerical space. + SRay thePickRay = *theHitRay; + for (QT3DSU32 idx = inLayerRenderData.m_OpaqueObjects.size(), end = 0; idx > end; + --idx) { + SRenderableObject *theRenderableObject = + inLayerRenderData.m_OpaqueObjects[idx - 1]; + if (inPickEverything + || theRenderableObject->m_RenderableFlags.GetPickable()) + IntersectRayWithSubsetRenderable(thePickRay, *theRenderableObject, + outIntersectionResult, + inTempAllocator); + } + for (QT3DSU32 idx = inLayerRenderData.m_TransparentObjects.size(), end = 0; + idx > end; --idx) { + SRenderableObject *theRenderableObject = + inLayerRenderData.m_TransparentObjects[idx - 1]; + if (inPickEverything + || theRenderableObject->m_RenderableFlags.GetPickable()) + IntersectRayWithSubsetRenderable(thePickRay, *theRenderableObject, + outIntersectionResult, + inTempAllocator); + } + } + } else { + IGraphObjectPickQuery *theQuery = + inLayerRenderData.m_LastFrameOffscreenRenderer->GetGraphObjectPickQuery(this); + if (theQuery) { + Qt3DSRenderPickResult theResult = + theQuery->Pick(inPresCoords, inViewportDimensions, inPickEverything); + if (theResult.m_HitObject) { + theResult.m_OffscreenRenderer = + inLayerRenderData.m_LastFrameOffscreenRenderer; + outIntersectionResult.push_back(theResult); + } + } else + inLayerRenderData.m_LastFrameOffscreenRenderer->Pick(inPresCoords, + inViewportDimensions, + this); + } + } + } + + static inline Qt3DSRenderPickSubResult ConstructSubResult(SRenderableImage &inImage) + { + return ConstructSubResult(inImage.m_Image); + } + + void Qt3DSRendererImpl::IntersectRayWithSubsetRenderable( + const SRay &inRay, SRenderableObject &inRenderableObject, + TPickResultArray &outIntersectionResultList, NVAllocatorCallback &inTempAllocator) + { + Option<SRayIntersectionResult> theIntersectionResultOpt(inRay.IntersectWithAABB( + inRenderableObject.m_GlobalTransform, inRenderableObject.m_Bounds)); + if (theIntersectionResultOpt.hasValue() == false) + return; + SRayIntersectionResult &theResult(*theIntersectionResultOpt); + + // Leave the coordinates relative for right now. + const SGraphObject *thePickObject = NULL; + if (inRenderableObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + thePickObject = + &static_cast<SSubsetRenderable *>(&inRenderableObject)->m_ModelContext.m_Model; + else if (inRenderableObject.m_RenderableFlags.IsText()) + thePickObject = &static_cast<STextRenderable *>(&inRenderableObject)->m_Text; +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + else if (inRenderableObject.m_RenderableFlags.isDistanceField()) + thePickObject = &static_cast<SDistanceFieldRenderable *>(&inRenderableObject)->m_text; +#endif + else if (inRenderableObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) + thePickObject = &static_cast<SCustomMaterialRenderable *>(&inRenderableObject) + ->m_ModelContext.m_Model; + else if (inRenderableObject.m_RenderableFlags.IsPath()) + thePickObject = &static_cast<SPathRenderable *>(&inRenderableObject)->m_Path; + + if (thePickObject != NULL) { + outIntersectionResultList.push_back(Qt3DSRenderPickResult( + *thePickObject, theResult.m_RayLengthSquared, theResult.m_RelXY)); + + // For subsets, we know we can find images on them which may have been the result + // of rendering a sub-presentation. + if (inRenderableObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) { + Qt3DSRenderPickSubResult *theLastResult = NULL; + for (SRenderableImage *theImage = + static_cast<SSubsetRenderable *>(&inRenderableObject)->m_FirstImage; + theImage != NULL; theImage = theImage->m_NextImage) { + if (theImage->m_Image.m_LastFrameOffscreenRenderer != NULL + && theImage->m_Image.m_TextureData.m_Texture != NULL) { + Qt3DSRenderPickSubResult *theSubResult = + (Qt3DSRenderPickSubResult *)inTempAllocator.allocate( + sizeof(Qt3DSRenderPickSubResult), "Qt3DSRenderPickSubResult", + __FILE__, __LINE__); + + new (theSubResult) Qt3DSRenderPickSubResult(ConstructSubResult(*theImage)); + if (theLastResult == NULL) + outIntersectionResultList.back().m_FirstSubObject = theSubResult; + else + theLastResult->m_NextSibling = theSubResult; + theLastResult = theSubResult; + } + } + } + } + } + +#ifndef EA_PLATFORM_WINDOWS +#define _snprintf snprintf +#endif + + NVRenderShaderProgram *Qt3DSRendererImpl::CompileShader(CRegisteredString inName, + const char8_t *inVert, + const char8_t *inFrag) + { + GetProgramGenerator().BeginProgram(); + GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)->Append(inVert); + GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)->Append(inFrag); + return GetProgramGenerator().CompileGeneratedShader(inName); + } + + const QT3DSF32 MINATTENUATION = 0; + const QT3DSF32 MAXATTENUATION = 1000; + + QT3DSF32 ClampFloat(QT3DSF32 value, QT3DSF32 min, QT3DSF32 max) + { + return value < min ? min : ((value > max) ? max : value); + } + + QT3DSF32 TranslateConstantAttenuation(QT3DSF32 attenuation) { return attenuation * .01f; } + + QT3DSF32 TranslateLinearAttenuation(QT3DSF32 attenuation) + { + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0001f; + } + + QT3DSF32 TranslateQuadraticAttenuation(QT3DSF32 attenuation) + { + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0000001f; + } + + SShaderGeneratorGeneratedShader *Qt3DSRendererImpl::GetShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet) + { + if (m_CurrentLayer == NULL) { + QT3DS_ASSERT(false); + return NULL; + } + TShaderMap::iterator theFind = m_Shaders.find(inRenderable.m_ShaderDescription); + SShaderGeneratorGeneratedShader *retval = NULL; + if (theFind == m_Shaders.end()) { + // Generate the shader. + NVRenderShaderProgram *theShader(GenerateShader(inRenderable, inFeatureSet)); + if (theShader) { + SShaderGeneratorGeneratedShader *theGeneratedShader = + (SShaderGeneratorGeneratedShader *)m_Context->GetAllocator().allocate( + sizeof(SShaderGeneratorGeneratedShader), "SShaderGeneratorGeneratedShader", + __FILE__, __LINE__); + new (theGeneratedShader) SShaderGeneratorGeneratedShader( + m_StringTable->RegisterStr(m_GeneratedShaderString.c_str()), *theShader); + m_Shaders.insert(make_pair(inRenderable.m_ShaderDescription, theGeneratedShader)); + retval = theGeneratedShader; + } + // We still insert something because we don't to attempt to generate the same bad shader + // twice. + else + m_Shaders.insert(make_pair(inRenderable.m_ShaderDescription, + (SShaderGeneratorGeneratedShader *)NULL)); + } else + retval = theFind->second; + + if (retval != NULL) { + if (!m_LayerShaders.contains(*retval)) { + m_LayerShaders.insert(*retval); + } + if (m_CurrentLayer && m_CurrentLayer->m_Camera) { + SCamera &theCamera(*m_CurrentLayer->m_Camera); + if (m_CurrentLayer->m_CameraDirection.hasValue() == false) + m_CurrentLayer->m_CameraDirection = theCamera.GetScalingCorrectDirection(); + } + } + return retval; + } + static QT3DSVec3 g_fullScreenRectFace[] = { + QT3DSVec3(-1, -1, 0), QT3DSVec3(-1, 1, 0), QT3DSVec3(1, 1, 0), QT3DSVec3(1, -1, 0), + }; + + static QT3DSVec2 g_fullScreenRectUVs[] = { QT3DSVec2(0, 0), QT3DSVec2(0, 1), QT3DSVec2(1, 1), + QT3DSVec2(1, 0) }; + + void Qt3DSRendererImpl::GenerateXYQuad() + { + if (m_QuadInputAssembler) + return; + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_uv", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + QT3DSF32 tempBuf[20]; + QT3DSF32 *bufPtr = tempBuf; + QT3DSVec3 *facePtr(g_fullScreenRectFace); + QT3DSVec2 *uvPtr(g_fullScreenRectUVs); + for (int j = 0; j < 4; j++, ++facePtr, ++uvPtr, bufPtr += 5) { + bufPtr[0] = facePtr->x; + bufPtr[1] = facePtr->y; + bufPtr[2] = facePtr->z; + bufPtr[3] = uvPtr->x; + bufPtr[4] = uvPtr->y; + } + m_QuadVertexBuffer = m_Context->CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, 20 * sizeof(QT3DSF32), + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32), toU8DataRef(tempBuf, 20)); + + QT3DSU8 indexData[] = { + 0, 1, 2, 0, 2, 3, + }; + m_QuadIndexBuffer = m_Context->CreateIndexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, qt3ds::render::NVRenderComponentTypes::QT3DSU8, + sizeof(indexData), toU8DataRef(indexData, sizeof(indexData))); + + // create our attribute layout + m_QuadAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + // create input assembler object + QT3DSU32 strides = m_QuadVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_QuadInputAssembler = m_Context->CreateInputAssembler( + m_QuadAttribLayout, toConstDataRef(&m_QuadVertexBuffer.mPtr, 1), m_QuadIndexBuffer, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + void Qt3DSRendererImpl::GenerateXYZPoint() + { + if (m_PointInputAssembler) + return; + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_uv", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + QT3DSF32 tempBuf[5]; + tempBuf[0] = tempBuf[1] = tempBuf[2] = 0.0; + tempBuf[3] = tempBuf[4] = 0.0; + + m_PointVertexBuffer = m_Context->CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Static, 5 * sizeof(QT3DSF32), + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32), toU8DataRef(tempBuf, 5)); + + // create our attribute layout + m_PointAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + // create input assembler object + QT3DSU32 strides = m_PointVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_PointInputAssembler = m_Context->CreateInputAssembler( + m_PointAttribLayout, toConstDataRef(&m_PointVertexBuffer.mPtr, 1), NULL, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + eastl::pair<NVRenderVertexBuffer *, NVRenderIndexBuffer *> Qt3DSRendererImpl::GetXYQuad() + { + if (!m_QuadInputAssembler) + GenerateXYQuad(); + + return eastl::make_pair(m_QuadVertexBuffer.mPtr, m_QuadIndexBuffer.mPtr); + } + + SLayerGlobalRenderProperties Qt3DSRendererImpl::GetLayerGlobalRenderProperties() + { + SLayerRenderData &theData = *m_CurrentLayer; + SLayer &theLayer = theData.m_Layer; + if (theData.m_CameraDirection.hasValue() == false) + theData.m_CameraDirection = theData.m_Camera->GetScalingCorrectDirection(); + + return SLayerGlobalRenderProperties( + theLayer, *theData.m_Camera, *theData.m_CameraDirection, theData.m_Lights, + theData.m_LightDirections, theData.m_ShadowMapManager.mPtr, theData.m_LayerDepthTexture, + theData.m_LayerSsaoTexture, theLayer.m_LightProbe, theLayer.m_LightProbe2, + theLayer.m_ProbeHorizon, theLayer.m_ProbeBright, theLayer.m_Probe2Window, + theLayer.m_Probe2Pos, theLayer.m_Probe2Fade, theLayer.m_ProbeFov); + } + + void Qt3DSRendererImpl::GenerateXYQuadStrip() + { + if (m_QuadStripInputAssembler) + return; + + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_uv", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 12), + }; + + // this buffer is filled dynmically + m_QuadStripVertexBuffer = + m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Dynamic, 0, + 3 * sizeof(QT3DSF32) + 2 * sizeof(QT3DSF32) // stride + , + NVDataRef<QT3DSU8>()); + + // create our attribute layout + m_QuadStripAttribLayout = m_Context->CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + // create input assembler object + QT3DSU32 strides = m_QuadStripVertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_QuadStripInputAssembler = m_Context->CreateInputAssembler( + m_QuadStripAttribLayout, toConstDataRef(&m_QuadStripVertexBuffer.mPtr, 1), NULL, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } + + void Qt3DSRendererImpl::UpdateCbAoShadow(const SLayer *pLayer, const SCamera *pCamera, + CResourceTexture2D &inDepthTexture) + { + if (m_Context->GetConstantBufferSupport()) { + CRegisteredString theName = m_Context->GetStringTable().RegisterStr("cbAoShadow"); + NVRenderConstantBuffer *pCB = m_Context->GetConstantBuffer(theName); + + if (!pCB) { + // the size is determined automatically later on + pCB = m_Context->CreateConstantBuffer( + theName, qt3ds::render::NVRenderBufferUsageType::Static, 0, NVDataRef<QT3DSU8>()); + if (!pCB) { + QT3DS_ASSERT(false); + return; + } + m_ConstantBuffers.insert(eastl::make_pair(theName, pCB)); + + // Add paramters. Note should match the appearance in the shader program + pCB->AddParam(m_Context->GetStringTable().RegisterStr("ao_properties"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("ao_properties2"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("shadow_properties"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("aoScreenConst"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + pCB->AddParam(m_Context->GetStringTable().RegisterStr("UvToEyeConst"), + qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4, 1); + } + + // update values + QT3DSVec4 aoProps(pLayer->m_AoStrength * 0.01f, pLayer->m_AoDistance * 0.4f, + pLayer->m_AoSoftness * 0.02f, pLayer->m_AoBias); + pCB->UpdateParam("ao_properties", NVDataRef<QT3DSU8>((QT3DSU8 *)&aoProps, 1)); + QT3DSVec4 aoProps2((QT3DSF32)pLayer->m_AoSamplerate, (pLayer->m_AoDither) ? 1.0f : 0.0f, 0.0f, + 0.0f); + pCB->UpdateParam("ao_properties2", NVDataRef<QT3DSU8>((QT3DSU8 *)&aoProps2, 1)); + QT3DSVec4 shadowProps(pLayer->m_ShadowStrength * 0.01f, pLayer->m_ShadowDist, + pLayer->m_ShadowSoftness * 0.01f, pLayer->m_ShadowBias); + pCB->UpdateParam("shadow_properties", NVDataRef<QT3DSU8>((QT3DSU8 *)&shadowProps, 1)); + + QT3DSF32 R2 = pLayer->m_AoDistance * pLayer->m_AoDistance * 0.16f; + QT3DSF32 rw = 100, rh = 100; + + if (inDepthTexture && inDepthTexture.GetTexture()) { + rw = (QT3DSF32)inDepthTexture.GetTexture()->GetTextureDetails().m_Width; + rh = (QT3DSF32)inDepthTexture.GetTexture()->GetTextureDetails().m_Height; + } + QT3DSF32 fov = (pCamera) ? pCamera->verticalFov(rw / rh) : 1.0f; + QT3DSF32 tanHalfFovY = tanf(0.5f * fov * (rh / rw)); + QT3DSF32 invFocalLenX = tanHalfFovY * (rw / rh); + + QT3DSVec4 aoScreenConst(1.0f / R2, rh / (2.0f * tanHalfFovY), 1.0f / rw, 1.0f / rh); + pCB->UpdateParam("aoScreenConst", NVDataRef<QT3DSU8>((QT3DSU8 *)&aoScreenConst, 1)); + QT3DSVec4 UvToEyeConst(2.0f * invFocalLenX, -2.0f * tanHalfFovY, -invFocalLenX, + tanHalfFovY); + pCB->UpdateParam("UvToEyeConst", NVDataRef<QT3DSU8>((QT3DSU8 *)&UvToEyeConst, 1)); + + // update buffer to hardware + pCB->Update(); + } + } + + // widget context implementation + + NVRenderVertexBuffer &Qt3DSRendererImpl::GetOrCreateVertexBuffer(CRegisteredString &inStr, + QT3DSU32 stride, + NVConstDataRef<QT3DSU8> bufferData) + { + NVRenderVertexBuffer *retval = GetVertexBuffer(inStr); + if (retval) { + // we update the buffer + retval->UpdateBuffer(bufferData, false); + return *retval; + } + retval = m_Context->CreateVertexBuffer(qt3ds::render::NVRenderBufferUsageType::Dynamic, + bufferData.size(), stride, bufferData); + m_WidgetVertexBuffers.insert(eastl::make_pair(inStr, retval)); + return *retval; + } + NVRenderIndexBuffer & + Qt3DSRendererImpl::GetOrCreateIndexBuffer(CRegisteredString &inStr, + qt3ds::render::NVRenderComponentTypes::Enum componentType, + size_t size, NVConstDataRef<QT3DSU8> bufferData) + { + NVRenderIndexBuffer *retval = GetIndexBuffer(inStr); + if (retval) { + // we update the buffer + retval->UpdateBuffer(bufferData, false); + return *retval; + } + + retval = m_Context->CreateIndexBuffer(qt3ds::render::NVRenderBufferUsageType::Dynamic, + componentType, size, bufferData); + m_WidgetIndexBuffers.insert(eastl::make_pair(inStr, retval)); + return *retval; + } + + NVRenderAttribLayout &Qt3DSRendererImpl::CreateAttributeLayout( + NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> attribs) + { + // create our attribute layout + NVRenderAttribLayout *theAttribLAyout = m_Context->CreateAttributeLayout(attribs); + return *theAttribLAyout; + } + + NVRenderInputAssembler &Qt3DSRendererImpl::GetOrCreateInputAssembler( + CRegisteredString &inStr, NVRenderAttribLayout *attribLayout, + NVConstDataRef<NVRenderVertexBuffer *> buffers, const NVRenderIndexBuffer *indexBuffer, + NVConstDataRef<QT3DSU32> strides, NVConstDataRef<QT3DSU32> offsets) + { + NVRenderInputAssembler *retval = GetInputAssembler(inStr); + if (retval) + return *retval; + + retval = + m_Context->CreateInputAssembler(attribLayout, buffers, indexBuffer, strides, offsets); + m_WidgetInputAssembler.insert(eastl::make_pair(inStr, retval)); + return *retval; + } + + NVRenderVertexBuffer *Qt3DSRendererImpl::GetVertexBuffer(CRegisteredString &inStr) + { + TStrVertBufMap::iterator theIter = m_WidgetVertexBuffers.find(inStr); + if (theIter != m_WidgetVertexBuffers.end()) + return theIter->second; + return NULL; + } + + NVRenderIndexBuffer *Qt3DSRendererImpl::GetIndexBuffer(CRegisteredString &inStr) + { + TStrIndexBufMap::iterator theIter = m_WidgetIndexBuffers.find(inStr); + if (theIter != m_WidgetIndexBuffers.end()) + return theIter->second; + return NULL; + } + + NVRenderInputAssembler *Qt3DSRendererImpl::GetInputAssembler(CRegisteredString &inStr) + { + TStrIAMap::iterator theIter = m_WidgetInputAssembler.find(inStr); + if (theIter != m_WidgetInputAssembler.end()) + return theIter->second; + return NULL; + } + + NVRenderShaderProgram *Qt3DSRendererImpl::GetShader(CRegisteredString inStr) + { + TStrShaderMap::iterator theIter = m_WidgetShaders.find(inStr); + if (theIter != m_WidgetShaders.end()) + return theIter->second; + return NULL; + } + + NVRenderShaderProgram *Qt3DSRendererImpl::CompileAndStoreShader(CRegisteredString inStr) + { + NVRenderShaderProgram *newProgram = GetProgramGenerator().CompileGeneratedShader(inStr); + if (newProgram) + m_WidgetShaders.insert(eastl::make_pair(inStr, newProgram)); + return newProgram; + } + + IShaderProgramGenerator &Qt3DSRendererImpl::GetProgramGenerator() + { + return m_qt3dsContext.GetShaderProgramGenerator(); + } + + STextDimensions Qt3DSRendererImpl::MeasureText(const STextRenderInfo &inText) + { + if (m_qt3dsContext.GetTextRenderer() != NULL) + return m_qt3dsContext.GetTextRenderer()->MeasureText(inText, 0); + return STextDimensions(); + } + + void Qt3DSRendererImpl::RenderText(const STextRenderInfo &inText, const QT3DSVec3 &inTextColor, + const QT3DSVec3 &inBackgroundColor, const QT3DSMat44 &inMVP) + { + if (m_qt3dsContext.GetTextRenderer() != NULL) { + ITextRenderer &theTextRenderer(*m_qt3dsContext.GetTextRenderer()); + NVRenderTexture2D *theTexture = m_qt3dsContext.GetResourceManager().AllocateTexture2D( + 32, 32, NVRenderTextureFormats::RGBA8); + STextTextureDetails theTextTextureDetails = + theTextRenderer.RenderText(inText, *theTexture); + STextRenderHelper theTextHelper(GetTextWidgetShader()); + if (theTextHelper.m_Shader != NULL) { + m_qt3dsContext.GetRenderContext().SetBlendingEnabled(false); + STextScaleAndOffset theScaleAndOffset(*theTexture, theTextTextureDetails, inText); + theTextHelper.m_Shader->Render(*theTexture, theScaleAndOffset, + QT3DSVec4(inTextColor, 1.0f), inMVP, QT3DSVec2(0, 0), + GetContext(), theTextHelper.m_QuadInputAssembler, + theTextHelper.m_QuadInputAssembler.GetIndexCount(), + theTextTextureDetails, inBackgroundColor); + } + m_qt3dsContext.GetResourceManager().Release(*theTexture); + } + } + + void Qt3DSRendererImpl::RenderText2D(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) + { + if (m_qt3dsContext.GetOnscreenTextRenderer() != NULL) { + GenerateXYQuadStrip(); + + if (PrepareTextureAtlasForRender()) { + TTextRenderAtlasDetailsAndTexture theRenderTextDetails; + ITextTextureAtlas *theTextureAtlas = m_qt3dsContext.GetTextureAtlas(); + QSize theWindow = m_qt3dsContext.GetWindowDimensions(); + + const wchar_t *wText = m_StringTable->GetWideStr(text); + STextRenderInfo theInfo; + theInfo.m_Text = m_StringTable->RegisterStr(wText); + theInfo.m_FontSize = 20; + // text scale 2% of screen we don't scale Y though because it becomes unreadable + theInfo.m_ScaleX = (theWindow.width() / 100.0f) * 1.5f / (theInfo.m_FontSize); + theInfo.m_ScaleY = 1.0f; + + theRenderTextDetails = theTextureAtlas->RenderText(theInfo); + + if (theRenderTextDetails.first.m_Vertices.size()) { + STextRenderHelper theTextHelper(GetOnscreenTextShader()); + if (theTextHelper.m_Shader != NULL) { + // setup 2D projection + SCamera theCamera; + theCamera.m_ClipNear = -1.0; + theCamera.m_ClipFar = 1.0; + + theCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + theCamera.m_Flags.SetOrthographic(true); + QT3DSVec2 theWindowDim((QT3DSF32)theWindow.width(), (QT3DSF32)theWindow.height()); + theCamera.CalculateGlobalVariables( + NVRenderRect(0, 0, theWindow.width(), theWindow.height()), + theWindowDim); + // We want a 2D lower left projection + QT3DSF32 *writePtr(theCamera.m_Projection.front()); + writePtr[12] = -1; + writePtr[13] = -1; + + // upload vertices + m_QuadStripVertexBuffer->UpdateBuffer(theRenderTextDetails.first.m_Vertices, + false); + + theTextHelper.m_Shader->Render2D( + *theRenderTextDetails.second, QT3DSVec4(inColor, 1.0f), + theCamera.m_Projection, GetContext(), + theTextHelper.m_QuadInputAssembler, + theRenderTextDetails.first.m_VertexCount, QT3DSVec2(x, y)); + } + // we release the memory here + QT3DS_FREE(m_Context->GetAllocator(), + theRenderTextDetails.first.m_Vertices.begin()); + } + } + } + } + + void Qt3DSRendererImpl::RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) + { + if (!IsLayerGpuProfilingEnabled()) + return; + + char messageLine[1024]; + TInstanceRenderMap::const_iterator theIter; + + QT3DSF32 startY = y; + + for (theIter = m_InstanceRenderMap.begin(); theIter != m_InstanceRenderMap.end(); theIter++) { + QT3DSF32 startX = x; + const SLayerRenderData *theLayerRenderData = theIter->second; + const SLayer *theLayer = &theLayerRenderData->m_Layer; + + if (theLayer->m_Flags.IsActive() && theLayerRenderData->m_LayerProfilerGpu.mPtr) { + const IRenderProfiler::TStrIDVec &idList = + theLayerRenderData->m_LayerProfilerGpu->GetTimerIDs(); + if (!idList.empty()) { + startY -= 22; + startX += 20; + RenderText2D(startX, startY, inColor, theLayer->m_Id); + IRenderProfiler::TStrIDVec::const_iterator theIdIter = idList.begin(); + for (theIdIter = idList.begin(); theIdIter != idList.end(); theIdIter++) { + startY -= 22; + sprintf(messageLine, "%s: %.3f ms", theIdIter->c_str(), + theLayerRenderData->m_LayerProfilerGpu->GetElapsedTime(*theIdIter)); + RenderText2D(startX + 20, startY, inColor, messageLine); + } + } + } + } + } + + // Given a node and a point in the node's local space (most likely its pivot point), we return + // a normal matrix so you can get the axis out, a transformation from node to camera + // a new position and a floating point scale factor so you can render in 1/2 perspective mode + // or orthographic mode if you would like to. + SWidgetRenderInformation + Qt3DSRendererImpl::GetWidgetRenderInformation(SNode &inNode, const QT3DSVec3 &inPos, + RenderWidgetModes::Enum inWidgetMode) + { + SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inNode); + SCamera *theCamera = theData->m_Camera; + if (theCamera == NULL || theData->m_LayerPrepResult.hasValue() == false) { + QT3DS_ASSERT(false); + return SWidgetRenderInformation(); + } + QT3DSMat44 theGlobalTransform(QT3DSMat44::createIdentity()); + if (inNode.m_Parent != NULL && inNode.m_Parent->m_Type != GraphObjectTypes::Layer + && !inNode.m_Flags.IsIgnoreParentTransform()) + theGlobalTransform = inNode.m_Parent->m_GlobalTransform; + QT3DSMat44 theCameraInverse(theCamera->m_GlobalTransform.getInverse()); + QT3DSMat44 theNodeParentToCamera; + if (inWidgetMode == RenderWidgetModes::Local) + theNodeParentToCamera = theCameraInverse * theGlobalTransform; + else + theNodeParentToCamera = theCameraInverse; + + QT3DSMat33 theNormalMat(theNodeParentToCamera.column0.getXYZ(), + theNodeParentToCamera.column1.getXYZ(), + theNodeParentToCamera.column2.getXYZ()); + theNormalMat = theNormalMat.getInverse().getTranspose(); + theNormalMat.column0.normalize(); + theNormalMat.column1.normalize(); + theNormalMat.column2.normalize(); + + QT3DSMat44 theTranslation(QT3DSMat44::createIdentity()); + theTranslation.column3.x = inNode.m_Position.x; + theTranslation.column3.y = inNode.m_Position.y; + theTranslation.column3.z = inNode.m_Position.z; + theTranslation.column3.z *= -1.0f; + + theGlobalTransform = theGlobalTransform * theTranslation; + + QT3DSMat44 theNodeToParentPlusTranslation = theCameraInverse * theGlobalTransform; + QT3DSVec3 thePos = theNodeToParentPlusTranslation.transform(inPos); + SScaleAndPosition theScaleAndPos = GetWorldToPixelScaleFactor(*theCamera, thePos, *theData); + QT3DSMat33 theLookAtMatrix(QT3DSMat33::createIdentity()); + if (theCamera->m_Flags.IsOrthographic() == false) { + QT3DSVec3 theNodeToCamera = theScaleAndPos.m_Position; + theNodeToCamera.normalize(); + QT3DSVec3 theOriginalAxis = QT3DSVec3(0, 0, -1); + QT3DSVec3 theRotAxis = theOriginalAxis.cross(theNodeToCamera); + QT3DSF32 theAxisLen = theRotAxis.normalize(); + if (theAxisLen > .05f) { + QT3DSF32 theRotAmount = acos(theOriginalAxis.dot(theNodeToCamera)); + QT3DSQuat theQuat(theRotAmount, theRotAxis); + theLookAtMatrix = QT3DSMat33(theQuat); + } + } + QT3DSVec3 thePosInWorldSpace = theGlobalTransform.transform(inPos); + QT3DSVec3 theCameraPosInWorldSpace = theCamera->GetGlobalPos(); + QT3DSVec3 theCameraOffset = thePosInWorldSpace - theCameraPosInWorldSpace; + QT3DSVec3 theDir = theCameraOffset; + theDir.normalize(); + // Things should be 600 units from the camera, as that is how all of our math is setup. + theCameraOffset = 600.0f * theDir; + return SWidgetRenderInformation( + theNormalMat, theNodeParentToCamera, theCamera->m_Projection, theCamera->m_Projection, + theLookAtMatrix, theCameraInverse, theCameraOffset, theScaleAndPos.m_Position, + theScaleAndPos.m_Scale, *theCamera); + } + + Option<QT3DSVec2> Qt3DSRendererImpl::GetLayerMouseCoords(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect) const + { + SLayerRenderData *theData = + const_cast<Qt3DSRendererImpl &>(*this).GetOrCreateLayerRenderDataForNode(inLayer); + return GetLayerMouseCoords(*theData, inMouseCoords, inViewportDimensions, + forceImageIntersect); + } + + bool IQt3DSRenderer::IsGlEsContext(qt3ds::render::NVRenderContextType inContextType) + { + qt3ds::render::NVRenderContextType esContextTypes(NVRenderContextValues::GLES2 + | NVRenderContextValues::GLES3 + | NVRenderContextValues::GLES3PLUS); + + if ((inContextType & esContextTypes)) + return true; + + return false; + } + + bool IQt3DSRenderer::IsGlEs3Context(qt3ds::render::NVRenderContextType inContextType) + { + if (inContextType == NVRenderContextValues::GLES3 + || inContextType == NVRenderContextValues::GLES3PLUS) + return true; + + return false; + } + + bool IQt3DSRenderer::IsGl2Context(qt3ds::render::NVRenderContextType inContextType) + { + if (inContextType == NVRenderContextValues::GL2) + return true; + + return false; + } + + IQt3DSRenderer &IQt3DSRenderer::CreateRenderer(IQt3DSRenderContext &inContext) + { + return *QT3DS_NEW(inContext.GetAllocator(), Qt3DSRendererImpl)(inContext); + } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImpl.h b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.h new file mode 100644 index 0000000..906afd6 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImpl.h @@ -0,0 +1,549 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SHADER_GENERATOR_IMPL_H +#define QT3DS_RENDER_SHADER_GENERATOR_IMPL_H +#include "Qt3DSRender.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderableObjects.h" +#include "Qt3DSRendererImplShaders.h" +#include "Qt3DSRendererImplLayerRenderData.h" +#include "foundation/Qt3DSFlags.h" +#include "Qt3DSRenderMesh.h" +#include "Qt3DSRenderModel.h" +#include "foundation/Qt3DSBounds3.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSInvasiveSet.h" +#include "EASTL/string.h" +#include "foundation/Qt3DSDataRef.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderRay.h" +#include "Qt3DSRenderText.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRendererImplLayerRenderHelper.h" +#include "Qt3DSRenderWidgets.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "Qt3DSRenderClippingFrustum.h" +#include "foundation/Qt3DSUnionCast.h" +#include "foundation/FastAllocator.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "Qt3DSRenderShaderKeys.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderProfiler.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" + +namespace qt3ds { +namespace render { + + inline bool FloatLessThan(QT3DSF32 lhs, QT3DSF32 rhs) + { + QT3DSF32 diff = lhs - rhs; + if (fabs(diff) < .001) + return false; + return diff < 0.0f ? true : false; + } + inline bool ISRenderObjectPtrLessThan(const SRenderableObject *lhs, + const SRenderableObject *rhs) + { + return FloatLessThan(lhs->m_CameraDistanceSq, rhs->m_CameraDistanceSq); + } + inline bool ISRenderObjectPtrGreatThan(const SRenderableObject *lhs, + const SRenderableObject *rhs) + { + return FloatLessThan(rhs->m_CameraDistanceSq, lhs->m_CameraDistanceSq); + } + inline bool NonZero(float inValue) { return fabs(inValue) > .001f; } + inline bool NonZero(QT3DSU32 inValue) { return inValue != 0; } + inline bool IsZero(float inValue) { return fabs(inValue) < .001f; } + inline bool IsNotOne(float inValue) { return fabs(1.0f - inValue) > .001f; } + + inline bool IsRectEdgeInBounds(QT3DSI32 inNewRectOffset, QT3DSI32 inNewRectWidth, + QT3DSI32 inCurrentRectOffset, QT3DSI32 inCurrentRectWidth) + { + QT3DSI32 newEnd = inNewRectOffset + inNewRectWidth; + QT3DSI32 currentEnd = inCurrentRectOffset + inCurrentRectWidth; + return inNewRectOffset >= inCurrentRectOffset && newEnd <= currentEnd; + } + + struct STextRenderHelper + { + STextShader *m_Shader; + NVRenderInputAssembler &m_QuadInputAssembler; + STextRenderHelper(STextShader *inShader, NVRenderInputAssembler &inQuadInputAssembler) + : m_Shader(inShader) + , m_QuadInputAssembler(inQuadInputAssembler) + { + } + }; + + struct SPickResultProcessResult : public Qt3DSRenderPickResult + { + SPickResultProcessResult(const Qt3DSRenderPickResult &inSrc) + : Qt3DSRenderPickResult(inSrc) + , m_WasPickConsumed(false) + { + } + SPickResultProcessResult() + : m_WasPickConsumed(false) + { + } + bool m_WasPickConsumed; + }; + + struct STextShaderPtr + { + NVAllocatorCallback &m_Allocator; + bool m_HasGeneratedShader; + STextShader *m_Shader; + STextShaderPtr(NVAllocatorCallback &alloc) + : m_Allocator(alloc) + , m_HasGeneratedShader(false) + , m_Shader(NULL) + { + } + bool HasGeneratedShader() { return m_HasGeneratedShader; } + void Set(STextShader *inShader) + { + m_Shader = inShader; + m_HasGeneratedShader = true; + } + ~STextShaderPtr() + { + if (m_Shader) + NVDelete(m_Allocator, m_Shader); + } + operator STextShader *() { return m_Shader; } + }; + + class QT3DS_AUTOTEST_EXPORT Qt3DSRendererImpl : public IQt3DSRenderer, public IRenderWidgetContext + { + typedef nvhash_map<SShaderDefaultMaterialKey, SShaderGeneratorGeneratedShader *> TShaderMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderConstantBuffer>> + TStrConstanBufMap; + typedef nvhash_map<SRenderInstanceId, NVScopedRefCounted<SLayerRenderData>, + eastl::hash<SRenderInstanceId>> TInstanceRenderMap; + typedef nvvector<SLayerRenderData *> TLayerRenderList; + typedef nvvector<Qt3DSRenderPickResult> TPickResultArray; + + // Items to implement the widget context. + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderVertexBuffer>> + TStrVertBufMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderIndexBuffer>> + TStrIndexBufMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderShaderProgram>> + TStrShaderMap; + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderInputAssembler>> TStrIAMap; + + typedef nvhash_map<long, SNode *> TBoneIdNodeMap; + + IQt3DSRenderContext &m_qt3dsContext; + NVScopedRefCounted<NVRenderContext> m_Context; + NVScopedRefCounted<IBufferManager> m_BufferManager; + NVScopedRefCounted<IOffscreenRenderManager> m_OffscreenRenderManager; + NVScopedRefCounted<IStringTable> m_StringTable; + InvasiveSet<SShaderGeneratorGeneratedShader, SGGSGet, SGGSSet> m_LayerShaders; + // For rendering bounding boxes. + NVScopedRefCounted<NVRenderVertexBuffer> m_BoxVertexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_BoxIndexBuffer; + NVScopedRefCounted<NVRenderShaderProgram> m_BoxShader; + NVScopedRefCounted<NVRenderShaderProgram> m_ScreenRectShader; + + NVScopedRefCounted<NVRenderVertexBuffer> m_AxisVertexBuffer; + NVScopedRefCounted<NVRenderShaderProgram> m_AxisShader; + + // X,Y quad, broken down into 2 triangles and normalized over + //-1,1. + NVScopedRefCounted<NVRenderVertexBuffer> m_QuadVertexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_QuadIndexBuffer; + NVScopedRefCounted<NVRenderIndexBuffer> m_RectIndexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_QuadInputAssembler; + NVScopedRefCounted<NVRenderInputAssembler> m_RectInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_QuadAttribLayout; + NVScopedRefCounted<NVRenderAttribLayout> m_RectAttribLayout; + + // X,Y triangle strip quads in screen coord dynamiclly setup + NVScopedRefCounted<NVRenderVertexBuffer> m_QuadStripVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_QuadStripInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_QuadStripAttribLayout; + + // X,Y,Z point which is used for instanced based rendering of points + NVScopedRefCounted<NVRenderVertexBuffer> m_PointVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_PointInputAssembler; + NVScopedRefCounted<NVRenderAttribLayout> m_PointAttribLayout; + + Option<NVScopedRefCounted<SLayerSceneShader>> m_SceneLayerShader; + Option<NVScopedRefCounted<SLayerProgAABlendShader>> m_LayerProgAAShader; + + TShaderMap m_Shaders; + TStrConstanBufMap m_ConstantBuffers; ///< store the the shader constant buffers + // Option is true if we have attempted to generate the shader. + // This does not mean we were successul, however. + Option<NVScopedRefCounted<SDefaultMaterialRenderableDepthShader>> + m_DefaultMaterialDepthPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthPrepassShaderDisplaced; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthTessLinearPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_DepthTessLinearPrepassShaderDisplaced; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthTessPhongPrepassShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_DepthTessNPatchPrepassShader; + Option<NVScopedRefCounted<STextDepthShader>> m_TextDepthPrepassShader; + Option<NVScopedRefCounted<SDefaultAoPassShader>> m_DefaultAoPassShader; + Option<NVScopedRefCounted<SDefaultAoPassShader>> m_FakeDepthShader; + Option<NVScopedRefCounted<SDefaultAoPassShader>> m_FakeCubemapDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthTessLinearShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthTessPhongShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_ParaboloidDepthTessNPatchShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthTessLinearShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthTessPhongShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_CubemapDepthTessNPatchShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> m_OrthographicDepthShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_OrthographicDepthTessLinearShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_OrthographicDepthTessPhongShader; + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> + m_OrthographicDepthTessNPatchShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_CubeShadowBlurXShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_CubeShadowBlurYShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_OrthoShadowBlurXShader; + Option<NVScopedRefCounted<SShadowmapPreblurShader>> m_OrthoShadowBlurYShader; + +#ifdef ADVANCED_BLEND_SW_FALLBACK + Option<NVScopedRefCounted<SAdvancedModeBlendShader>> m_AdvancedModeOverlayBlendShader; + Option<NVScopedRefCounted<SAdvancedModeBlendShader>> m_AdvancedModeColorBurnBlendShader; + Option<NVScopedRefCounted<SAdvancedModeBlendShader>> m_AdvancedModeColorDodgeBlendShader; +#endif + // Text shaders may be generated on demand. + STextShaderPtr m_TextShader; + STextShaderPtr m_TextPathShader; + STextShaderPtr m_TextWidgetShader; + STextShaderPtr m_TextOnscreenShader; + + // Overlay used to render all widgets. + NVRenderRect m_BeginFrameViewport; + NVScopedRefCounted<NVRenderTexture2D> m_WidgetTexture; + NVScopedRefCounted<NVRenderFrameBuffer> m_WidgetFBO; + +#ifdef ADVANCED_BLEND_SW_FALLBACK + // Advanced blend mode SW fallback + CResourceTexture2D m_LayerBlendTexture; + NVScopedRefCounted<NVRenderFrameBuffer> m_BlendFB; +#endif + // Allocator for temporary data that is cleared after every layer. + TInstanceRenderMap m_InstanceRenderMap; + TLayerRenderList m_LastFrameLayers; + volatile QT3DSI32 mRefCount; + + // Set from the first layer. + TPickResultArray m_LastPickResults; + + // Temporary information stored only when rendering a particular layer. + SLayerRenderData *m_CurrentLayer; + QT3DSMat44 m_ViewProjection; + eastl::string m_GeneratedShaderString; + + TStrVertBufMap m_WidgetVertexBuffers; + TStrIndexBufMap m_WidgetIndexBuffers; + TStrShaderMap m_WidgetShaders; + TStrIAMap m_WidgetInputAssembler; + + TBoneIdNodeMap m_BoneIdNodeMap; + + bool m_PickRenderPlugins; + bool m_LayerCachingEnabled; + bool m_LayerGPuProfilingEnabled; + SShaderDefaultMaterialKeyProperties m_DefaultMaterialShaderKeyProperties; + + public: + Qt3DSRendererImpl(IQt3DSRenderContext &ctx); + virtual ~Qt3DSRendererImpl(); + SShaderDefaultMaterialKeyProperties &DefaultMaterialShaderKeyProperties() + { + return m_DefaultMaterialShaderKeyProperties; + } + + // NVRefCounted + void addRef() override; + void release() override; + + void EnableLayerCaching(bool inEnabled) override { m_LayerCachingEnabled = inEnabled; } + bool IsLayerCachingEnabled() const override { return m_LayerCachingEnabled; } + + void EnableLayerGpuProfiling(bool inEnabled) override; + bool IsLayerGpuProfilingEnabled() const override { return m_LayerGPuProfilingEnabled; } + + // Calls prepare layer for render + // and then do render layer. + bool PrepareLayerForRender(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool inRenderSiblings, const SRenderInstanceId id) override; + void RenderLayer(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + bool clear, QT3DSVec4 clearColor, bool inRenderSiblings, + const SRenderInstanceId id) override; + void ChildrenUpdated(SNode &inParent) override; + QT3DSF32 GetTextScale(const SText &inText) override; + + SCamera *GetCameraForNode(const SNode &inNode) const override; + Option<SCuboidRect> GetCameraBounds(const SGraphObject &inObject) override; + virtual SLayer *GetLayerForNode(const SNode &inNode) const; + SLayerRenderData *GetOrCreateLayerRenderDataForNode(const SNode &inNode, + const SRenderInstanceId id = nullptr); + + IRenderWidgetContext &GetRenderWidgetContext() + { + return *this; + } + + void BeginFrame() override; + void EndFrame() override; + + void PickRenderPlugins(bool inPick) override { m_PickRenderPlugins = inPick; } + Qt3DSRenderPickResult Pick(SLayer &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickSiblings, + bool inPickEverything, + const SRenderInstanceId id) override; + + virtual Option<QT3DSVec2> + FacePosition(SNode &inNode, NVBounds3 inBounds, const QT3DSMat44 &inGlobalTransform, + const QT3DSVec2 &inViewportDimensions, const QT3DSVec2 &inMouseCoords, + NVDataRef<SGraphObject *> inMapperObjects, SBasisPlanes::Enum inPlane) override; + + virtual Qt3DSRenderPickResult PickOffscreenLayer(SLayer &inLayer, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, + bool inPickEverything); + + QT3DSVec3 UnprojectToPosition(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec2 &inMouseVec) const override; + QT3DSVec3 UnprojectWithDepth(SNode &inNode, QT3DSVec3 &inPosition, + const QT3DSVec3 &inMouseVec) const override; + QT3DSVec3 ProjectPosition(SNode &inNode, const QT3DSVec3 &inPosition) const override; + + Option<SLayerPickSetup> GetLayerPickSetup(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QSize &inPickDims) override; + + Option<NVRenderRectF> GetLayerRect(SLayer &inLayer) override; + + void RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection) override; + + // Note that this allocator is completely reset on BeginFrame. + NVAllocatorCallback &GetPerFrameAllocator() override + { + return m_qt3dsContext.GetPerFrameAllocator(); + } + void RenderLayerRect(SLayer &inLayer, const QT3DSVec3 &inColor) override; + void AddRenderWidget(IRenderWidget &inWidget) override; + + SScaleAndPosition GetWorldToPixelScaleFactor(SLayer &inLayer, + const QT3DSVec3 &inWorldPoint) override; + SScaleAndPosition GetWorldToPixelScaleFactor(const SCamera &inCamera, + const QT3DSVec3 &inWorldPoint, + SLayerRenderData &inRenderData); + + void ReleaseLayerRenderResources(SLayer &inLayer, const SRenderInstanceId id) override; + + void RenderQuad(const QT3DSVec2 inDimensions, const QT3DSMat44 &inMVP, + NVRenderTexture2D &inQuadTexture) override; + void RenderQuad() override; + + void RenderPointsIndirect() override; + + // render a screen aligned 2D text + void RenderText2D(QT3DSF32 x, QT3DSF32 y, qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor, + const char *text) override; + bool PrepareTextureAtlasForRender(); + + // render Gpu profiler values + void RenderGpuProfilerStats(QT3DSF32 x, QT3DSF32 y, + qt3ds::foundation::Option<qt3ds::QT3DSVec3> inColor) override; + + // Callback during the layer render process. + void LayerNeedsFrameClear(SLayerRenderData &inLayer); + void BeginLayerDepthPassRender(SLayerRenderData &inLayer); + void EndLayerDepthPassRender(); + void BeginLayerRender(SLayerRenderData &inLayer); + void EndLayerRender(); + void PrepareImageForIbl(SImage &inImage); + + NVRenderShaderProgram *CompileShader(CRegisteredString inName, const char8_t *inVert, + const char8_t *inFrame); + + NVRenderShaderProgram *GenerateShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet); + SShaderGeneratorGeneratedShader *GetShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet); + + SDefaultAoPassShader *GetDefaultAoPassShader(TShaderFeatureSet inFeatureSet); + SDefaultAoPassShader *GetFakeDepthShader(TShaderFeatureSet inFeatureSet); + SDefaultAoPassShader *GetFakeCubeDepthShader(TShaderFeatureSet inFeatureSet); + SDefaultMaterialRenderableDepthShader *GetRenderableDepthShader(); + + SRenderableDepthPrepassShader *GetParaboloidDepthShader(TessModeValues::Enum inTessMode); + SRenderableDepthPrepassShader *GetParaboloidDepthNoTessShader(); + SRenderableDepthPrepassShader *GetParaboloidDepthTessLinearShader(); + SRenderableDepthPrepassShader *GetParaboloidDepthTessPhongShader(); + SRenderableDepthPrepassShader *GetParaboloidDepthTessNPatchShader(); + SRenderableDepthPrepassShader *GetCubeShadowDepthShader(TessModeValues::Enum inTessMode); + SRenderableDepthPrepassShader *GetCubeDepthNoTessShader(); + SRenderableDepthPrepassShader *GetCubeDepthTessLinearShader(); + SRenderableDepthPrepassShader *GetCubeDepthTessPhongShader(); + SRenderableDepthPrepassShader *GetCubeDepthTessNPatchShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthShader(TessModeValues::Enum inTessMode); + SRenderableDepthPrepassShader *GetOrthographicDepthNoTessShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthTessLinearShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthTessPhongShader(); + SRenderableDepthPrepassShader *GetOrthographicDepthTessNPatchShader(); + + SRenderableDepthPrepassShader *GetDepthPrepassShader(bool inDisplaced); + SRenderableDepthPrepassShader *GetDepthTessPrepassShader(TessModeValues::Enum inTessMode, + bool inDisplaced); + SRenderableDepthPrepassShader *GetDepthTessLinearPrepassShader(bool inDisplaced); + SRenderableDepthPrepassShader *GetDepthTessPhongPrepassShader(); + SRenderableDepthPrepassShader *GetDepthTessNPatchPrepassShader(); + STextDepthShader *GetTextDepthShader(); + STextRenderHelper GetShader(STextRenderable &inRenderable, bool inUsePathRendering); + STextRenderHelper GetTextShader(bool inUsePathRendering); + STextRenderHelper GetTextWidgetShader(); + STextRenderHelper GetOnscreenTextShader(); + SLayerSceneShader *GetSceneLayerShader(); + NVRenderShaderProgram *GetTextAtlasEntryShader(); + void GenerateXYQuad(); + void GenerateXYQuadStrip(); + void GenerateXYZPoint(); + eastl::pair<NVRenderVertexBuffer *, NVRenderIndexBuffer *> GetXYQuad(); + SLayerProgAABlendShader *GetLayerProgAABlendShader(); + SShadowmapPreblurShader *GetCubeShadowBlurXShader(); + SShadowmapPreblurShader *GetCubeShadowBlurYShader(); + SShadowmapPreblurShader *GetOrthoShadowBlurXShader(); + SShadowmapPreblurShader *GetOrthoShadowBlurYShader(); + +#ifdef ADVANCED_BLEND_SW_FALLBACK + SAdvancedModeBlendShader *GetAdvancedBlendModeShader(AdvancedBlendModes::Enum blendMode); + SAdvancedModeBlendShader *GetOverlayBlendModeShader(); + SAdvancedModeBlendShader *GetColorBurnBlendModeShader(); + SAdvancedModeBlendShader *GetColorDodgeBlendModeShader(); +#endif + SLayerRenderData *GetLayerRenderData() { return m_CurrentLayer; } + SLayerGlobalRenderProperties GetLayerGlobalRenderProperties(); + void UpdateCbAoShadow(const SLayer *pLayer, const SCamera *pCamera, + CResourceTexture2D &inDepthTexture); + + NVRenderContext &GetContext() { return *m_Context; } + + IQt3DSRenderContext &GetQt3DSContext() { return m_qt3dsContext; } + + void DrawScreenRect(NVRenderRectF inRect, const QT3DSVec3 &inColor); + // Binds an offscreen texture. Widgets are rendered last. + void SetupWidgetLayer(); + +#ifdef ADVANCED_BLEND_SW_FALLBACK + NVScopedRefCounted<NVRenderTexture2D> GetLayerBlendTexture() + { + return m_LayerBlendTexture.GetTexture(); + } + + NVScopedRefCounted<NVRenderFrameBuffer> GetBlendFB() + { + return m_BlendFB; + } +#endif + // widget context implementation + virtual NVRenderVertexBuffer & + GetOrCreateVertexBuffer(CRegisteredString &inStr, QT3DSU32 stride, + NVConstDataRef<QT3DSU8> bufferData = NVConstDataRef<QT3DSU8>()) override; + virtual NVRenderIndexBuffer & + GetOrCreateIndexBuffer(CRegisteredString &inStr, + qt3ds::render::NVRenderComponentTypes::Enum componentType, size_t size, + NVConstDataRef<QT3DSU8> bufferData = NVConstDataRef<QT3DSU8>()) override; + virtual NVRenderAttribLayout & + CreateAttributeLayout(NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> attribs) override; + virtual NVRenderInputAssembler & + GetOrCreateInputAssembler(CRegisteredString &inStr, NVRenderAttribLayout *attribLayout, + NVConstDataRef<NVRenderVertexBuffer *> buffers, + const NVRenderIndexBuffer *indexBuffer, + NVConstDataRef<QT3DSU32> strides, NVConstDataRef<QT3DSU32> offsets) override; + + NVRenderVertexBuffer *GetVertexBuffer(CRegisteredString &inStr) override; + NVRenderIndexBuffer *GetIndexBuffer(CRegisteredString &inStr) override; + NVRenderInputAssembler *GetInputAssembler(CRegisteredString &inStr) override; + + NVRenderShaderProgram *GetShader(CRegisteredString inStr) override; + NVRenderShaderProgram *CompileAndStoreShader(CRegisteredString inStr) override; + IShaderProgramGenerator &GetProgramGenerator() override; + + STextDimensions MeasureText(const STextRenderInfo &inText) override; + void RenderText(const STextRenderInfo &inText, const QT3DSVec3 &inTextColor, + const QT3DSVec3 &inBackgroundColor, const QT3DSMat44 &inMVP) override; + + // Given a node and a point in the node's local space (most likely its pivot point), we + // return + // a normal matrix so you can get the axis out, a transformation from node to camera + // a new position and a floating point scale factor so you can render in 1/2 perspective + // mode + // or orthographic mode if you would like to. + virtual SWidgetRenderInformation + GetWidgetRenderInformation(SNode &inNode, const QT3DSVec3 &inPos, + RenderWidgetModes::Enum inWidgetMode) override; + + Option<QT3DSVec2> GetLayerMouseCoords(SLayer &inLayer, const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect = false) const override; + + protected: + Option<QT3DSVec2> GetLayerMouseCoords(SLayerRenderData &inLayer, const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect = false) const; + SPickResultProcessResult ProcessPickResultList(bool inPickEverything); + // If the mouse y coordinates need to be flipped we expect that to happen before entry into + // this function + void GetLayerHitObjectList(SLayerRenderData &inLayer, const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, bool inPickEverything, + TPickResultArray &outIntersectionResult, + NVAllocatorCallback &inTempAllocator); + void IntersectRayWithSubsetRenderable(const SRay &inRay, + SRenderableObject &inRenderableObject, + TPickResultArray &outIntersectionResultList, + NVAllocatorCallback &inTempAllocator); + }; +} +} + +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp new file mode 100644 index 0000000..362a602 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp @@ -0,0 +1,2220 @@ +/**************************************************************************** +** +** 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 "Qt3DSRendererUtil.h" + +#ifdef WIN32 +#pragma warning(disable : 4355) +#endif + +#define QT3DS_CACHED_POST_EFFECT +const float QT3DS_DEGREES_TO_RADIANS = 0.0174532925199f; + +namespace qt3ds { +namespace render { + using eastl::reverse; + using eastl::stable_sort; + using qt3ds::render::NVRenderContextScopedProperty; + using qt3ds::QT3DSVec2; + + SLayerRenderData::SLayerRenderData(SLayer &inLayer, Qt3DSRendererImpl &inRenderer) + : SLayerRenderPreparationData(inLayer, inRenderer) + , m_LayerTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_TemporalAATexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerDepthTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerPrepassDepthTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerWidgetTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerSsaoTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerMultisampleTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerMultisamplePrepassDepthTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerMultisampleWidgetTexture(inRenderer.GetQt3DSContext().GetResourceManager()) + , m_LayerCachedTexture(NULL) + , m_AdvancedBlendDrawTexture(NULL) + , m_AdvancedBlendBlendTexture(NULL) + , m_AdvancedModeDrawFB(NULL) + , m_AdvancedModeBlendFB(NULL) + , m_ProgressiveAAPassIndex(0) + , m_TemporalAAPassIndex(0) + , m_NonDirtyTemporalAAPassIndex(0) + , m_TextScale(1.0f) + , mRefCount(0) + , m_DepthBufferFormat(NVRenderTextureFormats::Unknown) + { + } + + SLayerRenderData::~SLayerRenderData() + { + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + if (m_LayerCachedTexture && m_LayerCachedTexture != m_LayerTexture) + theResourceManager.Release(*m_LayerCachedTexture); + if (m_AdvancedModeDrawFB) { + m_AdvancedModeDrawFB->release(); + m_AdvancedModeDrawFB = NULL; + } + if (m_AdvancedModeBlendFB) { + m_AdvancedModeBlendFB->release(); + m_AdvancedModeBlendFB = NULL; + } + if (m_AdvancedBlendBlendTexture) + m_AdvancedBlendBlendTexture = NULL; + if (m_AdvancedBlendDrawTexture) + m_AdvancedBlendDrawTexture = NULL; + } + void SLayerRenderData::PrepareForRender(const QSize &inViewportDimensions) + { + SLayerRenderPreparationData::PrepareForRender(inViewportDimensions); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + // at that time all values shoud be updated + m_Renderer.UpdateCbAoShadow(&m_Layer, m_Camera, m_LayerDepthTexture); + + // Generate all necessary lighting keys + + if (thePrepResult.m_Flags.WasLayerDataDirty()) { + m_ProgressiveAAPassIndex = 0; + } + + // Get rid of the layer texture if we aren't rendering to texture this frame. + if (m_LayerTexture && !thePrepResult.m_Flags.ShouldRenderToTexture()) { + if (m_LayerCachedTexture && m_LayerCachedTexture != m_LayerTexture) { + theResourceManager.Release(*m_LayerCachedTexture); + m_LayerCachedTexture = NULL; + } + + m_LayerTexture.ReleaseTexture(); + m_LayerDepthTexture.ReleaseTexture(); + m_LayerWidgetTexture.ReleaseTexture(); + m_LayerSsaoTexture.ReleaseTexture(); + m_LayerMultisampleTexture.ReleaseTexture(); + m_LayerMultisamplePrepassDepthTexture.ReleaseTexture(); + m_LayerMultisampleWidgetTexture.ReleaseTexture(); + } + + if (NeedsWidgetTexture() == false) + m_LayerWidgetTexture.ReleaseTexture(); + + if (m_LayerDepthTexture && !thePrepResult.m_Flags.RequiresDepthTexture()) + m_LayerDepthTexture.ReleaseTexture(); + + if (m_LayerSsaoTexture && !thePrepResult.m_Flags.RequiresSsaoPass()) + m_LayerSsaoTexture.ReleaseTexture(); + + m_Renderer.LayerNeedsFrameClear(*this); + + // Clean up the texture cache if layer dimensions changed + if (inViewportDimensions.width() != m_previousDimensions.width() + || inViewportDimensions.height() != m_previousDimensions.height()) { + m_LayerTexture.ReleaseTexture(); + m_LayerDepthTexture.ReleaseTexture(); + m_LayerSsaoTexture.ReleaseTexture(); + m_LayerWidgetTexture.ReleaseTexture(); + m_LayerPrepassDepthTexture.ReleaseTexture(); + m_TemporalAATexture.ReleaseTexture(); + m_LayerMultisampleTexture.ReleaseTexture(); + m_LayerMultisamplePrepassDepthTexture.ReleaseTexture(); + m_LayerMultisampleWidgetTexture.ReleaseTexture(); + + m_previousDimensions.setWidth(inViewportDimensions.width()); + m_previousDimensions.setHeight(inViewportDimensions.height()); + + theResourceManager.DestroyFreeSizedResources(); + + // Effect system uses different resource manager, so clean that up too + m_Renderer.GetQt3DSContext().GetEffectSystem().GetResourceManager() + .DestroyFreeSizedResources(); + } + } + + NVRenderTextureFormats::Enum SLayerRenderData::GetDepthBufferFormat() + { + if (m_DepthBufferFormat == NVRenderTextureFormats::Unknown) { + QT3DSU32 theExistingDepthBits = m_Renderer.GetContext().GetDepthBits(); + QT3DSU32 theExistingStencilBits = m_Renderer.GetContext().GetStencilBits(); + switch (theExistingDepthBits) { + case 32: + m_DepthBufferFormat = NVRenderTextureFormats::Depth32; + break; + case 24: + // check if we have stencil bits + if (theExistingStencilBits > 0) + m_DepthBufferFormat = + NVRenderTextureFormats::Depth24Stencil8; // currently no stencil usage + // should be Depth24Stencil8 in + // this case + else + m_DepthBufferFormat = NVRenderTextureFormats::Depth24; + break; + case 16: + m_DepthBufferFormat = NVRenderTextureFormats::Depth16; + break; + default: + QT3DS_ASSERT(false); + m_DepthBufferFormat = NVRenderTextureFormats::Depth16; + break; + } + } + return m_DepthBufferFormat; + } + + NVRenderFrameBufferAttachments::Enum + SLayerRenderData::GetFramebufferDepthAttachmentFormat(NVRenderTextureFormats::Enum depthFormat) + { + NVRenderFrameBufferAttachments::Enum fmt = NVRenderFrameBufferAttachments::Depth; + + switch (depthFormat) { + case NVRenderTextureFormats::Depth16: + case NVRenderTextureFormats::Depth24: + case NVRenderTextureFormats::Depth32: + fmt = NVRenderFrameBufferAttachments::Depth; + break; + case NVRenderTextureFormats::Depth24Stencil8: + fmt = NVRenderFrameBufferAttachments::DepthStencil; + break; + default: + QT3DS_ASSERT(false); + break; + } + + return fmt; + } + + void SLayerRenderData::RenderAoPass() + { + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theContext(m_Renderer.GetContext()); + SDefaultAoPassShader *shader = m_Renderer.GetDefaultAoPassShader(GetShaderFeatureSet()); + if (shader == NULL) + return; + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetActiveShader(&(shader->m_Shader)); + + // Setup constants + shader->m_CameraDirection.Set(m_CameraDirection); + shader->m_ViewMatrix.Set(m_Camera->m_GlobalTransform); + + shader->m_DepthTexture.Set(m_LayerDepthTexture); + shader->m_DepthSamplerSize.Set(QT3DSVec2(m_LayerDepthTexture->GetTextureDetails().m_Width, + m_LayerDepthTexture->GetTextureDetails().m_Height)); + + // Important uniforms for AO calculations + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + shader->m_CameraProperties.Set(theCameraProps); + shader->m_AoShadowParams.Set(); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + m_Renderer.EndLayerDepthPassRender(); + } + + void SLayerRenderData::RenderFakeDepthMapPass(NVRenderTexture2D *theDepthTex, + NVRenderTextureCube *theDepthCube) + { + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theContext(m_Renderer.GetContext()); + SDefaultAoPassShader *shader = theDepthTex + ? m_Renderer.GetFakeDepthShader(GetShaderFeatureSet()) + : m_Renderer.GetFakeCubeDepthShader(GetShaderFeatureSet()); + if (shader == NULL) + return; + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetActiveShader(&(shader->m_Shader)); + + // Setup constants + shader->m_CameraDirection.Set(m_CameraDirection); + shader->m_ViewMatrix.Set(m_Camera->m_GlobalTransform); + + shader->m_DepthTexture.Set(theDepthTex); + shader->m_CubeTexture.Set(theDepthCube); + shader->m_DepthSamplerSize.Set(QT3DSVec2(theDepthTex->GetTextureDetails().m_Width, + theDepthTex->GetTextureDetails().m_Height)); + + // Important uniforms for AO calculations + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + shader->m_CameraProperties.Set(theCameraProps); + shader->m_AoShadowParams.Set(); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + } + + namespace { + + void computeFrustumBounds(const SCamera &inCamera, const NVRenderRectF &inViewPort, + QT3DSVec3 &ctrBound, QT3DSVec3 camVerts[8]) + { + QT3DSVec3 camEdges[4]; + + const QT3DSF32 *dataPtr(inCamera.m_GlobalTransform.front()); + QT3DSVec3 camX(dataPtr[0], dataPtr[1], dataPtr[2]); + QT3DSVec3 camY(dataPtr[4], dataPtr[5], dataPtr[6]); + QT3DSVec3 camZ(dataPtr[8], dataPtr[9], dataPtr[10]); + + float tanFOV = tanf(inCamera.verticalFov(inViewPort) * 0.5f); + float asTanFOV = tanFOV * inViewPort.m_Width / inViewPort.m_Height; + camEdges[0] = -asTanFOV * camX + tanFOV * camY + camZ; + camEdges[1] = asTanFOV * camX + tanFOV * camY + camZ; + camEdges[2] = asTanFOV * camX - tanFOV * camY + camZ; + camEdges[3] = -asTanFOV * camX - tanFOV * camY + camZ; + + for (int i = 0; i < 4; ++i) { + camEdges[i].x = -camEdges[i].x; + camEdges[i].y = -camEdges[i].y; + } + + camVerts[0] = inCamera.m_Position + camEdges[0] * inCamera.m_ClipNear; + camVerts[1] = inCamera.m_Position + camEdges[0] * inCamera.m_ClipFar; + camVerts[2] = inCamera.m_Position + camEdges[1] * inCamera.m_ClipNear; + camVerts[3] = inCamera.m_Position + camEdges[1] * inCamera.m_ClipFar; + camVerts[4] = inCamera.m_Position + camEdges[2] * inCamera.m_ClipNear; + camVerts[5] = inCamera.m_Position + camEdges[2] * inCamera.m_ClipFar; + camVerts[6] = inCamera.m_Position + camEdges[3] * inCamera.m_ClipNear; + camVerts[7] = inCamera.m_Position + camEdges[3] * inCamera.m_ClipFar; + + ctrBound = camVerts[0]; + for (int i = 1; i < 8; ++i) { + ctrBound += camVerts[i]; + } + ctrBound *= 0.125f; + } + + void SetupCameraForShadowMap(const QT3DSVec2 &inCameraVec, NVRenderContext & /*inContext*/, + const NVRenderRectF &inViewport, const SCamera &inCamera, + const SLight *inLight, SCamera &theCamera) + { + // setup light matrix + QT3DSU32 mapRes = 1 << inLight->m_ShadowMapRes; + NVRenderRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes); + theCamera.m_ClipNear = 1.0f; + theCamera.m_ClipFar = inLight->m_ShadowMapFar; + // Setup camera projection + QT3DSVec3 inLightPos = inLight->GetGlobalPos(); + QT3DSVec3 inLightDir = inLight->GetDirection(); + + if (inLight->m_Flags.IsLeftHanded()) + inLightPos.z = -inLightPos.z; + + inLightPos -= inLightDir * inCamera.m_ClipNear; + theCamera.m_FOV = inLight->m_ShadowMapFov * QT3DS_DEGREES_TO_RADIANS; + + if (inLight->m_LightType == RenderLightTypes::Directional) { + QT3DSVec3 frustBounds[8], boundCtr; + computeFrustumBounds(inCamera, inViewport, boundCtr, frustBounds); + + QT3DSVec3 forward = inLightDir; + forward.normalize(); + QT3DSVec3 right = forward.cross(QT3DSVec3(0, 1, 0)); + right.normalize(); + QT3DSVec3 up = right.cross(forward); + up.normalize(); + + // Calculate bounding box of the scene camera frustum + float minDistanceZ = std::numeric_limits<float>::max(); + float maxDistanceZ = -std::numeric_limits<float>::max(); + float minDistanceY = std::numeric_limits<float>::max(); + float maxDistanceY = -std::numeric_limits<float>::max(); + float minDistanceX = std::numeric_limits<float>::max(); + float maxDistanceX = -std::numeric_limits<float>::max(); + for (int i = 0; i < 8; ++i) { + float distanceZ = frustBounds[i].dot(forward); + if (distanceZ < minDistanceZ) + minDistanceZ = distanceZ; + if (distanceZ > maxDistanceZ) + maxDistanceZ = distanceZ; + float distanceY = frustBounds[i].dot(up); + if (distanceY < minDistanceY) + minDistanceY = distanceY; + if (distanceY > maxDistanceY) + maxDistanceY = distanceY; + float distanceX = frustBounds[i].dot(right); + if (distanceX < minDistanceX) + minDistanceX = distanceX; + if (distanceX > maxDistanceX) + maxDistanceX = distanceX; + } + + // Apply bounding box parameters to shadow map camera projection matrix + // so that the whole scene is fit inside the shadow map + inLightPos = boundCtr; + theViewport.m_Height = abs(maxDistanceY - minDistanceY); + theViewport.m_Width = abs(maxDistanceX - minDistanceX); + theCamera.m_ClipNear = -abs(maxDistanceZ - minDistanceZ); + theCamera.m_ClipFar = abs(maxDistanceZ - minDistanceZ); + } + + theCamera.m_Flags.SetLeftHanded(false); + + theCamera.m_Flags.ClearOrSet(inLight->m_LightType == RenderLightTypes::Directional, + NodeFlagValues::Orthographic); + theCamera.m_Parent = NULL; + theCamera.m_Pivot = inLight->m_Pivot; + + if (inLight->m_LightType != RenderLightTypes::Point) { + theCamera.LookAt(inLightPos, QT3DSVec3(0, 1.0, 0), inLightPos + inLightDir); + } else { + theCamera.LookAt(inLightPos, QT3DSVec3(0, 1.0, 0), QT3DSVec3(0, 0, 0)); + } + + theCamera.CalculateGlobalVariables(theViewport, + QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + } + } + + void SetupCubeShadowCameras(const SLight *inLight, SCamera inCameras[6]) + { + // setup light matrix + QT3DSU32 mapRes = 1 << inLight->m_ShadowMapRes; + NVRenderRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes); + QT3DSVec3 rotOfs[6]; + + QT3DS_ASSERT(inLight != NULL); + QT3DS_ASSERT(inLight->m_LightType != RenderLightTypes::Directional); + + QT3DSVec3 inLightPos = inLight->GetGlobalPos(); + if (inLight->m_Flags.IsLeftHanded()) + inLightPos.z = -inLightPos.z; + + rotOfs[0] = QT3DSVec3(0.f, -NVHalfPi, NVPi); + rotOfs[1] = QT3DSVec3(0.f, NVHalfPi, NVPi); + rotOfs[2] = QT3DSVec3(NVHalfPi, 0.f, 0.f); + rotOfs[3] = QT3DSVec3(-NVHalfPi, 0.f, 0.f); + rotOfs[4] = QT3DSVec3(0.f, NVPi, -NVPi); + rotOfs[5] = QT3DSVec3(0.f, 0.f, NVPi); + + for (int i = 0; i < 6; ++i) { + inCameras[i].m_Flags.SetLeftHanded(false); + + inCameras[i].m_Flags.ClearOrSet(false, NodeFlagValues::Orthographic); + inCameras[i].m_Parent = NULL; + inCameras[i].m_Pivot = inLight->m_Pivot; + inCameras[i].m_ClipNear = 1.0f; + inCameras[i].m_ClipFar = NVMax<QT3DSF32>(2.0f, inLight->m_ShadowMapFar); + inCameras[i].m_FOV = inLight->m_ShadowMapFov * QT3DS_DEGREES_TO_RADIANS; + + inCameras[i].m_Position = inLightPos; + inCameras[i].m_Rotation = rotOfs[i]; + inCameras[i].CalculateGlobalVariables( + theViewport, QT3DSVec2(theViewport.m_Width, theViewport.m_Height)); + } + + /* + if ( inLight->m_LightType == RenderLightTypes::Point ) return; + + QT3DSVec3 viewDirs[6]; + QT3DSVec3 viewUp[6]; + QT3DSMat33 theDirMatrix( inLight->m_GlobalTransform.getUpper3x3() ); + + viewDirs[0] = theDirMatrix.transform( QT3DSVec3( 1.f, 0.f, 0.f ) ); + viewDirs[2] = theDirMatrix.transform( QT3DSVec3( 0.f, -1.f, 0.f ) ); + viewDirs[4] = theDirMatrix.transform( QT3DSVec3( 0.f, 0.f, 1.f ) ); + viewDirs[0].normalize(); viewDirs[2].normalize(); viewDirs[4].normalize(); + viewDirs[1] = -viewDirs[0]; + viewDirs[3] = -viewDirs[2]; + viewDirs[5] = -viewDirs[4]; + + viewUp[0] = viewDirs[2]; + viewUp[1] = viewDirs[2]; + viewUp[2] = viewDirs[5]; + viewUp[3] = viewDirs[4]; + viewUp[4] = viewDirs[2]; + viewUp[5] = viewDirs[2]; + + for (int i = 0; i < 6; ++i) + { + inCameras[i].LookAt( inLightPos, viewUp[i], inLightPos + viewDirs[i] ); + inCameras[i].CalculateGlobalVariables( theViewport, QT3DSVec2( theViewport.m_Width, + theViewport.m_Height ) ); + } + */ + } + + inline void RenderRenderableShadowMapPass(SLayerRenderData &inData, SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, TShaderFeatureSet, + QT3DSU32 lightIndex, const SCamera &inCamera) + { + if (!inObject.m_RenderableFlags.IsShadowCaster()) + return; + + SShadowMapEntry *pEntry = inData.m_ShadowMapManager->GetShadowMapEntry(lightIndex); + + if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + static_cast<SSubsetRenderableBase &>(inObject).RenderShadowMapPass( + inCameraProps, inData.m_Lights[lightIndex], inCamera, pEntry); + else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) { + static_cast<SSubsetRenderableBase &>(inObject).RenderShadowMapPass( + inCameraProps, inData.m_Lights[lightIndex], inCamera, pEntry); + } else if (inObject.m_RenderableFlags.IsPath()) { + static_cast<SPathRenderable &>(inObject).RenderShadowMapPass( + inCameraProps, inData.m_Lights[lightIndex], inCamera, pEntry); + } + } + + void SLayerRenderData::RenderShadowCubeBlurPass(CResourceFrameBuffer *theFB, + NVRenderTextureCube *target0, + NVRenderTextureCube *target1, QT3DSF32 filterSz, + QT3DSF32 clipFar) + { + NVRenderContext &theContext(m_Renderer.GetContext()); + + SShadowmapPreblurShader *shaderX = m_Renderer.GetCubeShadowBlurXShader(); + SShadowmapPreblurShader *shaderY = m_Renderer.GetCubeShadowBlurYShader(); + + if (shaderX == NULL) + return; + if (shaderY == NULL) + return; + // if ( theShader == NULL ) return; + + // Enable drawing to 6 color attachment buffers for cubemap passes + qt3ds::QT3DSI32 buffers[6] = { 0, 1, 2, 3, 4, 5 }; + qt3ds::foundation::NVConstDataRef<qt3ds::QT3DSI32> bufferList(buffers, 6); + theContext.SetDrawBuffers(bufferList); + + // Attach framebuffer targets + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, *target1, + NVRenderTextureCubeFaces::CubePosX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color1, *target1, + NVRenderTextureCubeFaces::CubeNegX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color2, *target1, + NVRenderTextureCubeFaces::CubePosY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color3, *target1, + NVRenderTextureCubeFaces::CubeNegY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color4, *target1, + NVRenderTextureCubeFaces::CubePosZ); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color5, *target1, + NVRenderTextureCubeFaces::CubeNegZ); + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + // theContext.SetColorWritesEnabled(true); + theContext.SetActiveShader(&(shaderX->m_Shader)); + + shaderX->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderX->m_DepthCube.Set(target0); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + theContext.SetActiveShader(&(shaderY->m_Shader)); + + // Lather, Rinse, and Repeat for the Y-blur pass + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, *target0, + NVRenderTextureCubeFaces::CubePosX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color1, *target0, + NVRenderTextureCubeFaces::CubeNegX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color2, *target0, + NVRenderTextureCubeFaces::CubePosY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color3, *target0, + NVRenderTextureCubeFaces::CubeNegY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color4, *target0, + NVRenderTextureCubeFaces::CubePosZ); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color5, *target0, + NVRenderTextureCubeFaces::CubeNegZ); + + shaderY->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderY->m_DepthCube.Set(target1); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + theContext.SetDepthWriteEnabled(true); + theContext.SetDepthTestEnabled(true); + // theContext.SetColorWritesEnabled(false); + + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubePosX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color1, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubeNegX); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color2, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubePosY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color3, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubeNegY); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color4, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubePosZ); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color5, NVRenderTextureOrRenderBuffer(), + NVRenderTextureCubeFaces::CubeNegZ); + + theContext.SetDrawBuffers(qt3ds::foundation::toConstDataRef((qt3ds::QT3DSI32)0)); + } + + void SLayerRenderData::RenderShadowMapBlurPass(CResourceFrameBuffer *theFB, + NVRenderTexture2D *target0, + NVRenderTexture2D *target1, QT3DSF32 filterSz, + QT3DSF32 clipFar) + { + NVRenderContext &theContext(m_Renderer.GetContext()); + + SShadowmapPreblurShader *shaderX = m_Renderer.GetOrthoShadowBlurXShader(); + SShadowmapPreblurShader *shaderY = m_Renderer.GetOrthoShadowBlurYShader(); + + if (shaderX == NULL) + return; + if (shaderY == NULL) + return; + + // Attach framebuffer target + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, *target1); + //(*theFB)->Attach( NVRenderFrameBufferAttachments::DepthStencil, *target1 ); + + // Set initial state + theContext.SetBlendingEnabled(false); + theContext.SetDepthWriteEnabled(false); + theContext.SetDepthTestEnabled(false); + theContext.SetColorWritesEnabled(true); + theContext.SetActiveShader(&(shaderX->m_Shader)); + + shaderX->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderX->m_DepthMap.Set(target0); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, *target0); + //(*theFB)->Attach( NVRenderFrameBufferAttachments::DepthStencil, *target0 ); + theContext.SetActiveShader(&(shaderY->m_Shader)); + + shaderY->m_CameraProperties.Set(QT3DSVec2(filterSz, clipFar)); + shaderY->m_DepthMap.Set(target1); + + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + + theContext.SetDepthWriteEnabled(true); + theContext.SetDepthTestEnabled(true); + theContext.SetColorWritesEnabled(false); + + //(*theFB)->Attach( NVRenderFrameBufferAttachments::DepthStencil, + //NVRenderTextureOrRenderBuffer() ); + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer()); + } + + void SLayerRenderData::RenderShadowMapPass(CResourceFrameBuffer *theFB) + { + SStackPerfTimer ___timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::RenderShadowMapPass"); + + if (m_Camera == NULL || !GetShadowMapManager()) + return; + + // Check if we have anything to render + if (m_OpaqueObjects.size() == 0 || m_Lights.size() == 0) + return; + + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + + // we may change the viewport + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theRenderContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + + // disable color writes + // theRenderContext.SetColorWritesEnabled( false ); + theRenderContext.SetColorWritesEnabled(true); + theRenderContext.SetDepthWriteEnabled(true); + theRenderContext.SetCullingEnabled(false); + theRenderContext.SetClearColor(QT3DSVec4(1.0f)); + + // we render the shadow map with a slight offset to prevent shadow acne and cull the front + // faces + NVScopedRefCounted<qt3ds::render::NVRenderRasterizerState> rsdefaultstate = + theRenderContext.CreateRasterizerState(0.0, 0.0, qt3ds::render::NVRenderFaces::Back); + NVScopedRefCounted<qt3ds::render::NVRenderRasterizerState> rsstate = + theRenderContext.CreateRasterizerState(1.5, 2.0, qt3ds::render::NVRenderFaces::Front); + theRenderContext.SetRasterizerState(rsstate); + + qt3ds::render::NVRenderClearFlags clearFlags(qt3ds::render::NVRenderClearValues::Depth + | qt3ds::render::NVRenderClearValues::Stencil + | qt3ds::render::NVRenderClearValues::Color); + + for (QT3DSU32 i = 0; i < m_Lights.size(); i++) { + // don't render shadows when not casting + if (m_Lights[i]->m_CastShadow == false) + continue; + SShadowMapEntry *pEntry = m_ShadowMapManager->GetShadowMapEntry(i); + if (pEntry && pEntry->m_DepthMap && pEntry->m_DepthCopy && pEntry->m_DepthRender) { + SCamera theCamera; + + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + SetupCameraForShadowMap(theCameraProps, m_Renderer.GetContext(), + __viewport.m_InitialValue, *m_Camera, + m_Lights[i], theCamera); + // we need this matrix for the final rendering + theCamera.CalculateViewProjectionMatrix(pEntry->m_LightVP); + pEntry->m_LightView = theCamera.m_GlobalTransform.getInverse(); + + STextureDetails theDetails(pEntry->m_DepthMap->GetTextureDetails()); + theRenderContext.SetViewport( + NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height)); + + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, *pEntry->m_DepthMap); + (*theFB)->Attach(NVRenderFrameBufferAttachments::DepthStencil, + *pEntry->m_DepthRender); + theRenderContext.Clear(clearFlags); + + RunRenderPass(RenderRenderableShadowMapPass, false, true, true, i, theCamera); + RenderShadowMapBlurPass(theFB, pEntry->m_DepthMap, pEntry->m_DepthCopy, + m_Lights[i]->m_ShadowFilter, m_Lights[i]->m_ShadowMapFar); + } else if (pEntry && pEntry->m_DepthCube && pEntry->m_CubeCopy + && pEntry->m_DepthRender) { + SCamera theCameras[6]; + + SetupCubeShadowCameras(m_Lights[i], theCameras); + + // pEntry->m_LightView = m_Lights[i]->m_LightType == RenderLightTypes::Point ? + // QT3DSMat44::createIdentity() + // : m_Lights[i]->m_GlobalTransform; + pEntry->m_LightView = QT3DSMat44::createIdentity(); + + STextureDetails theDetails(pEntry->m_DepthCube->GetTextureDetails()); + theRenderContext.SetViewport( + NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height)); + + // int passes = m_Lights[i]->m_LightType == RenderLightTypes::Point ? 6 : 5; + int passes = 6; + for (int k = 0; k < passes; ++k) { + // theCameras[k].CalculateViewProjectionMatrix( pEntry->m_LightCubeVP[k] ); + pEntry->m_LightCubeView[k] = theCameras[k].m_GlobalTransform.getInverse(); + theCameras[k].CalculateViewProjectionMatrix(pEntry->m_LightVP); + + // Geometry shader multiplication really doesn't work unless you have a + // 6-layered 3D depth texture... + // Otherwise, you have no way to depth test while rendering... + // which more or less completely defeats the purpose of having a cubemap render + // target. + NVRenderTextureCubeFaces::Enum curFace = + (NVRenderTextureCubeFaces::Enum)(k + 1); + //(*theFB)->AttachFace( NVRenderFrameBufferAttachments::DepthStencil, + //*pEntry->m_DepthCube, curFace ); + (*theFB)->Attach(NVRenderFrameBufferAttachments::DepthStencil, + *pEntry->m_DepthRender); + (*theFB)->AttachFace(NVRenderFrameBufferAttachments::Color0, + *pEntry->m_DepthCube, curFace); + (*theFB)->IsComplete(); + theRenderContext.Clear(clearFlags); + + RunRenderPass(RenderRenderableShadowMapPass, false, true, true, i, + theCameras[k]); + } + + RenderShadowCubeBlurPass(theFB, pEntry->m_DepthCube, pEntry->m_CubeCopy, + m_Lights[i]->m_ShadowFilter, m_Lights[i]->m_ShadowMapFar); + } + } + + (*theFB)->Attach(NVRenderFrameBufferAttachments::Depth, NVRenderTextureOrRenderBuffer()); + (*theFB)->Attach(NVRenderFrameBufferAttachments::Color0, NVRenderTextureOrRenderBuffer()); + + // enable color writes + theRenderContext.SetColorWritesEnabled(true); + theRenderContext.SetCullingEnabled(true); + theRenderContext.SetClearColor(QT3DSVec4(0.0f)); + // reset rasterizer state + theRenderContext.SetRasterizerState(rsdefaultstate); + + m_Renderer.EndLayerDepthPassRender(); + } + + inline void RenderRenderableDepthPass(SLayerRenderData &inData, SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, TShaderFeatureSet, QT3DSU32, + const SCamera &inCamera) + { + if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) { + static_cast<SSubsetRenderable &>(inObject).RenderDepthPass(inCameraProps); + } else if (inObject.m_RenderableFlags.IsText()) { + static_cast<STextRenderable &>(inObject).RenderDepthPass(inCameraProps); +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + } else if (inObject.m_RenderableFlags.isDistanceField()) { + static_cast<SDistanceFieldRenderable &>(inObject).RenderDepthPass(inCameraProps); +#endif + } else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) { + static_cast<SCustomMaterialRenderable &>(inObject).RenderDepthPass( + inCameraProps, inData.m_Layer, inData.m_Lights, inCamera, NULL); + } else if (inObject.m_RenderableFlags.IsPath()) { + static_cast<SPathRenderable &>(inObject).RenderDepthPass( + inCameraProps, inData.m_Layer, inData.m_Lights, inCamera, NULL); + } else { + QT3DS_ASSERT(false); + } + } + + void SLayerRenderData::RenderDepthPass(bool inEnableTransparentDepthWrite) + { + SStackPerfTimer ___timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::RenderDepthPass"); + if (m_Camera == NULL) + return; + + // Avoid running this method if possible. + if ((inEnableTransparentDepthWrite == false + && (m_OpaqueObjects.size() == 0 + || m_Layer.m_Flags.IsLayerEnableDepthPrepass() == false)) + || m_Layer.m_Flags.IsLayerEnableDepthTest() == false) + return; + + m_Renderer.BeginLayerDepthPassRender(*this); + + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + + // disable color writes + theRenderContext.SetColorWritesEnabled(false); + theRenderContext.SetDepthWriteEnabled(true); + + qt3ds::render::NVRenderClearFlags clearFlags(qt3ds::render::NVRenderClearValues::Stencil + | qt3ds::render::NVRenderClearValues::Depth); + theRenderContext.Clear(clearFlags); + + RunRenderPass(RenderRenderableDepthPass, false, true, inEnableTransparentDepthWrite, 0, + *m_Camera); + + // enable color writes + theRenderContext.SetColorWritesEnabled(true); + + m_Renderer.EndLayerDepthPassRender(); + } + + inline void RenderRenderable(SLayerRenderData &inData, SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, TShaderFeatureSet inFeatureSet, QT3DSU32, + const SCamera &inCamera) + { + if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + static_cast<SSubsetRenderable &>(inObject).Render(inCameraProps, inFeatureSet); + else if (inObject.m_RenderableFlags.IsText()) + static_cast<STextRenderable &>(inObject).Render(inCameraProps); +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + else if (inObject.m_RenderableFlags.isDistanceField()) + static_cast<SDistanceFieldRenderable &>(inObject).Render(inCameraProps); +#endif + else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) { + // PKC : Need a better place to do this. + SCustomMaterialRenderable &theObject = + static_cast<SCustomMaterialRenderable &>(inObject); + if (!inData.m_Layer.m_LightProbe && theObject.m_Material.m_IblProbe) + inData.SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", + theObject.m_Material.m_IblProbe->m_TextureData.m_Texture + != NULL); + else if (inData.m_Layer.m_LightProbe) + inData.SetShaderFeature("QT3DS_ENABLE_LIGHT_PROBE", + inData.m_Layer.m_LightProbe->m_TextureData.m_Texture + != NULL); + + static_cast<SCustomMaterialRenderable &>(inObject).Render( + inCameraProps, inData, inData.m_Layer, inData.m_Lights, inCamera, + inData.m_LayerDepthTexture, inData.m_LayerSsaoTexture, inFeatureSet); + } else if (inObject.m_RenderableFlags.IsPath()) { + static_cast<SPathRenderable &>(inObject).Render( + inCameraProps, inData.m_Layer, inData.m_Lights, inCamera, + inData.m_LayerDepthTexture, inData.m_LayerSsaoTexture, inFeatureSet); + } else { + QT3DS_ASSERT(false); + } + } + + void SLayerRenderData::RunRenderPass(TRenderRenderableFunction inRenderFn, + bool inEnableBlending, bool inEnableDepthWrite, + bool inEnableTransparentDepthWrite, QT3DSU32 indexLight, + const SCamera &inCamera, CResourceFrameBuffer *theFB) + { + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + theRenderContext.SetDepthFunction(qt3ds::render::NVRenderBoolOp::LessThanOrEqual); + theRenderContext.SetBlendingEnabled(false); + QT3DSVec2 theCameraProps = QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar); + NVDataRef<SRenderableObject *> theOpaqueObjects = GetOpaqueRenderableObjects(); + bool usingDepthBuffer = + m_Layer.m_Flags.IsLayerEnableDepthTest() && theOpaqueObjects.size() > 0; + + if (usingDepthBuffer) { + theRenderContext.SetDepthTestEnabled(true); + theRenderContext.SetDepthWriteEnabled(inEnableDepthWrite); + } else { + theRenderContext.SetDepthWriteEnabled(false); + theRenderContext.SetDepthTestEnabled(false); + } + + for (QT3DSU32 idx = 0, end = theOpaqueObjects.size(); idx < end; ++idx) { + SRenderableObject &theObject(*theOpaqueObjects[idx]); + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, m_SourceLightDirections, + theObject.m_ScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + inRenderFn(*this, theObject, theCameraProps, GetShaderFeatureSet(), indexLight, + inCamera); + } + + // transparent objects + if (inEnableBlending || m_Layer.m_Flags.IsLayerEnableDepthTest() == false) { + theRenderContext.SetBlendingEnabled(true && inEnableBlending); + theRenderContext.SetDepthWriteEnabled(inEnableTransparentDepthWrite); + + NVDataRef<SRenderableObject *> theTransparentObjects = GetTransparentRenderableObjects(); + // Assume all objects have transparency if the layer's depth test enabled flag is true. + if (m_Layer.m_Flags.IsLayerEnableDepthTest() == true) { + for (QT3DSU32 idx = 0, end = theTransparentObjects.size(); idx < end; ++idx) { + SRenderableObject &theObject(*theTransparentObjects[idx]); + if (!(theObject.m_RenderableFlags.IsCompletelyTransparent())) { +#ifdef ADVANCED_BLEND_SW_FALLBACK + // SW fallback for advanced blend modes. + // Renders transparent objects to a separate FBO and blends them in shader + // with the opaque items and background. + DefaultMaterialBlendMode::Enum blendMode + = DefaultMaterialBlendMode::Enum::Normal; + if (theObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + blendMode = static_cast<SSubsetRenderable &>(theObject).getBlendingMode(); + bool useBlendFallback = (blendMode == DefaultMaterialBlendMode::Overlay || + blendMode == DefaultMaterialBlendMode::ColorBurn || + blendMode == DefaultMaterialBlendMode::ColorDodge) && + !theRenderContext.IsAdvancedBlendHwSupported() && + !theRenderContext.IsAdvancedBlendHwSupportedKHR() && + m_LayerPrepassDepthTexture; + if (useBlendFallback) + SetupDrawFB(true); +#endif + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, + m_SourceLightDirections, + theObject.m_ScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + + inRenderFn(*this, theObject, theCameraProps, GetShaderFeatureSet(), + indexLight, inCamera); +#ifdef ADVANCED_BLEND_SW_FALLBACK + // SW fallback for advanced blend modes. + // Continue blending after transparent objects have been rendered to a FBO + if (useBlendFallback) { + BlendAdvancedToFB(blendMode, true, theFB); + // restore blending status + theRenderContext.SetBlendingEnabled(inEnableBlending); + // restore depth test status + theRenderContext.SetDepthTestEnabled(usingDepthBuffer); + theRenderContext.SetDepthWriteEnabled(inEnableTransparentDepthWrite); + } +#endif + } + } + } + // If the layer doesn't have depth enabled then we have to render via an alternate route + // where the transparent objects vector could have both opaque and transparent objects. + else { + for (QT3DSU32 idx = 0, end = theTransparentObjects.size(); idx < end; ++idx) { + SRenderableObject &theObject(*theTransparentObjects[idx]); + if (!(theObject.m_RenderableFlags.IsCompletelyTransparent())) { +#ifdef ADVANCED_BLEND_SW_FALLBACK + DefaultMaterialBlendMode::Enum blendMode + = DefaultMaterialBlendMode::Enum::Normal; + if (theObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) + blendMode = static_cast<SSubsetRenderable &>(theObject).getBlendingMode(); + bool useBlendFallback = (blendMode == DefaultMaterialBlendMode::Overlay || + blendMode == DefaultMaterialBlendMode::ColorBurn || + blendMode == DefaultMaterialBlendMode::ColorDodge) && + !theRenderContext.IsAdvancedBlendHwSupported() && + !theRenderContext.IsAdvancedBlendHwSupportedKHR(); + + if (theObject.m_RenderableFlags.HasTransparency()) { + theRenderContext.SetBlendingEnabled(true && inEnableBlending); + // If we have SW fallback for blend mode, render to a FBO and blend back. + // Slow as this must be done per-object (transparent and opaque items + // are mixed, not batched) + if (useBlendFallback) + SetupDrawFB(false); + } +#endif + SScopedLightsListScope lightsScope(m_Lights, m_LightDirections, + m_SourceLightDirections, + theObject.m_ScopedLights); + SetShaderFeature(m_CGLightingFeatureName, m_Lights.empty() == false); + inRenderFn(*this, theObject, theCameraProps, GetShaderFeatureSet(), + indexLight, inCamera); +#ifdef ADVANCED_BLEND_SW_FALLBACK + if (useBlendFallback) { + BlendAdvancedToFB(blendMode, false, theFB); + // restore blending status + theRenderContext.SetBlendingEnabled(inEnableBlending); + + } +#endif + } + } + } + } + } + + void SLayerRenderData::Render(CResourceFrameBuffer *theFB) + { + SStackPerfTimer ___timer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::Render"); + if (m_Camera == NULL) + return; + + m_Renderer.BeginLayerRender(*this); + RunRenderPass(RenderRenderable, true, !m_Layer.m_Flags.IsLayerEnableDepthPrepass(), false, + 0, *m_Camera, theFB); + m_Renderer.EndLayerRender(); + } + + void SLayerRenderData::CreateGpuProfiler() + { + if (m_Renderer.GetContext().IsTimerQuerySupported()) { + m_LayerProfilerGpu = IRenderProfiler::CreateGpuProfiler( + m_Renderer.GetContext().GetFoundation(), m_Renderer.GetQt3DSContext(), + m_Renderer.GetContext()); + } + } + + void SLayerRenderData::StartProfiling(CRegisteredString &nameID, bool sync) + { + if (m_LayerProfilerGpu.mPtr) { + m_LayerProfilerGpu->StartTimer(nameID, false, sync); + } + } + + void SLayerRenderData::EndProfiling(CRegisteredString &nameID) + { + if (m_LayerProfilerGpu.mPtr) { + m_LayerProfilerGpu->EndTimer(nameID); + } + } + + void SLayerRenderData::StartProfiling(const char *nameID, bool sync) + { + if (m_LayerProfilerGpu.mPtr) { + CRegisteredString theStr( + m_Renderer.GetQt3DSContext().GetStringTable().RegisterStr(nameID)); + m_LayerProfilerGpu->StartTimer(theStr, false, sync); + } + } + + void SLayerRenderData::EndProfiling(const char *nameID) + { + if (m_LayerProfilerGpu.mPtr) { + CRegisteredString theStr( + m_Renderer.GetQt3DSContext().GetStringTable().RegisterStr(nameID)); + m_LayerProfilerGpu->EndTimer(theStr); + } + } + + void SLayerRenderData::AddVertexCount(QT3DSU32 count) + { + if (m_LayerProfilerGpu.mPtr) { + m_LayerProfilerGpu->AddVertexCount(count); + } + } + + // Assumes the viewport is setup appropriately to render the widget. + void SLayerRenderData::RenderRenderWidgets() + { + if (m_Camera) { + NVRenderContext &theContext(m_Renderer.GetContext()); + for (QT3DSU32 idx = 0, end = m_IRenderWidgets.size(); idx < end; ++idx) { + IRenderWidget &theWidget = *m_IRenderWidgets[idx]; + theWidget.Render(m_Renderer, theContext); + } + } + } + +#ifdef ADVANCED_BLEND_SW_FALLBACK + void SLayerRenderData::BlendAdvancedEquationSwFallback(NVRenderTexture2D *drawTexture, + NVRenderTexture2D *layerTexture, + AdvancedBlendModes::Enum blendMode) + { + NVRenderContext &theContext(m_Renderer.GetContext()); + SAdvancedModeBlendShader *shader = m_Renderer.GetAdvancedBlendModeShader(blendMode); + if (shader == NULL) + return; + + theContext.SetActiveShader(&(shader->m_Shader)); + + shader->m_baseLayer.Set(layerTexture); + shader->m_blendLayer.Set(drawTexture); + // Draw a fullscreen quad + m_Renderer.RenderQuad(); + } + + void SLayerRenderData::SetupDrawFB(bool depthEnabled) + { + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + // create drawing FBO and texture, if not existing + if (!m_AdvancedModeDrawFB) + m_AdvancedModeDrawFB = theRenderContext.CreateFrameBuffer(); + if (!m_AdvancedBlendDrawTexture) { + m_AdvancedBlendDrawTexture = theRenderContext.CreateTexture2D(); + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + m_AdvancedBlendDrawTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theViewport.m_Width, + theViewport.m_Height, + NVRenderTextureFormats::RGBA8); + m_AdvancedModeDrawFB->Attach(NVRenderFrameBufferAttachments::Color0, + *m_AdvancedBlendDrawTexture); + // Use existing depth prepass information when rendering transparent objects to a FBO + if (depthEnabled) + m_AdvancedModeDrawFB->Attach(NVRenderFrameBufferAttachments::Depth, + *m_LayerPrepassDepthTexture); + } + theRenderContext.SetRenderTarget(m_AdvancedModeDrawFB); + // make sure that depth testing is on in order to render just the + // depth-passed objects (=transparent objects) and leave background intact + if (depthEnabled) + theRenderContext.SetDepthTestEnabled(true); + theRenderContext.SetBlendingEnabled(false); + // clear color commonly is the layer background, make sure that it is all-zero here + QT3DSVec4 originalClrColor = theRenderContext.GetClearColor(); + theRenderContext.SetClearColor(QT3DSVec4(0.0)); + theRenderContext.Clear(NVRenderClearValues::Color); + theRenderContext.SetClearColor(originalClrColor); + + } + void SLayerRenderData::BlendAdvancedToFB(DefaultMaterialBlendMode::Enum blendMode, + bool depthEnabled, CResourceFrameBuffer *theFB) + { + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + AdvancedBlendModes::Enum advancedMode; + + switch (blendMode) { + case DefaultMaterialBlendMode::Overlay: + advancedMode = AdvancedBlendModes::Overlay; + break; + case DefaultMaterialBlendMode::ColorBurn: + advancedMode = AdvancedBlendModes::ColorBurn; + break; + case DefaultMaterialBlendMode::ColorDodge: + advancedMode = AdvancedBlendModes::ColorDodge; + break; + default: + Q_UNREACHABLE(); + } + // create blending FBO and texture if not existing + if (!m_AdvancedModeBlendFB) + m_AdvancedModeBlendFB = theRenderContext.CreateFrameBuffer(); + if (!m_AdvancedBlendBlendTexture) { + m_AdvancedBlendBlendTexture = theRenderContext.CreateTexture2D(); + m_AdvancedBlendBlendTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theViewport.m_Width, + theViewport.m_Height, + NVRenderTextureFormats::RGBA8); + m_AdvancedModeBlendFB->Attach(NVRenderFrameBufferAttachments::Color0, + *m_AdvancedBlendBlendTexture); + } + theRenderContext.SetRenderTarget(m_AdvancedModeBlendFB); + + // Blend transparent objects with SW fallback shaders. + // Disable depth testing as transparent objects have already been + // depth-checked; here we want to run shader for all layer pixels + if (depthEnabled) + { + theRenderContext.SetDepthTestEnabled(false); + theRenderContext.SetDepthWriteEnabled(false); + } + BlendAdvancedEquationSwFallback(m_AdvancedBlendDrawTexture, m_LayerTexture, advancedMode); + theRenderContext.SetRenderTarget(*theFB); + // setup read target + theRenderContext.SetReadTarget(m_AdvancedModeBlendFB); + theRenderContext.SetReadBuffer(NVReadFaces::Color0); + theRenderContext.BlitFramebuffer(0, 0, theViewport.m_Width, theViewport.m_Height, + 0, 0, theViewport.m_Width, theViewport.m_Height, + NVRenderClearValues::Color, + NVRenderTextureMagnifyingOp::Nearest); + } +#endif + + void SLayerRenderData::RenderToViewport() + { + if (m_LayerPrepResult->IsLayerVisible()) { + if (GetOffscreenRenderer()) { + if (m_Layer.m_Background == LayerBackground::Color) { + m_LastFrameOffscreenRenderer->RenderWithClear( + CreateOffscreenRenderEnvironment(), m_Renderer.GetContext(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor(), + SScene::AlwaysClear, m_Layer.m_ClearColor, &m_Layer); + } else { + m_LastFrameOffscreenRenderer->Render( + CreateOffscreenRenderEnvironment(), m_Renderer.GetContext(), + m_Renderer.GetQt3DSContext().GetPresentationScaleFactor(), + SScene::ClearIsOptional, &m_Layer); + } + } else { + RenderDepthPass(false); + Render(); + RenderRenderWidgets(); + } + } + } + // These are meant to be pixel offsets, so you need to divide them by the width/height + // of the layer respectively. + const QT3DSVec2 s_VertexOffsets[SLayerRenderPreparationData::MAX_AA_LEVELS] = { + QT3DSVec2(-0.170840f, -0.553840f), // 1x + QT3DSVec2(0.162960f, -0.319340f), // 2x + QT3DSVec2(0.360260f, -0.245840f), // 3x + QT3DSVec2(-0.561340f, -0.149540f), // 4x + QT3DSVec2(0.249460f, 0.453460f), // 5x + QT3DSVec2(-0.336340f, 0.378260f), // 6x + QT3DSVec2(0.340000f, 0.166260f), // 7x + QT3DSVec2(0.235760f, 0.527760f), // 8x + }; + + // Blend factors are in the form of (frame blend factor, accumulator blend factor) + const QT3DSVec2 s_BlendFactors[SLayerRenderPreparationData::MAX_AA_LEVELS] = { + QT3DSVec2(0.500000f, 0.500000f), // 1x + QT3DSVec2(0.333333f, 0.666667f), // 2x + QT3DSVec2(0.250000f, 0.750000f), // 3x + QT3DSVec2(0.200000f, 0.800000f), // 4x + QT3DSVec2(0.166667f, 0.833333f), // 5x + QT3DSVec2(0.142857f, 0.857143f), // 6x + QT3DSVec2(0.125000f, 0.875000f), // 7x + QT3DSVec2(0.111111f, 0.888889f), // 8x + }; + + const QT3DSVec2 s_TemporalVertexOffsets[SLayerRenderPreparationData::MAX_TEMPORAL_AA_LEVELS] = { + QT3DSVec2(.3f, .3f), QT3DSVec2(-.3f, -.3f) + }; + + static inline void OffsetProjectionMatrix(QT3DSMat44 &inProjectionMatrix, QT3DSVec2 inVertexOffsets) + { + inProjectionMatrix.column3.x = + inProjectionMatrix.column3.x + inProjectionMatrix.column3.w * inVertexOffsets.x; + inProjectionMatrix.column3.y = + inProjectionMatrix.column3.y + inProjectionMatrix.column3.w * inVertexOffsets.y; + } + + CRegisteredString depthPassStr; + + // Render this layer's data to a texture. Required if we have any effects, + // prog AA, or if forced. + void SLayerRenderData::RenderToTexture() + { + QT3DS_ASSERT(m_LayerPrepResult->m_Flags.ShouldRenderToTexture()); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + NVRenderContext &theRenderContext(m_Renderer.GetContext()); + QSize theLayerTextureDimensions = thePrepResult.GetTextureDimensions(); + QSize theLayerOriginalTextureDimensions = theLayerTextureDimensions; + NVRenderTextureFormats::Enum DepthTextureFormat = NVRenderTextureFormats::Depth24Stencil8; + NVRenderTextureFormats::Enum ColorTextureFormat = NVRenderTextureFormats::RGBA8; + if (thePrepResult.m_LastEffect + && theRenderContext.GetRenderContextType() != NVRenderContextValues::GLES2) { + if (m_Layer.m_Background != LayerBackground::Transparent) + ColorTextureFormat = NVRenderTextureFormats::R11G11B10; + else + ColorTextureFormat = NVRenderTextureFormats::RGBA16F; + } + NVRenderTextureFormats::Enum ColorSSAOTextureFormat = NVRenderTextureFormats::RGBA8; + + bool needsRender = false; + QT3DSU32 sampleCount = 1; + // check multsample mode and MSAA texture support + if (m_Layer.m_MultisampleAAMode != AAModeValues::NoAA + && theRenderContext.AreMultisampleTexturesSupported()) + sampleCount = (QT3DSU32)m_Layer.m_MultisampleAAMode; + + bool isMultisamplePass = false; + if (theRenderContext.GetRenderContextType() != NVRenderContextValues::GLES2) + isMultisamplePass = + (sampleCount > 1) || (m_Layer.m_MultisampleAAMode == AAModeValues::SSAA); + + qt3ds::render::NVRenderTextureTargetType::Enum thFboAttachTarget = + qt3ds::render::NVRenderTextureTargetType::Texture2D; + + // If the user has disabled all layer caching this has the side effect of disabling the + // progressive AA algorithm. + if (thePrepResult.m_Flags.WasLayerDataDirty() + || thePrepResult.m_Flags.WasDirty() + || m_Renderer.IsLayerCachingEnabled() == false + || thePrepResult.m_Flags.ShouldRenderToTexture()) { + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + needsRender = true; + } + + CResourceTexture2D *renderColorTexture = &m_LayerTexture; + CResourceTexture2D *renderPrepassDepthTexture = &m_LayerPrepassDepthTexture; + CResourceTexture2D *renderWidgetTexture = &m_LayerWidgetTexture; + NVRenderContextScopedProperty<bool> __multisampleEnabled( + theRenderContext, &NVRenderContext::IsMultisampleEnabled, + &NVRenderContext::SetMultisampleEnabled); + theRenderContext.SetMultisampleEnabled(false); + if (isMultisamplePass) { + renderColorTexture = &m_LayerMultisampleTexture; + renderPrepassDepthTexture = &m_LayerMultisamplePrepassDepthTexture; + renderWidgetTexture = &m_LayerMultisampleWidgetTexture; + // for SSAA we don't use MS textures + if (m_Layer.m_MultisampleAAMode != AAModeValues::SSAA) + thFboAttachTarget = qt3ds::render::NVRenderTextureTargetType::Texture2D_MS; + } + QT3DSU32 maxTemporalPassIndex = m_Layer.m_TemporalAAEnabled ? 2 : 0; + + // If all the dimensions match then we do not have to re-render the layer. + if (m_LayerTexture.TextureMatches(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), ColorTextureFormat) + && (!thePrepResult.m_Flags.RequiresDepthTexture() + || m_LayerDepthTexture.TextureMatches(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + DepthTextureFormat)) + && m_ProgressiveAAPassIndex >= thePrepResult.m_MaxAAPassIndex + && m_NonDirtyTemporalAAPassIndex >= maxTemporalPassIndex && needsRender == false) { + return; + } + + // adjust render size for SSAA + if (m_Layer.m_MultisampleAAMode == AAModeValues::SSAA) { + QT3DSU32 ow, oh; + CRendererUtil::GetSSAARenderSize(theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), + ow, oh); + theLayerTextureDimensions = QSize(ow, oh); + } + + // If our pass index == thePreResult.m_MaxAAPassIndex then + // we shouldn't get into here. + + IResourceManager &theResourceManager = m_Renderer.GetQt3DSContext().GetResourceManager(); + bool hadLayerTexture = true; + + if (renderColorTexture->EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + ColorTextureFormat, sampleCount)) { + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + hadLayerTexture = false; + } + + if (thePrepResult.m_Flags.RequiresDepthTexture()) { + // The depth texture doesn't need to be multisample, the prepass depth does. + if (m_LayerDepthTexture.EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + DepthTextureFormat)) { + // Depth textures are generally not bilinear filtered. + m_LayerDepthTexture->SetMinFilter(NVRenderTextureMinifyingOp::Nearest); + m_LayerDepthTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Nearest); + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + } + } + + if (thePrepResult.m_Flags.RequiresSsaoPass()) { + if (m_LayerSsaoTexture.EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + ColorSSAOTextureFormat)) { + m_LayerSsaoTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + m_LayerSsaoTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + m_ProgressiveAAPassIndex = 0; + m_NonDirtyTemporalAAPassIndex = 0; + } + } + + QT3DS_ASSERT(!thePrepResult.m_Flags.RequiresDepthTexture() || m_LayerDepthTexture); + QT3DS_ASSERT(!thePrepResult.m_Flags.RequiresSsaoPass() || m_LayerSsaoTexture); + + CResourceTexture2D theLastLayerTexture(theResourceManager); + SLayerProgAABlendShader *theBlendShader = NULL; + QT3DSU32 aaFactorIndex = 0; + bool isProgressiveAABlendPass = + m_ProgressiveAAPassIndex && m_ProgressiveAAPassIndex < thePrepResult.m_MaxAAPassIndex; + bool isTemporalAABlendPass = m_Layer.m_TemporalAAEnabled && m_ProgressiveAAPassIndex == 0; + + if (isProgressiveAABlendPass || isTemporalAABlendPass) { + theBlendShader = m_Renderer.GetLayerProgAABlendShader(); + if (theBlendShader) { + m_LayerTexture.EnsureTexture(theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), + ColorTextureFormat); + QT3DSVec2 theVertexOffsets; + if (isProgressiveAABlendPass) { + theLastLayerTexture.StealTexture(m_LayerTexture); + aaFactorIndex = (m_ProgressiveAAPassIndex - 1); + theVertexOffsets = s_VertexOffsets[aaFactorIndex]; + } else { + if (m_TemporalAATexture.GetTexture()) + theLastLayerTexture.StealTexture(m_TemporalAATexture); + else { + if (hadLayerTexture) { + theLastLayerTexture.StealTexture(m_LayerTexture); + } + } + theVertexOffsets = s_TemporalVertexOffsets[m_TemporalAAPassIndex]; + ++m_TemporalAAPassIndex; + ++m_NonDirtyTemporalAAPassIndex; + m_TemporalAAPassIndex = m_TemporalAAPassIndex % MAX_TEMPORAL_AA_LEVELS; + } + if (theLastLayerTexture.GetTexture()) { + theVertexOffsets.x = + theVertexOffsets.x / (theLayerOriginalTextureDimensions.width() / 2.0f); + theVertexOffsets.y = + theVertexOffsets.y / (theLayerOriginalTextureDimensions.height() / 2.0f); + // Run through all models and update MVP. + // run through all texts and update MVP. + // run through all path and update MVP. + + // TODO - optimize this exact matrix operation. + for (QT3DSU32 idx = 0, end = m_ModelContexts.size(); idx < end; ++idx) { + QT3DSMat44 &originalProjection(m_ModelContexts[idx]->m_ModelViewProjection); + OffsetProjectionMatrix(originalProjection, theVertexOffsets); + } + for (QT3DSU32 idx = 0, end = m_OpaqueObjects.size(); idx < end; ++idx) { + if (m_OpaqueObjects[idx]->m_RenderableFlags.IsPath()) { + SPathRenderable &theRenderable = + static_cast<SPathRenderable &>(*m_OpaqueObjects[idx]); + OffsetProjectionMatrix(theRenderable.m_ModelViewProjection, + theVertexOffsets); + } + } + for (QT3DSU32 idx = 0, end = m_TransparentObjects.size(); idx < end; ++idx) { + if (m_TransparentObjects[idx]->m_RenderableFlags.IsText()) { + STextRenderable &theRenderable = + static_cast<STextRenderable &>(*m_TransparentObjects[idx]); + OffsetProjectionMatrix(theRenderable.m_ModelViewProjection, + theVertexOffsets); +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + } else if (m_TransparentObjects[idx]->m_RenderableFlags + .isDistanceField()) { + SDistanceFieldRenderable &theRenderable + = static_cast<SDistanceFieldRenderable &>( + *m_TransparentObjects[idx]); + OffsetProjectionMatrix(theRenderable.m_mvp, + theVertexOffsets); +#endif + } else if (m_TransparentObjects[idx]->m_RenderableFlags.IsPath()) { + SPathRenderable &theRenderable = + static_cast<SPathRenderable &>(*m_TransparentObjects[idx]); + OffsetProjectionMatrix(theRenderable.m_ModelViewProjection, + theVertexOffsets); + } + } + } + } + } + if (theLastLayerTexture.GetTexture() == NULL) { + isProgressiveAABlendPass = false; + isTemporalAABlendPass = false; + } + // Sometimes we will have stolen the render texture. + renderColorTexture->EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), ColorTextureFormat, + sampleCount); + + if (!isTemporalAABlendPass) + m_TemporalAATexture.ReleaseTexture(); + + // Allocating a frame buffer can cause it to be bound, so we need to save state before this + // happens. + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuf( + theRenderContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + // Match the bit depth of the current render target to avoid popping when we switch from aa + // to non aa layers + // We have to all this here in because once we change the FB by allocating an FB we are + // screwed. + NVRenderTextureFormats::Enum theDepthFormat(GetDepthBufferFormat()); + NVRenderFrameBufferAttachments::Enum theDepthAttachmentFormat( + GetFramebufferDepthAttachmentFormat(theDepthFormat)); + + // Definitely disable the scissor rect if it is running right now. + NVRenderContextScopedProperty<bool> __scissorEnabled( + theRenderContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled, false); + CResourceFrameBuffer theFB(theResourceManager); + // Allocates the frame buffer which has the side effect of setting the current render target + // to that frame buffer. + theFB.EnsureFrameBuffer(); + + bool hasDepthObjects = m_OpaqueObjects.size() > 0; + bool requiresDepthStencilBuffer = + hasDepthObjects || thePrepResult.m_Flags.RequiresStencilBuffer(); + NVRenderRect theNewViewport(0, 0, theLayerTextureDimensions.width(), + theLayerTextureDimensions.height()); + { + theRenderContext.SetRenderTarget(theFB); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theRenderContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport, + theNewViewport); + QT3DSVec4 clearColor(0.0f); + if (m_Layer.m_Background == LayerBackground::Color) + clearColor = m_Layer.m_ClearColor; + + NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theRenderContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor, + clearColor); + if (requiresDepthStencilBuffer) { + if (renderPrepassDepthTexture->EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + theDepthFormat, sampleCount)) { + (*renderPrepassDepthTexture)->SetMinFilter(NVRenderTextureMinifyingOp::Nearest); + (*renderPrepassDepthTexture) + ->SetMagFilter(NVRenderTextureMagnifyingOp::Nearest); + } + } + + if (thePrepResult.m_Flags.RequiresDepthTexture() && m_ProgressiveAAPassIndex == 0) { + // Setup FBO with single depth buffer target. + // Note this does not use multisample. + NVRenderFrameBufferAttachments::Enum theAttachment = + GetFramebufferDepthAttachmentFormat(DepthTextureFormat); + theFB->Attach(theAttachment, *m_LayerDepthTexture); + + // In this case transparent objects also may write their depth. + RenderDepthPass(true); + theFB->Attach(theAttachment, NVRenderTextureOrRenderBuffer()); + } + + if (thePrepResult.m_Flags.RequiresSsaoPass() && m_ProgressiveAAPassIndex == 0 + && m_Camera != nullptr) { + StartProfiling("AO pass", false); + // Setup FBO with single color buffer target + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *m_LayerSsaoTexture); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + RenderAoPass(); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer()); + EndProfiling("AO pass"); + } + + if (thePrepResult.m_Flags.RequiresShadowMapPass() && m_ProgressiveAAPassIndex == 0) { + // shadow map path + RenderShadowMapPass(&theFB); + } + + if (sampleCount > 1) { + theRenderContext.SetMultisampleEnabled(true); + } + + qt3ds::render::NVRenderClearFlags clearFlags = qt3ds::render::NVRenderClearValues::Color; + + // render depth prepass + if ((*renderPrepassDepthTexture)) { + theFB->Attach(theDepthAttachmentFormat, **renderPrepassDepthTexture, + thFboAttachTarget); + + if (m_Layer.m_Flags.IsLayerEnableDepthPrepass()) { + StartProfiling("Depth pass", false); + RenderDepthPass(false); + EndProfiling("Depth pass"); + } else { + clearFlags |= (qt3ds::render::NVRenderClearValues::Depth); + clearFlags |= (qt3ds::render::NVRenderClearValues::Stencil); + // enable depth write for the clear below + theRenderContext.SetDepthWriteEnabled(true); + } + } + + theFB->Attach(NVRenderFrameBufferAttachments::Color0, **renderColorTexture, + thFboAttachTarget); + if (m_Layer.m_Background != LayerBackground::Unspecified) + theRenderContext.Clear(clearFlags); + + // We don't clear the depth buffer because the layer render code we are about to call + // will do this. + StartProfiling("Render pass", false); + Render(&theFB); + // Debug measure to view the depth map to ensure we're rendering it correctly. + //if (m_Layer.m_TemporalAAEnabled) { + // RenderFakeDepthMapPass(m_ShadowMapManager->GetShadowMapEntry(0)->m_DepthMap, + // m_ShadowMapManager->GetShadowMapEntry(0)->m_DepthCube); + //} + EndProfiling("Render pass"); + + // Now before going further, we downsample and resolve the multisample information. + // This allows all algorithms running after + // this point to run unchanged. + if (isMultisamplePass) { + if (m_Layer.m_MultisampleAAMode != AAModeValues::SSAA) { + // Resolve the FBO to the layer texture + CRendererUtil::ResolveMutisampleFBOColorOnly( + theResourceManager, m_LayerTexture, theRenderContext, + theLayerTextureDimensions.width(), theLayerTextureDimensions.height(), + ColorTextureFormat, *theFB); + + theRenderContext.SetMultisampleEnabled(false); + } else { + // Resolve the FBO to the layer texture + CRendererUtil::ResolveSSAAFBOColorOnly( + theResourceManager, m_LayerTexture, + theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), theRenderContext, + theLayerTextureDimensions.width(), theLayerTextureDimensions.height(), + ColorTextureFormat, *theFB); + } + } + + // CN - when I tried to get anti-aliased widgets I lost all transparency on the widget + // layer which made it overwrite the object you were + // manipulating. When I tried to use parallel nsight on it the entire studio + // application crashed on startup. + if (NeedsWidgetTexture()) { + m_LayerWidgetTexture.EnsureTexture(theLayerTextureDimensions.width(), + theLayerTextureDimensions.height(), + NVRenderTextureFormats::RGBA8); + theRenderContext.SetRenderTarget(theFB); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *m_LayerWidgetTexture); + theFB->Attach(GetFramebufferDepthAttachmentFormat(DepthTextureFormat), + *m_LayerDepthTexture); + theRenderContext.SetClearColor(QT3DSVec4(0.0f)); + theRenderContext.Clear(qt3ds::render::NVRenderClearValues::Color); + // We should already have the viewport and everything setup for this. + RenderRenderWidgets(); + } + + if (theLastLayerTexture.GetTexture() != NULL + && (isProgressiveAABlendPass || isTemporalAABlendPass)) { + theRenderContext.SetViewport( + NVRenderRect(0, 0, theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height())); + CResourceTexture2D targetTexture( + theResourceManager, theLayerOriginalTextureDimensions.width(), + theLayerOriginalTextureDimensions.height(), ColorTextureFormat); + theFB->Attach(theDepthAttachmentFormat, + NVRenderTextureOrRenderBuffer()); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, *targetTexture); + QT3DSVec2 theBlendFactors; + if (isProgressiveAABlendPass) + theBlendFactors = s_BlendFactors[aaFactorIndex]; + else + theBlendFactors = QT3DSVec2(.5f, .5f); + + theRenderContext.SetDepthTestEnabled(false); + theRenderContext.SetBlendingEnabled(false); + theRenderContext.SetCullingEnabled(false); + theRenderContext.SetActiveShader(theBlendShader->m_Shader); + theBlendShader->m_AccumSampler.Set(theLastLayerTexture); + theBlendShader->m_LastFrame.Set(m_LayerTexture); + theBlendShader->m_BlendFactors.Set(theBlendFactors); + m_Renderer.RenderQuad(); + theFB->Attach(NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer()); + if (isTemporalAABlendPass) + m_TemporalAATexture.StealTexture(m_LayerTexture); + m_LayerTexture.StealTexture(targetTexture); + } + + m_LayerTexture->SetMinFilter(NVRenderTextureMinifyingOp::Linear); + m_LayerTexture->SetMagFilter(NVRenderTextureMagnifyingOp::Linear); + + // Don't remember why needs widget texture is false here. + // Should have commented why progAA plus widgets is a fail. + if (m_ProgressiveAAPassIndex < thePrepResult.m_MaxAAPassIndex + && NeedsWidgetTexture() == false) + ++m_ProgressiveAAPassIndex; + +// now we render all post effects +#ifdef QT3DS_CACHED_POST_EFFECT + ApplyLayerPostEffects(); +#endif + + if (m_LayerPrepassDepthTexture) { + // Detach any depth buffers. + theFB->Attach(theDepthAttachmentFormat, qt3ds::render::NVRenderTextureOrRenderBuffer(), + thFboAttachTarget); + } + + theFB->Attach(NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer(), thFboAttachTarget); + // Let natural scoping rules destroy the other stuff. + } + } + + void SLayerRenderData::ApplyLayerPostEffects() + { + if (m_Layer.m_FirstEffect == NULL) { + if (m_LayerCachedTexture) { + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + theResourceManager.Release(*m_LayerCachedTexture); + m_LayerCachedTexture = NULL; + } + return; + } + + IEffectSystem &theEffectSystem(m_Renderer.GetQt3DSContext().GetEffectSystem()); + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + // we use the non MSAA buffer for the effect + NVRenderTexture2D *theLayerColorTexture = m_LayerTexture; + NVRenderTexture2D *theLayerDepthTexture = m_LayerDepthTexture; + + NVRenderTexture2D *theCurrentTexture = theLayerColorTexture; + for (SEffect *theEffect = m_Layer.m_FirstEffect; theEffect; + theEffect = theEffect->m_NextEffect) { + if (theEffect->m_Flags.IsActive() && m_Camera) { + StartProfiling(theEffect->m_ClassName, false); + + NVRenderTexture2D *theRenderedEffect = theEffectSystem.RenderEffect( + SEffectRenderArgument(*theEffect, *theCurrentTexture, + QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar), + theLayerDepthTexture, m_LayerPrepassDepthTexture)); + + EndProfiling(theEffect->m_ClassName); + + // If the texture came from rendering a chain of effects, then we don't need it + // after this. + if (theCurrentTexture != theLayerColorTexture) + theResourceManager.Release(*theCurrentTexture); + + theCurrentTexture = theRenderedEffect; + + if (!theRenderedEffect) { + QString errorMsg = QObject::tr("Failed to compile \"%1\" effect.\nConsider" + " removing it from the presentation.") + .arg(theEffect->m_ClassName.c_str()); + QT3DS_ALWAYS_ASSERT_MESSAGE(errorMsg.toUtf8()); + break; + } + } + } + + if (m_LayerCachedTexture && m_LayerCachedTexture != m_LayerTexture) { + theResourceManager.Release(*m_LayerCachedTexture); + m_LayerCachedTexture = NULL; + } + + if (theCurrentTexture != m_LayerTexture) + m_LayerCachedTexture = theCurrentTexture; + } + + inline bool AnyCompletelyNonTransparentObjects(TRenderableObjectList &inObjects) + { + for (QT3DSU32 idx = 0, end = inObjects.size(); idx < end; ++idx) { + if (inObjects[idx]->m_RenderableFlags.IsCompletelyTransparent() == false) + return true; + } + return false; + } + + void SLayerRenderData::RunnableRenderToViewport(qt3ds::render::NVRenderFrameBuffer *theFB) + { + // If we have an effect, an opaque object, or any transparent objects that aren't completely + // transparent + // or an offscreen renderer or a layer widget texture + // Then we can't possible affect the resulting render target. + bool needsToRender = m_Layer.m_FirstEffect != NULL || m_OpaqueObjects.empty() == false + || AnyCompletelyNonTransparentObjects(m_TransparentObjects) || GetOffscreenRenderer() + || m_LayerWidgetTexture || m_BoundingRectColor.hasValue() + || m_Layer.m_Background == LayerBackground::Color; + + if (needsToRender == false) + return; + + NVRenderContext &theContext(m_Renderer.GetContext()); + theContext.resetStates(); + + NVRenderContextScopedProperty<NVRenderFrameBuffer *> __fbo( + theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget); + qt3ds::render::NVRenderRect theCurrentViewport = theContext.GetViewport(); + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + NVRenderContextScopedProperty<bool> theScissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled); + NVRenderContextScopedProperty<qt3ds::render::NVRenderRect> theScissorRect( + theContext, &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + NVRenderRectF theScreenRect(thePrepResult.GetLayerToPresentationViewport()); + + bool blendingEnabled = m_Layer.m_Background != LayerBackground::Unspecified; + if (!thePrepResult.m_Flags.ShouldRenderToTexture()) { + theContext.SetViewport( + m_LayerPrepResult->GetLayerToPresentationViewport().ToIntegerRect()); + theContext.SetScissorTestEnabled(true); + theContext.SetScissorRect( + m_LayerPrepResult->GetLayerToPresentationScissorRect().ToIntegerRect()); + if (m_Layer.m_Background == LayerBackground::Color) { + NVRenderContextScopedProperty<QT3DSVec4> __clearColor( + theContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor, + m_Layer.m_ClearColor); + theContext.Clear(NVRenderClearValues::Color); + } + RenderToViewport(); + } else { +// First, render the layer along with whatever progressive AA is appropriate. +// The render graph should have taken care of the render to texture step. +#ifdef QT3DS_CACHED_POST_EFFECT + NVRenderTexture2D *theLayerColorTexture = + (m_LayerCachedTexture) ? m_LayerCachedTexture : m_LayerTexture; +#else + // Then render all but the last effect + IEffectSystem &theEffectSystem(m_Renderer.GetQt3DSContext().GetEffectSystem()); + IResourceManager &theResourceManager(m_Renderer.GetQt3DSContext().GetResourceManager()); + // we use the non MSAA buffer for the effect + NVRenderTexture2D *theLayerColorTexture = m_LayerTexture; + NVRenderTexture2D *theLayerDepthTexture = m_LayerDepthTexture; + + NVRenderTexture2D *theCurrentTexture = theLayerColorTexture; + for (SEffect *theEffect = m_Layer.m_FirstEffect; + theEffect && theEffect != thePrepResult.m_LastEffect; + theEffect = theEffect->m_NextEffect) { + if (theEffect->m_Flags.IsActive() && m_Camera) { + StartProfiling(theEffect->m_ClassName, false); + + NVRenderTexture2D *theRenderedEffect = theEffectSystem.RenderEffect( + SEffectRenderArgument(*theEffect, *theCurrentTexture, + QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar), + theLayerDepthTexture, m_LayerPrepassDepthTexture)); + + EndProfiling(theEffect->m_ClassName); + + // If the texture came from rendering a chain of effects, then we don't need it + // after this. + if (theCurrentTexture != theLayerColorTexture) + theResourceManager.Release(*theCurrentTexture); + + theCurrentTexture = theRenderedEffect; + } + } +#endif + // Now the last effect or straight to the scene if we have no last effect + // There are two cases we need to consider here. The first is when we shouldn't + // transform + // the result and thus we need to setup an MVP that just maps to the viewport width and + // height. + // The second is when we are expected to render to the scene using some global + // transform. + QT3DSMat44 theFinalMVP(QT3DSMat44::createIdentity()); + SCamera theTempCamera; + NVRenderRect theLayerViewport( + thePrepResult.GetLayerToPresentationViewport().ToIntegerRect()); + NVRenderRect theLayerClip( + thePrepResult.GetLayerToPresentationScissorRect().ToIntegerRect()); + + { + QT3DSMat33 ignored; + QT3DSMat44 theViewProjection; + // We could cache these variables + theTempCamera.m_Flags.SetOrthographic(true); + theTempCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty); + // Move the camera back far enough that we can see everything + QT3DSF32 theCameraSetback(10); + // Attempt to ensure the layer can never be clipped. + theTempCamera.m_Position.z = -theCameraSetback; + theTempCamera.m_ClipFar = 2.0f * theCameraSetback; + // Render the layer texture to the entire viewport. + SCameraGlobalCalculationResult theResult = theTempCamera.CalculateGlobalVariables( + theLayerViewport, + QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, (QT3DSF32)theLayerViewport.m_Height)); + theTempCamera.CalculateViewProjectionMatrix(theViewProjection); + SNode theTempNode; + theFinalMVP = theViewProjection; + qt3ds::render::NVRenderBlendFunctionArgument blendFunc; + qt3ds::render::NVRenderBlendEquationArgument blendEqu; + + switch (m_Layer.m_BlendType) { + case LayerBlendTypes::Screen: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + case LayerBlendTypes::Multiply: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::DstColor, NVRenderDstBlendFunc::Zero, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + case LayerBlendTypes::Add: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + case LayerBlendTypes::Subtract: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ReverseSubtract, + NVRenderBlendEquation::ReverseSubtract); + break; + case LayerBlendTypes::Overlay: + // SW fallback doesn't use blend equation + // note blend func is not used here anymore + if (theContext.IsAdvancedBlendHwSupported() || + theContext.IsAdvancedBlendHwSupportedKHR()) + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Overlay, NVRenderBlendEquation::Overlay); + break; + case LayerBlendTypes::ColorBurn: + // SW fallback doesn't use blend equation + // note blend func is not used here anymore + if (theContext.IsAdvancedBlendHwSupported() || + theContext.IsAdvancedBlendHwSupportedKHR()) + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorBurn, NVRenderBlendEquation::ColorBurn); + break; + case LayerBlendTypes::ColorDodge: + // SW fallback doesn't use blend equation + // note blend func is not used here anymore + if (theContext.IsAdvancedBlendHwSupported() || + theContext.IsAdvancedBlendHwSupportedKHR()) + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorDodge, NVRenderBlendEquation::ColorDodge); + break; + default: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + blendEqu = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add); + break; + } + theContext.SetBlendFunction(blendFunc); + theContext.SetBlendEquation(blendEqu); + theContext.SetBlendingEnabled(blendingEnabled); + theContext.SetDepthTestEnabled(false); + } + + { + theContext.SetScissorTestEnabled(true); + theContext.SetViewport(theLayerViewport); + theContext.SetScissorRect(theLayerClip); + + // Remember the camera we used so we can get a valid pick ray + m_SceneCamera = theTempCamera; + theContext.SetDepthTestEnabled(false); +#ifndef QT3DS_CACHED_POST_EFFECT + if (thePrepResult.m_LastEffect && m_Camera) { + StartProfiling(thePrepResult.m_LastEffect->m_ClassName, false); + // inUseLayerMPV is true then we are rendering directly to the scene and thus we + // should enable blending + // for the final render pass. Else we should leave it. + theEffectSystem.RenderEffect( + SEffectRenderArgument(*thePrepResult.m_LastEffect, *theCurrentTexture, + QT3DSVec2(m_Camera->m_ClipNear, m_Camera->m_ClipFar), + theLayerDepthTexture, m_LayerPrepassDepthTexture), + theFinalMVP, blendingEnabled); + EndProfiling(thePrepResult.m_LastEffect->m_ClassName); + // If the texture came from rendering a chain of effects, then we don't need it + // after this. + if (theCurrentTexture != theLayerColorTexture) + theResourceManager.Release(*theCurrentTexture); + } else +#endif + { + theContext.SetCullingEnabled(false); + theContext.SetBlendingEnabled(blendingEnabled); + theContext.SetDepthTestEnabled(false); +#ifdef ADVANCED_BLEND_SW_FALLBACK + NVScopedRefCounted<NVRenderTexture2D> screenTexture = + m_Renderer.GetLayerBlendTexture(); + NVScopedRefCounted<NVRenderFrameBuffer> blendFB = m_Renderer.GetBlendFB(); + + // Layer blending for advanced blending modes if SW fallback is needed + // rendering to FBO and blending with separate shader + if (screenTexture) { + // Blending is enabled only if layer background has been chosen transparent + // Layers with advanced blending modes + if (blendingEnabled && (m_Layer.m_BlendType == LayerBlendTypes::Overlay || + m_Layer.m_BlendType == LayerBlendTypes::ColorBurn || + m_Layer.m_BlendType == LayerBlendTypes::ColorDodge)) { + theContext.SetScissorTestEnabled(false); + theContext.SetBlendingEnabled(false); + + // Get part matching to layer from screen texture and + // use that for blending + qt3ds::render::NVRenderTexture2D *blendBlitTexture; + blendBlitTexture = theContext.CreateTexture2D(); + blendBlitTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theLayerViewport.m_Width, + theLayerViewport.m_Height, + NVRenderTextureFormats::RGBA8); + qt3ds::render::NVRenderFrameBuffer *blitFB; + blitFB = theContext.CreateFrameBuffer(); + blitFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*blendBlitTexture)); + blendFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*screenTexture)); + theContext.SetRenderTarget(blitFB); + theContext.SetReadTarget(blendFB); + theContext.SetReadBuffer(NVReadFaces::Color0); + theContext.BlitFramebuffer(theLayerViewport.m_X, theLayerViewport.m_Y, + theLayerViewport.m_Width + + theLayerViewport.m_X, + theLayerViewport.m_Height + + theLayerViewport.m_Y, + 0, 0, + theLayerViewport.m_Width, + theLayerViewport.m_Height, + NVRenderClearValues::Color, + NVRenderTextureMagnifyingOp::Nearest); + + qt3ds::render::NVRenderTexture2D *blendResultTexture; + blendResultTexture = theContext.CreateTexture2D(); + blendResultTexture->SetTextureData(NVDataRef<QT3DSU8>(), 0, + theLayerViewport.m_Width, + theLayerViewport.m_Height, + NVRenderTextureFormats::RGBA8); + qt3ds::render::NVRenderFrameBuffer *resultFB; + resultFB = theContext.CreateFrameBuffer(); + resultFB->Attach(NVRenderFrameBufferAttachments::Color0, + NVRenderTextureOrRenderBuffer(*blendResultTexture)); + theContext.SetRenderTarget(resultFB); + + AdvancedBlendModes::Enum advancedMode; + switch (m_Layer.m_BlendType) { + case LayerBlendTypes::Overlay: + advancedMode = AdvancedBlendModes::Overlay; + break; + case LayerBlendTypes::ColorBurn: + advancedMode = AdvancedBlendModes::ColorBurn; + break; + case LayerBlendTypes::ColorDodge: + advancedMode = AdvancedBlendModes::ColorDodge; + break; + default: + advancedMode = AdvancedBlendModes::None; + break; + } + + theContext.SetViewport(NVRenderRect(0, 0, theLayerViewport.m_Width, + theLayerViewport.m_Height)); + BlendAdvancedEquationSwFallback(theLayerColorTexture, blendBlitTexture, + advancedMode); + blitFB->release(); + // save blending result to screen texture for use with other layers + theContext.SetViewport(theLayerViewport); + theContext.SetRenderTarget(blendFB); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *blendResultTexture); + // render the blended result + theContext.SetRenderTarget(theFB); + theContext.SetScissorTestEnabled(true); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *blendResultTexture); + resultFB->release(); + } else { + // Layers with normal blending modes + // save result for future use + theContext.SetViewport(theLayerViewport); + theContext.SetScissorTestEnabled(false); + theContext.SetBlendingEnabled(true); + theContext.SetRenderTarget(blendFB); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); + theContext.SetRenderTarget(theFB); + theContext.SetScissorTestEnabled(true); + theContext.SetViewport(theCurrentViewport); + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); + } + } else { + // No advanced blending SW fallback needed + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); + } +#else + m_Renderer.RenderQuad(QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, + (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *theLayerColorTexture); +#endif + } + if (m_LayerWidgetTexture) { + theContext.SetBlendingEnabled(false); + m_Renderer.SetupWidgetLayer(); + SLayerRenderPreparationResult &thePrepResult(*m_LayerPrepResult); + NVRenderRectF thePresRect(thePrepResult.GetPresentationViewport()); + NVRenderRectF theLayerRect(thePrepResult.GetLayerToPresentationViewport()); + + // Ensure we remove any offsetting in the layer rect that was caused simply by + // the + // presentation rect offsetting but then use a new rect. + NVRenderRectF theWidgetLayerRect(theLayerRect.m_X - thePresRect.m_X, + theLayerRect.m_Y - thePresRect.m_Y, + theLayerRect.m_Width, theLayerRect.m_Height); + theContext.SetScissorTestEnabled(false); + theContext.SetViewport(theWidgetLayerRect.ToIntegerRect()); + m_Renderer.RenderQuad( + QT3DSVec2((QT3DSF32)theLayerViewport.m_Width, (QT3DSF32)theLayerViewport.m_Height), + theFinalMVP, *m_LayerWidgetTexture); + } + } + } // End offscreen render code. + + if (m_BoundingRectColor.hasValue()) { + NVRenderContextScopedProperty<NVRenderRect> __viewport( + theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport); + NVRenderContextScopedProperty<bool> theScissorEnabled( + theContext, &NVRenderContext::IsScissorTestEnabled, + &NVRenderContext::SetScissorTestEnabled); + NVRenderContextScopedProperty<qt3ds::render::NVRenderRect> theScissorRect( + theContext, &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect); + m_Renderer.SetupWidgetLayer(); + // Setup a simple viewport to render to the entire presentation viewport. + theContext.SetViewport( + NVRenderRect(0, 0, (QT3DSU32)thePrepResult.GetPresentationViewport().m_Width, + (QT3DSU32)thePrepResult.GetPresentationViewport().m_Height)); + + NVRenderRectF thePresRect(thePrepResult.GetPresentationViewport()); + + // Remove any offsetting from the presentation rect since the widget layer is a + // stand-alone fbo. + NVRenderRectF theWidgetScreenRect(theScreenRect.m_X - thePresRect.m_X, + theScreenRect.m_Y - thePresRect.m_Y, + theScreenRect.m_Width, theScreenRect.m_Height); + theContext.SetScissorTestEnabled(false); + m_Renderer.DrawScreenRect(theWidgetScreenRect, *m_BoundingRectColor); + } + theContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha)); + theContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + } + +#define RENDER_FRAME_NEW(type) QT3DS_NEW(m_Renderer.GetPerFrameAllocator(), type) + + void SLayerRenderData::AddLayerRenderStep() + { + SStackPerfTimer __perfTimer(m_Renderer.GetQt3DSContext().GetPerfTimer(), + "SLayerRenderData::AddLayerRenderStep"); + QT3DS_ASSERT(m_Camera); + if (!m_Camera) + return; + + IRenderList &theGraph(m_Renderer.GetQt3DSContext().GetRenderList()); + + qt3ds::render::NVRenderRect theCurrentViewport = theGraph.GetViewport(); + if (!m_LayerPrepResult.hasValue()) + PrepareForRender( + QSize(theCurrentViewport.m_Width, theCurrentViewport.m_Height)); + } + + void SLayerRenderData::PrepareForRender() + { + // When we render to the scene itself (as opposed to an offscreen buffer somewhere) + // then we use the MVP of the layer somewhat. + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + PrepareForRender( + QSize((QT3DSU32)theViewport.m_Width, (QT3DSU32)theViewport.m_Height)); + } + + void SLayerRenderData::ResetForFrame() + { + SLayerRenderPreparationData::ResetForFrame(); + m_BoundingRectColor.setEmpty(); + } + + void SLayerRenderData::PrepareAndRender(const QT3DSMat44 &inViewProjection) + { + TRenderableObjectList theTransparentObjects(m_TransparentObjects); + TRenderableObjectList theOpaqueObjects(m_OpaqueObjects); + theTransparentObjects.clear(); + theOpaqueObjects.clear(); + m_ModelContexts.clear(); + SLayerRenderPreparationResultFlags theFlags; + PrepareRenderablesForRender(inViewProjection, Empty(), 1.0, theFlags); + RenderDepthPass(false); + Render(); + } + + struct SLayerRenderToTextureRunnable : public IRenderTask + { + SLayerRenderData &m_Data; + SLayerRenderToTextureRunnable(SLayerRenderData &d) + : m_Data(d) + { + } + + void Run() override { m_Data.RenderToTexture(); } + }; + + static inline OffscreenRendererDepthValues::Enum + GetOffscreenRendererDepthValue(NVRenderTextureFormats::Enum inBufferFormat) + { + switch (inBufferFormat) { + case NVRenderTextureFormats::Depth32: + return OffscreenRendererDepthValues::Depth32; + case NVRenderTextureFormats::Depth24: + return OffscreenRendererDepthValues::Depth24; + case NVRenderTextureFormats::Depth24Stencil8: + return OffscreenRendererDepthValues::Depth24; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case NVRenderTextureFormats::Depth16: + return OffscreenRendererDepthValues::Depth16; + } + } + + SOffscreenRendererEnvironment SLayerRenderData::CreateOffscreenRenderEnvironment() + { + OffscreenRendererDepthValues::Enum theOffscreenDepth( + GetOffscreenRendererDepthValue(GetDepthBufferFormat())); + NVRenderRect theViewport = m_Renderer.GetQt3DSContext().GetRenderList().GetViewport(); + return SOffscreenRendererEnvironment(theViewport.m_Width, theViewport.m_Height, + NVRenderTextureFormats::RGBA8, theOffscreenDepth, + false, AAModeValues::NoAA); + } + + IRenderTask &SLayerRenderData::CreateRenderToTextureRunnable() + { + return *RENDER_FRAME_NEW(SLayerRenderToTextureRunnable)(*this); + } + + void SLayerRenderData::addRef() { atomicIncrement(&mRefCount); } + + void SLayerRenderData::release() { QT3DS_IMPLEMENT_REF_COUNT_RELEASE(m_Allocator); } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h new file mode 100644 index 0000000..4e237b0 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_IMPL_LAYER_RENDER_DATA_H +#define QT3DS_RENDERER_IMPL_LAYER_RENDER_DATA_H +#include "Qt3DSRender.h" +#include "Qt3DSRendererImplLayerRenderPreparationData.h" +#include "Qt3DSRenderResourceBufferObjects.h" + +namespace qt3ds { +namespace render { + +struct AdvancedBlendModes +{ + enum Enum { + None = 0, + Overlay, + ColorBurn, + ColorDodge + }; +}; + struct QT3DS_AUTOTEST_EXPORT SLayerRenderData : public SLayerRenderPreparationData + { + + // Layers can be rendered offscreen for many reasons; effects, progressive aa, + // or just because a flag forces it. If they are rendered offscreen we can then + // cache the result so we don't render the layer again if it isn't dirty. + CResourceTexture2D m_LayerTexture; + CResourceTexture2D m_TemporalAATexture; + // Sometimes we need to render our depth buffer to a depth texture. + CResourceTexture2D m_LayerDepthTexture; + CResourceTexture2D m_LayerPrepassDepthTexture; + CResourceTexture2D m_LayerWidgetTexture; + CResourceTexture2D m_LayerSsaoTexture; + // if we render multisampled we need resolve buffers + CResourceTexture2D m_LayerMultisampleTexture; + CResourceTexture2D m_LayerMultisamplePrepassDepthTexture; + CResourceTexture2D m_LayerMultisampleWidgetTexture; + // the texture contains the render result inclusive post effects + NVRenderTexture2D *m_LayerCachedTexture; + + NVRenderTexture2D *m_AdvancedBlendDrawTexture; + NVRenderTexture2D *m_AdvancedBlendBlendTexture; + qt3ds::render::NVRenderFrameBuffer *m_AdvancedModeDrawFB; + qt3ds::render::NVRenderFrameBuffer *m_AdvancedModeBlendFB; + + // True if this layer was rendered offscreen. + // If this object has no value then this layer wasn't rendered at all. + SOffscreenRendererEnvironment m_LastOffscreenRenderEnvironment; + + // GPU profiler per layer + NVScopedRefCounted<IRenderProfiler> m_LayerProfilerGpu; + + SCamera m_SceneCamera; + QT3DSVec2 m_SceneDimensions; + + // ProgressiveAA algorithm details. + QT3DSU32 m_ProgressiveAAPassIndex; + // Increments every frame regardless to provide appropriate jittering + QT3DSU32 m_TemporalAAPassIndex; + // Ensures we don't stop on an in-between frame; we will run two frames after the dirty flag + // is clear. + QT3DSU32 m_NonDirtyTemporalAAPassIndex; + QT3DSF32 m_TextScale; + + volatile QT3DSI32 mRefCount; + Option<QT3DSVec3> m_BoundingRectColor; + NVRenderTextureFormats::Enum m_DepthBufferFormat; + + QSize m_previousDimensions; + + SLayerRenderData(SLayer &inLayer, Qt3DSRendererImpl &inRenderer); + + virtual ~SLayerRenderData(); + + void PrepareForRender(); + + // Internal Call + void PrepareForRender(const QSize &inViewportDimensions) override; + + NVRenderTextureFormats::Enum GetDepthBufferFormat(); + NVRenderFrameBufferAttachments::Enum + GetFramebufferDepthAttachmentFormat(NVRenderTextureFormats::Enum depthFormat); + + // Render this layer assuming viewport and RT are setup. Just renders exactly this item + // no effects. + void RenderDepthPass(bool inEnableTransparentDepthWrite = false); + void RenderAoPass(); + void RenderFakeDepthMapPass(NVRenderTexture2D *theDepthTex, + NVRenderTextureCube *theDepthCube); + void RenderShadowMapPass(CResourceFrameBuffer *theFB); + void RenderShadowCubeBlurPass(CResourceFrameBuffer *theFB, NVRenderTextureCube *target0, + NVRenderTextureCube *target1, QT3DSF32 filterSz, QT3DSF32 clipFar); + void RenderShadowMapBlurPass(CResourceFrameBuffer *theFB, NVRenderTexture2D *target0, + NVRenderTexture2D *target1, QT3DSF32 filterSz, QT3DSF32 clipFar); + + void Render(CResourceFrameBuffer *theFB = NULL); + void ResetForFrame() override; + + void CreateGpuProfiler(); + void StartProfiling(CRegisteredString &nameID, bool sync); + void EndProfiling(CRegisteredString &nameID); + void StartProfiling(const char *nameID, bool sync); + void EndProfiling(const char *nameID); + void AddVertexCount(QT3DSU32 count); + + void RenderToViewport(); + // Render this layer's data to a texture. Required if we have any effects, + // prog AA, or if forced. + void RenderToTexture(); + + void ApplyLayerPostEffects(); + + void RunnableRenderToViewport(qt3ds::render::NVRenderFrameBuffer *theFB); + + void AddLayerRenderStep(); + + void RenderRenderWidgets(); + +#ifdef ADVANCED_BLEND_SW_FALLBACK + void BlendAdvancedEquationSwFallback(NVRenderTexture2D *drawTexture, + NVRenderTexture2D *m_LayerTexture, + AdvancedBlendModes::Enum blendMode); +#endif + // test method to render this layer to a given view projection without running the entire + // layer setup system. This assumes the client has setup the viewport, scissor, and render + // target + // the way they want them. + void PrepareAndRender(const QT3DSMat44 &inViewProjection); + + SOffscreenRendererEnvironment CreateOffscreenRenderEnvironment() override; + IRenderTask &CreateRenderToTextureRunnable() override; + + void addRef(); + void release(); + + protected: + // Used for both the normal passes and the depth pass. + // When doing the depth pass, we disable blending completely because it does not really make + // sense + // to write blend equations into + void RunRenderPass(TRenderRenderableFunction renderFn, bool inEnableBlending, + bool inEnableDepthWrite, bool inEnableTransparentDepthWrite, + QT3DSU32 indexLight, const SCamera &inCamera, + CResourceFrameBuffer *theFB = NULL); +#ifdef ADVANCED_BLEND_SW_FALLBACK + //Functions for advanced blending mode fallback + void SetupDrawFB(bool depthEnabled); + void BlendAdvancedToFB(DefaultMaterialBlendMode::Enum blendMode, bool depthEnabled, + CResourceFrameBuffer *theFB); +#endif + }; +} +} +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp new file mode 100644 index 0000000..07ee513 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** 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 "Qt3DSRendererImplLayerRenderHelper.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSTextRenderer.h" + +using namespace qt3ds::render; + +namespace { +// left/top +QT3DSF32 GetMinValue(QT3DSF32 start, QT3DSF32 width, QT3DSF32 value, LayerUnitTypes::Enum units) +{ + if (units == LayerUnitTypes::Pixels) + return start + value; + + return start + (value * width / 100.0f); +} + +// width/height +QT3DSF32 GetValueLen(QT3DSF32 width, QT3DSF32 value, LayerUnitTypes::Enum units) +{ + if (units == LayerUnitTypes::Pixels) + return value; + + return width * value / 100.0f; +} + +// right/bottom +QT3DSF32 GetMaxValue(QT3DSF32 start, QT3DSF32 width, QT3DSF32 value, LayerUnitTypes::Enum units) +{ + if (units == LayerUnitTypes::Pixels) + return start + width - value; + + return start + width - (value * width / 100.0f); +} + +QT3DSVec2 ToRectRelativeCoords(const QT3DSVec2 &inCoords, const NVRenderRectF &inRect) +{ + return QT3DSVec2(inCoords.x - inRect.m_X, inCoords.y - inRect.m_Y); +} +} + +SLayerRenderHelper::SLayerRenderHelper() + : m_Layer(NULL) + , m_Camera(NULL) + , m_Offscreen(false) +{ +} + +SLayerRenderHelper::SLayerRenderHelper(const NVRenderRectF &inPresentationViewport, + const NVRenderRectF &inPresentationScissor, + const QT3DSVec2 &inPresentationDesignDimensions, + SLayer &inLayer, bool inOffscreen, + qt3ds::render::ScaleModes::Enum inScaleMode, + qt3ds::QT3DSVec2 inScaleFactor) + : m_PresentationViewport(inPresentationViewport) + , m_PresentationScissor(inPresentationScissor) + , m_PresentationDesignDimensions(inPresentationDesignDimensions) + , m_Layer(&inLayer) + , m_Offscreen(inOffscreen) + , m_ScaleMode(inScaleMode) + , m_ScaleFactor(inScaleFactor) +{ + { + QT3DSF32 left = m_Layer->m_Left; + QT3DSF32 right = m_Layer->m_Right; + QT3DSF32 width = m_Layer->m_Width; + + if (m_ScaleMode == qt3ds::render::ScaleModes::FitSelected) { + if (m_Layer->m_LeftUnits == LayerUnitTypes::Pixels) + left *= m_ScaleFactor.x; + + if (m_Layer->m_RightUnits == LayerUnitTypes::Pixels) + right *= m_ScaleFactor.x; + + if (m_Layer->m_WidthUnits == LayerUnitTypes::Pixels) + width *= m_ScaleFactor.x; + } + + QT3DSF32 horzMin = GetMinValue(inPresentationViewport.m_X, inPresentationViewport.m_Width, + left, m_Layer->m_LeftUnits); + QT3DSF32 horzWidth = GetValueLen(inPresentationViewport.m_Width, width, m_Layer->m_WidthUnits); + QT3DSF32 horzMax = GetMaxValue(inPresentationViewport.m_X, inPresentationViewport.m_Width, + right, m_Layer->m_RightUnits); + + switch (inLayer.m_HorizontalFieldValues) { + case HorizontalFieldValues::LeftWidth: + m_Viewport.m_X = horzMin; + m_Viewport.m_Width = horzWidth; + break; + case HorizontalFieldValues::LeftRight: + m_Viewport.m_X = horzMin; + m_Viewport.m_Width = horzMax - horzMin; + break; + case HorizontalFieldValues::WidthRight: + m_Viewport.m_Width = horzWidth; + m_Viewport.m_X = horzMax - horzWidth; + break; + } + } + { + QT3DSF32 top = m_Layer->m_Top; + QT3DSF32 bottom = m_Layer->m_Bottom; + QT3DSF32 height = m_Layer->m_Height; + + if (m_ScaleMode == qt3ds::render::ScaleModes::FitSelected) { + + if (m_Layer->m_TopUnits == LayerUnitTypes::Pixels) + top *= m_ScaleFactor.y; + + if (m_Layer->m_BottomUnits == LayerUnitTypes::Pixels) + bottom *= m_ScaleFactor.y; + + if (m_Layer->m_HeightUnits == LayerUnitTypes::Pixels) + height *= m_ScaleFactor.y; + } + + QT3DSF32 vertMin = GetMinValue(inPresentationViewport.m_Y, inPresentationViewport.m_Height, + bottom, m_Layer->m_BottomUnits); + QT3DSF32 vertWidth = + GetValueLen(inPresentationViewport.m_Height, height, m_Layer->m_HeightUnits); + QT3DSF32 vertMax = GetMaxValue(inPresentationViewport.m_Y, inPresentationViewport.m_Height, + top, m_Layer->m_TopUnits); + + switch (inLayer.m_VerticalFieldValues) { + case VerticalFieldValues::HeightBottom: + m_Viewport.m_Y = vertMin; + m_Viewport.m_Height = vertWidth; + break; + case VerticalFieldValues::TopBottom: + m_Viewport.m_Y = vertMin; + m_Viewport.m_Height = vertMax - vertMin; + break; + case VerticalFieldValues::TopHeight: + m_Viewport.m_Height = vertWidth; + m_Viewport.m_Y = vertMax - vertWidth; + break; + } + } + + m_Viewport.m_Width = NVMax(1.0f, m_Viewport.m_Width); + m_Viewport.m_Height = NVMax(1.0f, m_Viewport.m_Height); + // Now force the viewport to be a multiple of four in width and height. This is because + // when rendering to a texture we have to respect this and not forcing it causes scaling issues + // that are noticeable especially in situations where customers are using text and such. + QT3DSF32 originalWidth = m_Viewport.m_Width; + QT3DSF32 originalHeight = m_Viewport.m_Height; + + m_Viewport.m_Width = (QT3DSF32)ITextRenderer::NextMultipleOf4((QT3DSU32)m_Viewport.m_Width); + m_Viewport.m_Height = (QT3DSF32)ITextRenderer::NextMultipleOf4((QT3DSU32)m_Viewport.m_Height); + + // Now fudge the offsets to account for this slight difference + m_Viewport.m_X += (originalWidth - m_Viewport.m_Width) / 2.0f; + m_Viewport.m_Y += (originalHeight - m_Viewport.m_Height) / 2.0f; + + m_Scissor = m_Viewport; + m_Scissor.EnsureInBounds(inPresentationScissor); + QT3DS_ASSERT(m_Scissor.m_Width >= 0.0f); + QT3DS_ASSERT(m_Scissor.m_Height >= 0.0f); +} + +// This is the viewport the camera will use to setup the projection. +NVRenderRectF SLayerRenderHelper::GetLayerRenderViewport() const +{ + if (m_Offscreen) + return NVRenderRectF(0, 0, m_Viewport.m_Width, (QT3DSF32)m_Viewport.m_Height); + else + return m_Viewport; +} + +QSize SLayerRenderHelper::GetTextureDimensions() const +{ + QT3DSU32 width = (QT3DSU32)m_Viewport.m_Width; + QT3DSU32 height = (QT3DSU32)m_Viewport.m_Height; + return QSize(ITextRenderer::NextMultipleOf4(width), + ITextRenderer::NextMultipleOf4(height)); +} + +SCameraGlobalCalculationResult SLayerRenderHelper::SetupCameraForRender(SCamera &inCamera) +{ + m_Camera = &inCamera; + NVRenderRectF rect = GetLayerRenderViewport(); + if (m_ScaleMode == ScaleModes::FitSelected) { + rect.m_Width = + (QT3DSF32)(ITextRenderer::NextMultipleOf4((QT3DSU32)(rect.m_Width / m_ScaleFactor.x))); + rect.m_Height = + (QT3DSF32)(ITextRenderer::NextMultipleOf4((QT3DSU32)(rect.m_Height / m_ScaleFactor.y))); + } + return m_Camera->CalculateGlobalVariables(rect, m_PresentationDesignDimensions); +} + +Option<QT3DSVec2> SLayerRenderHelper::GetLayerMouseCoords(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect) const +{ + // First invert the y so we are dealing with numbers in a normal coordinate space. + // Second, move into our layer's coordinate space + QT3DSVec2 correctCoords(inMouseCoords.x, inWindowDimensions.y - inMouseCoords.y); + QT3DSVec2 theLocalMouse = m_Viewport.ToRectRelative(correctCoords); + + QT3DSF32 theRenderRectWidth = m_Viewport.m_Width; + QT3DSF32 theRenderRectHeight = m_Viewport.m_Height; + // Crop the mouse to the rect. Apply no further translations. + if (inForceIntersect == false + && (theLocalMouse.x < 0.0f || theLocalMouse.x >= theRenderRectWidth + || theLocalMouse.y < 0.0f || theLocalMouse.y >= theRenderRectHeight)) { + return Empty(); + } + return theLocalMouse; +} + +Option<SRay> SLayerRenderHelper::GetPickRay(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect, + bool sceneCameraView) const +{ + if (m_Camera == NULL) + return Empty(); + Option<QT3DSVec2> theCoords( + GetLayerMouseCoords(inMouseCoords, inWindowDimensions, inForceIntersect)); + if (theCoords.hasValue()) { + // The cameras projection is different if we are onscreen vs. offscreen. + // When offscreen, we need to move the mouse coordinates into a local space + // to the layer. + return m_Camera->Unproject(*theCoords, m_Viewport, m_PresentationDesignDimensions, + sceneCameraView); + } + return Empty(); +} + +bool SLayerRenderHelper::IsLayerVisible() const +{ + return m_Scissor.m_Height >= 2.0f && m_Scissor.m_Width >= 2.0f; +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h new file mode 100644 index 0000000..616ebe1 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderHelper.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#include "Qt3DSRender.h" +#include "foundation/Qt3DSVec2.h" +#include "render/Qt3DSRenderBaseTypes.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderContextCore.h" + +namespace qt3ds { +namespace render { + + /** An independent, testable entity to encapsulate taking at least: + * layer, current viewport rect, current scissor rect, presentation design dimensions + * and producing a set of rectangles: + * layer viewport rect (inside viewport rect and calculated using outer viewport rect info) + * layer scissor rect (inside current scissor rect) + * layer camera rect (may be the viewport rect) + * + * In the case where we have to render offscreen for this layer then we need to handle produce + * a set of texture dimensions and the layer camera rect ends up being same size but with no + *offsets. + * + * This object should handle part of any translation from screenspace to global space. + * I am using language level access control on this object because it needs specific + * interface design that will enable future modifications. + */ + struct SLayerRenderHelper + { + private: + NVRenderRectF m_PresentationViewport; + NVRenderRectF m_PresentationScissor; + QT3DSVec2 m_PresentationDesignDimensions; + SLayer *m_Layer; + SCamera *m_Camera; + bool m_Offscreen; + + NVRenderRectF m_Viewport; + NVRenderRectF m_Scissor; + + ScaleModes::Enum m_ScaleMode; + QT3DSVec2 m_ScaleFactor; + + public: + SLayerRenderHelper(); + + SLayerRenderHelper(const NVRenderRectF &inPresentationViewport, + const NVRenderRectF &inPresentationScissor, + const QT3DSVec2 &inPresentationDesignDimensions, SLayer &inLayer, + bool inOffscreen, qt3ds::render::ScaleModes::Enum inScaleMode, + qt3ds::QT3DSVec2 inScaleFactor); + + NVRenderRectF GetPresentationViewport() const { return m_PresentationViewport; } + NVRenderRectF GetPresentationScissor() const { return m_PresentationScissor; } + QT3DSVec2 GetPresentationDesignDimensions() const { return m_PresentationDesignDimensions; } + SLayer *GetLayer() const { return m_Layer; } + SCamera *GetCamera() const { return m_Camera; } + bool IsOffscreen() const { return m_Offscreen; } + + // Does not differ whether offscreen or not, simply states how this layer maps to the + // presentation + NVRenderRectF GetLayerToPresentationViewport() const { return m_Viewport; } + // Does not differ whether offscreen or not, scissor rect of how this layer maps to + // presentation. + NVRenderRectF GetLayerToPresentationScissorRect() const { return m_Scissor; } + + QSize GetTextureDimensions() const; + + SCameraGlobalCalculationResult SetupCameraForRender(SCamera &inCamera); + + Option<QT3DSVec2> GetLayerMouseCoords(const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect) const; + + Option<SRay> GetPickRay(const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inWindowDimensions, + bool inForceIntersect, bool sceneCameraView = false) const; + + // Checks the various viewports and determines if the layer is visible or not. + bool IsLayerVisible() const; + + private: + // Viewport used when actually rendering. In the case where this is an offscreen item then + // it may be + // different than the layer to presentation viewport. + NVRenderRectF GetLayerRenderViewport() const; + }; +} +} 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(); + } +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h new file mode 100644 index 0000000..5b8d6e1 --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_IMPL_LAYER_RENDER_PREPARATION_DATA_H +#define QT3DS_RENDERER_IMPL_LAYER_RENDER_PREPARATION_DATA_H +#include "Qt3DSRender.h" +#include "foundation/Qt3DSFlags.h" +#include "Qt3DSRendererImplLayerRenderHelper.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderableObjects.h" +#include "Qt3DSRenderClippingFrustum.h" +#include "Qt3DSRenderResourceTexture2D.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRenderProfiler.h" +#include "Qt3DSRenderShadowMap.h" +#include "foundation/Qt3DSPool.h" +#include "Qt3DSRenderableObjects.h" + +namespace qt3ds { +namespace render { + struct SLayerRenderData; + class Qt3DSRendererImpl; + struct SRenderableObject; + + struct LayerRenderPreparationResultFlagValues + { + enum Enum { + // Was the data in this layer dirty (meaning re-render to texture, possibly) + WasLayerDataDirty = 1, + // Was the data in this layer dirty *or* this layer *or* any effect dirty. + WasDirty = 1 << 1, + // An effect or flag or rotation on the layer dictates this object should + // render to the texture. + ShouldRenderToTexture = 1 << 2, + // Some effects require depth texturing, this should be set on the effect + // instance. + RequiresDepthTexture = 1 << 3, + + // Should create independent viewport + // If we aren't rendering to texture we still may have width/height manipulations + // that require our own viewport. + ShouldCreateIndependentViewport = 1 << 4, + + // SSAO should be done in a separate pass + // Note that having an AO pass necessitates a DepthTexture so this flag should + // never be set without the RequiresDepthTexture flag as well. + RequiresSsaoPass = 1 << 5, + + // if some light cause shadow + // we need a separate per light shadow map pass + RequiresShadowMapPass = 1 << 6, + + // Currently we use a stencil-cover algorithm to render bezier curves. + RequiresStencilBuffer = 1 << 7 + }; + }; + + struct SLayerRenderPreparationResultFlags + : public NVFlags<LayerRenderPreparationResultFlagValues::Enum, QT3DSU32> + { + bool WasLayerDataDirty() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::WasLayerDataDirty); + } + void SetLayerDataDirty(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::WasLayerDataDirty); + } + + bool WasDirty() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::WasDirty); + } + void SetWasDirty(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::WasDirty); + } + + bool ShouldRenderToTexture() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::ShouldRenderToTexture); + } + void SetShouldRenderToTexture(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::ShouldRenderToTexture); + } + + bool RequiresDepthTexture() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresDepthTexture); + } + void SetRequiresDepthTexture(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresDepthTexture); + } + + bool ShouldCreateIndependentViewport() const + { + return this->operator&( + LayerRenderPreparationResultFlagValues::ShouldCreateIndependentViewport); + } + void SetShouldCreateIndependentViewport(bool inValue) + { + clearOrSet(inValue, + LayerRenderPreparationResultFlagValues::ShouldCreateIndependentViewport); + } + + bool RequiresSsaoPass() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresSsaoPass); + } + void SetRequiresSsaoPass(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresSsaoPass); + } + + bool RequiresShadowMapPass() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresShadowMapPass); + } + void SetRequiresShadowMapPass(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresShadowMapPass); + } + + bool RequiresStencilBuffer() const + { + return this->operator&(LayerRenderPreparationResultFlagValues::RequiresStencilBuffer); + } + void SetRequiresStencilBuffer(bool inValue) + { + clearOrSet(inValue, LayerRenderPreparationResultFlagValues::RequiresStencilBuffer); + } + }; + + struct SLayerRenderPreparationResult : public SLayerRenderHelper + { + SEffect *m_LastEffect; + SLayerRenderPreparationResultFlags m_Flags; + QT3DSU32 m_MaxAAPassIndex; + SLayerRenderPreparationResult() + : m_LastEffect(NULL) + , m_MaxAAPassIndex(0) + { + } + SLayerRenderPreparationResult(const SLayerRenderHelper &inHelper) + : SLayerRenderHelper(inHelper) + , m_LastEffect(NULL) + , m_MaxAAPassIndex(0) + { + } + }; + + struct SRenderableNodeEntry + { + SNode *m_Node; + TNodeLightEntryList m_Lights; + SRenderableNodeEntry() + : m_Node(NULL) + { + } + SRenderableNodeEntry(SNode &inNode) + : m_Node(&inNode) + { + } + }; + + struct SScopedLightsListScope + { + nvvector<SLight *> &m_LightsList; + nvvector<QT3DSVec3> &m_LightDirList; + QT3DSU32 m_ListOriginalSize; + SScopedLightsListScope(nvvector<SLight *> &inLights, nvvector<QT3DSVec3> &inDestLightDirList, + nvvector<QT3DSVec3> &inSrcLightDirList, + TNodeLightEntryList &inScopedLights) + : m_LightsList(inLights) + , m_LightDirList(inDestLightDirList) + , m_ListOriginalSize(m_LightsList.size()) + { + for (TNodeLightEntryList::iterator iter = inScopedLights.begin(), + end = inScopedLights.end(); + iter != end; ++iter) { + m_LightsList.push_back(iter->m_Light); + m_LightDirList.push_back(inSrcLightDirList[iter->m_LightIndex]); + } + } + ~SScopedLightsListScope() + { + m_LightsList.resize(m_ListOriginalSize); + m_LightDirList.resize(m_ListOriginalSize); + } + }; + + struct SDefaultMaterialPreparationResult + { + SRenderableImage *m_FirstImage; + QT3DSF32 m_Opacity; + SRenderableObjectFlags m_RenderableFlags; + SShaderDefaultMaterialKey m_MaterialKey; + bool m_Dirty; + + SDefaultMaterialPreparationResult(SShaderDefaultMaterialKey inMaterialKey); + }; + + // Data used strictly in the render preparation step. + struct SLayerRenderPreparationData + { + typedef void (*TRenderRenderableFunction)(SLayerRenderData &inData, + SRenderableObject &inObject, + const QT3DSVec2 &inCameraProps, + TShaderFeatureSet inShaderFeatures, + QT3DSU32 lightIndex, const SCamera &inCamera); + typedef nvhash_map<SLight *, SNode *> TLightToNodeMap; + typedef Pool<SNodeLightEntry, ForwardingAllocator> TNodeLightEntryPoolType; + + enum Enum { + MAX_AA_LEVELS = 8, + MAX_TEMPORAL_AA_LEVELS = 2, + }; + + SLayer &m_Layer; + Qt3DSRendererImpl &m_Renderer; + NVAllocatorCallback &m_Allocator; + // List of nodes we can render, not all may be active. Found by doing a depth-first + // search through m_FirstChild if length is zero. + + TNodeLightEntryPoolType m_RenderableNodeLightEntryPool; + nvvector<SRenderableNodeEntry> m_RenderableNodes; + TLightToNodeMap m_LightToNodeMap; // map of lights to nodes to cache if we have looked up a + // given scoped light yet. + // Built at the same time as the renderable nodes map. + // these are processed so they are available when the shaders for the models + // are being generated. + nvvector<SNode *> m_CamerasAndLights; + + // Results of prepare for render. + SCamera *m_Camera; + nvvector<SLight *> m_Lights; // Only contains lights that are global. + TRenderableObjectList m_OpaqueObjects; + TRenderableObjectList m_TransparentObjects; + // Sorted lists of the rendered objects. There may be other transforms applied so + // it is simplest to duplicate the lists. + TRenderableObjectList m_RenderedOpaqueObjects; + TRenderableObjectList m_RenderedTransparentObjects; + QT3DSMat44 m_ViewProjection; + SClippingFrustum m_ClippingFrustum; + Option<SLayerRenderPreparationResult> m_LayerPrepResult; + // Widgets drawn at particular times during the rendering process + nvvector<IRenderWidget *> m_IRenderWidgets; + Option<QT3DSVec3> m_CameraDirection; + // Scoped lights need a level of indirection into a light direction list. The source light + // directions list is as long as there are lights on the layer. It holds invalid + // information for + // any lights that are not both active and scoped; but the relative position for a given + // light + // in this list is completely constant and immutable; this relative position is saved on a + // structure + // and used when looking up the light direction for a given light. + nvvector<QT3DSVec3> m_SourceLightDirections; + nvvector<QT3DSVec3> m_LightDirections; + TModelContextPtrList m_ModelContexts; + NVScopedRefCounted<IOffscreenRenderer> m_LastFrameOffscreenRenderer; + + eastl::vector<SShaderPreprocessorFeature> m_Features; + CRegisteredString m_CGLightingFeatureName; + bool m_FeaturesDirty; + size_t m_FeatureSetHash; + bool m_TooManyLightsError; + + // shadow mapps + NVScopedRefCounted<Qt3DSShadowMap> m_ShadowMapManager; + + SLayerRenderPreparationData(SLayer &inLayer, Qt3DSRendererImpl &inRenderer); + virtual ~SLayerRenderPreparationData(); + bool GetOffscreenRenderer(); + bool GetShadowMapManager(); + bool NeedsWidgetTexture() const; + + SShaderDefaultMaterialKey GenerateLightingKey(DefaultMaterialLighting::Enum inLightingType); + + void PrepareImageForRender(SImage &inImage, ImageMapTypes::Enum inMapType, + SRenderableImage *&ioFirstImage, SRenderableImage *&ioNextImage, + SRenderableObjectFlags &ioFlags, + SShaderDefaultMaterialKey &ioGeneratedShaderKey, + QT3DSU32 inImageIndex); + + SDefaultMaterialPreparationResult + PrepareDefaultMaterialForRender(SDefaultMaterial &inMaterial, + SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity, + bool inClearMaterialFlags); + + SDefaultMaterialPreparationResult + PrepareCustomMaterialForRender(SCustomMaterial &inMaterial, + SRenderableObjectFlags &inExistingFlags, QT3DSF32 inOpacity); + + bool PrepareModelForRender(SModel &inModel, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, + TNodeLightEntryList &inScopedLights); + + bool PrepareTextForRender(SText &inText, const QT3DSMat44 &inViewProjection, + QT3DSF32 inTextScaleFactor, + SLayerRenderPreparationResultFlags &ioFlags); + bool PreparePathForRender(SPath &inPath, const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, + SLayerRenderPreparationResultFlags &ioFlags); + // Helper function used during PRepareForRender and PrepareAndRender + bool PrepareRenderablesForRender(const QT3DSMat44 &inViewProjection, + const Option<SClippingFrustum> &inClipFrustum, + QT3DSF32 inTextScaleFactor, + SLayerRenderPreparationResultFlags &ioFlags); + + // returns true if this object will render something different than it rendered the last + // time. + virtual void PrepareForRender(const QSize &inViewportDimensions); + bool CheckLightProbeDirty(SImage &inLightProbe); + void AddRenderWidget(IRenderWidget &inWidget); + void SetShaderFeature(const char *inName, bool inValue); + void SetShaderFeature(CRegisteredString inName, bool inValue); + NVConstDataRef<SShaderPreprocessorFeature> GetShaderFeatureSet(); + size_t GetShaderFeatureSetHash(); + // The graph object is not const because this traversal updates dirty state on the objects. + eastl::pair<bool, SGraphObject *> ResolveReferenceMaterial(SGraphObject *inMaterial); + + QT3DSVec3 GetCameraDirection(); + // Per-frame cache of renderable objects post-sort. + NVDataRef<SRenderableObject *> GetOpaqueRenderableObjects(); + // If layer depth test is false, this may also contain opaque objects. + NVDataRef<SRenderableObject *> GetTransparentRenderableObjects(); + + virtual void ResetForFrame(); + + // The render list and gl context are setup for what the embedded item will + // need. + virtual SOffscreenRendererEnvironment CreateOffscreenRenderEnvironment() = 0; + + virtual IRenderTask &CreateRenderToTextureRunnable() = 0; + }; +} +} +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp new file mode 100644 index 0000000..a3d3fab --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.cpp @@ -0,0 +1,3007 @@ +/**************************************************************************** +** +** 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 "Qt3DSRendererImpl.h" +#include "foundation/Qt3DSFoundation.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCache.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "Qt3DSVertexPipelineImpl.h" + +// This adds support for the depth buffers in the shader so we can do depth +// texture-based effects. +#define QT3DS_RENDER_SUPPORT_DEPTH_TEXTURE 1 + +namespace qt3ds { +namespace render { + + void STextShader::Render(NVRenderTexture2D &inTexture, + const STextScaleAndOffset &inScaleAndOffset, const QT3DSVec4 &inTextColor, + const QT3DSMat44 &inMVP, const QT3DSVec2 &inCameraVec, + NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor) + { + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(&m_Shader); + m_MVP.Set(inMVP); + m_Sampler.Set(&inTexture); + m_TextColor.Set(inTextColor); + m_Dimensions.Set(QT3DSVec4(inScaleAndOffset.m_TextScale.x, inScaleAndOffset.m_TextScale.y, + inScaleAndOffset.m_TextOffset.x, inScaleAndOffset.m_TextOffset.y)); + m_CameraProperties.Set(inCameraVec); + STextureDetails theTextureDetails = inTexture.GetTextureDetails(); + QT3DSF32 theWidthScale = + (QT3DSF32)inTextTextureDetails.m_TextWidth / (QT3DSF32)theTextureDetails.m_Width; + QT3DSF32 theHeightScale = + (QT3DSF32)inTextTextureDetails.m_TextHeight / (QT3DSF32)theTextureDetails.m_Height; + m_BackgroundColor.Set(inBackgroundColor); + + m_TextDimensions.Set( + QT3DSVec3(theWidthScale, theHeightScale, inTextTextureDetails.m_FlipY ? 1.0f : 0.0f)); + inRenderContext.SetInputAssembler(&inInputAssemblerBuffer); + inRenderContext.Draw(NVRenderDrawMode::Triangles, count, 0); + } + + void STextShader::RenderPath(NVRenderPathFontItem &inPathFontItem, + NVRenderPathFontSpecification &inPathFontSpec, + const STextScaleAndOffset &inScaleAndOffset, + const QT3DSVec4 &inTextColor, const QT3DSMat44 &inViewProjection, + const QT3DSMat44 &inModel, const QT3DSVec2 &, + NVRenderContext &inRenderContext, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor) + { + qt3ds::render::NVRenderBoolOp::Enum theDepthFunction = inRenderContext.GetDepthFunction(); + bool isDepthEnabled = inRenderContext.IsDepthTestEnabled(); + bool isStencilEnabled = inRenderContext.IsStencilTestEnabled(); + bool isDepthWriteEnabled = inRenderContext.IsDepthWriteEnabled(); + qt3ds::render::NVRenderStencilFunctionArgument theArg(qt3ds::render::NVRenderBoolOp::NotEqual, 0, + 0xFF); + qt3ds::render::NVRenderStencilOperationArgument theOpArg(qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Keep, + qt3ds::render::NVRenderStencilOp::Zero); + NVScopedRefCounted<NVRenderDepthStencilState> depthStencilState = + inRenderContext.CreateDepthStencilState(isDepthEnabled, isDepthWriteEnabled, + theDepthFunction, false, theArg, theArg, + theOpArg, theOpArg); + + inRenderContext.SetActiveShader(NULL); + inRenderContext.SetCullingEnabled(false); + + inRenderContext.SetDepthStencilState(depthStencilState); + + // setup transform + QT3DSMat44 offsetMatrix = QT3DSMat44::createIdentity(); + offsetMatrix.setPosition(QT3DSVec3( + inScaleAndOffset.m_TextOffset.x - (QT3DSF32)inTextTextureDetails.m_TextWidth / 2.0f, + inScaleAndOffset.m_TextOffset.y - (QT3DSF32)inTextTextureDetails.m_TextHeight / 2.0f, + 0.0)); + + QT3DSMat44 pathMatrix = inPathFontItem.GetTransform(); + + inRenderContext.SetPathProjectionMatrix(inViewProjection); + inRenderContext.SetPathModelViewMatrix(inModel * offsetMatrix * pathMatrix); + + // first pass + inPathFontSpec.StencilFillPathInstanced(inPathFontItem); + + // second pass + inRenderContext.SetActiveProgramPipeline(m_ProgramPipeline); + m_TextColor.Set(inTextColor); + m_BackgroundColor.Set(inBackgroundColor); + + inRenderContext.SetStencilTestEnabled(true); + inPathFontSpec.CoverFillPathInstanced(inPathFontItem); + + inRenderContext.SetStencilTestEnabled(isStencilEnabled); + inRenderContext.SetDepthFunction(theDepthFunction); + + inRenderContext.SetActiveProgramPipeline(NULL); + } + + void STextShader::Render2D(NVRenderTexture2D &inTexture, const QT3DSVec4 &inTextColor, + const QT3DSMat44 &inMVP, NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + QT3DSVec2 inVertexOffsets) + { + // inRenderContext.SetCullingEnabled( false ); + inRenderContext.SetBlendingEnabled(true); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + + inRenderContext.SetActiveShader(&m_Shader); + + qt3ds::render::NVRenderBlendFunctionArgument blendFunc( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add, + NVRenderBlendEquation::Add); + + inRenderContext.SetBlendFunction(blendFunc); + inRenderContext.SetBlendEquation(blendEqu); + + m_MVP.Set(inMVP); + m_Sampler.Set(&inTexture); + m_TextColor.Set(inTextColor); + m_VertexOffsets.Set(inVertexOffsets); + + inRenderContext.SetInputAssembler(&inInputAssemblerBuffer); + inRenderContext.Draw(NVRenderDrawMode::Triangles, count, 0); + } + + using eastl::make_pair; + + static inline void AddVertexDepth(SShaderVertexCodeGenerator &vertexShader) + { + // near plane, far plane + vertexShader.AddInclude("viewProperties.glsllib"); + vertexShader.AddVarying("vertex_depth", "float"); + // the w coordinate is the unormalized distance to the object from the camera + // We want the normalized distance, with 0 representing the far plane and 1 representing + // the near plane, of the object in the vertex depth variable. + + vertexShader << "\tvertex_depth = calculateVertexDepth( camera_properties, gl_Position );" + << Endl; + } + + // Helper implements the vertex pipeline for mesh subsets when bound to the default material. + // Should be completely possible to use for custom materials with a bit of refactoring. + struct SSubsetMaterialVertexPipeline : public SVertexPipelineImpl + { + Qt3DSRendererImpl &m_Renderer; + SSubsetRenderable &m_Renderable; + TessModeValues::Enum m_TessMode; + + SSubsetMaterialVertexPipeline(Qt3DSRendererImpl &renderer, SSubsetRenderable &renderable, + bool inWireframeRequested) + : SVertexPipelineImpl(renderer.GetQt3DSContext().GetAllocator(), + renderer.GetQt3DSContext().GetDefaultMaterialShaderGenerator(), + renderer.GetQt3DSContext().GetShaderProgramGenerator(), + renderer.GetQt3DSContext().GetStringTable(), false) + , m_Renderer(renderer) + , m_Renderable(renderable) + , m_TessMode(TessModeValues::NoTess) + { + if (m_Renderer.GetContext().IsTessellationSupported()) { + m_TessMode = renderable.m_TessellationMode; + } + + if (m_Renderer.GetContext().IsGeometryStageSupported() + && m_TessMode != TessModeValues::NoTess) + m_Wireframe = inWireframeRequested; + } + + void InitializeTessControlShader() + { + if (m_TessMode == TessModeValues::NoTess + || ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl) == NULL) + return; + + IShaderStageGenerator &tessCtrlShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + + SetupTessIncludes(ShaderGeneratorStages::TessControl, m_TessMode); + + tessCtrlShader.Append("void main() {\n"); + + tessCtrlShader.Append("\tctWorldPos[0] = varWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = varWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = varWorldPos[2];"); + + if (m_TessMode == TessModeValues::TessPhong + || m_TessMode == TessModeValues::TessNPatch) { + tessCtrlShader.Append("\tctNorm[0] = varObjectNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = varObjectNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = varObjectNormal[2];"); + } + if (m_TessMode == TessModeValues::TessNPatch) { + tessCtrlShader.Append("\tctTangent[0] = varTangent[0];"); + tessCtrlShader.Append("\tctTangent[1] = varTangent[1];"); + tessCtrlShader.Append("\tctTangent[2] = varTangent[2];"); + } + + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + } + void InitializeTessEvaluationShader() + { + if (m_TessMode == TessModeValues::NoTess + || ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval) == NULL) + return; + + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + SetupTessIncludes(ShaderGeneratorStages::TessEval, m_TessMode); + + if (m_TessMode == TessModeValues::TessLinear) + m_Renderer.GetQt3DSContext() + .GetDefaultMaterialShaderGenerator() + .AddDisplacementImageUniforms(tessEvalShader, m_DisplacementIdx, + m_DisplacementImage); + + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("normal_matrix", "mat3"); + + tessEvalShader.Append("void main() {"); + + if (m_TessMode == TessModeValues::TessNPatch) { + tessEvalShader.Append("\tctNorm[0] = varObjectNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = varObjectNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = varObjectNormalTC[2];"); + + tessEvalShader.Append("\tctTangent[0] = varTangentTC[0];"); + tessEvalShader.Append("\tctTangent[1] = varTangentTC[1];"); + tessEvalShader.Append("\tctTangent[2] = varTangentTC[2];"); + } + + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + } + + void FinalizeTessControlShader() + { + IShaderStageGenerator &tessCtrlShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + // add varyings we must pass through + typedef TStrTableStrMap::const_iterator TParamIter; + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessCtrlShader << "\t" << iter->first.c_str() + << "TC[gl_InvocationID] = " << iter->first.c_str() + << "[gl_InvocationID];\n"; + } + } + + void FinalizeTessEvaluationShader() + { + IShaderStageGenerator &tessEvalShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + eastl::string outExt(""); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) + outExt = "TE"; + + // add varyings we must pass through + typedef TStrTableStrMap::const_iterator TParamIter; + if (m_TessMode == TessModeValues::TessNPatch) { + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str() + << " = gl_TessCoord.z * " << iter->first.c_str() << "TC[0] + "; + tessEvalShader << "gl_TessCoord.x * " << iter->first.c_str() << "TC[1] + "; + tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[2];\n"; + } + + // transform the normal + if (m_GenerationFlags & GenerationFlagValues::WorldNormal) + tessEvalShader << "\n\tvarNormal" << outExt.c_str() + << " = normalize(normal_matrix * teNorm);\n"; + // transform the tangent + if (m_GenerationFlags & GenerationFlagValues::TangentBinormal) { + tessEvalShader << "\n\tvarTangent" << outExt.c_str() + << " = normalize(normal_matrix * teTangent);\n"; + // transform the binormal + tessEvalShader << "\n\tvarBinormal" << outExt.c_str() + << " = normalize(normal_matrix * teBinormal);\n"; + } + } else { + for (TParamIter iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str() + << " = gl_TessCoord.x * " << iter->first.c_str() << "TC[0] + "; + tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[1] + "; + tessEvalShader << "gl_TessCoord.z * " << iter->first.c_str() << "TC[2];\n"; + } + + // displacement mapping makes only sense with linear tessellation + if (m_TessMode == TessModeValues::TessLinear && m_DisplacementImage) { + IDefaultMaterialShaderGenerator::SImageVariableNames theNames = + m_Renderer.GetQt3DSContext() + .GetDefaultMaterialShaderGenerator() + .GetImageVariableNames(m_DisplacementIdx); + tessEvalShader << "\tpos.xyz = defaultMaterialFileDisplacementTexture( " + << theNames.m_ImageSampler << ", displaceAmount, " + << theNames.m_ImageFragCoords << outExt.c_str(); + tessEvalShader << ", varObjectNormal" << outExt.c_str() << ", pos.xyz );" + << Endl; + tessEvalShader << "\tvarWorldPos" << outExt.c_str() + << "= (model_matrix * pos).xyz;" << Endl; + tessEvalShader << "\tvarViewVector" << outExt.c_str() + << "= normalize(camera_position - " + << "varWorldPos" << outExt.c_str() << ");" << Endl; + } + + // transform the normal + tessEvalShader << "\n\tvarNormal" << outExt.c_str() + << " = normalize(normal_matrix * varObjectNormal" << outExt.c_str() + << ");\n"; + } + + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); + } + + void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override + { + m_DisplacementIdx = displacementImageIdx; + m_DisplacementImage = displacementImage; + + TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags()); + if (m_TessMode != TessModeValues::NoTess) { + theStages |= ShaderGeneratorStages::TessControl; + theStages |= ShaderGeneratorStages::TessEval; + } + if (m_Wireframe) { + theStages |= ShaderGeneratorStages::Geometry; + } + ProgramGenerator().BeginProgram(theStages); + if (m_TessMode != TessModeValues::NoTess) { + InitializeTessControlShader(); + InitializeTessEvaluationShader(); + } + if (m_Wireframe) { + InitializeWireframeGeometryShader(); + } + // Open up each stage. + IShaderStageGenerator &vertexShader(Vertex()); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader << "void main()" << Endl << "{" << Endl; + vertexShader << "\tvec3 uTransform;" << Endl; + vertexShader << "\tvec3 vTransform;" << Endl; + + if (displacementImage) { + GenerateUVCoords(); + MaterialGenerator().GenerateImageUVCoordinates(*this, displacementImageIdx, 0, + *displacementImage); + if (!HasTessellation()) { + vertexShader.AddUniform("displaceAmount", "float"); + // we create the world position setup here + // because it will be replaced with the displaced position + SetCode(GenerationFlagValues::WorldPosition); + vertexShader.AddUniform("model_matrix", "mat4"); + + vertexShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + IDefaultMaterialShaderGenerator::SImageVariableNames theVarNames = + MaterialGenerator().GetImageVariableNames(displacementImageIdx); + + vertexShader.AddUniform(theVarNames.m_ImageSampler, "sampler2D"); + + vertexShader + << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + << theVarNames.m_ImageSampler << ", displaceAmount, " + << theVarNames.m_ImageFragCoords << ", attr_norm, attr_pos );" << Endl; + AddInterpolationParameter("varWorldPos", "vec3"); + vertexShader.Append("\tvec3 local_model_world_position = (model_matrix * " + "vec4(displacedPos, 1.0)).xyz;"); + AssignOutput("varWorldPos", "local_model_world_position"); + } + } + // for tessellation we pass on the position in object coordinates + // Also note that gl_Position is written in the tess eval shader + if (HasTessellation()) + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + else { + vertexShader.AddUniform("model_view_projection", "mat4"); + if (displacementImage) + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + else + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + } + + if (HasTessellation()) { + GenerateWorldPosition(); + GenerateWorldNormal(); + GenerateObjectNormal(); + GenerateVarTangentAndBinormal(); + } + } + + void BeginFragmentGeneration() override + { + Fragment().AddUniform("material_diffuse", "vec4"); + Fragment() << "void main()" << Endl << "{" << Endl; + // We do not pass object opacity through the pipeline. + Fragment() << "\tfloat object_opacity = material_diffuse.a;" << Endl; + } + + void AssignOutput(const char8_t *inVarName, const char8_t *inVarValue) override + { + Vertex() << "\t" << inVarName << " = " << inVarValue << ";\n"; + } + void DoGenerateUVCoords(QT3DSU32 inUVSet = 0) override + { + QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1); + + if (inUVSet == 0) { + Vertex().AddIncoming("attr_uv0", "vec2"); + Vertex() << "\tvarTexCoord0 = attr_uv0;" << Endl; + } else if (inUVSet == 1) { + Vertex().AddIncoming("attr_uv1", "vec2"); + Vertex() << "\tvarTexCoord1 = attr_uv1;" << Endl; + } + } + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + void DoGenerateWorldNormal() override + { + IShaderStageGenerator &vertexGenerator(Vertex()); + vertexGenerator.AddIncoming("attr_norm", "vec3"); + vertexGenerator.AddUniform("normal_matrix", "mat3"); + if (HasTessellation() == false) { + vertexGenerator.Append( + "\tvec3 world_normal = normalize(normal_matrix * attr_norm).xyz;"); + vertexGenerator.Append("\tvarNormal = world_normal;"); + } + } + void DoGenerateObjectNormal() override + { + AddInterpolationParameter("varObjectNormal", "vec3"); + Vertex().Append("\tvarObjectNormal = attr_norm;"); + } + void DoGenerateWorldPosition() override + { + Vertex().Append( + "\tvec3 local_model_world_position = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + AssignOutput("varWorldPos", "local_model_world_position"); + } + + void DoGenerateVarTangentAndBinormal() override + { + Vertex().AddIncoming("attr_textan", "vec3"); + Vertex().AddIncoming("attr_binormal", "vec3"); + + bool hasNPatchTessellation = m_TessMode == TessModeValues::TessNPatch; + + if (!hasNPatchTessellation) { + Vertex() << "\tvarTangent = normal_matrix * attr_textan;" << Endl + << "\tvarBinormal = normal_matrix * attr_binormal;" << Endl; + } else { + Vertex() << "\tvarTangent = attr_textan;" << Endl + << "\tvarBinormal = attr_binormal;" << Endl; + } + } + + void DoGenerateVertexColor() override + { + Vertex().AddIncoming("attr_color", "vec3"); + Vertex().Append("\tvarColor = attr_color;"); + } + + void EndVertexGeneration(bool) override + { + + if (HasTessellation()) { + // finalize tess control shader + FinalizeTessControlShader(); + // finalize tess evaluation shader + FinalizeTessEvaluationShader(); + + TessControl().Append("}"); + TessEval().Append("}"); + } + if (m_Wireframe) { + // finalize geometry shader + FinalizeWireframeGeometryShader(); + Geometry().Append("}"); + } + Vertex().Append("}"); + } + + void EndFragmentGeneration(bool) override { Fragment().Append("}"); } + + void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override + { + m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType))); + Vertex().AddOutgoing(inName, inType); + Fragment().AddIncoming(inName, inType); + if (HasTessellation()) { + eastl::string nameBuilder(inName); + nameBuilder.append("TC"); + TessControl().AddOutgoing(nameBuilder.c_str(), inType); + + nameBuilder.assign(inName); + if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) { + nameBuilder.append("TE"); + Geometry().AddOutgoing(inName, inType); + } + TessEval().AddOutgoing(nameBuilder.c_str(), inType); + } + } + + IShaderStageGenerator &ActiveStage() override { return Vertex(); } + }; + + NVRenderShaderProgram *Qt3DSRendererImpl::GenerateShader(SSubsetRenderable &inRenderable, + TShaderFeatureSet inFeatureSet) + { + // build a string that allows us to print out the shader we are generating to the log. + // This is time consuming but I feel like it doesn't happen all that often and is very + // useful to users + // looking at the log file. + QLatin1String logPrefix("mesh subset pipeline-- "); + + m_GeneratedShaderString.clear(); + m_GeneratedShaderString.assign(logPrefix.data()); + + SShaderDefaultMaterialKey theKey(inRenderable.m_ShaderDescription); + theKey.ToString(m_GeneratedShaderString, m_DefaultMaterialShaderKeyProperties); + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr(m_GeneratedShaderString.c_str()); + NVRenderShaderProgram *cachedProgram = theCache.GetProgram(theCacheKey, inFeatureSet); + if (cachedProgram) + return cachedProgram; + + SSubsetMaterialVertexPipeline pipeline( + *this, inRenderable, + m_DefaultMaterialShaderKeyProperties.m_WireframeMode.GetValue(theKey)); + return m_qt3dsContext.GetDefaultMaterialShaderGenerator().GenerateShader( + inRenderable.m_Material, inRenderable.m_ShaderDescription, pipeline, inFeatureSet, + m_CurrentLayer->m_Lights, inRenderable.m_FirstImage, + inRenderable.m_RenderableFlags.HasTransparency(), + logPrefix.data()); + } + + // -------------- Special cases for shadows ------------------- + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetParaboloidDepthShader(TessModeValues::Enum inTessMode) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetParaboloidDepthNoTessShader(); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetParaboloidDepthTessLinearShader(); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetParaboloidDepthTessPhongShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetParaboloidDepthTessNPatchShader(); + } + + return GetParaboloidDepthNoTessShader(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthNoTessShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + IShaderProgramGenerator::OutputParaboloidDepthVertex(vertexShader); + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthTessLinearShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthTessLinearShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth tess linear shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + // vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + // vertexShader.Append("\tworld_pos = attr_pos;"); + vertexShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + // tessCtrlShader.AddOutgoing( "outUVTC", "vec2" ); + // tessCtrlShader.AddOutgoing( "outNormalTC", "vec3" ); + tessCtrlShader.Append("void main() {\n"); + // tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + // tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + // tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + IShaderProgramGenerator::OutputParaboloidDepthTessEval(tessEvalShader); + tessEvalShader.Append("}"); + + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthTessPhongShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthTessPhongShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth tess phong shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + // vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + // vertexShader.Append("\tworld_pos = attr_pos;"); + vertexShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + // tessCtrlShader.AddOutgoing( "outUVTC", "vec2" ); + // tessCtrlShader.AddOutgoing( "outNormalTC", "vec3" ); + tessCtrlShader.Append("void main() {\n"); + // tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + // tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + // tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + IShaderProgramGenerator::OutputParaboloidDepthTessEval(tessEvalShader); + tessEvalShader.Append("}"); + + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetParaboloidDepthTessNPatchShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_ParaboloidDepthTessNPatchShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("paraboloid depth tess NPatch shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + // vertexShader.AddOutgoing("world_pos", "vec4"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + // vertexShader.Append("\tworld_pos = attr_pos;"); + vertexShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + // tessCtrlShader.AddOutgoing( "outUVTC", "vec2" ); + // tessCtrlShader.AddOutgoing( "outNormalTC", "vec3" ); + tessCtrlShader.Append("void main() {\n"); + // tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + // tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + // tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + IShaderProgramGenerator::OutputParaboloidDepthTessEval(tessEvalShader); + tessEvalShader.Append("}"); + + IShaderProgramGenerator::OutputParaboloidDepthFragment(fragmentShader); + } + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetCubeShadowDepthShader(TessModeValues::Enum inTessMode) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetCubeDepthNoTessShader(); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetCubeDepthTessLinearShader(); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetCubeDepthTessPhongShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetCubeDepthTessNPatchShader(); + } + + return GetCubeDepthNoTessShader(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthNoTessShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + + IShaderProgramGenerator::OutputCubeFaceDepthVertex(vertexShader); + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthTessLinearShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthTessLinearShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth linear tess shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("}"); + + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tworld_pos = model_matrix * pos;"); + tessEvalShader.Append("\tworld_pos /= world_pos.w;"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthTessPhongShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthTessPhongShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth phong tess shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tworld_pos = model_matrix * pos;"); + tessEvalShader.Append("\tworld_pos /= world_pos.w;"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetCubeDepthTessNPatchShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_CubemapDepthTessNPatchShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("cubemap face depth npatch tess shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + + if (!depthShaderProgram) { + // GetProgramGenerator().BeginProgram( + // TShaderGeneratorStageFlags(ShaderGeneratorStages::Vertex | + // ShaderGeneratorStages::Fragment | ShaderGeneratorStages::Geometry) ); + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + // IShaderStageGenerator& geometryShader( *GetProgramGenerator().GetStage( + // ShaderGeneratorStages::Geometry ) ); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + + // IShaderProgramGenerator::OutputCubeFaceDepthGeometry( geometryShader ); + IShaderProgramGenerator::OutputCubeFaceDepthFragment(fragmentShader); + + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append( + "\toutNormalTC[gl_InvocationID] = outNormal[gl_InvocationID];\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("world_pos", "vec4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tctNorm[0] = outNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = outNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = outNormalTC[2];"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tworld_pos = model_matrix * pos;"); + tessEvalShader.Append("\tworld_pos /= world_pos.w;"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetOrthographicDepthShader(TessModeValues::Enum inTessMode) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetOrthographicDepthNoTessShader(); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetOrthographicDepthTessLinearShader(); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetOrthographicDepthTessPhongShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetOrthographicDepthTessNPatchShader(); + } + + return GetOrthographicDepthNoTessShader(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthNoTessShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddOutgoing("outDepth", "vec3"); + vertexShader.Append("void main() {"); + vertexShader.Append( + " gl_Position = model_view_projection * vec4( attr_pos, 1.0 );"); + vertexShader.Append(" outDepth.x = gl_Position.z / gl_Position.w;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthTessLinearShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthTessLinearShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth tess linear shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("outDepth", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthTessPhongShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthTessPhongShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth tess phong shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfloat depth = (outDepth.x + 1.0) * 0.5;"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddOutgoing("outDepth", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetOrthographicDepthTessNPatchShader() + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthShader = + m_OrthographicDepthTessNPatchShader; + + if (theDepthShader.hasValue() == false) { + TStrType name; + name.assign("orthographic depth tess npatch shader"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + fragmentShader.AddUniform("model_view_projection", "mat4"); + fragmentShader.AddUniform("camera_properties", "vec2"); + fragmentShader.AddUniform("camera_position", "vec3"); + fragmentShader.AddUniform("camera_direction", "vec3"); + fragmentShader.AddInclude("depthpass.glsllib"); + + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + // fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("\tfloat depth = (outDepth.x - camera_properties.x) / " + "(camera_properties.y - camera_properties.x);"); + fragmentShader.Append("\tfragOutput = vec4(depth);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.AddUniform("model_matrix", "mat4"); + tessEvalShader.AddOutgoing("outDepth", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + tessEvalShader.Append("\toutDepth.x = gl_Position.z / gl_Position.w;"); + tessEvalShader.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + + return theDepthShader.getValue(); + } + + // --------------------------------- + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetDepthPrepassShader(bool inDisplaced) + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthPrePassShader = + (!inDisplaced) ? m_DepthPrepassShader : m_DepthPrepassShaderDisplaced; + + if (theDepthPrePassShader.hasValue() == false) { + // check if we do displacement mapping + TStrType name; + name.assign("depth prepass shader"); + if (inDisplaced) + name.append(" displacement"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + + vertexShader.Append("void main() {"); + + if (inDisplaced) { + GetQt3DSContext() + .GetDefaultMaterialShaderGenerator() + .AddDisplacementMappingForDepthPass(vertexShader); + } else { + vertexShader.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + } + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return theDepthPrePassShader.getValue(); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetDepthTessPrepassShader(TessModeValues::Enum inTessMode, bool inDisplaced) + { + if (!m_qt3dsContext.GetRenderContext().IsTessellationSupported() + || inTessMode == TessModeValues::NoTess) { + return GetDepthPrepassShader(inDisplaced); + } else if (inTessMode == TessModeValues::TessLinear) { + return GetDepthTessLinearPrepassShader(inDisplaced); + } else if (inTessMode == TessModeValues::TessPhong) { + return GetDepthTessPhongPrepassShader(); + } else if (inTessMode == TessModeValues::TessNPatch) { + return GetDepthTessNPatchPrepassShader(); + } + + return GetDepthPrepassShader(inDisplaced); + } + + SRenderableDepthPrepassShader * + Qt3DSRendererImpl::GetDepthTessLinearPrepassShader(bool inDisplaced) + { + Option<NVScopedRefCounted<SRenderableDepthPrepassShader>> &theDepthPrePassShader = + (!inDisplaced) ? m_DepthTessLinearPrepassShader + : m_DepthTessLinearPrepassShaderDisplaced; + + if (theDepthPrePassShader.hasValue() == false) { + // check if we do displacement mapping + TStrType name; + name.assign("depth tess linear prepass shader"); + if (inDisplaced) + name.append(" displacement"); + + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = m_qt3dsContext.GetStringTable().RegisterStr(name.c_str()); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + if (inDisplaced) { + vertexShader.AddIncoming("attr_uv0", "vec2"); + vertexShader.AddIncoming("attr_norm", "vec3"); + + vertexShader.AddUniform("displacementMap_rot", "vec4"); + vertexShader.AddUniform("displacementMap_offset", "vec3"); + + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddOutgoing("outUV", "vec2"); + } + vertexShader.AddOutgoing("outWorldPos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + if (inDisplaced) { + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("\tvec3 uTransform = vec3( displacementMap_rot.x, " + "displacementMap_rot.y, displacementMap_offset.x );"); + vertexShader.Append("\tvec3 vTransform = vec3( displacementMap_rot.z, " + "displacementMap_rot.w, displacementMap_offset.y );"); + vertexShader.AddInclude( + "defaultMaterialLighting.glsllib"); // getTransformedUVCoords is in the + // lighting code addition. + vertexShader << "\tvec2 uv_coords = attr_uv0;" << Endl; + vertexShader << "\toutUV = getTransformedUVCoords( vec3( uv_coords, 1.0), " + "uTransform, vTransform );\n"; + } + vertexShader.Append("\toutWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationLinear.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.AddOutgoing("outUVTC", "vec2"); + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + + if (inDisplaced) { + tessCtrlShader.Append("\toutUVTC[gl_InvocationID] = outUV[gl_InvocationID];"); + tessCtrlShader.Append( + "\toutNormalTC[gl_InvocationID] = outNormal[gl_InvocationID];"); + } + + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationLinear.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + if (inDisplaced) { + tessEvalShader.AddUniform("displacementSampler", "sampler2D"); + tessEvalShader.AddUniform("displaceAmount", "float"); + tessEvalShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + } + tessEvalShader.AddOutgoing("outUV", "vec2"); + tessEvalShader.AddOutgoing("outNormal", "vec3"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + + if (inDisplaced) { + tessEvalShader << "\toutUV = gl_TessCoord.x * outUVTC[0] + gl_TessCoord.y * " + "outUVTC[1] + gl_TessCoord.z * outUVTC[2];" + << Endl; + tessEvalShader + << "\toutNormal = gl_TessCoord.x * outNormalTC[0] + gl_TessCoord.y * " + "outNormalTC[1] + gl_TessCoord.z * outNormalTC[2];" + << Endl; + tessEvalShader + << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + "displacementSampler , displaceAmount, outUV , outNormal, pos.xyz );" + << Endl; + tessEvalShader.Append( + "\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + } else + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;"); + + tessEvalShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + } + + SShaderCacheProgramFlags theFlags; + theFlags.SetTessellationEnabled(true); + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + name.c_str(), theFlags, TShaderFeatureSet()); + + if (depthShaderProgram) { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + theDepthPrePassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return theDepthPrePassShader->mPtr; + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetDepthTessPhongPrepassShader() + { + if (m_DepthTessPhongPrepassShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("depth tess phong prepass shader"); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddOutgoing("outWorldPos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddInclude("tessellationPhong.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationPhong.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); + tessEvalShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + } + + SShaderCacheProgramFlags theFlags; + theFlags.SetTessellationEnabled(true); + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "depth tess phong prepass shader", theFlags, TShaderFeatureSet()); + + if (depthShaderProgram) { + m_DepthTessPhongPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + m_DepthTessPhongPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return m_DepthTessPhongPrepassShader->mPtr; + } + + SRenderableDepthPrepassShader *Qt3DSRendererImpl::GetDepthTessNPatchPrepassShader() + { + if (m_DepthTessNPatchPrepassShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("depth tess npatch prepass shader"); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &vertexShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &tessCtrlShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessControl)); + IShaderStageGenerator &tessEvalShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)); + IShaderStageGenerator &fragmentShader( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexShader.AddIncoming("attr_pos", "vec3"); + vertexShader.AddIncoming("attr_norm", "vec3"); + vertexShader.AddOutgoing("outNormal", "vec3"); + vertexShader.AddOutgoing("outWorldPos", "vec3"); + vertexShader.AddUniform("model_view_projection", "mat4"); + vertexShader.AddUniform("model_matrix", "mat4"); + vertexShader.Append("void main() {"); + vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);"); + vertexShader.Append("\toutWorldPos = (model_matrix * vec4(attr_pos, 1.0)).xyz;"); + vertexShader.Append("\toutNormal = attr_norm;"); + vertexShader.Append("}"); + fragmentShader.Append("void main() {"); + fragmentShader.Append("\tfragOutput = vec4(0.0, 0.0, 0.0, 0.0);"); + fragmentShader.Append("}"); + + tessCtrlShader.AddOutgoing("outNormalTC", "vec3"); + tessCtrlShader.AddInclude("tessellationNPatch.glsllib"); + tessCtrlShader.AddUniform("tessLevelInner", "float"); + tessCtrlShader.AddUniform("tessLevelOuter", "float"); + tessCtrlShader.Append("void main() {\n"); + tessCtrlShader.Append("\tctWorldPos[0] = outWorldPos[0];"); + tessCtrlShader.Append("\tctWorldPos[1] = outWorldPos[1];"); + tessCtrlShader.Append("\tctWorldPos[2] = outWorldPos[2];"); + tessCtrlShader.Append("\tctNorm[0] = outNormal[0];"); + tessCtrlShader.Append("\tctNorm[1] = outNormal[1];"); + tessCtrlShader.Append("\tctNorm[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tctTangent[0] = outNormal[0];"); // we don't care for the tangent + tessCtrlShader.Append("\tctTangent[1] = outNormal[1];"); + tessCtrlShader.Append("\tctTangent[2] = outNormal[2];"); + tessCtrlShader.Append( + "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;"); + tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n"); + tessCtrlShader.Append( + "\toutNormalTC[gl_InvocationID] = outNormal[gl_InvocationID];\n"); + tessCtrlShader.Append("}"); + + tessEvalShader.AddInclude("tessellationNPatch.glsllib"); + tessEvalShader.AddUniform("model_view_projection", "mat4"); + tessEvalShader.Append("void main() {"); + tessEvalShader.Append("\tctNorm[0] = outNormalTC[0];"); + tessEvalShader.Append("\tctNorm[1] = outNormalTC[1];"); + tessEvalShader.Append("\tctNorm[2] = outNormalTC[2];"); + tessEvalShader.Append( + "\tctTangent[0] = outNormalTC[0];"); // we don't care for the tangent + tessEvalShader.Append("\tctTangent[1] = outNormalTC[1];"); + tessEvalShader.Append("\tctTangent[2] = outNormalTC[2];"); + tessEvalShader.Append("\tvec4 pos = tessShader( );\n"); + tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n"); + tessEvalShader.Append("}"); + } else if (theCache.IsShaderCachePersistenceEnabled()) { + // we load from shader cache set default shader stages + GetProgramGenerator().BeginProgram(TShaderGeneratorStageFlags( + ShaderGeneratorStages::Vertex | ShaderGeneratorStages::TessControl + | ShaderGeneratorStages::TessEval | ShaderGeneratorStages::Fragment)); + } + + SShaderCacheProgramFlags theFlags; + theFlags.SetTessellationEnabled(true); + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "depth tess npatch prepass shader", theFlags, TShaderFeatureSet()); + + if (depthShaderProgram) { + m_DepthTessNPatchPrepassShader = NVScopedRefCounted<SRenderableDepthPrepassShader>( + QT3DS_NEW(GetContext().GetAllocator(), + SRenderableDepthPrepassShader)(*depthShaderProgram, GetContext())); + } else { + m_DepthTessNPatchPrepassShader = + NVScopedRefCounted<SRenderableDepthPrepassShader>(); + } + } + return m_DepthTessNPatchPrepassShader->mPtr; + } + + SDefaultAoPassShader *Qt3DSRendererImpl::GetDefaultAoPassShader(TShaderFeatureSet inFeatureSet) + { + if (m_DefaultAoPassShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("fullscreen AO pass shader"); + NVRenderShaderProgram *aoPassShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!aoPassShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &theVertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &theFragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_uv", "vec2"); + theVertexGenerator.AddOutgoing("uv_coords", "vec2"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = vec4(attr_pos.xy, 0.5, 1.0 );"); + theVertexGenerator.Append("\tuv_coords = attr_uv;"); + theVertexGenerator.Append("}"); + + // fragmentGenerator.AddInclude( "SSAOCustomMaterial.glsllib" ); + theFragmentGenerator.AddInclude("viewProperties.glsllib"); + theFragmentGenerator.AddInclude("screenSpaceAO.glsllib"); + if (m_Context->GetRenderContextType() == NVRenderContextValues::GLES2) { + theFragmentGenerator + << "\tuniform vec4 ao_properties;" << Endl + << "\tuniform vec4 ao_properties2;" << Endl + << "\tuniform vec4 shadow_properties;" << Endl + << "\tuniform vec4 aoScreenConst;" << Endl + << "\tuniform vec4 UvToEyeConst;" << Endl; + } else { + theFragmentGenerator + << "layout (std140) uniform cbAoShadow { " << Endl << "\tvec4 ao_properties;" + << Endl << "\tvec4 ao_properties2;" << Endl << "\tvec4 shadow_properties;" + << Endl << "\tvec4 aoScreenConst;" << Endl << "\tvec4 UvToEyeConst;" << Endl + << "};" << Endl; + } + theFragmentGenerator.AddUniform("camera_direction", "vec3"); + theFragmentGenerator.AddUniform("depth_sampler", "sampler2D"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator << "\tfloat aoFactor;" << Endl; + theFragmentGenerator << "\tvec3 screenNorm;" << Endl; + + // We're taking multiple depth samples and getting the derivatives at each of them + // to get a more + // accurate view space normal vector. When we do only one, we tend to get bizarre + // values at the edges + // surrounding objects, and this also ends up giving us weird AO values. + // If we had a proper screen-space normal map, that would also do the trick. + if (m_Context->GetRenderContextType() == NVRenderContextValues::GLES2) { + theFragmentGenerator.AddUniform("depth_sampler_size", "vec2"); + theFragmentGenerator.Append("\tivec2 iCoords = ivec2( gl_FragCoord.xy );"); + theFragmentGenerator.Append("\tfloat depth = getDepthValue( " + "texture2D(depth_sampler, vec2(iCoords)" + " / depth_sampler_size), camera_properties );"); + theFragmentGenerator.Append( + "\tdepth = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tdepth = (depth - camera_properties.x) / " + "(camera_properties.y - camera_properties.x);"); + theFragmentGenerator.Append("\tfloat depth2 = getDepthValue( " + "texture2D(depth_sampler, vec2(iCoords+ivec2(1))" + " / depth_sampler_size), camera_properties );"); + theFragmentGenerator.Append( + "\tdepth2 = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tfloat depth3 = getDepthValue( " + "texture2D(depth_sampler, vec2(iCoords-ivec2(1))" + " / depth_sampler_size), camera_properties );"); + } else { + theFragmentGenerator.Append("\tivec2 iCoords = ivec2( gl_FragCoord.xy );"); + theFragmentGenerator.Append("\tfloat depth = getDepthValue( " + "texelFetch(depth_sampler, iCoords, 0), " + "camera_properties );"); + theFragmentGenerator.Append( + "\tdepth = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tdepth = (depth - camera_properties.x) / " + "(camera_properties.y - camera_properties.x);"); + theFragmentGenerator.Append("\tfloat depth2 = getDepthValue( " + "texelFetch(depth_sampler, iCoords+ivec2(1), 0), " + "camera_properties );"); + theFragmentGenerator.Append( + "\tdepth2 = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tfloat depth3 = getDepthValue( " + "texelFetch(depth_sampler, iCoords-ivec2(1), 0), " + "camera_properties );"); + } + theFragmentGenerator.Append( + "\tdepth3 = depthValueToLinearDistance( depth, camera_properties );"); + theFragmentGenerator.Append("\tvec3 tanU = vec3(10, 0, dFdx(depth));"); + theFragmentGenerator.Append("\tvec3 tanV = vec3(0, 10, dFdy(depth));"); + theFragmentGenerator.Append("\tscreenNorm = normalize(cross(tanU, tanV));"); + theFragmentGenerator.Append("\ttanU = vec3(10, 0, dFdx(depth2));"); + theFragmentGenerator.Append("\ttanV = vec3(0, 10, dFdy(depth2));"); + theFragmentGenerator.Append("\tscreenNorm += normalize(cross(tanU, tanV));"); + theFragmentGenerator.Append("\ttanU = vec3(10, 0, dFdx(depth3));"); + theFragmentGenerator.Append("\ttanV = vec3(0, 10, dFdy(depth3));"); + theFragmentGenerator.Append("\tscreenNorm += normalize(cross(tanU, tanV));"); + theFragmentGenerator.Append("\tscreenNorm = -normalize(screenNorm);"); + + theFragmentGenerator.Append("\taoFactor = \ + SSambientOcclusion( depth_sampler, screenNorm, ao_properties, ao_properties2, \ + camera_properties, aoScreenConst, UvToEyeConst );"); + + theFragmentGenerator.Append( + "\tgl_FragColor = vec4(aoFactor, aoFactor, aoFactor, 1.0);"); + + theFragmentGenerator.Append("}"); + } + + aoPassShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "fullscreen AO pass shader", SShaderCacheProgramFlags(), inFeatureSet); + + if (aoPassShaderProgram) { + m_DefaultAoPassShader = NVScopedRefCounted<SDefaultAoPassShader>( + QT3DS_NEW(GetContext().GetAllocator(), SDefaultAoPassShader)(*aoPassShaderProgram, + GetContext())); + } else { + m_DefaultAoPassShader = NVScopedRefCounted<SDefaultAoPassShader>(); + } + } + return m_DefaultAoPassShader->mPtr; + } + + SDefaultAoPassShader *Qt3DSRendererImpl::GetFakeDepthShader(TShaderFeatureSet inFeatureSet) + { + if (m_FakeDepthShader.hasValue() == false) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("depth display shader"); + NVRenderShaderProgram *depthShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!depthShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &theVertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &theFragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_uv", "vec2"); + theVertexGenerator.AddOutgoing("uv_coords", "vec2"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = vec4(attr_pos.xy, 0.5, 1.0 );"); + theVertexGenerator.Append("\tuv_coords = attr_uv;"); + theVertexGenerator.Append("}"); + + theFragmentGenerator.AddUniform("depth_sampler", "sampler2D"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tivec2 iCoords = ivec2( gl_FragCoord.xy );"); + theFragmentGenerator.Append( + "\tfloat depSample = texelFetch(depth_sampler, iCoords, 0).x;"); + theFragmentGenerator.Append( + "\tgl_FragColor = vec4( depSample, depSample, depSample, 1.0 );"); + theFragmentGenerator.Append("\treturn;"); + theFragmentGenerator.Append("}"); + } + + depthShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "depth display shader", SShaderCacheProgramFlags(), inFeatureSet); + + if (depthShaderProgram) { + m_FakeDepthShader = NVScopedRefCounted<SDefaultAoPassShader>( + QT3DS_NEW(GetContext().GetAllocator(), SDefaultAoPassShader)( + *depthShaderProgram, GetContext())); + } else { + m_FakeDepthShader = NVScopedRefCounted<SDefaultAoPassShader>(); + } + } + return m_FakeDepthShader->mPtr; + } + + SDefaultAoPassShader *Qt3DSRendererImpl::GetFakeCubeDepthShader(TShaderFeatureSet inFeatureSet) + { + if (!m_FakeCubemapDepthShader.hasValue()) { + IShaderCache &theCache = m_qt3dsContext.GetShaderCache(); + CRegisteredString theCacheKey = + m_qt3dsContext.GetStringTable().RegisterStr("cube depth display shader"); + NVRenderShaderProgram *cubeShaderProgram = + theCache.GetProgram(theCacheKey, TShaderFeatureSet()); + if (!cubeShaderProgram) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &theVertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &theFragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_uv", "vec2"); + theVertexGenerator.AddOutgoing("sample_dir", "vec3"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = vec4(attr_pos.xy, 0.5, 1.0 );"); + theVertexGenerator.Append( + "\tsample_dir = vec3(4.0 * (attr_uv.x - 0.5), -1.0, 4.0 * (attr_uv.y - 0.5));"); + theVertexGenerator.Append("}"); + theFragmentGenerator.AddUniform("depth_cube", "samplerCube"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append( + "\tfloat smpDepth = texture( depth_cube, sample_dir ).x;"); + theFragmentGenerator.Append( + "\tgl_FragColor = vec4(smpDepth, smpDepth, smpDepth, 1.0);"); + theFragmentGenerator.Append("}"); + } + + cubeShaderProgram = GetProgramGenerator().CompileGeneratedShader( + "cube depth display shader", SShaderCacheProgramFlags(), inFeatureSet); + + if (cubeShaderProgram) { + m_FakeCubemapDepthShader = NVScopedRefCounted<SDefaultAoPassShader>( + QT3DS_NEW(GetContext().GetAllocator(), SDefaultAoPassShader)(*cubeShaderProgram, + GetContext())); + } else { + m_FakeCubemapDepthShader = NVScopedRefCounted<SDefaultAoPassShader>(); + } + } + return m_FakeCubemapDepthShader.getValue(); + } + + STextRenderHelper Qt3DSRendererImpl::GetTextShader(bool inUsePathRendering) + { + STextShaderPtr &thePtr = (!inUsePathRendering) ? m_TextShader : m_TextPathShader; + if (thePtr.HasGeneratedShader()) + return STextRenderHelper(thePtr, *m_QuadInputAssembler); + + NVRenderShaderProgram *theShader = NULL; + NVRenderProgramPipeline *thePipeline = NULL; + + if (!inUsePathRendering) { + GetProgramGenerator().BeginProgram(); + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("text_dimensions", "vec4"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator + << "\tvec3 textPos = vec3(attr_pos.x * text_dimensions.x + text_dimensions.z" + << ", attr_pos.y * text_dimensions.y + text_dimensions.w" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(textPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_textdimensions", "vec3"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.AddUniform("text_backgroundcolor", "vec3"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;"); + // Enable rendering from a sub-rect + + fragmentGenerator + << "\ttheCoords.x = theCoords.x * text_textdimensions.x;" << Endl + << "\ttheCoords.y = theCoords.y * text_textdimensions.y;" << Endl + // flip the y uv coord if the dimension's z variable is set + << "\tif ( text_textdimensions.z > 0.0 ) theCoords.y = 1.0 - theCoords.y;" << Endl; + fragmentGenerator.Append( + "\tvec4 c = texture2D(text_image, theCoords);"); + fragmentGenerator.Append( + "\tfragOutput = vec4(mix(text_backgroundcolor.rgb, " + "text_textcolor.rgb, c.rgb), c.a) * text_textcolor.a;"); + + vertexGenerator.Append("}"); + fragmentGenerator.Append("}"); + const char *shaderName = "text shader"; + theShader = GetProgramGenerator().CompileGeneratedShader( + shaderName, SShaderCacheProgramFlags(), TShaderFeatureSet(), false); + } else { + GetProgramGenerator().BeginProgram( + TShaderGeneratorStageFlags(ShaderGeneratorStages::Fragment)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_backgroundcolor", "vec3"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfragOutput = vec4(mix(text_backgroundcolor.rgb, " + "text_textcolor.rgb, text_textcolor.a), text_textcolor.a );"); + fragmentGenerator.Append("}"); + + const char *shaderName = "text path shader"; + theShader = GetProgramGenerator().CompileGeneratedShader( + shaderName, SShaderCacheProgramFlags(), TShaderFeatureSet(), true); + + // setup program pipeline + if (theShader) { + thePipeline = GetContext().CreateProgramPipeline(); + if (thePipeline) { + thePipeline->SetProgramStages( + theShader, + qt3ds::render::NVRenderShaderTypeFlags(NVRenderShaderTypeValue::Fragment)); + } + } + } + + if (theShader == NULL) { + thePtr.Set(NULL); + } else { + GenerateXYQuad(); + thePtr.Set(QT3DS_NEW(m_Context->GetAllocator(), STextShader)(*theShader, thePipeline)); + } + return STextRenderHelper(thePtr, *m_QuadInputAssembler); + } + + STextDepthShader *Qt3DSRendererImpl::GetTextDepthShader() + { + if (m_TextDepthPrepassShader.hasValue() == false) { + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("text_dimensions", "vec4"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator + << "\tvec3 textPos = vec3(attr_pos.x * text_dimensions.x + text_dimensions.z" + << ", attr_pos.y * text_dimensions.y + text_dimensions.w" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(textPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + + fragmentGenerator.AddUniform("text_textdimensions", "vec3"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;"); + // Enable rendering from a sub-rect + + fragmentGenerator + << "\ttheCoords.x = theCoords.x * text_textdimensions.x;" << Endl + << "\ttheCoords.y = theCoords.y * text_textdimensions.y;" << Endl + // flip the y uv coord if the dimension's z variable is set + << "\tif ( text_textdimensions.z > 0.0 ) theCoords.y = 1.0 - theCoords.y;" << Endl; + fragmentGenerator.Append("\tfloat alpha_mask = texture2D( text_image, theCoords ).r;"); + fragmentGenerator.Append("\tif ( alpha_mask < .05 ) discard;"); + vertexGenerator.Append("}"); + fragmentGenerator.Append("}"); + const char *shaderName = "text depth shader"; + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + shaderName, SShaderCacheProgramFlags(), TShaderFeatureSet()); + if (theShader == NULL) { + m_TextDepthPrepassShader = NVScopedRefCounted<STextDepthShader>(); + } else { + GenerateXYQuad(); + m_TextDepthPrepassShader = NVScopedRefCounted<STextDepthShader>( + QT3DS_NEW(m_Context->GetAllocator(), STextDepthShader)( + m_Context->GetAllocator(), *theShader, *m_QuadInputAssembler)); + } + } + return m_TextDepthPrepassShader->mPtr; + } + + STextRenderHelper Qt3DSRendererImpl::GetTextWidgetShader() + { + if (m_TextWidgetShader.HasGeneratedShader()) + return STextRenderHelper(m_TextWidgetShader, *m_QuadInputAssembler); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("text_dimensions", "vec4"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator + << "\tvec3 textPos = vec3(attr_pos.x * text_dimensions.x + text_dimensions.z" + << ", attr_pos.y * text_dimensions.y + text_dimensions.w" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(textPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_textdimensions", "vec3"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.AddUniform("text_backgroundcolor", "vec3"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;"); + // Enable rendering from a sub-rect + + fragmentGenerator << "\ttheCoords.x = theCoords.x * text_textdimensions.x;" << Endl + << "\ttheCoords.y = theCoords.y * text_textdimensions.y;" << Endl + // flip the y uv coord if the dimension's z variable is set + << "\tif ( text_textdimensions.z > 0.0 ) theCoords.y = 1.0 - theCoords.y;" + << Endl; + fragmentGenerator.Append( + "\tfloat alpha_mask = texture2D( text_image, theCoords ).r * text_textcolor.a;"); + fragmentGenerator.Append("\tfragOutput = vec4(mix(text_backgroundcolor.rgb, " + "text_textcolor.rgb, alpha_mask), 1.0 );"); + fragmentGenerator.Append("}"); + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "text widget shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (theShader == NULL) + m_TextWidgetShader.Set(NULL); + else { + GenerateXYQuad(); + m_TextWidgetShader.Set(QT3DS_NEW(m_Context->GetAllocator(), STextShader)(*theShader)); + } + return STextRenderHelper(m_TextWidgetShader, *m_QuadInputAssembler); + } + + STextRenderHelper Qt3DSRendererImpl::GetOnscreenTextShader() + { + if (m_TextOnscreenShader.HasGeneratedShader()) + return STextRenderHelper(m_TextOnscreenShader, *m_QuadStripInputAssembler); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddUniform("vertex_offsets", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + + vertexGenerator.Append("\tvec3 pos = attr_pos + vec3(vertex_offsets, 0.0);"); + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(pos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("text_textcolor", "vec4"); + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat alpha = texture2D( text_image, uv_coords ).a;"); + fragmentGenerator.Append( + "\tfragOutput = vec4(text_textcolor.r, text_textcolor.g, text_textcolor.b, alpha);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "onscreen texture shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + if (theShader == NULL) + m_TextOnscreenShader.Set(NULL); + else { + GenerateXYQuadStrip(); + m_TextOnscreenShader.Set(QT3DS_NEW(m_Context->GetAllocator(), STextShader)(*theShader)); + } + return STextRenderHelper(m_TextOnscreenShader, *m_QuadStripInputAssembler); + } + + NVRenderShaderProgram *Qt3DSRendererImpl::GetTextAtlasEntryShader() + { + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("text_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat alpha = texture2D( text_image, uv_coords ).a;"); + fragmentGenerator.Append("\tfragOutput = vec4(alpha, alpha, alpha, alpha);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "texture atlas entry shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + return theShader; + } + + STextRenderHelper Qt3DSRendererImpl::GetShader(STextRenderable & /*inRenderable*/, + bool inUsePathRendering) + { + return GetTextShader(inUsePathRendering); + } + + SLayerSceneShader *Qt3DSRendererImpl::GetSceneLayerShader() + { + if (m_SceneLayerShader.hasValue()) + return m_SceneLayerShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + // xy of text dimensions are scaling factors, zw are offset factors. + vertexGenerator.AddUniform("layer_dimensions", "vec2"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator << "\tvec3 layerPos = vec3(attr_pos.x * layer_dimensions.x / 2.0" + << ", attr_pos.y * layer_dimensions.y / 2.0" + << ", attr_pos.z);" << Endl; + + vertexGenerator.Append("\tgl_Position = model_view_projection * vec4(layerPos, 1.0);"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("layer_image", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 theCoords = uv_coords;\n"); + fragmentGenerator.Append("\tvec4 theLayerTexture = texture2D( layer_image, theCoords );\n"); + fragmentGenerator.Append("\tif( theLayerTexture.a == 0.0 ) discard;\n"); + fragmentGenerator.Append("\tfragOutput = theLayerTexture;\n"); + fragmentGenerator.Append("}"); + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "layer shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SLayerSceneShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SLayerSceneShader)(*theShader); + m_SceneLayerShader = retval; + return m_SceneLayerShader.getValue(); + } + + SLayerProgAABlendShader *Qt3DSRendererImpl::GetLayerProgAABlendShader() + { + if (m_LayerProgAAShader.hasValue()) + return m_LayerProgAAShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + fragmentGenerator.AddUniform("accumulator", "sampler2D"); + fragmentGenerator.AddUniform("last_frame", "sampler2D"); + fragmentGenerator.AddUniform("blend_factors", "vec2"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 accum = texture2D( accumulator, uv_coords );"); + fragmentGenerator.Append("\tvec4 lastFrame = texture2D( last_frame, uv_coords );"); + fragmentGenerator.Append( + "\tgl_FragColor = accum*blend_factors.y + lastFrame*blend_factors.x;"); + fragmentGenerator.Append("}"); + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "layer progressiveAA blend shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SLayerProgAABlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SLayerProgAABlendShader)(*theShader); + m_LayerProgAAShader = retval; + return m_LayerProgAAShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetCubeShadowBlurXShader() + { + if (m_CubeShadowBlurXShader.hasValue()) + return m_CubeShadowBlurXShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + // vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_pos.xy;"); + vertexGenerator.Append("}"); + + // This with the ShadowBlurYShader design for a 2-pass 5x5 (sigma=1.0) + // Weights computed using -- http://dev.theomader.com/gaussian-kernel-calculator/ + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthCube", "samplerCube"); + // fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("layout(location = 0) out vec4 frag0;"); + fragmentGenerator.Append("layout(location = 1) out vec4 frag1;"); + fragmentGenerator.Append("layout(location = 2) out vec4 frag2;"); + fragmentGenerator.Append("layout(location = 3) out vec4 frag3;"); + fragmentGenerator.Append("layout(location = 4) out vec4 frag4;"); + fragmentGenerator.Append("layout(location = 5) out vec4 frag5;"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat ofsScale = camera_properties.x / 2500.0;"); + fragmentGenerator.Append("\tvec3 dir0 = vec3(1.0, -uv_coords.y, -uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir1 = vec3(-1.0, -uv_coords.y, uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir2 = vec3(uv_coords.x, 1.0, uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir3 = vec3(uv_coords.x, -1.0, -uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir4 = vec3(uv_coords.x, -uv_coords.y, 1.0);"); + fragmentGenerator.Append("\tvec3 dir5 = vec3(-uv_coords.x, -uv_coords.y, -1.0);"); + fragmentGenerator.Append("\tfloat depth0;"); + fragmentGenerator.Append("\tfloat depth1;"); + fragmentGenerator.Append("\tfloat depth2;"); + fragmentGenerator.Append("\tfloat outDepth;"); + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir0).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir0 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir0 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir0 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir0 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag0 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir1).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir1 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir1 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir1 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir1 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag1 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir2).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir2 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir2 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir2 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir2 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag2 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir3).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir3 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir3 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir3 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir3 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag3 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir4).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir4 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir4 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir4 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir4 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag4 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir5).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir5 + vec3(-ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir5 + vec3(ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir5 + vec3(-2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir5 + vec3(2.0*ofsScale, 0.0, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag5 = vec4(outDepth);"); + + fragmentGenerator.Append("}"); + + CRegisteredString featureName(m_StringTable->RegisterStr("NO_FRAG_OUTPUT")); + SShaderPreprocessorFeature noFragOutputFeature(featureName, true); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "cubemap shadow blur X shader", SShaderCacheProgramFlags(), + TShaderFeatureSet(&noFragOutputFeature, 1)); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_CubeShadowBlurXShader = retval; + return m_CubeShadowBlurXShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetCubeShadowBlurYShader() + { + if (m_CubeShadowBlurYShader.hasValue()) + return m_CubeShadowBlurYShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + // vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_pos.xy;"); + vertexGenerator.Append("}"); + + // This with the ShadowBlurXShader design for a 2-pass 5x5 (sigma=1.0) + // Weights computed using -- http://dev.theomader.com/gaussian-kernel-calculator/ + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthCube", "samplerCube"); + // fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("layout(location = 0) out vec4 frag0;"); + fragmentGenerator.Append("layout(location = 1) out vec4 frag1;"); + fragmentGenerator.Append("layout(location = 2) out vec4 frag2;"); + fragmentGenerator.Append("layout(location = 3) out vec4 frag3;"); + fragmentGenerator.Append("layout(location = 4) out vec4 frag4;"); + fragmentGenerator.Append("layout(location = 5) out vec4 frag5;"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tfloat ofsScale = camera_properties.x / 2500.0;"); + fragmentGenerator.Append("\tvec3 dir0 = vec3(1.0, -uv_coords.y, -uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir1 = vec3(-1.0, -uv_coords.y, uv_coords.x);"); + fragmentGenerator.Append("\tvec3 dir2 = vec3(uv_coords.x, 1.0, uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir3 = vec3(uv_coords.x, -1.0, -uv_coords.y);"); + fragmentGenerator.Append("\tvec3 dir4 = vec3(uv_coords.x, -uv_coords.y, 1.0);"); + fragmentGenerator.Append("\tvec3 dir5 = vec3(-uv_coords.x, -uv_coords.y, -1.0);"); + fragmentGenerator.Append("\tfloat depth0;"); + fragmentGenerator.Append("\tfloat depth1;"); + fragmentGenerator.Append("\tfloat depth2;"); + fragmentGenerator.Append("\tfloat outDepth;"); + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir0).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir0 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir0 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir0 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir0 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag0 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir1).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir1 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir1 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir1 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir1 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag1 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir2).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir2 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir2 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir2 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir2 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag2 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir3).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir3 + vec3(0.0, 0.0, -ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir3 + vec3(0.0, 0.0, ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir3 + vec3(0.0, 0.0, -2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir3 + vec3(0.0, 0.0, 2.0*ofsScale)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag3 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir4).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir4 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir4 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir4 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir4 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag4 = vec4(outDepth);"); + + fragmentGenerator.Append("\tdepth0 = texture(depthCube, dir5).x;"); + fragmentGenerator.Append( + "\tdepth1 = texture(depthCube, dir5 + vec3(0.0, -ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth1 += texture(depthCube, dir5 + vec3(0.0, ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 = texture(depthCube, dir5 + vec3(0.0, -2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\tdepth2 += texture(depthCube, dir5 + vec3(0.0, 2.0*ofsScale, 0.0)).x;"); + fragmentGenerator.Append( + "\toutDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfrag5 = vec4(outDepth);"); + + fragmentGenerator.Append("}"); + + CRegisteredString featureName(m_StringTable->RegisterStr("NO_FRAG_OUTPUT")); + SShaderPreprocessorFeature noFragOutputFeature(featureName, true); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "cubemap shadow blur Y shader", SShaderCacheProgramFlags(), + TShaderFeatureSet(&noFragOutputFeature, 1)); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_CubeShadowBlurYShader = retval; + return m_CubeShadowBlurYShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetOrthoShadowBlurXShader() + { + if (m_OrthoShadowBlurXShader.hasValue()) + return m_OrthoShadowBlurXShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_uv.xy;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 ofsScale = vec2( camera_properties.x / 7680.0, 0.0 );"); + fragmentGenerator.Append("\tfloat depth0 = texture(depthSrc, uv_coords).x;"); + fragmentGenerator.Append("\tfloat depth1 = texture(depthSrc, uv_coords + ofsScale).x;"); + fragmentGenerator.Append("\tdepth1 += texture(depthSrc, uv_coords - ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat depth2 = texture(depthSrc, uv_coords + 2.0 * ofsScale).x;"); + fragmentGenerator.Append("\tdepth2 += texture(depthSrc, uv_coords - 2.0 * ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat outDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfragOutput = vec4(outDepth);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "shadow map blur X shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_OrthoShadowBlurXShader = retval; + return m_OrthoShadowBlurXShader.getValue(); + } + + SShadowmapPreblurShader *Qt3DSRendererImpl::GetOrthoShadowBlurYShader() + { + if (m_OrthoShadowBlurYShader.hasValue()) + return m_OrthoShadowBlurYShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords.xy = attr_uv.xy;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("camera_properties", "vec2"); + fragmentGenerator.AddUniform("depthSrc", "sampler2D"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec2 ofsScale = vec2( 0.0, camera_properties.x / 7680.0 );"); + fragmentGenerator.Append("\tfloat depth0 = texture(depthSrc, uv_coords).x;"); + fragmentGenerator.Append("\tfloat depth1 = texture(depthSrc, uv_coords + ofsScale).x;"); + fragmentGenerator.Append("\tdepth1 += texture(depthSrc, uv_coords - ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat depth2 = texture(depthSrc, uv_coords + 2.0 * ofsScale).x;"); + fragmentGenerator.Append("\tdepth2 += texture(depthSrc, uv_coords - 2.0 * ofsScale).x;"); + fragmentGenerator.Append( + "\tfloat outDepth = 0.38774 * depth0 + 0.24477 * depth1 + 0.06136 * depth2;"); + fragmentGenerator.Append("\tfragOutput = vec4(outDepth);"); + fragmentGenerator.Append("}"); + + NVRenderShaderProgram *theShader = GetProgramGenerator().CompileGeneratedShader( + "shadow map blur Y shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SShadowmapPreblurShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SShadowmapPreblurShader)(*theShader); + m_OrthoShadowBlurYShader = retval; + return m_OrthoShadowBlurYShader.getValue(); + } + +#ifdef ADVANCED_BLEND_SW_FALLBACK + SAdvancedModeBlendShader * + Qt3DSRendererImpl::GetAdvancedBlendModeShader(AdvancedBlendModes::Enum blendMode) + { + // Select between blend equations. + if (blendMode == AdvancedBlendModes::Overlay) { + return GetOverlayBlendModeShader(); + } else if (blendMode == AdvancedBlendModes::ColorBurn) { + return GetColorBurnBlendModeShader(); + } else if (blendMode == AdvancedBlendModes::ColorDodge) { + return GetColorDodgeBlendModeShader(); + } + return {}; + } + + SAdvancedModeBlendShader *Qt3DSRendererImpl::GetOverlayBlendModeShader() + { + if (m_AdvancedModeOverlayBlendShader.hasValue()) + return m_AdvancedModeOverlayBlendShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("base_layer", "sampler2D"); + fragmentGenerator.AddUniform("blend_layer", "sampler2D"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 base = texture2D(base_layer, uv_coords);"); + fragmentGenerator.Append("\tif (base.a != 0.0) base.rgb /= base.a;"); + fragmentGenerator.Append("\telse base = vec4(0.0);"); + fragmentGenerator.Append("\tvec4 blend = texture2D(blend_layer, uv_coords);"); + fragmentGenerator.Append("\tif (blend.a != 0.0) blend.rgb /= blend.a;"); + fragmentGenerator.Append("\telse blend = vec4(0.0);"); + + fragmentGenerator.Append("\tvec4 res = vec4(0.0);"); + fragmentGenerator.Append("\tfloat p0 = base.a * blend.a;"); + fragmentGenerator.Append("\tfloat p1 = base.a * (1.0 - blend.a);"); + fragmentGenerator.Append("\tfloat p2 = blend.a * (1.0 - base.a);"); + fragmentGenerator.Append("\tres.a = p0 + p1 + p2;"); + + NVRenderShaderProgram *theShader; + fragmentGenerator.Append( + "\tfloat f_rs_rd = (base.r < 0.5? (2.0 * base.r * blend.r) : " + "(1.0 - 2.0 * (1.0 - base.r) * (1.0 - blend.r)));"); + fragmentGenerator.Append( + "\tfloat f_gs_gd = (base.g < 0.5? (2.0 * base.g * blend.g) : " + "(1.0 - 2.0 * (1.0 - base.g) * (1.0 - blend.g)));"); + fragmentGenerator.Append( + "\tfloat f_bs_bd = (base.b < 0.5? (2.0 * base.b * blend.b) : " + "(1.0 - 2.0 * (1.0 - base.b) * (1.0 - blend.b)));"); + fragmentGenerator.Append("\tres.r = f_rs_rd * p0 + base.r * p1 + blend.r * p2;"); + fragmentGenerator.Append("\tres.g = f_gs_gd * p0 + base.g * p1 + blend.g * p2;"); + fragmentGenerator.Append("\tres.b = f_bs_bd * p0 + base.b * p1 + blend.b * p2;"); + fragmentGenerator.Append("\tgl_FragColor = vec4(res.rgb * res.a, res.a);"); + fragmentGenerator.Append("}"); + theShader = GetProgramGenerator().CompileGeneratedShader( + "advanced overlay shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + + NVScopedRefCounted<SAdvancedModeBlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SAdvancedModeBlendShader)(*theShader); + m_AdvancedModeOverlayBlendShader = retval; + return m_AdvancedModeOverlayBlendShader.getValue(); + } + + SAdvancedModeBlendShader *Qt3DSRendererImpl::GetColorBurnBlendModeShader() + { + if (m_AdvancedModeColorBurnBlendShader.hasValue()) + return m_AdvancedModeColorBurnBlendShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("base_layer", "sampler2D"); + fragmentGenerator.AddUniform("blend_layer", "sampler2D"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 base = texture2D(base_layer, uv_coords);"); + fragmentGenerator.Append("\tif (base.a != 0.0) base.rgb /= base.a;"); + fragmentGenerator.Append("\telse base = vec4(0.0);"); + fragmentGenerator.Append("\tvec4 blend = texture2D(blend_layer, uv_coords);"); + fragmentGenerator.Append("\tif (blend.a != 0.0) blend.rgb /= blend.a;"); + fragmentGenerator.Append("\telse blend = vec4(0.0);"); + + fragmentGenerator.Append("\tvec4 res = vec4(0.0);"); + fragmentGenerator.Append("\tfloat p0 = base.a * blend.a;"); + fragmentGenerator.Append("\tfloat p1 = base.a * (1.0 - blend.a);"); + fragmentGenerator.Append("\tfloat p2 = blend.a * (1.0 - base.a);"); + fragmentGenerator.Append("\tres.a = p0 + p1 + p2;"); + + NVRenderShaderProgram *theShader; + fragmentGenerator.Append( + "\tfloat f_rs_rd = ((base.r == 1.0) ? 1.0 : " + "(blend.r == 0.0) ? 0.0 : 1.0 - min(1.0, ((1.0 - base.r) / blend.r)));"); + fragmentGenerator.Append( + "\tfloat f_gs_gd = ((base.g == 1.0) ? 1.0 : " + "(blend.g == 0.0) ? 0.0 : 1.0 - min(1.0, ((1.0 - base.g) / blend.g)));"); + fragmentGenerator.Append( + "\tfloat f_bs_bd = ((base.b == 1.0) ? 1.0 : " + "(blend.b == 0.0) ? 0.0 : 1.0 - min(1.0, ((1.0 - base.b) / blend.b)));"); + fragmentGenerator.Append("\tres.r = f_rs_rd * p0 + base.r * p1 + blend.r * p2;"); + fragmentGenerator.Append("\tres.g = f_gs_gd * p0 + base.g * p1 + blend.g * p2;"); + fragmentGenerator.Append("\tres.b = f_bs_bd * p0 + base.b * p1 + blend.b * p2;"); + fragmentGenerator.Append("\tgl_FragColor = vec4(res.rgb * res.a, res.a);"); + fragmentGenerator.Append("}"); + + theShader = GetProgramGenerator().CompileGeneratedShader( + "advanced colorBurn shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SAdvancedModeBlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SAdvancedModeBlendShader)(*theShader); + m_AdvancedModeColorBurnBlendShader = retval; + return m_AdvancedModeColorBurnBlendShader.getValue(); + + } + + SAdvancedModeBlendShader *Qt3DSRendererImpl::GetColorDodgeBlendModeShader() + { + if (m_AdvancedModeColorDodgeBlendShader.hasValue()) + return m_AdvancedModeColorDodgeBlendShader.getValue(); + + GetProgramGenerator().BeginProgram(); + + IShaderStageGenerator &vertexGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragmentGenerator( + *GetProgramGenerator().GetStage(ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_uv", "vec2"); + vertexGenerator.AddOutgoing("uv_coords", "vec2"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append("\tgl_Position = vec4(attr_pos, 1.0 );"); + vertexGenerator.Append("\tuv_coords = attr_uv;"); + vertexGenerator.Append("}"); + + fragmentGenerator.AddUniform("base_layer", "sampler2D"); + fragmentGenerator.AddUniform("blend_layer", "sampler2D"); + + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tvec4 base = texture2D(base_layer, uv_coords);"); + fragmentGenerator.Append("\tif (base.a != 0.0) base.rgb /= base.a;"); + fragmentGenerator.Append("\telse base = vec4(0.0);"); + fragmentGenerator.Append("\tvec4 blend = texture2D(blend_layer, uv_coords);"); + fragmentGenerator.Append("\tif (blend.a != 0.0) blend.rgb /= blend.a;"); + fragmentGenerator.Append("\telse blend = vec4(0.0);"); + + fragmentGenerator.Append("\tvec4 res = vec4(0.0);"); + fragmentGenerator.Append("\tfloat p0 = base.a * blend.a;"); + fragmentGenerator.Append("\tfloat p1 = base.a * (1.0 - blend.a);"); + fragmentGenerator.Append("\tfloat p2 = blend.a * (1.0 - base.a);"); + fragmentGenerator.Append("\tres.a = p0 + p1 + p2;"); + + NVRenderShaderProgram *theShader; + fragmentGenerator.Append( + "\tfloat f_rs_rd = ((base.r == 0.0) ? 0.0 : " + "(blend.r == 1.0) ? 1.0 : min(base.r / (1.0 - blend.r), 1.0));"); + fragmentGenerator.Append( + "\tfloat f_gs_gd = ((base.g == 0.0) ? 0.0 : " + "(blend.g == 1.0) ? 1.0 : min(base.g / (1.0 - blend.g), 1.0));"); + fragmentGenerator.Append( + "\tfloat f_bs_bd = ((base.b == 0.0) ? 0.0 : " + "(blend.b == 1.0) ? 1.0 : min(base.b / (1.0 - blend.b), 1.0));"); + fragmentGenerator.Append("\tres.r = f_rs_rd * p0 + base.r * p1 + blend.r * p2;"); + fragmentGenerator.Append("\tres.g = f_gs_gd * p0 + base.g * p1 + blend.g * p2;"); + fragmentGenerator.Append("\tres.b = f_bs_bd * p0 + base.b * p1 + blend.b * p2;"); + + fragmentGenerator.Append("\tgl_FragColor = vec4(res.rgb * res.a, res.a);"); + fragmentGenerator.Append("}"); + theShader = GetProgramGenerator().CompileGeneratedShader( + "advanced colorDodge shader", SShaderCacheProgramFlags(), TShaderFeatureSet()); + NVScopedRefCounted<SAdvancedModeBlendShader> retval; + if (theShader) + retval = QT3DS_NEW(m_Context->GetAllocator(), SAdvancedModeBlendShader)(*theShader); + m_AdvancedModeColorDodgeBlendShader = retval; + return m_AdvancedModeColorDodgeBlendShader.getValue(); + + } +#endif +} +} diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h new file mode 100644 index 0000000..1ac85db --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplShaders.h @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDERER_IMPL_SHADERS_H +#define QT3DS_RENDERER_IMPL_SHADERS_H +#include "Qt3DSRender.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "render/Qt3DSRenderProgramPipeline.h" + +namespace qt3ds { +namespace render { + using qt3ds::render::NVRenderCachedShaderProperty; + using qt3ds::render::NVRenderCachedShaderBuffer; + + /** + * Cached tessellation property lookups this is on a per mesh base + */ + struct SShaderTessellationProperties + { + NVRenderCachedShaderProperty<QT3DSF32> m_EdgeTessLevel; ///< tesselation value for the edges + NVRenderCachedShaderProperty<QT3DSF32> m_InsideTessLevel; ///< tesselation value for the inside + NVRenderCachedShaderProperty<QT3DSF32> + m_PhongBlend; ///< blending between linear and phong component + NVRenderCachedShaderProperty<QT3DSVec2> + m_DistanceRange; ///< distance range for min and max tess level + NVRenderCachedShaderProperty<QT3DSF32> m_DisableCulling; ///< if set to 1.0 this disables + ///backface culling optimization in + ///the tess shader + + SShaderTessellationProperties() {} + SShaderTessellationProperties(NVRenderShaderProgram &inShader) + : m_EdgeTessLevel("tessLevelOuter", inShader) + , m_InsideTessLevel("tessLevelInner", inShader) + , m_PhongBlend("phongBlend", inShader) + , m_DistanceRange("distanceRange", inShader) + , m_DisableCulling("disableCulling", inShader) + { + } + }; + + /** + * The results of generating a shader. Caches all possible variable names into + * typesafe objects. + */ + struct SShaderGeneratorGeneratedShader + { + QT3DSU32 m_LayerSetIndex; + CRegisteredString m_QueryString; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewportMatrix; + SShaderTessellationProperties m_Tessellation; + + SShaderGeneratorGeneratedShader(CRegisteredString inQueryString, + NVRenderShaderProgram &inShader) + : m_LayerSetIndex(QT3DS_MAX_U32) + , m_QueryString(inQueryString) + , m_Shader(inShader) + , m_ViewportMatrix("viewport_matrix", inShader) + , m_Tessellation(inShader) + { + m_Shader.addRef(); + } + ~SShaderGeneratorGeneratedShader() { m_Shader.release(); } + static QT3DSU32 GetLayerIndex(const SShaderGeneratorGeneratedShader &inShader) + { + return inShader.m_LayerSetIndex; + } + static void SetLayerIndex(SShaderGeneratorGeneratedShader &inShader, QT3DSU32 idx) + { + inShader.m_LayerSetIndex = idx; + } + }; + + struct SDefaultMaterialRenderableDepthShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + + QT3DSI32 m_RefCount; + SDefaultMaterialRenderableDepthShader(NVRenderShaderProgram &inShader, + NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_RefCount(0) + { + m_Shader.addRef(); + } + + ~SDefaultMaterialRenderableDepthShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + /** + * Cached texture property lookups, used one per texture so a shader generator for N + * textures will have an array of N of these lookup objects. + */ + struct SShaderTextureProperties + { + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderCachedShaderProperty<QT3DSVec3> m_Offsets; + NVRenderCachedShaderProperty<QT3DSVec4> m_Rotations; + SShaderTextureProperties(const char *sampName, const char *offName, const char *rotName, + NVRenderShaderProgram &inShader) + : m_Sampler(sampName, inShader) + , m_Offsets(offName, inShader) + , m_Rotations(rotName, inShader) + { + } + SShaderTextureProperties() {} + }; + + struct SRenderableDepthPrepassShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + NVRenderCachedShaderProperty<QT3DSMat44> m_GlobalTransform; + NVRenderCachedShaderProperty<QT3DSMat44> m_Projection; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPosition; + NVRenderCachedShaderProperty<QT3DSF32> m_DisplaceAmount; + SShaderTextureProperties m_DisplacementProps; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraDirection; + // NVRenderCachedShaderProperty<QT3DSMat44> m_ShadowMV[6]; + + QT3DSI32 m_RefCount; + // Cache the tessellation property name lookups + SShaderTessellationProperties m_Tessellation; + + SRenderableDepthPrepassShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_GlobalTransform("model_matrix", inShader) + , m_Projection("projection", inShader) + , m_CameraPosition("camera_position", inShader) + , m_DisplaceAmount("displaceAmount", inShader) + , m_DisplacementProps("displacementSampler", "displacementMap_offset", + "displacementMap_rot", inShader) + , m_CameraProperties("camera_properties", inShader) + , m_CameraDirection("camera_direction", inShader) + , m_RefCount(0) + , m_Tessellation(inShader) + { + /* + m_ShadowMV[0].m_Shader = &inShader; + m_ShadowMV[0].m_Constant = inShader.GetShaderConstant( "shadow_mv0" ); + m_ShadowMV[1].m_Shader = &inShader; + m_ShadowMV[1].m_Constant = inShader.GetShaderConstant( "shadow_mv1" ); + m_ShadowMV[2].m_Shader = &inShader; + m_ShadowMV[2].m_Constant = inShader.GetShaderConstant( "shadow_mv2" ); + m_ShadowMV[3].m_Shader = &inShader; + m_ShadowMV[3].m_Constant = inShader.GetShaderConstant( "shadow_mv3" ); + m_ShadowMV[4].m_Shader = &inShader; + m_ShadowMV[4].m_Constant = inShader.GetShaderConstant( "shadow_mv4" ); + m_ShadowMV[5].m_Shader = &inShader; + m_ShadowMV[5].m_Constant = inShader.GetShaderConstant( "shadow_mv5" ); + */ + m_Shader.addRef(); + } + + ~SRenderableDepthPrepassShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + struct SDefaultAoPassShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraDirection; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture; + NVRenderCachedShaderProperty<NVRenderTextureCube *> m_CubeTexture; + NVRenderCachedShaderProperty<QT3DSVec2> m_DepthSamplerSize; + + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + QT3DSI32 m_RefCount; + + SDefaultAoPassShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_ViewMatrix("view_matrix", inShader) + , m_CameraProperties("camera_properties", inShader) + , m_CameraDirection("camera_direction", inShader) + , m_DepthTexture("depth_sampler", inShader) + , m_CubeTexture("depth_cube", inShader) + , m_DepthSamplerSize("depth_sampler_size", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~SDefaultAoPassShader() { m_Shader.release(); } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + struct STextShader + { + NVRenderShaderProgram &m_Shader; + + NVScopedRefCounted<NVRenderProgramPipeline> m_ProgramPipeline; + + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + // Dimensions and offsetting of the image. + NVRenderCachedShaderProperty<QT3DSVec4> m_Dimensions; + // The fourth member of text color is the opacity + NVRenderCachedShaderProperty<QT3DSVec4> m_TextColor; + NVRenderCachedShaderProperty<QT3DSVec3> m_BackgroundColor; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + // Dimensions and offsetting of the texture + NVRenderCachedShaderProperty<QT3DSVec3> m_TextDimensions; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + // Used only for onscreen text + NVRenderCachedShaderProperty<QT3DSVec2> m_VertexOffsets; + + STextShader(NVRenderShaderProgram &shader, NVRenderProgramPipeline *pipeline = NULL) + : m_Shader(shader) + , m_ProgramPipeline(pipeline) + , m_MVP("model_view_projection", shader) + , m_Dimensions("text_dimensions", shader) + , m_TextColor("text_textcolor", shader) + , m_BackgroundColor("text_backgroundcolor", shader) + , m_Sampler("text_image", shader) + , m_TextDimensions("text_textdimensions", shader) + , m_CameraProperties("camera_properties", shader) + , m_VertexOffsets("vertex_offsets", shader) + { + if (!pipeline) + m_Shader.addRef(); + } + ~STextShader() + { + if (!m_ProgramPipeline.mPtr) + m_Shader.release(); + } + void Render(NVRenderTexture2D &inTexture, const STextScaleAndOffset &inScaleAndOffset, + const QT3DSVec4 &inTextColor, const QT3DSMat44 &inMVP, const QT3DSVec2 &inCameraVec, + NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor); + + void RenderPath(NVRenderPathFontItem &inPathFontItem, + NVRenderPathFontSpecification &inPathFontSpec, + const STextScaleAndOffset &inScaleAndOffset, const QT3DSVec4 &inTextColor, + const QT3DSMat44 &inViewProjection, const QT3DSMat44 &inModel, + const QT3DSVec2 &inCameraVec, NVRenderContext &inRenderContext, + const STextTextureDetails &inTextTextureDetails, + const QT3DSVec3 &inBackgroundColor); + + void Render2D(NVRenderTexture2D &inTexture, const QT3DSVec4 &inTextColor, const QT3DSMat44 &inMVP, + NVRenderContext &inRenderContext, + NVRenderInputAssembler &inInputAssemblerBuffer, QT3DSU32 count, + QT3DSVec2 inVertexOffsets); + }; + + struct STextDepthShader + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + // Dimensions and offsetting of the image. + NVRenderCachedShaderProperty<QT3DSVec4> m_Dimensions; + NVRenderCachedShaderProperty<QT3DSVec3> m_TextDimensions; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderInputAssembler &m_QuadInputAssembler; + QT3DSI32 m_RefCount; + + STextDepthShader(NVAllocatorCallback &alloc, NVRenderShaderProgram &prog, + NVRenderInputAssembler &assembler) + : m_Allocator(alloc) + , m_Shader(prog) + , m_MVP("model_view_projection", prog) + , m_Dimensions("text_dimensions", prog) + , m_TextDimensions("text_textdimensions", prog) + , m_CameraProperties("camera_properties", prog) + , m_Sampler("text_image", prog) + , m_QuadInputAssembler(assembler) + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~STextDepthShader() { m_Shader.release(); } + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) + NVDelete(m_Allocator, this); + } + }; + + struct SLayerProgAABlendShader + { + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AccumSampler; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LastFrame; + NVRenderCachedShaderProperty<QT3DSVec2> m_BlendFactors; + volatile QT3DSI32 mRefCount; + SLayerProgAABlendShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_AccumSampler("accumulator", inShader) + , m_LastFrame("last_frame", inShader) + , m_BlendFactors("blend_factors", inShader) + , mRefCount(0) + { + } + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + }; + + struct SLayerSceneShader + { + NVRenderShaderProgram &m_Shader; + + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + // Dimensions and offsetting of the image. + NVRenderCachedShaderProperty<QT3DSVec2> m_Dimensions; + // The fourth member of text color is the opacity + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + + volatile QT3DSI32 mRefCount; + + SLayerSceneShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_Dimensions("layer_dimensions", inShader) + , m_Sampler("layer_image", inShader) + , mRefCount(0) + { + m_Shader.addRef(); + } + ~SLayerSceneShader() { m_Shader.release(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader.GetRenderContext().GetAllocator()) + }; + + struct SShadowmapPreblurShader + { + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + NVRenderCachedShaderProperty<NVRenderTextureCube *> m_DepthCube; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthMap; + + volatile QT3DSI32 mRefCount; + + SShadowmapPreblurShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_CameraProperties("camera_properties", inShader) + , m_DepthCube("depthCube", inShader) + , m_DepthMap("depthSrc", inShader) + , mRefCount(0) + { + m_Shader.addRef(); + } + ~SShadowmapPreblurShader() { m_Shader.release(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader.GetRenderContext().GetAllocator()) + }; + +#ifdef ADVANCED_BLEND_SW_FALLBACK + struct SAdvancedModeBlendShader + { + NVRenderShaderProgram &m_Shader; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_baseLayer; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_blendLayer; + + volatile QT3DSI32 mRefCount; + + SAdvancedModeBlendShader(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_baseLayer("base_layer", inShader) + , m_blendLayer("blend_layer", inShader) + , mRefCount(0) + { + m_Shader.addRef(); + } + ~SAdvancedModeBlendShader() { m_Shader.release(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader.GetRenderContext().GetAllocator()) + + }; +#endif + + struct SGGSGet + { + QT3DSU32 operator()(const SShaderGeneratorGeneratedShader &inShader) + { + return inShader.m_LayerSetIndex; + } + }; + struct SGGSSet + { + void operator()(SShaderGeneratorGeneratedShader &inShader, QT3DSU32 idx) + { + inShader.m_LayerSetIndex = idx; + } + }; +} +} +#endif diff --git a/src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h b/src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h new file mode 100644 index 0000000..a9b5e7b --- /dev/null +++ b/src/runtimerender/rendererimpl/Qt3DSVertexPipelineImpl.h @@ -0,0 +1,463 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_VERTEX_PIPELINE_IMPL_H +#define QT3DS_VERTEX_PIPELINE_IMPL_H +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" + +namespace qt3ds { +namespace render { + // Baseclass for the vertex pipelines to be sure we have consistent implementations. + struct SVertexPipelineImpl : public IDefaultMaterialVertexPipeline + { + struct GenerationFlagValues + { + enum Enum { + UVCoords = 1, + EnvMapReflection = 1 << 1, + ViewVector = 1 << 2, + WorldNormal = 1 << 3, + ObjectNormal = 1 << 4, + WorldPosition = 1 << 5, + TangentBinormal = 1 << 6, + UVCoords1 = 1 << 7, + VertexColor = 1 << 8, + }; + }; + + typedef TStrTableStrMap::const_iterator TParamIter; + typedef NVFlags<GenerationFlagValues::Enum> TGenerationFlags; + + IMaterialShaderGenerator &m_MaterialGenerator; + IShaderProgramGenerator &m_ProgramGenerator; + IStringTable &m_StringTable; + Qt3DSString m_TempString; + + TGenerationFlags m_GenerationFlags; + bool m_Wireframe; + TStrTableStrMap m_InterpolationParameters; + QT3DSU32 m_DisplacementIdx; + SRenderableImage *m_DisplacementImage; + QStringList m_addedFunctions; + + SVertexPipelineImpl(NVAllocatorCallback &inAllocator, IMaterialShaderGenerator &inMaterial, + IShaderProgramGenerator &inProgram, IStringTable &inStringTable, + bool inWireframe // only works if tessellation is true + ) + + : m_MaterialGenerator(inMaterial) + , m_ProgramGenerator(inProgram) + , m_StringTable(inStringTable) + , m_Wireframe(inWireframe) + , m_InterpolationParameters(inAllocator, "m_InterpolationParameters") + , m_DisplacementIdx(0) + , m_DisplacementImage(NULL) + { + } + + // Trues true if the code was *not* set. + bool SetCode(GenerationFlagValues::Enum inCode) + { + if (((QT3DSU32)m_GenerationFlags & inCode) != 0) + return true; + m_GenerationFlags |= inCode; + return false; + } + bool HasCode(GenerationFlagValues::Enum inCode) + { + return ((QT3DSU32)(m_GenerationFlags & inCode)) != 0; + } + IShaderProgramGenerator &ProgramGenerator() { return m_ProgramGenerator; } + IShaderStageGenerator &Vertex() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::Vertex); + } + IShaderStageGenerator &TessControl() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl); + } + IShaderStageGenerator &TessEval() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval); + } + IShaderStageGenerator &Geometry() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry); + } + IShaderStageGenerator &Fragment() + { + return *ProgramGenerator().GetStage(ShaderGeneratorStages::Fragment); + } + IMaterialShaderGenerator &MaterialGenerator() { return m_MaterialGenerator; } + + void SetupDisplacement(QT3DSU32 displacementImageIdx, SRenderableImage *displacementImage) + { + m_DisplacementIdx = displacementImageIdx; + m_DisplacementImage = displacementImage; + } + + CRegisteredString Str(const char8_t *inItem) { return m_StringTable.RegisterStr(inItem); } + + bool HasTessellation() const + { + return m_ProgramGenerator.GetEnabledStages() & ShaderGeneratorStages::TessEval; + } + bool HasGeometryStage() const + { + return m_ProgramGenerator.GetEnabledStages() & ShaderGeneratorStages::Geometry; + } + bool HasDisplacment() const { return m_DisplacementImage != NULL; } + + void InitializeWireframeGeometryShader() + { + if (m_Wireframe && ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry) + && ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)) { + IShaderStageGenerator &geometryShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry)); + // currently geometry shader is only used for drawing wireframe + if (m_Wireframe) { + geometryShader.AddUniform("viewport_matrix", "mat4"); + geometryShader.AddOutgoing("varEdgeDistance", "vec3"); + geometryShader.Append("layout (triangles) in;"); + geometryShader.Append("layout (triangle_strip, max_vertices = 3) out;"); + geometryShader.Append("void main() {"); + + // how this all work see + // http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf + + geometryShader.Append( + "// project points to screen space\n" + "\tvec3 p0 = vec3(viewport_matrix * (gl_in[0].gl_Position / " + "gl_in[0].gl_Position.w));\n" + "\tvec3 p1 = vec3(viewport_matrix * (gl_in[1].gl_Position / " + "gl_in[1].gl_Position.w));\n" + "\tvec3 p2 = vec3(viewport_matrix * (gl_in[2].gl_Position / " + "gl_in[2].gl_Position.w));\n" + "// compute triangle heights\n" + "\tfloat e1 = length(p1 - p2);\n" + "\tfloat e2 = length(p2 - p0);\n" + "\tfloat e3 = length(p1 - p0);\n" + "\tfloat alpha = acos( (e2*e2 + e3*e3 - e1*e1) / (2.0*e2*e3) );\n" + "\tfloat beta = acos( (e1*e1 + e3*e3 - e2*e2) / (2.0*e1*e3) );\n" + "\tfloat ha = abs( e3 * sin( beta ) );\n" + "\tfloat hb = abs( e3 * sin( alpha ) );\n" + "\tfloat hc = abs( e2 * sin( alpha ) );\n"); + } + } + } + + void FinalizeWireframeGeometryShader() + { + IShaderStageGenerator &geometryShader( + *ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry)); + + if (m_Wireframe == true && ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry) + && ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval)) { + const char8_t *theExtension("TE["); + // we always assume triangles + for (int i = 0; i < 3; i++) { + char buf[10]; + sprintf(buf, "%d", i); + for (TStrTableStrMap::const_iterator iter = m_InterpolationParameters.begin(), + end = m_InterpolationParameters.end(); + iter != end; ++iter) { + geometryShader << "\t" << iter->first.c_str() << " = " + << iter->first.c_str() << theExtension << buf << "];\n"; + } + + geometryShader << "\tgl_Position = gl_in[" << buf << "].gl_Position;\n"; + // the triangle distance is interpolated through the shader stage + if (i == 0) + geometryShader << "\n\tvarEdgeDistance = vec3(ha*" + << "gl_in[" << buf << "].gl_Position.w, 0.0, 0.0);\n"; + else if (i == 1) + geometryShader << "\n\tvarEdgeDistance = vec3(0.0, hb*" + << "gl_in[" << buf << "].gl_Position.w, 0.0);\n"; + else if (i == 2) + geometryShader << "\n\tvarEdgeDistance = vec3(0.0, 0.0, hc*" + << "gl_in[" << buf << "].gl_Position.w);\n"; + + // submit vertex + geometryShader << "\tEmitVertex();\n"; + } + // end primitive + geometryShader << "\tEndPrimitive();\n"; + } + } + + virtual void SetupTessIncludes(ShaderGeneratorStages::Enum inStage, + TessModeValues::Enum inTessMode) + { + IShaderStageGenerator &tessShader(*ProgramGenerator().GetStage(inStage)); + + // depending on the selected tessellation mode chose program + switch (inTessMode) { + case TessModeValues::TessPhong: + tessShader.AddInclude("tessellationPhong.glsllib"); + break; + case TessModeValues::TessNPatch: + tessShader.AddInclude("tessellationNPatch.glsllib"); + break; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case TessModeValues::TessLinear: + tessShader.AddInclude("tessellationLinear.glsllib"); + break; + } + } + + void GenerateUVCoords(QT3DSU32 inUVSet = 0) override + { + if (inUVSet == 0 && SetCode(GenerationFlagValues::UVCoords)) + return; + if (inUVSet == 1 && SetCode(GenerationFlagValues::UVCoords1)) + return; + + QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1); + + if (inUVSet == 0) + AddInterpolationParameter("varTexCoord0", "vec2"); + else if (inUVSet == 1) + AddInterpolationParameter("varTexCoord1", "vec2"); + + DoGenerateUVCoords(inUVSet); + } + void GenerateEnvMapReflection() override + { + if (SetCode(GenerationFlagValues::EnvMapReflection)) + return; + + GenerateWorldPosition(); + GenerateWorldNormal(); + IShaderStageGenerator &activeGenerator(ActiveStage()); + activeGenerator.AddInclude("viewProperties.glsllib"); + AddInterpolationParameter("var_object_to_camera", "vec3"); + activeGenerator.Append("\tvar_object_to_camera = normalize( local_model_world_position " + "- camera_position );"); + // World normal cannot be relied upon in the vertex shader because of bump maps. + Fragment().Append("\tvec3 environment_map_reflection = reflect( " + "normalize(var_object_to_camera), world_normal.xyz );"); + Fragment().Append("\tenvironment_map_reflection *= vec3( 0.5, 0.5, 0 );"); + Fragment().Append("\tenvironment_map_reflection += vec3( 0.5, 0.5, 1.0 );"); + } + void GenerateViewVector() override + { + if (SetCode(GenerationFlagValues::ViewVector)) + return; + GenerateWorldPosition(); + IShaderStageGenerator &activeGenerator(ActiveStage()); + activeGenerator.AddInclude("viewProperties.glsllib"); + AddInterpolationParameter("varViewVector", "vec3"); + activeGenerator.Append("\tvec3 local_view_vector = normalize(camera_position - " + "local_model_world_position);"); + AssignOutput("varViewVector", "local_view_vector"); + Fragment() << "\tvec3 view_vector = normalize(varViewVector);" << Endl; + } + + // fragment shader expects varying vertex normal + // lighting in vertex pipeline expects world_normal + void GenerateWorldNormal() override + { + if (SetCode(GenerationFlagValues::WorldNormal)) + return; + AddInterpolationParameter("varNormal", "vec3"); + DoGenerateWorldNormal(); + Fragment().Append("\tvec3 world_normal = normalize( varNormal );"); + } + void GenerateObjectNormal() override + { + if (SetCode(GenerationFlagValues::ObjectNormal)) + return; + DoGenerateObjectNormal(); + Fragment().Append("\tvec3 object_normal = normalize(varObjectNormal);"); + } + void GenerateWorldPosition() override + { + if (SetCode(GenerationFlagValues::WorldPosition)) + return; + + ActiveStage().AddUniform("model_matrix", "mat4"); + AddInterpolationParameter("varWorldPos", "vec3"); + DoGenerateWorldPosition(); + + AssignOutput("varWorldPos", "local_model_world_position"); + } + void GenerateVarTangentAndBinormal() override + { + if (SetCode(GenerationFlagValues::TangentBinormal)) + return; + AddInterpolationParameter("varTangent", "vec3"); + AddInterpolationParameter("varBinormal", "vec3"); + DoGenerateVarTangentAndBinormal(); + Fragment() << "\tvec3 tangent = normalize(varTangent);" << Endl + << "\tvec3 binormal = normalize(varBinormal);" << Endl; + } + void GenerateVertexColor() override + { + if (SetCode(GenerationFlagValues::VertexColor)) + return; + AddInterpolationParameter("varColor", "vec3"); + DoGenerateVertexColor(); + Fragment().Append("\tvec3 vertColor = varColor;"); + } + + bool HasActiveWireframe() override { return m_Wireframe; } + + // IShaderStageGenerator interface + void AddIncoming(const char8_t *name, const char8_t *type) override + { + ActiveStage().AddIncoming(name, type); + } + void AddIncoming(const TStrType &name, const char8_t *type) override + { + AddIncoming(name.c_str(), type); + } + void AddIncoming(const QString &name, const char8_t *type) override + { + AddIncoming(name.toUtf8().constData(), type); + } + + void AddOutgoing(const char8_t *name, const char8_t *type) override + { + AddInterpolationParameter(name, type); + } + void AddOutgoing(const TStrType &name, const char8_t *type) override + { + AddOutgoing(name.c_str(), type); + } + void AddOutgoing(const QString &name, const char8_t *type) override + { + AddOutgoing(name.toUtf8().constData(), type); + } + void AddUniform(const QString &name, const char8_t *type) override + { + AddUniform(name.toUtf8().constData(), type); + } + + void AddUniform(const char8_t *name, const char8_t *type) override + { + ActiveStage().AddUniform(name, type); + } + void AddUniform(const TStrType &name, const char8_t *type) override + { + AddUniform(name.c_str(), type); + } + + void AddInclude(const char8_t *name) override { ActiveStage().AddInclude(name); } + void AddInclude(const TStrType &name) override { AddInclude(name.c_str()); } + void AddInclude(const QString &name) override + { + QByteArray arr = name.toLatin1(); + AddInclude(arr.data()); + } + + void AddFunction(const QString &functionName) override + { + if (!m_addedFunctions.contains(functionName)) { + m_addedFunctions.push_back(functionName); + QString includeName; + QTextStream stream(&includeName); + stream << "func" << functionName << ".glsllib"; + AddInclude(includeName); + } + } + + void AddConstantBuffer(const char *name, const char *layout) override + { + ActiveStage().AddConstantBuffer(name, layout); + } + void AddConstantBuffer(const QString &name, const char *layout) override + { + AddConstantBuffer(name.toUtf8().constData(), layout); + } + + void AddConstantBufferParam(const char *cbName, const char *paramName, + const char *type) override + { + ActiveStage().AddConstantBufferParam(cbName, paramName, type); + } + void AddConstantBufferParam(const QString &cbName, const QString ¶mName, + const char *type) override + { + AddConstantBufferParam(cbName.toUtf8().constData(), + paramName.toUtf8().constData(), type); + } + + IShaderStageGenerator &operator<<(const char *data) override + { + ActiveStage() << data; + return *this; + } + IShaderStageGenerator &operator<<(const TStrType &data) override + { + ActiveStage() << data; + return *this; + } + IShaderStageGenerator &operator<<(const QString &data) override + { + ActiveStage() << data; + return *this; + } + IShaderStageGenerator &operator<<(const SEndlType &data) override + { + ActiveStage() << data; + return *this; + } + void Append(const char *data) override { ActiveStage().Append(data); } + void AppendPartial(const char *data) override { ActiveStage().Append(data); } + + ShaderGeneratorStages::Enum Stage() const override + { + return const_cast<SVertexPipelineImpl *>(this)->ActiveStage().Stage(); + } + + void BeginVertexGeneration(QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override = 0; + void AssignOutput(const char8_t *inVarName, const char8_t *inVarValueExpr) override = 0; + void EndVertexGeneration(bool customShader) override = 0; + + void BeginFragmentGeneration() override = 0; + void EndFragmentGeneration(bool customShader) override = 0; + + virtual IShaderStageGenerator &ActiveStage() = 0; + virtual void AddInterpolationParameter(const char8_t *inParamName, + const char8_t *inParamType) = 0; + + virtual void DoGenerateUVCoords(QT3DSU32 inUVSet) = 0; + virtual void DoGenerateWorldNormal() = 0; + virtual void DoGenerateObjectNormal() = 0; + virtual void DoGenerateWorldPosition() = 0; + virtual void DoGenerateVarTangentAndBinormal() = 0; + virtual void DoGenerateVertexColor() = 0; + }; +} +} + +#endif |