From b4954701093739e7a4e54a0669f306922d0d4605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pasi=20Kera=CC=88nen?= Date: Thu, 6 Jun 2019 16:22:02 +0300 Subject: Long live the slayer! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial commit of OpenGL Runtime to repository. Based on SHA1 61823aaccc6510699a54b34a2fe3f7523dab3b4e of qt3dstudio repository. Task-number: QT3DS-3600 Change-Id: Iaeb80237399f0e5656a19ebec9d1ab3a681d8832 Reviewed-by: Pasi Keränen --- .../rendererimpl/Qt3DSRendererImpl.cpp | 2051 ++++++++++++++++++++ 1 file changed, 2051 insertions(+) create mode 100644 src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp (limited to 'src/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp') 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 + +#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(&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(&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(inLayer.m_NextSibling); + return NULL; + } + + static inline void MaybePushLayer(SLayer &inLayer, nvvector &outLayerList) + { + inLayer.CalculateGlobalVariables(); + if (inLayer.m_Flags.IsGloballyActive() && inLayer.m_Flags.IsLayerRenderToTarget()) + outLayerList.push_back(&inLayer); + } + static void BuildRenderableLayers(SLayer &inLayer, nvvector &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 renderableLayers(m_qt3dsContext.GetPerFrameAllocator(), "LayerVector"); + // Found by fair roll of the dice. + renderableLayers.reserve(4); + + BuildRenderableLayers(inLayer, renderableLayers, inRenderSiblings); + + bool retval = false; + + for (nvvector::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 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::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::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(static_cast(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(theIter->second.mPtr); + + SLayerRenderData *theRenderData = QT3DS_NEW(m_Context->GetAllocator(), SLayerRenderData)( + const_cast(*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(*this).GetOrCreateLayerRenderDataForNode(inNode); + if (theLayer) + return theLayer->m_Camera; + return NULL; + } + + Option Qt3DSRendererImpl::GetCameraBounds(const SGraphObject &inObject) + { + if (GraphObjectTypes::IsNodeType(inObject.m_Type)) { + const SNode &theNode = static_cast(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(); + } + + 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 __scissorRect( theContext, + // &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect, theScissorRect ); + qt3ds::render::NVRenderContextScopedProperty __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 __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 + 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( + 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 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(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 IntersectRayWithNode(const SNode &inNode, + SRenderableObject &inRenderableObject, + const SRay &inPickRay) + { + if (inRenderableObject.m_RenderableFlags.IsText()) { + STextRenderable &theRenderable = static_cast(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( + 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(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(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 Qt3DSRendererImpl::FacePosition(SNode &inNode, NVBounds3 inBounds, + const QT3DSMat44 &inGlobalTransform, + const QT3DSVec2 &inViewportDimensions, + const QT3DSVec2 &inMouseCoords, + NVDataRef 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(currentObject); + SModel *theParentModel = NULL; + if (theImage.m_Parent + && theImage.m_Parent->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial *theMaterial = + static_cast(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 relativeHit = + FacePosition(*theParentModel, theModelBounds, theParentModel->m_GlobalTransform, + theViewportDimensions, theMouseCoords, NVDataRef(), + SBasisPlanes::XY); + if (relativeHit.isEmpty()) { + return Empty(); + } + Qt3DSRenderPickSubResult theResult = ConstructSubResult(theImage); + QT3DSVec2 hitInUVSpace = (*relativeHit) + QT3DSVec2(.5f, .5f); + eastl::pair mouseAndViewport = + GetMouseCoordsAndViewportFromSubObject(hitInUVSpace, theResult); + theMouseCoords = mouseAndViewport.first; + theViewportDimensions = mouseAndViewport.second; + } + } + + Option 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 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(*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(*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(*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 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 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 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 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 &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 mVertexBuffer; + NVScopedRefCounted mInputAssembler; + NVScopedRefCounted mAttribLayout; + // temporay FB + using qt3ds::render::NVRenderContextScopedProperty; + NVRenderContextScopedProperty __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 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 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 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 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 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(&inRenderableObject)->m_ModelContext.m_Model; + else if (inRenderableObject.m_RenderableFlags.IsText()) + thePickObject = &static_cast(&inRenderableObject)->m_Text; +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + else if (inRenderableObject.m_RenderableFlags.isDistanceField()) + thePickObject = &static_cast(&inRenderableObject)->m_text; +#endif + else if (inRenderableObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) + thePickObject = &static_cast(&inRenderableObject) + ->m_ModelContext.m_Model; + else if (inRenderableObject.m_RenderableFlags.IsPath()) + thePickObject = &static_cast(&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(&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 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()); + + // 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()); + 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 *)&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 *)&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 *)&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 *)&aoScreenConst, 1)); + QT3DSVec4 UvToEyeConst(2.0f * invFocalLenX, -2.0f * tanHalfFovY, -invFocalLenX, + tanHalfFovY); + pCB->UpdateParam("UvToEyeConst", NVDataRef((QT3DSU8 *)&UvToEyeConst, 1)); + + // update buffer to hardware + pCB->Update(); + } + } + + // widget context implementation + + NVRenderVertexBuffer &Qt3DSRendererImpl::GetOrCreateVertexBuffer(CRegisteredString &inStr, + QT3DSU32 stride, + NVConstDataRef 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 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 attribs) + { + // create our attribute layout + NVRenderAttribLayout *theAttribLAyout = m_Context->CreateAttributeLayout(attribs); + return *theAttribLAyout; + } + + NVRenderInputAssembler &Qt3DSRendererImpl::GetOrCreateInputAssembler( + CRegisteredString &inStr, NVRenderAttribLayout *attribLayout, + NVConstDataRef buffers, const NVRenderIndexBuffer *indexBuffer, + NVConstDataRef strides, NVConstDataRef 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 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 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 Qt3DSRendererImpl::GetLayerMouseCoords(SLayer &inLayer, + const QT3DSVec2 &inMouseCoords, + const QT3DSVec2 &inViewportDimensions, + bool forceImageIntersect) const + { + SLayerRenderData *theData = + const_cast(*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); + } +} +} -- cgit v1.2.3