diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 29f6da440..9b1e95b3c 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -207,8 +207,13 @@ public: mColorShift = color; mColorShiftEnd = color; } + virtual void setColorShiftEnd(unsigned int color) { mColorShiftEnd = color; } virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; } virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; } + virtual void setColorGradientHorizontal(bool horizontal) {} + virtual void setReflectionsFalloff(float falloff) {} + virtual void setFlipX(bool flip) {} + virtual void setFlipY(bool flip) {} virtual void setImage(const std::string& path, bool tile = false) {} diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index d36f0225a..7780c7011 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -262,6 +262,11 @@ std::map> {"itemRotationOrigin", NORMALIZED_PAIR}, {"itemHorizontalAlignment", STRING}, {"itemVerticalAlignment", STRING}, + {"horizontalOffset", FLOAT}, + {"verticalOffset", FLOAT}, + {"reflections", BOOLEAN}, + {"reflectionsOpacity", FLOAT}, + {"reflectionsFalloff", FLOAT}, {"unfocusedItemOpacity", FLOAT}, {"maxItemCount", FLOAT}, {"defaultLogo", PATH}, // For backward compatibility with legacy themes. diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 875ecedf7..b6890a079 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -40,6 +40,7 @@ ImageComponent::ImageComponent(bool forceLoad, bool dynamic) , mColorShiftEnd {0xFFFFFFFF} , mColorGradientHorizontal {true} , mFadeOpacity {0.0f} + , mReflectionsFalloff {0.0f} , mFading {false} , mForceLoad {forceLoad} , mDynamic {dynamic} @@ -248,7 +249,7 @@ void ImageComponent::cropTransparentPadding(const float maxSizeX, const float ma glm::ivec2 imageSize {mTexture.get()->getSize()}; cimg_library::CImg imageCImg(imageSize.x, imageSize.y, 1, 4, 0); - int paddingCoords[4] {}; + int paddingCoords[4] {0, 0, 0, 0}; // We need to convert our RGBA data to the CImg internal format as CImg does not interleave // the pixels (as in RGBARGBARGBA). @@ -377,7 +378,7 @@ void ImageComponent::updateVertices() void ImageComponent::updateColors() { - const float opacity = (mOpacity * (mFading ? mFadeOpacity : 1.0f)); + const float opacity {mOpacity * (mFading ? mFadeOpacity : 1.0f)}; const unsigned int color {(mColorShift & 0xFFFFFF00) | static_cast((mColorShift & 0xFF) * opacity)}; const unsigned int colorEnd {(mColorShiftEnd & 0xFFFFFF00) | @@ -421,6 +422,7 @@ void ImageComponent::render(const glm::mat4& parentTrans) mVertices->saturation = mSaturation * mThemeSaturation; mVertices->opacity = mThemeOpacity; mVertices->dimming = mDimming; + mVertices->reflectionsFalloff = mReflectionsFalloff; mRenderer->drawTriangleStrips(&mVertices[0], 4); } diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 8eda39e58..1849f149a 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -66,8 +66,8 @@ public: // Multiply all pixels in the image by this color when rendering. void setColorShift(unsigned int color) override; - void setColorShiftEnd(unsigned int color); - void setColorGradientHorizontal(bool horizontal); + void setColorShiftEnd(unsigned int color) override; + void setColorGradientHorizontal(bool horizontal) override; unsigned int getColorShift() const override { return mColorShift; } @@ -75,8 +75,9 @@ public: void setSaturation(float saturation) override; void setDimming(float dimming) override; - void setFlipX(bool flip); // Mirror on the X axis. - void setFlipY(bool flip); // Mirror on the Y axis. + void setReflectionsFalloff(float falloff) override { mReflectionsFalloff = falloff; } + void setFlipX(bool flip) override; // Mirror on the X axis. + void setFlipY(bool flip) override; // Mirror on the Y axis. // Flag indicating if rotation should be based on target size vs. actual size. void setRotateByTargetSize(bool rotate) { mRotateByTargetSize = rotate; } @@ -129,6 +130,7 @@ private: std::shared_ptr mTexture; float mFadeOpacity; + float mReflectionsFalloff; bool mFading; bool mForceLoad; bool mDynamic; diff --git a/es-core/src/components/primary/CarouselComponent.h b/es-core/src/components/primary/CarouselComponent.h index 2dad4b35c..20742fd7c 100644 --- a/es-core/src/components/primary/CarouselComponent.h +++ b/es-core/src/components/primary/CarouselComponent.h @@ -117,6 +117,7 @@ private: CarouselType mType; std::string mItemType; std::string mDefaultItem; + bool mLegacyMode; std::shared_ptr mFont; unsigned int mTextColor; unsigned int mTextBackgroundColor; @@ -133,6 +134,11 @@ private: unsigned int mCarouselColor; unsigned int mCarouselColorEnd; bool mColorGradientHorizontal; + bool mReflections; + float mReflectionsOpacity; + float mReflectionsFalloff; + float mHorizontalOffset; + float mVerticalOffset; }; template @@ -146,6 +152,7 @@ CarouselComponent::CarouselComponent() , mPreviousScrollVelocity {0} , mTriggerJump {false} , mType {CarouselType::HORIZONTAL} + , mLegacyMode {false} , mFont {Font::get(FONT_SIZE_LARGE)} , mTextColor {0x000000FF} , mTextBackgroundColor {0xFFFFFF00} @@ -161,6 +168,11 @@ CarouselComponent::CarouselComponent() , mCarouselColor {0} , mCarouselColorEnd {0} , mColorGradientHorizontal {true} + , mReflections {false} + , mReflectionsOpacity {0.5f} + , mReflectionsFalloff {1.0f} + , mHorizontalOffset {0.0f} + , mVerticalOffset {0.0f} { } @@ -183,7 +195,7 @@ void CarouselComponent::addEntry(Entry& entry, const std::shared_ptr(false, false); item->setLinearInterpolation(true); - item->setMaxSize(glm::round(mItemSize * mItemScale)); + item->setMaxSize(mItemSize * mItemScale); item->applyTheme(theme, "system", "image_logo", ThemeFlags::PATH | ThemeFlags::COLOR); item->setRotateByTargetSize(true); @@ -197,7 +209,7 @@ void CarouselComponent::addEntry(Entry& entry, const std::shared_ptr(false, false); item->setLinearInterpolation(true); item->setImage(entry.data.itemPath); - item->setMaxSize(glm::round(mItemSize * mItemScale)); + item->setMaxSize(mItemSize * mItemScale); item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->setRotateByTargetSize(true); entry.data.item = item; @@ -207,7 +219,7 @@ void CarouselComponent::addEntry(Entry& entry, const std::shared_ptr(false, false); defaultItem->setLinearInterpolation(true); defaultItem->setImage(entry.data.defaultItemPath); - defaultItem->setMaxSize(glm::round(mItemSize * mItemScale)); + defaultItem->setMaxSize(mItemSize * mItemScale); defaultItem->applyTheme(theme, "system", "", ThemeFlags::ALL); defaultItem->setRotateByTargetSize(true); entry.data.item = defaultItem; @@ -403,37 +415,60 @@ template void CarouselComponent::render(const glm::mat4& parentT float xOff {0.0f}; float yOff {0.0f}; - switch (mType) { - case CarouselType::HORIZONTAL_WHEEL: - case CarouselType::VERTICAL_WHEEL: - xOff = std::round((mSize.x - mItemSize.x) / 2.0f - (mEntryCamOffset * itemSpacing.y)); - yOff = (mSize.y - mItemSize.y) / 2.0f; - break; - case CarouselType::VERTICAL: - itemSpacing.y = - ((mSize.y - (mItemSize.y * mMaxItemCount)) / (mMaxItemCount)) + mItemSize.y; - yOff = (mSize.y - mItemSize.y) / 2.0f - (mEntryCamOffset * itemSpacing.y); - if (mItemHorizontalAlignment == ALIGN_LEFT) + if (mType == CarouselType::HORIZONTAL_WHEEL || mType == CarouselType::VERTICAL_WHEEL) { + xOff = (mSize.x - mItemSize.x) / 2.0f - (mEntryCamOffset * itemSpacing.y); + yOff = (mSize.y - mItemSize.y) / 2.0f; + } + else if (mType == CarouselType::VERTICAL) { + itemSpacing.y = ((mSize.y - (mItemSize.y * mMaxItemCount)) / (mMaxItemCount)) + mItemSize.y; + yOff = (mSize.y - mItemSize.y) / 2.0f - (mEntryCamOffset * itemSpacing.y); + if (mItemHorizontalAlignment == ALIGN_LEFT) { + if (mLegacyMode) xOff = mItemSize.x / 10.0f; - else if (mItemHorizontalAlignment == ALIGN_RIGHT) - xOff = mSize.x - (mItemSize.x * 1.1f); else - xOff = (mSize.x - mItemSize.x) / 2.0f; - break; - case CarouselType::HORIZONTAL: - default: - itemSpacing.x = - ((mSize.x - (mItemSize.x * mMaxItemCount)) / (mMaxItemCount)) + mItemSize.x; - xOff = std::round((mSize.x - mItemSize.x) / 2.0f - (mEntryCamOffset * itemSpacing.x)); - if (mItemVerticalAlignment == ALIGN_TOP) + xOff = 0.0f; + } + else if (mItemHorizontalAlignment == ALIGN_RIGHT) { + if (mLegacyMode) + xOff = mSize.x - mItemSize.x * 1.1f; + else + xOff = mSize.x - mItemSize.x; + } + else { + xOff = (mSize.x - mItemSize.x) / 2.0f; + } + } + else { // HORIZONTAL. + itemSpacing.x = ((mSize.x - (mItemSize.x * mMaxItemCount)) / (mMaxItemCount)) + mItemSize.x; + xOff = (mSize.x - mItemSize.x) / 2.0f - (mEntryCamOffset * itemSpacing.x); + if (mItemVerticalAlignment == ALIGN_TOP) { + if (mLegacyMode) yOff = mItemSize.y / 10.0f; - else if (mItemVerticalAlignment == ALIGN_BOTTOM) + else + yOff = 0.0f; + } + else if (mItemVerticalAlignment == ALIGN_BOTTOM) { + if (mLegacyMode) yOff = mSize.y - (mItemSize.y * 1.1f); else + yOff = mSize.y - mItemSize.y - (mReflections ? ((mItemSize.y * mItemScale)) : 0.0f); + } + else { + if (mLegacyMode) yOff = (mSize.y - mItemSize.y) / 2.0f; - break; + else + yOff = (mSize.y - (mItemSize.y * (mReflections ? 2.0f : 1.0f))) / 2.0f; + } } + if (!mLegacyMode) { + xOff += mSize.x * mHorizontalOffset; + yOff += mSize.y * mVerticalOffset; + } + + xOff = std::round(xOff); + yOff = std::round(yOff); + int center {static_cast(mEntryCamOffset)}; int itemInclusion {static_cast(std::ceil(mMaxItemCount / 2.0f))}; bool singleEntry {mEntries.size() == 1}; @@ -441,28 +476,27 @@ template void CarouselComponent::render(const glm::mat4& parentT for (int i = center - itemInclusion; i < center + itemInclusion + 2; ++i) { int index {i}; - // If there is only a single entry, then only render the item once (in the center). - if (singleEntry) { - mEntries.at(0).data.item->render( - glm::translate(carouselTrans, glm::vec3 {0 + xOff, 0 + yOff, 0.0f})); - break; - } - while (index < 0) index += static_cast(mEntries.size()); while (index >= static_cast(mEntries.size())) index -= static_cast(mEntries.size()); - glm::mat4 itemTrans {carouselTrans}; - itemTrans = glm::translate( - itemTrans, glm::vec3 {i * itemSpacing.x + xOff, i * itemSpacing.y + yOff, 0.0f}); - float distance {i - mEntryCamOffset}; + if (singleEntry) + distance = 0.0f; + float scale {1.0f + ((mItemScale - 1.0f) * (1.0f - fabsf(distance)))}; scale = std::min(mItemScale, std::max(1.0f, scale)); scale /= mItemScale; + glm::mat4 itemTrans {carouselTrans}; + if (singleEntry) + itemTrans = glm::translate(carouselTrans, glm::vec3 {xOff, yOff, 0.0f}); + else + itemTrans = glm::translate( + itemTrans, glm::vec3 {i * itemSpacing.x + xOff, i * itemSpacing.y + yOff, 0.0f}); + float opacity {0.0f}; if (distance == 0.0f || mUnfocusedItemOpacity == 1.0f) { @@ -486,17 +520,33 @@ template void CarouselComponent::render(const glm::mat4& parentT comp->setRotationOrigin(mItemRotationOrigin); } - // When running at lower resolutions, prevent the scale-down to go all the way to - // the minimum value. This avoids potential single-pixel alignment issues when the - // item can't be vertically placed exactly in the middle of the carousel. Although - // the problem theoretically exists at all resolutions, it's not visble at around - // 1080p and above. - if (std::min(Renderer::getScreenWidth(), Renderer::getScreenHeight()) < 1080.0f) - scale = glm::clamp(scale, 1.0f / mItemScale + 0.01f, 1.0f); - comp->setScale(scale); comp->setOpacity(opacity); comp->render(itemTrans); + + // Don't attempt to add reflections for text entries. + if (mReflections && (mEntries.at(index).data.itemPath != "" || + mEntries.at(index).data.defaultItemPath != "")) { + itemTrans = + glm::translate(itemTrans, glm::vec3 {0.0f, comp->getSize().y * scale, 0.0f}); + const unsigned int colorShift {comp->getColorShift()}; + comp->setColorGradientHorizontal(false); + comp->setColorShift(0xFFFFFF00 | static_cast(mReflectionsOpacity * 255.0f)); + float falloff {glm::clamp(mReflectionsFalloff, 0.0f, 1.0f)}; + falloff = mReflectionsOpacity * (1.0f - falloff); + comp->setColorShiftEnd(0xFFFFFF00 | static_cast(falloff * 255.0f)); + // "Extra" falloff as a value of 1.0 already fades the image completely. + if (mReflectionsFalloff > 1.0f) + comp->setReflectionsFalloff(mReflectionsFalloff - 1.0f); + comp->setFlipY(true); + comp->render(itemTrans); + comp->setFlipY(false); + comp->setColorShift(colorShift); + comp->setReflectionsFalloff(0.0f); + } + + if (singleEntry) + break; } mRenderer->popClipRect(); } @@ -522,6 +572,8 @@ void CarouselComponent::applyTheme(const std::shared_ptr& theme, if (!elem) return; + mLegacyMode = theme->isLegacyTheme(); + if (elem->has("type")) { const std::string type {elem->get("type")}; if (type == "horizontal") { @@ -567,7 +619,7 @@ void CarouselComponent::applyTheme(const std::shared_ptr& theme, } } - if (!theme->isLegacyTheme()) { + if (!mLegacyMode) { if (elem->has("itemScale")) mItemScale = glm::clamp(elem->get("itemScale"), 0.5f, 3.0f); if (elem->has("itemSize")) { @@ -633,76 +685,99 @@ void CarouselComponent::applyTheme(const std::shared_ptr& theme, } } + if (elem->has("horizontalOffset")) + mHorizontalOffset = glm::clamp(elem->get("horizontalOffset"), -1.0f, 1.0f); + + if (elem->has("verticalOffset")) + mVerticalOffset = glm::clamp(elem->get("verticalOffset"), -1.0f, 1.0f); + + if (elem->has("reflections") && elem->get("reflections")) { + if (mType == CarouselType::HORIZONTAL) { + mReflections = elem->get("reflections"); + } + else { + LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property " + " only supported for horizontal carousel type"; + } + } + + if (elem->has("reflectionsOpacity")) + mReflectionsOpacity = glm::clamp(elem->get("reflectionsOpacity"), 0.1f, 1.0f); + + if (elem->has("reflectionsFalloff")) + mReflectionsFalloff = glm::clamp(elem->get("reflectionsFalloff"), 0.0f, 5.0f); + if (elem->has("unfocusedItemOpacity")) mUnfocusedItemOpacity = glm::clamp(elem->get("unfocusedItemOpacity"), 0.1f, 1.0f); } - // Start of legacy themes only section. + // Legacy themes. + if (mLegacyMode) { + if (elem->has("logoScale")) + mItemScale = glm::clamp(elem->get("logoScale"), 0.5f, 3.0f); + if (elem->has("logoSize")) { + // Keep size within a 0.05 and 1.0 multiple of the screen size. + glm::vec2 itemSize {elem->get("logoSize")}; + if (std::max(itemSize.x, itemSize.y) > 1.0f) { + itemSize /= std::max(itemSize.x, itemSize.y); + } + else if (std::min(itemSize.x, itemSize.y) < 0.005f) { + float ratio {std::min(itemSize.x, itemSize.y) / 0.005f}; + itemSize /= ratio; + // Just an extra precaution if a crazy ratio was used. + itemSize.x = glm::clamp(itemSize.x, 0.005f, 1.0f); + itemSize.y = glm::clamp(itemSize.y, 0.005f, 1.0f); + } + mItemSize = + itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight()); + } - if (elem->has("logoScale")) - mItemScale = glm::clamp(elem->get("logoScale"), 0.5f, 3.0f); - if (elem->has("logoSize")) { - // Keep size within a 0.05 and 1.0 multiple of the screen size. - glm::vec2 itemSize {elem->get("logoSize")}; - if (std::max(itemSize.x, itemSize.y) > 1.0f) { - itemSize /= std::max(itemSize.x, itemSize.y); + if (elem->has("maxLogoCount")) { + if (theme->isLegacyTheme()) + mMaxItemCount = + std::ceil(glm::clamp(elem->get("maxLogoCount"), 0.5f, 30.0f)); + else + mMaxItemCount = glm::clamp(elem->get("maxLogoCount"), 0.5f, 30.0f); } - else if (std::min(itemSize.x, itemSize.y) < 0.005f) { - float ratio {std::min(itemSize.x, itemSize.y) / 0.005f}; - itemSize /= ratio; - // Just an extra precaution if a crazy ratio was used. - itemSize.x = glm::clamp(itemSize.x, 0.005f, 1.0f); - itemSize.y = glm::clamp(itemSize.y, 0.005f, 1.0f); - } - mItemSize = itemSize * glm::vec2(Renderer::getScreenWidth(), Renderer::getScreenHeight()); - } - if (elem->has("maxLogoCount")) { - if (theme->isLegacyTheme()) - mMaxItemCount = std::ceil(glm::clamp(elem->get("maxLogoCount"), 0.5f, 30.0f)); - else - mMaxItemCount = glm::clamp(elem->get("maxLogoCount"), 0.5f, 30.0f); - } + if (elem->has("logoRotation")) + mItemRotation = elem->get("logoRotation"); + if (elem->has("logoRotationOrigin")) + mItemRotationOrigin = elem->get("logoRotationOrigin"); - if (elem->has("logoRotation")) - mItemRotation = elem->get("logoRotation"); - if (elem->has("logoRotationOrigin")) - mItemRotationOrigin = elem->get("logoRotationOrigin"); - - if (elem->has("logoAlignment")) { - const std::string alignment {elem->get("logoAlignment")}; - if (alignment == "left" && mType != CarouselType::HORIZONTAL) { - mItemHorizontalAlignment = ALIGN_LEFT; - mItemVerticalAlignment = ALIGN_CENTER; - } - else if (alignment == "right" && mType != CarouselType::HORIZONTAL) { - mItemHorizontalAlignment = ALIGN_RIGHT; - mItemVerticalAlignment = ALIGN_CENTER; - } - else if (alignment == "top" && mType != CarouselType::VERTICAL) { - mItemVerticalAlignment = ALIGN_TOP; - mItemHorizontalAlignment = ALIGN_CENTER; - } - else if (alignment == "bottom" && mType != CarouselType::VERTICAL) { - mItemVerticalAlignment = ALIGN_BOTTOM; - mItemHorizontalAlignment = ALIGN_CENTER; - } - else if (alignment == "center") { - mItemHorizontalAlignment = ALIGN_CENTER; - mItemVerticalAlignment = ALIGN_CENTER; - } - else { - LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property " - " defined as \"" - << alignment << "\""; - mItemHorizontalAlignment = ALIGN_CENTER; - mItemVerticalAlignment = ALIGN_CENTER; + if (elem->has("logoAlignment")) { + const std::string alignment {elem->get("logoAlignment")}; + if (alignment == "left" && mType != CarouselType::HORIZONTAL) { + mItemHorizontalAlignment = ALIGN_LEFT; + mItemVerticalAlignment = ALIGN_CENTER; + } + else if (alignment == "right" && mType != CarouselType::HORIZONTAL) { + mItemHorizontalAlignment = ALIGN_RIGHT; + mItemVerticalAlignment = ALIGN_CENTER; + } + else if (alignment == "top" && mType != CarouselType::VERTICAL) { + mItemVerticalAlignment = ALIGN_TOP; + mItemHorizontalAlignment = ALIGN_CENTER; + } + else if (alignment == "bottom" && mType != CarouselType::VERTICAL) { + mItemVerticalAlignment = ALIGN_BOTTOM; + mItemHorizontalAlignment = ALIGN_CENTER; + } + else if (alignment == "center") { + mItemHorizontalAlignment = ALIGN_CENTER; + mItemVerticalAlignment = ALIGN_CENTER; + } + else { + LOG(LogWarning) << "CarouselComponent: Invalid theme configuration, property " + " defined as \"" + << alignment << "\""; + mItemHorizontalAlignment = ALIGN_CENTER; + mItemVerticalAlignment = ALIGN_CENTER; + } } } - // End of legacy theme section. - mFont = Font::getFromTheme(elem, properties, mFont); if (elem->has("textColor")) diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index 0c0153aa7..5631dd7be 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -62,6 +62,7 @@ public: float opacity; float saturation; float dimming; + float reflectionsFalloff; unsigned int shaders; unsigned int shaderFlags; @@ -69,6 +70,7 @@ public: : opacity {1.0f} , saturation {1.0f} , dimming {1.0f} + , reflectionsFalloff {0.0f} , shaders {0} , shaderFlags {0} { @@ -81,6 +83,7 @@ public: , opacity {1.0f} , saturation {1.0f} , dimming {1.0f} + , reflectionsFalloff {0.0f} , shaders {0} , shaderFlags {0} { diff --git a/es-core/src/renderers/RendererOpenGL.cpp b/es-core/src/renderers/RendererOpenGL.cpp index b41894479..4abf0ea26 100644 --- a/es-core/src/renderers/RendererOpenGL.cpp +++ b/es-core/src/renderers/RendererOpenGL.cpp @@ -277,8 +277,8 @@ void RendererOpenGL::destroyContext() void RendererOpenGL::setMatrix(const glm::mat4& matrix) { - mTrans = matrix; - mTrans = getProjectionMatrix() * mTrans; + // Set matrix for use with shader. + mTrans = getProjectionMatrix() * matrix; } void RendererOpenGL::setScissor(const Rect& scissor) @@ -421,6 +421,7 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices, mCoreShader->setOpacity(vertices->opacity); mCoreShader->setSaturation(vertices->saturation); mCoreShader->setDimming(vertices->dimming); + mCoreShader->setReflectionsFalloff(vertices->reflectionsFalloff); mCoreShader->setFlags(vertices->shaderFlags); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); mLastShader = mCoreShader; diff --git a/es-core/src/renderers/ShaderOpenGL.cpp b/es-core/src/renderers/ShaderOpenGL.cpp index d8744585b..d223b32a2 100644 --- a/es-core/src/renderers/ShaderOpenGL.cpp +++ b/es-core/src/renderers/ShaderOpenGL.cpp @@ -22,6 +22,7 @@ ShaderOpenGL::ShaderOpenGL() , mShaderOpacity {0} , mShaderSaturation {0} , mShaderDimming {0} + , mShaderReflectionsFalloff {0} , mShaderFlags {0} { } @@ -123,6 +124,7 @@ void ShaderOpenGL::getVariableLocations(GLuint programID) mShaderOpacity = glGetUniformLocation(mProgramID, "opacity"); mShaderSaturation = glGetUniformLocation(mProgramID, "saturation"); mShaderDimming = glGetUniformLocation(mProgramID, "dimming"); + mShaderReflectionsFalloff = glGetUniformLocation(mProgramID, "reflectionsFalloff"); mShaderFlags = glGetUniformLocation(mProgramID, "shaderFlags"); } @@ -174,6 +176,12 @@ void ShaderOpenGL::setDimming(GLfloat dimming) GL_CHECK_ERROR(glUniform1f(mShaderDimming, dimming)); } +void ShaderOpenGL::setReflectionsFalloff(GLfloat falloff) +{ + if (mShaderReflectionsFalloff != -1) + GL_CHECK_ERROR(glUniform1f(mShaderReflectionsFalloff, falloff)); +} + void ShaderOpenGL::setFlags(GLuint flags) { if (mShaderFlags != -1) diff --git a/es-core/src/renderers/ShaderOpenGL.h b/es-core/src/renderers/ShaderOpenGL.h index 2d92779f4..7eee3c8fa 100644 --- a/es-core/src/renderers/ShaderOpenGL.h +++ b/es-core/src/renderers/ShaderOpenGL.h @@ -71,6 +71,7 @@ public: void setOpacity(GLfloat opacity); void setSaturation(GLfloat saturation); void setDimming(GLfloat dimming); + void setReflectionsFalloff(GLfloat falloff); void setFlags(GLuint flags); // Sets the shader program to use the loaded shaders. void activateShaders(); @@ -95,6 +96,7 @@ private: GLint mShaderOpacity; GLint mShaderSaturation; GLint mShaderDimming; + GLint mShaderReflectionsFalloff; GLint mShaderFlags; }; diff --git a/resources/shaders/glsl/core.glsl b/resources/shaders/glsl/core.glsl index 36d19abac..4982d86ee 100644 --- a/resources/shaders/glsl/core.glsl +++ b/resources/shaders/glsl/core.glsl @@ -37,6 +37,7 @@ in vec2 texCoord; uniform float opacity; uniform float saturation; uniform float dimming; +uniform float reflectionsFalloff; uniform uint shaderFlags; uniform sampler2D textureSampler; @@ -83,6 +84,10 @@ void main() if (0u != (shaderFlags & 1u)) sampledColor = sampledColor.bgra; + // Reflections falloff. + if (reflectionsFalloff > 0.0) + sampledColor.a = mix(sampledColor.a, sampledColor.a - reflectionsFalloff, texCoord.y); + FragColor = sampledColor; } #endif