diff --git a/interface/resources/shaders/errorShader.frag b/interface/resources/shaders/errorShader.frag new file mode 100644 index 0000000000..acfd23505e --- /dev/null +++ b/interface/resources/shaders/errorShader.frag @@ -0,0 +1,30 @@ +vec3 getErrorColor() { + vec3 positionWS = iWorldOrientation * (_positionMS.xyz * iWorldScale) + iWorldPosition; + float checkSize = 0.1; + vec3 edges = round(mod(positionWS, vec3(checkSize)) / checkSize); + float checkerboard = mod(edges.x + edges.y + edges.z, 2.0); + return mix(vec3(1, 0, 1), vec3(0.0), checkerboard); +} + +// version 1 +vec3 getProceduralColor() { + return getErrorColor(); +} + +// version 2 +float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) { + diffuse = getErrorColor(); + return 1.0; +} + +// version 3 +float getProceduralFragment(inout ProceduralFragment data) { + data.emissive = getErrorColor(); + return 1.0; +} + +// version 4 +float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition data) { + data.emissive = getErrorColor(); + return 1.0; +} diff --git a/interface/resources/shaders/errorSkyboxShader.frag b/interface/resources/shaders/errorSkyboxShader.frag new file mode 100644 index 0000000000..1c9b333ae5 --- /dev/null +++ b/interface/resources/shaders/errorSkyboxShader.frag @@ -0,0 +1,7 @@ +vec3 getSkyboxColor() { + vec3 normal = normalize(_normal); + float checkSize = 0.1; + vec3 edges = round(mod(normal, vec3(checkSize)) / checkSize); + float checkerboard = mod(edges.x + edges.y + edges.z, 2.0); + return mix(vec3(1, 0, 1), vec3(0.0), checkerboard); +} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2f2c31e27d..6c03ddec63 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -64,7 +64,6 @@ #include "SpeechRecognizer.h" #endif -#include "MeshPartPayload.h" #include "scripting/RenderScriptingInterface.h" extern bool DEV_DECIMATE_TEXTURES; diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index e11bf32702..e126a5734e 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -16,7 +16,7 @@ #include #include "ScreenName.h" -#include +#include STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager){ auto scriptEngine = manager->engine().get(); @@ -252,7 +252,7 @@ void RenderScriptingInterface::forceProceduralMaterialsEnabled(bool enabled) { _proceduralMaterialsEnabledSetting.set(enabled); Menu::getInstance()->setIsOptionChecked(MenuOption::MaterialProceduralShaders, enabled); - ModelMeshPartPayload::enableMaterialProceduralShaders = enabled; + Procedural::enableProceduralShaders = enabled; }); } diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 139da1bf20..3162ab95e6 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -28,6 +28,8 @@ Q_LOGGING_CATEGORY(proceduralLog, "hifi.gpu.procedural") +bool Procedural::enableProceduralShaders = false; + // User-data parsing constants static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity"; static const QString VERTEX_URL_KEY = "vertexShaderURL"; @@ -377,6 +379,27 @@ void Procedural::prepare(gpu::Batch& batch, _proceduralPipelines[key] = gpu::Pipeline::create(program, key.isTransparent() ? _transparentState : _opaqueState); + // Error fallback: pink checkerboard + if (_errorFallbackFragmentSource.isEmpty()) { + QFile file(_errorFallbackFragmentPath); + file.open(QIODevice::ReadOnly); + _errorFallbackFragmentSource = QTextStream(&file).readAll(); + } + vertexSource.replacements.erase(PROCEDURAL_BLOCK); + fragmentSource.replacements[PROCEDURAL_BLOCK] = _errorFallbackFragmentSource.toStdString(); + gpu::ShaderPointer errorVertexShader = gpu::Shader::createVertex(vertexSource); + gpu::ShaderPointer errorFragmentShader = gpu::Shader::createPixel(fragmentSource); + gpu::ShaderPointer errorProgram = gpu::Shader::createProgram(errorVertexShader, errorFragmentShader); + _errorPipelines[key] = gpu::Pipeline::create(errorProgram, _opaqueState); + + // Disabled fallback: nothing + vertexSource.replacements.erase(PROCEDURAL_BLOCK); + fragmentSource.replacements.erase(PROCEDURAL_BLOCK); + gpu::ShaderPointer disabledVertexShader = gpu::Shader::createVertex(vertexSource); + gpu::ShaderPointer disabledFragmentShader = gpu::Shader::createPixel(fragmentSource); + gpu::ShaderPointer disabledProgram = gpu::Shader::createProgram(disabledVertexShader, disabledFragmentShader); + _disabledPipelines[key] = gpu::Pipeline::create(disabledProgram, _opaqueState); + _lastCompile = usecTimestampNow(); if (_firstCompile == 0) { _firstCompile = _lastCompile; @@ -385,8 +408,15 @@ void Procedural::prepare(gpu::Batch& batch, recompiledShader = true; } + gpu::PipelinePointer finalPipeline = recompiledShader ? _proceduralPipelines[key] : pipeline->second; + if (!enableProceduralShaders) { + finalPipeline = _disabledPipelines[key]; + } else if (!finalPipeline || finalPipeline->getProgram()->compilationHasFailed()) { + finalPipeline = _errorPipelines[key]; + } + // FIXME: need to handle forward rendering - batch.setPipeline(recompiledShader ? _proceduralPipelines[key] : pipeline->second); + batch.setPipeline(finalPipeline); bool recreateUniforms = _shaderDirty || _uniformsDirty || recompiledShader || _prevKey != key; if (recreateUniforms) { @@ -546,4 +576,6 @@ void graphics::ProceduralMaterial::initializeProcedural() { // FIXME: Setup proper uniform slots and use correct pipelines for forward rendering _procedural._opaqueFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural); _procedural._transparentFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural_translucent); + + _procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorShader.frag").path(); } \ No newline at end of file diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index d343956b17..3ca2f41f0b 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -127,12 +127,16 @@ public: gpu::Shader::Source _opaqueFragmentSource; gpu::Shader::Source _transparentFragmentSource; + QString _errorFallbackFragmentPath; + gpu::StatePointer _opaqueState { std::make_shared() }; gpu::StatePointer _transparentState { std::make_shared() }; static std::function opaqueStencil; static std::function transparentStencil; + static bool enableProceduralShaders; + protected: // DO NOT TOUCH // We have to pack these in a particular way to match the ProceduralCommon.slh @@ -179,6 +183,8 @@ protected: bool _shaderDirty { true }; bool _uniformsDirty { true }; + QString _errorFallbackFragmentSource; + // Rendering objects UniformLambdas _uniforms; NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; @@ -186,6 +192,8 @@ protected: std::unordered_map _fragmentReplacements; std::unordered_map _proceduralPipelines; + std::unordered_map _errorPipelines; + std::unordered_map _disabledPipelines; StandardInputs _standardInputs; gpu::BufferPointer _standardInputsBuffer; diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 432e02fe10..5cbf11f298 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -22,6 +22,8 @@ ProceduralSkybox::ProceduralSkybox(uint64_t created) : graphics::Skybox(), _crea _procedural._vertexSource = shader::Source::get(shader::graphics::vertex::skybox); _procedural._opaqueFragmentSource = shader::Source::get(shader::procedural::fragment::proceduralSkybox); + _procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorSkyboxShader.frag").path(); + _procedural.setDoesFade(false); // Adjust the pipeline state for background using the stencil test diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 73ee842cd1..0ea4f14767 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -26,8 +26,6 @@ using namespace render; -bool ModelMeshPartPayload::enableMaterialProceduralShaders = false; - ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const uint64_t& created) : _meshIndex(meshIndex), @@ -352,9 +350,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) { } if (_shapeKey.hasOwnPipeline()) { - if (!(enableMaterialProceduralShaders)) { - return; - } auto procedural = std::static_pointer_cast(_drawMaterials.top().material); auto& schema = _drawMaterials.getSchemaBuffer().get(); glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 0482212f84..ecc9e997f0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -71,8 +71,6 @@ public: void setBlendshapeBuffer(const std::unordered_map& blendshapeBuffers, const QVector& blendedMeshSizes); - static bool enableMaterialProceduralShaders; - private: void initCache(const ModelPointer& model, int shapeID);