From 69714a89ca0b748fc290f3e7d733805133bbb2c3 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 26 Nov 2019 14:48:43 -0800 Subject: [PATCH] material entities support cullFaceMode and support gltf doubleSided --- interface/src/graphics/WorldBox.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 6 +- .../src/RenderableGizmoEntityItem.cpp | 2 +- .../src/RenderableLineEntityItem.cpp | 2 +- .../src/RenderableShapeEntityItem.cpp | 3 +- libraries/fbx/src/GLTFSerializer.cpp | 4 + .../src/graphics-scripting/Forward.h | 2 + .../GraphicsScriptingInterface.cpp | 5 + .../graphics-scripting/ScriptableModel.cpp | 3 + libraries/graphics/src/graphics/Material.cpp | 21 ++- libraries/graphics/src/graphics/Material.h | 20 +++ .../procedural/ProceduralMaterialCache.cpp | 24 +++- libraries/render-utils/src/GeometryCache.cpp | 121 ++++++++++-------- libraries/render-utils/src/GeometryCache.h | 21 +-- .../render-utils/src/MeshPartPayload.cpp | 6 + libraries/render-utils/src/Model.cpp | 2 +- .../render-utils/src/RenderPipelines.cpp | 78 +++++------ libraries/render/src/render/ShapePipeline.h | 51 +++++++- 18 files changed, 256 insertions(+), 117 deletions(-) diff --git a/interface/src/graphics/WorldBox.cpp b/interface/src/graphics/WorldBox.cpp index 648d6d3177..0e15d9da86 100644 --- a/interface/src/graphics/WorldBox.cpp +++ b/interface/src/graphics/WorldBox.cpp @@ -22,7 +22,7 @@ namespace render { PerformanceTimer perfTimer("worldBox"); auto& batch = *args->_batch; - DependencyManager::get()->bindSimpleProgram(batch, false, false, true, false, false, true, args->_renderMethod == Args::RenderMethod::FORWARD); + DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true, args->_renderMethod == Args::RenderMethod::FORWARD); WorldBoxRenderData::renderWorldBox(args, batch); } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 5d5d809e15..0ba8705482 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -795,7 +795,7 @@ void Avatar::render(RenderArgs* renderArgs) { pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch.setModelTransform(pointerTransform); - geometryCache->bindSimpleProgram(batch, false, false, true, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD); + geometryCache->bindSimpleProgram(batch, false, false, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor, _leftPointerGeometryID); } } @@ -819,7 +819,7 @@ void Avatar::render(RenderArgs* renderArgs) { pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch.setModelTransform(pointerTransform); - geometryCache->bindSimpleProgram(batch, false, false, true, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD); + geometryCache->bindSimpleProgram(batch, false, false, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD); geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor, _rightPointerGeometryID); } } @@ -1107,7 +1107,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); - DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true, true, forward); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true, forward); DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, bevelDistance, backgroundColor, _nameRectGeometryID); } diff --git a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp index a066107a15..9081d0727f 100644 --- a/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp @@ -253,7 +253,7 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) { }); bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES; - geometryCache->bindSimpleProgram(batch, false, isTransparent(), false, wireframe, true, true, forward); + geometryCache->bindSimpleProgram(batch, false, isTransparent(), wireframe, true, true, forward, graphics::MaterialKey::CULL_NONE); batch.setModelTransform(transform); diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index aaef0b3f7d..6e2be1b41e 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -50,7 +50,7 @@ void LineEntityRenderer::doRender(RenderArgs* args) { transform.setRotation(modelTransform.getRotation()); batch.setModelTransform(transform); if (_linePoints.size() > 1) { - DependencyManager::get()->bindSimpleProgram(batch, false, false, true, false, false, true, + DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true, _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD); DependencyManager::get()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 88cc78b6b6..3cc18ee412 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -200,6 +200,7 @@ ShapeKey ShapeEntityRenderer::getShapeKey() { if (drawMaterialKey.isUnlit()) { builder.withUnlit(); } + builder.withCullFaceMode(mat->second.getCullFaceMode()); } else if (pipelineType == Pipeline::PROCEDURAL) { builder.withOwnPipeline(); } @@ -251,7 +252,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; render::ShapePipelinePointer pipeline = geometryCache->getShapePipelinePointer(outColor.a < 1.0f, false, - renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD); + renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD, materials.top().material->getCullFaceMode()); if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) { geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline); } else { diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index c75e717609..9d9d16d733 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1715,6 +1715,10 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& hfmMat, const GLTFMaterial& mat hfmMat._material->setOpacityCutoff(material.alphaCutoff); } + if (material.defined["doubleSided"] && material.doubleSided) { + hfmMat._material->setCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); + } + if (material.defined["emissiveFactor"] && material.emissiveFactor.size() == 3) { glm::vec3 emissive = glm::vec3(material.emissiveFactor[0], material.emissiveFactor[1], material.emissiveFactor[2]); hfmMat._material->setEmissive(emissive); diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 9efaa0a90d..acef5a5bd4 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -65,6 +65,7 @@ namespace scriptable { * @property {Mat4|string} texCoordTransform1 * @property {string} lightmapParams * @property {string} materialParams + * @property {string} cullFaceMode * @property {boolean} defaultFallthrough * @property {string} procedural */ @@ -99,6 +100,7 @@ namespace scriptable { QString lightMap; QString scatteringMap; std::array texCoordTransforms; + QString cullFaceMode; bool defaultFallthrough; std::unordered_map propertyFallthroughs; // not actually exposed to script diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 4d95709f15..62614ea6e8 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -495,6 +495,11 @@ namespace scriptable { obj.setProperty("materialParams", FALLTHROUGH); } + if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::CULL_FACE_MODE)) { + obj.setProperty("cullFaceMode", FALLTHROUGH); + } else if (!material.cullFaceMode.isEmpty()) { + obj.setProperty("cullFaceMode", material.cullFaceMode); + } } else if (material.model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { obj.setProperty("procedural", material.procedural); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 4a56db0d04..28cd49e7c4 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -45,6 +45,7 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const occlusionMap = material.occlusionMap; lightMap = material.lightMap; scatteringMap = material.scatteringMap; + cullFaceMode = material.cullFaceMode; } else if (model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { procedural = material.procedural; } @@ -131,6 +132,8 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { texCoordTransforms[i] = material->getTexCoordTransform(i); } + + cullFaceMode = QString(graphics::MaterialKey::getCullFaceModeName(material->getCullFaceMode()).c_str()); } else if (model.toStdString() == graphics::Material::HIFI_SHADER_SIMPLE) { procedural = material->getProceduralString(); } diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 1b96ed433b..41cd319595 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -27,7 +27,7 @@ const float Material::DEFAULT_ROUGHNESS { 1.0f }; const float Material::DEFAULT_SCATTERING{ 0.0f }; const MaterialKey::OpacityMapMode Material::DEFAULT_OPACITY_MAP_MODE{ MaterialKey::OPACITY_MAP_OPAQUE }; const float Material::DEFAULT_OPACITY_CUTOFF { 0.5f }; - +const MaterialKey::CullFaceMode Material::DEFAULT_CULL_FACE_MODE { MaterialKey::CULL_BACK }; std::string MaterialKey::getOpacityMapModeName(OpacityMapMode mode) { const std::string names[3] = { "OPACITY_MAP_OPAQUE", "OPACITY_MAP_MASK", "OPACITY_MAP_BLEND" }; @@ -44,6 +44,21 @@ bool MaterialKey::getOpacityMapModeFromName(const std::string& modeName, Materia return false; } +std::string MaterialKey::getCullFaceModeName(CullFaceMode mode) { + const std::string names[3] = { "CULL_NONE", "CULL_FRONT", "CULL_BACK" }; + return names[mode]; +} + +bool MaterialKey::getCullFaceModeFromName(const std::string& modeName, CullFaceMode& mode) { + for (int i = CULL_NONE; i < NUM_CULL_FACE_MODES; i++) { + mode = (CullFaceMode)i; + if (modeName == getCullFaceModeName(mode)) { + return true; + } + } + return false; +} + const std::string Material::HIFI_PBR { "hifi_pbr" }; const std::string Material::HIFI_SHADER_SIMPLE { "hifi_shader_simple" }; @@ -67,6 +82,7 @@ Material::Material(const Material& material) : _texcoordTransforms(material._texcoordTransforms), _lightmapParams(material._lightmapParams), _materialParams(material._materialParams), + _cullFaceMode(material._cullFaceMode), _textureMaps(material._textureMaps), _defaultFallthrough(material._defaultFallthrough), _propertyFallthroughs(material._propertyFallthroughs) @@ -89,6 +105,7 @@ Material& Material::operator=(const Material& material) { _texcoordTransforms = material._texcoordTransforms; _lightmapParams = material._lightmapParams; _materialParams = material._materialParams; + _cullFaceMode = material._cullFaceMode; _textureMaps = material._textureMaps; _defaultFallthrough = material._defaultFallthrough; @@ -144,7 +161,7 @@ void Material::setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode) { _key.setOpacityMapMode(opacityMapMode); } -MaterialKey::OpacityMapMode Material::getOpacityMapMode() const { +MaterialKey::OpacityMapMode Material::getOpacityMapMode() const { return _key.getOpacityMapMode(); } diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 48ab8151c5..7a411e5b2c 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -83,6 +83,16 @@ public: // find the enum value from a string, return true if match found static bool getOpacityMapModeFromName(const std::string& modeName, OpacityMapMode& mode); + enum CullFaceMode { + CULL_NONE = 0, + CULL_FRONT, + CULL_BACK, + + NUM_CULL_FACE_MODES + }; + static std::string getCullFaceModeName(CullFaceMode mode); + static bool getCullFaceModeFromName(const std::string& modeName, CullFaceMode& mode); + // The signature is the Flags Flags _flags; @@ -349,6 +359,10 @@ public: void setOpacityCutoff(float opacityCutoff); float getOpacityCutoff() const { return _opacityCutoff; } + static const MaterialKey::CullFaceMode DEFAULT_CULL_FACE_MODE; + void setCullFaceMode(MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; } + MaterialKey::CullFaceMode getCullFaceMode() const { return _cullFaceMode; } + void setUnlit(bool value); bool isUnlit() const { return _key.isUnlit(); } @@ -403,6 +417,7 @@ public: TEXCOORDTRANSFORM1, LIGHTMAP_PARAMS, MATERIAL_PARAMS, + CULL_FACE_MODE, NUM_TOTAL_FLAGS }; @@ -436,6 +451,7 @@ private: std::array _texcoordTransforms; glm::vec2 _lightmapParams { 0.0, 1.0 }; glm::vec2 _materialParams { 0.0, 1.0 }; + MaterialKey::CullFaceMode _cullFaceMode { DEFAULT_CULL_FACE_MODE }; TextureMaps _textureMaps; bool _defaultFallthrough { false }; @@ -524,6 +540,9 @@ public: graphics::MaterialKey getMaterialKey() const { return graphics::MaterialKey(_schemaBuffer.get()._key); } const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; } + void setCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { _cullFaceMode = cullFaceMode; } + graphics::MaterialKey::CullFaceMode getCullFaceMode() const { return _cullFaceMode; } + void setNeedsUpdate(bool needsUpdate) { _needsUpdate = needsUpdate; } void setTexturesLoading(bool value) { _texturesLoading = value; } void setInitialized() { _initialized = true; } @@ -536,6 +555,7 @@ public: private: gpu::BufferView _schemaBuffer; + graphics::MaterialKey::CullFaceMode _cullFaceMode { graphics::Material::DEFAULT_CULL_FACE_MODE }; gpu::TextureTablePointer _textureTable { std::make_shared() }; bool _needsUpdate { false }; bool _texturesLoading { false }; diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp index b9611358e7..a97cb294b4 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp @@ -143,7 +143,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {string} opacityMap - The URL of the opacity texture image. Set the value the same as the albedoMap * value for transparency. * "hifi_pbr" model only. - * @property {number|string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: + * @property {string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be: * "OPACITY_MAP_OPAQUE" for ignoring the opacity map information. * "OPACITY_MAP_MASK" for using the opacity map as a mask, where only the texel greater than opacityCutoff are visible and rendered opaque. * "OPACITY_MAP_BLEND" for using the opacity map for alpha blending the material surface with the background. @@ -151,6 +151,13 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the Opacity map * when opacityMapMode is "OPACITY_MAP_MASK", range 0.01.0. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. + * @property {string} cullFaceMode - The mode defining which side of the geometry should be rendered. Values can be: + *
    + *
  • "CULL_NONE" for rendering both sides of the geometry.
  • + *
  • "CULL_FRONT" for culling the front faces of the geometry.
  • + *
  • "CULL_BACK" (the default) for culling the back faces of the geometry.
  • + *
+ * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. * @property {string} roughnessMap - The URL of the roughness texture image. You can use this or glossMap, but not * both. * Set to "fallthrough" to fall through to the material below. "hifi_pbr" model only. @@ -285,7 +292,20 @@ std::pair> NetworkMaterialResource } else if (value.isDouble()) { material->setOpacityCutoff(value.toDouble()); } - } else if (key == "scattering") { + } else if (key == "cullFaceMode") { + auto value = materialJSON.value(key); + if (value.isString()) { + auto valueString = value.toString(); + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::CULL_FACE_MODE); + } else { + graphics::MaterialKey::CullFaceMode mode; + if (graphics::MaterialKey::getCullFaceModeFromName(valueString.toStdString(), mode)) { + material->setCullFaceMode(mode); + } + } + } + } else if (key == "scattering") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 621c20227c..e0d23535e4 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -723,7 +723,7 @@ gpu::ShaderPointer GeometryCache::_forwardUnlitShader; gpu::ShaderPointer GeometryCache::_forwardSimpleFadeShader; gpu::ShaderPointer GeometryCache::_forwardUnlitFadeShader; -std::map, render::ShapePipelinePointer> GeometryCache::_shapePipelines; +std::map, render::ShapePipelinePointer> GeometryCache::_shapePipelines; GeometryCache::GeometryCache() : _nextID(0) { @@ -776,15 +776,18 @@ void GeometryCache::initializeShapePipelines() { bool transparent = i & 1; bool unlit = i & 2; bool forward = i & 4; - _shapePipelines[std::make_tuple(transparent, unlit, forward)] = getShapePipeline(false, transparent, true, unlit, false, forward); + for (int cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_NONE; cullFaceMode < graphics::MaterialKey::CullFaceMode::NUM_CULL_FACE_MODES; cullFaceMode++) { + auto cullMode = (graphics::MaterialKey::CullFaceMode)cullFaceMode; + _shapePipelines[std::make_tuple(transparent, unlit, forward, cullMode)] = getShapePipeline(false, transparent, unlit, false, forward, cullMode); + } } } } -render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool transparent, bool culled, - bool unlit, bool depthBias, bool forward) { +render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool transparent, bool unlit, bool depthBias, bool forward, + graphics::MaterialKey::CullFaceMode cullFaceMode) { - return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, false, true, forward), nullptr, + return std::make_shared(getSimplePipeline(textured, transparent, unlit, depthBias, false, true, forward, cullFaceMode), nullptr, [](const render::ShapePipeline& pipeline, gpu::Batch& batch, render::Args* args) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); DependencyManager::get()->setupKeyLightBatch(args, batch); @@ -792,12 +795,12 @@ render::ShapePipelinePointer GeometryCache::getShapePipeline(bool textured, bool ); } -render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured, bool transparent, bool culled, - bool unlit, bool depthBias, bool forward) { +render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured, bool transparent, bool unlit, bool depthBias, bool forward, + graphics::MaterialKey::CullFaceMode cullFaceMode) { auto fadeEffect = DependencyManager::get(); auto fadeBatchSetter = fadeEffect->getBatchSetter(); auto fadeItemSetter = fadeEffect->getItemUniformSetter(); - return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, true, true, forward), nullptr, + return std::make_shared(getSimplePipeline(textured, transparent, unlit, depthBias, true, true, forward, cullFaceMode), nullptr, [fadeBatchSetter, fadeItemSetter](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args* args) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); fadeBatchSetter(shapePipeline, batch, args); @@ -2049,54 +2052,60 @@ void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bo class SimpleProgramKey { public: enum FlagBit { - IS_TEXTURED_FLAG = 0, - IS_TRANSPARENT_FLAG, - IS_CULLED_FLAG, - IS_UNLIT_FLAG, - HAS_DEPTH_BIAS_FLAG, - IS_FADING_FLAG, - IS_ANTIALIASED_FLAG, - IS_FORWARD_FLAG, + IS_TEXTURED_BIT = 0, + IS_TRANSPARENT_BIT, + IS_UNLIT_BIT, + IS_DEPTH_BIASED_BIT, + IS_FADING_BIT, + IS_ANTIALIASED_BIT, + IS_FORWARD_BIT, + IS_CULL_FACE_NONE_BIT, // if neither of these are set, we're CULL_FACE_BACK + IS_CULL_FACE_FRONT_BIT, NUM_FLAGS, }; + typedef std::bitset Flags; - enum Flag { - IS_TEXTURED = (1 << IS_TEXTURED_FLAG), - IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG), - IS_CULLED = (1 << IS_CULLED_FLAG), - IS_UNLIT = (1 << IS_UNLIT_FLAG), - HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), - IS_FADING = (1 << IS_FADING_FLAG), - IS_ANTIALIASED = (1 << IS_ANTIALIASED_FLAG), - IS_FORWARD = (1 << IS_FORWARD_FLAG), - }; - typedef unsigned short Flags; - - bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } - - bool isTextured() const { return isFlag(IS_TEXTURED); } - bool isTransparent() const { return isFlag(IS_TRANSPARENT); } - bool isCulled() const { return isFlag(IS_CULLED); } - bool isUnlit() const { return isFlag(IS_UNLIT); } - bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } - bool isFading() const { return isFlag(IS_FADING); } - bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); } - bool isForward() const { return isFlag(IS_FORWARD); } + bool isTextured() const { return _flags[IS_TEXTURED_BIT]; } + bool isTransparent() const { return _flags[IS_TRANSPARENT_BIT]; } + bool isUnlit() const { return _flags[IS_UNLIT_BIT]; } + bool hasDepthBias() const { return _flags[IS_DEPTH_BIASED_BIT]; } + bool isFading() const { return _flags[IS_FADING_BIT]; } + bool isAntiAliased() const { return _flags[IS_ANTIALIASED_BIT]; } + bool isForward() const { return _flags[IS_FORWARD_BIT]; } + bool isCullFaceNone() const { return _flags[IS_CULL_FACE_NONE_BIT]; } + bool isCullFaceFront() const { return _flags[IS_CULL_FACE_FRONT_BIT]; } Flags _flags = 0; -#if defined(__clang__) - __attribute__((unused)) -#endif - short _spare = 0; // Padding - int getRaw() const { return *reinterpret_cast(this); } + unsigned long getRaw() const { return _flags.to_ulong(); } + SimpleProgramKey(bool textured = false, bool transparent = false, bool unlit = false, bool depthBias = false, bool fading = false, + bool isAntiAliased = true, bool forward = false, graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CULL_BACK) { + _flags.set(IS_TEXTURED_BIT, textured); + _flags.set(IS_TRANSPARENT_BIT, transparent); + _flags.set(IS_UNLIT_BIT, unlit); + _flags.set(IS_DEPTH_BIASED_BIT, depthBias); + _flags.set(IS_FADING_BIT, fading); + _flags.set(IS_ANTIALIASED_BIT, isAntiAliased); + _flags.set(IS_FORWARD_BIT, forward); - SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true, bool forward = false) { - _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) | - (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0) | (fading ? IS_FADING : 0) | (isAntiAliased ? IS_ANTIALIASED : 0) | (forward ? IS_FORWARD : 0); + switch (cullFaceMode) { + case graphics::MaterialKey::CullFaceMode::CULL_NONE: + _flags.set(IS_CULL_FACE_NONE_BIT); + _flags.reset(IS_CULL_FACE_FRONT_BIT); + break; + case graphics::MaterialKey::CullFaceMode::CULL_FRONT: + _flags.reset(IS_CULL_FACE_NONE_BIT); + _flags.set(IS_CULL_FACE_FRONT_BIT); + break; + case graphics::MaterialKey::CullFaceMode::CULL_BACK: + _flags.reset(IS_CULL_FACE_NONE_BIT); + _flags.reset(IS_CULL_FACE_FRONT_BIT); + break; + default: + break; + } } SimpleProgramKey(int bitmask) : _flags(bitmask) {} @@ -2141,8 +2150,9 @@ gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent, bool return _webPipelines[{ transparent, forward }]; } -void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool isAntiAliased, bool forward) { - batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased, false, isAntiAliased, forward)); +void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool unlit, bool depthBiased, bool isAntiAliased, + bool forward, graphics::MaterialKey::CullFaceMode cullFaceMode) { + batch.setPipeline(getSimplePipeline(textured, transparent, unlit, depthBiased, false, isAntiAliased, forward, cullFaceMode)); // If not textured, set a default albedo map if (!textured) { @@ -2151,8 +2161,9 @@ void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool tra } } -gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool fading, bool isAntiAliased, bool forward) { - SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased, fading, isAntiAliased, forward }; +gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool unlit, bool depthBiased, bool fading, bool isAntiAliased, + bool forward, graphics::MaterialKey::CullFaceMode cullFaceMode) { + SimpleProgramKey config { textured, transparent, unlit, depthBiased, fading, isAntiAliased, forward, cullFaceMode }; // If the pipeline already exists, return it auto it = _simplePrograms.find(config); @@ -2189,10 +2200,12 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp // If the pipeline did not exist, make it auto state = std::make_shared(); - if (config.isCulled()) { - state->setCullMode(gpu::State::CULL_BACK); - } else { + if (config.isCullFaceNone()) { state->setCullMode(gpu::State::CULL_NONE); + } else if (config.isCullFaceFront()) { + state->setCullMode(gpu::State::CULL_FRONT); + } else { + state->setCullMode(gpu::State::CULL_BACK); } state->setDepthTest(true, true, gpu::LESS_EQUAL); if (config.hasDepthBias()) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index bfd133183d..03865c6cc7 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -162,18 +162,19 @@ public: static const int UNKNOWN_ID; // Bind the pipeline and get the state to render static geometry - void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool isAntiAliased = true, bool forward = false); + void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool unlit = false, bool depthBias = false, + bool isAntiAliased = true, bool forward = false, graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_BACK); // Get the pipeline to render static geometry - static gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true, bool forward = false); + static gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool unlit = false, bool depthBias = false, + bool fading = false, bool isAntiAliased = true, bool forward = false, graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_BACK); void bindWebBrowserProgram(gpu::Batch& batch, bool transparent, bool forward); gpu::PipelinePointer getWebBrowserProgram(bool transparent, bool forward); static std::map, gpu::PipelinePointer> _webPipelines; static void initializeShapePipelines(); - render::ShapePipelinePointer getShapePipelinePointer(bool transparent, bool unlit, bool forward) { return _shapePipelines[std::make_tuple(transparent, unlit, forward)]; } + render::ShapePipelinePointer getShapePipelinePointer(bool transparent, bool unlit, bool forward, + graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CULL_BACK) { return _shapePipelines[std::make_tuple(transparent, unlit, forward, cullFaceMode)]; } // Static (instanced) geometry void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); @@ -456,13 +457,13 @@ private: static gpu::ShaderPointer _forwardSimpleFadeShader; static gpu::ShaderPointer _forwardUnlitFadeShader; - static std::map, render::ShapePipelinePointer> _shapePipelines; + static std::map, render::ShapePipelinePointer> _shapePipelines; static QHash _simplePrograms; - static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool forward = false); - static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool forward = false); + static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool unlit = false, + bool depthBias = false, bool forward = false, graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_BACK); + static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool unlit = false, + bool depthBias = false, bool forward = false, graphics::MaterialKey::CullFaceMode cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_BACK); }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 64140bbb79..8bb406b5c8 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -140,6 +140,9 @@ ShapeKey MeshPartPayload::getShapeKey() const { if (drawMaterialKey.isUnlit()) { builder.withUnlit(); } + if (material) { + builder.withCullFaceMode(material->getCullFaceMode()); + } } return builder.build(); @@ -409,6 +412,9 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, PrimitiveMode pr if (isUnlit) { builder.withUnlit(); } + if (material) { + builder.withCullFaceMode(material->getCullFaceMode()); + } } _shapeKey = builder.build(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index da3ae24978..2c33955bd0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1052,7 +1052,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch, bool forward) { Transform meshToWorld(meshToWorldMatrix); batch.setModelTransform(meshToWorld); - DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true, true, forward); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, forward, graphics::MaterialKey::CULL_NONE); for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { for (auto &partTriangleSet : meshTriangleSets) { diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index ff2e3610c2..7f1ca4bd71 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -259,46 +259,44 @@ void addPlumberPipeline(ShapePlumber& plumber, gpu::ShaderPointer program = gpu::Shader::createProgram(programId); - for (int i = 0; i < 8; i++) { - bool isCulled = (i & 1); - bool isBiased = (i & 2); - bool isWireframed = (i & 4); + for (int i = 0; i < 4; i++) { + bool isBiased = (i & 1); + bool isWireframed = (i & 2); + for (int cullFaceMode = graphics::MaterialKey::CullFaceMode::CULL_NONE; cullFaceMode < graphics::MaterialKey::CullFaceMode::NUM_CULL_FACE_MODES; cullFaceMode++) { + auto state = std::make_shared(); + key.isTranslucent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); - auto state = std::make_shared(); - key.isTranslucent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); + // Depth test depends on transparency + state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); + state->setBlendFunction(key.isTranslucent(), + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - // Depth test depends on transparency - state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); - state->setBlendFunction(key.isTranslucent(), - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + ShapeKey::Builder builder(key); + builder.withCullFaceMode((graphics::MaterialKey::CullFaceMode)cullFaceMode); + state->setCullMode((gpu::State::CullMode)cullFaceMode); + if (isWireframed) { + builder.withWireframe(); + state->setFillMode(gpu::State::FILL_LINE); + } + if (isBiased) { + builder.withDepthBias(); + state->setDepthBias(1.0f); + state->setDepthBiasSlopeScale(1.0f); + } - ShapeKey::Builder builder(key); - if (!isCulled) { - builder.withoutCullFace(); + auto baseBatchSetter = (forceLightBatchSetter || key.isTranslucent()) ? &lightBatchSetter : &batchSetter; + render::ShapePipeline::BatchSetter finalBatchSetter; + if (extraBatchSetter) { + finalBatchSetter = [baseBatchSetter, extraBatchSetter](const ShapePipeline& pipeline, gpu::Batch& batch, render::Args* args) { + baseBatchSetter(pipeline, batch, args); + extraBatchSetter(pipeline, batch, args); + }; + } else { + finalBatchSetter = baseBatchSetter; + } + plumber.addPipeline(builder.build(), program, state, finalBatchSetter, itemSetter); } - state->setCullMode(isCulled ? gpu::State::CULL_BACK : gpu::State::CULL_NONE); - if (isWireframed) { - builder.withWireframe(); - state->setFillMode(gpu::State::FILL_LINE); - } - if (isBiased) { - builder.withDepthBias(); - state->setDepthBias(1.0f); - state->setDepthBiasSlopeScale(1.0f); - } - - auto baseBatchSetter = (forceLightBatchSetter || key.isTranslucent()) ? &lightBatchSetter : &batchSetter; - render::ShapePipeline::BatchSetter finalBatchSetter; - if (extraBatchSetter) { - finalBatchSetter = [baseBatchSetter, extraBatchSetter](const ShapePipeline& pipeline, gpu::Batch& batch, render::Args* args) { - baseBatchSetter(pipeline, batch, args); - extraBatchSetter(pipeline, batch, args); - }; - } else { - finalBatchSetter = baseBatchSetter; - } - plumber.addPipeline(builder.build(), program, state, finalBatchSetter, itemSetter); } } @@ -644,6 +642,12 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial wasSet = true; } break; + case graphics::Material::CULL_FACE_MODE: + if (!fallthrough) { + multiMaterial.setCullFaceMode(material->getCullFaceMode()); + wasSet = true; + } + break; default: break; } @@ -685,6 +689,8 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial case graphics::Material::MATERIAL_PARAMS: // these are initialized to the correct default values in Schema() break; + case graphics::Material::CULL_FACE_MODE: + multiMaterial.setCullFaceMode(graphics::Material::DEFAULT_CULL_FACE_MODE); case graphics::MaterialKey::ALBEDO_MAP_BIT: if (schemaKey.isAlbedoMap()) { drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index cf41c85dd9..04b9919140 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -15,6 +15,7 @@ #include #include +#include #include "Args.h" @@ -34,8 +35,9 @@ public: DUAL_QUAT_SKINNED, DEPTH_BIAS, WIREFRAME, - NO_CULL_FACE, FADE, + CULL_FACE_NONE, // if neither of these are set, we're CULL_FACE_BACK + CULL_FACE_FRONT, OWN_PIPELINE, INVALID, @@ -81,9 +83,29 @@ public: Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); return (*this); } Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } - Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); return (*this); } Builder& withFade() { _flags.set(FADE); return (*this); } + Builder& withoutCullFace() { return withCullFaceMode(graphics::MaterialKey::CullFaceMode::CULL_NONE); } + Builder& withCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { + switch (cullFaceMode) { + case graphics::MaterialKey::CullFaceMode::CULL_NONE: + _flags.set(CULL_FACE_NONE); + _flags.reset(CULL_FACE_FRONT); + break; + case graphics::MaterialKey::CullFaceMode::CULL_FRONT: + _flags.reset(CULL_FACE_NONE); + _flags.set(CULL_FACE_FRONT); + break; + case graphics::MaterialKey::CullFaceMode::CULL_BACK: + _flags.reset(CULL_FACE_NONE); + _flags.reset(CULL_FACE_FRONT); + break; + default: + break; + } + return (*this); + } + Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); } Builder& invalidate() { _flags.set(INVALID); return (*this); } @@ -137,8 +159,27 @@ public: Builder& withWireframe() { _flags.set(WIREFRAME); _mask.set(WIREFRAME); return (*this); } Builder& withoutWireframe() { _flags.reset(WIREFRAME); _mask.set(WIREFRAME); return (*this); } - Builder& withCullFace() { _flags.reset(NO_CULL_FACE); _mask.set(NO_CULL_FACE); return (*this); } - Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); _mask.set(NO_CULL_FACE); return (*this); } + Builder& withCullFaceMode(graphics::MaterialKey::CullFaceMode cullFaceMode) { + switch (cullFaceMode) { + case graphics::MaterialKey::CullFaceMode::CULL_NONE: + _flags.set(CULL_FACE_NONE); + _flags.reset(CULL_FACE_FRONT); + break; + case graphics::MaterialKey::CullFaceMode::CULL_FRONT: + _flags.reset(CULL_FACE_NONE); + _flags.set(CULL_FACE_FRONT); + break; + case graphics::MaterialKey::CullFaceMode::CULL_BACK: + _flags.reset(CULL_FACE_NONE); + _flags.reset(CULL_FACE_FRONT); + break; + default: + break; + } + _mask.set(CULL_FACE_NONE); + _mask.set(CULL_FACE_FRONT); + return (*this); + } Builder& withFade() { _flags.set(FADE); _mask.set(FADE); return (*this); } Builder& withoutFade() { _flags.reset(FADE); _mask.set(FADE); return (*this); } @@ -168,7 +209,7 @@ public: bool isDualQuatSkinned() const { return _flags[DUAL_QUAT_SKINNED]; } bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } bool isWireframe() const { return _flags[WIREFRAME]; } - bool isCullFace() const { return !_flags[NO_CULL_FACE]; } + bool isCullFace() const { return !_flags[CULL_FACE_NONE] && !_flags[CULL_FACE_FRONT]; } bool isFaded() const { return _flags[FADE]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; }