summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp')
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp2074
1 files changed, 2074 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp b/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp
new file mode 100644
index 00000000..3841dd07
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialSystem.cpp
@@ -0,0 +1,2074 @@
+/****************************************************************************
+**
+** 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 "Qt3DSRenderCustomMaterialSystem.h"
+#include "Qt3DSRenderCustomMaterialRenderContext.h"
+#include "Qt3DSRenderContextCore.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "Qt3DSRenderCustomMaterial.h"
+#include "Qt3DSRenderDynamicObjectSystemCommands.h"
+#include "Qt3DSRenderBufferManager.h"
+#include "Qt3DSRenderResourceManager.h"
+#include "Qt3DSRenderMesh.h"
+#include "Qt3DSRenderCamera.h"
+#include "Qt3DSRenderLight.h"
+#include "Qt3DSRenderLayer.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "render/Qt3DSRenderComputeShader.h"
+#include "foundation/PreAllocatedAllocator.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/Qt3DSTime.h"
+#include "Qt3DSRenderDynamicObjectSystemUtil.h"
+#include "Qt3DSRenderableImage.h"
+#include "rendererimpl/Qt3DSVertexPipelineImpl.h"
+#include "rendererimpl/Qt3DSRendererImplLayerRenderData.h"
+#include "Qt3DSRenderCustomMaterialShaderGenerator.h"
+#include "Qt3DSRenderModel.h"
+
+using namespace qt3ds::render;
+using namespace qt3ds::render::dynamic;
+using qt3ds::render::NVRenderContextScopedProperty;
+using qt3ds::render::NVRenderCachedShaderProperty;
+using qt3ds::render::NVRenderCachedShaderBuffer;
+
+SCustomMaterialVertexPipeline::SCustomMaterialVertexPipeline(IQt3DSRenderContext *inContext,
+ TessModeValues::Enum inTessMode)
+ : SVertexPipelineImpl(
+ inContext->GetAllocator(), inContext->GetCustomMaterialShaderGenerator(),
+ inContext->GetShaderProgramGenerator(), inContext->GetStringTable(), false)
+ , m_Context(inContext)
+ , m_TessMode(TessModeValues::NoTess)
+{
+ if (m_Context->GetRenderContext().IsTessellationSupported()) {
+ m_TessMode = inTessMode;
+ }
+
+ if (m_Context->GetRenderContext().IsGeometryStageSupported()
+ && m_TessMode != TessModeValues::NoTess) {
+ m_Wireframe = inContext->GetWireframeMode();
+ }
+}
+
+void SCustomMaterialVertexPipeline::InitializeTessControlShader()
+{
+ if (m_TessMode == TessModeValues::NoTess
+ || ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl) == NULL) {
+ return;
+ }
+
+ IShaderStageGenerator &tessCtrlShader(
+ *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl));
+
+ tessCtrlShader.AddUniform("tessLevelInner", "float");
+ tessCtrlShader.AddUniform("tessLevelOuter", "float");
+
+ SetupTessIncludes(ShaderGeneratorStages::TessControl, m_TessMode);
+
+ tessCtrlShader.Append("void main() {\n");
+
+ tessCtrlShader.Append("\tctWorldPos[0] = varWorldPos[0];");
+ tessCtrlShader.Append("\tctWorldPos[1] = varWorldPos[1];");
+ tessCtrlShader.Append("\tctWorldPos[2] = varWorldPos[2];");
+
+ if (m_TessMode == TessModeValues::TessPhong || m_TessMode == TessModeValues::TessNPatch) {
+ tessCtrlShader.Append("\tctNorm[0] = varObjectNormal[0];");
+ tessCtrlShader.Append("\tctNorm[1] = varObjectNormal[1];");
+ tessCtrlShader.Append("\tctNorm[2] = varObjectNormal[2];");
+ }
+ if (m_TessMode == TessModeValues::TessNPatch) {
+ tessCtrlShader.Append("\tctTangent[0] = varObjTangent[0];");
+ tessCtrlShader.Append("\tctTangent[1] = varObjTangent[1];");
+ tessCtrlShader.Append("\tctTangent[2] = varObjTangent[2];");
+ }
+
+ tessCtrlShader.Append(
+ "\tgl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;");
+ tessCtrlShader.Append("\ttessShader( tessLevelOuter, tessLevelInner);\n");
+}
+
+void SCustomMaterialVertexPipeline::InitializeTessEvaluationShader()
+{
+ if (m_TessMode == TessModeValues::NoTess
+ || ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval) == NULL) {
+ return;
+ }
+
+ IShaderStageGenerator &tessEvalShader(
+ *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval));
+
+ tessEvalShader.AddUniform("model_view_projection", "mat4");
+ tessEvalShader.AddUniform("normal_matrix", "mat3");
+
+ SetupTessIncludes(ShaderGeneratorStages::TessEval, m_TessMode);
+
+ if (m_TessMode == TessModeValues::TessLinear && m_DisplacementImage) {
+ tessEvalShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib");
+ tessEvalShader.AddUniform("model_matrix", "mat4");
+ tessEvalShader.AddUniform("displace_tiling", "vec3");
+ tessEvalShader.AddUniform("displaceAmount", "float");
+ tessEvalShader.AddUniform(m_DisplacementImage->m_Image.m_ImageShaderName.c_str(),
+ "sampler2D");
+ }
+
+ tessEvalShader.Append("void main() {");
+
+ if (m_TessMode == TessModeValues::TessNPatch) {
+ tessEvalShader.Append("\tctNorm[0] = varObjectNormalTC[0];");
+ tessEvalShader.Append("\tctNorm[1] = varObjectNormalTC[1];");
+ tessEvalShader.Append("\tctNorm[2] = varObjectNormalTC[2];");
+
+ tessEvalShader.Append("\tctTangent[0] = varTangentTC[0];");
+ tessEvalShader.Append("\tctTangent[1] = varTangentTC[1];");
+ tessEvalShader.Append("\tctTangent[2] = varTangentTC[2];");
+ }
+
+ tessEvalShader.Append("\tvec4 pos = tessShader( );\n");
+}
+
+void SCustomMaterialVertexPipeline::FinalizeTessControlShader()
+{
+ IShaderStageGenerator &tessCtrlShader(
+ *ProgramGenerator().GetStage(ShaderGeneratorStages::TessControl));
+ // add varyings we must pass through
+ typedef TStrTableStrMap::const_iterator TParamIter;
+ for (TParamIter iter = m_InterpolationParameters.begin(),
+ end = m_InterpolationParameters.end();
+ iter != end; ++iter) {
+ tessCtrlShader << "\t" << iter->first.c_str()
+ << "TC[gl_InvocationID] = " << iter->first.c_str()
+ << "[gl_InvocationID];\n";
+ }
+}
+
+void SCustomMaterialVertexPipeline::FinalizeTessEvaluationShader()
+{
+ IShaderStageGenerator &tessEvalShader(
+ *ProgramGenerator().GetStage(ShaderGeneratorStages::TessEval));
+
+ eastl::string outExt("");
+ if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry)
+ outExt = "TE";
+
+ // add varyings we must pass through
+ typedef TStrTableStrMap::const_iterator TParamIter;
+ if (m_TessMode == TessModeValues::TessNPatch) {
+ for (TParamIter iter = m_InterpolationParameters.begin(),
+ end = m_InterpolationParameters.end();
+ iter != end; ++iter) {
+ tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str()
+ << " = gl_TessCoord.z * " << iter->first.c_str() << "TC[0] + ";
+ tessEvalShader << "gl_TessCoord.x * " << iter->first.c_str() << "TC[1] + ";
+ tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[2];\n";
+ }
+
+ // transform the normal
+ if (m_GenerationFlags & GenerationFlagValues::WorldNormal)
+ tessEvalShader << "\n\tvarNormal" << outExt.c_str()
+ << " = normalize(normal_matrix * teNorm);\n";
+ // transform the tangent
+ if (m_GenerationFlags & GenerationFlagValues::TangentBinormal) {
+ tessEvalShader << "\n\tvarTangent" << outExt.c_str()
+ << " = normalize(normal_matrix * teTangent);\n";
+ // transform the binormal
+ tessEvalShader << "\n\tvarBinormal" << outExt.c_str()
+ << " = normalize(normal_matrix * teBinormal);\n";
+ }
+ } else {
+ for (TParamIter iter = m_InterpolationParameters.begin(),
+ end = m_InterpolationParameters.end();
+ iter != end; ++iter) {
+ tessEvalShader << "\t" << iter->first.c_str() << outExt.c_str()
+ << " = gl_TessCoord.x * " << iter->first.c_str() << "TC[0] + ";
+ tessEvalShader << "gl_TessCoord.y * " << iter->first.c_str() << "TC[1] + ";
+ tessEvalShader << "gl_TessCoord.z * " << iter->first.c_str() << "TC[2];\n";
+ }
+
+ // displacement mapping makes only sense with linear tessellation
+ if (m_TessMode == TessModeValues::TessLinear && m_DisplacementImage) {
+ tessEvalShader
+ << "\ttexture_coordinate_info tmp = textureCoordinateInfo( varTexCoord0"
+ << outExt.c_str() << ", varTangent" << outExt.c_str() << ", varBinormal"
+ << outExt.c_str() << " );" << Endl;
+ tessEvalShader << "\ttmp = transformCoordinate( rotationTranslationScale( vec3( "
+ "0.000000, 0.000000, 0.000000 ), vec3( 0.000000, 0.000000, "
+ "0.000000 ), displace_tiling ), tmp);"
+ << Endl;
+
+ tessEvalShader << "\tpos.xyz = defaultMaterialFileDisplacementTexture( "
+ << m_DisplacementImage->m_Image.m_ImageShaderName.c_str()
+ << ", displaceAmount, "
+ << "tmp.position.xy";
+ tessEvalShader << ", varObjectNormal" << outExt.c_str() << ", pos.xyz );" << Endl;
+ tessEvalShader << "\tvarWorldPos" << outExt.c_str() << "= (model_matrix * pos).xyz;"
+ << Endl;
+ }
+
+ // transform the normal
+ tessEvalShader << "\n\tvarNormal" << outExt.c_str()
+ << " = normalize(normal_matrix * varObjectNormal" << outExt.c_str()
+ << ");\n";
+ }
+
+ tessEvalShader.Append("\tgl_Position = model_view_projection * pos;\n");
+}
+
+// Responsible for beginning all vertex and fragment generation (void main() { etc).
+void SCustomMaterialVertexPipeline::BeginVertexGeneration(QT3DSU32 displacementImageIdx,
+ SRenderableImage *displacementImage)
+{
+ m_DisplacementIdx = displacementImageIdx;
+ m_DisplacementImage = displacementImage;
+
+ TShaderGeneratorStageFlags theStages(IShaderProgramGenerator::DefaultFlags());
+
+ if (m_TessMode != TessModeValues::NoTess) {
+ theStages |= ShaderGeneratorStages::TessControl;
+ theStages |= ShaderGeneratorStages::TessEval;
+ }
+ if (m_Wireframe) {
+ theStages |= ShaderGeneratorStages::Geometry;
+ }
+
+ ProgramGenerator().BeginProgram(theStages);
+
+ if (m_TessMode != TessModeValues::NoTess) {
+ InitializeTessControlShader();
+ InitializeTessEvaluationShader();
+ }
+ if (m_Wireframe) {
+ InitializeWireframeGeometryShader();
+ }
+
+ IShaderStageGenerator &vertexShader(Vertex());
+
+ // thinks we need
+ vertexShader.AddInclude("viewProperties.glsllib");
+ vertexShader.AddInclude("customMaterial.glsllib");
+
+ vertexShader.AddIncoming("attr_pos", "vec3");
+ vertexShader << "void main()" << Endl << "{" << Endl;
+
+ if (displacementImage) {
+ GenerateUVCoords(0);
+ if (!HasTessellation()) {
+ vertexShader.AddUniform("displaceAmount", "float");
+ vertexShader.AddUniform("displace_tiling", "vec3");
+ // we create the world position setup here
+ // because it will be replaced with the displaced position
+ SetCode(GenerationFlagValues::WorldPosition);
+ vertexShader.AddUniform("model_matrix", "mat4");
+
+ vertexShader.AddInclude("defaultMaterialFileDisplacementTexture.glsllib");
+ vertexShader.AddUniform(displacementImage->m_Image.m_ImageShaderName.c_str(),
+ "sampler2D");
+
+ vertexShader << "\ttexture_coordinate_info tmp = textureCoordinateInfo( texCoord0, "
+ "varTangent, varBinormal );"
+ << Endl;
+ vertexShader << "\ttmp = transformCoordinate( rotationTranslationScale( vec3( "
+ "0.000000, 0.000000, 0.000000 ), vec3( 0.000000, 0.000000, "
+ "0.000000 ), displace_tiling ), tmp);"
+ << Endl;
+
+ vertexShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( "
+ << displacementImage->m_Image.m_ImageShaderName.c_str()
+ << ", displaceAmount, "
+ << "tmp.position.xy"
+ << ", attr_norm, attr_pos );" << Endl;
+
+ AddInterpolationParameter("varWorldPos", "vec3");
+ vertexShader.Append("\tvec3 local_model_world_position = (model_matrix * "
+ "vec4(displacedPos, 1.0)).xyz;");
+ AssignOutput("varWorldPos", "local_model_world_position");
+ }
+ }
+
+ if (HasTessellation()) {
+ vertexShader.Append("\tgl_Position = vec4(attr_pos, 1.0);");
+ } else {
+ vertexShader.AddUniform("model_view_projection", "mat4");
+ if (displacementImage)
+ vertexShader.Append(
+ "\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);");
+ else
+ vertexShader.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);");
+ }
+
+ if (HasTessellation()) {
+ GenerateWorldPosition();
+ GenerateWorldNormal();
+ GenerateObjectNormal();
+ GenerateVarTangentAndBinormal();
+ }
+}
+
+void SCustomMaterialVertexPipeline::BeginFragmentGeneration()
+{
+ Fragment().AddUniform("object_opacity", "float");
+ Fragment() << "void main()" << Endl << "{" << Endl;
+}
+
+void SCustomMaterialVertexPipeline::AssignOutput(const char8_t *inVarName,
+ const char8_t *inVarValue)
+{
+ Vertex() << "\t" << inVarName << " = " << inVarValue << ";\n";
+}
+
+void SCustomMaterialVertexPipeline::GenerateUVCoords(QT3DSU32 inUVSet)
+{
+ if (inUVSet == 0 && SetCode(GenerationFlagValues::UVCoords))
+ return;
+ if (inUVSet == 1 && SetCode(GenerationFlagValues::UVCoords1))
+ return;
+
+ QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1);
+
+ if (inUVSet == 0)
+ AddInterpolationParameter("varTexCoord0", "vec3");
+ else if (inUVSet == 1)
+ AddInterpolationParameter("varTexCoord1", "vec3");
+
+ DoGenerateUVCoords(inUVSet);
+}
+
+void SCustomMaterialVertexPipeline::GenerateWorldNormal()
+{
+ if (SetCode(GenerationFlagValues::WorldNormal))
+ return;
+ AddInterpolationParameter("varNormal", "vec3");
+ DoGenerateWorldNormal();
+}
+
+void SCustomMaterialVertexPipeline::GenerateObjectNormal()
+{
+ if (SetCode(GenerationFlagValues::ObjectNormal))
+ return;
+ DoGenerateObjectNormal();
+}
+
+void SCustomMaterialVertexPipeline::GenerateVarTangentAndBinormal()
+{
+ if (SetCode(GenerationFlagValues::TangentBinormal))
+ return;
+ AddInterpolationParameter("varTangent", "vec3");
+ AddInterpolationParameter("varBinormal", "vec3");
+ AddInterpolationParameter("varObjTangent", "vec3");
+ AddInterpolationParameter("varObjBinormal", "vec3");
+ DoGenerateVarTangentAndBinormal();
+}
+
+void SCustomMaterialVertexPipeline::GenerateWorldPosition()
+{
+ if (SetCode(GenerationFlagValues::WorldPosition))
+ return;
+
+ ActiveStage().AddUniform("model_matrix", "mat4");
+ AddInterpolationParameter("varWorldPos", "vec3");
+ AddInterpolationParameter("varObjPos", "vec3");
+ DoGenerateWorldPosition();
+}
+
+// responsible for closing all vertex and fragment generation
+void SCustomMaterialVertexPipeline::EndVertexGeneration()
+{
+ if (HasTessellation()) {
+ // finalize tess control shader
+ FinalizeTessControlShader();
+ // finalize tess evaluation shader
+ FinalizeTessEvaluationShader();
+
+ TessControl().Append("}");
+ TessEval().Append("}");
+
+ if (m_Wireframe) {
+ // finalize geometry shader
+ FinalizeWireframeGeometryShader();
+ Geometry().Append("}");
+ }
+ }
+
+ Vertex().Append("}");
+}
+
+void SCustomMaterialVertexPipeline::EndFragmentGeneration()
+{
+ Fragment().Append("}");
+}
+
+IShaderStageGenerator &SCustomMaterialVertexPipeline::ActiveStage()
+{
+ return Vertex();
+}
+
+void SCustomMaterialVertexPipeline::AddInterpolationParameter(const char8_t *inName,
+ const char8_t *inType)
+{
+ m_InterpolationParameters.insert(eastl::make_pair(Str(inName), Str(inType)));
+ Vertex().AddOutgoing(inName, inType);
+ Fragment().AddIncoming(inName, inType);
+
+ if (HasTessellation()) {
+ eastl::string nameBuilder(inName);
+ nameBuilder.append("TC");
+ TessControl().AddOutgoing(nameBuilder.c_str(), inType);
+
+ nameBuilder.assign(inName);
+ if (ProgramGenerator().GetEnabledStages() & ShaderGeneratorStages::Geometry) {
+ nameBuilder.append("TE");
+ Geometry().AddOutgoing(inName, inType);
+ }
+ TessEval().AddOutgoing(nameBuilder.c_str(), inType);
+ }
+}
+
+void SCustomMaterialVertexPipeline::DoGenerateUVCoords(QT3DSU32 inUVSet)
+{
+ QT3DS_ASSERT(inUVSet == 0 || inUVSet == 1);
+
+ if (inUVSet == 0) {
+ Vertex().AddIncoming("attr_uv0", "vec2");
+ Vertex() << "\tvec3 texCoord0 = vec3( attr_uv0, 0.0 );" << Endl;
+ AssignOutput("varTexCoord0", "texCoord0");
+ } else if (inUVSet == 1) {
+ Vertex().AddIncoming("attr_uv1", "vec2");
+ Vertex() << "\tvec3 texCoord1 = vec3( attr_uv1, 1.0 );" << Endl;
+ AssignOutput("varTexCoord1", "texCoord1");
+ }
+}
+
+void SCustomMaterialVertexPipeline::DoGenerateWorldNormal()
+{
+ IShaderStageGenerator &vertexGenerator(Vertex());
+ vertexGenerator.AddIncoming("attr_norm", "vec3");
+ vertexGenerator.AddUniform("normal_matrix", "mat3");
+
+ if (HasTessellation() == false) {
+ Vertex().Append("\tvarNormal = normalize( normal_matrix * attr_norm );");
+ }
+}
+
+void SCustomMaterialVertexPipeline::DoGenerateObjectNormal()
+{
+ AddInterpolationParameter("varObjectNormal", "vec3");
+ Vertex().Append("\tvarObjectNormal = attr_norm;");
+}
+
+void SCustomMaterialVertexPipeline::DoGenerateWorldPosition()
+{
+ Vertex().Append("\tvarObjPos = attr_pos;");
+ Vertex().Append("\tvec4 worldPos = (model_matrix * vec4(attr_pos, 1.0));");
+ AssignOutput("varWorldPos", "worldPos.xyz");
+}
+
+void SCustomMaterialVertexPipeline::DoGenerateVarTangentAndBinormal()
+{
+ Vertex().AddIncoming("attr_textan", "vec3");
+ Vertex().AddIncoming("attr_binormal", "vec3");
+
+ Vertex() << "\tvarTangent = normal_matrix * attr_textan;" << Endl
+ << "\tvarBinormal = normal_matrix * attr_binormal;" << Endl;
+
+ Vertex() << "\tvarObjTangent = attr_textan;" << Endl << "\tvarObjBinormal = attr_binormal;"
+ << Endl;
+}
+
+void SCustomMaterialVertexPipeline::DoGenerateVertexColor()
+{
+ Vertex().AddIncoming("attr_color", "vec3");
+ Vertex().Append("\tvarColor = attr_color;");
+}
+
+
+struct SMaterialClass
+{
+ NVAllocatorCallback *m_Allocator;
+ IDynamicObjectClass *m_Class;
+ bool m_HasTransparency;
+ bool m_HasRefraction;
+ bool m_HasDisplacement;
+ bool m_AlwaysDirty;
+ QT3DSU32 m_ShaderKey;
+ QT3DSU32 m_LayerCount;
+ QT3DSI32 mRefCount;
+ SMaterialClass(NVAllocatorCallback &alloc, IDynamicObjectClass &inCls)
+ : m_Allocator(&alloc)
+ , m_Class(&inCls)
+ , m_HasTransparency(false)
+ , m_HasRefraction(false)
+ , m_HasDisplacement(false)
+ , m_AlwaysDirty(false)
+ , m_ShaderKey(0)
+ , m_LayerCount(0)
+ , mRefCount(0)
+ {
+ }
+
+ ~SMaterialClass() {}
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(*m_Allocator)
+
+ void AfterWrite()
+ {
+ m_Allocator = NULL;
+ m_Class = NULL;
+ mRefCount = 0;
+ }
+
+ void AfterRead(NVAllocatorCallback &alloc, IDynamicObjectClass &inCls)
+ {
+ m_Allocator = &alloc;
+ m_Class = &inCls;
+ mRefCount = 0;
+ }
+};
+
+typedef nvhash_map<CRegisteredString, NVScopedRefCounted<SMaterialClass>> TStringMaterialMap;
+typedef eastl::pair<CRegisteredString, CRegisteredString> TStrStrPair;
+
+namespace eastl {
+template <>
+struct hash<TStrStrPair>
+{
+ size_t operator()(const TStrStrPair &item) const
+ {
+ return hash<CRegisteredString>()(item.first) ^ hash<CRegisteredString>()(item.second);
+ }
+};
+}
+
+struct SShaderMapKey
+{
+ TStrStrPair m_Name;
+ eastl::vector<SShaderPreprocessorFeature> m_Features;
+ TessModeValues::Enum m_TessMode;
+ bool m_WireframeMode;
+ SShaderDefaultMaterialKey m_MaterialKey;
+ size_t m_HashCode;
+ SShaderMapKey(TStrStrPair inName, TShaderFeatureSet inFeatures, TessModeValues::Enum inTessMode,
+ bool inWireframeMode, SShaderDefaultMaterialKey inMaterialKey)
+ : m_Name(inName)
+ , m_Features(inFeatures.begin(), inFeatures.end())
+ , m_TessMode(inTessMode)
+ , m_WireframeMode(inWireframeMode)
+ , m_MaterialKey(inMaterialKey)
+ {
+ m_HashCode = eastl::hash<TStrStrPair>()(m_Name)
+ ^ HashShaderFeatureSet(toDataRef(m_Features.data(), (QT3DSU32)m_Features.size()))
+ ^ eastl::hash<QT3DSU32>()(m_TessMode) ^ eastl::hash<bool>()(m_WireframeMode)
+ ^ eastl::hash<size_t>()(inMaterialKey.hash());
+ }
+ bool operator==(const SShaderMapKey &inKey) const
+ {
+ return m_Name == inKey.m_Name && m_Features == inKey.m_Features
+ && m_TessMode == inKey.m_TessMode && m_WireframeMode == inKey.m_WireframeMode
+ && m_MaterialKey == inKey.m_MaterialKey;
+ }
+};
+
+namespace eastl {
+template <>
+struct hash<SShaderMapKey>
+{
+ size_t operator()(const SShaderMapKey &inKey) const { return inKey.m_HashCode; }
+};
+}
+
+namespace {
+
+struct SCustomMaterialTextureData
+{
+ NVScopedRefCounted<NVRenderShaderProgram> m_Shader;
+ qt3ds::render::NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler;
+ qt3ds::render::NVRenderTexture2D *m_Texture;
+ bool m_needsMips;
+ volatile QT3DSI32 mRefCount;
+
+ SCustomMaterialTextureData(NVRenderShaderProgram &inShader, NVRenderTexture2D *inTexture,
+ const char *inTexName, bool needMips)
+ : m_Shader(inShader)
+ , m_Sampler(inTexName, inShader)
+ , m_Texture(inTexture)
+ , m_needsMips(needMips)
+ , mRefCount(0)
+ {
+ }
+
+ void Set(const SPropertyDefinition *inDefinition)
+ {
+ if (m_Texture && inDefinition) {
+ m_Texture->SetMagFilter(inDefinition->m_MagFilterOp);
+ m_Texture->SetMinFilter(inDefinition->m_MinFilterOp);
+ m_Texture->SetTextureWrapS(inDefinition->m_CoordOp);
+ m_Texture->SetTextureWrapT(inDefinition->m_CoordOp);
+ } else if (m_Texture) {
+ // set some defaults
+ m_Texture->SetMinFilter(NVRenderTextureMinifyingOp::Linear);
+ m_Texture->SetTextureWrapS(NVRenderTextureCoordOp::ClampToEdge);
+ m_Texture->SetTextureWrapT(NVRenderTextureCoordOp::ClampToEdge);
+ }
+
+ if ((m_Texture->GetNumMipmaps() == 0) && m_needsMips)
+ m_Texture->GenerateMipmaps();
+
+ m_Sampler.Set(m_Texture);
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator())
+
+ static SCustomMaterialTextureData CreateTextureEntry(NVRenderShaderProgram &inShader,
+ NVRenderTexture2D *inTexture,
+ const char *inTexName, bool needMips)
+ {
+ return SCustomMaterialTextureData(inShader, inTexture, inTexName, needMips);
+ }
+};
+
+typedef eastl::pair<CRegisteredString, NVScopedRefCounted<SCustomMaterialTextureData>>
+ TCustomMaterialTextureEntry;
+
+/**
+ * Cached tessellation property lookups this is on a per mesh base
+ */
+struct SCustomMaterialsTessellationProperties
+{
+ NVRenderCachedShaderProperty<QT3DSF32> m_EdgeTessLevel; ///< tesselation value for the edges
+ NVRenderCachedShaderProperty<QT3DSF32> m_InsideTessLevel; ///< tesselation value for the inside
+ NVRenderCachedShaderProperty<QT3DSF32>
+ m_PhongBlend; ///< blending between linear and phong component
+ NVRenderCachedShaderProperty<QT3DSVec2>
+ m_DistanceRange; ///< distance range for min and max tess level
+ NVRenderCachedShaderProperty<QT3DSF32> m_DisableCulling; ///< if set to 1.0 this disables backface
+ ///culling optimization in the tess shader
+
+ SCustomMaterialsTessellationProperties() {}
+ SCustomMaterialsTessellationProperties(NVRenderShaderProgram &inShader)
+ : m_EdgeTessLevel("tessLevelOuter", inShader)
+ , m_InsideTessLevel("tessLevelInner", inShader)
+ , m_PhongBlend("phongBlend", inShader)
+ , m_DistanceRange("distanceRange", inShader)
+ , m_DisableCulling("disableCulling", inShader)
+ {
+ }
+};
+
+/* We setup some shared state on the custom material shaders */
+struct SCustomMaterialShader
+{
+ NVScopedRefCounted<NVRenderShaderProgram> m_Shader;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_ModelMatrix;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_ViewProjMatrix;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix;
+ NVRenderCachedShaderProperty<QT3DSMat33> m_NormalMatrix;
+ NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPos;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_ProjMatrix;
+ NVRenderCachedShaderProperty<QT3DSMat44> m_ViewportMatrix;
+ NVRenderCachedShaderProperty<QT3DSVec2> m_CamProperties;
+ NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture;
+ NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AOTexture;
+ NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe;
+ NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeProps;
+ NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOpts;
+ NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeRot;
+ NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOfs;
+ NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe2;
+ NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbe2Props;
+ NVRenderCachedShaderProperty<QT3DSI32> m_LightCount;
+ NVRenderCachedShaderProperty<QT3DSI32> m_AreaLightCount;
+ NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams;
+ SCustomMaterialsTessellationProperties m_Tessellation;
+ SDynamicShaderProgramFlags m_ProgramFlags;
+ volatile QT3DSI32 mRefCount;
+ SCustomMaterialShader(NVRenderShaderProgram &inShader, SDynamicShaderProgramFlags inFlags)
+ : m_Shader(inShader)
+ , m_ModelMatrix("model_matrix", inShader)
+ , m_ViewProjMatrix("model_view_projection", inShader)
+ , m_ViewMatrix("view_matrix", inShader)
+ , m_NormalMatrix("normal_matrix", inShader)
+ , m_CameraPos("camera_position", inShader)
+ , m_ProjMatrix("view_projection_matrix", inShader)
+ , m_ViewportMatrix("viewport_matrix", inShader)
+ , m_CamProperties("camera_properties", inShader)
+ , m_DepthTexture("depth_sampler", inShader)
+ , m_AOTexture("ao_sampler", inShader)
+ , m_LightProbe("light_probe", inShader)
+ , m_LightProbeProps("light_probe_props", inShader)
+ , m_LightProbeOpts("light_probe_opts", inShader)
+ , m_LightProbeRot("light_probe_rotation", inShader)
+ , m_LightProbeOfs("light_probe_offset", inShader)
+ , m_LightProbe2("light_probe2", inShader)
+ , m_LightProbe2Props("light_probe2_props", inShader)
+ , m_LightCount("uNumLights", inShader)
+ , m_AreaLightCount("uNumAreaLights", inShader)
+ , m_AoShadowParams("cbAoShadow", inShader)
+ , m_Tessellation(inShader)
+ , m_ProgramFlags(inFlags)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator())
+};
+
+struct SMaterialOrComputeShader
+{
+ SCustomMaterialShader *m_MaterialShader;
+ NVRenderShaderProgram *m_ComputeShader;
+ SMaterialOrComputeShader()
+ : m_MaterialShader(NULL)
+ , m_ComputeShader(NULL)
+ {
+ }
+ SMaterialOrComputeShader(SCustomMaterialShader &inMaterialShader)
+ : m_MaterialShader(&inMaterialShader)
+ , m_ComputeShader(NULL)
+ {
+ }
+ SMaterialOrComputeShader(NVRenderShaderProgram &inComputeShader)
+ : m_MaterialShader(NULL)
+ , m_ComputeShader(&inComputeShader)
+ {
+ QT3DS_ASSERT(inComputeShader.GetProgramType() == NVRenderShaderProgram::ProgramType::Compute);
+ }
+ bool IsValid() const { return m_MaterialShader || m_ComputeShader; }
+ bool IsComputeShader() const { return m_ComputeShader != NULL; }
+ bool IsMaterialShader() const { return m_MaterialShader != NULL; }
+ SCustomMaterialShader &MaterialShader()
+ {
+ QT3DS_ASSERT(IsMaterialShader());
+ return *m_MaterialShader;
+ }
+ NVRenderShaderProgram &ComputeShader()
+ {
+ QT3DS_ASSERT(IsComputeShader());
+ return *m_ComputeShader;
+ }
+};
+
+struct SCustomMaterialBuffer
+{
+ CRegisteredString m_Name;
+ NVScopedRefCounted<NVRenderFrameBuffer> m_FrameBuffer;
+ NVScopedRefCounted<NVRenderTexture2D> m_Texture;
+ SAllocateBufferFlags m_Flags;
+
+ SCustomMaterialBuffer(CRegisteredString inName, NVRenderFrameBuffer &inFb,
+ NVRenderTexture2D &inTexture, SAllocateBufferFlags inFlags)
+ : m_Name(inName)
+ , m_FrameBuffer(&inFb)
+ , m_Texture(&inTexture)
+ , m_Flags(inFlags)
+ {
+ }
+ SCustomMaterialBuffer() {}
+};
+
+struct SMaterialSystem;
+typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderVertexBuffer>>
+ TStringVertexBufferMap;
+typedef nvhash_map<CRegisteredString, NVScopedRefCounted<NVRenderInputAssembler>>
+ TStringAssemblerMap;
+
+struct SStringMemoryBarrierFlagMap
+{
+ const char8_t *m_Name;
+ qt3ds::render::NVRenderBufferBarrierValues::Enum m_Value;
+ SStringMemoryBarrierFlagMap(const char8_t *nm,
+ qt3ds::render::NVRenderBufferBarrierValues::Enum val)
+ : m_Name(nm)
+ , m_Value(val)
+ {
+ }
+};
+
+SStringMemoryBarrierFlagMap g_StringMemoryFlagMap[] = {
+ SStringMemoryBarrierFlagMap("vertex_attribute",
+ qt3ds::render::NVRenderBufferBarrierValues::VertexAttribArray),
+ SStringMemoryBarrierFlagMap("element_array",
+ qt3ds::render::NVRenderBufferBarrierValues::ElementArray),
+ SStringMemoryBarrierFlagMap("uniform_buffer",
+ qt3ds::render::NVRenderBufferBarrierValues::UniformBuffer),
+ SStringMemoryBarrierFlagMap("texture_fetch",
+ qt3ds::render::NVRenderBufferBarrierValues::TextureFetch),
+ SStringMemoryBarrierFlagMap("shader_image_access",
+ qt3ds::render::NVRenderBufferBarrierValues::ShaderImageAccess),
+ SStringMemoryBarrierFlagMap("command_buffer",
+ qt3ds::render::NVRenderBufferBarrierValues::CommandBuffer),
+ SStringMemoryBarrierFlagMap("pixel_buffer",
+ qt3ds::render::NVRenderBufferBarrierValues::PixelBuffer),
+ SStringMemoryBarrierFlagMap("texture_update",
+ qt3ds::render::NVRenderBufferBarrierValues::TextureUpdate),
+ SStringMemoryBarrierFlagMap("buffer_update",
+ qt3ds::render::NVRenderBufferBarrierValues::BufferUpdate),
+ SStringMemoryBarrierFlagMap("frame_buffer",
+ qt3ds::render::NVRenderBufferBarrierValues::Framebuffer),
+ SStringMemoryBarrierFlagMap("transform_feedback",
+ qt3ds::render::NVRenderBufferBarrierValues::TransformFeedback),
+ SStringMemoryBarrierFlagMap("atomic_counter",
+ qt3ds::render::NVRenderBufferBarrierValues::AtomicCounter),
+ SStringMemoryBarrierFlagMap("shader_storage",
+ qt3ds::render::NVRenderBufferBarrierValues::ShaderStorage),
+};
+
+struct SStringBlendFuncMap
+{
+ const char8_t *m_Name;
+ qt3ds::render::NVRenderSrcBlendFunc::Enum m_Value;
+ SStringBlendFuncMap(const char8_t *nm, qt3ds::render::NVRenderSrcBlendFunc::Enum val)
+ : m_Name(nm)
+ , m_Value(val)
+ {
+ }
+};
+
+SStringBlendFuncMap g_BlendFuncMap[] = {
+#define QT3DS_RENDER_HANDLE_BLEND_FUNC(nm) \
+ SStringBlendFuncMap(#nm, qt3ds::render::NVRenderSrcBlendFunc::nm),
+#define QT3DS_RENDER_HANDLE_SRC_BLEND_FUNC(nm) \
+ SStringBlendFuncMap(#nm, qt3ds::render::NVRenderSrcBlendFunc::nm),
+ QT3DS_RENDER_ITERATE_BLEND_FUNC
+#undef QT3DS_RENDER_HANDLE_BLEND_FUNC
+#undef QT3DS_RENDER_HANDLE_SRC_BLEND_FUNC
+};
+
+struct SMaterialSystem : public ICustomMaterialSystem
+{
+ typedef nvhash_map<SShaderMapKey, NVScopedRefCounted<SCustomMaterialShader>> TShaderMap;
+ typedef eastl::pair<CRegisteredString, SImage *> TAllocatedImageEntry;
+
+ IQt3DSRenderContextCore &m_CoreContext;
+ IQt3DSRenderContext *m_Context;
+ mutable qt3ds::render::SPreAllocatedAllocator m_Allocator;
+ TStringMaterialMap m_StringMaterialMap;
+ TShaderMap m_ShaderMap;
+ nvvector<TCustomMaterialTextureEntry> m_TextureEntries;
+ nvvector<SCustomMaterialBuffer> m_AllocatedBuffers;
+ nvvector<TAllocatedImageEntry> m_AllocatedImages;
+ bool m_UseFastBlits;
+ eastl::string m_ShaderNameBuilder;
+ QT3DSU64 m_LastFrameTime;
+ QT3DSF32 m_MillisecondsSinceLastFrame;
+ QT3DSI32 mRefCount;
+
+ SMaterialSystem(IQt3DSRenderContextCore &ct)
+ : m_CoreContext(ct)
+ , m_Context(NULL)
+ , m_Allocator(ct.GetAllocator())
+ , m_StringMaterialMap(ct.GetAllocator(), "SMaterialSystem::m_StringMaterialMap")
+ , m_ShaderMap(ct.GetAllocator(), "SMaterialSystem::m_ShaderMap")
+ , m_TextureEntries(ct.GetAllocator(), "SMaterialSystem::m_TextureEntries")
+ , m_AllocatedBuffers(ct.GetAllocator(), "SMaterialSystem::m_AllocatedBuffers")
+ , m_AllocatedImages(ct.GetAllocator(), "SMaterialSystem::m_AllocatedImages")
+ , m_UseFastBlits(true)
+ , m_LastFrameTime(0)
+ , m_MillisecondsSinceLastFrame(0)
+ , mRefCount(0)
+ {
+ }
+
+ ~SMaterialSystem()
+ {
+ while (m_AllocatedBuffers.size())
+ m_AllocatedBuffers.replace_with_last(0);
+
+ for (QT3DSU32 idx = 0; idx < m_AllocatedImages.size(); ++idx) {
+ SImage *pImage = m_AllocatedImages[idx].second;
+ QT3DS_FREE(m_CoreContext.GetAllocator(), pImage);
+ }
+ m_AllocatedImages.clear();
+ }
+
+ void ReleaseBuffer(QT3DSU32 inIdx)
+ {
+ // Don't call this on MaterialSystem destroy.
+ // This causes issues for scene liftime buffers
+ // because the resource manager is destroyed before
+ IResourceManager &theManager(m_Context->GetResourceManager());
+ SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[inIdx]);
+ theEntry.m_FrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0,
+ NVRenderTextureOrRenderBuffer());
+
+ theManager.Release(*theEntry.m_FrameBuffer);
+ theManager.Release(*theEntry.m_Texture);
+ m_AllocatedBuffers.replace_with_last(inIdx);
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_CoreContext.GetAllocator())
+
+ bool IsMaterialRegistered(CRegisteredString inStr) override
+ {
+ return m_StringMaterialMap.find(inStr) != m_StringMaterialMap.end();
+ }
+
+ bool RegisterMaterialClass(CRegisteredString inName,
+ NVConstDataRef<dynamic::SPropertyDeclaration> inProperties) override
+ {
+ if (IsMaterialRegistered(inName))
+ return false;
+ m_CoreContext.GetDynamicObjectSystemCore().Register(
+ inName, inProperties, sizeof(SCustomMaterial), GraphObjectTypes::CustomMaterial);
+ IDynamicObjectClass *theClass =
+ m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(inName);
+ if (theClass == NULL) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ SMaterialClass *theNewClass = QT3DS_NEW(m_Allocator, SMaterialClass)(m_Allocator, *theClass);
+ m_StringMaterialMap.insert(eastl::make_pair(inName, theNewClass));
+ return true;
+ }
+
+ SMaterialClass *GetMaterialClass(CRegisteredString inStr)
+ {
+ TStringMaterialMap::iterator theIter = m_StringMaterialMap.find(inStr);
+ if (theIter != m_StringMaterialMap.end())
+ return theIter->second;
+ return NULL;
+ }
+
+ const SMaterialClass *GetMaterialClass(CRegisteredString inStr) const
+ {
+ return const_cast<SMaterialSystem *>(this)->GetMaterialClass(inStr);
+ }
+
+ virtual NVConstDataRef<SPropertyDefinition>
+ GetCustomMaterialProperties(CRegisteredString inCustomMaterialName) const override
+ {
+ IDynamicObjectClass *theMaterialClass =
+ m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(inCustomMaterialName);
+
+ if (theMaterialClass)
+ return theMaterialClass->GetProperties();
+
+ return NVConstDataRef<SPropertyDefinition>();
+ }
+
+ virtual QT3DSU32 FindBuffer(CRegisteredString inName)
+ {
+ for (QT3DSU32 idx = 0, end = m_AllocatedBuffers.size(); idx < end; ++idx)
+ if (m_AllocatedBuffers[idx].m_Name == inName)
+ return idx;
+ return m_AllocatedBuffers.size();
+ }
+
+ virtual QT3DSU32 FindAllocatedImage(CRegisteredString inName)
+ {
+ for (QT3DSU32 idx = 0, end = m_AllocatedImages.size(); idx < end; ++idx)
+ if (m_AllocatedImages[idx].first == inName)
+ return idx;
+ return QT3DSU32(-1);
+ }
+
+ virtual bool TextureNeedsMips(const SPropertyDefinition *inPropDec,
+ qt3ds::render::NVRenderTexture2D *inTexture)
+ {
+ if (inPropDec && inTexture) {
+ return bool((inPropDec->m_MinFilterOp == NVRenderTextureMinifyingOp::LinearMipmapLinear)
+ && (inTexture->GetNumMipmaps() == 0));
+ }
+
+ return false;
+ }
+
+ virtual void SetTexture(NVRenderShaderProgram &inShader, CRegisteredString inPropName,
+ NVRenderTexture2D *inTexture,
+ const SPropertyDefinition *inPropDec = NULL, bool needMips = false)
+ {
+ SCustomMaterialTextureData *theTextureEntry(NULL);
+ for (QT3DSU32 idx = 0, end = m_TextureEntries.size(); idx < end && theTextureEntry == NULL;
+ ++idx) {
+ if (m_TextureEntries[idx].first == inPropName
+ && m_TextureEntries[idx].second->m_Shader.mPtr == &inShader
+ && m_TextureEntries[idx].second->m_Texture == inTexture) {
+ theTextureEntry = m_TextureEntries[idx].second;
+ break;
+ }
+ }
+ if (theTextureEntry == NULL) {
+ NVScopedRefCounted<SCustomMaterialTextureData> theNewEntry =
+ QT3DS_NEW(m_CoreContext.GetAllocator(), SCustomMaterialTextureData)(
+ SCustomMaterialTextureData::CreateTextureEntry(inShader, inTexture, inPropName,
+ needMips));
+ m_TextureEntries.push_back(eastl::make_pair(inPropName, theNewEntry));
+ theTextureEntry = theNewEntry.mPtr;
+ }
+ theTextureEntry->Set(inPropDec);
+ }
+
+ void SetPropertyEnumNames(CRegisteredString inName, CRegisteredString inPropName,
+ NVConstDataRef<CRegisteredString> inNames) override
+ {
+ m_CoreContext.GetDynamicObjectSystemCore().SetPropertyEnumNames(inName, inPropName,
+ inNames);
+ }
+
+ void SetPropertyTextureSettings(CRegisteredString inName, CRegisteredString inPropName,
+ CRegisteredString inPropPath,
+ NVRenderTextureTypeValue::Enum inTexType,
+ NVRenderTextureCoordOp::Enum inCoordOp,
+ NVRenderTextureMagnifyingOp::Enum inMagFilterOp,
+ NVRenderTextureMinifyingOp::Enum inMinFilterOp) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inName);
+ if (theClass && inTexType == NVRenderTextureTypeValue::Displace) {
+ theClass->m_HasDisplacement = true;
+ }
+ m_CoreContext.GetDynamicObjectSystemCore().SetPropertyTextureSettings(
+ inName, inPropName, inPropPath, inTexType, inCoordOp, inMagFilterOp, inMinFilterOp);
+ }
+
+ void SetMaterialClassShader(CRegisteredString inName, const char8_t *inShaderType,
+ const char8_t *inShaderVersion, const char8_t *inShaderData,
+ bool inHasGeomShader, bool inIsComputeShader) override
+ {
+ m_CoreContext.GetDynamicObjectSystemCore().SetShaderData(inName, inShaderData, inShaderType,
+ inShaderVersion, inHasGeomShader,
+ inIsComputeShader);
+ }
+
+ SCustomMaterial *CreateCustomMaterial(CRegisteredString inName,
+ NVAllocatorCallback &inSceneGraphAllocator) override
+ {
+ SCustomMaterial *theMaterial = static_cast<SCustomMaterial *>(
+ m_CoreContext.GetDynamicObjectSystemCore().CreateInstance(inName,
+ inSceneGraphAllocator));
+ SMaterialClass *theClass = GetMaterialClass(inName);
+
+ if (theMaterial) {
+ QT3DSU32 key = 0, count = 0;
+ if (theClass) {
+ key = theClass->m_ShaderKey;
+ count = theClass->m_LayerCount;
+ }
+ theMaterial->Initialize(key, count);
+ }
+
+ return theMaterial;
+ }
+
+ void SetCustomMaterialTransparency(CRegisteredString inName, bool inHasTransparency) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inName);
+
+ if (theClass == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ theClass->m_HasTransparency = inHasTransparency;
+ }
+
+ void SetCustomMaterialRefraction(CRegisteredString inName, bool inHasRefraction) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inName);
+
+ if (theClass == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ theClass->m_HasRefraction = inHasRefraction;
+ }
+
+ void SetCustomMaterialAlwaysDirty(CRegisteredString inName, bool inIsAlwaysDirty) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inName);
+
+ if (theClass == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ theClass->m_AlwaysDirty = inIsAlwaysDirty;
+ }
+
+ void SetCustomMaterialShaderKey(CRegisteredString inName, QT3DSU32 inShaderKey) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inName);
+
+ if (theClass == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ theClass->m_ShaderKey = inShaderKey;
+ }
+
+ void SetCustomMaterialLayerCount(CRegisteredString inName, QT3DSU32 inLayerCount) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inName);
+
+ if (theClass == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ theClass->m_LayerCount = inLayerCount;
+ }
+
+ void SetCustomMaterialCommands(CRegisteredString inName,
+ NVConstDataRef<dynamic::SCommand *> inCommands) override
+ {
+ m_CoreContext.GetDynamicObjectSystemCore().SetRenderCommands(inName, inCommands);
+ }
+
+ CRegisteredString GetShaderCacheKey(CRenderString &inShaderKeyBuffer, const char8_t *inId,
+ const char8_t *inProgramMacro,
+ const dynamic::SDynamicShaderProgramFlags &inFlags)
+ {
+ inShaderKeyBuffer.assign(inId);
+ if (inProgramMacro && *inProgramMacro) {
+ inShaderKeyBuffer.append("#");
+ inShaderKeyBuffer.append(inProgramMacro);
+ }
+ if (inFlags.IsTessellationEnabled()) {
+ inShaderKeyBuffer.append("#");
+ inShaderKeyBuffer.append(TessModeValues::toString(inFlags.m_TessMode));
+ }
+ if (inFlags.IsGeometryShaderEnabled() && inFlags.m_WireframeMode) {
+ inShaderKeyBuffer.append("#");
+ inShaderKeyBuffer.append(inFlags.wireframeToString(inFlags.m_WireframeMode));
+ }
+
+ return m_CoreContext.GetStringTable().RegisterStr(inShaderKeyBuffer.c_str());
+ }
+
+ NVRenderShaderProgram *GetShader(SCustomMaterialRenderContext &inRenderContext,
+ const SCustomMaterial &inMaterial,
+ const SBindShader &inCommand, TShaderFeatureSet inFeatureSet,
+ const dynamic::SDynamicShaderProgramFlags &inFlags)
+ {
+ ICustomMaterialShaderGenerator &theMaterialGenerator(
+ m_Context->GetCustomMaterialShaderGenerator());
+
+ // generate key
+ CRenderString theShaderKeyBuffer;
+ CRegisteredString theKey = GetShaderCacheKey(theShaderKeyBuffer, inCommand.m_ShaderPath,
+ inCommand.m_ShaderDefine, inFlags);
+
+ SCustomMaterialVertexPipeline thePipeline(m_Context,
+ inRenderContext.m_Model.m_TessellationMode);
+
+ NVRenderShaderProgram *theProgram = theMaterialGenerator.GenerateShader(
+ inMaterial, inRenderContext.m_MaterialKey, thePipeline, inFeatureSet,
+ inRenderContext.m_Lights, inRenderContext.m_FirstImage,
+ (inMaterial.m_hasTransparency || inMaterial.m_hasRefraction),
+ "custom material pipeline-- ", inCommand.m_ShaderPath.c_str());
+
+ return theProgram;
+ }
+
+ SMaterialOrComputeShader BindShader(SCustomMaterialRenderContext &inRenderContext,
+ const SCustomMaterial &inMaterial,
+ const SBindShader &inCommand,
+ TShaderFeatureSet inFeatureSet)
+ {
+ NVRenderShaderProgram *theProgram = NULL;
+
+ SDynamicShaderProgramFlags theFlags(inRenderContext.m_Model.m_TessellationMode,
+ inRenderContext.m_Subset.m_WireframeMode);
+ theFlags.SetTessellationEnabled(inRenderContext.m_Model.m_TessellationMode
+ != TessModeValues::NoTess);
+ theFlags.SetGeometryShaderEnabled(inRenderContext.m_Subset.m_WireframeMode);
+
+ SShaderMapKey skey = SShaderMapKey(
+ TStrStrPair(inCommand.m_ShaderPath, inCommand.m_ShaderDefine), inFeatureSet,
+ theFlags.m_TessMode, theFlags.m_WireframeMode, inRenderContext.m_MaterialKey);
+ eastl::pair<TShaderMap::iterator, bool> theInsertResult(m_ShaderMap.insert(
+ eastl::make_pair(skey, NVScopedRefCounted<SCustomMaterialShader>(NULL))));
+
+ if (theInsertResult.second) {
+ theProgram = GetShader(inRenderContext, inMaterial, inCommand, inFeatureSet, theFlags);
+
+ if (theProgram) {
+ theInsertResult.first->second =
+ QT3DS_NEW(m_Allocator, SCustomMaterialShader)(*theProgram, theFlags);
+ }
+ } else if (theInsertResult.first->second)
+ theProgram = theInsertResult.first->second->m_Shader;
+
+ if (theProgram) {
+ if (theProgram->GetProgramType() == NVRenderShaderProgram::ProgramType::Graphics) {
+ if (theInsertResult.first->second) {
+ NVRenderContext &theContext(m_Context->GetRenderContext());
+ theContext.SetActiveShader(theInsertResult.first->second->m_Shader);
+ }
+
+ return *theInsertResult.first->second;
+ } else {
+ NVRenderContext &theContext(m_Context->GetRenderContext());
+ theContext.SetActiveShader(theProgram);
+ return *(static_cast<NVRenderShaderProgram *>(theProgram));
+ }
+ }
+ return SMaterialOrComputeShader();
+ }
+
+ void DoApplyInstanceValue(SCustomMaterial & /* inMaterial */, QT3DSU8 *inDataPtr,
+ CRegisteredString inPropertyName,
+ NVRenderShaderDataTypes::Enum inPropertyType,
+ NVRenderShaderProgram &inShader,
+ const SPropertyDefinition &inDefinition)
+ {
+ qt3ds::render::NVRenderShaderConstantBase *theConstant =
+ inShader.GetShaderConstant(inPropertyName);
+ using namespace qt3ds::render;
+ if (theConstant) {
+ if (theConstant->GetShaderConstantType() == inPropertyType) {
+ if (inPropertyType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) {
+ StaticAssert<sizeof(CRegisteredString)
+ == sizeof(NVRenderTexture2DPtr)>::valid_expression();
+ CRegisteredString *theStrPtr = reinterpret_cast<CRegisteredString *>(inDataPtr);
+ IBufferManager &theBufferManager(m_Context->GetBufferManager());
+ NVRenderTexture2D *theTexture = NULL;
+
+ if (theStrPtr->IsValid()) {
+ SImageTextureData theTextureData =
+ theBufferManager.LoadRenderImage(*theStrPtr);
+ if (theTextureData.m_Texture) {
+ theTexture = theTextureData.m_Texture;
+ SetTexture(inShader, inPropertyName, theTexture, &inDefinition,
+ TextureNeedsMips(&inDefinition, theTexture));
+ }
+ }
+ } else {
+ switch (inPropertyType) {
+#define HANDLE_QT3DS_SHADER_DATA_TYPE(type) \
+ case NVRenderShaderDataTypes::type: \
+ inShader.SetPropertyValue(theConstant, *(reinterpret_cast<type *>(inDataPtr))); \
+ break;
+ ITERATE_QT3DS_SHADER_DATA_TYPES
+#undef HANDLE_QT3DS_SHADER_DATA_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ } else {
+ qCCritical(INVALID_OPERATION,
+ "CustomMaterial ApplyInstanceValue command datatype and "
+ "shader datatypes differ for property %s",
+ inPropertyName.c_str());
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ void ApplyInstanceValue(SCustomMaterial &inMaterial, SMaterialClass &inClass,
+ NVRenderShaderProgram &inShader, const SApplyInstanceValue &inCommand)
+ {
+ // sanity check
+ if (inCommand.m_PropertyName.IsValid()) {
+ bool canGetData =
+ inCommand.m_ValueOffset + getSizeofShaderDataType(inCommand.m_ValueType)
+ <= inMaterial.m_DataSectionByteSize;
+ if (canGetData == false) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ QT3DSU8 *dataPtr = inMaterial.GetDataSectionBegin() + inCommand.m_ValueOffset;
+ const SPropertyDefinition *theDefinition =
+ inClass.m_Class->FindPropertyByName(inCommand.m_PropertyName);
+ if (theDefinition)
+ DoApplyInstanceValue(inMaterial, dataPtr, inCommand.m_PropertyName,
+ inCommand.m_ValueType, inShader, *theDefinition);
+ } else {
+ NVConstDataRef<SPropertyDefinition> theDefs = inClass.m_Class->GetProperties();
+ for (QT3DSU32 idx = 0, end = theDefs.size(); idx < end; ++idx) {
+ const SPropertyDefinition &theDefinition(theDefs[idx]);
+ qt3ds::render::NVRenderShaderConstantBase *theConstant =
+ inShader.GetShaderConstant(theDefinition.m_Name);
+
+ // This is fine, the property wasn't found and we continue, no problem.
+ if (!theConstant)
+ continue;
+ QT3DSU8 *dataPtr = inMaterial.GetDataSectionBegin() + theDefinition.m_Offset;
+ DoApplyInstanceValue(inMaterial, dataPtr, theDefinition.m_Name,
+ theDefinition.m_DataType, inShader, theDefinition);
+ }
+ }
+ }
+
+ void ApplyBlending(const SApplyBlending &inCommand)
+ {
+ NVRenderContext &theContext(m_Context->GetRenderContext());
+
+ theContext.SetBlendingEnabled(true);
+
+ qt3ds::render::NVRenderBlendFunctionArgument blendFunc =
+ qt3ds::render::NVRenderBlendFunctionArgument(
+ inCommand.m_SrcBlendFunc, inCommand.m_DstBlendFunc, inCommand.m_SrcBlendFunc,
+ inCommand.m_DstBlendFunc);
+
+ qt3ds::render::NVRenderBlendEquationArgument blendEqu(NVRenderBlendEquation::Add,
+ NVRenderBlendEquation::Add);
+
+ theContext.SetBlendFunction(blendFunc);
+ theContext.SetBlendEquation(blendEqu);
+ }
+
+ // we currently only bind a source texture
+ const NVRenderTexture2D *ApplyBufferValue(const SCustomMaterial &inMaterial,
+ NVRenderShaderProgram &inShader,
+ const SApplyBufferValue &inCommand,
+ const NVRenderTexture2D *inSourceTexture)
+ {
+ const NVRenderTexture2D *theTexture = NULL;
+
+ if (inCommand.m_BufferName.IsValid()) {
+ QT3DSU32 bufferIdx = FindBuffer(inCommand.m_BufferName);
+ if (bufferIdx < m_AllocatedBuffers.size()) {
+ SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]);
+ theTexture = theEntry.m_Texture;
+ } else {
+ // we must have allocated the read target before
+ qCCritical(INTERNAL_ERROR,
+ "CustomMaterial: ApplyBufferValue: Failed to setup read target");
+ QT3DS_ASSERT(false);
+ }
+ } else
+ theTexture = inSourceTexture;
+
+ if (inCommand.m_ParamName.IsValid()) {
+ qt3ds::render::NVRenderShaderConstantBase *theConstant =
+ inShader.GetShaderConstant(inCommand.m_ParamName);
+
+ if (theConstant) {
+ if (theConstant->GetShaderConstantType()
+ != NVRenderShaderDataTypes::NVRenderTexture2DPtr) {
+ qCCritical(INVALID_OPERATION,
+ "CustomMaterial %s: Binding buffer to parameter %s that is not a texture",
+ inMaterial.m_ClassName.c_str(), inCommand.m_ParamName.c_str());
+ QT3DS_ASSERT(false);
+ } else {
+ SetTexture(inShader, inCommand.m_ParamName,
+ const_cast<NVRenderTexture2D *>(theTexture));
+ }
+ }
+ }
+
+ return theTexture;
+ }
+
+ void AllocateBuffer(const SAllocateBuffer &inCommand, NVRenderFrameBuffer *inTarget)
+ {
+ STextureDetails theSourceTextureDetails;
+ NVRenderTexture2D *theTexture = NULL;
+ // get color attachment we always assume at location 0
+ if (inTarget) {
+ NVRenderTextureOrRenderBuffer theSourceTexture =
+ inTarget->GetAttachment(NVRenderFrameBufferAttachments::Color0);
+ // we need a texture
+ if (theSourceTexture.HasTexture2D()) {
+ theSourceTextureDetails = theSourceTexture.GetTexture2D()->GetTextureDetails();
+ } else {
+ qCCritical(INVALID_OPERATION, "CustomMaterial %s: Invalid source texture",
+ inCommand.m_Name.c_str());
+ QT3DS_ASSERT(false);
+ return;
+ }
+ } else {
+ NVRenderContext &theContext = m_Context->GetRenderContext();
+ // if we allocate a buffer based on the default target use viewport to get the dimension
+ NVRenderRect theViewport(theContext.GetViewport());
+ theSourceTextureDetails.m_Height = theViewport.m_Height;
+ theSourceTextureDetails.m_Width = theViewport.m_Width;
+ }
+
+ QT3DSU32 theWidth = (QT3DSU32)(theSourceTextureDetails.m_Width * inCommand.m_SizeMultiplier);
+ QT3DSU32 theHeight = (QT3DSU32)(theSourceTextureDetails.m_Height * inCommand.m_SizeMultiplier);
+ NVRenderTextureFormats::Enum theFormat = inCommand.m_Format;
+ if (theFormat == NVRenderTextureFormats::Unknown)
+ theFormat = theSourceTextureDetails.m_Format;
+ IResourceManager &theResourceManager(m_Context->GetResourceManager());
+ // size intentionally requiried every loop;
+ QT3DSU32 bufferIdx = FindBuffer(inCommand.m_Name);
+ if (bufferIdx < m_AllocatedBuffers.size()) {
+ SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]);
+ STextureDetails theDetails = theEntry.m_Texture->GetTextureDetails();
+ if (theDetails.m_Width == theWidth && theDetails.m_Height == theHeight
+ && theDetails.m_Format == theFormat) {
+ theTexture = theEntry.m_Texture;
+ } else {
+ ReleaseBuffer(bufferIdx);
+ }
+ }
+
+ if (theTexture == NULL) {
+ NVRenderFrameBuffer *theFB(theResourceManager.AllocateFrameBuffer());
+ NVRenderTexture2D *theTexture(
+ theResourceManager.AllocateTexture2D(theWidth, theHeight, theFormat));
+ theTexture->SetMagFilter(inCommand.m_FilterOp);
+ theTexture->SetMinFilter(
+ static_cast<NVRenderTextureMinifyingOp::Enum>(inCommand.m_FilterOp));
+ theTexture->SetTextureWrapS(inCommand.m_TexCoordOp);
+ theTexture->SetTextureWrapT(inCommand.m_TexCoordOp);
+ theFB->Attach(NVRenderFrameBufferAttachments::Color0, *theTexture);
+ m_AllocatedBuffers.push_back(SCustomMaterialBuffer(
+ inCommand.m_Name, *theFB, *theTexture, inCommand.m_BufferFlags));
+ }
+ }
+
+ NVRenderFrameBuffer *BindBuffer(const SCustomMaterial &inMaterial, const SBindBuffer &inCommand,
+ bool &outClearTarget, QT3DSVec2 &outDestSize)
+ {
+ NVRenderFrameBuffer *theBuffer = NULL;
+ NVRenderTexture2D *theTexture = NULL;
+
+ // search for the buffer
+ QT3DSU32 bufferIdx = FindBuffer(inCommand.m_BufferName);
+ if (bufferIdx < m_AllocatedBuffers.size()) {
+ theBuffer = m_AllocatedBuffers[bufferIdx].m_FrameBuffer;
+ theTexture = m_AllocatedBuffers[bufferIdx].m_Texture;
+ }
+
+ if (theBuffer == NULL) {
+ qCCritical(INVALID_OPERATION, "Effect %s: Failed to find buffer %s for bind",
+ inMaterial.m_ClassName.c_str(), inCommand.m_BufferName.c_str());
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+
+ if (theTexture) {
+ STextureDetails theDetails(theTexture->GetTextureDetails());
+ m_Context->GetRenderContext().SetViewport(
+ NVRenderRect(0, 0, (QT3DSU32)theDetails.m_Width, (QT3DSU32)theDetails.m_Height));
+ outDestSize = QT3DSVec2((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height);
+ outClearTarget = inCommand.m_NeedsClear;
+ }
+
+ return theBuffer;
+ }
+
+ void computeScreenCoverage(SCustomMaterialRenderContext &inRenderContext, QT3DSI32 *xMin,
+ QT3DSI32 *yMin, QT3DSI32 *xMax, QT3DSI32 *yMax)
+ {
+ NVRenderContext &theContext(m_Context->GetRenderContext());
+ TNVBounds2BoxPoints outPoints;
+ QT3DSVec4 projMin(QT3DS_MAX_REAL);
+ QT3DSVec4 projMax(-QT3DS_MAX_REAL);
+
+ // get points
+ inRenderContext.m_Subset.m_Bounds.expand(outPoints);
+ for (QT3DSU32 idx = 0; idx < 8; ++idx) {
+ QT3DSVec4 homPoint(outPoints[idx], 1.0);
+ QT3DSVec4 projPoint = inRenderContext.m_ModelViewProjection.transform(homPoint);
+ projPoint /= projPoint.w;
+
+ if (projMin.x > projPoint.x)
+ projMin.x = projPoint.x;
+ if (projMin.y > projPoint.y)
+ projMin.y = projPoint.y;
+ if (projMin.z > projPoint.z)
+ projMin.z = projPoint.z;
+
+ if (projMax.x < projPoint.x)
+ projMax.x = projPoint.x;
+ if (projMax.y < projPoint.y)
+ projMax.y = projPoint.y;
+ if (projMax.z < projPoint.z)
+ projMax.z = projPoint.z;
+ }
+
+ NVRenderRect theViewport(theContext.GetViewport());
+ QT3DSI32 x1 = QT3DSI32(projMax.x * (theViewport.m_Width / 2)
+ + (theViewport.m_X + (theViewport.m_Width / 2)));
+ QT3DSI32 y1 = QT3DSI32(projMax.y * (theViewport.m_Height / 2)
+ + (theViewport.m_Y + (theViewport.m_Height / 2)));
+
+ QT3DSI32 x2 = QT3DSI32(projMin.x * (theViewport.m_Width / 2)
+ + (theViewport.m_X + (theViewport.m_Width / 2)));
+ QT3DSI32 y2 = QT3DSI32(projMin.y * (theViewport.m_Height / 2)
+ + (theViewport.m_Y + (theViewport.m_Height / 2)));
+
+ if (x1 > x2) {
+ *xMin = x2;
+ *xMax = x1;
+ } else {
+ *xMin = x1;
+ *xMax = x2;
+ }
+ if (y1 > y2) {
+ *yMin = y2;
+ *yMax = y1;
+ } else {
+ *yMin = y1;
+ *yMax = y2;
+ }
+ }
+
+ void BlitFramebuffer(SCustomMaterialRenderContext &inRenderContext,
+ const SApplyBlitFramebuffer &inCommand, NVRenderFrameBuffer *inTarget)
+ {
+ NVRenderContext &theContext(m_Context->GetRenderContext());
+ // we change the read/render targets here
+ NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuffer(
+ theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget);
+ // we may alter scissor
+ NVRenderContextScopedProperty<bool> theScissorEnabled(
+ theContext, &NVRenderContext::IsScissorTestEnabled,
+ &NVRenderContext::SetScissorTestEnabled);
+
+ if (inCommand.m_DestBufferName.IsValid()) {
+ QT3DSU32 bufferIdx = FindBuffer(inCommand.m_DestBufferName);
+ if (bufferIdx < m_AllocatedBuffers.size()) {
+ SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]);
+ theContext.SetRenderTarget(theEntry.m_FrameBuffer);
+ } else {
+ // we must have allocated the read target before
+ qCCritical(INTERNAL_ERROR,
+ "CustomMaterial: BlitFramebuffer: Failed to setup render target");
+ QT3DS_ASSERT(false);
+ }
+ } else {
+ // our dest is the default render target
+ theContext.SetRenderTarget(inTarget);
+ }
+
+ if (inCommand.m_SourceBufferName.IsValid()) {
+ QT3DSU32 bufferIdx = FindBuffer(inCommand.m_SourceBufferName);
+ if (bufferIdx < m_AllocatedBuffers.size()) {
+ SCustomMaterialBuffer &theEntry(m_AllocatedBuffers[bufferIdx]);
+ theContext.SetReadTarget(theEntry.m_FrameBuffer);
+ theContext.SetReadBuffer(NVReadFaces::Color0);
+ } else {
+ // we must have allocated the read target before
+ qCCritical(INTERNAL_ERROR,
+ "CustomMaterial: BlitFramebuffer: Failed to setup read target");
+ QT3DS_ASSERT(false);
+ }
+ } else {
+ // our source is the default read target
+ // depending on what we render we assume color0 or back
+ theContext.SetReadTarget(inTarget);
+ NVReadFaces::Enum value = (inTarget) ? NVReadFaces::Color0 : NVReadFaces::Back;
+ theContext.SetReadBuffer(value);
+ }
+
+ NVRenderRect theViewport(theContext.GetViewport());
+ theContext.SetScissorTestEnabled(false);
+
+ if (!m_UseFastBlits) {
+ // only copy sreen amount of pixels
+ QT3DSI32 xMin, yMin, xMax, yMax;
+ computeScreenCoverage(inRenderContext, &xMin, &yMin, &xMax, &yMax);
+
+ // same dimension
+ theContext.BlitFramebuffer(xMin, yMin, xMax, yMax, xMin, yMin, xMax, yMax,
+ NVRenderClearValues::Color,
+ NVRenderTextureMagnifyingOp::Nearest);
+ } else {
+ // same dimension
+ theContext.BlitFramebuffer(
+ theViewport.m_X, theViewport.m_Y, theViewport.m_X + theViewport.m_Width,
+ theViewport.m_Y + theViewport.m_Height, theViewport.m_X, theViewport.m_Y,
+ theViewport.m_X + theViewport.m_Width, theViewport.m_Y + theViewport.m_Height,
+ NVRenderClearValues::Color, NVRenderTextureMagnifyingOp::Nearest);
+ }
+ }
+
+ SLayerGlobalRenderProperties
+ GetLayerGlobalRenderProperties(SCustomMaterialRenderContext &inRenderContext)
+ {
+ const SLayer &theLayer = inRenderContext.m_Layer;
+ const SLayerRenderData &theData = inRenderContext.m_LayerData;
+
+ return SLayerGlobalRenderProperties(
+ theLayer, const_cast<SCamera &>(inRenderContext.m_Camera), theData.m_CameraDirection,
+ inRenderContext.m_Lights, NVDataRef<QT3DSVec3>(), theData.m_ShadowMapManager.mPtr,
+ const_cast<NVRenderTexture2D *>(inRenderContext.m_DepthTexture),
+ const_cast<NVRenderTexture2D *>(inRenderContext.m_AOTexture), theLayer.m_LightProbe,
+ theLayer.m_LightProbe2, theLayer.m_ProbeHorizon, theLayer.m_ProbeBright,
+ theLayer.m_Probe2Window, theLayer.m_Probe2Pos, theLayer.m_Probe2Fade,
+ theLayer.m_ProbeFov);
+ }
+
+ void RenderPass(SCustomMaterialRenderContext &inRenderContext, SCustomMaterialShader &inShader,
+ NVRenderTexture2D * /* inSourceTexture */
+ ,
+ NVRenderFrameBuffer *inFrameBuffer, bool inRenderTargetNeedsClear,
+ NVRenderInputAssembler &inAssembler, QT3DSU32 inCount, QT3DSU32 inOffset)
+ {
+ NVRenderContext &theContext(m_Context->GetRenderContext());
+ theContext.SetRenderTarget(inFrameBuffer);
+
+ QT3DSVec4 clearColor(0.0);
+ NVRenderContextScopedProperty<QT3DSVec4> __clearColor(
+ theContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor,
+ clearColor);
+ if (inRenderTargetNeedsClear) {
+ theContext.Clear(qt3ds::render::NVRenderClearValues::Color);
+ }
+
+ ICustomMaterialShaderGenerator &theMaterialGenerator(
+ m_Context->GetCustomMaterialShaderGenerator());
+
+ theMaterialGenerator.SetMaterialProperties(
+ *inShader.m_Shader, inRenderContext.m_Material, QT3DSVec2(1.0, 1.0),
+ inRenderContext.m_ModelViewProjection, inRenderContext.m_NormalMatrix,
+ inRenderContext.m_ModelMatrix, inRenderContext.m_FirstImage, inRenderContext.m_Opacity,
+ GetLayerGlobalRenderProperties(inRenderContext));
+
+ // I think the prim type should always be fetched from the
+ // current mesh subset setup because there you get the actual draw mode
+ // for this frame
+ NVRenderDrawMode::Enum theDrawMode = inAssembler.GetPrimitiveType();
+
+ // tesselation
+ if (inRenderContext.m_Subset.m_PrimitiveType == NVRenderDrawMode::Patches) {
+ QT3DSVec2 camProps(inRenderContext.m_Camera.m_ClipNear,
+ inRenderContext.m_Camera.m_ClipFar);
+ theDrawMode = inRenderContext.m_Subset.m_PrimitiveType;
+ inShader.m_Tessellation.m_EdgeTessLevel.Set(inRenderContext.m_Subset.m_EdgeTessFactor);
+ inShader.m_Tessellation.m_InsideTessLevel.Set(
+ inRenderContext.m_Subset.m_InnerTessFactor);
+ // the blend value is hardcoded
+ inShader.m_Tessellation.m_PhongBlend.Set(0.75);
+ // this should finally be based on some user input
+ inShader.m_Tessellation.m_DistanceRange.Set(camProps);
+ // enable culling
+ inShader.m_Tessellation.m_DisableCulling.Set(0.0);
+ }
+
+ if (inRenderContext.m_Subset.m_WireframeMode) {
+ NVRenderRect theViewport(theContext.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_ViewportMatrix.Set(vpMatrix);
+ }
+
+ theContext.SetInputAssembler(&inAssembler);
+
+ theContext.SetCullingEnabled(true);
+ QT3DSU32 count = inCount;
+ QT3DSU32 offset = inOffset;
+
+ theContext.Draw(theDrawMode, count, offset);
+ }
+
+ void DoRenderCustomMaterial(SCustomMaterialRenderContext &inRenderContext,
+ const SCustomMaterial &inMaterial, SMaterialClass &inClass,
+ NVRenderFrameBuffer *inTarget, TShaderFeatureSet inFeatureSet)
+ {
+ NVRenderContext &theContext = m_Context->GetRenderContext();
+ SCustomMaterialShader *theCurrentShader(NULL);
+
+ NVRenderFrameBuffer *theCurrentRenderTarget(inTarget);
+ NVRenderRect theOriginalViewport(theContext.GetViewport());
+ NVRenderTexture2D *theCurrentSourceTexture = 0;
+
+ // for refrative materials we come from the transparent render path
+ // but we do not want to do blending
+ bool wasBlendingEnabled = theContext.IsBlendingEnabled();
+ if (inMaterial.m_hasRefraction)
+ theContext.SetBlendingEnabled(false);
+
+ NVRenderContextScopedProperty<NVRenderFrameBuffer *> __framebuffer(
+ theContext, &NVRenderContext::GetRenderTarget, &NVRenderContext::SetRenderTarget);
+ NVRenderContextScopedProperty<NVRenderRect> __viewport(
+ theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport);
+
+ QT3DSVec2 theDestSize;
+ bool theRenderTargetNeedsClear = false;
+
+ NVConstDataRef<dynamic::SCommand *> theCommands(inClass.m_Class->GetRenderCommands());
+ for (QT3DSU32 commandIdx = 0, commandEnd = theCommands.size(); commandIdx < commandEnd;
+ ++commandIdx) {
+ const SCommand &theCommand(*theCommands[commandIdx]);
+
+ switch (theCommand.m_Type) {
+ case CommandTypes::AllocateBuffer:
+ AllocateBuffer(static_cast<const SAllocateBuffer &>(theCommand), inTarget);
+ break;
+ case CommandTypes::BindBuffer:
+ theCurrentRenderTarget =
+ BindBuffer(inMaterial, static_cast<const SBindBuffer &>(theCommand),
+ theRenderTargetNeedsClear, theDestSize);
+ break;
+ case CommandTypes::BindTarget:
+ // Restore the previous render target and info.
+ theCurrentRenderTarget = inTarget;
+ theContext.SetViewport(theOriginalViewport);
+ break;
+ case CommandTypes::BindShader: {
+ theCurrentShader = NULL;
+ SMaterialOrComputeShader theBindResult =
+ BindShader(inRenderContext, inMaterial,
+ static_cast<const SBindShader &>(theCommand), inFeatureSet);
+ if (theBindResult.IsMaterialShader())
+ theCurrentShader = &theBindResult.MaterialShader();
+ } break;
+ case CommandTypes::ApplyInstanceValue:
+ // we apply the property update explicitly at the render pass
+ break;
+ case CommandTypes::Render:
+ if (theCurrentShader) {
+ RenderPass(inRenderContext, *theCurrentShader, theCurrentSourceTexture,
+ theCurrentRenderTarget, theRenderTargetNeedsClear,
+ *inRenderContext.m_Subset.m_InputAssembler,
+ inRenderContext.m_Subset.m_Count, inRenderContext.m_Subset.m_Offset);
+ }
+ // reset
+ theRenderTargetNeedsClear = false;
+ break;
+ case CommandTypes::ApplyBlending:
+ ApplyBlending(static_cast<const SApplyBlending &>(theCommand));
+ break;
+ case CommandTypes::ApplyBufferValue:
+ if (theCurrentShader)
+ ApplyBufferValue(inMaterial, *theCurrentShader->m_Shader,
+ static_cast<const SApplyBufferValue &>(theCommand),
+ theCurrentSourceTexture);
+ break;
+ case CommandTypes::ApplyBlitFramebuffer:
+ BlitFramebuffer(inRenderContext,
+ static_cast<const SApplyBlitFramebuffer &>(theCommand), inTarget);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ if (inMaterial.m_hasRefraction)
+ theContext.SetBlendingEnabled(wasBlendingEnabled);
+
+ // Release any per-frame buffers
+ for (QT3DSU32 idx = 0; idx < m_AllocatedBuffers.size(); ++idx) {
+ if (m_AllocatedBuffers[idx].m_Flags.IsSceneLifetime() == false) {
+ ReleaseBuffer(idx);
+ --idx;
+ }
+ }
+ }
+
+ const char *GetShaderName(const SCustomMaterial &inMaterial) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName);
+ if (!theClass)
+ return NULL;
+
+ NVConstDataRef<dynamic::SCommand *> theCommands = theClass->m_Class->GetRenderCommands();
+ TShaderAndFlags thePrepassShader;
+ for (QT3DSU32 idx = 0, end = theCommands.size();
+ idx < end && thePrepassShader.first.mPtr == NULL; ++idx) {
+ const SCommand &theCommand = *theCommands[idx];
+ if (theCommand.m_Type == CommandTypes::BindShader) {
+ const SBindShader &theBindCommand = static_cast<const SBindShader &>(theCommand);
+ return theBindCommand.m_ShaderPath.c_str();
+ }
+ }
+
+ QT3DS_ASSERT(false);
+ return NULL;
+ }
+
+ void ApplyShaderPropertyValues(const SCustomMaterial &inMaterial,
+ NVRenderShaderProgram &inProgram) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName);
+ if (!theClass)
+ return;
+
+ SApplyInstanceValue applier;
+ ApplyInstanceValue(const_cast<SCustomMaterial &>(inMaterial), *theClass, inProgram,
+ applier);
+ }
+
+ virtual void PrepareTextureForRender(SMaterialClass &inClass, SCustomMaterial &inMaterial)
+ {
+ NVConstDataRef<SPropertyDefinition> thePropDefs = inClass.m_Class->GetProperties();
+ for (QT3DSU32 idx = 0, end = thePropDefs.size(); idx < end; ++idx) {
+ if (thePropDefs[idx].m_DataType == NVRenderShaderDataTypes::NVRenderTexture2DPtr) {
+ if (thePropDefs[idx].m_TexUsageType == NVRenderTextureTypeValue::Displace) {
+ SImage *pImage = NULL;
+
+ // we only do this to not miss if "None" is selected
+ CRegisteredString theStrPtr = *reinterpret_cast<CRegisteredString *>(
+ inMaterial.GetDataSectionBegin() + thePropDefs[idx].m_Offset);
+
+ if (theStrPtr.IsValid()) {
+
+ QT3DSU32 index = FindAllocatedImage(thePropDefs[idx].m_ImagePath);
+ if (index == QT3DSU32(-1)) {
+ pImage = QT3DS_NEW(m_CoreContext.GetAllocator(), SImage)();
+ m_AllocatedImages.push_back(
+ eastl::make_pair(thePropDefs[idx].m_ImagePath, pImage));
+ } else
+ pImage = m_AllocatedImages[index].second;
+
+ if (inMaterial.m_DisplacementMap != pImage) {
+ inMaterial.m_DisplacementMap = pImage;
+ inMaterial.m_DisplacementMap->m_ImagePath =
+ thePropDefs[idx].m_ImagePath;
+ inMaterial.m_DisplacementMap->m_ImageShaderName =
+ thePropDefs[idx].m_Name; // this is our name in the shader
+ inMaterial.m_DisplacementMap->m_VerticalTilingMode =
+ thePropDefs[idx].m_CoordOp;
+ inMaterial.m_DisplacementMap->m_HorizontalTilingMode =
+ thePropDefs[idx].m_CoordOp;
+ }
+ } else {
+ inMaterial.m_DisplacementMap = NULL;
+ }
+ } else if (thePropDefs[idx].m_TexUsageType == NVRenderTextureTypeValue::Emissive2) {
+ SImage *pImage = NULL;
+
+ // we only do this to not miss if "None" is selected
+ CRegisteredString theStrPtr = *reinterpret_cast<CRegisteredString *>(
+ inMaterial.GetDataSectionBegin() + thePropDefs[idx].m_Offset);
+
+ if (theStrPtr.IsValid()) {
+ QT3DSU32 index = FindAllocatedImage(thePropDefs[idx].m_ImagePath);
+ if (index == QT3DSU32(-1)) {
+ pImage = QT3DS_NEW(m_CoreContext.GetAllocator(), SImage)();
+ m_AllocatedImages.push_back(
+ eastl::make_pair(thePropDefs[idx].m_ImagePath, pImage));
+ } else
+ pImage = m_AllocatedImages[index].second;
+
+ if (inMaterial.m_EmissiveMap2 != pImage) {
+ inMaterial.m_EmissiveMap2 = pImage;
+ inMaterial.m_EmissiveMap2->m_ImagePath = thePropDefs[idx].m_ImagePath;
+ inMaterial.m_EmissiveMap2->m_ImageShaderName =
+ thePropDefs[idx].m_Name; // this is our name in the shader
+ inMaterial.m_EmissiveMap2->m_VerticalTilingMode =
+ thePropDefs[idx].m_CoordOp;
+ inMaterial.m_EmissiveMap2->m_HorizontalTilingMode =
+ thePropDefs[idx].m_CoordOp;
+ }
+ } else {
+ inMaterial.m_EmissiveMap2 = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void PrepareDisplacementForRender(SMaterialClass &inClass, SCustomMaterial &inMaterial)
+ {
+ if (inMaterial.m_DisplacementMap == NULL)
+ return;
+
+ // our displacement mappin in MDL has fixed naming
+ NVConstDataRef<SPropertyDefinition> thePropDefs = inClass.m_Class->GetProperties();
+ for (QT3DSU32 idx = 0, end = thePropDefs.size(); idx < end; ++idx) {
+ if (thePropDefs[idx].m_DataType == NVRenderShaderDataTypes::QT3DSF32
+ && AreEqual(thePropDefs[idx].m_Name.c_str(), "displaceAmount")) {
+ QT3DSF32 theValue = *reinterpret_cast<const QT3DSF32 *>(inMaterial.GetDataSectionBegin()
+ + thePropDefs[idx].m_Offset);
+ inMaterial.m_DisplaceAmount = theValue;
+ } else if (thePropDefs[idx].m_DataType == NVRenderShaderDataTypes::QT3DSVec3
+ && AreEqual(thePropDefs[idx].m_Name.c_str(), "displace_tiling")) {
+ QT3DSVec3 theValue = *reinterpret_cast<const QT3DSVec3 *>(inMaterial.GetDataSectionBegin()
+ + thePropDefs[idx].m_Offset);
+ if (theValue.x != inMaterial.m_DisplacementMap->m_Scale.x
+ || theValue.y != inMaterial.m_DisplacementMap->m_Scale.y) {
+ inMaterial.m_DisplacementMap->m_Scale = QT3DSVec2(theValue.x, theValue.y);
+ inMaterial.m_DisplacementMap->m_Flags.SetTransformDirty(true);
+ }
+ }
+ }
+ }
+
+ void PrepareMaterialForRender(SMaterialClass &inClass, SCustomMaterial &inMaterial)
+ {
+ PrepareTextureForRender(inClass, inMaterial);
+
+ if (inClass.m_HasDisplacement)
+ PrepareDisplacementForRender(inClass, inMaterial);
+ }
+
+ // Returns true if the material is dirty and thus will produce a different render result
+ // than previously. This effects things like progressive AA.
+ // TODO - return more information, specifically about transparency (object is transparent,
+ // object is completely transparent
+ bool PrepareForRender(const SModel & /*inModel*/, const SRenderSubset & /*inSubset*/,
+ SCustomMaterial &inMaterial, bool clearMaterialDirtyFlags) override
+ {
+ SMaterialClass *theMaterialClass = GetMaterialClass(inMaterial.m_ClassName);
+ if (theMaterialClass == NULL) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+
+ PrepareMaterialForRender(*theMaterialClass, inMaterial);
+
+ inMaterial.m_hasTransparency = theMaterialClass->m_HasTransparency;
+ inMaterial.m_hasRefraction = theMaterialClass->m_HasRefraction;
+ inMaterial.m_hasVolumetricDF = false;
+
+ bool wasDirty = inMaterial.IsDirty() || theMaterialClass->m_AlwaysDirty;
+ if (clearMaterialDirtyFlags)
+ inMaterial.UpdateDirtyForFrame();
+
+ return wasDirty;
+ }
+
+ // TODO - handle UIC specific features such as vertex offsets for prog-aa and opacity.
+ void RenderSubset(SCustomMaterialRenderContext &inRenderContext,
+ TShaderFeatureSet inFeatureSet) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inRenderContext.m_Material.m_ClassName);
+
+ // Ensure that our overall render context comes back no matter what the client does.
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendFunctionArgument>
+ __blendFunction(m_Context->GetRenderContext(), &NVRenderContext::GetBlendFunction,
+ &NVRenderContext::SetBlendFunction,
+ qt3ds::render::NVRenderBlendFunctionArgument());
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendEquationArgument>
+ __blendEquation(m_Context->GetRenderContext(), &NVRenderContext::GetBlendEquation,
+ &NVRenderContext::SetBlendEquation,
+ qt3ds::render::NVRenderBlendEquationArgument());
+
+ NVRenderContextScopedProperty<bool> theBlendEnabled(m_Context->GetRenderContext(),
+ &NVRenderContext::IsBlendingEnabled,
+ &NVRenderContext::SetBlendingEnabled);
+
+ DoRenderCustomMaterial(inRenderContext, inRenderContext.m_Material, *theClass,
+ m_Context->GetRenderContext().GetRenderTarget(), inFeatureSet);
+ }
+
+ bool RenderDepthPrepass(const QT3DSMat44 &inMVP, const SCustomMaterial &inMaterial,
+ const SRenderSubset &inSubset) override
+ {
+ SMaterialClass *theClass = GetMaterialClass(inMaterial.m_ClassName);
+ NVConstDataRef<dynamic::SCommand *> theCommands = theClass->m_Class->GetRenderCommands();
+ TShaderAndFlags thePrepassShader;
+ for (QT3DSU32 idx = 0, end = theCommands.size();
+ idx < end && thePrepassShader.first.mPtr == NULL; ++idx) {
+ const SCommand &theCommand = *theCommands[idx];
+ if (theCommand.m_Type == CommandTypes::BindShader) {
+ const SBindShader &theBindCommand = static_cast<const SBindShader &>(theCommand);
+ thePrepassShader = m_Context->GetDynamicObjectSystem().GetDepthPrepassShader(
+ theBindCommand.m_ShaderPath, CRegisteredString(), TShaderFeatureSet());
+ }
+ }
+
+ if (thePrepassShader.first.mPtr == NULL)
+ return false;
+
+ NVRenderContext &theContext = m_Context->GetRenderContext();
+ NVRenderShaderProgram &theProgram = *thePrepassShader.first;
+ theContext.SetActiveShader(&theProgram);
+ theProgram.SetPropertyValue("model_view_projection", inMVP);
+ theContext.SetInputAssembler(inSubset.m_InputAssemblerPoints);
+ theContext.Draw(NVRenderDrawMode::Lines, inSubset.m_PosVertexBuffer->GetNumVertexes(), 0);
+ return true;
+ }
+
+ void OnMaterialActivationChange(const SCustomMaterial &inMaterial, bool inActive) override
+ {
+ Q_UNUSED(inMaterial)
+ Q_UNUSED(inActive)
+ }
+
+ void EndFrame() override
+ {
+ QT3DSU64 currentFrameTime = qt3ds::foundation::Time::getCurrentTimeInTensOfNanoSeconds();
+ if (m_LastFrameTime) {
+ QT3DSU64 timePassed = currentFrameTime - m_LastFrameTime;
+ m_MillisecondsSinceLastFrame = static_cast<QT3DSF32>(timePassed / 100000.0);
+ }
+ m_LastFrameTime = currentFrameTime;
+ }
+
+ void Save(qt3ds::render::SWriteBuffer &ioBuffer,
+ const qt3ds::render::SStrRemapMap &inRemapMap,
+ const char8_t * /*inProjectDir*/) const override
+ {
+ QT3DSU32 offset = ioBuffer.size();
+ ioBuffer.write((QT3DSU32)m_StringMaterialMap.size());
+ for (TStringMaterialMap::const_iterator iter = m_StringMaterialMap.begin(),
+ end = m_StringMaterialMap.end();
+ iter != end; ++iter) {
+ size_t nameOffset = ioBuffer.size() - offset;
+ (void)nameOffset;
+ CRegisteredString materialName(iter->first);
+ materialName.Remap(inRemapMap);
+ ioBuffer.write(materialName);
+ const SMaterialClass *materialClass = iter->second.mPtr;
+ QT3DSU32 offset = ioBuffer.size();
+ ioBuffer.write(*materialClass);
+ QT3DSU8 *materialOffset = ioBuffer.begin() + offset;
+ SMaterialClass *writtenClass = (SMaterialClass *)materialOffset;
+ writtenClass->AfterWrite();
+ ioBuffer.align(4);
+ }
+ }
+
+ void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock,
+ const char8_t * /*inProjectDir*/) override
+ {
+ m_Allocator.m_PreAllocatedBlock = inData;
+ m_Allocator.m_OwnsMemory = false;
+ qt3ds::render::SDataReader theReader(inData.begin(), inData.end());
+ QT3DSU32 numMaterialClasses = theReader.LoadRef<QT3DSU32>();
+ for (QT3DSU32 idx = 0; idx < numMaterialClasses; ++idx) {
+ CRegisteredString clsName = theReader.LoadRef<CRegisteredString>();
+ clsName.Remap(inStrDataBlock);
+ IDynamicObjectClass *theDynamicCls =
+ m_CoreContext.GetDynamicObjectSystemCore().GetDynamicObjectClass(clsName);
+ SMaterialClass *theReadClass = theReader.Load<SMaterialClass>();
+ theReader.Align(4);
+ if (theDynamicCls) {
+ theReadClass->AfterRead(m_Allocator, *theDynamicCls);
+ m_StringMaterialMap.insert(eastl::make_pair(clsName, theReadClass));
+ }
+ }
+ }
+
+ ICustomMaterialSystem &GetCustomMaterialSystem(IQt3DSRenderContext &inContext) override
+ {
+ m_Context = &inContext;
+
+ // check for fast blits
+ NVRenderContext &theContext = m_Context->GetRenderContext();
+ m_UseFastBlits = theContext.GetRenderBackendCap(
+ qt3ds::render::NVRenderBackend::NVRenderBackendCaps::FastBlits);
+
+ return *this;
+ }
+};
+}
+
+ICustomMaterialSystemCore &
+ICustomMaterialSystemCore::CreateCustomMaterialSystemCore(IQt3DSRenderContextCore &ctx)
+{
+ return *QT3DS_NEW(ctx.GetAllocator(), SMaterialSystem)(ctx);
+}
+