diff options
Diffstat (limited to 'src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp')
-rw-r--r-- | src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp | 1261 |
1 files changed, 1261 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp b/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp new file mode 100644 index 00000000..fe6a6dc3 --- /dev/null +++ b/src/Runtime/Source/runtimerender/Qt3DSRenderCustomMaterialShaderGenerator.cpp @@ -0,0 +1,1261 @@ +/**************************************************************************** +** +** 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 "Qt3DSRenderCustomMaterialShaderGenerator.h" +#include "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderableImage.h" +#include "Qt3DSRenderImage.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderLight.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderShadowMap.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderCustomMaterialSystem.h" +#include "Qt3DSRenderLightConstantProperties.h" + +using namespace qt3ds::render; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; + +namespace { +struct SShaderLightProperties +{ + NVScopedRefCounted<NVRenderShaderProgram> m_Shader; + RenderLightTypes::Enum m_LightType; + SLightSourceShader m_LightData; + volatile QT3DSI32 mRefCount; + + SShaderLightProperties(NVRenderShaderProgram &inShader) + : m_Shader(inShader) + , m_LightType(RenderLightTypes::Directional) + , mRefCount(0) + { + } + + void Set(const SLight *inLight) + { + QT3DSVec3 dir(0, 0, 1); + if (inLight->m_LightType == RenderLightTypes::Directional) { + dir = inLight->GetScalingCorrectDirection(); + // we lit in world sapce + dir *= -1; + m_LightData.m_position = QT3DSVec4(dir, 0.0); + } else if (inLight->m_LightType == RenderLightTypes::Area) { + dir = inLight->GetScalingCorrectDirection(); + m_LightData.m_position = QT3DSVec4(inLight->GetGlobalPos(), 1.0); + } else { + dir = inLight->GetGlobalPos(); + m_LightData.m_position = QT3DSVec4(dir, 1.0); + } + + m_LightType = inLight->m_LightType; + + m_LightData.m_direction = QT3DSVec4(dir, 0.0); + + float normalizedBrightness = inLight->m_Brightness / 100.0f; + m_LightData.m_diffuse = QT3DSVec4(inLight->m_DiffuseColor * normalizedBrightness, 1.0); + m_LightData.m_specular = QT3DSVec4(inLight->m_SpecularColor * normalizedBrightness, 1.0); + + if (inLight->m_LightType == RenderLightTypes::Area) { + m_LightData.m_width = inLight->m_AreaWidth; + m_LightData.m_height = inLight->m_AreaWidth; + + QT3DSMat33 theDirMatrix(inLight->m_GlobalTransform.getUpper3x3()); + m_LightData.m_right = + QT3DSVec4(theDirMatrix.transform(QT3DSVec3(1, 0, 0)), inLight->m_AreaWidth); + m_LightData.m_up = + QT3DSVec4(theDirMatrix.transform(QT3DSVec3(0, 1, 0)), inLight->m_AreaHeight); + } else { + m_LightData.m_width = 0.0; + m_LightData.m_height = 0.0; + m_LightData.m_right = QT3DSVec4(0.0f); + m_LightData.m_up = QT3DSVec4(0.0f); + + // These components only apply to CG lights + m_LightData.m_ambient = QT3DSVec4(inLight->m_AmbientColor, 1.0); + + m_LightData.m_constantAttenuation = 1.0; + m_LightData.m_linearAttenuation = inLight->m_LinearFade; + m_LightData.m_quadraticAttenuation = inLight->m_ExponentialFade; + m_LightData.m_spotCutoff = 180.0; + } + + if (m_LightType == RenderLightTypes::Point) { + m_LightData.m_shadowView = QT3DSMat44::createIdentity(); + } else { + m_LightData.m_shadowView = inLight->m_GlobalTransform; + } + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Shader->GetRenderContext().GetAllocator()) + + static SShaderLightProperties CreateLightEntry(NVRenderShaderProgram &inShader) + { + return SShaderLightProperties(inShader); + } +}; + +/** + * Cached texture property lookups, used one per texture so a shader generator for N + * textures will have an array of N of these lookup objects. + */ +struct SShaderTextureProperties +{ + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderCachedShaderProperty<QT3DSVec3> m_Offsets; + NVRenderCachedShaderProperty<QT3DSVec4> m_Rotations; + SShaderTextureProperties(const char *sampName, const char *offName, const char *rotName, + NVRenderShaderProgram &inShader) + : m_Sampler(sampName, inShader) + , m_Offsets(offName, inShader) + , m_Rotations(rotName, inShader) + { + } + SShaderTextureProperties() {} +}; + +/* We setup some shared state on the custom material shaders */ +struct SShaderGeneratorGeneratedShader +{ + typedef nvhash_map<QT3DSU32, SShaderTextureProperties> TCustomMaterialImagMap; + + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + // Specific properties we know the shader has to have. + 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; + NVRenderCachedShaderProperty<QT3DSI32> m_ShadowMapCount; + NVRenderCachedShaderProperty<QT3DSI32> m_ShadowCubeCount; + NVRenderCachedShaderProperty<QT3DSF32> m_Opacity; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_LightsBuffer; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AreaLightsBuffer; + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *m_lightsProperties; + SLightConstantProperties<SShaderGeneratorGeneratedShader> *m_areaLightsProperties; + + typedef NVRenderCachedShaderPropertyArray<NVRenderTexture2D *, + QT3DS_MAX_NUM_SHADOWS> ShadowMapPropertyArray; + typedef NVRenderCachedShaderPropertyArray<NVRenderTextureCube *, + QT3DS_MAX_NUM_SHADOWS> ShadowCubePropertyArray; + + ShadowMapPropertyArray m_shadowMaps; + ShadowCubePropertyArray m_shadowCubes; + + // Cache the image property name lookups + TCustomMaterialImagMap m_Images; // Images external to custom material usage + volatile QT3DSI32 m_RefCount; + + SShaderGeneratorGeneratedShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , 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_ShadowMapCount("uNumShadowMaps", inShader) + , m_ShadowCubeCount("uNumShadowCubes", inShader) + , m_Opacity("object_opacity", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_LightsBuffer("cbBufferLights", inShader) + , m_AreaLightsBuffer("cbBufferAreaLights", inShader) + , m_lightsProperties(nullptr) + , m_areaLightsProperties(nullptr) + , m_shadowMaps("shadowMaps[0]", inShader) + , m_shadowCubes("shadowCubes[0]", inShader) + , m_Images(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_Images") + , m_RefCount(0) + { + m_Shader.addRef(); + } + + ~SShaderGeneratorGeneratedShader() + { + m_Shader.release(); + delete m_lightsProperties; + delete m_areaLightsProperties; + } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *GetLightProperties(int count) + { + if (!m_lightsProperties || m_areaLightsProperties->m_lightCountInt < count) { + if (m_lightsProperties) + delete m_lightsProperties; + m_lightsProperties = new SLightConstantProperties<SShaderGeneratorGeneratedShader> + ("lights", "uNumLights", *this, false, count); + } + return m_lightsProperties; + } + SLightConstantProperties<SShaderGeneratorGeneratedShader> *GetAreaLightProperties(int count) + { + if (!m_areaLightsProperties || m_areaLightsProperties->m_lightCountInt < count) { + if (m_areaLightsProperties) + delete m_areaLightsProperties; + m_areaLightsProperties = new SLightConstantProperties<SShaderGeneratorGeneratedShader> + ("areaLights", "uNumAreaLights", *this, false, count); + } + return m_areaLightsProperties; + } +}; + +struct SShaderGenerator : public ICustomMaterialShaderGenerator +{ + typedef CRenderString TStrType; + typedef nvhash_map<NVRenderShaderProgram *, NVScopedRefCounted<SShaderGeneratorGeneratedShader>> + TProgramToShaderMap; + typedef eastl::pair<size_t, NVScopedRefCounted<SShaderLightProperties>> + TCustomMaterialLightEntry; + typedef eastl::pair<size_t, NVRenderCachedShaderProperty<NVRenderTexture2D *>> TShadowMapEntry; + typedef eastl::pair<size_t, NVRenderCachedShaderProperty<NVRenderTextureCube *>> + TShadowCubeEntry; + typedef qt3ds::foundation::nvhash_map<CRegisteredString, + NVScopedRefCounted<qt3ds::render::NVRenderConstantBuffer>> + TStrConstanBufMap; + + IQt3DSRenderContext &m_RenderContext; + IShaderProgramGenerator &m_ProgramGenerator; + + const SCustomMaterial *m_CurrentMaterial; + SShaderDefaultMaterialKey *m_CurrentKey; + IDefaultMaterialVertexPipeline *m_CurrentPipeline; + TShaderFeatureSet m_CurrentFeatureSet; + NVDataRef<SLight *> m_Lights; + SRenderableImage *m_FirstImage; + bool m_HasTransparency; + + TStrType m_ImageStem; + TStrType m_ImageSampler; + TStrType m_ImageFragCoords; + TStrType m_ImageRotScale; + TStrType m_ImageOffset; + + eastl::string m_GeneratedShaderString; + + SShaderDefaultMaterialKeyProperties m_DefaultMaterialShaderKeyProperties; + TProgramToShaderMap m_ProgramToShaderMap; + + nvvector<TCustomMaterialLightEntry> m_LightEntries; + + TStrConstanBufMap m_ConstantBuffers; ///< store all constants buffers + + QT3DSI32 m_RefCount; + + SShaderGenerator(IQt3DSRenderContext &inRc) + : m_RenderContext(inRc) + , m_ProgramGenerator(m_RenderContext.GetShaderProgramGenerator()) + , m_CurrentMaterial(NULL) + , m_CurrentKey(NULL) + , m_CurrentPipeline(NULL) + , m_FirstImage(NULL) + , m_HasTransparency(false) + , m_ProgramToShaderMap(inRc.GetAllocator(), "m_ProgramToShaderMap") + , m_LightEntries(inRc.GetAllocator(), "m_LightEntries") + , m_ConstantBuffers(inRc.GetAllocator(), "m_ConstantBuffers") + , m_RefCount(0) + { + } + + void addRef() override { atomicIncrement(&m_RefCount); } + void release() override + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + m_ConstantBuffers.clear(); + NVDelete(m_RenderContext.GetAllocator(), this); + } + } + + IShaderProgramGenerator &ProgramGenerator() { return m_ProgramGenerator; } + IDefaultMaterialVertexPipeline &VertexGenerator() { return *m_CurrentPipeline; } + IShaderStageGenerator &FragmentGenerator() + { + return *m_ProgramGenerator.GetStage(ShaderGeneratorStages::Fragment); + } + SShaderDefaultMaterialKey &Key() { return *m_CurrentKey; } + const SCustomMaterial &Material() { return *m_CurrentMaterial; } + TShaderFeatureSet FeatureSet() { return m_CurrentFeatureSet; } + bool HasTransparency() { return m_HasTransparency; } + + QT3DSU32 + ConvertTextureTypeValue(ImageMapTypes::Enum inType) + { + NVRenderTextureTypeValue::Enum retVal = NVRenderTextureTypeValue::Unknown; + + switch (inType) { + case ImageMapTypes::LightmapIndirect: + retVal = NVRenderTextureTypeValue::LightmapIndirect; + break; + case ImageMapTypes::LightmapRadiosity: + retVal = NVRenderTextureTypeValue::LightmapRadiosity; + break; + case ImageMapTypes::LightmapShadow: + retVal = NVRenderTextureTypeValue::LightmapShadow; + break; + case ImageMapTypes::Bump: + retVal = NVRenderTextureTypeValue::Bump; + break; + case ImageMapTypes::Diffuse: + retVal = NVRenderTextureTypeValue::Diffuse; + break; + case ImageMapTypes::Displacement: + retVal = NVRenderTextureTypeValue::Displace; + break; + default: + retVal = NVRenderTextureTypeValue::Unknown; + break; + } + + QT3DS_ASSERT(retVal != NVRenderTextureTypeValue::Unknown); + + return (QT3DSU32)retVal; + } + + SImageVariableNames GetImageVariableNames(QT3DSU32 imageIdx) override + { + // convert to NVRenderTextureTypeValue + NVRenderTextureTypeValue::Enum texType = (NVRenderTextureTypeValue::Enum)imageIdx; + m_ImageStem = NVRenderTextureTypeValue::toString(texType); + m_ImageStem.append("_"); + m_ImageSampler = m_ImageStem; + m_ImageSampler.append("sampler"); + m_ImageFragCoords = m_ImageStem; + m_ImageFragCoords.append("uv_coords"); + m_ImageRotScale = m_ImageStem; + m_ImageRotScale.append("rot_scale"); + m_ImageOffset = m_ImageStem; + m_ImageOffset.append("offset"); + + SImageVariableNames retVal; + retVal.m_ImageSampler = m_ImageSampler.c_str(); + retVal.m_ImageFragCoords = m_ImageFragCoords.c_str(); + return retVal; + } + + void SetImageShaderVariables(SShaderGeneratorGeneratedShader &inShader, + SRenderableImage &inImage) + { + // skip displacement and emissive mask maps which are handled differently + if (inImage.m_MapType == ImageMapTypes::Displacement + || inImage.m_MapType == ImageMapTypes::Emissive) + return; + + SShaderGeneratorGeneratedShader::TCustomMaterialImagMap::iterator iter = + inShader.m_Images.find(inImage.m_MapType); + if (iter == inShader.m_Images.end()) { + SImageVariableNames names = + GetImageVariableNames(ConvertTextureTypeValue(inImage.m_MapType)); + inShader.m_Images.insert(eastl::make_pair( + (QT3DSU32)inImage.m_MapType, + SShaderTextureProperties(names.m_ImageSampler, m_ImageOffset.c_str(), + m_ImageRotScale.c_str(), inShader.m_Shader))); + iter = inShader.m_Images.find(inImage.m_MapType); + } + + SShaderTextureProperties &theShaderProps = iter->second; + const QT3DSMat44 &textureTransform = inImage.m_Image.m_TextureTransform; + const QT3DSF32 *dataPtr(textureTransform.front()); + QT3DSVec3 offsets(dataPtr[12], dataPtr[13], 0.0f); + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + // The image horizontal and vertical tiling modes need to be set here, before we set texture + // on the shader. + // because setting the image on the texture forces the textue to bind and immediately apply + // any tex params. + inImage.m_Image.m_TextureData.m_Texture->SetTextureWrapS( + inImage.m_Image.m_HorizontalTilingMode); + inImage.m_Image.m_TextureData.m_Texture->SetTextureWrapT( + inImage.m_Image.m_VerticalTilingMode); + + theShaderProps.m_Sampler.Set(inImage.m_Image.m_TextureData.m_Texture); + theShaderProps.m_Offsets.Set(offsets); + theShaderProps.m_Rotations.Set(rotations); + } + + void GenerateImageUVCoordinates(IShaderStageGenerator &, QT3DSU32, QT3DSU32, SRenderableImage &) override {} + + ///< get the light constant buffer and generate if necessary + NVRenderConstantBuffer *GetLightConstantBuffer(const char *name, QT3DSU32 inLightCount) + { + NVRenderContext &theContext(m_RenderContext.GetRenderContext()); + + // we assume constant buffer support + QT3DS_ASSERT(theContext.GetConstantBufferSupport()); + // we only create if if we have lights + if (!inLightCount || !theContext.GetConstantBufferSupport()) + return NULL; + + CRegisteredString theName = theContext.GetStringTable().RegisterStr(name); + NVRenderConstantBuffer *pCB = theContext.GetConstantBuffer(theName); + + if (!pCB) { + // create with size of all structures + int for light count + SLightSourceShader s[QT3DS_MAX_NUM_LIGHTS]; + NVDataRef<QT3DSU8> cBuffer((QT3DSU8 *)&s, (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + + (4 * sizeof(QT3DSI32))); + pCB = theContext.CreateConstantBuffer( + name, qt3ds::render::NVRenderBufferUsageType::Static, + (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + (4 * sizeof(QT3DSI32)), cBuffer); + if (!pCB) { + QT3DS_ASSERT(false); + return NULL; + } + // init first set + memset(&s[0], 0x0, sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS); + QT3DSI32 cgLights[4] = {0, 0, 0, 0}; + pCB->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32) * 4)); + pCB->UpdateRaw(4 * sizeof(QT3DSI32), + NVDataRef<QT3DSU8>((QT3DSU8 *)&s[0], + sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS)); + pCB->Update(); // update to hardware + + m_ConstantBuffers.insert(eastl::make_pair(theName, pCB)); + } + + return pCB; + } + + bool GenerateVertexShader(SShaderDefaultMaterialKey &, const char8_t *inShaderPathName) + { + qt3ds::render::IDynamicObjectSystem &theDynamicSystem( + m_RenderContext.GetDynamicObjectSystem()); + CRenderString theShaderBuffer; + const char8_t *vertSource = theDynamicSystem.GetShaderSource( + m_RenderContext.GetStringTable().RegisterStr(inShaderPathName), theShaderBuffer); + + QT3DS_ASSERT(vertSource); + eastl::string srcString(vertSource); + + // Check if the vertex shader portion already contains a main function + // The same string contains both the vertex and the fragment shader + // The last "#ifdef FRAGMENT_SHADER" should mark the start of the fragment shader + eastl_size_t fragmentDefStart = srcString.find("#ifdef FRAGMENT_SHADER"); + eastl_size_t nextIndex = fragmentDefStart; + while (nextIndex != eastl::string::npos) { + nextIndex = srcString.find("#ifdef FRAGMENT_SHADER", nextIndex + 1); + if (nextIndex != eastl::string::npos) + fragmentDefStart = nextIndex; + } + eastl_size_t mainStart = srcString.find("void main()"); + + if (mainStart != eastl::string::npos && (fragmentDefStart == eastl::string::npos + || mainStart < fragmentDefStart)) { + TShaderGeneratorStageFlags stages(IShaderProgramGenerator::DefaultFlags()); + ProgramGenerator().BeginProgram(stages); + IDefaultMaterialVertexPipeline &vertexShader(VertexGenerator()); + vertexShader << "#define VERTEX_SHADER\n\n"; + vertexShader << srcString.data() << Endl; + return true; + } + + // vertex displacement + QT3DSU32 imageIdx = 0; + SRenderableImage *displacementImage = NULL; + QT3DSU32 displacementImageIdx = 0; + + for (SRenderableImage *img = m_FirstImage; img != NULL; + img = img->m_NextImage, ++imageIdx) { + if (img->m_MapType == ImageMapTypes::Displacement) { + displacementImage = img; + displacementImageIdx = imageIdx; + break; + } + } + + // the pipeline opens/closes up the shaders stages + VertexGenerator().BeginVertexGeneration(displacementImageIdx, displacementImage); + return false; + } + + SShaderGeneratorGeneratedShader &GetShaderForProgram(NVRenderShaderProgram &inProgram) + { + eastl::pair<TProgramToShaderMap::iterator, bool> inserter = + m_ProgramToShaderMap.insert(eastl::make_pair( + &inProgram, NVScopedRefCounted<SShaderGeneratorGeneratedShader>(NULL))); + if (inserter.second) { + NVAllocatorCallback &alloc(m_RenderContext.GetRenderContext().GetAllocator()); + inserter.first->second = QT3DS_NEW(alloc, SShaderGeneratorGeneratedShader)( + inProgram, m_RenderContext.GetRenderContext()); + } + return *inserter.first->second; + } + + virtual SShaderLightProperties *SetLight(NVRenderShaderProgram &inShader, size_t lightIdx, + size_t shadeIdx, const SLight *inLight, + SShadowMapEntry *inShadow, QT3DSI32 shadowIdx, + QT3DSF32 shadowDist) + { + SShaderLightProperties *theLightEntry(NULL); + for (QT3DSU32 idx = 0, end = m_LightEntries.size(); idx < end && theLightEntry == NULL; + ++idx) { + if (m_LightEntries[idx].first == lightIdx + && m_LightEntries[idx].second->m_Shader.mPtr == &inShader + && m_LightEntries[idx].second->m_LightType == inLight->m_LightType) { + theLightEntry = m_LightEntries[idx].second; + } + } + if (theLightEntry == NULL) { + // create a new name + eastl::string lightName; + if (inLight->m_LightType == RenderLightTypes::Area) + lightName = "arealights"; + else + lightName = "lights"; + char buf[16]; + _snprintf(buf, 16, "[%d]", int(shadeIdx)); + lightName.append(buf); + + NVScopedRefCounted<SShaderLightProperties> theNewEntry = + QT3DS_NEW(m_RenderContext.GetAllocator(), + SShaderLightProperties)(SShaderLightProperties::CreateLightEntry(inShader)); + m_LightEntries.push_back(eastl::make_pair(lightIdx, theNewEntry)); + theLightEntry = theNewEntry.mPtr; + } + theLightEntry->Set(inLight); + theLightEntry->m_LightData.m_shadowControls = + QT3DSVec4(inLight->m_ShadowBias, inLight->m_ShadowFactor, shadowDist, 0.0); + theLightEntry->m_LightData.m_shadowIdx = (inShadow) ? shadowIdx : -1; + + return theLightEntry; + } + + void SetShadowMaps(NVRenderShaderProgram &inProgram, SShadowMapEntry *inShadow, + QT3DSI32 &numShadowMaps, QT3DSI32 &numShadowCubes, bool shadowMap, + SShaderGeneratorGeneratedShader::ShadowMapPropertyArray &shadowMaps, + SShaderGeneratorGeneratedShader::ShadowCubePropertyArray &shadowCubes) + { + Q_UNUSED(inProgram) + if (inShadow) { + if (shadowMap == false && inShadow->m_DepthCube + && (numShadowCubes < QT3DS_MAX_NUM_SHADOWS)) { + shadowCubes.m_array[numShadowCubes] = inShadow->m_DepthCube.mPtr; + ++numShadowCubes; + } else if (shadowMap && inShadow->m_DepthMap + && (numShadowMaps < QT3DS_MAX_NUM_SHADOWS)) { + shadowMaps.m_array[numShadowMaps] = inShadow->m_DepthMap.mPtr; + ++numShadowMaps; + } + } + } + + void SetGlobalProperties(NVRenderShaderProgram &inProgram, const SLayer & /*inLayer*/ + , + SCamera &inCamera, QT3DSVec3, NVDataRef<SLight *> inLights, + NVDataRef<QT3DSVec3>, Qt3DSShadowMap *inShadowMaps) + { + SShaderGeneratorGeneratedShader &theShader(GetShaderForProgram(inProgram)); + m_RenderContext.GetRenderContext().SetActiveShader(&inProgram); + + SCamera &theCamera(inCamera); + + QT3DSVec2 camProps(theCamera.m_ClipNear, theCamera.m_ClipFar); + theShader.m_CamProperties.Set(camProps); + theShader.m_CameraPos.Set(theCamera.GetGlobalPos()); + + if (theShader.m_ViewMatrix.IsValid()) + theShader.m_ViewMatrix.Set(theCamera.m_GlobalTransform.getInverse()); + + if (theShader.m_ProjMatrix.IsValid()) { + QT3DSMat44 vProjMat; + inCamera.CalculateViewProjectionMatrix(vProjMat); + theShader.m_ProjMatrix.Set(vProjMat); + } + + // set lights separate for area lights + QT3DSI32 cgLights = 0, areaLights = 0; + QT3DSI32 numShadowMaps = 0, numShadowCubes = 0; + + // this call setup the constant buffer for ambient occlusion and shadow + theShader.m_AoShadowParams.Set(); + + if (m_RenderContext.GetRenderContext().GetConstantBufferSupport()) { + NVRenderConstantBuffer *pLightCb = + GetLightConstantBuffer("cbBufferLights", inLights.size()); + NVRenderConstantBuffer *pAreaLightCb = + GetLightConstantBuffer("cbBufferAreaLights", inLights.size()); + + // Split the count between CG lights and area lights + for (QT3DSU32 lightIdx = 0; lightIdx < inLights.size() && pLightCb; ++lightIdx) { + SShadowMapEntry *theShadow = NULL; + if (inShadowMaps && inLights[lightIdx]->m_CastShadow) + theShadow = inShadowMaps->GetShadowMapEntry(lightIdx); + + QT3DSI32 shdwIdx = (inLights[lightIdx]->m_LightType + != RenderLightTypes::Directional) + ? numShadowCubes + : numShadowMaps; + SetShadowMaps(inProgram, theShadow, numShadowMaps, numShadowCubes, + inLights[lightIdx]->m_LightType == RenderLightTypes::Directional, + theShader.m_shadowMaps, theShader.m_shadowCubes); + + if (inLights[lightIdx]->m_LightType == RenderLightTypes::Area) { + SShaderLightProperties *theAreaLightEntry = + SetLight(inProgram, lightIdx, areaLights, inLights[lightIdx], theShadow, + shdwIdx, inCamera.m_ClipFar); + + if (theAreaLightEntry && pAreaLightCb) { + pAreaLightCb->UpdateRaw( + areaLights * sizeof(SLightSourceShader) + (4 * sizeof(QT3DSI32)), + NVDataRef<QT3DSU8>((QT3DSU8 *)&theAreaLightEntry->m_LightData, + sizeof(SLightSourceShader))); + } + + areaLights++; + } else { + SShaderLightProperties *theLightEntry = + SetLight(inProgram, lightIdx, cgLights, inLights[lightIdx], theShadow, + shdwIdx, inCamera.m_ClipFar); + + if (theLightEntry && pLightCb) { + pLightCb->UpdateRaw( + cgLights * sizeof(SLightSourceShader) + (4 * sizeof(QT3DSI32)), + NVDataRef<QT3DSU8>((QT3DSU8 *)&theLightEntry->m_LightData, + sizeof(SLightSourceShader))); + } + + cgLights++; + } + } + + if (pLightCb) { + pLightCb->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32))); + theShader.m_LightsBuffer.Set(); + } + if (pAreaLightCb) { + pAreaLightCb->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&areaLights, + sizeof(QT3DSI32))); + theShader.m_AreaLightsBuffer.Set(); + } + + theShader.m_LightCount.Set(cgLights); + theShader.m_AreaLightCount.Set(areaLights); + } else { + QVector<SShaderLightProperties *> lprop; + QVector<SShaderLightProperties *> alprop; + for (QT3DSU32 lightIdx = 0; lightIdx < inLights.size(); ++lightIdx) { + + SShadowMapEntry *theShadow = NULL; + if (inShadowMaps && inLights[lightIdx]->m_CastShadow) + theShadow = inShadowMaps->GetShadowMapEntry(lightIdx); + + QT3DSI32 shdwIdx = (inLights[lightIdx]->m_LightType + != RenderLightTypes::Directional) + ? numShadowCubes + : numShadowMaps; + SetShadowMaps(inProgram, theShadow, numShadowMaps, numShadowCubes, + inLights[lightIdx]->m_LightType == RenderLightTypes::Directional, + theShader.m_shadowMaps, theShader.m_shadowCubes); + + SShaderLightProperties *p = SetLight(inProgram, lightIdx, areaLights, + inLights[lightIdx], theShadow, + shdwIdx, inCamera.m_ClipFar); + if (inLights[lightIdx]->m_LightType == RenderLightTypes::Area) + alprop.push_back(p); + else + lprop.push_back(p); + } + SLightConstantProperties<SShaderGeneratorGeneratedShader> *lightProperties + = theShader.GetLightProperties(lprop.size()); + SLightConstantProperties<SShaderGeneratorGeneratedShader> *areaLightProperties + = theShader.GetAreaLightProperties(alprop.size()); + + lightProperties->updateLights(lprop); + areaLightProperties->updateLights(alprop); + + theShader.m_LightCount.Set(lprop.size()); + theShader.m_AreaLightCount.Set(alprop.size()); + } + for (int i = numShadowMaps; i < QT3DS_MAX_NUM_SHADOWS; ++i) + theShader.m_shadowMaps.m_array[i] = NULL; + for (int i = numShadowCubes; i < QT3DS_MAX_NUM_SHADOWS; ++i) + theShader.m_shadowCubes.m_array[i] = NULL; + theShader.m_shadowMaps.Set(numShadowMaps); + theShader.m_shadowCubes.Set(numShadowCubes); + theShader.m_ShadowMapCount.Set(numShadowMaps); + theShader.m_ShadowCubeCount.Set(numShadowCubes); + } + + void SetMaterialProperties(NVRenderShaderProgram &inProgram, const SCustomMaterial &inMaterial, + const QT3DSVec2 &, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + NVRenderTexture2D *inDepthTexture, NVRenderTexture2D *inSSaoTexture, + SImage *inLightProbe, SImage *inLightProbe2, QT3DSF32 inProbeHorizon, + QT3DSF32 inProbeBright, QT3DSF32 inProbe2Window, QT3DSF32 inProbe2Pos, + QT3DSF32 inProbe2Fade, QT3DSF32 inProbeFOV) + { + ICustomMaterialSystem &theMaterialSystem(m_RenderContext.GetCustomMaterialSystem()); + SShaderGeneratorGeneratedShader &theShader(GetShaderForProgram(inProgram)); + + theShader.m_ViewProjMatrix.Set(inModelViewProjection); + theShader.m_NormalMatrix.Set(inNormalMatrix); + theShader.m_ModelMatrix.Set(inGlobalTransform); + + theShader.m_DepthTexture.Set(inDepthTexture); + theShader.m_AOTexture.Set(inSSaoTexture); + + theShader.m_Opacity.Set(inOpacity); + + qt3ds::render::SImage *theLightProbe = inLightProbe; + qt3ds::render::SImage *theLightProbe2 = inLightProbe2; + + if (inMaterial.m_IblProbe && inMaterial.m_IblProbe->m_TextureData.m_Texture) { + theLightProbe = inMaterial.m_IblProbe; + } + + if (theLightProbe) { + if (theLightProbe->m_TextureData.m_Texture) { + NVRenderTextureCoordOp::Enum theHorzLightProbeTilingMode = + theLightProbe->m_HorizontalTilingMode; + NVRenderTextureCoordOp::Enum theVertLightProbeTilingMode = + theLightProbe->m_VerticalTilingMode; + theLightProbe->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + + const QT3DSMat44 &textureTransform = theLightProbe->m_TextureTransform; + // We separate rotational information from offset information so that just maybe the + // shader + // will attempt to push less information to the card. + const QT3DSF32 *dataPtr(textureTransform.front()); + // The third member of the offsets contains a flag indicating if the texture was + // premultiplied or not. + // We use this to mix the texture alpha. + // light_probe_offsets.w is now no longer being used to enable/disable fast IBL, + // (it's now the only option) + // So now, it's storing the number of mip levels in the IBL image. + QT3DSVec4 offsets(dataPtr[12], dataPtr[13], + theLightProbe->m_TextureData.m_TextureFlags.IsPreMultiplied() ? 1.0f + : 0.0f, + (float)theLightProbe->m_TextureData.m_Texture->GetNumMipmaps()); + // Fast IBL is always on; + // inRenderContext.m_Layer.m_FastIbl ? 1.0f : 0.0f ); + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + theShader.m_LightProbeRot.Set(rotations); + theShader.m_LightProbeOfs.Set(offsets); + + if ((!inMaterial.m_IblProbe) && (inProbeFOV < 180.f)) { + theShader.m_LightProbeOpts.Set( + QT3DSVec4(0.01745329251994329547f * inProbeFOV, 0.0f, 0.0f, 0.0f)); + } + + // Also make sure to add the secondary texture, but it should only be added if the + // primary + // (i.e. background) texture is also there. + if (theLightProbe2 && theLightProbe2->m_TextureData.m_Texture) { + theLightProbe2->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe2->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + theShader.m_LightProbe2.Set(theLightProbe2->m_TextureData.m_Texture); + theShader.m_LightProbe2Props.Set( + QT3DSVec4(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f)); + + const QT3DSMat44 &xform2 = theLightProbe2->m_TextureTransform; + const QT3DSF32 *dataPtr(xform2.front()); + + theShader.m_LightProbeProps.Set( + QT3DSVec4(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f)); + } else { + theShader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + theShader.m_LightProbeProps.Set( + QT3DSVec4(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f)); + } + } else { + theShader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + theShader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + + theShader.m_LightProbe.Set(theLightProbe->m_TextureData.m_Texture); + + } else { + theShader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + theShader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + + // finally apply custom material shader properties + theMaterialSystem.ApplyShaderPropertyValues(inMaterial, inProgram); + + // additional textures + for (SRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_NextImage) + SetImageShaderVariables(theShader, *theImage); + } + + void SetMaterialProperties(NVRenderShaderProgram &inProgram, + const SGraphObject &inMaterial, const QT3DSVec2 &inCameraVec, + const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, + const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) override + { + const SCustomMaterial &theCustomMaterial( + reinterpret_cast<const SCustomMaterial &>(inMaterial)); + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::CustomMaterial); + + SetGlobalProperties(inProgram, inRenderProperties.m_Layer, inRenderProperties.m_Camera, + inRenderProperties.m_CameraDirection, inRenderProperties.m_Lights, + inRenderProperties.m_LightDirections, + inRenderProperties.m_ShadowMapManager); + + SetMaterialProperties(inProgram, theCustomMaterial, inCameraVec, inModelViewProjection, + inNormalMatrix, inGlobalTransform, inFirstImage, inOpacity, + inRenderProperties.m_DepthTexture, inRenderProperties.m_SSaoTexture, + inRenderProperties.m_LightProbe, inRenderProperties.m_LightProbe2, + inRenderProperties.m_ProbeHorizon, inRenderProperties.m_ProbeBright, + inRenderProperties.m_Probe2Window, inRenderProperties.m_Probe2Pos, + inRenderProperties.m_Probe2Fade, inRenderProperties.m_ProbeFOV); + } + + void GenerateLightmapIndirectFunc(IShaderStageGenerator &inFragmentShader, + SImage *pEmissiveLightmap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec3 computeMaterialLightmapIndirect()\n{\n"; + inFragmentShader << " vec4 indirect = vec4( 0.0, 0.0, 0.0, 0.0 );\n"; + if (pEmissiveLightmap) { + SImageVariableNames names = + GetImageVariableNames(ConvertTextureTypeValue(ImageMapTypes::LightmapIndirect)); + inFragmentShader.AddUniform(names.m_ImageSampler, "sampler2D"); + inFragmentShader.AddUniform(m_ImageOffset, "vec3"); + inFragmentShader.AddUniform(m_ImageRotScale, "vec4"); + + inFragmentShader << "\n indirect = evalIndirectLightmap( " << m_ImageSampler + << ", varTexCoord1, "; + inFragmentShader << m_ImageRotScale << ", "; + inFragmentShader << m_ImageOffset << " );\n\n"; + } + + inFragmentShader << " return indirect.rgb;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapRadiosityFunc(IShaderStageGenerator &inFragmentShader, + SImage *pRadiosityLightmap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec3 computeMaterialLightmapRadiosity()\n{\n"; + inFragmentShader << " vec4 radiosity = vec4( 1.0, 1.0, 1.0, 1.0 );\n"; + if (pRadiosityLightmap) { + SImageVariableNames names = + GetImageVariableNames(ConvertTextureTypeValue(ImageMapTypes::LightmapRadiosity)); + inFragmentShader.AddUniform(names.m_ImageSampler, "sampler2D"); + inFragmentShader.AddUniform(m_ImageOffset, "vec3"); + inFragmentShader.AddUniform(m_ImageRotScale, "vec4"); + + inFragmentShader << "\n radiosity = evalRadiosityLightmap( " << m_ImageSampler + << ", varTexCoord1, "; + inFragmentShader << m_ImageRotScale << ", "; + inFragmentShader << m_ImageOffset << " );\n\n"; + } + + inFragmentShader << " return radiosity.rgb;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapShadowFunc(IShaderStageGenerator &inFragmentShader, + SImage *pBakedShadowMap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec4 computeMaterialLightmapShadow()\n{\n"; + inFragmentShader << " vec4 shadowMask = vec4( 1.0, 1.0, 1.0, 1.0 );\n"; + if (pBakedShadowMap) { + SImageVariableNames names = + GetImageVariableNames((QT3DSU32)NVRenderTextureTypeValue::LightmapShadow); + // Add uniforms + inFragmentShader.AddUniform(names.m_ImageSampler, "sampler2D"); + inFragmentShader.AddUniform(m_ImageOffset, "vec3"); + inFragmentShader.AddUniform(m_ImageRotScale, "vec4"); + + inFragmentShader << "\n shadowMask = evalShadowLightmap( " << m_ImageSampler + << ", texCoord0, "; + inFragmentShader << m_ImageRotScale << ", "; + inFragmentShader << m_ImageOffset << " );\n\n"; + } + + inFragmentShader << " return shadowMask;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapIndirectSetupCode(IShaderStageGenerator &inFragmentShader, + SRenderableImage *pIndirectLightmap, + SRenderableImage *pRadiosityLightmap) + { + if (!pIndirectLightmap && !pRadiosityLightmap) + return; + + eastl::string finalValue; + + inFragmentShader << "\n"; + inFragmentShader << "void initializeLayerVariablesWithLightmap(void)\n{\n"; + if (pIndirectLightmap) { + inFragmentShader + << " vec3 lightmapIndirectValue = computeMaterialLightmapIndirect( );\n"; + finalValue.append("vec4(lightmapIndirectValue, 1.0)"); + } + if (pRadiosityLightmap) { + inFragmentShader + << " vec3 lightmapRadisoityValue = computeMaterialLightmapRadiosity( );\n"; + if (finalValue.empty()) + finalValue.append("vec4(lightmapRadisoityValue, 1.0)"); + else + finalValue.append(" + vec4(lightmapRadisoityValue, 1.0)"); + } + + finalValue.append(";\n"); + + char buf[16]; + for (QT3DSU32 idx = 0; idx < Material().m_LayerCount; idx++) { + _snprintf(buf, 16, "[%d]", idx); + inFragmentShader << " layers" << buf << ".base += " << finalValue.c_str(); + inFragmentShader << " layers" << buf << ".layer += " << finalValue.c_str(); + } + + inFragmentShader << "}\n\n"; + } + + void GenerateLightmapShadowCode(IShaderStageGenerator &inFragmentShader, + SRenderableImage *pBakedShadowMap) + { + if (pBakedShadowMap) { + inFragmentShader << " tmpShadowTerm *= computeMaterialLightmapShadow( );\n\n"; + } + } + + void ApplyEmissiveMask(IShaderStageGenerator &inFragmentShader, SImage *pEmissiveMaskMap) + { + inFragmentShader << "\n"; + inFragmentShader << "vec3 computeMaterialEmissiveMask()\n{\n"; + inFragmentShader << " vec3 emissiveMask = vec3( 1.0, 1.0, 1.0 );\n"; + if (pEmissiveMaskMap) { + inFragmentShader << " texture_coordinate_info tci;\n"; + inFragmentShader << " texture_coordinate_info transformed_tci;\n"; + inFragmentShader << " tci = textureCoordinateInfo( texCoord0, tangent, binormal );\n"; + inFragmentShader << " transformed_tci = transformCoordinate( " + "rotationTranslationScale( vec3( 0.000000, 0.000000, 0.000000 ), "; + inFragmentShader << "vec3( 0.000000, 0.000000, 0.000000 ), vec3( 1.000000, 1.000000, " + "1.000000 ) ), tci );\n"; + inFragmentShader << " emissiveMask = fileTexture( " + << pEmissiveMaskMap->m_ImageShaderName.c_str() + << ", vec3( 0, 0, 0 ), vec3( 1, 1, 1 ), mono_alpha, transformed_tci, "; + inFragmentShader << "vec2( 0.000000, 1.000000 ), vec2( 0.000000, 1.000000 ), " + "wrap_repeat, wrap_repeat, gamma_default ).tint;\n"; + } + + inFragmentShader << " return emissiveMask;\n"; + inFragmentShader << "}\n\n"; + } + + void GenerateFragmentShader(SShaderDefaultMaterialKey &, const char8_t *inShaderPathName, + bool hasCustomVertexShader) + { + qt3ds::render::IDynamicObjectSystem &theDynamicSystem( + m_RenderContext.GetDynamicObjectSystem()); + CRenderString theShaderBuffer; + const char8_t *fragSource = theDynamicSystem.GetShaderSource( + m_RenderContext.GetStringTable().RegisterStr(inShaderPathName), theShaderBuffer); + + QT3DS_ASSERT(fragSource); + + // light maps + bool hasLightmaps = false; + SRenderableImage *lightmapShadowImage = NULL; + SRenderableImage *lightmapIndirectImage = NULL; + SRenderableImage *lightmapRadisoityImage = NULL; + + for (SRenderableImage *img = m_FirstImage; img != NULL; img = img->m_NextImage) { + if (img->m_MapType == ImageMapTypes::LightmapIndirect) { + lightmapIndirectImage = img; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapRadiosity) { + lightmapRadisoityImage = img; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapShadow) { + lightmapShadowImage = img; + } + } + + if (!hasCustomVertexShader) { + VertexGenerator().GenerateUVCoords(0); + // for lightmaps we expect a second set of uv coordinates + if (hasLightmaps) + VertexGenerator().GenerateUVCoords(1); + } + + IDefaultMaterialVertexPipeline &vertexShader(VertexGenerator()); + IShaderStageGenerator &fragmentShader(FragmentGenerator()); + + eastl::string srcString(fragSource); + + if (m_RenderContext.GetRenderContext().GetRenderContextType() + == NVRenderContextValues::GLES2) { + eastl::string::size_type pos = 0; + while ((pos = srcString.find("out vec4 fragColor", pos)) != eastl::string::npos) { + srcString.insert(pos, "//"); + pos += int(strlen("//out vec4 fragColor")); + } + } + + fragmentShader << "#define FRAGMENT_SHADER\n\n"; + + // Check if the fragment shader portion already contains a main function + // The same string contains both the vertex and the fragment shader + // The last "#ifdef FRAGMENT_SHADER" should mark the start of the fragment shader + eastl_size_t fragmentDefStart = srcString.find("#ifdef FRAGMENT_SHADER"); + eastl_size_t nextIndex = fragmentDefStart; + while (nextIndex != eastl::string::npos) { + nextIndex = srcString.find("#ifdef FRAGMENT_SHADER", nextIndex + 1); + if (nextIndex != eastl::string::npos) + fragmentDefStart = nextIndex; + } + eastl_size_t mainStart = srcString.find("void main()"); + if (fragmentDefStart == eastl::string::npos) + return; + + if (mainStart != eastl::string::npos && mainStart < fragmentDefStart) + mainStart = srcString.find("void main()", mainStart + 1); + + bool hasCustomFragmentShader = mainStart != eastl::string::npos; + + if (!hasCustomFragmentShader) + fragmentShader.AddInclude("evalLightmaps.glsllib"); + + // check dielectric materials + if (!Material().IsDielectric()) + fragmentShader << "#define MATERIAL_IS_NON_DIELECTRIC 1\n\n"; + else + fragmentShader << "#define MATERIAL_IS_NON_DIELECTRIC 0\n\n"; + + fragmentShader << "#define QT3DS_ENABLE_RNM 0\n\n"; + + fragmentShader << srcString.data() << Endl; + + if (hasCustomFragmentShader) { + fragmentShader << "#define FRAGMENT_SHADER\n\n"; + if (!hasCustomVertexShader) { + vertexShader.GenerateWorldNormal(); + vertexShader.GenerateVarTangentAndBinormal(); + vertexShader.GenerateWorldPosition(); + + vertexShader.GenerateViewVector(); + } + return; + } + + if (Material().HasLighting() && lightmapIndirectImage) { + GenerateLightmapIndirectFunc(fragmentShader, &lightmapIndirectImage->m_Image); + } + if (Material().HasLighting() && lightmapRadisoityImage) { + GenerateLightmapRadiosityFunc(fragmentShader, &lightmapRadisoityImage->m_Image); + } + if (Material().HasLighting() && lightmapShadowImage) { + GenerateLightmapShadowFunc(fragmentShader, &lightmapShadowImage->m_Image); + } + + if (Material().HasLighting() && (lightmapIndirectImage || lightmapRadisoityImage)) + GenerateLightmapIndirectSetupCode(fragmentShader, lightmapIndirectImage, + lightmapRadisoityImage); + + if (Material().HasLighting()) { + ApplyEmissiveMask(fragmentShader, Material().m_EmissiveMap2); + } + + // setup main + VertexGenerator().BeginFragmentGeneration(); + + // since we do pixel lighting we always need this if lighting is enabled + // We write this here because the functions below may also write to + // the fragment shader + if (Material().HasLighting()) { + vertexShader.GenerateWorldNormal(); + vertexShader.GenerateVarTangentAndBinormal(); + vertexShader.GenerateWorldPosition(); + + if (Material().IsSpecularEnabled()) + vertexShader.GenerateViewVector(); + } + + fragmentShader << " initializeBaseFragmentVariables();" << Endl; + fragmentShader << " computeTemporaries();" << Endl; + fragmentShader << " normal = normalize( computeNormal() );" << Endl; + fragmentShader << " initializeLayerVariables();" << Endl; + fragmentShader << " float alpha = clamp( evalCutout(), 0.0, 1.0 );" << Endl; + + if (Material().IsCutOutEnabled()) { + fragmentShader << " if ( alpha <= 0.0f )" << Endl; + fragmentShader << " discard;" << Endl; + } + + // indirect / direct lightmap init + if (Material().HasLighting() && (lightmapIndirectImage || lightmapRadisoityImage)) + fragmentShader << " initializeLayerVariablesWithLightmap();" << Endl; + + // shadow map + GenerateLightmapShadowCode(fragmentShader, lightmapShadowImage); + + // main Body + fragmentShader << "#include \"customMaterialFragBodyAO.glsllib\"" << Endl; + + // for us right now transparency means we render a glass style material + if (m_HasTransparency && !Material().IsTransmissive()) + fragmentShader << " rgba = computeGlass( normal, materialIOR, alpha, rgba );" << Endl; + if (Material().IsTransmissive()) + fragmentShader << " rgba = computeOpacity( rgba );" << Endl; + + if (VertexGenerator().HasActiveWireframe()) { + fragmentShader.Append("vec3 edgeDistance = varEdgeDistance * gl_FragCoord.w;"); + fragmentShader.Append( + "\tfloat d = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z);"); + fragmentShader.Append("\tfloat mixVal = smoothstep(0.0, 1.0, d);"); // line width 1.0 + + fragmentShader.Append("\trgba = mix( vec4(0.0, 1.0, 0.0, 1.0), rgba, mixVal);"); + } + fragmentShader << " rgba.a *= object_opacity;" << Endl; + if (m_RenderContext.GetRenderContext().GetRenderContextType() + == NVRenderContextValues::GLES2) + fragmentShader << " gl_FragColor = rgba;" << Endl; + else + fragmentShader << " fragColor = rgba;" << Endl; + } + + NVRenderShaderProgram *GenerateCustomMaterialShader(const char8_t *inShaderPrefix, + const char8_t *inCustomMaterialName) + { + // build a string that allows us to print out the shader we are generating to the log. + // This is time consuming but I feel like it doesn't happen all that often and is very + // useful to users + // looking at the log file. + m_GeneratedShaderString.clear(); + m_GeneratedShaderString.assign(nonNull(inShaderPrefix)); + m_GeneratedShaderString.append(inCustomMaterialName); + SShaderDefaultMaterialKey theKey(Key()); + theKey.ToString(m_GeneratedShaderString, m_DefaultMaterialShaderKeyProperties); + + bool hasCustomVertexShader = GenerateVertexShader(theKey, inCustomMaterialName); + GenerateFragmentShader(theKey, inCustomMaterialName, hasCustomVertexShader); + + VertexGenerator().EndVertexGeneration(); + VertexGenerator().EndFragmentGeneration(); + + NVRenderShaderProgram *program = ProgramGenerator().CompileGeneratedShader( + m_GeneratedShaderString.c_str(), SShaderCacheProgramFlags(), FeatureSet()); + if (program && hasCustomVertexShader) { + // Change uniforms names to match runtime 2.x uniforms + SShaderGeneratorGeneratedShader &shader(GetShaderForProgram(*program)); + shader.m_ModelMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("modelMatrix", + *program); + shader.m_ViewProjMatrix = NVRenderCachedShaderProperty<QT3DSMat44>( + "modelViewProjection", *program); + shader.m_ViewMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("viewMatrix", *program); + shader.m_NormalMatrix = NVRenderCachedShaderProperty<QT3DSMat33>("modelNormalMatrix", + *program); + shader.m_ProjMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("viewProjectionMatrix", + *program); + shader.m_ViewportMatrix = NVRenderCachedShaderProperty<QT3DSMat44>("viewportMatrix", + *program); + shader.m_CameraPos = NVRenderCachedShaderProperty<QT3DSVec3>("eyePosition", *program); + } + return program; + } + + virtual NVRenderShaderProgram * + GenerateShader(const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, + bool inHasTransparency, const char8_t *inShaderPrefix, + const char8_t *inCustomMaterialName) override + { + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::CustomMaterial); + m_CurrentMaterial = reinterpret_cast<const SCustomMaterial *>(&inMaterial); + m_CurrentKey = &inShaderDescription; + m_CurrentPipeline = static_cast<IDefaultMaterialVertexPipeline *>(&inVertexPipeline); + m_CurrentFeatureSet = inFeatureSet; + m_Lights = inLights; + m_FirstImage = inFirstImage; + m_HasTransparency = inHasTransparency; + + return GenerateCustomMaterialShader(inShaderPrefix, inCustomMaterialName); + } +}; +} + +ICustomMaterialShaderGenerator & +ICustomMaterialShaderGenerator::CreateCustomMaterialShaderGenerator(IQt3DSRenderContext &inRc) +{ + return *QT3DS_NEW(inRc.GetAllocator(), SShaderGenerator)(inRc); +} |