diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Render')
24 files changed, 11294 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Render/IStudioRenderer.h b/src/Authoring/Qt3DStudio/Render/IStudioRenderer.h new file mode 100644 index 00000000..6adaafbf --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/IStudioRenderer.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_RENDERER +#define QT3DS_STUDIO_RENDERER +#pragma once + +#include "IDocSceneGraph.h" +#include <EASTL/vector.h> +#include <EASTL/string.h> +#include "Doc.h" + +#include <QRect> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace Q3DStudio { +using qt3ds::QT3DSI32; +class IStudioRenderer : public IDocSceneGraph +{ +protected: + virtual ~IStudioRenderer() {} + +public: + friend class std::shared_ptr<IStudioRenderer>; + + virtual bool IsInitialized() = 0; + + virtual void Initialize(QWidget *inWindow) = 0; + + virtual void SetViewRect(const QRect &inRect) = 0; + virtual void setFullSizePreview(bool enabled) = 0; + virtual void setIsSceneCameraView(bool sceneCameraView) = 0; + + virtual void GetEditCameraList(QStringList &outCameras) = 0; + virtual void SetPolygonFillModeEnabled(bool inEnableFillMode) = 0; + virtual bool IsPolygonFillModeEnabled() const = 0; + virtual bool DoesEditCameraSupportRotation(QT3DSI32 inIndex) = 0; + virtual bool AreGuidesEnabled() const = 0; + virtual void SetGuidesEnabled(bool val) = 0; + virtual bool AreGuidesEditable() const = 0; + virtual void SetGuidesEditable(bool val) = 0; + virtual bool getObjectError(qt3dsdm::Qt3DSDMInstanceHandle theInstance, + QString &error) const = 0; + // Setting the camera to -1 disables the edit cameras + // So setting the camera to 0- (numcameras - 1) will set change the active + // edit camera. + virtual void SetEditCamera(QT3DSI32 inIndex) = 0; + virtual QT3DSI32 GetEditCamera() const = 0; + virtual void EditCameraZoomToFit() = 0; + + virtual bool isMouseDown() const = 0; + + // This must be safe to call from multiple places + virtual void Close() = 0; + + // synchronously render the content + virtual void RenderNow() = 0; + virtual void getPreviewFbo(QSize &outFboDim, qt3ds::QT3DSU32 &outFboTexture) = 0; + + virtual void MakeContextCurrent() = 0; + virtual void ReleaseContext() = 0; + + virtual void RegisterSubpresentations( + const QVector<SubPresentationRecord> &subpresentations) = 0; + + // Uses the global studio app to get the doc and dispatch. + static std::shared_ptr<IStudioRenderer> CreateStudioRenderer(); +}; +}; + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/PathWidget.cpp b/src/Authoring/Qt3DStudio/Render/PathWidget.cpp new file mode 100644 index 00000000..90b82205 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/PathWidget.cpp @@ -0,0 +1,512 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "PathWidget.h" +#include "Qt3DSRenderWidgets.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "render/Qt3DSRenderInputAssembler.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathSubPath.h" +#include "Qt3DSRenderPathManager.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" + +using namespace qt3ds::widgets; + +namespace { + +QT3DSVec3 toVec3(QT3DSVec2 inPoint) +{ + return QT3DSVec3(inPoint.x, inPoint.y, 0.0f); +} +QT3DSVec3 toVec3(QT3DSVec2 inPoint, float z) +{ + return QT3DSVec3(inPoint.x, inPoint.y, z); +} + +struct SPointEntry +{ + QT3DSVec2 m_Position; + QT3DSVec3 m_Color; + QT3DSF32 m_ObjectId; + SPointEntry(QT3DSVec2 pos, QT3DSVec3 color, size_t objId) + : m_Position(pos) + , m_Color(color) + , m_ObjectId((QT3DSF32)objId) + { + } + SPointEntry() {} +}; + +struct SPathWidget : public IPathWidget +{ + typedef nvvector<eastl::pair<QT3DSU32, qt3ds::studio::SPathPick::EAnchorProperty>> + TReverseAnchorBuffer; + + NVAllocatorCallback &m_Allocator; + IQt3DSRenderContext &m_Context; + + NVScopedRefCounted<NVRenderVertexBuffer> m_PointVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_PointAssembler; + NVScopedRefCounted<NVRenderShaderProgram> m_PointShader; + NVScopedRefCounted<NVRenderShaderProgram> m_PointPickShader; + + NVScopedRefCounted<NVRenderVertexBuffer> m_LineVertexBuffer; + NVScopedRefCounted<NVRenderInputAssembler> m_LineAssembler; + NVScopedRefCounted<NVRenderShaderProgram> m_LineShader; + + nvvector<eastl::pair<QT3DSVec2, QT3DSVec2>> m_LineBuffer; + nvvector<SPointEntry> m_PointBuffer; + TReverseAnchorBuffer m_AnchorIndexBuffer; + SWidgetRenderSetupResult m_RenderSetup; + QT3DSVec2 m_PointViewportDimensions; + QT3DSMat44 m_PointMVP; + + QT3DSI32 m_RefCount; + SPathWidget(NVAllocatorCallback &inAlloc, IQt3DSRenderContext &inRc) + : m_Allocator(inAlloc) + , m_Context(inRc) + , m_LineBuffer(inAlloc, "m_LineBuffer") + , m_PointBuffer(inAlloc, "m_PointBuffer") + , m_AnchorIndexBuffer(inAlloc, "m_AnchorIndexBuffer") + , m_LineShader(nullptr) + , m_RefCount(0) + { + } + + void addRef() override { ++m_RefCount; } + void release() override + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } + + void SetNode(SNode &inNode) override { m_Node = &inNode; } + + QT3DSVec3 ToGlobalSpace(QT3DSVec3 inPoint) { return m_Node->m_GlobalTransform.transform(inPoint); } + + QT3DSVec3 ToCameraSpace(QT3DSVec3 inPoint) + { + QT3DSVec3 theGlobalPos = m_Node->m_GlobalTransform.transform(inPoint); + return m_RenderSetup.m_WidgetInfo.m_CameraGlobalInverse.transform(theGlobalPos); + } + + QT3DSVec3 ToCameraSpace(QT3DSVec2 inPoint) + { + return ToCameraSpace(QT3DSVec3(inPoint.x, inPoint.y, 0.0f)); + } + + void GeneratePointVertexGeometrySections(IShaderProgramGenerator &inProgramGenerator) + { + IShaderStageGenerator &vertex(*inProgramGenerator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &geometry( + *inProgramGenerator.GetStage(ShaderGeneratorStages::Geometry)); + vertex.AddIncoming("attr_pos", "vec2"); + vertex.AddIncoming("attr_color", "vec3"); + vertex.AddIncoming("attr_objid", "float"); + vertex.AddOutgoing("point_color", "vec3"); + vertex.AddOutgoing("object_id", "float"); + vertex.AddUniform("model_view_projection", "mat4"); + vertex << "void main()" << Endl << "{" << Endl + << "\tgl_Position = model_view_projection * vec4( attr_pos.xy, 0.0, 1.0 );" << Endl + << "\tpoint_color = attr_color;" << Endl << "\tobject_id = uint(attr_objid);" << Endl + << "}" << Endl; + + geometry.AddUniform("viewport_dimensions", "vec2"); + geometry.AddUniform("pointsize", "float"); + geometry.AddIncoming("point_color", "vec3"); + geometry.AddIncoming("object_id", "float"); + geometry.AddOutgoing("point_colorGE", "vec3"); + geometry.AddOutgoing("object_idGE", "float"); + geometry.AddOutgoing("uv_coords", "vec2"); + geometry.Append("layout (points) in;"); + geometry.Append("layout (triangle_strip, max_vertices = 4) out;"); + geometry.Append( + "void main() {" + "// project points to screen space\n" + "\tvec2 p0 = vec2(gl_in[0].gl_Position.xy) / gl_in[0].gl_Position.w;\n" + "\tvec2 increments = (pointsize / viewport_dimensions) / 2.0;\n" + "\tgl_Position = vec4( p0.x - increments.x, p0.y + increments.y, 0.0, 1.0 );\n" + "\tpoint_colorGE = point_color[0];\n" + "\tuv_coords = vec2(0.0, 0.0);\n" + "\tobject_idGE = object_id[0];\n" + "\tEmitVertex();\n" + "\tgl_Position = vec4( p0.x + increments.x, p0.y + increments.y, 0.0, 1.0 );\n" + "\tpoint_colorGE = point_color[0];\n" + "\tuv_coords = vec2(1.0, 0.0);\n" + "\tobject_idGE = object_id[0];\n" + "\tEmitVertex();\n" + "\tgl_Position = vec4( p0.x - increments.x, p0.y - increments.y, 0.0, 1.0 );\n" + "\tpoint_colorGE = point_color[0];\n" + "\tuv_coords = vec2(0.0, 1.0);\n" + "\tobject_idGE = object_id[0];\n" + "\tEmitVertex();\n" + "\tgl_Position = vec4( p0.x + increments.x, p0.y - increments.y, 0.0, 1.0 );\n" + "\tpoint_colorGE = point_color[0];\n" + "\tuv_coords = vec2(1.0, 1.0);\n" + "\tobject_idGE = object_id[0];\n" + "\tEmitVertex();\n" + "\tEndPrimitive();\n" + "}\n"); + } + + NVRenderShaderProgram *GetPointShader(IShaderProgramGenerator &inProgramGenerator) + { + if (m_PointShader) + return m_PointShader.mPtr; + inProgramGenerator.BeginProgram(IShaderProgramGenerator::DefaultFlags() + | ShaderGeneratorStages::Geometry); + GeneratePointVertexGeometrySections(inProgramGenerator); + + IShaderStageGenerator &fragment( + *inProgramGenerator.GetStage(ShaderGeneratorStages::Fragment)); + fragment.AddIncoming("point_colorGE", "vec3"); + fragment.AddIncoming("uv_coords", "vec2"); + fragment.Append("void main()\n" + "{\n" + "\tvec2 coords = uv_coords - vec2(.5, .5);\n" + "\tfloat coordLen = length( coords );\n" + "\tfloat margin = .4;\n" + "\tfloat leftover = min( 1.0, (coordLen - margin)/.1 );\n" + "\tfloat alpha = coordLen < margin ? 1.0 : mix( 1.0, 0.0, leftover );\n" + "\tfragOutput = vec4(point_colorGE, alpha);\n" + "}\n"); + + m_PointShader = inProgramGenerator.CompileGeneratedShader("path widget point shader"); + return m_PointShader.mPtr; + } + + NVRenderShaderProgram *GetPointPickShader(IShaderProgramGenerator &inProgramGenerator) + { + if (m_PointPickShader) + return m_PointPickShader.mPtr; + inProgramGenerator.BeginProgram(IShaderProgramGenerator::DefaultFlags() + | ShaderGeneratorStages::Geometry); + GeneratePointVertexGeometrySections(inProgramGenerator); + + IShaderStageGenerator &fragment( + *inProgramGenerator.GetStage(ShaderGeneratorStages::Fragment)); + fragment.AddIncoming("object_idGE", "float"); + fragment.AddIncoming("uv_coords", "vec2"); + fragment.Append("void main()\n" + "{\n" + "\tvec2 coords = uv_coords - vec2(.5, .5);\n" + "\tfloat coordLen = length( coords );\n" + "\tfloat margin = .4;\n" + "\tuint object_id = coordLen < margin ? uint(object_idGE + 1) : uint(0);\n" + "\tfragOutput.r = float(object_id % 256)/255.0;\n" + "\tfragOutput.g = float(object_id / 256)/255.0;\n" + //"\tfragOutput.g = float(object_id) / 10.0;\n" + "\tfragOutput.b = 0.0;\n" + "\tfragOutput.a = 1.0;\n" + "}\n"); + m_PointPickShader = + inProgramGenerator.CompileGeneratedShader("path widget point pick shader"); + return m_PointPickShader.mPtr; + } + + NVRenderShaderProgram *GetLineShader(IShaderProgramGenerator &inProgramGenerator) + { + if (m_LineShader) + return m_LineShader.mPtr; + + inProgramGenerator.BeginProgram(IShaderProgramGenerator::DefaultFlags() + | ShaderGeneratorStages::Geometry); + + IShaderStageGenerator &vertex(*inProgramGenerator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &geometry( + *inProgramGenerator.GetStage(ShaderGeneratorStages::Geometry)); + IShaderStageGenerator &fragment( + *inProgramGenerator.GetStage(ShaderGeneratorStages::Fragment)); + + vertex.AddIncoming("attr_pos", "vec2"); + vertex.AddUniform("model_view_projection", "mat4"); + vertex << "void main()" << Endl << "{" << Endl + << "\tgl_Position = model_view_projection * vec4( attr_pos.xy, 0.0, 1.0 );" << Endl + << "}" << Endl; + + geometry.AddUniform("viewport_dimensions", "vec2"); + geometry.AddUniform("linewidth", "float"); + geometry.AddOutgoing("uv_coords", "vec2"); + geometry.Append( + "layout (lines) in;\n" + "layout (triangle_strip, max_vertices = 4) out;\n" + "void main()\n" + "{\n" + "\tvec2 p0 = vec2(gl_in[0].gl_Position.xy) / gl_in[0].gl_Position.w;\n" + "\tvec2 p1 = vec2(gl_in[1].gl_Position.xy) / gl_in[1].gl_Position.w;\n" + "\tvec2 slope = normalize( p1 - p0 );\n" + "\tvec2 tangent = vec2(slope.y, -slope.x);\n" + "\tvec2 increments = (linewidth / viewport_dimensions) / 2.0;\n" + "\tvec2 tangentVec = vec2( tangent.x * increments.x, tangent.y * increments.y );\n" + "\tgl_Position = vec4( p0 - tangentVec, 0.0, 1.0);\n" + "\tuv_coords = vec2(0.0, 0.0);\n" + "\tEmitVertex();\n" + "\tgl_Position = vec4( p0 + tangentVec, 0.0, 1.0);\n" + "\tuv_coords = vec2(1.0, 0.0);\n" + "\tEmitVertex();\n" + "\tgl_Position = vec4( p1 - tangentVec, 0.0, 1.0);\n" + "\tuv_coords = vec2(0.0, 1.0);\n" + "\tEmitVertex();\n" + "\tgl_Position = vec4( p1 + tangentVec, 0.0, 1.0);\n" + "\tuv_coords = vec2(1.0, 1.0);\n" + "\tEmitVertex();\n" + "\tEndPrimitive();\n" + "}\n"); + + fragment.AddUniform("line_color", "vec3"); + fragment.AddIncoming("uv_coords", "vec2"); + fragment.Append("void main()\n" + "{\n" + "\tvec2 coords = uv_coords - vec2(.5, .5);\n" + "\tfloat coordLen = abs(coords.x);\n" + "\tfloat margin = .1;\n" + "\tfloat leftover = min( 1.0, (coordLen - margin)/.3 );\n" + "\tleftover = leftover * leftover;\n" + "\tfloat alpha = coordLen < margin ? 1.0 : mix( 1.0, 0.0, leftover );\n" + "\tfragOutput = vec4(line_color, alpha);\n" + "}\n"); + + m_LineShader = inProgramGenerator.CompileGeneratedShader("path widget line shader"); + return m_LineShader.mPtr; + } + + void RenderPointBuffer(NVRenderShaderProgram &inProgram, const QT3DSMat44 &inMVP, + NVRenderContext &inRenderContext) + { + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetStencilTestEnabled(false); + inRenderContext.SetBlendingEnabled(true); + inRenderContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha, + qt3ds::render::NVRenderSrcBlendFunc::One, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha)); + inRenderContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + inRenderContext.SetActiveShader(&inProgram); + inProgram.SetPropertyValue("model_view_projection", inMVP); + inProgram.SetPropertyValue("pointsize", (QT3DSF32)15.0f); + inProgram.SetPropertyValue("viewport_dimensions", m_PointViewportDimensions); + inRenderContext.SetInputAssembler(m_PointAssembler); + inRenderContext.Draw(NVRenderDrawMode::Points, m_PointBuffer.size(), 0); + } + + void PushPoint(const SPointEntry &inEntry, QT3DSU32 inAnchorIndex, + qt3ds::studio::SPathPick::EAnchorProperty inProperty) + { + QT3DSU32 anchorIndex = (QT3DSU32)m_PointBuffer.size(); + m_PointBuffer.push_back(inEntry); + m_AnchorIndexBuffer.push_back(eastl::make_pair(inAnchorIndex, inProperty)); + } + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override + { + if (!m_Node) + return; + SPath &thePath = static_cast<SPath &>(*m_Node); + IPathManager &theManager = m_Context.GetPathManager(); + + QT3DSVec3 anchorColor(0, 1, 0); + QT3DSVec3 controlColor(0, 0, 1); + m_LineBuffer.clear(); + m_PointBuffer.clear(); + // point index -> anchor index + m_AnchorIndexBuffer.clear(); + QT3DSU32 anchorIndex = 0; + + for (SPathSubPath *theSubPath = thePath.m_FirstSubPath; theSubPath; + theSubPath = theSubPath->m_NextSubPath) { + NVDataRef<qt3ds::render::SPathAnchorPoint> thePathBuffer( + theManager.GetPathSubPathBuffer(*theSubPath)); + if (thePathBuffer.size() == 0) + return; + + QT3DSU32 numAnchors = thePathBuffer.size(); + for (QT3DSU32 idx = 0, end = numAnchors; idx < end; ++idx) { + const qt3ds::render::SPathAnchorPoint &theAnchorPoint(thePathBuffer[idx]); + if (idx > 0) { + QT3DSVec2 incoming(qt3ds::render::IPathManagerCore::GetControlPointFromAngleDistance( + theAnchorPoint.m_Position, theAnchorPoint.m_IncomingAngle, + theAnchorPoint.m_IncomingDistance)); + PushPoint(SPointEntry(incoming, controlColor, m_PointBuffer.size()), + anchorIndex, qt3ds::studio::SPathPick::IncomingControl); + m_LineBuffer.push_back(eastl::make_pair(theAnchorPoint.m_Position, incoming)); + } + PushPoint(SPointEntry(theAnchorPoint.m_Position, anchorColor, m_PointBuffer.size()), + anchorIndex, qt3ds::studio::SPathPick::Anchor); + if (idx < (numAnchors - 1)) { + QT3DSVec2 outgoing(qt3ds::render::IPathManagerCore::GetControlPointFromAngleDistance( + theAnchorPoint.m_Position, theAnchorPoint.m_OutgoingAngle, + theAnchorPoint.m_OutgoingDistance)); + PushPoint(SPointEntry(outgoing, controlColor, m_PointBuffer.size()), + anchorIndex, qt3ds::studio::SPathPick::OutgoingControl); + m_LineBuffer.push_back(eastl::make_pair(theAnchorPoint.m_Position, outgoing)); + } + ++anchorIndex; + } + } + + m_RenderSetup = SWidgetRenderSetupResult(inWidgetContext, *m_Node, + qt3ds::render::RenderWidgetModes::Local); + + m_PointMVP = m_RenderSetup.m_WidgetInfo.m_LayerProjection + * m_RenderSetup.m_WidgetInfo.m_CameraGlobalInverse * m_Node->m_GlobalTransform; + + NVRenderRect theViewport = inRenderContext.GetViewport(); + m_PointViewportDimensions = QT3DSVec2((QT3DSF32)theViewport.m_Width, (QT3DSF32)theViewport.m_Height); + + if (!m_LineBuffer.empty()) { + QT3DSU32 vertItemSize = sizeof(QT3DSVec2); + QT3DSU32 vertBufSize = m_LineBuffer.size() * vertItemSize * 2; + if ((!m_LineVertexBuffer) || m_LineVertexBuffer->Size() < vertBufSize) { + m_LineVertexBuffer = inRenderContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Dynamic, vertBufSize, vertItemSize, + qt3ds::foundation::toU8DataRef(m_LineBuffer.data(), (QT3DSU32)m_LineBuffer.size())); + m_LineAssembler = nullptr; + } else + m_LineVertexBuffer->UpdateBuffer( + qt3ds::foundation::toU8DataRef(m_LineBuffer.data(), (QT3DSU32)m_LineBuffer.size())); + + if (m_LineAssembler == nullptr) { + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 0), + }; + NVRenderAttribLayout *theAttribLayout = + &inWidgetContext.CreateAttributeLayout(toConstDataRef(theEntries, 1)); + m_LineAssembler = inRenderContext.CreateInputAssembler( + theAttribLayout, toConstDataRef(&m_LineVertexBuffer.mPtr, 1), nullptr, + toConstDataRef(vertItemSize), toConstDataRef((QT3DSU32)0)); + } + inRenderContext.SetInputAssembler(m_LineAssembler); + NVRenderShaderProgram *lineShader = + GetLineShader(inWidgetContext.GetProgramGenerator()); + if (lineShader) { + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetStencilTestEnabled(false); + inRenderContext.SetBlendingEnabled(true); + inRenderContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument( + qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha, + qt3ds::render::NVRenderSrcBlendFunc::One, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha)); + inRenderContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + inRenderContext.SetActiveShader(lineShader); + lineShader->SetPropertyValue("model_view_projection", m_PointMVP); + lineShader->SetPropertyValue("line_color", QT3DSVec3(1.0, 1.0, 0.0)); + // Note the line needs to be wide enough to account for anti-aliasing. + lineShader->SetPropertyValue("linewidth", 3.0f); + lineShader->SetPropertyValue("viewport_dimensions", m_PointViewportDimensions); + inRenderContext.Draw(NVRenderDrawMode::Lines, m_LineBuffer.size() * 2, 0); + } + } + { + QT3DSU32 vertItemSize = (sizeof(SPointEntry)); + QT3DSU32 vertBufSize = m_PointBuffer.size() * vertItemSize; + if ((!m_PointVertexBuffer) || m_PointVertexBuffer->Size() < vertBufSize) { + m_PointVertexBuffer = inRenderContext.CreateVertexBuffer( + qt3ds::render::NVRenderBufferUsageType::Dynamic, vertBufSize, vertItemSize, + qt3ds::foundation::toU8DataRef(m_PointBuffer.data(), (QT3DSU32)m_PointBuffer.size())); + m_PointAssembler = nullptr; + } else + m_PointVertexBuffer->UpdateBuffer( + qt3ds::foundation::toU8DataRef(m_PointBuffer.data(), (QT3DSU32)m_PointBuffer.size())); + if (m_PointAssembler == nullptr) { + qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 0), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_color", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3, 8), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_objid", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 1, 20), + }; + NVRenderAttribLayout *theAttribLayout = + &inWidgetContext.CreateAttributeLayout(toConstDataRef(theEntries, 3)); + m_PointAssembler = inRenderContext.CreateInputAssembler( + theAttribLayout, toConstDataRef(&m_PointVertexBuffer.mPtr, 1), nullptr, + toConstDataRef(vertItemSize), toConstDataRef((QT3DSU32)0)); + } + NVRenderShaderProgram *thePointShader = + GetPointShader(inWidgetContext.GetProgramGenerator()); + GetPointPickShader(inWidgetContext.GetProgramGenerator()); + + if (thePointShader) { + m_PointMVP = m_RenderSetup.m_WidgetInfo.m_LayerProjection + * m_RenderSetup.m_WidgetInfo.m_CameraGlobalInverse * m_Node->m_GlobalTransform; + + RenderPointBuffer(*m_PointShader, m_PointMVP, inRenderContext); + } + } + } + + void RenderPick(const QT3DSMat44 &inProjPreMult, NVRenderContext &inRenderContext, + QSize inWinDimensions) override + { + if (m_PointAssembler == nullptr || m_PointPickShader == nullptr) + return; + // The projection premultiplication step moves the viewport around till + // it is centered over the mouse and scales everything *post* rendering (to keep appropriate + // aspect). + QT3DSMat44 theMVP = inProjPreMult * m_PointMVP; + m_PointViewportDimensions = + QT3DSVec2((QT3DSF32)inWinDimensions.width(), (QT3DSF32)inWinDimensions.height()); + + RenderPointBuffer(*m_PointPickShader, theMVP, inRenderContext); + } + + qt3ds::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) override + { + inPickIndex -= 1; + + if (inPickIndex < m_AnchorIndexBuffer.size()) + return qt3ds::studio::SPathPick(m_AnchorIndexBuffer[inPickIndex].first, + m_AnchorIndexBuffer[inPickIndex].second); + else { + QT3DS_ASSERT(false); + return qt3ds::studio::SPathPick(); + } + } +}; +} + +IPathWidget &IPathWidget::CreatePathWidget(NVAllocatorCallback &inCallback, IQt3DSRenderContext &inRc) +{ + return *QT3DS_NEW(inCallback, SPathWidget)(inCallback, inRc); +} diff --git a/src/Authoring/Qt3DStudio/Render/PathWidget.h b/src/Authoring/Qt3DStudio/Render/PathWidget.h new file mode 100644 index 00000000..6396b3e9 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/PathWidget.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_PATH_WIDGET_H +#define QT3DS_STUDIO_PATH_WIDGET_H +#pragma once +#include "StudioWidget.h" +#include "Qt3DSDMHandles.h" +#include "StudioPickValues.h" + +namespace qt3ds { +namespace widgets { + + class IPathWidget : public IStudioWidgetBase + { + public: + qt3ds::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) override = 0; + static IPathWidget &CreatePathWidget(NVAllocatorCallback &inAlloc, + IQt3DSRenderContext &inRenderContext); + }; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/StudioGradientWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioGradientWidget.cpp new file mode 100644 index 00000000..1b514f65 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioGradientWidget.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioGradientWidget.h" + +namespace qt3ds { +namespace widgets { + +using namespace qt3ds::render; + +NVConstDataRef<NVRenderVertexBufferEntry> +SGradientWidget::GetVertexBufferAttributesAndStride(QT3DSU32 &stride) +{ + static NVRenderVertexBufferEntry theEntries[] = { + NVRenderVertexBufferEntry("attr_pos", NVRenderComponentTypes::QT3DSF32, 3) + }; + + stride = 3 * sizeof(QT3DSF32); + + return toConstDataRef(theEntries, 1); +} + +NVRenderInputAssembler *SGradientWidget::CreateSphere(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, + QT3DSF32 radius) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr("Gradient"); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) + return retval; + + nvvector<QT3DSVec3> theVertexData(m_allocator, "SGradientWidget::theVertexData"); + nvvector<QT3DSI8> theIndexData(m_allocator, "SGradientWidget::theIndexData"); + + // the sphere is just a box + theVertexData.push_back(QT3DSVec3(-radius, radius, -radius)); + theVertexData.push_back(QT3DSVec3(radius, radius, -radius)); + theVertexData.push_back(QT3DSVec3(radius, -radius, -radius)); + theVertexData.push_back(QT3DSVec3(-radius, -radius, -radius)); + theVertexData.push_back(QT3DSVec3(-radius, radius, radius)); + theVertexData.push_back(QT3DSVec3(radius, radius, radius)); + theVertexData.push_back(QT3DSVec3(radius, -radius, radius)); + theVertexData.push_back(QT3DSVec3(-radius, -radius, radius)); + + theIndexData.push_back(0); theIndexData.push_back(1); theIndexData.push_back(2); + theIndexData.push_back(0); theIndexData.push_back(2); theIndexData.push_back(3); + + theIndexData.push_back(1); theIndexData.push_back(5); theIndexData.push_back(6); + theIndexData.push_back(1); theIndexData.push_back(6); theIndexData.push_back(2); + + theIndexData.push_back(5); theIndexData.push_back(4); theIndexData.push_back(7); + theIndexData.push_back(5); theIndexData.push_back(7); theIndexData.push_back(6); + + theIndexData.push_back(4); theIndexData.push_back(0); theIndexData.push_back(3); + theIndexData.push_back(4); theIndexData.push_back(3); theIndexData.push_back(7); + + theIndexData.push_back(4); theIndexData.push_back(5); theIndexData.push_back(1); + theIndexData.push_back(4); theIndexData.push_back(1); theIndexData.push_back(0); + + theIndexData.push_back(6); theIndexData.push_back(7); theIndexData.push_back(3); + theIndexData.push_back(6); theIndexData.push_back(3); theIndexData.push_back(2); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + GetVertexBufferAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + NVRenderIndexBuffer *theIndexBuffer = &inWidgetContext.GetOrCreateIndexBuffer( + theItemName, NVRenderComponentTypes::QT3DSU8, theIndexData.size(), + toU8DataRef(theIndexData.begin(), theIndexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +NVRenderShaderProgram *SGradientWidget::CreateGradientShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString itemName = inRenderContext.GetStringTable().RegisterStr("GradientShader"); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(itemName); + if (retval) + return retval; + + IShaderProgramGenerator &generator(inWidgetContext.GetProgramGenerator()); + generator.BeginProgram(); + IShaderStageGenerator &vertGenerator( + *generator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragGenerator( + *generator.GetStage(ShaderGeneratorStages::Fragment)); + vertGenerator.AddIncoming("attr_pos", "vec3"); + vertGenerator.AddOutgoing("pos", "vec3"); + vertGenerator.AddUniform("normalMatrix", "mat3"); + vertGenerator.AddUniform("projectionMatrix", "mat4"); + vertGenerator.AddUniform("drawMode", "int"); + // These are required in order to scale the scale widget the way we want to scale it. + vertGenerator.Append("void main() {"); + vertGenerator.Append("\tpos = attr_pos;"); + vertGenerator.Append("\tif (drawMode == 0)"); + vertGenerator.Append("\t\tgl_Position = projectionMatrix * vec4(normalMatrix * pos, 1.0);"); + vertGenerator.Append("\telse\n\t\tgl_Position = vec4(pos.x > 0 ? 1.0 : -1.0, pos.y > 0 \ + ? 1.0 : -1.0, 0.0, 1.0);"); + vertGenerator.Append("}"); + fragGenerator.AddIncoming("pos", "vec3"); + fragGenerator.AddUniform("color0", "vec3"); + fragGenerator.AddUniform("color1", "vec3"); + fragGenerator.AddUniform("color2", "vec3"); + fragGenerator.AddUniform("color3", "vec3"); + fragGenerator.AddUniform("drawMode", "int"); + fragGenerator.Append("void main() {"); + fragGenerator.Append("\tvec3 npos = normalize(pos);"); + fragGenerator.Append("\tvec3 color = vec3(0.0);"); + fragGenerator.Append("\tif (drawMode == 0) {"); + fragGenerator.Append("\t\tif (npos.y > 0.0)"); + fragGenerator.Append("\t\t\tcolor = mix(color1, color0, pow(npos.y, 0.25));"); + fragGenerator.Append("\t\telse\n\t\t\tcolor = mix(color3, color2, pow(-npos.y, 0.5));"); + fragGenerator.Append("\t} else {\n\t\tcolor = mix(color3, color0, 0.5 * npos.y + 0.5);\n\t}"); + fragGenerator.Append("\tgl_FragColor.rgb = color;"); + fragGenerator.Append("\tgl_FragColor.a = 1.0;"); + fragGenerator.Append("}"); + return inWidgetContext.CompileAndStoreShader(itemName); +} + +void SGradientWidget::Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext, + bool fullScreenQuad) +{ + if (m_sphere == nullptr) { + m_sphere = CreateSphere(inWidgetContext, inRenderContext, 100.0f); + m_shader = CreateGradientShader(inWidgetContext, inRenderContext); + + inRenderContext.SetActiveShader(m_shader); + m_shader->SetPropertyValue("color0", QT3DSVec3(0.6f, 0.6f, 0.6f)); + m_shader->SetPropertyValue("color1", QT3DSVec3(0.4f, 0.4f, 0.4f)); + m_shader->SetPropertyValue("color2", QT3DSVec3(0.1f, 0.1f, 0.1f)); + m_shader->SetPropertyValue("color3", QT3DSVec3(0.35f, 0.35f, 0.35f)); + } + + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_shader); + + if (fullScreenQuad) { + // draw fullscreen quad + m_shader->SetPropertyValue("drawMode", 1); + inRenderContext.SetInputAssembler(m_sphere); + inRenderContext.Draw(NVRenderDrawMode::Triangles, 6, 0); + } else { + // draw sphere + SWidgetRenderInformation info + = inWidgetContext.GetWidgetRenderInformation(*m_node, QT3DSVec3(), + RenderWidgetModes::Global); + m_shader->SetPropertyValue("drawMode", 0); + m_shader->SetPropertyValue("normalMatrix", info.m_NormalMatrix); + m_shader->SetPropertyValue("projectionMatrix", info.m_PureProjection); + + inRenderContext.SetInputAssembler(m_sphere); + + inRenderContext.Draw(NVRenderDrawMode::Triangles, + m_sphere->GetIndexCount(), 0); + } +} + + +SGradientWidget &SGradientWidget::CreateGradientWidget(NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, SGradientWidget)(inAlloc); +} + +} +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioGradientWidget.h b/src/Authoring/Qt3DStudio/Render/StudioGradientWidget.h new file mode 100644 index 00000000..0fd71c89 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioGradientWidget.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STUDIO_GRADIENT_WIDGET_H +#define STUDIO_GRADIENT_WIDGET_H + +#include "Qt3DSCommonPrecompile.h" +#include "StudioWidgetImpl.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "StudioUtils.h" + +namespace qt3ds { +namespace widgets { + +class SGradientWidget +{ + NVAllocatorCallback &m_allocator; + NVRenderInputAssembler *m_sphere; + NVRenderShaderProgram *m_shader; + SNode *m_node; + + volatile QT3DSI32 mRefCount; + +public: + SGradientWidget(NVAllocatorCallback &inAlloc) + : m_allocator(inAlloc) + , m_sphere(nullptr) + , m_shader(nullptr) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_allocator) + + NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> + GetVertexBufferAttributesAndStride(QT3DSU32 &stride); + NVRenderInputAssembler *CreateSphere(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, + QT3DSF32 radius); + + NVRenderShaderProgram *CreateGradientShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext, + bool fullScreenQuad); + + void SetNode(SNode &inNode) + { + m_node = &inNode; + } + + static SGradientWidget &CreateGradientWidget(NVAllocatorCallback &inAlloc); +}; + +} +} + +#endif // STUDIO_GRADIENT_WIDGET_H diff --git a/src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.cpp new file mode 100644 index 00000000..f1020fb1 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioHelperGridWidget.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "render/Qt3DSRenderShaderProgram.h" + +#include <QtGui/qcolor.h> + +using namespace qt3ds::render; + +namespace qt3ds { +namespace widgets { + +SHelperGridWidget::SHelperGridWidget(NVAllocatorCallback &inAlloc) + : IRenderWidget() + , m_rotation(QT3DSMat44::createIdentity()) + , m_dirty(true) + , m_allocator(inAlloc) + , mRefCount(0) +{ +} + +void SHelperGridWidget::setNode(SNode *node) +{ + m_Node = node; +} + +void SHelperGridWidget::rotate(float angleRadians, const QT3DSVec3 &axis) +{ + m_rotation = QT3DSMat44::createIdentity(); + if (angleRadians != 0.f) + m_rotation.rotate(angleRadians, axis); +} + +void SHelperGridWidget::setColors(const QColor &gridColor, const QColor &xColor, + const QColor &yColor) +{ + QT3DSVec3 theGridColor = QT3DSVec3(gridColor.redF(), gridColor.greenF(), gridColor.blueF()); + QT3DSVec3 theXColor = QT3DSVec3(xColor.redF(), xColor.greenF(), xColor.blueF()); + QT3DSVec3 theYColor = QT3DSVec3(yColor.redF(), yColor.greenF(), yColor.blueF()); + + if (theGridColor != m_gridColor || theXColor != m_xColor || theYColor != m_yColor) + m_dirty = true; + + m_gridColor = theGridColor; + m_xColor = theXColor; + m_yColor = theYColor; +} + +void SHelperGridWidget::setLines(int count, float spacing) +{ + if (count != m_lineCount || spacing != m_lineSpacing) + m_dirty = true; + + m_lineCount = count; + m_lineSpacing = spacing; +} + +void SHelperGridWidget::setupShader(IRenderWidgetContext &context) +{ + m_shader = context.GetShader(m_itemName); + if (!m_shader) { + qt3ds::render::IShaderProgramGenerator &generator(context.GetProgramGenerator()); + generator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &vertexGenerator( + *generator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &fragmentGenerator( + *generator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + vertexGenerator.AddIncoming("attr_pos", "vec3"); + vertexGenerator.AddIncoming("attr_color", "vec3"); + vertexGenerator.AddOutgoing("output_color", "vec3"); + vertexGenerator.AddUniform("model_view_projection", "mat4"); + vertexGenerator.Append("void main() {"); + vertexGenerator.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + vertexGenerator.Append("\toutput_color = attr_color;"); + vertexGenerator.Append("}"); + fragmentGenerator.Append("void main() {"); + fragmentGenerator.Append("\tgl_FragColor.rgb = output_color;"); + fragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + fragmentGenerator.Append("}"); + m_shader = context.CompileAndStoreShader(m_itemName); + } +} + +void SHelperGridWidget::setupGraphicsObjects(IRenderWidgetContext &context, + NVDataRef<QT3DSVec3> lines) +{ + qt3ds::render::NVRenderVertexBufferEntry entries[] = { + qt3ds::render::NVRenderVertexBufferEntry( + "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_color", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3, 12), + }; + + NVRenderVertexBuffer *vertexBuffer = &context.GetOrCreateVertexBuffer( + m_itemName, 6 * sizeof(QT3DSF32), toU8DataRef(lines.begin(), lines.size())); + m_inputAssembler = context.GetInputAssembler(m_itemName); + if (!m_inputAssembler && vertexBuffer) { + // create our attribute layout + NVRenderAttribLayout *attribLayout + = &context.CreateAttributeLayout(toConstDataRef(entries, 2)); + + QT3DSU32 strides = vertexBuffer->GetStride(); + QT3DSU32 offsets = 0; + m_inputAssembler = &context.GetOrCreateInputAssembler( + m_itemName, attribLayout, toConstDataRef(&vertexBuffer, 1), nullptr, + toConstDataRef(&strides, 1), toConstDataRef(&offsets, 1)); + } +} + +void SHelperGridWidget::Render(IRenderWidgetContext &widgetContext, NVRenderContext &renderContext) +{ + m_itemName = renderContext.GetStringTable().RegisterStr("SHelperGridWidget"); + + setupShader(widgetContext); + + if (m_shader) { + SWidgetRenderInformation theInfo(widgetContext.GetWidgetRenderInformation( + *m_Node, QT3DSVec3(0), RenderWidgetModes::Local)); + + QT3DSMat44 nodeToCamera = theInfo.m_NodeParentToCamera * m_Node->m_LocalTransform + * m_rotation; + + if (m_dirty) { + m_dirty = false; + // Line data is line end points and color for each point + const int lineCount = m_lineCount * 2 + 1; + const int centerLineIndex = m_lineCount; + const float lineStart = float(m_lineCount) * -m_lineSpacing; + const int totalCount = lineCount * 8; + m_lineData.resize(totalCount); + // Create grid of lines along y-plane. Center lines are colored according to axes. + for (int i = 0; i < lineCount; ++i) { + int idx = i * 8; + m_lineData[idx] = QT3DSVec3(lineStart + m_lineSpacing * i, 0.f, lineStart); + m_lineData[idx + 2] = QT3DSVec3(lineStart + m_lineSpacing * i, 0.f, -lineStart); + m_lineData[idx + 4] = QT3DSVec3(lineStart, 0.f, lineStart + m_lineSpacing * i); + m_lineData[idx + 6] = QT3DSVec3(-lineStart, 0.f, lineStart + m_lineSpacing * i); + if (i == centerLineIndex) { + m_lineData[idx + 1] = m_yColor; + m_lineData[idx + 3] = m_yColor; + m_lineData[idx + 5] = m_xColor; + m_lineData[idx + 7] = m_xColor; + } else { + m_lineData[idx + 1] = m_gridColor; + m_lineData[idx + 3] = m_gridColor; + m_lineData[idx + 5] = m_gridColor; + m_lineData[idx + 7] = m_gridColor; + } + } + setupGraphicsObjects(widgetContext, + toDataRef(static_cast<QT3DSVec3 *>(m_lineData.data()), + QT3DSU32(totalCount))); + } + + if (m_inputAssembler) { + renderContext.SetBlendingEnabled(false); + renderContext.SetDepthWriteEnabled(true); + renderContext.SetDepthTestEnabled(true); + renderContext.SetCullingEnabled(false); + renderContext.SetActiveShader(m_shader); + m_shader->SetPropertyValue("model_view_projection", + theInfo.m_LayerProjection * nodeToCamera); + renderContext.SetInputAssembler(m_inputAssembler); + renderContext.Draw(qt3ds::render::NVRenderDrawMode::Lines, m_lineCount * 8 + 8, 0); + } + } +} + +SHelperGridWidget &SHelperGridWidget::createHelperGridWidget(NVAllocatorCallback &alloc) +{ + return *QT3DS_NEW(alloc, SHelperGridWidget)(alloc); +} + +} +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.h b/src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.h new file mode 100644 index 00000000..d3c8c497 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_HELPER_GRID_WIDGET_H +#define QT3DS_STUDIO_HELPER_GRID_WIDGET_H + +#include "Qt3DSRender.h" +#include "Qt3DSRenderWidgets.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSContainers.h" + +using namespace qt3ds::render; + +namespace qt3ds { +namespace widgets { + +struct SHelperGridWidget : public IRenderWidget, public NVRefCounted +{ + QT3DSVec3 m_gridColor = QT3DSVec3(0.5f, 0.5f, 0.5f); + QT3DSVec3 m_xColor = QT3DSVec3(1.f, 0.f, 0.f); + QT3DSVec3 m_yColor = QT3DSVec3(0.f, 1.f, 0.f); + QVector<QT3DSVec3> m_lineData; + int m_lineCount = 10; // Number of lines each side of center line + float m_lineSpacing = 50; + QT3DSMat44 m_rotation; + NVScopedRefCounted<NVRenderInputAssembler> m_inputAssembler; + NVScopedRefCounted<NVRenderShaderProgram> m_shader; + CRegisteredString m_itemName; + bool m_dirty; + + NVAllocatorCallback &m_allocator; + volatile QT3DSI32 mRefCount; + + SHelperGridWidget(NVAllocatorCallback &inAlloc); + virtual ~SHelperGridWidget() override {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_allocator) + + void setNode(SNode *node); + void rotate(float angleRadians, const QT3DSVec3 &axis); + void setColors(const QColor &gridColor, const QColor &xColor, const QColor &yColor); + void setLines(int count, float spacing); + void setupShader(IRenderWidgetContext &context); + void setupGraphicsObjects(IRenderWidgetContext &context, NVDataRef<QT3DSVec3> lines); + void Render(IRenderWidgetContext &widgetContext, NVRenderContext &renderContext) override; + + static SHelperGridWidget &createHelperGridWidget(NVAllocatorCallback &alloc); +}; + +} +} + + +#endif // QT3DS_STUDIO_HELPER_GRID_WIDGET_H diff --git a/src/Authoring/Qt3DStudio/Render/StudioPickValues.h b/src/Authoring/Qt3DStudio/Render/StudioPickValues.h new file mode 100644 index 00000000..3d59fd87 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioPickValues.h @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_PICK_VALUES_H +#define QT3DS_STUDIO_PICK_VALUES_H +#pragma once +#include "foundation/Qt3DSDiscriminatedUnion.h" +#include "foundation/Qt3DSUnionCast.h" +#include "Qt3DSDMHandles.h" +#include "StaticMaxSize.h" + +namespace qt3ds { +namespace studio { + using qt3dsdm::Qt3DSDMInstanceHandle; + using qt3dsdm::Qt3DSDMGuideHandle; + + struct StudioPickValueTypes + { + enum Enum { + UnknownValueType = 0, + Instance, + Widget, + Guide, + Path, + }; + }; + + struct SWidgetPick + { + qt3ds::QT3DSI32 m_WidgetId; + SWidgetPick(qt3ds::QT3DSI32 id = 0) + : m_WidgetId(id) + { + } + }; + + struct SPathPick + { + enum EAnchorProperty { + Anchor = 0, + IncomingControl, + OutgoingControl, + }; + + qt3ds::QT3DSU32 m_AnchorIndex; + EAnchorProperty m_Property; + + SPathPick() + : m_AnchorIndex(0) + , m_Property(Anchor) + { + } + + SPathPick(qt3ds::QT3DSU32 ai, EAnchorProperty p) + : m_AnchorIndex(ai) + , m_Property(p) + { + } + }; + + template <typename TDataType> + struct SStudioPickValueTypeMap + { + }; + + template <> + struct SStudioPickValueTypeMap<Qt3DSDMInstanceHandle> + { + static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Instance; } + }; + + template <> + struct SStudioPickValueTypeMap<SWidgetPick> + { + static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Widget; } + }; + + template <> + struct SStudioPickValueTypeMap<Qt3DSDMGuideHandle> + { + static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Guide; } + }; + + template <> + struct SStudioPickValueTypeMap<SPathPick> + { + static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Path; } + }; + + struct SStudioPickValueTraits + { + typedef StudioPickValueTypes::Enum TIdType; + enum { + TBufferSize = Q3DStudio::StaticMaxSize<qt3dsdm::Qt3DSDMInstanceHandle, + SWidgetPick, + qt3dsdm::Qt3DSDMGuideHandle, + SPathPick>::value + }; + + static TIdType getNoDataId() { return StudioPickValueTypes::UnknownValueType; } + + template <typename TDataType> + static TIdType getType() + { + return SStudioPickValueTypeMap<TDataType>().GetType(); + } + + template <typename TRetType, typename TVisitorType> + static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor) + { + switch (inType) { + case StudioPickValueTypes::Instance: + return inVisitor(*qt3ds::NVUnionCast<qt3dsdm::Qt3DSDMInstanceHandle *>(inData)); + case StudioPickValueTypes::Widget: + return inVisitor(*qt3ds::NVUnionCast<SWidgetPick *>(inData)); + case StudioPickValueTypes::Guide: + return inVisitor(*qt3ds::NVUnionCast<qt3dsdm::Qt3DSDMGuideHandle *>(inData)); + case StudioPickValueTypes::Path: + return inVisitor(*qt3ds::NVUnionCast<SPathPick *>(inData)); + default: + QT3DS_ASSERT(false); + case StudioPickValueTypes::UnknownValueType: + return inVisitor(); + } + } + + template <typename TRetType, typename TVisitorType> + static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor) + { + switch (inType) { + case StudioPickValueTypes::Instance: + return inVisitor(*qt3ds::NVUnionCast<const qt3dsdm::Qt3DSDMInstanceHandle *>(inData)); + case StudioPickValueTypes::Widget: + return inVisitor(*qt3ds::NVUnionCast<const SWidgetPick *>(inData)); + case StudioPickValueTypes::Guide: + return inVisitor(*qt3ds::NVUnionCast<const qt3dsdm::Qt3DSDMGuideHandle *>(inData)); + case StudioPickValueTypes::Path: + return inVisitor(*qt3ds::NVUnionCast<const SPathPick *>(inData)); + default: + QT3DS_ASSERT(false); + case StudioPickValueTypes::UnknownValueType: + return inVisitor(); + } + } + }; + + typedef qt3ds::foundation:: + DiscriminatedUnion<qt3ds::foundation:: + DiscriminatedUnionGenericBase<SStudioPickValueTraits, + SStudioPickValueTraits::TBufferSize>, + SStudioPickValueTraits::TBufferSize> + TStudioPickValueType; + + struct SStudioPickValue : public TStudioPickValueType + { + SStudioPickValue() {} + SStudioPickValue(Qt3DSDMInstanceHandle inst) + : TStudioPickValueType(inst) + { + } + SStudioPickValue(SWidgetPick inst) + : TStudioPickValueType(inst) + { + } + SStudioPickValue(Qt3DSDMGuideHandle inst) + : TStudioPickValueType(inst) + { + } + SStudioPickValue(SPathPick inst) + : TStudioPickValueType(inst) + { + } + SStudioPickValue(const SStudioPickValue &other) + : TStudioPickValueType(static_cast<const TStudioPickValueType &>(other)) + { + } + SStudioPickValue &operator=(const SStudioPickValue &other) + { + TStudioPickValueType::operator=(static_cast<const TStudioPickValueType &>(other)); + return *this; + } + int GetWidgetId() const + { + if (getType() == StudioPickValueTypes::Widget) + return getData<SWidgetPick>().m_WidgetId; + return 0; + } + }; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/StudioRenderer.cpp b/src/Authoring/Qt3DStudio/Render/StudioRenderer.cpp new file mode 100644 index 00000000..e2af9606 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioRenderer.cpp @@ -0,0 +1,1160 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioRendererImpl.h" +#include "StudioRendererTranslation.h" +#include "StudioPreferences.h" +#include "HotKeys.h" +#include "StudioUtils.h" +#include "Qt3DSMath.h" +#include "Qt3DSOffscreenRenderKey.h" +#include "Qt3DSOffscreenRenderManager.h" +#include "q3dsqmlrender.h" +#include "q3dsqmlstreamproxy.h" +#include "StudioSubPresentationRenderer.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderEffectSystem.h" + +#include <QtCore/qdebug.h> + +#ifdef _WIN32 +#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union +#endif +using namespace qt3ds::studio; + +namespace { + +const QT3DSU32 g_WheelFactor = 10; // the wheel zoom factor + +struct SEditCameraDefinition +{ + EditCameraTypes::Enum m_Type; + // Directional cameras have a direction they point + QT3DSVec3 m_Direction; // not normalized + QString m_Name; +}; + +SEditCameraDefinition g_EditCameraDefinitions[] = { + { EditCameraTypes::Perspective, QT3DSVec3(1, -1, -1), QObject::tr("Perspective View") }, + { EditCameraTypes::Orthographic, QT3DSVec3(1, -1, -1), QObject::tr("Orthographic View") }, + { EditCameraTypes::Directional, QT3DSVec3(0, -1, 0), QObject::tr("Top View") }, + { EditCameraTypes::Directional, QT3DSVec3(0, 1, 0), QObject::tr("Bottom View") }, + { EditCameraTypes::Directional, QT3DSVec3(1, 0, 0), QObject::tr("Left View") }, + { EditCameraTypes::Directional, QT3DSVec3(-1, 0, 0), QObject::tr("Right View") }, + { EditCameraTypes::Directional, QT3DSVec3(0, 0, -1), QObject::tr("Front View") }, + { EditCameraTypes::Directional, QT3DSVec3(0, 0, 1), QObject::tr("Back View") }, +}; +QT3DSU32 g_NumEditCameras = sizeof(g_EditCameraDefinitions) / sizeof(*g_EditCameraDefinitions); + +struct StudioSubPresentation +{ + SubPresentationRecord subpresentation; + IOffscreenRenderer *renderer; + + bool operator == (const SubPresentationRecord &r) const + { + return r.m_id == subpresentation.m_id && + r.m_argsOrSrc == subpresentation.m_argsOrSrc && + r.m_type == subpresentation.m_type; + } +}; + +struct SRendererImpl : public IStudioRenderer, + public IDataModelListener, + public IReloadListener, + public CPresentationChangeListener, + public CSceneDragListener, + public CToolbarChangeListener, + public IOffscreenRenderer::IOffscreenRendererCallback +{ + typedef eastl::vector<Option<SEditorCameraInformation>> TEditCameraInfoList; + std::shared_ptr<CWGLRenderContext> m_RenderContext; + NVScopedRefCounted<IQt3DSRenderContext> m_Context; + QRect m_Rect; + CDispatch &m_Dispatch; + CDoc &m_Doc; + std::shared_ptr<STranslation> m_Translation; + CPt m_MouseDownPoint; + CPt m_PreviousMousePoint; + bool m_HasPresentation; + bool m_Closed; + CUpdateableDocumentEditor m_UpdatableEditor; + MovementTypes::Enum m_LastDragToolMode; + bool m_MaybeDragStart; + TEditCameraInfoList m_EditCameraInformation; + QT3DSI32 m_EditCameraIndex; + SEditorCameraInformation m_MouseDownCameraInformation; + SStudioPickValue m_PickResult; + bool m_RenderRequested; + int m_LastToolMode; + bool m_GuidesEnabled; + qt3dsdm::TSignalConnectionPtr m_SelectionSignal; + float m_pixelRatio; + QHash<QString, StudioSubPresentation> m_subpresentations; + QScopedPointer<Q3DSQmlStreamProxy> m_proxy; + QMap<QString, int> m_initialFrameMap; + bool m_fullSizePreview = false; + bool m_mouseDown = false; + + SRendererImpl() + : m_Dispatch(*g_StudioApp.GetCore()->GetDispatch()) + , m_Doc(*g_StudioApp.GetCore()->GetDoc()) + , m_HasPresentation(false) + , m_Closed(false) + , m_UpdatableEditor(m_Doc) + , m_LastDragToolMode(MovementTypes::Unknown) + , m_MaybeDragStart(false) + , m_EditCameraIndex(-1) + , m_RenderRequested(false) + , m_LastToolMode(0) + , m_GuidesEnabled(true) + , m_pixelRatio(0.0) + { + m_Dispatch.AddReloadListener(this); + m_Dispatch.AddDataModelListener(this); + m_Dispatch.AddPresentationChangeListener(this); + m_SelectionSignal = + m_Dispatch.ConnectSelectionChange(std::bind(&SRendererImpl::OnSelectionChange, this)); + m_Dispatch.AddSceneDragListener(this); + m_Dispatch.AddToolbarChangeListener(this); + } + ~SRendererImpl() override + { + Close(); + m_Dispatch.RemoveDataModelListener(this); + m_Dispatch.RemovePresentationChangeListener(this); + m_Dispatch.RemoveSceneDragListener(this); + m_Dispatch.RemoveToolbarChangeListener(this); + } + + // IDocSceneGraph + QT3DSVec3 GetIntendedPosition(qt3dsdm::Qt3DSDMInstanceHandle inHandle, CPt inPoint) override + { + if (m_Translation) + return m_Translation->GetIntendedPosition(inHandle, inPoint); + + return QT3DSVec3(0, 0, 0); + } + + void RegisterSubpresentations(const QVector<SubPresentationRecord> &subpresentations) override + { + if (m_proxy.isNull()) + m_proxy.reset(new Q3DSQmlStreamProxy()); + IOffscreenRenderManager &offscreenMgr(m_Context->GetOffscreenRenderManager()); + const QString projectPath = m_Doc.GetCore()->getProjectFile().getProjectPath(); + // setPath expects full path, but strips the filename + m_proxy->setPath(projectPath + QLatin1Char('/')); + QVector<SubPresentationRecord> toUnregister; + QVector<SubPresentationRecord> toRegister; + const auto keys = m_subpresentations.keys(); + for (QString key : keys) { + if (!subpresentations.contains(m_subpresentations[key].subpresentation)) + toUnregister.append(m_subpresentations[key].subpresentation); + } + + for (int i = 0; i < subpresentations.size(); ++i) { + if (!m_subpresentations.contains(subpresentations[i].m_id) + || !(m_subpresentations[subpresentations[i].m_id] == subpresentations[i])) { + toRegister.append(subpresentations[i]); + } + } + + for (int i = 0; i < toUnregister.size(); ++i) { + QByteArray data = toUnregister[i].m_id.toLocal8Bit(); + qt3ds::render::CRegisteredString rid + = m_Context->GetStringTable().RegisterStr(data.data()); + offscreenMgr.ReleaseOffscreenRenderer(qt3ds::render::SOffscreenRendererKey(rid)); + m_subpresentations.remove(toUnregister[i].m_id); + m_proxy->unregisterPresentation(toUnregister[i].m_id); + } + + for (int i = 0; i < toRegister.size(); ++i) { + QByteArray data = toRegister[i].m_id.toLocal8Bit(); + qt3ds::render::CRegisteredString rid + = m_Context->GetStringTable().RegisterStr(data.data()); + if (toRegister[i].m_type == QStringLiteral("presentation-qml")) { + m_proxy->registerPresentation(toRegister[i].m_id, toRegister[i].m_argsOrSrc); + + qt3ds::render::IOffscreenRenderer *theOffscreenRenderer = + QT3DS_NEW(m_Context->GetAllocator(), + Q3DSQmlRender)(*m_Context, data.data()); + offscreenMgr.RegisterOffscreenRenderer( + qt3ds::render::SOffscreenRendererKey(rid), *theOffscreenRenderer); + m_subpresentations[toRegister[i].m_id].renderer = theOffscreenRenderer; + theOffscreenRenderer->addCallback(this); + } else { + qt3ds::render::IOffscreenRenderer *theOffscreenRenderer = + QT3DS_NEW(m_Context->GetAllocator(), + StudioSubpresentationRenderer)(*m_Context, toRegister[i].m_id, + toRegister[i].m_argsOrSrc, + projectPath); + offscreenMgr.RegisterOffscreenRenderer( + qt3ds::render::SOffscreenRendererKey(rid), *theOffscreenRenderer); + m_subpresentations[toRegister[i].m_id].renderer = theOffscreenRenderer; + theOffscreenRenderer->addCallback(this); + } + m_subpresentations[toRegister[i].m_id].subpresentation = toRegister[i]; + } + // Process qml proxy events so that we have initialized the qml producer, + // then get the desired environment to initialize the qml renderer. + QCoreApplication::processEvents(); + for (int i = 0; i < toRegister.size(); ++i) { + if (toRegister[i].m_type == QLatin1String("presentation-qml")) + m_subpresentations[toRegister[i].m_id].renderer + ->GetDesiredEnvironment(QT3DSVec2(1.0f, 1.0f)); + } + RequestRender(); + } + + void ReleaseOffscreenRenderersForSubpresentations() + { + if (!m_Context.mPtr) + return; + + IOffscreenRenderManager &offscreenMgr(m_Context->GetOffscreenRenderManager()); + + QVector<SubPresentationRecord> toUnregister; + + const auto keys = m_subpresentations.keys(); + for (QString key : keys) + toUnregister.append(m_subpresentations[key].subpresentation); + + for (int i = 0; i < toUnregister.size(); ++i) { + QByteArray data = toUnregister[i].m_id.toLocal8Bit(); + qt3ds::render::CRegisteredString rid + = m_Context->GetStringTable().RegisterStr(data.data()); + offscreenMgr.ReleaseOffscreenRenderer(qt3ds::render::SOffscreenRendererKey(rid)); + } + } + + void onOffscreenRendererInitialized(const QString &id) override + { + // Request render after first frame rendered by the offscreen renderer + m_initialFrameMap[id] = 1; + } + + void onOffscreenRendererFrame(const QString &id) override + { + if (m_initialFrameMap.contains(id)) { + RequestRender(); + m_initialFrameMap.remove(id); + } + } + + ITextRenderer *GetTextRenderer() override + { + if (m_Context.mPtr) + return m_Context->GetTextRenderer(); + return nullptr; + } + + ITextRenderer *GetDistanceFieldRenderer() override + { + if (m_Context.mPtr) + return m_Context->getDistanceFieldRenderer(); + return nullptr; + } + + // The buffer manager may not be available + IBufferManager *GetBufferManager() override + { + if (m_Context.mPtr) + return &m_Context->GetBufferManager(); + return nullptr; + } + + IPathManager *GetPathManager() override + { + if (m_Context.mPtr) + return &m_Context->GetPathManager(); + return nullptr; + } + + qt3ds::foundation::IStringTable *GetRenderStringTable() override + { + if (m_Context.mPtr) + return &m_Context->GetStringTable(); + return nullptr; + } + + bool IsInitialized() override { return m_Context.mPtr != nullptr; } + + void Initialize(QWidget *inWindow) override + { + if (m_Closed) + return; + QT3DS_ASSERT(!m_RenderContext); + QT3DS_ASSERT(m_Context.mPtr == nullptr); + try { + m_RenderContext = std::make_shared<CWGLRenderContext>(inWindow); + + Q3DStudio::CString theResourcePath = Q3DStudio::CString::fromQString( + StudioUtils::resourcePath()); + NVScopedRefCounted<qt3ds::render::IQt3DSRenderContextCore> theCore = + qt3ds::render::IQt3DSRenderContextCore::Create( + m_RenderContext->GetRenderContext().GetFoundation(), + m_RenderContext->GetRenderContext().GetStringTable()); + + // Create legacy text renderer - studio needs this for drag widget text rendering. + // Legacy renderer also has better separation between project and system fonts, so + // we also want to keep it around for those purposes. Actual rendering of text is done + // with distance field renderer if it is enabled. + qt3ds::render::ITextRendererCore &theTextRenderer( + qt3ds::render::ITextRendererCore::CreateQtTextRenderer( + m_RenderContext->GetRenderContext().GetFoundation(), + m_RenderContext->GetRenderContext().GetStringTable())); + theCore->SetTextRendererCore(theTextRenderer); + +#if QT_VERSION >= QT_VERSION_CHECK(5,12,2) + if (qt3ds::render::IQt3DSRenderContextCore::distanceFieldEnabled()) { + ITextRendererCore &distanceFieldRenderer( + ITextRendererCore::createDistanceFieldRenderer( + m_RenderContext->GetRenderContext().GetFoundation())); + theCore->setDistanceFieldRenderer(distanceFieldRenderer); + } +#endif + + m_Context = theCore->CreateRenderContext( + m_RenderContext->GetRenderContext(), + m_RenderContext->GetRenderContext().GetStringTable().RegisterStr( + theResourcePath.c_str()), false, nullptr); + + // Allow the artist to interact with the top level objects alone. + m_Context->GetRenderer().PickRenderPlugins(false); + + SetupTextRenderer(); + + m_Context->SetAuthoringMode(true); + + InitializePointerTags(m_Context->GetStringTable()); + SetViewRect(m_Rect); +#ifdef KDAB_TEMPORARILY_REMOVE + // KDAB_TODO the below call asserts on windows + m_RenderContext->GetRenderContext().SetClearColor(QT3DSVec4(0, 0, 0, 1)); +#endif + if (m_HasPresentation) + CreateTranslator(); + + // Notify that renderer has been initialized + m_Dispatch.FireOnRendererInitialized(); + } catch (...) { + m_Context = nullptr; + m_RenderContext = std::shared_ptr<CWGLRenderContext>(); + throw; + } + } + + void SetViewRect(const QRect &inRect) override + { + if (m_RenderContext) + m_RenderContext->resized(); + + m_Rect = inRect; + if (IsInitialized()) { + m_pixelRatio = StudioUtils::devicePixelRatio(); + SetTranslationViewport(); + } + } + + void setFullSizePreview(bool enabled) override + { + m_fullSizePreview = enabled; + } + + void setIsSceneCameraView(bool sceneCameraView) override + { + m_RenderContext->GetRenderContext().setIsSceneCameraView(sceneCameraView); + } + + // Request that this object renders. May be ignored if a transaction + // is ongoing so we don't get multiple rendering per transaction. + void RequestRender() override + { + if (m_RenderContext) + m_RenderContext->requestRender(); + } + + void RenderRequestedRender() + { + if (m_RenderRequested) { + m_RenderContext->requestRender(); + } + } + + void RenderNow() override + { + Render(); + } + + bool getObjectError(qt3dsdm::Qt3DSDMInstanceHandle theInstance, QString &error) const + { + auto translator = m_Translation->GetOrCreateTranslator(theInstance); + if (translator) { + error = static_cast<SGraphObjectTranslator *>(translator)->GetError(); + return true; + } + error = QString(); + return false; + } + + void getPreviewFbo(QSize &outFboDim, qt3ds::QT3DSU32 &outFboTexture) override + { + if (m_Translation) { + outFboDim = QSize(m_Translation->m_previewFboDimensions.x, + m_Translation->m_previewFboDimensions.y); + // The handle is a void * so first cast to size_t to avoid truncating pointer warning + if (m_Translation->m_previewTexture) { + outFboTexture = static_cast<qt3ds::QT3DSU32>(reinterpret_cast<size_t>( + m_Translation->m_previewTexture->GetTextureObjectHandle())); + } else { + outFboTexture = 0; + } + + } else { + outFboDim = QSize(0, 0); + outFboTexture = 0; + } + } + + bool isMouseDown() const override + { + return m_mouseDown; + } + + void MakeContextCurrent() override + { + if (m_RenderContext) + m_RenderContext->BeginRender(); + } + + void ReleaseContext() override + { + if (m_RenderContext) + m_RenderContext->EndRender(); + } + + void Render() + { + m_RenderRequested = false; + if (!m_Closed && IsInitialized()) { + m_RenderContext->BeginRender(); + if (m_Translation) + m_Translation->PreRender(m_fullSizePreview); + NVRenderContext &theContext = m_RenderContext->GetRenderContext(); + theContext.SetDepthWriteEnabled(true); + theContext.Clear(qt3ds::render::NVRenderClearFlags( + qt3ds::render::NVRenderClearValues::Color + | qt3ds::render::NVRenderClearValues::Depth)); + if (m_Translation) { + if (m_fullSizePreview) { + // Full size preview is used for scene camera tab + m_Translation->Render(0, false, true); + m_Translation->PreRender(false); + } + m_Translation->Render(m_PickResult.GetWidgetId(), m_GuidesEnabled, false); + } + + m_RenderContext->EndRender(); + } + } + void GetEditCameraList(QStringList &outCameras) override + { + outCameras.clear(); + for (QT3DSU32 idx = 0; idx < g_NumEditCameras; ++idx) + outCameras.push_back(g_EditCameraDefinitions[idx].m_Name); + } + void SetPolygonFillModeEnabled(bool inEnableLight) override + { + CStudioPreferences::setEditViewFillMode(inEnableLight); + RequestRender(); + } + + bool DoesEditCameraSupportRotation(QT3DSI32 inIndex) override + { + if (inIndex >= 0 && inIndex < (QT3DSI32)g_NumEditCameras) + return g_EditCameraDefinitions[inIndex].m_Type != EditCameraTypes::Directional; + return false; + } + + bool AreGuidesEnabled() const override { return m_GuidesEnabled; } + + void SetGuidesEnabled(bool val) override { m_GuidesEnabled = val; } + + bool AreGuidesEditable() const override { return m_Doc.isValid() ? m_Doc.GetDocumentReader().AreGuidesEditable() : false; } + + void SetGuidesEditable(bool val) override { if (m_Doc.isValid()) m_Doc.GetDocumentReader().SetGuidesEditable(val); } + + // Setting the camera to -1 disables the edit cameras + // So setting the camera to 0- (numcameras - 1) will set change the active + // edit camera. + void SetEditCamera(QT3DSI32 inIndex) override + { + QT3DSI32 oldIndex = m_EditCameraIndex; + m_EditCameraIndex = qMin(inIndex, (QT3DSI32)g_NumEditCameras); + // save the old edit camera information + if (oldIndex != m_EditCameraIndex && m_Translation && m_Translation->m_EditCameraEnabled) { + while (m_EditCameraInformation.size() <= (QT3DSU32)oldIndex) + m_EditCameraInformation.push_back(Empty()); + + m_EditCameraInformation[oldIndex] = m_Translation->m_EditCameraInfo; + } + + ApplyEditCameraIndex(); + RequestRender(); + } + + QT3DSI32 GetEditCamera() const override + { + if (m_EditCameraIndex >= 0 && m_EditCameraIndex < (QT3DSI32)g_NumEditCameras) + return m_EditCameraIndex; + return -1; + } + + bool IsPolygonFillModeEnabled() const override + { + return GetEditCamera() >= 0 && CStudioPreferences::isEditViewFillMode(); + } + + void EditCameraZoomToFit() override + { + qt3dsdm::Qt3DSDMInstanceHandle theInstance = m_Doc.GetSelectedInstance(); + if (!m_Translation || m_Translation->m_EditCameraEnabled == false) + return; + // If we aren't pointed at a node then bounce up the asset graph till we are. + while (theInstance.Valid() && m_Translation->GetOrCreateTranslator(theInstance) + && GraphObjectTypes::IsNodeType( + m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject().m_Type) + == false) { + theInstance = m_Translation->m_AssetGraph.GetParent(theInstance); + } + // If we still aren't pointed at a node then use the active layer. + if (theInstance.Valid() == false + || m_Translation->GetOrCreateTranslator(theInstance) == nullptr + || GraphObjectTypes::IsNodeType( + m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject().m_Type) + == false) { + theInstance = m_Doc.GetActiveLayer(); + } + + // If we *still* aren't pointed at a node then bail. + if (m_Translation->GetOrCreateTranslator(theInstance) == nullptr + || GraphObjectTypes::IsNodeType( + m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject().m_Type) + == false) { + return; + } + + SNode &theNode = static_cast<SNode &>( + m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject()); + qt3ds::NVBounds3 theBounds; + theBounds.setEmpty(); + if (theNode.m_Type == GraphObjectTypes::Layer) { + SNode *theEditLayer = m_Translation->GetEditCameraLayer(); + if (theEditLayer) { + for (SNode *theChild = theEditLayer->m_FirstChild; theChild; + theChild = theChild->m_NextSibling) { + qt3ds::NVBounds3 childBounds = theChild->GetBounds( + m_Context->GetBufferManager(), m_Context->GetPathManager()); + if (childBounds.isEmpty() == false) { + childBounds.transform(theChild->m_GlobalTransform); + theBounds.include(childBounds); + } + } + } + } else { + theBounds = + theNode.GetBounds(m_Context->GetBufferManager(), m_Context->GetPathManager()); + } + + // Fake bounds for non-physical objects + if (theBounds.isEmpty()) { + const int dim = 50.0f; // Dimensions of a default sized cube + theBounds = qt3ds::NVBounds3(QT3DSVec3(-dim, -dim, -dim), QT3DSVec3(dim, dim, dim)); + } + + // Empty groups don't have proper global transform, so we need to recalculate it. + // For simplicity's sake, we recalculate for all groups, not just empty ones. + if (theNode.m_Type == GraphObjectTypes::Node) + theNode.CalculateGlobalVariables(); + + QT3DSVec3 theCenter = theNode.m_GlobalTransform.transform(theBounds.getCenter()); + + // Center the edit camera so that it points directly at the bounds center point + m_Translation->m_EditCameraInfo.m_Position = theCenter; + // Now we need to adjust the camera's zoom such that the view frustum contains the bounding + // box. + // But to do that I need to figure out what the view frustum is at -600 units from the near + // clip plane + + QT3DSVec3 theExtents = theBounds.getExtents().multiply(theNode.m_Scale); + + // get the largest extent and then some addition so things fit nicely in the viewport. + QT3DSF32 theMaxPossibleRadius = theExtents.magnitude(); + + // easiest case, the viewport dimensions map directly to the + m_Translation->m_EditCameraInfo.m_ViewRadius = theMaxPossibleRadius; + RequestRender(); + } + + // This must be safe to call from multiple places + void Close() override + { + ReleaseOffscreenRenderersForSubpresentations(); + m_subpresentations.clear(); + m_proxy.reset(); + m_Closed = true; + m_Translation = std::shared_ptr<STranslation>(); + m_Context = nullptr; + m_RenderContext = std::shared_ptr<CWGLRenderContext>(); + } + + // Data model listener + + // Fired before a large group of notifications come out so views can + // only refresh their view once. + void OnBeginDataModelNotifications() override {} + // Fired after a large gruop of notifications (onInstancePropertyChanged, etc) come out + // so views can be careful about refreshing their data and there view + void OnEndDataModelNotifications() override { Render(); } + + // Fired during 3d drag or mouse move events (or keyframe drag) or likewise + // events so that views that need to update based on the new data can. + void OnImmediateRefreshInstanceSingle(qt3dsdm::Qt3DSDMInstanceHandle inInstance) override + { + if (m_Translation) { + m_Translation->MarkDirty(inInstance); + // Pass to translation system + Render(); + } + } + // Same thing, but fired when more than one instance is being refreshed. + void OnImmediateRefreshInstanceMultiple(qt3dsdm::Qt3DSDMInstanceHandle *inInstance, + long inInstanceCount) override + { + // Pass to translation system + if (m_Translation) { + m_Translation->MarkDirty(inInstance, inInstanceCount); + // Pass to translation system + Render(); + } + Render(); + } + + void onReloadEffectInstance(qt3dsdm::Qt3DSDMInstanceHandle inInstance) override + { + if (m_Translation) + m_Translation->ReleaseEffect(inInstance); + } + + void onReloadMaterialInstance(qt3dsdm::Qt3DSDMInstanceHandle inInstance) override + { + if (m_Translation) + m_Translation->releaseMaterial(inInstance); + } + + void ApplyEditCameraIndex() + { + if (!m_Translation) + return; + if (m_EditCameraIndex < 0 || m_EditCameraIndex >= (QT3DSI32)g_NumEditCameras) + m_Translation->m_EditCameraEnabled = false; + else { + const SEditCameraDefinition &theDefinition(g_EditCameraDefinitions[m_EditCameraIndex]); + + while ((size_t)m_EditCameraIndex >= m_EditCameraInformation.size()) + m_EditCameraInformation.push_back(Empty()); + + Option<SEditorCameraInformation> &theCameraInfo = + m_EditCameraInformation[m_EditCameraIndex]; + + if (!theCameraInfo.hasValue()) { + theCameraInfo = SEditorCameraInformation(); + // TODO - consider resizing clip planes to scene so we use the depth buffer more + // accurately + // or consider requesting a larger depth buffer from the windowing system. + // Setup the camera + QT3DSVec3 normalizedDir = theDefinition.m_Direction; + normalizedDir.normalize(); + if (theDefinition.m_Type == EditCameraTypes::Directional) { + theCameraInfo->m_Direction = normalizedDir; + } else { + theCameraInfo->m_Direction = QT3DSVec3(0, 0, -1); + theCameraInfo->m_xRotation = -qt3ds::NVAtan(normalizedDir.x / normalizedDir.z); + theCameraInfo->m_yRotation = qt3ds::NVAsin(normalizedDir.y); + } + theCameraInfo->m_CameraType = theDefinition.m_Type; + } + + m_Translation->m_EditCameraEnabled = true; + m_Translation->m_EditCameraInfo = theCameraInfo; + m_Translation->updateHelperGridFromSettings(); + } + } + + void SetTranslationViewport() + { + if (m_Translation) { + m_Translation->SetViewport(QT3DSF32(m_Rect.right() - m_Rect.left()), + QT3DSF32(m_Rect.bottom() - m_Rect.top())); + } + } + + void CreateTranslator() + { + if (!m_Translation) { + if (m_Context.mPtr) { + m_Translation = std::make_shared<STranslation>(std::ref(*this), + std::ref(*m_Context.mPtr)); + ApplyEditCameraIndex(); + SetTranslationViewport(); + } + } + } + + void SetupTextRenderer() + { + if (m_Context.mPtr && m_Context->GetTextRenderer()) { + if (m_Context->getDistanceFieldRenderer()) + m_Context->getDistanceFieldRenderer()->ClearProjectFontDirectories(); + m_Context->GetTextRenderer()->ClearProjectFontDirectories(); + QString projectPath = g_StudioApp.GetCore()->getProjectFile().getProjectPath(); + if (!projectPath.isEmpty()) { + // Add the installed font folders from the res dir. + Q3DStudio::CString thePath( + Q3DStudio::CString::fromQString( + StudioUtils::resourcePath() + QStringLiteral("/Font"))); + // For QT3DS-3353 assume project fonts are in a subdirectory relative to project. + QString projectFontPath = projectPath + QStringLiteral("/fonts"); + m_Context->GetTextRenderer()->AddSystemFontDirectory( + m_Context->GetStringTable().RegisterStr(thePath.c_str())); + m_Context->GetTextRenderer()->AddProjectFontDirectory( + m_Context->GetStringTable().RegisterStr(projectFontPath.toLatin1().data())); + if (m_Context->getDistanceFieldRenderer()) { + m_Context->getDistanceFieldRenderer()->AddSystemFontDirectory( + m_Context->GetStringTable().RegisterStr(thePath.c_str())); + m_Context->getDistanceFieldRenderer()->AddProjectFontDirectory( + m_Context->GetStringTable().RegisterStr( + projectFontPath.toLatin1().data())); + } + } + } + } + + //========================================================================== + /** + * New presentation is being created. + */ + void OnNewPresentation() override + { + OnClosingPresentation(); + m_proxy.reset(); + ReleaseOffscreenRenderersForSubpresentations(); + m_subpresentations.clear(); + m_HasPresentation = true; + // Reset edit camera information. + m_EditCameraInformation.clear(); + // Rebuild translation + CreateTranslator(); + SetupTextRenderer(); + RequestRender(); + } + + //========================================================================== + /** + * The current presentation is being closed. + */ + void OnClosingPresentation() override + { + // Clear the shader cache so that shaders are reloaded when loading the next presentation + if (m_Context) { + m_Context->GetCustomMaterialSystem().clearCaches(); + m_Context->GetEffectSystem().clearCaches(); + } + + // Destroy translation + m_Translation = std::shared_ptr<STranslation>(); + m_HasPresentation = false; + } + + void OnSelectionChange() { RequestRender(); } + + qt3dsdm::Qt3DSDMInstanceHandle GetAnchorPointFromPick(SPathPick &inPick) + { + return m_Translation->GetAnchorPoint(inPick); + } + + Qt3DSDMInstanceHandle getObjectAt(const QPoint &pt) override + { + if (m_Translation == nullptr) + return Qt3DSDMInstanceHandle(); + + const QPoint point(pt * m_pixelRatio); + const auto pick = m_Translation->Pick(point, TranslationSelectMode::Single, true); + if (pick.getType() == StudioPickValueTypes::Instance) + return pick.getData<Qt3DSDMInstanceHandle>(); + return Qt3DSDMInstanceHandle(); + } + + //========================================================================== + // CSceneDragListener + //========================================================================== + void OnSceneMouseDown(SceneDragSenderType::Enum inSenderType, QPoint inPoint, int) override + { + if (m_Translation == nullptr) + return; + + m_mouseDown = true; + inPoint.setX(inPoint.x() * m_pixelRatio); + inPoint.setY(inPoint.y() * m_pixelRatio); + + m_PickResult = SStudioPickValue(); + if (inSenderType == SceneDragSenderType::SceneWindow) { + PickTargetAreas::Enum pickArea = m_Translation->GetPickArea(inPoint); + if (pickArea == PickTargetAreas::Presentation) { + TranslationSelectMode::Enum theSelectMode = TranslationSelectMode::Group; + switch (g_StudioApp.GetSelectMode()) { + case STUDIO_SELECTMODE_ENTITY: + theSelectMode = TranslationSelectMode::Single; + break; + case STUDIO_SELECTMODE_GROUP: + theSelectMode = TranslationSelectMode::Group; + break; + default: + QT3DS_ASSERT(false); + break; + } + m_RenderContext->BeginRender(); + m_PickResult = m_Translation->Pick(inPoint, theSelectMode); + m_RenderContext->EndRender(); + // If we definitely did not pick a widget. + if (m_PickResult.getType() == StudioPickValueTypes::Instance) { + qt3dsdm::Qt3DSDMInstanceHandle theHandle( + m_PickResult.getData<Qt3DSDMInstanceHandle>()); + if (QApplication::keyboardModifiers() & Qt::ControlModifier) { + m_Doc.ToggleDataModelObjectToSelection(theHandle); + } else { + if (m_Doc.getSelectedInstancesCount() > 1) + m_Doc.DeselectAllItems(true); + + if (theHandle != m_Doc.GetSelectedInstance()) + m_Doc.SelectDataModelObject(theHandle); + } + } else if (m_PickResult.getType() == StudioPickValueTypes::Guide) { + m_Doc.NotifySelectionChanged( + m_PickResult.getData<qt3dsdm::Qt3DSDMGuideHandle>()); + } else if (m_PickResult.getType() == StudioPickValueTypes::Path) { + SPathPick thePick = m_PickResult.getData<SPathPick>(); + qt3dsdm::Qt3DSDMInstanceHandle theAnchorHandle = + m_Translation->GetAnchorPoint(thePick); + if (theAnchorHandle.Valid() && theAnchorHandle != m_Doc.GetSelectedInstance()) + m_Doc.SelectDataModelObject(theAnchorHandle); + } else if (m_PickResult.getType() == StudioPickValueTypes::UnknownValueType) { + m_Doc.DeselectAllItems(true); + } + RequestRender(); + } else if (pickArea == PickTargetAreas::Matte) { + qt3ds::foundation::Option<qt3dsdm::SGuideInfo> pickResult = + m_Translation->PickRulers(inPoint); + if (pickResult.hasValue()) { + Q3DStudio::IDocumentEditor &docEditor( + m_UpdatableEditor.EnsureEditor(QObject::tr("Create Guide"), + __FILE__, __LINE__)); + Qt3DSDMGuideHandle newGuide = docEditor.CreateGuide(*pickResult); + m_PickResult = SStudioPickValue(newGuide); + m_Doc.NotifySelectionChanged(newGuide); + } else { + m_Doc.DeselectAllItems(true); + } + } + } + + m_LastDragToolMode = MovementTypes::Unknown; + m_MaybeDragStart = true; + m_MouseDownPoint = inPoint; + m_PreviousMousePoint = inPoint; + m_MouseDownCameraInformation = m_Translation->m_EditCameraInfo; + m_LastToolMode = g_StudioApp.GetToolMode(); + } + + void OnSceneMouseDrag(SceneDragSenderType::Enum, QPoint inPoint, int inToolMode, + int inFlags) override + { + if (m_Translation == nullptr || !m_mouseDown) + return; + + inPoint.setX(inPoint.x() * m_pixelRatio); + inPoint.setY(inPoint.y() * m_pixelRatio); + + if (m_MaybeDragStart) { + // Dragging in the first 5 pixels will be ignored to avoid unconsciously accidental + // moves + CPt theDragDistance = inPoint - m_MouseDownPoint; + if (m_PickResult.getType() == StudioPickValueTypes::Widget + || inToolMode != STUDIO_TOOLMODE_SCALE) { + if (theDragDistance.x * theDragDistance.x + theDragDistance.y * theDragDistance.y + <= 25) + return; + } else { + if (qAbs(theDragDistance.y) <= 5) + return; + } + } + + m_MaybeDragStart = false; + + // If the tool mode changes then we throw out the last widget pick if there was one. + if (m_LastToolMode != inToolMode) + m_PickResult = SStudioPickValue(); + m_LastToolMode = inToolMode; + + // General dragging + if (m_PickResult.getType() == StudioPickValueTypes::Instance + || m_PickResult.getType() + == StudioPickValueTypes::UnknownValueType) // matte drag and widget drag + { + // Not sure what right-click drag does in the scene. + bool isEditCamera = m_Translation->m_EditCameraEnabled; + int theCameraToolMode = isEditCamera ? (inToolMode & (STUDIO_CAMERATOOL_MASK)) : 0; + bool rightClick = (inFlags & CHotKeys::MOUSE_RBUTTON) != 0; + + if (theCameraToolMode == 0) { + if (m_Doc.GetDocumentReader().IsInstance(m_Doc.GetSelectedInstance())) { + if (m_Doc.getSelectedInstancesCount() == 1) { + bool rightClick = (inFlags & CHotKeys::MOUSE_RBUTTON) != 0; + MovementTypes::Enum theMovement(MovementTypes::Unknown); + + switch (inToolMode) { + default: + QT3DS_ASSERT(false); + break; + case STUDIO_TOOLMODE_MOVE: + if (rightClick) + theMovement = MovementTypes::TranslateAlongCameraDirection; + else + theMovement = MovementTypes::Translate; + break; + case STUDIO_TOOLMODE_SCALE: + if (rightClick) + theMovement = MovementTypes::ScaleZ; + else + theMovement = MovementTypes::Scale; + break; + case STUDIO_TOOLMODE_ROTATE: + if (rightClick) + theMovement = MovementTypes::RotationAboutCameraDirection; + else + theMovement = MovementTypes::Rotation; + break; + } + + if (theMovement != MovementTypes::Unknown) { + bool theLockToAxis = (inFlags & CHotKeys::MODIFIER_SHIFT) != 0; + + if (m_LastDragToolMode != MovementTypes::Unknown + && theMovement != m_LastDragToolMode) { + m_UpdatableEditor.RollbackEditor(); + m_MouseDownPoint = inPoint; + } + + m_LastDragToolMode = theMovement; + + switch (theMovement) { + case MovementTypes::TranslateAlongCameraDirection: + m_Translation->TranslateSelectedInstanceAlongCameraDirection( + m_MouseDownPoint, inPoint, m_UpdatableEditor); + break; + case MovementTypes::Translate: + m_Translation->TranslateSelectedInstance( + m_MouseDownPoint, inPoint, m_UpdatableEditor, theLockToAxis); + break; + case MovementTypes::ScaleZ: + m_Translation->ScaleSelectedInstanceZ(m_MouseDownPoint, inPoint, + m_UpdatableEditor); + break; + case MovementTypes::Scale: + m_Translation->ScaleSelectedInstance(m_MouseDownPoint, inPoint, + m_UpdatableEditor); + break; + case MovementTypes::Rotation: + m_Translation->RotateSelectedInstance( + m_MouseDownPoint, m_PreviousMousePoint, inPoint, + m_UpdatableEditor, theLockToAxis); + break; + case MovementTypes::RotationAboutCameraDirection: + m_Translation->RotateSelectedInstanceAboutCameraDirectionVector( + m_PreviousMousePoint, inPoint, m_UpdatableEditor); + break; + default: + break; + } + } + } + } + } else { + QT3DSF32 theXDistance = static_cast<QT3DSF32>(inPoint.x() - m_MouseDownPoint.x); + QT3DSF32 theYDistance = static_cast<QT3DSF32>(inPoint.y() - m_MouseDownPoint.y); + QT3DSF32 theSubsetXDistance = static_cast<QT3DSF32>(inPoint.x() - m_PreviousMousePoint.x); + QT3DSF32 theSubsetYDistance = static_cast<QT3DSF32>(inPoint.y() - m_PreviousMousePoint.y); + + // Edit cameras are not implemented. + switch (theCameraToolMode) { + case STUDIO_TOOLMODE_CAMERA_PAN: { + QT3DSVec3 theXAxis = + m_Translation->m_EditCamera.m_GlobalTransform.column0.getXYZ(); + QT3DSVec3 theYAxis = + m_Translation->m_EditCamera.m_GlobalTransform.column1.getXYZ(); + QT3DSVec3 theXChange = -1.0f * theXAxis * theXDistance; + QT3DSVec3 theYChange = theYAxis * theYDistance; + QT3DSVec3 theDiff = theXChange + theYChange; + m_Translation->m_EditCameraInfo.m_Position = + m_MouseDownCameraInformation.m_Position + theDiff; + RequestRender(); + } break; + case STUDIO_TOOLMODE_CAMERA_ZOOM: { + QT3DSF32 theMultiplier = 1.0f + theSubsetYDistance / 40.0f; + m_Translation->m_EditCameraInfo.m_ViewRadius = + qMax(.0001f, m_Translation->m_EditCameraInfo.m_ViewRadius * theMultiplier); + RequestRender(); + } break; + case STUDIO_TOOLMODE_CAMERA_ROTATE: { + if (m_Translation->m_EditCameraInfo.SupportsRotation()) { + if (!rightClick) { + m_Translation->m_EditCameraInfo.m_xRotation = + m_MouseDownCameraInformation.m_xRotation + + (theSubsetXDistance * g_RotationScaleFactor / 20.0f); + m_Translation->m_EditCameraInfo.m_yRotation = + m_MouseDownCameraInformation.m_yRotation + - (theSubsetYDistance * g_RotationScaleFactor / 20.0f); + // Avoid rounding errors stemming from extremely large rotation angles + if (m_Translation->m_EditCameraInfo.m_xRotation < -qt3ds::NVPi) + m_Translation->m_EditCameraInfo.m_xRotation += qt3ds::NVPi * 2.0f; + if (m_Translation->m_EditCameraInfo.m_xRotation > qt3ds::NVPi) + m_Translation->m_EditCameraInfo.m_xRotation -= qt3ds::NVPi * 2.0f; + if (m_Translation->m_EditCameraInfo.m_yRotation < -qt3ds::NVPi) + m_Translation->m_EditCameraInfo.m_yRotation += qt3ds::NVPi * 2.0f; + if (m_Translation->m_EditCameraInfo.m_yRotation > qt3ds::NVPi) + m_Translation->m_EditCameraInfo.m_yRotation -= qt3ds::NVPi * 2.0f; + } + m_MouseDownCameraInformation.m_xRotation = + m_Translation->m_EditCameraInfo.m_xRotation; + m_MouseDownCameraInformation.m_yRotation = + m_Translation->m_EditCameraInfo.m_yRotation; + RequestRender(); + } + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + } // if ( m_PickResult.m_WidgetId.hasValue() == false ) + + // We need to do widget-specific dragging. + else if (m_PickResult.getType() == StudioPickValueTypes::Widget) { + m_Translation->PerformWidgetDrag(m_PickResult.GetWidgetId(), m_MouseDownPoint, + m_PreviousMousePoint, inPoint, m_UpdatableEditor); + } else if (m_PickResult.getType() == StudioPickValueTypes::Guide) { + m_Translation->PerformGuideDrag(m_PickResult.getData<Qt3DSDMGuideHandle>(), inPoint, + m_UpdatableEditor); + } else if (m_PickResult.getType() == StudioPickValueTypes::Path) { + SPathPick thePick = m_PickResult.getData<SPathPick>(); + m_Translation->PerformPathDrag(thePick, m_MouseDownPoint, m_PreviousMousePoint, inPoint, + m_UpdatableEditor); + } + m_PreviousMousePoint = inPoint; + } + + void OnSceneMouseUp(SceneDragSenderType::Enum) override + { + m_MaybeDragStart = false; + m_mouseDown = false; + bool guideExists = true; + Qt3DSDMGuideHandle theSelectedGuide; + if (m_PickResult.getType() == StudioPickValueTypes::Guide) { + theSelectedGuide = m_PickResult.getData<Qt3DSDMGuideHandle>(); + guideExists = m_Translation->CheckGuideInPresentationRect(theSelectedGuide, + m_UpdatableEditor); + } + m_UpdatableEditor.CommitEditor(); + m_PickResult = SStudioPickValue(); + if (m_Translation) + m_Translation->EndDrag(); + if (theSelectedGuide.GetHandleValue()) { + // Get rid of selection if things aren't editable or if guide has been deleted. + if (guideExists && m_Doc.GetDocumentReader().AreGuidesEditable()) + m_Doc.NotifySelectionChanged(theSelectedGuide); + else + m_Doc.NotifySelectionChanged(); + } + RequestRender(); + } + + void OnSceneMouseDblClick(SceneDragSenderType::Enum inSenderType, QPoint inPoint) override + { + if (inSenderType == SceneDragSenderType::SceneWindow && m_Translation) { + inPoint.setX(inPoint.x() * m_pixelRatio); + inPoint.setY(inPoint.y() * m_pixelRatio); + m_RenderContext->BeginRender(); + SStudioPickValue theResult( + m_Translation->Pick(inPoint, TranslationSelectMode::NestedComponentSingle)); + m_RenderContext->EndRender(); + + if (theResult.getType() == StudioPickValueTypes::Instance) + m_Doc.SelectAndNavigateToDataModelObject(theResult.getData<Qt3DSDMInstanceHandle>()); + else if (theResult.getType() == StudioPickValueTypes::Path) { + SPathPick thePickValue = theResult.getData<SPathPick>(); + qt3dsdm::Qt3DSDMInstanceHandle theAnchorHandle = + m_Translation->GetAnchorPoint(thePickValue); + if (theAnchorHandle.Valid() && theAnchorHandle != m_Doc.GetSelectedInstance()) { + m_Doc.SelectDataModelObject(theAnchorHandle); + } + } + } + } + + void OnSceneMouseWheel(SceneDragSenderType::Enum inSenderType, short inDelta, + int inToolMode) override + { + ASSERT(inSenderType == SceneDragSenderType::Matte); + if (inToolMode == STUDIO_TOOLMODE_CAMERA_ZOOM && m_Translation) { + QT3DSF32 theMultiplier = 1.0f - inDelta / static_cast<QT3DSF32>(120 * g_WheelFactor); + m_Translation->m_EditCameraInfo.m_ViewRadius = + qMax(.0001f, m_Translation->m_EditCameraInfo.m_ViewRadius * theMultiplier); + RequestRender(); + } + } + + void OnToolbarChange() override { RequestRender(); } +}; +} + +std::shared_ptr<IStudioRenderer> IStudioRenderer::CreateStudioRenderer() +{ + return std::make_shared<SRendererImpl>(); +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioRendererImpl.h b/src/Authoring/Qt3DStudio/Render/StudioRendererImpl.h new file mode 100644 index 00000000..530bf384 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioRendererImpl.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_RENDERER_IMPL_H +#define QT3DS_STUDIO_RENDERER_IMPL_H +#pragma once +#include "IStudioRenderer.h" +#include "WGLRenderContext.h" +#include "Qt3DSDMDataTypes.h" +#include "Qt3DSDMSignals.h" +#include "Qt3DSRenderContextCore.h" +#include "StudioApp.h" +#include "Doc.h" +#include "Qt3DSDMStudioSystem.h" +#include "ClientDataModelBridge.h" +#include "IDocumentReader.h" +#include "Qt3DSFileTools.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSVec4.h" +#include "DispatchListeners.h" +#include "Dispatch.h" +#include "Core.h" +#include "foundation/Qt3DSInvasiveSet.h" +#include "Qt3DSRenderer.h" +#include "Qt3DSRenderScene.h" +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderModel.h" +#include "Qt3DSRenderDefaultMaterial.h" +#include "Qt3DSRenderLight.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderImage.h" +#include "Qt3DSRenderPresentation.h" +#include "StudioProjectSettings.h" +#include "Qt3DSRenderUIPSharedTranslation.h" +#include "Qt3DSRenderBufferManager.h" +#include "StudioFullSystem.h" +#include "Qt3DSDMSignals.h" +#include "CoreConst.h" +#include "IDocumentEditor.h" +#include "foundation/Qt3DSPlane.h" +#include "foundation/Qt3DSQuat.h" +#include "Qt3DSTextRenderer.h" +#include "foundation/Qt3DSOption.h" +#include "foundation/Qt3DSMathUtils.h" +#include "Qt3DSRenderEffect.h" +#include "Qt3DSRenderPath.h" +#include "Qt3DSRenderPathSubPath.h" + +namespace qt3ds { +namespace studio { + using Q3DStudio::IDocumentReader; + using Q3DStudio::CUpdateableDocumentEditor; + using Q3DStudio::TIdentifier; + using Q3DStudio::IStudioRenderer; + using qt3ds::foundation::NVScopedRefCounted; + using qt3ds::QT3DSVec3; + using qt3ds::QT3DSQuat; + using qt3ds::QT3DSF32; + using qt3ds::QT3DSMat44; + using qt3ds::QT3DSMat33; + using qt3ds::QT3DSI32; + using qt3ds::QT3DSVec2; + using qt3ds::QT3DSVec4; + using qt3ds::QT3DSU32; + using qt3ds::foundation::Empty; + using qt3ds::foundation::InvasiveSet; + using qt3ds::foundation::nvhash_map; + using qt3ds::foundation::nvvector; + using qt3ds::foundation::rotationArc; + using qt3ds::foundation::Option; + using qt3ds::foundation::Empty; + using qt3ds::render::IQt3DSRenderContext; + using qt3ds::render::SScene; + using qt3ds::render::SLayer; + using qt3ds::render::SNode; + using qt3ds::render::SGraphObject; + using qt3ds::render::SLight; + using qt3ds::render::SCamera; + using qt3ds::render::SDefaultMaterial; + using qt3ds::render::SImage; + using qt3ds::render::SModel; + using qt3ds::render::SText; + using qt3ds::render::GraphObjectTypes; + using qt3ds::render::SRay; + using qt3ds::render::ITextRenderer; + using qt3ds::render::SEffect; + using qt3ds::render::IEffectSystem; + using qt3ds::render::SDynamicObject; + using qt3ds::render::SCustomMaterial; + using qt3ds::render::IDynamicObjectSystem; + using qt3ds::render::ICustomMaterialSystem; + using qt3ds::render::IBufferManager; + using qt3ds::render::IPathManager; + using qt3ds::render::SPath; + using qt3ds::render::SPathSubPath; + using qt3ds::render::SReferencedMaterial; + using qt3ds::render::CRegisteredString; + using qt3ds::render::IStringTable; + using qt3dsdm::SFloat3; + using qt3dsdm::SLong4; + using qt3dsdm::SComposerObjectDefinitions; + using qt3dsdm::Qt3DSDMInstanceHandle; + using qt3dsdm::Qt3DSDMPropertyHandle; +} +} +#endif diff --git a/src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.cpp b/src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.cpp new file mode 100644 index 00000000..d24f1061 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.cpp @@ -0,0 +1,4346 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioRendererImpl.h" +#include "StudioRendererTranslation.h" +#include "Qt3DSRenderEffectSystem.h" +#include "foundation/StrConvertUTF.h" +#include "Qt3DSFileTools.h" +#include "Qt3DSRenderUIPLoader.h" +#include "Qt3DSRenderWidgets.h" +#include "foundation/Qt3DSBounds3.h" +#include "Qt3DSRenderResourceManager.h" +#include "render/Qt3DSRenderFrameBuffer.h" +#include "Qt3DSRenderCamera.h" +#include "foundation/Qt3DSPlane.h" +#include "Qt3DSRenderRotationHelper.h" +#include "Qt3DSRenderPluginGraphObject.h" +#include "Qt3DSRenderPlugin.h" +#include "StudioCoreSystem.h" +#include "Qt3DSDMDataCore.h" +#include "Qt3DSRenderPluginPropertyValue.h" +#include "Qt3DSRenderEffectSystem.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderMaterialHelpers.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderReferencedMaterial.h" +#include "Qt3DSRenderPixelGraphicsTypes.h" +#include "Qt3DSRenderPixelGraphicsRenderer.h" +#include "Qt3DSRenderPathManager.h" + +#include "PathWidget.h" +#include "Qt3DSRenderLightmaps.h" +#include "StudioPreferences.h" +#include "HotKeys.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLight.h" + +#pragma warning(disable : 4100) // unreferenced formal parameter + +using namespace qt3ds::studio; +QT3DSU32 qt3ds::studio::g_GraphObjectTranslatorTag; +using qt3ds::render::SPGGraphObject; +using qt3ds::render::SPGRect; +using qt3ds::render::SPGVertLine; +using qt3ds::render::SPGHorzLine; +using qt3ds::render::NVRenderRectF; +using qt3ds::render::NVRenderRect; + +namespace { +using namespace qt3dsdm; + +struct STranslatorDataModelParser +{ + STranslation &m_Context; + Qt3DSDMInstanceHandle m_InstanceHandle; + STranslatorDataModelParser(STranslation &inContext, Qt3DSDMInstanceHandle inInstance) + : m_Context(inContext) + , m_InstanceHandle(inInstance) + { + } + Qt3DSDMInstanceHandle GetInstanceHandle() { return m_InstanceHandle; } + + template <typename TDataType> + inline Option<TDataType> GetPropertyValue(qt3dsdm::Qt3DSDMPropertyHandle inProperty) + { + Option<SValue> theValue = + m_Context.m_Reader.GetRawInstancePropertyValue(GetInstanceHandle(), inProperty); + if (theValue.hasValue()) + return qt3dsdm::get<TDataType>(*theValue); + return Empty(); + } + + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSF32 &outValue) + { + Option<float> theValue = GetPropertyValue<float>(inProperty); + if (theValue.hasValue()) { + outValue = theValue.getValue(); + return true; + } + return false; + } + + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSU32 &outValue) + { + auto theValue = GetPropertyValue<qt3ds::QT3DSI32>(inProperty); + if (theValue.hasValue()) { + outValue = qMax(theValue.getValue(), 0); + return true; + } + return false; + } + + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSI32 &outValue) + { + auto theValue = GetPropertyValue<qt3ds::QT3DSI32>(inProperty); + if (theValue.hasValue()) { + outValue = *theValue; + return true; + } + return false; + } + + bool ParseRadianProperty(Qt3DSDMPropertyHandle inProperty, QT3DSF32 &outValue) + { + if (ParseProperty(inProperty, outValue)) { + TORAD(outValue); + return true; + } + return false; + } + bool ParseRadianProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec3 &outValue) + { + if (ParseProperty(inProperty, outValue)) { + TORAD(outValue.x); + TORAD(outValue.y); + TORAD(outValue.z); + return true; + } + return false; + } + bool ParseOpacityProperty(Qt3DSDMPropertyHandle inProperty, QT3DSF32 &outValue) + { + if (ParseProperty(inProperty, outValue)) { + outValue = (1.0f / 100.0f) * outValue; + return true; + } + return false; + } + bool ParseRotationOrder(Qt3DSDMPropertyHandle inProperty, QT3DSU32 &outValue) + { + qt3ds::render::CRegisteredString temp; + if (ParseProperty(inProperty, temp)) { + outValue = qt3ds::render::MapRotationOrder(temp); + return true; + } + return false; + } + bool ParseOrientation(Qt3DSDMPropertyHandle inProperty, qt3ds::render::NodeFlags &outValue) + { + qt3ds::render::CRegisteredString temp; + if (ParseProperty(inProperty, temp)) { + bool isLeftHanded = strcmp(temp.c_str(), "Left Handed") == 0; + outValue.SetLeftHanded(isLeftHanded); + return true; + } + return false; + } + + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, bool &outValue) + { + Option<bool> theValue = GetPropertyValue<bool>(inProperty); + if (theValue.hasValue()) { + outValue = theValue.getValue(); + return true; + } + return false; + } + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec2 &outValue) + { + Option<qt3dsdm::SFloat2> theValue = GetPropertyValue<qt3dsdm::SFloat2>(inProperty); + if (theValue.hasValue()) { + outValue = QT3DSVec2(theValue->m_Floats[0], theValue->m_Floats[1]); + return true; + } + return false; + } + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec3 &outValue) + { + Option<SFloat3> theValue = GetPropertyValue<SFloat3>(inProperty); + if (theValue.hasValue()) { + outValue = QT3DSVec3(theValue->m_Floats[0], theValue->m_Floats[1], theValue->m_Floats[2]); + return true; + } + return false; + } + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, QT3DSVec4 &outValue) + { + Option<SFloat4> theValue = GetPropertyValue<SFloat4>(inProperty); + if (theValue.hasValue()) { + outValue = QT3DSVec4(theValue->m_Floats[0], theValue->m_Floats[1], + theValue->m_Floats[2], theValue->m_Floats[3]); + return true; + } + return false; + } + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::CRegisteredString &outValue) + { + Option<qt3dsdm::TDataStrPtr> theValue = GetPropertyValue<qt3dsdm::TDataStrPtr>(inProperty); + if (theValue.hasValue() && *theValue) { + qt3ds::render::IStringTable &theStrTable(m_Context.m_Context.GetStringTable()); + outValue = theStrTable.RegisterStr((*theValue)->GetData()); + return true; + } + return false; + } + + bool ParseAndResolveSourcePath(qt3dsdm::Qt3DSDMPropertyHandle inProperty, + qt3ds::render::CRegisteredString &outValue) + { + if (ParseProperty(inProperty, outValue)) { + if (outValue.IsValid() && outValue.c_str()[0] != '#') { + Q3DStudio::CFilePath theDirectory = m_Context.m_Doc.GetDocumentDirectory(); + Q3DStudio::CFilePath theResolvedPath = + Q3DStudio::CFilePath::CombineBaseAndRelative(theDirectory, outValue.c_str()); + if (theResolvedPath.exists()) { + outValue = m_Context.m_Context.GetStringTable() + .RegisterStr(theResolvedPath.toCString()); + } + } + return true; + } + return false; + } + + template <typename TEnumType> + bool ParseEnumProperty(qt3dsdm::Qt3DSDMPropertyHandle inProperty, TEnumType &ioValue) + { + qt3ds::render::CRegisteredString temp; + if (ParseProperty(inProperty, temp)) { + qt3ds::render::SEnumNameMap *theNameMap(qt3ds::render::SEnumParseMap<TEnumType>::GetMap()); + for (qt3ds::render::SEnumNameMap *theIter = theNameMap; + theIter->m_Name && *theIter->m_Name; ++theIter) { + // hack to match advanced overlay types, whose name start with a '*' + const char8_t *p = temp; + if (*p == '*') + ++p; + if (strcmp(p, theIter->m_Name) == 0) { + ioValue = (TEnumType)theIter->m_Enum; + return true; + } + } + } + return false; + } + + bool ParseNodeFlagsProperty(qt3dsdm::Qt3DSDMPropertyHandle inProperty, + qt3ds::render::NodeFlags &outValue, + qt3ds::render::NodeFlagValues::Enum theFlag) + { + bool temp = false; + if (ParseProperty(inProperty, temp)) { + outValue.ClearOrSet(temp, theFlag); + return true; + } + return false; + } + bool ParseNodeFlagsInverseProperty(qt3dsdm::Qt3DSDMPropertyHandle inProperty, + qt3ds::render::NodeFlags &outValue, + qt3ds::render::NodeFlagValues::Enum theFlag) + { + bool temp = false; + if (ParseProperty(inProperty, temp)) { + outValue.ClearOrSet(!temp, theFlag); + return true; + } + return false; + } + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::SImage *&ioImage) + { + Option<SLong4> theData = GetPropertyValue<SLong4>(inProperty); + if (theData.hasValue()) { + qt3dsdm::Qt3DSDMInstanceHandle theInstance( + m_Context.m_Reader.GetInstanceForGuid(*theData)); + SGraphObjectTranslator *imageTranslator = m_Context.GetOrCreateTranslator(theInstance); + if (imageTranslator + && imageTranslator->GetGraphObject().m_Type + == qt3ds::render::GraphObjectTypes::Image) { + SImage *theNewImage = static_cast<SImage *>(&imageTranslator->GetGraphObject()); + ioImage = theNewImage; + } else + ioImage = nullptr; + return true; + } + return false; + } + + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::SGraphObject *&ioObjRef) + { + Option<SObjectRefType> theData = GetPropertyValue<SObjectRefType>(inProperty); + if (theData.hasValue()) { + qt3dsdm::Qt3DSDMInstanceHandle theInstance( + m_Context.m_Reader.GetInstanceForObjectRef(m_InstanceHandle, *theData)); + SGraphObjectTranslator *theItemTranslator = + m_Context.GetOrCreateTranslator(theInstance); + if (theItemTranslator) + ioObjRef = &theItemTranslator->GetGraphObject(); + } + return true; + } + + bool ParseProperty(Qt3DSDMPropertyHandle inProperty, qt3ds::render::SNode *&ioNodePtr) + { + Option<SObjectRefType> theData = GetPropertyValue<SObjectRefType>(inProperty); + SNode *theNewNodePtr = nullptr; + if (theData.hasValue()) { + qt3dsdm::Qt3DSDMInstanceHandle theInstance( + m_Context.m_Reader.GetInstanceForObjectRef(m_InstanceHandle, *theData)); + SGraphObjectTranslator *theItemTranslator = + m_Context.GetOrCreateTranslator(theInstance); + if (theItemTranslator) { + SGraphObject &theObject = theItemTranslator->GetGraphObject(); + if (GraphObjectTypes::IsNodeType(theObject.m_Type)) + theNewNodePtr = &static_cast<SNode &>(theObject); + } + } + ioNodePtr = theNewNodePtr; + return true; + } +}; + +// Define parse tables +#define Scene_ClearColor m_Scene.m_BackgroundColor +#define Scene_UseClearColor m_Scene.m_BgColorEnable +#define Node_Rotation m_Node.m_Rotation +#define Node_Position m_Node.m_Position +#define Node_Scale m_Node.m_Scale +#define Node_Pivot m_Node.m_Pivot +#define Node_LocalOpacity m_Node.m_Opacity +#define Node_RotationOrder m_Node.m_RotationOrder +#define Node_LeftHanded m_Node.m_Orientation +#define Group_ordered m_Node.m_ordered +#define Layer_TemporalAAEnabled m_Layer.m_TemporalAA +#define Layer_LayerEnableDepthTest m_Layer.m_DisableDepthTest +#define Layer_LayerEnableDepthPrePass m_Layer.m_DisableDepthPrepass +#define Layer_ClearColor m_Layer.m_BackgroundColor +#define Layer_Background m_Layer.m_Background +#define Layer_BlendType m_Layer.m_BlendType +#define Layer_Size m_Layer.m_Size +#define Layer_Location m_Layer.m_Location +#define Layer_ProgressiveAAMode m_Layer.m_ProgressiveAA +#define Layer_MultisampleAAMode m_Layer.m_MultisampleAA +#define Layer_DynamicResize m_Layer.m_DynamicResize +#define Layer_DynamicPaddingUnits m_Layer.m_DynamicPaddingUnits +#define Layer_DynamicPadding m_Layer.m_DynamicPadding +#define Layer_DynamicCombine m_Layer.m_DynamicCombine +#define Layer_HorizontalFieldValues m_Layer.m_HorizontalFieldValues +#define Layer_Left m_Layer.m_Left +#define Layer_LeftUnits m_Layer.m_LeftUnits +#define Layer_Width m_Layer.m_Width +#define Layer_WidthUnits m_Layer.m_WidthUnits +#define Layer_Right m_Layer.m_Right +#define Layer_RightUnits m_Layer.m_RightUnits +#define Layer_VerticalFieldValues m_Layer.m_VerticalFieldValues +#define Layer_Top m_Layer.m_Top +#define Layer_TopUnits m_Layer.m_TopUnits +#define Layer_Height m_Layer.m_Height +#define Layer_HeightUnits m_Layer.m_HeightUnits +#define Layer_Bottom m_Layer.m_Bottom +#define Layer_BottomUnits m_Layer.m_BottomUnits +#define Layer_AoEnabled m_Layer.m_AoEnabled +#define Layer_AoStrength m_Layer.m_AoStrength +#define Layer_AoDistance m_Layer.m_AoDistance +#define Layer_AoSoftness m_Layer.m_AoSoftness +#define Layer_AoBias m_Layer.m_AoBias +#define Layer_AoSamplerate m_Layer.m_AoSamplerate +#define Layer_AoDither m_Layer.m_AoDither +#define Layer_ShadowStrength m_Layer.m_ShadowStrength +#define Layer_ShadowDist m_Layer.m_ShadowDist +#define Layer_ShadowSoftness m_Layer.m_ShadowSoftness +#define Layer_ShadowBias m_Layer.m_ShadowBias +#define Layer_LightProbe m_Layer.m_LightProbe +#define Layer_ProbeBright m_Layer.m_ProbeBright +#define Layer_FastIbl m_Layer.m_FastIbl +#define Layer_ProbeHorizon m_Layer.m_ProbeHorizon +#define Layer_ProbeFov m_Layer.m_ProbeFov +#define Layer_LightProbe2 m_Layer.m_LightProbe2 +#define Layer_Probe2Fade m_Layer.m_Probe2Fade +#define Layer_Probe2Window m_Layer.m_Probe2Window +#define Layer_Probe2Pos m_Layer.m_Probe2Pos +#define Layer_TexturePath m_Asset.m_SourcePath +#define Camera_ClipNear m_Camera.m_ClipNear +#define Camera_ClipFar m_Camera.m_ClipFar +#define Camera_FOV m_Camera.m_Fov +#define Camera_FOVHorizontal m_Camera.m_FovHorizontal +#define Camera_EnableFrustumCulling m_Camera.m_EnableFrustumCulling +#define Camera_Orthographic m_Camera.m_Orthographic +#define Camera_ScaleMode m_Camera.m_ScaleMode +#define Camera_ScaleAnchor m_Camera.m_ScaleAnchor +#define Light_LightType m_Light.m_LightType +#define Light_Scope m_Light.m_Scope +#define Light_DiffuseColor m_Light.m_LightColor +#define Light_SpecularColor m_Light.m_SpecularColor +#define Light_AmbientColor m_Light.m_AmbientColor +#define Light_Brightness m_Light.m_Brightness +#define Light_LinearFade m_Light.m_LinearFade +#define Light_ExponentialFade m_Light.m_ExpFade +#define Light_AreaWidth m_Light.m_AreaWidth +#define Light_AreaHeight m_Light.m_AreaHeight +#define Light_CastShadow m_Light.m_CastShadow +#define Light_ShadowBias m_Light.m_ShadowBias +#define Light_ShadowFactor m_Light.m_ShadowFactor +#define Light_ShadowMapRes m_Light.m_ShadowMapRes +#define Light_ShadowMapFar m_Light.m_ShadowMapFar +#define Light_ShadowMapFov m_Light.m_ShadowMapFov +#define Light_ShadowFilter m_Light.m_ShadowFilter +#define Model_MeshPath m_Asset.m_SourcePath +#define Model_ShadowCaster m_Model.m_ShadowCaster +#define Model_TessellationMode m_Model.m_Tessellation +#define Model_EdgeTess m_Model.m_EdgeTess +#define Model_InnerTess m_Model.m_InnerTess +#define Lightmaps_LightmapIndirect m_Lightmaps.m_LightmapIndirect +#define Lightmaps_LightmapRadiosity m_Lightmaps.m_LightmapRadiosity +#define Lightmaps_LightmapShadow m_Lightmaps.m_LightmapShadow +#define MaterialBase_IblProbe m_MaterialBase.m_IblProbe +#define Material_Lighting m_Material.m_ShaderLighting +#define Material_BlendMode m_Material.m_BlendMode +#define Material_DiffuseColor m_Material.m_DiffuseColor +#define Material_DiffuseMaps_0 m_Material.m_DiffuseMap1 +#define Material_DiffuseMaps_1 m_Material.m_DiffuseMap2 +#define Material_DiffuseMaps_2 m_Material.m_DiffuseMap3 +#define Material_EmissivePower m_Material.m_EmissivePower +#define Material_EmissiveColor m_Material.m_EmissiveColor +#define Material_EmissiveMap m_Material.m_EmissiveMap +#define Material_EmissiveMap2 m_Material.m_EmissiveMap2 +#define Material_SpecularReflection m_Material.m_SpecularReflection +#define Material_SpecularMap m_Material.m_SpecularMap +#define Material_SpecularModel m_Material.m_SpecularModel +#define Material_SpecularTint m_Material.m_SpecularTint +#define Material_IOR m_Material.m_IOR +#define Material_FresnelPower m_Material.m_FresnelPower +#define Material_SpecularAmount m_Material.m_SpecularAmount +#define Material_SpecularRoughness m_Material.m_SpecularRoughness +#define Material_RoughnessMap m_Material.m_RoughnessMap +#define Material_Opacity m_Material.m_Opacity +#define Material_OpacityMap m_Material.m_OpacityMap +#define Material_BumpMap m_Material.m_BumpMap +#define Material_BumpAmount m_Material.m_BumpAmount +#define Material_NormalMap m_Material.m_NormalMap +#define Material_DisplacementMap m_Material.m_DisplacementMap +#define Material_DisplaceAmount m_Material.m_DisplaceAmount +#define Material_TranslucencyMap m_Material.m_TranslucencyMap +#define Material_TranslucentFalloff m_Material.m_TranslucentFalloff +#define Material_DiffuseLightWrap m_Material.m_DiffuseLightWrap +#define Material_ReferencedMaterial m_ReferencedMaterial.m_ReferencedMaterial +#define Material_VertexColors m_Material.m_VertexColors +#define Material_TransparencyMode m_Material.m_TransparencyMode +#define Material_CullMode m_Material.m_CullMode +#define Image_ImagePath m_Asset.m_SourcePath +#define Image_OffscreenRendererId m_Image.m_SubPresentation +#define Image_Scale_X m_Image.m_RepeatU +#define Image_Scale_Y m_Image.m_RepeatV +#define Image_Pivot_X m_Image.m_PivotU +#define Image_Pivot_Y m_Image.m_PivotV +#define Image_Rotation m_Image.m_RotationUV +#define Image_Position_X m_Image.m_PositionU +#define Image_Position_Y m_Image.m_PositionV +#define Image_MappingMode m_Image.m_TextureMapping +#define Image_HorizontalTilingMode m_Image.m_TilingU +#define Image_VerticalTilingMode m_Image.m_TilingV +#define Image_MinFilter m_Image.m_MinFilter +#define Image_MagFilter m_Image.m_MagFilter +#define Text_Text m_Text.m_TextString +#define Text_Font m_Text.m_Font +#define Text_FontSize m_Text.m_Size +#define Text_HorizontalAlignment m_Text.m_HorzAlign +#define Text_VerticalAlignment m_Text.m_VertAlign +#define Text_Leading m_Text.m_Leading +#define Text_Tracking m_Text.m_Tracking +#define Text_DropShadow m_Text.m_DropShadow +#define Text_DropShadowStrength m_Text.m_DropShadowStrength +#define Text_DropShadowOffsetX m_Text.m_DropShadowOffsetX +#define Text_DropShadowOffsetY m_Text.m_DropShadowOffsetY +#define Text_WordWrap m_Text.m_WordWrap +#define Text_BoundingBox m_Text.m_BoundingBox +#define Text_Elide m_Text.m_Elide +#define Text_TextColor m_Text.m_TextColor +#define Text_EnableAcceleratedFont m_Text.m_EnableAcceleratedFont +#define Path_Width m_Path.m_Width +#define Path_LinearError m_Path.m_LinearError +#define Path_InnerTessAmount m_Path.m_InnerTessAmount +#define Path_EdgeTessAmount m_Path.m_EdgeTessAmount +#define Path_Opacity m_Path.m_Opacity +#define Path_BeginCapping m_Path.m_BeginCap +#define Path_BeginCapOffset m_Path.m_BeginCapOffset +#define Path_BeginCapOpacity m_Path.m_BeginCapOpacity +#define Path_BeginCapWidth m_Path.m_BeginCapWidth +#define Path_EndCapping m_Path.m_EndCap +#define Path_EndCapOffset m_Path.m_EndCapOffset +#define Path_EndCapOpacity m_Path.m_EndCapOpacity +#define Path_EndCapWidth m_Path.m_EndCapWidth +#define Path_PathType m_Path.m_PathType +#define Path_PaintStyle m_Path.m_PaintStyle +#define Path_PathBuffer m_Asset.m_SourcePath +#define SubPath_Closed m_SubPath.m_Closed + +// Fill in implementations for the actual parse tables. +#define HANDLE_QT3DS_RENDER_PROPERTY(type, name, dirty) \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_VEC3_PROPERTY(type, name, dirty) \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_REAL_VEC2_PROPERTY(type, name, dirty) \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_COLOR_PROPERTY(type, name, dirty) \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_RADIAN_PROPERTY(type, name, dirty) \ + theParser.ParseRadianProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_VEC3_RADIAN_PROPERTY(type, name, dirty) \ + theParser.ParseRadianProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_OPACITY_PROPERTY(type, name, dirty) \ + theParser.ParseOpacityProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_ROTATION_ORDER_PROPERTY(type, name, dirty) \ + theParser.ParseRotationOrder(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_NODE_ORIENTATION_PROPERTY(type, name, dirty) \ + theParser.ParseOrientation(inContext.m_ObjectDefinitions.type##_##name, theItem.m_Flags); +#define HANDLE_QT3DS_RENDER_DEPTH_TEST_PROPERTY(type, name, dirty) \ + if (theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name)) \ + theItem.m_##name = !theItem.m_##name; +#define HANDLE_QT3DS_NODE_FLAGS_PROPERTY(type, name, dirty) \ + theParser.ParseNodeFlagsProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_Flags, \ + qt3ds::render::NodeFlagValues::name); +#define HANDLE_QT3DS_NODE_FLAGS_INVERSE_PROPERTY(type, name, dirty) \ + theParser.ParseNodeFlagsInverseProperty(inContext.m_ObjectDefinitions.type##_##name, \ + theItem.m_Flags, qt3ds::render::NodeFlagValues::name); +#define HANDLE_QT3DS_RENDER_ENUM_PROPERTY(type, name, dirty) \ + theParser.ParseEnumProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name); +#define HANDLE_QT3DS_RENDER_SOURCEPATH_PROPERTY(type, name, dirty) \ + theParser.ParseAndResolveSourcePath(inContext.m_ObjectDefinitions.type##_##name, \ + theItem.m_##name); +#define HANDLE_QT3DS_RENDER_ARRAY_PROPERTY(type, name, index, dirty) \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##index, \ + theItem.m_##name[index]); +#define HANDLE_QT3DS_RENDER_VEC2_PROPERTY(type, name, dirty) \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##X, \ + theItem.m_##name.x); \ + theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##Y, theItem.m_##name.y); +#define HANDLE_QT3DS_RENDER_COLOR_VEC3_PROPERTY( \ + type, name, dirty) // noop by intention already handled by HANDLE_QT3DS_RENDER_COLOR_PROPERTY +#define HANDLE_QT3DS_RENDER_TRANSFORM_VEC3_PROPERTY( \ + type, name, dirty) // noop by intention already handled by HANDLE_QT3DS_RENDER_VEC3_PROPERTY + +struct SSceneTranslator : public SGraphObjectTranslator +{ + SSceneTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SScene)()) + { + } + + void PushTranslation(STranslation &inContext) override + { + SScene &theItem = static_cast<SScene &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_SCENE_PROPERTIES + SLayer *theCurrentLayer = nullptr; + theItem.m_FirstChild = nullptr; + for (long idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); + idx < end; ++idx) { + SGraphObjectTranslator::PushTranslation(inContext); + qt3dsdm::Qt3DSDMInstanceHandle theLayer = + inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); + SGraphObjectTranslator *theTranslator = inContext.GetOrCreateTranslator(theLayer); + if (theTranslator && theTranslator->GetGraphObject().m_Type == + qt3ds::render::GraphObjectTypes::Layer) { + SLayer *theLayerObj = static_cast<SLayer *>(&theTranslator->GetGraphObject()); + theLayerObj->m_NextSibling = nullptr; + if (theItem.m_FirstChild == nullptr) + theItem.m_FirstChild = theLayerObj; + else + theCurrentLayer->m_NextSibling = theLayerObj; + theCurrentLayer = theLayerObj; + } + } + } + void ClearChildren() override + { + SScene &theItem = static_cast<SScene &>(GetGraphObject()); + SLayer *theLastChild = nullptr; + + for (SLayer *theChild = theItem.m_FirstChild; theChild; + theChild = static_cast<SLayer *>(theChild->m_NextSibling)) { + if (theLastChild) + theLastChild->m_NextSibling = nullptr; + theChild->m_Parent = nullptr; + theLastChild = theChild; + } + theItem.m_FirstChild = nullptr; + } + void AppendChild(SGraphObject &inChild) override + { + if (inChild.m_Type != GraphObjectTypes::Layer) + return; + SScene &theItem = static_cast<SScene &>(GetGraphObject()); + SLayer &theLayer = static_cast<SLayer &>(inChild); + theItem.AddChild(theLayer); + } + void SetActive(bool /*inActive*/) override + { + // How could we not be active? + } +}; + +struct SNodeTranslator : public SGraphObjectTranslator +{ + SNodeTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SNode)()) + { + Initialize(); + } + SNodeTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, SNode &inNode) + : SGraphObjectTranslator(inInstance, inNode) + { + Initialize(); + } + void Initialize() + { + SNode &theNode = static_cast<SNode &>(GetGraphObject()); + // Ensure the global transform is valid because we use this before we render sometimes. + theNode.m_GlobalTransform = QT3DSMat44::createIdentity(); + } + static inline bool IsNodeType(qt3ds::render::GraphObjectTypes::Enum inType) + { + return qt3ds::render::GraphObjectTypes::IsNodeType(inType); + } + void PushTranslation(STranslation &inContext) override + { + SGraphObjectTranslator::PushTranslation(inContext); + SNode &theItem = static_cast<SNode &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_NODE_PROPERTIES + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_BoneId, + theItem.m_SkeletonId); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Group.m_ordered, + theItem.m_ordered); + bool ignoresParent = false; + if (theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_IgnoresParent, + ignoresParent)) + theItem.m_Flags.SetIgnoreParentTransform(ignoresParent); + } + void AppendChild(SGraphObject &inChild) override + { + if (GraphObjectTypes::IsNodeType(inChild.m_Type) == false) { + QT3DS_ASSERT(false); + return; + } + + SNode &theItem = static_cast<SNode &>(GetGraphObject()); + SNode &theChild = static_cast<SNode &>(inChild); + theItem.AddChild(theChild); + theItem.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); + theChild.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); + } + void ClearChildren() override + { + SNode &theItem = static_cast<SNode &>(GetGraphObject()); + SNode *theLastChild = nullptr; + for (SNode *theChild = theItem.m_FirstChild; theChild; theChild = theChild->m_NextSibling) { + if (theLastChild) + theLastChild->m_NextSibling = nullptr; + theLastChild = theChild; + theChild->m_Parent = nullptr; + } + theItem.m_FirstChild = nullptr; + } + + void SetActive(bool inActive) override + { + SNode &theNode = static_cast<SNode &>(GetGraphObject()); + if (inActive != theNode.m_Flags.IsActive()) { + theNode.m_Flags.SetActive(inActive); + theNode.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); + } + } +}; + +struct SLayerTranslator : public SNodeTranslator +{ + SLayerTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SLayer)()) + { + } + void PushTranslation(STranslation &inContext) override + { + SNodeTranslator::PushTranslation(inContext); + SLayer &theItem = static_cast<SLayer &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_LAYER_PROPERTIES + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Layer.m_AoSamplerate, + theItem.m_AoSamplerate); + } + void AppendChild(SGraphObject &inChild) override + { + if (GraphObjectTypes::IsNodeType(inChild.m_Type)) { + SNodeTranslator::AppendChild(inChild); + } else if (inChild.m_Type == GraphObjectTypes::Effect) { + SLayer &theItem = static_cast<SLayer &>(GetGraphObject()); + theItem.AddEffect(static_cast<SEffect &>(inChild)); + } else if (inChild.m_Type == GraphObjectTypes::RenderPlugin) { + SLayer &theItem = static_cast<SLayer &>(GetGraphObject()); + theItem.m_RenderPlugin = &static_cast<qt3ds::render::SRenderPlugin &>(inChild); + } + } + void ClearChildren() override + { + SNodeTranslator::ClearChildren(); + SLayer &theItem = static_cast<SLayer &>(GetGraphObject()); + SEffect *theLastChild = nullptr; + for (SEffect *theChild = theItem.m_FirstEffect; theChild; + theChild = theChild->m_NextEffect) { + if (theLastChild) + theLastChild->m_NextEffect = nullptr; + theLastChild = theChild; + theChild->m_Layer = nullptr; + } + theItem.m_FirstEffect = nullptr; + theItem.m_RenderPlugin = nullptr; + // Don't clear the light probe properties because those are added/removed as part of the + // normal + // property scan, they aren't added as generic children. + } +}; +struct SLightTranslator : public SNodeTranslator +{ + SLightTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SLight)()) + { + } + void PushTranslation(STranslation &inContext) override + { + SNodeTranslator::PushTranslation(inContext); + SLight &theItem = static_cast<SLight &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_LIGHT_PROPERTIES + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Light.m_ShadowMapRes, + theItem.m_ShadowMapRes); + } + void AppendChild(SGraphObject &inChild) override { SNodeTranslator::AppendChild(inChild); } + void ClearChildren() override { SNodeTranslator::ClearChildren(); } +}; +struct SCameraTranslator : public SNodeTranslator +{ + SCameraTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SCamera)()) + { + } + void PushTranslation(STranslation &inContext) override + { + SNodeTranslator::PushTranslation(inContext); + SCamera &theItem = static_cast<SCamera &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_CAMERA_PROPERTIES + } +}; +struct SModelTranslator : public SNodeTranslator +{ + SModelTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SModel)()) + { + } + void PushTranslation(STranslation &inContext) override + { + SNodeTranslator::PushTranslation(inContext); + SModel &theItem = static_cast<SModel &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_MODEL_PROPERTIES + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Model.m_PoseRoot, + theItem.m_SkeletonRoot); + + theItem.m_FirstMaterial = nullptr; + for (long idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); + idx < end; ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theItemHandle = + inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); + SGraphObjectTranslator *theTranslator = inContext.GetOrCreateTranslator(theItemHandle); + if (theTranslator && IsMaterial(theTranslator->GetGraphObject())) { + SGraphObject *theMaterial = &theTranslator->GetGraphObject(); + SetNextMaterialSibling(*theMaterial, nullptr); + theItem.AddMaterial(*theMaterial); + } + } + } + void AppendChild(SGraphObject &inChild) override + { + if (GraphObjectTypes::IsNodeType(inChild.m_Type)) { + SNodeTranslator::AppendChild(inChild); + } else if (IsMaterial(inChild)) { + SModel &theItem = static_cast<SModel &>(GetGraphObject()); + theItem.AddMaterial(inChild); + } else { + QT3DS_ASSERT(false); + } + } + void ClearChildren() override + { + SModel &theItem = static_cast<SModel &>(GetGraphObject()); + SNodeTranslator::ClearChildren(); + + SGraphObject *theLastMaterial = nullptr; + for (SGraphObject *theMaterial = theItem.m_FirstMaterial; theMaterial; + theMaterial = qt3ds::render::GetNextMaterialSibling(theMaterial)) { + if (theLastMaterial) + qt3ds::render::SetNextMaterialSibling(*theLastMaterial, nullptr); + theLastMaterial = theMaterial; + } + theItem.m_FirstMaterial = nullptr; + } +}; + +static SFloat2 ToFloat2(const Option<SValue> &inValue) +{ + if (inValue.hasValue()) + return inValue->getData<SFloat2>(); + return SFloat2(); +} + +static float ToFloat(const Option<SValue> &inValue) +{ + if (inValue.hasValue()) + return inValue->getData<QT3DSF32>(); + return 0.0f; +} + +struct SPathSubPathTranslator : public SGraphObjectTranslator +{ + eastl::vector<qt3ds::render::SPathAnchorPoint> m_PathBuffer; + SPathSubPathTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SPathSubPath)()) + { + } + + void PushTranslation(STranslation &inContext) override + { + SPathSubPath &theItem = static_cast<SPathSubPath &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_PATH_SUBPATH_PROPERTIES + m_PathBuffer.clear(); + Q3DStudio::IDocumentReader &theReader(inContext.m_Doc.GetDocumentReader()); + QT3DSU32 anchorCount = 0; + for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); + idx < end; ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theAnchor = + inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); + if (theReader.GetObjectTypeName(theAnchor) == L"PathAnchorPoint") + ++anchorCount; + } + QT3DSU32 anchorIdx = 0; + for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle()); + idx < end; ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theAnchor = + inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx); + if (theReader.GetObjectTypeName(theAnchor) == L"PathAnchorPoint") { + SFloat2 theAnchorPos = ToFloat2(theReader.GetInstancePropertyValue( + theAnchor, + inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_Position.m_Property)); + float theIncomingAngle = ToFloat(theReader.GetInstancePropertyValue( + theAnchor, + inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingAngle.m_Property)); + float theIncomingDistance = ToFloat(theReader.GetInstancePropertyValue( + theAnchor, + inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingDistance.m_Property)); + float theOutgoingDistance = ToFloat(theReader.GetInstancePropertyValue( + theAnchor, + inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_OutgoingDistance.m_Property)); + qt3ds::render::SPathAnchorPoint thePoint(QT3DSVec2(theAnchorPos[0], theAnchorPos[1]), + theIncomingAngle, theIncomingAngle + 180.0f, + theIncomingDistance, theOutgoingDistance); + m_PathBuffer.push_back(thePoint); + ++anchorIdx; + } + } + inContext.m_Context.GetPathManager().SetPathSubPathData( + theItem, + qt3ds::foundation::toConstDataRef(m_PathBuffer.begin(), (QT3DSU32)m_PathBuffer.size())); + } + + void AppendChild(SGraphObject &) override {} + + void ClearChildren() override {} + + void SetActive(bool /*inActive*/) override {} +}; + +struct SPathTranslator : public SNodeTranslator +{ + SPathTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SPath)()) + { + } + void AppendChild(SGraphObject &inChild) override + { + if (GraphObjectTypes::IsMaterialType(inChild.m_Type)) { + SPath &theItem = static_cast<SPath &>(GetGraphObject()); + theItem.AddMaterial(&inChild); + theItem.m_Flags.SetDirty(true); + } else if (inChild.m_Type == GraphObjectTypes::PathSubPath) { + SPath &theItem = static_cast<SPath &>(GetGraphObject()); + theItem.AddSubPath(static_cast<SPathSubPath &>(inChild)); + theItem.m_Flags.SetDirty(true); + } else { + SNodeTranslator::AppendChild(inChild); + } + } + + void ClearChildren() override + { + SNodeTranslator::ClearChildren(); + SPath &theItem = static_cast<SPath &>(GetGraphObject()); + theItem.ClearMaterials(); + theItem.ClearSubPaths(); + } + + void PushTranslation(STranslation &inContext) override + { + SNodeTranslator::PushTranslation(inContext); + SPath &theItem = static_cast<SPath &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_PATH_PROPERTIES + } +}; + +struct SDefaultMaterialTranslator : public SGraphObjectTranslator +{ + SDefaultMaterialTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SDefaultMaterial)()) + { + } + + void PushTranslation(STranslation &inContext) override + { + SGraphObjectTranslator::PushTranslation(inContext); + SDefaultMaterial &theItem = static_cast<SDefaultMaterial &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_MATERIAL_PROPERTIES + + // qt3dsdm::Qt3DSDMInstanceHandle parent = inContext.m_AssetGraph.GetParent( + // GetInstanceHandle() ); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, + theItem.m_Lightmaps.m_LightmapIndirect); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, + theItem.m_Lightmaps.m_LightmapRadiosity); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, + theItem.m_Lightmaps.m_LightmapShadow); + } + + void AppendChild(SGraphObject &) override {} + void ClearChildren() override {} + + void SetActive(bool /*inActive*/) override {} +}; + +struct SImageTranslator : public SGraphObjectTranslator +{ + SImageTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SImage)()) + { + } + + void PushTranslation(STranslation &inContext) override + { + SGraphObjectTranslator::PushTranslation(inContext); + SImage &theItem = static_cast<SImage &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_IMAGE_PROPERTIES + + theItem.m_Flags.SetDirty(true); + theItem.m_Flags.SetTransformDirty(true); + } + void AppendChild(SGraphObject &child) override + { + SImage &theItem = static_cast<SImage &>(GetGraphObject()); + if (child.m_Type == GraphObjectTypes::RenderPlugin) + theItem.m_RenderPlugin = &static_cast<qt3ds::render::SRenderPlugin &>(child); + } + void ClearChildren() override + { + SImage &theItem = static_cast<SImage &>(GetGraphObject()); + theItem.m_RenderPlugin = nullptr; + } + + void SetActive(bool /*inActive*/) override {} +}; + +struct STextTranslator : public SNodeTranslator +{ + STextTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SText)()) + { + } + + void PushTranslation(STranslation &inContext) override + { + SNodeTranslator::PushTranslation(inContext); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + SText &theItem = static_cast<SText &>(GetGraphObject()); + ITERATE_QT3DS_RENDER_TEXT_PROPERTIES + theItem.m_Flags.SetTextDirty(true); + } +}; + +inline qt3ds::QT3DSVec2 ToRenderType(const qt3dsdm::SFloat2 &inType) +{ + return qt3ds::QT3DSVec2(inType.m_Floats[0], inType.m_Floats[1]); +} +inline qt3ds::QT3DSVec3 ToRenderType(const qt3dsdm::SFloat3 &inType) +{ + return qt3ds::QT3DSVec3(inType.m_Floats[0], inType.m_Floats[1], inType.m_Floats[2]); +} +inline qt3ds::QT3DSVec4 ToRenderType(const qt3dsdm::SFloat4 &inType) +{ + return qt3ds::QT3DSVec4(inType.m_Floats[0], inType.m_Floats[1], inType.m_Floats[2], + inType.m_Floats[3]); +} + +struct SDynamicObjectTranslator : public SGraphObjectTranslator +{ + typedef eastl::vector<eastl::pair<QT3DSU32, int>> TIdxToPropertyMap; + eastl::basic_string<qt3ds::foundation::TWCharEASTLConverter::TCharType> m_ConvertStr; + TIdxToPropertyMap m_PropertyMap; + + SDynamicObjectTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &, + SDynamicGraphObject &inObject) + : SGraphObjectTranslator(inInstance, inObject) + { + } + + void PushTranslation(STranslation &inContext) override + { + SDynamicGraphObject &theItem = static_cast<SDynamicGraphObject &>(GetGraphObject()); + SDynamicObject &dynObj = *theItem.m_dynamicObject; + IDynamicObjectSystem &theSystem = inContext.m_Context.GetDynamicObjectSystem(); + using namespace qt3ds::render::dynamic; + using qt3ds::foundation::NVConstDataRef; + NVConstDataRef<SPropertyDefinition> theProperties + = theSystem.GetProperties(dynObj.m_ClassName); + if (m_PropertyMap.size() == 0) { + for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { + const SPropertyDefinition &theDefinition(theProperties[idx]); + qt3ds::foundation::ConvertUTF(theDefinition.m_Name.c_str(), 0, m_ConvertStr); + const wchar_t *thePropName = + reinterpret_cast<const wchar_t *>(m_ConvertStr.c_str()); + qt3dsdm::Qt3DSDMPropertyHandle theProperty = + inContext.m_Reader.FindProperty(GetInstanceHandle(), thePropName); + if (theProperty.Valid()) + m_PropertyMap.push_back(eastl::make_pair(idx, theProperty.GetHandleValue())); + } + } + std::shared_ptr<IDataCore> theDataCore = + inContext.m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetDataCore(); + for (TIdxToPropertyMap::iterator theIter = m_PropertyMap.begin(), end = m_PropertyMap.end(); + theIter != end; ++theIter) { + const SPropertyDefinition &theDefinition(theProperties[theIter->first]); + qt3dsdm::Qt3DSDMPropertyHandle theProperty = theIter->second; + // Sometimes it is possible to have dirty properties that no longer exist, e.g. + // when undoing standard material -> custom material change. We just ignore changes + // to such properties. + if (theDataCore->IsProperty(theProperty)) { + Option<qt3dsdm::SValue> theValueOpt = + inContext.m_Reader.GetInstancePropertyValue(GetInstanceHandle(), theProperty); + if (theValueOpt.hasValue()) { + qt3dsdm::SValue &theValue(*theValueOpt); + switch (qt3dsdm::GetValueType(theValue)) { + case qt3dsdm::DataModelDataType::Long: + if (theDefinition.m_DataType + == qt3ds::render::NVRenderShaderDataTypes::QT3DSI32) { + dynObj.SetPropertyValue(theDefinition, + qt3dsdm::get<qt3ds::QT3DSI32>(theValue)); + } else { + QT3DS_ASSERT(false); + } + break; + case qt3dsdm::DataModelDataType::Bool: + if (theDefinition.m_DataType + == qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool) { + dynObj.SetPropertyValue(theDefinition, qt3dsdm::get<bool>(theValue)); + } else { + QT3DS_ASSERT(false); + } + break; + case qt3dsdm::DataModelDataType::Float: + if (theDefinition.m_DataType + == qt3ds::render::NVRenderShaderDataTypes::QT3DSF32) { + dynObj.SetPropertyValue(theDefinition, qt3dsdm::get<float>(theValue)); + } else { + QT3DS_ASSERT(false); + } + break; + case qt3dsdm::DataModelDataType::Float2: + if (theDefinition.m_DataType + == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2) { + dynObj.SetPropertyValue( + theDefinition, ToRenderType(qt3dsdm::get<qt3dsdm::SFloat2>(theValue))); + } else { + QT3DS_ASSERT(false); + } + break; + case qt3dsdm::DataModelDataType::Float3: + if (theDefinition.m_DataType + == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3) { + dynObj.SetPropertyValue( + theDefinition, ToRenderType(qt3dsdm::get<qt3dsdm::SFloat3>(theValue))); + } else { + QT3DS_ASSERT(false); + } + break; + case qt3dsdm::DataModelDataType::Float4: + if (theDefinition.m_DataType + == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec4) { + dynObj.SetPropertyValue( + theDefinition, ToRenderType(qt3dsdm::get<qt3dsdm::SFloat4>(theValue))); + } else { + QT3DS_ASSERT(false); + } + break; + case qt3dsdm::DataModelDataType::Long4: { + // Image + qt3dsdm::SLong4 theData = qt3dsdm::get<qt3dsdm::SLong4>(theValue); + qt3dsdm::Qt3DSDMInstanceHandle imageInstance + = inContext.m_Reader.GetInstanceForObjectRef( + GetInstanceHandle(), theData); + if (imageInstance.Valid()) { + SGraphObjectTranslator *imageTranslator + = inContext.GetOrCreateTranslator(imageInstance); + if (imageTranslator) { + SImage *img = static_cast<SImage *>(&imageTranslator->GetGraphObject()); + theItem.setImage(theDefinition.m_Name, img); + eastl::string theWorkspace; + dynObj.SetPropertyValue( + theDefinition, theDefinition.m_Name.c_str(), + inContext.m_Doc.GetCore()->getProjectFile() + .getProjectPath().toLocal8Bit().constData(), + theWorkspace, inContext.m_Context.GetStringTable()); + } + } else { + eastl::string theWorkspace; + dynObj.SetPropertyValue( + theDefinition, nullptr, + inContext.m_Doc.GetCore()->getProjectFile() + .getProjectPath().toLocal8Bit().constData(), + theWorkspace, inContext.m_Context.GetStringTable()); + } + } break; + // Could be either an enum or a texture. + case qt3dsdm::DataModelDataType::String: { + qt3dsdm::TDataStrPtr theData = qt3dsdm::get<qt3dsdm::TDataStrPtr>(theValue); + if (theData) { + eastl::string theStr; + qt3ds::render::ConvertWideUTF(theData->GetData(), 0, theStr); + eastl::string theWorkspace; + if (theDefinition.m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) { + if (g_StudioApp.getDocumentVersion() < 7) { + // Old style texture. This needs to be run trough document editor + Qt3DSDMInstanceHandle instance = GetInstanceHandle(); + Qt3DSDMPropertyHandle property = theProperty; + SValue value = theValue; + CDoc &doc = inContext.m_Doc; + QTimer::singleShot(10, [instance, property, value, &doc](){ + Q3DStudio::CUpdateableDocumentEditor updatableEditor(doc); + updatableEditor.EnsureEditor("Set Property", __FILE__, + __LINE__) + .SetInstancePropertyValue(instance, property, + value); + }); + g_StudioApp.SetConvertingPresentationOn(); + } else { + // Ignore the value + } + } else { + dynObj.SetPropertyValue( + theDefinition, theStr.c_str(), + inContext.m_Doc.GetCore()->getProjectFile() + .getProjectPath().toLocal8Bit().constData(), + theWorkspace, inContext.m_Context.GetStringTable()); + } + } + } break; + default: + QT3DS_ASSERT(false); + } + } + } + } + } + + void AppendChild(SGraphObject &) override {} + void ClearChildren() override {} +}; + +struct SEffectTranslator : public SDynamicObjectTranslator +{ + // TODO - move this map to inContext and have it looked up by name. + IEffectSystem *m_EffectSystem; + + SEffectTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc, + SEffect &inEffect) + : SDynamicObjectTranslator(inInstance, inAlloc, inEffect) + , m_EffectSystem(nullptr) + { + } + void PushTranslation(STranslation &inContext) override + { + m_EffectSystem = &inContext.m_Context.GetEffectSystem(); + SDynamicObjectTranslator::PushTranslation(inContext); + } + + void SetActive(bool inActive) override + { + SEffect &theItem = static_cast<SEffect &>(GetGraphObject()); + if (m_EffectSystem) { + theItem.SetActive(inActive, *m_EffectSystem); + if (inActive && inActive != theItem.m_Flags.IsActive()) + m_EffectSystem->SetEffectRequiresCompilation(theItem.m_ClassName, true); + } else { + theItem.m_Flags.SetActive(inActive); + } + } + + void ResetEffect() override + { + SEffect &theItem = static_cast<SEffect &>(GetGraphObject()); + if (m_EffectSystem) + theItem.Reset(*m_EffectSystem); + } + + const QString GetError() override + { + SEffect &theItem = static_cast<SEffect &>(GetGraphObject()); + return QString::fromUtf8(theItem.GetError().c_str()); + } + + void SetError(const QString &error) override + { + SEffect &theItem = static_cast<SEffect &>(GetGraphObject()); + theItem.SetError(m_EffectSystem->GetResourceManager().GetRenderContext() + .GetStringTable().RegisterStr(error)); + } +}; +struct SCustomMaterialTranslator : public SDynamicObjectTranslator +{ + ICustomMaterialSystem *m_MaterialSystem; + + SCustomMaterialTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3ds::NVAllocatorCallback &inAlloc, SCustomMaterial &inMaterial) + : SDynamicObjectTranslator(inInstance, inAlloc, inMaterial) + , m_MaterialSystem(nullptr) + { + } + + void PushTranslation(STranslation &inContext) override + { + SDynamicObjectTranslator::PushTranslation(inContext); + SCustomMaterial &theItem = static_cast<SCustomMaterial &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_CUSTOM_MATERIAL_PROPERTIES + + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, + theItem.m_Lightmaps.m_LightmapIndirect); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, + theItem.m_Lightmaps.m_LightmapRadiosity); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, + theItem.m_Lightmaps.m_LightmapShadow); + } + + void SetActive(bool inActive) override + { + if (m_MaterialSystem) { + SCustomMaterial &theItem = static_cast<SCustomMaterial &>(GetGraphObject()); + if (inActive != theItem.m_Flags.IsActive()) { + theItem.m_Flags.SetActive(inActive); + // Force compilation in Studio if custom shader became active, so we know + // if it has any compilation errors. + if (inActive) + m_MaterialSystem->setRequiresCompilation(theItem.m_ClassName, true); + + m_MaterialSystem->OnMaterialActivationChange(theItem, inActive); + } + } + } + + const QString GetError() override + { + SCustomMaterial &theItem = static_cast<SCustomMaterial &>(GetGraphObject()); + return QString::fromUtf8(theItem.GetError().c_str()); + } + + void SetError(const QString &error) override + { + SCustomMaterial &theItem = static_cast<SCustomMaterial &>(GetGraphObject()); + theItem.SetError(m_MaterialSystem->getContext()->GetStringTable().RegisterStr(error)); + } +}; +struct SReferencedMaterialTranslator : public SGraphObjectTranslator +{ + SReferencedMaterialTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SReferencedMaterial)()) + { + } + void PushTranslation(STranslation &inContext) override + { + SGraphObjectTranslator::PushTranslation(inContext); + SReferencedMaterial &theItem = static_cast<SReferencedMaterial &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + ITERATE_QT3DS_RENDER_REFERENCED_MATERIAL_PROPERTIES + + theItem.m_Dirty.SetDirty(); + if (theItem.m_ReferencedMaterial == &theItem) { + qCCritical(qt3ds::INVALID_OPERATION, + "Referenced material is referencing itself."); + } else if (theItem.m_ReferencedMaterial + && theItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::DefaultMaterial) { + SDefaultMaterial *theDefaultItem = + static_cast<SDefaultMaterial *>(theItem.m_ReferencedMaterial); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, + theDefaultItem->m_Lightmaps.m_LightmapIndirect); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, + theDefaultItem->m_Lightmaps.m_LightmapRadiosity); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, + theDefaultItem->m_Lightmaps.m_LightmapShadow); + } else if (theItem.m_ReferencedMaterial + && theItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::CustomMaterial) { + SCustomMaterial *theDefaultItem = + static_cast<SCustomMaterial *>(theItem.m_ReferencedMaterial); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect, + theDefaultItem->m_Lightmaps.m_LightmapIndirect); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity, + theDefaultItem->m_Lightmaps.m_LightmapRadiosity); + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow, + theDefaultItem->m_Lightmaps.m_LightmapShadow); + } + } + + void AppendChild(SGraphObject &) override {} + + void ClearChildren() override {} + + void SetActive(bool /*inActive*/) override {} +}; +using qt3ds::render::SRenderPlugin; +using qt3ds::render::SRenderPropertyValueUpdate; +using qt3ds::render::IRenderPluginClass; +using qt3ds::render::SRenderPluginPropertyDeclaration; +struct SRenderPluginPropertyUpdateFactory +{ + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, float value, + const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &) + { + ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value)); + } + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, qt3ds::QT3DSI32 value, + const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &) + { + ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value)); + } + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, bool value, + const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &) + { + ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value)); + } + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, qt3dsdm::TDataStrPtr value, + const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &, + qt3ds::foundation::IStringTable &strTable) + { + if (value) { + ioUpdates.push_back( + SRenderPropertyValueUpdate(theDec.m_Name, strTable.RegisterStr(value->GetData()))); + } + } + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, qt3dsdm::SStringRef value, + const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &, + qt3ds::foundation::IStringTable &strTable) + { + ioUpdates.push_back( + SRenderPropertyValueUpdate(theDec.m_Name, strTable.RegisterStr(value.m_Id))); + } + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, + const qt3dsdm::SFloat2 &value, const SRenderPluginPropertyDeclaration &theDec, + IRenderPluginClass &inClass) + { + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0])); + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1])); + } + + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, + const qt3dsdm::SFloat3 &value, const SRenderPluginPropertyDeclaration &theDec, + IRenderPluginClass &inClass) + { + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0])); + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1])); + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset + 2).first, value.m_Floats[2])); + } + + static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, + const qt3dsdm::SFloat4 &value, const SRenderPluginPropertyDeclaration &theDec, + IRenderPluginClass &inClass) + { + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0])); + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1])); + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset + 2).first, value.m_Floats[2])); + ioUpdates.push_back(SRenderPropertyValueUpdate( + inClass.GetPropertyValueInfo(theDec.m_StartOffset + 3).first, value.m_Floats[3])); + } +}; +struct SRenderPluginTranslator : public SGraphObjectTranslator +{ + eastl::vector<SRenderPropertyValueUpdate> m_PropertyUpdates; + + SRenderPluginTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SRenderPlugin)()) + { + } + + void PushTranslation(STranslation &inContext) override + { + SRenderPlugin &theItem = static_cast<SRenderPlugin &>(GetGraphObject()); + // First, get the instance via resolving the source path. + Qt3DSDMPropertyHandle sourcepath = + inContext.m_Reader.FindProperty(GetInstanceHandle(), L"sourcepath"); + Option<SValue> theSourcePath = + inContext.m_Reader.GetInstancePropertyValue(GetInstanceHandle(), sourcepath); + qt3dsdm::TDataStrPtr theData = theSourcePath->getData<qt3dsdm::TDataStrPtr>(); + if (!theData) + return; + + Q3DStudio::CFilePath theFullPath = inContext.m_Doc.GetResolvedPathToDoc(theData->GetData()); + qt3ds::foundation::IStringTable &theStrTable = inContext.m_Context.GetStringTable(); + theItem.m_PluginPath = theStrTable.RegisterStr(theFullPath.toCString()); + qt3ds::render::IRenderPluginInstance *theInstance = + inContext.m_Context.GetRenderPluginManager().GetOrCreateRenderPluginInstance( + theItem.m_PluginPath, &theItem); + + // Couldn't load the instance, so we can't render the instance. + if (theInstance == nullptr) + return; + // Grab the instance's parent and get the properties that are specific to just that + // instance. + TInstanceHandleList derivationParents; + std::shared_ptr<IDataCore> theDataCore = + inContext.m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetDataCore(); + theDataCore->GetInstanceParents(GetInstanceHandle(), derivationParents); + if (derivationParents.size() == 0) + return; + TPropertyHandleList theSpecificProperties; + theDataCore->GetInstanceProperties(derivationParents[0], theSpecificProperties); + eastl::string propStem; + eastl::string propname; + m_PropertyUpdates.clear(); + qt3ds::render::IRenderPluginClass &theClass = theInstance->GetPluginClass(); + using qt3ds::render::SRenderPluginPropertyDeclaration; + if (theClass.GetRegisteredProperties().size() == 0) { + for (size_t idx = 0, end = theSpecificProperties.size(); idx < end; ++idx) { + Qt3DSDMPropertyDefinition theProperty = + theDataCore->GetProperty(theSpecificProperties[idx]); + qt3dsdm::AdditionalMetaDataType::Value theMetaType = + inContext.m_StudioSystem.GetActionMetaData()->GetAdditionalMetaDataType( + GetInstanceHandle(), theSpecificProperties[idx]); + CRegisteredString thePropName(theStrTable.RegisterStr(theProperty.m_Name.c_str())); + switch (theProperty.m_Type) { + case DataModelDataType::Float: + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Float)); + break; + case DataModelDataType::Float2: + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Vector2)); + break; + case DataModelDataType::Float3: + if (theMetaType != AdditionalMetaDataType::Color) { + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Vector3)); + } else { + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Color)); + } + break; + case DataModelDataType::Float4: + if (theMetaType == AdditionalMetaDataType::Color) { + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Color)); + } + break; + case DataModelDataType::Long: + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Long)); + break; + case DataModelDataType::String: + case DataModelDataType::StringRef: + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::String)); + break; + case DataModelDataType::Bool: + theClass.RegisterProperty(SRenderPluginPropertyDeclaration( + thePropName, qt3ds::render::SRenderPluginPropertyTypes::Boolean)); + break; + default: + // Unsupported plugin property. + QT3DS_ASSERT(false); + break; + } + } + } + for (size_t idx = 0, end = theSpecificProperties.size(); idx < end; ++idx) { + Option<SValue> thePropValueOpt = inContext.m_Reader.GetInstancePropertyValue( + GetInstanceHandle(), theSpecificProperties[idx]); + if (thePropValueOpt.hasValue()) { + SValue &thePropValue = thePropValueOpt.getValue(); + Qt3DSDMPropertyDefinition theProperty = + theDataCore->GetProperty(theSpecificProperties[idx]); + SRenderPluginPropertyDeclaration theDeclaration(theClass.GetPropertyDeclaration( + theStrTable.RegisterStr(theProperty.m_Name.c_str()))); + + switch (thePropValue.getType()) { + case DataModelDataType::None: + QT3DS_ASSERT(false); + break; + case DataModelDataType::Float: + SRenderPluginPropertyUpdateFactory::Add( + m_PropertyUpdates, thePropValue.getData<float>(), theDeclaration, theClass); + break; + case DataModelDataType::Float2: + SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, + thePropValue.getData<SFloat2>(), + theDeclaration, theClass); + break; + case DataModelDataType::Float3: + SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, + thePropValue.getData<SFloat3>(), + theDeclaration, theClass); + break; + case DataModelDataType::Float4: + SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, + thePropValue.getData<SFloat4>(), + theDeclaration, theClass); + break; + case DataModelDataType::Long: + SRenderPluginPropertyUpdateFactory::Add( + m_PropertyUpdates, thePropValue.getData<qt3ds::QT3DSI32>(), theDeclaration, theClass); + break; + case DataModelDataType::String: + SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, + thePropValue.getData<TDataStrPtr>(), + theDeclaration, theClass, theStrTable); + break; + case DataModelDataType::Bool: + SRenderPluginPropertyUpdateFactory::Add( + m_PropertyUpdates, thePropValue.getData<bool>(), theDeclaration, theClass); + break; + case DataModelDataType::StringRef: + SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates, + thePropValue.getData<SStringRef>(), + theDeclaration, theClass, theStrTable); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + } + theInstance->Update(NVConstDataRef<SRenderPropertyValueUpdate>(m_PropertyUpdates.data(), + m_PropertyUpdates.size())); + } + void AppendChild(SGraphObject &) override {} + void ClearChildren() override {} + void SetActive(bool inActive) override + { + SRenderPlugin &theItem = static_cast<SRenderPlugin &>(GetGraphObject()); + theItem.m_Flags.SetActive(inActive); + } +}; + +struct SAliasTranslator : public SGraphObjectTranslator +{ + SGraphObjectTranslator *m_ReferenceTree; + Qt3DSDMInstanceHandle m_ReferencedInstance; + SAliasTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc) + : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SNode)()) + , m_ReferenceTree(nullptr) + { + } + void RecurseAndCreateTranslators(STranslation &inContext, + qt3dsdm::Qt3DSDMInstanceHandle inInstance) + { + for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(inInstance); idx < end; + ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theChild = inContext.m_AssetGraph.GetChild(inInstance, idx); + inContext.GetOrCreateTranslator(theChild, m_InstanceHandle); + RecurseAndCreateTranslators(inContext, theChild); + } + } + void PushTranslation(STranslation &inContext) override + { + STranslatorDataModelParser theParser(inContext, GetInstanceHandle()); + Option<SObjectRefType> theData = theParser.GetPropertyValue<SObjectRefType>( + inContext.m_ObjectDefinitions.m_Alias.m_ReferencedNode); + m_ReferencedInstance = Qt3DSDMInstanceHandle(); + m_ReferenceTree = nullptr; + ((SNode *)m_GraphObject)->m_Flags.SetDirty(true); + if (theData.hasValue()) { + m_ReferencedInstance = + inContext.m_Reader.GetInstanceForObjectRef(GetInstanceHandle(), *theData); + if (inContext.m_Reader.IsInstance(m_ReferencedInstance)) { + m_ReferenceTree = + inContext.GetOrCreateTranslator(m_ReferencedInstance, m_InstanceHandle); + if (m_ReferenceTree + && !GraphObjectTypes::IsNodeType(m_ReferenceTree->GetGraphObject().m_Type)) { + QT3DS_ASSERT(false); + m_ReferenceTree = nullptr; + m_ReferencedInstance = Qt3DSDMInstanceHandle(); + } else { + RecurseAndCreateTranslators(inContext, m_ReferencedInstance); + } + } + } + } + + void AfterRenderGraphIsBuilt(STranslation &inContext) override + { + SNode &theItem = static_cast<SNode &>(GetGraphObject()); + STranslatorDataModelParser theParser(inContext, m_InstanceHandle); + ITERATE_QT3DS_RENDER_NODE_PROPERTIES + theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_BoneId, + theItem.m_SkeletonId); + bool ignoresParent = false; + if (theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_IgnoresParent, + ignoresParent)) + theItem.m_Flags.SetIgnoreParentTransform(ignoresParent); + theItem.m_Flags.SetDirty(true); + } + void AppendChild(SGraphObject &inObject) override + { + if (m_ReferenceTree) + m_ReferenceTree->AppendChild(inObject); + } + void ClearChildren() override + { + if (m_ReferenceTree) + m_ReferenceTree->ClearChildren(); + } + void SetActive(bool inActive) override + { + SNode &theItem = static_cast<SNode &>(GetGraphObject()); + theItem.m_Flags.SetActive(inActive); + } + SGraphObject &GetGraphObject() override + { + if (m_ReferenceTree) + return *m_ReferenceTree->m_GraphObject; + return *m_GraphObject; + } + qt3dsdm::Qt3DSDMInstanceHandle GetSceneGraphInstanceHandle() override + { + if (m_ReferencedInstance.Valid()) + return m_ReferencedInstance; + return m_InstanceHandle; + } + Qt3DSDMInstanceHandle GetInstanceHandle() override { return m_InstanceHandle; } + + SGraphObject &GetNonAliasedGraphObject() override { return *m_GraphObject; } +}; +} + +void SGraphObjectTranslator::PushTranslation(STranslation &inTranslatorContext) +{ + Q3DStudio::CString theId = inTranslatorContext.m_Reader.GetFileId(GetInstanceHandle()); + if (theId.size()) + GetGraphObject().m_Id = + inTranslatorContext.m_Context.GetStringTable().RegisterStr(theId.c_str()); +} + +bool STranslation::IncludeNode(const SNode &inNode) +{ + SGraphObjectTranslator *theTranslator = inNode.m_UserData.DynamicCast<SGraphObjectTranslator>(); + if (theTranslator + && m_Doc.GetDocumentReader().IsCurrentlyActive(theTranslator->GetInstanceHandle())) + return true; + return false; +} + +void STranslation::ReleaseEffect(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + if (!m_Reader.IsInstance(inInstance)) + return; + + qt3dsdm::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance); + qt3dsdm::Qt3DSDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance); + + if (theType == qt3dsdm::ComposerObjectTypes::Unknown && theParentClass.Valid()) + theType = m_ObjectDefinitions.GetType(theParentClass); + + if (theType == qt3dsdm::ComposerObjectTypes::Effect) { + IEffectSystem &theSystem = m_Context.GetEffectSystem(); + if (theParentClass.Valid()) { + Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass); + CRegisteredString theNameStr + = m_Context.GetStringTable().RegisterStr(theInstanceName); + + if (theSystem.IsEffectRegistered(theNameStr)) { + TInstanceToTranslatorMap::iterator theTranslatorList + = m_TranslatorMap.find(inInstance); + if (theTranslatorList != m_TranslatorMap.end()) + m_TranslatorMap.erase(theTranslatorList); + theSystem.SetEffectRequiresCompilation(theNameStr, true); + } + } + } +} + +void STranslation::releaseMaterial(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + if (!m_Reader.IsInstance(inInstance)) + return; + + qt3dsdm::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance); + qt3dsdm::Qt3DSDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance); + + if (theType == qt3dsdm::ComposerObjectTypes::Unknown && theParentClass.Valid()) + theType = m_ObjectDefinitions.GetType(theParentClass); + + if (theType == qt3dsdm::ComposerObjectTypes::CustomMaterial) { + ICustomMaterialSystem &theSystem = m_Context.GetCustomMaterialSystem(); + if (theParentClass.Valid()) { + Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass); + CRegisteredString theNameStr + = m_Context.GetStringTable().RegisterStr(theInstanceName); + + if (theSystem.IsMaterialRegistered(theNameStr)) { + TInstanceToTranslatorMap::iterator theTranslatorList + = m_TranslatorMap.find(inInstance); + if (theTranslatorList != m_TranslatorMap.end()) + m_TranslatorMap.erase(theTranslatorList); + theSystem.setRequiresCompilation(theNameStr, true); + } + } + } +} + +SGraphObjectTranslator *STranslation::CreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + SGraphObjectTranslator *theNewTranslator = nullptr; + qt3dsdm::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance); + qt3dsdm::Qt3DSDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance); + if (theType == NULL && theParentClass.Valid()) + theType = m_ObjectDefinitions.GetType(theParentClass); + + // For the subset of possible instances, pick out the valid translators. + switch (theType) { + case qt3dsdm::ComposerObjectTypes::Group: + case qt3dsdm::ComposerObjectTypes::Signal: + case qt3dsdm::ComposerObjectTypes::Component: + case qt3dsdm::ComposerObjectTypes::Node: + theNewTranslator = QT3DS_NEW(m_Allocator, SNodeTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Scene: + theNewTranslator = QT3DS_NEW(m_Allocator, SSceneTranslator)(inInstance, m_Allocator); + m_Scene = static_cast<SScene *>(&theNewTranslator->GetGraphObject()); + m_Scene->m_Presentation = &m_Presentation; + break; + case qt3dsdm::ComposerObjectTypes::Layer: + theNewTranslator = QT3DS_NEW(m_Allocator, SLayerTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Light: + theNewTranslator = QT3DS_NEW(m_Allocator, SLightTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Camera: + theNewTranslator = QT3DS_NEW(m_Allocator, SCameraTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Model: + theNewTranslator = QT3DS_NEW(m_Allocator, SModelTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Image: + theNewTranslator = QT3DS_NEW(m_Allocator, SImageTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Text: + theNewTranslator = QT3DS_NEW(m_Allocator, STextTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Material: + theNewTranslator = QT3DS_NEW(m_Allocator, SDefaultMaterialTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::ReferencedMaterial: + theNewTranslator = + QT3DS_NEW(m_Allocator, SReferencedMaterialTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Alias: + theNewTranslator = QT3DS_NEW(m_Allocator, SAliasTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Path: + theNewTranslator = QT3DS_NEW(m_Allocator, SPathTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::SubPath: + theNewTranslator = QT3DS_NEW(m_Allocator, SPathSubPathTranslator)(inInstance, m_Allocator); + break; + case qt3dsdm::ComposerObjectTypes::Effect: { + IEffectSystem &theSystem = m_Context.GetEffectSystem(); + if (theParentClass.Valid()) { + Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass); + CRegisteredString theNameStr = + m_Context.GetStringTable().RegisterStr(theInstanceName); + + if (theSystem.IsEffectRegistered(theNameStr) + && theSystem.DoesEffectRequireCompilation(theNameStr)) { + theSystem.UnregisterEffect(theNameStr); + } + + if (!theSystem.IsEffectRegistered(theNameStr)) { + // We assume the effect has already been registered and such. + qt3dsdm::IMetaData &theMetaData(*m_StudioSystem.GetActionMetaData()); + Q3DStudio::CString theInstancePath = m_Reader.GetSourcePath(theParentClass); + Option<qt3dsdm::SMetaDataEffect> theMetaEffect = + theMetaData.GetEffectBySourcePath( + m_Context.GetStringTable().GetNarrowStr(theInstancePath)); + if (theMetaEffect.hasValue()) { + qt3ds::render::IUIPLoader::CreateEffectClassFromMetaEffect( + theNameStr, m_Context.GetFoundation(), theSystem, theMetaEffect, + m_Context.GetStringTable()); + theSystem.SetEffectRequiresCompilation(theNameStr, true); + } + } + + if (theSystem.IsEffectRegistered(theNameStr)) { + theNewTranslator = QT3DS_NEW(m_Allocator, SEffectTranslator)( + inInstance, m_Allocator, + *theSystem.CreateEffectInstance(theNameStr, m_Allocator)); + } + } + } + break; + case qt3dsdm::ComposerObjectTypes::CustomMaterial: { + ICustomMaterialSystem &system = m_Context.GetCustomMaterialSystem(); + if (theParentClass.Valid()) { + Q3DStudio::CString instanceName = m_Reader.GetName(theParentClass); + CRegisteredString name + = m_Context.GetStringTable().RegisterStr(instanceName); + + bool requiresCompilation = false; + if (system.IsMaterialRegistered(name) && system.requiresCompilation(name)) { + system.unregisterMaterial(name); + requiresCompilation = true; + } + + if (!system.IsMaterialRegistered(name)) { + // We assume the effect has already been registered and such. + qt3dsdm::IMetaData &metaData(*m_StudioSystem.GetActionMetaData()); + Q3DStudio::CString instancePath = m_Reader.GetSourcePath(theParentClass); + Option<qt3dsdm::SMetaDataCustomMaterial> materialData + = metaData.GetMaterialBySourcePath(m_Context.GetStringTable() + .GetNarrowStr(instancePath)); + if (materialData.hasValue()) { + qt3ds::render::IUIPLoader::CreateMaterialClassFromMetaMaterial( + name, m_Context.GetFoundation(), system, materialData, + m_Context.GetStringTable()); + system.setRequiresCompilation(name, requiresCompilation); + } + } + if (system.IsMaterialRegistered(name)) { + theNewTranslator = QT3DS_NEW(m_Allocator, SCustomMaterialTranslator)( + inInstance, m_Allocator, + *system.CreateCustomMaterial(name, m_Allocator)); + static_cast<SCustomMaterialTranslator *>(theNewTranslator) + ->m_MaterialSystem = &system; + } + } + } + break; + case qt3dsdm::ComposerObjectTypes::RenderPlugin: { + theNewTranslator = QT3DS_NEW(m_Allocator, SRenderPluginTranslator)(inInstance, m_Allocator); + } + break; + default: + break; + } + + return theNewTranslator; +} + +bool CompareTranslator(const STranslation::THandleTranslatorPair &first, + const STranslation::THandleTranslatorPair &second) +{ + return first.first == second.first; +} + +struct STranslatorPredicate +{ + Qt3DSDMInstanceHandle m_Instance; + STranslatorPredicate(Qt3DSDMInstanceHandle &ins) + : m_Instance(ins) + { + } + bool operator()(const STranslation::THandleTranslatorPair &first) const + { + return first.first == m_Instance; + } +}; + +Option<STranslation::THandleTranslatorPair> +FindTranslator(STranslation::THandleTranslatorPairList &inList, + Qt3DSDMInstanceHandle inInstance = Qt3DSDMInstanceHandle()) +{ + STranslation::THandleTranslatorPairList::iterator iter = + eastl::find_if(inList.begin(), inList.end(), STranslatorPredicate(inInstance)); + if (iter != inList.end()) + return *iter; + return Empty(); +} + +SGraphObjectTranslator *STranslation::GetOrCreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + return GetOrCreateTranslator(inInstance, Qt3DSDMInstanceHandle()); +} + +SGraphObjectTranslator * +STranslation::GetOrCreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMInstanceHandle inAliasInstance) +{ + TInstanceToTranslatorMap::iterator theTranslatorList = + m_TranslatorMap.insert(eastl::make_pair(inInstance, THandleTranslatorPairList())).first; + THandleTranslatorPairList &theList = theTranslatorList->second; + Option<STranslation::THandleTranslatorPair> theExistingTranslator = + FindTranslator(theList, inAliasInstance); + + if (theExistingTranslator.hasValue()) { + return theExistingTranslator->second; + } + if (m_Reader.IsInstance(inInstance) == false) + return nullptr; + + SGraphObjectTranslator *theNewTranslator = CreateTranslator(inInstance); + if (theNewTranslator != nullptr) { + theNewTranslator->m_AliasInstanceHandle = inAliasInstance; + m_DirtySet.insert(*theNewTranslator); + theList.push_back(THandleTranslatorPair(inAliasInstance, theNewTranslator)); + } + + return theNewTranslator; +} + +STranslation::THandleTranslatorPairList & +STranslation::GetTranslatorsForInstance(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + return m_TranslatorMap.insert(eastl::make_pair(inInstance, THandleTranslatorPairList())) + .first->second; +} + +qt3dsdm::Qt3DSDMInstanceHandle STranslation::GetAnchorPoint(QT3DSU32 inAnchorIndex) +{ + SGraphObjectTranslator *thePathTranslator = + static_cast<SGraphObjectTranslator *>(m_PathWidget->GetNode().m_UserData.m_UserData); + if (thePathTranslator == nullptr) + return qt3dsdm::Qt3DSDMInstanceHandle(); + qt3dsdm::Qt3DSDMInstanceHandle thePathHandle = thePathTranslator->GetInstanceHandle(); + QT3DSU32 theAnchorIndex = 0; + for (QT3DSI32 idx = 0, end = m_AssetGraph.GetChildCount(thePathHandle); idx < end; ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theChildInstance = m_AssetGraph.GetChild(thePathHandle, idx); + if (m_Doc.GetDocumentReader().GetObjectTypeName(theChildInstance) == L"SubPath") { + QT3DSI32 numAnchors = m_AssetGraph.GetChildCount(theChildInstance); + QT3DSU32 endIndex = theAnchorIndex + (QT3DSU32)numAnchors; + if (endIndex > inAnchorIndex) { + return m_AssetGraph.GetChild(theChildInstance, inAnchorIndex - theAnchorIndex); + } else + theAnchorIndex = endIndex; + } + } + return qt3dsdm::Qt3DSDMInstanceHandle(); +} + +void STranslation::updateHelperGridFromSettings() +{ + m_helperGridEnabled = CStudioPreferences::isHelperGridOn(); + if (m_helperGridEnabled && m_helperGridWidget && m_EditCameraEnabled) { + if (m_EditCameraInfo.m_CameraType == EditCameraTypes::Directional) { + if (m_EditCameraInfo.m_Direction.x != 0.f) { + m_helperGridWidget->setColors(CStudioPreferences::helperGridColor(), + CStudioPreferences::yAxisColor(), + CStudioPreferences::zAxisColor()); + m_helperGridWidget->rotate(float(M_PI) / 2.f, QT3DSVec3(0.f, 0.f, 1.f)); + } else if (m_EditCameraInfo.m_Direction.y != 0.f) { + m_helperGridWidget->setColors(CStudioPreferences::helperGridColor(), + CStudioPreferences::xAxisColor(), + CStudioPreferences::zAxisColor()); + m_helperGridWidget->rotate(0.f, QT3DSVec3()); + } else { + m_helperGridWidget->setColors(CStudioPreferences::helperGridColor(), + CStudioPreferences::xAxisColor(), + CStudioPreferences::yAxisColor()); + m_helperGridWidget->rotate(float(M_PI) / 2.f, QT3DSVec3(1.f, 0.f, 0.f)); + } + } else { + m_helperGridWidget->setColors(CStudioPreferences::helperGridColor(), + CStudioPreferences::xAxisColor(), + CStudioPreferences::zAxisColor()); + m_helperGridWidget->rotate(0.f, QT3DSVec3()); + } + m_helperGridWidget->setLines(CStudioPreferences::helperGridLines(), + float(CStudioPreferences::helperGridSpacing())); + } +} + +void STranslation::updateAxisHelperFromSettings() +{ + m_axisHelperEnabled = CStudioPreferences::isAxisHelperOn(); +} + +qt3dsdm::Qt3DSDMInstanceHandle STranslation::GetAnchorPoint(SPathPick &inPick) +{ + return GetAnchorPoint(inPick.m_AnchorIndex); +} + +namespace qt3ds { +namespace studio { + struct SEditorLayerTranslator : public SLayerTranslator + { + SEditorLayerTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3ds::NVAllocatorCallback &inAlloc) + : SLayerTranslator(inInstance, inAlloc) + { + } + void PushTranslation(STranslation &) override + { + SLayer &theItem = static_cast<SLayer &>(GetGraphObject()); + theItem.m_Flags.SetActive(true); + } + void AppendChild(SGraphObject &inChild) override + { + if (GraphObjectTypes::IsNodeType(inChild.m_Type)) { + SNodeTranslator::AppendChild(inChild); + } + } + }; +} +} + +STranslation::STranslation(IStudioRenderer &inRenderer, IQt3DSRenderContext &inContext) + : m_Renderer(inRenderer) + , m_Context(inContext) + , m_Doc(*g_StudioApp.GetCore()->GetDoc()) + , m_Reader(m_Doc.GetDocumentReader()) + , m_ObjectDefinitions( + m_Doc.GetStudioSystem()->GetClientDataModelBridge()->GetObjectDefinitions()) + , m_StudioSystem(*m_Doc.GetStudioSystem()) + , m_FullSystem(*m_Doc.GetStudioSystem()->GetFullSystem()) + , m_AssetGraph(*m_Doc.GetAssetGraph()) + , m_Allocator(inContext.GetRenderContext().GetFoundation()) + , m_TranslatorMap(inContext.GetAllocator(), "STranslation::m_TranslatorMap") + , m_DirtySet(inContext.GetAllocator(), "STranslation::m_DirtySet") + , m_Scene(nullptr) + , m_SignalConnections(inContext.GetAllocator(), "STranslation::m_SignalConnections") + , m_ComponentTimeDepth(0) + , m_KeyRepeat(0) + , m_EditCameraEnabled(false) + , m_EditLightEnabled(false) + , m_Viewport(0, 0) + , m_EditCameraLayerTranslator(nullptr) + , m_AxisHelperLayerTranslator(nullptr) + , m_PixelBuffer(inContext.GetAllocator(), "STranslation::m_PixelBuffer") + , m_editModeCamerasAndLights(inContext.GetAllocator(), + "STranslation::m_editModeCamerasAndLights") + , m_GuideAllocator(inContext.GetAllocator(), "STranslation::m_GuideAllocator") +{ + m_EditCamera.m_Flags.SetActive(true); + m_EditLight.m_Flags.SetActive(true); + qt3dsdm::Qt3DSDMInstanceHandle theScene = m_AssetGraph.GetRoot(0); + m_GraphIterator.ClearResults(); + m_AssetGraph.GetDepthFirst(m_GraphIterator, theScene); + for (; !m_GraphIterator.IsDone(); ++m_GraphIterator) { + qt3dsdm::Qt3DSDMInstanceHandle theInstance(m_GraphIterator.GetCurrent()); + GetOrCreateTranslator(theInstance); + } + qt3dsdm::IStudioFullSystemSignalProvider *theProvider = m_FullSystem.GetSignalProvider(); + m_SignalConnections.push_back( + theProvider->ConnectInstanceCreated(std::bind(&STranslation::DoMarkDirty, + this, std::placeholders::_1))); + m_SignalConnections.push_back(theProvider->ConnectInstanceDeleted( + std::bind(&STranslation::ReleaseTranslation, this, std::placeholders::_1))); + m_SignalConnections.push_back( + theProvider->ConnectInstancePropertyValue(std::bind(&STranslation::DoMarkDirty, + this, std::placeholders::_1))); + m_SignalConnections.push_back(m_AssetGraph.ConnectChildAdded( + std::bind(&STranslation::MarkGraphInstanceDirty, this, std::placeholders::_1, + std::placeholders::_2))); + m_SignalConnections.push_back(m_AssetGraph.ConnectChildMoved( + std::bind(&STranslation::MarkGraphInstanceDirty, this, std::placeholders::_1, + std::placeholders::_2))); + m_SignalConnections.push_back(m_AssetGraph.ConnectChildRemoved( + std::bind(&STranslation::MarkGraphInstanceDirty, this, std::placeholders::_1, + std::placeholders::_2))); + m_SignalConnections.push_back(theProvider->ConnectBeginComponentTime( + std::bind(&STranslation::MarkBeginComponentTime, this, std::placeholders::_1))); + m_SignalConnections.push_back(theProvider->ConnectComponentTime( + std::bind(&STranslation::MarkComponentTime, this, std::placeholders::_1))); + + ::CColor color = CStudioPreferences::rulerBackgroundColor(); // Rectangles under tick marks + m_rectColor = QT3DSVec4(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f, + 1.f); + color = CStudioPreferences::rulerTickColor(); // Tick marks + m_lineColor = QT3DSVec4(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f, + 1.f); + color = CStudioPreferences::guideNormalColor(); + m_guideColor = QT3DSVec4(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f, + 1.f); + color = CStudioPreferences::guideSelectedColor(); + m_selectedGuideColor = QT3DSVec4(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f, + 1.f); + color = CStudioPreferences::guideFillColor(); // Not sure what this is used for + m_guideFillColor = QT3DSVec4(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f, + 1.f); + color = CStudioPreferences::guideFillSelectedColor(); // Not sure what this is used for + m_selectedGuideFillColor = QT3DSVec4(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f, + 1.f); +} + +void STranslation::BuildRenderGraph(SGraphObjectTranslator &inParent, bool scenePreviewPass, + Qt3DSDMInstanceHandle inAliasHandle) +{ + SGraphObjectTranslator &theParentTranslator(inParent); + theParentTranslator.ClearChildren(); + + updateAxisHelperFromSettings(); + if (theParentTranslator.GetGraphObject().m_Type == GraphObjectTypes::Scene && + m_axisHelperEnabled && !scenePreviewPass) { + if (m_AxisHelperLayerTranslator && m_AxisHelperLayerTranslator->GetInstanceHandle() + != inParent.GetInstanceHandle()) { + QT3DS_FREE(m_Allocator, m_AxisHelperLayerTranslator); + m_AxisHelperLayerTranslator = nullptr; + } + if (!m_AxisHelperLayerTranslator) { + m_AxisHelperLayerTranslator = + QT3DS_NEW(m_Allocator, SEditorLayerTranslator)(inParent.GetInstanceHandle(), m_Allocator); + SLayer &layer = static_cast<SLayer &>(m_AxisHelperLayerTranslator->GetGraphObject()); + layer.m_Background = LayerBackground::Color; + static const QT3DSVec4 matteColor(CStudioPreferences::matteColor().redF(), + CStudioPreferences::matteColor().greenF(), + CStudioPreferences::matteColor().blueF(), 0.5f); + layer.m_ClearColor = m_EditCameraEnabled ? QT3DSVec4(0, 0, 0, 0.5) : matteColor; + layer.m_LeftUnits = LayerUnitTypes::Pixels; + layer.m_RightUnits = LayerUnitTypes::Pixels; + layer.m_HeightUnits = LayerUnitTypes::Pixels; + layer.m_WidthUnits = LayerUnitTypes::Pixels; + layer.m_TopUnits = LayerUnitTypes::Pixels; + layer.m_BottomUnits = LayerUnitTypes::Pixels; + layer.m_HorizontalFieldValues = HorizontalFieldValues::LeftWidth; + layer.m_VerticalFieldValues = VerticalFieldValues::HeightBottom; + float pixelRatio = float(StudioUtils::devicePixelRatio()); + layer.m_Top = 10 * pixelRatio; + layer.m_Left = 10 * pixelRatio; + layer.m_Right = 10 * pixelRatio; + layer.m_Bottom = 10 * pixelRatio; + layer.m_Width = 120 * pixelRatio; + layer.m_Height = 120 * pixelRatio; + layer.m_Flags.SetDirty(true); + m_AxisCamera.m_Id = m_Context.GetStringTable().RegisterStr("AxisCamera"); + } + theParentTranslator.AppendChild(m_AxisHelperLayerTranslator->GetGraphObject()); + SLayer &layer = static_cast<SLayer &>(m_AxisHelperLayerTranslator->GetGraphObject()); + layer.m_NextSibling = layer.m_PreviousSibling = nullptr; + m_AxisHelperLayerTranslator->SetActive(true); + m_AxisHelperLayerTranslator->AppendChild(m_AxisCamera); + m_AxisCamera.m_Flags.SetActive(true); + // Make sure axis helper layer children gets updated + m_Context.GetRenderer().ChildrenUpdated(layer); + } + + if (m_EditCameraEnabled && !scenePreviewPass) { + const auto objectType = theParentTranslator.GetGraphObject().m_Type; + if (objectType == GraphObjectTypes::Layer) { + theParentTranslator.AppendChild(m_EditCamera); + if (m_EditLightEnabled) { + m_EditLight.m_Parent = &m_EditCamera; + m_EditCamera.m_FirstChild = &m_EditLight; + } else { + m_EditCamera.m_FirstChild = nullptr; + m_EditLight.m_Parent = nullptr; + } + } else if (objectType == GraphObjectTypes::Light) { + m_editModeCamerasAndLights.push_back(&inParent); + } else if (objectType == GraphObjectTypes::Camera) { + m_editModeCamerasAndLights.push_back(&inParent); + } + } + + // Alias handles propagate down the scene graph. + if (inParent.GetInstanceHandle() != inParent.GetSceneGraphInstanceHandle()) + inAliasHandle = inParent.GetInstanceHandle(); + for (long idx = 0, end = m_AssetGraph.GetChildCount(inParent.GetSceneGraphInstanceHandle()); + idx < end; ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theChild( + m_AssetGraph.GetChild(inParent.GetSceneGraphInstanceHandle(), idx)); + SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theChild, inAliasHandle); + if (theTranslator == nullptr) + continue; + + // We we have edit cameras active, we only render the active layer and we remove any cameras + // in the active layer. Furthermore if our edit light is active, then we also remove any + // active lights in the layer. + if (m_EditCameraEnabled && !scenePreviewPass) { + if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Layer) { + if (theChild == m_Doc.GetActiveLayer()) { + if (m_EditCameraLayerTranslator != nullptr + && m_EditCameraLayerTranslator->GetInstanceHandle() != theChild) { + QT3DS_FREE(m_Allocator, m_EditCameraLayerTranslator); + m_EditCameraLayerTranslator = nullptr; + } + if (!m_EditCameraLayerTranslator) { + m_EditCameraLayerTranslator = + QT3DS_NEW(m_Allocator, SEditorLayerTranslator)(theChild, + m_Allocator); + } + theTranslator = m_EditCameraLayerTranslator; + theParentTranslator.AppendChild(theTranslator->GetGraphObject()); + BuildRenderGraph(*m_EditCameraLayerTranslator, scenePreviewPass); + } + } else { + theParentTranslator.AppendChild(theTranslator->GetGraphObject()); + BuildRenderGraph(theChild, scenePreviewPass, inAliasHandle); + + if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Effect) + theTranslator->SetActive(false); + else if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Camera) + theTranslator->SetActive(false); + else if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Light + && m_EditLightEnabled == true) + theTranslator->SetActive(false); + else + theTranslator->SetActive(m_Reader.IsCurrentlyActive(theChild)); + } + } else // Else build the graph and it will be an exact copy of the asset graph. + { + theParentTranslator.AppendChild(theTranslator->GetGraphObject()); + if (m_Reader.IsCurrentlyActive(theChild)) { + BuildRenderGraph(theChild, scenePreviewPass, inAliasHandle); + theTranslator->SetActive(true); + } else { + theTranslator->SetActive(false); + DeactivateScan(*theTranslator, inAliasHandle); + } + } + } + if (GraphObjectTypes::Layer == theParentTranslator.GetGraphObject().m_Type) + m_Context.GetRenderer().ChildrenUpdated( + static_cast<SLayer &>(theParentTranslator.GetGraphObject())); + + // Allow certain nodes to override their children. + theParentTranslator.AfterRenderGraphIsBuilt(*this); +} + +void STranslation::DeactivateScan(SGraphObjectTranslator &inParent, + Qt3DSDMInstanceHandle inAliasHandle) +{ + // Alias handles propagate down the scene graph. + if (inParent.GetInstanceHandle() != inParent.GetSceneGraphInstanceHandle()) + inAliasHandle = inParent.GetInstanceHandle(); + for (long idx = 0, end = m_AssetGraph.GetChildCount(inParent.GetSceneGraphInstanceHandle()); + idx < end; ++idx) { + qt3dsdm::Qt3DSDMInstanceHandle theChild( + m_AssetGraph.GetChild(inParent.GetSceneGraphInstanceHandle(), idx)); + SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theChild, inAliasHandle); + if (theTranslator == nullptr) + continue; + theTranslator->SetActive(false); + DeactivateScan(*theTranslator, inAliasHandle); + } +} + +// We build the render graph every time we render. This may seem wasteful +void STranslation::BuildRenderGraph(qt3dsdm::Qt3DSDMInstanceHandle inParent, bool scenePreviewPass, + Qt3DSDMInstanceHandle inAliasHandle) +{ + SGraphObjectTranslator *theParentTranslator = GetOrCreateTranslator(inParent, inAliasHandle); + if (theParentTranslator == nullptr) + return; + if (m_Reader.IsCurrentlyActive(inParent) == false) { + theParentTranslator->SetActive(false); + return; + } + BuildRenderGraph(*theParentTranslator, scenePreviewPass, inAliasHandle); +} + +void STranslation::ReleaseTranslation(Q3DStudio::TIdentifier inInstance) +{ + m_TranslatorMap.erase(inInstance); +} + +void STranslation::MarkDirty(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + // Anchor points are not handled individually. + if (m_Reader.GetObjectTypeName(inInstance) == L"PathAnchorPoint") + inInstance = m_AssetGraph.GetParent(inInstance); + GetOrCreateTranslator(inInstance); + + THandleTranslatorPairList &theTranslators = GetTranslatorsForInstance(inInstance); + for (size_t idx = 0, end = theTranslators.size(); idx < end; ++idx) { + m_DirtySet.insert(*theTranslators[(eastl::allocator::size_type)idx].second); + // Reset effect when effect parameters change, as with certain corner cases + // some effects would accumulate ~infinitely and result would not reflect + // actual parameter setting unless reset (f.ex corona, other blur types) + m_DirtySet.back()->ResetEffect(); + } + RequestRender(); +} + +QT3DSVec2 STranslation::GetPreviewViewportDimensions() const +{ + CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings(); + QSize thePresSize = theSettings->getPresentationSize(); + return QT3DSVec2(thePresSize.width(), thePresSize.height()); +} + +void STranslation::PreRender(bool scenePreviewPass) +{ + // Run through the entire asset graph and mark active or inactive if we have an + // associated render representation. + // If we cache all the components and some of their state then we don't have to do this + // but for now it is more stable to run through the graph. + // There is always one root, the scene. + TIdentifier theRoot = m_AssetGraph.GetRoot(0); + if (!scenePreviewPass) + m_editModeCamerasAndLights.clear(); + ClearDirtySet(); + m_EditLightEnabled = CStudioPreferences::isEditModeLightingEnabled(); + BuildRenderGraph(theRoot, scenePreviewPass); + QT3DSVec2 theViewportDims(GetViewportDimensions()); + if (scenePreviewPass) { + m_Context.SetScaleMode(qt3ds::render::ScaleModes::FitSelected); + theViewportDims = GetPreviewViewportDimensions(); + } else { + m_Context.SetScaleMode(qt3ds::render::ScaleModes::ExactSize); + } + + static const QT3DSVec4 matteColor(CStudioPreferences::matteColor().redF(), + CStudioPreferences::matteColor().greenF(), + CStudioPreferences::matteColor().blueF(), 1.0f); + m_Context.SetMatteColor(matteColor); + m_Context.setMatteEnabled(true); + // Ensure the camera points where it should + if (m_EditCameraEnabled && !scenePreviewPass) { + m_EditCameraInfo.ApplyToCamera(m_EditCamera, theViewportDims); + m_EditLight.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty); + updateHelperGridFromSettings(); + } + if (m_axisHelperEnabled && !scenePreviewPass) { + if (m_EditCameraEnabled) { + m_EditCameraInfo.ApplyToCamera(m_AxisCamera, theViewportDims, true); + } else { + // Get scene camera + SLayerTranslator *layerTranslator = static_cast<SLayerTranslator *>(GetOrCreateTranslator(m_Doc.GetActiveLayer())); + SNode *layerNode = static_cast<SNode *>(layerTranslator->m_GraphObject); + for (SNode *child = layerNode->m_FirstChild; child; child = child->m_NextSibling) { + if (child->m_Type == GraphObjectTypes::Enum::Camera) { + m_AxisCamera = *(SCamera *)child; + m_AxisCamera.m_Parent = (SNode *)&m_AxisHelperLayerTranslator->GetGraphObject(); + m_AxisCamera.m_NextSibling = nullptr; + m_AxisCamera.m_PreviousSibling = nullptr; + m_AxisCamera.m_FirstChild = nullptr; + m_AxisCamera.m_PreviousSibling = nullptr; + break; + } + } + } + } + + if (m_Scene) { + CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings(); + QSize thePresSize = theSettings->getPresentationSize(); + // The presentation sizes are used for when we have to render a layer offscreen. + // If their width and height isn't set, then they use the presentation dimensions. + m_Presentation.m_PresentationDimensions + = QT3DSVec2((QT3DSF32)thePresSize.width(), (QT3DSF32)thePresSize.height()); + m_Context.SetWindowDimensions( + QSize((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y)); + m_Context.SetPresentationDimensions( + QSize((QT3DSU32)m_Presentation.m_PresentationDimensions.x, + (QT3DSU32)m_Presentation.m_PresentationDimensions.y)); + + // set if we draw geometry in wireframe mode + m_Context.SetWireframeMode(CStudioPreferences::isWireframeModeOn()); + + if (m_EditCameraEnabled && !scenePreviewPass) { + m_Presentation.m_PresentationDimensions = theViewportDims; + m_Context.SetPresentationDimensions( + QSize((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y)); + m_Context.SetSceneColor(QT3DSVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } else { + + TIdentifier theRoot = m_AssetGraph.GetRoot(0); + SGraphObjectTranslator *theSceneTranslator = GetOrCreateTranslator(theRoot); + if (theSceneTranslator) { + SScene &theScene = static_cast<SScene &>(theSceneTranslator->GetGraphObject()); + if (scenePreviewPass) { + if (theScene.m_UseClearColor) + m_Context.SetMatteColor(theScene.m_ClearColor); + else + m_Context.SetMatteColor(QT3DSVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } else { + if (theScene.m_UseClearColor) + m_Context.SetSceneColor(theScene.m_ClearColor); + else + m_Context.SetSceneColor(QT3DSVec4(QT3DSVec3(0.0f), 1.0f)); + } + } + } + m_Presentation.m_preferKTX = theSettings->getPreferCompressedTextures(); + m_Presentation.m_flipCompressedTextures = theSettings->getFlipCompressedTextures(); + } + if (m_EditCameraEnabled == false && g_StudioApp.IsAuthorZoom()) { + if (m_Presentation.m_PresentationDimensions.x > theViewportDims.x + || m_Presentation.m_PresentationDimensions.y > theViewportDims.y) { + m_Context.SetScaleMode(qt3ds::render::ScaleModes::FitSelected); + } + } +} + +static void CreatePixelRect(STranslation &inTranslation, QT3DSF32 left, QT3DSF32 right, QT3DSF32 bottom, + QT3DSF32 top, QT3DSVec4 color) +{ + SPGRect *theRect = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGRect)(); + theRect->m_Left = left; + theRect->m_Right = right; + theRect->m_Top = top; + theRect->m_Bottom = bottom; + theRect->m_FillColor = color; + inTranslation.m_GuideContainer.push_back(theRect); +} + +static void CreatePixelVertLine(STranslation &inTranslation, QT3DSF32 inXPos, QT3DSF32 inBottom, + QT3DSF32 inTop, QT3DSVec4 color) +{ + SPGVertLine *theLine = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGVertLine)(); + theLine->m_X = inXPos; + theLine->m_Bottom = inBottom; + theLine->m_Top = inTop; + theLine->m_LineColor = color; + inTranslation.m_GuideContainer.push_back(theLine); +} + +static void CreatePixelHorzLine(STranslation &inTranslation, QT3DSF32 inYPos, QT3DSF32 inLeft, + QT3DSF32 inRight, QT3DSVec4 color) +{ + SPGHorzLine *theLine = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGHorzLine)(); + theLine->m_Y = inYPos; + theLine->m_Left = inLeft; + theLine->m_Right = inRight; + theLine->m_LineColor = color; + inTranslation.m_GuideContainer.push_back(theLine); +} + +static void CreateTopBottomTickMarks(STranslation &inTranslation, QT3DSF32 posX, QT3DSF32 innerBottom, + QT3DSF32 innerTop, QT3DSF32 outerBottom, QT3DSF32 outerTop, + QT3DSF32 lineHeight, QT3DSVec4 lineColor) +{ + CreatePixelVertLine(inTranslation, posX, innerBottom - lineHeight, innerBottom, lineColor); + CreatePixelVertLine(inTranslation, posX, innerTop, innerTop + lineHeight, lineColor); +} + +static void DrawTickMarksOnHorizontalRects(STranslation &inTranslation, QT3DSF32 innerLeft, + QT3DSF32 innerRight, QT3DSF32 innerBottom, QT3DSF32 innerTop, + QT3DSF32 outerBottom, QT3DSF32 outerTop, QT3DSVec4 lineColor) +{ + QT3DSF32 centerPosX = qFloor(innerLeft + (innerRight - innerLeft) / 2.0f + .5f); + CreateTopBottomTickMarks(inTranslation, centerPosX, innerBottom, innerTop, outerBottom, + outerTop, 15, lineColor); + for (QT3DSU32 incrementor = 10; + (centerPosX + incrementor) < innerRight && (centerPosX - incrementor) > innerLeft; + incrementor += 10) { + QT3DSF32 rightEdge = centerPosX + incrementor; + QT3DSF32 leftEdge = centerPosX - incrementor; + QT3DSF32 lineHeight = 0; + if (incrementor % 100 == 0) + lineHeight = 11; + else if (incrementor % 20) + lineHeight = 4; + else + lineHeight = 2; + + if (rightEdge < innerRight) + CreateTopBottomTickMarks(inTranslation, rightEdge, innerBottom, innerTop, outerBottom, + outerTop, lineHeight, lineColor); + if (leftEdge > innerLeft) + CreateTopBottomTickMarks(inTranslation, leftEdge, innerBottom, innerTop, outerBottom, + outerTop, lineHeight, lineColor); + } +} + +static void CreateLeftRightTickMarks(STranslation &inTranslation, QT3DSF32 inYPos, QT3DSF32 innerLeft, + QT3DSF32 innerRight, QT3DSF32 outerLeft, QT3DSF32 outerRight, + QT3DSF32 lineLength, QT3DSVec4 lineColor) +{ + CreatePixelHorzLine(inTranslation, inYPos, innerLeft - lineLength, innerLeft, lineColor); + CreatePixelHorzLine(inTranslation, inYPos, innerRight, innerRight + lineLength, lineColor); +} + +static void DrawTickMarksOnVerticalRects(STranslation &inTranslation, QT3DSF32 innerLeft, + QT3DSF32 innerRight, QT3DSF32 innerBottom, QT3DSF32 innerTop, + QT3DSF32 outerLeft, QT3DSF32 outerRight, QT3DSVec4 lineColor) +{ + QT3DSF32 centerPosY = qFloor(innerBottom + (innerTop - innerBottom) / 2.0f + .5f); + CreateLeftRightTickMarks(inTranslation, centerPosY, innerLeft, innerRight, outerLeft, + outerRight, 15, lineColor); + for (QT3DSU32 incrementor = 10; + (centerPosY + incrementor) < innerTop && (centerPosY - incrementor) > innerBottom; + incrementor += 10) { + QT3DSF32 topEdge = centerPosY + incrementor; + QT3DSF32 bottomEdge = centerPosY - incrementor; + QT3DSF32 lineHeight = 0; + if (incrementor % 100 == 0) + lineHeight = 11; + else if (incrementor % 20) + lineHeight = 4; + else + lineHeight = 2; + + if (topEdge < innerTop) + CreateLeftRightTickMarks(inTranslation, topEdge, innerLeft, innerRight, outerLeft, + outerRight, lineHeight, lineColor); + if (bottomEdge > innerBottom) + CreateLeftRightTickMarks(inTranslation, bottomEdge, innerLeft, innerRight, outerLeft, + outerRight, lineHeight, lineColor); + } +} + +class IGuideElementFactory +{ +protected: + virtual ~IGuideElementFactory() {} +public: + virtual void CreateLine(QT3DSF32 inPos) = 0; + virtual void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) = 0; +}; + +static void CreateGuide(IGuideElementFactory &inFactory, QT3DSF32 inPos, QT3DSF32 inWidth) +{ + QT3DSF32 halfWidth = inWidth / 2.0f; + QT3DSF32 leftLine = qFloor(inPos + 1.0f - halfWidth); + inFactory.CreateLine(leftLine); + // Then we are done if not enough width + if (inWidth < 2.0f) + return; + + QT3DSF32 rightLine = leftLine + inWidth - 1; + inFactory.CreateLine(rightLine); + + if (inWidth < 3.0f) + return; + QT3DSF32 rectStart = leftLine + 1; + QT3DSF32 rectStop = rectStart + inWidth - 2.0f; + inFactory.CreateRect(rectStart, rectStop); +} + +struct SHorizontalGuideFactory : public IGuideElementFactory +{ + STranslation &m_Translation; + QT3DSF32 m_Start; + QT3DSF32 m_Stop; + QT3DSVec4 m_LineColor; + QT3DSVec4 m_FillColor; + SHorizontalGuideFactory(STranslation &trans, QT3DSF32 start, QT3DSF32 stop, QT3DSVec4 lineColor, + QT3DSVec4 fillColor) + : m_Translation(trans) + , m_Start(start) + , m_Stop(stop) + , m_LineColor(lineColor) + , m_FillColor(fillColor) + { + } + void CreateLine(QT3DSF32 inPos) override + { + CreatePixelHorzLine(m_Translation, inPos, m_Start, m_Stop, m_LineColor); + } + + void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) override + { + CreatePixelRect(m_Translation, m_Start, m_Stop, inPosMin, inPosMax, m_FillColor); + } +}; + +struct SVerticalGuideFactory : public IGuideElementFactory +{ + STranslation &m_Translation; + QT3DSF32 m_Start; + QT3DSF32 m_Stop; + QT3DSVec4 m_LineColor; + QT3DSVec4 m_FillColor; + SVerticalGuideFactory(STranslation &trans, QT3DSF32 start, QT3DSF32 stop, QT3DSVec4 lineColor, + QT3DSVec4 fillColor) + : m_Translation(trans) + , m_Start(start) + , m_Stop(stop) + , m_LineColor(lineColor) + , m_FillColor(fillColor) + { + } + void CreateLine(QT3DSF32 inPos) override + { + CreatePixelVertLine(m_Translation, inPos, m_Start, m_Stop, m_LineColor); + } + + void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) override + { + CreatePixelRect(m_Translation, inPosMin, inPosMax, m_Start, m_Stop, m_FillColor); + } +}; + +qt3ds::render::NVRenderRect STranslation::GetPreviewViewport() const +{ + QT3DSVec2 vp = GetPreviewViewportDimensions(); + return qt3ds::render::NVRenderRect(0, 0, vp.x, vp.y); +} + +void STranslation::Render(int inWidgetId, bool inDrawGuides, bool scenePreviewPass) +{ + // For now, we just render. + // Next step will be to get the bounding boxes and such setup. + // but we will want a custom renderer to do that. + if (m_Scene) { + // Note that begin frame is called before we allocate the bounding box and axis widgets so + // that we can take advantage of the renderer's per-frame-allocator. + m_Context.BeginFrame(true); + + qt3dsdm::TInstanceHandleList theHandles = m_Doc.GetSelectedValue().GetSelectedInstances(); + + if (scenePreviewPass) { + qt3ds::render::NVRenderContext &renderContext(m_Context.GetRenderContext()); + QT3DSVec2 previewDims(GetPreviewViewportDimensions()); + if (m_previewFboDimensions != previewDims) { + m_previewFboDimensions = previewDims; + if (m_previewFbo) + m_Context.GetResourceManager().Release(*m_previewFbo); + if (m_previewRenderBuffer) + m_Context.GetResourceManager().Release(*m_previewRenderBuffer); + if (m_previewTexture) + m_Context.GetResourceManager().Release(*m_previewTexture); + m_previewFbo = nullptr; + m_previewRenderBuffer = nullptr; + m_previewTexture = nullptr; + } + if (!m_previewFbo) + m_previewFbo = m_Context.GetResourceManager().AllocateFrameBuffer(); + if (!m_previewTexture) { + m_previewTexture = renderContext.CreateTexture2D(); + m_previewTexture->SetTextureData(qt3ds::foundation::NVDataRef<qt3ds::QT3DSU8>(), + 0, previewDims.x, previewDims.y, + qt3ds::render::NVRenderTextureFormats::RGBA8); + m_previewFbo->Attach( + qt3ds::render::NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer(*m_previewTexture)); + } + if (!m_previewRenderBuffer) { + m_previewRenderBuffer = m_Context.GetResourceManager().AllocateRenderBuffer( + previewDims.x, previewDims.y, + qt3ds::render::NVRenderRenderBufferFormats::Depth24); + m_previewFbo->Attach( + qt3ds::render::NVRenderFrameBufferAttachments::Depth, + qt3ds::render::NVRenderTextureOrRenderBuffer(*m_previewRenderBuffer)); + } + renderContext.SetRenderTarget(m_previewFbo); + } else { + // Render the bounding boxes and extra widgets. + // This is called *before* the render because these sort of appendages need to be added + // to the layer renderables. + + // The helper grid is drawn when in edit camera mode + if (m_EditCameraEnabled && m_helperGridEnabled) { + // Helper grid is a child of the currently selected layer + if (!m_helperGridWidget) { + m_helperGridWidget = qt3ds::widgets::SHelperGridWidget + ::createHelperGridWidget(m_Context.GetAllocator()); + updateHelperGridFromSettings(); + } + SNode *helperGridParent = GetEditCameraLayer(); + if (helperGridParent) { + m_helperGridWidget->setNode(helperGridParent); + m_Context.GetRenderer().AddRenderWidget(*m_helperGridWidget); + } + } + + // Don't show the bounding box or pivot for the component we are *in* the component + SGraphObjectTranslator *theTranslator = nullptr; + long theToolMode = g_StudioApp.GetToolMode(); + int theCameraToolMode = m_EditCameraEnabled + ? (theToolMode & STUDIO_CAMERATOOL_MASK) : 0; + bool shouldDisplayWidget = false; + if (theCameraToolMode == 0) { + switch (theToolMode) { + default: + break; + case STUDIO_TOOLMODE_MOVE: + case STUDIO_TOOLMODE_ROTATE: + case STUDIO_TOOLMODE_SCALE: + shouldDisplayWidget = true; + break; + }; + } + + bool selectedPath = false; + + for (size_t selectedIdx = 0, selectedEnd = theHandles.size(); selectedIdx < selectedEnd; + ++selectedIdx) { + qt3dsdm::Qt3DSDMInstanceHandle theInstance = theHandles[selectedIdx]; + if (theInstance + != m_Doc.GetDocumentReader().GetComponentForSlide(m_Doc.GetActiveSlide())) { + if (m_Doc.GetDocumentReader().GetObjectTypeName(theInstance) + == L"PathAnchorPoint") { + theInstance = m_AssetGraph.GetParent(m_AssetGraph.GetParent(theInstance)); + shouldDisplayWidget = false; + } + theTranslator = GetOrCreateTranslator(theInstance); + // Get the tool mode right now. + if (theTranslator) { + GraphObjectTypes::Enum theType(theTranslator->GetGraphObject().m_Type); + if (CStudioPreferences::isBoundingBoxesOn()) { + switch (theType) { + case GraphObjectTypes::Node: + DrawGroupBoundingBoxes(*theTranslator); + break; + case GraphObjectTypes::Text: + case GraphObjectTypes::Model: + case GraphObjectTypes::Layer: + case GraphObjectTypes::Light: + case GraphObjectTypes::Path: + DrawNonGroupBoundingBoxes(*theTranslator); + break; + default: + break; + } + } + + // Don't draw the axis if there is a widget. + if (CStudioPreferences::isPivotPointOn()) { + switch (theTranslator->GetGraphObject().m_Type) { + case GraphObjectTypes::Node: + case GraphObjectTypes::Text: + case GraphObjectTypes::Model: + drawPivot(*theTranslator); + break; + default: + break; + } + } + + if (theType == GraphObjectTypes::Path && selectedPath == false) { + selectedPath = true; + if (!m_PathWidget) { + m_PathWidget = qt3ds::widgets::IPathWidget::CreatePathWidget( + m_Context.GetAllocator(), m_Context); + } + m_PathWidget->SetNode( + static_cast<SNode &>(theTranslator->GetGraphObject())); + m_Context.GetRenderer().AddRenderWidget(*m_PathWidget); + } + } + } + } + + if (theHandles.size() > 1) + theTranslator = nullptr; + + qt3ds::widgets::IStudioWidget *theNextWidget(nullptr); + if (theTranslator + && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) + && theTranslator->GetGraphObject().m_Type != GraphObjectTypes::Layer) { + + qt3ds::render::SNode &theNode( + static_cast<qt3ds::render::SNode &>(theTranslator->GetGraphObject())); + const GraphObjectTypes::Enum type = theTranslator->GetGraphObject().m_Type; + + // Don't draw widgets for non-visible nodes + bool isActive = theNode.m_Flags.IsActive(); + // Light and camera nodes are never active, so check from doc + if (type == GraphObjectTypes::Camera || type == GraphObjectTypes::Light) + isActive = m_Reader.IsCurrentlyActive(theHandles[0]); + shouldDisplayWidget = shouldDisplayWidget && isActive; + + SCamera *theRenderCamera = m_Context.GetRenderer().GetCameraForNode(theNode); + bool isActiveCamera = theRenderCamera == (static_cast<SCamera *>(&theNode)); + if (shouldDisplayWidget && !isActiveCamera + && ((type == GraphObjectTypes::Camera && m_EditCameraEnabled) + || type != GraphObjectTypes::Camera)) { + switch (theToolMode) { + default: + QT3DS_ASSERT(false); + break; + case STUDIO_TOOLMODE_MOVE: + // Render translation widget + if (!m_TranslationWidget) { + m_TranslationWidget + = qt3ds::widgets::IStudioWidget::CreateTranslationWidget( + m_Context.GetAllocator()); + } + theNextWidget = m_TranslationWidget.mPtr; + break; + case STUDIO_TOOLMODE_ROTATE: + if (!m_RotationWidget) { + m_RotationWidget = qt3ds::widgets::IStudioWidget::CreateRotationWidget( + m_Context.GetAllocator()); + } + theNextWidget = m_RotationWidget.mPtr; + break; + + case STUDIO_TOOLMODE_SCALE: + if (!m_ScaleWidget) { + m_ScaleWidget = qt3ds::widgets::IStudioWidget::CreateScaleWidget( + m_Context.GetAllocator()); + } + theNextWidget = m_ScaleWidget.mPtr; + break; + } + + if (theNextWidget) { + SNode &node = static_cast<SNode &>(theTranslator->GetGraphObject()); + theNextWidget->SetNode(node); + m_Context.GetRenderer().AddRenderWidget(*theNextWidget); + } + } + } + if (m_LastRenderedWidget.mPtr && m_LastRenderedWidget.mPtr != theNextWidget) + ResetWidgets(); + + m_LastRenderedWidget = theNextWidget; + if (m_LastRenderedWidget) { + m_LastRenderedWidget->SetSubComponentId(inWidgetId); + switch (g_StudioApp.GetManipulationMode()) { + case StudioManipulationModes::Local: + m_LastRenderedWidget->SetRenderWidgetMode( + qt3ds::render::RenderWidgetModes::Local); + break; + case StudioManipulationModes::Global: + m_LastRenderedWidget->SetRenderWidgetMode( + qt3ds::render::RenderWidgetModes::Global); + break; + default: + QT3DS_ASSERT(false); + break; + } + } + } + + if (!m_AxisHelperWidget ) { + m_AxisHelperWidget + = qt3ds::widgets::IStudioWidget::CreateTranslationWidget( + m_Context.GetAllocator()); + m_AxisHelperWidget->setAsAxisHelper(true); + } + + auto layer = GetAxisHelperLayer(); + if (layer != nullptr) { + updateAxisHelperFromSettings(); + m_AxisHelperWidget->SetNode(*layer); + if (m_axisHelperEnabled) + m_Context.GetRenderer().AddRenderWidget(*m_AxisHelperWidget); + } + QVector<bool> layerDepthPrepasses = {}; + if (scenePreviewPass) { + m_Context.GetRenderContext().SetViewport(GetPreviewViewport()); + m_Context.SetSceneColor(Option<QT3DSVec4>()); + } else { + SLayer *child = m_Scene->m_FirstChild; + do { + layerDepthPrepasses.append(child->m_Flags.IsLayerEnableDepthPrepass()); + child->m_Flags.SetLayerEnableDepthPrepass(true); + child = (SLayer *)child->m_NextSibling; + } while (child); + } + + m_Scene->PrepareForRender(m_Context); + + m_Context.RunRenderTasks(); + + if (!scenePreviewPass && m_EditCameraEnabled) { + if (m_GradientWidget == nullptr) + m_GradientWidget = qt3ds::widgets::SGradientWidget + ::CreateGradientWidget(m_Context.GetAllocator()); + // render gradient background + SNode *node = GetEditCameraLayer(); + m_GradientWidget->SetNode(*node); + m_GradientWidget->Render(m_Context.GetRenderWidgetContext(), + m_Context.GetRenderContext(), + m_EditCameraInfo.IsOrthographic()); + } + + m_Scene->Render(scenePreviewPass + ? GetPreviewViewportDimensions() + : GetViewportDimensions(), m_Context, SScene::DoNotClear); + + if (!scenePreviewPass && m_editModeCamerasAndLights.size() > 0) { + if (!m_VisualAidWidget) { + m_VisualAidWidget = qt3ds::widgets::SVisualAidWidget + ::CreateVisualAidWidget(m_Context.GetAllocator()); + } + for (SGraphObjectTranslator *translator : m_editModeCamerasAndLights) { + SGraphObject &object = translator->GetGraphObject(); + qt3dsdm::Qt3DSDMInstanceHandle handle = translator->GetInstanceHandle(); + m_VisualAidWidget->setSelected(false); + + for (int j = 0; j < theHandles.size(); ++j) { + if (handle == theHandles[j]) { + m_VisualAidWidget->setSelected(true); + break; + } + } + + if (object.m_Type == GraphObjectTypes::Camera) { + if (m_InnerRect.isNull()) { + // this happens when the initial view is not the camera view + NVRenderRect thePresentationViewport = m_Context.GetPresentationViewport(); + m_InnerRect.m_Left = thePresentationViewport.m_X; + m_InnerRect.m_Right = thePresentationViewport.m_X + + thePresentationViewport.m_Width; + m_InnerRect.m_Bottom = thePresentationViewport.m_Y; + m_InnerRect.m_Top = thePresentationViewport.m_Y + + thePresentationViewport.m_Height; + } + + QT3DSVec2 dim = QT3DSVec2(m_InnerRect.m_Right - m_InnerRect.m_Left, + m_InnerRect.m_Top - m_InnerRect.m_Bottom); + NVRenderRectF theViewport(0, 0, dim.x, dim.y); + static_cast<SCamera *>(&object)->CalculateGlobalVariables(theViewport, dim); + } + m_VisualAidWidget->SetNode(static_cast<SNode *>(&object)); + m_VisualAidWidget->Render(m_Context.GetRenderWidgetContext(), + m_Context.GetRenderContext()); + } + } + + if (inDrawGuides && !m_EditCameraEnabled && !g_StudioApp.IsAuthorZoom()) { + m_GuideContainer.clear(); + // Figure out the matte area. + NVRenderRect theContextViewport = m_Context.GetContextViewport(); + NVRenderRect thePresentationViewport = m_Context.GetPresentationViewport(); + m_Context.GetRenderContext().SetViewport(theContextViewport); + QT3DSI32 innerLeft = thePresentationViewport.m_X; + QT3DSI32 innerRight = thePresentationViewport.m_X + thePresentationViewport.m_Width; + QT3DSI32 innerBottom = thePresentationViewport.m_Y; + QT3DSI32 innerTop = thePresentationViewport.m_Y + thePresentationViewport.m_Height; + + QT3DSI32 outerLeft = innerLeft - 16; + QT3DSI32 outerRight = innerRight + 16; + QT3DSI32 outerBottom = innerBottom - 16; + QT3DSI32 outerTop = innerTop + 16; + // Retain the rects for picking purposes. + m_InnerRect = SRulerRect(innerLeft, innerTop, innerRight, innerBottom); + m_OuterRect = SRulerRect(outerLeft, outerTop, outerRight, outerBottom); + + // Draw tick marks around the presentation + CreatePixelRect(*this, (QT3DSF32)outerLeft, (QT3DSF32)innerLeft, (QT3DSF32)innerBottom, + (QT3DSF32)innerTop, m_rectColor); + CreatePixelRect(*this, (QT3DSF32)innerRight, (QT3DSF32)outerRight, + (QT3DSF32)innerBottom, (QT3DSF32)innerTop, m_rectColor); + CreatePixelRect(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)outerBottom, + (QT3DSF32)innerBottom, m_rectColor); + CreatePixelRect(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)innerTop, + (QT3DSF32)outerTop, m_rectColor); + DrawTickMarksOnHorizontalRects(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, + (QT3DSF32)innerBottom, (QT3DSF32)innerTop, + (QT3DSF32)outerBottom, (QT3DSF32)outerTop, m_lineColor); + DrawTickMarksOnVerticalRects(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, + (QT3DSF32)innerBottom, (QT3DSF32)innerTop, + (QT3DSF32)outerLeft, (QT3DSF32)outerRight, m_lineColor); + qt3dsdm::TGuideHandleList theGuides = m_Doc.GetDocumentReader().GetGuides(); + qt3dsdm::Qt3DSDMGuideHandle theSelectedGuide; + Q3DStudio::SSelectedValue theSelection = m_Doc.GetSelectedValue(); + if (theSelection.getType() == Q3DStudio::SelectedValueTypes::Guide) + theSelectedGuide = theSelection.getData<qt3dsdm::Qt3DSDMGuideHandle>(); + + // Draw guides + for (size_t guideIdx = 0, guideEnd = theGuides.size(); guideIdx < guideEnd; + ++guideIdx) { + qt3dsdm::SGuideInfo theInfo = + m_Doc.GetDocumentReader().GetGuideInfo(theGuides[guideIdx]); + bool isGuideSelected = theGuides[guideIdx] == theSelectedGuide; + QT3DSVec4 theColor = isGuideSelected ? m_selectedGuideColor : m_guideColor; + QT3DSVec4 theFillColor = isGuideSelected ? m_selectedGuideFillColor + : m_guideFillColor; + switch (theInfo.m_Direction) { + case qt3dsdm::GuideDirections::Horizontal: { + SHorizontalGuideFactory theFactory(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, + theColor, theFillColor); + CreateGuide(theFactory, (QT3DSF32)m_InnerRect.m_Bottom + theInfo.m_Position, + (QT3DSF32)theInfo.m_Width); + } break; + case qt3dsdm::GuideDirections::Vertical: { + SVerticalGuideFactory theFactory(*this, (QT3DSF32)innerBottom, (QT3DSF32)innerTop, + theColor, theFillColor); + CreateGuide(theFactory, (QT3DSF32)m_InnerRect.m_Left + theInfo.m_Position, + (QT3DSF32)theInfo.m_Width); + } break; + default: + QT3DS_ASSERT(false); + break; + } + } + m_Context.GetPixelGraphicsRenderer().Render( + qt3ds::foundation::toDataRef(m_GuideContainer.data(), m_GuideContainer.size())); + + m_GuideContainer.clear(); + m_GuideAllocator.reset(); + } + + if (!scenePreviewPass && m_previewTexture) { + // Hack: For some reason, the m_previewTexture is only valid later if it is + // actually drawn somewhere during the main render pass, so draw a dummy quad + m_Context.GetRenderContext().SetViewport(NVRenderRect(0, 0, 0, 0)); + m_Context.GetRenderer().RenderQuad(QT3DSVec2(0.0f), QT3DSMat44(), + *m_previewTexture); + } + + if (scenePreviewPass) + m_Context.GetRenderContext().SetRenderTarget(nullptr); + + m_Context.EndFrame(); + + if (m_ZoomRender.hasValue()) { + RenderZoomRender(*m_ZoomRender); + m_ZoomRender = Empty(); + } + + // Render the pick buffer, useful for debugging why a widget wasn't hit. + /* + if ( m_PickBuffer ) + { + qt3ds::render::NVRenderContext& theRenderContext( m_Context.GetRenderContext() ); + qt3ds::render::STextureDetails thePickDetails = m_PickBuffer->GetTextureDetails(); + theRenderContext.SetViewport( qt3ds::render::NVRenderRect( 0, 0, thePickDetails.m_Width, + thePickDetails.m_Height ) ); + qt3ds::render::SCamera theCamera; + theCamera.MarkDirty( qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty ); + theCamera.m_Flags.SetOrthographic( true ); + QT3DSVec2 theDimensions( (QT3DSF32)thePickDetails.m_Width, (QT3DSF32)thePickDetails.m_Height ); + theCamera.CalculateGlobalVariables( render::NVRenderRectF( 0, 0, theDimensions.x, + theDimensions.y ), theDimensions ); + QT3DSMat44 theVP; + theCamera.CalculateViewProjectionMatrix( theVP ); + theRenderContext.SetCullingEnabled( false ); + theRenderContext.SetBlendingEnabled( false ); + theRenderContext.SetDepthTestEnabled( false ); + theRenderContext.SetDepthWriteEnabled( false ); + m_Context.GetRenderer().RenderQuad( theDimensions, theVP, *m_PickBuffer ); + }*/ + if (!scenePreviewPass) { + SLayer *child = m_Scene->m_FirstChild; + int childIndex = 0; + do { + child->m_Flags.SetLayerEnableDepthPrepass(layerDepthPrepasses.at(childIndex)); + child = (SLayer *)child->m_NextSibling; + ++childIndex; + } while (child); + } + } +} + +void STranslation::ResetWidgets() +{ + if (m_ScaleWidget) + m_ScaleWidget->SetAxisScale(QT3DSVec3(1, 1, 1)); + if (m_RotationWidget) + m_RotationWidget->ClearRotationEdges(); + m_CumulativeRotation = 0.0f; +} + +void STranslation::DoPrepareForDrag(SNode *inSelectedNode) +{ + if (inSelectedNode == nullptr) + return; + + m_MouseDownNode = *inSelectedNode; + m_MouseDownParentGlobalTransformInverse = Empty(); + m_MouseDownParentRotationInverse = Empty(); + m_MouseDownGlobalRotation = Empty(); + // Orphan this node manually since it is a straight copy + m_MouseDownNode.m_Parent = nullptr; + m_MouseDownNode.m_FirstChild = nullptr; + m_MouseDownNode.m_NextSibling = nullptr; + SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*inSelectedNode); + m_CumulativeRotation = 0.0f; + if (theCamera == nullptr) + return; + m_MouseDownCamera = *theCamera; + m_LastPathDragValue = Empty(); +} + +void STranslation::EndDrag() +{ + ResetWidgets(); +} + +bool STranslation::IsPathWidgetActive() +{ + qt3dsdm::TInstanceHandleList theHandles = m_Doc.GetSelectedValue().GetSelectedInstances(); + for (size_t selectedIdx = 0, selectedEnd = theHandles.size(); selectedIdx < selectedEnd; + ++selectedIdx) { + qt3dsdm::Qt3DSDMInstanceHandle theInstance(theHandles[selectedIdx]); + if (m_Doc.GetDocumentReader().GetObjectTypeName(theInstance) == L"PathAnchorPoint") + theInstance = m_AssetGraph.GetParent(m_AssetGraph.GetParent(theInstance)); + SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theInstance); + if (theTranslator && theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Path) + return true; + } + return false; +} + +inline qt3ds::render::SLayer *GetLayerForNode(const qt3ds::render::SNode &inNode) +{ + SNode *theNode; + // empty loop intentional + for (theNode = const_cast<SNode *>(&inNode); + theNode && theNode->m_Type != GraphObjectTypes::Layer; theNode = theNode->m_Parent) { + } + if (theNode && theNode->m_Type == GraphObjectTypes::Layer) + return static_cast<SLayer *>(theNode); + return nullptr; +} + +void STranslation::RenderZoomRender(SZoomRender &inRender) +{ + SLayer *theLayer(inRender.m_Layer); + CPt thePoint(inRender.m_Point); + if (theLayer) { + qt3ds::render::IQt3DSRenderer &theRenderer(m_Context.GetRenderer()); + Option<qt3ds::render::SLayerPickSetup> thePickSetup( + theRenderer.GetLayerPickSetup(*theLayer, QT3DSVec2((QT3DSF32)thePoint.x, (QT3DSF32)thePoint.y), + QSize(16, 16))); + if (thePickSetup.hasValue()) { + qt3ds::render::NVRenderContext &theRenderContext(m_Context.GetRenderContext()); + theRenderContext.SetViewport(qt3ds::render::NVRenderRect(0, 0, 100, 100)); + theRenderContext.SetScissorRect(qt3ds::render::NVRenderRect(0, 0, 100, 100)); + theRenderContext.SetDepthWriteEnabled(true); + theRenderContext.SetScissorTestEnabled(true); + theRenderContext.SetClearColor(QT3DSVec4(.2f, .2f, .2f, 0.0f)); + theRenderContext.Clear(qt3ds::render::NVRenderClearFlags( + qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth)); + theRenderer.RunLayerRender(*theLayer, thePickSetup->m_ViewProjection); + theRenderContext.SetScissorTestEnabled(false); + } + } +} + +void STranslation::DrawBoundingBox(SNode &inNode, QT3DSVec3 inColor) +{ + qt3ds::NVBounds3 theBounds = inNode.GetBounds(m_Context.GetBufferManager(), + m_Context.GetPathManager(), true, this); + qt3ds::render::IRenderWidget &theBBoxWidget = qt3ds::render::IRenderWidget::CreateBoundingBoxWidget( + inNode, theBounds, inColor, m_Context.GetRenderer().GetPerFrameAllocator()); + m_Context.GetRenderer().AddRenderWidget(theBBoxWidget); +} + +void STranslation::drawPivot(SGraphObjectTranslator &inTranslator) +{ + if (GraphObjectTypes::IsNodeType(inTranslator.GetGraphObject().m_Type)) { + qt3ds::render::IRenderWidget &theAxisWidget = qt3ds::render::IRenderWidget::CreateAxisWidget( + static_cast<SNode &>(inTranslator.GetGraphObject()), + m_Context.GetRenderer().GetPerFrameAllocator()); + m_Context.GetRenderer().AddRenderWidget(theAxisWidget); + } else { + QT3DS_ASSERT(false); + } +} + +void STranslation::SetViewport(qt3ds::QT3DSF32 inWidth, qt3ds::QT3DSF32 inHeight) +{ + m_Viewport = QT3DSVec2(inWidth, inHeight); + if (m_EditCameraEnabled) { + // Update inner rect as it is used to calculate camera frustrum for visual aid widget + QSize theSize = g_StudioApp.GetCore()->GetStudioProjectSettings()->getPresentationSize(); + m_InnerRect.m_Top = 0; + m_InnerRect.m_Bottom = theSize.height(); + m_InnerRect.m_Left = 0; + m_InnerRect.m_Right = theSize.width(); + } +} + +Option<QT3DSU32> STranslation::PickWidget(CPt inMouseCoords, TranslationSelectMode::Enum, + qt3ds::widgets::IStudioWidgetBase &inWidget) +{ + SNode &theNode = inWidget.GetNode(); + SGraphObjectTranslator *theWidgetTranslator = + theNode.m_UserData.DynamicCast<SGraphObjectTranslator>(); + SLayer *theLayer = GetLayerForNode(theNode); + if (theLayer && theWidgetTranslator) { + Option<qt3ds::render::SLayerPickSetup> thePickSetup( + m_Context.GetRenderer().GetLayerPickSetup( + *theLayer, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y), + QSize(4, 4))); + if (thePickSetup.hasValue()) { + qt3ds::render::NVRenderContext &theContext(m_Context.GetRenderContext()); + qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderFrameBuffer *> + __currentrt(theContext, &qt3ds::render::NVRenderContext::GetRenderTarget, + &qt3ds::render::NVRenderContext::SetRenderTarget); + qt3ds::render::NVRenderFrameBuffer *theFBO = + m_Context.GetResourceManager().AllocateFrameBuffer(); + const QT3DSU32 fboDims = 8; + if (!m_PickBuffer) { + m_PickBuffer = theContext.CreateTexture2D(); + m_PickBuffer->SetTextureData(qt3ds::foundation::NVDataRef<qt3ds::QT3DSU8>(), 0, fboDims, + fboDims, + qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8); + } + qt3ds::render::NVRenderRenderBuffer *theRenderBuffer = + m_Context.GetResourceManager().AllocateRenderBuffer( + fboDims, fboDims, qt3ds::render::NVRenderRenderBufferFormats::Depth16); + theFBO->Attach(qt3ds::render::NVRenderFrameBufferAttachments::Color0, + qt3ds::render::NVRenderTextureOrRenderBuffer(*m_PickBuffer)); + theFBO->Attach(qt3ds::render::NVRenderFrameBufferAttachments::Depth, + qt3ds::render::NVRenderTextureOrRenderBuffer(*theRenderBuffer)); + qt3ds::render::NVRenderRect theViewport(0, 0, fboDims, fboDims); + theContext.SetViewport(theViewport); + theContext.SetDepthWriteEnabled(true); + theContext.SetDepthTestEnabled(true); + theContext.SetScissorTestEnabled(false); + theContext.SetBlendingEnabled(false); + theContext.SetClearColor(QT3DSVec4(0, 0, 0, 0)); + theContext.Clear(qt3ds::render::NVRenderClearFlags( + qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth)); + inWidget.RenderPick(thePickSetup->m_ProjectionPreMultiply, theContext, + QSize(4, 4)); + // Now read the pixels back. + m_PixelBuffer.resize(fboDims * fboDims * 3); + theContext.ReadPixels(theViewport, qt3ds::render::NVRenderReadPixelFormats::RGB8, + m_PixelBuffer); + m_Context.GetResourceManager().Release(*theFBO); + m_Context.GetResourceManager().Release(*theRenderBuffer); + eastl::hash_map<QT3DSU32, QT3DSU32> tallies; + QT3DSU32 numPixels = fboDims * fboDims; + for (QT3DSU32 idx = 0; idx < numPixels; ++idx) { + qt3ds::QT3DSU16 theChannelAmount = + m_PixelBuffer[idx * 3] + (m_PixelBuffer[idx * 3 + 1] << 8); + if (theChannelAmount) + tallies.insert(eastl::make_pair(theChannelAmount, (QT3DSU32)0)).first->second += 1; + } + QT3DSU32 tallyMaxTally = 0; + QT3DSU32 tallyMaxIdx = 0; + for (eastl::hash_map<QT3DSU32, QT3DSU32>::iterator iter = tallies.begin(), + end = tallies.end(); + iter != end; ++iter) { + if (iter->second > tallyMaxTally) { + tallyMaxTally = iter->second; + tallyMaxIdx = iter->first; + } + } + if (tallyMaxIdx > 0) { + return tallyMaxIdx; + } + } + } + return Empty(); +} + +SStudioPickValue STranslation::Pick(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode, + bool ignoreWidgets) +{ + bool requestRender = false; + + if (!ignoreWidgets) { + if (m_Doc.GetDocumentReader().AreGuidesEditable()) { + qt3dsdm::TGuideHandleList theGuides = m_Doc.GetDocumentReader().GetGuides(); + CPt renderSpacePt(inMouseCoords.x - (long)m_InnerRect.m_Left, + (long)GetViewportDimensions().y - inMouseCoords.y + - (long)m_InnerRect.m_Bottom); + for (size_t guideIdx = 0, guideEnd = theGuides.size(); + guideIdx < guideEnd; ++guideIdx) { + qt3dsdm::SGuideInfo theGuideInfo = + m_Doc.GetDocumentReader().GetGuideInfo(theGuides[guideIdx]); + float width = (theGuideInfo.m_Width / 2.0f) + 2.0f; + switch (theGuideInfo.m_Direction) { + case qt3dsdm::GuideDirections::Horizontal: + if (fabs((float)renderSpacePt.y - theGuideInfo.m_Position) <= width) + return theGuides[guideIdx]; + break; + case qt3dsdm::GuideDirections::Vertical: + if (fabs((float)renderSpacePt.x - theGuideInfo.m_Position) <= width) + return theGuides[guideIdx]; + break; + default: + break; + } + } + } + if (IsPathWidgetActive()) { + Option<QT3DSU32> picked = PickWidget(inMouseCoords, inSelectMode, *m_PathWidget); + if (picked.hasValue()) { + RequestRender(); + DoPrepareForDrag(&m_PathWidget->GetNode()); + return m_PathWidget->PickIndexToPickValue(*picked); + } + } + // Pick against the widget first if possible. + if (m_LastRenderedWidget && (m_LastRenderedWidget->GetNode().m_Flags.IsActive() + || m_LastRenderedWidget->GetNode().m_Type + == GraphObjectTypes::Light + || m_LastRenderedWidget->GetNode().m_Type + == GraphObjectTypes::Camera) + && !m_LastRenderedWidget->isNodeBehindCamera()) { + Option<QT3DSU32> picked = PickWidget(inMouseCoords, inSelectMode, + *m_LastRenderedWidget); + if (picked.hasValue()) { + RequestRender(); + DoPrepareForDrag(&m_LastRenderedWidget->GetNode()); + return m_LastRenderedWidget->PickIndexToPickValue(*picked); + } + } + } + // Pick against Lights and Cameras + // This doesn't use the color picker or renderer pick + float lastDist = 99999999999999.0f; + int lastIndex = -1; + for (int i = 0; i < int(m_editModeCamerasAndLights.size()); ++i) { + const QT3DSVec2 mouseCoords((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y); + float dist; + SGraphObject &object = m_editModeCamerasAndLights[i]->GetGraphObject(); + m_VisualAidWidget->SetNode(static_cast<SNode *>(&object)); + if (m_VisualAidWidget->pick(m_Context.GetRenderer().GetRenderWidgetContext(), + dist, GetViewportDimensions(), mouseCoords)) { + if (dist < lastDist) { + lastDist = dist; + lastIndex = i; + } + } + } + + if (m_Scene && m_Scene->m_FirstChild) { + qt3ds::render::Qt3DSRenderPickResult thePickResult = + m_Context.GetRenderer().Pick(*m_Scene->m_FirstChild, GetViewportDimensions(), + QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y)); + if (thePickResult.m_HitObject) { + const SGraphObject &theObject = *thePickResult.m_HitObject; + + // check hit distance to cameras and lights + if (lastIndex != -1 && thePickResult.m_CameraDistanceSq > lastDist * lastDist) { + DoPrepareForDrag(static_cast<SNode *>( + &(m_editModeCamerasAndLights[lastIndex]->GetGraphObject()))); + return m_editModeCamerasAndLights[lastIndex]->GetInstanceHandle(); + } + + if (theObject.m_Type == GraphObjectTypes::Model + || theObject.m_Type == GraphObjectTypes::Text + || theObject.m_Type == GraphObjectTypes::Path) { + const SNode &theTranslatorModel(static_cast<const SNode &>(theObject)); + SGraphObjectTranslator *theTranslator = + theTranslatorModel.m_UserData.DynamicCast<SGraphObjectTranslator>(); + const SNode *theModelPtr = &theTranslatorModel; + if (theTranslator->GetPossiblyAliasedInstanceHandle() + != theTranslator->GetInstanceHandle()) { + theTranslator = + GetOrCreateTranslator(theTranslator->GetPossiblyAliasedInstanceHandle()); + theModelPtr = + static_cast<const SNode *>(&theTranslator->GetNonAliasedGraphObject()); + } + Qt3DSDMInstanceHandle theActiveComponent = + m_Reader.GetComponentForSlide(m_Doc.GetActiveSlide()); + if (inSelectMode == TranslationSelectMode::Group) { + // Bounce up the hierarchy till one of two conditions are met + // the parent is a layer or the our component is the active component + // but the parent's is not. + while (theTranslator && GraphObjectTypes::IsNodeType( + theTranslator->GetGraphObject().m_Type)) { + SNode *myNode = static_cast<SNode *>(&theTranslator->GetGraphObject()); + if (myNode->m_Parent == nullptr) { + theTranslator = nullptr; + break; + } + SNode *parentNode = myNode->m_Parent; + SGraphObjectTranslator *theParentTranslator = + parentNode->m_UserData.DynamicCast<SGraphObjectTranslator>(); + Qt3DSDMInstanceHandle myComponent = + m_Reader.GetAssociatedComponent(theTranslator->GetInstanceHandle()); + Qt3DSDMInstanceHandle myParentComponent = m_Reader.GetAssociatedComponent( + theParentTranslator->GetInstanceHandle()); + if (parentNode->m_Type == GraphObjectTypes::Layer) { + if (myParentComponent != theActiveComponent) + theTranslator = nullptr; + break; + } + if (myComponent == theActiveComponent + && myParentComponent != theActiveComponent) + break; + theTranslator = theParentTranslator; + } + } else { + // Bounce up until we get into the active component and then stop. + while (inSelectMode == TranslationSelectMode::Single && theTranslator + && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) + && m_Reader.GetAssociatedComponent(theTranslator->GetInstanceHandle()) + != theActiveComponent) { + SNode *theNode = static_cast<SNode *>(&theTranslator->GetGraphObject()); + theNode = theNode->m_Parent; + if (theNode && theNode->m_Type != GraphObjectTypes::Layer) + theTranslator = + theNode->m_UserData.DynamicCast<SGraphObjectTranslator>(); + else + theTranslator = nullptr; + } + } + + if (theTranslator) { + QT3DS_ASSERT(GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type)); + DoPrepareForDrag(static_cast<SNode *>(&theTranslator->GetGraphObject())); + return theTranslator->GetInstanceHandle(); + } + } + } + if (requestRender) + RequestRender(); + } + + if (lastIndex != -1) { + DoPrepareForDrag(static_cast<SNode *>( + &(m_editModeCamerasAndLights[lastIndex]->GetGraphObject()))); + return m_editModeCamerasAndLights[lastIndex]->GetInstanceHandle(); + } + + return SStudioPickValue(); +} + +qt3ds::foundation::Option<qt3dsdm::SGuideInfo> STranslation::PickRulers(CPt inMouseCoords) +{ + CPt renderSpacePt(inMouseCoords.x, (long)GetViewportDimensions().y - inMouseCoords.y); + // If mouse is inside outer rect but outside inner rect. + if (m_OuterRect.Contains(renderSpacePt.x, renderSpacePt.y) + && !m_InnerRect.Contains(renderSpacePt.x, renderSpacePt.y)) { + std::shared_ptr<qt3dsdm::IGuideSystem> theGuideSystem = + m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetGuideSystem(); + if (renderSpacePt.x >= m_InnerRect.m_Left && renderSpacePt.x <= m_InnerRect.m_Right) { + return qt3dsdm::SGuideInfo((QT3DSF32)renderSpacePt.y - (QT3DSF32)m_InnerRect.m_Bottom, + qt3dsdm::GuideDirections::Horizontal); + } else if (renderSpacePt.y >= m_InnerRect.m_Bottom + && renderSpacePt.y <= m_InnerRect.m_Top) { + return qt3dsdm::SGuideInfo((QT3DSF32)renderSpacePt.x - (QT3DSF32)m_InnerRect.m_Left, + qt3dsdm::GuideDirections::Vertical); + } + } + return qt3ds::foundation::Option<qt3dsdm::SGuideInfo>(); +} + +QT3DSVec3 STranslation::GetIntendedPosition(qt3dsdm::Qt3DSDMInstanceHandle inInstance, CPt inPos) +{ + ClearDirtySet(); + SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(inInstance); + if (theTranslator == nullptr) + return QT3DSVec3(0, 0, 0); + if (GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) == false) + return QT3DSVec3(0, 0, 0); + SNode *theNode = static_cast<SNode *>(&theTranslator->GetGraphObject()); + SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); + { + // Get the node's parent + Qt3DSDMInstanceHandle theParent = m_AssetGraph.GetParent(inInstance); + SGraphObjectTranslator *theParentTranslator = GetOrCreateTranslator(theParent); + if (theParentTranslator + && GraphObjectTypes::IsNodeType(theParentTranslator->GetGraphObject().m_Type)) + theCamera = m_Context.GetRenderer().GetCameraForNode( + *static_cast<SNode *>(&theParentTranslator->GetGraphObject())); + } + if (theCamera == nullptr) + return QT3DSVec3(0, 0, 0); + + QT3DSVec3 theGlobalPos(theNode->GetGlobalPos()); + return m_Context.GetRenderer().UnprojectToPosition(*theCamera, theGlobalPos, + QT3DSVec2((QT3DSF32)inPos.x, (QT3DSF32)inPos.y)); +} + +static void CheckLockToAxis(QT3DSF32 &inXDistance, QT3DSF32 &inYDistance, bool inLockToAxis) +{ + if (inLockToAxis) { + if (fabs(inXDistance) > fabs(inYDistance)) + inYDistance = 0; + else + inXDistance = 0; + } +} + +void STranslation::ApplyPositionalChange(QT3DSVec3 inDiff, SNode &inNode, + CUpdateableDocumentEditor &inEditor) +{ + if (m_MouseDownParentGlobalTransformInverse.isEmpty()) { + if (inNode.m_Parent) + m_MouseDownParentGlobalTransformInverse = + inNode.m_Parent->m_GlobalTransform.getInverse(); + else + m_MouseDownParentGlobalTransformInverse = QT3DSMat44::createIdentity(); + } + QT3DSMat44 theGlobalTransform = m_MouseDownNode.m_GlobalTransform; + QT3DSMat44 theNewLocalTransform = + m_MouseDownParentGlobalTransformInverse.getValue() * theGlobalTransform; + QT3DSVec3 theOldPos = theNewLocalTransform.column3.getXYZ(); + theOldPos.z *= -1; + + theGlobalTransform.column3 += QT3DSVec4(inDiff, 0.0f); + theNewLocalTransform = m_MouseDownParentGlobalTransformInverse.getValue() * theGlobalTransform; + QT3DSVec3 thePos = theNewLocalTransform.column3.getXYZ(); + thePos.z *= -1; + + QT3DSVec3 theDiff = thePos - theOldPos; + + SetPosition(m_MouseDownNode.m_Position + theDiff, inEditor); +} + +void STranslation::TranslateSelectedInstanceAlongCameraDirection( + CPt inOriginalCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) +{ + SNode *theNode = GetSelectedNode(); + if (theNode == nullptr) + return; + SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); + if (theCamera == nullptr) + return; + QT3DSF32 theYDistance = QT3DSF32(inMouseCoords.y - inOriginalCoords.y); + if (fabs(theYDistance) == 0) + return; + + QT3DSF32 theMouseMultiplier = 1.0f / 2.0f; + QT3DSF32 theDistanceMultiplier = 1.0f + theYDistance * theMouseMultiplier; + QT3DSVec3 theCameraDir = m_MouseDownCamera.GetDirection(); + + QT3DSVec3 theDiff = theCameraDir * theDistanceMultiplier; + ApplyPositionalChange(theDiff, *theNode, inEditor); +} + +void STranslation::TranslateSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor, bool inLockToAxis) +{ + SNode *theNode = GetSelectedNode(); + if (theNode == nullptr) + return; + qt3ds::render::IQt3DSRenderer &theRenderer(m_Context.GetRenderer()); + + QT3DSF32 theXDistance = QT3DSF32(inMouseCoords.x - inOriginalCoords.x); + QT3DSF32 theYDistance = QT3DSF32(inMouseCoords.y - inOriginalCoords.y); + if (fabs(theXDistance) == 0 && fabs(theYDistance) == 0) + return; + + CheckLockToAxis(theXDistance, theYDistance, inLockToAxis); + + inMouseCoords.x = inOriginalCoords.x + (long)theXDistance; + inMouseCoords.y = inOriginalCoords.y + (long)theYDistance; + QT3DSVec3 theNodeGlobal = m_MouseDownNode.GetGlobalPos(); + QT3DSVec3 theOriginalPos = theRenderer.UnprojectToPosition( + *theNode, theNodeGlobal, QT3DSVec2((QT3DSF32)inOriginalCoords.x, (QT3DSF32)inOriginalCoords.y)); + QT3DSVec3 theNewPos = theRenderer.UnprojectToPosition( + *theNode, theNodeGlobal, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y)); + + QT3DSVec3 theDiff = theNewPos - theOriginalPos; + ApplyPositionalChange(theDiff, *theNode, inEditor); +} + +void STranslation::ScaleSelectedInstanceZ(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor) +{ + SNode *theNode = GetSelectedNode(); + if (theNode == nullptr) + return; + + // Scale scales uniformly and responds to mouse Y only. + QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y; + if (fabs(theYDistance) == 0) + return; + + QT3DSF32 theMouseMultiplier = 1.0f / 40.0f; + QT3DSF32 theScaleMultiplier = 1.0f + theYDistance * theMouseMultiplier; + + SetScale(QT3DSVec3(m_MouseDownNode.m_Scale.x, m_MouseDownNode.m_Scale.y, + m_MouseDownNode.m_Scale.z * theScaleMultiplier), + inEditor); +} + +void STranslation::ScaleSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor) +{ + SNode *theNode = GetSelectedNode(); + if (theNode == nullptr) + return; + + // Scale scales uniformly and responds to mouse Y only. + QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y; + if (fabs(theYDistance) == 0) + return; + + QT3DSF32 theMouseMultiplier = 1.0f / 40.0f; + QT3DSF32 theScaleMultiplier = 1.0f + theYDistance * theMouseMultiplier; + + SetScale(m_MouseDownNode.m_Scale * theScaleMultiplier, inEditor); +} + +void STranslation::CalculateNodeGlobalRotation(SNode &inNode) +{ + if (inNode.m_Parent) + CalculateNodeGlobalRotation(*inNode.m_Parent); + if (m_MouseDownParentRotationInverse.isEmpty()) { + m_MouseDownParentRotationInverse = QT3DSMat33::createIdentity(); + m_MouseDownGlobalRotation = QT3DSMat33::createIdentity(); + } + + QT3DSMat44 localRotation; + inNode.CalculateRotationMatrix(localRotation); + if (inNode.m_Flags.IsLeftHanded()) + SNode::FlipCoordinateSystem(localRotation); + QT3DSMat33 theRotation; + SNode::GetMatrixUpper3x3(theRotation, localRotation); + + m_MouseDownParentRotationInverse = m_MouseDownGlobalRotation; + m_MouseDownGlobalRotation = m_MouseDownGlobalRotation.getValue() * theRotation; +} + +void STranslation::ApplyRotationToSelectedInstance(const QT3DSQuat &inFinalRotation, SNode &inNode, + CUpdateableDocumentEditor &inEditor, + bool inIsMouseRelative) +{ + if (m_MouseDownParentRotationInverse.isEmpty()) { + CalculateNodeGlobalRotation(inNode); + m_MouseDownParentRotationInverse = m_MouseDownParentRotationInverse->getInverse(); + } + QT3DSMat33 theRotationMatrix(inFinalRotation); + + QT3DSMat33 theFinalGlobal = theRotationMatrix * m_MouseDownGlobalRotation.getValue(); + QT3DSMat33 theLocalGlobal = m_MouseDownParentRotationInverse.getValue() * theFinalGlobal; + QT3DSVec3 theRotations = inNode.GetRotationVectorFromRotationMatrix(theLocalGlobal); + theRotations = qt3ds::render::SRotationHelper::ToNearestAngle(inNode.m_Rotation, theRotations, + inNode.m_RotationOrder); + SetRotation(theRotations, inEditor); + // Trackball rotation is relative to the previous mouse position. + // Rotation manipulator rotation is relative to only the original mouse down position + // so inIsMouseRelative is false for rotations that are relative to the original mouse down + // position. + if (inIsMouseRelative) + m_MouseDownGlobalRotation = theFinalGlobal; +} + +void STranslation::RotateSelectedInstanceAboutCameraDirectionVector( + CPt inPreviousMouseCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor) +{ + SNode *theNode = GetSelectedNode(); + if (theNode == nullptr) + return; + SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); + if (theCamera == nullptr) + return; + + QT3DSVec3 theDirection = m_MouseDownCamera.GetDirection(); + QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inPreviousMouseCoords.y; + QT3DSQuat theYRotation(-1.0f * theYDistance * g_RotationScaleFactor, theDirection); + + ApplyRotationToSelectedInstance(theYRotation, *theNode, inEditor); +} + +// This method never feels right to me. It is difficult to apply it to a single axis (of course for +// that you can use the inspector palette). +void STranslation::RotateSelectedInstance(CPt inOriginalCoords, CPt inPreviousCoords, + CPt inMouseCoords, CUpdateableDocumentEditor &inEditor, + bool inLockToAxis) +{ + SNode *theNode = GetSelectedNode(); + if (theNode == nullptr) + return; + SCamera *theCamera = m_Context.GetRenderer().GetCameraForNode(*theNode); + if (theCamera == nullptr) + return; + // We want to do a similar translation to what we did below but we need to calculate the + // parent's global rotation without scale included. + + QT3DSF32 theXDistance = (QT3DSF32)inMouseCoords.x - (QT3DSF32)inPreviousCoords.x; + QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inPreviousCoords.y; + bool xIsZero = fabs(theXDistance) < .001f; + bool yIsZero = fabs(theYDistance) < .001f; + if (xIsZero && yIsZero) + return; + + if (inLockToAxis) { + QT3DSF32 originalDistX = (QT3DSF32)inMouseCoords.x - (QT3DSF32)inOriginalCoords.x; + QT3DSF32 originalDistY = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y; + if (inLockToAxis) { + if (fabs(originalDistX) > fabs(originalDistY)) + theYDistance = 0; + else + theXDistance = 0; + } + } + + QT3DSVec3 theXAxis = m_MouseDownCamera.m_GlobalTransform.column0.getXYZ(); + QT3DSVec3 theYAxis = m_MouseDownCamera.m_GlobalTransform.column1.getXYZ(); + + QT3DSVec3 theFinalAxis = theXDistance * theYAxis + theYDistance * theXAxis; + QT3DSF32 theTotalDistance = theFinalAxis.normalize(); + QT3DSQuat theRotation(theTotalDistance * g_RotationScaleFactor / 2.0f, theFinalAxis); + + ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor); +} + +inline void NiceAdd(QT3DSF32 &ioValue, QT3DSF32 inIncrement) +{ + QT3DSF32 temp = ioValue + inIncrement; + // Round to nearest .5 + QT3DSF32 sign = temp >= 0 ? 1.0f : -1.0f; + QT3DSU32 largerValue = (QT3DSU32)(fabs(temp * 10.0f)); + + QT3DSU32 leftover = largerValue % 10; + // Round down to zero + largerValue -= leftover; + if (leftover < 2) + leftover = 0; + else if (leftover > 7) { + leftover = 0; + largerValue += 10; + } else + leftover = 5; + largerValue += leftover; + + ioValue = sign * (QT3DSF32)largerValue / 10.0f; +} + +static inline QT3DSVec3 GetAxis(QT3DSU32 inIndex, QT3DSMat33 &inMatrix) +{ + QT3DSVec3 retval(0, 0, 0); + switch (inIndex) { + case 0: + retval = inMatrix.column0; + break; + case 1: + retval = inMatrix.column1; + break; + case 2: + retval = inMatrix.column2; + break; + default: + QT3DS_ASSERT(false); + break; + } + retval.normalize(); + return retval; +} + +inline Option<QT3DSF32> GetScaleAlongAxis(const QT3DSVec3 &inAxis, const QT3DSVec3 &inObjToOriginal, + const QT3DSVec3 &inObjToCurrent) +{ + QT3DSF32 lhs = inAxis.dot(inObjToCurrent); + QT3DSF32 rhs = inAxis.dot(inObjToOriginal); + if (fabs(rhs) > .001f) + return lhs / rhs; + return Empty(); +} + +// Make a nice rotation from the incoming rotation +static inline QT3DSF32 MakeNiceRotation(QT3DSF32 inAngle) +{ + TODEG(inAngle); + inAngle *= 10.0f; + QT3DSF32 sign = inAngle > 0.0f ? 1.0f : -1.0f; + // Attempt to ensure angle is prtty clean + QT3DSU32 clampedAngle = (QT3DSU32)(fabs(inAngle) + .5f); + QT3DSU32 leftover = clampedAngle % 10; + clampedAngle -= leftover; + if (leftover <= 2) + leftover = 0; + else if (leftover <= 7) + leftover = 5; + else + leftover = 10; + clampedAngle += leftover; + QT3DSF32 retval = (QT3DSF32)clampedAngle; + retval = (retval * sign) / 10.0f; + TORAD(retval); + return retval; +} + +static inline QT3DSF32 ShortestAngleDifference(QT3DSF32 inCumulative, QT3DSF32 inNewTotal) +{ + QT3DSF32 diff = qt3ds::render::SRotationHelper::ToMinimalAngle(inNewTotal - inCumulative); + return inCumulative + diff; +} + +Option<SDragPreparationResult> +STranslation::PrepareWidgetDrag(qt3ds::widgets::StudioWidgetComponentIds::Enum inComponentId, + qt3ds::widgets::StudioWidgetTypes::Enum inWidgetId, + qt3ds::render::RenderWidgetModes::Enum inWidgetMode, SNode &inNode, + CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords) +{ + SDragPreparationResult retval; + retval.m_ComponentId = inComponentId; + retval.m_WidgetType = inWidgetId; + retval.m_WidgetMode = inWidgetMode; + qt3ds::render::IQt3DSRenderer &theRenderer(m_Context.GetRenderer()); + retval.m_Renderer = &theRenderer; + retval.m_Node = &inNode; + retval.m_Layer = GetLayerForNode(inNode); + retval.m_Camera = theRenderer.GetCameraForNode(inNode); + QSize theUnsignedDimensions(m_Context.GetWindowDimensions()); + QT3DSVec2 theWindowDimensions((QT3DSF32)theUnsignedDimensions.width(), + (QT3DSF32)theUnsignedDimensions.height()); + if (retval.m_Camera == nullptr || retval.m_Layer == nullptr) + return Empty(); + + SCamera &theCamera(*retval.m_Camera); + SLayer &theLayer(*retval.m_Layer); + QT3DSVec2 theLayerOriginalCoords = m_Context.GetRenderer().GetLayerMouseCoords( + *retval.m_Layer, QT3DSVec2((QT3DSF32)inOriginalCoords.x, (QT3DSF32)inOriginalCoords.y), + theWindowDimensions, true); + + QT3DSVec2 theLayerMouseCoords = m_Context.GetRenderer().GetLayerMouseCoords( + *retval.m_Layer, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y), + theWindowDimensions, true); + + QT3DSVec2 thePreviousLayerMouseCoords = m_Context.GetRenderer().GetLayerMouseCoords( + *retval.m_Layer, QT3DSVec2((QT3DSF32)inPreviousMouseCoords.x, (QT3DSF32)inPreviousMouseCoords.y), + theWindowDimensions, true); + QT3DSMat44 theGlobalTransform(QT3DSMat44::createIdentity()); + if (inWidgetMode == qt3ds::render::RenderWidgetModes::Local) { + theGlobalTransform = m_MouseDownNode.m_GlobalTransform; + } + retval.m_GlobalTransform = theGlobalTransform; + QT3DSMat33 theNormalMat(theGlobalTransform.column0.getXYZ(), theGlobalTransform.column1.getXYZ(), + theGlobalTransform.column2.getXYZ()); + theNormalMat = theNormalMat.getInverse().getTranspose(); + retval.m_NormalMatrix = theNormalMat; + qt3ds::render::NVRenderRectF theLayerRect(theRenderer.GetLayerRect(theLayer)); + SRay theOriginalRay = + theCamera.Unproject(theLayerOriginalCoords, theLayerRect, theWindowDimensions); + SRay theCurrentRay = + theCamera.Unproject(theLayerMouseCoords, theLayerRect, theWindowDimensions); + SRay thePreviousRay = + theCamera.Unproject(thePreviousLayerMouseCoords, theLayerRect, theWindowDimensions); + retval.m_OriginalRay = theOriginalRay; + retval.m_CurrentRay = theCurrentRay; + retval.m_PreviousRay = thePreviousRay; + QT3DSVec3 theAxis; + QT3DSVec3 thePlaneNormal; + bool isPlane = false; + QT3DSVec3 globalPos = inNode.GetGlobalPivot(); + QT3DSVec3 camGlobalPos = theCamera.GetGlobalPos(); + QT3DSVec3 theCamDirection; + retval.m_GlobalPos = globalPos; + retval.m_CameraGlobalPos = camGlobalPos; + if (theCamera.m_Flags.IsOrthographic()) + theCamDirection = theCamera.GetDirection(); + else { + theCamDirection = globalPos - camGlobalPos; + // The normal will be normalized below. + } + theCamDirection.normalize(); + retval.m_CameraDirection = theCamDirection; + retval.m_AxisIndex = 0; + switch (inComponentId) { + default: + QT3DS_ASSERT(false); + break; + case qt3ds::widgets::StudioWidgetComponentIds::XAxis: + theAxis = QT3DSVec3(1, 0, 0); + break; + case qt3ds::widgets::StudioWidgetComponentIds::YAxis: + theAxis = QT3DSVec3(0, 1, 0); + retval.m_AxisIndex = 1; + break; + case qt3ds::widgets::StudioWidgetComponentIds::ZAxis: + theAxis = QT3DSVec3(0, 0, -1); + retval.m_AxisIndex = 2; + break; + case qt3ds::widgets::StudioWidgetComponentIds::XPlane: + thePlaneNormal = QT3DSVec3(1, 0, 0); + isPlane = true; + break; + case qt3ds::widgets::StudioWidgetComponentIds::YPlane: + thePlaneNormal = QT3DSVec3(0, 1, 0); + isPlane = true; + break; + case qt3ds::widgets::StudioWidgetComponentIds::ZPlane: + thePlaneNormal = QT3DSVec3(0, 0, -1); + isPlane = true; + break; + case qt3ds::widgets::StudioWidgetComponentIds::CameraPlane: { + isPlane = true; + thePlaneNormal = theCamDirection; + } break; + } + retval.m_IsPlane = isPlane; + if (inWidgetId == qt3ds::widgets::StudioWidgetTypes::Rotation) { + if (isPlane == false) { + theAxis = theNormalMat.transform(theAxis); + theAxis.normalize(); + thePlaneNormal = theAxis; + retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * thePlaneNormal.dot(globalPos)); + } else { + if (inComponentId != qt3ds::widgets::StudioWidgetComponentIds::CameraPlane) { + thePlaneNormal = theNormalMat.transform(thePlaneNormal); + } + thePlaneNormal.normalize(); + retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * (thePlaneNormal.dot(globalPos))); + } + } else { + if (isPlane == false) { + theAxis = theNormalMat.transform(theAxis); + theAxis.normalize(); + QT3DSVec3 theCameraToObj = globalPos - camGlobalPos; + QT3DSVec3 theTemp = theAxis.cross(theOriginalRay.m_Direction); + // When the axis is parallel to the camera, we can't drag meaningfully + if (theTemp.magnitudeSquared() < .05f) { + // Attempt to find a better axis by moving the object back towards the camera. + QT3DSF32 theSign = theCameraToObj.dot(theCamDirection) > 0.0 ? -1.0f : 1.0f; + QT3DSF32 theDistance = theCameraToObj.dot(theCamDirection); + QT3DSVec3 thePoint = globalPos + (theDistance * theSign) * theAxis; + // Check if we actually moved to right direction + QT3DSVec3 theNewCameraToObj = thePoint - camGlobalPos; + QT3DSF32 theNewDistance = theNewCameraToObj.dot(theCamDirection); + if (theNewDistance > theDistance) + thePoint = globalPos - (theDistance * theSign) * theAxis; + + QT3DSVec3 theNewDir = thePoint - camGlobalPos; + theNewDir.normalize(); + theTemp = theAxis.cross(theNewDir); + // Attempt again to find a better cross + if (theTemp.magnitudeSquared() < .05f) + return Empty(); + } + thePlaneNormal = theTemp.cross(theAxis); + thePlaneNormal.normalize(); + retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * thePlaneNormal.dot(globalPos)); + } else { + thePlaneNormal = theNormalMat.transform(thePlaneNormal); + thePlaneNormal.normalize(); + retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * (thePlaneNormal.dot(globalPos))); + } + } + retval.m_Axis = theAxis; + retval.m_OriginalPlaneCoords = theOriginalRay.Intersect(retval.m_Plane); + retval.m_CurrentPlaneCoords = theCurrentRay.Intersect(retval.m_Plane); + retval.m_PreviousPlaneCoords = thePreviousRay.Intersect(retval.m_Plane); + + // Resolve t-value from current ray intersection calculation + // Its sign indicates the position relative to the camera + float t = 0.f; + + // Ray direction is normalized, so one of the components must be > .1f + if (qAbs(theCurrentRay.m_Direction.x) > .1f) { + t = (retval.m_CurrentPlaneCoords->x - theCurrentRay.m_Origin.x) + / theCurrentRay.m_Direction.x; + } else if (qAbs(theCurrentRay.m_Direction.y) > .1f) { + t = (retval.m_CurrentPlaneCoords->y - theCurrentRay.m_Origin.y) + / theCurrentRay.m_Direction.y; + } else if (qAbs(theCurrentRay.m_Direction.z) > .1f) { + t = (retval.m_CurrentPlaneCoords->z - theCurrentRay.m_Origin.z) + / theCurrentRay.m_Direction.z; + } + retval.m_isBehindCamera = t < 0; + return retval; +} + +void STranslation::PerformWidgetDrag(int inWidgetSubComponent, CPt inOriginalCoords, + CPt inPreviousMouseCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor) +{ + if (inWidgetSubComponent == 0 || m_LastRenderedWidget == nullptr) { + QT3DS_ASSERT(false); + return; + } + Option<SDragPreparationResult> thePrepResult(PrepareWidgetDrag( + static_cast<qt3ds::widgets::StudioWidgetComponentIds::Enum>(inWidgetSubComponent), + m_LastRenderedWidget->GetWidgetType(), m_LastRenderedWidget->GetRenderWidgetMode(), + m_LastRenderedWidget->GetNode(), inOriginalCoords, inPreviousMouseCoords, inMouseCoords)); + if (!thePrepResult.hasValue()) + return; + + Option<QT3DSVec3> theOriginalPlaneCoords(thePrepResult->m_OriginalPlaneCoords); + Option<QT3DSVec3> theCurrentPlaneCoords(thePrepResult->m_CurrentPlaneCoords); + QT3DSVec3 globalPos(thePrepResult->m_GlobalPos); + bool isPlane(thePrepResult->m_IsPlane); + bool isBehindCamera(thePrepResult->m_isBehindCamera); + QT3DSVec3 theAxis(thePrepResult->m_Axis); + QT3DSU32 axisIndex(thePrepResult->m_AxisIndex); + SNode *theNode(thePrepResult->m_Node); + SRay theCurrentRay(thePrepResult->m_CurrentRay); + SRay theOriginalRay(thePrepResult->m_OriginalRay); + QT3DSVec3 thePlaneNormal(thePrepResult->m_Plane.n); + QT3DSVec3 theCamDirection(thePrepResult->m_CameraDirection); + QT3DSVec3 camGlobalPos(thePrepResult->m_CameraGlobalPos); + + switch (m_LastRenderedWidget->GetWidgetType()) { + default: + QT3DS_ASSERT(false); + return; + case qt3ds::widgets::StudioWidgetTypes::Scale: { + if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue() + && !isBehindCamera) { + QT3DSVec3 objToOriginal = globalPos - *theOriginalPlaneCoords; + QT3DSVec3 objToCurrent = globalPos - *theCurrentPlaneCoords; + QT3DSVec3 theScaleMultiplier(1, 1, 1); + if (!isPlane) { + // Ensure that we only have a scale vector in the direction of the axis. + objToOriginal = theAxis * (theAxis.dot(objToOriginal)); + objToCurrent = theAxis * (theAxis.dot(objToCurrent)); + QT3DSF32 objToOriginalDot = theAxis.dot(objToOriginal); + if (fabs(objToOriginalDot) > .001f) + theScaleMultiplier[axisIndex] = + theAxis.dot(objToCurrent) / theAxis.dot(objToOriginal); + else + theScaleMultiplier[axisIndex] = 0.0f; + } + + QT3DSMat33 theNodeAxisMatrix(theNode->m_GlobalTransform.column0.getXYZ(), + theNode->m_GlobalTransform.column1.getXYZ(), + theNode->m_GlobalTransform.column2.getXYZ()); + theNodeAxisMatrix = theNodeAxisMatrix.getInverse().getTranspose(); + QT3DSVec3 &theLocalXAxis(theNodeAxisMatrix.column0); + QT3DSVec3 &theLocalYAxis(theNodeAxisMatrix.column1); + QT3DSVec3 &theLocalZAxis(theNodeAxisMatrix.column2); + theLocalXAxis.normalize(); + theLocalYAxis.normalize(); + theLocalZAxis.normalize(); + + Option<QT3DSF32> theXScale = GetScaleAlongAxis(theLocalXAxis, objToOriginal, objToCurrent); + Option<QT3DSF32> theYScale = GetScaleAlongAxis(theLocalYAxis, objToOriginal, objToCurrent); + Option<QT3DSF32> theZScale = GetScaleAlongAxis(theLocalZAxis, objToOriginal, objToCurrent); + QT3DSVec3 theScale = m_MouseDownNode.m_Scale; + if (theXScale.isEmpty() && theYScale.isEmpty() && theZScale.isEmpty()) { + theScale = QT3DSVec3(0, 0, 0); + } else { + if (theXScale.hasValue()) + theScale.x *= *theXScale; + if (theYScale.hasValue()) + theScale.y *= *theYScale; + if (theZScale.hasValue()) + theScale.z *= *theZScale; + } + m_LastRenderedWidget->SetAxisScale(theScaleMultiplier); + SetScale(theScale, inEditor); + } + } break; + case qt3ds::widgets::StudioWidgetTypes::Rotation: { + QT3DSF32 theIntersectionCosine = theOriginalRay.m_Direction.dot(thePlaneNormal); + QT3DSVec3 objToPrevious; + QT3DSVec3 objToCurrent; + if (fabs(theIntersectionCosine) > .08f) { + if (!theOriginalPlaneCoords.hasValue() || !theCurrentPlaneCoords.hasValue()) + return; + objToPrevious = globalPos - *theOriginalPlaneCoords; + objToCurrent = globalPos - *theCurrentPlaneCoords; + objToPrevious.normalize(); + QT3DSF32 lineLen = objToCurrent.normalize(); + + if (!thePrepResult->m_Camera->m_Flags.IsOrthographic()) { + // Flip object vector if coords are behind camera to get the correct angle + QT3DSVec3 camToCurrent = camGlobalPos - *theCurrentPlaneCoords; + if (camToCurrent.dot(theCamDirection) >= 0.0f) { + objToCurrent = -objToCurrent; + // Negative line length seems counterintuitive, but since the end point is + // behind the camera, it results in correct line when rendered + lineLen = -lineLen; + } + } + + QT3DSF32 cosAngle = objToPrevious.dot(objToCurrent); + QT3DSVec3 theCrossProd = objToPrevious.cross(objToCurrent); + QT3DSF32 theCrossPlaneDot = theCrossProd.dot(thePlaneNormal); + QT3DSF32 angleSign = theCrossPlaneDot >= 0.0f ? 1.0f : -1.0f; + QT3DSF32 angleRad = acos(cosAngle) * angleSign; + angleRad = MakeNiceRotation(angleRad); + QT3DSQuat theRotation(angleRad, thePlaneNormal); + + m_CumulativeRotation = ShortestAngleDifference(m_CumulativeRotation, angleRad); + m_LastRenderedWidget->SetRotationEdges(-1.0f * objToPrevious, thePlaneNormal, + m_CumulativeRotation, lineLen); + ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor, false); + } + // In this case we are viewing the plane of rotation pretty much dead on, so we need to + // assume the camera and the object are both in the plane of rotation. In this case we + // *sort* of need to do trackball rotation but force it to one plane of rotation. + else { + // Setup a plane 600 units away from the camera and have the gadget run from there. + + // This keeps rotation consistent. + QT3DSVec3 thePlaneSpot = theCamDirection * -600.0f + camGlobalPos; + qt3ds::NVPlane theCameraPlaneAtObject(theCamDirection, + -1.0f * (theCamDirection.dot(thePlaneSpot))); + theCurrentPlaneCoords = theCurrentRay.Intersect(theCameraPlaneAtObject); + theOriginalPlaneCoords = theOriginalRay.Intersect(theCameraPlaneAtObject); + QT3DSVec3 theChangeVector = *theOriginalPlaneCoords - *theCurrentPlaneCoords; + // Remove any component of the change vector that doesn't lie in the plane. + theChangeVector = + theChangeVector - theChangeVector.dot(thePlaneNormal) * thePlaneNormal; + QT3DSF32 theDistance = theChangeVector.normalize(); + // We want about 90* per 200 units in imaginary 600-units-from-camera space. + QT3DSF32 theScaleFactor = 1.0f / 200.0f; + if (thePrepResult->m_Camera->m_Flags.IsOrthographic()) + theScaleFactor = 1.0f / 100.0f; + + QT3DSF32 theDeg = 90.0f * theDistance * theScaleFactor; + // Check the sign of the angle. + QT3DSVec3 theCurrentIsectDir = camGlobalPos - *theCurrentPlaneCoords; + QT3DSVec3 thePreviousIsectDir = camGlobalPos - *theOriginalPlaneCoords; + QT3DSVec3 theCrossProd = theCurrentIsectDir.cross(thePreviousIsectDir); + QT3DSF32 theAngleSign = theCrossProd.dot(thePlaneNormal) > 0.0f ? 1.0f : -1.0f; + theDeg *= theAngleSign; + QT3DSF32 theRad(theDeg); + TORAD(theRad); + theRad = MakeNiceRotation(theRad); + QT3DSQuat theRotation(theRad, thePlaneNormal); + + // Call SetRotationEdges also here so rotation angle text gets drawn. + // Here just draw it in the middle of rotation circle for simplicity. + m_CumulativeRotation = ShortestAngleDifference(m_CumulativeRotation, theRad); + m_LastRenderedWidget->SetRotationEdges(QT3DSVec3(0.0f), thePlaneNormal, + m_CumulativeRotation, 0.0f); + + ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor, false); + } + } break; + case qt3ds::widgets::StudioWidgetTypes::Translation: { + if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue() + && !isBehindCamera) { + QT3DSVec3 theDiff = *theCurrentPlaneCoords - *theOriginalPlaneCoords; + if (isPlane) { + ApplyPositionalChange(theDiff, *theNode, inEditor); + } else { + QT3DSVec3 theMovement = theAxis * theAxis.dot(theDiff); + ApplyPositionalChange(theMovement, *theNode, inEditor); + } + } + } break; + } +} + +static float RoundToNearest(float inValue, float inMin, float inMax, float inRound) +{ + float half = (inMin + inMax) / 2.0f; + inValue -= half; + inValue = inRound * qFloor(inValue / inRound + .5f); + inValue += half; + inValue -= inMin; + return inValue; +} + +void STranslation::PerformGuideDrag(Qt3DSDMGuideHandle inGuide, CPt inPoint, + CUpdateableDocumentEditor &inEditor) +{ + qt3dsdm::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(inGuide); + CPt renderSpacePt(inPoint.x, (long)GetViewportDimensions().y - inPoint.y); + switch (theInfo.m_Direction) { + case qt3dsdm::GuideDirections::Horizontal: + theInfo.m_Position = RoundToNearest((float)renderSpacePt.y, (float)m_InnerRect.m_Bottom, + (float)m_InnerRect.m_Top, 10.0f); + break; + case qt3dsdm::GuideDirections::Vertical: + theInfo.m_Position = RoundToNearest((float)renderSpacePt.x, (float)m_InnerRect.m_Left, + (float)m_InnerRect.m_Right, 10.0f); + break; + default: + QT3DS_ASSERT(FALSE); + break; + break; + } + inEditor.EnsureEditor(QObject::tr("Drag Guide"), __FILE__, __LINE__).UpdateGuide(inGuide, + theInfo); + inEditor.FireImmediateRefresh(qt3dsdm::Qt3DSDMInstanceHandle()); +} + +bool STranslation::CheckGuideInPresentationRect(Qt3DSDMGuideHandle inGuide, + CUpdateableDocumentEditor &inEditor) +{ + qt3dsdm::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(inGuide); + bool inPresentation = false; + QT3DSF32 presHeight = (QT3DSF32)m_InnerRect.m_Top - (QT3DSF32)m_InnerRect.m_Bottom; + QT3DSF32 presWidth = (QT3DSF32)m_InnerRect.m_Right - (QT3DSF32)m_InnerRect.m_Left; + switch (theInfo.m_Direction) { + case qt3dsdm::GuideDirections::Horizontal: + inPresentation = 0.0f <= theInfo.m_Position && presHeight >= theInfo.m_Position; + break; + case qt3dsdm::GuideDirections::Vertical: + inPresentation = 0.0f <= theInfo.m_Position && presWidth >= theInfo.m_Position; + break; + default: + break; + } + + if (!inPresentation) + inEditor.EnsureEditor(QObject::tr("Delete Guide"), __FILE__, __LINE__).DeleteGuide(inGuide); + + return inPresentation; +} + +void STranslation::PerformPathDrag(qt3ds::studio::SPathPick &inPathPick, CPt inOriginalCoords, + CPt inPreviousMouseCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor) +{ + Option<SDragPreparationResult> thePrepResult(PrepareWidgetDrag( + qt3ds::widgets::StudioWidgetComponentIds::ZPlane, + qt3ds::widgets::StudioWidgetTypes::Translation, qt3ds::render::RenderWidgetModes::Local, + m_PathWidget->GetNode(), inOriginalCoords, inPreviousMouseCoords, inMouseCoords)); + if (!thePrepResult.hasValue()) + return; + + Option<QT3DSVec3> theOriginalPlaneCoords(thePrepResult->m_OriginalPlaneCoords); + Option<QT3DSVec3> theCurrentPlaneCoords(thePrepResult->m_CurrentPlaneCoords); + Option<QT3DSVec3> thePreviousPlaneCoords(thePrepResult->m_PreviousPlaneCoords); + if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) { + QT3DSVec3 theGlobalDiff = *theCurrentPlaneCoords - *theOriginalPlaneCoords; + QT3DSMat44 theGlobalInverse = thePrepResult->m_GlobalTransform.getInverse(); + QT3DSVec3 theCurrentPos = theGlobalInverse.transform(*theCurrentPlaneCoords); + QT3DSVec3 theOldPos = theGlobalInverse.transform(*theOriginalPlaneCoords); + QT3DSVec3 theDiff = theCurrentPos - theOldPos; + // Now find the anchor point; nontrivial. + SPathTranslator *theTranslator = + reinterpret_cast<SPathTranslator *>(thePrepResult->m_Node->m_UserData.m_UserData); + qt3dsdm::Qt3DSDMInstanceHandle thePathHandle = theTranslator->GetInstanceHandle(); + qt3dsdm::Qt3DSDMInstanceHandle theAnchorHandle = GetAnchorPoint(inPathPick); + + if (theAnchorHandle.Valid()) { + qt3dsdm::Qt3DSDMPropertyHandle thePosProperty = + m_ObjectDefinitions.m_PathAnchorPoint.m_Position.m_Property; + qt3dsdm::Qt3DSDMPropertyHandle theAngleProperty = + m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingAngle.m_Property; + qt3dsdm::Qt3DSDMPropertyHandle theIncomingDistanceProperty = + m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingDistance.m_Property; + qt3dsdm::Qt3DSDMPropertyHandle theOutgoingDistanceProperty = + m_ObjectDefinitions.m_PathAnchorPoint.m_OutgoingDistance.m_Property; + + IDocumentReader &theReader(m_Doc.GetDocumentReader()); + SFloat2 anchorPos = + *theReader.GetTypedInstancePropertyValue<SFloat2>(theAnchorHandle, thePosProperty); + QT3DSVec2 anchorPosVec = QT3DSVec2(anchorPos[0], anchorPos[1]); + if (m_LastPathDragValue.hasValue() == false) { + SPathAnchorDragInitialValue initialValue; + initialValue.m_Position = anchorPosVec; + initialValue.m_IncomingAngle = theReader.GetTypedInstancePropertyValue<float>( + theAnchorHandle, theAngleProperty); + initialValue.m_IncomingDistance = theReader.GetTypedInstancePropertyValue<float>( + theAnchorHandle, theIncomingDistanceProperty); + initialValue.m_OutgoingDistance = theReader.GetTypedInstancePropertyValue<float>( + theAnchorHandle, theOutgoingDistanceProperty); + m_LastPathDragValue = initialValue; + } + SPathAnchorDragInitialValue &lastValue(*m_LastPathDragValue); + QT3DSVec2 theCurrentValue; + switch (inPathPick.m_Property) { + case SPathPick::Anchor: + theCurrentValue = lastValue.m_Position; + break; + case SPathPick::IncomingControl: + theCurrentValue = qt3ds::render::IPathManagerCore::GetControlPointFromAngleDistance( + lastValue.m_Position, lastValue.m_IncomingAngle, lastValue.m_IncomingDistance); + break; + case SPathPick::OutgoingControl: + theCurrentValue = qt3ds::render::IPathManagerCore::GetControlPointFromAngleDistance( + lastValue.m_Position, lastValue.m_IncomingAngle + 180.0f, + lastValue.m_OutgoingDistance); + break; + default: + QT3DS_ASSERT(false); + break; + } + theCurrentValue[0] += theDiff.x; + theCurrentValue[1] += theDiff.y; + Q3DStudio::IDocumentEditor &theEditor = inEditor.EnsureEditor( + QObject::tr("Anchor Point Drag"), __FILE__, __LINE__); + switch (inPathPick.m_Property) { + case SPathPick::Anchor: + theEditor.SetInstancePropertyValue(theAnchorHandle, thePosProperty, + SFloat2(theCurrentValue.x, theCurrentValue.y)); + break; + case SPathPick::IncomingControl: { + QT3DSVec2 angleDistance = + qt3ds::render::IPathManagerCore::GetAngleDistanceFromControlPoint( + anchorPosVec, theCurrentValue); + float angleDiff = angleDistance.x - lastValue.m_IncomingAngle; + float minimalDiff = + qRadiansToDegrees(qt3ds::render::SRotationHelper::ToMinimalAngle( + qDegreesToRadians(angleDiff))); + float newAngle = lastValue.m_IncomingAngle + minimalDiff; + theEditor.SetInstancePropertyValue(theAnchorHandle, theAngleProperty, newAngle); + theEditor.SetInstancePropertyValue(theAnchorHandle, theIncomingDistanceProperty, + angleDistance.y); + } break; + case SPathPick::OutgoingControl: { + QT3DSVec2 angleDistance = + qt3ds::render::IPathManagerCore::GetAngleDistanceFromControlPoint( + anchorPosVec, theCurrentValue); + angleDistance.x += 180.0f; + float angleDiff = angleDistance.x - lastValue.m_IncomingAngle; + float minimalDiff = + qRadiansToDegrees(qt3ds::render::SRotationHelper::ToMinimalAngle( + qDegreesToRadians(angleDiff))); + float newAngle = lastValue.m_IncomingAngle + minimalDiff; + theEditor.SetInstancePropertyValue(theAnchorHandle, theAngleProperty, newAngle); + theEditor.SetInstancePropertyValue(theAnchorHandle, theOutgoingDistanceProperty, + angleDistance.y); + } break; + } + + inEditor.FireImmediateRefresh(m_AssetGraph.GetParent(theAnchorHandle)); + } + } +} + +SNode *STranslation::GetEditCameraLayer() +{ + if (m_EditCameraLayerTranslator) + return static_cast<SNode *>(&m_EditCameraLayerTranslator->GetGraphObject()); + return nullptr; +} + +SNode *STranslation::GetAxisHelperLayer() +{ + if (m_AxisHelperLayerTranslator) + return static_cast<SNode *>(&m_AxisHelperLayerTranslator->GetGraphObject()); + return nullptr; +} + +PickTargetAreas::Enum STranslation::GetPickArea(CPt inPoint) +{ + qt3ds::render::NVRenderRectF displayViewport = m_Context.GetDisplayViewport(); + QT3DSVec2 thePickPoint((QT3DSF32)inPoint.x, + m_Context.GetWindowDimensions().height() - (QT3DSF32)inPoint.y); + QT3DSF32 left = displayViewport.m_X; + QT3DSF32 right = displayViewport.m_X + displayViewport.m_Width; + QT3DSF32 top = displayViewport.m_Y + displayViewport.m_Height; + QT3DSF32 bottom = displayViewport.m_Y; + if (thePickPoint.x < left || thePickPoint.x > right || thePickPoint.y < bottom + || thePickPoint.y > top) + return PickTargetAreas::Matte; + return PickTargetAreas::Presentation; +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.h b/src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.h new file mode 100644 index 00000000..ed7ea70d --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.h @@ -0,0 +1,732 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_RENDERER_TRANSLATION_H +#define QT3DS_STUDIO_RENDERER_TRANSLATION_H +#pragma once +#include "StudioRendererImpl.h" +#include "Qt3DSRenderLayer.h" +#include "Qt3DSRenderer.h" +#include "StudioWidget.h" +#include "render/Qt3DSRenderTexture2D.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "foundation/FastAllocator.h" +#include "StudioPickValues.h" +#include "Qt3DSDMGuides.h" +#include "PathWidget.h" +#include "StudioPreferences.h" +#include "StudioGradientWidget.h" +#include "StudioVisualAidWidget.h" +#include "StudioHelperGridWidget.h" + +namespace qt3ds { +namespace studio { + struct SGraphObjectTranslator; + extern QT3DSU32 g_GraphObjectTranslatorTag; + inline void InitializePointerTags(IStringTable &) { g_GraphObjectTranslatorTag = 0x0088BEEF; } +} +} +namespace qt3ds { +namespace render { + template <> + struct SPointerTag<qt3ds::studio::SGraphObjectTranslator> + { + static QT3DSU32 GetTag() { return qt3ds::studio::g_GraphObjectTranslatorTag; } + }; +} +} + +namespace qt3ds { +namespace studio { + + typedef std::shared_ptr<qt3dsdm::ISignalConnection> TSignalConnection; + + struct STranslation; + + struct SGraphObjectTranslator + { + protected: + qt3dsdm::Qt3DSDMInstanceHandle m_InstanceHandle; + + public: + // This will never be null. The reason it is a pointer is because + // alias translators need to switch which graph object they point to + qt3dsdm::Qt3DSDMInstanceHandle m_AliasInstanceHandle; + SGraphObject *m_GraphObject; + QT3DSU32 m_DirtyIndex; + SGraphObjectTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, SGraphObject &inObj) + : m_InstanceHandle(inInstance) + , m_GraphObject(&inObj) + , m_DirtyIndex(QT3DS_MAX_U32) + { + m_GraphObject->m_UserData = qt3ds::render::STaggedPointer(this); + } + // The destructors will not be called at this time for most of the objects + // but they will be released. + virtual ~SGraphObjectTranslator() {} + // Push new data into the UIC render graph. + virtual void PushTranslation(STranslation &inTranslatorContext); + virtual void AfterRenderGraphIsBuilt(STranslation &) {} + virtual void SetActive(bool inActive) = 0; + virtual void ClearChildren() = 0; + virtual void AppendChild(SGraphObject &inChild) = 0; + virtual void ResetEffect() {} + virtual const QString GetError() { return {}; } + virtual void SetError(const QString &error) { Q_UNUSED(error); } + virtual SGraphObject &GetGraphObject() { return *m_GraphObject; } + virtual SGraphObject &GetNonAliasedGraphObject() { return *m_GraphObject; } + virtual qt3dsdm::Qt3DSDMInstanceHandle GetInstanceHandle() { return m_InstanceHandle; } + virtual qt3dsdm::Qt3DSDMInstanceHandle GetSceneGraphInstanceHandle() + { + return m_InstanceHandle; + } + virtual qt3dsdm::Qt3DSDMInstanceHandle GetPossiblyAliasedInstanceHandle() + { + if (m_AliasInstanceHandle.Valid()) + return m_AliasInstanceHandle; + return GetInstanceHandle(); + } + }; + + struct STranslatorGetDirty + { + QT3DSU32 operator()(const SGraphObjectTranslator &inTrans) const + { + return inTrans.m_DirtyIndex; + } + }; + struct STranslatorSetDirty + { + void operator()(SGraphObjectTranslator &inTrans, QT3DSU32 idx) const + { + inTrans.m_DirtyIndex = idx; + } + }; + + typedef InvasiveSet<SGraphObjectTranslator, STranslatorGetDirty, STranslatorSetDirty> + TTranslatorDirtySet; + + struct TranslationSelectMode + { + enum Enum { + Group = 0, + Single = 1, + NestedComponentSingle, + }; + }; + + struct EditCameraTypes + { + enum Enum { + SceneCamera = 0, + Perspective, + Orthographic, + Directional, + }; + }; + + const QT3DSF32 g_EditCameraFOV = 45.0f; + const QT3DSF32 g_RotationScaleFactor = 2.0f * QT3DSF32(M_PI) / 180.0f; + + struct SEditorCameraInformation + { + QT3DSVec3 m_Position; + QT3DSVec3 m_Direction; + QT3DSF32 m_ViewRadius; + EditCameraTypes::Enum m_CameraType; + NVReal m_xRotation = 0.0f; + NVReal m_yRotation = 0.0f; + SEditorCameraInformation() + : m_Position(0, 0, 0) + , m_Direction(0, 0, 0) + , m_ViewRadius(600) + , m_CameraType(EditCameraTypes::Perspective) + { + } + + void ApplyToCamera(SCamera &inCamera, QT3DSVec2 inViewport, bool noScale = false) + { + // Setup shared default values. + inCamera.m_ClipFar = 2000000.0f; + inCamera.m_ClipNear = 1.0f; + + // In orthographic view adjust near clipping based on zoom level + if (m_CameraType == EditCameraTypes::Orthographic) { + // This seems like a suitable math + inCamera.m_ClipNear = (100000 / m_ViewRadius) - (50 * m_ViewRadius); + } + + if (m_CameraType == EditCameraTypes::Perspective) { + inCamera.m_FOV = g_EditCameraFOV; + TORAD(inCamera.m_FOV); + inCamera.m_Flags.SetOrthographic(false); + } else { + inCamera.m_Flags.SetOrthographic(true); + } + + // The goal is to setup a global transform that + QT3DSMat44 thePivotMatrix = QT3DSMat44::createIdentity(); + thePivotMatrix.column3.x = m_Position.x; + thePivotMatrix.column3.y = m_Position.y; + thePivotMatrix.column3.z = m_Position.z; + QT3DSMat44 theGlobalTransform = thePivotMatrix; + + QT3DSVec3 theUpDir(0, 1, 0); + if (m_CameraType == EditCameraTypes::Directional) { + QT3DSF32 theTestLen = m_Direction.cross(theUpDir).magnitudeSquared(); + if (theTestLen < .01f) + theUpDir = QT3DSVec3(0, 0, 1) * m_Direction.dot(QT3DSVec3(0, 1, 0)); + theUpDir.normalize(); + } + + QT3DSMat33 theLookAtMatrix = inCamera.GetLookAtMatrix(theUpDir, m_Direction); + QT3DSMat44 theRotationTransform = + QT3DSMat44(theLookAtMatrix.column0, theLookAtMatrix.column1, + theLookAtMatrix.column2, QT3DSVec3(0, 0, 0)); + + if (m_CameraType != EditCameraTypes::Directional) { + theRotationTransform.rotate(-m_xRotation, QT3DSVec3(0.0f, 1.0f, 0.0f)); + theRotationTransform.rotate(m_yRotation, QT3DSVec3(1.0f, 0.0f, 0.0f)); + inCamera.m_Rotation = inCamera.GetRotationVectorFromRotationMatrix(theRotationTransform.getUpper3x3()); + } + + // The view radius dictates the zoom. + QT3DSF32 theZoom = 1.0f; + if (inCamera.m_Flags.IsOrthographic()) { + QT3DSF32 theViewport = qMin(inViewport.x, inViewport.y); + theZoom = (m_ViewRadius * 2.0f) / theViewport; + } else { + // We know the hypotenuse is 600. + // So if we want to zoom the scene, we do this. + theZoom = m_ViewRadius / (sinf(inCamera.m_FOV / 2.0f) * 600.f); + } + QT3DSMat44 theScaleMatrix = QT3DSMat44(QT3DSVec4(theZoom, theZoom, theZoom, 1)); + QT3DSMat44 thePositionMatrix = QT3DSMat44::createIdentity(); + thePositionMatrix.column3.x = m_Position.x; + thePositionMatrix.column3.y = m_Position.y; + thePositionMatrix.column3.z = m_Position.z + 600; + theGlobalTransform = theGlobalTransform * theRotationTransform; + if (!noScale) + theGlobalTransform = theGlobalTransform * theScaleMatrix; + theGlobalTransform = theGlobalTransform * thePivotMatrix.getInverse(); + theGlobalTransform = theGlobalTransform * thePositionMatrix; + // This works because the camera has no hierarchy. + inCamera.m_LocalTransform = theGlobalTransform; + inCamera.m_Flags.SetTransformDirty(false); + inCamera.MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformNotDirty); + } + + bool IsOrthographic() const { return m_CameraType != EditCameraTypes::Perspective; } + + bool SupportsRotation() const { return m_CameraType != EditCameraTypes::Directional; } + }; + struct MovementTypes + { + enum Enum { + Unknown = 0, + Translate, + TranslateAlongCameraDirection, + Scale, + ScaleZ, + Rotation, + RotationAboutCameraDirection, + }; + }; + + struct SEditorLayerTranslator; + struct SZoomRender + { + CPt m_Point; + qt3ds::render::SLayer *m_Layer; + SZoomRender(CPt inPoint, qt3ds::render::SLayer *inLayer) + : m_Point(inPoint) + , m_Layer(inLayer) + { + } + SZoomRender() + : m_Layer(nullptr) + { + } + }; + + struct PickTargetAreas + { + enum Enum { + Presentation, + Matte, + }; + }; + + struct SRulerRect + { + QT3DSI32 m_Left; + QT3DSI32 m_Top; + QT3DSI32 m_Right; + QT3DSI32 m_Bottom; + SRulerRect() + : m_Left(0) + , m_Top(0) + , m_Right(0) + , m_Bottom(0) + { + } + SRulerRect(QT3DSI32 l, QT3DSI32 t, QT3DSI32 r, QT3DSI32 b) + : m_Left(l) + , m_Top(t) + , m_Right(r) + , m_Bottom(b) + { + } + + bool Contains(QT3DSI32 x, QT3DSI32 y) const + { + return x >= m_Left && x <= m_Right && y >= m_Bottom && y <= m_Top; + } + + bool isNull() const + { + return m_Left == 0 && m_Top == 0 && m_Right == 0 && m_Bottom == 0; + } + }; + + struct SDragPreparationResult + { + qt3ds::render::IQt3DSRenderer *m_Renderer; + SNode *m_Node; + SLayer *m_Layer; + SCamera *m_Camera; + qt3ds::render::NVPlane m_Plane; + QT3DSVec3 m_GlobalPos; + QT3DSVec3 m_CameraGlobalPos; + QT3DSVec3 m_CameraDirection; + QT3DSVec3 m_Axis; + QT3DSMat44 m_GlobalTransform; + QT3DSMat33 m_NormalMatrix; + QT3DSU32 m_AxisIndex; + qt3ds::widgets::StudioWidgetComponentIds::Enum m_ComponentId; + qt3ds::widgets::StudioWidgetTypes::Enum m_WidgetType; + qt3ds::render::RenderWidgetModes::Enum m_WidgetMode; + SRay m_OriginalRay; + SRay m_CurrentRay; + SRay m_PreviousRay; + Option<QT3DSVec3> m_OriginalPlaneCoords; + Option<QT3DSVec3> m_CurrentPlaneCoords; + Option<QT3DSVec3> m_PreviousPlaneCoords; + bool m_IsPlane; + bool m_isBehindCamera; + SDragPreparationResult() {} + }; + + struct SPathAnchorDragInitialValue + { + QT3DSVec2 m_Position; + float m_IncomingAngle; + float m_IncomingDistance; + float m_OutgoingDistance; + SPathAnchorDragInitialValue() {} + }; + + struct STranslation : public qt3ds::render::IQt3DSRenderNodeFilter + { + typedef eastl::pair<qt3dsdm::Qt3DSDMInstanceHandle, SGraphObjectTranslator *> + THandleTranslatorPair; + typedef eastl::vector<THandleTranslatorPair> THandleTranslatorPairList; + // Now that we have aliases, one instance handle can map to several translators. One + // translator, however, only + // maps to one instance handle. + typedef nvhash_map<qt3dsdm::Qt3DSDMInstanceHandle, THandleTranslatorPairList, eastl::hash<int>> + TInstanceToTranslatorMap; + IStudioRenderer &m_Renderer; + IQt3DSRenderContext &m_Context; + CDoc &m_Doc; + IDocumentReader &m_Reader; + SComposerObjectDefinitions &m_ObjectDefinitions; + qt3dsdm::CStudioSystem &m_StudioSystem; + qt3dsdm::CStudioFullSystem &m_FullSystem; + Q3DStudio::CGraph &m_AssetGraph; + + // allocator for scene graph and translators + qt3ds::foundation::SSAutoDeallocatorAllocator m_Allocator; + // All translator related containers must come after the allocator + TInstanceToTranslatorMap m_TranslatorMap; + TTranslatorDirtySet m_DirtySet; + qt3ds::render::SPresentation m_Presentation; + qt3ds::render::SScene *m_Scene; + Q3DStudio::CGraphIterator m_GraphIterator; + nvvector<TSignalConnection> m_SignalConnections; + QT3DSI32 m_ComponentTimeDepth; + SNode m_MouseDownNode; + SCamera m_MouseDownCamera; + Option<QT3DSMat44> m_MouseDownParentGlobalTransformInverse; + Option<QT3DSMat33> m_MouseDownParentRotationInverse; + Option<QT3DSMat33> m_MouseDownGlobalRotation; + QT3DSI32 m_KeyRepeat; + bool m_EditCameraEnabled; + bool m_EditLightEnabled; + bool m_helperGridEnabled = true; + bool m_axisHelperEnabled = true; + SEditorCameraInformation m_EditCameraInfo; + SCamera m_EditCamera; + SCamera m_AxisCamera; + SLight m_EditLight; + QT3DSVec2 m_Viewport; + SEditorLayerTranslator *m_EditCameraLayerTranslator; + SEditorLayerTranslator *m_AxisHelperLayerTranslator; + Option<SZoomRender> m_ZoomRender; + NVScopedRefCounted<qt3ds::widgets::IStudioWidget> m_TranslationWidget; + NVScopedRefCounted<qt3ds::widgets::IStudioWidget> m_AxisHelperWidget; + NVScopedRefCounted<qt3ds::widgets::IStudioWidget> m_RotationWidget; + NVScopedRefCounted<qt3ds::widgets::IStudioWidget> m_ScaleWidget; + NVScopedRefCounted<qt3ds::widgets::IStudioWidget> m_LastRenderedWidget; + NVScopedRefCounted<qt3ds::widgets::SGradientWidget> m_GradientWidget; + NVScopedRefCounted<qt3ds::widgets::SVisualAidWidget> m_VisualAidWidget; + NVScopedRefCounted<qt3ds::widgets::SHelperGridWidget> m_helperGridWidget; + + NVScopedRefCounted<qt3ds::widgets::IPathWidget> m_PathWidget; + NVScopedRefCounted<qt3ds::render::NVRenderTexture2D> m_PickBuffer; + Option<SPathAnchorDragInitialValue> m_LastPathDragValue; + nvvector<qt3ds::QT3DSU8> m_PixelBuffer; + nvvector<SGraphObjectTranslator *> m_editModeCamerasAndLights; + QT3DSF32 m_CumulativeRotation; + eastl::vector<qt3ds::render::SPGGraphObject *> m_GuideContainer; + qt3ds::foundation::SFastAllocator<> m_GuideAllocator; + // The rects are maintained from last render because the render context + // doesn't guarantee the rects it returns are valid outside of begin/end render calls. + SRulerRect m_OuterRect; + SRulerRect m_InnerRect; // presentation rect. + + QT3DSVec4 m_rectColor; + QT3DSVec4 m_lineColor; + QT3DSVec4 m_guideColor; + QT3DSVec4 m_selectedGuideColor; + QT3DSVec4 m_guideFillColor; + QT3DSVec4 m_selectedGuideFillColor; + + NVScopedRefCounted<qt3ds::render::NVRenderTexture2D> m_previewTexture; + NVScopedRefCounted<qt3ds::render::NVRenderFrameBuffer> m_previewFbo; + NVScopedRefCounted<qt3ds::render::NVRenderRenderBuffer> m_previewRenderBuffer; + QT3DSVec2 m_previewFboDimensions; + + STranslation(IStudioRenderer &inRenderer, IQt3DSRenderContext &inContext); + void MarkBeginComponentTime(qt3dsdm::Qt3DSDMSlideHandle) { ++m_ComponentTimeDepth; } + + void MarkComponentTime(qt3dsdm::Qt3DSDMSlideHandle) + { + m_ComponentTimeDepth = qMax(0, m_ComponentTimeDepth - 1); + if (m_ComponentTimeDepth == 0) + RequestRender(); + } + + void ReleaseTranslation(Q3DStudio::TIdentifier inInstance); + + void MarkGraphInstanceDirty(Q3DStudio::TIdentifier inInstance, + Q3DStudio::TIdentifier /*inParent*/) + { + MarkDirty(inInstance); + } + + void MarkDirty(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + + void DoMarkDirty(qt3dsdm::Qt3DSDMInstanceHandle inInstance) {MarkDirty(inInstance);} + + void MarkDirty(qt3dsdm::Qt3DSDMInstanceHandle *inInstance, long inInstanceCount) + { + for (long idx = 0; idx < inInstanceCount; ++idx) + MarkDirty(inInstance[idx]); + } + + void DrawBoundingBox(SNode &inNode, QT3DSVec3 inColor); + + void DrawChildBoundingBoxes(SNode &inNode) + { + ::CColor color = CStudioPreferences::groupBoundingBoxColor(); + QT3DSVec3 colorVec(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f); + for (SNode *theChild = inNode.m_FirstChild; theChild; + theChild = theChild->m_NextSibling) { + if (IncludeNode(*theChild)) + DrawBoundingBox(*theChild, colorVec); + } + } + + void DrawGroupBoundingBoxes(SGraphObjectTranslator &inTranslator) + { + SNode &theNode = static_cast<SNode &>(inTranslator.GetGraphObject()); + if (theNode.m_FirstChild) { + ::CColor color = CStudioPreferences::groupBoundingBoxColor(); + QT3DSVec3 colorVec(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f); + DrawBoundingBox(theNode, colorVec); + if (inTranslator.GetGraphObject().m_Type != GraphObjectTypes::Layer) + DrawChildBoundingBoxes(theNode); + } + } + + void DrawNonGroupBoundingBoxes(SGraphObjectTranslator &inTranslator) + { + SNode &theNode = static_cast<SNode &>(inTranslator.GetGraphObject()); + if (inTranslator.GetGraphObject().m_Type != GraphObjectTypes::Layer) { + ::CColor color = CStudioPreferences::singleBoundingBoxColor(); + QT3DSVec3 colorVec(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f); + DrawBoundingBox(theNode, colorVec); + DrawChildBoundingBoxes(theNode); + } else { + ::CColor color = CStudioPreferences::singleBoundingBoxColor(); + QT3DSVec3 colorVec(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f); + m_Context.GetRenderer().RenderLayerRect( + static_cast<SLayer &>(inTranslator.GetGraphObject()), colorVec); + } + } + + void drawPivot(SGraphObjectTranslator &inTranslator); + + void SetViewport(QT3DSF32 inWidth, QT3DSF32 inHeight); + + QT3DSVec2 GetViewportDimensions() const { return m_Viewport; } + QT3DSVec2 GetPreviewViewportDimensions() const; + qt3ds::render::NVRenderRect GetPreviewViewport() const; + + void ClearDirtySet() + { + // The dirty set may be modified while this operation is taking place in the case of + // alias nodes. + for (qt3ds::QT3DSU32 idx = 0; idx < (qt3ds::QT3DSU32)m_DirtySet.size(); ++idx) { + if (m_Reader.IsInstance(m_DirtySet[idx]->GetInstanceHandle())) + m_DirtySet[idx]->PushTranslation(*this); + } + m_DirtySet.clear(); + } + // We build the render graph every time we render. This may seem wasteful + void BuildRenderGraph(qt3dsdm::Qt3DSDMInstanceHandle inParent, bool scenePreviewPass, + Qt3DSDMInstanceHandle inAliasHandle + = qt3dsdm::Qt3DSDMInstanceHandle()); + void BuildRenderGraph(SGraphObjectTranslator &inParent, bool scenePreviewPass, + qt3dsdm::Qt3DSDMInstanceHandle inAliasHandle + = qt3dsdm::Qt3DSDMInstanceHandle()); + void + DeactivateScan(SGraphObjectTranslator &inParent, + qt3dsdm::Qt3DSDMInstanceHandle inAliasHandle = qt3dsdm::Qt3DSDMInstanceHandle()); + void PreRender(bool scenePreviewPass); + void Render(int inWidgetId, bool inDrawGuides, bool scenePreviewPass); + void EndRender(); + void DoPrepareForDrag(SNode *inSelectedNode); + void ResetWidgets(); + void EndDrag(); + bool IsPathWidgetActive(); + + void PrepareForDrag() { DoPrepareForDrag(GetSelectedNode()); } + + SStudioPickValue Pick(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode, + bool ignoreWidgets = false); + Option<QT3DSU32> PickWidget(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode, + qt3ds::widgets::IStudioWidgetBase &inWidget); + + qt3ds::foundation::Option<qt3dsdm::SGuideInfo> PickRulers(CPt inMouseCoords); + + SNode *GetSelectedNode() + { + qt3dsdm::Qt3DSDMInstanceHandle theHandle = m_Doc.GetSelectedInstance(); + SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theHandle); + if (theTranslator + && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type)) + return static_cast<SNode *>(&theTranslator->GetGraphObject()); + return nullptr; + } + static inline SFloat3 ToDataModel(const QT3DSVec3 &inValue) + { + return SFloat3(inValue.x, inValue.y, inValue.z); + } + + static inline SFloat3 ToDataModelRotation(const QT3DSVec3 &inValue) + { + SFloat3 retval = ToDataModel(inValue); + TODEG(retval.m_Floats[0]); + TODEG(retval.m_Floats[1]); + TODEG(retval.m_Floats[2]); + return retval; + } + + void SetPosition(const QT3DSVec3 &inPosition, CUpdateableDocumentEditor &inEditor) + { + inEditor.EnsureEditor(QObject::tr("Set Position"), __FILE__, __LINE__) + .SetInstancePropertyValue(m_Doc.GetSelectedInstance(), + m_ObjectDefinitions.m_Node.m_Position, + ToDataModel(inPosition)); + inEditor.FireImmediateRefresh(m_Doc.GetSelectedInstance()); + } + void SetRotation(const QT3DSVec3 &inRotation, CUpdateableDocumentEditor &inEditor) + { + inEditor.EnsureEditor(QObject::tr("Set Rotation"), __FILE__, __LINE__) + .SetInstancePropertyValue(m_Doc.GetSelectedInstance(), + m_ObjectDefinitions.m_Node.m_Rotation, + ToDataModelRotation(inRotation)); + inEditor.FireImmediateRefresh(m_Doc.GetSelectedInstance()); + } + void SetScale(const QT3DSVec3 &inScale, CUpdateableDocumentEditor &inEditor) + { + inEditor.EnsureEditor(QObject::tr("Set Scale"), __FILE__, __LINE__) + .SetInstancePropertyValue(m_Doc.GetSelectedInstance(), + m_ObjectDefinitions.m_Node.m_Scale, ToDataModel(inScale)); + inEditor.FireImmediateRefresh(m_Doc.GetSelectedInstance()); + } + + QT3DSVec3 GetIntendedPosition(qt3dsdm::Qt3DSDMInstanceHandle inInstance, CPt inPos); + + void ApplyPositionalChange(QT3DSVec3 inDiff, SNode &inNode, + CUpdateableDocumentEditor &inEditor); + + void TranslateSelectedInstanceAlongCameraDirection(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor); + + void TranslateSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor, bool inLockToAxis); + + void ScaleSelectedInstanceZ(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor); + + void ScaleSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor); + + void CalculateNodeGlobalRotation(SNode &inNode); + + void ApplyRotationToSelectedInstance(const QT3DSQuat &inFinalRotation, SNode &inNode, + CUpdateableDocumentEditor &inEditor, + bool inIsMouseRelative = true); + + void RotateSelectedInstanceAboutCameraDirectionVector(CPt inPreviousMouseCoords, + CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor); + + // This method never feels right to me. It is difficult to apply it to a single axis (of + // course for that + // you can use the inspector palette). + void RotateSelectedInstance(CPt inOriginalCoords, CPt inPreviousMouseCoords, + CPt inMouseCoords, CUpdateableDocumentEditor &inEditor, + bool inLockToAxis); + + Option<SDragPreparationResult> + PrepareWidgetDrag(qt3ds::widgets::StudioWidgetComponentIds::Enum inComponentId, + qt3ds::widgets::StudioWidgetTypes::Enum inWidgetId, + qt3ds::render::RenderWidgetModes::Enum inWidgetMode, SNode &inNode, + CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords); + + void PerformWidgetDrag(int inWidgetSubComponent, CPt inOriginalCoords, + CPt inPreviousMouseCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor); + + void PerformGuideDrag(Qt3DSDMGuideHandle inGuide, CPt inPoint, + CUpdateableDocumentEditor &inEditor); + bool CheckGuideInPresentationRect(Qt3DSDMGuideHandle inGuide, + CUpdateableDocumentEditor &inEditor); + + void PerformPathDrag(qt3ds::studio::SPathPick &inPathPick, CPt inOriginalCoords, + CPt inPreviousMouseCoords, CPt inMouseCoords, + CUpdateableDocumentEditor &inEditor); + + void RequestRender() + { + if (m_ComponentTimeDepth == 0) + m_Renderer.RequestRender(); + } + + void RenderZoomRender(SZoomRender &inRender); + + // IQt3DSRenderNodeFilter + bool IncludeNode(const SNode &inNode) override; + + PickTargetAreas::Enum GetPickArea(CPt inPoint); + + SNode *GetEditCameraLayer(); + SNode *GetAxisHelperLayer(); + + void ReleaseEffect(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + void releaseMaterial(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + // Create a new translator for this type. Do not add to any maps or anything else. + SGraphObjectTranslator *CreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + // Returns the canonical translator for a given instance or creates a new translator if none + // exist. + SGraphObjectTranslator *GetOrCreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + // Create a new aliased translator for this type. + SGraphObjectTranslator *GetOrCreateTranslator(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMInstanceHandle inAliasInstance); + THandleTranslatorPairList & + GetTranslatorsForInstance(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + qt3dsdm::Qt3DSDMInstanceHandle GetAnchorPoint(SPathPick &inPick); + qt3dsdm::Qt3DSDMInstanceHandle GetAnchorPoint(QT3DSU32 inAnchorIndex); + + void updateHelperGridFromSettings(); + void updateAxisHelperFromSettings(); + }; + + struct SDisableUseClearColor + { + SGraphObjectTranslator *m_SceneTranslator; + bool m_PreviousUseClearColor; + bool m_DisableUseClearColor; + + SDisableUseClearColor(STranslation &inTranslation, bool disableUseClearColor) + : m_SceneTranslator(nullptr) + , m_PreviousUseClearColor(false) + , m_DisableUseClearColor(disableUseClearColor) + { + if (m_DisableUseClearColor) { + TIdentifier theRoot = inTranslation.m_AssetGraph.GetRoot(0); + m_SceneTranslator = inTranslation.GetOrCreateTranslator(theRoot); + if (m_SceneTranslator) { + SScene &theScene = static_cast<SScene &>(m_SceneTranslator->GetGraphObject()); + m_PreviousUseClearColor = theScene.m_UseClearColor; + SetUseClearColor(false); + } + } + } + + ~SDisableUseClearColor() + { + if (m_DisableUseClearColor) { + SetUseClearColor(m_PreviousUseClearColor); + } + } + + void SetUseClearColor(bool inUseClearColor) + { + if (m_SceneTranslator) { + SScene &theScene = static_cast<SScene &>(m_SceneTranslator->GetGraphObject()); + theScene.m_UseClearColor = inUseClearColor; + } + } + }; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/StudioRotationWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioRotationWidget.cpp new file mode 100644 index 00000000..a44bf9b7 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioRotationWidget.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioWidgetImpl.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderCamera.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "StudioUtils.h" +#include "StudioPreferences.h" + +using namespace qt3ds::widgets; + +namespace { + +struct SRotationWidget : public SStudioWidgetImpl<StudioWidgetTypes::Rotation> +{ + typedef SStudioWidgetImpl<StudioWidgetTypes::Rotation> TBase; + NVRenderInputAssembler *m_XAxis; + NVRenderInputAssembler *m_YAxis; + NVRenderInputAssembler *m_ZAxis; + NVRenderInputAssembler *m_CameraAxis; + // We use a rect to clear the Z buffer. + NVRenderInputAssembler *m_CameraRect; + + NVRenderShaderProgram *m_ZClearShader; + + volatile QT3DSI32 mRefCount; + + SRotationWidget(NVAllocatorCallback &inAlloc) + : TBase(inAlloc) + , m_XAxis(nullptr) + , m_YAxis(nullptr) + , m_ZAxis(nullptr) + , m_CameraAxis(nullptr) + , m_CameraRect(nullptr) + , m_ZClearShader(nullptr) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator) + + NVRenderInputAssembler *CreateRing(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, QT3DSVec3 inDirection, + QT3DSF32 inInnerRadius, QT3DSF32 inOuterRadius, QT3DSF32 inRingColor, + const char *inRingName) + { + QT3DS_ASSERT(inInnerRadius <= inOuterRadius); + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inRingName); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) { + return retval; + } + + TResultVecType theVertexData(m_Allocator, "SRotationWidget::theVertexData"); + + QT3DSI32 numSubDivisions = 50; + QT3DSF32 arcRad = 360.0f / (QT3DSF32)numSubDivisions; + TORAD(arcRad); + QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0)); + if (tempCross.magnitudeSquared() < .05f) + tempCross = inDirection.cross(QT3DSVec3(1, 0, 0)); + + QT3DSVec3 upDir = inDirection.cross(tempCross); + QT3DSVec3 leftDir = upDir.cross(inDirection); + upDir.normalize(); + leftDir.normalize(); + + QT3DSF32 ringWidth = inOuterRadius - inInnerRadius; + QT3DSF32 ringHalfWidth = ringWidth / 2.0f; + QT3DSF32 middleRadius = inInnerRadius + ringHalfWidth; + + for (QT3DSI32 idx = 0, numLooper = numSubDivisions; idx < numLooper; ++idx) { + QT3DSF32 startDeg = idx * 360.0f / numSubDivisions; + QT3DSF32 endDeg = (idx + 1) * 360.0f / numSubDivisions; + QT3DSF32 startRad(startDeg); + QT3DSF32 endRad(endDeg); + TORAD(startRad); + TORAD(endRad); + QT3DSF32 startSin = NVSin(startRad); + QT3DSF32 endSin = NVSin(endRad); + QT3DSF32 startCos = NVCos(startRad); + QT3DSF32 endCos = NVCos(endRad); + + QT3DSVec3 startDir = startSin * upDir + startCos * leftDir; + QT3DSVec3 endDir = endSin * upDir + endCos * leftDir; + + QT3DSVec3 discStart = startDir * inInnerRadius; + QT3DSVec3 discEnd = endDir * inInnerRadius; + QT3DSVec3 ringStart = startDir * inOuterRadius; + QT3DSVec3 ringEnd = endDir * inOuterRadius; + + QT3DSVec3 middleStart = startDir * (middleRadius); + QT3DSVec3 middleEnd = endDir * (middleRadius); + QT3DSVec3 middleTopLeft = middleStart + inDirection * ringHalfWidth; + QT3DSVec3 middleTopRight = middleStart - inDirection * ringHalfWidth; + QT3DSVec3 middleBottomLeft = middleEnd + inDirection * ringHalfWidth; + QT3DSVec3 middleBottomRight = middleEnd - inDirection * ringHalfWidth; + + // Now two tris for the ring + theVertexData.push_back(QT3DSVec4(discStart, inRingColor)); + theVertexData.push_back(QT3DSVec4(ringStart, inRingColor)); + theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor)); + theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor)); + theVertexData.push_back(QT3DSVec4(discEnd, inRingColor)); + theVertexData.push_back(QT3DSVec4(discStart, inRingColor)); + // Two tris for the ring that is perpendicular to the viewer + theVertexData.push_back(QT3DSVec4(middleTopLeft, inRingColor)); + theVertexData.push_back(QT3DSVec4(middleTopRight, inRingColor)); + theVertexData.push_back(QT3DSVec4(middleBottomRight, inRingColor)); + theVertexData.push_back(QT3DSVec4(middleBottomRight, inRingColor)); + theVertexData.push_back(QT3DSVec4(middleBottomLeft, inRingColor)); + theVertexData.push_back(QT3DSVec4(middleTopLeft, inRingColor)); + } + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + IStudioWidget::GetVertexBufferAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; + } + + NVRenderInputAssembler *CreateRect(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, QT3DSVec3 inDirection, + QT3DSF32 inHalfWidth, const char *inItemName) + { + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inItemName); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) { + return retval; + } + + QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0)); + if (tempCross.magnitudeSquared() < .05f) + tempCross = inDirection.cross(QT3DSVec3(1, 0, 0)); + + QT3DSVec3 upDir = inDirection.cross(tempCross); + QT3DSVec3 leftDir = upDir.cross(inDirection); + + TResultVecType theVertexData(m_Allocator, "SRotationWidget::theVertexData"); + + theVertexData.push_back(QT3DSVec4(upDir * inHalfWidth, 0.0f)); + theVertexData.push_back(QT3DSVec4(leftDir * inHalfWidth, 0.0f)); + theVertexData.push_back(QT3DSVec4(upDir * -1.0f * inHalfWidth, 0.0f)); + + theVertexData.push_back(QT3DSVec4(upDir * -1.0f * inHalfWidth, 0.0f)); + theVertexData.push_back(QT3DSVec4(leftDir * -1.0f * inHalfWidth, 0.0f)); + theVertexData.push_back(QT3DSVec4(upDir * inHalfWidth, 0.0f)); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + IStudioWidget::GetVertexBufferAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + return retval; + } + + NVRenderShaderProgram *CreateZClearShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, + const char *inItemName) + { + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inItemName); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(theItemName); + if (retval) { + return retval; + } + + qt3ds::render::IShaderProgramGenerator &theGenerator(inWidgetContext.GetProgramGenerator()); + theGenerator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &theVertexGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &theFragmentGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddUniform("model_view_projection", "mat4"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + theVertexGenerator.Append("}"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tgl_FragColor.rgb = vec3(0.0, 0.0, 0.0);"); + theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + theFragmentGenerator.Append("}"); + return inWidgetContext.CompileAndStoreShader(theItemName); + } + + static inline QT3DSVec3 ToFixedCameraPos(const QT3DSVec3 &inCameraPos, const SCamera &inCamera) + { + if (inCamera.m_Flags.IsOrthographic()) { + return QT3DSVec3(inCameraPos.x, inCameraPos.y, -600.f); + } + QT3DSF32 multiplier = -600.f / inCameraPos.z; + return inCameraPos * multiplier; + } + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override + { + // Widgets have to clear the depth buffer; they shouldn't interact with other components + // but they should self-occlude. + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + inRenderContext.SetBlendFunction( + qt3ds::render::NVRenderBlendFunctionArgument( + qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha, + qt3ds::render::NVRenderSrcBlendFunc::One, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha)); + inRenderContext.SetBlendEquation( + qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + + float pixelRatio = float(StudioUtils::devicePixelRatio()); + QT3DSF32 theRingRadius = 2 * CStudioPreferences::selectorLineLength() * pixelRatio; + QT3DSF32 theRingWidth = CStudioPreferences::selectorLineWidth() * pixelRatio; + QT3DSF32 theRingInner = theRingRadius; + QT3DSF32 theRingOuter = theRingRadius + theRingWidth; + if (m_XAxis == nullptr) { + TBase::SetupRender(inWidgetContext, inRenderContext); + m_PickShader = IStudioWidget::CreateWidgetPickShader(inWidgetContext, inRenderContext); + m_XAxis = CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(-1, 0, 0), + theRingInner, theRingOuter, 0.0f, "RotationWidgetXAxis"); + m_YAxis = CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(0, -1, 0), + theRingInner, theRingOuter, 0.0f, "RotationWidgetYAxis"); + m_ZAxis = CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), + theRingInner, theRingOuter, 0.0f, "RotationWidgetZAxis"); + m_CameraAxis = + CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), + theRingInner + 5, theRingOuter + 5, 0.0f, + "RotationWidgetCameraAxis"); + m_CameraRect = CreateRect(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), 200.0f, + "RotationWidgetZClear"); + m_ZClearShader = + CreateZClearShader(inWidgetContext, inRenderContext, "RotationWidgetZClear"); + } + QT3DSVec3 theXColor(GetXAxisColor()); + QT3DSVec3 theYColor(GetYAxisColor()); + QT3DSVec3 theZColor(GetZAxisColor()); + QT3DSVec3 theRingColor(QT3DSVec3(.8f, .8f, .8f)); + + QT3DSMat44 theMVP = TBase::SetupMVP(inWidgetContext); + + if (isNodeBehindCamera()) + return; + + inRenderContext.SetCullingEnabled(false); + QT3DSMat44 theCameraMVP = m_WidgetInfo.m_LayerProjection * m_CameraTranslationScale; + + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetColorWritesEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetActiveShader(m_ZClearShader); + inRenderContext.SetInputAssembler(m_CameraRect); + m_ZClearShader->SetPropertyValue("model_view_projection", theCameraMVP); + inRenderContext.Draw(NVRenderDrawMode::Triangles, m_CameraRect->GetVertexCount(), 0); + + inRenderContext.SetColorWritesEnabled(true); + inRenderContext.SetActiveShader(m_Shader); + m_Shader->SetPropertyValue("model_view_projection", theCameraMVP); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetDepthWriteEnabled(false); + RenderSingleToneGeometry(StudioWidgetComponentIds::CameraPlane, theRingColor, + inRenderContext, m_CameraAxis); + + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetActiveShader(m_Shader); + m_Shader->SetPropertyValue("model_view_projection", theMVP); + RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext, + m_XAxis); + RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext, + m_YAxis); + RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext, + m_ZAxis); + + if (m_RotationWedge.hasValue()) { + BeginImmediateDrawing(inWidgetContext, inRenderContext); + + QT3DSMat33 theMVPRotation(m_WidgetInfo.m_CameraGlobalInverse.column0.getXYZ(), + m_WidgetInfo.m_CameraGlobalInverse.column1.getXYZ(), + m_WidgetInfo.m_CameraGlobalInverse.column2.getXYZ()); + theMVPRotation = theMVPRotation.getInverse().getTranspose(); + + QT3DSVec3 theRotationAxis = theMVPRotation.transform(m_RotationWedge->m_RotationAxis); + QT3DSVec3 theStartDirection = theMVPRotation.transform(m_RotationWedge->m_StartDirection); + theRotationAxis.normalize(); + theStartDirection.normalize(); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetBlendingEnabled(true); + QT3DSVec4 lineColor(1.0f, 1.0f, 1.0f, .7f); + QT3DSVec4 fillColor(1.0f, 1.0f, 1.0f, .2f); + switch (m_Highlight) { + default: + break; + case StudioWidgetComponentIds::XAxis: + lineColor = QT3DSVec4(theXColor, .7f); + fillColor = QT3DSVec4(theXColor, .2f); + break; + case StudioWidgetComponentIds::YAxis: + lineColor = QT3DSVec4(theYColor, .7f); + fillColor = QT3DSVec4(theYColor, .2f); + break; + case StudioWidgetComponentIds::ZAxis: + lineColor = QT3DSVec4(theZColor, .7f); + fillColor = QT3DSVec4(theZColor, .2f); + break; + } + QT3DSVec3 theStartPos(m_WidgetInfo.m_Position); + QT3DSF32 theStartLineLen = theRingOuter * m_WidgetInfo.m_Scale; + if (m_Highlight == StudioWidgetComponentIds::CameraPlane) + theStartLineLen = (theRingOuter + 5) * m_WidgetInfo.m_Scale; + // Get the end line length in camera space. + QT3DSVec3 theGlobalStart = m_Node->GetGlobalPivot(); + QT3DSQuat theGlobalRot(m_RotationWedge->m_Angle, m_RotationWedge->m_RotationAxis); + QT3DSVec3 theGlobalDir = theGlobalRot.rotate(m_RotationWedge->m_StartDirection); + QT3DSVec3 theGlobalEnd = theGlobalStart + theGlobalDir * m_RotationWedge->m_EndLineLen; + // Transform both start, end into camera space and get the length of the resulting + // vector + QT3DSVec3 theCameraEnd = m_WidgetInfo.m_CameraGlobalInverse.transform(theGlobalEnd); + // Draw lines in world space + SCamera &theCamera(*m_WidgetInfo.m_Camera); + QT3DSVec3 lineStart(ToFixedCameraPos(theStartPos, theCamera)); + QT3DSVec3 startLineEnd( + ToFixedCameraPos(theStartPos + theStartDirection * theStartLineLen, theCamera)); + QT3DSVec3 endLineEnd(ToFixedCameraPos(theCameraEnd, theCamera)); + DrawImmediateLine(lineStart, startLineEnd, 1.0f, lineColor); + DrawImmediateLine(lineStart, endLineEnd, 1.0f, lineColor); + DrawFilledArc(theStartPos, theStartDirection, theStartLineLen, theRotationAxis, + m_RotationWedge->m_Angle, fillColor); + // Now setup the model-view-projection. + QT3DSMat44 theProjection = m_WidgetInfo.m_LayerProjection; + EndImmediateDrawing(inWidgetContext, inRenderContext, theProjection); + + // Now we attempt to render some text. First we format it. + + char textBuffer[25] = { 0 }; + QT3DSF32 angleDeg(m_RotationWedge->m_Angle); + TODEG(angleDeg); + sprintf(textBuffer, " %.1f ", -angleDeg); // spaces added for margin + STextRenderInfo theInfo; + theInfo.m_Text = inRenderContext.GetStringTable().RegisterStr(textBuffer); + theInfo.m_HorizontalAlignment = TextHorizontalAlignment::Center; + theInfo.m_VerticalAlignment = TextVerticalAlignment::Bottom; + theInfo.m_FontSize = 24.0f * pixelRatio; + theInfo.m_Font = inRenderContext.GetStringTable().RegisterStr("TitilliumWeb-Regular"); + QT3DSMat44 theTransMatrix(QT3DSMat44::createIdentity()); + theTransMatrix.column3.x = endLineEnd.x; + theTransMatrix.column3.y = endLineEnd.y; + theTransMatrix.column3.z = endLineEnd.z; + // We want to scale the text *down* so that it looks better. + theTransMatrix.column0[0] = m_WidgetInfo.m_Scale * .8f; + theTransMatrix.column1[1] = m_WidgetInfo.m_Scale * .8f; + theTransMatrix.column2[2] = m_WidgetInfo.m_Scale * .8f; + QT3DSMat44 theTextMVP = theProjection * theTransMatrix; + inWidgetContext.RenderText(theInfo, QT3DSVec3(1.0f, 1.0f, 1.0f), + QT3DSVec3(.2f, .2f, .2f), theTextMVP); + } + m_Highlight = StudioWidgetComponentIds::NoId; + } + + void RenderPick(const QT3DSMat44 &inProjPremult, NVRenderContext &inRenderContext, + QSize /*inWinDimensions*/) override + { + if (m_XAxis && m_PickShader) { + QT3DSMat44 theCameraMVP = + inProjPremult * m_WidgetInfo.m_PureProjection * m_CameraTranslationScale; + QT3DSMat44 theMVP = inProjPremult * m_WidgetInfo.m_PureProjection * m_TranslationScale; + + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + inRenderContext.SetDepthTestEnabled(false); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetBlendingEnabled(true); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_ZClearShader); + m_ZClearShader->SetPropertyValue("model_view_projection", theCameraMVP); + inRenderContext.SetInputAssembler(m_CameraRect); + inRenderContext.Draw(NVRenderDrawMode::Triangles, m_CameraRect->GetVertexCount(), 0); + + inRenderContext.SetActiveShader(m_PickShader); + m_PickShader->SetPropertyValue("model_view_projection", theCameraMVP); + RenderPickBuffer(StudioWidgetComponentIds::CameraPlane, m_CameraAxis, inRenderContext); + + m_PickShader->SetPropertyValue("model_view_projection", theMVP); + + RenderPickBuffer(StudioWidgetComponentIds::XAxis, m_XAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::YAxis, m_YAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::ZAxis, m_ZAxis, inRenderContext); + } + } +}; +} + +IStudioWidget &IStudioWidget::CreateRotationWidget(NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, SRotationWidget)(inAlloc); +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioScaleWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioScaleWidget.cpp new file mode 100644 index 00000000..287f7b01 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioScaleWidget.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioWidgetImpl.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "StudioUtils.h" +#include "StudioPreferences.h" + +using namespace qt3ds::widgets; + +namespace { + +struct SScaleWidget : public SStudioWidgetImpl<StudioWidgetTypes::Scale> +{ + typedef SStudioWidgetImpl<StudioWidgetTypes::Scale> TBase; + + NVRenderInputAssembler *m_XAxis; + NVRenderInputAssembler *m_YAxis; + NVRenderInputAssembler *m_ZAxis; + + NVRenderInputAssembler *m_XPlane; + NVRenderInputAssembler *m_YPlane; + NVRenderInputAssembler *m_ZPlane; + + volatile QT3DSI32 mRefCount; + + SScaleWidget(NVAllocatorCallback &inAlloc) + : TBase(inAlloc) + , m_XAxis(nullptr) + , m_YAxis(nullptr) + , m_ZAxis(nullptr) + , m_XPlane(nullptr) + , m_YPlane(nullptr) + , m_ZPlane(nullptr) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator) + + NVRenderInputAssembler *CreateScaleAxis(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, + const QT3DSVec3 &inDirection, QT3DSF32 inStartOffset, + QT3DSF32 inLength, QT3DSF32 inWidth, QT3DSF32 inBoxSideLength, + const char *inAxisName) + { + + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inAxisName); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) { + return retval; + } + + QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0)); + if (tempCross.magnitudeSquared() < .05f) + tempCross = inDirection.cross(QT3DSVec3(1, 0, 0)); + + QT3DSVec3 upDir = inDirection.cross(tempCross); + QT3DSVec3 leftDir = upDir.cross(inDirection); + + TResultVecType theVertexData(m_Allocator, "SScaleWidget::theVertexData"); + + QT3DSVec3 rectStart = inDirection * inStartOffset; + // Rect end is also box start, obviously + QT3DSVec3 rectEnd = inDirection * (inStartOffset + inLength); + QT3DSVec3 boxEnd = inDirection * (inStartOffset + inLength + inBoxSideLength); + + QT3DSF32 axisHalfWidth = inWidth / 2.0f; + // Create the axis + CreateRect(rectStart, rectEnd, upDir, axisHalfWidth, 0.0f, theVertexData); + CreateRect(rectStart, rectEnd, leftDir, axisHalfWidth, 0.0f, theVertexData); + // Create box at the top. + QT3DSF32 boxSideHalfLength = inBoxSideLength / 2; + // Get the four sides + QT3DSVec3 boxRectStart = rectEnd + (leftDir * boxSideHalfLength); + QT3DSVec3 boxRectEnd = boxEnd + (leftDir * boxSideHalfLength); + CreateRect(boxRectStart, boxRectEnd, upDir, boxSideHalfLength, 0.0f, theVertexData); + + boxRectStart = rectEnd - leftDir * boxSideHalfLength; + boxRectEnd = boxEnd - leftDir * boxSideHalfLength; + CreateRect(boxRectStart, boxRectEnd, upDir, boxSideHalfLength, 0.0f, theVertexData); + + boxRectStart = rectEnd + upDir * boxSideHalfLength; + boxRectEnd = boxEnd + upDir * boxSideHalfLength; + CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData); + + boxRectStart = rectEnd - upDir * boxSideHalfLength; + boxRectEnd = boxEnd - upDir * boxSideHalfLength; + CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData); + + // now create the top and bottom + // bottom + boxRectStart = rectEnd + upDir * boxSideHalfLength; + boxRectEnd = rectEnd - upDir * boxSideHalfLength; + CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData); + + // top + boxRectStart = boxEnd + upDir * boxSideHalfLength; + boxRectEnd = boxEnd - upDir * boxSideHalfLength; + CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + IStudioWidget::GetVertexBufferAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; + } + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override + { + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + float pixelRatio = float(StudioUtils::devicePixelRatio()); + QT3DSF32 axisWidth = pixelRatio; + QT3DSF32 triWidth = 3 * CStudioPreferences::selectorLineWidth() * pixelRatio; + QT3DSF32 axisStart = CStudioPreferences::selectorLineLength() / 3.0f * pixelRatio; + QT3DSF32 axisLength = CStudioPreferences::selectorLineLength() * pixelRatio; + QT3DSF32 axisTotalLength = triWidth + axisLength; + if (m_XAxis == nullptr) { + TBase::SetupRender(inWidgetContext, inRenderContext); + + m_XAxis = CreateScaleAxis(inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0), + axisStart, axisLength, axisWidth, triWidth, + "ScaleWidgetXAxis"); + m_YAxis = CreateScaleAxis(inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0), + axisStart, axisLength, axisWidth, triWidth, + "ScaleWidgetYAxis"); + m_ZAxis = CreateScaleAxis(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), + axisStart, axisLength, axisWidth, triWidth, + "ScaleWidgetZAxis"); + + QT3DSF32 axisPos = GetDiscPos() * pixelRatio; + QT3DSF32 axisDiscRadius = GetDiscRadius() * pixelRatio; + QT3DSF32 axisRingRadius = GetDiscRingRadius() * pixelRatio; + m_XPlane = + CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0), + QT3DSVec3(0, axisPos, -axisPos), axisDiscRadius, axisRingRadius, + 0.0f, 1.0f, "ScaleWidgetXPlane"); + m_YPlane = + CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0), + QT3DSVec3(axisPos, 0, -axisPos), axisDiscRadius, axisRingRadius, + 0.0f, 1.0f, "ScaleWidgetYPlane"); + m_ZPlane = + CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), + QT3DSVec3(axisPos, axisPos, 0), axisDiscRadius, axisRingRadius, + 0.0f, 1.0f, "ScaleWidgetZPlane"); + + inRenderContext.SetActiveShader(m_Shader); + m_Shader->SetPropertyValue("attr_pos_add_start", axisStart + 1); + } + + QT3DSMat44 theMVP = TBase::SetupMVP(inWidgetContext); + + if (isNodeBehindCamera()) + return; + + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_Shader); + m_Shader->SetPropertyValue("model_view_projection", theMVP); + // temporary set color1 to white so we can hopefully see mistakes. + m_Shader->SetPropertyValue("color1", QT3DSVec3(1, 1, 1)); + + QT3DSVec3 theXColor(GetXAxisColor()); + QT3DSVec3 theYColor(GetYAxisColor()); + QT3DSVec3 theZColor(GetZAxisColor()); + QT3DSVec3 theRingColor(QT3DSVec3(.8f, .8f, .8f)); + QT3DSVec3 theEndOffset = QT3DSVec3(axisTotalLength); + QT3DSVec3 theScaledEnd = QT3DSVec3(theEndOffset.x * m_AxisScale.x, + theEndOffset.y * m_AxisScale.y, + theEndOffset.z * m_AxisScale.z); + QT3DSVec3 theEndAddition = theScaledEnd - theEndOffset; + + m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(theEndAddition.x, 0, 0)); + RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext, + m_XAxis); + + m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(0, theEndAddition.y, 0)); + RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext, + m_YAxis); + + m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(0, 0, -1.0f * theEndAddition.z)); + RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext, + m_ZAxis); + + m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(0, 0, 0)); + RenderTwoToneGeometry(StudioWidgetComponentIds::XPlane, theXColor, theRingColor, + inRenderContext, m_XPlane); + RenderTwoToneGeometry(StudioWidgetComponentIds::YPlane, theYColor, theRingColor, + inRenderContext, m_YPlane); + RenderTwoToneGeometry(StudioWidgetComponentIds::ZPlane, theZColor, theRingColor, + inRenderContext, m_ZPlane); + + m_Highlight = StudioWidgetComponentIds::NoId; + } + + void RenderPick(const QT3DSMat44 &inProjPremult, NVRenderContext &inRenderContext, + QSize /*inWinDimensions*/) override + { + if (m_XAxis && m_PickShader) { + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_PickShader); + // The projection premultiplication step moves the viewport around till + // it is centered over the mouse and scales everything *post* rendering (to keep + // appropriate aspect). + QT3DSMat44 theMVP = inProjPremult * m_PureProjection * m_TranslationScale; + m_PickShader->SetPropertyValue("model_view_projection", theMVP); + + RenderPickBuffer(StudioWidgetComponentIds::XAxis, m_XAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::YAxis, m_YAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::ZAxis, m_ZAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::XPlane, m_XPlane, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::YPlane, m_YPlane, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::ZPlane, m_ZPlane, inRenderContext); + } + } +}; +} + +IStudioWidget &IStudioWidget::CreateScaleWidget(NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, SScaleWidget)(inAlloc); +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.cpp b/src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.cpp new file mode 100644 index 00000000..e76f9ba4 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.cpp @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "StudioSubPresentationRenderer.h" +#include "render/Qt3DSRenderContext.h" +#include "q3dspresentation.h" +#include "q3dsviewersettings.h" + +#include <QtCore/qfileinfo.h> +#include <qxmlstream.h> + +using namespace qt3ds::render; + +class RendererThread : public QThread +{ +public: + RendererThread(QThread *mainThread) + : m_running(false) + , m_updated(false) + , m_initialized(false) + , m_mainThread(mainThread) + , m_semaphore(0) + { + } + + QSize readPresentationSize(const QString &url) + { + QFile file(url); + file.open(QFile::Text | QFile::ReadOnly); + if (!file.isOpen()) { + qWarning () << file.errorString(); + return QSize(); + } + QXmlStreamReader reader(&file); + reader.setNamespaceProcessing(false); + + if (reader.readNextStartElement() && reader.qualifiedName() == QLatin1String("UIP")) { + if (reader.readNextStartElement() + && reader.qualifiedName() == QLatin1String("Project")) { + if (reader.readNextStartElement() + && reader.qualifiedName() == QLatin1String("ProjectSettings")) { + const auto attrib = reader.attributes(); + return QSize(attrib.value(QLatin1String("presentationWidth")).toInt(), + attrib.value(QLatin1String("presentationHeight")).toInt()); + } + } + } + return QSize(); + } + + void initialize(const QString &id, const QString &presentation, const QString &path) + { + m_path = path; + m_presentation = presentation; + + m_surfaceViewer.reset(new Q3DSSurfaceViewer); + m_surfaceViewer->setPresentationId(id); + m_context.reset(new QT_PREPEND_NAMESPACE(QOpenGLContext)); + m_surface.reset(new QOffscreenSurface); + + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + if (QT_PREPEND_NAMESPACE(QOpenGLContext)::currentContext()) + format = QT_PREPEND_NAMESPACE(QOpenGLContext)::currentContext()->format(); + m_context->setShareContext(QT_PREPEND_NAMESPACE(QOpenGLContext)::currentContext()); + m_context->setFormat(format); + m_surface->setFormat(format); + m_context->create(); + m_surface->create(); + m_context->moveToThread(this); + m_surface->moveToThread(this); + m_surfaceViewer->moveToThread(this); + + QObject::connect(m_surfaceViewer.data(), &Q3DSSurfaceViewer::frameUpdate, + this, &RendererThread::frameRendered); + m_initialized = true; + } + + void run() override + { + QFileInfo info(m_path + "/" + m_presentation); + m_size = readPresentationSize(m_path + "/" + m_presentation); + + m_context->makeCurrent(m_surface.data()); + + m_fbo.reset(new QOpenGLFramebufferObject(m_size, + QOpenGLFramebufferObject::CombinedDepthStencil)); + + m_surfaceViewer->setSize(m_size); + m_surfaceViewer->setAutoSize(false); + m_surfaceViewer->setUpdateInterval(-1); + m_surfaceViewer->presentation()->setSource(QUrl::fromLocalFile(info.absoluteFilePath())); + m_surfaceViewer->settings()->setMatteColor(Qt::transparent); + m_surfaceViewer->create(m_surface.data(), m_context.data(), m_fbo->handle()); + m_running = true; + m_semaphore.release(); + + m_context->doneCurrent(); + + while (true) { + QMutexLocker lock(&m_mutex); + if (!m_running) + break; + m_context->makeCurrent(m_surface.data()); + if (!m_surfaceViewer->isRunning()) { + qWarning () << "Subpresentation stopped running. Stopping updating."; + break; + } + + m_surfaceViewer->update(); + m_context->functions()->glFlush(); + m_updated = true; + m_context->doneCurrent(); + lock.unlock(); + + // Update approx 30 fps + QThread::usleep(33000); + } + m_fbo.reset(); + m_context->doneCurrent(); + m_context.reset(); + m_surfaceViewer->destroy(); + m_surfaceViewer.reset(); + m_surface->moveToThread(m_mainThread); + m_semaphore.release(); + } + + void frameRendered() + { + m_updated = true; + } + + QScopedPointer<Q3DSSurfaceViewer> m_surfaceViewer; + QScopedPointer<QT_PREPEND_NAMESPACE(QOpenGLContext)> m_context; + QScopedPointer<QOffscreenSurface> m_surface; + QScopedPointer<QOpenGLFramebufferObject> m_fbo; + QString m_path; + QString m_presentation; + QMutex m_mutex; + bool m_running, m_updated, m_initialized; + QThread *m_mainThread; + QSemaphore m_semaphore; + QSize m_size; +}; + +StudioSubpresentationRenderer::StudioSubpresentationRenderer( + IQt3DSRenderContext &inRenderContext, const QString &id, + const QString &presentation, const QString &path) + : m_renderContext(inRenderContext), m_id(id), m_presentation(presentation), m_path(path) + , m_program(nullptr) + , m_vao(nullptr) + , m_vertices(nullptr) + , m_callback(nullptr) + , m_rendererType(inRenderContext.GetStringTable().RegisterStr("StudioPresentationRenderer")) + , mRefCount(0) +{ + m_thread.reset(new RendererThread(QThread::currentThread())); +} + +StudioSubpresentationRenderer::~StudioSubpresentationRenderer() +{ + QMutexLocker lock(&m_thread->m_mutex); + if (m_thread->m_running) { + m_thread->m_running = false; + lock.unlock(); + m_thread->m_semaphore.acquire(); + } +} + +CRegisteredString StudioSubpresentationRenderer::GetOffscreenRendererType() +{ + return m_rendererType; +} + +SOffscreenRendererEnvironment +StudioSubpresentationRenderer::GetDesiredEnvironment(QT3DSVec2 inPresentationScaleFactor) +{ + // If we aren't using a clear color, then we are expected to blend with the background + if (!m_thread->m_initialized) + initialize(); + NVRenderTextureFormats::Enum format = NVRenderTextureFormats::RGBA8; + return SOffscreenRendererEnvironment( + QT3DSU32(m_thread->m_size.width() * inPresentationScaleFactor.x), + QT3DSU32(m_thread->m_size.height() * inPresentationScaleFactor.y), + format, OffscreenRendererDepthValues::Depth24, false, + AAModeValues::NoAA); +} + +SOffscreenRenderFlags StudioSubpresentationRenderer::NeedsRender( + const SOffscreenRendererEnvironment &inEnvironment, + QT3DSVec2 inPresentationScaleFactor, + const SRenderInstanceId instanceId) +{ + Q_UNUSED(inEnvironment) + Q_UNUSED(inPresentationScaleFactor) + Q_UNUSED(instanceId) + return SOffscreenRenderFlags(true, true); +} + +void StudioSubpresentationRenderer::initialize() +{ + m_thread->initialize(m_id, m_presentation, m_path); + m_thread->start(); + m_thread->m_semaphore.acquire(); + if (m_callback) + m_callback->onOffscreenRendererInitialized(m_id); +} + +void StudioSubpresentationRenderer::Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + const SRenderInstanceId instanceId) +{ + Q_UNUSED(inEnvironment) + Q_UNUSED(inPresentationScaleFactor) + Q_UNUSED(instanceId) + inRenderContext.PushPropertySet(); + if (!m_thread->m_initialized) { + initialize(); + } + QMutexLocker lock(&m_thread->m_mutex); + if (m_thread->m_initialized && m_thread->m_updated) { + QT_PREPEND_NAMESPACE(QOpenGLContext) *context + = QT_PREPEND_NAMESPACE(QOpenGLContext)::currentContext(); + QOpenGLFunctions *func = context->functions(); + GLuint texture = m_thread->m_fbo->texture(); + func->glDisable(GL_DEPTH_TEST); + if (inColorBufferNeedsClear == SScene::RenderClearCommand::AlwaysClear) + func->glDisable(GL_BLEND); + else + func->glEnable(GL_BLEND); + func->glBlendEquation(GL_FUNC_ADD); + func->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + if (!m_program) + initializeFboCopy(); + + m_program->bind(); + + if (!m_vao) { + m_vao = new QOpenGLVertexArrayObject; + m_vao->create(); + m_vao->bind(); + m_vertices->bind(); + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + func->glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(float), nullptr); + func->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 6 * sizeof(float), + reinterpret_cast<const void *>(4 * sizeof(GLfloat))); + m_vertices->release(); + } else { + m_vao->bind(); + } + + func->glActiveTexture(GL_TEXTURE0); + func->glBindTexture(GL_TEXTURE_2D, texture); + func->glDrawArrays(GL_TRIANGLES, 0, 6); + func->glEnable(GL_DEPTH_TEST); + func->glBindTexture(GL_TEXTURE_2D, 0); + + m_program->release(); + m_vao->release(); + + if (m_callback) + m_callback->onOffscreenRendererFrame(m_id); + } + inRenderContext.PopPropertySet(true); +} + +void StudioSubpresentationRenderer::RenderWithClear( + const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + QT3DSVec4 inclearColor, + const SRenderInstanceId instanceId) +{ + inRenderContext.SetClearColor(inclearColor); + Render(inEnvironment, inRenderContext, inPresentationScaleFactor, + inColorBufferNeedsClear, instanceId); +} + +void StudioSubpresentationRenderer::initializeFboCopy() +{ + m_program = new QOpenGLShaderProgram; + if (m_thread->m_context->format().renderableType() == QSurfaceFormat::OpenGLES) { + static const char *vsSource = + "attribute highp vec4 pos;\n" + "attribute highp vec2 tc;\n" + "varying lowp vec2 texcoord;\n" + "void main() {\n" + " texcoord = tc;\n" + " gl_Position = pos;\n" + "}\n"; + static const char *fsSource = + "varying highp vec2 texcoord;\n" + "uniform sampler2D sampler;\n" + "void main() {\n" + " gl_FragColor = texture2D(sampler, texcoord);\n" + "}\n"; + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsSource); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsSource); + } else { + static const char *vsSource = + "#version 150 core\n" + "in vec4 pos;\n" + "in vec2 tc;\n" + "out vec2 texcoord;\n" + "void main() {\n" + " texcoord = tc;\n" + " gl_Position = pos;\n" + "}\n"; + static const char *fsSource = + "#version 150 core\n" + "in vec2 texcoord;\n" + "out vec4 fragColor;\n" + "uniform sampler2D sampler;\n" + "void main() {\n" + " fragColor = texture(sampler, texcoord);\n" + "}\n"; + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsSource); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsSource); + } + + m_program->bindAttributeLocation("pos", 0); + m_program->bindAttributeLocation("tc", 1); + + if (!m_program->link()) { + QByteArray logData(m_program->log().toLocal8Bit()); + const char *log = logData.data(); + qFatal("Failed to create shader program: %s", log); + } + + m_vertices = new QOpenGLBuffer; + m_vertices->create(); + m_vertices->bind(); + + static const float vertices[] = + { + -1, -1, 0, 1, 0, 0, + 1, -1, 0, 1, 1, 0, + 1, 1, 0, 1, 1, 1, + -1, -1, 0, 1, 0, 0, + 1, 1, 0, 1, 1, 1, + -1, 1, 0, 1, 0, 1, + }; + + m_vertices->allocate(vertices, sizeof(vertices)); + m_vertices->release(); +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.h b/src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.h new file mode 100644 index 00000000..3ff434e6 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_SUBPRESENTATION_RENDERER_H +#define QT3DS_STUDIO_SUBPRESENTATION_RENDERER_H + +#include "Qt3DSOffscreenRenderManager.h" +#include "Qt3DSRenderContextCore.h" +#include "q3dssurfaceviewer.h" + +#include <QtGui/qopenglshaderprogram.h> +#include <QtGui/qopenglvertexarrayobject.h> +#include <QtGui/qopenglbuffer.h> +#include <QtGui/qoffscreensurface.h> +#include <QtGui/qopenglcontext.h> +#include <QtCore/qscopedpointer.h> + +class RendererThread; + +class StudioSubpresentationRenderer : public qt3ds::render::IOffscreenRenderer +{ +public: + StudioSubpresentationRenderer(qt3ds::render::IQt3DSRenderContext &inRenderContext, + const QString &id, const QString &presentation, + const QString &path); + ~StudioSubpresentationRenderer(); + + void addRef() override + { + qt3ds::render::atomicIncrement(&mRefCount); + } + void release() override + { + qt3ds::render::QT3DSI32 value = qt3ds::render::atomicDecrement(&mRefCount); + if (value <= 0) + qt3ds::render::NVDelete(m_renderContext.GetAllocator(), this); + } + + qt3ds::render::CRegisteredString GetOffscreenRendererType() override; + qt3ds::render::SOffscreenRendererEnvironment + GetDesiredEnvironment(qt3ds::render::QT3DSVec2 inPresentationScaleFactor) override; + + qt3ds::render::SOffscreenRenderFlags NeedsRender( + const qt3ds::render::SOffscreenRendererEnvironment &inEnvironment, + qt3ds::render::QT3DSVec2 inPresentationScaleFactor, + const qt3ds::render::SRenderInstanceId instanceId) override; + + void Render(const qt3ds::render::SOffscreenRendererEnvironment &inEnvironment, + qt3ds::render::NVRenderContext &inRenderContext, + qt3ds::render::QT3DSVec2 inPresentationScaleFactor, + qt3ds::render::SScene::RenderClearCommand inColorBufferNeedsClear, + const qt3ds::render::SRenderInstanceId instanceId) override; + void RenderWithClear(const qt3ds::render::SOffscreenRendererEnvironment &inEnvironment, + qt3ds::render::NVRenderContext &inRenderContext, + qt3ds::render::QT3DSVec2 inPresentationScaleFactor, + qt3ds::render::SScene::RenderClearCommand inColorBufferNeedsClear, + qt3ds::render::QT3DSVec4 inclearColor, + const qt3ds::render::SRenderInstanceId instanceId) override; + + qt3ds::render::IGraphObjectPickQuery *GetGraphObjectPickQuery( + const qt3ds::render::SRenderInstanceId instanceId) override + { + Q_UNUSED(instanceId); + return nullptr; + } + + bool Pick(const qt3ds::render::QT3DSVec2 &inMouseCoords, + const qt3ds::render::QT3DSVec2 &inViewportDimensions, + const qt3ds::render::SRenderInstanceId instanceId) override + { + Q_UNUSED(inMouseCoords); + Q_UNUSED(inViewportDimensions); + Q_UNUSED(instanceId); + return false; + } + + void addCallback(IOffscreenRendererCallback *cb) override + { + m_callback = cb; + } + +private: + void initializeFboCopy(); + void initialize(); + + qt3ds::render::IQt3DSRenderContext &m_renderContext; + QString m_id; + QString m_presentation; + QString m_path; + + QScopedPointer<RendererThread> m_thread; + QOpenGLShaderProgram *m_program; + QOpenGLVertexArrayObject *m_vao; + QOpenGLBuffer *m_vertices; + IOffscreenRendererCallback *m_callback; + qt3ds::render::CRegisteredString m_rendererType; + volatile qt3ds::render::QT3DSI32 mRefCount; +}; + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/StudioTranslationWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioTranslationWidget.cpp new file mode 100644 index 00000000..1ff7bd48 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioTranslationWidget.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioWidgetImpl.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "Qt3DSRenderNode.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "StudioUtils.h" +#include "StudioPreferences.h" + +using namespace qt3ds::widgets; + +namespace { + +struct STranslationWidget : public SStudioWidgetImpl<StudioWidgetTypes::Translation> +{ + typedef SStudioWidgetImpl<StudioWidgetTypes::Translation> TBase; + NVRenderInputAssembler *m_XAxis; + NVRenderInputAssembler *m_YAxis; + NVRenderInputAssembler *m_ZAxis; + NVRenderInputAssembler *m_wideXAxis; + NVRenderInputAssembler *m_wideYAxis; + NVRenderInputAssembler *m_wideZAxis; + NVRenderInputAssembler *m_XPlane; + NVRenderInputAssembler *m_YPlane; + NVRenderInputAssembler *m_ZPlane; + + volatile QT3DSI32 mRefCount; + + STranslationWidget(NVAllocatorCallback &inAlloc) + : TBase(inAlloc) + , m_XAxis(nullptr) + , m_YAxis(nullptr) + , m_ZAxis(nullptr) + , m_wideXAxis(nullptr) + , m_wideYAxis(nullptr) + , m_wideZAxis(nullptr) + , m_XPlane(nullptr) + , m_YPlane(nullptr) + , m_ZPlane(nullptr) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator); + + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override + { + // Widgets have to clear the depth buffer; they shouldn't interact with other components + // but they should self-occlude. + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + inRenderContext.SetDepthTestEnabled(true); + if (m_XAxis == nullptr) { + TBase::SetupRender(inWidgetContext, inRenderContext); + float pixelRatio = float(StudioUtils::devicePixelRatio()); + QT3DSF32 axisWidth = pixelRatio; + QT3DSF32 triWidth = 4 * CStudioPreferences::selectorLineWidth() * pixelRatio; + QT3DSF32 axisStart = CStudioPreferences::selectorLineLength() / 3.0f * pixelRatio; + QT3DSF32 axisLength = CStudioPreferences::selectorLineLength() * pixelRatio; + QT3DSF32 triLength = axisStart; + m_XAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0), + axisStart, axisLength, triLength, axisWidth, triWidth, + "TranslationWidgetXAxis"); + m_YAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0), + axisStart, axisLength, triLength, axisWidth, triWidth, + "TranslationWidgetYAxis"); + m_ZAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), + axisStart, axisLength, triLength, axisWidth, triWidth, + "TranslationWidgetZAxis"); + + QT3DSF32 axisPos = GetDiscPos() * pixelRatio; + QT3DSF32 axisDiscRadius = GetDiscRadius() * pixelRatio; + QT3DSF32 axisRingRadius = GetDiscRingRadius() * pixelRatio; + m_XPlane = + CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0), + QT3DSVec3(0, axisPos, -axisPos), axisDiscRadius, axisRingRadius, + 0.0f, 1.0f, "TranslationWidgetXPlane"); + m_YPlane = + CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0), + QT3DSVec3(axisPos, 0, -axisPos), axisDiscRadius, axisRingRadius, + 0.0f, 1.0f, "TranslationWidgetYPlane"); + m_ZPlane = + CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), + QT3DSVec3(axisPos, axisPos, 0), axisDiscRadius, axisRingRadius, + 0.0f, 1.0f, "TranslationWidgetZPlane"); + } + + QT3DSMat44 theMVP; + + if (m_isAxisHelper) { + auto helpWidgetInfo + = inWidgetContext.GetWidgetRenderInformation(*m_Node, + QT3DSVec3(0, 0, 0), + RenderWidgetModes::Global); + auto viewport = inRenderContext.GetViewport(); + SCamera theTempCamera; + + QT3DSMat44 theViewProjection; + theTempCamera.m_Flags.SetOrthographic(true); + + // Move the camera back far enough that we can see everything + static QT3DSF32 theCameraSetback(600); + + // Move the camera to position axis helper to the lower left. + theTempCamera.m_Position.z = -theCameraSetback; + theTempCamera.m_Position.x = 0; + theTempCamera.m_Position.y = 0; + theTempCamera.m_ClipFar = 2.0f * theCameraSetback; + // Use entire viewport + theTempCamera.CalculateGlobalVariables( + viewport, + QT3DSVec2((QT3DSF32)viewport.m_Width, (QT3DSF32)viewport.m_Height)); + theTempCamera.CalculateViewProjectionMatrix(theViewProjection); + + // Get the rotation for the scene view camera (the actual direction of world axes) + // and rotate the widget axes to match world axes. Scale the widget to be smaller + // and less intrusive than actual translation widget. + auto rot = QT3DSMat44::createIdentity(); + if (helpWidgetInfo.m_Camera->m_Flags.IsOrthographic()) { + rot = helpWidgetInfo.m_Camera->m_LocalTransform; + rot.setPosition({0, 0, 0}); + rot = rot.getInverse().getTranspose(); + } else { + helpWidgetInfo.m_Camera->CalculateRotationMatrix(rot); + } + + helpWidgetInfo.m_Camera->FlipCoordinateSystem(rot); + // In the scale calculation 200.f = 120 / 0.6; 120 being the default width and 0.6 + // being the default scale for the axis helper + const float scale = float(viewport.m_Width) / 200.f; + rot.scale(QT3DSVec4(scale, scale, scale, 1)); + theMVP = theViewProjection * rot.getTranspose(); + + if (m_wideXAxis == nullptr) { + QT3DSF32 axisWidth = 6; + QT3DSF32 triWidth = axisWidth * 3; + QT3DSF32 axisStart = 0; + QT3DSF32 axisLength = axisWidth * 10; + QT3DSF32 triLength = axisWidth * 5; + m_wideXAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, + QT3DSVec3(1, 0, 0), axisStart, axisLength, triLength, + axisWidth, triWidth, "TranslationWidgetWideXAxis"); + m_wideYAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, + QT3DSVec3(0, 1, 0), axisStart, axisLength, triLength, + axisWidth, triWidth, "TranslationWidgetWideYAxis"); + m_wideZAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, + QT3DSVec3(0, 0, -1), axisStart, axisLength, triLength, + axisWidth, triWidth, "TranslationWidgetWideZAxis"); + } + } else { + theMVP = TBase::SetupMVP(inWidgetContext); + } + + if (isNodeBehindCamera()) + return; + + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_Shader); + m_Shader->SetPropertyValue("model_view_projection", theMVP); + // temporary set color1 to white so we can hopefully see mistakes. + m_Shader->SetPropertyValue("color1", QT3DSVec3(1, 1, 1)); + + QT3DSVec3 theXColor(GetXAxisColor()); + QT3DSVec3 theYColor(GetYAxisColor()); + QT3DSVec3 theZColor(GetZAxisColor()); + QT3DSVec3 theRingColor(QT3DSVec3(.8f, .8f, .8f)); + + if (!m_isAxisHelper) { + RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext, + m_XAxis); + RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext, + m_YAxis); + RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext, + m_ZAxis); + RenderTwoToneGeometry(StudioWidgetComponentIds::XPlane, theXColor, theRingColor, + inRenderContext, m_XPlane); + RenderTwoToneGeometry(StudioWidgetComponentIds::YPlane, theYColor, theRingColor, + inRenderContext, m_YPlane); + RenderTwoToneGeometry(StudioWidgetComponentIds::ZPlane, theZColor, theRingColor, + inRenderContext, m_ZPlane); + } else { + RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext, + m_wideXAxis); + RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext, + m_wideYAxis); + RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext, + m_wideZAxis); + } + m_Highlight = StudioWidgetComponentIds::NoId; + } + + void RenderPick(const QT3DSMat44 &inProjPremult, NVRenderContext &inRenderContext, + QSize /*inWinDimensions*/) override + { + if (m_XAxis && m_PickShader) { + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(true); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetActiveShader(m_PickShader); + // The projection premultiplication step moves the viewport around till + // it is centered over the mouse and scales everything *post* rendering (to keep + // appropriate aspect). + QT3DSMat44 theMVP = inProjPremult * m_PureProjection * m_TranslationScale; + m_PickShader->SetPropertyValue("model_view_projection", theMVP); + + RenderPickBuffer(StudioWidgetComponentIds::XAxis, m_XAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::YAxis, m_YAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::ZAxis, m_ZAxis, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::XPlane, m_XPlane, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::YPlane, m_YPlane, inRenderContext); + RenderPickBuffer(StudioWidgetComponentIds::ZPlane, m_ZPlane, inRenderContext); + } + } + + void setAsAxisHelper(bool isAxisHelper) override + { + m_isAxisHelper = isAxisHelper; + } +}; +} + +IStudioWidget &IStudioWidget::CreateTranslationWidget(NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, STranslationWidget)(inAlloc); +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.cpp new file mode 100644 index 00000000..f1e9d7d2 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.cpp @@ -0,0 +1,730 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioWidgetImpl.h" +#include "foundation/Qt3DSAtomic.h" +#include "render/Qt3DSRenderContext.h" +#include "render/Qt3DSRenderVertexBuffer.h" +#include "Qt3DSRenderNode.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderShaderCodeGenerator.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "StudioUtils.h" +#include "StudioPreferences.h" +#include "StudioVisualAidWidget.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderLight.h" +#include "OptimizedArithmetic.h" + +namespace qt3ds { +namespace widgets { + +static const float fepsilon = 1e-6f; +static const float pointLightOuterRadius = 40.0f; +static const float directionalLightRadius = 40.0f; +static const float directionalLightLength = 100.0f; + +SVisualAidWidget::SVisualAidWidget(NVAllocatorCallback &inAlloc) + : m_node(nullptr) + , m_billboard(nullptr) + , m_cameraBox(nullptr) + , m_directionalLight(nullptr) + , m_pointLight(nullptr) + , m_areaLight(nullptr) + , m_renderCameraShader(nullptr) + , m_renderShader(nullptr) + , m_billboardShader(nullptr) + , m_billboardCameraTexture(nullptr) + , m_billboardLightTexture(nullptr) + , m_selected(false) + , m_allocator(inAlloc) + , mRefCount(0) +{ + +} + +NVConstDataRef<NVRenderVertexBufferEntry> +SVisualAidWidget::getCameraBoxAttributesAndStride(QT3DSU32 &stride) +{ + static NVRenderVertexBufferEntry theEntries[] = { + NVRenderVertexBufferEntry("attr_pos", NVRenderComponentTypes::QT3DSF32, 4) + }; + + stride = 4 * sizeof(QT3DSF32); + + return toConstDataRef(theEntries, 1); +} + +NVConstDataRef<NVRenderVertexBufferEntry> +SVisualAidWidget::getLightAttributesAndStride(QT3DSU32 &stride) +{ + static NVRenderVertexBufferEntry theEntries[] = { + NVRenderVertexBufferEntry("attr_pos", NVRenderComponentTypes::QT3DSF32, 3) + }; + + stride = 3 * sizeof(QT3DSF32); + + return toConstDataRef(theEntries, 1); +} + +NVConstDataRef<NVRenderVertexBufferEntry> +SVisualAidWidget::getBillboardAttributesAndStride(QT3DSU32 &stride) +{ + static NVRenderVertexBufferEntry theEntries[] = { + NVRenderVertexBufferEntry("attr_pos", NVRenderComponentTypes::QT3DSF32, 3), + NVRenderVertexBufferEntry("attr_tc", NVRenderComponentTypes::QT3DSF32, 2, + 3 * sizeof(QT3DSF32)) + }; + + stride = 6 * sizeof(QT3DSF32); + + return toConstDataRef(theEntries, 2); +} + + +NVRenderInputAssembler *SVisualAidWidget::createCameraBox(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr("CameraBox"); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) + return retval; + + nvvector<QT3DSVec4> theVertexData(m_allocator, "SVisualAidWidget::theVertexData"); + nvvector<QT3DSI8> theIndexData(m_allocator, "SVisualAidWidget::theIndexData"); + + // 9 vertices, origin and near and far rectangles in ndc + theVertexData.push_back(QT3DSVec4(0.0, 0.0, 0.0, 0.0)); + theVertexData.push_back(QT3DSVec4(-1.0, -1.0, -1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(-1.0, 1.0, -1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(1.0, 1.0, -1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(1.0, -1.0, -1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(-1.0, -1.0, 1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(-1.0, 1.0, 1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(1.0, 1.0, 1.0, 1.0)); + theVertexData.push_back(QT3DSVec4(1.0, -1.0, 1.0, 1.0)); + + // origin to near + theIndexData.push_back(0); theIndexData.push_back(1); + theIndexData.push_back(0); theIndexData.push_back(2); + theIndexData.push_back(0); theIndexData.push_back(3); + theIndexData.push_back(0); theIndexData.push_back(4); + // near rect + theIndexData.push_back(1); theIndexData.push_back(2); + theIndexData.push_back(2); theIndexData.push_back(3); + theIndexData.push_back(3); theIndexData.push_back(4); + theIndexData.push_back(4); theIndexData.push_back(1); + // near to far + theIndexData.push_back(1); theIndexData.push_back(5); + theIndexData.push_back(2); theIndexData.push_back(6); + theIndexData.push_back(3); theIndexData.push_back(7); + theIndexData.push_back(4); theIndexData.push_back(8); + // far rect + theIndexData.push_back(5); theIndexData.push_back(6); + theIndexData.push_back(6); theIndexData.push_back(7); + theIndexData.push_back(7); theIndexData.push_back(8); + theIndexData.push_back(8); theIndexData.push_back(5); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + getCameraBoxAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + NVRenderIndexBuffer *theIndexBuffer = &inWidgetContext.GetOrCreateIndexBuffer( + theItemName, NVRenderComponentTypes::QT3DSU8, theIndexData.size(), + toU8DataRef(theIndexData.begin(), theIndexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +NVRenderInputAssembler *SVisualAidWidget::createBillboard(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr("Billboard"); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) + return retval; + + nvvector<QT3DSVec3> theVertexData(m_allocator, "SVisualAidWidget::theVertexData"); + nvvector<QT3DSI8> theIndexData(m_allocator, "SVisualAidWidget::theIndexData"); + + theVertexData.push_back(QT3DSVec3(-1.0, -1.0, 0.0)); + theVertexData.push_back(QT3DSVec3(0.0, 0.0, 0.0)); + theVertexData.push_back(QT3DSVec3(-1.0, 1.0, 0.0)); + theVertexData.push_back(QT3DSVec3(0.0, 1.0, 0.0)); + theVertexData.push_back(QT3DSVec3(1.0, 1.0, 0.0)); + theVertexData.push_back(QT3DSVec3(1.0, 1.0, 0.0)); + theVertexData.push_back(QT3DSVec3(1.0, -1.0, 0.0)); + theVertexData.push_back(QT3DSVec3(1.0, 0.0, 0.0)); + + theIndexData.push_back(0); theIndexData.push_back(1); theIndexData.push_back(2); + theIndexData.push_back(0); theIndexData.push_back(3); theIndexData.push_back(2); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + getBillboardAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + NVRenderIndexBuffer *theIndexBuffer = &inWidgetContext.GetOrCreateIndexBuffer( + theItemName, NVRenderComponentTypes::QT3DSU8, theIndexData.size(), + toU8DataRef(theIndexData.begin(), theIndexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +NVRenderInputAssembler *SVisualAidWidget::createDirectionalLight( + IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) +{ + CRegisteredString theItemName + = inRenderContext.GetStringTable().RegisterStr("DirectionalLight"); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) + return retval; + + nvvector<QT3DSVec3> theVertexData(m_allocator, "SVisualAidWidget::theVertexData"); + nvvector<QT3DSI8> theIndexData(m_allocator, "SVisualAidWidget::theIndexData"); + + const int ringVertices = 96; + int skip = 1; + int v = 0; + + // disc with parallel lines + for (int i = 0; i < ringVertices; ++i) { + float x = directionalLightRadius * cos(2.0 * M_PI * i / ringVertices); + float y = directionalLightRadius * sin(2.0 * M_PI * i / ringVertices); + theVertexData.push_back(QT3DSVec3(x, y, 0.0)); + if (i > 0) { + theIndexData.push_back(v - skip); + theIndexData.push_back(v); + skip = 1; + } + ++v; + if ((i%8) == 1) { + theVertexData.push_back(QT3DSVec3(x, y, -directionalLightLength)); + theIndexData.push_back(v - 1); + theIndexData.push_back(v); + skip = 2; + ++v; + } + } + theIndexData.push_back(v - skip); + theIndexData.push_back(0); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + getLightAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + NVRenderIndexBuffer *theIndexBuffer = &inWidgetContext.GetOrCreateIndexBuffer( + theItemName, NVRenderComponentTypes::QT3DSU8, theIndexData.size(), + toU8DataRef(theIndexData.begin(), theIndexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +NVRenderInputAssembler *SVisualAidWidget::createPointLight(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr("PointLight"); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) + return retval; + + nvvector<QT3DSVec3> theVertexData(m_allocator, "SVisualAidWidget::theVertexData"); + nvvector<QT3DSI16> theIndexData(m_allocator, "SVisualAidWidget::theIndexData"); + + const int latSlices = 7; + const float innerRadius = pointLightOuterRadius / 3.0f; + const float outerRadius = pointLightOuterRadius; + +#define lngVar(n) ((n&1) ? -3.0 : 3.0) +#define latVar(n) ((n&1) ? -2.0 : 2.0) + + int v = 0; + + for (int i = 0; i <= latSlices; ++i) { + float lat = (175 / latSlices) * i + 5 + latVar(i); + int longSlices = latSlices * 2 * sin(lat / 180 * M_PI); + lat -= 90.0; + + for (int j = 0; j < longSlices; ++j) { + float lng = (360 / longSlices) * j + ((j & 1) ? -180.0 : 0.0); + + float q = cos((lngVar(j) + lat) / 180 * M_PI); + float x = cos(2.0 * lng / 180 * M_PI) * q; + float y = sin((lngVar(j) + lat) / 180 * M_PI); + float z = sin(2.0 * lng / 180 * M_PI) * q; + + theVertexData.push_back(QT3DSVec3(x, y, z) * innerRadius); + theVertexData.push_back(QT3DSVec3(x, y, z) * outerRadius); + theIndexData.push_back(v); + theIndexData.push_back(v + 1); + v += 2; + } + } + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + getLightAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + NVRenderIndexBuffer *theIndexBuffer = &inWidgetContext.GetOrCreateIndexBuffer( + theItemName, NVRenderComponentTypes::QT3DSU16, theIndexData.size() * 2, + toU8DataRef(theIndexData.begin(), theIndexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +NVRenderInputAssembler *SVisualAidWidget::createAreaLight(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr("AreaLight"); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) + return retval; + + nvvector<QT3DSVec3> theVertexData(m_allocator, "SVisualAidWidget::theVertexData"); + nvvector<QT3DSI8> theIndexData(m_allocator, "SVisualAidWidget::theIndexData"); + + theVertexData.push_back(QT3DSVec3(-1, -1, 0)); + theVertexData.push_back(QT3DSVec3(1,- 1, 0)); + theVertexData.push_back(QT3DSVec3(1, 1, 0)); + theVertexData.push_back(QT3DSVec3(-1, 1, 0)); + theIndexData.push_back(0); theIndexData.push_back(1); + theIndexData.push_back(1); theIndexData.push_back(2); + theIndexData.push_back(2); theIndexData.push_back(3); + theIndexData.push_back(3); theIndexData.push_back(0); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + getLightAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + NVRenderIndexBuffer *theIndexBuffer = &inWidgetContext.GetOrCreateIndexBuffer( + theItemName, NVRenderComponentTypes::QT3DSU8, theIndexData.size(), + toU8DataRef(theIndexData.begin(), theIndexData.size())); + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), theIndexBuffer, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +NVRenderShaderProgram *SVisualAidWidget::createRenderShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString itemName = inRenderContext.GetStringTable().RegisterStr("LineShader"); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(itemName); + if (retval) + return retval; + + IShaderProgramGenerator &generator(inWidgetContext.GetProgramGenerator()); + generator.BeginProgram(); + IShaderStageGenerator &vertGenerator( + *generator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragGenerator( + *generator.GetStage(ShaderGeneratorStages::Fragment)); + vertGenerator.AddIncoming("attr_pos", "vec3"); + vertGenerator.AddUniform("mvpMatrix", "mat4"); + + vertGenerator.Append("void main() {"); + vertGenerator.Append("\tgl_Position = mvpMatrix * vec4(attr_pos, 1.0);"); + vertGenerator.Append("}"); + + fragGenerator.AddUniform("color", "vec3"); + fragGenerator.AddUniform("opacity", "float"); + fragGenerator.Append("void main() {"); + fragGenerator.Append("\tgl_FragColor.rgb = color;"); + fragGenerator.Append("\tgl_FragColor.a = opacity;"); + fragGenerator.Append("}"); + + return inWidgetContext.CompileAndStoreShader(itemName); +} + +NVRenderShaderProgram *SVisualAidWidget::createRenderCameraShader( + IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) +{ + CRegisteredString itemName = inRenderContext.GetStringTable().RegisterStr("CameraRenderShader"); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(itemName); + if (retval) + return retval; + + IShaderProgramGenerator &generator(inWidgetContext.GetProgramGenerator()); + generator.BeginProgram(); + IShaderStageGenerator &vertGenerator( + *generator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragGenerator( + *generator.GetStage(ShaderGeneratorStages::Fragment)); + vertGenerator.AddIncoming("attr_pos", "vec4"); + vertGenerator.AddUniform("invProjMatrix", "mat4"); + vertGenerator.AddUniform("mvpMatrix", "mat4"); + vertGenerator.AddUniform("orthographic", "int"); + + vertGenerator.Append("void main() {"); + vertGenerator.Append("\tvec4 pos = vec4(0.0, 0.0, 0.0, 1.0);"); + vertGenerator.Append("\tif (attr_pos.w != 0.0) {"); + vertGenerator.Append("\t\tpos = invProjMatrix * attr_pos;"); + vertGenerator.Append("\t\tpos = pos / pos.w;"); + vertGenerator.Append("\t}"); + vertGenerator.Append("\tgl_Position = mvpMatrix * vec4(pos.xyz, 1.0);"); + vertGenerator.Append("}"); + + fragGenerator.AddUniform("color", "vec3"); + fragGenerator.AddUniform("opacity", "float"); + fragGenerator.Append("void main() {"); + fragGenerator.Append("\tgl_FragColor.rgb = color;"); + fragGenerator.Append("\tgl_FragColor.a = opacity;"); + fragGenerator.Append("}"); + + return inWidgetContext.CompileAndStoreShader(itemName); +} + +NVRenderShaderProgram *SVisualAidWidget::createBillboardShader( + IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) +{ + CRegisteredString itemName = inRenderContext.GetStringTable().RegisterStr("BillboardShader"); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(itemName); + if (retval) + return retval; + + IShaderProgramGenerator &generator(inWidgetContext.GetProgramGenerator()); + generator.BeginProgram(); + IShaderStageGenerator &vertGenerator( + *generator.GetStage(ShaderGeneratorStages::Vertex)); + IShaderStageGenerator &fragGenerator( + *generator.GetStage(ShaderGeneratorStages::Fragment)); + + vertGenerator.AddIncoming("attr_pos", "vec3"); + vertGenerator.AddIncoming("attr_tc", "vec2"); + vertGenerator.AddUniform("billboardMatrix", "mat4"); + vertGenerator.AddUniform("anchor", "vec2"); + vertGenerator.AddOutgoing("texcoord", "vec2"); + + vertGenerator.Append("void main() {"); + vertGenerator.Append("texcoord = attr_tc;"); + vertGenerator.Append("\tgl_Position = billboardMatrix * vec4(attr_pos.x + anchor.x, " \ + "\tattr_pos.y + anchor.y, attr_pos.z, 1.0);"); + vertGenerator.Append("}"); + + fragGenerator.AddIncoming("texcoord", "vec2"); + fragGenerator.AddUniform("color", "vec3"); + fragGenerator.AddUniform("opacity", "float"); + fragGenerator.AddUniform("billboardTexture", "sampler2D"); + fragGenerator.Append("void main() {"); + fragGenerator.Append("\tgl_FragColor.rgb = texture2D(billboardTexture, texcoord).rgb;"); + fragGenerator.Append("\tgl_FragColor.a = texture2D(billboardTexture, texcoord).a * opacity;"); + //fragGenerator.Append("\tif (gl_FragColor.a <= 0.005) discard; "); + fragGenerator.Append("}"); + + return inWidgetContext.CompileAndStoreShader(itemName); +} + +void SVisualAidWidget::renderCamera(SNode *node, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + SCamera *camera = static_cast<SCamera *>(node); + Q_ASSERT(camera); + QT3DSMat44 projection = camera->m_Projection; + QT3DSMat44 ip = projection.getInverse(); + + if (!m_cameraBox) + m_cameraBox = createCameraBox(inWidgetContext, inRenderContext); + if (!m_renderCameraShader) + m_renderCameraShader = createRenderCameraShader(inWidgetContext, inRenderContext); + + inRenderContext.SetInputAssembler(m_cameraBox); + inRenderContext.SetActiveShader(m_renderCameraShader); + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetCullingEnabled(false); + + SWidgetRenderSetupResult theSetup(inWidgetContext, *node, RenderWidgetModes::Global); + + m_renderCameraShader->SetPropertyValue("orthographic", + camera->m_Flags.IsOrthographic() ? 1 : 0); + m_renderCameraShader->SetPropertyValue("invProjMatrix", ip); + m_renderCameraShader->SetPropertyValue("mvpMatrix", theSetup.m_PureProjection + * theSetup.m_WidgetInfo.m_NodeParentToCamera + * node->m_GlobalTransform); + + ::CColor color = CStudioPreferences::singleBoundingBoxColor(); + QT3DSVec3 colorVec(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f); + + m_renderCameraShader->SetPropertyValue("color", m_selected ? colorVec : QT3DSVec3(1, 1, 1)); + inRenderContext.SetBlendFunction( + qt3ds::render::NVRenderBlendFunctionArgument( + qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha, + qt3ds::render::NVRenderSrcBlendFunc::One, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha)); + inRenderContext.SetBlendEquation( + qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + inRenderContext.SetBlendingEnabled(true); + m_renderCameraShader->SetPropertyValue("opacity", 0.5f); + + inRenderContext.Draw(NVRenderDrawMode::Lines, m_cameraBox->GetIndexCount(), 0); +} + +void SVisualAidWidget::renderBillboard(SNode *node, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + if (!m_billboard) + m_billboard = createBillboard(inWidgetContext, inRenderContext); + if (!m_billboardShader) + m_billboardShader = createBillboardShader(inWidgetContext, inRenderContext); + + if (!m_billboardCameraTexture) { + QImage img(":/images/Asset-Camera-Pick.png"); + img = img.mirrored(); + img = img.rgbSwapped(); + m_billboardCameraTexture = inRenderContext.CreateTexture2D(); + NVDataRef<QT3DSU8> data(img.bits(), img.byteCount()); + m_billboardCameraTexture->SetTextureData(data, 0, img.width(), img.height(), + NVRenderTextureFormats::RGBA8); + + img = QImage(":/images/Asset-Light-Pick.png"); + img = img.mirrored(); + img = img.rgbSwapped(); + m_billboardLightTexture = inRenderContext.CreateTexture2D(); + data = NVDataRef<QT3DSU8>(img.bits(), img.byteCount()); + m_billboardLightTexture->SetTextureData(data, 0, img.width(), img.height(), + NVRenderTextureFormats::RGBA8); + } + + inRenderContext.SetInputAssembler(m_billboard); + inRenderContext.SetActiveShader(m_billboardShader); + inRenderContext.SetBlendingEnabled(true); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetCullingEnabled(false); + inRenderContext.SetBlendFunction( + qt3ds::render::NVRenderBlendFunctionArgument( + qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha, + qt3ds::render::NVRenderSrcBlendFunc::Zero, + qt3ds::render::NVRenderDstBlendFunc::One)); + inRenderContext.SetBlendEquation( + qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + + SWidgetRenderSetupResult theSetup(inWidgetContext, *node, RenderWidgetModes::Global); + QT3DSMat44 billboardMatrix = QT3DSMat44::createIdentity(); + billboardMatrix.setPosition((theSetup.m_WidgetInfo.m_NodeParentToCamera + * node->m_GlobalTransform).getPosition()); + billboardMatrix.scale(QT3DSVec4(8, 8, 1, 1)); + billboardMatrix = theSetup.m_PureProjection * billboardMatrix; + + m_billboardShader->SetPropertyValue("billboardMatrix", billboardMatrix); + m_billboardShader->SetPropertyValue("billboardTexture", + node->m_Type == GraphObjectTypes::Camera + ? m_billboardCameraTexture.mPtr + : m_billboardLightTexture.mPtr); + m_billboardShader->SetPropertyValue("opacity", m_selected ? 0.9f : 0.7f); + m_billboardShader->SetPropertyValue("anchor", QT3DSVec2(-1, -1)); + + inRenderContext.Draw(NVRenderDrawMode::Triangles, m_billboard->GetIndexCount(), 0); +} + +void SVisualAidWidget::renderLight(SNode *node, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + SLight *light = static_cast<SLight *>(node); + Q_ASSERT(light); + + NVRenderInputAssembler *input = nullptr; + QT3DSMat44 areaScaleMatrix = QT3DSMat44::createIdentity(); + + switch (light->m_LightType) { + case RenderLightTypes::Directional: { + if (!m_directionalLight) + m_directionalLight = createDirectionalLight(inWidgetContext, inRenderContext); + input = m_directionalLight; + } break; + + case RenderLightTypes::Point: { + if (!m_pointLight) + m_pointLight = createPointLight(inWidgetContext, inRenderContext); + input = m_pointLight; + } break; + + case RenderLightTypes::Area: { + const float w = light->m_AreaWidth * 0.5f; + const float h = light->m_AreaHeight * 0.5f; + areaScaleMatrix.scale(QT3DSVec4(w, h, 0.0, 1.0)); + if (!m_areaLight) + m_areaLight = createAreaLight(inWidgetContext, inRenderContext); + input = m_areaLight; + } break; + + default: + break; + } + if (input) { + if (!m_renderShader) + m_renderShader = createRenderShader(inWidgetContext, inRenderContext); + inRenderContext.SetInputAssembler(input); + inRenderContext.SetActiveShader(m_renderShader); + inRenderContext.SetBlendingEnabled(false); + inRenderContext.SetDepthTestEnabled(true); + inRenderContext.SetDepthWriteEnabled(false); + inRenderContext.SetCullingEnabled(false); + + SWidgetRenderSetupResult theSetup(inWidgetContext, *node, RenderWidgetModes::Global); + + m_renderShader->SetPropertyValue("mvpMatrix", theSetup.m_PureProjection + * theSetup.m_WidgetInfo.m_NodeParentToCamera + * node->m_GlobalTransform + * areaScaleMatrix); + + ::CColor color = CStudioPreferences::singleBoundingBoxColor(); + QT3DSVec3 colorVec(color.GetRed() / 255.f, + color.GetGreen() / 255.f, + color.GetBlue() / 255.f); + + m_renderShader->SetPropertyValue("color", m_selected ? colorVec : QT3DSVec3(1, 1, 1)); + + inRenderContext.SetBlendFunction( + qt3ds::render::NVRenderBlendFunctionArgument( + qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha, + qt3ds::render::NVRenderSrcBlendFunc::One, + qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha)); + inRenderContext.SetBlendEquation( + qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Add, NVRenderBlendEquation::Add)); + inRenderContext.SetBlendingEnabled(true); + m_renderShader->SetPropertyValue("opacity", 0.5f); + inRenderContext.Draw(NVRenderDrawMode::Lines, input->GetIndexCount(), 0); + } +} + +void SVisualAidWidget::SetNode(SNode *inNode) +{ + if (inNode == m_node) + return; + m_node = inNode; +} + +void SVisualAidWidget::Render(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + switch (m_node->m_Type) { + case GraphObjectTypes::Camera: + renderCamera(m_node, inWidgetContext, inRenderContext); + break; + + case GraphObjectTypes::Light: + renderLight(m_node, inWidgetContext, inRenderContext); + break; + + default: + break; + } + renderBillboard(m_node, inWidgetContext, inRenderContext); +} + +bool SVisualAidWidget::pick(IRenderWidgetContext &inWidgetContext, float &dist, QT3DSVec2 viewport, + QT3DSVec2 pos) +{ + SWidgetRenderSetupResult theSetup(inWidgetContext, *m_node, RenderWidgetModes::Global); + SCamera *pickCamera = theSetup.m_WidgetInfo.m_Camera; + QT3DSMat44 pip = pickCamera->m_Projection.getInverse(); + float x = pos.x / viewport.x; + float y = 1.0f - pos.y / viewport.y; + x = 2.0 * x - 1.0; + y = 2.0 * y - 1.0; + + QT3DSVec2 anchor(-1, -1); + + QT3DSVec4 n(x, y, -1, 1), f(x, y, 1, 1); + QT3DSVec4 np = pip.transform(n); + QT3DSVec4 fp = pip.transform(f); + + np = np * (1.0 / np.w); + fp = fp * (1.0 / fp.w); + + QT3DSMat44 billboardMatrix = QT3DSMat44::createIdentity(); + billboardMatrix.setPosition((theSetup.m_WidgetInfo.m_NodeParentToCamera + *m_node->m_GlobalTransform).getPosition()); + billboardMatrix.scale(QT3DSVec4(8, 8, 1, 1)); + + QT3DSMat44 toBillboard = billboardMatrix.getInverse(); + + np = toBillboard.transform(np); + fp = toBillboard.transform(fp); + + QT3DSVec3 c = np.getXYZ(); + QT3DSVec3 dir = (fp - np).getXYZ(); + dir.normalize(); + + QT3DSVec2 min(anchor.x - 1, anchor.y - 1); + QT3DSVec2 max(anchor.x + 1, anchor.y + 1); + + if (qAbs(dir.z) > fepsilon) { + QT3DSVec2 xy = QT3DSVec2(dir.x / dir.z, dir.y / dir.z); + QT3DSVec2 p = xy * (-c.z) + QT3DSVec2(c.x, c.y); + if (p.x >= min.x && p.x <= max.x && + p.y >= min.y && p.y <= max.y) { + QT3DSVec3 ip = QT3DSVec3(p.x, p.y, 0) - c; + dist = ip.magnitude(); + return true; + } + } + + return false; +} + +SVisualAidWidget &SVisualAidWidget::CreateVisualAidWidget(NVAllocatorCallback &inAlloc) +{ + return *QT3DS_NEW(inAlloc, SVisualAidWidget)(inAlloc); +} + +} +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.h b/src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.h new file mode 100644 index 00000000..8a91fa9c --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_VISUAL_AID_WIDGET_H +#define QT3DS_STUDIO_VISUAL_AID_WIDGET_H + +#include "StudioWidget.h" +#include "foundation/Qt3DSContainers.h" +#include "render/Qt3DSRenderShaderProgram.h" + +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" + + +namespace qt3ds { +namespace widgets { + +struct SVisualAidWidget +{ + SNode *m_node; + NVScopedRefCounted<NVRenderInputAssembler> m_billboard; + NVScopedRefCounted<NVRenderInputAssembler> m_cameraBox; + NVScopedRefCounted<NVRenderInputAssembler> m_directionalLight; + NVScopedRefCounted<NVRenderInputAssembler> m_pointLight; + NVScopedRefCounted<NVRenderInputAssembler> m_areaLight; + NVScopedRefCounted<NVRenderShaderProgram> m_renderCameraShader; + NVScopedRefCounted<NVRenderShaderProgram> m_renderShader; + NVScopedRefCounted<NVRenderShaderProgram> m_billboardShader; + NVScopedRefCounted<NVRenderTexture2D> m_billboardCameraTexture; + NVScopedRefCounted<NVRenderTexture2D> m_billboardLightTexture; + + bool m_selected; + NVAllocatorCallback &m_allocator; + volatile QT3DSI32 mRefCount; + + SVisualAidWidget(NVAllocatorCallback &inAlloc); + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_allocator) + + NVConstDataRef<NVRenderVertexBufferEntry> getCameraBoxAttributesAndStride(QT3DSU32 &stride); + NVConstDataRef<NVRenderVertexBufferEntry> getLightAttributesAndStride(QT3DSU32 &stride); + NVConstDataRef<NVRenderVertexBufferEntry> getBillboardAttributesAndStride(QT3DSU32 &stride); + void renderCamera(SNode *node, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + void renderLight(SNode *node, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + void renderBillboard(SNode *node, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + void SetNode(SNode *inNode); + NVRenderShaderProgram *createRenderShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderShaderProgram *createBillboardShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderShaderProgram *createRenderCameraShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderInputAssembler *createCameraBox(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderInputAssembler *createBillboard(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderInputAssembler *createDirectionalLight(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderInputAssembler *createPointLight(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + NVRenderInputAssembler *createAreaLight(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext); + + bool pick(IRenderWidgetContext &inWidgetContext, float &dist, + QT3DSVec2 viewport, QT3DSVec2 pos); + + void setSelected(bool selected) + { + m_selected = selected; + } + + static SVisualAidWidget &CreateVisualAidWidget(NVAllocatorCallback &inAlloc); +}; + +} +} + + +#endif // QT3DS_STUDIO_VISUAL_AID_WIDGET_H diff --git a/src/Authoring/Qt3DStudio/Render/StudioWidget.cpp b/src/Authoring/Qt3DStudio/Render/StudioWidget.cpp new file mode 100644 index 00000000..cccce4d4 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioWidget.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "StudioWidget.h" +#include "Qt3DSRenderWidgets.h" +#include "Qt3DSRenderContextCore.h" +#include "render/Qt3DSRenderContext.h" +#include "foundation/Qt3DSContainers.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" + +using namespace qt3ds::widgets; + +void IStudioWidget::CreateRect(QT3DSVec3 rectStart, QT3DSVec3 rectEnd, QT3DSVec3 orth1, QT3DSF32 axisHalfWidth, + QT3DSF32 inColorIndex, TResultVecType &outResult) +{ + outResult.push_back(QT3DSVec4(rectStart + orth1 * axisHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(rectEnd + orth1 * axisHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(rectEnd - orth1 * axisHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(rectEnd - orth1 * axisHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(rectStart - orth1 * axisHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(rectStart + orth1 * axisHalfWidth, inColorIndex)); +} + +void IStudioWidget::CreateTriangle(QT3DSVec3 triStart, QT3DSVec3 triEnd, QT3DSVec3 orth1, QT3DSF32 triHalfWidth, + QT3DSF32 inColorIndex, TResultVecType &outResult) +{ + outResult.push_back(QT3DSVec4(triStart + orth1 * triHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(triStart - orth1 * triHalfWidth, inColorIndex)); + outResult.push_back(QT3DSVec4(triEnd, inColorIndex)); +} + +NVRenderInputAssembler *IStudioWidget::CreateRingedDisc( + NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, QT3DSVec3 inDirection, QT3DSVec3 inCenterPt, QT3DSF32 inInnerRadius, + QT3DSF32 inOuterRadius, QT3DSF32 inDiscColor, QT3DSF32 inRingColor, const char *inItemName) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inItemName); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) { + return retval; + } + TResultVecType theVertexData(inAllocator, "STranslationWidget::theVertexData"); + QT3DS_ASSERT(inInnerRadius < inOuterRadius); + QT3DSI32 numSubDivisions = 50; + QT3DSF32 arcRad = 360.0f / (QT3DSF32)numSubDivisions; + TORAD(arcRad); + QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0)); + if (tempCross.magnitudeSquared() < .05f) + tempCross = inDirection.cross(QT3DSVec3(1, 0, 0)); + + QT3DSVec3 upDir = inDirection.cross(tempCross); + QT3DSVec3 leftDir = upDir.cross(inDirection); + upDir.normalize(); + leftDir.normalize(); + + for (QT3DSI32 idx = 0, numLooper = numSubDivisions; idx < numLooper; ++idx) { + QT3DSF32 startDeg = idx * 360.0f / numSubDivisions; + QT3DSF32 endDeg = (idx + 1) * 360.0f / numSubDivisions; + QT3DSF32 startRad(startDeg); + QT3DSF32 endRad(endDeg); + TORAD(startRad); + TORAD(endRad); + QT3DSF32 startSin = NVSin(startRad); + QT3DSF32 endSin = NVSin(endRad); + QT3DSF32 startCos = NVCos(startRad); + QT3DSF32 endCos = NVCos(endRad); + + QT3DSVec3 startDir = startSin * upDir + startCos * leftDir; + QT3DSVec3 endDir = endSin * upDir + endCos * leftDir; + + QT3DSVec3 discStart = inCenterPt + startDir * inInnerRadius; + QT3DSVec3 discEnd = inCenterPt + endDir * inInnerRadius; + QT3DSVec3 ringStart = inCenterPt + startDir * inOuterRadius; + QT3DSVec3 ringEnd = inCenterPt + endDir * inOuterRadius; + + // Create the Triangles + // disc first + theVertexData.push_back(QT3DSVec4(inCenterPt, inDiscColor)); + theVertexData.push_back(QT3DSVec4(discStart, inDiscColor)); + theVertexData.push_back(QT3DSVec4(discEnd, inDiscColor)); + + // Now two tris for the ring + theVertexData.push_back(QT3DSVec4(discStart, inRingColor)); + theVertexData.push_back(QT3DSVec4(ringStart, inRingColor)); + theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor)); + theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor)); + theVertexData.push_back(QT3DSVec4(discEnd, inRingColor)); + theVertexData.push_back(QT3DSVec4(discStart, inRingColor)); + } + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + IStudioWidget::GetVertexBufferAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +} + +// Create an axis with a triangle at the top. Really we create two axis that are orthogonal to each +// other. +NVRenderInputAssembler * +IStudioWidget::CreateAxis(NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, const QT3DSVec3 &inAxisDirection, + QT3DSF32 inAxisStartOffset, QT3DSF32 inAxisLength, QT3DSF32 inTriLength, + QT3DSF32 inAxisWidth, QT3DSF32 inTriWidth, const char *inAxisName) +{ + CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inAxisName); + NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName); + if (retval) { + return retval; + } + + TResultVecType theVertexData(inAllocator, "STranslationWidget::theVertexData"); + QT3DSVec3 orth1 = inAxisDirection.cross(QT3DSVec3(0, 0, 1)); + if (orth1.magnitudeSquared() < .05f) + orth1 = inAxisDirection.cross(QT3DSVec3(0, 1, 0)); + QT3DSVec3 orth2 = inAxisDirection.cross(orth1); + + // Draw a rect that starts at inAxisStartOffset + QT3DSVec3 rectStart = inAxisDirection * inAxisStartOffset; + // Rect end is also tri start, obviously + QT3DSVec3 rectEnd = inAxisDirection * (inAxisStartOffset + inAxisLength); + QT3DSVec3 triEnd = inAxisDirection * (inAxisStartOffset + inAxisLength + inTriLength); + + QT3DSF32 axisHalfWidth = inAxisWidth / 2.0f; + QT3DSF32 triHalfWidth = inTriWidth / 2.0f; + CreateRect(rectStart, rectEnd, orth1, axisHalfWidth, 0.0f, theVertexData); + CreateTriangle(rectEnd, triEnd, orth1, triHalfWidth, 0.0f, theVertexData); + CreateRect(rectStart, rectEnd, orth2, axisHalfWidth, 0.0f, theVertexData); + CreateTriangle(rectEnd, triEnd, orth2, triHalfWidth, 0.0f, theVertexData); + + QT3DSU32 stride; + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout( + IStudioWidget::GetVertexBufferAttributesAndStride(stride)); + NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size())); + + retval = &inWidgetContext.GetOrCreateInputAssembler( + theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + + return retval; +}; + +SWidgetRenderSetupResult::SWidgetRenderSetupResult(IRenderWidgetContext &inWidgetContext, + SNode &inNode, + RenderWidgetModes::Enum inWidgetMode) +{ + m_WidgetInfo = + inWidgetContext.GetWidgetRenderInformation(inNode, QT3DSVec3(0, 0, 0), inWidgetMode); + QT3DSMat44 theTranslationScale(QT3DSMat44::createIdentity()); + QT3DSMat33 theRotationMult(QT3DSMat33::createIdentity()); + bool includeNodeRotation = inWidgetMode == RenderWidgetModes::Local ? true : false; + if (includeNodeRotation) { + QT3DSMat44 theNodeRotation; + inNode.CalculateRotationMatrix(theNodeRotation); + if (inNode.m_Flags.IsLeftHanded()) { + SNode::FlipCoordinateSystem(theNodeRotation); + } + theRotationMult = + QT3DSMat33(theNodeRotation.column0.getXYZ(), theNodeRotation.column1.getXYZ(), + theNodeRotation.column2.getXYZ()); + } + + QT3DSMat33 theRotationMatrix = m_WidgetInfo.m_NormalMatrix * theRotationMult; + QT3DSMat33 theScaleMatrix(QT3DSMat33::createIdentity()); + theScaleMatrix.column0[0] = m_WidgetInfo.m_Scale; + theScaleMatrix.column1[1] = m_WidgetInfo.m_Scale; + theScaleMatrix.column2[2] = m_WidgetInfo.m_Scale; + QT3DSMat33 theCombined = theRotationMatrix * theScaleMatrix; + theTranslationScale.column0 = QT3DSVec4(theCombined.column0, 0.0f); + theTranslationScale.column1 = QT3DSVec4(theCombined.column1, 0.0f); + theTranslationScale.column2 = QT3DSVec4(theCombined.column2, 0.0f); + theTranslationScale.column3.x = m_WidgetInfo.m_Position.x; + theTranslationScale.column3.y = m_WidgetInfo.m_Position.y; + theTranslationScale.column3.z = m_WidgetInfo.m_Position.z; + m_TranslationScale = theTranslationScale; + m_PureProjection = m_WidgetInfo.m_PureProjection; + + QT3DSMat44 theCameraTransScale(QT3DSMat44::createIdentity()); + theCameraTransScale.column0 = QT3DSVec4(m_WidgetInfo.m_LookAtMatrix.column0, 0.0f); + theCameraTransScale.column1 = QT3DSVec4(m_WidgetInfo.m_LookAtMatrix.column1, 0.0f); + theCameraTransScale.column2 = QT3DSVec4(m_WidgetInfo.m_LookAtMatrix.column2, 0.0f); + theCameraTransScale.column3.x = m_WidgetInfo.m_Position.x; + theCameraTransScale.column3.y = m_WidgetInfo.m_Position.y; + theCameraTransScale.column3.z = m_WidgetInfo.m_Position.z; + theCameraTransScale.column0[0] = m_WidgetInfo.m_Scale; + theCameraTransScale.column1[1] = m_WidgetInfo.m_Scale; + theCameraTransScale.column2[2] = m_WidgetInfo.m_Scale; + m_CameraTranslationScale = theCameraTransScale; + m_SetupResult = m_WidgetInfo.m_LayerProjection * theTranslationScale; +} + +CRegisteredString IStudioWidget::GetSharedShaderName(IStringTable &inStrTable) +{ + return inStrTable.RegisterStr("IStudioWidget Shader"); +} + +CRegisteredString IStudioWidget::GetSharedPickShaderName(IStringTable &inStrTable) +{ + return inStrTable.RegisterStr("IStudioWidget Pick Shader"); +} + +NVRenderShaderProgram *IStudioWidget::CreateWidgetShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString theSharedName(GetSharedShaderName(inRenderContext.GetStringTable())); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(theSharedName); + if (retval) + return retval; + qt3ds::render::IShaderProgramGenerator &theGenerator(inWidgetContext.GetProgramGenerator()); + theGenerator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &theVertexGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &theFragmentGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_color_index", "float"); + theVertexGenerator.AddOutgoing("output_color_index", "float"); + theVertexGenerator.AddUniform("model_view_projection", "mat4"); + // These are required in order to scale the scale widget the way we want to scale it. + theVertexGenerator.AddUniform("attr_pos_add_start", "float"); + theVertexGenerator.AddUniform("attr_pos_add_amount", "vec3"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator + << "\tvec3 thePos = attr_pos;" << Endl + << "\tif ( length(thePos) > attr_pos_add_start ) thePos = thePos + attr_pos_add_amount;" + << Endl; + theVertexGenerator.Append("\tgl_Position = model_view_projection * vec4(thePos, 1.0);"); + theVertexGenerator.Append("\toutput_color_index = attr_color_index;"); + theVertexGenerator.Append("}"); + theFragmentGenerator.AddUniform("color0", "vec3"); + theFragmentGenerator.AddUniform("color1", "vec3"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tgl_FragColor.rgb = output_color_index > 0.0 ? color1 : color0;"); + theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + theFragmentGenerator.Append("}"); + return inWidgetContext.CompileAndStoreShader(theSharedName); +} + +NVRenderShaderProgram *IStudioWidget::CreateWidgetPickShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext) +{ + CRegisteredString theSharedName(GetSharedPickShaderName(inRenderContext.GetStringTable())); + NVRenderShaderProgram *retval = inWidgetContext.GetShader(theSharedName); + if (retval) + return retval; + qt3ds::render::IShaderProgramGenerator &theGenerator(inWidgetContext.GetProgramGenerator()); + theGenerator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &theVertexGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &theFragmentGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddUniform("model_view_projection", "mat4"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + theVertexGenerator.Append("}"); + theFragmentGenerator.AddUniform("object_id", "int"); + theFragmentGenerator.Append("void main() {"); + if (inRenderContext.GetRenderContextType() == NVRenderContextValues::GLES2) { + theFragmentGenerator.Append("int moddiv = object_id / 256;"); + theFragmentGenerator.Append( + "\tgl_FragColor.r = float(object_id - moddiv * 256)/255.0;"); + } else { + theFragmentGenerator.Append("\tgl_FragColor.r = float(object_id % 256)/255.0;"); + } + theFragmentGenerator.Append("\tgl_FragColor.g = float(object_id / 256)/255.0;"); + theFragmentGenerator.Append("\tgl_FragColor.b = 0.0;"); + theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;"); + theFragmentGenerator.Append("}"); + return inWidgetContext.CompileAndStoreShader(theSharedName); +} + +NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> +IStudioWidget::GetVertexBufferAttributesAndStride(QT3DSU32 &stride) +{ + static qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, + 3), + qt3ds::render::NVRenderVertexBufferEntry("attr_color_index", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 1, 12), + }; + + stride = 3 * sizeof(QT3DSF32) + 1 * sizeof(QT3DSF32); + + return toConstDataRef(theEntries, 2); +} diff --git a/src/Authoring/Qt3DStudio/Render/StudioWidget.h b/src/Authoring/Qt3DStudio/Render/StudioWidget.h new file mode 100644 index 00000000..1287612e --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioWidget.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_RENDERER_WIDGET_H +#define QT3DS_STUDIO_RENDERER_WIDGET_H +#pragma once +#include "Qt3DSRenderWidgets.h" +#include "foundation/Qt3DSRefCounted.h" +#include "StudioPickValues.h" + +namespace qt3ds { +namespace widgets { + using namespace qt3ds::render; + + struct StudioWidgetTypes + { + enum Enum { + Unknown, + Translation, + Scale, + Rotation + }; + }; + + // These are also the ids used as colors in the pick image. + struct StudioWidgetComponentIds + { + enum Enum { + NoId = 0, + XAxis, + YAxis, + ZAxis, + XPlane, + YPlane, + ZPlane, + CameraPlane, + LastId, + }; + }; + + // Functionality shared between the path widget and the various manipulation gadgets + class IStudioWidgetBase : public IRenderWidget, public NVRefCounted + { + public: + virtual void SetNode(SNode &inNode) = 0; + virtual void RenderPick(const QT3DSMat44 &inProjPreMult, NVRenderContext &inRenderContext, + QSize inWinDimensions) = 0; + virtual qt3ds::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) = 0; + }; + + typedef nvvector<QT3DSVec4> TResultVecType; + + struct SWidgetRenderSetupResult + { + QT3DSMat44 m_TranslationScale; + QT3DSMat44 m_CameraTranslationScale; + QT3DSMat44 m_PureProjection; + SWidgetRenderInformation m_WidgetInfo; + QT3DSMat44 m_SetupResult; + + SWidgetRenderSetupResult() {} + SWidgetRenderSetupResult(IRenderWidgetContext &inWidgetContext, SNode &inNode, + RenderWidgetModes::Enum inWidgetMode); + }; + + class IStudioWidget : public IStudioWidgetBase + { + public: + static CRegisteredString GetSharedShaderName(IStringTable &inStrTable); + static CRegisteredString GetSharedPickShaderName(IStringTable &inStrTable); + static NVRenderShaderProgram *CreateWidgetShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + static NVRenderShaderProgram *CreateWidgetPickShader(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext); + static NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry> + GetVertexBufferAttributesAndStride(QT3DSU32 &stride); + static NVRenderInputAssembler * + CreateRingedDisc(NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, QT3DSVec3 inDirection, QT3DSVec3 inCenterPt, + QT3DSF32 inInnerRadius, QT3DSF32 inOuterRadius, QT3DSF32 inDiscColor, + QT3DSF32 inRingColor, const char *inItemName); + static NVRenderInputAssembler * + CreateAxis(NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, const QT3DSVec3 &inAxisDirection, + QT3DSF32 inAxisStartOffset, QT3DSF32 inAxisLength, QT3DSF32 inTriLength, + QT3DSF32 inAxisWidth, QT3DSF32 inTriWidth, const char *inAxisName); + static void CreateRect(QT3DSVec3 rectStart, QT3DSVec3 rectEnd, QT3DSVec3 orth1, QT3DSF32 axisHalfWidth, + QT3DSF32 inColorIndex, TResultVecType &outResult); + static void CreateTriangle(QT3DSVec3 triStart, QT3DSVec3 triEnd, QT3DSVec3 orth1, QT3DSF32 triHalfWidth, + QT3DSF32 inColorIndex, TResultVecType &outResult); + + void SetNode(SNode &inNode) override = 0; + virtual void setAsAxisHelper(bool isAxisWidget) = 0; + + virtual StudioWidgetTypes::Enum GetWidgetType() const = 0; + virtual void SetSubComponentId(int inSubComponentId) = 0; + virtual void SetRenderWidgetMode(RenderWidgetModes::Enum inSpace) = 0; + virtual RenderWidgetModes::Enum GetRenderWidgetMode() const = 0; + // When we render the axis, we can scale the axis item itself + virtual void SetAxisScale(const QT3DSVec3 &inNewScale) = 0; + // Set the start/end positions of the rotation arc so the rotation gadget can show + // the angle and optionally display an angle readout. The start direction should + // be a normalized direction in world space, and the angle should be in radians + // inRotationAxis is expected to be a normalized direction in world space. + virtual void SetRotationEdges(const QT3DSVec3 &inStartDirection, const QT3DSVec3 &inRotationAxis, + QT3DSF32 inAngleRad, QT3DSF32 inEndLineLen) = 0; + virtual void ClearRotationEdges() = 0; + virtual bool isNodeBehindCamera() const = 0; + + static IStudioWidget &CreateTranslationWidget(NVAllocatorCallback &inAlloc); + static IStudioWidget &CreateRotationWidget(NVAllocatorCallback &inAlloc); + static IStudioWidget &CreateScaleWidget(NVAllocatorCallback &inAlloc); + }; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/StudioWidgetImpl.h b/src/Authoring/Qt3DStudio/Render/StudioWidgetImpl.h new file mode 100644 index 00000000..9205fc5d --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/StudioWidgetImpl.h @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2006 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_STUDIO_RENDERER_WIDGET_IMPL_H +#define QT3DS_STUDIO_RENDERER_WIDGET_IMPL_H +#pragma once + +#include "StudioWidget.h" +#include "foundation/Qt3DSContainers.h" +#include "render/Qt3DSRenderShaderProgram.h" + +#include "Qt3DSRenderNode.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderCamera.h" +#include "StudioPreferences.h" + +namespace qt3ds { +namespace widgets { + + typedef nvvector<QT3DSVec4> TResultVecType; + + struct SRotationWedge + { + QT3DSVec3 m_StartDirection; // world space position + QT3DSVec3 m_RotationAxis; + QT3DSF32 m_Angle; // angle in radians. + QT3DSF32 m_EndLineLen; + SRotationWedge() {} + SRotationWedge(const QT3DSVec3 &inStartDirection, const QT3DSVec3 &inRotationAxis, QT3DSF32 inAngle, + QT3DSF32 inEndLineLen) + : m_StartDirection(inStartDirection) + , m_RotationAxis(inRotationAxis) + , m_Angle(inAngle) + , m_EndLineLen(inEndLineLen) + { + } + }; + + struct SImmediateVertex + { + QT3DSVec3 m_Position; + QT3DSVec4 m_Color; + SImmediateVertex(const QT3DSVec3 &inPosition, const QT3DSVec4 &inColor) + : m_Position(inPosition) + , m_Color(inColor) + { + } + SImmediateVertex() {} + }; + + template <StudioWidgetTypes::Enum TWidgetType> + struct SStudioWidgetImpl : public IStudioWidget + { + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram *m_Shader; + NVRenderShaderProgram *m_PickShader; + QT3DSMat44 m_TranslationScale; + QT3DSMat44 m_CameraTranslationScale; + QT3DSMat44 m_PureProjection; + SWidgetRenderInformation m_WidgetInfo; + StudioWidgetComponentIds::Enum m_Highlight; + RenderWidgetModes::Enum m_WidgetMode; + + QT3DSVec3 m_AxisScale; + Option<SRotationWedge> m_RotationWedge; + nvvector<SImmediateVertex> m_ImmediateBuffer; + NVRenderVertexBuffer *m_ImmediateVertexBuffer; + NVRenderInputAssembler *m_ImmediateInputAssembler; + NVRenderShaderProgram *m_ImmediateShader; + bool m_isAxisHelper; + + SStudioWidgetImpl(NVAllocatorCallback &inAlloc) + : m_Allocator(inAlloc) + , m_Shader(NULL) + , m_PickShader(NULL) + , m_Highlight(StudioWidgetComponentIds::NoId) + , m_WidgetMode(RenderWidgetModes::Local) + , m_AxisScale(QT3DSVec3(1, 1, 1)) + , m_ImmediateBuffer(m_Allocator, "STranslationWidget::theVertexData") + , m_ImmediateVertexBuffer(NULL) + , m_ImmediateInputAssembler(NULL) + , m_ImmediateShader(NULL) + , m_isAxisHelper(false) + { + } + + void SetNode(SNode &inNode) override { m_Node = &inNode; } + + void SetSubComponentId(int inId) override + { + if (inId > 0 && inId < (int)StudioWidgetComponentIds::LastId) + m_Highlight = static_cast<StudioWidgetComponentIds::Enum>(inId); + else + m_Highlight = StudioWidgetComponentIds::NoId; + } + + StudioWidgetTypes::Enum GetWidgetType() const override { return TWidgetType; } + qt3ds::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) override + { + return qt3ds::studio::SWidgetPick((QT3DSI32)inPickIndex); + } + + void SetupRender(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) + { + m_Shader = IStudioWidget::CreateWidgetShader(inWidgetContext, inRenderContext); + m_PickShader = IStudioWidget::CreateWidgetPickShader(inWidgetContext, inRenderContext); + } + + QT3DSMat44 SetupMVP(IRenderWidgetContext &inWidgetContext) + { + SWidgetRenderSetupResult theSetup(inWidgetContext, *m_Node, m_WidgetMode); + m_TranslationScale = theSetup.m_TranslationScale; + m_CameraTranslationScale = theSetup.m_CameraTranslationScale; + m_PureProjection = theSetup.m_PureProjection; + m_WidgetInfo = theSetup.m_WidgetInfo; + return theSetup.m_SetupResult; + } + + void RenderSingleToneGeometry(StudioWidgetComponentIds::Enum inId, + const QT3DSVec3 &inOriginalColor, + NVRenderContext &inRenderContext, + NVRenderInputAssembler *inGeometryAssembly) + { + bool isHighlighted = inId == m_Highlight; + QT3DSVec3 theColor = isHighlighted ? QT3DSVec3(1, 1, 0) : inOriginalColor; + + m_Shader->SetPropertyValue("color0", theColor); + inRenderContext.SetInputAssembler(inGeometryAssembly); + inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Triangles, + inGeometryAssembly->GetVertexCount(), 0); + }; + + void RenderTwoToneGeometry(StudioWidgetComponentIds::Enum inId, QT3DSVec3 inColor0, + QT3DSVec3 inColor1, NVRenderContext &inRenderContext, + NVRenderInputAssembler *inGeometryAssembly) + { + bool isHighlighted = inId == m_Highlight; + if (isHighlighted) { + inColor0 = inColor1 = QT3DSVec3(1, 1, 0); + } + + m_Shader->SetPropertyValue("color0", inColor0); + m_Shader->SetPropertyValue("color1", inColor1); + inRenderContext.SetInputAssembler(inGeometryAssembly); + inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Triangles, + inGeometryAssembly->GetVertexCount(), 0); + }; + + void SetRenderWidgetMode(RenderWidgetModes::Enum inSpace) override { m_WidgetMode = inSpace; } + + RenderWidgetModes::Enum GetRenderWidgetMode() const override { return m_WidgetMode; } + + void RenderPickBuffer(StudioWidgetComponentIds::Enum inId, + NVRenderInputAssembler *inGeometryAssembly, + NVRenderContext &inRenderContext) + { + QT3DSI32 theObjectId = inId; + m_PickShader->SetPropertyValue("object_id", theObjectId); + inRenderContext.SetInputAssembler(inGeometryAssembly); + inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Triangles, + inGeometryAssembly->GetVertexCount(), 0); + } + + void BeginImmediateDrawing(IRenderWidgetContext &, NVRenderContext &) + { + m_ImmediateBuffer.clear(); + } + + void DrawImmediateRect(const QT3DSVec3 &rectStart, const QT3DSVec3 &rectEnd, const QT3DSVec3 &orth1, + QT3DSF32 axisHalfWidth, const QT3DSVec4 &inColor) + { + StaticAssert<sizeof(SImmediateVertex) == 7 * sizeof(QT3DSF32)>::valid_expression(); + m_ImmediateBuffer.push_back( + SImmediateVertex(rectStart + orth1 * axisHalfWidth, inColor)); + m_ImmediateBuffer.push_back(SImmediateVertex(rectEnd + orth1 * axisHalfWidth, inColor)); + m_ImmediateBuffer.push_back(SImmediateVertex(rectEnd - orth1 * axisHalfWidth, inColor)); + m_ImmediateBuffer.push_back(SImmediateVertex(rectEnd - orth1 * axisHalfWidth, inColor)); + m_ImmediateBuffer.push_back( + SImmediateVertex(rectStart - orth1 * axisHalfWidth, inColor)); + m_ImmediateBuffer.push_back( + SImmediateVertex(rectStart + orth1 * axisHalfWidth, inColor)); + } + + void DrawImmediateLine(const QT3DSVec3 &inStart, const QT3DSVec3 &inEnd, QT3DSF32 inWidth, + const QT3DSVec4 &inColor) + { + QT3DSVec3 theDir = inEnd - inStart; + theDir.normalize(); + QT3DSVec3 theTemp = theDir.cross(QT3DSVec3(0, 0, 1)); + QT3DSF32 theTempLen = theTemp.normalize(); + if (theTempLen < .01f) { + theTemp = theDir.cross(QT3DSVec3(0, 1, 0)); + theTemp.normalize(); + } + QT3DSVec3 rectStart(inStart); + QT3DSVec3 rectEnd(inEnd); + QT3DSVec3 orth1 = theDir.cross(theTemp); + QT3DSVec3 orth2 = orth1.cross(theDir); + orth1.normalize(); + orth2.normalize(); + QT3DSF32 axisHalfWidth = inWidth / 2.0f; + DrawImmediateRect(rectStart, rectEnd, orth1, axisHalfWidth, inColor); + DrawImmediateRect(rectStart, rectEnd, orth2, axisHalfWidth, inColor); + } + + void DrawFilledArc(const QT3DSVec3 &inStartPos, const QT3DSVec3 &inStartDirection, QT3DSF32 inArcLen, + const QT3DSVec3 &inRotationAxis, QT3DSF32 inAngle, const QT3DSVec4 &inFillColor) + { + // 25 small triangles per 180 degrees + QT3DSF32 arcLen = (QT3DSF32)(M_PI / 25.0f); + QT3DSU32 increments = qMax((QT3DSU32)1, (QT3DSU32)((fabs(inArcLen) / arcLen) + .5f)); + QT3DSF32 angleMultiplier = inAngle / (QT3DSF32)increments; + for (QT3DSU32 idx = 0; idx < increments; ++idx) { + QT3DSF32 localAngle = angleMultiplier * idx; + QT3DSF32 nextAngle = angleMultiplier * (idx + 1); + QT3DSQuat theQuat(localAngle, inRotationAxis); + QT3DSQuat nextQuat(nextAngle, inRotationAxis); + QT3DSVec3 startDir = theQuat.rotate(inStartDirection); + QT3DSVec3 endDir = nextQuat.rotate(inStartDirection); + QT3DSVec3 arcStart = inStartPos + (startDir * inArcLen); + QT3DSVec3 arcEnd = inStartPos + (endDir * inArcLen); + m_ImmediateBuffer.push_back(SImmediateVertex(inStartPos, inFillColor)); + m_ImmediateBuffer.push_back(SImmediateVertex(arcStart, inFillColor)); + m_ImmediateBuffer.push_back(SImmediateVertex(arcEnd, inFillColor)); + } + } + + void EndImmediateDrawing(IRenderWidgetContext &inWidgetContext, + NVRenderContext &inRenderContext, const QT3DSMat44 &inProjection) + { + static qt3ds::render::NVRenderVertexBufferEntry theEntries[] = { + qt3ds::render::NVRenderVertexBufferEntry("attr_pos", + qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3), + qt3ds::render::NVRenderVertexBufferEntry( + "attr_color", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 4, 12), + }; + + if (m_ImmediateBuffer.empty()) + return; + + CRegisteredString theShaderName( + inRenderContext.GetStringTable().RegisterStr("StudioWidgetImmedateShader")); + m_ImmediateShader = inWidgetContext.GetShader(theShaderName); + + if (m_ImmediateShader == nullptr) { + qt3ds::render::IShaderProgramGenerator &theGenerator( + inWidgetContext.GetProgramGenerator()); + theGenerator.BeginProgram(); + qt3ds::render::IShaderStageGenerator &theVertexGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Vertex)); + qt3ds::render::IShaderStageGenerator &theFragmentGenerator( + *theGenerator.GetStage(qt3ds::render::ShaderGeneratorStages::Fragment)); + theVertexGenerator.AddIncoming("attr_pos", "vec3"); + theVertexGenerator.AddIncoming("attr_color", "vec4"); + theVertexGenerator.AddUniform("model_view_projection", "mat4"); + theVertexGenerator.AddOutgoing("vertex_color", "vec4"); + theVertexGenerator.Append("void main() {"); + theVertexGenerator.Append( + "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);"); + theVertexGenerator.Append("\tvertex_color = attr_color;"); + theVertexGenerator.Append("}"); + theFragmentGenerator.Append("void main() {"); + theFragmentGenerator.Append("\tgl_FragColor = vertex_color;"); + theFragmentGenerator.Append("}"); + + m_ImmediateShader = inWidgetContext.CompileAndStoreShader(theShaderName); + } + + CRegisteredString theBufferName = + inRenderContext.GetStringTable().RegisterStr("StudioWidgetImmediateBuffer"); + m_ImmediateVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer( + theBufferName, 3 * sizeof(QT3DSF32) + 4 * sizeof(QT3DSF32), + toU8DataRef(m_ImmediateBuffer.begin(), m_ImmediateBuffer.size())); + + if (!m_ImmediateInputAssembler) { + QT3DSU32 stride = m_ImmediateVertexBuffer->GetStride(); + QT3DSU32 offset = 0; + NVRenderAttribLayout *theAttribLayout = + &inWidgetContext.CreateAttributeLayout(toConstDataRef(theEntries, 2)); + + CRegisteredString theString = + inRenderContext.GetStringTable().RegisterStr("StudioWidgetImmediateBuffer"); + m_ImmediateInputAssembler = &inWidgetContext.GetOrCreateInputAssembler( + theString, theAttribLayout, toConstDataRef(&m_ImmediateVertexBuffer, 1), nullptr, + toConstDataRef(&stride, 1), toConstDataRef(&offset, 1)); + } + + if (m_ImmediateShader && m_ImmediateInputAssembler) { + inRenderContext.SetActiveShader(m_ImmediateShader); + m_ImmediateShader->SetPropertyValue("model_view_projection", inProjection); + inRenderContext.SetInputAssembler(m_ImmediateInputAssembler); + inRenderContext.Draw(NVRenderDrawMode::Triangles, + m_ImmediateInputAssembler->GetVertexCount(), 0); + } + } + + void SetAxisScale(const QT3DSVec3 &inAxisScale) override { m_AxisScale = inAxisScale; } + + void setAsAxisHelper(bool isAxisHelper) override { m_isAxisHelper = isAxisHelper; } + + void SetRotationEdges(const QT3DSVec3 &inStartDirection, const QT3DSVec3 &inRotationAxis, + QT3DSF32 inAngleRad, QT3DSF32 inEndLineLen) override + { + m_RotationWedge = + SRotationWedge(inStartDirection, inRotationAxis, inAngleRad, inEndLineLen); + } + + void ClearRotationEdges() override { m_RotationWedge = Empty(); } + + bool isNodeBehindCamera() const override + { + return m_WidgetInfo.m_Camera && m_Node + && m_WidgetInfo.m_Camera->GetDirection().dot( + (m_Node->GetGlobalPos() - m_WidgetInfo.m_Camera->GetGlobalPos())) > 0.f; + } + + static inline QT3DSVec3 ToGLSLColor(const QColor &c) + { + return QT3DSVec3(c.redF(), c.greenF(), c.blueF()); + } + + static QT3DSVec3 GetXAxisColor() { return ToGLSLColor(CStudioPreferences::xAxisColor()); } + static QT3DSVec3 GetYAxisColor() { return ToGLSLColor(CStudioPreferences::yAxisColor()); } + static QT3DSVec3 GetZAxisColor() { return ToGLSLColor(CStudioPreferences::zAxisColor()); } + + static inline QT3DSF32 GetDiscPos() { return 65.0f; } + static inline QT3DSF32 GetDiscRadius() { return 7.0f; } + static inline QT3DSF32 GetDiscRingRadius() { return GetDiscRadius() + 2.0f; } + }; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Render/WGLRenderContext.cpp b/src/Authoring/Qt3DStudio/Render/WGLRenderContext.cpp new file mode 100644 index 00000000..57428631 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/WGLRenderContext.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 1999-2001 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" + +#include "WGLRenderContext.h" +#include "foundation/TrackingAllocator.h" +#include "foundation/Qt3DSFoundation.h" +#include "render/Qt3DSRenderContext.h" +#include "EASTL/string.h" +#include "foundation/Qt3DSLogging.h" + +#include <QtGui/QOpenGLContext> +#include <QtGui/QSurfaceFormat> +#include <QtWidgets/qopenglwidget.h> + +//QT3DS_DEFINE_THISFILE; + +//============================================================================= +/** + * Constructor: Creates the object + */ +CWGLRenderContext::CWGLRenderContext(Qt3DSWindow inWindow) + : m_qtContext(0) + , m_Foundation(Q3DStudio::Foundation::SStudioFoundation::Create()) +{ + Open(inWindow); +} + +//============================================================================= +/** + * Destructor: Destroys the object. + */ +CWGLRenderContext::~CWGLRenderContext() +{ + Close(); +} + +//============================================================================= +/** + * Open the render context. + * @param inRenderWindow window handle + * @param inWindowSize window size + */ +void CWGLRenderContext::Open(Qt3DSWindow inRenderWindow) +{ + // needed because NVidia cards will fail all the system functions below if there is no window. + // note: the only time inRenderWindow is nullptr is when CThumbnailGenerator is used. Bug3075. + if (!inRenderWindow) + return; + + QObject* qObject = static_cast<QObject*>(inRenderWindow); + QOpenGLWidget* qRenderWidget = qobject_cast<QOpenGLWidget*>(qObject); + Q_ASSERT(qRenderWidget); + + OpenNormalContext(qRenderWidget); +} + +static bool compareContextVersion(QSurfaceFormat a, QSurfaceFormat b) +{ + if (a.renderableType() != b.renderableType()) + return false; + if (a.majorVersion() != b.majorVersion()) + return false; + if (a.minorVersion() > b.minorVersion()) + return false; + return true; +} + +QSurfaceFormat CWGLRenderContext::selectSurfaceFormat(QOpenGLWidget *window) +{ +#if defined(Q_OS_MACOS) + Q_UNUSED(window) + QSurfaceFormat openGL33Format; + openGL33Format.setRenderableType(QSurfaceFormat::OpenGL); + openGL33Format.setProfile(QSurfaceFormat::CoreProfile); + openGL33Format.setMajorVersion(3); + openGL33Format.setMinorVersion(3); + openGL33Format.setStencilBufferSize(8); + QSurfaceFormat::setDefaultFormat(openGL33Format); + return openGL33Format; +#else + struct ContextVersion { + int major; + int minor; + qt3ds::render::NVRenderContextType contextType; + }; + + ContextVersion versions[] = { + {4, 5, qt3ds::render::NVRenderContextValues::GL4}, + {4, 4, qt3ds::render::NVRenderContextValues::GL4}, + {4, 3, qt3ds::render::NVRenderContextValues::GL4}, + {4, 2, qt3ds::render::NVRenderContextValues::GL4}, + {4, 1, qt3ds::render::NVRenderContextValues::GL4}, + {3, 3, qt3ds::render::NVRenderContextValues::GL3}, + {2, 1, qt3ds::render::NVRenderContextValues::GL2}, + {2, 0, qt3ds::render::NVRenderContextValues::GLES2}, + {0, 0, qt3ds::render::NVRenderContextValues::NullContext} + }; + + QSurfaceFormat result = window->format(); + bool valid = false; + + for (const auto& ver : versions) { + if (ver.contextType == qt3ds::render::NVRenderContextValues::NullContext) + break; + + // make an offscreen surface + context to query version + QScopedPointer<QOffscreenSurface> offscreenSurface(new QOffscreenSurface); + + QSurfaceFormat format = window->format(); + if (ver.contextType == qt3ds::render::NVRenderContextValues::GLES2) { + format.setRenderableType(QSurfaceFormat::OpenGLES); + } else { + format.setRenderableType(QSurfaceFormat::OpenGL); + if (ver.major >= 2) + format.setProfile(QSurfaceFormat::CoreProfile); + } + format.setMajorVersion(ver.major); + format.setMinorVersion(ver.minor); + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + + offscreenSurface->setFormat(format); + offscreenSurface->create(); + Q_ASSERT(offscreenSurface->isValid()); + + QScopedPointer<QOpenGLContext> queryContext(new QOpenGLContext); + queryContext->setFormat(format); + if (queryContext->create() && compareContextVersion(format, queryContext->format())) { + valid = true; + result = format; + break; + } + } // of version test iteration + + if (!valid) { + qFatal("Unable to select suitable OpenGL context"); + } + + qDebug() << Q_FUNC_INFO << "selected surface format:" << result; + QSurfaceFormat::setDefaultFormat(result); + return result; +#endif +} + +//============================================================================= +/** + * Open a non-multisample render context. + * @param inRenderWindow window handle + * @param inWindowSize window size + * @param inPixelDesc the pixel descriptor struct + */ +void CWGLRenderContext::OpenNormalContext(QOpenGLWidget* inRenderWindow) +{ + // Close before trying to open + Close(); + + // Save off the window + m_Window = inRenderWindow; + m_qtContext = m_Window->context(); + Q_ASSERT(m_qtContext); + + qt3ds::foundation::NVScopedRefCounted<qt3ds::foundation::IStringTable> theStringTable = + qt3ds::foundation::IStringTable::CreateStringTable(*m_Foundation.m_AllocatorCallback); + QSurfaceFormat contextFormat = m_qtContext->format(); + m_RenderContext = NVScopedRefCounted<NVRenderContext>( + NVRenderContext::CreateGL(*m_Foundation.m_Foundation, *theStringTable, + contextFormat)); + if (m_RenderContext) { + m_RenderContext->SetDefaultDepthBufferBitCount(contextFormat.depthBufferSize()); + m_RenderContext->SetDefaultRenderTarget(inRenderWindow->defaultFramebufferObject()); + } +} + +//============================================================================= +/** + * Close the render context. + */ +void CWGLRenderContext::Close() +{ + m_qtContext = 0; +} + +//============================================================================= +/** + * Prepare the render context to begin rendering. + */ +void CWGLRenderContext::BeginRender() +{ + m_Window->makeCurrent(); + if (m_lastWidgetFBO != m_Window->defaultFramebufferObject()) { + m_lastWidgetFBO = m_Window->defaultFramebufferObject(); + m_RenderContext->SetDefaultRenderTarget(m_lastWidgetFBO); + } +} + +//============================================================================= +/** + * Finalize the rendering of this frame, and present the result. + */ +void CWGLRenderContext::EndRender() +{ + m_Window->doneCurrent(); +} + +void CWGLRenderContext::resized() +{ + if (m_RenderContext) { + m_RenderContext->SetDefaultRenderTarget(m_Window->defaultFramebufferObject()); + } +} + +void CWGLRenderContext::requestRender() +{ + m_Window->update(); +} diff --git a/src/Authoring/Qt3DStudio/Render/WGLRenderContext.h b/src/Authoring/Qt3DStudio/Render/WGLRenderContext.h new file mode 100644 index 00000000..100c459a --- /dev/null +++ b/src/Authoring/Qt3DStudio/Render/WGLRenderContext.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 1999-2002 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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$ +** +****************************************************************************/ +//============================================================================== +// Prefix +//============================================================================== +#ifndef __WGLRENDERCONTEXT_H_ +#define __WGLRENDERCONTEXT_H_ +#include "PlatformTypes.h" +#include "Q3DStudioNVFoundation.h" +#include "render/Qt3DSRenderBaseTypes.h" + +#include <QSurfaceFormat> + +QT_FORWARD_DECLARE_CLASS(QOpenGLContext) +QT_FORWARD_DECLARE_CLASS(QOpenGLWidget) + +namespace qt3ds { +class NVFoundation; +} + +namespace qt3ds { +namespace render { + class NVRenderContext; + class CAllocator; +} +} + +using qt3ds::NVFoundation; +using qt3ds::render::NVRenderContext; +using qt3ds::render::CAllocator; +using qt3ds::foundation::NVScopedRefCounted; + +class GLogErrorString; + +//============================================================================== +/** + * @class CWGLRenderContext: The OpenGL subclass of the CRenderContext class. + */ +class CWGLRenderContext +{ + // Field Members +protected: + QOpenGLContext *m_qtContext; + + Q3DStudio::Foundation::SStudioFoundation m_Foundation; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + QOpenGLWidget* m_Window; + qt3ds::render::NVRenderContextType m_ContextType; + + quint32 m_lastWidgetFBO = 0; + + // Construction +public: + CWGLRenderContext(Qt3DSWindow inRenderWindow); + ~CWGLRenderContext(); + + // Access +public: + void BeginRender(); + void EndRender(); + + // Only available after open. + NVRenderContext &GetRenderContext() { return *m_RenderContext; } + + static QSurfaceFormat selectSurfaceFormat(QOpenGLWidget* window); + + void resized(); + + void requestRender(); + + // Implementation +protected: + void Open(Qt3DSWindow inRenderWindow); + + void OpenNormalContext(QOpenGLWidget* inRenderWindow); + void Close(); +}; + +#endif // __WGLRENDERCONTEXT_H_ |