summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Render
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Render')
-rw-r--r--src/Authoring/Qt3DStudio/Render/IStudioRenderer.h99
-rw-r--r--src/Authoring/Qt3DStudio/Render/PathWidget.cpp512
-rw-r--r--src/Authoring/Qt3DStudio/Render/PathWidget.h49
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioGradientWidget.cpp200
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioGradientWidget.h89
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.cpp205
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioHelperGridWidget.h78
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioPickValues.h217
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioRenderer.cpp1160
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioRendererImpl.h132
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.cpp4346
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioRendererTranslation.h732
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioRotationWidget.cpp440
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioScaleWidget.cpp270
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.cpp374
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioSubPresentationRenderer.h124
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioTranslationWidget.cpp261
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.cpp730
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioVisualAidWidget.h107
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioWidget.cpp321
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioWidget.h142
-rw-r--r--src/Authoring/Qt3DStudio/Render/StudioWidgetImpl.h358
-rw-r--r--src/Authoring/Qt3DStudio/Render/WGLRenderContext.cpp243
-rw-r--r--src/Authoring/Qt3DStudio/Render/WGLRenderContext.h105
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_