summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/Qt3DSRenderPathManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/Qt3DSRenderPathManager.cpp')
-rw-r--r--src/runtimerender/Qt3DSRenderPathManager.cpp1964
1 files changed, 1964 insertions, 0 deletions
diff --git a/src/runtimerender/Qt3DSRenderPathManager.cpp b/src/runtimerender/Qt3DSRenderPathManager.cpp
new file mode 100644
index 0000000..5b2c51d
--- /dev/null
+++ b/src/runtimerender/Qt3DSRenderPathManager.cpp
@@ -0,0 +1,1964 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderPathManager.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSIntrinsics.h"
+#include "foundation/Qt3DSContainers.h"
+#include "EASTL/string.h"
+#include "Qt3DSRenderContextCore.h"
+#include "foundation/Utils.h"
+#include "foundation/StringConversionImpl.h"
+#include "render/Qt3DSRenderVertexBuffer.h"
+#include "render/Qt3DSRenderInputAssembler.h"
+#include "Qt3DSRenderPath.h"
+#include "EASTL/sort.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderVertexBuffer.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "Qt3DSRenderShaderCodeGenerator.h"
+#include "Qt3DSRenderDynamicObjectSystem.h"
+#include "Qt3DSRenderCamera.h"
+#include "Qt3DSRenderPathRenderContext.h"
+#include "Qt3DSRenderShaderCodeGeneratorV2.h"
+#include "Qt3DSRenderDefaultMaterialShaderGenerator.h"
+#include "Qt3DSRenderCustomMaterialShaderGenerator.h"
+#include "Qt3DSRenderCustomMaterial.h"
+#include "Qt3DSRenderCustomMaterialSystem.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "rendererimpl/Qt3DSVertexPipelineImpl.h"
+#include "foundation/Qt3DSMathUtils.h"
+#include "render/Qt3DSRenderPathRender.h"
+#include "render/Qt3DSRenderPathSpecification.h"
+#include "Qt3DSRenderPathSubPath.h"
+#include "Qt3DSImportPath.h"
+#include "Qt3DSRenderPathMath.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "foundation/Qt3DSMutex.h"
+
+using namespace qt3ds::render;
+using qt3ds::render::NVRenderCachedShaderProperty;
+using qt3ds::render::NVRenderCachedShaderBuffer;
+using qt3ds::render::NVRenderStencilFunctionArgument;
+using qt3ds::render::NVRenderBoolOp;
+using qt3ds::render::NVRenderStencilOperationArgument;
+using qt3ds::render::NVRenderStencilOp;
+
+typedef qt3dsimp::SPathBuffer TImportPathBuffer;
+using namespace qt3ds::render::path;
+
+typedef eastl::pair<CRegisteredString, CRegisteredString> TStrStrPair;
+
+namespace eastl {
+template <>
+struct hash<TStrStrPair>
+{
+ size_t operator()(const TStrStrPair &item) const
+ {
+ return eastl::hash<CRegisteredString>()(item.first)
+ ^ eastl::hash<CRegisteredString>()(item.second);
+ }
+};
+}
+
+struct SPathShaderMapKey
+{
+ CRegisteredString m_Name;
+ SShaderDefaultMaterialKey m_MaterialKey;
+ size_t m_HashCode;
+ SPathShaderMapKey(CRegisteredString inName, SShaderDefaultMaterialKey inKey)
+ : m_Name(inName)
+ , m_MaterialKey(inKey)
+ {
+ m_HashCode = eastl::hash<TStrStrPair>()(m_Name) ^ m_MaterialKey.hash();
+ }
+ bool operator==(const SPathShaderMapKey &inKey) const
+ {
+ return m_Name == inKey.m_Name && m_MaterialKey == inKey.m_MaterialKey;
+ }
+};
+
+namespace eastl {
+template <>
+struct hash<SPathShaderMapKey>
+{
+ size_t operator()(const SPathShaderMapKey &inKey) const { return inKey.m_HashCode; }
+};
+}
+
+namespace {
+
+struct SPathSubPathBuffer
+{
+ NVAllocatorCallback &m_Allocator;
+ nvvector<SPathAnchorPoint> m_SourceData;
+ SPathDirtyFlags m_Flags;
+ SPathSubPath &m_SubPath;
+ bool m_Closed;
+
+ QT3DSI32 m_RefCount;
+
+ SPathSubPathBuffer(NVAllocatorCallback &alloc, SPathSubPath &inSubPath)
+ : m_Allocator(alloc)
+ , m_SourceData(alloc, "m_SourceData")
+ , m_SubPath(inSubPath)
+ , m_Closed(false)
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() { atomicIncrement(&m_RefCount); }
+ void release()
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Allocator);
+ NVDelete(alloc, this);
+ }
+ }
+};
+
+struct SImportPathWrapper
+{
+ NVAllocatorCallback &m_Alloc;
+ qt3dsimp::SPathBuffer *m_Path;
+ QT3DSI32 m_RefCount;
+
+ SImportPathWrapper(NVAllocatorCallback &inAlloc, qt3dsimp::SPathBuffer &inPath)
+ : m_Alloc(inAlloc)
+ , m_Path(&inPath)
+ , m_RefCount(0)
+ {
+ }
+
+ ~SImportPathWrapper() { m_Path->Free(m_Alloc); }
+
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Alloc);
+ NVDelete(alloc, this);
+ }
+ }
+};
+
+typedef NVScopedRefCounted<SImportPathWrapper> TPathBufferPtr;
+
+struct SPathBuffer
+{
+ NVAllocatorCallback &m_Allocator;
+ nvvector<NVScopedRefCounted<SPathSubPathBuffer>> m_SubPaths;
+ TPathBufferPtr m_PathBuffer;
+
+ NVScopedRefCounted<NVRenderVertexBuffer> m_PatchData;
+ NVScopedRefCounted<NVRenderInputAssembler> m_InputAssembler;
+ NVScopedRefCounted<NVRenderPathRender> m_PathRender;
+
+ QT3DSVec2 m_BeginTaperData;
+ QT3DSVec2 m_EndTaperData;
+ QT3DSU32 m_NumVertexes;
+ PathTypes::Enum m_PathType;
+ QT3DSF32 m_Width;
+ QT3DSF32 m_CPUError;
+ NVBounds3 m_Bounds;
+ Option<STaperInformation> m_BeginTaper;
+ Option<STaperInformation> m_EndTaper;
+ CRegisteredString m_SourcePath;
+
+ // Cached data for geometry paths
+
+ SPathDirtyFlags m_Flags;
+
+ QT3DSI32 m_RefCount;
+
+ SPathBuffer(NVAllocatorCallback &alloc)
+ : m_Allocator(alloc)
+ , m_SubPaths(alloc, "m_SubPaths")
+ , m_NumVertexes(0)
+ , m_PathType(PathTypes::Geometry)
+ , m_Width(0.0f)
+ , m_CPUError(0.0f)
+ , m_Bounds(NVBounds3::empty())
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() { atomicIncrement(&m_RefCount); }
+ void release()
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Allocator);
+ NVDelete(alloc, this);
+ }
+ }
+
+ void ClearGeometryPathData()
+ {
+ m_PatchData = NULL;
+ m_InputAssembler = NULL;
+ }
+
+ void ClearPaintedPathData() { m_PathRender = NULL; }
+
+ qt3dsimp::SPathBuffer GetPathData(qt3dsimp::IPathBufferBuilder &inSpec)
+ {
+ if (m_SubPaths.size()) {
+ inSpec.Clear();
+ for (QT3DSU32 idx = 0, end = m_SubPaths.size(); idx < end; ++idx) {
+ const SPathSubPathBuffer &theSubPathBuffer(*m_SubPaths[idx]);
+ for (QT3DSU32 equationIdx = 0, equationEnd = theSubPathBuffer.m_SourceData.size();
+ equationIdx < equationEnd; ++equationIdx) {
+ const SPathAnchorPoint &thePoint = theSubPathBuffer.m_SourceData[equationIdx];
+ if (equationIdx == 0) {
+ inSpec.MoveTo(thePoint.m_Position);
+ } else {
+ const SPathAnchorPoint &thePrevPoint =
+ theSubPathBuffer.m_SourceData[equationIdx - 1];
+ QT3DSVec2 c1 = IPathManager::GetControlPointFromAngleDistance(
+ thePrevPoint.m_Position, thePrevPoint.m_OutgoingAngle,
+ thePrevPoint.m_OutgoingDistance);
+ QT3DSVec2 c2 = IPathManager::GetControlPointFromAngleDistance(
+ thePoint.m_Position, thePoint.m_IncomingAngle,
+ thePoint.m_IncomingDistance);
+ QT3DSVec2 p2 = thePoint.m_Position;
+ inSpec.CubicCurveTo(c1, c2, p2);
+ }
+ }
+ if (theSubPathBuffer.m_Closed)
+ inSpec.Close();
+ }
+ return inSpec.GetPathBuffer();
+ } else if (m_PathBuffer.mPtr)
+ return *m_PathBuffer.mPtr->m_Path;
+ return qt3dsimp::SPathBuffer();
+ }
+
+ void SetPathType(PathTypes::Enum inPathType)
+ {
+ if (inPathType != m_PathType) {
+ switch (m_PathType) {
+ case PathTypes::Geometry:
+ ClearGeometryPathData();
+ break;
+ case PathTypes::Painted:
+ ClearPaintedPathData();
+ break;
+ default:
+ QT3DS_ALWAYS_ASSERT_MESSAGE("Unexpected path type");
+ // No further processing for unexpected path type
+ return;
+ }
+ m_Flags.clearOrSet(true, PathDirtyFlagValues::PathType);
+ }
+ m_PathType = inPathType;
+ }
+
+ static Option<STaperInformation> ToTaperInfo(PathCapping::Enum capping, QT3DSF32 capOffset,
+ QT3DSF32 capOpacity, QT3DSF32 capWidth)
+ {
+ if (capping == PathCapping::Noner)
+ return Empty();
+
+ return STaperInformation(capOffset, capOpacity, capWidth);
+ }
+
+ void SetBeginTaperInfo(PathCapping::Enum capping, QT3DSF32 capOffset, QT3DSF32 capOpacity,
+ QT3DSF32 capWidth)
+ {
+ Option<STaperInformation> newBeginInfo =
+ ToTaperInfo(capping, capOffset, capOpacity, capWidth);
+ if (!OptionEquals(newBeginInfo, m_BeginTaper)) {
+ m_BeginTaper = newBeginInfo;
+ m_Flags.clearOrSet(true, PathDirtyFlagValues::BeginTaper);
+ }
+ }
+
+ void SetEndTaperInfo(PathCapping::Enum capping, QT3DSF32 capOffset, QT3DSF32 capOpacity,
+ QT3DSF32 capWidth)
+ {
+ Option<STaperInformation> newEndInfo =
+ ToTaperInfo(capping, capOffset, capOpacity, capWidth);
+ if (!OptionEquals(newEndInfo, m_EndTaper)) {
+ m_EndTaper = newEndInfo;
+ m_Flags.clearOrSet(true, PathDirtyFlagValues::EndTaper);
+ }
+ }
+
+ void SetWidth(QT3DSF32 inWidth)
+ {
+ if (inWidth != m_Width) {
+ m_Width = inWidth;
+ m_Flags.clearOrSet(true, PathDirtyFlagValues::Width);
+ }
+ }
+
+ void SetCPUError(QT3DSF32 inError)
+ {
+ if (inError != m_CPUError) {
+ m_CPUError = inError;
+ m_Flags.clearOrSet(true, PathDirtyFlagValues::CPUError);
+ }
+ }
+};
+
+struct SPathGeneratedShader
+{
+ NVAllocatorCallback &m_Allocator;
+ NVRenderShaderProgram &m_Shader;
+ NVRenderCachedShaderProperty<QT3DSF32> m_Width;
+ NVRenderCachedShaderProperty<QT3DSF32> m_InnerTessAmount;
+ NVRenderCachedShaderProperty<QT3DSF32> m_EdgeTessAmount;
+ NVRenderCachedShaderProperty<QT3DSVec2> m_BeginTaperData;
+ NVRenderCachedShaderProperty<QT3DSVec2> m_EndTaperData;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_WireframeViewMatrix;
+
+ QT3DSI32 m_RefCount;
+
+ SPathGeneratedShader(NVRenderShaderProgram &sh, NVAllocatorCallback &alloc)
+ : m_Allocator(alloc)
+ , m_Shader(sh)
+ , m_Width("pathWidth", sh)
+ , m_InnerTessAmount("tessInnerLevel", sh)
+ , m_EdgeTessAmount("tessEdgeLevel", sh)
+ , m_BeginTaperData("beginTaperInfo", sh)
+ , m_EndTaperData("endTaperInfo", sh)
+ , m_WireframeViewMatrix("viewport_matrix", sh)
+ , m_RefCount(0)
+ {
+ m_Shader.addRef();
+ }
+ ~SPathGeneratedShader() { m_Shader.release(); }
+
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &allocator(m_Allocator);
+ NVDelete(allocator, this);
+ }
+ }
+};
+
+struct SPathVertexPipeline : public SVertexPipelineImpl
+{
+
+ SPathVertexPipeline(IShaderProgramGenerator &inProgGenerator,
+ IMaterialShaderGenerator &inMaterialGenerator, NVAllocatorCallback &inAlloc,
+ IStringTable &inStringTable, bool inWireframe)
+ : SVertexPipelineImpl(inAlloc, inMaterialGenerator, inProgGenerator, inStringTable,
+ inWireframe)
+ {
+ }
+
+ // Trues true if the code was *not* set.
+ bool SetCode(GenerationFlagValues::Enum inCode)
+ {
+ if (((QT3DSU32)m_GenerationFlags & inCode) != 0)
+ return true;
+ m_GenerationFlags |= inCode;
+ return false;
+ }
+
+ void AssignTessEvalVarying(const char8_t *inVarName, const char8_t *inVarValueExpr)
+ {
+ const char8_t *ext = "";
+ if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry)
+ ext = "TE";
+ TessEval() << "\t" << inVarName << ext << " = " << inVarValueExpr << ";" << Endl;
+ }
+
+ void AssignOutput(const char8_t *inVarName, const char8_t *inVarValueExpr) override
+ {
+ AssignTessEvalVarying(inVarName, inVarValueExpr);
+ }
+
+ void InitializeTessShaders()
+ {
+ IShaderStageGenerator &theTessControl(TessControl());
+ IShaderStageGenerator &theTessEval(TessEval());
+
+ // first setup tessellation control shader
+ theTessControl.AddUniform("tessEdgeLevel", "float");
+ theTessControl.AddUniform("tessInnerLevel", "float");
+
+ theTessControl.AddInclude("tessellationPath.glsllib");
+
+ theTessControl.Append("void main() {\n");
+ theTessControl.Append(
+ "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;");
+ theTessControl.Append("\ttessShader( tessEdgeLevel, tessInnerLevel );\n");
+
+ bool hasGeometryShader =
+ ProgramGenerator().GetStage(ShaderGeneratorStages::Geometry) != NULL;
+
+ // second setup tessellation control shader
+ eastl::string outExt("");
+ if (hasGeometryShader)
+ outExt = "TE";
+
+ theTessEval.AddInclude("tessellationPath.glsllib");
+ theTessEval.AddUniform("normal_matrix", "mat3");
+ theTessEval.AddUniform("model_view_projection", "mat4");
+ theTessEval.AddUniform("pathWidth", "float");
+ theTessEval.AddUniform("material_diffuse", "vec4");
+ AddInterpolationParameter("varTexCoord0", "vec2");
+ AddInterpolationParameter("varTessOpacity", "float");
+
+ theTessEval.Append("void main() {\n");
+ theTessEval.Append("\tSTessShaderResult shaderResult = tessShader( pathWidth );\n");
+ theTessEval.Append("\tvec3 pos = shaderResult.m_Position;\n");
+ AssignTessEvalVarying("varTessOpacity", "shaderResult.m_Opacity");
+ AssignTessEvalVarying("varTexCoord0", "shaderResult.m_TexCoord.xy");
+ if (hasGeometryShader)
+ theTessEval << "\tvec2 varTexCoord0 = shaderResult.m_TexCoord.xy;\n";
+
+ theTessEval << "\tvec3 object_normal = vec3(0.0, 0.0, 1.0);\n";
+ theTessEval << "\tvec3 world_normal = normal_matrix * object_normal;\n";
+ theTessEval << "\tvec3 tangent = vec3( shaderResult.m_Tangent, 0.0 );\n";
+ theTessEval << "\tvec3 binormal = vec3( shaderResult.m_Binormal, 0.0 );\n";
+
+ // These are necessary for texture generation.
+ theTessEval << "\tvec3 uTransform;" << Endl;
+ theTessEval << "\tvec3 vTransform;" << Endl;
+
+ if (m_DisplacementImage) {
+ MaterialGenerator().GenerateImageUVCoordinates(*this, m_DisplacementIdx, 0,
+ *m_DisplacementImage);
+ theTessEval.AddUniform("displaceAmount", "float");
+ theTessEval.AddUniform("model_matrix", "mat4");
+ theTessEval.AddInclude("defaultMaterialFileDisplacementTexture.glsllib");
+ IDefaultMaterialShaderGenerator::SImageVariableNames theVarNames =
+ MaterialGenerator().GetImageVariableNames(m_DisplacementIdx);
+
+ theTessEval.AddUniform(theVarNames.m_ImageSampler, "sampler2D");
+ IDefaultMaterialShaderGenerator::SImageVariableNames theNames =
+ MaterialGenerator().GetImageVariableNames(m_DisplacementIdx);
+ theTessEval << "\tpos = defaultMaterialFileDisplacementTexture( "
+ << theNames.m_ImageSampler << ", displaceAmount, "
+ << theNames.m_ImageFragCoords << outExt.c_str() << ", vec3( 0.0, 0.0, 1.0 )"
+ << ", pos.xyz );" << Endl;
+ }
+ }
+ void FinalizeTessControlShader() {}
+
+ void FinalizeTessEvaluationShader()
+ {
+ eastl::string outExt("");
+ if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry)
+ outExt = "TE";
+
+ IShaderStageGenerator &tessEvalShader(
+ *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval));
+ tessEvalShader.Append("\tgl_Position = model_view_projection * vec4( pos, 1.0 );\n");
+ }
+
+ void BeginVertexGeneration(QT3DSU32 displacementImageIdx,
+ SRenderableImage *displacementImage) override
+ {
+ SetupDisplacement(displacementImageIdx, displacementImage);
+
+ TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags());
+ theStages |= ShaderGeneratorStages::TessControl;
+ theStages |= ShaderGeneratorStages::TessEval;
+ if (m_Wireframe) {
+ theStages |= ShaderGeneratorStages::Geometry;
+ }
+ ProgramGenerator().BeginProgram(theStages);
+ InitializeTessShaders();
+ if (m_Wireframe) {
+ InitializeWireframeGeometryShader();
+ }
+ // Open up each stage.
+ IShaderStageGenerator &vertexShader(Vertex());
+
+ vertexShader.AddIncoming("attr_pos", "vec4");
+
+ // useless vert shader because real work is done in TES.
+ vertexShader << "void main()\n"
+ "{\n";
+ vertexShader << "\tgl_Position = attr_pos;\n"; // if tessellation is enabled pass down
+ // object coordinates;
+ vertexShader << "}\n";
+ }
+
+ void BeginFragmentGeneration() override
+ {
+ Fragment().AddUniform("material_diffuse", "vec4");
+ Fragment() << "void main()" << Endl << "{" << Endl;
+ // We do not pass object opacity through the pipeline.
+ Fragment() << "\tfloat object_opacity = varTessOpacity * material_diffuse.a;" << Endl;
+ }
+ void DoGenerateUVCoords(QT3DSU32) override
+ {
+ // these are always generated regardless
+ }
+
+ // fragment shader expects varying vertex normal
+ // lighting in vertex pipeline expects world_normal
+ void DoGenerateWorldNormal() override { AssignTessEvalVarying("varNormal", "world_normal"); }
+ void DoGenerateObjectNormal() override
+ {
+ AssignTessEvalVarying("varObjectNormal", "object_normal");
+ }
+ void DoGenerateWorldPosition() override
+ {
+ TessEval().AddUniform("model_matrix", "mat4");
+ TessEval()
+ << "\tvec3 local_model_world_position = vec3((model_matrix * vec4(pos, 1.0)).xyz);\n";
+ }
+ void DoGenerateVarTangentAndBinormal() override
+ {
+ TessEval().AddUniform("normal_matrix", "mat3");
+ AssignOutput("varTangent", "normal_matrix * tangent");
+ AssignOutput("varBinormal", "normal_matrix * binormal");
+ }
+
+ void DoGenerateVertexColor() override
+ {
+ Vertex().AddIncoming("attr_color", "vec3");
+ Vertex() << "\tvarColor = attr_color;" << Endl;
+ }
+
+ void EndVertexGeneration(bool) override
+ {
+
+ if (HasTessellation()) {
+ // finalize tess control shader
+ FinalizeTessControlShader();
+ // finalize tess evaluation shader
+ FinalizeTessEvaluationShader();
+
+ TessControl().Append("}");
+ TessEval().Append("}");
+ }
+ if (m_Wireframe) {
+ // finalize geometry shader
+ FinalizeWireframeGeometryShader();
+ Geometry().Append("}");
+ }
+ }
+
+ void EndFragmentGeneration(bool) override { Fragment().Append("}"); }
+
+ void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override
+ {
+ m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType)));
+ Fragment().AddIncoming(inName, inType);
+ if (HasTessellation()) {
+ eastl::string nameBuilder;
+ nameBuilder.assign(inName);
+ if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry)
+ nameBuilder.append("TE");
+
+ TessEval().AddOutgoing(nameBuilder.c_str(), inType);
+ }
+ }
+
+ IShaderStageGenerator &ActiveStage() override { return TessEval(); }
+};
+
+struct SPathXYGeneratedShader
+{
+ NVAllocatorCallback &m_Allocator;
+ NVRenderShaderProgram &m_Shader;
+ NVRenderCachedShaderProperty<QT3DSVec4> m_RectDimensions;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_ModelMatrix;
+ NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPosition;
+ NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties;
+ QT3DSI32 m_RefCount;
+
+ SPathXYGeneratedShader(NVRenderShaderProgram &sh, NVAllocatorCallback &alloc)
+ : m_Allocator(alloc)
+ , m_Shader(sh)
+ , m_RectDimensions("uni_rect_dimensions", sh)
+ , m_ModelMatrix("model_matrix", sh)
+ , m_CameraPosition("camera_position", sh)
+ , m_CameraProperties("camera_properties", sh)
+ , m_RefCount(0)
+ {
+ m_Shader.addRef();
+ }
+ virtual ~SPathXYGeneratedShader() { m_Shader.release(); }
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &allocator(m_Allocator);
+ NVDelete(allocator, this);
+ }
+ }
+};
+
+// Helper implements the vertex pipeline for mesh subsets when bound to the default material.
+// Should be completely possible to use for custom materials with a bit of refactoring.
+struct SXYRectVertexPipeline : public SVertexPipelineImpl
+{
+
+ SXYRectVertexPipeline(IShaderProgramGenerator &inProgGenerator,
+ IMaterialShaderGenerator &inMaterialGenerator,
+ NVAllocatorCallback &inAlloc, IStringTable &inStringTable)
+ : SVertexPipelineImpl(inAlloc, inMaterialGenerator, inProgGenerator, inStringTable, false)
+ {
+ }
+
+ void BeginVertexGeneration(QT3DSU32 displacementImageIdx,
+ SRenderableImage *displacementImage) override
+ {
+ m_DisplacementIdx = displacementImageIdx;
+ m_DisplacementImage = displacementImage;
+
+ TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags());
+ ProgramGenerator().BeginProgram(theStages);
+ // Open up each stage.
+ IShaderStageGenerator &vertexShader(Vertex());
+ vertexShader.AddIncoming("attr_pos", "vec2");
+ vertexShader.AddUniform("uni_rect_dimensions", "vec4");
+
+ vertexShader << "void main()" << Endl << "{" << Endl;
+ vertexShader << "\tvec3 uTransform;" << Endl;
+ vertexShader << "\tvec3 vTransform;" << Endl;
+
+ vertexShader.AddUniform("model_view_projection", "mat4");
+ vertexShader
+ << "\tfloat posX = mix( uni_rect_dimensions.x, uni_rect_dimensions.z, attr_pos.x );"
+ << Endl;
+ vertexShader
+ << "\tfloat posY = mix( uni_rect_dimensions.y, uni_rect_dimensions.w, attr_pos.y );"
+ << Endl;
+ vertexShader << "\tvec3 pos = vec3(posX, posY, 0.0 );" << Endl;
+ vertexShader.Append("\tgl_Position = model_view_projection * vec4(pos, 1.0);");
+ }
+
+ void OutputParaboloidDepthShaders()
+ {
+ TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags());
+ ProgramGenerator().BeginProgram(theStages);
+ IShaderStageGenerator &vertexShader(Vertex());
+ vertexShader.AddIncoming("attr_pos", "vec2");
+ vertexShader.AddUniform("uni_rect_dimensions", "vec4");
+ vertexShader.AddUniform("model_view_projection", "mat4");
+ vertexShader << "void main()" << Endl << "{" << Endl;
+ vertexShader
+ << "\tfloat posX = mix( uni_rect_dimensions.x, uni_rect_dimensions.z, attr_pos.x );"
+ << Endl;
+ vertexShader
+ << "\tfloat posY = mix( uni_rect_dimensions.y, uni_rect_dimensions.w, attr_pos.y );"
+ << Endl;
+ vertexShader << "\tvec3 pos = vec3(posX, posY, 0.0 );" << Endl;
+ IShaderProgramGenerator::OutputParaboloidDepthTessEval(vertexShader);
+ vertexShader << "}" << Endl;
+
+ IShaderProgramGenerator::OutputParaboloidDepthFragment(Fragment());
+ }
+
+ void OutputCubeFaceDepthShaders()
+ {
+ TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags());
+ ProgramGenerator().BeginProgram(theStages);
+ IShaderStageGenerator &vertexShader(Vertex());
+ IShaderStageGenerator &fragmentShader(Fragment());
+ vertexShader.AddIncoming("attr_pos", "vec2");
+ vertexShader.AddUniform("uni_rect_dimensions", "vec4");
+ vertexShader.AddUniform("model_matrix", "mat4");
+ vertexShader.AddUniform("model_view_projection", "mat4");
+
+ vertexShader.AddOutgoing("world_pos", "vec4");
+ vertexShader.Append("void main() {");
+ vertexShader.Append(
+ " float posX = mix( uni_rect_dimensions.x, uni_rect_dimensions.z, attr_pos.x );");
+ vertexShader.Append(
+ " float posY = mix( uni_rect_dimensions.y, uni_rect_dimensions.w, attr_pos.y );");
+ vertexShader.Append(" world_pos = model_matrix * vec4( posX, posY, 0.0, 1.0 );");
+ vertexShader.Append(" world_pos /= world_pos.w;");
+ vertexShader.Append(
+ " gl_Position = model_view_projection * vec4( posX, posY, 0.0, 1.0 );");
+ vertexShader.Append("}");
+
+ fragmentShader.AddUniform("camera_position", "vec3");
+ fragmentShader.AddUniform("camera_properties", "vec2");
+
+ BeginFragmentGeneration();
+ fragmentShader.Append(
+ "\tfloat dist = 0.5 * length( world_pos.xyz - camera_position );"); // Why?
+ fragmentShader.Append(
+ "\tdist = (dist - camera_properties.x) / (camera_properties.y - camera_properties.x);");
+ fragmentShader.Append("\tfragOutput = vec4(dist);");
+ fragmentShader.Append("}");
+ }
+
+ void BeginFragmentGeneration() override
+ {
+ Fragment().AddUniform("material_diffuse", "vec4");
+ Fragment() << "void main()" << Endl << "{" << Endl;
+ // We do not pass object opacity through the pipeline.
+ Fragment() << "\tfloat object_opacity = material_diffuse.a;" << Endl;
+ }
+
+ void AssignOutput(const char8_t *inVarName, const char8_t *inVarValue) override
+ {
+ Vertex() << "\t" << inVarName << " = " << inVarValue << ";\n";
+ }
+ void DoGenerateUVCoords(QT3DSU32) override { Vertex() << "\tvarTexCoord0 = attr_pos;" << Endl; }
+
+ // fragment shader expects varying vertex normal
+ // lighting in vertex pipeline expects world_normal
+ void DoGenerateWorldNormal() override
+ {
+ IShaderStageGenerator &vertexGenerator(Vertex());
+ vertexGenerator.AddUniform("normal_matrix", "mat3");
+ vertexGenerator.Append(
+ "\tvec3 world_normal = normalize(normal_matrix * vec3( 0.0, 0.0, 1.0) ).xyz;");
+ vertexGenerator.Append("\tvarNormal = world_normal;");
+ }
+
+ void DoGenerateObjectNormal() override
+ {
+ AddInterpolationParameter("varObjectNormal", "vec3");
+ Vertex().Append("\tvarObjectNormal = vec3(0.0, 0.0, 1.0 );");
+ }
+
+ void DoGenerateWorldPosition() override
+ {
+ Vertex().Append("\tvec3 local_model_world_position = (model_matrix * vec4(pos, 1.0)).xyz;");
+ AssignOutput("varWorldPos", "local_model_world_position");
+ }
+
+ void DoGenerateVarTangentAndBinormal() override
+ {
+ Vertex().AddIncoming("attr_textan", "vec3");
+ Vertex().AddIncoming("attr_binormal", "vec3");
+ Vertex() << "\tvarTangent = normal_matrix * vec3(1.0, 0.0, 0.0);" << Endl
+ << "\tvarBinormal = normal_matrix * vec3(0.0, 1.0, 0.0);" << Endl;
+ }
+
+ void DoGenerateVertexColor() override
+ {
+ Vertex().AddIncoming("attr_color", "vec3");
+ Vertex() << "\tvarColor = attr_color;" << Endl;
+ }
+
+ void EndVertexGeneration(bool) override { Vertex().Append("}"); }
+
+ void EndFragmentGeneration(bool) override { Fragment().Append("}"); }
+
+ void AddInterpolationParameter(const char8_t *inName, const char8_t *inType) override
+ {
+ m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType)));
+ Vertex().AddOutgoing(inName, inType);
+ Fragment().AddIncoming(inName, inType);
+ }
+
+ IShaderStageGenerator &ActiveStage() override { return Vertex(); }
+};
+
+struct SPathManager : public IPathManager
+{
+ typedef nvhash_map<SPath *, NVScopedRefCounted<SPathBuffer>> TPathBufferHash;
+ typedef nvhash_map<SPathSubPath *, NVScopedRefCounted<SPathSubPathBuffer>>
+ TPathSubPathBufferHash;
+ typedef nvhash_map<SPathShaderMapKey, NVScopedRefCounted<SPathGeneratedShader>> TShaderMap;
+ typedef nvhash_map<SPathShaderMapKey, NVScopedRefCounted<SPathXYGeneratedShader>>
+ TPaintedShaderMap;
+ typedef nvhash_map<CRegisteredString, TPathBufferPtr> TStringPathBufferMap;
+
+ IQt3DSRenderContextCore &m_CoreContext;
+ IQt3DSRenderContext *m_RenderContext;
+ eastl::string m_IdBuilder;
+ TPathSubPathBufferHash m_SubPathBuffers;
+ TPathBufferHash m_Buffers;
+ nvvector<SResultCubic> m_SubdivResult;
+ nvvector<QT3DSF32> m_KeyPointVec;
+ nvvector<QT3DSVec4> m_PatchBuffer;
+ TShaderMap m_PathGeometryShaders;
+ TPaintedShaderMap m_PathPaintedShaders;
+ TStringPathBufferMap m_SourcePathBufferMap;
+ Mutex m_PathBufferMutex;
+
+ NVScopedRefCounted<SPathGeneratedShader> m_DepthShader;
+ NVScopedRefCounted<SPathGeneratedShader> m_DepthDisplacementShader;
+ NVScopedRefCounted<SPathGeneratedShader> m_GeometryShadowShader;
+ NVScopedRefCounted<SPathGeneratedShader> m_GeometryCubeShadowShader;
+ NVScopedRefCounted<SPathGeneratedShader> m_GeometryDisplacementShadowShader;
+
+ NVScopedRefCounted<SPathXYGeneratedShader> m_PaintedDepthShader;
+ NVScopedRefCounted<SPathXYGeneratedShader> m_PaintedShadowShader;
+ NVScopedRefCounted<SPathXYGeneratedShader> m_PaintedCubeShadowShader;
+ NVScopedRefCounted<NVRenderInputAssembler> m_PaintedRectInputAssembler;
+ NVScopedRefCounted<NVRenderVertexBuffer> m_PaintedRectVertexBuffer;
+ NVScopedRefCounted<NVRenderIndexBuffer> m_PaintedRectIndexBuffer;
+
+ nvvector<NVScopedRefCounted<NVRenderDepthStencilState>> m_DepthStencilStates;
+
+ NVScopedRefCounted<NVRenderPathSpecification> m_PathSpecification;
+ NVScopedRefCounted<qt3dsimp::IPathBufferBuilder> m_PathBuilder;
+
+ QT3DSI32 m_RefCount;
+
+ SPathManager(IQt3DSRenderContextCore &inRC)
+ : m_CoreContext(inRC)
+ , m_RenderContext(NULL)
+ , m_SubPathBuffers(inRC.GetAllocator(), "m_SubPathBuffers")
+ , m_Buffers(inRC.GetAllocator(), "m_Buffers")
+ , m_SubdivResult(inRC.GetAllocator(), "m_SubdivResult")
+ , m_KeyPointVec(inRC.GetAllocator(), "m_KeyPointVec")
+ , m_PatchBuffer(inRC.GetAllocator(), "m_QuadStrip")
+ , m_PathGeometryShaders(inRC.GetAllocator(), "m_PathGeometryShaders")
+ , m_PathPaintedShaders(inRC.GetAllocator(), "m_PathPaintedShaders")
+ , m_SourcePathBufferMap(inRC.GetAllocator(), "m_SourcePathBufferMap")
+ , m_PathBufferMutex(inRC.GetAllocator())
+ , m_DepthStencilStates(inRC.GetAllocator(), "m_DepthStencilStates")
+ , m_RefCount(0)
+ {
+ }
+
+ virtual ~SPathManager() { m_PaintedRectInputAssembler = NULL; }
+
+ NVAllocatorCallback &GetAllocator() { return m_CoreContext.GetAllocator(); }
+ IStringTable &GetStringTable() { return m_CoreContext.GetStringTable(); }
+ NVFoundationBase &GetFoundation() { return m_CoreContext.GetFoundation(); }
+
+ void addRef() override { atomicIncrement(&m_RefCount); }
+ void release() override
+ {
+ atomicDecrement(&m_RefCount);
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(GetAllocator());
+ NVDelete(alloc, this);
+ }
+ }
+ // Called during binary load which is heavily threaded.
+ void SetPathSubPathData(const SPathSubPath &inPath,
+ NVConstDataRef<SPathAnchorPoint> inPathCubicCurves) override
+ {
+ Mutex::ScopedLock __locker(m_PathBufferMutex);
+ eastl::pair<TPathSubPathBufferHash::iterator, bool> inserter =
+ m_SubPathBuffers.insert(eastl::make_pair((SPathSubPath *)&inPath,
+ NVScopedRefCounted<SPathSubPathBuffer>(NULL)));
+ if (!inserter.first->second)
+ inserter.first->second = QT3DS_NEW(GetAllocator(), SPathSubPathBuffer)(
+ GetAllocator(), const_cast<SPathSubPath &>(inPath));
+ SPathSubPathBuffer &theBuffer = *inserter.first->second.mPtr;
+ theBuffer.m_SourceData.assign(inPathCubicCurves.begin(), inPathCubicCurves.end());
+ theBuffer.m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData);
+ }
+
+ SPathBuffer *GetPathBufferObject(const SPath &inPath)
+ {
+ eastl::pair<TPathBufferHash::iterator, bool> inserter = m_Buffers.insert(
+ eastl::make_pair((SPath *)&inPath, NVScopedRefCounted<SPathBuffer>(NULL)));
+ if (inserter.second) {
+ inserter.first->second = QT3DS_NEW(GetAllocator(), SPathBuffer)(GetAllocator());
+ }
+ return inserter.first->second.mPtr;
+ }
+
+ SPathSubPathBuffer *GetPathBufferObject(const SPathSubPath &inSubPath)
+ {
+ TPathSubPathBufferHash::iterator iter = m_SubPathBuffers.find((SPathSubPath *)&inSubPath);
+ if (iter != m_SubPathBuffers.end())
+ return iter->second.mPtr;
+ return NULL;
+ }
+
+ NVDataRef<SPathAnchorPoint> GetPathSubPathBuffer(const SPathSubPath &inPath) override
+ {
+ SPathSubPathBuffer *theBuffer = GetPathBufferObject(inPath);
+ if (theBuffer)
+ return toDataRef(theBuffer->m_SourceData.data(), (QT3DSU32)theBuffer->m_SourceData.size());
+ return NVDataRef<SPathAnchorPoint>();
+ }
+
+ NVDataRef<SPathAnchorPoint> ResizePathSubPathBuffer(const SPathSubPath &inPath,
+ QT3DSU32 inNumAnchors) override
+ {
+ SPathSubPathBuffer *theBuffer = GetPathBufferObject(inPath);
+ if (theBuffer == NULL)
+ SetPathSubPathData(inPath, NVConstDataRef<SPathAnchorPoint>());
+ theBuffer = GetPathBufferObject(inPath);
+ theBuffer->m_SourceData.resize(inNumAnchors);
+ theBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData);
+ return toDataRef(theBuffer->m_SourceData.data(), (QT3DSU32)theBuffer->m_SourceData.size());
+ }
+
+ // This needs to be done using roots of the first derivative.
+ NVBounds3 GetBounds(const SPath &inPath) override
+ {
+ NVBounds3 retval(NVBounds3::empty());
+
+ SPathBuffer *thePathBuffer = GetPathBufferObject(inPath);
+ if (thePathBuffer) {
+ SPathDirtyFlags geomDirtyFlags(
+ PathDirtyFlagValues::SourceData | PathDirtyFlagValues::BeginTaper
+ | PathDirtyFlagValues::EndTaper | PathDirtyFlagValues::Width
+ | PathDirtyFlagValues::CPUError);
+
+ if ((((QT3DSU32)thePathBuffer->m_Flags) & (QT3DSU32)geomDirtyFlags) == 0) {
+ return thePathBuffer->m_Bounds;
+ }
+ }
+
+ for (SPathSubPath *theSubPath = inPath.m_FirstSubPath; theSubPath;
+ theSubPath = theSubPath->m_NextSubPath) {
+ SPathSubPathBuffer *theBuffer = GetPathBufferObject(*theSubPath);
+ if (!theBuffer)
+ continue;
+
+ QT3DSU32 numAnchors = theBuffer->m_SourceData.size();
+ for (QT3DSU32 idx = 0, end = numAnchors; idx < end; ++idx) {
+ const SPathAnchorPoint &thePoint(theBuffer->m_SourceData[idx]);
+ QT3DSVec2 position(thePoint.m_Position);
+ retval.include(QT3DSVec3(position.x, position.y, 0.0f));
+ if (idx) {
+ QT3DSVec2 incoming(IPathManagerCore::GetControlPointFromAngleDistance(
+ thePoint.m_Position, thePoint.m_IncomingAngle,
+ thePoint.m_IncomingDistance));
+ retval.include(QT3DSVec3(incoming.x, incoming.y, 0.0f));
+ }
+
+ if (idx < (numAnchors - 1)) {
+ QT3DSVec2 outgoing(IPathManagerCore::GetControlPointFromAngleDistance(
+ thePoint.m_Position, thePoint.m_OutgoingAngle,
+ thePoint.m_OutgoingDistance));
+ retval.include(QT3DSVec3(outgoing.x, outgoing.y, 0.0f));
+ }
+ }
+ }
+
+ return retval;
+ }
+
+ IPathManager &OnRenderSystemInitialize(IQt3DSRenderContext &context) override
+ {
+ m_RenderContext = &context;
+ return *this;
+ }
+
+ // find a point that will join these two curves *if* they are not first derivative continuous
+ static Option<QT3DSVec2> GetAdjoiningPoint(QT3DSVec2 prevC2, QT3DSVec2 point, QT3DSVec2 C1, QT3DSF32 pathWidth)
+ {
+ QT3DSVec2 incomingDxDy = (point - prevC2);
+ QT3DSVec2 outgoingDxDy = (C1 - point);
+ incomingDxDy.normalize();
+ outgoingDxDy.normalize();
+ float determinant = (incomingDxDy.x * outgoingDxDy.y) - (incomingDxDy.y * outgoingDxDy.x);
+ if (fabs(determinant) > .001f) {
+ float mult = determinant > 0.0f ? 1.0f : -1.0f;
+ QT3DSVec2 incomingNormal(incomingDxDy.y, -incomingDxDy.x);
+ QT3DSVec2 outgoingNormal(outgoingDxDy.y, -outgoingDxDy.x);
+
+ QT3DSVec2 leftEdge = point + mult * incomingNormal * pathWidth;
+ QT3DSVec2 rightEdge = point + mult * outgoingNormal * pathWidth;
+
+ return (leftEdge + rightEdge) / 2.0f;
+ }
+ return Empty();
+ }
+
+ Option<eastl::pair<QT3DSU32, QT3DSF32>> FindBreakEquation(QT3DSF32 inTaperStart)
+ {
+ QT3DSF32 lengthTotal = 0;
+ for (QT3DSU32 idx = 0, end = m_SubdivResult.size(); idx < end; ++idx) {
+ if (lengthTotal + m_SubdivResult[idx].m_Length > inTaperStart) {
+ QT3DSF32 breakTValue = (inTaperStart - lengthTotal) / m_SubdivResult[idx].m_Length;
+ nvvector<SResultCubic>::iterator breakIter = m_SubdivResult.begin() + idx;
+ SCubicBezierCurve theCurve(breakIter->m_P1, breakIter->m_C1, breakIter->m_C2,
+ breakIter->m_P2);
+ eastl::pair<SCubicBezierCurve, SCubicBezierCurve> subdivCurve =
+ theCurve.SplitCubicBezierCurve(breakTValue);
+ QT3DSF32 originalBreakT =
+ breakIter->m_TStart + (breakIter->m_TStop - breakIter->m_TStart) * breakTValue;
+ // Update the existing item to point to the second equation
+ breakIter->m_P1 = subdivCurve.second.m_Points[0];
+ breakIter->m_C1 = subdivCurve.second.m_Points[1];
+ breakIter->m_C2 = subdivCurve.second.m_Points[2];
+ breakIter->m_P2 = subdivCurve.second.m_Points[3];
+ QT3DSF32 originalLength = breakIter->m_Length;
+ QT3DSF32 originalStart = breakIter->m_TStart;
+ breakIter->m_Length *= (1.0f - breakTValue);
+ breakIter->m_TStart = originalBreakT;
+ SResultCubic newCubic(subdivCurve.first.m_Points[0], subdivCurve.first.m_Points[1],
+ subdivCurve.first.m_Points[2], subdivCurve.first.m_Points[3],
+ breakIter->m_EquationIndex, originalStart, originalBreakT,
+ originalLength * breakTValue);
+
+ m_SubdivResult.insert(breakIter, newCubic);
+ return eastl::make_pair(idx, breakTValue);
+ }
+ lengthTotal += m_SubdivResult[idx].m_Length;
+ }
+ return Empty();
+ }
+
+ bool PrepareGeometryPathForRender(const SPath &inPath, SPathBuffer &inPathBuffer)
+ {
+
+ m_SubdivResult.clear();
+ m_KeyPointVec.clear();
+ const SPath &thePath(inPath);
+
+ inPathBuffer.SetBeginTaperInfo(thePath.m_BeginCapping, thePath.m_BeginCapOffset,
+ thePath.m_BeginCapOpacity, thePath.m_BeginCapWidth);
+ inPathBuffer.SetEndTaperInfo(thePath.m_EndCapping, thePath.m_EndCapOffset,
+ thePath.m_EndCapOpacity, thePath.m_EndCapWidth);
+ inPathBuffer.SetWidth(inPath.m_Width);
+ inPathBuffer.SetCPUError(inPath.m_LinearError);
+
+ SPathDirtyFlags geomDirtyFlags(PathDirtyFlagValues::SourceData
+ | PathDirtyFlagValues::BeginTaper
+ | PathDirtyFlagValues::EndTaper | PathDirtyFlagValues::Width
+ | PathDirtyFlagValues::CPUError);
+
+ bool retval = false;
+ if (!inPathBuffer.m_PatchData
+ || (((QT3DSU32)inPathBuffer.m_Flags) & (QT3DSU32)geomDirtyFlags) != 0) {
+ qt3dsimp::SPathBuffer thePathData = inPathBuffer.GetPathData(*m_PathBuilder);
+
+ QT3DSU32 dataIdx = 0;
+ QT3DSVec2 prevPoint(0, 0);
+ QT3DSU32 equationIdx = 0;
+ for (QT3DSU32 commandIdx = 0, commandEnd = thePathData.m_Commands.size();
+ commandIdx < commandEnd; ++commandIdx) {
+ switch (thePathData.m_Commands[commandIdx]) {
+ case qt3dsimp::PathCommand::MoveTo:
+ prevPoint =
+ QT3DSVec2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ break;
+ case qt3dsimp::PathCommand::CubicCurveTo: {
+ QT3DSVec2 c1(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ QT3DSVec2 c2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ QT3DSVec2 p2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ OuterAdaptiveSubdivideBezierCurve(
+ m_SubdivResult, m_KeyPointVec, SCubicBezierCurve(prevPoint, c1, c2, p2),
+ NVMax(inPath.m_LinearError, 1.0f), equationIdx);
+ ++equationIdx;
+ prevPoint = p2;
+ } break;
+ case qt3dsimp::PathCommand::Close:
+ break;
+
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ QT3DSF32 theLocalWidth = inPath.m_Width / 2.0f;
+
+ QT3DSVec2 theBeginTaperData(theLocalWidth, thePath.m_GlobalOpacity);
+ QT3DSVec2 theEndTaperData(theLocalWidth, thePath.m_GlobalOpacity);
+
+ QT3DSF32 pathLength = 0.0f;
+ for (QT3DSU32 idx = 0, end = m_SubdivResult.size(); idx < end; ++idx)
+ pathLength += m_SubdivResult[idx].m_Length;
+
+ if (thePath.m_BeginCapping == PathCapping::Taper
+ || thePath.m_EndCapping == PathCapping::Taper) {
+ QT3DSF32 maxTaperStart = pathLength / 2.0f;
+ if (thePath.m_BeginCapping == PathCapping::Taper) {
+ // Can't start more than halfway across the path.
+ QT3DSF32 taperStart = NVMin(thePath.m_BeginCapOffset, maxTaperStart);
+ QT3DSF32 endTaperWidth = thePath.m_BeginCapWidth;
+ QT3DSF32 endTaperOpacity = thePath.m_GlobalOpacity * thePath.m_BeginCapOpacity;
+ theBeginTaperData = QT3DSVec2(endTaperWidth, endTaperOpacity);
+ // Find where we need to break the current equations.
+ Option<eastl::pair<QT3DSU32, QT3DSF32>> breakEquationAndT(
+ FindBreakEquation(taperStart));
+ if (breakEquationAndT.hasValue()) {
+ QT3DSU32 breakEquation = breakEquationAndT->first;
+
+ QT3DSF32 lengthTotal = 0;
+ for (QT3DSU32 idx = 0, end = breakEquation; idx <= end; ++idx) {
+ SResultCubic &theCubic = m_SubdivResult[idx];
+ theCubic.m_Mode = SResultCubic::BeginTaper;
+
+ theCubic.m_TaperMultiplier[0] = lengthTotal / taperStart;
+ lengthTotal += theCubic.m_Length;
+ theCubic.m_TaperMultiplier[1] = lengthTotal / taperStart;
+ }
+ }
+ }
+ if (thePath.m_EndCapping == PathCapping::Taper) {
+ QT3DSF32 taperStart = NVMin(thePath.m_EndCapOffset, maxTaperStart);
+ QT3DSF32 endTaperWidth = thePath.m_EndCapWidth;
+ QT3DSF32 endTaperOpacity = thePath.m_GlobalOpacity * thePath.m_EndCapOpacity;
+ theEndTaperData = QT3DSVec2(endTaperWidth, endTaperOpacity);
+ // Invert taper start so that the forward search works.
+ Option<eastl::pair<QT3DSU32, QT3DSF32>> breakEquationAndT(
+ FindBreakEquation(pathLength - taperStart));
+
+ if (breakEquationAndT.hasValue()) {
+ QT3DSU32 breakEquation = breakEquationAndT->first;
+ ++breakEquation;
+
+ QT3DSF32 lengthTotal = 0;
+ for (QT3DSU32 idx = breakEquation, end = m_SubdivResult.size(); idx < end;
+ ++idx) {
+ SResultCubic &theCubic = m_SubdivResult[idx];
+ theCubic.m_Mode = SResultCubic::EndTaper;
+
+ theCubic.m_TaperMultiplier[0] = 1.0f - (lengthTotal / taperStart);
+ lengthTotal += theCubic.m_Length;
+ theCubic.m_TaperMultiplier[1] = 1.0f - (lengthTotal / taperStart);
+ }
+ }
+ }
+ }
+
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+ // Create quads out of each point.
+ if (m_SubdivResult.empty())
+ return false;
+
+ // Generate patches.
+ m_PatchBuffer.clear();
+ QT3DSF32 pathWidth = thePath.m_Width / 2.0f;
+ // texture coords
+ float texCoordU = 0.0;
+
+ for (QT3DSU32 idx = 0, end = m_SubdivResult.size(); idx < end; ++idx) {
+ // create patches
+ SResultCubic thePoint(m_SubdivResult[idx]);
+
+ m_PatchBuffer.push_back(CreateVec4(thePoint.m_P1, thePoint.m_C1));
+ m_PatchBuffer.push_back(CreateVec4(thePoint.m_C2, thePoint.m_P2));
+
+ // Now we need to take care of cases where the control points of the adjoining
+ // SubPaths
+ // do not line up; i.e. there is a discontinuity of the 1st derivative
+ // The simplest way to do this is to move the edge vertex to a halfway point
+ // between a line bisecting the two control lines
+ QT3DSVec2 incomingAdjoining(thePoint.m_P1);
+ QT3DSVec2 outgoingAdjoining(thePoint.m_P2);
+ if (idx) {
+ SResultCubic previousCurve = m_SubdivResult[idx - 1];
+ if (previousCurve.m_EquationIndex != thePoint.m_EquationIndex) {
+ QT3DSF32 anchorWidth =
+ thePoint.GetP1Width(pathWidth, theBeginTaperData.x, theEndTaperData.x);
+ Option<QT3DSVec2> adjoining = GetAdjoiningPoint(
+ previousCurve.m_C2, thePoint.m_P1, thePoint.m_C1, anchorWidth);
+ if (adjoining.hasValue())
+ incomingAdjoining = *adjoining;
+ }
+ }
+ if (idx < (end - 1)) {
+ SResultCubic nextCurve = m_SubdivResult[idx + 1];
+ if (nextCurve.m_EquationIndex != thePoint.m_EquationIndex) {
+ QT3DSF32 anchorWidth =
+ thePoint.GetP2Width(pathWidth, theBeginTaperData.x, theEndTaperData.x);
+ Option<QT3DSVec2> adjoining = GetAdjoiningPoint(thePoint.m_C2, thePoint.m_P2,
+ nextCurve.m_C1, anchorWidth);
+ if (adjoining.hasValue())
+ outgoingAdjoining = *adjoining;
+ }
+ }
+ m_PatchBuffer.push_back(CreateVec4(incomingAdjoining, outgoingAdjoining));
+
+ QT3DSVec4 taperData(0.0f);
+ taperData.x = thePoint.m_TaperMultiplier.x;
+ taperData.y = thePoint.m_TaperMultiplier.y;
+ // Note we could put a *lot* more data into this thing.
+ taperData.z = (QT3DSF32)thePoint.m_Mode;
+ m_PatchBuffer.push_back(taperData);
+
+ // texture coord generation
+ // note we only generate u here. v is generated in the tess shader
+ // u coord for P1 and C1
+ QT3DSVec2 udata(texCoordU, texCoordU + (thePoint.m_Length / pathLength));
+ texCoordU = udata.y;
+ m_PatchBuffer.push_back(QT3DSVec4(udata.x, udata.y, 0.0, 0.0));
+ }
+
+ // buffer size is 3.0*4.0*bufSize
+ QT3DSU32 bufSize = (QT3DSU32)m_PatchBuffer.size() * sizeof(QT3DSVec4);
+ QT3DSU32 stride = sizeof(QT3DSVec4);
+
+ if ((!inPathBuffer.m_PatchData) || inPathBuffer.m_PatchData->Size() < bufSize) {
+ inPathBuffer.m_PatchData = theRenderContext.CreateVertexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Dynamic, bufSize, stride,
+ toU8DataRef(m_PatchBuffer.data(), (QT3DSU32)m_PatchBuffer.size()));
+ inPathBuffer.m_NumVertexes = (QT3DSU32)m_PatchBuffer.size();
+ inPathBuffer.m_InputAssembler = NULL;
+ } else {
+ QT3DS_ASSERT(inPathBuffer.m_PatchData->Size() >= bufSize);
+ inPathBuffer.m_PatchData->UpdateBuffer(
+ toU8DataRef(m_PatchBuffer.data(), (QT3DSU32)m_PatchBuffer.size()));
+ }
+
+ if (!inPathBuffer.m_InputAssembler) {
+ qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 4),
+ };
+
+ NVRenderDrawMode::Enum primType = NVRenderDrawMode::Patches;
+
+ NVRenderAttribLayout *theLayout =
+ theRenderContext.CreateAttributeLayout(toConstDataRef(theEntries, 1));
+ // How many vertices the TCS shader has access to in order to produce its output
+ // array of vertices.
+ const QT3DSU32 inputPatchVertexCount = 5;
+ inPathBuffer.m_InputAssembler = theRenderContext.CreateInputAssembler(
+ theLayout, toConstDataRef(inPathBuffer.m_PatchData.mPtr), NULL,
+ toConstDataRef(stride), toConstDataRef((QT3DSU32)0), primType,
+ inputPatchVertexCount);
+ }
+ inPathBuffer.m_BeginTaperData = theBeginTaperData;
+ inPathBuffer.m_EndTaperData = theEndTaperData;
+
+ // cache bounds
+ NVBounds3 bounds = GetBounds(inPath);
+ inPathBuffer.m_Bounds.minimum = bounds.minimum;
+ inPathBuffer.m_Bounds.maximum = bounds.maximum;
+ }
+
+ return retval;
+ }
+
+ IMaterialShaderGenerator *GetMaterialShaderGenertator(SPathRenderContext &inRenderContext)
+ {
+ bool isDefaultMaterial =
+ (inRenderContext.m_Material.m_Type == GraphObjectTypes::DefaultMaterial);
+
+ IMaterialShaderGenerator *theMaterialGenerator = NULL;
+ if (isDefaultMaterial)
+ theMaterialGenerator = &m_RenderContext->GetDefaultMaterialShaderGenerator();
+ else
+ theMaterialGenerator = &m_RenderContext->GetCustomMaterialShaderGenerator();
+
+ return theMaterialGenerator;
+ }
+
+ CRegisteredString GetMaterialNameForKey(SPathRenderContext &inRenderContext)
+ {
+ bool isDefaultMaterial =
+ (inRenderContext.m_Material.m_Type == GraphObjectTypes::DefaultMaterial);
+
+ if (!isDefaultMaterial) {
+ ICustomMaterialSystem &theMaterialSystem(m_RenderContext->GetCustomMaterialSystem());
+ const SCustomMaterial &theCustomMaterial(
+ reinterpret_cast<const SCustomMaterial &>(inRenderContext.m_Material));
+
+ return m_RenderContext->GetStringTable().RegisterStr(
+ theMaterialSystem.GetShaderName(theCustomMaterial));
+ }
+
+ return m_RenderContext->GetStringTable().RegisterStr("");
+ }
+
+ bool PreparePaintedPathForRender(const SPath &inPath, SPathBuffer &inPathBuffer)
+ {
+ NVRenderContext &theContext(this->m_RenderContext->GetRenderContext());
+ if (!inPathBuffer.m_PathRender
+ || (((QT3DSU32)inPathBuffer.m_Flags) & PathDirtyFlagValues::SourceData)) {
+ if (!inPathBuffer.m_PathRender) {
+ inPathBuffer.m_PathRender = theContext.CreatePathRender();
+ }
+
+ if (inPathBuffer.m_PathRender == NULL || m_PathSpecification == NULL) {
+ // QT3DS_ASSERT( false );
+ return false;
+ }
+
+ m_PathSpecification->Reset();
+ qt3dsimp::SPathBuffer thePathData = inPathBuffer.GetPathData(*m_PathBuilder);
+
+ QT3DSU32 dataIdx = 0;
+ for (QT3DSU32 commandIdx = 0, commandEnd = thePathData.m_Commands.size();
+ commandIdx < commandEnd; ++commandIdx) {
+
+ switch (thePathData.m_Commands[commandIdx]) {
+ case qt3dsimp::PathCommand::MoveTo:
+ m_PathSpecification->MoveTo(
+ QT3DSVec2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]));
+ dataIdx += 2;
+ break;
+ case qt3dsimp::PathCommand::CubicCurveTo: {
+ QT3DSVec2 c1(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ QT3DSVec2 c2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ QT3DSVec2 p2(thePathData.m_Data[dataIdx], thePathData.m_Data[dataIdx + 1]);
+ dataIdx += 2;
+ m_PathSpecification->CubicCurveTo(c1, c2, p2);
+ } break;
+ case qt3dsimp::PathCommand::Close:
+ m_PathSpecification->ClosePath();
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ inPathBuffer.m_PathRender->SetPathSpecification(*m_PathSpecification);
+
+ // cache bounds
+ NVBounds3 bounds = GetBounds(inPath);
+ inPathBuffer.m_Bounds.minimum = bounds.minimum;
+ inPathBuffer.m_Bounds.maximum = bounds.maximum;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool PrepareForRender(const SPath &inPath) override
+ {
+ SPathBuffer *thePathBuffer = GetPathBufferObject(inPath);
+ if (!thePathBuffer) {
+ return false;
+ }
+ NVRenderContext &theContext(this->m_RenderContext->GetRenderContext());
+ if (!m_PathSpecification)
+ m_PathSpecification = theContext.CreatePathSpecification();
+ if (!m_PathSpecification)
+ return false;
+ if (!m_PathBuilder)
+ m_PathBuilder = qt3dsimp::IPathBufferBuilder::CreateBuilder(GetFoundation());
+
+ thePathBuffer->SetPathType(inPath.m_PathType);
+ bool retval = false;
+ if (inPath.m_PathBuffer.IsValid() == false) {
+ thePathBuffer->m_PathBuffer = NULL;
+ // Ensure the SubPath list is identical and clear, percolating any dirty flags up to the
+ // path buffer.
+ QT3DSU32 SubPathIdx = 0;
+ for (const SPathSubPath *theSubPath = inPath.m_FirstSubPath; theSubPath;
+ theSubPath = theSubPath->m_NextSubPath, ++SubPathIdx) {
+ SPathSubPathBuffer *theSubPathBuffer = GetPathBufferObject(*theSubPath);
+ if (theSubPathBuffer == NULL)
+ continue;
+ thePathBuffer->m_Flags =
+ (QT3DSU32)(thePathBuffer->m_Flags | theSubPathBuffer->m_Flags);
+
+ if (theSubPathBuffer->m_Closed != theSubPath->m_Closed) {
+ thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData);
+ theSubPathBuffer->m_Closed = theSubPath->m_Closed;
+ }
+
+ if (thePathBuffer->m_SubPaths.size() <= SubPathIdx
+ || thePathBuffer->m_SubPaths[SubPathIdx] != theSubPathBuffer) {
+ thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData);
+ if (thePathBuffer->m_SubPaths.size() <= SubPathIdx)
+ thePathBuffer->m_SubPaths.push_back(theSubPathBuffer);
+ else
+ thePathBuffer->m_SubPaths[SubPathIdx] = theSubPathBuffer;
+ }
+
+ theSubPathBuffer->m_Flags.Clear();
+ }
+
+ if (SubPathIdx != thePathBuffer->m_SubPaths.size()) {
+ thePathBuffer->m_SubPaths.resize(SubPathIdx);
+ thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData);
+ }
+ } else {
+ thePathBuffer->m_SubPaths.clear();
+ eastl::pair<TStringPathBufferMap::iterator, bool> inserter =
+ m_SourcePathBufferMap.insert(
+ eastl::make_pair(inPath.m_PathBuffer, TPathBufferPtr()));
+ if (inserter.second) {
+ NVScopedRefCounted<IRefCountedInputStream> theStream =
+ m_CoreContext.GetInputStreamFactory().GetStreamForFile(
+ inPath.m_PathBuffer.c_str());
+ if (theStream) {
+ qt3dsimp::SPathBuffer *theNewBuffer =
+ qt3dsimp::SPathBuffer::Load(*theStream, GetFoundation());
+ if (theNewBuffer)
+ inserter.first->second = QT3DS_NEW(GetAllocator(), SImportPathWrapper)(
+ GetAllocator(), *theNewBuffer);
+ }
+ }
+ if (thePathBuffer->m_PathBuffer != inserter.first->second) {
+ thePathBuffer->m_PathBuffer = inserter.first->second;
+ thePathBuffer->m_Flags.clearOrSet(true, PathDirtyFlagValues::SourceData);
+ }
+ }
+
+ if (inPath.m_PathType == PathTypes::Geometry)
+ retval = PrepareGeometryPathForRender(inPath, *thePathBuffer);
+ else
+ retval = PreparePaintedPathForRender(inPath, *thePathBuffer);
+ thePathBuffer->m_Flags.Clear();
+ return retval;
+ }
+
+ void SetMaterialProperties(NVRenderShaderProgram &inShader, SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties &inRenderProperties)
+ {
+ IMaterialShaderGenerator *theMaterialGenerator =
+ GetMaterialShaderGenertator(inRenderContext);
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+ theRenderContext.SetActiveShader(&inShader);
+
+ theMaterialGenerator->SetMaterialProperties(
+ inShader, inRenderContext.m_Material, inRenderContext.m_CameraVec,
+ inRenderContext.m_ModelViewProjection, inRenderContext.m_NormalMatrix,
+ inRenderContext.m_Path.m_GlobalTransform, inRenderContext.m_FirstImage,
+ inRenderContext.m_Opacity, inRenderProperties);
+ }
+
+ void DoRenderGeometryPath(SPathGeneratedShader &inShader, SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties &inRenderProperties,
+ SPathBuffer &inPathBuffer)
+ {
+ if (inPathBuffer.m_InputAssembler == NULL)
+ return;
+
+ SetMaterialProperties(inShader.m_Shader, inRenderContext, inRenderProperties);
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+
+ inShader.m_BeginTaperData.Set(inPathBuffer.m_BeginTaperData);
+ inShader.m_EndTaperData.Set(inPathBuffer.m_EndTaperData);
+ if (inRenderContext.m_EnableWireframe) {
+ // we need the viewport matrix
+ NVRenderRect theViewport(theRenderContext.GetViewport());
+ QT3DSMat44 vpMatrix;
+ vpMatrix.column0 = QT3DSVec4((float)theViewport.m_Width / 2.0f, 0.0, 0.0, 0.0);
+ vpMatrix.column1 = QT3DSVec4(0.0, (float)theViewport.m_Height / 2.0f, 0.0, 0.0);
+ vpMatrix.column2 = QT3DSVec4(0.0, 0.0, 1.0, 0.0);
+ vpMatrix.column3 =
+ QT3DSVec4((float)theViewport.m_Width / 2.0f + (float)theViewport.m_X,
+ (float)theViewport.m_Height / 2.0f + (float)theViewport.m_Y, 0.0, 1.0);
+
+ inShader.m_WireframeViewMatrix.Set(vpMatrix);
+ }
+
+ QT3DSF32 tessEdgeValue = NVMin(64.0f, NVMax(1.0f, inRenderContext.m_Path.m_EdgeTessAmount));
+ QT3DSF32 tessInnerValue = NVMin(64.0f, NVMax(1.0f, inRenderContext.m_Path.m_InnerTessAmount));
+ inShader.m_EdgeTessAmount.Set(tessEdgeValue);
+ inShader.m_InnerTessAmount.Set(tessInnerValue);
+ inShader.m_Width.Set(inRenderContext.m_Path.m_Width / 2.0f);
+ theRenderContext.SetInputAssembler(inPathBuffer.m_InputAssembler);
+ theRenderContext.SetCullingEnabled(false);
+ NVRenderDrawMode::Enum primType = NVRenderDrawMode::Patches;
+ theRenderContext.Draw(primType, (QT3DSU32)inPathBuffer.m_NumVertexes, 0);
+ }
+
+ NVRenderDepthStencilState *GetDepthStencilState()
+ {
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+ NVRenderBoolOp::Enum theDepthFunction = theRenderContext.GetDepthFunction();
+ bool isDepthEnabled = theRenderContext.IsDepthTestEnabled();
+ bool isStencilEnabled = theRenderContext.IsStencilTestEnabled();
+ bool isDepthWriteEnabled = theRenderContext.IsDepthWriteEnabled();
+ for (QT3DSU32 idx = 0, end = m_DepthStencilStates.size(); idx < end; ++idx) {
+ NVRenderDepthStencilState &theState = *m_DepthStencilStates[idx];
+ if (theState.GetDepthFunc() == theDepthFunction
+ && theState.GetDepthEnabled() == isDepthEnabled
+ && theState.GetDepthMask() == isDepthWriteEnabled)
+ return &theState;
+ }
+ NVRenderStencilFunctionArgument theArg(NVRenderBoolOp::NotEqual, 0, 0xFF);
+ NVRenderStencilOperationArgument theOpArg(NVRenderStencilOp::Keep, NVRenderStencilOp::Keep,
+ NVRenderStencilOp::Zero);
+ m_DepthStencilStates.push_back(theRenderContext.CreateDepthStencilState(
+ isDepthEnabled, isDepthWriteEnabled, theDepthFunction, isStencilEnabled, theArg, theArg,
+ theOpArg, theOpArg));
+ return m_DepthStencilStates.back();
+ }
+
+ static void DoSetCorrectiveScale(const QT3DSMat44 &mvp, QT3DSMat44 &outScale, NVBounds3 pathBounds)
+ {
+ // Compute the projected locations for the paraboloid and regular projection
+ // and thereby set the appropriate scaling factor.
+ QT3DSVec3 points[4];
+ QT3DSVec3 projReg[4], projParab[4];
+ points[0] = pathBounds.minimum;
+ points[1] = QT3DSVec3(pathBounds.maximum.x, pathBounds.minimum.y, pathBounds.minimum.z);
+ points[2] = pathBounds.maximum;
+ points[3] = QT3DSVec3(pathBounds.minimum.x, pathBounds.maximum.y, pathBounds.maximum.z);
+
+ // Do the two different projections.
+ for (int i = 0; i < 4; ++i) {
+ QT3DSVec4 tmp;
+ tmp = mvp.transform(QT3DSVec4(points[i], 1.0f));
+ tmp /= tmp.w;
+ projReg[i] = tmp.getXYZ();
+ projParab[i] = tmp.getXYZ().getNormalized();
+ projParab[i] /= projParab[i].z + 1.0f;
+ }
+
+ NVBounds3 boundsA, boundsB;
+ for (int i = 0; i < 4; ++i) {
+ boundsA.include(projReg[i]);
+ boundsB.include(projParab[i]);
+ }
+ QT3DSF32 xscale =
+ (boundsB.maximum.x - boundsB.minimum.x) / (boundsA.maximum.x - boundsA.minimum.x);
+ QT3DSF32 yscale =
+ (boundsB.maximum.y - boundsB.minimum.y) / (boundsA.maximum.y - boundsA.minimum.y);
+ QT3DSF32 zscale = (boundsB.maximum - boundsB.minimum).magnitudeSquared()
+ / (boundsA.maximum - boundsA.minimum).magnitudeSquared();
+ // The default minimum here is just a stupid figure that looks good on our content because
+ // we'd
+ // been using it for a little while before. Just for demo.
+ xscale = NVMin<QT3DSF32>(0.5333333f, NVMin<QT3DSF32>(xscale, yscale));
+ yscale = NVMin<QT3DSF32>(0.5333333f, NVMin<QT3DSF32>(xscale, yscale));
+ outScale.scale(QT3DSVec4(xscale, yscale, zscale, 1.0f));
+ }
+
+ void DoRenderPaintedPath(SPathXYGeneratedShader &inShader, SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties &inRenderProperties,
+ SPathBuffer &inPathBuffer, bool isParaboloidPass = false)
+ {
+ if (!inPathBuffer.m_PathRender)
+ return;
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+ if (!m_PaintedRectInputAssembler) {
+ QT3DSVec2 vertexes[] = {
+ QT3DSVec2(0.0, 0.0), QT3DSVec2(1.0, 0.0), QT3DSVec2(1.0, 1.0), QT3DSVec2(0.0, 1.0),
+ };
+
+ QT3DSU8 indexes[] = {
+ 0, 1, 2, 2, 3, 0,
+ };
+
+ QT3DSU32 stride = sizeof(QT3DSVec2);
+
+ NVRenderVertexBufferEntry theBufferEntries[] = { NVRenderVertexBufferEntry(
+ "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 0) };
+
+ m_PaintedRectVertexBuffer = theRenderContext.CreateVertexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Static, 4 * sizeof(QT3DSVec2), sizeof(QT3DSVec2),
+ toU8DataRef(vertexes, 4));
+ m_PaintedRectIndexBuffer = theRenderContext.CreateIndexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Static,
+ qt3ds::render::NVRenderComponentTypes::QT3DSU8, 6, toU8DataRef(indexes, 6));
+ NVRenderAttribLayout *theAttribLayout =
+ theRenderContext.CreateAttributeLayout(toConstDataRef(theBufferEntries, 1));
+ m_PaintedRectInputAssembler = theRenderContext.CreateInputAssembler(
+ theAttribLayout, toConstDataRef(m_PaintedRectVertexBuffer.mPtr),
+ m_PaintedRectIndexBuffer.mPtr, toConstDataRef(stride), toConstDataRef((QT3DSU32)0),
+ qt3ds::render::NVRenderDrawMode::Triangles);
+ }
+
+ // our current render target needs stencil
+ QT3DS_ASSERT(theRenderContext.GetStencilBits() > 0);
+
+ theRenderContext.SetDepthStencilState(GetDepthStencilState());
+
+ // http://developer.download.nvidia.com/assets/gamedev/files/Mixing_Path_Rendering_and_3D.pdf
+ theRenderContext.SetPathStencilDepthOffset(-.05f, -1.0f);
+
+ // Stencil out the geometry.
+ QT3DSMat44 pathMdlView = QT3DSMat44::createIdentity();
+ // Why is this happening? Well, it's because the painted-on path rendering is always
+ // a flat splatted 2D object. This is bad because a paraboloid projection demands a very
+ // different
+ // non-linear space into which we must draw. Path Rendering does not allow this sort of
+ // spatial
+ // warping internally, and all we end up passing in as a simple perspective projection.
+ // So for the fix, I'm scaling the actual "object" size so that it fits into the correctly
+ // projected
+ // polygon inside the paraboloid depth pass. Obviously, this scaling factor is wrong, and
+ // not generic
+ // enough to cover cases like polygons covering a large spread of the FOV and so on. It's
+ // really
+ // just a filthy awful, morally deplorable HACK. But it's basically the quickest fix at
+ // hand.
+ // This is also about the only possible approach that *could* work short of rendering the
+ // paths in
+ // a render-to-texture pass and splatting that texture on a sufficiently tessellated quad.
+ // Unless
+ // there's a way to program NVPR's internal projection scheme, that is.
+ // Geometry-based paths will work out better, I think, because they're actually creating
+ // geometry.
+ // This is essentially a 2D painting process inside a quad where the actual rendered region
+ // isn't
+ // exactly where NVPR thinks it should be because they're not projecting points the same
+ // way.
+ if (isParaboloidPass) {
+ DoSetCorrectiveScale(inRenderContext.m_ModelViewProjection, pathMdlView,
+ inPathBuffer.m_PathRender->GetPathObjectStrokeBox());
+ }
+
+ bool isStencilEnabled = theRenderContext.IsStencilTestEnabled();
+ theRenderContext.SetStencilTestEnabled(true);
+ theRenderContext.SetPathProjectionMatrix(inRenderContext.m_ModelViewProjection);
+ theRenderContext.SetPathModelViewMatrix(pathMdlView);
+
+ if (inRenderContext.m_IsStroke) {
+ inPathBuffer.m_PathRender->SetStrokeWidth(inRenderContext.m_Path.m_Width);
+ inPathBuffer.m_PathRender->StencilStroke();
+ } else
+ inPathBuffer.m_PathRender->StencilFill();
+
+ // The stencil buffer will dictate whether this object renders or not. So we need to ignore
+ // the depth test result.
+ NVRenderBoolOp::Enum theDepthFunc = theRenderContext.GetDepthFunction();
+ theRenderContext.SetDepthFunction(NVRenderBoolOp::AlwaysTrue);
+ // Now render the path; this resets the stencil buffer.
+ SetMaterialProperties(inShader.m_Shader, inRenderContext, inRenderProperties);
+ NVBounds3 rectBounds = inPathBuffer.m_PathRender->GetPathObjectStrokeBox();
+ if (isParaboloidPass) {
+ rectBounds.scale(1.570796326795f);
+ } // PKC : More of the same ugly hack.
+ inShader.m_RectDimensions.Set(QT3DSVec4(rectBounds.minimum.x, rectBounds.minimum.y,
+ rectBounds.maximum.x, rectBounds.maximum.y));
+ theRenderContext.SetInputAssembler(m_PaintedRectInputAssembler);
+ theRenderContext.SetCullingEnabled(false);
+ // Render exactly two triangles
+ theRenderContext.Draw(NVRenderDrawMode::Triangles, 6, 0);
+ theRenderContext.SetStencilTestEnabled(isStencilEnabled);
+ theRenderContext.SetDepthFunction(theDepthFunc);
+ }
+
+ void RenderDepthPrepass(SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties inRenderProperties,
+ TShaderFeatureSet inFeatureSet) override
+ {
+ SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path);
+ if (!thePathBuffer) {
+ return;
+ }
+
+ if (thePathBuffer->m_PathType == PathTypes::Geometry) {
+ QT3DSU32 displacementIdx = 0;
+ QT3DSU32 imageIdx = 0;
+ SRenderableImage *displacementImage = 0;
+
+ for (SRenderableImage *theImage = inRenderContext.m_FirstImage;
+ theImage != NULL && displacementImage == NULL;
+ theImage = theImage->m_NextImage, ++imageIdx) {
+ if (theImage->m_MapType == ImageMapTypes::Displacement) {
+ displacementIdx = imageIdx;
+ displacementImage = theImage;
+ }
+ }
+
+ NVScopedRefCounted<SPathGeneratedShader> &theDesiredDepthShader =
+ displacementImage == NULL ? m_DepthShader : m_DepthDisplacementShader;
+
+ if (!theDesiredDepthShader) {
+ IDefaultMaterialShaderGenerator &theMaterialGenerator(
+ m_RenderContext->GetDefaultMaterialShaderGenerator());
+ SPathVertexPipeline thePipeline(
+ m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator,
+ m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable(), false);
+ thePipeline.BeginVertexGeneration(displacementIdx, displacementImage);
+ thePipeline.BeginFragmentGeneration();
+ thePipeline.Fragment().Append("\tfragOutput = vec4(1.0, 1.0, 1.0, 1.0);");
+ thePipeline.EndVertexGeneration(false);
+ thePipeline.EndFragmentGeneration(false);
+ const char8_t *shaderName = "path depth";
+ if (displacementImage)
+ shaderName = "path depth displacement";
+
+ SShaderCacheProgramFlags theFlags;
+ NVRenderShaderProgram *theProgram =
+ thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags,
+ inFeatureSet);
+ if (theProgram) {
+ theDesiredDepthShader =
+ QT3DS_NEW(m_RenderContext->GetAllocator(),
+ SPathGeneratedShader)(*theProgram, m_RenderContext->GetAllocator());
+ }
+ }
+ if (theDesiredDepthShader) {
+ DoRenderGeometryPath(*theDesiredDepthShader, inRenderContext, inRenderProperties,
+ *thePathBuffer);
+ }
+ } else {
+ // painted path, go stroke route for now.
+ if (!m_PaintedDepthShader) {
+ IDefaultMaterialShaderGenerator &theMaterialGenerator(
+ m_RenderContext->GetDefaultMaterialShaderGenerator());
+ SXYRectVertexPipeline thePipeline(
+ m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator,
+ m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable());
+ thePipeline.BeginVertexGeneration(0, NULL);
+ thePipeline.BeginFragmentGeneration();
+ thePipeline.Fragment().Append("\tfragOutput = vec4(1.0, 1.0, 1.0, 1.0);");
+ thePipeline.EndVertexGeneration(false);
+ thePipeline.EndFragmentGeneration(false);
+ const char8_t *shaderName = "path painted depth";
+ SShaderCacheProgramFlags theFlags;
+ NVRenderShaderProgram *theProgram =
+ thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags,
+ inFeatureSet);
+ if (theProgram) {
+ m_PaintedDepthShader =
+ QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)(
+ *theProgram, m_RenderContext->GetAllocator());
+ }
+ }
+ if (m_PaintedDepthShader) {
+
+ DoRenderPaintedPath(*m_PaintedDepthShader, inRenderContext, inRenderProperties,
+ *thePathBuffer);
+ }
+ }
+ }
+
+ void RenderShadowMapPass(SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties inRenderProperties,
+ TShaderFeatureSet inFeatureSet) override
+ {
+ SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path);
+ if (!thePathBuffer) {
+ return;
+ }
+
+ if (inRenderContext.m_Material.m_Type != GraphObjectTypes::DefaultMaterial)
+ return;
+
+ if (thePathBuffer->m_PathType == PathTypes::Painted) {
+ // painted path, go stroke route for now.
+ if (!m_PaintedShadowShader) {
+ IDefaultMaterialShaderGenerator &theMaterialGenerator(
+ m_RenderContext->GetDefaultMaterialShaderGenerator());
+ SXYRectVertexPipeline thePipeline(
+ m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator,
+ m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable());
+ thePipeline.OutputParaboloidDepthShaders();
+ const char8_t *shaderName = "path painted paraboloid depth";
+ SShaderCacheProgramFlags theFlags;
+ NVRenderShaderProgram *theProgram =
+ thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags,
+ inFeatureSet);
+ if (theProgram) {
+ m_PaintedShadowShader =
+ QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)(
+ *theProgram, m_RenderContext->GetAllocator());
+ }
+ }
+ if (m_PaintedShadowShader) {
+ // Setup the shader paraboloid information.
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+ theRenderContext.SetActiveShader(&m_PaintedShadowShader->m_Shader);
+
+ DoRenderPaintedPath(*m_PaintedShadowShader, inRenderContext, inRenderProperties,
+ *thePathBuffer, true);
+ }
+ } else {
+ // Until we've also got a proper path render path for this, we'll call the old-fashioned
+ // stuff.
+ RenderDepthPrepass(inRenderContext, inRenderProperties, inFeatureSet);
+ // QT3DS_ASSERT( false );
+ }
+ }
+
+ void RenderCubeFaceShadowPass(SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties inRenderProperties,
+ TShaderFeatureSet inFeatureSet) override
+ {
+ SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path);
+ if (!thePathBuffer) {
+ return;
+ }
+
+ if (inRenderContext.m_Material.m_Type != GraphObjectTypes::DefaultMaterial)
+ return;
+
+ if (thePathBuffer->m_PathType == PathTypes::Painted) {
+ if (!m_PaintedCubeShadowShader) {
+ IDefaultMaterialShaderGenerator &theMaterialGenerator(
+ m_RenderContext->GetDefaultMaterialShaderGenerator());
+ SXYRectVertexPipeline thePipeline(
+ m_RenderContext->GetShaderProgramGenerator(), theMaterialGenerator,
+ m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable());
+ thePipeline.OutputCubeFaceDepthShaders();
+ const char8_t *shaderName = "path painted cube face depth";
+ SShaderCacheProgramFlags theFlags;
+ NVRenderShaderProgram *theProgram =
+ thePipeline.ProgramGenerator().CompileGeneratedShader(shaderName, theFlags,
+ inFeatureSet);
+ if (theProgram) {
+ m_PaintedCubeShadowShader =
+ QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)(
+ *theProgram, m_RenderContext->GetAllocator());
+ }
+ }
+ if (m_PaintedCubeShadowShader) {
+ // Setup the shader information.
+ NVRenderContext &theRenderContext(m_RenderContext->GetRenderContext());
+ theRenderContext.SetActiveShader(&m_PaintedCubeShadowShader->m_Shader);
+
+ m_PaintedCubeShadowShader->m_CameraPosition.Set(
+ inRenderContext.m_Camera.GetGlobalPos());
+ m_PaintedCubeShadowShader->m_CameraProperties.Set(
+ QT3DSVec2(1.0f, inRenderContext.m_Camera.m_ClipFar));
+ m_PaintedCubeShadowShader->m_ModelMatrix.Set(inRenderContext.m_ModelMatrix);
+
+ DoRenderPaintedPath(*m_PaintedCubeShadowShader, inRenderContext, inRenderProperties,
+ *thePathBuffer, false);
+ }
+ } else {
+ // Until we've also got a proper path render path for this, we'll call the old-fashioned
+ // stuff.
+ RenderDepthPrepass(inRenderContext, inRenderProperties, inFeatureSet);
+ }
+ }
+
+ void RenderPath(SPathRenderContext &inRenderContext,
+ SLayerGlobalRenderProperties inRenderProperties,
+ TShaderFeatureSet inFeatureSet) override
+ {
+ SPathBuffer *thePathBuffer = GetPathBufferObject(inRenderContext.m_Path);
+ if (!thePathBuffer) {
+ return;
+ }
+
+ bool isDefaultMaterial =
+ (inRenderContext.m_Material.m_Type == GraphObjectTypes::DefaultMaterial);
+
+ if (thePathBuffer->m_PathType == PathTypes::Geometry) {
+ IMaterialShaderGenerator *theMaterialGenerator =
+ GetMaterialShaderGenertator(inRenderContext);
+
+ // we need a more evolved key her for custom materials
+ // the same key can still need a different shader
+ SPathShaderMapKey sPathkey = SPathShaderMapKey(GetMaterialNameForKey(inRenderContext),
+ inRenderContext.m_MaterialKey);
+ eastl::pair<TShaderMap::iterator, bool> inserter = m_PathGeometryShaders.insert(
+ eastl::make_pair(sPathkey, NVScopedRefCounted<SPathGeneratedShader>(NULL)));
+ if (inserter.second) {
+ SPathVertexPipeline thePipeline(
+ m_RenderContext->GetShaderProgramGenerator(), *theMaterialGenerator,
+ m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable(),
+ m_RenderContext->GetWireframeMode());
+
+ NVRenderShaderProgram *theProgram = NULL;
+
+ if (isDefaultMaterial) {
+ theProgram = theMaterialGenerator->GenerateShader(
+ inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline,
+ inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage,
+ inRenderContext.m_Opacity < 1.0, "path geometry pipeline-- ");
+ } else {
+ ICustomMaterialSystem &theMaterialSystem(
+ m_RenderContext->GetCustomMaterialSystem());
+ const SCustomMaterial &theCustomMaterial(
+ reinterpret_cast<const SCustomMaterial &>(inRenderContext.m_Material));
+
+ theProgram = theMaterialGenerator->GenerateShader(
+ inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline,
+ inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage,
+ inRenderContext.m_Opacity < 1.0, "path geometry pipeline-- ",
+ theMaterialSystem.GetShaderName(theCustomMaterial));
+ }
+
+ if (theProgram)
+ inserter.first->second =
+ QT3DS_NEW(m_RenderContext->GetAllocator(),
+ SPathGeneratedShader)(*theProgram, m_RenderContext->GetAllocator());
+ }
+ if (!inserter.first->second)
+ return;
+
+ DoRenderGeometryPath(*inserter.first->second.mPtr, inRenderContext, inRenderProperties,
+ *thePathBuffer);
+ } else {
+ IMaterialShaderGenerator *theMaterialGenerator =
+ GetMaterialShaderGenertator(inRenderContext);
+
+ // we need a more evolved key her for custom materials
+ // the same key can still need a different shader
+ SPathShaderMapKey sPathkey = SPathShaderMapKey(GetMaterialNameForKey(inRenderContext),
+ inRenderContext.m_MaterialKey);
+ eastl::pair<TPaintedShaderMap::iterator, bool> inserter = m_PathPaintedShaders.insert(
+ eastl::make_pair(sPathkey, NVScopedRefCounted<SPathXYGeneratedShader>(NULL)));
+
+ if (inserter.second) {
+ SXYRectVertexPipeline thePipeline(
+ m_RenderContext->GetShaderProgramGenerator(), *theMaterialGenerator,
+ m_RenderContext->GetAllocator(), m_RenderContext->GetStringTable());
+
+ NVRenderShaderProgram *theProgram = NULL;
+
+ if (isDefaultMaterial) {
+ theProgram = theMaterialGenerator->GenerateShader(
+ inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline,
+ inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage,
+ inRenderContext.m_Opacity < 1.0, "path painted pipeline-- ");
+ } else {
+ ICustomMaterialSystem &theMaterialSystem(
+ m_RenderContext->GetCustomMaterialSystem());
+ const SCustomMaterial &theCustomMaterial(
+ reinterpret_cast<const SCustomMaterial &>(inRenderContext.m_Material));
+
+ theProgram = theMaterialGenerator->GenerateShader(
+ inRenderContext.m_Material, inRenderContext.m_MaterialKey, thePipeline,
+ inFeatureSet, inRenderProperties.m_Lights, inRenderContext.m_FirstImage,
+ inRenderContext.m_Opacity < 1.0, "path painted pipeline-- ",
+ theMaterialSystem.GetShaderName(theCustomMaterial));
+ }
+
+ if (theProgram)
+ inserter.first->second =
+ QT3DS_NEW(m_RenderContext->GetAllocator(), SPathXYGeneratedShader)(
+ *theProgram, m_RenderContext->GetAllocator());
+ }
+ if (!inserter.first->second)
+ return;
+
+ DoRenderPaintedPath(*inserter.first->second.mPtr, inRenderContext, inRenderProperties,
+ *thePathBuffer);
+ }
+ }
+};
+}
+
+QT3DSVec2 IPathManagerCore::GetControlPointFromAngleDistance(QT3DSVec2 inPosition, float inIncomingAngle,
+ float inIncomingDistance)
+{
+ if (inIncomingDistance == 0.0f)
+ return inPosition;
+ float angleRad = degToRad(inIncomingAngle);
+ float angleSin = NVSin(angleRad);
+ float angleCos = NVCos(angleRad);
+ QT3DSVec2 relativeAngles = QT3DSVec2(angleCos * inIncomingDistance, angleSin * inIncomingDistance);
+ return inPosition + relativeAngles;
+}
+
+QT3DSVec2 IPathManagerCore::GetAngleDistanceFromControlPoint(QT3DSVec2 inPosition, QT3DSVec2 inControlPoint)
+{
+ QT3DSVec2 relative = inControlPoint - inPosition;
+ float angleRad = atan2(relative.y, relative.x);
+ float distance = relative.magnitude();
+ return QT3DSVec2(radToDeg(angleRad), distance);
+}
+
+IPathManagerCore &IPathManagerCore::CreatePathManagerCore(IQt3DSRenderContextCore &ctx)
+{
+ return *QT3DS_NEW(ctx.GetAllocator(), SPathManager)(ctx);
+}