diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index c10f6ec934b..ced77124f74 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -56,7 +56,6 @@ set(cmake_SOURCE_FILES SSE2NEON.cmake TemplateCheck.cmake TinyEXR.cmake - TinyGLTF.cmake Tracy.cmake Tut.cmake UI.cmake diff --git a/indra/cmake/TinyGLTF.cmake b/indra/cmake/TinyGLTF.cmake deleted file mode 100644 index 92b2de309fe..00000000000 --- a/indra/cmake/TinyGLTF.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# -*- cmake -*- -include(Prebuilt) - -use_prebuilt_binary(tinygltf) - -set(TINYGLTF_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tinygltf) - diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 6f7510fef27..a7cf9d2e329 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -134,6 +134,16 @@ const std::string LLSettingsSky::SETTING_SKY_DROPLET_RADIUS("droplet_radius"); const std::string LLSettingsSky::SETTING_SKY_ICE_LEVEL("ice_level"); const std::string LLSettingsSky::SETTING_REFLECTION_PROBE_AMBIANCE("reflection_probe_ambiance"); +const std::string LLSettingsSky::SETTING_AMBIENT_SKY_SATURATION("ambient_sky_saturation"); + +const std::string LLSettingsSky::SETTING_SKY_VERSION("sky_version"); +const std::string LLSettingsSky::SETTING_SUN_BRIGHTNESS("sun_brightness"); + +const std::string LLSettingsSky::SETTING_HDR_OFFSET("hdr_offset"); +const std::string LLSettingsSky::SETTING_HDR_MAX("hdr_max"); +const std::string LLSettingsSky::SETTING_HDR_MIN("hdr_min"); +const std::string LLSettingsSky::SETTING_HDR_TONEMAPPER("hdr_tonemapper"); +const std::string LLSettingsSky::SETTING_HDR_TONEMAPPER_AMOUNT("hdr_tonemapper_amount"); const LLUUID LLSettingsSky::DEFAULT_ASSET_ID("651510b8-5f4d-8991-1592-e7eeab2a5a06"); @@ -442,12 +452,15 @@ void LLSettingsSky::replaceSettings(const LLSettingsBase::ptr_t& other_sky) LLSettingsSky::ptr_t other = PTR_NAMESPACE::dynamic_pointer_cast(other_sky); + mSkySettingVersion = other->mSkySettingVersion; + mCanAutoAdjust = other->mCanAutoAdjust; mReflectionProbeAmbiance = other->mReflectionProbeAmbiance; mSunScale = other->mSunScale; mSunRotation = other->mSunRotation; mSunlightColor = other->mSunlightColor; + mSunBrightness = other->mSunBrightness; mStarBrightness = other->mStarBrightness; mMoonBrightness = other->mMoonBrightness; mMoonScale = other->mMoonScale; @@ -499,6 +512,12 @@ void LLSettingsSky::replaceSettings(const LLSettingsBase::ptr_t& other_sky) mRainbowTextureId = other->mRainbowTextureId; mBloomTextureId = other->mBloomTextureId; + mHDRMin = other->mHDRMin; + mHDRMax = other->mHDRMax; + mHDROffset = other->mHDROffset; + mTonemapper = other->mTonemapper; + mTonemapMix = other->mTonemapMix; + mNextSunTextureId.setNull(); mNextMoonTextureId.setNull(); mNextCloudTextureId.setNull(); @@ -605,6 +624,7 @@ void LLSettingsSky::blend(LLSettingsBase::ptr_t &end, F64 blendf) mCanAutoAdjust = other->mCanAutoAdjust; mSunRotation = slerp((F32)blendf, mSunRotation, other->mSunRotation); + mSunBrightness = lerp(mSunBrightness, other->mSunBrightness, (F32)blendf); mMoonRotation = slerp((F32)blendf, mMoonRotation, other->mMoonRotation); lerpColor(mSunlightColor, other->mSunlightColor, (F32)blendf); lerpColor(mGlow, other->mGlow, (F32)blendf); @@ -631,6 +651,14 @@ void LLSettingsSky::blend(LLSettingsBase::ptr_t &end, F64 blendf) mSkyIceLevel = lerp(mSkyIceLevel, other->mSkyIceLevel, (F32)blendf); mPlanetRadius = lerp(mPlanetRadius, other->mPlanetRadius, (F32)blendf); + mTonemapMix = lerp(mTonemapMix, other->mTonemapMix, (F32)blendf); + mHDRMax = lerp(mHDRMax, other->mHDRMax, (F32)blendf); + mHDRMin = lerp(mHDRMin, other->mHDRMin, (F32)blendf); + mHDROffset = lerp(mHDROffset, other->mHDROffset, (F32)blendf); + mAmbientSkySaturation = lerp(mAmbientSkySaturation, other->mAmbientSkySaturation, (F32)blendf); + + mTonemapper = other->mTonemapper; + // Legacy settings if (other->mHasLegacyHaze) @@ -835,6 +863,9 @@ LLSettingsSky::validation_list_t LLSettingsSky::validationList() validation.push_back(Validator(SETTING_REFLECTION_PROBE_AMBIANCE, false, LLSD::TypeReal, boost::bind(&Validator::verifyFloatRange, _1, _2, LLSD(llsd::array(0.0f, 10.0f))))); + validation.push_back(Validator(SETTING_AMBIENT_SKY_SATURATION, false, LLSD::TypeReal, + boost::bind(&Validator::verifyFloatRange, _1, _2, llsd::array(0.0f, 1.0f)))); + validation.push_back(Validator(SETTING_RAYLEIGH_CONFIG, true, LLSD::TypeArray, &validateRayleighLayers)); validation.push_back(Validator(SETTING_ABSORPTION_CONFIG, true, LLSD::TypeArray, &validateAbsorptionLayers)); validation.push_back(Validator(SETTING_MIE_CONFIG, true, LLSD::TypeArray, &validateMieLayers)); @@ -1204,10 +1235,43 @@ void LLSettingsSky::loadValuesFromLLSD() mReflectionProbeAmbiance = (F32)settings[SETTING_REFLECTION_PROBE_AMBIANCE].asReal(); } - mHDRMax = 2.0f; - mHDRMin = 0.5f; - mHDROffset = 1.0f; - mTonemapMix = 1.0f; + + // Legacy (pre-Visual Polish) skies are version 1. + // Post-VP skies are are 2 and higher. + // Note: version increments should be saved for behavioral changes, not necessarily param additions. + // The version should be set server-side, not viewer-side. + // - Geenz, Feb 21, 2026 + if (settings.has(SETTING_SKY_VERSION)) + { + mSkySettingVersion = (U8)settings[SETTING_SKY_VERSION].asReal(); + } + else + { + mSkySettingVersion = 1; + } + + if (mSkySettingVersion > 1) + { + mHDRMax = (F32)settings[SETTING_HDR_MAX].asReal(); + mHDRMin = (F32)settings[SETTING_HDR_MIN].asReal(); + mHDROffset = (F32)settings[SETTING_HDR_OFFSET].asReal(); + mTonemapMix = (F32)settings[SETTING_HDR_TONEMAPPER_AMOUNT].asReal(); + mTonemapper = (U8)settings[SETTING_HDR_TONEMAPPER].asInteger(); + mSunBrightness = (F32)settings[SETTING_SUN_BRIGHTNESS].asReal(); + mAmbientSkySaturation = settings.has(SETTING_AMBIENT_SKY_SATURATION) + ? (F32)settings[SETTING_AMBIENT_SKY_SATURATION].asReal() + : 0.0f; + } + else + { + mHDRMax = 2.0f; + mHDRMin = 0.5f; + mHDROffset = 1.0f; + mTonemapMix = 1.0f; + mTonemapper = 0; + mSunBrightness = 130000.f; // Brightness roughly around high noon in lux. + mAmbientSkySaturation = 0.0f; + } mSunTextureId = settings[SETTING_SUN_TEXTUREID].asUUID(); mMoonTextureId = settings[SETTING_MOON_TEXTUREID].asUUID(); @@ -1326,6 +1390,17 @@ void LLSettingsSky::saveValuesToLLSD() settings[SETTING_SKY_DROPLET_RADIUS] = mSkyDropletRadius; settings[SETTING_SKY_ICE_LEVEL] = mSkyIceLevel; settings[SETTING_PLANET_RADIUS] = mPlanetRadius; + if (mSkySettingVersion > 1) + { + settings[SETTING_HDR_MAX] = mHDRMax; + settings[SETTING_HDR_MIN] = mHDRMin; + settings[SETTING_HDR_OFFSET] = mHDROffset; + settings[SETTING_HDR_TONEMAPPER] = mTonemapper; + settings[SETTING_HDR_TONEMAPPER_AMOUNT] = mTonemapMix; + settings[SETTING_SKY_VERSION] = mSkySettingVersion; + settings[SETTING_SUN_BRIGHTNESS] = mSunBrightness; + settings[SETTING_AMBIENT_SKY_SATURATION] = mAmbientSkySaturation; + } LLSD& legacy = settings[SETTING_LEGACY_HAZE]; set_legacy(settings, legacy, SETTING_DISTANCE_MULTIPLIER, mLegacyDistanceMultiplier, LLSD::Real(mDistanceMultiplier)); @@ -1540,6 +1615,18 @@ void LLSettingsSky::setReflectionProbeAmbiance(F32 ambiance) setLLSDDirty(); } +F32 LLSettingsSky::getAmbientSkySaturation() const +{ + return mAmbientSkySaturation; +} + +void LLSettingsSky::setAmbientSkySaturation(F32 val) +{ + mAmbientSkySaturation = val; + setDirtyFlag(true); + setLLSDDirty(); +} + void LLSettingsSky::setAmbientColor(const LLColor3 &val) { mAmbientColor = val; @@ -2063,7 +2150,7 @@ F32 LLSettingsSky::getGamma() const F32 LLSettingsSky::getHDRMin(bool auto_adjust) const { - if (mCanAutoAdjust && !auto_adjust) + if ((mCanAutoAdjust || mSkySettingVersion < 2) && !auto_adjust) return 0.f; return mHDRMin; @@ -2071,7 +2158,7 @@ F32 LLSettingsSky::getHDRMin(bool auto_adjust) const F32 LLSettingsSky::getHDRMax(bool auto_adjust) const { - if (mCanAutoAdjust && !auto_adjust) + if ((mCanAutoAdjust || mSkySettingVersion < 2) && !auto_adjust) return 0.f; return mHDRMax; @@ -2079,15 +2166,36 @@ F32 LLSettingsSky::getHDRMax(bool auto_adjust) const F32 LLSettingsSky::getHDROffset(bool auto_adjust) const { - if (mCanAutoAdjust && !auto_adjust) + if ((mCanAutoAdjust || mSkySettingVersion < 2) && !auto_adjust) return 1.0f; return mHDROffset; } +void LLSettingsSky::setHDRMin(F32 val) +{ + mHDRMin = val; + setDirtyFlag(true); + setLLSDDirty(); +} + +void LLSettingsSky::setHDRMax(F32 val) +{ + mHDRMax = val; + setDirtyFlag(true); + setLLSDDirty(); +} + +void LLSettingsSky::setHDROffset(F32 val) +{ + mHDROffset = val; + setDirtyFlag(true); + setLLSDDirty(); +} + F32 LLSettingsSky::getTonemapMix(bool auto_adjust) const { - if (mCanAutoAdjust && !auto_adjust) + if ((mCanAutoAdjust || mSkySettingVersion < 2) && !auto_adjust) { // legacy settings do not support tonemaping return 0.0f; @@ -2099,6 +2207,32 @@ F32 LLSettingsSky::getTonemapMix(bool auto_adjust) const void LLSettingsSky::setTonemapMix(F32 mix) { mTonemapMix = mix; + setDirtyFlag(true); + setLLSDDirty(); +} + +U8 LLSettingsSky::getTonemapper() const +{ + return mTonemapper; +} + +void LLSettingsSky::setTonemapper(U8 tonemapper) +{ + mTonemapper = tonemapper; + setDirtyFlag(true); + setLLSDDirty(); +} + +U8 LLSettingsSky::getSkySettingVersion() const +{ + return mSkySettingVersion; +} + +void LLSettingsSky::setSkySettingVersion(U8 version) +{ + mSkySettingVersion = version; + setDirtyFlag(true); + setLLSDDirty(); } void LLSettingsSky::setGamma(F32 val) @@ -2189,6 +2323,24 @@ void LLSettingsSky::setStarBrightness(F32 val) setLLSDDirty(); } +F32 LLSettingsSky::getSunBrightness() const +{ + if (mSkySettingVersion < 2) + { + // V1 skies used a ~1.0 light scale. + // Return the lux equivalent for a PBR pipeline (shader normalizes by dividing by 100000). + return 130000.f; + } + return mSunBrightness; +} + +void LLSettingsSky::setSunBrightness(F32 val) +{ + mSunBrightness = val; + setDirtyFlag(true); + setLLSDDirty(); +} + LLColor3 LLSettingsSky::getSunlightColor() const { return mSunlightColor; diff --git a/indra/llinventory/llsettingssky.h b/indra/llinventory/llsettingssky.h index ff75aea5490..e8ac1f011bd 100644 --- a/indra/llinventory/llsettingssky.h +++ b/indra/llinventory/llsettingssky.h @@ -40,6 +40,8 @@ const F32 MOON_DIST = 384.400e6f; class LLSettingsSky: public LLSettingsBase { public: + static const std::string SETTING_SKY_VERSION; + static const U8 MAX_SKY_SETTINGS_VERSION = 2; static const std::string SETTING_AMBIENT; static const std::string SETTING_BLOOM_TEXTUREID; static const std::string SETTING_RAINBOW_TEXTUREID; @@ -75,6 +77,7 @@ class LLSettingsSky: public LLSettingsBase static const std::string SETTING_SUN_ROTATION; static const std::string SETTING_SUN_SCALE; static const std::string SETTING_SUN_TEXTUREID; + static const std::string SETTING_SUN_BRIGHTNESS; static const std::string SETTING_PLANET_RADIUS; static const std::string SETTING_SKY_BOTTOM_RADIUS; @@ -98,6 +101,7 @@ class LLSettingsSky: public LLSettingsBase static const std::string SETTING_SKY_ICE_LEVEL; static const std::string SETTING_REFLECTION_PROBE_AMBIANCE; + static const std::string SETTING_AMBIENT_SKY_SATURATION; static const std::string SETTING_LEGACY_HAZE; @@ -106,6 +110,13 @@ class LLSettingsSky: public LLSettingsBase static const F32 DEFAULT_AUTO_ADJUST_PROBE_AMBIANCE; static F32 sAutoAdjustProbeAmbiance; + static const std::string SETTING_HDR_OFFSET; + static const std::string SETTING_HDR_MAX; + static const std::string SETTING_HDR_MIN; + + static const std::string SETTING_HDR_TONEMAPPER; + static const std::string SETTING_HDR_TONEMAPPER_AMOUNT; + typedef PTR_NAMESPACE::shared_ptr ptr_t; //--------------------------------------------------------------------- @@ -174,6 +185,9 @@ class LLSettingsSky: public LLSettingsBase void setReflectionProbeAmbiance(F32 ambiance); + F32 getAmbientSkySaturation() const; + void setAmbientSkySaturation(F32 val); + //--------------------------------------------------------------------- LLColor3 getAmbientColor() const; void setAmbientColor(const LLColor3 &val); @@ -213,8 +227,17 @@ class LLSettingsSky: public LLSettingsBase F32 getHDRMin(bool auto_adjust = false) const; F32 getHDRMax(bool auto_adjust = false) const; F32 getHDROffset(bool auto_adjust = false) const; + void setHDRMin(F32 val); + void setHDRMax(F32 val); + void setHDROffset(F32 val); + F32 getTonemapMix(bool auto_adjust = false) const; void setTonemapMix(F32 mix); + U8 getTonemapper() const; + void setTonemapper(U8 tonemapper); + + U8 getSkySettingVersion() const; + void setSkySettingVersion(U8 version); void setGamma(F32 val); @@ -240,6 +263,9 @@ class LLSettingsSky: public LLSettingsBase F32 getStarBrightness() const; void setStarBrightness(F32 val); + F32 getSunBrightness() const; + void setSunBrightness(F32 val); + LLColor3 getSunlightColor() const; void setSunlightColor(const LLColor3 &val); @@ -373,11 +399,14 @@ class LLSettingsSky: public LLSettingsBase LLUUID mNextHaloTextureId; bool mCanAutoAdjust; + U8 mSkySettingVersion; LLQuaternion mSunRotation; LLQuaternion mMoonRotation; LLColor3 mSunlightColor; + F32 mSunBrightness; LLColor3 mGlow; F32 mReflectionProbeAmbiance; + F32 mAmbientSkySaturation; F32 mSunScale; F32 mStarBrightness; F32 mMoonBrightness; @@ -387,6 +416,7 @@ class LLSettingsSky: public LLSettingsBase F32 mCloudVariance; F32 mCloudShadow; F32 mCloudScale; + U8 mTonemapper; F32 mTonemapMix; F32 mHDROffset; F32 mHDRMax; diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index ff0cad58d63..a68a032854c 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -8,10 +8,15 @@ include(LLCoreHttp) include(LLPhysicsExtensions) include(LLPrimitive) include(GLM) -include(TinyGLTF) +include(Mikktspace) set(llprimitive_SOURCE_FILES + gltf/accessor.cpp + gltf/animation.cpp + gltf/asset.cpp + gltf/primitive.cpp lldaeloader.cpp + llgltfloader.cpp llgltfmaterial.cpp llmaterialid.cpp llmaterial.cpp @@ -30,9 +35,15 @@ set(llprimitive_SOURCE_FILES set(llprimitive_HEADER_FILES CMakeLists.txt + gltf/accessor.h + gltf/animation.h + gltf/asset.h + gltf/buffer_util.h + gltf/common.h + gltf/primitive.h lldaeloader.h + llgltfloader.h llgltfmaterial.h - llgltfmaterial_templates.h legacy_object_types.h llmaterial.h llmaterialid.h @@ -65,6 +76,8 @@ target_link_libraries(llprimitive llxml llcharacter llrender + llappearance + llmeshoptimizer ll::colladadom ll::glm ) diff --git a/indra/newview/gltf/accessor.cpp b/indra/llprimitive/gltf/accessor.cpp similarity index 99% rename from indra/newview/gltf/accessor.cpp rename to indra/llprimitive/gltf/accessor.cpp index f0ad3fa5944..beee1a3b7cf 100644 --- a/indra/newview/gltf/accessor.cpp +++ b/indra/llprimitive/gltf/accessor.cpp @@ -24,10 +24,11 @@ * $/LicenseInfo$ */ -#include "../llviewerprecompiledheaders.h" +#include "linden_common.h" #include "asset.h" #include "buffer_util.h" +#include "lldir.h" #include "llfilesystem.h" using namespace LL::GLTF; diff --git a/indra/newview/gltf/accessor.h b/indra/llprimitive/gltf/accessor.h similarity index 100% rename from indra/newview/gltf/accessor.h rename to indra/llprimitive/gltf/accessor.h diff --git a/indra/newview/gltf/animation.cpp b/indra/llprimitive/gltf/animation.cpp similarity index 98% rename from indra/newview/gltf/animation.cpp rename to indra/llprimitive/gltf/animation.cpp index 31549986af4..5c4a3502b04 100644 --- a/indra/newview/gltf/animation.cpp +++ b/indra/llprimitive/gltf/animation.cpp @@ -24,11 +24,10 @@ * $/LicenseInfo$ */ -#include "../llviewerprecompiledheaders.h" +#include "linden_common.h" #include "asset.h" #include "buffer_util.h" -#include "../llskinningutil.h" using namespace LL::GLTF; using namespace boost::json; @@ -399,6 +398,7 @@ Skin::~Skin() } } +#if 0 // viewer-side rendering code — needs llskinningutil void Skin::uploadMatrixPalette(Asset& asset) { // prepare matrix palette @@ -456,6 +456,7 @@ void Skin::uploadMatrixPalette(Asset& asset) glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +#endif // viewer-side rendering code bool Skin::prep(Asset& asset) { diff --git a/indra/newview/gltf/animation.h b/indra/llprimitive/gltf/animation.h similarity index 100% rename from indra/newview/gltf/animation.h rename to indra/llprimitive/gltf/animation.h diff --git a/indra/newview/gltf/asset.cpp b/indra/llprimitive/gltf/asset.cpp similarity index 90% rename from indra/newview/gltf/asset.cpp rename to indra/llprimitive/gltf/asset.cpp index bb8adf236a1..ff790d9044d 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/llprimitive/gltf/asset.cpp @@ -24,19 +24,13 @@ * $/LicenseInfo$ */ -#include "../llviewerprecompiledheaders.h" +#include "linden_common.h" #include "asset.h" #include "llvolumeoctree.h" -#include "../llviewershadermgr.h" -#include "../llviewercontrol.h" -#include "../llviewertexturelist.h" -#include "../pipeline.h" #include "buffer_util.h" +#include "lldir.h" #include "llimagejpeg.h" -#include "../llskinningutil.h" - -#include using namespace LL::GLTF; using namespace boost::json; @@ -48,7 +42,13 @@ namespace LL { static std::unordered_set ExtensionsSupported = { "KHR_materials_unlit", - "KHR_texture_transform" + "KHR_texture_transform", + "KHR_materials_transmission", + "KHR_materials_ior", + "KHR_materials_volume", + "KHR_materials_dispersion", + "KHR_materials_emissive_strength", + "KHR_materials_specular" }; static std::unordered_set ExtensionsIgnored = { @@ -127,10 +127,9 @@ void Asset::updateTransforms() { scene.updateTransforms(*this); } - - uploadTransforms(); } +#if 0 // viewer-side rendering code — needs llskinningutil, llviewershadermgr, etc. void Asset::uploadTransforms() { LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; @@ -236,6 +235,7 @@ void Asset::uploadMaterials() glBufferData(GL_UNIFORM_BUFFER, md.size() * sizeof(vec4), md.data(), GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +#endif // viewer-side rendering code S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, LLVector4a* intersection, // return the intersection point @@ -448,6 +448,7 @@ const Image& Image::operator=(const Value& src) void Asset::update() { +#if 0 // viewer-side rendering code — needs gFrameTimeSeconds, gSavedSettings, LLViewerTexture, etc. LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; F32 dt = gFrameTimeSeconds - mLastUpdateTime; @@ -489,6 +490,7 @@ void Asset::update() } } } +#endif } bool Asset::prep() @@ -524,6 +526,7 @@ bool Asset::prep() } } +#if 0 // viewer-side texture code — needs LLViewerTextureManager for (auto& image : mImages) { if (!image.prep(*this, mLoadIntoVRAM)) @@ -531,6 +534,7 @@ bool Asset::prep() return false; } } +#endif for (auto& mesh : mMeshes) { @@ -555,6 +559,8 @@ bool Asset::prep() return false; } } + +#if 0 // viewer-side rendering code — needs gDebugProgram, LLVertexBuffer if (mLoadIntoVRAM) { // prepare vertex buffers @@ -590,9 +596,7 @@ bool Asset::prep() for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant) { -#ifdef SHOW_ASSERT U32 attribute_mask = 0; -#endif // for each mesh for (auto& mesh : mMeshes) { @@ -610,82 +614,16 @@ bool Asset::prep() // all primitives of a given variant and material should all have the same attribute mask llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask); -#ifdef SHOW_ASSERT attribute_mask |= primitive.mAttributeMask; -#endif - } - } - } - - // allocate vertex buffer and pack it - if (vertex_count[variant] > 0) - { - U32 mat_idx = mat_id + 1; - #if 0 - LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask); - - rd.mBatches[variant][mat_idx].mVertexBuffer = vb; - vb->allocateBuffer(vertex_count[variant], - index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used - vb->setBuffer(); - - for (auto& mesh : mMeshes) - { - for (auto& primitive : mesh.mPrimitives) - { - if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) - { - primitive.upload(vb); - } } } - - vb->unmapBuffer(); - - vb->unbind(); - #endif } } } } - - // sanity check that all primitives have a vertex buffer - for (auto& mesh : mMeshes) - { - for (auto& primitive : mesh.mPrimitives) - { - //llassert(primitive.mVertexBuffer.notNull()); - } - } } - #if 0 - // build render batches - for (S32 node_id = 0; node_id < mNodes.size(); ++node_id) - { - Node& node = mNodes[node_id]; - - if (node.mMesh != INVALID_INDEX) - { - auto& mesh = mMeshes[node.mMesh]; - - S32 mat_idx = mesh.mPrimitives[0].mMaterial + 1; - - S32 double_sided = mat_idx == 0 ? 0 : mMaterials[mat_idx - 1].mDoubleSided; - - for (S32 j = 0; j < mesh.mPrimitives.size(); ++j) - { - auto& primitive = mesh.mPrimitives[j]; - - S32 variant = primitive.mShaderVariant; - - RenderData& rd = mRenderData[double_sided]; - RenderBatch& rb = rd.mBatches[variant][mat_idx]; +#endif - rb.mPrimitives.push_back({ j, node_id }); - } - } - } - #endif return true; } @@ -957,10 +895,9 @@ void Asset::eraseBufferView(S32 bufferView) } -LLViewerFetchedTexture* fetch_texture(const LLUUID& id); - bool Image::prep(Asset& asset, bool loadIntoVRAM) { +#if 0 // viewer-side texture code — needs LLViewerTextureManager, LLLocalBitmapMgr, etc. mLoadIntoTexturePipe = loadIntoVRAM; LLUUID id; @@ -991,10 +928,13 @@ bool Image::prep(Asset& asset, bool loadIntoVRAM) // Block until prep is done on the main thread return prep_future.get(); +#endif + return false; } bool Image::prepImpl(Asset& asset, const LLUUID& id) { +#if 0 // viewer-side texture code if (id.notNull()) { // loaded from an asset, fetch the texture from the asset system mTexture = fetch_texture(id); @@ -1061,6 +1001,8 @@ bool Image::prepImpl(Asset& asset, const LLUUID& id) } return true; +#endif + return false; } @@ -1088,6 +1030,7 @@ void Image::clearData(Asset& asset) bool Image::save(Asset& asset, const std::string& folder) { +#if 0 // viewer-side texture code // NOTE: this *MUST* be a lossless save // Artists use this to save their work repeatedly, so // adding any compression artifacts here will degrade @@ -1190,6 +1133,8 @@ bool Image::save(Asset& asset, const std::string& folder) clearData(asset); return true; +#endif + return false; } void TextureInfo::serialize(object& dst) const @@ -1324,6 +1269,106 @@ void Material::Unlit::serialize(object& dst) const // no members and object has already been created, nothing to do } +const Material::Transmission& Material::Transmission::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "transmissionFactor", mTransmissionFactor); + } + return *this; +} + +void Material::Transmission::serialize(object& dst) const +{ + write(mTransmissionFactor, "transmissionFactor", dst, 0.f); +} + +const Material::IOR& Material::IOR::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "ior", mIor); + } + return *this; +} + +void Material::IOR::serialize(object& dst) const +{ + write(mIor, "ior", dst, 1.5f); +} + +const Material::Volume& Material::Volume::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "thicknessFactor", mThicknessFactor); + copy(src, "attenuationDistance", mAttenuationDistance); + copy(src, "attenuationColor", mAttenuationColor); + } + return *this; +} + +void Material::Volume::serialize(object& dst) const +{ + write(mThicknessFactor, "thicknessFactor", dst, 0.f); + write(mAttenuationDistance, "attenuationDistance", dst, std::numeric_limits::infinity()); + write(mAttenuationColor, "attenuationColor", dst, vec3(1.f, 1.f, 1.f)); +} + +const Material::Dispersion& Material::Dispersion::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "dispersion", mDispersion); + } + return *this; +} + +void Material::Dispersion::serialize(object& dst) const +{ + write(mDispersion, "dispersion", dst, 0.f); +} + +const Material::EmissiveStrength& Material::EmissiveStrength::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "emissiveStrength", mEmissiveStrength); + } + return *this; +} + +void Material::EmissiveStrength::serialize(object& dst) const +{ + write(mEmissiveStrength, "emissiveStrength", dst, 1.0f); +} + +const Material::Specular& Material::Specular::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "specularFactor", mSpecularFactor); + copy(src, "specularTexture", mSpecularTexture); + copy(src, "specularColorFactor", mSpecularColorFactor); + copy(src, "specularColorTexture", mSpecularColorTexture); + } + return *this; +} + +void Material::Specular::serialize(object& dst) const +{ + write(mSpecularFactor, "specularFactor", dst, 1.0f); + write(mSpecularTexture, "specularTexture", dst); + write(mSpecularColorFactor, "specularColorFactor", dst, vec3(1.f, 1.f, 1.f)); + write(mSpecularColorTexture, "specularColorTexture", dst); +} + void TextureTransform::getPacked(vec4* packed) const { packed[0] = vec4(mScale.x, mScale.y, mRotation, mOffset.x); @@ -1364,7 +1409,14 @@ void Material::serialize(object& dst) const write(mAlphaMode, "alphaMode", dst, Material::AlphaMode::OPAQUE); write(mAlphaCutoff, "alphaCutoff", dst, 0.5f); write(mDoubleSided, "doubleSided", dst, false); - write_extensions(dst, &mUnlit, "KHR_materials_unlit"); + write_extensions(dst, + &mUnlit, "KHR_materials_unlit", + &mTransmission, "KHR_materials_transmission", + &mIOR, "KHR_materials_ior", + &mVolume, "KHR_materials_volume", + &mDispersion, "KHR_materials_dispersion", + &mEmissiveStrength, "KHR_materials_emissive_strength", + &mSpecular, "KHR_materials_specular"); } const Material& Material::operator=(const Value& src) @@ -1381,7 +1433,13 @@ const Material& Material::operator=(const Value& src) copy(src, "alphaCutoff", mAlphaCutoff); copy(src, "doubleSided", mDoubleSided); copy_extensions(src, - "KHR_materials_unlit", &mUnlit ); + "KHR_materials_unlit", &mUnlit, + "KHR_materials_transmission", &mTransmission, + "KHR_materials_ior", &mIOR, + "KHR_materials_volume", &mVolume, + "KHR_materials_dispersion", &mDispersion, + "KHR_materials_emissive_strength", &mEmissiveStrength, + "KHR_materials_specular", &mSpecular); } return *this; } diff --git a/indra/newview/gltf/asset.h b/indra/llprimitive/gltf/asset.h similarity index 86% rename from indra/newview/gltf/asset.h rename to indra/llprimitive/gltf/asset.h index 2802664ed37..41b270d3fc2 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/llprimitive/gltf/asset.h @@ -33,10 +33,9 @@ #include "animation.h" #include "boost/json.hpp" #include "common.h" -#include "../llviewertexture.h" +#include "llgltexture.h" #include "llglslshader.h" - -extern F32SecondsImplicit gFrameTimeSeconds; +#include // wingdi defines OPAQUE, which conflicts with our enum #if defined(OPAQUE) @@ -122,6 +121,65 @@ namespace LL void serialize(boost::json::object& dst) const; }; + class Transmission : public Extension // KHR_materials_transmission + { + public: + F32 mTransmissionFactor = 0.f; + + const Transmission& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class IOR : public Extension // KHR_materials_ior + { + public: + F32 mIor = 1.5f; + + const IOR& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class Volume : public Extension // KHR_materials_volume + { + public: + F32 mThicknessFactor = 0.f; + F32 mAttenuationDistance = std::numeric_limits::infinity(); + vec3 mAttenuationColor = vec3(1.f, 1.f, 1.f); + + const Volume& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class Dispersion : public Extension // KHR_materials_dispersion + { + public: + F32 mDispersion = 0.f; + + const Dispersion& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class EmissiveStrength : public Extension // KHR_materials_emissive_strength + { + public: + F32 mEmissiveStrength = 1.0f; + + const EmissiveStrength& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class Specular : public Extension // KHR_materials_specular + { + public: + F32 mSpecularFactor = 1.0f; + TextureInfo mSpecularTexture; + vec3 mSpecularColorFactor = vec3(1.f, 1.f, 1.f); + TextureInfo mSpecularColorTexture; + + const Specular& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + enum class AlphaMode { OPAQUE, @@ -157,6 +215,12 @@ namespace LL F32 mAlphaCutoff = 0.5f; bool mDoubleSided = false; Unlit mUnlit; + Transmission mTransmission; + IOR mIOR; + Volume mVolume; + Dispersion mDispersion; + EmissiveStrength mEmissiveStrength; + Specular mSpecular; bool isMultiUV() const; @@ -304,7 +368,7 @@ namespace LL bool mLoadIntoTexturePipe = false; - LLPointer mTexture; + LLPointer mTexture; const Image& operator=(const Value& src); void serialize(boost::json::object& dst) const; @@ -384,7 +448,7 @@ namespace LL std::string mFilename; // the last time update() was called according to gFrameTimeSeconds - F32 mLastUpdateTime = gFrameTimeSeconds; + F32 mLastUpdateTime = 0.f; // data used for rendering // 0 - single sided diff --git a/indra/newview/gltf/buffer_util.h b/indra/llprimitive/gltf/buffer_util.h similarity index 95% rename from indra/newview/gltf/buffer_util.h rename to indra/llprimitive/gltf/buffer_util.h index 53dee98cd1f..21ce562a400 100644 --- a/indra/newview/gltf/buffer_util.h +++ b/indra/llprimitive/gltf/buffer_util.h @@ -599,14 +599,23 @@ namespace LL // for internal use only, use copy_extensions instead template - inline bool _copy_extension(const boost::json::object& extensions, std::string_view member, T* dst) + inline void _copy_extension(bool& success, const boost::json::object& extensions, std::string_view member, T* dst) { if (extensions.contains(member)) { - return copy(extensions.at(member), *dst); + if (copy(extensions.at(member), *dst)) + { + success = true; + } } + } - return false; + // for internal use only, use copy_extensions instead + template + inline void _copy_extension(bool& success, const boost::json::object& extensions, std::string_view member, T* dst, Types... args) + { + _copy_extension(success, extensions, member, dst); + _copy_extension(success, extensions, args...); } // Copy all extensions from src.extensions to provided destinations @@ -615,10 +624,12 @@ namespace LL // "KHR_materials_unlit", &mUnlit, // "KHR_materials_pbrSpecularGlossiness", &mPbrSpecularGlossiness); // returns true if any of the extensions are copied - template + template inline bool copy_extensions(const boost::json::value& src, Types... args) { // extract the extensions object (don't assume it exists and verify that it is an object) + bool success = false; + if (src.is_object()) { boost::json::object obj = src.get_object(); @@ -628,27 +639,17 @@ namespace LL if (extensions.is_object()) { const boost::json::object& ext_obj = extensions.as_object(); - bool success = false; - // copy each extension, return true if any of them succeed, do not short circuit on success - U32 count = sizeof...(args); - for (U32 i = 0; i < count; i += 2) - { - if (_copy_extension(ext_obj, args...)) - { - success = true; - } - } - return success; + _copy_extension(success, ext_obj, args...); } } } - return false; + return success; } - // internal use aonly, use write_extensions instead + // internal use only, use write_extensions instead template - inline bool _write_extension(boost::json::object& extensions, const T* src, string_view member) + inline void _write_extension(bool &success, boost::json::object& extensions, const T* src, string_view member) { if (src->mPresent) { @@ -656,17 +657,24 @@ namespace LL if (write(*src, v)) { extensions[member] = v; - return true; + success = true; } } - return false; + } + + // for internal use only, use write_extensions instead + template + inline void _write_extension(bool &success, boost::json::object& extensions, const T* src, string_view member, Types... args) + { + _write_extension(success, extensions, src, member); + _write_extension(success, extensions, args...); } // Write all extensions to dst.extensions // Usage: // write_extensions(dst, - // mUnlit, "KHR_materials_unlit", - // mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness"); + // &mUnlit, "KHR_materials_unlit", + // &mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness"); // returns true if any of the extensions are written template inline bool write_extensions(boost::json::object& dst, Types... args) @@ -674,15 +682,7 @@ namespace LL bool success = false; boost::json::object extensions; - U32 count = sizeof...(args) - 1; - - for (U32 i = 0; i < count; i += 2) - { - if (_write_extension(extensions, args...)) - { - success = true; - } - } + _write_extension(success, extensions, args...); if (success) { diff --git a/indra/newview/gltf/common.h b/indra/llprimitive/gltf/common.h similarity index 100% rename from indra/newview/gltf/common.h rename to indra/llprimitive/gltf/common.h diff --git a/indra/newview/gltf/primitive.cpp b/indra/llprimitive/gltf/primitive.cpp similarity index 99% rename from indra/newview/gltf/primitive.cpp rename to indra/llprimitive/gltf/primitive.cpp index 81caff8ab2e..bde3e719ad7 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/llprimitive/gltf/primitive.cpp @@ -24,11 +24,11 @@ * $/LicenseInfo$ */ -#include "../llviewerprecompiledheaders.h" +#include "linden_common.h" #include "asset.h" #include "buffer_util.h" -#include "../llviewershadermgr.h" +#include "llglslshader.h" #include "mikktspace/mikktspace.hh" @@ -307,7 +307,7 @@ struct MikktMesh } }; - +#if 0 static void vertical_flip(std::vector& texcoords) { for (auto& tc : texcoords) @@ -315,6 +315,7 @@ static void vertical_flip(std::vector& texcoords) tc[1] = 1.f - tc[1]; } } +#endif bool Primitive::prep(Asset& asset) { @@ -532,6 +533,7 @@ bool Primitive::prep(Asset& asset) void Primitive::upload(LLVertexBuffer* buffer) { +#if 0 // viewer-side rendering code — needs LLVertexBuffer mVertexBuffer = buffer; // we store these buffer sizes as S32 elsewhere llassert(mPositions.size() <= size_t(S32_MAX)); @@ -587,6 +589,7 @@ void Primitive::upload(LLVertexBuffer* buffer) } mVertexBuffer->setIndexData(index_array.data(), mIndexOffset, getIndexCount()); } +#endif } void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2) diff --git a/indra/newview/gltf/primitive.h b/indra/llprimitive/gltf/primitive.h similarity index 100% rename from indra/newview/gltf/primitive.h rename to indra/llprimitive/gltf/primitive.h diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp similarity index 89% rename from indra/newview/gltf/llgltfloader.cpp rename to indra/llprimitive/llgltfloader.cpp index 5a94a2c6c68..67d90c77c14 100644 --- a/indra/newview/gltf/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -24,28 +24,12 @@ * $/LicenseInfo$ */ +#include "linden_common.h" + #include "llgltfloader.h" #include "meshoptimizer.h" #include -// Import & define single-header gltf import/export lib -#define TINYGLTF_IMPLEMENTATION -#define TINYGLTF_USE_CPP14 // default is C++ 11 - -// tinygltf by default loads image files using STB -#define STB_IMAGE_IMPLEMENTATION -// to use our own image loading: -// 1. replace this definition with TINYGLTF_NO_STB_IMAGE -// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data) - -// tinygltf saves image files using STB -#define STB_IMAGE_WRITE_IMPLEMENTATION -// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data) - -// Additionally, disable inclusion of STB header files entirely with -// TINYGLTF_NO_INCLUDE_STB_IMAGE -// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE -#include "tinygltf/tiny_gltf.h" // TODO: includes inherited from dae loader. Validate / prune @@ -131,7 +115,6 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) // Clear the material cache for new file mMaterialCache.clear(); - tinygltf::TinyGLTF loader; std::string filename_lc(filename); LLStringUtil::toLower(filename_lc); @@ -186,6 +169,9 @@ bool LLGLTFLoader::OpenFile(const std::string &filename) bool meshesLoaded = parseMeshes(); + mMaterialsLoaded = parseMaterials(); + if (mMaterialsLoaded) uploadMaterials(); + setLoadState(DONE); return meshesLoaded; @@ -635,7 +621,7 @@ LLGLTFLoader::LLGLTFImportMaterial LLGLTFLoader::processMaterial(S32 material_in LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex]; if (image.mTexture.notNull()) { - mTexturesNeedScaling |= image.mHeight > LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT || image.mWidth > LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT; + mTexturesNeedScaling |= image.mHeight > LLGLTexture::MAX_IMAGE_SIZE_DEFAULT || image.mWidth > LLGLTexture::MAX_IMAGE_SIZE_DEFAULT; impMat.setDiffuseMap(image.mTexture->getID()); LL_INFOS("GLTF_IMPORT") << "Using existing texture ID: " << image.mTexture->getID().asString() << LL_ENDL; } @@ -646,6 +632,34 @@ LLGLTFLoader::LLGLTFImportMaterial LLGLTFLoader::processMaterial(S32 material_in } } } + + // Process metallic-roughness texture + if (material->mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex >= 0) + { + std::string full_path; + processTexture(full_path, material->mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex, "metallic_roughness", material->mName); + } + + // Process normal texture + if (material->mNormalTexture.mIndex >= 0) + { + std::string full_path; + processTexture(full_path, material->mNormalTexture.mIndex, "normal", material->mName); + } + + // Process occlusion texture + if (material->mOcclusionTexture.mIndex >= 0) + { + std::string full_path; + processTexture(full_path, material->mOcclusionTexture.mIndex, "occlusion", material->mName); + } + + // Process emissive texture + if (material->mEmissiveTexture.mIndex >= 0) + { + std::string full_path; + processTexture(full_path, material->mEmissiveTexture.mIndex, "emissive", material->mName); + } } // Create cached material with both material and name @@ -656,6 +670,210 @@ LLGLTFLoader::LLGLTFImportMaterial LLGLTFLoader::processMaterial(S32 material_in return cachedMat; } +bool LLGLTFLoader::parseMaterials() +{ + if (!mGltfLoaded) return false; + + // fill local texture data structures + mGltfSamplers.clear(); + for (const auto& in_sampler : mGLTFAsset.mSamplers) + { + gltf_sampler sampler; + sampler.magFilter = in_sampler.mMagFilter > 0 ? in_sampler.mMagFilter : GL_LINEAR; + sampler.minFilter = in_sampler.mMinFilter > 0 ? in_sampler.mMinFilter : GL_LINEAR; + sampler.wrapS = in_sampler.mWrapS; + sampler.wrapT = in_sampler.mWrapT; + sampler.name = in_sampler.mName; + mGltfSamplers.push_back(sampler); + } + + mGltfImages.clear(); + for (const auto& in_image : mGLTFAsset.mImages) + { + gltf_image image; + image.numChannels = in_image.mComponent > 0 ? in_image.mComponent : 0; + image.bytesPerChannel = in_image.mBits > 0 ? (in_image.mBits >> 3) : 0; + image.pixelType = in_image.mPixelType; + image.size = 0; + image.height = in_image.mHeight > 0 ? in_image.mHeight : 0; + image.width = in_image.mWidth > 0 ? in_image.mWidth : 0; + image.data = nullptr; + + mGltfImages.push_back(image); + } + + mGltfTextures.clear(); + for (const auto& in_tex : mGLTFAsset.mTextures) + { + gltf_texture tex; + tex.imageIdx = in_tex.mSource >= 0 ? in_tex.mSource : 0; + tex.samplerIdx = in_tex.mSampler >= 0 ? in_tex.mSampler : 0; + tex.imageUuid.setNull(); + + if (tex.imageIdx >= mGltfImages.size() || (!mGltfSamplers.empty() && tex.samplerIdx >= mGltfSamplers.size())) + { + LL_WARNS("GLTF_IMPORT") << "Texture sampler/image index error" << LL_ENDL; + return false; + } + + mGltfTextures.push_back(tex); + } + + // parse each material + for (const auto& gltf_material : mGLTFAsset.mMaterials) + { + gltf_render_material mat; + mat.name = gltf_material.mName; + + const auto& pbr = gltf_material.mPbrMetallicRoughness; + mat.hasPBR = true; + + mat.baseColor = LLColor4(pbr.mBaseColorFactor[0], pbr.mBaseColorFactor[1], pbr.mBaseColorFactor[2], pbr.mBaseColorFactor[3]); + mat.hasBaseTex = pbr.mBaseColorTexture.mIndex >= 0; + mat.baseColorTexIdx = pbr.mBaseColorTexture.mIndex >= 0 ? pbr.mBaseColorTexture.mIndex : 0; + mat.baseColorTexCoords = pbr.mBaseColorTexture.mTexCoord; + + mat.metalness = pbr.mMetallicFactor; + mat.roughness = pbr.mRoughnessFactor; + mat.hasMRTex = pbr.mMetallicRoughnessTexture.mIndex >= 0; + mat.metalRoughTexIdx = pbr.mMetallicRoughnessTexture.mIndex >= 0 ? pbr.mMetallicRoughnessTexture.mIndex : 0; + mat.metalRoughTexCoords = pbr.mMetallicRoughnessTexture.mTexCoord; + + mat.normalScale = gltf_material.mNormalTexture.mScale; + mat.hasNormalTex = gltf_material.mNormalTexture.mIndex >= 0; + mat.normalTexIdx = gltf_material.mNormalTexture.mIndex >= 0 ? gltf_material.mNormalTexture.mIndex : 0; + mat.normalTexCoords = gltf_material.mNormalTexture.mTexCoord; + + mat.occlusionScale = gltf_material.mOcclusionTexture.mStrength; + mat.hasOcclusionTex = gltf_material.mOcclusionTexture.mIndex >= 0; + mat.occlusionTexIdx = gltf_material.mOcclusionTexture.mIndex >= 0 ? gltf_material.mOcclusionTexture.mIndex : 0; + mat.occlusionTexCoords = gltf_material.mOcclusionTexture.mTexCoord; + + mat.emissiveColor = LLColor4(gltf_material.mEmissiveFactor[0], gltf_material.mEmissiveFactor[1], gltf_material.mEmissiveFactor[2], 1.0f); + mat.hasEmissiveTex = gltf_material.mEmissiveTexture.mIndex >= 0; + mat.emissiveTexIdx = gltf_material.mEmissiveTexture.mIndex >= 0 ? gltf_material.mEmissiveTexture.mIndex : 0; + mat.emissiveTexCoords = gltf_material.mEmissiveTexture.mTexCoord; + + switch (gltf_material.mAlphaMode) + { + case LL::GLTF::Material::AlphaMode::MASK: + mat.alphaMode = "MASK"; + break; + case LL::GLTF::Material::AlphaMode::BLEND: + mat.alphaMode = "BLEND"; + break; + default: + mat.alphaMode = "OPAQUE"; + break; + } + mat.alphaMask = gltf_material.mAlphaCutoff; + + if (gltf_material.mTransmission.mPresent) + { + mat.transmissionFactor = gltf_material.mTransmission.mTransmissionFactor; + } + + if (gltf_material.mIOR.mPresent) + { + mat.iorFactor = gltf_material.mIOR.mIor; + } + + if (gltf_material.mVolume.mPresent) + { + mat.attenuationColor = LLColor4(gltf_material.mVolume.mAttenuationColor[0], + gltf_material.mVolume.mAttenuationColor[1], + gltf_material.mVolume.mAttenuationColor[2], 1.0f); + mat.attenuationDistance = gltf_material.mVolume.mAttenuationDistance; + mat.thicknessFactor = gltf_material.mVolume.mThicknessFactor; + } + + if (gltf_material.mDispersion.mPresent) + { + mat.dispersionFactor = gltf_material.mDispersion.mDispersion; + } + + if ((mat.hasNormalTex && (mat.normalTexIdx >= mGltfTextures.size())) || + (mat.hasOcclusionTex && (mat.occlusionTexIdx >= mGltfTextures.size())) || + (mat.hasEmissiveTex && (mat.emissiveTexIdx >= mGltfTextures.size())) || + (mat.hasBaseTex && (mat.baseColorTexIdx >= mGltfTextures.size())) || + (mat.hasMRTex && (mat.metalRoughTexIdx >= mGltfTextures.size()))) + { + LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL; + return false; + } + + if ((mat.hasNormalTex && (mat.normalTexCoords > 2)) || + (mat.hasOcclusionTex && (mat.occlusionTexCoords > 2)) || + (mat.hasEmissiveTex && (mat.emissiveTexCoords > 2)) || + (mat.hasBaseTex && (mat.baseColorTexCoords > 2)) || + (mat.hasMRTex && (mat.metalRoughTexCoords > 2))) + { + LL_WARNS("GLTF_IMPORT") << "Image texcoord index error" << LL_ENDL; + return false; + } + + mGltfMaterials.push_back(mat); + } + + return true; +} + +void LLGLTFLoader::uploadMaterials() +{ + for (gltf_render_material& mat : mGltfMaterials) + { + if (mat.hasBaseTex) + { + gltf_texture& gtex = mGltfTextures[mat.baseColorTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasMRTex) + { + gltf_texture& gtex = mGltfTextures[mat.metalRoughTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasNormalTex) + { + gltf_texture& gtex = mGltfTextures[mat.normalTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasOcclusionTex) + { + gltf_texture& gtex = mGltfTextures[mat.occlusionTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + + if (mat.hasEmissiveTex) + { + gltf_texture& gtex = mGltfTextures[mat.emissiveTexIdx]; + if (gtex.imageUuid.isNull()) + { + gtex.imageUuid = imageBufferToTextureUUID(gtex); + } + } + } +} + +LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex) +{ + return LLUUID::null; +} + std::string LLGLTFLoader::processTexture(std::string& full_path_out, S32 texture_index, const std::string& texture_type, const std::string& material_name) { S32 sourceIndex; diff --git a/indra/newview/gltf/llgltfloader.h b/indra/llprimitive/llgltfloader.h similarity index 73% rename from indra/newview/gltf/llgltfloader.h rename to indra/llprimitive/llgltfloader.h index a847e567a62..f597b41d76a 100644 --- a/indra/newview/gltf/llgltfloader.h +++ b/indra/llprimitive/llgltfloader.h @@ -27,14 +27,95 @@ #ifndef LL_LLGLTFLoader_H #define LL_LLGLTFLoader_H -#include "tinygltf/tiny_gltf.h" - -#include "asset.h" +#include "gltf/asset.h" #include "llglheaders.h" #include "lljointdata.h" #include "llmodelloader.h" +// gltf_* structs are temporary, used to organize the subset of data that eventually goes into the material LLSD + +class gltf_sampler +{ +public: + // Uses GL enums + S32 minFilter; // GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR + S32 magFilter; // GL_NEAREST or GL_LINEAR + S32 wrapS; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT + S32 wrapT; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT + std::string name; // optional, currently unused +}; + +class gltf_image +{ +public:// Note that glTF images are defined with row 0 at the top (opposite of OpenGL) + U8* data; // ptr to decoded image data + U32 size; // in bytes, regardless of channel width + U32 width; + U32 height; + U32 numChannels; // range 1..4 + U32 bytesPerChannel; // converted from gltf "bits", expects only 8, 16 or 32 as input + U32 pixelType; // one of (TINYGLTF_COMPONENT_TYPE)_UNSIGNED_BYTE, _UNSIGNED_SHORT, _UNSIGNED_INT, or _FLOAT +}; + +class gltf_texture +{ +public: + U32 imageIdx; + U32 samplerIdx; + LLUUID imageUuid = LLUUID::null; +}; + +class gltf_render_material +{ +public: + std::string name; + + // scalar values + LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present. + double metalness; + double roughness; + double normalScale; // scale applies only to X,Y components of normal + double occlusionScale; // strength multiplier for occlusion + LLColor4 emissiveColor; // emissive mulitiplier, assumed linear encoding (spec 2.0 is silent) + std::string alphaMode; // "OPAQUE", "MASK" or "BLEND" + double alphaMask; // alpha cut-off + + // KHR extension fields + F32 transmissionFactor = 0.f; + F32 iorFactor = 1.5f; + LLColor4 attenuationColor = LLColor4::white; + F32 attenuationDistance = std::numeric_limits::infinity(); + F32 thicknessFactor = 0.f; + F32 dispersionFactor = 0.f; + + // textures + U32 baseColorTexIdx; // always sRGB encoded + U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel + U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0) + U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded + U32 emissiveTexIdx; // always stored as sRGB, in nits (candela / meter^2) + + // texture coordinates + U32 baseColorTexCoords; + U32 metalRoughTexCoords; + U32 normalTexCoords; + U32 occlusionTexCoords; + U32 emissiveTexCoords; + + bool hasPBR; + bool hasBaseTex, hasMRTex, hasNormalTex, hasOcclusionTex, hasEmissiveTex; + + // This field is populated after upload + LLUUID material_uuid = LLUUID::null; +}; + +class gltf_mesh +{ +public: + std::string name; +}; + class LLGLTFLoader : public LLModelLoader { public: @@ -109,7 +190,6 @@ class LLGLTFLoader : public LLModelLoader protected: LL::GLTF::Asset mGLTFAsset; - tinygltf::Model mGltfModel; bool mGltfLoaded = false; bool mApplyXYRotation = false; @@ -143,8 +223,18 @@ class LLGLTFLoader : public LLModelLoader typedef std::map MaterialCache; MaterialCache mMaterialCache; + std::vector mGltfMeshes; + std::vector mGltfMaterials; + std::vector mGltfTextures; + std::vector mGltfImages; + std::vector mGltfSamplers; + bool mMaterialsLoaded = false; + private: bool parseMeshes(); + bool parseMaterials(); + void uploadMaterials(); + LLUUID imageBufferToTextureUUID(const gltf_texture& tex); void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const; void processNodeHierarchy(S32 node_idx, std::map& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params); bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx); diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp index 1c470012720..dd0d841ff3c 100644 --- a/indra/llprimitive/llgltfmaterial.cpp +++ b/indra/llprimitive/llgltfmaterial.cpp @@ -31,9 +31,7 @@ #include "llsdserialize.h" -// NOTE -- this should be the one and only place tiny_gltf.h is included -#include "tinygltf/tiny_gltf.h" -#include "llgltfmaterial_templates.h" +#include const char* const LLGLTFMaterial::ASSET_VERSION = "1.1"; const char* const LLGLTFMaterial::ASSET_TYPE = "GLTF 2.0"; @@ -66,6 +64,10 @@ LLGLTFMaterial::LLGLTFMaterial() mBaseColor.set(1.f, 1.f, 1.f, 1.f); mMetallicFactor = mRoughnessFactor = 1.f; mAlphaCutoff = 0.5f; + mEmissiveStrength = 1.f; + mSpecularFactor = 1.f; + mSpecularColorFactor.set(1.f, 1.f, 1.f); + mIOR = 1.5f; for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) { mTextureTransform[i].mScale.set(1.f, 1.f); @@ -124,6 +126,10 @@ LLGLTFMaterial& LLGLTFMaterial::operator=(const LLGLTFMaterial& rhs) mMetallicFactor = rhs.mMetallicFactor; mRoughnessFactor = rhs.mRoughnessFactor; mAlphaCutoff = rhs.mAlphaCutoff; + mEmissiveStrength = rhs.mEmissiveStrength; + mSpecularFactor = rhs.mSpecularFactor; + mSpecularColorFactor = rhs.mSpecularColorFactor; + mIOR = rhs.mIOR; mDoubleSided = rhs.mDoubleSided; mAlphaMode = rhs.mAlphaMode; @@ -174,6 +180,10 @@ bool LLGLTFMaterial::operator==(const LLGLTFMaterial& rhs) const mMetallicFactor == rhs.mMetallicFactor && mRoughnessFactor == rhs.mRoughnessFactor && mAlphaCutoff == rhs.mAlphaCutoff && + mEmissiveStrength == rhs.mEmissiveStrength && + mSpecularFactor == rhs.mSpecularFactor && + mSpecularColorFactor == rhs.mSpecularColorFactor && + mIOR == rhs.mIOR && mDoubleSided == rhs.mDoubleSided && mAlphaMode == rhs.mAlphaMode && @@ -185,188 +195,571 @@ bool LLGLTFMaterial::operator==(const LLGLTFMaterial& rhs) const bool LLGLTFMaterial::fromJSON(const std::string& json, std::string& warn_msg, std::string& error_msg) { LL_PROFILE_ZONE_SCOPED; - tinygltf::TinyGLTF gltf; - - tinygltf::Model model_in; - - if (gltf.LoadASCIIFromString(&model_in, &error_msg, &warn_msg, json.c_str(), static_cast(json.length()), "")) + try { - setFromModel(model_in, 0); - - return true; + boost::json::value doc = boost::json::parse(json); + return setFromDocument(doc, 0); + } + catch (const boost::system::system_error& e) + { + error_msg = e.what(); + return false; } - - return false; } std::string LLGLTFMaterial::asJSON(bool prettyprint) const { LL_PROFILE_ZONE_SCOPED; - tinygltf::TinyGLTF gltf; + boost::json::value doc = writeDocument(); + return boost::json::serialize(doc); +} - tinygltf::Model model_out; +// static +std::string LLGLTFMaterial::getTextureURI(const boost::json::value& doc, S32 texture_index) +{ + if (texture_index < 0) return ""; - std::ostringstream str; + const auto* textures = doc.if_object() ? doc.as_object().if_contains("textures") : nullptr; + if (!textures || !textures->is_array()) return ""; - writeToModel(model_out, 0); + const auto& tex_arr = textures->as_array(); + if ((size_t)texture_index >= tex_arr.size()) return ""; - // To ensure consistency in asset upload, this should be the only reference - // to WriteGltfSceneToStream in the viewer. - gltf.WriteGltfSceneToStream(&model_out, str, prettyprint, false); + const auto& tex = tex_arr[(size_t)texture_index]; + if (!tex.is_object()) return ""; - return std::move(str).str(); + const auto* source_val = tex.as_object().if_contains("source"); + if (!source_val || !source_val->is_int64()) return ""; + + S32 source_idx = (S32)source_val->as_int64(); + if (source_idx < 0) return ""; + + const auto* images = doc.as_object().if_contains("images"); + if (!images || !images->is_array()) return ""; + + const auto& img_arr = images->as_array(); + if ((size_t)source_idx >= img_arr.size()) return ""; + + const auto& img = img_arr[(size_t)source_idx]; + if (!img.is_object()) return ""; + + const auto* uri_val = img.as_object().if_contains("uri"); + if (!uri_val || !uri_val->is_string()) return ""; + + return std::string(uri_val->as_string()); } -void LLGLTFMaterial::setFromModel(const tinygltf::Model& model, S32 mat_index) +// static +void LLGLTFMaterial::readTextureInfo(const boost::json::value& doc, const boost::json::object& tex_info, LLUUID& texture_id, TextureTransform& transform) { LL_PROFILE_ZONE_SCOPED; - if (model.materials.size() <= mat_index) + const auto* index_val = tex_info.if_contains("index"); + if (!index_val || !index_val->is_int64()) return; + + S32 tex_idx = (S32)index_val->as_int64(); + std::string uri = getTextureURI(doc, tex_idx); + texture_id.set(uri); + + const auto* extensions_val = tex_info.if_contains("extensions"); + if (extensions_val && extensions_val->is_object()) { - return; + const auto& extensions = extensions_val->as_object(); + const auto* transform_val = extensions.if_contains(GLTF_FILE_EXTENSION_TRANSFORM); + if (transform_val && transform_val->is_object()) + { + const auto& transform_obj = transform_val->as_object(); + transform.mOffset = vec2FromJson(transform_obj, GLTF_FILE_EXTENSION_TRANSFORM_OFFSET, getDefaultTextureOffset()); + transform.mScale = vec2FromJson(transform_obj, GLTF_FILE_EXTENSION_TRANSFORM_SCALE, getDefaultTextureScale()); + transform.mRotation = floatFromJson(transform_obj, GLTF_FILE_EXTENSION_TRANSFORM_ROTATION, getDefaultTextureRotation()); + } } +} - const tinygltf::Material& material_in = model.materials[mat_index]; +void LLGLTFMaterial::readTextureInfo(const boost::json::value& doc, const boost::json::object& tex_info, TextureInfo id) +{ + readTextureInfo(doc, tex_info, mTextureId[id], mTextureTransform[id]); +} - // Apply base color texture - setFromTexture(model, material_in.pbrMetallicRoughness.baseColorTexture, GLTF_TEXTURE_INFO_BASE_COLOR); - // Apply normal map - setFromTexture(model, material_in.normalTexture, GLTF_TEXTURE_INFO_NORMAL); - // Apply metallic-roughness texture - setFromTexture(model, material_in.pbrMetallicRoughness.metallicRoughnessTexture, GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS); - // Apply emissive texture - setFromTexture(model, material_in.emissiveTexture, GLTF_TEXTURE_INFO_EMISSIVE); +bool LLGLTFMaterial::setFromDocument(const boost::json::value& doc, S32 mat_index) +{ + LL_PROFILE_ZONE_SCOPED; + if (!doc.is_object()) return false; - setAlphaMode(material_in.alphaMode); - mAlphaCutoff = llclamp((F32)material_in.alphaCutoff, 0.f, 1.f); + const auto& root = doc.as_object(); + const auto* materials_val = root.if_contains("materials"); + if (!materials_val || !materials_val->is_array()) return false; - mBaseColor.set(material_in.pbrMetallicRoughness.baseColorFactor); - mEmissiveColor.set(material_in.emissiveFactor); + const auto& materials = materials_val->as_array(); + if ((size_t)mat_index >= materials.size()) return false; - mMetallicFactor = llclamp((F32)material_in.pbrMetallicRoughness.metallicFactor, 0.f, 1.f); - mRoughnessFactor = llclamp((F32)material_in.pbrMetallicRoughness.roughnessFactor, 0.f, 1.f); + const auto& mat = materials[(size_t)mat_index]; + if (!mat.is_object()) return false; - mDoubleSided = material_in.doubleSided; + const auto& mat_obj = mat.as_object(); - if (material_in.extras.IsObject()) + // PBR metallic roughness + const auto* pbr_val = mat_obj.if_contains("pbrMetallicRoughness"); + if (pbr_val && pbr_val->is_object()) { - tinygltf::Value::Object extras = material_in.extras.Get(); - const auto& alpha_mode = extras.find("override_alpha_mode"); - if (alpha_mode != extras.end()) + const auto& pbr = pbr_val->as_object(); + + const auto* base_color_tex = pbr.if_contains("baseColorTexture"); + if (base_color_tex && base_color_tex->is_object()) + { + readTextureInfo(doc, base_color_tex->as_object(), GLTF_TEXTURE_INFO_BASE_COLOR); + } + + const auto* mr_tex = pbr.if_contains("metallicRoughnessTexture"); + if (mr_tex && mr_tex->is_object()) { - mOverrideAlphaMode = alpha_mode->second.Get(); + readTextureInfo(doc, mr_tex->as_object(), GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS); } - const auto& double_sided = extras.find("override_double_sided"); - if (double_sided != extras.end()) + const auto* bcf = pbr.if_contains("baseColorFactor"); + if (bcf && bcf->is_array()) { - mOverrideDoubleSided = double_sided->second.Get(); + const auto& arr = bcf->as_array(); + if (arr.size() >= 4) + { + mBaseColor.set( + (F32)arr[0].to_number(), + (F32)arr[1].to_number(), + (F32)arr[2].to_number(), + (F32)arr[3].to_number()); + } + } + + const auto* mf = pbr.if_contains("metallicFactor"); + if (mf && mf->is_number()) + { + mMetallicFactor = llclamp((F32)mf->to_number(), 0.f, 1.f); + } + + const auto* rf = pbr.if_contains("roughnessFactor"); + if (rf && rf->is_number()) + { + mRoughnessFactor = llclamp((F32)rf->to_number(), 0.f, 1.f); } } -} -// static -LLVector2 LLGLTFMaterial::vec2FromJson(const tinygltf::Value::Object& object, const char* key, const LLVector2& default_value) -{ - const auto it = object.find(key); - if (it == object.end()) + // Normal texture + const auto* normal_tex = mat_obj.if_contains("normalTexture"); + if (normal_tex && normal_tex->is_object()) { - return default_value; + readTextureInfo(doc, normal_tex->as_object(), GLTF_TEXTURE_INFO_NORMAL); } - const tinygltf::Value& vec2_json = std::get<1>(*it); - if (!vec2_json.IsArray() || vec2_json.ArrayLen() < LENGTHOFVECTOR2) + + // Emissive texture + const auto* emissive_tex = mat_obj.if_contains("emissiveTexture"); + if (emissive_tex && emissive_tex->is_object()) { - return default_value; + readTextureInfo(doc, emissive_tex->as_object(), GLTF_TEXTURE_INFO_EMISSIVE); } - LLVector2 value; - for (U32 i = 0; i < LENGTHOFVECTOR2; ++i) + + // Alpha mode + const auto* am = mat_obj.if_contains("alphaMode"); + if (am && am->is_string()) + { + setAlphaMode(std::string(am->as_string())); + } + + // Alpha cutoff + const auto* ac = mat_obj.if_contains("alphaCutoff"); + if (ac && ac->is_number()) + { + mAlphaCutoff = llclamp((F32)ac->to_number(), 0.f, 1.f); + } + + // Emissive factor + const auto* ef = mat_obj.if_contains("emissiveFactor"); + if (ef && ef->is_array()) + { + const auto& arr = ef->as_array(); + if (arr.size() >= 3) + { + mEmissiveColor.set( + (F32)arr[0].to_number(), + (F32)arr[1].to_number(), + (F32)arr[2].to_number()); + } + } + + // Double sided + const auto* ds = mat_obj.if_contains("doubleSided"); + if (ds && ds->is_bool()) + { + mDoubleSided = ds->as_bool(); + } + + // Material extensions + const auto* extensions_val = mat_obj.if_contains("extensions"); + if (extensions_val && extensions_val->is_object()) + { + const auto& extensions = extensions_val->as_object(); + + // KHR_materials_emissive_strength + const auto* emissive_strength_ext = extensions.if_contains("KHR_materials_emissive_strength"); + if (emissive_strength_ext && emissive_strength_ext->is_object()) + { + const auto& es = emissive_strength_ext->as_object(); + const auto* es_val = es.if_contains("emissiveStrength"); + if (es_val && es_val->is_number()) + { + mEmissiveStrength = llmax((F32)es_val->to_number(), 0.f); + } + } + + // KHR_materials_specular + const auto* specular_ext = extensions.if_contains("KHR_materials_specular"); + if (specular_ext && specular_ext->is_object()) + { + const auto& spec = specular_ext->as_object(); + + const auto* sf = spec.if_contains("specularFactor"); + if (sf && sf->is_number()) + { + mSpecularFactor = llclamp((F32)sf->to_number(), 0.f, 1.f); + } + + const auto* scf = spec.if_contains("specularColorFactor"); + if (scf && scf->is_array()) + { + const auto& arr = scf->as_array(); + if (arr.size() >= 3) + { + mSpecularColorFactor.set( + (F32)arr[0].to_number(), + (F32)arr[1].to_number(), + (F32)arr[2].to_number()); + } + } + + const auto* spec_tex = spec.if_contains("specularTexture"); + if (spec_tex && spec_tex->is_object()) + { + readTextureInfo(doc, spec_tex->as_object(), GLTF_TEXTURE_INFO_SPECULAR); + } + + const auto* spec_color_tex = spec.if_contains("specularColorTexture"); + if (spec_color_tex && spec_color_tex->is_object()) + { + readTextureInfo(doc, spec_color_tex->as_object(), GLTF_TEXTURE_INFO_SPECULAR); + } + } + + // KHR_materials_ior + const auto* ior_ext = extensions.if_contains("KHR_materials_ior"); + if (ior_ext && ior_ext->is_object()) + { + const auto& ior_obj = ior_ext->as_object(); + const auto* ior_val = ior_obj.if_contains("ior"); + if (ior_val && ior_val->is_number()) + { + mIOR = llmax((F32)ior_val->to_number(), 0.f); + } + } + } + + // Extras + const auto* extras_val = mat_obj.if_contains("extras"); + if (extras_val && extras_val->is_object()) { - const tinygltf::Value& real_json = vec2_json.Get(i); - if (!real_json.IsReal()) + const auto& extras = extras_val->as_object(); + const auto* override_am = extras.if_contains("override_alpha_mode"); + if (override_am && override_am->is_bool()) { - return default_value; + mOverrideAlphaMode = override_am->as_bool(); + } + + const auto* override_ds = extras.if_contains("override_double_sided"); + if (override_ds && override_ds->is_bool()) + { + mOverrideDoubleSided = override_ds->as_bool(); } - value.mV[i] = (F32)real_json.Get(); } - return value; + + return true; } // static -F32 LLGLTFMaterial::floatFromJson(const tinygltf::Value::Object& object, const char* key, const F32 default_value) +LLVector2 LLGLTFMaterial::vec2FromJson(const boost::json::object& obj, const char* key, const LLVector2& default_value) { - const auto it = object.find(key); - if (it == object.end()) + const auto* val = obj.if_contains(key); + if (!val || !val->is_array()) return default_value; + + const auto& arr = val->as_array(); + if (arr.size() < LENGTHOFVECTOR2) return default_value; + + LLVector2 result; + for (U32 i = 0; i < LENGTHOFVECTOR2; ++i) { - return default_value; + if (!arr[i].is_number()) return default_value; + result.mV[i] = (F32)arr[i].to_number(); } - const tinygltf::Value& real_json = std::get<1>(*it); - if (!real_json.IsReal()) + return result; +} + +// static +F32 LLGLTFMaterial::floatFromJson(const boost::json::object& obj, const char* key, F32 default_value) +{ + const auto* val = obj.if_contains(key); + if (!val || !val->is_number()) return default_value; + return (F32)val->to_number(); +} + +// static +void LLGLTFMaterial::writeTextureEntry(boost::json::array& images, boost::json::array& textures, boost::json::object& dst, const char* key, const LLUUID& texture_id, const TextureTransform& transform, bool force_write) +{ + LL_PROFILE_ZONE_SCOPED; + const bool is_blank_transform = transform == sDefault.mTextureTransform[0]; + if (!force_write && texture_id.isNull() && is_blank_transform) + { + return; + } + + // Add image entry + S32 image_idx = (S32)images.size(); + boost::json::object image_obj; + image_obj["uri"] = texture_id.asString(); + images.push_back(image_obj); + + // Add texture entry + S32 texture_idx = (S32)textures.size(); + boost::json::object tex_obj; + tex_obj["source"] = image_idx; + textures.push_back(tex_obj); + + // Build texture info + boost::json::object tex_info; + tex_info["index"] = texture_idx; + + if (!is_blank_transform) { - return default_value; + boost::json::object transform_obj; + transform_obj[GLTF_FILE_EXTENSION_TRANSFORM_OFFSET] = boost::json::array({ + boost::json::value(transform.mOffset.mV[VX]), + boost::json::value(transform.mOffset.mV[VY]) + }); + transform_obj[GLTF_FILE_EXTENSION_TRANSFORM_SCALE] = boost::json::array({ + boost::json::value(transform.mScale.mV[VX]), + boost::json::value(transform.mScale.mV[VY]) + }); + transform_obj[GLTF_FILE_EXTENSION_TRANSFORM_ROTATION] = transform.mRotation; + + boost::json::object extensions; + extensions[GLTF_FILE_EXTENSION_TRANSFORM] = transform_obj; + tex_info["extensions"] = extensions; } - return (F32)real_json.GetNumberAsDouble(); + + dst[key] = tex_info; } -void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const +boost::json::value LLGLTFMaterial::writeDocument() const { LL_PROFILE_ZONE_SCOPED; - if (model.materials.size() < mat_index+1) + boost::json::object root; + + // Asset info + boost::json::object asset; + asset["version"] = "2.0"; + root["asset"] = asset; + + boost::json::array images; + boost::json::array textures; + + // Build material + boost::json::object mat_obj; + + // PBR metallic roughness + boost::json::object pbr; + bool has_pbr = false; + + // Base color texture + writeTextureEntry(images, textures, pbr, "baseColorTexture", + mTextureId[GLTF_TEXTURE_INFO_BASE_COLOR], mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR]); + if (pbr.contains("baseColorTexture")) has_pbr = true; + + // Metallic roughness texture + writeTextureEntry(images, textures, pbr, "metallicRoughnessTexture", + mTextureId[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS], mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]); + if (pbr.contains("metallicRoughnessTexture")) has_pbr = true; + + // Base color factor + boost::json::array bcf = { + boost::json::value((double)mBaseColor.mV[VRED]), + boost::json::value((double)mBaseColor.mV[VGREEN]), + boost::json::value((double)mBaseColor.mV[VBLUE]), + boost::json::value((double)mBaseColor.mV[VALPHA]) + }; + if (mBaseColor != getDefaultBaseColor()) { - model.materials.resize(mat_index + 1); + pbr["baseColorFactor"] = bcf; + has_pbr = true; } - tinygltf::Material& material_out = model.materials[mat_index]; + if (mMetallicFactor != getDefaultMetallicFactor()) + { + pbr["metallicFactor"] = (double)mMetallicFactor; + has_pbr = true; + } - // set base color texture - writeToTexture(model, material_out.pbrMetallicRoughness.baseColorTexture, GLTF_TEXTURE_INFO_BASE_COLOR); - // set normal texture - writeToTexture(model, material_out.normalTexture, GLTF_TEXTURE_INFO_NORMAL); - // set metallic-roughness texture - writeToTexture(model, material_out.pbrMetallicRoughness.metallicRoughnessTexture, GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS); - // set emissive texture - writeToTexture(model, material_out.emissiveTexture, GLTF_TEXTURE_INFO_EMISSIVE); - // set occlusion texture - // *NOTE: This is required for ORM materials for GLTF compliance. - // See: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_material_occlusiontexture - writeToTexture(model, material_out.occlusionTexture, GLTF_TEXTURE_INFO_OCCLUSION); + if (mRoughnessFactor != getDefaultRoughnessFactor()) + { + pbr["roughnessFactor"] = (double)mRoughnessFactor; + has_pbr = true; + } + if (has_pbr) + { + // Always write base color factor if we have PBR at all + if (!pbr.contains("baseColorFactor")) + { + pbr["baseColorFactor"] = bcf; + } + if (!pbr.contains("metallicFactor")) + { + pbr["metallicFactor"] = (double)mMetallicFactor; + } + if (!pbr.contains("roughnessFactor")) + { + pbr["roughnessFactor"] = (double)mRoughnessFactor; + } + mat_obj["pbrMetallicRoughness"] = pbr; + } - material_out.alphaMode = getAlphaMode(); - material_out.alphaCutoff = mAlphaCutoff; + // Normal texture + boost::json::object normal_dst; + writeTextureEntry(images, textures, normal_dst, "normalTexture_tmp", + mTextureId[GLTF_TEXTURE_INFO_NORMAL], mTextureTransform[GLTF_TEXTURE_INFO_NORMAL]); + if (normal_dst.contains("normalTexture_tmp")) + { + mat_obj["normalTexture"] = normal_dst["normalTexture_tmp"]; + } - mBaseColor.write(material_out.pbrMetallicRoughness.baseColorFactor); + // Emissive texture + boost::json::object emissive_dst; + writeTextureEntry(images, textures, emissive_dst, "emissiveTexture_tmp", + mTextureId[GLTF_TEXTURE_INFO_EMISSIVE], mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE]); + if (emissive_dst.contains("emissiveTexture_tmp")) + { + mat_obj["emissiveTexture"] = emissive_dst["emissiveTexture_tmp"]; + } - if (mEmissiveColor != LLGLTFMaterial::getDefaultEmissiveColor()) + // Occlusion texture (GLTF compliance for ORM) + boost::json::object occlusion_dst; + writeTextureEntry(images, textures, occlusion_dst, "occlusionTexture_tmp", + mTextureId[GLTF_TEXTURE_INFO_OCCLUSION], mTextureTransform[GLTF_TEXTURE_INFO_OCCLUSION]); + if (occlusion_dst.contains("occlusionTexture_tmp")) { - material_out.emissiveFactor.resize(3); - mEmissiveColor.write(material_out.emissiveFactor); + mat_obj["occlusionTexture"] = occlusion_dst["occlusionTexture_tmp"]; } - material_out.pbrMetallicRoughness.metallicFactor = mMetallicFactor; - material_out.pbrMetallicRoughness.roughnessFactor = mRoughnessFactor; + mat_obj["alphaMode"] = getAlphaMode(); + mat_obj["alphaCutoff"] = (double)mAlphaCutoff; + mat_obj["doubleSided"] = mDoubleSided; - material_out.doubleSided = mDoubleSided; + if (mEmissiveColor != getDefaultEmissiveColor()) + { + mat_obj["emissiveFactor"] = boost::json::array({ + boost::json::value((double)mEmissiveColor.mV[0]), + boost::json::value((double)mEmissiveColor.mV[1]), + boost::json::value((double)mEmissiveColor.mV[2]) + }); + } - // generate "extras" string - tinygltf::Value::Object extras; + // Extras + boost::json::object extras; bool write_extras = false; if (mOverrideAlphaMode && mAlphaMode == getDefaultAlphaMode()) { - extras["override_alpha_mode"] = tinygltf::Value(mOverrideAlphaMode); + extras["override_alpha_mode"] = true; write_extras = true; } - if (mOverrideDoubleSided && mDoubleSided == getDefaultDoubleSided()) { - extras["override_double_sided"] = tinygltf::Value(mOverrideDoubleSided); + extras["override_double_sided"] = true; write_extras = true; } - if (write_extras) { - material_out.extras = tinygltf::Value(extras); + mat_obj["extras"] = extras; } - model.asset.version = "2.0"; + // Material extensions + boost::json::object mat_extensions; + + // KHR_materials_emissive_strength + if (mEmissiveStrength != getDefaultEmissiveStrength()) + { + boost::json::object es_ext; + es_ext["emissiveStrength"] = (double)mEmissiveStrength; + mat_extensions["KHR_materials_emissive_strength"] = es_ext; + } + + // KHR_materials_specular + { + bool has_specular = false; + boost::json::object specular_ext; + + if (mSpecularFactor != getDefaultSpecularFactor()) + { + specular_ext["specularFactor"] = (double)mSpecularFactor; + has_specular = true; + } + + if (mSpecularColorFactor != getDefaultSpecularColorFactor()) + { + specular_ext["specularColorFactor"] = boost::json::array({ + boost::json::value((double)mSpecularColorFactor.mV[0]), + boost::json::value((double)mSpecularColorFactor.mV[1]), + boost::json::value((double)mSpecularColorFactor.mV[2]) + }); + has_specular = true; + } + + boost::json::object spec_tex_dst; + writeTextureEntry(images, textures, spec_tex_dst, "specularTexture_tmp", + mTextureId[GLTF_TEXTURE_INFO_SPECULAR], mTextureTransform[GLTF_TEXTURE_INFO_SPECULAR]); + if (spec_tex_dst.contains("specularTexture_tmp")) + { + specular_ext["specularTexture"] = spec_tex_dst["specularTexture_tmp"]; + specular_ext["specularColorTexture"] = spec_tex_dst["specularTexture_tmp"]; + has_specular = true; + } + + if (has_specular) + { + mat_extensions["KHR_materials_specular"] = specular_ext; + } + } + + // KHR_materials_ior + if (mIOR != getDefaultIOR()) + { + boost::json::object ior_ext; + ior_ext["ior"] = (double)mIOR; + mat_extensions["KHR_materials_ior"] = ior_ext; + } + + if (!mat_extensions.empty()) + { + mat_obj["extensions"] = mat_extensions; + } + + boost::json::array materials; + materials.push_back(mat_obj); + root["materials"] = materials; + + if (!images.empty()) + { + root["images"] = images; + } + if (!textures.empty()) + { + root["textures"] = textures; + } + + return root; } void LLGLTFMaterial::sanitizeAssetMaterial() @@ -434,6 +827,11 @@ void LLGLTFMaterial::setEmissiveId(const LLUUID& id, bool for_override) setTextureId(GLTF_TEXTURE_INFO_EMISSIVE, id, for_override); } +void LLGLTFMaterial::setSpecularId(const LLUUID& id, bool for_override) +{ + setTextureId(GLTF_TEXTURE_INFO_SPECULAR, id, for_override); +} + void LLGLTFMaterial::setBaseColorFactor(const LLColor4& baseColor, bool for_override) { mBaseColor.set(baseColor); @@ -523,6 +921,46 @@ void LLGLTFMaterial::setDoubleSided(bool double_sided, bool for_override) mOverrideDoubleSided = for_override && mDoubleSided == getDefaultDoubleSided(); } +void LLGLTFMaterial::setEmissiveStrength(F32 strength, bool for_override) +{ + mEmissiveStrength = llmax(strength, 0.f); + if (for_override) + { + if (mEmissiveStrength == getDefaultEmissiveStrength()) + { + mEmissiveStrength -= FLT_EPSILON; + } + } +} + +void LLGLTFMaterial::setSpecularFactor(F32 factor, bool for_override) +{ + mSpecularFactor = llclamp(factor, 0.f, for_override ? 1.f - FLT_EPSILON : 1.f); +} + +void LLGLTFMaterial::setIOR(F32 ior, bool for_override) +{ + mIOR = llmax(ior, 0.f); + if (for_override && mIOR == getDefaultIOR()) + { + mIOR -= FLT_EPSILON; + } +} + +void LLGLTFMaterial::setSpecularColorFactor(const LLColor3& color, bool for_override) +{ + mSpecularColorFactor = color; + mSpecularColorFactor.clamp(); + + if (for_override) + { + if (mSpecularColorFactor == getDefaultSpecularColorFactor()) + { + mSpecularColorFactor.mV[0] -= FLT_EPSILON; + } + } +} + void LLGLTFMaterial::setTextureOffset(TextureInfo texture_info, const LLVector2& offset) { mTextureTransform[texture_info].mOffset = offset; @@ -578,6 +1016,26 @@ bool LLGLTFMaterial::getDefaultDoubleSided() return sDefault.mDoubleSided; } +F32 LLGLTFMaterial::getDefaultEmissiveStrength() +{ + return sDefault.mEmissiveStrength; +} + +F32 LLGLTFMaterial::getDefaultSpecularFactor() +{ + return sDefault.mSpecularFactor; +} + +LLColor3 LLGLTFMaterial::getDefaultSpecularColorFactor() +{ + return sDefault.mSpecularColorFactor; +} + +F32 LLGLTFMaterial::getDefaultIOR() +{ + return sDefault.mIOR; +} + LLVector2 LLGLTFMaterial::getDefaultTextureOffset() { return sDefault.mTextureTransform[0].mOffset; @@ -654,6 +1112,26 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat) mDoubleSided = override_mat.mDoubleSided; } + if (override_mat.mEmissiveStrength != getDefaultEmissiveStrength()) + { + mEmissiveStrength = override_mat.mEmissiveStrength; + } + + if (override_mat.mSpecularFactor != getDefaultSpecularFactor()) + { + mSpecularFactor = override_mat.mSpecularFactor; + } + + if (override_mat.mSpecularColorFactor != getDefaultSpecularColorFactor()) + { + mSpecularColorFactor = override_mat.mSpecularColorFactor; + } + + if (override_mat.mIOR != getDefaultIOR()) + { + mIOR = override_mat.mIOR; + } + for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) { if (override_mat.mTextureTransform[i].mOffset != getDefaultTextureOffset()) @@ -733,6 +1211,26 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d data["ds"] = override_mat.mDoubleSided; } + if (override_mat.mEmissiveStrength != getDefaultEmissiveStrength()) + { + data["es"] = override_mat.mEmissiveStrength; + } + + if (override_mat.mSpecularFactor != getDefaultSpecularFactor()) + { + data["sf"] = override_mat.mSpecularFactor; + } + + if (override_mat.mSpecularColorFactor != getDefaultSpecularColorFactor()) + { + data["sc"] = override_mat.mSpecularColorFactor.getValue(); + } + + if (override_mat.mIOR != getDefaultIOR()) + { + data["ior"] = override_mat.mIOR; + } + for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) { if (override_mat.mTextureTransform[i].mOffset != getDefaultTextureOffset()) @@ -834,6 +1332,46 @@ void LLGLTFMaterial::applyOverrideLLSD(const LLSD& data) mOverrideDoubleSided = true; } + const LLSD& es = data["es"]; + if (es.isReal()) + { + mEmissiveStrength = (F32)es.asReal(); + if (mEmissiveStrength == getDefaultEmissiveStrength()) + { + mEmissiveStrength -= FLT_EPSILON; + } + } + + const LLSD& sf = data["sf"]; + if (sf.isReal()) + { + mSpecularFactor = (F32)sf.asReal(); + if (mSpecularFactor == getDefaultSpecularFactor()) + { + mSpecularFactor -= FLT_EPSILON; + } + } + + const LLSD& sc = data["sc"]; + if (sc.isDefined()) + { + mSpecularColorFactor.setValue(sc); + if (mSpecularColorFactor == getDefaultSpecularColorFactor()) + { + mSpecularColorFactor.mV[0] -= FLT_EPSILON; + } + } + + const LLSD& ior = data["ior"]; + if (ior.isReal()) + { + mIOR = (F32)ior.asReal(); + if (mIOR == getDefaultIOR()) + { + mIOR -= FLT_EPSILON; + } + } + const LLSD& ti = data["ti"]; if (ti.isArray()) { @@ -863,17 +1401,44 @@ void LLGLTFMaterial::applyOverrideLLSD(const LLSD& data) LLUUID LLGLTFMaterial::getHash() const { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - // *HACK: hash the bytes of this object but do not include the ref count - // neither the local texture overrides (which is a map, with pointers to - // key/value pairs that would change from one LLGLTFMaterial instance to - // the other, even though the key/value pairs could be the same, and stored - // elsewhere in the memory heap or on the stack). - // Note: this does work properly to compare two LLGLTFMaterial instances - // only because the padding bytes between their member variables have been - // dutifully zeroed in the constructor. HB - const size_t offset = intptr_t(&mLocalTexDataDigest) - intptr_t(this); - return HBXXH128::digest((const void*)((const char*)this + offset), - sizeof(*this) - offset); + + // Hash each field explicitly rather than hashing raw object bytes. + // Raw byte hashing is fragile across compilers/platforms due to + // struct padding and std::map layout side effects. + HBXXH128 hash; + + hash.update(&mLocalTexDataDigest, sizeof(mLocalTexDataDigest)); + + for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) + { + hash.update(mTextureId[i].mData, UUID_BYTES); + } + + for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) + { + TextureTransform::Pack packed; + mTextureTransform[i].getPacked(packed); + hash.update(packed, sizeof(packed)); + } + + hash.update(mBaseColor.mV, sizeof(mBaseColor.mV)); + hash.update(mEmissiveColor.mV, sizeof(mEmissiveColor.mV)); + + hash.update(&mMetallicFactor, sizeof(mMetallicFactor)); + hash.update(&mRoughnessFactor, sizeof(mRoughnessFactor)); + hash.update(&mAlphaCutoff, sizeof(mAlphaCutoff)); + hash.update(&mEmissiveStrength, sizeof(mEmissiveStrength)); + hash.update(&mSpecularFactor, sizeof(mSpecularFactor)); + hash.update(mSpecularColorFactor.mV, sizeof(mSpecularColorFactor.mV)); + hash.update(&mIOR, sizeof(mIOR)); + + hash.update(&mAlphaMode, sizeof(mAlphaMode)); + hash.update(&mDoubleSided, sizeof(mDoubleSided)); + hash.update(&mOverrideDoubleSided, sizeof(mOverrideDoubleSided)); + hash.update(&mOverrideAlphaMode, sizeof(mOverrideAlphaMode)); + + hash.finalize(); + return hash.digest(); } void LLGLTFMaterial::addLocalTextureTracking(const LLUUID& tracking_id, const LLUUID& tex_id) diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h index c37062e7d3d..a90ece13567 100644 --- a/indra/llprimitive/llgltfmaterial.h +++ b/indra/llprimitive/llgltfmaterial.h @@ -34,17 +34,12 @@ #include "lluuid.h" #include "hbxxh.h" +#include + #include #include #include -namespace tinygltf -{ - class Model; - struct TextureInfo; - class Value; -} - class LLTextureEntry; class LLGLTFMaterial : public LLRefCount @@ -105,6 +100,7 @@ class LLGLTFMaterial : public LLRefCount // -Cosmic,2023-01-26 GLTF_TEXTURE_INFO_OCCLUSION = GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS, GLTF_TEXTURE_INFO_EMISSIVE, + GLTF_TEXTURE_INFO_SPECULAR, GLTF_TEXTURE_INFO_COUNT }; @@ -127,6 +123,7 @@ class LLGLTFMaterial : public LLRefCount void setNormalId(const LLUUID& id, bool for_override = false); void setOcclusionRoughnessMetallicId(const LLUUID& id, bool for_override = false); void setEmissiveId(const LLUUID& id, bool for_override = false); + void setSpecularId(const LLUUID& id, bool for_override = false); void setBaseColorFactor(const LLColor4& baseColor, bool for_override = false); void setAlphaCutoff(F32 cutoff, bool for_override = false); @@ -135,6 +132,10 @@ class LLGLTFMaterial : public LLRefCount void setRoughnessFactor(F32 roughness, bool for_override = false); void setAlphaMode(S32 mode, bool for_override = false); void setDoubleSided(bool double_sided, bool for_override = false); + void setEmissiveStrength(F32 strength, bool for_override = false); + void setSpecularFactor(F32 factor, bool for_override = false); + void setSpecularColorFactor(const LLColor3& color, bool for_override = false); + void setIOR(F32 ior, bool for_override = false); // *NOTE: texture offsets only exist in overrides, so "for_override" is not needed @@ -150,6 +151,10 @@ class LLGLTFMaterial : public LLRefCount static LLColor4 getDefaultBaseColor(); static LLColor3 getDefaultEmissiveColor(); static bool getDefaultDoubleSided(); + static F32 getDefaultEmissiveStrength(); + static F32 getDefaultSpecularFactor(); + static LLColor3 getDefaultSpecularColorFactor(); + static F32 getDefaultIOR(); static LLVector2 getDefaultTextureOffset(); static LLVector2 getDefaultTextureScale(); static F32 getDefaultTextureRotation(); @@ -167,21 +172,13 @@ class LLGLTFMaterial : public LLRefCount // returns true if successful // if unsuccessful, the contents of this LLGLTFMaterial should be left unchanged and false is returned // json - the json text to load from - // warn_msg - warning message from TinyGLTF if any - // error_msg - error_msg from TinyGLTF if any + // warn_msg - warning message if any + // error_msg - error message if any bool fromJSON(const std::string& json, std::string& warn_msg, std::string& error_msg); // get the contents of this LLGLTFMaterial as a json string std::string asJSON(bool prettyprint = false) const; - // initialize from given tinygltf::Model - // model - the model to reference - // mat_index - index of material in model's material array - void setFromModel(const tinygltf::Model& model, S32 mat_index); - - // write to given tinygltf::Model - void writeToModel(tinygltf::Model& model, S32 mat_index) const; - virtual void applyOverride(const LLGLTFMaterial& override_mat); // apply the given LLSD override data @@ -231,21 +228,20 @@ class LLGLTFMaterial : public LLRefCount F32& tex_offset_s, F32& tex_offset_t, F32& tex_rotation); protected: - static LLVector2 vec2FromJson(const std::map& object, const char* key, const LLVector2& default_value); - static F32 floatFromJson(const std::map& object, const char* key, const F32 default_value); - - template - static void allocateTextureImage(tinygltf::Model& model, T& texture_info, const std::string& uri); - - template - void setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id); - template - static void setFromTexture(const tinygltf::Model& model, const T& texture_info, LLUUID& texture_id, TextureTransform& transform); - - template - void writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write = false) const; - template - static void writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write = false); + // Parse from glTF JSON document + bool setFromDocument(const boost::json::value& doc, S32 mat_index); + // Serialize to glTF JSON document + boost::json::value writeDocument() const; + // Resolve texture index -> textures[].source -> images[].uri + static std::string getTextureURI(const boost::json::value& doc, S32 texture_index); + // Read texture info from JSON + void readTextureInfo(const boost::json::value& doc, const boost::json::object& tex_info, TextureInfo id); + static void readTextureInfo(const boost::json::value& doc, const boost::json::object& tex_info, LLUUID& texture_id, TextureTransform& transform); + // Write texture entry to JSON arrays + static void writeTextureEntry(boost::json::array& images, boost::json::array& textures, boost::json::object& dst, const char* key, const LLUUID& texture_id, const TextureTransform& transform, bool force_write = false); + // JSON helpers + static LLVector2 vec2FromJson(const boost::json::object& obj, const char* key, const LLVector2& default_value); + static F32 floatFromJson(const boost::json::object& obj, const char* key, F32 default_value); // Used to update the digest of the mTrackingIdToLocalTexture map each time // it is changed; this way, that digest can be used by the fast getHash() @@ -282,6 +278,10 @@ class LLGLTFMaterial : public LLRefCount F32 mMetallicFactor; F32 mRoughnessFactor; F32 mAlphaCutoff; + F32 mEmissiveStrength; + F32 mSpecularFactor; + LLColor3 mSpecularColorFactor; + F32 mIOR; AlphaMode mAlphaMode; diff --git a/indra/llprimitive/llgltfmaterial_templates.h b/indra/llprimitive/llgltfmaterial_templates.h deleted file mode 100644 index 4ec7f312afe..00000000000 --- a/indra/llprimitive/llgltfmaterial_templates.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - * @file llgltfmaterial_templates.h - * @brief Material template definition - * - * $LicenseInfo:firstyear=2023&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2023, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#pragma once - -#include "llgltfmaterial.h" - -// Use templates here as workaround for the different similar texture info classes in tinygltf -// Includer must first include tiny_gltf.h with the desired flags - -template -std::string gltf_get_texture_image(const tinygltf::Model& model, const T& texture_info) -{ - const S32 texture_idx = texture_info.index; - if (texture_idx < 0 || texture_idx >= model.textures.size()) - { - return ""; - } - const tinygltf::Texture& texture = model.textures[texture_idx]; - - // Ignore texture.sampler for now - - const S32 image_idx = texture.source; - if (image_idx < 0 || image_idx >= model.images.size()) - { - return ""; - } - const tinygltf::Image& image = model.images[image_idx]; - - return image.uri; -} - -template -void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id) -{ - setFromTexture(model, texture_info, mTextureId[texture_info_id], mTextureTransform[texture_info_id]); - const std::string uri = gltf_get_texture_image(model, texture_info); -} - -// static -template -void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, LLUUID& texture_id, TextureTransform& transform) -{ - LL_PROFILE_ZONE_SCOPED; - const std::string uri = gltf_get_texture_image(model, texture_info); - texture_id.set(uri); - - const tinygltf::Value::Object& extensions_object = texture_info.extensions; - const auto transform_it = extensions_object.find(GLTF_FILE_EXTENSION_TRANSFORM); - if (transform_it != extensions_object.end()) - { - const tinygltf::Value& transform_json = std::get<1>(*transform_it); - if (transform_json.IsObject()) - { - const tinygltf::Value::Object& transform_object = transform_json.Get(); - transform.mOffset = vec2FromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_OFFSET, getDefaultTextureOffset()); - transform.mScale = vec2FromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_SCALE, getDefaultTextureScale()); - transform.mRotation = floatFromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_ROTATION, getDefaultTextureRotation()); - } - } -} - -// static -template -void LLGLTFMaterial::allocateTextureImage(tinygltf::Model& model, T& texture_info, const std::string& uri) -{ - const S32 image_idx = static_cast(model.images.size()); - model.images.emplace_back(); - model.images[image_idx].uri = uri; - - // The texture, not to be confused with the texture info - const S32 texture_idx = static_cast(model.textures.size()); - model.textures.emplace_back(); - tinygltf::Texture& texture = model.textures[texture_idx]; - texture.source = image_idx; - - texture_info.index = texture_idx; -} - -// static -template -void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write) const -{ - writeToTexture(model, texture_info, mTextureId[texture_info_id], mTextureTransform[texture_info_id], force_write); -} - -// static -template -void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write) -{ - LL_PROFILE_ZONE_SCOPED; - const bool is_blank_transform = transform == sDefault.mTextureTransform[0]; - // Check if this material matches all the fallback values, and if so, then - // skip including it to reduce material size - if (!force_write && texture_id.isNull() && is_blank_transform) - { - return; - } - - // tinygltf will discard this texture info if there is no valid texture, - // causing potential loss of information for overrides, so ensure one is - // defined. -Cosmic,2023-01-30 - allocateTextureImage(model, texture_info, texture_id.asString()); - - if (!is_blank_transform) - { - tinygltf::Value::Object transform_map; - transform_map[GLTF_FILE_EXTENSION_TRANSFORM_OFFSET] = tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(transform.mOffset.mV[VX]), - tinygltf::Value(transform.mOffset.mV[VY]) - })); - transform_map[GLTF_FILE_EXTENSION_TRANSFORM_SCALE] = tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(transform.mScale.mV[VX]), - tinygltf::Value(transform.mScale.mV[VY]) - })); - transform_map[GLTF_FILE_EXTENSION_TRANSFORM_ROTATION] = tinygltf::Value(transform.mRotation); - texture_info.extensions[GLTF_FILE_EXTENSION_TRANSFORM] = tinygltf::Value(transform_map); - } -} diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index ac482ffbf9c..efae833402e 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -600,7 +600,7 @@ LLGLTFMaterial* LLTextureEntry::getGLTFRenderMaterial() const return mGLTFRenderMaterial; } - llassert(getGLTFMaterialOverride() == nullptr || getGLTFMaterialOverride()->isClearedForBaseMaterial()); + ////////llassert(getGLTFMaterialOverride() == nullptr || getGLTFMaterialOverride()->isClearedForBaseMaterial()); return getGLTFMaterial(); } diff --git a/indra/llprimitive/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp index 4f2de82386a..53e09d1545d 100644 --- a/indra/llprimitive/tests/llgltfmaterial_test.cpp +++ b/indra/llprimitive/tests/llgltfmaterial_test.cpp @@ -31,26 +31,6 @@ #include "../llgltfmaterial.h" #include "lluuid.cpp" -// Import & define single-header gltf import/export lib -#define TINYGLTF_IMPLEMENTATION -#define TINYGLTF_USE_CPP14 // default is C++ 11 - -// tinygltf by default loads image files using STB -#define STB_IMAGE_IMPLEMENTATION -// to use our own image loading: -// 1. replace this definition with TINYGLTF_NO_STB_IMAGE -// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data) - -// tinygltf saves image files using STB -#define STB_IMAGE_WRITE_IMPLEMENTATION -// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data) - -// Disable reading external images to prevent warnings and speed up the tests. -// We don't need this for the tests, but still need the filesystem -// implementation to be defined in order for llprimitive to link correctly. -#define TINYGLTF_NO_EXTERNAL_IMAGE 1 - -#include "tinygltf/tiny_gltf.h" namespace tut { @@ -72,6 +52,7 @@ namespace tut material.setNormalId(LLUUID::generateNewID()); material.setOcclusionRoughnessMetallicId(LLUUID::generateNewID()); material.setEmissiveId(LLUUID::generateNewID()); + material.setSpecularId(LLUUID::generateNewID()); } void apply_test_material_texture_transforms(LLGLTFMaterial& material) @@ -96,6 +77,10 @@ namespace tut material.setEmissiveColorFactor(LLColor3(test_fraction_big, test_fraction_big, test_fraction_big)); material.setMetallicFactor(test_fraction); material.setRoughnessFactor(test_fraction); + material.setEmissiveStrength(test_fraction); + material.setSpecularFactor(test_fraction); + material.setSpecularColorFactor(LLColor3(test_fraction_big, test_fraction_big, test_fraction_big)); + material.setIOR(test_fraction); } LLGLTFMaterial create_test_material() @@ -145,10 +130,10 @@ namespace tut #if LL_WINDOWS // If any fields are added/changed, these tests should be updated (consider also updating ASSET_VERSION in LLGLTFMaterial) // This test result will vary between compilers, so only test a single platform - ensure_equals("fields supported for GLTF (sizeof check)", sizeof(LLGLTFMaterial), 232); + ensure_equals("fields supported for GLTF (sizeof check)", sizeof(LLGLTFMaterial), 296); #endif #endif - ensure_equals("LLGLTFMaterial texture info count", (U32)LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT, 4); + ensure_equals("LLGLTFMaterial texture info count", (U32)LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT, 5); } // Test that occlusion and metallicRoughness are the same (They are different for asset validation. See lluploadmaterial.cpp) @@ -419,6 +404,10 @@ namespace tut source_mat.mTrackingIdToLocalTexture[LLUUID::generateNewID()] = LLUUID::generateNewID(); source_mat.mLocalTexDataDigest = 1; source_mat.mAlphaMode = LLGLTFMaterial::ALPHA_MODE_MASK; + source_mat.mEmissiveStrength = test_fraction; + source_mat.mSpecularFactor = test_fraction; + source_mat.mSpecularColorFactor.set(test_fraction_big, test_fraction_big, test_fraction_big); + source_mat.mIOR = test_fraction; source_mat.mDoubleSided = true; LLGLTFMaterial hash_mat; @@ -433,6 +422,10 @@ namespace tut ENSURE_HASH_CHANGED(hash_mat, source_mat, mMetallicFactor); ENSURE_HASH_CHANGED(hash_mat, source_mat, mRoughnessFactor); ENSURE_HASH_CHANGED(hash_mat, source_mat, mAlphaCutoff); + ENSURE_HASH_CHANGED(hash_mat, source_mat, mEmissiveStrength); + ENSURE_HASH_CHANGED(hash_mat, source_mat, mSpecularFactor); + ENSURE_HASH_CHANGED(hash_mat, source_mat, mSpecularColorFactor); + ENSURE_HASH_CHANGED(hash_mat, source_mat, mIOR); ENSURE_HASH_CHANGED(hash_mat, source_mat, mAlphaMode); ENSURE_HASH_CHANGED(hash_mat, source_mat, mDoubleSided); ENSURE_HASH_CHANGED(hash_mat, source_mat, mOverrideDoubleSided); diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 272a99aaa58..abdc2ed6b62 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -60,6 +60,7 @@ class LLShaderFeatures bool hasHeroProbes = false; bool isPBRTerrain = false; bool hasTonemap = false; + bool hasMotionBlur = false; }; // ============= Structure for caching shader uniforms =============== diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 0b0d69812f0..5d36bb4bcfb 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -95,9 +95,28 @@ void LLRenderTarget::resize(U32 resx, U32 resy) { gGL.getTexUnit(0)->bindManual(mUsage, mDepth); U32 internal_type = LLTexUnit::getInternalType(mUsage); - LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); - sBytesAllocated += pix_diff*4; + if (mGenerateMipMaps != LLTexUnit::TMG_NONE && mMipLevels > 1) + { + // Recalculate mip levels for new resolution + mMipLevels = 1 + (U32)floor(log10((float)llmax(mResX, mResY)) / log10(2.0)); + + for (U32 level = 0; level < mMipLevels; level++) + { + U32 w = llmax(1U, mResX >> level); + U32 h = llmax(1U, mResY >> level); + glTexImage2D(internal_type, level, GL_DEPTH_COMPONENT24, w, h, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + } + glTexParameteri(internal_type, GL_TEXTURE_MAX_LEVEL, mMipLevels - 1); + + sBytesAllocated += (S32)(pix_diff * 4 * 1.33f); + } + else + { + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + sBytesAllocated += pix_diff * 4; + } } } @@ -300,10 +319,29 @@ bool LLRenderTarget::allocateDepth() U32 internal_type = LLTexUnit::getInternalType(mUsage); stop_glerror(); clear_glerror(); - LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - sBytesAllocated += mResX*mResY*4; + if (mGenerateMipMaps != LLTexUnit::TMG_NONE && mMipLevels > 1) + { + // Allocate depth mip chain for Hi-Z pyramid generation. + for (U32 level = 0; level < mMipLevels; level++) + { + U32 w = llmax(1U, mResX >> level); + U32 h = llmax(1U, mResY >> level); + glTexImage2D(internal_type, level, GL_DEPTH_COMPONENT24, w, h, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + } + glTexParameteri(internal_type, GL_TEXTURE_MAX_LEVEL, mMipLevels - 1); + + // ~33% extra for mip chain + sBytesAllocated += (U32)(mResX * mResY * 4 * 1.33f); + } + else + { + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT24, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL, false); + sBytesAllocated += mResX * mResY * 4; + } + + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); if (glGetError() != GL_NO_ERROR) { @@ -314,6 +352,63 @@ bool LLRenderTarget::allocateDepth() return true; } +void LLRenderTarget::bindDepthMipLevel(S32 level) +{ + llassert(mFBO); + llassert(mDepth); + llassert(level < (S32)mMipLevels); + + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + sCurFBO = mFBO; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + LLTexUnit::getInternalType(mUsage), mDepth, level); + glDrawBuffer(GL_NONE); + + S32 w = llmax(1, (S32)(mResX >> level)); + S32 h = llmax(1, (S32)(mResY >> level)); + glViewport(0, 0, w, h); +} + +void LLRenderTarget::resetDepthMipLevel() +{ + llassert(mFBO); + llassert(mDepth); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + LLTexUnit::getInternalType(mUsage), mDepth, 0); + glViewport(0, 0, mResX, mResY); + + GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3}; + glDrawBuffers((GLsizei)mTex.size(), drawbuffers); +} + +void LLRenderTarget::bindColorMipLevel(S32 level, U32 attachment) +{ + llassert(mFBO); + llassert(attachment < mTex.size()); + llassert(level < (S32)mMipLevels); + + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + sCurFBO = mFBO; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachment, + LLTexUnit::getInternalType(mUsage), mTex[attachment], level); + + S32 w = llmax(1, (S32)(mResX >> level)); + S32 h = llmax(1, (S32)(mResY >> level)); + glViewport(0, 0, w, h); +} + +void LLRenderTarget::resetColorMipLevel(U32 attachment) +{ + llassert(mFBO); + llassert(attachment < mTex.size()); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachment, + LLTexUnit::getInternalType(mUsage), mTex[attachment], 0); + glViewport(0, 0, mResX, mResY); +} + void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) { llassert(!isBoundInStack()); @@ -347,6 +442,21 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) } } +void LLRenderTarget::blitDepthFrom(LLRenderTarget& source) +{ + llassert(mFBO); + llassert(source.mFBO); + llassert(mDepth); // this target must own its depth buffer + llassert(source.mDepth); // source must have depth + + glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO); + glBlitFramebuffer(0, 0, source.mResX, source.mResY, + 0, 0, mResX, mResY, + GL_DEPTH_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_FRAMEBUFFER, sCurFBO); +} + void LLRenderTarget::release() { LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index cd3290cf663..affbb94aebf 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -116,6 +116,11 @@ class LLRenderTarget //share depth buffer with provided render target void shareDepthBuffer(LLRenderTarget& target); + //blit depth from source render target into this target's depth buffer + //supports different resolutions (nearest-filter downsample/upsample) + //this target must have its own depth buffer allocated + void blitDepthFrom(LLRenderTarget& source); + //free any allocated resources //safe to call redundantly // asserts that this target is not currently bound or present in the RT stack @@ -148,6 +153,21 @@ class LLRenderTarget U32 getNumTextures() const; U32 getDepth(void) const { return mDepth; } + U32 getMipLevels() const { return mMipLevels; } + + // Rebind FBO depth attachment at a specific mip level and set viewport. + // Used for Hi-Z pyramid generation. Caller must call resetDepthMipLevel() when done. + void bindDepthMipLevel(S32 level); + + // Restore FBO depth attachment to mip level 0 and full viewport. + void resetDepthMipLevel(); + + // Rebind FBO color attachment at a specific mip level and set viewport. + // Used for per-mip SSR filter. Caller must call resetColorMipLevel() when done. + void bindColorMipLevel(S32 level, U32 attachment = 0); + + // Restore FBO color attachment to mip level 0 and full viewport. + void resetColorMipLevel(U32 attachment = 0); void bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilterOptions filter_options = LLTexUnit::TFO_BILINEAR); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index b8545b3ed9c..6f2a0a50b0b 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -174,6 +174,14 @@ bool LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) } } + if (features->hasMotionBlur) + { + if (!shader->attachVertexObject("deferred/velocityFuncV.glsl")) + { + return false; + } + } + if (!shader->attachVertexObject("deferred/textureUtilV.glsl")) { return false; @@ -743,7 +751,7 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev } // Master definition can be found in deferredUtil.glsl - extra_code_text[extra_code_count++] = strdup("struct GBufferInfo { vec4 albedo; vec4 specular; vec3 normal; vec4 emissive; float gbufferFlag; float envIntensity; };\n"); + extra_code_text[extra_code_count++] = strdup("struct GBufferInfo { vec4 albedo; vec4 specular; vec3 normal; vec4 emissive; float gbufferFlag; float envIntensity; float occlusion; };\n"); //copy file into memory enum { @@ -1320,11 +1328,12 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("cloud_noise_texture_next"); mReservedUniforms.push_back("lightnorm"); mReservedUniforms.push_back("sunlight_color"); + mReservedUniforms.push_back("sun_intensity"); mReservedUniforms.push_back("ambient_color"); mReservedUniforms.push_back("sky_hdr_scale"); - mReservedUniforms.push_back("sky_sunlight_scale"); mReservedUniforms.push_back("sky_ambient_scale"); mReservedUniforms.push_back("classic_mode"); + mReservedUniforms.push_back("sun_lux"); mReservedUniforms.push_back("blue_horizon"); mReservedUniforms.push_back("blue_density"); mReservedUniforms.push_back("haze_horizon"); @@ -1395,16 +1404,18 @@ void LLShaderMgr::initAttribsAndUniforms() llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH + 1); mReservedUniforms.push_back("iterationCount"); - mReservedUniforms.push_back("rayStep"); - mReservedUniforms.push_back("distanceBias"); - mReservedUniforms.push_back("depthRejectBias"); + mReservedUniforms.push_back("maxThickness"); + mReservedUniforms.push_back("depthBias"); mReservedUniforms.push_back("glossySampleCount"); mReservedUniforms.push_back("noiseSine"); - mReservedUniforms.push_back("adaptiveStepMultiplier"); + mReservedUniforms.push_back("maxZDepth"); + mReservedUniforms.push_back("maxRoughness"); mReservedUniforms.push_back("modelview_delta"); mReservedUniforms.push_back("inv_modelview_delta"); mReservedUniforms.push_back("cube_snapshot"); + mReservedUniforms.push_back("default_probe_render"); + mReservedUniforms.push_back("reflection_probe_quality"); mReservedUniforms.push_back("tc_scale"); mReservedUniforms.push_back("rcp_screen_res"); @@ -1440,6 +1451,7 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("lightFunc"); mReservedUniforms.push_back("lightMap"); mReservedUniforms.push_back("bloomMap"); + mReservedUniforms.push_back("velocityMap"); mReservedUniforms.push_back("projectionMap"); mReservedUniforms.push_back("norm_mat"); @@ -1552,6 +1564,23 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("areaTex"); mReservedUniforms.push_back("searchTex"); mReservedUniforms.push_back("blendTex"); + mReservedUniforms.push_back("subsampleIndices"); + mReservedUniforms.push_back("currentColorTex"); + mReservedUniforms.push_back("previousColorTex"); + mReservedUniforms.push_back("velocityTex"); + + mReservedUniforms.push_back("current_modelview_matrix"); + mReservedUniforms.push_back("last_modelview_matrix"); + mReservedUniforms.push_back("last_modelview_matrix_inverse"); + mReservedUniforms.push_back("current_object_matrix"); + mReservedUniforms.push_back("last_object_matrix"); + mReservedUniforms.push_back("lastMatrixPalette"); + mReservedUniforms.push_back("motion_blur_strength"); + + mReservedUniforms.push_back("specularFactor"); + mReservedUniforms.push_back("specularColorFactor"); + mReservedUniforms.push_back("emissiveStrength"); + mReservedUniforms.push_back("ior"); llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 1b638e6e065..25a829aa5b1 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -119,11 +119,12 @@ class LLShaderMgr CLOUD_NOISE_MAP_NEXT, // "cloud_noise_texture_next" LIGHTNORM, // "lightnorm" SUNLIGHT_COLOR, // "sunlight_color" + SUN_INTENSITY, // "sun_intensity" AMBIENT, // "ambient_color" SKY_HDR_SCALE, // "sky_hdr_scale" - SKY_SUNLIGHT_SCALE, // "sky_sunlight_scale" SKY_AMBIENT_SCALE, // "sky_ambient_scale" CLASSIC_MODE, // "classic_mode" + SUN_LUX, // "sun_lux" BLUE_HORIZON, // "blue_horizon" BLUE_DENSITY, // "blue_density" HAZE_HORIZON, // "haze_horizon" @@ -183,16 +184,18 @@ class LLShaderMgr DEFERRED_SHADOW_TARGET_WIDTH, // "shadow_target_width" DEFERRED_SSR_ITR_COUNT, // "iterationCount" - DEFERRED_SSR_RAY_STEP, // "rayStep" - DEFERRED_SSR_DIST_BIAS, // "distanceBias" - DEFERRED_SSR_REJECT_BIAS, // "depthRejectBias" + DEFERRED_SSR_MAX_THICKNESS, // "maxThickness" + DEFERRED_SSR_DEPTH_BIAS, // "depthBias" DEFERRED_SSR_GLOSSY_SAMPLES, // "glossySampleCount" DEFERRED_SSR_NOISE_SINE, // "noiseSine" - DEFERRED_SSR_ADAPTIVE_STEP_MULT, // "adaptiveStepMultiplier" + DEFERRED_SSR_MAX_Z, // "maxZDepth" + DEFERRED_SSR_MAX_ROUGHNESS, // "maxRoughness" MODELVIEW_DELTA_MATRIX, // "modelview_delta" INVERSE_MODELVIEW_DELTA_MATRIX, // "inv_modelview_delta" CUBE_SNAPSHOT, // "cube_snapshot" + DEFAULT_PROBE_RENDER, // "default_probe_render" + REFLECTION_PROBE_QUALITY, // "reflection_probe_quality" FXAA_TC_SCALE, // "tc_scale" FXAA_RCP_SCREEN_RES, // "rcp_screen_res" @@ -225,6 +228,7 @@ class LLShaderMgr DEFERRED_LIGHTFUNC, // "lightFunc" DEFERRED_LIGHT, // "lightMap" DEFERRED_BLOOM, // "bloomMap" + DEFERRED_VELOCITY, // "velocityMap" DEFERRED_PROJECTION, // "projectionMap" DEFERRED_NORM_MATRIX, // "norm_mat" SPECULAR_COLOR, // "specular_color" @@ -340,6 +344,23 @@ class LLShaderMgr SMAA_AREA_TEX, // "areaTex" SMAA_SEARCH_TEX, // "searchTex" SMAA_BLEND_TEX, // "blendTex" + SMAA_SUBSAMPLE_INDICES, // "subsampleIndices" + SMAA_CURRENT_COLOR_TEX, // "currentColorTex" + SMAA_PREVIOUS_COLOR_TEX, // "previousColorTex" + SMAA_VELOCITY_TEX, // "velocityTex" + + CURRENT_MODELVIEW_MATRIX, // "current_modelview_matrix" + LAST_MODELVIEW_MATRIX, // "last_modelview_matrix" + LAST_MODELVIEW_MATRIX_INVERSE, // "last_modelview_matrix_inverse" + CURRENT_OBJECT_MATRIX, // "current_object_matrix" + LAST_OBJECT_MATRIX, // "last_object_matrix" + AVATAR_LAST_MATRIX, // "lastMatrixPalette" + MOTION_BLUR_STRENGTH, // "motion_blur_strength" + + SPECULAR_FACTOR, // "specularFactor" (KHR_materials_specular) + SPECULAR_COLOR_FACTOR, // "specularColorFactor" (KHR_materials_specular) + EMISSIVE_STRENGTH, // "emissiveStrength" (KHR_materials_emissive_strength) + IOR, // "ior" (KHR_materials_ior) END_RESERVED_UNIFORMS } eGLSLReservedUniforms; diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index 34643d5f5c1..5a37167f181 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -37,6 +37,7 @@ #include "llstring.h" #include "v3math.h" #include "v3dmath.h" +#include "v4math.h" #include "v4coloru.h" #include "v4color.h" #include "v3color.h" @@ -63,6 +64,7 @@ template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); +template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); @@ -72,6 +74,7 @@ template <> eControlType get_control_type(); template <> LLSD convert_to_llsd(const U32& in); template <> LLSD convert_to_llsd(const LLVector3& in); template <> LLSD convert_to_llsd(const LLVector3d& in); +template <> LLSD convert_to_llsd(const LLVector4& in); template <> LLSD convert_to_llsd(const LLRect& in); template <> LLSD convert_to_llsd(const LLColor4& in); template <> LLSD convert_to_llsd(const LLColor3& in); @@ -85,6 +88,7 @@ template <> std::string convert_from_llsd(const LLSD& sd, eControlT template <> LLWString convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLVector3 convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLVector3d convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); +template <> LLVector4 convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLRect convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLColor4 convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template <> LLColor4U convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); @@ -124,6 +128,9 @@ bool LLControlVariable::llsd_compare(const LLSD& a, const LLSD & b) case TYPE_VEC3D: result = LLVector3d(a) == LLVector3d(b); break; + case TYPE_VEC4: + result = LLVector4(a) == LLVector4(b); + break; case TYPE_QUAT: result = LLQuaternion(a) == LLQuaternion(b); break; @@ -373,6 +380,7 @@ const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32" ,"Rect" ,"Color4" ,"Color3" + ,"Vector4" ,"LLSD" }; @@ -532,6 +540,11 @@ LLControlVariable* LLControlGroup::declareVec3d(const std::string& name, const L return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist); } +LLControlVariable* LLControlGroup::declareVec4(const std::string& name, const LLVector4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist) +{ + return declareControl(name, TYPE_VEC4, initial_val.getValue(), comment, persist); +} + LLControlVariable* LLControlGroup::declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist) { return declareControl(name, TYPE_QUAT, initial_val.getValue(), comment, persist); @@ -614,6 +627,11 @@ LLVector3d LLControlGroup::getVector3d(std::string_view name) return get(name); } +LLVector4 LLControlGroup::getVector4(std::string_view name) +{ + return get(name); +} + LLQuaternion LLControlGroup::getQuaternion(std::string_view name) { return get(name); @@ -714,6 +732,11 @@ void LLControlGroup::setVector3d(std::string_view name, const LLVector3d &val) set(name, val); } +void LLControlGroup::setVector4(std::string_view name, const LLVector4 &val) +{ + set(name, val); +} + void LLControlGroup::setQuaternion(std::string_view name, const LLQuaternion &val) { set(name, val); @@ -1262,6 +1285,11 @@ template <> eControlType get_control_type() return TYPE_VEC3D; } +template <> eControlType get_control_type() +{ + return TYPE_VEC4; +} + template <> eControlType get_control_type() { return TYPE_QUAT; @@ -1302,6 +1330,11 @@ template <> LLSD convert_to_llsd(const LLVector3d& in) { return in.getValue(); } +template <> LLSD convert_to_llsd(const LLVector4& in) +{ + return in.getValue(); +} + template <> LLSD convert_to_llsd(const LLQuaternion& in) { return in.getValue(); @@ -1418,6 +1451,18 @@ LLVector3d convert_from_llsd(const LLSD& sd, eControlType type, std: } } +template<> +LLVector4 convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name) +{ + if (type == TYPE_VEC4) + return LLVector4(sd); + else + { + CONTROL_ERRS << "Invalid LLVector4 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL; + return LLVector4(); + } +} + template<> LLQuaternion convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name) { diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 5aa2b9715ed..c2bcd20c85f 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -43,6 +43,7 @@ class LLVector3; class LLVector3d; +class LLVector4; class LLQuaternion; class LLColor4; class LLColor3; @@ -61,6 +62,7 @@ typedef enum e_control_type TYPE_RECT, TYPE_COL4, TYPE_COL3, + TYPE_VEC4, TYPE_LLSD, TYPE_COUNT } eControlType; @@ -196,6 +198,7 @@ class LLControlGroup : public LLInstanceTracker LLControlVariable* declareString(const std::string& name, const std::string &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); LLControlVariable* declareVec3(const std::string& name, const LLVector3 &initial_val,const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); LLControlVariable* declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); + LLControlVariable* declareVec4(const std::string& name, const LLVector4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); LLControlVariable* declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); LLControlVariable* declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); LLControlVariable* declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT); @@ -212,6 +215,7 @@ class LLControlGroup : public LLInstanceTracker LLWString getWString(std::string_view name); LLVector3 getVector3(std::string_view name); LLVector3d getVector3d(std::string_view name); + LLVector4 getVector4(std::string_view name); LLRect getRect(std::string_view name); LLSD getLLSD(std::string_view name); LLQuaternion getQuaternion(std::string_view name); @@ -250,6 +254,7 @@ class LLControlGroup : public LLInstanceTracker void setString(std::string_view name, const std::string& val); void setVector3(std::string_view name, const LLVector3 &val); void setVector3d(std::string_view name, const LLVector3d &val); + void setVector4(std::string_view name, const LLVector4 &val); void setQuaternion(std::string_view name, const LLQuaternion &val); void setRect(std::string_view name, const LLRect &val); void setColor4(std::string_view name, const LLColor4 &val); @@ -416,6 +421,7 @@ template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); +template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); template <> eControlType get_control_type(); @@ -425,6 +431,7 @@ template <> eControlType get_control_type(); template <> LLSD convert_to_llsd(const U32& in); template <> LLSD convert_to_llsd(const LLVector3& in); template <> LLSD convert_to_llsd(const LLVector3d& in); +template <> LLSD convert_to_llsd(const LLVector4& in); template <> LLSD convert_to_llsd(const LLQuaternion& in); template <> LLSD convert_to_llsd(const LLRect& in); template <> LLSD convert_to_llsd(const LLColor4& in); @@ -434,6 +441,7 @@ template<> std::string convert_from_llsd(const LLSD& sd, eControlTy template<> LLWString convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template<> LLVector3 convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template<> LLVector3d convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); +template<> LLVector4 convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template<> LLQuaternion convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template<> LLRect convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); template<> bool convert_from_llsd(const LLSD& sd, eControlType type, std::string_view control_name); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 1d1553355ef..67aae310622 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -81,11 +81,6 @@ endif () set(viewer_SOURCE_FILES gltfscenemanager.cpp - gltf/asset.cpp - gltf/accessor.cpp - gltf/primitive.cpp - gltf/animation.cpp - gltf/llgltfloader.cpp groupchatlistener.cpp llaccountingcostmanager.cpp llaisapi.cpp @@ -611,7 +606,7 @@ set(viewer_SOURCE_FILES lltexturestats.cpp lltextureview.cpp llthumbnailctrl.cpp - lltinygltfhelper.cpp + llgltfhelper.cpp lltoast.cpp lltoastalertpanel.cpp lltoastgroupnotifypanel.cpp @@ -759,12 +754,6 @@ set(viewer_HEADER_FILES ViewerInstall.cmake gltfscenemanager.h groupchatlistener.h - gltf/asset.h - gltf/accessor.h - gltf/buffer_util.h - gltf/primitive.h - gltf/animation.h - gltf/llgltfloader.h llaccountingcost.h llaccountingcostmanager.h llaisapi.h @@ -1283,7 +1272,7 @@ set(viewer_HEADER_FILES lltexturestats.h lltextureview.h llthumbnailctrl.h - lltinygltfhelper.h + llgltfhelper.h lltoast.h lltoastalertpanel.h lltoastgroupnotifypanel.h @@ -2418,6 +2407,13 @@ if (LL_TESTS) LL_TEST_ADDITIONAL_LIBRARIES ${test_libs} ) + set_property( SOURCE + llworldmap.cpp + llworldmipmap.cpp + APPEND PROPERTY + LL_TEST_ADDITIONAL_LIBRARIES llprimitive + ) + LL_ADD_PROJECT_UNIT_TESTS(${VIEWER_BINARY_NAME} "${viewer_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a4a93238926..bcf4a24af7d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4402,6 +4402,17 @@ Value 1 + LocalGLTFMaterialOverrides + + Comment + Apply GLTF material extension overrides locally (for extensions the server doesn't yet support) + Persist + 1 + Type + Boolean + Value + 0 + LoginContentVersion Comment @@ -7486,7 +7497,7 @@ RenderBufferVisualization Comment - Outputs a selected buffer to the screen. -1 = final render buffer. 0 = Albedo, 1 = Specular/ORM, 2 = Normal, 3 = Emissive, 4 = Eye luminance, 5 = FXAA Luminance/SMAA Edge Tex, 6 = SMAA Blend Weights + Outputs a selected buffer to the screen. -1 = final render buffer. 0 = Albedo, 1 = Specular/ORM, 2 = Normal, 3 = Emissive, 4 = Eye luminance, 5 = FXAA Luminance/SMAA Edge Tex, 6 = SMAA Blend Weights, 7 = Velocity Persist 0 Type @@ -7564,7 +7575,7 @@ Vector3 Value - 3.0 + 1.5 3.0 2.0 @@ -7683,6 +7694,17 @@ 0.00 + RenderMirrorCount + + Comment + Number of mirror probes. Includes system water probe. Min 1. + Persist + 1 + Type + S32 + Value + 2 + RenderMirrors Comment @@ -7694,6 +7716,28 @@ Value 0 + RenderMotionBlur + + Comment + Enable per-object motion blur velocity buffer generation. + Persist + 1 + Type + Boolean + Value + 0 + + RenderMotionBlurStrength + + Comment + Maximum motion blur length in pixels (higher = stronger blur) + Persist + 1 + Type + S32 + Value + 32 + RenderScreenSpaceReflections Comment @@ -7708,68 +7752,90 @@ RenderScreenSpaceReflectionIterations Comment - Number of times the ray march algorithm runs to find a potential hit. + Maximum ray march iterations for Hi-Z SSR trace. Persist 1 Type S32 Value - 25 + 48 - RenderScreenSpaceReflectionRayStep + RenderScreenSpaceReflectionMaxThickness Comment - How big the step is between each run. + Maximum thickness for SSR hit validation (view-space units). Persist 1 Type F32 Value - 0.1 + 1.0 + + RenderScreenSpaceReflectionDepthBias + + Comment + Depth bias scale for SSR ray origin offset along surface normal. + Persist + 1 + Type + F32 + Value + 0.0 + + RenderScreenSpaceReflectionGlossySamples + + Comment + Maximum number of samples to apply for glossy SSR. + Persist + 1 + Type + S32 + Value + 2 - RenderScreenSpaceReflectionDistanceBias + RenderScreenSpaceReflectionMaxDepth Comment - Distance bias to apply when rejecting a potential sample. + Maximum depth from the camera to attempt a trace. Persist 1 Type F32 Value - 0.015 + 256 - RenderScreenSpaceReflectionDepthRejectBias + RenderScreenSpaceReflectionMaxRoughness Comment - Bias against the depth buffer before rejecting a sample. + Maximum permitted roughness for screen space reflections. Persist 1 Type F32 Value - 0.001 + 1 - RenderScreenSpaceReflectionAdaptiveStepMultiplier + RenderScreenSpaceReflectionsFilterScale Comment - Multiplier to scale adaptive stepping. + Blur size for the SSR spatial filter (0.0 to disable, default 2.0). Persist 1 Type F32 Value - 1.6 + 2.0 - RenderScreenSpaceReflectionGlossySamples + RenderScreenSpaceReflectionsResolutionMultiplier Comment - Maximum number of samples to apply for glossy SSR. + Resolution multiplier for the SSR buffer (0.25 to 1.0). Persist 1 Type - S32 + F32 Value - 4 + 1.0 RenderBumpmapMinDistanceSquared @@ -9251,6 +9317,17 @@ Value 1 + RenderReflectionProbeQuality + + Comment + Quality of reflection probe rendering. (0 - Low quality RGB only, 1 - Medium quality with alpha preservation for sky blending) + Persist + 1 + Type + S32 + Value + 1 + RenderReflectionProbeDynamicAllocation Comment @@ -9327,7 +9404,7 @@ Type F32 Value - 2.0 + 0.5 RenderReflectionProbeLevel @@ -9395,6 +9472,17 @@ Value 0 + RenderSkyVersionOverride + + Comment + Override sky setting version for testing. 0 = use version from sky settings, 1 = force legacy (v1), 2+ = force that version. + Persist + 1 + Type + S32 + Value + 0 + RenderSkyAutoAdjustAmbientScale Comment @@ -10161,6 +10249,17 @@ Value 1 + RenderHDRUseEnvironmentSettings + + Comment + When enabled, HDR settings (tonemapper, tonemap mix, HDR min/max/offset) are driven by environment settings instead of debug settings. + Persist + 1 + Type + Boolean + Value + 0 + ReplaySession Comment @@ -11805,6 +11904,22 @@ Value 20.0 + TextureChannelPriority + + Comment + Per-channel texture streaming aggressiveness. X=normals, Y=diffuse, Z=specular/metallic, W=emissive. 1.0=baseline, higher=more aggressive downrez. + Persist + 1 + Type + Vector4 + Value + + 5 + 7.5 + 20 + 7.5 + + TextureCameraBoost Comment diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl index 67f84ecaeca..241531f484d 100644 --- a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl +++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl @@ -25,6 +25,7 @@ in vec4 weight4; uniform mat3x4 matrixPalette[MAX_JOINTS_PER_MESH_OBJECT]; +uniform mat3x4 lastMatrixPalette[MAX_JOINTS_PER_MESH_OBJECT]; mat4 getObjectSkinnedTransform() { @@ -70,3 +71,37 @@ mat4 getObjectSkinnedTransform() } +mat4 getLastObjectSkinnedTransform() +{ + vec4 w = fract(weight4); + vec4 index = floor(weight4); + + index = min(index, vec4(MAX_JOINTS_PER_MESH_OBJECT-1)); + index = max(index, vec4(0.0)); + + w *= 1.0/(w.x+w.y+w.z+w.w); + + int i1 = int(index.x); + int i2 = int(index.y); + int i3 = int(index.z); + int i4 = int(index.w); + + mat3 mat = mat3(lastMatrixPalette[i1])*w.x; + mat += mat3(lastMatrixPalette[i2])*w.y; + mat += mat3(lastMatrixPalette[i3])*w.z; + mat += mat3(lastMatrixPalette[i4])*w.w; + + vec3 trans = vec3(lastMatrixPalette[i1][0].w, lastMatrixPalette[i1][1].w, lastMatrixPalette[i1][2].w)*w.x; + trans += vec3(lastMatrixPalette[i2][0].w, lastMatrixPalette[i2][1].w, lastMatrixPalette[i2][2].w)*w.y; + trans += vec3(lastMatrixPalette[i3][0].w, lastMatrixPalette[i3][1].w, lastMatrixPalette[i3][2].w)*w.z; + trans += vec3(lastMatrixPalette[i4][0].w, lastMatrixPalette[i4][1].w, lastMatrixPalette[i4][2].w)*w.w; + + mat4 ret; + ret[0] = vec4(mat[0], 0); + ret[1] = vec4(mat[1], 0); + ret[2] = vec4(mat[2], 0); + ret[3] = vec4(trans, 1.0); + + return ret; +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/SMAA.glsl b/indra/newview/app_settings/shaders/class1/deferred/SMAA.glsl index 58373089653..cca49d1e382 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/SMAA.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/SMAA.glsl @@ -550,7 +550,7 @@ uniform vec4 SMAA_RT_METRICS; #endif #ifndef SMAA_DECODE_VELOCITY -#define SMAA_DECODE_VELOCITY(sample) sample.rg +#define SMAA_DECODE_VELOCITY(sample) (sample.rg * 0.5) #endif //----------------------------------------------------------------------------- diff --git a/indra/newview/app_settings/shaders/class1/deferred/SMAABlendWeightsF.glsl b/indra/newview/app_settings/shaders/class1/deferred/SMAABlendWeightsF.glsl index 3332c5f58ff..5f2c5258205 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/SMAABlendWeightsF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/SMAABlendWeightsF.glsl @@ -34,6 +34,7 @@ in vec4 vary_offset[3]; uniform sampler2D edgesTex; uniform sampler2D areaTex; uniform sampler2D searchTex; +uniform vec4 subsampleIndices; vec4 SMAABlendingWeightCalculationPS(vec2 texcoord, vec2 pixcoord, @@ -51,7 +52,7 @@ void main() edgesTex, areaTex, searchTex, - vec4(0.0) + subsampleIndices ); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/SMAAResolveF.glsl b/indra/newview/app_settings/shaders/class1/deferred/SMAAResolveF.glsl new file mode 100644 index 00000000000..da6fbed0cd5 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/SMAAResolveF.glsl @@ -0,0 +1,59 @@ +/** + * @file SMAAResolveF.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +in vec2 vary_texcoord0; + +uniform sampler2D currentColorTex; +uniform sampler2D previousColorTex; +#if SMAA_REPROJECTION +uniform sampler2D velocityTex; +#endif + +#define float4 vec4 +#define float2 vec2 +#define SMAATexture2D(tex) sampler2D tex + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ); + +void main() +{ + frag_color = SMAAResolvePS(vary_texcoord0, + currentColorTex, + previousColorTex + #if SMAA_REPROJECTION + , velocityTex + #endif + ); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/SMAAResolveV.glsl b/indra/newview/app_settings/shaders/class1/deferred/SMAAResolveV.glsl new file mode 100644 index 00000000000..2b04775b712 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/SMAAResolveV.glsl @@ -0,0 +1,34 @@ +/** + * @file SMAAResolveV.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +in vec3 position; + +out vec2 vary_texcoord0; + +void main() +{ + gl_Position = vec4(position.xyz, 1.0); + vary_texcoord0 = (gl_Position.xy * 0.5 + 0.5); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaMaskShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaMaskShadowF.glsl index f1e0295859d..5b34f58723e 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaMaskShadowF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaMaskShadowF.glsl @@ -32,22 +32,13 @@ in float target_pos_x; in float pos_w; in vec2 vary_texcoord0; +void bayerDitherDiscard(float alpha, float threshold); + void main() { float alpha = texture(diffuseMap, vary_texcoord0.xy).a; - if (alpha < 0.05) // treat as totally transparent - { - discard; - } - - if (alpha < minimum_alpha) - { - if (fract(0.5*floor(target_pos_x / pos_w )) < 0.25) - { - discard; - } - } + bayerDitherDiscard(alpha, minimum_alpha); frag_color = vec4(1,1,1,1); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaShadowF.glsl index 18ce998cb68..8b1e06e9c49 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaShadowF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaShadowF.glsl @@ -34,22 +34,13 @@ in float target_pos_x; in vec2 vary_texcoord0; uniform vec4 color; +void bayerDitherDiscard(float alpha, float threshold); + void main() { float alpha = texture(diffuseMap, vary_texcoord0.xy).a * color.a; - if (alpha < 0.05) // treat as totally transparent - { - discard; - } - - if (alpha < minimum_alpha) // treat as semi-transparent - { - if (fract(0.5*floor(target_pos_x / pos_w )) < 0.25) - { - discard; - } - } + bayerDitherDiscard(alpha, minimum_alpha); frag_color = vec4(1,1,1,1); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarVelocityF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarVelocityF.glsl new file mode 100644 index 00000000000..2986861f5f9 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarVelocityF.glsl @@ -0,0 +1,48 @@ +/** + * @file avatarVelocityF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +uniform sampler2D diffuseMap; + +in vec4 vary_cur_clip; +in vec4 vary_last_clip; +in vec2 vary_texcoord0; + +void main() +{ + float alpha = texture(diffuseMap, vary_texcoord0.xy).a; + if (alpha < 0.2) + { + discard; + } + + vec2 cur_ndc = vary_cur_clip.xy / vary_cur_clip.w; + vec2 last_ndc = vary_last_clip.xy / vary_last_clip.w; + + frag_color = vec4(cur_ndc - last_ndc, 0.0, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarVelocityV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarVelocityV.glsl new file mode 100644 index 00000000000..f570f955a85 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarVelocityV.glsl @@ -0,0 +1,68 @@ +/** + * @file avatarVelocityV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 projection_matrix; +uniform vec4 lastMatrixPalette[45]; + +in vec3 position; +in vec4 weight; +in vec2 texcoord0; + +out vec2 vary_texcoord0; + +mat4 getSkinnedTransform(); + +void writeVaryVelocity(vec4 pos, vec4 last_pos); + +mat4 getLastSkinnedTransform() +{ + mat4 ret; + int i = int(floor(weight.x)); + float x = fract(weight.x); + + ret[0] = mix(lastMatrixPalette[i+0], lastMatrixPalette[i+1], x); + ret[1] = mix(lastMatrixPalette[i+15], lastMatrixPalette[i+16], x); + ret[2] = mix(lastMatrixPalette[i+30], lastMatrixPalette[i+31], x); + ret[3] = vec4(0,0,0,1); + + return ret; +} + +void main() +{ + vec4 pos = vec4(position.xyz, 1.0); + + mat4 current_skin = getSkinnedTransform(); + vec4 current_clip = projection_matrix * (current_skin * pos); + + mat4 last_skin = getLastSkinnedTransform(); + vec4 last_clip = projection_matrix * (last_skin * pos); + + gl_Position = current_clip; + + writeVaryVelocity(current_clip, last_clip); + + vary_texcoord0 = texcoord0; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl index 23a3ca4911a..25fe044135d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl @@ -51,8 +51,14 @@ void main() vec2 dlt = kern_scale * delta / (1.0+norm.xy*norm.xy); dlt /= max(-pos.z*dist_factor, 1.0); - vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free' - vec4 col = defined_weight.xyxx * ccol; + // Only blur SSAO (G channel), pass through shadows (R, B, A channels) + // Initialize: R with no blur, G with blur weight, B and A with no blur + float defined_weight = kern[0].x; // weight for SSAO blur only + vec4 col; + col.r = ccol.r; // directional shadow - no blur + col.g = kern[0].x * ccol.g; // SSAO - apply blur + col.b = ccol.b; // spot shadow 0 - no blur + col.a = ccol.a; // spot shadow 1 - no blur // relax tolerance according to distance to avoid speckling artifacts, as angles and distances are a lot more abrupt within a small screen area at larger distances float pointplanedist_tolerance_pow2 = pos.z*pos.z*0.00005; @@ -85,8 +91,9 @@ void main() if (d*d <= pointplanedist_tolerance_pow2) { - col += texture(lightMap, samptc)*k[i].xyxx; - defined_weight += k[i].xy; + vec4 sampcol = texture(lightMap, samptc); + col.g += sampcol.g * k[i].x; // only blur SSAO + defined_weight += k[i].x; } } @@ -100,12 +107,13 @@ void main() if (d*d <= pointplanedist_tolerance_pow2) { - col += texture(lightMap, samptc)*k[i].xyxx; - defined_weight += k[i].xy; + vec4 sampcol = texture(lightMap, samptc); + col.g += sampcol.g * k[i].x; // only blur SSAO + defined_weight += k[i].x; } } - col /= defined_weight.xyxx; + col.g /= defined_weight; //col.y *= col.y; frag_color = max(col, vec4(0)); diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl index dba9c46332f..12109757737 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl @@ -379,6 +379,7 @@ void pbrIbl(vec3 diffuseColor, float ao, // ambient occlusion factor float nv, // normal dot view vector float perceptualRough, + float specularWeight, out vec3 diffuseOut, out vec3 specularOut) { @@ -388,7 +389,7 @@ void pbrIbl(vec3 diffuseColor, vec3 specularLight = radiance; vec3 diffuse = diffuseLight * diffuseColor; - vec3 specular = specularLight * (specularColor * brdf.x + brdf.y); + vec3 specular = specularLight * (specularColor * brdf.x + brdf.y * specularWeight); diffuseOut = diffuse * ao; specularOut = specular * ao; @@ -406,7 +407,6 @@ struct PBRInfo float LdotH; // cos angle between light direction and half vector float VdotH; // cos angle between view direction and half vector float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) - float metalness; // metallic value at the surface vec3 reflectance0; // full reflectance color (normal incidence angle) vec3 reflectance90; // reflectance color at grazing angle float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) @@ -456,7 +456,7 @@ float microfacetDistribution(PBRInfo pbrInputs) void pbrPunctual(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 v, // surface point to camera vec3 l, @@ -474,7 +474,7 @@ void pbrPunctual(vec3 diffuseColor, vec3 specularColor, // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. - float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); + float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0) * specularWeight; vec3 specularEnvironmentR0 = specularColor.rgb; vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90; @@ -495,7 +495,6 @@ void pbrPunctual(vec3 diffuseColor, vec3 specularColor, LdotH, VdotH, perceptualRoughness, - metallic, specularEnvironmentR0, specularEnvironmentR90, alphaRoughness, @@ -520,7 +519,7 @@ void pbrPunctual(vec3 diffuseColor, vec3 specularColor, vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 p, // pixel position vec3 v, // view vector (negative normalized pixel position) @@ -553,7 +552,7 @@ vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, vec3 diffPunc = vec3(0); vec3 specPunc = vec3(0); - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv, nl, diffPunc, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, specularWeight, n.xyz, v, lv, nl, diffPunc, specPunc); color = intensity * clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)); } float final_scale = 1.0; @@ -562,22 +561,24 @@ vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, return color * final_scale; } -void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor) +void calcDiffuseSpecular(vec3 baseColor, float metallic, float specularFactor, vec3 specularColorFactor, float ior, inout vec3 diffuseColor, inout vec3 specularColor, inout float specularWeight) { - vec3 f0 = vec3(0.04); - diffuseColor = baseColor*(vec3(1.0)-f0); - diffuseColor *= 1.0 - metallic; - specularColor = mix(f0, baseColor, metallic); + float ior_f0 = pow((ior - 1.0) / (ior + 1.0), 2.0); + vec3 dielectric_f0 = min(vec3(ior_f0) * specularColorFactor, vec3(1.0)) * specularFactor; + float dielectric_f90 = specularFactor; + specularColor = mix(dielectric_f0, baseColor, metallic); + diffuseColor = mix(baseColor * (1.0 - max(dielectric_f0.r, max(dielectric_f0.g, dielectric_f0.b))), vec3(0.0), metallic); + specularWeight = mix(dielectric_f90, 1.0, metallic); } -vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 v, vec3 norm, float perceptualRoughness, vec3 light_dir, vec3 sunlit, float scol, vec3 radiance, vec3 irradiance, vec3 colorEmissive, float ao, vec3 additive, vec3 atten) +vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float specularWeight, vec3 v, vec3 norm, float perceptualRoughness, vec3 light_dir, vec3 sunlit, float scol, vec3 radiance, vec3 irradiance, vec3 colorEmissive, float ao, vec3 additive, vec3 atten) { vec3 color = vec3(0); float NdotV = clamp(abs(dot(norm, v)), 0.001, 1.0); vec3 iblDiff = vec3(0); vec3 iblSpec = vec3(0); - pbrIbl(diffuseColor, specularColor, radiance, irradiance, ao, NdotV, perceptualRoughness, iblDiff, iblSpec); + pbrIbl(diffuseColor, specularColor, radiance, irradiance, ao, NdotV, perceptualRoughness, specularWeight, iblDiff, iblSpec); color += iblDiff; @@ -585,7 +586,7 @@ vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 v, float nl = 0; vec3 diffPunc = vec3(0); vec3 specPunc = vec3(0); - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, norm, v, normalize(light_dir), nl, diffPunc, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, specularWeight, norm, v, normalize(light_dir), nl, diffPunc, specPunc); // Depending on the sky, we combine these differently. if (classic_mode > 0) @@ -612,7 +613,7 @@ vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 v, } else { - color += clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit * 3.0 * scol; + color += clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit * scol; } color.rgb += iblSpec.rgb; diff --git a/indra/newview/app_settings/shaders/class1/deferred/gbufferUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/gbufferUtil.glsl index 1ccc6e9fbc7..4e623a434c5 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/gbufferUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/gbufferUtil.glsl @@ -52,6 +52,7 @@ GBufferInfo getGBuffer(vec2 screenpos) ret.normal = decodeNormal(normInfo).xyz; ret.specular = specInfo; ret.envIntensity = normInfo.b; + ret.occlusion = normInfo.b; ret.gbufferFlag = normInfo.w; ret.emissive = emissInfo; diff --git a/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl b/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl index 2ed4ba3163e..980e6d487ee 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl @@ -59,3 +59,32 @@ vec4 decodeNormal(vec4 norm) n.z = 1-f/2; return n; } + +// 4x4 Bayer dither matrix normalized to [0, 1] +const float BAYER_PATTERN[16] = float[16]( + 0.0/16.0, 8.0/16.0, 2.0/16.0, 10.0/16.0, + 12.0/16.0, 4.0/16.0, 14.0/16.0, 6.0/16.0, + 3.0/16.0, 11.0/16.0, 1.0/16.0, 9.0/16.0, + 15.0/16.0, 7.0/16.0, 13.0/16.0, 5.0/16.0 +); + +// Discard fragment based on dithered alpha threshold. +// Fragments with alpha < 0.05 are always discarded. +// Fragments with alpha >= threshold are always kept. +// Fragments in between are dithered using a 4x4 Bayer pattern. +void bayerDitherDiscard(float alpha, float threshold) +{ + if (alpha < 0.05) + { + discard; + } + + if (alpha < threshold) + { + ivec2 ipos = ivec2(mod(gl_FragCoord.xy, 4.0)); + if (alpha < BAYER_PATTERN[ipos.y * 4 + ipos.x]) + { + discard; + } + } +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/hiZReduceF.glsl b/indra/newview/app_settings/shaders/class1/deferred/hiZReduceF.glsl new file mode 100644 index 00000000000..4bfdb07dd1f --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/hiZReduceF.glsl @@ -0,0 +1,66 @@ +/** + * @file class1/deferred/hiZReduceF.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Min-reduce downsample for Hi-Z pyramid generation. +// Reads 2x2 block from source mip level, outputs min to gl_FragDepth. +// Handles odd source dimensions by sampling the extra column/row, +// matching Godot's MODE_ODD_WIDTH / MODE_ODD_HEIGHT behavior. + +uniform sampler2D depthMap; +uniform int srcLevel; + +void main() +{ + ivec2 destCoord = ivec2(gl_FragCoord.xy); + ivec2 srcCoord = destCoord * 2; + ivec2 srcSize = textureSize(depthMap, srcLevel); + + float d0 = texelFetch(depthMap, srcCoord, srcLevel).r; + float d1 = texelFetch(depthMap, min(srcCoord + ivec2(1, 0), srcSize - 1), srcLevel).r; + float d2 = texelFetch(depthMap, min(srcCoord + ivec2(0, 1), srcSize - 1), srcLevel).r; + float d3 = texelFetch(depthMap, min(srcCoord + ivec2(1, 1), srcSize - 1), srcLevel).r; + + float depth = min(min(d0, d1), min(d2, d3)); + + // When the source has an odd dimension, the last dest texel's 2x2 block + // doesn't cover the final row/column. Sample the extra texels so the + // Hi-Z guarantee (min of all covered texels) is maintained. + if ((srcSize.x & 1) == 1) + { + depth = min(depth, texelFetch(depthMap, min(srcCoord + ivec2(2, 0), srcSize - 1), srcLevel).r); + depth = min(depth, texelFetch(depthMap, min(srcCoord + ivec2(2, 1), srcSize - 1), srcLevel).r); + } + if ((srcSize.y & 1) == 1) + { + depth = min(depth, texelFetch(depthMap, min(srcCoord + ivec2(0, 2), srcSize - 1), srcLevel).r); + depth = min(depth, texelFetch(depthMap, min(srcCoord + ivec2(1, 2), srcSize - 1), srcLevel).r); + } + if ((srcSize.x & 1) == 1 && (srcSize.y & 1) == 1) + { + depth = min(depth, texelFetch(depthMap, min(srcCoord + ivec2(2, 2), srcSize - 1), srcLevel).r); + } + + gl_FragDepth = depth; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/motionBlurF.glsl b/indra/newview/app_settings/shaders/class1/deferred/motionBlurF.glsl new file mode 100644 index 00000000000..c85d7ed22c6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/motionBlurF.glsl @@ -0,0 +1,79 @@ +/** + * @file motionBlurF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +uniform sampler2D diffuseRect; +uniform sampler2D velocityMap; +uniform vec2 screen_res; +uniform int motion_blur_strength; + +in vec2 vary_fragcoord; + +void main() +{ + vec2 uv = vary_fragcoord; + vec2 vel = texture(velocityMap, uv).rg; + + // NDC velocity to pixel velocity + vec2 pixel_vel = vel * screen_res * 0.5; + float speed = length(pixel_vel); + + // Early out for negligible motion + if (speed < 0.5) + { + frag_color = texture(diffuseRect, uv); + return; + } + + // Clamp to max blur length + float max_blur = float(motion_blur_strength); + if (speed > max_blur) + { + pixel_vel *= max_blur / speed; + } + + // Step size in UV space per iteration + vec2 step_uv = (pixel_vel / screen_res) * (2.0 / 32.0); + + // Start sampling ahead of center + vec2 sample_uv = uv + step_uv * 16.0; + + // 32-sample triangle-weighted blur + vec3 color = vec3(0.0); + float total = 0.0; + + for (int i = 0; i < 32; ++i) + { + float w = 32.0 - abs(float(i) - 16.0); + total += w; + color += texture(diffuseRect, sample_uv).rgb * w; + sample_uv -= step_uv; + } + + frag_color = vec4(color / total, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaBlendF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaBlendF.glsl index dbaab9bbda3..033dcce208a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaBlendF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaBlendF.glsl @@ -33,24 +33,14 @@ in vec4 vertex_color; in vec2 vary_texcoord0; uniform float minimum_alpha; +void bayerDitherDiscard(float alpha, float threshold); + void main() { - float alpha = texture(diffuseMap,vary_texcoord0.xy).a; - + float alpha = texture(diffuseMap, vary_texcoord0.xy).a; alpha *= vertex_color.a; - if (alpha < 0.05) // treat as totally transparent - { - discard; - } - - if (alpha < 0.88) // treat as semi-transparent - { - if (fract(0.5*floor(target_pos_x / post_pos.w )) < 0.25) - { - discard; - } - } + bayerDitherDiscard(alpha, 0.88); frag_color = vec4(1,1,1,1); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl index abe61fe8925..264910b8bca 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl @@ -35,6 +35,10 @@ uniform sampler2D diffuseMap; //always in sRGB space uniform float metallicFactor; uniform float roughnessFactor; uniform vec3 emissiveColor; +uniform float specularFactor; +uniform vec3 specularColorFactor; +uniform float emissiveStrength; +uniform float ior; uniform sampler2D bumpMap; uniform sampler2D emissiveMap; uniform sampler2D specularMap; // Packed: Occlusion, Metal, Roughness @@ -97,27 +101,32 @@ void main() // metal 0.0 vec3 spec = texture(specularMap, metallic_roughness_texcoord.xy).rgb; - spec.g *= roughnessFactor; - spec.b *= metallicFactor; + float perceptualRoughness = spec.g * roughnessFactor; + float metallic = spec.b * metallicFactor; + float ao = spec.r; - vec3 emissive = emissiveColor; + // KHR_materials_ior + KHR_materials_specular + float ior_f0 = pow((ior - 1.0) / (ior + 1.0), 2.0); + vec3 dielectric_f0 = min(vec3(ior_f0) * specularColorFactor, vec3(1.0)) * specularFactor; + float dielectric_f90 = specularFactor; + + vec3 f0 = mix(dielectric_f0, col, metallic); + vec3 diffuse = mix(col * (1.0 - max(dielectric_f0.r, max(dielectric_f0.g, dielectric_f0.b))), vec3(0.0), metallic); + float specWeight = mix(dielectric_f90, 1.0, metallic); + + // KHR_materials_emissive_strength + vec3 emissive = emissiveColor * emissiveStrength; emissive *= srgb_to_linear(texture(emissiveMap, emissive_texcoord.xy).rgb); tnorm *= gl_FrontFacing ? 1.0 : -1.0; - //spec.rgb = vec3(1,1,0); - //col = vec3(0,0,0); - //emissive = vary_tangent.xyz*0.5+0.5; - //emissive = vec3(sign*0.5+0.5); - //emissive = vNt * 0.5 + 0.5; - //emissive = tnorm*0.5+0.5; // See: C++: addDeferredAttachments(), GLSL: softenLightF - frag_data[0] = max(vec4(col, 0.0), vec4(0)); // Diffuse - frag_data[1] = max(vec4(spec.rgb,0.0), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal. - frag_data[2] = encodeNormal(tnorm, 0, GBUFFER_FLAG_HAS_PBR); // normal, environment intensity, flags + frag_data[0] = max(vec4(diffuse, specWeight), vec4(0)); // Diffuse + specularWeight + frag_data[1] = max(vec4(f0, perceptualRoughness), vec4(0)); // F0 + roughness + frag_data[2] = encodeNormal(tnorm, ao, GBUFFER_FLAG_HAS_PBR); // normal, occlusion, flags #if defined(HAS_EMISSIVE) - frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive + frag_data[3] = max(vec4(emissive, ior), vec4(0)); // Emissive + IOR #endif } diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl index 1b90cf9fde9..4b3c4fbbfda 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl @@ -428,12 +428,22 @@ void main() // Matte plastic potato terrain #define mix_orm vec3(1.0, 1.0, 0.0) #endif - frag_data[0] = max(vec4(pbr_mix.col.xyz, 0.0), vec4(0)); // Diffuse - frag_data[1] = max(vec4(mix_orm.rgb, base_color_factor_alpha), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal. - frag_data[2] = encodeNormal(tnorm, 0, GBUFFER_FLAG_HAS_PBR); // normal, flags + // Pre-compute shading-space values for new GBuffer layout (default specular: factor=1, colorFactor=white) + float metallic = mix_orm.b; + float perceptualRoughness = mix_orm.g; + float ao = mix_orm.r; + vec3 col = pbr_mix.col.xyz; + + vec3 f0 = mix(vec3(0.04), col, metallic); + vec3 diffuse = col * (1.0 - metallic); + float specWeight = 1.0; + + frag_data[0] = max(vec4(diffuse, specWeight), vec4(0)); // Diffuse + specularWeight + frag_data[1] = max(vec4(f0, perceptualRoughness), vec4(0)); // F0 + roughness + frag_data[2] = encodeNormal(tnorm, ao, GBUFFER_FLAG_HAS_PBR); // normal, occlusion, flags #if defined(HAS_EMISSIVE) - frag_data[3] = max(vec4(mix_emissive,0), vec4(0)); // PBR sRGB Emissive + frag_data[3] = max(vec4(mix_emissive, 1.5), vec4(0)); // Emissive + IOR #endif } diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl index f208ac746b4..785ccab19f9 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl @@ -33,6 +33,8 @@ in vec4 vertex_color; in vec2 vary_texcoord0; uniform float minimum_alpha; +void bayerDitherDiscard(float alpha, float threshold); + void main() { float alpha = diffuseLookup(vary_texcoord0.xy).a; @@ -46,18 +48,7 @@ void main() alpha *= vertex_color.a; #endif - if (alpha < 0.05) // treat as totally transparent - { - discard; - } - - if (alpha < 0.88) // treat as semi-transparent - { - if (fract(0.5*floor(target_pos_x / post_pos.w )) < 0.25) - { - discard; - } - } + bayerDitherDiscard(alpha, 0.88); frag_color = vec4(1,1,1,1); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl index 6f7bd2bf3c0..1979a5539ce 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl @@ -51,43 +51,138 @@ uniform mat4 inv_proj; uniform vec2 screen_res; uniform int sun_up_factor; +// Helper function for optimized PCF sampling +float sampleShadowMap(sampler2DShadow shadowMap, vec2 base_uv, float u, float v, vec2 shadowMapSizeInv, float lightDepth) +{ + vec2 uv = base_uv + vec2(u, v) * shadowMapSizeInv; + return texture(shadowMap, vec3(uv, lightDepth)); +} + +// Optimized 4x4 PCF sampling based on The Witness implementation float pcfShadow(sampler2DShadow shadowMap, vec3 norm, vec4 stc, float bias_mul, vec2 pos_screen, vec3 light_dir) { #if defined(SUN_SHADOW) - float offset = shadow_bias * bias_mul; stc.xyz /= stc.w; - stc.z += offset * 2.0; - stc.x = floor(stc.x*shadow_res.x + fract(pos_screen.y*shadow_res.y))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here - float cs = texture(shadowMap, stc.xyz); - float shadow = cs * 4.0; - shadow += texture(shadowMap, stc.xyz+vec3( 1.5/shadow_res.x, 0.5/shadow_res.y, 0.0)); - shadow += texture(shadowMap, stc.xyz+vec3( 0.5/shadow_res.x, -1.5/shadow_res.y, 0.0)); - shadow += texture(shadowMap, stc.xyz+vec3(-1.5/shadow_res.x, -0.5/shadow_res.y, 0.0)); - shadow += texture(shadowMap, stc.xyz+vec3(-0.5/shadow_res.x, 1.5/shadow_res.y, 0.0)); - return clamp(shadow * 0.125, 0.0, 1.0); + + float lightDepth = stc.z; + float offset = shadow_bias * bias_mul; + lightDepth += offset * 2.0; + + vec2 shadowMapSize = shadow_res; + vec2 shadowMapSizeInv = 1.0 / shadowMapSize; + + vec2 uv = stc.xy * shadowMapSize; // 1 unit = 1 texel + + vec2 base_uv; + base_uv.x = floor(uv.x + 0.5); + base_uv.y = floor(uv.y + 0.5); + + float s = (uv.x + 0.5 - base_uv.x); + float t = (uv.y + 0.5 - base_uv.y); + + base_uv -= vec2(0.5, 0.5); + base_uv *= shadowMapSizeInv; + + // 4x4 PCF kernel (FilterSize 5 from The Witness) + float uw0 = (4.0 - 3.0 * s); + float uw1 = 7.0; + float uw2 = (1.0 + 3.0 * s); + + float u0 = (3.0 - 2.0 * s) / uw0 - 2.0; + float u1 = (3.0 + s) / uw1; + float u2 = s / uw2 + 2.0; + + float vw0 = (4.0 - 3.0 * t); + float vw1 = 7.0; + float vw2 = (1.0 + 3.0 * t); + + float v0 = (3.0 - 2.0 * t) / vw0 - 2.0; + float v1 = (3.0 + t) / vw1; + float v2 = t / vw2 + 2.0; + + float sum = 0.0; + + sum += uw0 * vw0 * sampleShadowMap(shadowMap, base_uv, u0, v0, shadowMapSizeInv, lightDepth); + sum += uw1 * vw0 * sampleShadowMap(shadowMap, base_uv, u1, v0, shadowMapSizeInv, lightDepth); + sum += uw2 * vw0 * sampleShadowMap(shadowMap, base_uv, u2, v0, shadowMapSizeInv, lightDepth); + + sum += uw0 * vw1 * sampleShadowMap(shadowMap, base_uv, u0, v1, shadowMapSizeInv, lightDepth); + sum += uw1 * vw1 * sampleShadowMap(shadowMap, base_uv, u1, v1, shadowMapSizeInv, lightDepth); + sum += uw2 * vw1 * sampleShadowMap(shadowMap, base_uv, u2, v1, shadowMapSizeInv, lightDepth); + + sum += uw0 * vw2 * sampleShadowMap(shadowMap, base_uv, u0, v2, shadowMapSizeInv, lightDepth); + sum += uw1 * vw2 * sampleShadowMap(shadowMap, base_uv, u1, v2, shadowMapSizeInv, lightDepth); + sum += uw2 * vw2 * sampleShadowMap(shadowMap, base_uv, u2, v2, shadowMapSizeInv, lightDepth); + + return sum / 144.0; #else return 1.0; #endif } +// Helper function for spot shadow PCF sampling +float sampleSpotShadowMap(sampler2DShadow shadowMap, vec2 base_uv, float u, float v, vec2 shadowMapSizeInv, float lightDepth) +{ + vec2 uv = base_uv + vec2(u, v) * shadowMapSizeInv; + return texture(shadowMap, vec3(uv, lightDepth)); +} + +// Optimized 4x4 PCF sampling for spot shadows float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float bias_scale, vec2 pos_screen) { #if defined(SPOT_SHADOW) stc.xyz /= stc.w; - stc.z += spot_shadow_bias * bias_scale; - stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap - float cs = texture(shadowMap, stc.xyz); - float shadow = cs; + float lightDepth = stc.z; + lightDepth += spot_shadow_bias * bias_scale; + + vec2 shadowMapSize = proj_shadow_res; + vec2 shadowMapSizeInv = 1.0 / shadowMapSize; + + vec2 uv = stc.xy * shadowMapSize; // 1 unit = 1 texel + + vec2 base_uv; + base_uv.x = floor(uv.x + 0.5); + base_uv.y = floor(uv.y + 0.5); + + float s = (uv.x + 0.5 - base_uv.x); + float t = (uv.y + 0.5 - base_uv.y); + + base_uv -= vec2(0.5, 0.5); + base_uv *= shadowMapSizeInv; + + // 4x4 PCF kernel (FilterSize 5 from The Witness) + float uw0 = (4.0 - 3.0 * s); + float uw1 = 7.0; + float uw2 = (1.0 + 3.0 * s); + + float u0 = (3.0 - 2.0 * s) / uw0 - 2.0; + float u1 = (3.0 + s) / uw1; + float u2 = s / uw2 + 2.0; + + float vw0 = (4.0 - 3.0 * t); + float vw1 = 7.0; + float vw2 = (1.0 + 3.0 * t); + + float v0 = (3.0 - 2.0 * t) / vw0 - 2.0; + float v1 = (3.0 + t) / vw1; + float v2 = t / vw2 + 2.0; + + float sum = 0.0; + + sum += uw0 * vw0 * sampleSpotShadowMap(shadowMap, base_uv, u0, v0, shadowMapSizeInv, lightDepth); + sum += uw1 * vw0 * sampleSpotShadowMap(shadowMap, base_uv, u1, v0, shadowMapSizeInv, lightDepth); + sum += uw2 * vw0 * sampleSpotShadowMap(shadowMap, base_uv, u2, v0, shadowMapSizeInv, lightDepth); + + sum += uw0 * vw1 * sampleSpotShadowMap(shadowMap, base_uv, u0, v1, shadowMapSizeInv, lightDepth); + sum += uw1 * vw1 * sampleSpotShadowMap(shadowMap, base_uv, u1, v1, shadowMapSizeInv, lightDepth); + sum += uw2 * vw1 * sampleSpotShadowMap(shadowMap, base_uv, u2, v1, shadowMapSizeInv, lightDepth); - vec2 off = 1.0/proj_shadow_res; - off.y *= 1.5; + sum += uw0 * vw2 * sampleSpotShadowMap(shadowMap, base_uv, u0, v2, shadowMapSizeInv, lightDepth); + sum += uw1 * vw2 * sampleSpotShadowMap(shadowMap, base_uv, u1, v2, shadowMapSizeInv, lightDepth); + sum += uw2 * vw2 * sampleSpotShadowMap(shadowMap, base_uv, u2, v2, shadowMapSizeInv, lightDepth); - shadow += texture(shadowMap, stc.xyz+vec3(off.x*2.0, off.y, 0.0)); - shadow += texture(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)); - shadow += texture(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)); - shadow += texture(shadowMap, stc.xyz+vec3(-off.x*2.0, -off.y, 0.0)); - return shadow*0.2; + return sum / 144.0; #else return 1.0; #endif diff --git a/indra/newview/app_settings/shaders/class1/deferred/skinnedVelocityAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skinnedVelocityAlphaV.glsl new file mode 100644 index 00000000000..fc6f640c0aa --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/skinnedVelocityAlphaV.glsl @@ -0,0 +1,94 @@ +/** + * @file skinnedVelocityAlphaV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 modelview_projection_matrix; +uniform mat4 projection_matrix; +uniform mat4 last_modelview_matrix; +uniform mat4 texture_matrix0; + +in vec3 position; +in vec4 weight4; +in vec4 diffuse_color; +in vec2 texcoord0; + +out vec2 vary_texcoord0; +out vec4 vertex_color; + +uniform mat3x4 lastMatrixPalette[MAX_JOINTS_PER_MESH_OBJECT]; + +mat4 getObjectSkinnedTransform(); + +void writeVaryVelocity(vec4 pos, vec4 last_pos); + +mat4 getLastObjectSkinnedTransform() +{ + vec4 w = fract(weight4); + vec4 index = floor(weight4); + + index = min(index, vec4(MAX_JOINTS_PER_MESH_OBJECT-1)); + index = max(index, vec4(0.0)); + + w *= 1.0/(w.x+w.y+w.z+w.w); + + int i1 = int(index.x); + int i2 = int(index.y); + int i3 = int(index.z); + int i4 = int(index.w); + + mat3 mat = mat3(lastMatrixPalette[i1])*w.x; + mat += mat3(lastMatrixPalette[i2])*w.y; + mat += mat3(lastMatrixPalette[i3])*w.z; + mat += mat3(lastMatrixPalette[i4])*w.w; + + vec3 trans = vec3(lastMatrixPalette[i1][0].w, lastMatrixPalette[i1][1].w, lastMatrixPalette[i1][2].w)*w.x; + trans += vec3(lastMatrixPalette[i2][0].w, lastMatrixPalette[i2][1].w, lastMatrixPalette[i2][2].w)*w.y; + trans += vec3(lastMatrixPalette[i3][0].w, lastMatrixPalette[i3][1].w, lastMatrixPalette[i3][2].w)*w.z; + trans += vec3(lastMatrixPalette[i4][0].w, lastMatrixPalette[i4][1].w, lastMatrixPalette[i4][2].w)*w.w; + + mat4 ret; + ret[0] = vec4(mat[0], 0); + ret[1] = vec4(mat[1], 0); + ret[2] = vec4(mat[2], 0); + ret[3] = vec4(trans, 1.0); + + return ret; +} + +void main() +{ + vec4 pos = vec4(position.xyz, 1.0); + + vec4 current_clip = modelview_projection_matrix * pos; + + mat4 last_mat = getLastObjectSkinnedTransform(); + vec4 last_clip = projection_matrix * (last_modelview_matrix * (last_mat * pos)); + + gl_Position = current_clip; + + writeVaryVelocity(current_clip, last_clip); + + vary_texcoord0 = (texture_matrix0 * vec4(texcoord0, 0, 1)).xy; + vertex_color = diffuse_color; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/skinnedVelocityV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skinnedVelocityV.glsl new file mode 100644 index 00000000000..038f32e663a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/skinnedVelocityV.glsl @@ -0,0 +1,85 @@ +/** + * @file skinnedVelocityV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 modelview_projection_matrix; +uniform mat4 projection_matrix; +uniform mat4 last_modelview_matrix; + +in vec3 position; +in vec4 weight4; + +uniform mat3x4 lastMatrixPalette[MAX_JOINTS_PER_MESH_OBJECT]; + +mat4 getObjectSkinnedTransform(); + +void writeVaryVelocity(vec4 pos, vec4 last_pos); + +mat4 getLastObjectSkinnedTransform() +{ + vec4 w = fract(weight4); + vec4 index = floor(weight4); + + index = min(index, vec4(MAX_JOINTS_PER_MESH_OBJECT-1)); + index = max(index, vec4(0.0)); + + w *= 1.0/(w.x+w.y+w.z+w.w); + + int i1 = int(index.x); + int i2 = int(index.y); + int i3 = int(index.z); + int i4 = int(index.w); + + mat3 mat = mat3(lastMatrixPalette[i1])*w.x; + mat += mat3(lastMatrixPalette[i2])*w.y; + mat += mat3(lastMatrixPalette[i3])*w.z; + mat += mat3(lastMatrixPalette[i4])*w.w; + + vec3 trans = vec3(lastMatrixPalette[i1][0].w, lastMatrixPalette[i1][1].w, lastMatrixPalette[i1][2].w)*w.x; + trans += vec3(lastMatrixPalette[i2][0].w, lastMatrixPalette[i2][1].w, lastMatrixPalette[i2][2].w)*w.y; + trans += vec3(lastMatrixPalette[i3][0].w, lastMatrixPalette[i3][1].w, lastMatrixPalette[i3][2].w)*w.z; + trans += vec3(lastMatrixPalette[i4][0].w, lastMatrixPalette[i4][1].w, lastMatrixPalette[i4][2].w)*w.w; + + mat4 ret; + ret[0] = vec4(mat[0], 0); + ret[1] = vec4(mat[1], 0); + ret[2] = vec4(mat[2], 0); + ret[3] = vec4(trans, 1.0); + + return ret; +} + +void main() +{ + vec4 pos = vec4(position.xyz, 1.0); + + vec4 current_clip = modelview_projection_matrix * pos; + + mat4 last_mat = getLastObjectSkinnedTransform(); + vec4 last_clip = projection_matrix * (last_modelview_matrix * (last_mat * pos)); + + gl_Position = current_clip; + + writeVaryVelocity(current_clip, last_clip); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/velocityAlphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/velocityAlphaF.glsl new file mode 100644 index 00000000000..53a4a12c31a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/velocityAlphaF.glsl @@ -0,0 +1,49 @@ +/** + * @file velocityAlphaF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +vec4 diffuseLookup(vec2 texcoord); +void bayerDitherDiscard(float alpha, float threshold); + +in vec4 vary_cur_clip; +in vec4 vary_last_clip; +in vec2 vary_texcoord0; +in vec4 vertex_color; + +void main() +{ + float alpha = diffuseLookup(vary_texcoord0.xy).a; + alpha *= vertex_color.a; + + bayerDitherDiscard(alpha, 0.88); + + vec2 cur_ndc = vary_cur_clip.xy / vary_cur_clip.w; + vec2 last_ndc = vary_last_clip.xy / vary_last_clip.w; + + frag_color = vec4(cur_ndc - last_ndc, 0.0, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/velocityAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/velocityAlphaV.glsl new file mode 100644 index 00000000000..805ead5f013 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/velocityAlphaV.glsl @@ -0,0 +1,70 @@ +/** + * @file velocityAlphaV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 modelview_projection_matrix; +uniform mat4 modelview_matrix; +uniform mat4 projection_matrix; +uniform mat4 last_modelview_matrix; +uniform mat4 last_object_matrix; +uniform mat4 texture_matrix0; + +in vec3 position; +in vec4 diffuse_color; +in vec2 texcoord0; + +out vec2 vary_texcoord0; +out vec4 vertex_color; + +void passTextureIndex(); +void writeVaryVelocity(vec4 pos, vec4 last_pos); + +#ifdef HAS_SKIN +mat4 getObjectSkinnedTransform(); +mat4 getLastObjectSkinnedTransform(); +#endif + +void main() +{ + vary_texcoord0 = (texture_matrix0 * vec4(texcoord0, 0, 1)).xy; + vertex_color = diffuse_color; + + passTextureIndex(); + +#ifdef HAS_SKIN + mat4 cur_mat = getObjectSkinnedTransform(); + vec4 pos = projection_matrix * modelview_matrix * cur_mat * vec4(position.xyz, 1.0); + gl_Position = pos; + + mat4 last_mat = getLastObjectSkinnedTransform(); + vec4 last_pos = projection_matrix * last_modelview_matrix * last_mat * vec4(position.xyz, 1.0); +#else + vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0); + gl_Position = pos; + + vec4 last_pos = projection_matrix * last_modelview_matrix * last_object_matrix * vec4(position.xyz, 1.0); +#endif + + writeVaryVelocity(pos, last_pos); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/velocityF.glsl b/indra/newview/app_settings/shaders/class1/deferred/velocityF.glsl new file mode 100644 index 00000000000..4aa70b9f9a8 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/velocityF.glsl @@ -0,0 +1,39 @@ +/** + * @file velocityF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +in vec4 vary_cur_clip; +in vec4 vary_last_clip; + +void main() +{ + vec2 cur_ndc = vary_cur_clip.xy / vary_cur_clip.w; + vec2 last_ndc = vary_last_clip.xy / vary_last_clip.w; + + frag_color = vec4(cur_ndc - last_ndc, 0.0, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/velocityFuncV.glsl b/indra/newview/app_settings/shaders/class1/deferred/velocityFuncV.glsl new file mode 100644 index 00000000000..4989cf56bb9 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/velocityFuncV.glsl @@ -0,0 +1,33 @@ +/** + * @file velocityFuncV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +out vec4 vary_cur_clip; +out vec4 vary_last_clip; + +void writeVaryVelocity(vec4 pos, vec4 last_pos) +{ + vary_cur_clip = pos; + vary_last_clip = last_pos; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/velocityV.glsl b/indra/newview/app_settings/shaders/class1/deferred/velocityV.glsl new file mode 100644 index 00000000000..09b6a46cb77 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/velocityV.glsl @@ -0,0 +1,58 @@ +/** + * @file velocityV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 modelview_projection_matrix; +uniform mat4 modelview_matrix; +uniform mat4 projection_matrix; +uniform mat4 last_modelview_matrix; +uniform mat4 last_object_matrix; + +in vec3 position; + +void writeVaryVelocity(vec4 pos, vec4 last_pos); + +#ifdef HAS_SKIN +mat4 getObjectSkinnedTransform(); +mat4 getLastObjectSkinnedTransform(); +#endif + +void main() +{ +#ifdef HAS_SKIN + mat4 cur_mat = getObjectSkinnedTransform(); + vec4 pos = projection_matrix * modelview_matrix * cur_mat * vec4(position.xyz, 1.0); + gl_Position = pos; + + mat4 last_mat = getLastObjectSkinnedTransform(); + vec4 last_pos = projection_matrix * last_modelview_matrix * last_mat * vec4(position.xyz, 1.0); +#else + vec4 pos = modelview_projection_matrix * vec4(position.xyz, 1.0); + gl_Position = pos; + + vec4 last_pos = projection_matrix * last_modelview_matrix * last_object_matrix * vec4(position.xyz, 1.0); +#endif + + writeVaryVelocity(pos, last_pos); +} diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl index daab7c19116..118e510bb86 100644 --- a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl +++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl @@ -34,6 +34,10 @@ vec3 emissiveColor = vec3(0,0,0); float metallicFactor = 1.0; float roughnessFactor = 1.0; float minimum_alpha = -1.0; +float specularFactor = 1.0; +vec3 specularColorFactor = vec3(1.0); +float emissiveStrength = 1.0; +float ior = 1.5; layout (std140) uniform GLTFMaterials { @@ -130,11 +134,11 @@ float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear); -void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor); +void calcDiffuseSpecular(vec3 baseColor, float metallic, float specularFactor, vec3 specularColorFactor, float ior, inout vec3 diffuseColor, inout vec3 specularColor, inout float specularWeight); vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, - float metallic, + float specularWeight, vec3 pos, vec3 norm, float perceptualRoughness, @@ -150,7 +154,7 @@ vec3 pbrBaseLight(vec3 diffuseColor, vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 p, // pixel position vec3 v, // view vector (negative normalized pixel position) @@ -238,16 +242,30 @@ void main() #ifndef ALPHA_BLEND #ifdef UNLIT vec4 color = basecolor; - color.rgb += emissive.rgb; + color.rgb += emissive.rgb * emissiveStrength; frag_color = color; #else - frag_data[0] = max(vec4(basecolor.rgb, 0.0), vec4(0)); - frag_data[1] = max(vec4(orm.rgb,0.0), vec4(0)); - frag_data[2] = encodeNormal(norm, 0, GBUFFER_FLAG_HAS_PBR); + float perceptualRoughness = orm.g; + float metallic = orm.b; + float ao = orm.r; + + // KHR_materials_ior + KHR_materials_specular + float ior_f0 = pow((ior - 1.0) / (ior + 1.0), 2.0); + vec3 dielectric_f0 = min(vec3(ior_f0) * specularColorFactor, vec3(1.0)) * specularFactor; + float dielectric_f90 = specularFactor; + + vec3 f0 = mix(dielectric_f0, basecolor.rgb, metallic); + vec3 diffuseOut = mix(basecolor.rgb * (1.0 - max(dielectric_f0.r, max(dielectric_f0.g, dielectric_f0.b))), vec3(0.0), metallic); + float specWeight = mix(dielectric_f90, 1.0, metallic); + + // KHR_materials_emissive_strength + vec3 emissiveOut = emissive * emissiveStrength; + + frag_data[0] = max(vec4(diffuseOut, specWeight), vec4(0)); + frag_data[1] = max(vec4(f0, perceptualRoughness), vec4(0)); + frag_data[2] = encodeNormal(norm, ao, GBUFFER_FLAG_HAS_PBR); -//#if defined(HAS_EMISSIVE) - frag_data[3] = max(vec4(emissive,0), vec4(0)); -//#endif + frag_data[3] = max(vec4(emissiveOut, ior), vec4(0)); #endif #endif @@ -290,20 +308,22 @@ void main() float gloss = 1.0 - perceptualRoughness; vec3 irradiance = vec3(0); vec3 radiance = vec3(0); - sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true, amblit); + sampleReflectionProbes(irradiance, radiance, frag, pos.xyz, norm.xyz, gloss, true, amblit); vec3 diffuseColor; vec3 specularColor; - calcDiffuseSpecular(basecolor.rgb, metallic, diffuseColor, specularColor); + float specWeight = 1.0; + calcDiffuseSpecular(basecolor.rgb, metallic, specularFactor, specularColorFactor, ior, diffuseColor, specularColor, specWeight); vec3 v = -normalize(pos.xyz); - vec3 color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, emissive, orm.r, additive, atten); + vec3 emissiveOut = emissive * emissiveStrength; + vec3 color = pbrBaseLight(diffuseColor, specularColor, specWeight, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, emissiveOut, orm.r, additive, atten); vec3 light = vec3(0); // Punctual lights -#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); +#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, specWeight, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); LIGHT_LOOP(1) LIGHT_LOOP(2) diff --git a/indra/newview/app_settings/shaders/class1/interface/gaussianF.glsl b/indra/newview/app_settings/shaders/class1/interface/gaussianF.glsl index 09eb7a6a6af..8415c373cd5 100644 --- a/indra/newview/app_settings/shaders/class1/interface/gaussianF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/gaussianF.glsl @@ -39,15 +39,15 @@ float linearDepth(float d, float znear, float zfar); void main() { - vec3 col = vec3(0,0,0); + vec4 col = vec4(0,0,0,0); float w[9] = float[9]( 0.0002, 0.0060, 0.0606, 0.2417, 0.3829, 0.2417, 0.0606, 0.0060, 0.0002 ); for (int i = 0; i < 9; ++i) { vec2 tc = vary_texcoord0 + (i-4)*direction*resScale; - col += texture(diffuseRect, tc).rgb * w[i]; + col += texture(diffuseRect, tc) * w[i]; } - frag_color = max(vec4(col, 0.0), vec4(0)); + frag_color = max(col, vec4(0)); } diff --git a/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl b/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl index 69bee991f5c..b8578fee979 100644 --- a/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl @@ -31,6 +31,6 @@ in vec2 vary_texcoord0; void main() { - vec3 col = texture(diffuseRect, vary_texcoord0.xy).rgb; - frag_color = vec4(col, 0.0); + vec4 col = texture(diffuseRect, vary_texcoord0.xy); + frag_color = col; } diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl index 5089b9e31ef..2c0ca56ae59 100644 --- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl +++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl @@ -25,6 +25,7 @@ uniform vec3 lightnorm; uniform vec3 sunlight_color; +uniform float sun_intensity; uniform vec3 moonlight_color; uniform int sun_up_factor; uniform vec3 ambient_color; @@ -39,7 +40,7 @@ uniform float max_y; uniform vec3 glow; uniform float scene_light_strength; uniform float sun_moon_glow_factor; -uniform float sky_sunlight_scale; +uniform float sun_lux; uniform float sky_ambient_scale; uniform int classic_mode; @@ -158,8 +159,8 @@ void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, ou sunlit = srgb_to_linear(sunlit); } - // multiply to get similar colors as when the "scaleSoftClip" implementation was doubling color values - // (allows for mixing of light sources other than sunlight e.g. reflection probes) - sunlit *= sky_sunlight_scale; + // Apply sun illuminance (lux) normalized for PBR lighting. + // Reference: 100000 lux (bright sunlight) = 1.0 normalized intensity + sunlit *= sun_lux / 100000.0; amblit *= sky_ambient_scale; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl index 0418f99459d..ea30795cd50 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl @@ -35,6 +35,10 @@ uniform sampler2D specularMap; // PBR: Packed: Occlusion, Metal, Roughness uniform float metallicFactor; uniform float roughnessFactor; uniform vec3 emissiveColor; +uniform float specularFactor; +uniform vec3 specularColorFactor; +uniform float emissiveStrength; +uniform float ior; #if defined(HAS_SUN_SHADOW) || defined(HAS_SSAO) uniform sampler2D lightMap; @@ -94,11 +98,11 @@ void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, void mirrorClip(vec3 pos); void waterClip(vec3 pos); -void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor); +void calcDiffuseSpecular(vec3 baseColor, float metallic, float specularFactor, vec3 specularColorFactor, float ior, inout vec3 diffuseColor, inout vec3 specularColor, inout float specularWeight); vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, - float metallic, + float specularWeight, vec3 pos, vec3 norm, float perceptualRoughness, @@ -114,7 +118,7 @@ vec3 pbrBaseLight(vec3 diffuseColor, vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 p, // pixel position vec3 v, // view vector (negative normalized pixel position) @@ -178,7 +182,7 @@ void main() float ao = orm.r; // emissiveColor is the emissive color factor from GLTF and is already in linear space - vec3 colorEmissive = emissiveColor; + vec3 colorEmissive = emissiveColor * emissiveStrength; // emissiveMap here is a vanilla RGB texture encoded as sRGB, manually convert to linear colorEmissive *= srgb_to_linear(texture(emissiveMap, emissive_texcoord.xy).rgb); @@ -186,20 +190,21 @@ void main() float gloss = 1.0 - perceptualRoughness; vec3 irradiance = amblit; vec3 radiance = vec3(0); - sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true, amblit); + sampleReflectionProbes(irradiance, radiance, frag, pos.xyz, norm.xyz, gloss, true, amblit); vec3 diffuseColor; vec3 specularColor; - calcDiffuseSpecular(col.rgb, metallic, diffuseColor, specularColor); + float specWeight = 1.0; + calcDiffuseSpecular(col.rgb, metallic, specularFactor, specularColorFactor, ior, diffuseColor, specularColor, specWeight); vec3 v = -normalize(pos.xyz); - color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten); + color = pbrBaseLight(diffuseColor, specularColor, specWeight, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten); vec3 light = vec3(0); // Punctual lights -#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); +#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, specWeight, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); LIGHT_LOOP(1) LIGHT_LOOP(2) diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl index 5708fc319fa..aa9b81a05f0 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl @@ -339,7 +339,7 @@ void main() vec3 ambenv = amblit; vec3 glossenv; vec3 legacyenv; - sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, pos.xy*0.5+0.5, pos.xyz, norm.xyz, glossiness, env, true, amblit_linear); + sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, gl_FragCoord.xy / screen_res, pos.xyz, norm.xyz, glossiness, env, true, amblit_linear); color = ambenv; diff --git a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl index 96cda1ef496..548ff6a0397 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl @@ -55,7 +55,7 @@ vec3 hue_to_rgb(float hue); void pbrPunctual(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 v, // surface point to camera vec3 l, // surface point to light @@ -87,17 +87,10 @@ void main() if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_PBR)) { - vec3 colorEmissive = gb.emissive.rgb; - vec3 orm = spec.rgb; - float perceptualRoughness = orm.g; - float metallic = orm.b; - vec3 f0 = vec3(0.04); - vec3 baseColor = diffuse.rgb; - - vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0); - diffuseColor *= 1.0 - metallic; - - vec3 specularColor = mix(f0, baseColor.rgb, metallic); + vec3 diffuseColor = diffuse.rgb; // RT0.rgb is pre-computed diffuse + float specularWeight = gb.albedo.a; // RT0.a + vec3 specularColor = spec.rgb; // RT1.rgb is F0 + float perceptualRoughness = spec.a; // RT1.a for (int light_idx = 0; light_idx < LIGHT_COUNT; ++light_idx) { @@ -119,7 +112,7 @@ void main() float nl = 0; vec3 diff = vec3(0); vec3 specPunc = vec3(0); - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv, nl, diff, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, specularWeight, n.xyz, v, lv, nl, diff, specPunc); final_color += intensity * clamp(nl * (diff + specPunc), vec3(0), vec3(10)); } } diff --git a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl index ceb37f36a5b..c4ae409c294 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl @@ -57,7 +57,7 @@ float getDepth(vec2 tc); void pbrPunctual(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 v, // surface point to camera vec3 l, // surface point to light @@ -94,17 +94,10 @@ void main() if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_PBR)) { - vec3 colorEmissive = gb.emissive.rgb; - vec3 orm = spec.rgb; - float perceptualRoughness = orm.g; - float metallic = orm.b; - vec3 f0 = vec3(0.04); - vec3 baseColor = diffuse.rgb; - - vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0); - diffuseColor *= 1.0 - metallic; - - vec3 specularColor = mix(f0, baseColor.rgb, metallic); + vec3 diffuseColor = diffuse.rgb; // RT0.rgb is pre-computed diffuse + float specularWeight = gb.albedo.a; // RT0.a + vec3 specularColor = spec.rgb; // RT1.rgb is F0 + float perceptualRoughness = spec.a; // RT1.a vec3 intensity = dist_atten * color * 3.25; // Legacy attenuation, magic number to balance with legacy materials @@ -112,7 +105,7 @@ void main() vec3 diffPunc = vec3(0); vec3 specPunc = vec3(0); - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, normalize(lv), nl, diffPunc, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, specularWeight, n.xyz, v, normalize(lv), nl, diffPunc, specPunc); final_color += intensity* clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)); } diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl index 136b3dd9668..293a828fc28 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl @@ -25,23 +25,26 @@ #define FLT_MAX 3.402823466e+38 -#if defined(SSR) -float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness); -#endif - uniform samplerCubeArray reflectionProbes; uniform samplerCubeArray irradianceProbes; uniform sampler2D sceneMap; +uniform vec2 screen_res; uniform int cube_snapshot; uniform float max_probe_lod; uniform bool transparent_surface; +uniform float ssrMipScale; + +float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness); + uniform int classic_mode; #define MAX_REFMAP_COUNT 256 // must match LL_MAX_REFLECTION_PROBE_COUNT +#define MAX_HERO_PROBE_COUNT 8 + layout (std140) uniform ReflectionProbes { // list of OBBs for user override probes @@ -50,7 +53,7 @@ layout (std140) uniform ReflectionProbes /// box[0..2] - plane 0 .. 2 in [A,B,C,D] notation // box[3][0..2] - plane thickness mat4 refBox[MAX_REFMAP_COUNT]; - mat4 heroBox; + mat4 heroBox[MAX_HERO_PROBE_COUNT]; // list of bounding spheres for reflection probes sorted by distance to camera (closest first) vec4 refSphere[MAX_REFMAP_COUNT]; // extra parameters @@ -59,7 +62,7 @@ layout (std140) uniform ReflectionProbes // z - fade in // w - znear vec4 refParams[MAX_REFMAP_COUNT]; - vec4 heroSphere; + vec4 heroSphere[MAX_HERO_PROBE_COUNT]; // index of cube map in reflectionProbes for a corresponding reflection probe // e.g. cube map channel of refSphere[2] is stored in refIndex[2] // refIndex.x - cubemap channel in reflectionProbes @@ -76,9 +79,13 @@ layout (std140) uniform ReflectionProbes // number of reflection probes present in refSphere int refmapCount; - int heroShape; int heroMipCount; int heroProbeCount; + + // heroParams[i] = { shape, cubeIndex, 0, 0 } + ivec4 heroParams[MAX_HERO_PROBE_COUNT]; + mat4 heroPlaneMatrix[MAX_HERO_PROBE_COUNT]; + vec4 heroClipPlane[MAX_HERO_PROBE_COUNT]; }; // Inputs @@ -498,7 +505,7 @@ float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, vec4 i, out float d // c - center of probe // r2 - radius of probe squared // i - index of probe -vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, int i) +vec4 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, int i) { // parallax adjustment vec3 v; @@ -528,9 +535,10 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, v = env_mat * v; - vec4 ret = textureLod(reflectionProbes, vec4(v.xyz, refIndex[i].x), lod) * refParams[i].y; + vec4 probeSample = textureLod(reflectionProbes, vec4(v.xyz, refIndex[i].x), lod); + probeSample.rgb *= refParams[i].y; - return ret.rgb; + return vec4(probeSample.rgb, probeSample.a); } // Tap an irradiance map @@ -575,6 +583,13 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int vec3 sampleProbes(vec3 pos, vec3 dir, float lod) { +#ifdef REFLECTION_PROBE_MED_QUALITY + // Sample void probe ONCE using original direction (medium quality mode and above) + vec3 voidDir = env_mat * dir; + vec4 voidSample = textureLod(reflectionProbes, vec4(voidDir, 0), lod); + vec3 voidColor = voidSample.rgb * refParams[0].y; +#endif + float wsum[2]; wsum[0] = 0; wsum[1] = 0; @@ -599,12 +614,22 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod) float w = 0; float dw = 0; - vec3 refcol; + vec4 refcol; { refcol = tapRefMap(pos, dir, w, dw, lod, refSphere[i].xyz, i); - col[p] += refcol.rgb*w; +#ifdef REFLECTION_PROBE_MED_QUALITY + // Medium quality and above: Blend with void probe based on alpha: alpha=0 (geometry) uses probe, alpha=1 (sky) uses void + // Square the alpha to make the blend softer at edges (helps with supersampled subpixel blending) + float blend_factor = refcol.a * refcol.a; + vec3 blended = mix(refcol.rgb, voidColor, blend_factor); +#else + // Low quality: No alpha blending, just use probe color + vec3 blended = refcol.rgb; +#endif + + col[p] += blended*w; wsum[p] += w; dwsum[p] += dw; } @@ -692,35 +717,73 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit) #if defined(HERO_PROBES) -uniform vec4 clipPlane; uniform samplerCubeArray heroProbes; void tapHeroProbe(inout vec3 glossenv, vec3 pos, vec3 norm, float glossiness) { - float clipDist = dot(pos.xyz, clipPlane.xyz) + clipPlane.w; - float w = 0; - float dw = 0; float falloffMult = 10; vec3 refnormpersp = reflect(pos.xyz, norm.xyz); - if (heroShape < 1) - { - float d = 0; - boxIntersect(pos, norm, heroBox, d, 1.0); - w = max(d, 0); - } - else + for (int pi = 0; pi < heroProbeCount; ++pi) { - float r = heroSphere.w; + int shape = heroParams[pi].x; + int cubeIndex = heroParams[pi].y; - w = sphereWeight(pos, refnormpersp, heroSphere.xyz, r, vec4(1), dw); - } + float clipDist = dot(pos.xyz, heroClipPlane[pi].xyz) + heroClipPlane[pi].w; + float w = 0; + float dw = 0; + + if (shape < 1) + { // box + float d = 0; + boxIntersect(pos, norm, heroBox[pi], d, 1.0); + w = max(d, 0); + } + else if (shape == 1) + { // sphere + float r = heroSphere[pi].w; + w = sphereWeight(pos, refnormpersp, heroSphere[pi].xyz, r, vec4(1), dw); + } + else + { // planar (shape == 2) + float d = 0; + boxIntersect(pos, norm, heroBox[pi], d, 1.0); + w = max(d, 0); + } + + clipDist = clipDist * 0.95 + 0.05; + clipDist = clamp(clipDist * falloffMult, 0, 1); + w = clamp(w * falloffMult * clipDist, 0, 1); + w = mix(0, w, clamp(glossiness - 0.75, 0, 1) * 4); - clipDist = clipDist * 0.95 + 0.05; - clipDist = clamp(clipDist * falloffMult, 0, 1); - w = clamp(w * falloffMult * clipDist, 0, 1); - w = mix(0, w, clamp(glossiness - 0.75, 0, 1) * 4); // We only generate a quarter of the mips for the hero probes. Linearly interpolate between normal probes and hero probes based upon glossiness. - glossenv = mix(glossenv, textureLod(heroProbes, vec4(env_mat * refnormpersp, 0), (1.0-glossiness)*heroMipCount).xyz, w); + if (w < 0.001) + continue; + + if (shape == 2) + { + // Planar: apply correction matrix then sample face 0 + vec3 worldDir = env_mat * refnormpersp; + vec3 corrected = mat3(heroPlaneMatrix[pi]) * worldDir; + vec3 cubemapDir = corrected; + + // Angular fade: attenuate for directions near face edges + float cosAngle = cubemapDir.x / max(length(cubemapDir), 0.001); + w *= smoothstep(0.0, 0.1, cosAngle); + + // Ensure face 0 is sampled + cubemapDir.x = max(cubemapDir.x, 0.001); + + glossenv = mix(glossenv, textureLod(heroProbes, + vec4(cubemapDir, cubeIndex), + (1.0 - glossiness) * heroMipCount).xyz, w); + } + else + { + glossenv = mix(glossenv, textureLod(heroProbes, + vec4(env_mat * refnormpersp, cubeIndex), + (1.0 - glossiness) * heroMipCount).xyz, w); + } + } } #else @@ -750,21 +813,37 @@ void doProbeSample(inout vec3 ambenv, inout vec3 glossenv, glossenv = sampleProbes(pos, normalize(refnormpersp), lod); #if defined(SSR) - if (cube_snapshot != 1 && glossiness >= 0.9) + if (cube_snapshot != 1) { - vec4 ssr = vec4(0); - if (transparent) + float roughness = 1.0 - glossiness; + if (roughness < 0.7) { - tapScreenSpaceReflection(1, tc, pos, norm, ssr, sceneMap, 1); - ssr.a *= glossiness; - } - else - { - tapScreenSpaceReflection(1, tc, pos, norm, ssr, sceneMap, glossiness); - } + vec4 ssr = vec4(0.0); + + if (transparent) + { + tapScreenSpaceReflection(1, tc, pos.xyz, norm, ssr, sceneMap, glossiness); + } + else + { + ssr = textureLod(sceneMap, tc, roughness * ssrMipScale); + } + if (ssr.a > 0.001) + { + ssr.rgb /= ssr.a; - glossenv = mix(glossenv, ssr.rgb, ssr.a); + float l = dot(ssr.rgb, vec3(0.2126, 0.7152, 0.0722)); + ssr.rgb /= max(1.0 - l, 0.001); + + ssr.a *= 1.0 - smoothstep(0.6, 0.7, roughness); + + if (transparent) + ssr.a *= glossiness; + + glossenv = mix(glossenv, ssr.rgb, ssr.a); + } + } } #endif @@ -867,20 +946,36 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout #if defined(SSR) if (cube_snapshot != 1) { - vec4 ssr = vec4(0); - - if (transparent) - { - tapScreenSpaceReflection(1, tc, pos, norm, ssr, sceneMap, 1); - ssr.a *= glossiness; - } - else + float roughness = 1.0 - glossiness; + if (roughness < 0.7) { - tapScreenSpaceReflection(1, tc, pos, norm, ssr, sceneMap, glossiness); - } + vec4 ssr = vec4(0.0); + + if (transparent) + { + tapScreenSpaceReflection(1, tc, pos.xyz, norm, ssr, sceneMap, glossiness); + } + else + { + float ssrLod = clamp(log2(1.0 + roughness * roughness * ssrMipScale * 4.0), 0.0, ssrMipScale); + ssr = textureLod(sceneMap, tc, ssrLod); + } + + if (ssr.a > 0.001) + { + ssr.rgb /= ssr.a; + float l = dot(ssr.rgb, vec3(0.2126, 0.7152, 0.0722)); + ssr.rgb /= max(1.0 - l, 0.001); - glossenv = mix(glossenv, ssr.rgb, ssr.a); - legacyenv = mix(legacyenv, ssr.rgb, ssr.a); + ssr.a *= 1.0 - smoothstep(0.6, 0.7, roughness); + + if (transparent) + ssr.a *= glossiness; + + glossenv = mix(glossenv, ssr.rgb, ssr.a); + legacyenv = mix(legacyenv, ssr.rgb, ssr.a); + } + } } #endif diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflAlphaF.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflAlphaF.glsl new file mode 100644 index 00000000000..e1802f2b4e7 --- /dev/null +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflAlphaF.glsl @@ -0,0 +1,72 @@ +/** + * @file class3/deferred/screenSpaceReflAlphaF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +in vec3 vary_position; +in vec3 vary_normal; +in vec2 base_color_texcoord; +in vec2 metallic_roughness_texcoord; +in vec4 vertex_color; + +uniform sampler2D diffuseMap; +uniform sampler2D specularMap; +uniform sampler2D sceneMap; +uniform vec2 screen_res; +uniform mat4 projection_matrix; +uniform float roughnessFactor; +uniform float minimum_alpha; + +float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness); +void bayerDitherDiscard(float alpha, float threshold); + +void main() +{ + vec4 baseColor = texture(diffuseMap, base_color_texcoord); + float alpha = baseColor.a * vertex_color.a; + + // Alpha mask test + if (minimum_alpha >= 0.0 && alpha < minimum_alpha) + discard; + + bayerDitherDiscard(alpha, 1.0); + + // Per-pixel roughness from ORM green channel, scaled by material factor + float roughness = texture(specularMap, metallic_roughness_texcoord).g * roughnessFactor; + float glossiness = 1.0 - roughness; + + // Derive tc from view-space position via projection rather than + // gl_FragCoord / screen_res — the SSR buffer may be at reduced resolution. + vec4 projPos = projection_matrix * vec4(vary_position, 1.0); + vec2 tc = (projPos.xy / projPos.w) * 0.5 + 0.5; + vec3 norm = normalize(vary_normal); + + vec4 ssrColor = vec4(0.0); + tapScreenSpaceReflection(1, tc, vary_position, norm, ssrColor, sceneMap, glossiness); + frag_color = ssrColor; + frag_color.a *= alpha; +} diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflAlphaV.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflAlphaV.glsl new file mode 100644 index 00000000000..67d95888cbb --- /dev/null +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflAlphaV.glsl @@ -0,0 +1,72 @@ +/** + * @file class3/deferred/screenSpaceReflAlphaV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 modelview_matrix; + +#ifdef HAS_SKIN +uniform mat4 projection_matrix; +mat4 getObjectSkinnedTransform(); +#else +uniform mat3 normal_matrix; +uniform mat4 modelview_projection_matrix; +#endif + +uniform mat4 texture_matrix0; +uniform vec4[2] texture_base_color_transform; +uniform vec4[2] texture_metallic_roughness_transform; + +in vec3 position; +in vec3 normal; +in vec2 texcoord0; +in vec4 diffuse_color; + +out vec3 vary_position; +out vec3 vary_normal; +out vec2 base_color_texcoord; +out vec2 metallic_roughness_texcoord; +out vec4 vertex_color; + +vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform); + +void main() +{ +#ifdef HAS_SKIN + mat4 mat = getObjectSkinnedTransform(); + mat = modelview_matrix * mat; + vec3 pos = (mat * vec4(position.xyz, 1.0)).xyz; + vary_normal = normalize((mat * vec4(normal.xyz + position.xyz, 1.0)).xyz - pos.xyz); + gl_Position = projection_matrix * vec4(pos, 1.0); +#else + vary_normal = normalize(normal_matrix * normal); + vec3 pos = (modelview_matrix * vec4(position.xyz, 1.0)).xyz; + gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); +#endif + vary_position = pos; + + base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0); + metallic_roughness_texcoord = texture_transform(texcoord0, texture_metallic_roughness_transform, texture_matrix0); + + vertex_color = diffuse_color; +} diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflFilterF.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflFilterF.glsl new file mode 100644 index 00000000000..55e9ae25a23 --- /dev/null +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflFilterF.glsl @@ -0,0 +1,68 @@ +/** + * @file screenSpaceReflFilterF.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +uniform sampler2D diffuseMap; +uniform vec2 pixelSize; +uniform int filterDir; +uniform float filterScale; + +in vec2 vary_fragcoord; + +void main() +{ + vec2 tc = vary_fragcoord.xy; + + const float weights[4] = float[](0.214607, 0.189879, 0.131514, 0.071303); + + vec2 dir = (filterDir == 0) ? vec2(pixelSize.x, 0.0) : vec2(0.0, pixelSize.y); + dir *= filterScale; + + vec4 center = texture(diffuseMap, tc); + float w0 = weights[0] * center.a; + + vec4 color = center * w0; + float total_weight = w0; + + for (int i = 1; i < 4; i++) + { + vec2 offset = dir * float(i); + + vec4 s1 = texture(diffuseMap, tc + offset); + float w1 = weights[i] * s1.a; + color += s1 * w1; + total_weight += w1; + + vec4 s2 = texture(diffuseMap, tc - offset); + float w2 = weights[i] * s2.a; + color += s2 * w2; + total_weight += w2; + } + + frag_color = color / max(total_weight, 0.001); +} diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl index 5eda28bd8ac..17e1ea1dd6a 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl @@ -36,11 +36,9 @@ uniform float zFar; in vec2 vary_fragcoord; in vec3 camera_ray; -uniform sampler2D specularRect; -uniform sampler2D diffuseRect; uniform sampler2D diffuseMap; -vec4 getNorm(vec2 screenpos); +GBufferInfo getGBuffer(vec2 screenpos); float getDepth(vec2 pos_screen); float linearDepth(float d, float znear, float zfar); float linearDepth01(float d, float znear, float zfar); @@ -56,32 +54,22 @@ void main() { vec2 tc = vary_fragcoord.xy; float depth = linearDepth01(getDepth(tc), zNear, zFar); - vec4 norm = getNorm(tc); // need `norm.w` for GET_GBUFFER_FLAG() + GBufferInfo gb = getGBuffer(tc); vec3 pos = getPositionWithDepth(tc, getDepth(tc)).xyz; - vec4 spec = texture(specularRect, tc); vec2 hitpixel; - vec4 diffuse = texture(diffuseRect, tc); - vec3 specCol = spec.rgb; + vec3 specCol = gb.specular.rgb; vec4 fcol = texture(diffuseMap, tc); - if (GET_GBUFFER_FLAG(norm.w, GBUFFER_FLAG_HAS_PBR)) + if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_PBR)) { - vec3 orm = specCol.rgb; - float perceptualRoughness = orm.g; - float metallic = orm.b; - vec3 f0 = vec3(0.04); - vec3 baseColor = diffuse.rgb; - - vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0); - - specCol = mix(f0, baseColor.rgb, metallic); + specCol = gb.specular.rgb; // Already F0 } vec4 collectedColor = vec4(0); - float w = tapScreenSpaceReflection(4, tc, pos, norm.xyz, collectedColor, diffuseMap, 0.f); + float w = tapScreenSpaceReflection(4, tc, pos, gb.normal, collectedColor, diffuseMap, 0.f); collectedColor.rgb *= specCol.rgb; diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflTraceF.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflTraceF.glsl new file mode 100644 index 00000000000..76b713e9c33 --- /dev/null +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflTraceF.glsl @@ -0,0 +1,65 @@ +/** + * @file class3/deferred/screenSpaceReflTraceF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +in vec2 vary_fragcoord; +in vec3 camera_ray; + +uniform sampler2D sceneMap; + +GBufferInfo getGBuffer(vec2 screenpos); +float getDepth(vec2 pos_screen); +vec4 getPositionWithDepth(vec2 pos_screen, float depth); + +float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness); + +void main() +{ + vec2 tc = vary_fragcoord.xy; + float depth = getDepth(tc); + + // skip sky pixels + if (depth >= 1.0) + { + frag_color = vec4(0.0); + return; + } + + GBufferInfo gb = getGBuffer(tc); + vec3 pos = getPositionWithDepth(tc, depth).xyz; + + float glossiness; + if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_PBR)) + glossiness = 1.0 - gb.specular.a; // RT1.a = perceptualRoughness + else + glossiness = gb.specular.a; // Legacy: a = glossiness + + vec4 ssrColor = vec4(0.0); + tapScreenSpaceReflection(1, tc, pos, gb.normal, ssrColor, sceneMap, glossiness); + frag_color = ssrColor; +} diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index e8901c7ba26..d08d67c08d3 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -23,372 +23,397 @@ * $/LicenseInfo$ */ +// Based on https://imanolfotia.com/blog/1 + uniform sampler2D sceneMap; uniform sampler2D sceneDepth; uniform vec2 screen_res; uniform mat4 projection_matrix; -//uniform float zNear; -//uniform float zFar; uniform mat4 inv_proj; -uniform mat4 modelview_delta; // should be transform from last camera space to current camera space +uniform mat4 modelview_delta; uniform mat4 inv_modelview_delta; +uniform int iterationCount; +uniform float maxThickness; +uniform float depthBias; +uniform float glossySampleCount; +uniform float noiseSine; +uniform float maxZDepth; +uniform float maxRoughness; +uniform vec2 ssrJitterOffset; +uniform int hizMipCount; + vec4 getPositionWithDepth(vec2 pos_screen, float depth); -float random (vec2 uv) +float random(vec2 uv) { - return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123); //simple random function + return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123); } -// Based off of https://github.com/RoundedGlint585/ScreenSpaceReflection/ -// A few tweaks here and there to suit our needs. - vec2 generateProjectedPosition(vec3 pos) { - vec4 samplePosition = projection_matrix * vec4(pos, 1.f); + vec4 samplePosition = projection_matrix * vec4(pos, 1.0); samplePosition.xy = (samplePosition.xy / samplePosition.w) * 0.5 + 0.5; + // Compensate for SMAA T2x jitter difference between current and previous frame. + // mSceneMap was rendered with the previous frame's jittered projection; + // offset the UV so depth/color lookups hit the correct texels. + samplePosition.xy += ssrJitterOffset; return samplePosition.xy; } -bool isBinarySearchEnabled = true; -bool isAdaptiveStepEnabled = true; -bool isExponentialStepEnabled = true; -bool debugDraw = false; - -uniform float iterationCount; -uniform float rayStep; -uniform float distanceBias; -uniform float depthRejectBias; -uniform float glossySampleCount; -uniform float adaptiveStepMultiplier; -uniform float noiseSine; - -float epsilon = 0.1; - float getLinearDepth(vec2 tc) { - float depth = texture(sceneDepth, tc).r; - + // Force LOD 0 — sceneDepth now carries the Hi-Z mip chain, + // and reflected UVs have chaotic derivatives that could cause + // auto-LOD selection to sample coarse Hi-Z levels. + float depth = textureLod(sceneDepth, tc, 0).r; vec4 pos = getPositionWithDepth(tc, depth); - return -pos.z; } -bool traceScreenRay(vec3 position, vec3 reflection, out vec4 hitColor, out float hitDepth, float depth, sampler2D textureFrame) +float projectDepth(vec3 viewPos) +{ + vec4 clip = projection_matrix * vec4(viewPos, 1.0); + return (clip.z / clip.w) * 0.5 + 0.5; +} + +// Hi-Z hierarchical screen-space ray trace (Godot parametric-T approach). +// origin/dir are in UV+depth space: (u, v, rawDepth) where depth is [0,1]. +// Returns hit position (uv + raw depth) or vec3(-1) on miss. +// +// Uses parametric T along the ray for all comparisons, avoiding the AMD +// absolute-depth comparison that breaks for camera-facing rays (dir.z < 0). +// Z is normalized so dir.z = ±1, making depth_t = (cellDepth - origin.z) / dir.z +// trivially comparable with the edge_t from cell boundary intersections. +// +// Our Hi-Z stores MIN depth (standard GL: 0=near, 1=far), so min = closest. +// Godot uses reversed-Z with MAX depth, but the logic maps cleanly: +// Godot: max(a,b,c,d) with reversed-Z → closest surface +// Ours: min(a,b,c,d) with standard-Z → closest surface +// The comparison flips accordingly. +// +// References: +// Godot Engine SSR: screen_space_reflection.glsl (MIT license) +// Sugulee/GPU Pro 5: Hi-Z Screen-Space Cone-Traced Reflections +// Returns vec4: xyz = hit position (UV + raw depth), w = 1.0 on hit. +// Returns vec4(-1) on miss (ray exits screen or runs out of iterations). +// Thickness-rejected surfaces are skipped (mip-0 march) but don't kill the trace. +vec4 hiZTrace(vec3 origin, vec3 dir, int maxIterations) { - // transform position and reflection into same coordinate frame as the sceneMap and sceneDepth - reflection += position; - position = (inv_modelview_delta * vec4(position, 1)).xyz; - reflection = (inv_modelview_delta * vec4(reflection, 1)).xyz; - reflection -= position; - - depth = -position.z; - - vec3 step = rayStep * reflection; - vec3 marchingPosition = position + step; - float delta; - float depthFromScreen; - vec2 screenPosition; - bool hit = false; - hitColor = vec4(0); - - int i = 0; - if (depth > depthRejectBias) + int maxLevel = hizMipCount - 1; + + // Guard near-zero Z direction — treat as Z+ epsilon. + if (abs(dir.z) < 1e-7) + dir.z = 1e-7; + + // Normalize direction so |dir.z| = 1. + // This makes depth_t = (cellDepth - origin.z) * zDir directly comparable + // with edge_t values along the XY axes. + // Reference: https://hacksoflife.blogspot.com/2020/10/a-tip-for-hiz-ssr-parametric-t-tracing.html + vec3 rayDir = dir / abs(dir.z); + + // Standard GL: 0=near, 1=far. Camera-facing rays move toward 0 (dir.z < 0). + // Godot (reversed-Z): facing_camera = rayDir.z >= 0 (toward near=1.0). + // For standard GL: facing_camera = dir.z < 0 (toward near=0.0). + bool facingCamera = dir.z < 0.0; + float zDir = rayDir.z; // ±1 after normalization + + // Cell step direction: +1 or -1 per axis (matches Godot cell_step). + vec2 cellStep = vec2(rayDir.x < 0.0 ? -1.0 : 1.0, + rayDir.y < 0.0 ? -1.0 : 1.0); + + int curLevel = 0; + float t = 0.0; + + // Compute t_max: parametric T to the screen edge where we stop tracing. + vec2 t0 = (vec2(0.0) - origin.xy) / rayDir.xy; + vec2 t1 = (vec2(1.0) - origin.xy) / rayDir.xy; + vec2 t2 = max(t0, t1); + float tMax = min(t2.x, t2.y); + + // Initial advance: push past origin cell to avoid self-intersection. + // Matches Godot's initial advance exactly. { - for (; i < iterationCount && !hit; i++) - { - screenPosition = generateProjectedPosition(marchingPosition); - if (screenPosition.x > 1 || screenPosition.x < 0 || - screenPosition.y > 1 || screenPosition.y < 0) - { - hit = false; - break; - } - depthFromScreen = getLinearDepth(screenPosition); - delta = abs(marchingPosition.z) - depthFromScreen; + vec2 cellIndex = floor(origin.xy * screen_res); + vec2 newCellIndex = cellIndex + clamp(cellStep, vec2(0.0), vec2(1.0)); + vec2 newCellPos = (newCellIndex / screen_res) + cellStep * 0.000001; + vec2 posT = (newCellPos - origin.xy) / rayDir.xy; + t = min(posT.x, posT.y); + } - if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) - { - break; - } + for (int i = 0; i < maxIterations && curLevel >= 0 && t < tMax; i++) + { + vec3 pos = origin + rayDir * t; - if (abs(delta) < distanceBias) - { - vec4 color = vec4(1); - if(debugDraw) - color = vec4( 0.5+ sign(delta)/2,0.3,0.5- sign(delta)/2, 0); - hitColor = texture(sceneMap, screenPosition) * color; - hitDepth = depthFromScreen; - hit = true; - break; - } - if (isBinarySearchEnabled && delta > 0) - { - break; - } - if (isAdaptiveStepEnabled) - { - float directionSign = sign(abs(marchingPosition.z) - depthFromScreen); - //this is sort of adapting step, should prevent lining reflection by doing sort of iterative converging - //some implementation doing it by binary search, but I found this idea more cheaty and way easier to implement - step = step * (1.0 - rayStep * max(directionSign, 0.0)); - marchingPosition += step * (-directionSign); - } - else - { - marchingPosition += step; - } + // Cell lookup at current mip level. + vec2 cellCount = vec2(max(1, int(screen_res.x) >> curLevel), + max(1, int(screen_res.y) >> curLevel)); + ivec2 cellIndex = ivec2(floor(pos.xy * cellCount)); + cellIndex = clamp(cellIndex, ivec2(0), ivec2(cellCount) - 1); + + // Min depth in this cell (closest surface, standard GL). + float cellDepth = texelFetch(sceneDepth, cellIndex, curLevel).r; + + // Parametric T to the depth surface. + // Since rayDir.z = ±1, this is (cellDepth - origin.z) * (±1). + float depthT = (cellDepth - origin.z) * zDir; + + // Parametric T to the nearest cell boundary in XY. + vec2 newCellIndex = vec2(cellIndex) + clamp(cellStep, vec2(0.0), vec2(1.0)); + vec2 newCellPos = (newCellIndex / cellCount) + cellStep * 0.000001; + vec2 posT = (newCellPos - origin.xy) / rayDir.xy; + float edgeT = min(posT.x, posT.y); + + // Hit detection (matches Godot exactly): + // Forward rays: hit if depth surface is reached before cell boundary. + // Camera-facing rays: hit if ray hasn't traveled past the depth surface. + bool hit = facingCamera ? (t <= depthT) : (depthT <= edgeT); - if (isExponentialStepEnabled) + int mipOffset = hit ? -1 : 1; + + // Thickness gate at mip 0 (matches Godot depth_tolerance check): + // Linearize depths and reject if surface is too far behind ray. + if (curLevel == 0 && hit) + { + float z0 = getPositionWithDepth(pos.xy, cellDepth).z; + float z1 = getPositionWithDepth(pos.xy, pos.z).z; + if ((z0 - z1) > maxThickness) { - step *= adaptiveStepMultiplier; + hit = false; + mipOffset = 0; // Stay at mip 0, march cell-by-cell. } } - if(isBinarySearchEnabled) + + // Advance parametric T (matches Godot exactly): + // Only advance to depthT for non-facing-camera hits. + // For facing-camera hits, descend without advancing. + if (hit) { - for(; i < iterationCount && !hit; i++) - { - step *= 0.5; - marchingPosition = marchingPosition - step * sign(delta); - - screenPosition = generateProjectedPosition(marchingPosition); - if (screenPosition.x > 1 || screenPosition.x < 0 || - screenPosition.y > 1 || screenPosition.y < 0) - { - hit = false; - break; - } - depthFromScreen = getLinearDepth(screenPosition); - delta = abs(marchingPosition.z) - depthFromScreen; - - if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) - { - break; - } - - if (abs(delta) < distanceBias && depthFromScreen != (depth - distanceBias)) - { - vec4 color = vec4(1); - if(debugDraw) - color = vec4( 0.5+ sign(delta)/2,0.3,0.5- sign(delta)/2, 0); - hitColor = texture(sceneMap, screenPosition) * color; - hitDepth = depthFromScreen; - hit = true; - break; - } - } + if (!facingCamera) + t = max(t, depthT); } + else + { + t = edgeT; + } + + curLevel = min(curLevel + mipOffset, maxLevel); } - return hit; + vec3 hitPos = origin + rayDir * t; + + // Final bounds check. + if (hitPos.x < 0.0 || hitPos.x > 1.0 || + hitPos.y < 0.0 || hitPos.y > 1.0 || + t >= tMax) + return vec4(-1.0); + + return vec4(hitPos, 1.0); } -uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( - vec3(0.5433144, 0.1122154, 0.2501391), - vec3(0.6575254, 0.721409, 0.16286), - vec3(0.02888453, 0.05170321, 0.7573566), - vec3(0.06635678, 0.8286457, 0.07157445), - vec3(0.8957489, 0.4005505, 0.7916042), - vec3(0.3423355, 0.5053263, 0.9193521), - vec3(0.9694794, 0.9461077, 0.5406441), - vec3(0.9975473, 0.02789414, 0.7320132), - vec3(0.07781899, 0.3862341, 0.918594), - vec3(0.4439073, 0.9686955, 0.4055861), - vec3(0.9657035, 0.6624081, 0.7082613), - vec3(0.7712346, 0.07273269, 0.3292839), - vec3(0.2489169, 0.2550394, 0.1950516), - vec3(0.7249326, 0.9328285, 0.3352458), - vec3(0.6028461, 0.4424961, 0.5393377), - vec3(0.2879795, 0.7427881, 0.6619173), - vec3(0.3193627, 0.0486145, 0.08109283), - vec3(0.1233155, 0.602641, 0.4378719), - vec3(0.9800708, 0.211729, 0.6771586), - vec3(0.4894537, 0.3319927, 0.8087631), - vec3(0.4802743, 0.6358885, 0.814935), - vec3(0.2692913, 0.9911493, 0.9934899), - vec3(0.5648789, 0.8553897, 0.7784553), - vec3(0.8497344, 0.7870212, 0.02065313), - vec3(0.7503014, 0.2826185, 0.05412734), - vec3(0.8045461, 0.6167251, 0.9532926), - vec3(0.04225039, 0.2141281, 0.8678675), - vec3(0.07116079, 0.9971236, 0.3396397), - vec3(0.464099, 0.480959, 0.2775862), - vec3(0.6346927, 0.31871, 0.6588384), - vec3(0.449012, 0.8189669, 0.2736875), - vec3(0.452929, 0.2119148, 0.672004), - vec3(0.01506042, 0.7102436, 0.9800494), - vec3(0.1970513, 0.4713539, 0.4644522), - vec3(0.13715, 0.7253224, 0.5056525), - vec3(0.9006432, 0.5335414, 0.02206874), - vec3(0.9960898, 0.7961011, 0.01468861), - vec3(0.3386469, 0.6337739, 0.9310676), - vec3(0.1745718, 0.9114985, 0.1728188), - vec3(0.6342545, 0.5721557, 0.4553517), - vec3(0.1347412, 0.1137158, 0.7793725), - vec3(0.3574478, 0.3448052, 0.08741581), - vec3(0.7283059, 0.4753885, 0.2240275), - vec3(0.8293507, 0.9971212, 0.2747005), - vec3(0.6501846, 0.000688076, 0.7795712), - vec3(0.01149416, 0.4930083, 0.792608), - vec3(0.666189, 0.1875442, 0.7256873), - vec3(0.8538797, 0.2107637, 0.1547532), - vec3(0.5826825, 0.9750752, 0.9105834), - vec3(0.8914346, 0.08266425, 0.5484225), - vec3(0.4374518, 0.02987111, 0.7810078), - vec3(0.2287418, 0.1443802, 0.1176908), - vec3(0.2671157, 0.8929081, 0.8989366), - vec3(0.5425819, 0.5524959, 0.6963879), - vec3(0.3515188, 0.8304397, 0.0502702), - vec3(0.3354864, 0.2130747, 0.141169), - vec3(0.9729427, 0.3509927, 0.6098799), - vec3(0.7585629, 0.7115368, 0.9099342), - vec3(0.0140543, 0.6072157, 0.9436461), - vec3(0.9190664, 0.8497264, 0.1643751), - vec3(0.1538157, 0.3219983, 0.2984214), - vec3(0.8854713, 0.2968667, 0.8511457), - vec3(0.1910622, 0.03047311, 0.3571215), - vec3(0.2456353, 0.5568692, 0.3530164), - vec3(0.6927255, 0.8073994, 0.5808484), - vec3(0.8089353, 0.8969175, 0.3427134), - vec3(0.194477, 0.7985603, 0.8712182), - vec3(0.7256182, 0.5653068, 0.3985921), - vec3(0.9889427, 0.4584851, 0.8363391), - vec3(0.5718582, 0.2127113, 0.2950557), - vec3(0.5480209, 0.0193435, 0.2992659), - vec3(0.6598953, 0.09478426, 0.92187), - vec3(0.1385615, 0.2193868, 0.205245), - vec3(0.7623423, 0.1790726, 0.1508465), - vec3(0.7569032, 0.3773386, 0.4393887), - vec3(0.5842971, 0.6538072, 0.5224424), - vec3(0.9954313, 0.5763943, 0.9169143), - vec3(0.001311183, 0.340363, 0.1488652), - vec3(0.8167927, 0.4947158, 0.4454727), - vec3(0.3978434, 0.7106082, 0.002727509), - vec3(0.5459411, 0.7473233, 0.7062873), - vec3(0.4151598, 0.5614617, 0.4748358), - vec3(0.4440694, 0.1195122, 0.9624678), - vec3(0.1081301, 0.4813806, 0.07047641), - vec3(0.2402785, 0.3633997, 0.3898734), - vec3(0.2317942, 0.6488295, 0.4221864), - vec3(0.01145542, 0.9304277, 0.4105759), - vec3(0.3563728, 0.9228861, 0.3282344), - vec3(0.855314, 0.6949819, 0.3175117), - vec3(0.730832, 0.01478493, 0.5728671), - vec3(0.9304829, 0.02653277, 0.712552), - vec3(0.4132186, 0.4127623, 0.6084146), - vec3(0.7517329, 0.9978395, 0.1330464), - vec3(0.5210338, 0.4318751, 0.9721575), - vec3(0.02953994, 0.1375937, 0.9458942), - vec3(0.1835506, 0.9896691, 0.7919457), - vec3(0.3857062, 0.2682322, 0.1264563), - vec3(0.6319699, 0.8735335, 0.04390657), - vec3(0.5630485, 0.3339024, 0.993995), - vec3(0.90701, 0.1512893, 0.8970422), - vec3(0.3027443, 0.1144253, 0.1488708), - vec3(0.9149003, 0.7382028, 0.7914025), - vec3(0.07979286, 0.6892691, 0.2866171), - vec3(0.7743186, 0.8046008, 0.4399814), - vec3(0.3128662, 0.4362317, 0.6030678), - vec3(0.1133721, 0.01605821, 0.391872), - vec3(0.5185481, 0.9210006, 0.7889017), - vec3(0.8217013, 0.325305, 0.1668191), - vec3(0.8358996, 0.1449739, 0.3668382), - vec3(0.1778213, 0.5599256, 0.1327691), - vec3(0.06690693, 0.5508637, 0.07212365), - vec3(0.9750564, 0.284066, 0.5727578), - vec3(0.4350255, 0.8949825, 0.03574753), - vec3(0.8931149, 0.9177974, 0.8123496), - vec3(0.9055127, 0.989903, 0.813235), - vec3(0.2897243, 0.3123978, 0.5083504), - vec3(0.1519223, 0.3958645, 0.2640327), - vec3(0.6840154, 0.6463035, 0.2346607), - vec3(0.986473, 0.8714055, 0.3960275), - vec3(0.6819352, 0.4169535, 0.8379834), - vec3(0.9147297, 0.6144146, 0.7313942), - vec3(0.6554981, 0.5014008, 0.9748477), - vec3(0.9805915, 0.1318207, 0.2371372), - vec3(0.5980836, 0.06796348, 0.9941338), - vec3(0.6836596, 0.9917196, 0.2319056), - vec3(0.5276511, 0.2745509, 0.5422578), - vec3(0.829482, 0.03758276, 0.1240466), - vec3(0.2698198, 0.0002266169, 0.3449324) -); - -vec3 getPoissonSample(int i) { - return POISSON3D_SAMPLES[i] * 2 - 1; +float calculateEdgeFade(vec2 screenPos) +{ + vec2 distFromCenter = abs(screenPos * 2.0 - 1.0); + vec2 fade = smoothstep(0.85, 1.0, distFromCenter); + return 1.0 - max(fade.x, fade.y); } -float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness) +float tapScreenSpaceReflection( + int totalSamples, + vec2 tc, + vec3 viewPos, + vec3 n, + inout vec4 collectedColor, + sampler2D source, + float glossiness) { -#ifdef TRANSPARENT_SURFACE -collectedColor = vec4(1, 0, 1, 1); - return 0; -#endif - collectedColor = vec4(0); - int hits = 0; + float roughness = 1.0 - glossiness; - float depth = -viewPos.z; + if (roughness >= maxRoughness) + return 0.0; - vec3 rayDirection = normalize(reflect(viewPos, normalize(n))); + vec3 viewDir = normalize(viewPos); + vec3 normal = normalize(n); - vec2 uv2 = tc * screen_res; - float c = (uv2.x + uv2.y) * 0.125; - float jitter = mod( c, 1.0); + float viewDotNormal = dot(-viewDir, normal); + if (viewDotNormal <= 0.0) + { + collectedColor = vec4(0.0); + return 0.0; + } - vec2 screenpos = 1 - abs(tc * 2 - 1); - float vignette = clamp((abs(screenpos.x) * abs(screenpos.y)) * 16,0, 1); - vignette *= clamp((dot(normalize(viewPos), n) * 0.5 + 0.5) * 5.5 - 0.8, 0, 1); + vec2 distFromCenter = abs(tc * 2.0 - 1.0); + float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); + if (baseEdgeFade <= 0.001) + { + collectedColor = vec4(0.0); + return 0.0; + } - float zFar = 128.0; - vignette *= clamp(1.0+(viewPos.z/zFar), 0.0, 1.0); + // Bias the ray origin along the normal, scaled by distance. + // Prevents grazing-angle rays from scraping the originating surface + // at distance where depth precision breaks down. + float biasAmount = max(0.01, -viewPos.z * depthBias); + vec3 biasedPos = viewPos - normal * biasAmount; - vignette *= clamp(glossiness * 3 - 1.7, 0, 1); + vec3 transformedPos = (inv_modelview_delta * vec4(biasedPos, 1.0)).xyz; + float startDepth = -transformedPos.z; - vec4 hitpoint; + if (startDepth > maxZDepth) + { + collectedColor = vec4(0.0); + return 0.0; + } - glossiness = 1 - glossiness; + vec3 perfectReflDir = normalize(reflect(viewDir, normal)); - totalSamples = int(max(glossySampleCount, glossySampleCount * glossiness * vignette)); + int numSamples = max(1, int(glossySampleCount)); + vec3 accumColor = vec3(0.0); + float accumFade = 0.0; + int hits = 0; - totalSamples = max(totalSamples, 1); - if (glossiness < 0.35) + for (int s = 0; s < numSamples; s++) { - if (vignette > 0) + vec3 reflectDir = perfectReflDir; + + // Jitter reflection direction based on roughness (importance-sampled GGX) + if (roughness > 0.001) { - for (int i = 0; i < totalSamples; i++) - { - vec3 firstBasis = normalize(cross(getPoissonSample(i), rayDirection)); - vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); - vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * glossiness); + float alpha = roughness * roughness; + float u1 = random(tc * screen_res + noiseSine + float(s) * 0.123); + float u2 = random(tc * screen_res * 1.7 + noiseSine + float(s) * 0.456 + 0.5); - //float hitDepth; + float theta = atan(alpha * sqrt(u1) / sqrt(1.0 - u1)); + float phi = 2.0 * 3.14159265 * u2; - bool hit = traceScreenRay(viewPos, normalize(reflectionDirectionRandomized), hitpoint, depth, depth, source); + vec3 up = abs(reflectDir.y) < 0.999 ? vec3(0, 1, 0) : vec3(1, 0, 0); + vec3 tangent = normalize(cross(up, reflectDir)); + vec3 bitangent = cross(reflectDir, tangent); - hitpoint.a = 0; + vec3 h = normalize( + sin(theta) * cos(phi) * tangent + + sin(theta) * sin(phi) * bitangent + + cos(theta) * reflectDir + ); - if (hit) - { - ++hits; - collectedColor += hitpoint; - collectedColor.a += 1; - } - } + reflectDir = normalize(reflect(-reflectDir, h)); + } - if (hits > 0) - { - collectedColor /= hits; - } - else - { - collectedColor = vec4(0); - } + vec3 reflTarget = viewPos + reflectDir; + vec3 transformedTarget = (inv_modelview_delta * vec4(reflTarget, 1.0)).xyz; + vec3 transformedReflDir = normalize(transformedTarget - transformedPos); + + if (transformedReflDir.z >= 0.5) + continue; + + // Fade rays pointing back toward camera to avoid sharp SSR boundary. + float cameraFacingFade = 1.0 - smoothstep(0.45, 0.5, transformedReflDir.z); + + // Push ray origin along surface normal to prevent self-intersection. + // Deterministic (not random) to avoid per-pixel noise. + // Scales with distance to match depth-buffer precision degradation. + float clearance = max(0.05, -viewPos.z * 0.002); + vec3 clearedPos = biasedPos + normal * clearance; + vec3 transformedClearedPos = (inv_modelview_delta * vec4(clearedPos, 1.0)).xyz; + + // Project ray origin and a nearby point to screen space (UV + raw depth). + // Use a short step (fraction of distance-to-camera) to ensure the end + // point stays in front of the camera — projecting at maxZDepth can wrap + // behind the camera for reflections going toward the viewer. + vec3 ssOrigin = vec3(generateProjectedPosition(transformedClearedPos), + projectDepth(transformedClearedPos)); + float stepDist = max(0.1, -transformedClearedPos.z * 0.1); + vec3 transformedEnd = transformedClearedPos + transformedReflDir * stepDist; + vec3 ssFar = vec3(generateProjectedPosition(transformedEnd), + projectDepth(transformedEnd)); + vec3 ssDir = ssFar - ssOrigin; + + vec4 result = hiZTrace(ssOrigin, ssDir, iterationCount); + + if (result.x < 0.0) + continue; + + vec2 hitTC = result.xy; + + // Read actual surface depth at the hit pixel (mip 0, exact pixel). + ivec2 hitPixel = ivec2(hitTC * screen_res); + hitPixel = clamp(hitPixel, ivec2(0), ivec2(screen_res) - 1); + float hitSurfaceDepth = texelFetch(sceneDepth, hitPixel, 0).r; + + // Reject sky / far-plane hits (forward-Z: 1.0 = far). + float hitDepth = -getPositionWithDepth(hitTC, hitSurfaceDepth).z; + if (hitDepth > maxZDepth) + continue; + + // Confidence validation (matches Godot): + // Compare the trace's final depth with the actual surface depth at that pixel. + // No dead zone — smoothstep from 0 penalizes any mismatch. + // Squared for strong near-hits with rapid falloff. + vec3 viewSpaceSurface = getPositionWithDepth(hitTC, hitSurfaceDepth).xyz; + vec3 viewSpaceHit = getPositionWithDepth(hitTC, result.z).xyz; + float hitDistance = length(viewSpaceSurface - viewSpaceHit); + float confidence = 1.0 - smoothstep(0.0, maxThickness, hitDistance); + confidence *= confidence; + + if (confidence < 0.95) + continue; + + // Short-ray back-face check (Godot-style): + // For rays that traveled < 3 pixels, compute a geometric normal at the + // hit point from depth buffer cross-derivatives. Reject if the reflected + // ray exits the surface (dot >= 0), which indicates self-intersection. + float rayLen = length(result.xy - ssOrigin.xy); + float rayPixelLen = rayLen * max(screen_res.x, screen_res.y); + if (rayPixelLen < 3.0) + { + float dR = texelFetch(sceneDepth, hitPixel + ivec2(1, 0), 0).r; + float dD = texelFetch(sceneDepth, hitPixel + ivec2(0, 1), 0).r; + vec3 posR = getPositionWithDepth((vec2(hitPixel) + vec2(1.5, 0.5)) / screen_res, dR).xyz; + vec3 posD = getPositionWithDepth((vec2(hitPixel) + vec2(0.5, 1.5)) / screen_res, dD).xyz; + vec3 hitGeomNormal = cross(posR - viewSpaceSurface, posD - viewSpaceSurface); + // Ensure normal faces toward camera (positive Z in view space). + if (hitGeomNormal.z < 0.0) hitGeomNormal = -hitGeomNormal; + hitGeomNormal = normalize(hitGeomNormal); + + if (dot(reflectDir, hitGeomNormal) >= 0.0) + continue; } + + float edgeFade = calculateEdgeFade(hitTC); + + float zFadeStart = maxZDepth * 0.8; + float zFade = 1.0 - smoothstep(zFadeStart, maxZDepth, hitDepth); + + vec4 sampledColor = textureLod(source, hitTC, 0.0); + + // Tone map hit color (Reinhard on luminance) to compress brights + // before mip chain averaging — prevents fireflies. + float luma = dot(sampledColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + sampledColor.rgb /= (1.0 + luma); + + // Fade-in: suppress near-origin artifacts from self-intersection. + float fadeIn = smoothstep(0.0, 0.01, rayLen); + float rayFade = 1.0 - smoothstep(0.6, 1.0, rayLen); + + float sampleFade = edgeFade * zFade * fadeIn * rayFade * confidence * cameraFacingFade; + + accumColor += sampledColor.rgb * sampleFade; + accumFade += sampleFade; + hits++; } - float hitAlpha = hits; - hitAlpha /= totalSamples; - collectedColor.a = hitAlpha * vignette; - return hits; + + if (hits == 0) + { + collectedColor = vec4(0.0); + return 0.0; + } + + accumColor /= float(numSamples); + accumFade /= float(numSamples); + + float combinedFade = accumFade * baseEdgeFade; + + collectedColor = vec4(accumColor * baseEdgeFade, combinedFade); + return 1.0; } diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflWaterF.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflWaterF.glsl new file mode 100644 index 00000000000..0654c591d3d --- /dev/null +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflWaterF.glsl @@ -0,0 +1,97 @@ +/** + * @file class3/deferred/screenSpaceReflWaterF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2007, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +uniform sampler2D bumpMap; +uniform sampler2D bumpMap2; +uniform float blend_factor; +uniform vec3 normScale; +uniform float blurMultiplier; +uniform vec2 screen_res; +uniform mat4 projection_matrix; + +in vec4 refCoord; +in vec4 littleWave; +in vec4 view; +in vec3 vary_position; +in vec3 vary_normal; +in vec3 vary_tangent; + +float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness); + +uniform sampler2D sceneMap; + +vec3 BlendNormal(vec3 bump1, vec3 bump2) +{ + return mix(bump1, bump2, blend_factor); +} + +void main() +{ + vec3 vN = vary_normal; + vec3 vT = vary_tangent; + vec3 vB = cross(vN, vT); + + // Generate wave normals (same as waterF.glsl) + vec2 bigwave = vec2(refCoord.w, view.w); + + vec3 wave1_a = texture(bumpMap, bigwave).xyz * 2.0 - 1.0; + vec3 wave2_a = texture(bumpMap, littleWave.xy).xyz * 2.0 - 1.0; + vec3 wave3_a = texture(bumpMap, littleWave.zw).xyz * 2.0 - 1.0; + + vec3 wave1_b = texture(bumpMap2, bigwave).xyz * 2.0 - 1.0; + vec3 wave2_b = texture(bumpMap2, littleWave.xy).xyz * 2.0 - 1.0; + vec3 wave3_b = texture(bumpMap2, littleWave.zw).xyz * 2.0 - 1.0; + + vec3 wave1 = BlendNormal(wave1_a, wave1_b); + vec3 wave2 = BlendNormal(wave2_a, wave2_b); + vec3 wave3 = BlendNormal(wave3_a, wave3_b); + + vec3 wavef = (wave1 + wave2 * 0.4 + wave3 * 0.6) * 0.5; + + // Same IBL normal computation as waterF.glsl + vec3 wave_ibl = wavef * normScale; + wave_ibl.z *= 2.0; + wave_ibl = normalize(wave_ibl.x * vT + wave_ibl.y * vB + wave_ibl.z * vN); + + // Water glossiness (same as waterF.glsl) + float perceptualRoughness = blurMultiplier * blurMultiplier; + float glossiness = 1.0 - perceptualRoughness; + + // Derive tc from view-space position via projection rather than + // gl_FragCoord / screen_res. screen_res is the deferred buffer size, + // but this shader may render into a reduced-resolution SSR buffer whose + // viewport (and thus gl_FragCoord) is smaller, causing an off-centre + // vignette and incorrect screen-edge fade. + vec4 projPos = projection_matrix * vec4(vary_position, 1.0); + vec2 tc = (projPos.xy / projPos.w) * 0.5 + 0.5; + + vec4 ssrColor = vec4(0.0); + tapScreenSpaceReflection(1, tc, vary_position, wave_ibl, ssrColor, sceneMap, glossiness); + frag_color = ssrColor; +} diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 948387a6eda..9dbf90ee37b 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -80,16 +80,12 @@ vec3 srgb_to_linear(vec3 c); uniform vec4 waterPlane; -uniform int cube_snapshot; - uniform float sky_hdr_scale; void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float nh, out float nl, out float nv, out float vh, out float lightDist); -void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor); - vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, - float metallic, + float specularWeight, vec3 pos, vec3 norm, float perceptualRoughness, @@ -136,7 +132,7 @@ void main() #endif #if defined(HAS_SUN_SHADOW) - float scol = max(scol_ambocc.r, baseColor.a); + float scol = scol_ambocc.r; #else float scol = 1.0; #endif @@ -166,10 +162,11 @@ void main() if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_PBR)) { - vec3 orm = spec.rgb; - float perceptualRoughness = orm.g; - float metallic = orm.b; - float ao = orm.r; + vec3 diffuseColor = baseColor.rgb; // RT0.rgb is pre-computed diffuse + float specularWeight = baseColor.a; // RT0.a + vec3 specularColor = spec.rgb; // RT1.rgb is F0 + float perceptualRoughness = spec.a; // RT1.a + float ao = gb.occlusion; // RT2.b vec3 irradiance = amblit_linear; @@ -180,12 +177,8 @@ void main() adjustIrradiance(irradiance, ambocc); - vec3 diffuseColor; - vec3 specularColor; - calcDiffuseSpecular(baseColor.rgb, metallic, diffuseColor, specularColor); - vec3 v = -normalize(pos.xyz); - color = pbrBaseLight(diffuseColor, specularColor, metallic, v, gb.normal, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten); + color = pbrBaseLight(diffuseColor, specularColor, specularWeight, v, gb.normal, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten); } else if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_HDRI)) { @@ -208,6 +201,9 @@ void main() // legacy shaders are still writng sRGB to gbuffer baseColor.rgb = srgb_to_linear(baseColor.rgb); + // For legacy surfaces, baseColor.a indicates fullbright (skip shadows) + scol = max(scol, baseColor.a); + spec.rgb = srgb_to_linear(spec.rgb); float da = clamp(dot(gb.normal, light_dir.xyz), 0.0, 1.0); @@ -281,5 +277,20 @@ void main() final_scale = 1.1; frag_color.rgb = clampHDRRange(color.rgb * final_scale); //output linear since local lights will be added to this shader's results + + // Alpha handling for reflection probe cubemaps +#ifdef CUBE_SNAPSHOT + if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_SKIP_ATMOS)) + { + // Sky/clouds: write 1.0 alpha (will use void probe) + frag_color.a = 1.0; + } + else + { + // Everything else: write 0.0 alpha (will use local probe) + frag_color.a = 0.0; + } +#else frag_color.a = 0.0; +#endif } diff --git a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl index a46e3688dc7..276153641f1 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl @@ -80,7 +80,7 @@ const float M_PI = 3.14159265; void pbrPunctual(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 v, // surface point to camera vec3 l, // surface point to light @@ -145,16 +145,10 @@ void main() if (GET_GBUFFER_FLAG(gb.gbufferFlag, GBUFFER_FLAG_HAS_PBR)) { - vec3 orm = spec.rgb; - float perceptualRoughness = orm.g; - float metallic = orm.b; - vec3 f0 = vec3(0.04); - vec3 baseColor = diffuse.rgb; - - vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0); - diffuseColor *= 1.0 - metallic; - - vec3 specularColor = mix(f0, baseColor.rgb, metallic); + vec3 diffuseColor = diffuse.rgb; // RT0.rgb is pre-computed diffuse + float specularWeight = gb.albedo.a; // RT0.a + vec3 specularColor = spec.rgb; // RT1.rgb is F0 + float perceptualRoughness = spec.a; // RT1.a vec3 diffPunc = vec3(0); vec3 specPunc = vec3(0); @@ -175,13 +169,13 @@ void main() vec3 intensity = dist_atten * dlit * 3.25 * shadow; // Legacy attenuation, magic number to balance with legacy materials - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, normalize(lv), nl, diffPunc, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, specularWeight, n.xyz, v, normalize(lv), nl, diffPunc, specPunc); final_color += intensity * clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)); } amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ) * 3.25; //magic number to balance with legacy ambiance - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, normalize(lv), nl, diffPunc, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, specularWeight, n.xyz, v, normalize(lv), nl, diffPunc, specPunc); final_color += amb_rgb * clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)); } diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl index 349f3b9a67d..694ff51b42a 100644 --- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl @@ -24,8 +24,11 @@ */ // class3/environment/waterF.glsl - -#define WATER_MINIMAL 1 +#ifdef SSR +#define WATER_MINIMAL_PLUS 1 +#else +#define WATER_MINIMAL_PLUS 1 +#endif out vec4 frag_color; @@ -42,7 +45,7 @@ void mirrorClip(vec3 pos); // PBR interface vec2 BRDF(float NoV, float roughness); -void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor); +void calcDiffuseSpecular(vec3 baseColor, float metallic, float specularFactor, vec3 specularColorFactor, float ior, inout vec3 diffuseColor, inout vec3 specularColor, inout float specularWeight); void pbrIbl(vec3 diffuseColor, vec3 specularColor, @@ -51,12 +54,13 @@ void pbrIbl(vec3 diffuseColor, float ao, // ambient occlusion factor float nv, // normal dot view vector float perceptualRoughness, + float specularWeight, out vec3 diffuse, out vec3 specular); void pbrPunctual(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, - float metallic, + float specularWeight, vec3 n, // normal vec3 v, // surface point to camera vec3 l, // surface point to light @@ -66,7 +70,7 @@ void pbrPunctual(vec3 diffuseColor, vec3 specularColor, vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, - float metallic, + float specularWeight, vec3 pos, vec3 norm, float perceptualRoughness, @@ -289,7 +293,7 @@ void main() #endif float metallic = 1.0; - float perceptualRoughness = blurMultiplier; + float perceptualRoughness = blurMultiplier * blurMultiplier; float gloss = 1 - perceptualRoughness; vec3 irradiance = vec3(0); @@ -305,8 +309,9 @@ void main() vec3 diffuseColor = vec3(0); vec3 specularColor = vec3(0); + float waterSpecWeight = 1.0; vec3 specular_linear = srgb_to_linear(specular); - calcDiffuseSpecular(specular_linear, metallic, diffuseColor, specularColor); + calcDiffuseSpecular(specular_linear, metallic, 1.0, vec3(1.0), 1.5, diffuseColor, specularColor, waterSpecWeight); vec3 v = -normalize(pos.xyz); @@ -320,7 +325,7 @@ void main() vec3 diffPunc = vec3(0); vec3 specPunc = vec3(0); - pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, normalize(wavef+up*max(dist, 32.0)/32.0*(1.0-vdu)), v, normalize(light_dir), nl, diffPunc, specPunc); + pbrPunctual(diffuseColor, specularColor, perceptualRoughness, waterSpecWeight, normalize(wavef+up*max(dist, 32.0)/32.0*(1.0-vdu)), v, normalize(light_dir), nl, diffPunc, specPunc); vec3 punctual = clamp(nl * (diffPunc + specPunc), vec3(0), vec3(10)) * sunlit_linear * shadow * atten; radiance *= df2.y; @@ -344,6 +349,6 @@ void main() float spec = min(max(max(punctual.r, punctual.g), punctual.b), 0); - frag_color = min(vec4(1),max(vec4(color.rgb, spec * water_mask), vec4(0))); + frag_color = max(vec4(color.rgb, spec * water_mask), vec4(0)); } diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 1090dd8ffb9..f04118175d2 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 74 +version 75 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -74,6 +74,8 @@ RenderGLMultiThreadedTextures 1 0 RenderGLMultiThreadedMedia 1 1 RenderReflectionProbeResolution 1 128 RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 8 +RenderScreenSpaceReflectionsResolutionMultiplier 1 1 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 @@ -86,6 +88,7 @@ RenderTonemapType 1 1 RenderTonemapMix 1 1 RenderDisableVintageMode 1 1 RenderMaxTextureResolution 1 2048 +RenderMirrorCount 1 4 RenderReflectionProbeCount 1 256 // @@ -105,6 +108,7 @@ RenderMaxPartCount 1 0 RenderTransparentWater 1 0 RenderReflectionsEnabled 1 0 RenderReflectionProbeDetail 1 0 +RenderReflectionProbeQuality 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 RenderTerrainPBRDetail 1 -4 @@ -117,8 +121,11 @@ WLSkyDetail 1 96 RenderFSAAType 1 0 RenderFSAASamples 1 0 RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflectionGlossySamples 1 1 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 @@ -148,6 +155,7 @@ RenderLocalLightCount 1 256 RenderTransparentWater 1 0 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 +RenderReflectionProbeQuality 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 RenderTerrainPBRDetail 1 -1 @@ -160,8 +168,11 @@ WLSkyDetail 1 96 RenderFSAAType 1 0 RenderFSAASamples 1 0 RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflectionGlossySamples 1 1 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 @@ -202,9 +213,13 @@ RenderFSAAType 1 1 RenderFSAASamples 1 1 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 +RenderReflectionProbeQuality 1 1 RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflectionGlossySamples 1 2 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 1 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 @@ -244,9 +259,13 @@ RenderFSAAType 1 1 RenderFSAASamples 1 1 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 2 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 1 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 @@ -286,9 +305,13 @@ RenderFSAAType 1 2 RenderFSAASamples 1 2 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 4 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.5 RenderReflectionProbeLevel 1 2 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 @@ -328,9 +351,13 @@ RenderFSAAType 1 2 RenderFSAASamples 1 2 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 4 +RenderScreenSpaceReflectionsResolutionMultiplier 1 1 RenderReflectionProbeLevel 1 3 RenderMirrors 1 0 +RenderMirrorCount 1 2 RenderHeroProbeResolution 1 1024 RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 @@ -370,9 +397,13 @@ RenderFSAAType 1 2 RenderFSAASamples 1 3 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 8 +RenderScreenSpaceReflectionsResolutionMultiplier 1 1 RenderReflectionProbeLevel 1 3 RenderMirrors 1 0 +RenderMirrorCount 1 4 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index c3e2dd0c41c..a767e809a32 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 73 +version 74 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -72,7 +72,10 @@ RenderGLMultiThreadedMedia 1 1 RenderAppleUseMultGL 1 1 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 2 +RenderReflectionProbeQuality 1 1 RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 8 +RenderScreenSpaceReflectionsResolutionMultiplier 1 1 RenderReflectionProbeLevel 1 3 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 @@ -86,6 +89,7 @@ RenderTonemapMix 1 1 RenderDisableVintageMode 1 1 RenderDownScaleMethod 1 0 RenderMaxTextureResolution 1 2048 +RenderMirrorCount 1 4 RenderReflectionProbeCount 1 256 // @@ -116,9 +120,13 @@ RenderFSAAType 1 0 RenderFSAASamples 1 0 RenderReflectionsEnabled 1 0 RenderReflectionProbeDetail 1 0 +RenderReflectionProbeQuality 1 0 RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflectionGlossySamples 1 1 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 @@ -159,9 +167,13 @@ RenderFSAAType 1 0 RenderFSAASamples 1 0 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 +RenderReflectionProbeQuality 1 0 RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflectionGlossySamples 1 1 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 @@ -202,9 +214,13 @@ RenderFSAAType 1 1 RenderFSAASamples 1 0 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 +RenderReflectionProbeQuality 1 1 RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflectionGlossySamples 1 2 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 @@ -244,9 +260,13 @@ RenderFSAAType 1 1 RenderFSAASamples 1 1 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 2 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.25 RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 @@ -286,9 +306,13 @@ RenderFSAAType 1 1 RenderFSAASamples 1 2 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 4 +RenderScreenSpaceReflectionsResolutionMultiplier 1 0.5 RenderReflectionProbeLevel 1 1 RenderMirrors 1 0 +RenderMirrorCount 1 1 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 @@ -328,9 +352,13 @@ RenderFSAAType 1 2 RenderFSAASamples 1 2 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 4 +RenderScreenSpaceReflectionsResolutionMultiplier 1 1 RenderReflectionProbeLevel 1 2 RenderMirrors 1 0 +RenderMirrorCount 1 2 RenderHeroProbeResolution 1 512 RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 @@ -370,9 +398,13 @@ RenderFSAAType 1 2 RenderFSAASamples 1 3 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderReflectionProbeQuality 1 1 +RenderScreenSpaceReflections 1 1 +RenderScreenSpaceReflectionGlossySamples 1 8 +RenderScreenSpaceReflectionsResolutionMultiplier 1 1 RenderReflectionProbeLevel 1 3 RenderMirrors 1 0 +RenderMirrorCount 1 4 RenderHeroProbeResolution 1 1024 RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 diff --git a/indra/newview/gltf/README.md b/indra/newview/gltf/README.md index a2d43be1d61..233b55740ed 100644 --- a/indra/newview/gltf/README.md +++ b/indra/newview/gltf/README.md @@ -140,9 +140,8 @@ our existing implementations. NEVER include buffer_util.h from a header. -Loading from and saving to disk (import/export) is currently done using tinygltf, but this is not a long term -solution. Eventually the implementation should rely solely on boost::json for reading and writing .gltf -files and should handle .bin files natively. +Loading from and saving to disk (import/export) uses boost::json for reading and writing .gltf +files. The implementation handles .bin files natively via LL::GLTF::Asset. When serializing Images and Buffers to the server, clients MUST store a single UUID "uri" field and nothing else. The server MUST reject any data that violates this requirement. diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ac452b38a0b..bfa45dbf8a0 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -29,7 +29,6 @@ #include "gltfscenemanager.h" #include "llviewermenufile.h" #include "llappviewer.h" -#include "lltinygltfhelper.h" #include "llvertexbuffer.h" #include "llselectmgr.h" #include "llagent.h" @@ -153,17 +152,20 @@ void GLTFSceneManager::uploadSelection() } else { - raw = image.mTexture->getRawImage(); + LLViewerFetchedTexture* fetched = static_cast(image.mTexture.get()); + raw = fetched->getRawImage(); } if (raw.isNull()) { - raw = image.mTexture->getSavedRawImage(); + LLViewerFetchedTexture* fetched = static_cast(image.mTexture.get()); + raw = fetched->getSavedRawImage(); } if (raw.isNull()) { - image.mTexture->readbackRawImage(); + LLViewerFetchedTexture* fetched = static_cast(image.mTexture.get()); + fetched->readbackRawImage(); } if (raw.notNull()) @@ -777,7 +779,7 @@ void GLTFSceneManager::bindTexture(Asset& asset, TextureType texture_type, Textu { Texture& texture = asset.mTextures[info.mIndex]; - LLViewerTexture* tex = asset.mImages[texture.mSource].mTexture; + LLGLTexture* tex = asset.mImages[texture.mSource].mTexture; if (tex) { LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gl bind texture"); diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 6f6faf9909b..c68fcbda256 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -288,6 +288,8 @@ class LLDrawable F32 mDistanceWRTCamera; + LLMatrix4 mLastVelocityMatrix; + static F32 sCurPixelAngle; //current pixels per radian private: diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index e60b3eb5dc5..30647e7eddd 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -241,6 +241,30 @@ void LLDrawPool::renderShadow(S32 pass) } +//virtual +void LLDrawPool::beginVelocityPass(S32 pass) +{ + +} + +//virtual +void LLDrawPool::endVelocityPass(S32 pass) +{ + +} + +//virtual +S32 LLDrawPool::getNumVelocityPasses() +{ + return 0; +} + +//virtual +void LLDrawPool::renderVelocity(S32 pass) +{ + +} + //============================= // Face Pool Implementation //============================= @@ -750,6 +774,178 @@ bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinI return !skipLastSkin; } +void LLRenderPass::pushVelocityBatches(U32 type) +{ + static const LLMatrix4 identity; + auto* begin = gPipeline.beginRenderMap(type); + auto* end = gPipeline.endRenderMap(type); + + for (LLCullResult::drawinfo_iterator i = begin; i != end; ) + { + LLDrawInfo& params = **i; + LLCullResult::increment_iterator(i, end); + + if (!params.mVertexBuffer.notNull()) + { + continue; + } + + LLGLDisable cull_face(params.mGLTFMaterial && params.mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); + + applyModelMatrix(params); + + // Upload the previous matrix from the drawable + const LLMatrix4* last_mat = params.mLastModelMatrix ? params.mLastModelMatrix : &identity; + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_OBJECT_MATRIX, 1, GL_FALSE, (GLfloat*)last_mat->mMatrix); + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); + + // Store current matrix on the drawable for next frame + const LLMatrix4* current_mat = params.mModelMatrix ? params.mModelMatrix : &identity; + if (params.mLastModelMatrix) + { + *params.mLastModelMatrix = *current_mat; + } + } +} + +void LLRenderPass::pushRiggedVelocityBatches(U32 type) +{ + const LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + bool skipLastSkin = false; + + auto* begin = gPipeline.beginRenderMap(type); + auto* end = gPipeline.endRenderMap(type); + + for (LLCullResult::drawinfo_iterator i = begin; i != end; ) + { + LLDrawInfo& params = **i; + LLCullResult::increment_iterator(i, end); + + if (!params.mVertexBuffer.notNull() || !params.mAvatar) + { + continue; + } + + if (!uploadMatrixPalette(params.mAvatar, params.mSkinInfo, lastAvatar, lastMeshId, skipLastSkin)) + { + continue; + } + + uploadLastMatrixPalette(params.mAvatar, params.mSkinInfo); + + applyModelMatrix(params); + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); + } +} + +void LLRenderPass::pushVelocityBatchesTextured(U32 type) +{ + static const LLMatrix4 identity; + auto* begin = gPipeline.beginRenderMap(type); + auto* end = gPipeline.endRenderMap(type); + + for (LLCullResult::drawinfo_iterator i = begin; i != end; ) + { + LLDrawInfo& params = **i; + LLCullResult::increment_iterator(i, end); + + if (!params.mVertexBuffer.notNull()) + { + continue; + } + + LLGLDisable cull_face(params.mGLTFMaterial && params.mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); + + applyModelMatrix(params); + + if (params.mTexture.notNull()) + { + gGL.getTexUnit(0)->bindFast(params.mTexture); + } + + // Upload the previous matrix from the drawable + const LLMatrix4* last_mat = params.mLastModelMatrix ? params.mLastModelMatrix : &identity; + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_OBJECT_MATRIX, 1, GL_FALSE, (GLfloat*)last_mat->mMatrix); + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); + + // Store current matrix on the drawable for next frame + const LLMatrix4* current_mat = params.mModelMatrix ? params.mModelMatrix : &identity; + if (params.mLastModelMatrix) + { + *params.mLastModelMatrix = *current_mat; + } + } +} + +void LLRenderPass::pushRiggedVelocityBatchesTextured(U32 type) +{ + const LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + bool skipLastSkin = false; + + auto* begin = gPipeline.beginRenderMap(type); + auto* end = gPipeline.endRenderMap(type); + + for (LLCullResult::drawinfo_iterator i = begin; i != end; ) + { + LLDrawInfo& params = **i; + LLCullResult::increment_iterator(i, end); + + if (!params.mVertexBuffer.notNull() || !params.mAvatar) + { + continue; + } + + if (!uploadMatrixPalette(params.mAvatar, params.mSkinInfo, lastAvatar, lastMeshId, skipLastSkin)) + { + continue; + } + + uploadLastMatrixPalette(params.mAvatar, params.mSkinInfo); + + applyModelMatrix(params); + + if (params.mTexture.notNull()) + { + gGL.getTexUnit(0)->bindFast(params.mTexture); + } + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); + } +} + +//static +bool LLRenderPass::uploadLastMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo) +{ + if (!avatar || !skinInfo) + { + return false; + } + + const LLVOAvatar::MatrixPaletteCache& mpc = avatar->updateSkinInfoMatrixPalette(skinInfo); + U32 count = static_cast(mpc.mMatrixPalette.size()); + + if (count == 0 || mpc.mLastGLMp.empty()) + { + return false; + } + + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_LAST_MATRIX, + count, + false, + (GLfloat*)&(mpc.mLastGLMp[0])); + + return true; +} + void setup_texture_matrix(LLDrawInfo& params) { if (params.mTextureMatrix) diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index 46696fc4a4c..4e9a83f7e4c 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -110,6 +110,11 @@ class LLDrawPool virtual S32 getNumShadowPasses(); virtual void renderShadow(S32 pass = 0); + virtual void beginVelocityPass(S32 pass); + virtual void endVelocityPass(S32 pass); + virtual S32 getNumVelocityPasses(); + virtual void renderVelocity(S32 pass = 0); + virtual void render(S32 pass = 0) {}; virtual void prerender() {}; virtual U32 getVertexDataMask() { return 0; } // DEPRECATED -- draw pool doesn't actually determine vertex data mask any more @@ -345,10 +350,10 @@ class LLRenderPass : public LLDrawPool LLRenderPass(const U32 type); virtual ~LLRenderPass(); - /*virtual*/ LLViewerTexture* getDebugTexture() { return NULL; } - LLViewerTexture* getTexture() { return NULL; } - bool isDead() { return false; } - void resetDrawOrders() { } + LLViewerTexture* getDebugTexture() override { return NULL; } + LLViewerTexture* getTexture() override { return NULL; } + bool isDead() override { return false; } + void resetDrawOrders() override { } static void applyModelMatrix(const LLDrawInfo& params); // For rendering that doesn't use LLDrawInfo for some reason @@ -392,6 +397,14 @@ class LLRenderPass : public LLDrawPool static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, const LLGLSLShader*& lastAvatarShader, bool& skipLastSkin); virtual void renderGroup(LLSpatialGroup* group, U32 type, bool texture = true); virtual void renderRiggedGroup(LLSpatialGroup* group, U32 type, bool texture = true); + + // Velocity buffer helpers — iterate render map, upload per-object last/current matrices, draw + void pushVelocityBatches(U32 type); + void pushRiggedVelocityBatches(U32 type); + void pushVelocityBatchesTextured(U32 type); + void pushRiggedVelocityBatchesTextured(U32 type); + + static bool uploadLastMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo); }; class LLFacePool : public LLDrawPool @@ -408,18 +421,18 @@ class LLFacePool : public LLDrawPool LLFacePool(const U32 type); virtual ~LLFacePool(); - bool isDead() { return mReferences.empty(); } + bool isDead() override { return mReferences.empty(); } - virtual LLViewerTexture *getTexture(); + LLViewerTexture *getTexture() override; virtual void dirtyTextures(const std::set& textures); virtual void enqueue(LLFace *face); virtual bool addFace(LLFace *face); virtual bool removeFace(LLFace *face); - virtual bool verify() const; // Verify that all data in the draw pool is correct! + bool verify() const override; // Verify that all data in the draw pool is correct! - virtual void resetDrawOrders(); + void resetDrawOrders() override; void resetAll(); void destroy(); @@ -431,10 +444,10 @@ class LLFacePool : public LLDrawPool void printDebugInfo() const; - bool isFacePool() { return true; } + bool isFacePool() override { return true; } // call drawIndexed on every draw face - void pushFaceGeometry(); + void pushFaceGeometry() override; friend class LLFace; friend class LLPipeline; diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 9d1b11880b5..2eb43dbc999 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -888,3 +888,39 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged) gPipeline.enableLightsDynamic(); } } + +S32 LLDrawPoolAlpha::getNumVelocityPasses() +{ + // Only render velocity from one instance to avoid double-stamping mLastModelMatrix + if (getType() == LLDrawPool::POOL_ALPHA_PRE_WATER) + return 0; + return 1; +} + +void LLDrawPoolAlpha::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityAlphaProgram.bind(); + gVelocityAlphaProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityAlphaProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityAlphaProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolAlpha::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityAlphaProgram.unbind(); +} + +void LLDrawPoolAlpha::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatchesTextured(LLRenderPass::PASS_ALPHA); + + gVelocityAlphaProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatchesTextured(LLRenderPass::PASS_ALPHA_RIGGED); +} diff --git a/indra/newview/lldrawpoolalpha.h b/indra/newview/lldrawpoolalpha.h index 25044beda04..93f126b1518 100644 --- a/indra/newview/lldrawpoolalpha.h +++ b/indra/newview/lldrawpoolalpha.h @@ -49,17 +49,22 @@ class LLDrawPoolAlpha final: public LLRenderPass LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_TEXCOORD0 }; - virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; } + U32 getVertexDataMask() override { return VERTEX_DATA_MASK; } LLDrawPoolAlpha(U32 type); - /*virtual*/ ~LLDrawPoolAlpha(); + ~LLDrawPoolAlpha() override; - /*virtual*/ S32 getNumPostDeferredPasses(); - /*virtual*/ void renderPostDeferred(S32 pass); - /*virtual*/ S32 getNumPasses() { return 1; } + S32 getNumPostDeferredPasses() override; + void renderPostDeferred(S32 pass) override; + S32 getNumPasses() override { return 1; } + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; void forwardRender(bool write_depth = false); - /*virtual*/ void prerender(); + void prerender() override; void renderDebugAlpha(); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index f0f589e7f45..9384da22905 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -408,6 +408,74 @@ void LLDrawPoolAvatar::renderShadow(S32 pass) } } +S32 LLDrawPoolAvatar::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolAvatar::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + sVertexProgram = &gAvatarVelocityProgram; + + if (sShaderLevel > 0) + { + sRenderingSkinned = true; + sVertexProgram->bind(); + } + + sVertexProgram->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + sVertexProgram->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + sVertexProgram->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + + gGL.diffuseColor4f(1, 1, 1, 1); +} + +void LLDrawPoolAvatar::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + if (sShaderLevel > 0) + { + sVertexProgram->unbind(); + } + sVertexProgram = NULL; + sRenderingSkinned = false; +} + +void LLDrawPoolAvatar::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + LLGLEnable cull(GL_CULL_FACE); + + if (mDrawFace.empty()) + { + return; + } + + const LLFace* facep = mDrawFace[0]; + if (!facep->getDrawable()) + { + return; + } + LLVOAvatar* avatarp = (LLVOAvatar*)facep->getDrawable()->getVObj().get(); + + if (avatarp->isDead() || avatarp->isUIAvatar() || avatarp->mDrawable.isNull()) + { + return; + } + + LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance(); + bool impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor(); + if (avatarp->isTooSlow() || impostor || (oa == LLVOAvatar::AOA_INVISIBLE)) + { + return; + } + + avatarp->renderSkinned(); +} + S32 LLDrawPoolAvatar::getNumPasses() { return 3; diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index 87a87e225ea..19f0dab5741 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -54,7 +54,7 @@ class LLDrawPoolAvatar : public LLFacePool }; ~LLDrawPoolAvatar(); - /*virtual*/ bool isDead(); + bool isDead() override; typedef enum { @@ -64,34 +64,39 @@ typedef enum NUM_SHADOW_PASSES } eShadowPass; - virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; } + U32 getVertexDataMask() override { return VERTEX_DATA_MASK; } - virtual S32 getShaderLevel() const; + S32 getShaderLevel() const override; LLDrawPoolAvatar(U32 type); static LLMatrix4& getModelView(); - /*virtual*/ S32 getNumPasses(); - /*virtual*/ void beginRenderPass(S32 pass); - /*virtual*/ void endRenderPass(S32 pass); - /*virtual*/ void prerender(); - /*virtual*/ void render(S32 pass = 0); + S32 getNumPasses() override; + void beginRenderPass(S32 pass) override; + void endRenderPass(S32 pass) override; + void prerender() override; + void render(S32 pass = 0) override; - /*virtual*/ S32 getNumDeferredPasses(); - /*virtual*/ void beginDeferredPass(S32 pass); - /*virtual*/ void endDeferredPass(S32 pass); - /*virtual*/ void renderDeferred(S32 pass); + S32 getNumDeferredPasses() override; + void beginDeferredPass(S32 pass) override; + void endDeferredPass(S32 pass) override; + void renderDeferred(S32 pass) override; - /*virtual*/ S32 getNumPostDeferredPasses(); - /*virtual*/ void beginPostDeferredPass(S32 pass); - /*virtual*/ void endPostDeferredPass(S32 pass); - /*virtual*/ void renderPostDeferred(S32 pass); + S32 getNumPostDeferredPasses() override; + void beginPostDeferredPass(S32 pass) override; + void endPostDeferredPass(S32 pass) override; + void renderPostDeferred(S32 pass) override; - /*virtual*/ S32 getNumShadowPasses(); - /*virtual*/ void beginShadowPass(S32 pass); - /*virtual*/ void endShadowPass(S32 pass); - /*virtual*/ void renderShadow(S32 pass); + S32 getNumShadowPasses() override; + void beginShadowPass(S32 pass) override; + void endShadowPass(S32 pass) override; + void renderShadow(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; void beginRigid(); void beginImpostor(); @@ -109,8 +114,8 @@ typedef enum void endDeferredImpostor(); void endDeferredSkinned(); - /*virtual*/ LLViewerTexture *getDebugTexture(); - /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display + LLViewerTexture *getDebugTexture() override; + LLColor3 getDebugColor() const; // For AGP debug display void renderAvatars(LLVOAvatar *single_avatar, S32 pass = -1); // renders only one avatar if single_avatar is not null. diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 6c151351ff9..78980858a53 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -592,6 +592,39 @@ void LLDrawPoolBump::renderDeferred(S32 pass) } +S32 LLDrawPoolBump::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolBump::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolBump::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.unbind(); +} + +void LLDrawPoolBump::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatches(LLRenderPass::PASS_BUMP); + + gVelocityProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatches(LLRenderPass::PASS_BUMP_RIGGED); +} + void LLDrawPoolBump::renderPostDeferred(S32 pass) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h index e1a468cd185..cdc1660d760 100644 --- a/indra/newview/lldrawpoolbump.h +++ b/indra/newview/lldrawpoolbump.h @@ -48,11 +48,11 @@ protected : static U32 sVertexMask; bool mShiny; - virtual U32 getVertexDataMask() override { return sVertexMask; } + U32 getVertexDataMask() override { return sVertexMask; } LLDrawPoolBump(); - /*virtual*/ void prerender() override; + void prerender() override; void pushBumpBatches(U32 type); void renderGroup(LLSpatialGroup* group, U32 type, bool texture) override; @@ -70,11 +70,16 @@ protected : static void bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel); static void unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel); - virtual S32 getNumDeferredPasses() override; - /*virtual*/ void renderDeferred(S32 pass) override; + S32 getNumDeferredPasses() override; + void renderDeferred(S32 pass) override; - virtual S32 getNumPostDeferredPasses() override { return 1; } - /*virtual*/ void renderPostDeferred(S32 pass) override; + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; + + S32 getNumPostDeferredPasses() override { return 1; } + void renderPostDeferred(S32 pass) override; static bool bindBumpMap(LLDrawInfo& params, S32 channel = -2); static bool bindBumpMap(LLFace* face, S32 channel = -2); diff --git a/indra/newview/lldrawpoolmaterials.cpp b/indra/newview/lldrawpoolmaterials.cpp index e7ec2022d25..c2e6dfcbe46 100644 --- a/indra/newview/lldrawpoolmaterials.cpp +++ b/indra/newview/lldrawpoolmaterials.cpp @@ -29,6 +29,7 @@ #include "lldrawpoolmaterials.h" #include "llviewershadermgr.h" +#include "llrender.h" #include "pipeline.h" #include "llglcommonfunc.h" #include "llvoavatar.h" @@ -287,3 +288,61 @@ void LLDrawPoolMaterials::renderDeferred(S32 pass) } } } + +S32 LLDrawPoolMaterials::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolMaterials::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolMaterials::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.unbind(); +} + +void LLDrawPoolMaterials::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + + // Static passes + pushVelocityBatches(LLRenderPass::PASS_MATERIAL); + pushVelocityBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK); + pushVelocityBatches(LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE); + pushVelocityBatches(LLRenderPass::PASS_SPECMAP); + pushVelocityBatches(LLRenderPass::PASS_SPECMAP_MASK); + pushVelocityBatches(LLRenderPass::PASS_SPECMAP_EMISSIVE); + pushVelocityBatches(LLRenderPass::PASS_NORMMAP); + pushVelocityBatches(LLRenderPass::PASS_NORMMAP_MASK); + pushVelocityBatches(LLRenderPass::PASS_NORMMAP_EMISSIVE); + pushVelocityBatches(LLRenderPass::PASS_NORMSPEC); + pushVelocityBatches(LLRenderPass::PASS_NORMSPEC_MASK); + pushVelocityBatches(LLRenderPass::PASS_NORMSPEC_EMISSIVE); + + // Rigged passes + gVelocityProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatches(LLRenderPass::PASS_MATERIAL_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_MATERIAL_ALPHA_MASK_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_MATERIAL_ALPHA_EMISSIVE_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_SPECMAP_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_SPECMAP_MASK_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_SPECMAP_EMISSIVE_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_NORMMAP_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_NORMMAP_MASK_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_NORMMAP_EMISSIVE_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_NORMSPEC_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_NORMSPEC_MASK_RIGGED); + pushRiggedVelocityBatches(LLRenderPass::PASS_NORMSPEC_EMISSIVE_RIGGED); +} diff --git a/indra/newview/lldrawpoolmaterials.h b/indra/newview/lldrawpoolmaterials.h index 345697ffd15..0c4c11fd964 100644 --- a/indra/newview/lldrawpoolmaterials.h +++ b/indra/newview/lldrawpoolmaterials.h @@ -65,6 +65,11 @@ class LLDrawPoolMaterials : public LLRenderPass void beginDeferredPass(S32 pass) override; void endDeferredPass(S32 pass) override; void renderDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; #endif //LL_LLDRAWPOOLMATERIALS_H diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp index 07f8ccb5496..5b185ab656c 100644 --- a/indra/newview/lldrawpoolpbropaque.cpp +++ b/indra/newview/lldrawpoolpbropaque.cpp @@ -29,6 +29,7 @@ #include "lldrawpool.h" #include "lldrawpoolpbropaque.h" #include "llviewershadermgr.h" +#include "llrender.h" #include "pipeline.h" #include "gltfscenemanager.h" @@ -93,3 +94,53 @@ void LLDrawPoolGLTFPBR::renderPostDeferred(S32 pass) } } +S32 LLDrawPoolGLTFPBR::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolGLTFPBR::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLSLShader& shader = (mRenderType == LLPipeline::RENDER_TYPE_PASS_GLTF_PBR_ALPHA_MASK) + ? gVelocityAlphaProgram : gVelocityProgram; + shader.bind(); + shader.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + shader.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + shader.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolGLTFPBR::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + if (mRenderType == LLPipeline::RENDER_TYPE_PASS_GLTF_PBR_ALPHA_MASK) + gVelocityAlphaProgram.unbind(); + else + gVelocityProgram.unbind(); +} + +void LLDrawPoolGLTFPBR::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + + bool alpha_mask = (mRenderType == LLPipeline::RENDER_TYPE_PASS_GLTF_PBR_ALPHA_MASK); + + // Static pass for this pool type only + if (alpha_mask) + pushVelocityBatchesTextured(mRenderType); + else + pushVelocityBatches(mRenderType); + + // Rigged pass for this pool type only + LLGLSLShader& shader = alpha_mask ? gVelocityAlphaProgram : gVelocityProgram; + shader.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + if (alpha_mask) + pushRiggedVelocityBatchesTextured(mRenderType + 1); + else + pushRiggedVelocityBatches(mRenderType + 1); +} + diff --git a/indra/newview/lldrawpoolpbropaque.h b/indra/newview/lldrawpoolpbropaque.h index a029a4ff44a..e154fe0064c 100644 --- a/indra/newview/lldrawpoolpbropaque.h +++ b/indra/newview/lldrawpoolpbropaque.h @@ -41,6 +41,11 @@ class LLDrawPoolGLTFPBR final : public LLRenderPass S32 getNumPostDeferredPasses() override; void renderPostDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; #endif // LL_LLDRAWPOOLPBROPAQUE_H diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp index 836a90adab7..ae7ac7ba7cc 100644 --- a/indra/newview/lldrawpoolsimple.cpp +++ b/indra/newview/lldrawpoolsimple.cpp @@ -181,6 +181,174 @@ void LLDrawPoolFullbright::renderPostDeferred(S32 pass) } } +// ============================================ +// Velocity Buffer Passes +// ============================================ + +// LLDrawPoolSimple +S32 LLDrawPoolSimple::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolSimple::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolSimple::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.unbind(); +} + +void LLDrawPoolSimple::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatches(LLRenderPass::PASS_SIMPLE); + + gVelocityProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatches(LLRenderPass::PASS_SIMPLE_RIGGED); +} + +// LLDrawPoolGrass +S32 LLDrawPoolGrass::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolGrass::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolGrass::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.unbind(); +} + +void LLDrawPoolGrass::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatches(LLRenderPass::PASS_GRASS); +} + +// LLDrawPoolAlphaMask +S32 LLDrawPoolAlphaMask::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolAlphaMask::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityAlphaProgram.bind(); + gVelocityAlphaProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityAlphaProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityAlphaProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolAlphaMask::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityAlphaProgram.unbind(); +} + +void LLDrawPoolAlphaMask::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatchesTextured(LLRenderPass::PASS_ALPHA_MASK); + + gVelocityAlphaProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatchesTextured(LLRenderPass::PASS_ALPHA_MASK_RIGGED); +} + +// LLDrawPoolFullbrightAlphaMask +S32 LLDrawPoolFullbrightAlphaMask::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolFullbrightAlphaMask::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityAlphaProgram.bind(); + gVelocityAlphaProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityAlphaProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityAlphaProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolFullbrightAlphaMask::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityAlphaProgram.unbind(); +} + +void LLDrawPoolFullbrightAlphaMask::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatchesTextured(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK); + + gVelocityAlphaProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatchesTextured(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED); +} + +// LLDrawPoolFullbright +S32 LLDrawPoolFullbright::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolFullbright::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolFullbright::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + gVelocityProgram.unbind(); +} + +void LLDrawPoolFullbright::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + pushVelocityBatches(LLRenderPass::PASS_FULLBRIGHT); + + gVelocityProgram.bind(true); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + LLGLSLShader::sCurBoundShaderPtr->uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); + pushRiggedVelocityBatches(LLRenderPass::PASS_FULLBRIGHT_RIGGED); +} + void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_FULLBRIGHT); diff --git a/indra/newview/lldrawpoolsimple.h b/indra/newview/lldrawpoolsimple.h index da7fdaaa442..ce80f1618ff 100644 --- a/indra/newview/lldrawpoolsimple.h +++ b/indra/newview/lldrawpoolsimple.h @@ -47,6 +47,11 @@ class LLDrawPoolSimple final : public LLRenderPass S32 getNumDeferredPasses() override; void renderDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; class LLDrawPoolGrass final : public LLRenderPass @@ -65,6 +70,11 @@ class LLDrawPoolGrass final : public LLRenderPass S32 getNumDeferredPasses() override { return 1; } void renderDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; class LLDrawPoolAlphaMask final : public LLRenderPass @@ -83,6 +93,11 @@ class LLDrawPoolAlphaMask final : public LLRenderPass S32 getNumDeferredPasses() override { return 1; } void renderDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; class LLDrawPoolFullbrightAlphaMask final : public LLRenderPass @@ -100,6 +115,11 @@ class LLDrawPoolFullbrightAlphaMask final : public LLRenderPass S32 getNumPostDeferredPasses() override { return 1; } void renderPostDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; @@ -118,6 +138,11 @@ class LLDrawPoolFullbright final : public LLRenderPass S32 getNumPostDeferredPasses() override { return 1; } void renderPostDeferred(S32 pass) override; + + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; }; class LLDrawPoolGlow final : public LLRenderPass diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 5e676bc5b3d..5a28e18c762 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -201,6 +201,60 @@ void LLDrawPoolTerrain::drawLoop() } } +//============================================ +// velocity buffer implementation +//============================================ +S32 LLDrawPoolTerrain::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolTerrain::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolTerrain::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + + gVelocityProgram.unbind(); +} + +void LLDrawPoolTerrain::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + LLGLEnable cull(GL_CULL_FACE); + + if (mDrawFace.empty()) + { + return; + } + + for (std::vector::iterator iter = mDrawFace.begin(); + iter != mDrawFace.end(); iter++) + { + LLFace *facep = *iter; + LLDrawable* drawable = facep->getDrawable(); + if (!drawable) continue; + + LLMatrix4* model_matrix = &(drawable->getRegion()->mRenderMatrix); + + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); + LLRenderPass::applyModelMatrix(model_matrix); + + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_OBJECT_MATRIX, 1, GL_FALSE, (GLfloat*)model_matrix->mMatrix); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_OBJECT_MATRIX, 1, GL_FALSE, (GLfloat*)model_matrix->mMatrix); + + facep->renderIndexed(); + } +} + void LLDrawPoolTerrain::renderFullShader() { const bool use_local_materials = gLocalTerrainMaterials.makeMaterialsReady(true, false); diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h index 23cf253b6a4..b23975ead08 100644 --- a/indra/newview/lldrawpoolterrain.h +++ b/indra/newview/lldrawpoolterrain.h @@ -42,25 +42,30 @@ class LLDrawPoolTerrain : public LLFacePool LLVertexBuffer::MAP_TEXCOORD1 }; - virtual U32 getVertexDataMask(); + U32 getVertexDataMask() override; LLDrawPoolTerrain(LLViewerTexture *texturep); virtual ~LLDrawPoolTerrain(); - /*virtual*/ S32 getNumDeferredPasses() { return 1; } - /*virtual*/ void beginDeferredPass(S32 pass); - /*virtual*/ void endDeferredPass(S32 pass); - /*virtual*/ void renderDeferred(S32 pass); + S32 getNumDeferredPasses() override { return 1; } + void beginDeferredPass(S32 pass) override; + void endDeferredPass(S32 pass) override; + void renderDeferred(S32 pass) override; - /*virtual*/ S32 getNumShadowPasses() { return 1; } - /*virtual*/ void beginShadowPass(S32 pass); - /*virtual*/ void endShadowPass(S32 pass); - /*virtual*/ void renderShadow(S32 pass); + S32 getNumShadowPasses() override { return 1; } + void beginShadowPass(S32 pass) override; + void endShadowPass(S32 pass) override; + void renderShadow(S32 pass) override; - /*virtual*/ void prerender(); - /*virtual*/ void dirtyTextures(const std::set& textures); - /*virtual*/ LLViewerTexture *getTexture(); - /*virtual*/ LLViewerTexture *getDebugTexture(); - /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; + + void prerender() override; + void dirtyTextures(const std::set& textures) override; + LLViewerTexture *getTexture() override; + LLViewerTexture *getDebugTexture() override; + LLColor3 getDebugColor() const; // For AGP debug display LLPointer mAlphaRampImagep; LLPointer m2DAlphaRampImagep; diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 26ef190fbbf..34ce4bde14a 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -133,6 +133,63 @@ void LLDrawPoolTree::endShadowPass(S32 pass) gDeferredTreeShadowProgram.unbind(); } +//============================================ +// velocity buffer implementation +//============================================ +S32 LLDrawPoolTree::getNumVelocityPasses() +{ + return 1; +} + +void LLDrawPoolTree::beginVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + + gVelocityProgram.bind(); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::LAST_MODELVIEW_MATRIX, 1, GL_FALSE, gGLLastModelView); + gVelocityProgram.uniformMatrix4fv(LLShaderMgr::CURRENT_MODELVIEW_MATRIX, 1, GL_FALSE, gGLModelView); + gVelocityProgram.uniform4f(LLShaderMgr::VIEWPORT, (F32)gGLViewport[0], (F32)gGLViewport[1], (F32)gGLViewport[2], (F32)gGLViewport[3]); +} + +void LLDrawPoolTree::endVelocityPass(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + + gVelocityProgram.unbind(); +} + +void LLDrawPoolTree::renderVelocity(S32 pass) +{ + LL_PROFILE_ZONE_SCOPED; + LLGLEnable cull(GL_CULL_FACE); + + if (mDrawFace.empty()) + { + return; + } + + for (std::vector::iterator iter = mDrawFace.begin(); + iter != mDrawFace.end(); iter++) + { + LLFace* face = *iter; + LLVertexBuffer* buff = face->getVertexBuffer(); + + if (buff) + { + LLMatrix4* model_matrix = &(face->getDrawable()->getRegion()->mRenderMatrix); + + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); + LLRenderPass::applyModelMatrix(model_matrix); + + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::CURRENT_OBJECT_MATRIX, 1, GL_FALSE, (GLfloat*)model_matrix->mMatrix); + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix4fv(LLShaderMgr::LAST_OBJECT_MATRIX, 1, GL_FALSE, (GLfloat*)model_matrix->mMatrix); + + buff->setBuffer(); + buff->drawRange(LLRender::TRIANGLES, 0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); + } + } +} + bool LLDrawPoolTree::verify() const { return true; diff --git a/indra/newview/lldrawpooltree.h b/indra/newview/lldrawpooltree.h index b13ae6c16d2..35cd6a0784c 100644 --- a/indra/newview/lldrawpooltree.h +++ b/indra/newview/lldrawpooltree.h @@ -41,23 +41,28 @@ class LLDrawPoolTree : public LLFacePool LLVertexBuffer::MAP_TEXCOORD0 }; - virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; } + U32 getVertexDataMask() override { return VERTEX_DATA_MASK; } LLDrawPoolTree(LLViewerTexture *texturep); - /*virtual*/ S32 getNumDeferredPasses() { return 1; } - /*virtual*/ void beginDeferredPass(S32 pass); - /*virtual*/ void endDeferredPass(S32 pass); - /*virtual*/ void renderDeferred(S32 pass); + S32 getNumDeferredPasses() override { return 1; } + void beginDeferredPass(S32 pass) override; + void endDeferredPass(S32 pass) override; + void renderDeferred(S32 pass) override; - /*virtual*/ S32 getNumShadowPasses() { return 1; } - /*virtual*/ void beginShadowPass(S32 pass); - /*virtual*/ void endShadowPass(S32 pass); - /*virtual*/ void renderShadow(S32 pass); + S32 getNumShadowPasses() override { return 1; } + void beginShadowPass(S32 pass) override; + void endShadowPass(S32 pass) override; + void renderShadow(S32 pass) override; - /*virtual*/ bool verify() const; - /*virtual*/ LLViewerTexture *getTexture(); - /*virtual*/ LLViewerTexture *getDebugTexture(); + S32 getNumVelocityPasses() override; + void beginVelocityPass(S32 pass) override; + void endVelocityPass(S32 pass) override; + void renderVelocity(S32 pass) override; + + bool verify() const override; + LLViewerTexture *getTexture() override; + LLViewerTexture *getDebugTexture() override; /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display static S32 sDiffTex; diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index cdf3244389b..03794868452 100644 --- a/indra/newview/lldrawpoolwater.cpp +++ b/indra/newview/lldrawpoolwater.cpp @@ -258,15 +258,31 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass) static LLStaticHashedString tonemap_type("tonemap_type"); static LLCachedControl exposure(gSavedSettings, "RenderExposure", 1.f); + static LLCachedControl should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", false); + static LLCachedControl use_env_hdr_settings(gSavedSettings, "RenderHDRUseEnvironmentSettings", false); + static LLCachedControl tonemap_type_setting(gSavedSettings, "RenderTonemapType", 0U); + static LLCachedControl tonemap_mix_setting(gSavedSettings, "RenderTonemapMix", 1.f); - F32 e = llclamp(exposure(), 0.5f, 4.f); + F32 e; + U32 tonemap_type_value; + F32 tonemap_mix_value; - static LLCachedControl should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", false); + if (use_env_hdr_settings && psky->getSkySettingVersion() > 1) + { + e = llclamp(psky->getHDROffset(should_auto_adjust()), 0.5f, 4.f); + tonemap_type_value = psky->getTonemapper(); + tonemap_mix_value = psky->getTonemapMix(should_auto_adjust()); + } + else + { + e = llclamp(exposure(), 0.5f, 4.f); + tonemap_type_value = tonemap_type_setting(); + tonemap_mix_value = tonemap_mix_setting(); + } shader->uniform1f(s_exposure, e); - static LLCachedControl tonemap_type_setting(gSavedSettings, "RenderTonemapType", 0U); - shader->uniform1i(tonemap_type, tonemap_type_setting); - shader->uniform1f(tonemap_mix, psky->getTonemapMix(should_auto_adjust())); + shader->uniform1i(tonemap_type, tonemap_type_value); + shader->uniform1f(tonemap_mix, tonemap_mix_value); F32 sunAngle = llmax(0.f, light_dir.mV[1]); F32 scaledAngle = 1.f - sunAngle; @@ -335,3 +351,71 @@ LLColor3 LLDrawPoolWater::getDebugColor() const { return LLColor3(0.f, 1.f, 1.f); } + +void LLDrawPoolWater::renderSSR() +{ + if (!gSSRWaterProgram.isComplete()) return; + if (mDrawFace.empty()) return; + + LL_PROFILE_GPU_ZONE("SSR water"); + + LLEnvironment& environment = LLEnvironment::instance(); + LLSettingsWater::ptr_t pwater = environment.getCurrentWater(); + + bool has_normal_mips = gSavedSettings.getBOOL("RenderWaterMipNormal"); + LLTexUnit::eTextureFilterOptions filter_mode = has_normal_mips ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT; + + F32 phase_time = (F32)LLFrameTimer::getElapsedSeconds() * 0.5f; + F32 blend_factor = (F32)pwater->getBlendFactor(); + + gPipeline.bindDeferredShader(gSSRWaterProgram); + + LLViewerTexture* tex_a = mWaterNormp[0]; + LLViewerTexture* tex_b = mWaterNormp[1]; + + if (tex_a && (!tex_b || (tex_a == tex_b))) + { + gSSRWaterProgram.bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_a); + tex_a->setFilteringOption(filter_mode); + blend_factor = 0; + } + else if (tex_b && !tex_a) + { + gSSRWaterProgram.bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_b); + tex_b->setFilteringOption(filter_mode); + blend_factor = 0; + } + else if (tex_b != tex_a) + { + gSSRWaterProgram.bindTexture(LLViewerShaderMgr::BUMP_MAP, tex_a); + tex_a->setFilteringOption(filter_mode); + gSSRWaterProgram.bindTexture(LLViewerShaderMgr::BUMP_MAP2, tex_b); + tex_b->setFilteringOption(filter_mode); + } + + gSSRWaterProgram.uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor); + + F32 water_height = environment.getWaterHeight(); + F32 camera_height = LLViewerCamera::getInstance()->getOrigin().mV[2]; + gSSRWaterProgram.uniform1f(LLShaderMgr::WATER_WATERHEIGHT, camera_height - water_height); + gSSRWaterProgram.uniform1f(LLShaderMgr::WATER_TIME, phase_time); + gSSRWaterProgram.uniform3fv(LLShaderMgr::WATER_EYEVEC, 1, LLViewerCamera::getInstance()->getOrigin().mV); + gSSRWaterProgram.uniform2fv(LLShaderMgr::WATER_WAVE_DIR1, 1, pwater->getWave1Dir().mV); + gSSRWaterProgram.uniform2fv(LLShaderMgr::WATER_WAVE_DIR2, 1, pwater->getWave2Dir().mV); + + LLVector3 light_dir = environment.getLightDirection(); + light_dir.normalize(); + gSSRWaterProgram.uniform3fv(LLShaderMgr::WATER_LIGHT_DIR, 1, light_dir.mV); + gSSRWaterProgram.uniform3fv(LLShaderMgr::WATER_NORM_SCALE, 1, pwater->getNormalScale().mV); + gSSRWaterProgram.uniform1f(LLShaderMgr::WATER_BLUR_MULTIPLIER, fmaxf(0, pwater->getBlurMultiplier()) * 2); + + LLVector4 rotated_light_direction = LLEnvironment::instance().getClampedLightNorm(); + gSSRWaterProgram.uniform3fv(LLViewerShaderMgr::LIGHTNORM, 1, rotated_light_direction.mV); + gSSRWaterProgram.uniform3fv(LLShaderMgr::WL_CAMPOSLOCAL, 1, LLViewerCamera::getInstance()->getOrigin().mV); + + LLGLDisable cullface(GL_CULL_FACE); + + pushWaterPlanes(0); + + gPipeline.unbindDeferredShader(gSSRWaterProgram); +} diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h index 7fc9b68bcf3..0cc46759957 100644 --- a/indra/newview/lldrawpoolwater.h +++ b/indra/newview/lldrawpoolwater.h @@ -75,6 +75,7 @@ class LLDrawPoolWater final: public LLFacePool void setNormalMaps(const LLUUID& normalMapId, const LLUUID& nextNormalMapId); void pushWaterPlanes(int pass); + void renderSSR(); protected: void renderOpaqueLegacyWater(); diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index e6d0b036e0d..4eff2a6a561 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -181,6 +181,7 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca LLGLSPipelineDepthTestSkyBox sky(true, true); sky_shader->uniform1i(LLShaderMgr::CUBE_SNAPSHOT, gCubeSnapshot ? 1 : 0); + sky_shader->uniform1i(LLShaderMgr::DEFAULT_PROBE_RENDER, LLPipeline::sDefaultProbeRender ? 1 : 0); LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); @@ -340,6 +341,7 @@ void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32 cloudshader->uniform1f(LLShaderMgr::BLEND_FACTOR, blend_factor); cloudshader->uniform1f(LLShaderMgr::CLOUD_VARIANCE, cloud_variance); cloudshader->uniform1f(LLShaderMgr::SUN_MOON_GLOW_FACTOR, psky->getSunMoonGlowFactor()); + cloudshader->uniform1i(LLShaderMgr::DEFAULT_PROBE_RENDER, LLPipeline::sDefaultProbeRender ? 1 : 0); /// Render the skydome renderDome(camPosLocal, camHeightLocal, cloudshader); diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp index a05f7256730..5b07f32cb64 100644 --- a/indra/newview/llfetchedgltfmaterial.cpp +++ b/indra/newview/llfetchedgltfmaterial.cpp @@ -57,6 +57,7 @@ LLFetchedGLTFMaterial& LLFetchedGLTFMaterial::operator=(const LLFetchedGLTFMater mNormalTexture = rhs.mNormalTexture; mMetallicRoughnessTexture = rhs.mMetallicRoughnessTexture; mEmissiveTexture = rhs.mEmissiveTexture; + mSpecularTexture = rhs.mSpecularTexture; return *this; } @@ -129,6 +130,10 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex) shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, mRoughnessFactor); shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, mMetallicFactor); shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, mEmissiveColor.mV); + shader->uniform1f(LLShaderMgr::SPECULAR_FACTOR, mSpecularFactor); + shader->uniform3fv(LLShaderMgr::SPECULAR_COLOR_FACTOR, 1, mSpecularColorFactor.mV); + shader->uniform1f(LLShaderMgr::EMISSIVE_STRENGTH, mEmissiveStrength); + shader->uniform1f(LLShaderMgr::IOR, mIOR); F32 normal_packed[8]; mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed); @@ -182,6 +187,12 @@ bool LLFetchedGLTFMaterial::replaceLocalTexture(const LLUUID& tracking_id, const mEmissiveTexture = fetch_texture(new_id); res = true; } + if (mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR] == old_id) + { + mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR] = new_id; + mSpecularTexture = fetch_texture(new_id); + res = true; + } for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) { @@ -228,6 +239,7 @@ void LLFetchedGLTFMaterial::clearFetchedTextures() mNormalTexture = nullptr; mMetallicRoughnessTexture = nullptr; mEmissiveTexture = nullptr; + mSpecularTexture = nullptr; } void LLFetchedGLTFMaterial::materialBegin() diff --git a/indra/newview/llfetchedgltfmaterial.h b/indra/newview/llfetchedgltfmaterial.h index 074e3fef415..5c9f847c6dc 100644 --- a/indra/newview/llfetchedgltfmaterial.h +++ b/indra/newview/llfetchedgltfmaterial.h @@ -67,6 +67,7 @@ class LLFetchedGLTFMaterial: public LLGLTFMaterial LLPointer mNormalTexture; LLPointer mMetallicRoughnessTexture; LLPointer mEmissiveTexture; + LLPointer mSpecularTexture; void clearFetchedTextures(); std::set mTextureEntires; diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index 0a8b8d321d0..87253b42734 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -82,6 +82,11 @@ namespace { const std::string BTN_SAVE("save_btn"); const std::string BTN_FLYOUT("btn_flyout"); const std::string BTN_CANCEL("cancel_btn"); + const std::string BTN_UPGRADE("btn_upgrade"); + const std::string BTN_UPGRADE_FLYOUT("btn_upgrade_flyout"); + const std::string XML_UPGRADE_MENU_FILE("menu_upgrade_settings.xml"); + const std::string ACTION_UPGRADE_CURRENT("upgrade_current"); + const std::string ACTION_UPGRADE_ALL("upgrade_all"); const std::string BTN_ADDFRAME("add_frame"); const std::string BTN_DELFRAME("delete_frame"); const std::string BTN_IMPORT("btn_import"); @@ -166,6 +171,7 @@ class LLDaySettingCopiedCallback : public LLInventoryCallback LLFloaterEditExtDayCycle::LLFloaterEditExtDayCycle(const LLSD &key) : LLFloaterEditEnvironmentBase(key), mFlyoutControl(nullptr), + mUpgradeFlyoutControl(nullptr), mDayLength(0), mCurrentTrack(1), mShiftCopyEnabled(false), @@ -198,6 +204,7 @@ LLFloaterEditExtDayCycle::~LLFloaterEditExtDayCycle() // Todo: consider remaking mFlyoutControl into full view class that initializes intself with floater, // complete with postbuild, e t c... delete mFlyoutControl; + delete mUpgradeFlyoutControl; } // virtual @@ -223,6 +230,10 @@ bool LLFloaterEditExtDayCycle::postBuild() mNameEditor->setKeystrokeCallback([this](LLLineEditor*, void*) { onNameKeystroke(); }, NULL); mCancelButton->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickCloseBtn(); }); + + mUpgradeFlyoutControl = new LLFlyoutComboBtnCtrl(this, BTN_UPGRADE, BTN_UPGRADE_FLYOUT, XML_UPGRADE_MENU_FILE, true); + mUpgradeFlyoutControl->setAction([this](LLUICtrl *ctrl, const LLSD &) { onButtonUpgrade(ctrl); }); + mTimeSlider->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTimeSliderCallback(); }); mAddFrameButton->setCommitCallback([this](LLUICtrl*, const LLSD&) { onAddFrame(); }); mDeleteFrameButton->setCommitCallback([this](LLUICtrl*, const LLSD&) { onRemoveFrame(); }); @@ -1222,6 +1233,47 @@ void LLFloaterEditExtDayCycle::updateButtons() button->setEnabled(extended_env); button->setToggleState(track == mCurrentTrack); } + + // upgrade button - only visible for sky tracks with a sky below max version + bool show_upgrade = false; + if (mCurrentTrack != LLSettingsDay::TRACK_WATER && mEditSky) + { + show_upgrade = mEditSky->getSkySettingVersion() < LLSettingsSky::MAX_SKY_SETTINGS_VERSION; + } + getChild(BTN_UPGRADE)->setVisible(show_upgrade); + getChild(BTN_UPGRADE_FLYOUT)->setVisible(show_upgrade); +} + +void LLFloaterEditExtDayCycle::onButtonUpgrade(LLUICtrl *ctrl) +{ + std::string action = ctrl->getName(); + + if (action == ACTION_UPGRADE_ALL) + { + if (!mEditDay) return; + for (U32 track = 1; track < LLSettingsDay::TRACK_MAX; ++track) + { + LLSettingsDay::CycleTrack_t &cycle_track = mEditDay->getCycleTrack(track); + for (auto &frame : cycle_track) + { + LLSettingsSky::ptr_t sky = std::static_pointer_cast(frame.second); + if (sky && sky->getSkySettingVersion() < LLSettingsSky::MAX_SKY_SETTINGS_VERSION) + { + sky->setSkySettingVersion(LLSettingsSky::MAX_SKY_SETTINGS_VERSION); + sky->update(); + } + } + } + } + else + { + if (!mEditSky) return; + mEditSky->setSkySettingVersion(LLSettingsSky::MAX_SKY_SETTINGS_VERSION); + mEditSky->update(); + } + + synchronizeTabs(); + updateButtons(); } void LLFloaterEditExtDayCycle::updateSlider() diff --git a/indra/newview/llfloatereditextdaycycle.h b/indra/newview/llfloatereditextdaycycle.h index 992b532967e..4eca5b67923 100644 --- a/indra/newview/llfloatereditextdaycycle.h +++ b/indra/newview/llfloatereditextdaycycle.h @@ -188,6 +188,7 @@ class LLFloaterEditExtDayCycle : public LLFloaterEditEnvironmentBase bool isAddingFrameAllowed(); void showHDRNotification(const LLSettingsDay::ptr_t &pday); + void onButtonUpgrade(LLUICtrl *ctrl); LLSettingsDay::ptr_t mEditDay; // edited copy LLSettingsDay::Seconds mDayLength; @@ -210,6 +211,7 @@ class LLFloaterEditExtDayCycle : public LLFloaterEditEnvironmentBase LLView* mWaterTabLayoutContainer; LLTextBox* mCurrentTimeLabel; LLFlyoutComboBtnCtrl * mFlyoutControl; + LLFlyoutComboBtnCtrl * mUpgradeFlyoutControl; LLHandle mTrackFloater; diff --git a/indra/newview/llfloaterenvironmentadjust.cpp b/indra/newview/llfloaterenvironmentadjust.cpp index 4825cbf7fb0..86bea0d2444 100644 --- a/indra/newview/llfloaterenvironmentadjust.cpp +++ b/indra/newview/llfloaterenvironmentadjust.cpp @@ -60,11 +60,19 @@ namespace const std::string FIELD_SKY_GLOW_FOCUS("glow_focus"); const std::string FIELD_SKY_GLOW_SIZE("glow_size"); const std::string FIELD_SKY_STAR_BRIGHTNESS("star_brightness"); + const std::string FIELD_SKY_SUN_BRIGHTNESS("sun_brightness"); + const std::string FIELD_SKY_TONEMAPPER("tonemapper"); + const std::string FIELD_SKY_TONEMAP_MIX("tonemap_mix"); + const std::string FIELD_SKY_HDR_OFFSET("hdr_offset"); + const std::string FIELD_SKY_HDR_MIN("hdr_min"); + const std::string FIELD_SKY_HDR_MAX("hdr_max"); const std::string FIELD_SKY_MOON_ROTATION("moon_rotation"); const std::string FIELD_SKY_MOON_AZIMUTH("moon_azimuth"); const std::string FIELD_SKY_MOON_ELEVATION("moon_elevation"); const std::string FIELD_REFLECTION_PROBE_AMBIANCE("probe_ambiance"); + const std::string FIELD_AMBIENT_SKY_SATURATION("ambient_sky_saturation"); const std::string BTN_RESET("btn_reset"); + const std::string BTN_UPGRADE("btn_upgrade"); const F32 SLIDER_SCALE_SUN_AMBIENT(3.0f); const F32 SLIDER_SCALE_BLUE_HORIZON_DENSITY(2.0f); @@ -101,6 +109,7 @@ bool LLFloaterEnvironmentAdjust::postBuild() getChild(FIELD_SKY_GLOW_FOCUS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onGlowChanged(); }); getChild(FIELD_SKY_GLOW_SIZE)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onGlowChanged(); }); getChild(FIELD_SKY_STAR_BRIGHTNESS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onStarBrightnessChanged(); }); + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunBrightnessChanged(); }); getChild(FIELD_SKY_SUN_ROTATION)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunRotationChanged(); }); getChild(FIELD_SKY_SUN_AZIMUTH)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunAzimElevChanged(); }); getChild(FIELD_SKY_SUN_ELEVATION)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunAzimElevChanged(); }); @@ -110,6 +119,7 @@ bool LLFloaterEnvironmentAdjust::postBuild() getChild(FIELD_SKY_MOON_AZIMUTH)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onMoonAzimElevChanged(); }); getChild(FIELD_SKY_MOON_ELEVATION)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onMoonAzimElevChanged(); }); getChild(BTN_RESET)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onButtonReset(); }); + getChild(BTN_UPGRADE)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onButtonUpgrade(); }); getChild(FIELD_SKY_CLOUD_MAP)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onCloudMapChanged(); }); getChild(FIELD_SKY_CLOUD_MAP)->setDefaultImageAssetID(LLSettingsSky::GetDefaultCloudNoiseTextureId()); @@ -120,6 +130,12 @@ bool LLFloaterEnvironmentAdjust::postBuild() getChild(FIELD_WATER_NORMAL_MAP)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onWaterMapChanged(); }); getChild(FIELD_REFLECTION_PROBE_AMBIANCE)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onReflectionProbeAmbianceChanged(); }); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onAmbientSkySaturationChanged(); }); + getChild(FIELD_SKY_TONEMAPPER)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTonemapperChanged(); }); + getChild(FIELD_SKY_TONEMAP_MIX)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTonemapMixChanged(); }); + getChild(FIELD_SKY_HDR_OFFSET)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onHDROffsetChanged(); }); + getChild(FIELD_SKY_HDR_MIN)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onHDRMinChanged(); }); + getChild(FIELD_SKY_HDR_MAX)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onHDRMaxChanged(); }); refresh(); return true; @@ -164,6 +180,17 @@ void LLFloaterEnvironmentAdjust::refresh() setEnabled(true); setAllChildrenEnabled(true); + bool can_upgrade = mLiveSky->getSkySettingVersion() < LLSettingsSky::MAX_SKY_SETTINGS_VERSION; + bool is_v2 = mLiveSky->getSkySettingVersion() >= 2; + getChild(BTN_UPGRADE)->setVisible(can_upgrade); + getChild(FIELD_SKY_TONEMAPPER)->setEnabled(is_v2); + getChild(FIELD_SKY_TONEMAP_MIX)->setEnabled(is_v2); + getChild(FIELD_SKY_HDR_OFFSET)->setEnabled(is_v2); + getChild(FIELD_SKY_HDR_MIN)->setEnabled(is_v2); + getChild(FIELD_SKY_HDR_MAX)->setEnabled(is_v2); + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setEnabled(is_v2); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setEnabled(is_v2); + getChild(FIELD_SKY_AMBIENT_LIGHT)->set(mLiveSky->getAmbientColor() / SLIDER_SCALE_SUN_AMBIENT); getChild(FIELD_SKY_BLUE_HORIZON)->set(mLiveSky->getBlueHorizon() / SLIDER_SCALE_BLUE_HORIZON_DENSITY); getChild(FIELD_SKY_BLUE_DENSITY)->set(mLiveSky->getBlueDensity() / SLIDER_SCALE_BLUE_HORIZON_DENSITY); @@ -180,6 +207,13 @@ void LLFloaterEnvironmentAdjust::refresh() static LLCachedControl should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", false); getChild(FIELD_REFLECTION_PROBE_AMBIANCE)->setValue(mLiveSky->getReflectionProbeAmbiance(should_auto_adjust)); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setValue(mLiveSky->getAmbientSkySaturation()); + + getChild(FIELD_SKY_TONEMAPPER)->setValue((S32)mLiveSky->getTonemapper()); + getChild(FIELD_SKY_TONEMAP_MIX)->setValue(mLiveSky->getTonemapMix()); + getChild(FIELD_SKY_HDR_OFFSET)->setValue(mLiveSky->getHDROffset()); + getChild(FIELD_SKY_HDR_MIN)->setValue(mLiveSky->getHDRMin()); + getChild(FIELD_SKY_HDR_MAX)->setValue(mLiveSky->getHDRMax()); LLColor3 glow(mLiveSky->getGlow()); @@ -187,6 +221,7 @@ void LLFloaterEnvironmentAdjust::refresh() getChild(FIELD_SKY_GLOW_SIZE)->setValue(2.0 - (glow.mV[0] / SLIDER_SCALE_GLOW_R)); getChild(FIELD_SKY_GLOW_FOCUS)->setValue(glow.mV[2] / SLIDER_SCALE_GLOW_B); getChild(FIELD_SKY_STAR_BRIGHTNESS)->setValue(mLiveSky->getStarBrightness()); + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setValue(mLiveSky->getSunBrightness()); getChild(FIELD_SKY_SUN_SCALE)->setValue(mLiveSky->getSunScale()); // Sun rotation @@ -260,6 +295,15 @@ void LLFloaterEnvironmentAdjust::onButtonReset() }); } + +void LLFloaterEnvironmentAdjust::onButtonUpgrade() +{ + if (!mLiveSky) return; + mLiveSky->setSkySettingVersion(LLSettingsSky::MAX_SKY_SETTINGS_VERSION); + mLiveSky->update(); + refresh(); +} + //------------------------------------------------------------------------- void LLFloaterEnvironmentAdjust::onAmbientLightChanged() { @@ -355,6 +399,14 @@ void LLFloaterEnvironmentAdjust::onStarBrightnessChanged() mLiveSky->update(); } +void LLFloaterEnvironmentAdjust::onSunBrightnessChanged() +{ + if (!mLiveSky) + return; + mLiveSky->setSunBrightness((F32)getChild(FIELD_SKY_SUN_BRIGHTNESS)->getValue().asReal()); + mLiveSky->update(); +} + void LLFloaterEnvironmentAdjust::onSunRotationChanged() { LLQuaternion quat = getChild(FIELD_SKY_SUN_ROTATION)->getRotation(); @@ -507,6 +559,48 @@ void LLFloaterEnvironmentAdjust::onReflectionProbeAmbianceChanged() mLiveSky->update(); } +void LLFloaterEnvironmentAdjust::onAmbientSkySaturationChanged() +{ + if (!mLiveSky) return; + mLiveSky->setAmbientSkySaturation((F32)getChild(FIELD_AMBIENT_SKY_SATURATION)->getValue().asReal()); + mLiveSky->update(); +} + +void LLFloaterEnvironmentAdjust::onTonemapperChanged() +{ + if (!mLiveSky) return; + mLiveSky->setTonemapper((U8)getChild(FIELD_SKY_TONEMAPPER)->getValue().asInteger()); + mLiveSky->update(); +} + +void LLFloaterEnvironmentAdjust::onTonemapMixChanged() +{ + if (!mLiveSky) return; + mLiveSky->setTonemapMix((F32)getChild(FIELD_SKY_TONEMAP_MIX)->getValue().asReal()); + mLiveSky->update(); +} + +void LLFloaterEnvironmentAdjust::onHDROffsetChanged() +{ + if (!mLiveSky) return; + mLiveSky->setHDROffset((F32)getChild(FIELD_SKY_HDR_OFFSET)->getValue().asReal()); + mLiveSky->update(); +} + +void LLFloaterEnvironmentAdjust::onHDRMinChanged() +{ + if (!mLiveSky) return; + mLiveSky->setHDRMin((F32)getChild(FIELD_SKY_HDR_MIN)->getValue().asReal()); + mLiveSky->update(); +} + +void LLFloaterEnvironmentAdjust::onHDRMaxChanged() +{ + if (!mLiveSky) return; + mLiveSky->setHDRMax((F32)getChild(FIELD_SKY_HDR_MAX)->getValue().asReal()); + mLiveSky->update(); +} + void LLFloaterEnvironmentAdjust::updateGammaLabel() { if (!mLiveSky) return; diff --git a/indra/newview/llfloaterenvironmentadjust.h b/indra/newview/llfloaterenvironmentadjust.h index c894473582e..97971423e72 100644 --- a/indra/newview/llfloaterenvironmentadjust.h +++ b/indra/newview/llfloaterenvironmentadjust.h @@ -72,6 +72,7 @@ class LLFloaterEnvironmentAdjust : public LLFloater void onGlowChanged(); void onStarBrightnessChanged(); + void onSunBrightnessChanged(); void onSunRotationChanged(); void onSunAzimElevChanged(); void onSunScaleChanged(); @@ -83,8 +84,15 @@ class LLFloaterEnvironmentAdjust : public LLFloater void onWaterMapChanged(); void onReflectionProbeAmbianceChanged(); + void onAmbientSkySaturationChanged(); + void onTonemapperChanged(); + void onTonemapMixChanged(); + void onHDROffsetChanged(); + void onHDRMinChanged(); + void onHDRMaxChanged(); void updateGammaLabel(); void onButtonReset(); + void onButtonUpgrade(); void onEnvironmentUpdated(LLEnvironment::EnvSelection_t env, S32 version); diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp index 1825797159e..4b0208c41c1 100644 --- a/indra/newview/llfloaterfixedenvironment.cpp +++ b/indra/newview/llfloaterfixedenvironment.cpp @@ -68,6 +68,7 @@ namespace const std::string BUTTON_NAME_CANCEL("btn_cancel"); const std::string BUTTON_NAME_FLYOUT("btn_flyout"); const std::string BUTTON_NAME_LOAD("btn_load"); + const std::string BUTTON_NAME_UPGRADE("btn_upgrade"); const std::string ACTION_SAVE("save_settings"); const std::string ACTION_SAVEAS("save_as_new_settings"); @@ -420,6 +421,8 @@ bool LLFloaterFixedEnvironmentWater::postBuild() if (!LLFloaterFixedEnvironment::postBuild()) return false; + getChild(BUTTON_NAME_UPGRADE)->setVisible(false); + LLPanelSettingsWater * panel; panel = new LLPanelSettingsWaterMainTab; panel->buildFromFile("panel_settings_water.xml"); @@ -487,6 +490,8 @@ bool LLFloaterFixedEnvironmentSky::postBuild() if (!LLFloaterFixedEnvironment::postBuild()) return false; + getChild(BUTTON_NAME_UPGRADE)->setClickedCallback([this](LLUICtrl *, const LLSD &) { onButtonUpgrade(); }); + LLPanelSettingsSky * panel; panel = new LLPanelSettingsSkyAtmosTab; panel->buildFromFile("panel_settings_sky_atmos.xml"); @@ -536,6 +541,25 @@ void LLFloaterFixedEnvironmentSky::onClose(bool app_quitting) LLFloaterFixedEnvironment::onClose(app_quitting); } +void LLFloaterFixedEnvironmentSky::refresh() +{ + LLFloaterFixedEnvironment::refresh(); + + LLSettingsSky::ptr_t sky = std::static_pointer_cast(mSettings); + bool can_upgrade = sky && sky->getSkySettingVersion() < LLSettingsSky::MAX_SKY_SETTINGS_VERSION; + getChild(BUTTON_NAME_UPGRADE)->setVisible(can_upgrade); +} + +void LLFloaterFixedEnvironmentSky::onButtonUpgrade() +{ + LLSettingsSky::ptr_t sky = std::static_pointer_cast(mSettings); + if (!sky) return; + sky->setSkySettingVersion(LLSettingsSky::MAX_SKY_SETTINGS_VERSION); + sky->update(); + syncronizeTabs(); + refresh(); +} + void LLFloaterFixedEnvironmentSky::doImportFromDisk() { // Load a a legacy Windlight XML from disk. LLFilePickerReplyThread::startPicker(boost::bind(&LLFloaterFixedEnvironmentSky::loadSkySettingFromFile, this, _1), LLFilePicker::FFLOAD_XML, false); diff --git a/indra/newview/llfloaterfixedenvironment.h b/indra/newview/llfloaterfixedenvironment.h index ac6acdd5687..041f4693e1c 100644 --- a/indra/newview/llfloaterfixedenvironment.h +++ b/indra/newview/llfloaterfixedenvironment.h @@ -127,12 +127,14 @@ class LLFloaterFixedEnvironmentSky : public LLFloaterFixedEnvironment virtual void onClose(bool app_quitting) override; protected: + virtual void refresh() override; virtual void updateEditEnvironment() override; virtual void doImportFromDisk() override; void loadSkySettingFromFile(const std::vector& filenames); private: + void onButtonUpgrade(); }; #endif // LL_FLOATERFIXEDENVIRONMENT_H diff --git a/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp b/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp index a8a1e507a85..f5bf394650a 100644 --- a/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp +++ b/indra/newview/llfloaterpreferencesgraphicsadvanced.cpp @@ -48,6 +48,7 @@ LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const L mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onRenderOptionEnable, this)); mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxNonImpostors", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::updateMaxNonImpostors,this)); mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxComplexity", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::updateMaxComplexity,this)); + mCommitCallbackRegistrar.add("Pref.UpdateReflectionsQuality", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onReflectionsQualityChanged, this)); mCommitCallbackRegistrar.add("Pref.Cancel", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnCancel, this, _2)); mCommitCallbackRegistrar.add("Pref.OK", boost::bind(&LLFloaterPreferenceGraphicsAdvanced::onBtnOK, this, _2)); @@ -171,6 +172,36 @@ void LLFloaterPreferenceGraphicsAdvanced::refresh() bool enable_complexity = gSavedSettings.getS32("RenderAvatarComplexityMode") != LLVOAvatar::AV_RENDER_ONLY_SHOW_FRIENDS; getChild("IndirectMaxComplexity")->setEnabled(enable_complexity); getChild("IndirectMaxNonImpostors")->setEnabled(enable_complexity); + + // Set Reflections Quality dropdown based on current settings + S32 probe_quality = gSavedSettings.getS32("RenderReflectionProbeQuality"); + bool ssr_enabled = gSavedSettings.getBOOL("RenderScreenSpaceReflections"); + bool mirrors_enabled = gSavedSettings.getBOOL("RenderMirrors"); + LLComboBox* reflections_combo = getChild("ReflectionsQuality"); + + if (mirrors_enabled && probe_quality == 1 && ssr_enabled) + { + reflections_combo->setValue(3); // Ultra + } + else if (probe_quality == 0) + { + reflections_combo->setValue(0); // Low + } + else if (probe_quality == 1 && !ssr_enabled) + { + reflections_combo->setValue(1); // Medium + } + else + { + reflections_combo->setValue(2); // High + } + + // Show/hide mirror sub-controls based on Ultra + bool show_mirror_controls = (reflections_combo->getValue().asInteger() == 3); + getChildView("MirrorResolutionText")->setVisible(show_mirror_controls); + getChildView("MirrorResolution")->setVisible(show_mirror_controls); + getChildView("HeroProbeUpdateText")->setVisible(show_mirror_controls); + getChildView("HeroProbeUpdateRate")->setVisible(show_mirror_controls); } void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledGraphics() @@ -405,3 +436,49 @@ void LLFloaterPreferenceGraphicsAdvanced::onBtnCancel(const LLSD& userdata) instance->onBtnCancel(userdata); } } + +void LLFloaterPreferenceGraphicsAdvanced::onReflectionsQualityChanged() +{ + LLComboBox* combo = getChild("ReflectionsQuality"); + S32 quality = combo->getValue().asInteger(); + + // Map quality levels to settings: + // 0 (Low) = probe quality 0, SSR off, mirrors off + // 1 (Medium) = probe quality 1, SSR off, mirrors off + // 2 (High) = probe quality 1, SSR on, mirrors off + // 3 (Ultra) = probe quality 1, SSR on, mirrors on + + if (quality == 0) + { + gSavedSettings.setS32("RenderReflectionProbeQuality", 0); + gSavedSettings.setBOOL("RenderScreenSpaceReflections", false); + gSavedSettings.setBOOL("RenderMirrors", false); + } + else if (quality == 1) + { + gSavedSettings.setS32("RenderReflectionProbeQuality", 1); + gSavedSettings.setBOOL("RenderScreenSpaceReflections", false); + gSavedSettings.setBOOL("RenderMirrors", false); + } + else if (quality == 2) + { + gSavedSettings.setS32("RenderReflectionProbeQuality", 1); + gSavedSettings.setBOOL("RenderScreenSpaceReflections", true); + gSavedSettings.setBOOL("RenderMirrors", false); + } + else // quality == 3 (Ultra) + { + gSavedSettings.setS32("RenderReflectionProbeQuality", 1); + gSavedSettings.setBOOL("RenderScreenSpaceReflections", true); + gSavedSettings.setBOOL("RenderMirrors", true); + } + + // Show/hide mirror sub-controls based on Ultra + bool show_mirror_controls = (quality == 3); + getChildView("MirrorResolutionText")->setVisible(show_mirror_controls); + getChildView("MirrorResolution")->setVisible(show_mirror_controls); + getChildView("HeroProbeUpdateText")->setVisible(show_mirror_controls); + getChildView("HeroProbeUpdateRate")->setVisible(show_mirror_controls); + + onRenderOptionEnable(); +} diff --git a/indra/newview/llfloaterpreferencesgraphicsadvanced.h b/indra/newview/llfloaterpreferencesgraphicsadvanced.h index 6f793c13799..c8373464ad3 100644 --- a/indra/newview/llfloaterpreferencesgraphicsadvanced.h +++ b/indra/newview/llfloaterpreferencesgraphicsadvanced.h @@ -55,6 +55,7 @@ class LLFloaterPreferenceGraphicsAdvanced : public LLFloater // callback for when client modifies a render option void onRenderOptionEnable(); void onAdvancedAtmosphericsEnable(); + void onReflectionsQualityChanged(); LOG_CLASS(LLFloaterPreferenceGraphicsAdvanced); protected: diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 01108b5cfab..92b3c92165a 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -106,6 +106,7 @@ void LLFloaterSettingsDebug::onCommitSettings() LLVector3 vector; LLVector3d vectord; + LLVector4 vector4; LLQuaternion quat; LLRect rect; LLColor4 col4; @@ -142,6 +143,13 @@ void LLFloaterSettingsDebug::onCommitSettings() vectord.mdV[VZ] = mValSpinner3->getValue().asReal(); controlp->set(vectord.getValue()); break; + case TYPE_VEC4: + vector4.mV[VX] = (F32)mValSpinner1->getValue().asReal(); + vector4.mV[VY] = (F32)mValSpinner2->getValue().asReal(); + vector4.mV[VZ] = (F32)mValSpinner3->getValue().asReal(); + vector4.mV[VW] = (F32)mValSpinner4->getValue().asReal(); + controlp->set(vector4.getValue()); + break; case TYPE_QUAT: quat.mQ[VX] = mValSpinner1->getValueF32(); quat.mQ[VY] = mValSpinner2->getValueF32(); @@ -352,6 +360,40 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp) } break; } + case TYPE_VEC4: + { + LLVector4 v; + v.setValue(sd); + mValSpinner1->setVisible(true); + mValSpinner1->setLabel(std::string("X")); + mValSpinner2->setVisible(true); + mValSpinner2->setLabel(std::string("Y")); + mValSpinner3->setVisible(true); + mValSpinner3->setLabel(std::string("Z")); + mValSpinner4->setVisible(true); + mValSpinner4->setLabel(std::string("W")); + if (!mValSpinner1->hasFocus()) + { + mValSpinner1->setPrecision(3); + mValSpinner1->setValue(v[VX]); + } + if (!mValSpinner2->hasFocus()) + { + mValSpinner2->setPrecision(3); + mValSpinner2->setValue(v[VY]); + } + if (!mValSpinner3->hasFocus()) + { + mValSpinner3->setPrecision(3); + mValSpinner3->setValue(v[VZ]); + } + if (!mValSpinner4->hasFocus()) + { + mValSpinner4->setPrecision(3); + mValSpinner4->setValue(v[VW]); + } + break; + } case TYPE_QUAT: { LLQuaternion q; diff --git a/indra/newview/llgltfhelper.cpp b/indra/newview/llgltfhelper.cpp new file mode 100644 index 00000000000..95910b0c2f5 --- /dev/null +++ b/indra/newview/llgltfhelper.cpp @@ -0,0 +1,445 @@ +/** + * @file llgltfhelper.cpp + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llgltfhelper.h" + +#include "llimage.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" +#include "lldir.h" + +static void strip_alpha_channel(LLPointer& img) +{ + if (img->getComponents() == 4) + { + LLImageRaw* tmp = new LLImageRaw(img->getWidth(), img->getHeight(), 3); + tmp->copyUnscaled4onto3(img); + img = tmp; + } +} + +// copy red channel from src_img to dst_img +// PRECONDITIONS: +// dst_img must be 3 component +// src_img and dst_image must have the same dimensions +static void copy_red_channel(const LLPointer& src_img, LLPointer& dst_img) +{ + llassert(src_img->getWidth() == dst_img->getWidth() && src_img->getHeight() == dst_img->getHeight()); + llassert(dst_img->getComponents() == 3); + + U32 pixel_count = dst_img->getWidth() * dst_img->getHeight(); + const U8* src = src_img->getData(); + U8* dst = dst_img->getData(); + S8 src_components = src_img->getComponents(); + + for (U32 i = 0; i < pixel_count; ++i) + { + dst[i * 3] = src[i * src_components]; + } +} + +// Decode image data from an LL::GLTF::Image's buffer view +static LLImageRaw* decodeImage(const LL::GLTF::Asset& asset, const LL::GLTF::Image& image, bool flip) +{ + const U8* data = nullptr; + U32 data_size = 0; + std::string mime_type = image.mMimeType; + + std::string buffer_data; // holds file data if needed + + if (image.mBufferView != LL::GLTF::INVALID_INDEX && + (size_t)image.mBufferView < asset.mBufferViews.size()) + { + const auto& bv = asset.mBufferViews[image.mBufferView]; + if (bv.mBuffer != LL::GLTF::INVALID_INDEX && + (size_t)bv.mBuffer < asset.mBuffers.size()) + { + const auto& buf = asset.mBuffers[bv.mBuffer]; + if ((size_t)(bv.mByteOffset + bv.mByteLength) <= buf.mData.size()) + { + data = (const U8*)buf.mData.data() + bv.mByteOffset; + data_size = (U32)bv.mByteLength; + } + } + } + else if (!image.mUri.empty()) + { + // External file URI - load from disk relative to asset + std::string folder = gDirUtilp->getDirName(asset.mFilename); + std::string filepath = folder + gDirUtilp->getDirDelimiter() + image.mUri; + + if (!gDirUtilp->fileExists(filepath)) + { + // URI might be escaped, unescape + filepath = folder + gDirUtilp->getDirDelimiter() + LLURI::unescape(image.mUri); + } + + llifstream ifs(filepath, std::ios::binary); + if (ifs.good()) + { + buffer_data.assign((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + data = (const U8*)buffer_data.data(); + data_size = (U32)buffer_data.size(); + + // Infer mime type from extension if not set + if (mime_type.empty()) + { + std::string ext = gDirUtilp->getExtension(image.mUri); + LLStringUtil::toLower(ext); + if (ext == "png") mime_type = "image/png"; + else if (ext == "jpg" || ext == "jpeg") mime_type = "image/jpeg"; + else if (ext == "bmp") mime_type = "image/bmp"; + } + } + } + + if (!data || data_size == 0) + { + return nullptr; + } + + // Use LLImageFormatted to decode from memory with the given mimetype + LLPointer formatted = LLImageFormatted::loadFromMemory(data, data_size, mime_type); + if (!formatted) + { + return nullptr; + } + + LLImageRaw* raw = new LLImageRaw(); + if (!formatted->decode(raw, 0.0f)) + { + // Use LLPointer to clean up via refcounted destructor + LLPointer guard(raw); + return nullptr; + } + + if (flip) + { + raw->verticalFlip(); + } + raw->optimizeAwayAlpha(); + + return raw; +} + +void LLGLTFHelper::initFetchedTextures(const LL::GLTF::Material& material, + LLPointer& base_color_img, + LLPointer& normal_img, + LLPointer& mr_img, + LLPointer& emissive_img, + LLPointer& occlusion_img, + LLPointer& base_color_tex, + LLPointer& normal_tex, + LLPointer& mr_tex, + LLPointer& emissive_tex) +{ + if (base_color_img) + { + base_color_tex = LLViewerTextureManager::getFetchedTexture(base_color_img, FTType::FTT_LOCAL_FILE, true); + } + + if (normal_img) + { + strip_alpha_channel(normal_img); + normal_tex = LLViewerTextureManager::getFetchedTexture(normal_img, FTType::FTT_LOCAL_FILE, true); + } + + if (mr_img) + { + strip_alpha_channel(mr_img); + + if (occlusion_img) + { + if (material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex != material.mOcclusionTexture.mIndex) + { + // occlusion is a distinct texture from pbrMetallicRoughness + // pack into mr red channel + S32 occlusion_idx = material.mOcclusionTexture.mIndex; + S32 mr_idx = material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex; + if (occlusion_idx != mr_idx) + { + LLImageDataLock lockIn(occlusion_img); + LLImageDataLock lockOut(mr_img); + //scale occlusion image to match resolution of mr image + occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight()); + + copy_red_channel(occlusion_img, mr_img); + } + } + } + else if (material.mOcclusionTexture.mIndex == LL::GLTF::INVALID_INDEX) + { + // no occlusion, make sure red channel of ORM is all 255 + occlusion_img = new LLImageRaw(mr_img->getWidth(), mr_img->getHeight(), 3); + occlusion_img->clear(255, 255, 255); + copy_red_channel(occlusion_img, mr_img); + } + } + else if (occlusion_img) + { + LLImageDataSharedLock lock(occlusion_img); + //no mr but occlusion exists, make a white mr_img and copy occlusion red channel over + mr_img = new LLImageRaw(occlusion_img->getWidth(), occlusion_img->getHeight(), 3); + mr_img->clear(255, 255, 255); + copy_red_channel(occlusion_img, mr_img); + } + + if (mr_img) + { + mr_tex = LLViewerTextureManager::getFetchedTexture(mr_img, FTType::FTT_LOCAL_FILE, true); + } + + if (emissive_img) + { + strip_alpha_channel(emissive_img); + emissive_tex = LLViewerTextureManager::getFetchedTexture(emissive_img, FTType::FTT_LOCAL_FILE, true); + } +} + +LLColor4 LLGLTFHelper::getColor(const std::vector& in) +{ + LLColor4 out; + for (S32 i = 0; i < llmin((S32)in.size(), 4); ++i) + { + out.mV[i] = (F32)in[i]; + } + + return out; +} + +LLImageRaw* LLGLTFHelper::getTexture(const std::string& folder, const LL::GLTF::Asset& asset, S32 texture_index, std::string& name, bool flip) +{ + if (texture_index < 0 || (size_t)texture_index >= asset.mTextures.size()) + { + return nullptr; + } + + S32 source_idx = asset.mTextures[texture_index].mSource; + if (source_idx < 0 || (size_t)source_idx >= asset.mImages.size()) + { + return nullptr; + } + + const auto& image = asset.mImages[source_idx]; + name = image.mName; + + return decodeImage(asset, image, flip); +} + +LLImageRaw* LLGLTFHelper::getTexture(const std::string& folder, const LL::GLTF::Asset& asset, S32 texture_index, bool flip) +{ + std::string name; + return getTexture(folder, asset, texture_index, name, flip); +} + +bool LLGLTFHelper::loadModel(const std::string& filename, LL::GLTF::Asset& asset_out) +{ + std::string exten = gDirUtilp->getExtension(filename); + + if (exten == "gltf" || exten == "glb") + { + try + { + if (!asset_out.load(filename, false)) + { + LL_WARNS("GLTF") << "Cannot load, error: Failed to load " << filename << LL_ENDL; + return false; + } + } + catch (const std::exception& e) + { + LL_WARNS("GLTF") << "Cannot load, exception: " << e.what() << " file: " << filename << LL_ENDL; + return false; + } + + if (asset_out.mMaterials.empty()) + { + // materials are missing + LL_WARNS("GLTF") << "Cannot load. File has no materials " << filename << LL_ENDL; + return false; + } + + return true; + } + + return false; +} + +bool LLGLTFHelper::getMaterialFromModel( + const std::string& filename, + const LL::GLTF::Asset& asset, + S32 mat_index, + LLFetchedGLTFMaterial* material, + std::string& material_name, + bool flip) +{ + llassert(material); + + if ((size_t)mat_index >= asset.mMaterials.size()) + { + // materials are missing + LL_WARNS("GLTF") << "Cannot load Material, Material " << mat_index << " is missing, " << filename << LL_ENDL; + return false; + } + + const auto& mat_in = asset.mMaterials[mat_index]; + material_name = mat_in.mName; + + // Serialize material to JSON, then load via fromJSON to populate LLGLTFMaterial fields + { + boost::json::object doc; + boost::json::object asset_obj; + asset_obj["version"] = "2.0"; + doc["asset"] = asset_obj; + + // Serialize the material + boost::json::object mat_json; + mat_in.serialize(mat_json); + + // Serialize referenced textures and images + boost::json::array textures_arr; + boost::json::array images_arr; + + auto serializeTexIndex = [&](S32 tex_index) { + if (tex_index >= 0 && (size_t)tex_index < asset.mTextures.size()) + { + const auto& tex = asset.mTextures[tex_index]; + // Ensure we have entries up to the needed index + while ((S32)textures_arr.size() <= tex_index) + { + textures_arr.push_back(boost::json::object()); + } + boost::json::object tex_obj; + tex_obj["source"] = tex.mSource; + textures_arr[(size_t)tex_index] = tex_obj; + + if (tex.mSource >= 0 && (size_t)tex.mSource < asset.mImages.size()) + { + while ((S32)images_arr.size() <= tex.mSource) + { + images_arr.push_back(boost::json::object()); + } + // For material system, URIs are UUIDs - but from file they won't be. + // We'll use a placeholder and rely on texture loading below. + boost::json::object img_obj; + img_obj["uri"] = LLUUID::null.asString(); + images_arr[(size_t)tex.mSource] = img_obj; + } + } + }; + + serializeTexIndex(mat_in.mPbrMetallicRoughness.mBaseColorTexture.mIndex); + serializeTexIndex(mat_in.mNormalTexture.mIndex); + serializeTexIndex(mat_in.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex); + serializeTexIndex(mat_in.mEmissiveTexture.mIndex); + serializeTexIndex(mat_in.mOcclusionTexture.mIndex); + + doc["materials"] = boost::json::array({ mat_json }); + if (!textures_arr.empty()) doc["textures"] = textures_arr; + if (!images_arr.empty()) doc["images"] = images_arr; + + std::string json_str = boost::json::serialize(doc); + std::string warn_msg, error_msg; + material->fromJSON(json_str, warn_msg, error_msg); + } + + std::string folder = gDirUtilp->getDirName(filename); + + // get base color texture + LLPointer base_img = LLGLTFHelper::getTexture(folder, asset, mat_in.mPbrMetallicRoughness.mBaseColorTexture.mIndex, flip); + // get normal map + LLPointer normal_img = LLGLTFHelper::getTexture(folder, asset, mat_in.mNormalTexture.mIndex, flip); + // get metallic-roughness texture + LLPointer mr_img = LLGLTFHelper::getTexture(folder, asset, mat_in.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex, flip); + // get emissive texture + LLPointer emissive_img = LLGLTFHelper::getTexture(folder, asset, mat_in.mEmissiveTexture.mIndex, flip); + // get occlusion map if needed + LLPointer occlusion_img; + if (mat_in.mOcclusionTexture.mIndex != mat_in.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex) + { + occlusion_img = LLGLTFHelper::getTexture(folder, asset, mat_in.mOcclusionTexture.mIndex, flip); + } + + LLPointer base_color_tex; + LLPointer normal_tex; + LLPointer mr_tex; + LLPointer emissive_tex; + + LLGLTFHelper::initFetchedTextures(mat_in, + base_img, normal_img, mr_img, emissive_img, occlusion_img, + base_color_tex, normal_tex, mr_tex, emissive_tex); + + if (base_color_tex) + { + base_color_tex->addTextureStats(64.f * 64.f, true); + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = base_color_tex->getID(); + material->mBaseColorTexture = base_color_tex; + } + else + { + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = LLUUID::null; + material->mBaseColorTexture = nullptr; + } + + if (normal_tex) + { + normal_tex->addTextureStats(64.f * 64.f, true); + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = normal_tex->getID(); + material->mNormalTexture = normal_tex; + } + else + { + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = LLUUID::null; + material->mNormalTexture = nullptr; + } + + if (mr_tex) + { + mr_tex->addTextureStats(64.f * 64.f, true); + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = mr_tex->getID(); + material->mMetallicRoughnessTexture = mr_tex; + } + else + { + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = LLUUID::null; + material->mMetallicRoughnessTexture = nullptr; + } + + if (emissive_tex) + { + emissive_tex->addTextureStats(64.f * 64.f, true); + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = emissive_tex->getID(); + material->mEmissiveTexture = emissive_tex; + } + else + { + material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = LLUUID::null; + material->mEmissiveTexture = nullptr; + } + + return true; +} diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/llgltfhelper.h similarity index 72% rename from indra/newview/lltinygltfhelper.h rename to indra/newview/llgltfhelper.h index a259609404d..bbb2e014b42 100644 --- a/indra/newview/lltinygltfhelper.h +++ b/indra/newview/llgltfhelper.h @@ -1,5 +1,5 @@ /** - * @file lltinygltfhelper.h + * @file llgltfhelper.h * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Second Life Viewer Source Code @@ -29,30 +29,28 @@ #include "llgltfmaterial.h" #include "llgltfmateriallist.h" #include "llpointer.h" -#include "tinygltf/tiny_gltf.h" +#include "gltf/asset.h" class LLImageRaw; class LLViewerFetchedTexture; -namespace LLTinyGLTFHelper +namespace LLGLTFHelper { LLColor4 getColor(const std::vector& in); - const tinygltf::Image* getImageFromTextureIndex(const tinygltf::Model& model, S32 texture_index); - LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name, bool flip = true); - LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, bool flip = true); + LLImageRaw* getTexture(const std::string& folder, const LL::GLTF::Asset& asset, S32 texture_index, std::string& name, bool flip = true); + LLImageRaw* getTexture(const std::string& folder, const LL::GLTF::Asset& asset, S32 texture_index, bool flip = true); - bool loadModel(const std::string& filename, tinygltf::Model& model_out); - bool saveModel(const std::string& filename, tinygltf::Model& model_in); + bool loadModel(const std::string& filename, LL::GLTF::Asset& asset_out); bool getMaterialFromModel( const std::string& filename, - const tinygltf::Model& model, + const LL::GLTF::Asset& asset, S32 mat_index, LLFetchedGLTFMaterial* material, std::string& material_name, bool flip = true); - void initFetchedTextures(tinygltf::Material& material, + void initFetchedTextures(const LL::GLTF::Material& material, LLPointer& base_color_img, LLPointer& normal_img, LLPointer& mr_img, @@ -63,4 +61,3 @@ namespace LLTinyGLTFHelper LLPointer& mr_tex, LLPointer& emissive_tex); } - diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp index b7844197807..5d0b69dfa96 100644 --- a/indra/newview/llgltfmateriallist.cpp +++ b/indra/newview/llgltfmateriallist.cpp @@ -33,7 +33,7 @@ #include "llfetchedgltfmaterial.h" #include "llfilesystem.h" #include "llsdserialize.h" -#include "lltinygltfhelper.h" +#include "llgltfhelper.h" #include "llviewercontrol.h" #include "llviewergenericmessage.h" #include "llviewerobjectlist.h" @@ -44,8 +44,6 @@ #include "llvocache.h" #include "llworld.h" -#include "tinygltf/tiny_gltf.h" - #include #include @@ -223,6 +221,55 @@ void LLGLTFMaterialList::applyOverrideMessage(LLMessageSystem* msg, const std::s if (obj) { + // Preserve locally-applied extension data that the server + // may have sanitized (e.g. KHR_materials_specular, + // KHR_materials_emissive_strength) + LLTextureEntry* tep = obj->getTE(te); + if (tep) + { + const LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride(); + LL_WARNS("GLTF") << "applyOverrideMessage: te=" << te + << " existing_override=" << (existing_override ? "yes" : "no") + << " od has sc=" << od[i].has("sc"); + if (existing_override) + { + LL_CONT << " existing specColor=(" + << existing_override->mSpecularColorFactor.mV[0] << "," + << existing_override->mSpecularColorFactor.mV[1] << "," + << existing_override->mSpecularColorFactor.mV[2] << ")"; + } + LL_CONT << LL_ENDL; + if (existing_override) + { + if (existing_override->mEmissiveStrength != LLGLTFMaterial::getDefaultEmissiveStrength() + && !od[i].has("es")) + { + mat->mEmissiveStrength = existing_override->mEmissiveStrength; + } + if (existing_override->mSpecularFactor != LLGLTFMaterial::getDefaultSpecularFactor() + && !od[i].has("sf")) + { + mat->mSpecularFactor = existing_override->mSpecularFactor; + } + if (existing_override->mSpecularColorFactor != LLGLTFMaterial::getDefaultSpecularColorFactor() + && !od[i].has("sc")) + { + mat->mSpecularColorFactor = existing_override->mSpecularColorFactor; + } + if (existing_override->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR].notNull() + && !od[i].has("tex")) + { + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR] = + existing_override->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR]; + } + if (existing_override->mIOR != LLGLTFMaterial::getDefaultIOR() + && !od[i].has("ior")) + { + mat->mIOR = existing_override->mIOR; + } + } + } + obj->setTEGLTFMaterialOverride(te, mat); if (obj->getTE(te) && obj->getTE(te)->isSelected()) { @@ -514,7 +561,7 @@ class AssetLoadUserData { public: AssetLoadUserData() {} - tinygltf::Model mModelIn; + std::string mData; LLPointer mMaterial; }; @@ -568,23 +615,7 @@ void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::ETyp { if (asset.has("data") && asset["data"].isString()) { - std::string data = asset["data"]; - - std::string warn_msg, error_msg; - - LL_PROFILE_ZONE_SCOPED; - tinygltf::TinyGLTF gltf; - - if (!gltf.LoadASCIIFromString(&asset_data->mModelIn, &error_msg, &warn_msg, data.c_str(), static_cast(data.length()), "")) - { - LL_WARNS("GLTF") << "Failed to decode material asset: " - << LL_NEWLINE - << warn_msg - << LL_NEWLINE - << error_msg - << LL_ENDL; - return false; - } + asset_data->mData = asset["data"].asString(); return true; } } @@ -603,7 +634,12 @@ void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::ETyp if (result) { - asset_data->mMaterial->setFromModel(asset_data->mModelIn, 0/*only one index*/); + std::string warn_msg, error_msg; + if (!asset_data->mMaterial->fromJSON(asset_data->mData, warn_msg, error_msg)) + { + LL_WARNS("GLTF") << "Failed to decode material asset: " << error_msg << LL_ENDL; + result = false; + } } else { diff --git a/indra/newview/llgltfmaterialpreviewmgr.cpp b/indra/newview/llgltfmaterialpreviewmgr.cpp index ebb9dc3e3c8..61583dae6c3 100644 --- a/indra/newview/llgltfmaterialpreviewmgr.cpp +++ b/indra/newview/llgltfmaterialpreviewmgr.cpp @@ -149,6 +149,7 @@ namespace textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = &material.mNormalTexture; textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = &material.mMetallicRoughnessTexture; textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = &material.mEmissiveTexture; + textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR] = &material.mSpecularTexture; LLGLTFPreviewTexture::MaterialLoadLevels levels; diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp index 0f3e220ae08..33f4da71846 100644 --- a/indra/newview/llheroprobemanager.cpp +++ b/indra/newview/llheroprobemanager.cpp @@ -69,7 +69,7 @@ LLHeroProbeManager::~LLHeroProbeManager() cleanup(); mHeroVOList.clear(); - mNearestHero = nullptr; + mActiveHeroes.clear(); } // helper class to seed octree with probes @@ -124,24 +124,58 @@ void LLHeroProbeManager::update() llassert(mProbes[0] == mDefaultProbe); - LLVector4a probe_pos; LLVector3 camera_pos = LLViewerCamera::instance().mOrigin; - bool probe_present = false; LLQuaternion cameraOrientation = LLViewerCamera::instance().getQuaternion(); LLVector3 cameraDirection = LLVector3::z_axis * cameraOrientation; - if (mHeroVOList.size() > 0) + S32 probeCount = (S32)mReflectionProbeCount; + + // --- Probe 0: System water mirror probe --- + { + F32 waterHeight = LLEnvironment::instance().getWaterHeight(); + LLVector3 waterPos(camera_pos.mV[VX], camera_pos.mV[VY], waterHeight); + LLVector3 waterNormal(0.f, 0.f, 1.f); + + LLVector3 offset = camera_pos - waterPos; + LLVector3 project = waterNormal * (offset * waterNormal); + LLVector3 reject = offset - project; + LLVector3 point = (reject - project) + waterPos; + + LLVector4a probe_pos; + probe_pos.load3(point.mV); + + mProbes[0]->mOrigin = probe_pos; + mProbes[0]->mRadius = 256.0f * 0.5f * sqrtf(2.0f); + + mCurrentClipPlane.setVec(waterPos, waterNormal); + mIsPlanar = true; + + LLVector3 camFwd = LLViewerCamera::instance().getAtAxis(); + LLVector3 camUp = LLViewerCamera::instance().getUpAxis(); + mPlanarLookDir = camFwd - 2.0f * (camFwd * waterNormal) * waterNormal; + mPlanarUpDir = camUp - 2.0f * (camUp * waterNormal) * waterNormal; + mPlanarLookDir.normalize(); + mPlanarUpDir.normalize(); + } + + // --- User probes (indices 1..N-1) --- + mActiveHeroes.clear(); + + if (mHeroVOList.size() > 0 && probeCount > 1) { - // Find our nearest hero candidate. - float last_distance = 99999.f; - float camera_center_distance = 99999.f; - mNearestHero = nullptr; + // Build sorted candidate list by distance + struct HeroCandidate + { + LLPointer vo; + float distance; + }; + std::vector candidates; + for (auto vo : mHeroVOList) { if (vo && !vo->isDead() && vo->mDrawable.notNull() && vo->isReflectionProbe() && vo->getReflectionProbeIsBox()) { - float distance = (LLViewerCamera::instance().getOrigin() - vo->getPositionAgent()).magVec(); - float center_distance = cameraDirection * (vo->getPositionAgent() - camera_pos); + float distance = (camera_pos - vo->getPositionAgent()).magVec(); if (distance > LLViewerCamera::instance().getFar()) continue; @@ -149,18 +183,12 @@ void LLHeroProbeManager::update() LLVector4a center; center.load3(vo->getPositionAgent().mV); LLVector4a size; - size.load3(vo->getScale().mV); - bool visible = LLViewerCamera::instance().AABBInFrustum(center, size); + if (!LLViewerCamera::instance().AABBInFrustum(center, size)) + continue; - if (distance < last_distance && center_distance < camera_center_distance && visible) - { - probe_present = true; - mNearestHero = vo; - last_distance = distance; - camera_center_distance = center_distance; - } + candidates.push_back({ vo, distance }); } else { @@ -168,57 +196,71 @@ void LLHeroProbeManager::update() } } - // Don't even try to do anything if we didn't find a single mirror present. - if (!probe_present) - return; + // Sort by distance, nearest first + std::sort(candidates.begin(), candidates.end(), + [](const HeroCandidate& a, const HeroCandidate& b) { return a.distance < b.distance; }); - if (mNearestHero != nullptr && !mNearestHero->isDead() && mNearestHero->mDrawable.notNull()) + // Pick up to N-1 nearest user probes + S32 maxUserProbes = probeCount - 1; + for (S32 i = 0; i < (S32)candidates.size() && i < maxUserProbes; ++i) { - LLVector3 hero_pos = mNearestHero->getPositionAgent(); - LLVector3 face_normal = LLVector3(0, 0, 1); - - face_normal *= mNearestHero->mDrawable->getWorldRotation(); - face_normal.normalize(); - - LLVector3 offset = camera_pos - hero_pos; - LLVector3 project = face_normal * (offset * face_normal); - LLVector3 reject = offset - project; - LLVector3 point = (reject - project) + hero_pos; + mActiveHeroes.push_back(candidates[i].vo); + } - mCurrentClipPlane.setVec(hero_pos, face_normal); - mMirrorPosition = hero_pos; - mMirrorNormal = face_normal; + // Set up each user probe + for (S32 i = 0; i < (S32)mActiveHeroes.size(); ++i) + { + S32 probeIdx = i + 1; // probe 0 is water + LLVOVolume* hero = mActiveHeroes[i]; - probe_pos.load3(point.mV); + LLVector3 hero_pos = hero->getPositionAgent(); + LLVector3 face_normal = LLVector3(0, 0, 1); + face_normal *= hero->mDrawable->getWorldRotation(); + face_normal.normalize(); - // Detect visible faces of a cube based on camera direction and distance + bool isPlanar = hero->getScale().mV[VZ] < 0.02f; - // Define the cube faces - static LLVector3 cubeFaces[6] = { - LLVector3(1, 0, 0), - LLVector3(-1, 0, 0), - LLVector3(0, 1, 0), - LLVector3(0, -1, 0), - LLVector3(0, 0, 1), - LLVector3(0, 0, -1) - }; + LLVector4a probe_pos; + if (isPlanar) + { + // Planar mirrors render from reflected camera position + LLVector3 offset = camera_pos - hero_pos; + LLVector3 project = face_normal * (offset * face_normal); + LLVector3 reject = offset - project; + LLVector3 point = (reject - project) + hero_pos; + probe_pos.load3(point.mV); + } + else + { + // Non-planar probes render from the hero object's center + probe_pos.load3(hero_pos.mV); + } - mProbes[0]->mOrigin = probe_pos; - mProbes[0]->mRadius = mNearestHero->getScale().magVec() * 0.5f; - } - else - { - mNearestHero = nullptr; - mDefaultProbe->mViewerObject = nullptr; + mProbes[probeIdx]->mOrigin = probe_pos; + mProbes[probeIdx]->mRadius = hero->getScale().magVec() * 0.5f; + mProbes[probeIdx]->mViewerObject = hero; } + } - mHeroProbeStrength = 1; + // Set backward compat mMirrorPosition/mMirrorNormal from nearest user probe (for clipPlane uniform) + if (!mActiveHeroes.empty()) + { + LLVOVolume* nearest = mActiveHeroes[0]; + LLVector3 face_normal = LLVector3(0, 0, 1); + face_normal *= nearest->mDrawable->getWorldRotation(); + face_normal.normalize(); + mMirrorPosition = nearest->getPositionAgent(); + mMirrorNormal = face_normal; } else { - mNearestHero = nullptr; - mDefaultProbe->mViewerObject = nullptr; + // Fall back to water plane + F32 waterHeight = LLEnvironment::instance().getWaterHeight(); + mMirrorPosition = LLVector3(camera_pos.mV[VX], camera_pos.mV[VY], waterHeight); + mMirrorNormal = LLVector3(0.f, 0.f, 1.f); } + + mHeroProbeStrength = 1; } void LLHeroProbeManager::renderProbes() @@ -234,8 +276,7 @@ void LLHeroProbeManager::renderProbes() static LLCachedControl sUpdateRate(gSavedSettings, "RenderHeroProbeUpdateRate", 0); F32 near_clip = 0.01f; - if (mNearestHero != nullptr && !mNearestHero->isDead() && - !gTeleportDisplay && !gDisconnected && !LLAppViewer::instance()->logoutRequestSent()) + if (!gTeleportDisplay && !gDisconnected && !LLAppViewer::instance()->logoutRequestSent()) { LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime"); @@ -244,6 +285,12 @@ void LLHeroProbeManager::renderProbes() gPipeline.mReflectionMapManager.mRadiancePass = true; mRenderingMirror = true; + // Reset shadow tracking for all probes + for (S32 i = 0; i < LL_MAX_HERO_PROBE_COUNT; ++i) + { + mHeroShadowsComplete[i] = false; + } + S32 rate = sUpdateRate; // rate must be divisor of 6 (1, 2, 3, or 6) @@ -256,31 +303,98 @@ void LLHeroProbeManager::renderProbes() rate = 6; } - S32 face = gFrameCount % 6; + S32 activeCount = 1 + (S32)mActiveHeroes.size(); // water probe + user probes - if (!mProbes.empty() && !mProbes[0].isNull() && !mProbes[0]->mOccluded) + for (S32 probeIdx = 0; probeIdx < activeCount; ++probeIdx) { - LL_PROFILE_ZONE_NUM(gFrameCount % rate); - LL_PROFILE_ZONE_NUM(rate); + if (probeIdx >= (S32)mProbes.size() || mProbes[probeIdx].isNull() || mProbes[probeIdx]->mOccluded) + continue; - bool dynamic = mNearestHero->getReflectionProbeIsDynamic() && sDetail() > 0; - for (U32 i = 0; i < 6; ++i) + mCurrentRenderingProbeIdx = probeIdx; + + // Set per-probe active state and mirror plane for applySpecial() + if (probeIdx == 0) { - if ((gFrameCount % rate) == (i % rate)) - { // update 6/rate faces per frame - LL_PROFILE_ZONE_NUM(i); - updateProbeFace(mProbes[0], i, dynamic, near_clip); + // Water probe — always planar + F32 waterHeight = LLEnvironment::instance().getWaterHeight(); + LLVector3 camera_pos = LLViewerCamera::instance().mOrigin; + + LLVector3 clipPos(camera_pos.mV[VX], camera_pos.mV[VY], waterHeight + 0.0001f); + LLVector3 waterNormal(0.f, 0.f, 1.f); + + mCurrentClipPlane.setVec(clipPos, waterNormal); + mMirrorPosition = clipPos; + mMirrorNormal = waterNormal; + mIsPlanar = true; + + LLVector3 camFwd = LLViewerCamera::instance().getAtAxis(); + LLVector3 camUp = LLViewerCamera::instance().getUpAxis(); + mPlanarLookDir = camFwd - 2.0f * (camFwd * waterNormal) * waterNormal; + mPlanarUpDir = camUp - 2.0f * (camUp * waterNormal) * waterNormal; + mPlanarLookDir.normalize(); + mPlanarUpDir.normalize(); + } + else + { + // User probe + LLVOVolume* hero = mActiveHeroes[probeIdx - 1]; + LLVector3 hero_pos = hero->getPositionAgent(); + LLVector3 face_normal = LLVector3(0, 0, 1); + face_normal *= hero->mDrawable->getWorldRotation(); + face_normal.normalize(); + + mCurrentClipPlane.setVec(hero_pos, face_normal); + mMirrorPosition = hero_pos; + mMirrorNormal = face_normal; + mIsPlanar = hero->getScale().mV[VZ] < 0.02f; + + if (mIsPlanar) + { + LLVector3 camFwd = LLViewerCamera::instance().getAtAxis(); + LLVector3 camUp = LLViewerCamera::instance().getUpAxis(); + mPlanarLookDir = camFwd - 2.0f * (camFwd * face_normal) * face_normal; + mPlanarUpDir = camUp - 2.0f * (camUp * face_normal) * face_normal; + mPlanarLookDir.normalize(); + mPlanarUpDir.normalize(); } } - generateRadiance(mProbes[0]); + + bool dynamic = false; + if (probeIdx == 0) + { + dynamic = sDetail() > 0; + } + else + { + dynamic = mActiveHeroes[probeIdx - 1]->getReflectionProbeIsDynamic() && sDetail() > 0; + } + + if (mIsPlanar) + { + updateProbeFace(mProbes[probeIdx], 0, dynamic, near_clip); + } + else + { + // Non-planar probes capture full environment from object center. + // Disable mirror clipping so mirrorClip() doesn't discard geometry. + mRenderingMirror = false; + for (U32 i = 0; i < 6; ++i) + { + if ((gFrameCount % rate) == (i % rate)) + { + updateProbeFace(mProbes[probeIdx], i, dynamic, near_clip); + } + } + mRenderingMirror = true; + } + + generateRadiance(mProbes[probeIdx]); } + mCurrentRenderingProbeIdx = -1; mRenderingMirror = false; gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass; - - mProbes[0]->mViewerObject = mNearestHero; - mProbes[0]->autoAdjustOrigin(); } } @@ -300,7 +414,15 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, bool // hacky hot-swap of camera specific render targets gPipeline.mRT = &gPipeline.mHeroProbeRT; - probe->update(mRenderTarget.getWidth(), face, is_dynamic, near_clip); + if (mIsPlanar) + { + probe->update(mRenderTarget.getWidth(), face, is_dynamic, near_clip, + true, mCurrentClipPlane, &mPlanarLookDir, &mPlanarUpDir); + } + else + { + probe->update(mRenderTarget.getWidth(), face, is_dynamic, near_clip); + } gPipeline.mRT = &gPipeline.mMainRT; @@ -493,31 +615,140 @@ void LLHeroProbeManager::updateUniforms() LLMatrix4a modelview; modelview.loadu(gGLModelView); - LLVector4a oa; // scratch space for transformed origin + LLVector4a oa; oa.set(0, 0, 0, 0); - mHeroData.heroProbeCount = 1; - if (mNearestHero != nullptr && !mNearestHero->isDead()) + // Zero out the hero data + memset(&mHeroData, 0, sizeof(mHeroData)); + + S32 activeCount = 1 + (S32)mActiveHeroes.size(); // water + user probes + mHeroData.heroProbeCount = activeCount; + + LLVector3 camera_pos = LLViewerCamera::instance().mOrigin; + + for (S32 pi = 0; pi < activeCount; ++pi) { - if (mNearestHero->getReflectionProbeIsBox()) + if (pi >= (S32)mProbes.size() || mProbes[pi].isNull()) + continue; + + // heroParams: x=shape, y=cubeIndex + mHeroData.heroParams[pi][1] = mProbes[pi]->mCubeIndex; + + if (pi == 0) { - LLVector3 s = mNearestHero->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f)); - mProbes[0]->mRadius = s.magVec(); + // Water probe — use reflected origin for sphere + modelview.affineTransform(mProbes[pi]->mOrigin, oa); + mHeroData.heroSphere[pi].set(oa.getF32ptr()); + mHeroData.heroSphere[pi].mV[3] = mProbes[pi]->mRadius; + + // Water probe — always planar + mHeroData.heroParams[pi][0] = 2; // planar shape + + F32 waterHeight = LLEnvironment::instance().getWaterHeight(); + LLVector3 waterNormal(0.f, 0.f, 1.f); + + LLVector3 camFwd = LLViewerCamera::instance().getAtAxis(); + LLVector3 camUp = LLViewerCamera::instance().getUpAxis(); + LLVector3 lookDir = camFwd - 2.0f * (camFwd * waterNormal) * waterNormal; + LLVector3 upDir = camUp - 2.0f * (camUp * waterNormal) * waterNormal; + lookDir.normalize(); + upDir.normalize(); + + LLVector3 reflRight = lookDir % upDir; + reflRight.normalize(); + + mHeroData.heroPlaneMatrix[pi].initRows( + LLVector4(lookDir.mV[VX], -upDir.mV[VX], -reflRight.mV[VX], 0), + LLVector4(lookDir.mV[VY], -upDir.mV[VY], -reflRight.mV[VY], 0), + LLVector4(lookDir.mV[VZ], -upDir.mV[VZ], -reflRight.mV[VZ], 0), + LLVector4(0, 0, 0, 1)); + + // Clip plane set just above water to avoid Z-fighting with underwater fog. + // Computed camera-relative to avoid float32 precision loss at large world coords. + glm::mat4 mat = glm::make_mat4(gGLModelView); + glm::mat3 R(mat); + + F32 clipHeight = waterHeight + 0.0001f; + glm::vec3 relPos(0.f, 0.f, clipHeight - camera_pos.mV[VZ]); + + glm::vec3 enorm = glm::normalize(R * glm::vec3(0.f, 0.f, 1.f)); + glm::vec3 ep = R * relPos; + mHeroData.heroClipPlane[pi].set(enorm.x, enorm.y, enorm.z, -glm::dot(ep, enorm)); + + // Box transform in eye space — 256x256x0.01 volume, no world-space rotation. + { + glm::vec3 halfScale(128.f, 128.f, 0.1f); + glm::mat4 boxMat = glm::inverse(glm::mat4(R) * glm::translate(glm::mat4(1.0f), relPos) * glm::scale(glm::mat4(1.0f), halfScale)); + mHeroData.heroBox[pi] = LLMatrix4(glm::value_ptr(boxMat)); + } } else { - mProbes[0]->mRadius = mNearestHero->getScale().mV[0] * 0.5f; - } + // User probe + LLVOVolume* hero = mActiveHeroes[pi - 1]; - modelview.affineTransform(mProbes[0]->mOrigin, oa); - mHeroData.heroShape = 0; - if (!mProbes[0]->getBox(mHeroData.heroBox)) - { - mHeroData.heroShape = 1; - } + if (hero->getReflectionProbeIsBox()) + { + LLVector3 s = hero->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f)); + mProbes[pi]->mRadius = s.magVec(); + } + else + { + mProbes[pi]->mRadius = hero->getScale().mV[0] * 0.5f; + } + + // Use hero object's actual position for sphere origin (not reflected camera position). + // This matches develop where autoAdjustOrigin() reset the origin before updateUniforms(). + LLVector4a heroObjOrigin; + heroObjOrigin.load3(hero->getPositionAgent().mV); + modelview.affineTransform(heroObjOrigin, oa); + mHeroData.heroSphere[pi].set(oa.getF32ptr()); + mHeroData.heroSphere[pi].mV[3] = mProbes[pi]->mRadius; + + mHeroData.heroParams[pi][0] = 0; // box shape + if (!mProbes[pi]->getBox(mHeroData.heroBox[pi])) + { + mHeroData.heroParams[pi][0] = 1; // sphere shape + } + + LLVector3 hero_pos = hero->getPositionAgent(); + LLVector3 face_normal = LLVector3(0, 0, 1); + face_normal *= hero->mDrawable->getWorldRotation(); + face_normal.normalize(); - mHeroData.heroSphere.set(oa.getF32ptr()); - mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius; + bool isPlanar = hero->getScale().mV[VZ] < 0.02f; + + if (isPlanar) + { + mHeroData.heroParams[pi][0] = 2; // planar shape + + LLVector3 camFwd = LLViewerCamera::instance().getAtAxis(); + LLVector3 camUp = LLViewerCamera::instance().getUpAxis(); + LLVector3 lookDir = camFwd - 2.0f * (camFwd * face_normal) * face_normal; + LLVector3 upDir = camUp - 2.0f * (camUp * face_normal) * face_normal; + lookDir.normalize(); + upDir.normalize(); + + LLVector3 reflRight = lookDir % upDir; + reflRight.normalize(); + + mHeroData.heroPlaneMatrix[pi].initRows( + LLVector4(lookDir.mV[VX], -upDir.mV[VX], -reflRight.mV[VX], 0), + LLVector4(lookDir.mV[VY], -upDir.mV[VY], -reflRight.mV[VY], 0), + LLVector4(lookDir.mV[VZ], -upDir.mV[VZ], -reflRight.mV[VZ], 0), + LLVector4(0, 0, 0, 1)); + } + + // Clip plane in eye space + glm::mat4 mat = glm::make_mat4(gGLModelView); + glm::mat4 invtrans = glm::transpose(glm::inverse(mat)); + invtrans[0][3] = invtrans[1][3] = invtrans[2][3] = 0.f; + + glm::vec3 enorm = glm::normalize(glm::vec3(invtrans * glm::vec4(face_normal.mV[VX], face_normal.mV[VY], face_normal.mV[VZ], 0.f))); + glm::vec3 ep = glm::vec3(mat * glm::vec4(hero_pos.mV[VX], hero_pos.mV[VY], hero_pos.mV[VZ], 1.f)); + + mHeroData.heroClipPlane[pi].set(enorm.x, enorm.y, enorm.z, -glm::dot(ep, enorm)); + } } llassert(mMipChain.size() <= size_t(S32_MAX)); @@ -539,12 +770,11 @@ void LLHeroProbeManager::renderDebug() void LLHeroProbeManager::initReflectionMaps() { - U32 count = LL_MAX_HERO_PROBE_COUNT; + S32 count = llclamp(LLPipeline::RenderMirrorCount, 1, LL_MAX_HERO_PROBE_COUNT); - if ((mTexture.isNull() || mReflectionProbeCount != count || mReset) && LLPipeline::RenderMirrors) + if ((mTexture.isNull() || mReflectionProbeCount != (U32)count || mReset) && LLPipeline::RenderMirrors) { - - if (mReset) + if (mReset || mReflectionProbeCount != (U32)count) { cleanup(); } @@ -558,28 +788,23 @@ void LLHeroProbeManager::initReflectionMaps() static LLCachedControl render_hdr(gSavedSettings, "RenderHDREnabled", true); - // store mReflectionProbeCount+2 cube maps, final two cube maps are used for render target and radiance map generation source) + // store count+2 cube maps (count probes + 2 scratch/staging) mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2, true, render_hdr); - if (mDefaultProbe.isNull()) + // Create all probes + for (S32 i = 0; i < count; ++i) { - llassert(mProbes.empty()); // default probe MUST be the first probe created - mDefaultProbe = new LLReflectionMap(); - mProbes.push_back(mDefaultProbe); + LLPointer probe = new LLReflectionMap(); + probe->mCubeIndex = i; + probe->mCubeArray = mTexture; + probe->mDistance = gSavedSettings.getF32("RenderHeroProbeDistance"); + probe->mRadius = 4096.f; + probe->mProbeIndex = i; + touch_default_probe(probe); + mProbes.push_back(probe); } - llassert(mProbes[0] == mDefaultProbe); - - // For hero probes, we treat this as the main mirror probe. - - mDefaultProbe->mCubeIndex = 0; - mDefaultProbe->mCubeArray = mTexture; - mDefaultProbe->mDistance = gSavedSettings.getF32("RenderHeroProbeDistance"); - mDefaultProbe->mRadius = 4096.f; - mDefaultProbe->mProbeIndex = 0; - touch_default_probe(mDefaultProbe); - - mProbes.push_back(mDefaultProbe); + mDefaultProbe = mProbes[0]; } if (mVertexBuffer.isNull()) diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h index 2737ec5ddf8..62db405f0f7 100644 --- a/indra/newview/llheroprobemanager.h +++ b/indra/newview/llheroprobemanager.h @@ -36,15 +36,18 @@ class LLSpatialGroup; class LLViewerObject; // number of reflection probes to keep in vram -#define LL_MAX_HERO_PROBE_COUNT 2 +#define LL_MAX_HERO_PROBE_COUNT 8 struct HeroProbeData { - LLMatrix4 heroBox; - LLVector4 heroSphere; - GLint heroShape; + LLMatrix4 heroBox[LL_MAX_HERO_PROBE_COUNT]; + LLVector4 heroSphere[LL_MAX_HERO_PROBE_COUNT]; GLint heroMipCount; GLint heroProbeCount; + // heroParams[i] = { shape, cubeIndex, 0, 0 } + GLint heroParams[LL_MAX_HERO_PROBE_COUNT][4]; + LLMatrix4 heroPlaneMatrix[LL_MAX_HERO_PROBE_COUNT]; + LLVector4 heroClipPlane[LL_MAX_HERO_PROBE_COUNT]; }; class alignas(16) LLHeroProbeManager @@ -88,9 +91,13 @@ class alignas(16) LLHeroProbeManager bool isMirrorPass() const { return mRenderingMirror; } LLVector3 mMirrorPosition; - LLVector3 mMirrorNormal; + LLVector3 mMirrorNormal; HeroProbeData mHeroData; + bool mHeroShadowsComplete[LL_MAX_HERO_PROBE_COUNT] = {}; + + S32 mCurrentRenderingProbeIdx = -1; + private: friend class LLPipeline; friend class LLReflectionMapManager; @@ -114,6 +121,10 @@ class alignas(16) LLHeroProbeManager LLPlane mCurrentClipPlane; + bool mIsPlanar = false; + LLVector3 mPlanarLookDir; + LLVector3 mPlanarUpDir; + // update the specified face of the specified probe void updateProbeFace(LLReflectionMap* probe, U32 face, bool is_dynamic, F32 near_clip); @@ -142,7 +153,7 @@ class alignas(16) LLHeroProbeManager bool mRenderingMirror = false; std::vector> mHeroVOList; - LLPointer mNearestHero; + std::vector> mActiveHeroes; // Part of a hacky workaround to fix #3331. bool mInitialized = false; diff --git a/indra/newview/lllocalgltfmaterials.cpp b/indra/newview/lllocalgltfmaterials.cpp index d6facad23da..0cce6adaeba 100644 --- a/indra/newview/lllocalgltfmaterials.cpp +++ b/indra/newview/lllocalgltfmaterials.cpp @@ -46,7 +46,7 @@ #include "llnotificationsutil.h" #include "llscrolllistctrl.h" #include "lltextureentry.h" -#include "lltinygltfhelper.h" +#include "llgltfhelper.h" #include "llviewertexture.h" /*=======================================*/ @@ -243,14 +243,14 @@ bool LLLocalGLTFMaterial::loadMaterial() LLStringUtil::toLower(filename_lc); std::string material_name; - tinygltf::Model model; - decode_successful = LLTinyGLTFHelper::loadModel(mFilename, model); + LL::GLTF::Asset asset; + decode_successful = LLGLTFHelper::loadModel(mFilename, asset); if (decode_successful) { // Might be a good idea to make these textures into local textures - decode_successful = LLTinyGLTFHelper::getMaterialFromModel( + decode_successful = LLGLTFHelper::getMaterialFromModel( mFilename, - model, + asset, mMaterialIndex, this, material_name); @@ -343,10 +343,10 @@ S32 LLLocalGLTFMaterialMgr::addUnit(const std::vector& filenames) S32 LLLocalGLTFMaterialMgr::addUnit(const std::string& filename) { - tinygltf::Model model; - LLTinyGLTFHelper::loadModel(filename, model); + LL::GLTF::Asset asset; + LLGLTFHelper::loadModel(filename, asset); - auto materials_in_file = model.materials.size(); + auto materials_in_file = asset.mMaterials.size(); if (materials_in_file <= 0) { return 0; diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index 4e14f416e9b..92b6cb9ff18 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -61,8 +61,8 @@ #include "llviewertexturelist.h" #include "llfloaterperms.h" -#include "tinygltf/tiny_gltf.h" -#include "lltinygltfhelper.h" +#include "llgltfhelper.h" +#include "gltf/asset.h" #include #include @@ -89,6 +89,12 @@ static const U32 MATERIAL_DOUBLE_SIDED_DIRTY = 0x1 << 8; static const U32 MATERIAL_ALPHA_MODE_DIRTY = 0x1 << 9; static const U32 MATERIAL_ALPHA_CUTOFF_DIRTY = 0x1 << 10; +static const U32 MATERIAL_EMISSIVE_STRENGTH_DIRTY = 0x1 << 11; +static const U32 MATERIAL_SPECULAR_FACTOR_DIRTY = 0x1 << 12; +static const U32 MATERIAL_SPECULAR_COLOR_DIRTY = 0x1 << 13; +static const U32 MATERIAL_SPECULAR_TEX_DIRTY = 0x1 << 14; +static const U32 MATERIAL_IOR_DIRTY = 0x1 << 15; + LLUUID LLMaterialEditor::mOverrideObjectId; S32 LLMaterialEditor::mOverrideObjectTE = -1; bool LLMaterialEditor::mOverrideInProgress = false; @@ -433,8 +439,10 @@ bool LLMaterialEditor::postBuild() mMetallicTextureCtrl = getChild("metallic_roughness_texture"); mEmissiveTextureCtrl = getChild("emissive_texture"); mNormalTextureCtrl = getChild("normal_texture"); + mSpecularTextureCtrl = getChild("specular_texture"); mBaseColorCtrl = getChild("base color"); mEmissiveColorCtrl = getChild("emissive color"); + mSpecularColorCtrl = getChild("specular color"); if (!gAgent.isGodlike()) { @@ -443,6 +451,7 @@ bool LLMaterialEditor::postBuild() mMetallicTextureCtrl->setFilterPermissionMasks(PERM_COPY | PERM_TRANSFER); mEmissiveTextureCtrl->setFilterPermissionMasks(PERM_COPY | PERM_TRANSFER); mNormalTextureCtrl->setFilterPermissionMasks(PERM_COPY | PERM_TRANSFER); + mSpecularTextureCtrl->setFilterPermissionMasks(PERM_COPY | PERM_TRANSFER); } // Texture callback @@ -450,6 +459,7 @@ bool LLMaterialEditor::postBuild() mMetallicTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitTexture, this, _1, _2, MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)); mEmissiveTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitTexture, this, _1, _2, MATERIAL_EMISIVE_TEX_DIRTY)); mNormalTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitTexture, this, _1, _2, MATERIAL_NORMAL_TEX_DIRTY)); + mSpecularTextureCtrl->setCommitCallback(boost::bind(&LLMaterialEditor::onCommitTexture, this, _1, _2, MATERIAL_SPECULAR_TEX_DIRTY)); mNormalTextureCtrl->setBlankImageAssetID(BLANK_OBJECT_NORMAL); @@ -460,12 +470,14 @@ bool LLMaterialEditor::postBuild() mMetallicTextureCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)); mEmissiveTextureCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_EMISIVE_TEX_DIRTY)); mNormalTextureCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_NORMAL_TEX_DIRTY)); + mSpecularTextureCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_SPECULAR_TEX_DIRTY)); // Save applied changes on 'OK' to our recovery mechanism. mBaseColorTextureCtrl->setOnSelectCallback(boost::bind(&LLMaterialEditor::onSelectCtrl, this, _1, _2, MATERIAL_BASE_COLOR_TEX_DIRTY)); mMetallicTextureCtrl->setOnSelectCallback(boost::bind(&LLMaterialEditor::onSelectCtrl, this, _1, _2, MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)); mEmissiveTextureCtrl->setOnSelectCallback(boost::bind(&LLMaterialEditor::onSelectCtrl, this, _1, _2, MATERIAL_EMISIVE_TEX_DIRTY)); mNormalTextureCtrl->setOnSelectCallback(boost::bind(&LLMaterialEditor::onSelectCtrl, this, _1, _2, MATERIAL_NORMAL_TEX_DIRTY)); + mSpecularTextureCtrl->setOnSelectCallback(boost::bind(&LLMaterialEditor::onSelectCtrl, this, _1, _2, MATERIAL_SPECULAR_TEX_DIRTY)); } else { @@ -473,6 +485,7 @@ bool LLMaterialEditor::postBuild() mMetallicTextureCtrl->setCanApplyImmediately(false); mEmissiveTextureCtrl->setCanApplyImmediately(false); mNormalTextureCtrl->setCanApplyImmediately(false); + mSpecularTextureCtrl->setCanApplyImmediately(false); } if (!mIsOverride) @@ -524,6 +537,20 @@ bool LLMaterialEditor::postBuild() childSetCommitCallback("metalness factor", changes_callback, (void*)&MATERIAL_METALLIC_ROUGHTNESS_METALNESS_DIRTY); childSetCommitCallback("roughness factor", changes_callback, (void*)&MATERIAL_METALLIC_ROUGHTNESS_ROUGHNESS_DIRTY); + // Specular + mSpecularColorCtrl->setCommitCallback(changes_callback, (void*)&MATERIAL_SPECULAR_COLOR_DIRTY); + if (mIsOverride) + { + mSpecularColorCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_SPECULAR_COLOR_DIRTY)); + mSpecularColorCtrl->setOnSelectCallback(boost::bind(&LLMaterialEditor::onSelectCtrl, this, _1, _2, MATERIAL_SPECULAR_COLOR_DIRTY)); + } + else + { + mSpecularColorCtrl->setCanApplyImmediately(false); + } + childSetCommitCallback("specular factor", changes_callback, (void*)&MATERIAL_SPECULAR_FACTOR_DIRTY); + childSetCommitCallback("ior", changes_callback, (void*)&MATERIAL_IOR_DIRTY); + // Emissive mEmissiveColorCtrl->setCommitCallback(changes_callback, (void*)&MATERIAL_EMISIVE_COLOR_DIRTY); if (mIsOverride) @@ -535,6 +562,7 @@ bool LLMaterialEditor::postBuild() { mEmissiveColorCtrl->setCanApplyImmediately(false); } + childSetCommitCallback("emissive strength", changes_callback, (void*)&MATERIAL_EMISSIVE_STRENGTH_DIRTY); if (!mIsOverride) { @@ -762,6 +790,16 @@ void LLMaterialEditor::setEmissiveColor(const LLColor4& color) mEmissiveColorCtrl->setValue(srgbColor4(color).getValue()); } +F32 LLMaterialEditor::getEmissiveStrength() +{ + return (F32)childGetValue("emissive strength").asReal(); +} + +void LLMaterialEditor::setEmissiveStrength(F32 strength) +{ + childSetValue("emissive strength", strength); +} + LLUUID LLMaterialEditor::getNormalId() { return mNormalTextureCtrl->getValue().asUUID(); @@ -796,6 +834,46 @@ void LLMaterialEditor::setDoubleSided(bool double_sided) childSetValue("double sided", double_sided); } +F32 LLMaterialEditor::getSpecularFactor() +{ + return (F32)childGetValue("specular factor").asReal(); +} + +void LLMaterialEditor::setSpecularFactor(F32 factor) +{ + childSetValue("specular factor", factor); +} + +LLColor3 LLMaterialEditor::getSpecularColorFactor() +{ + return LLColor3(mSpecularColorCtrl->get()); +} + +void LLMaterialEditor::setSpecularColorFactor(const LLColor3& color) +{ + mSpecularColorCtrl->set(LLColor4(color)); +} + +LLUUID LLMaterialEditor::getSpecularId() +{ + return mSpecularTextureCtrl->getImageAssetID(); +} + +void LLMaterialEditor::setSpecularId(const LLUUID& id) +{ + mSpecularTextureCtrl->setImageAssetID(id); +} + +F32 LLMaterialEditor::getIOR() +{ + return (F32)childGetValue("ior").asReal(); +} + +void LLMaterialEditor::setIOR(F32 ior) +{ + childSetValue("ior", ior); +} + void LLMaterialEditor::resetUnsavedChanges() { mUnsavedChanges = 0; @@ -923,6 +1001,16 @@ void LLMaterialEditor::setEnableEditing(bool can_modify) mMetallicTextureCtrl->setEnabled(can_modify); mEmissiveTextureCtrl->setEnabled(can_modify); mNormalTextureCtrl->setEnabled(can_modify); + + // PBR Extensions V1 — hide controls when server flag is absent + bool pbr_ext = false; + LLViewerRegion* region = gAgent.getRegion(); + if (region) + pbr_ext = region->pbrExtensionsV1Enabled(); + + childSetVisible("specular_layout_pnl", pbr_ext); + childSetVisible("emissive_strength_lbl", pbr_ext); + childSetVisible("emissive strength", pbr_ext); } void LLMaterialEditor::subscribeToLocalTexture(S32 dirty_flag, const LLUUID& tracking_id) @@ -1258,15 +1346,11 @@ bool LLMaterialEditor::decodeAsset(const std::vector& buffer) { std::string data = asset["data"]; - tinygltf::TinyGLTF gltf; - tinygltf::TinyGLTF loader; - std::string error_msg; - std::string warn_msg; - - tinygltf::Model model_in; - - if (loader.LoadASCIIFromString(&model_in, &error_msg, &warn_msg, data.c_str(), static_cast(data.length()), "")) + try { + LL::GLTF::Asset gltf_asset; + boost::json::value doc = boost::json::parse(data); + gltf_asset = LL::GLTF::Asset(doc); // assets are only supposed to have one item // *NOTE: This duplicates some functionality from // LLGLTFMaterial::fromJSON, but currently does the job @@ -1274,13 +1358,11 @@ bool LLMaterialEditor::decodeAsset(const std::vector& buffer) // However, LLGLTFMaterial::asJSON should always be // used when uploading materials, to ensure the // asset is valid. - return setFromGltfModel(model_in, 0, true); + return setFromGltfModel(gltf_asset, 0, true); } - else + catch (const boost::system::system_error& e) { - LL_WARNS("MaterialEditor") << "Floater " << getKey() << " Failed to decode material asset: " << LL_NEWLINE - << warn_msg << LL_NEWLINE - << error_msg << LL_ENDL; + LL_WARNS("MaterialEditor") << "Floater " << getKey() << " Failed to decode material asset: " << e.what() << LL_ENDL; } } } @@ -1907,7 +1989,7 @@ static void pack_textures( void LLMaterialEditor::uploadMaterialFromModel( const std::string& filename, - tinygltf::Model& model_in, + LL::GLTF::Asset& asset, S32 index, const LLUUID& dest) { @@ -1916,13 +1998,13 @@ void LLMaterialEditor::uploadMaterialFromModel( return; } - if (model_in.materials.empty()) + if (asset.mMaterials.empty()) { // materials are missing return; } - if (index >= 0 && model_in.materials.size() <= index) + if (index >= 0 && asset.mMaterials.size() <= (size_t)index) { // material is missing return; @@ -1933,7 +2015,7 @@ void LLMaterialEditor::uploadMaterialFromModel( // instead of fighting for a single instance. LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor", LLSD().with("filename", filename).with("index", LLSD::Integer(index))); me->mUploadFolder = dest; - me->loadMaterial(model_in, filename, index, false); + me->loadMaterial(asset, filename, index, false); me->saveIfNeeded(); } @@ -1942,26 +2024,8 @@ void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 ind { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - tinygltf::TinyGLTF loader; - std::string error_msg; - std::string warn_msg; - - bool loaded = false; - tinygltf::Model model_in; - - std::string filename_lc = filename; - LLStringUtil::toLower(filename_lc); - - // Load a tinygltf model fom a file. Assumes that the input filename has already been - // been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish. - if (std::string::npos == filename_lc.rfind(".gltf")) - { // file is binary - loaded = loader.LoadBinaryFromFile(&model_in, &error_msg, &warn_msg, filename); - } - else - { // file is ascii - loaded = loader.LoadASCIIFromFile(&model_in, &error_msg, &warn_msg, filename); - } + LL::GLTF::Asset model_in; + bool loaded = LLGLTFHelper::loadModel(filename, model_in); if (!loaded) { @@ -1969,14 +2033,14 @@ void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 ind return; } - if (model_in.materials.empty()) + if (model_in.mMaterials.empty()) { // materials are missing LLNotificationsUtil::add("CannotUploadMaterial"); return; } - if (index >= 0 && model_in.materials.size() <= index) + if (index >= 0 && model_in.mMaterials.size() <= (size_t)index) { // material is missing LLNotificationsUtil::add("CannotUploadMaterial"); @@ -1990,7 +2054,7 @@ void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 ind me->mUploadFolder = dest_folder; me->loadMaterial(model_in, filename, index); } - else if (model_in.materials.size() == 1) + else if (model_in.mMaterials.size() == 1) { // Only one material, just load it LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor"); @@ -2001,12 +2065,9 @@ void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 ind { // Multiple materials, Promt user to select material std::list material_list; - std::vector::const_iterator mat_iter = model_in.materials.begin(); - std::vector::const_iterator mat_end = model_in.materials.end(); - - for (; mat_iter != mat_end; mat_iter++) + for (const auto& mat : model_in.mMaterials) { - std::string mat_name = mat_iter->name; + std::string mat_name = mat.mName; if (mat_name.empty()) { material_list.push_back("Material " + std::to_string(material_list.size())); @@ -2446,44 +2507,40 @@ void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notificati void upload_bulk(const std::vector& filenames, LLFilePicker::ELoadFilter type, bool allow_2k, const LLUUID& dest); -void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::string &filename, S32 index, bool open_floater) +void LLMaterialEditor::loadMaterial(const LL::GLTF::Asset& model_in, const std::string& filename, S32 index, bool open_floater) { - if (index == model_in.materials.size()) + if ((size_t)index == model_in.mMaterials.size()) { // bulk upload all the things upload_bulk({ filename }, LLFilePicker::FFLOAD_MATERIAL, true, mUploadFolder); return; } - if (model_in.materials.size() <= index) + if (model_in.mMaterials.size() <= (size_t)index) { return; } std::string folder = gDirUtilp->getDirName(filename); - tinygltf::Material material_in = model_in.materials[index]; - - tinygltf::Model model_out; - model_out.asset.version = "2.0"; - model_out.materials.resize(1); + const auto& material_in = model_in.mMaterials[index]; // get base color texture - LLPointer base_color_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, mBaseColorName); + LLPointer base_color_img = LLGLTFHelper::getTexture(folder, model_in, material_in.mPbrMetallicRoughness.mBaseColorTexture.mIndex, mBaseColorName); // get normal map - LLPointer normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index, mNormalName); + LLPointer normal_img = LLGLTFHelper::getTexture(folder, model_in, material_in.mNormalTexture.mIndex, mNormalName); // get metallic-roughness texture - LLPointer mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, mMetallicRoughnessName); + LLPointer mr_img = LLGLTFHelper::getTexture(folder, model_in, material_in.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex, mMetallicRoughnessName); // get emissive texture - LLPointer emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index, mEmissiveName); + LLPointer emissive_img = LLGLTFHelper::getTexture(folder, model_in, material_in.mEmissiveTexture.mIndex, mEmissiveName); // get occlusion map if needed LLPointer occlusion_img; - if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index) + if (material_in.mOcclusionTexture.mIndex != material_in.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex) { std::string tmp; - occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index, tmp); + occlusion_img = LLGLTFHelper::getTexture(folder, model_in, material_in.mOcclusionTexture.mIndex, tmp); } - LLTinyGLTFHelper::initFetchedTextures(material_in, base_color_img, normal_img, mr_img, emissive_img, occlusion_img, + LLGLTFHelper::initFetchedTextures(material_in, base_color_img, normal_img, mr_img, emissive_img, occlusion_img, mBaseColorFetched, mNormalFetched, mMetallicRoughnessFetched, mEmissiveFetched); pack_textures(base_color_img, normal_img, mr_img, emissive_img, occlusion_img, mBaseColorJ2C, mNormalJ2C, mMetallicRoughnessJ2C, mEmissiveJ2C); @@ -2607,22 +2664,26 @@ void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std:: } } -bool LLMaterialEditor::setFromGltfModel(const tinygltf::Model& model, S32 index, bool set_textures) +bool LLMaterialEditor::setFromGltfModel(const LL::GLTF::Asset& asset, S32 index, bool set_textures) { - if (model.materials.size() > index) + if (asset.mMaterials.size() > (size_t)index) { - const tinygltf::Material& material_in = model.materials[index]; + const auto& material_in = asset.mMaterials[index]; if (set_textures) { - S32 index; + S32 tex_idx; LLUUID id; // get base color texture - index = material_in.pbrMetallicRoughness.baseColorTexture.index; - if (index >= 0) + tex_idx = material_in.mPbrMetallicRoughness.mBaseColorTexture.mIndex; + if (tex_idx >= 0 && (size_t)tex_idx < asset.mTextures.size()) { - id.set(model.images[index].uri); + S32 src = asset.mTextures[tex_idx].mSource; + if (src >= 0 && (size_t)src < asset.mImages.size()) + { + id.set(asset.mImages[src].mUri); + } setBaseColorId(id); } else @@ -2631,10 +2692,14 @@ bool LLMaterialEditor::setFromGltfModel(const tinygltf::Model& model, S32 index, } // get normal map - index = material_in.normalTexture.index; - if (index >= 0) + tex_idx = material_in.mNormalTexture.mIndex; + if (tex_idx >= 0 && (size_t)tex_idx < asset.mTextures.size()) { - id.set(model.images[index].uri); + S32 src = asset.mTextures[tex_idx].mSource; + if (src >= 0 && (size_t)src < asset.mImages.size()) + { + id.set(asset.mImages[src].mUri); + } setNormalId(id); } else @@ -2643,10 +2708,14 @@ bool LLMaterialEditor::setFromGltfModel(const tinygltf::Model& model, S32 index, } // get metallic-roughness texture - index = material_in.pbrMetallicRoughness.metallicRoughnessTexture.index; - if (index >= 0) + tex_idx = material_in.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex; + if (tex_idx >= 0 && (size_t)tex_idx < asset.mTextures.size()) { - id.set(model.images[index].uri); + S32 src = asset.mTextures[tex_idx].mSource; + if (src >= 0 && (size_t)src < asset.mImages.size()) + { + id.set(asset.mImages[src].mUri); + } setMetallicRoughnessId(id); } else @@ -2655,10 +2724,14 @@ bool LLMaterialEditor::setFromGltfModel(const tinygltf::Model& model, S32 index, } // get emissive texture - index = material_in.emissiveTexture.index; - if (index >= 0) + tex_idx = material_in.mEmissiveTexture.mIndex; + if (tex_idx >= 0 && (size_t)tex_idx < asset.mTextures.size()) { - id.set(model.images[index].uri); + S32 src = asset.mTextures[tex_idx].mSource; + if (src >= 0 && (size_t)src < asset.mImages.size()) + { + id.set(asset.mImages[src].mUri); + } setEmissiveId(id); } else @@ -2667,25 +2740,101 @@ bool LLMaterialEditor::setFromGltfModel(const tinygltf::Model& model, S32 index, } } - setAlphaMode(material_in.alphaMode); - setAlphaCutoff((F32)material_in.alphaCutoff); + // Convert alpha mode enum to string + std::string alpha_mode; + switch (material_in.mAlphaMode) + { + case LL::GLTF::Material::AlphaMode::MASK: + alpha_mode = "MASK"; + break; + case LL::GLTF::Material::AlphaMode::BLEND: + alpha_mode = "BLEND"; + break; + case LL::GLTF::Material::AlphaMode::OPAQUE: + default: + alpha_mode = "OPAQUE"; + break; + } + setAlphaMode(alpha_mode); + setAlphaCutoff((F32)material_in.mAlphaCutoff); - setBaseColor(LLTinyGLTFHelper::getColor(material_in.pbrMetallicRoughness.baseColorFactor)); - setEmissiveColor(LLTinyGLTFHelper::getColor(material_in.emissiveFactor)); + const auto& bcf = material_in.mPbrMetallicRoughness.mBaseColorFactor; + setBaseColor(LLColor4((F32)bcf.x, (F32)bcf.y, (F32)bcf.z, (F32)bcf.w)); + const auto& ef = material_in.mEmissiveFactor; + setEmissiveColor(LLColor4((F32)ef.x, (F32)ef.y, (F32)ef.z, 1.0f)); + if (material_in.mEmissiveStrength.mPresent) + { + setEmissiveStrength((F32)material_in.mEmissiveStrength.mEmissiveStrength); + } + else + { + setEmissiveStrength(1.0f); + } + + setMetalnessFactor((F32)material_in.mPbrMetallicRoughness.mMetallicFactor); + setRoughnessFactor((F32)material_in.mPbrMetallicRoughness.mRoughnessFactor); - setMetalnessFactor((F32)material_in.pbrMetallicRoughness.metallicFactor); - setRoughnessFactor((F32)material_in.pbrMetallicRoughness.roughnessFactor); + setDoubleSided(material_in.mDoubleSided); - setDoubleSided(material_in.doubleSided); + // Specular + if (material_in.mSpecular.mPresent) + { + setSpecularFactor((F32)material_in.mSpecular.mSpecularFactor); + const auto& scf = material_in.mSpecular.mSpecularColorFactor; + setSpecularColorFactor(LLColor3((F32)scf.x, (F32)scf.y, (F32)scf.z)); + + if (set_textures) + { + // Prefer specularColorTexture (RGB), fall back to specularTexture (A) + S32 spec_tex_idx = material_in.mSpecular.mSpecularColorTexture.mIndex; + if (spec_tex_idx < 0) + { + spec_tex_idx = material_in.mSpecular.mSpecularTexture.mIndex; + } + if (spec_tex_idx >= 0 && (size_t)spec_tex_idx < asset.mTextures.size()) + { + S32 src = asset.mTextures[spec_tex_idx].mSource; + if (src >= 0 && (size_t)src < asset.mImages.size()) + { + LLUUID spec_id; + spec_id.set(asset.mImages[src].mUri); + setSpecularId(spec_id); + } + } + else + { + setSpecularId(LLUUID::null); + } + } + } + else + { + setSpecularFactor(1.0f); + setSpecularColorFactor(LLColor3(1.f, 1.f, 1.f)); + if (set_textures) + { + setSpecularId(LLUUID::null); + } + } + + // IOR + if (material_in.mIOR.mPresent) + { + setIOR((F32)material_in.mIOR.mIor); + } + else + { + setIOR(1.5f); + } } return true; } /** - * Build a texture name from the contents of the (in tinyGLFT parlance) - * Image URI. This often is filepath to the original image on the users' - * local file system. + * Build a texture name from the contents of the GLTF Image URI. + * This often is filepath to the original image on the users' + * local file system. */ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, const std::string texture_type) { @@ -2760,7 +2909,7 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c * the name of the material, a material description and the names of the * composite textures. */ -void LLMaterialEditor::setFromGltfMetaData(const std::string& filename, const tinygltf::Model& model, S32 index) +void LLMaterialEditor::setFromGltfMetaData(const std::string& filename, const LL::GLTF::Asset& asset, S32 index) { // Use the name (without any path/extension) of the file that was // uploaded as the base of the material name. Then if the name of the @@ -2775,16 +2924,16 @@ void LLMaterialEditor::setFromGltfMetaData(const std::string& filename, const ti // generic name like "Scene" or "Default" so using this in the name // is less useful than you might imagine. std::string material_name; - if (model.materials.size() > index && !model.materials[index].name.empty()) + if (asset.mMaterials.size() > (size_t)index && !asset.mMaterials[index].mName.empty()) { - material_name = model.materials[index].name; + material_name = asset.mMaterials[index].mName; } - else if (model.scenes.size() > 0) + else if (asset.mScenes.size() > 0) { - const tinygltf::Scene& scene_in = model.scenes[0]; - if (scene_in.name.length()) + const auto& scene_in = asset.mScenes[0]; + if (scene_in.mName.length()) { - material_name = scene_in.name; + material_name = scene_in.mName; } else { @@ -2831,50 +2980,69 @@ void LLMaterialEditor::setFromGltfMetaData(const std::string& filename, const ti /** * Extract / derive the names of each composite texture. For each, the - * index is used to to determine which of the "Images" is used. If the index - * is -1 then that texture type is not present in the material (Seems to be - * quite common that a material is missing 1 or more types of texture) + * texture index is resolved through the textures array to find the + * source image. If the index is -1 then that texture type is not present + * in the material (quite common that a material is missing 1 or more + * types of texture). */ - if (model.materials.size() > index) + if (asset.mMaterials.size() > (size_t)index) { - const tinygltf::Material& first_material = model.materials[index]; + const auto& first_material = asset.mMaterials[index]; + + // Helper lambda to resolve a texture index to an image URI + auto getImageUri = [&asset](S32 tex_idx) -> std::string + { + if (tex_idx >= 0 && (size_t)tex_idx < asset.mTextures.size()) + { + S32 src = asset.mTextures[tex_idx].mSource; + if (src >= 0 && (size_t)src < asset.mImages.size()) + { + return asset.mImages[src].mUri; + } + } + return std::string(); + }; mBaseColorName = MATERIAL_BASE_COLOR_DEFAULT_NAME; // note: unlike the other textures, base color doesn't have its own entry - // in the tinyGLTF Material struct. Rather, it is taken from a + // at the top level of the Material struct. Rather, it is taken from a // sub-texture in the pbrMetallicRoughness member - int index = first_material.pbrMetallicRoughness.baseColorTexture.index; - if (index > -1 && index < model.images.size()) + S32 tex_idx = first_material.mPbrMetallicRoughness.mBaseColorTexture.mIndex; + std::string image_uri = getImageUri(tex_idx); + if (!image_uri.empty()) { // sanitize the name we decide to use for each texture - std::string texture_name = getImageNameFromUri(model.images[index].uri, MATERIAL_BASE_COLOR_DEFAULT_NAME); + std::string texture_name = getImageNameFromUri(image_uri, MATERIAL_BASE_COLOR_DEFAULT_NAME); LLInventoryObject::correctInventoryName(texture_name); mBaseColorName = texture_name; } mEmissiveName = MATERIAL_EMISSIVE_DEFAULT_NAME; - index = first_material.emissiveTexture.index; - if (index > -1 && index < model.images.size()) + tex_idx = first_material.mEmissiveTexture.mIndex; + image_uri = getImageUri(tex_idx); + if (!image_uri.empty()) { - std::string texture_name = getImageNameFromUri(model.images[index].uri, MATERIAL_EMISSIVE_DEFAULT_NAME); + std::string texture_name = getImageNameFromUri(image_uri, MATERIAL_EMISSIVE_DEFAULT_NAME); LLInventoryObject::correctInventoryName(texture_name); mEmissiveName = texture_name; } mMetallicRoughnessName = MATERIAL_METALLIC_DEFAULT_NAME; - index = first_material.pbrMetallicRoughness.metallicRoughnessTexture.index; - if (index > -1 && index < model.images.size()) + tex_idx = first_material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mIndex; + image_uri = getImageUri(tex_idx); + if (!image_uri.empty()) { - std::string texture_name = getImageNameFromUri(model.images[index].uri, MATERIAL_METALLIC_DEFAULT_NAME); + std::string texture_name = getImageNameFromUri(image_uri, MATERIAL_METALLIC_DEFAULT_NAME); LLInventoryObject::correctInventoryName(texture_name); mMetallicRoughnessName = texture_name; } mNormalName = MATERIAL_NORMAL_DEFAULT_NAME; - index = first_material.normalTexture.index; - if (index > -1 && index < model.images.size()) + tex_idx = first_material.mNormalTexture.mIndex; + image_uri = getImageUri(tex_idx); + if (!image_uri.empty()) { - std::string texture_name = getImageNameFromUri(model.images[index].uri, MATERIAL_NORMAL_DEFAULT_NAME); + std::string texture_name = getImageNameFromUri(image_uri, MATERIAL_NORMAL_DEFAULT_NAME); LLInventoryObject::correctInventoryName(texture_name); mNormalName = texture_name; } @@ -3148,12 +3316,85 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedNodeFunctor material->setAlphaCutoff(revert_mat->mAlphaCutoff, false); } + if (changed_flags & MATERIAL_EMISSIVE_STRENGTH_DIRTY) + { + material->setEmissiveStrength(mEditor->getEmissiveStrength(), true); + } + else if ((reverted_flags & MATERIAL_EMISSIVE_STRENGTH_DIRTY) && revert_mat.notNull()) + { + material->setEmissiveStrength(revert_mat->mEmissiveStrength, false); + } + + if (changed_flags & MATERIAL_SPECULAR_FACTOR_DIRTY) + { + material->setSpecularFactor(mEditor->getSpecularFactor(), true); + } + else if ((reverted_flags & MATERIAL_SPECULAR_FACTOR_DIRTY) && revert_mat.notNull()) + { + material->setSpecularFactor(revert_mat->mSpecularFactor, false); + } + + if (changed_flags & MATERIAL_SPECULAR_COLOR_DIRTY) + { + material->setSpecularColorFactor(mEditor->getSpecularColorFactor(), true); + } + else if ((reverted_flags & MATERIAL_SPECULAR_COLOR_DIRTY) && revert_mat.notNull()) + { + material->setSpecularColorFactor(revert_mat->mSpecularColorFactor, false); + } + + if (changed_flags & MATERIAL_IOR_DIRTY) + { + material->setIOR(mEditor->getIOR(), true); + } + else if ((reverted_flags & MATERIAL_IOR_DIRTY) && revert_mat.notNull()) + { + material->setIOR(revert_mat->mIOR, false); + } + + if (changed_flags & MATERIAL_SPECULAR_TEX_DIRTY) + { + material->setSpecularId(mEditor->getSpecularId(), true); + LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_SPECULAR_TEX_DIRTY); + if (tracking_id.notNull()) + { + LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material); + } + } + else if ((reverted_flags & MATERIAL_SPECULAR_TEX_DIRTY) && revert_mat.notNull()) + { + material->setSpecularId(revert_mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR], false); + LLUUID tracking_id = mEditor->getLocalTextureTrackingIdFromFlag(MATERIAL_SPECULAR_TEX_DIRTY); + if (tracking_id.notNull()) + { + LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(tracking_id, material); + } + } + if (mObjectTE == te && mObjectId == objectp->getID()) { mSuccess = true; } LLGLTFMaterialList::queueModify(objectp, te, material); + + static LLCachedControl local_gltf_overrides(gSavedSettings, "LocalGLTFMaterialOverrides", false); + if (local_gltf_overrides) + { + // Apply override locally for immediate visual feedback. + // The server will sanitize fields it doesn't understand + // (e.g. KHR_materials_specular, KHR_materials_emissive_strength), + // so we need to apply locally to ensure those fields take effect. + objectp->setTEGLTFMaterialOverride(te, material); + + // Update the region cache with extension data so that + // applyCacheMiscExtras won't clobber it during drawable rebuild. + LLViewerRegion* regionp = objectp->getRegion(); + if (regionp) + { + regionp->cacheGLTFOverrideSide(objectp->getLocalID(), objectp->getID(), te, material); + } + } } return true; } @@ -3179,6 +3420,9 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedNodeFunctor void LLMaterialEditor::applyToSelection() { + LL_WARNS("GLTF") << "applyToSelection: mIsOverride=" << mIsOverride + << " mUnsavedChanges=0x" << std::hex << mUnsavedChanges + << " mRevertedChanges=0x" << mRevertedChanges << std::dec << LL_ENDL; if (!mIsOverride) { // Only apply if working with 'live' materials @@ -3247,11 +3491,17 @@ void LLMaterialEditor::getGLTFMaterial(LLGLTFMaterial* mat) mat->mRoughnessFactor = getRoughnessFactor(); mat->mEmissiveColor = getEmissiveColor(); + mat->mEmissiveStrength = getEmissiveStrength(); mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = getEmissiveId(); mat->mDoubleSided = getDoubleSided(); mat->setAlphaMode(getAlphaMode()); mat->mAlphaCutoff = getAlphaCutoff(); + + mat->mSpecularFactor = getSpecularFactor(); + mat->mSpecularColorFactor = getSpecularColorFactor(); + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR] = getSpecularId(); + mat->mIOR = getIOR(); } void LLMaterialEditor::setFromGLTFMaterial(LLGLTFMaterial* mat) @@ -3265,12 +3515,18 @@ void LLMaterialEditor::setFromGLTFMaterial(LLGLTFMaterial* mat) setRoughnessFactor(mat->mRoughnessFactor); setEmissiveColor(mat->mEmissiveColor); + setEmissiveStrength(mat->mEmissiveStrength); setEmissiveId(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]); setDoubleSided(mat->mDoubleSided); setAlphaMode(mat->getAlphaMode()); setAlphaCutoff(mat->mAlphaCutoff); + setSpecularFactor(mat->mSpecularFactor); + setSpecularColorFactor(mat->mSpecularColorFactor); + setSpecularId(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR]); + setIOR(mat->mIOR); + if (mat->hasLocalTextures()) { for (LLGLTFMaterial::local_tex_map_t::value_type &val : mat->mTrackingIdToLocalTexture) @@ -3296,6 +3552,10 @@ void LLMaterialEditor::setFromGLTFMaterial(LLGLTFMaterial* mat) { subscribeToLocalTexture(MATERIAL_NORMAL_TEX_DIRTY, val.first); } + if (world_id == mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_SPECULAR]) + { + subscribeToLocalTexture(MATERIAL_SPECULAR_TEX_DIRTY, val.first); + } } } } @@ -3809,9 +4069,9 @@ void LLMaterialEditor::clearTextures() void LLMaterialEditor::loadDefaults() { - tinygltf::Model model_in; - model_in.materials.resize(1); - setFromGltfModel(model_in, 0, true); + LL::GLTF::Asset asset; + asset.mMaterials.resize(1); + setFromGltfModel(asset, 0, true); } bool LLMaterialEditor::capabilitiesAvailable() diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h index 1abdd7f84c0..7b2b9643e5e 100644 --- a/indra/newview/llmaterialeditor.h +++ b/indra/newview/llmaterialeditor.h @@ -40,10 +40,7 @@ class LLTextureCtrl; class LLTextBox; class LLViewerInventoryItem; -namespace tinygltf -{ - class Model; -} +namespace LL { namespace GLTF { class Asset; } } // todo: Consider making into a notification or just merging with // presets. Layout is identical to camera/graphics presets so there @@ -89,9 +86,9 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener LLMaterialEditor(const LLSD& key); ~LLMaterialEditor(); - bool setFromGltfModel(const tinygltf::Model& model, S32 index, bool set_textures = false); + bool setFromGltfModel(const LL::GLTF::Asset& asset, S32 index, bool set_textures = false); - void setFromGltfMetaData(const std::string& filename, const tinygltf::Model& model, S32 index); + void setFromGltfMetaData(const std::string& filename, const LL::GLTF::Asset& asset, S32 index); // open a file dialog and select a gltf/glb file for import static void importMaterial(const LLUUID dest_folder = LLUUID::null); @@ -106,7 +103,7 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener // @index if -1 and file contains more than one material, // will promt to select specific one static void uploadMaterialFromModel(const std::string& filename, - tinygltf::Model& model, + LL::GLTF::Asset& asset, S32 index, const LLUUID& dest_folder_id = LLUUID::null); static void loadMaterialFromFile(const std::string& filename, S32 index = -1, const LLUUID& dest_folder = LLUUID::null); @@ -209,6 +206,9 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener LLColor4 getEmissiveColor(); void setEmissiveColor(const LLColor4& color); + F32 getEmissiveStrength(); + void setEmissiveStrength(F32 strength); + LLUUID getNormalId(); void setNormalId(const LLUUID& id); void setNormalUploadId(const LLUUID& id); @@ -216,6 +216,16 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener bool getDoubleSided(); void setDoubleSided(bool double_sided); + F32 getSpecularFactor(); + void setSpecularFactor(F32 factor); + LLColor3 getSpecularColorFactor(); + void setSpecularColorFactor(const LLColor3& color); + LLUUID getSpecularId(); + void setSpecularId(const LLUUID& id); + + F32 getIOR(); + void setIOR(F32 ior); + void setCanSaveAs(bool value); void setCanSave(bool value); void setEnableEditing(bool can_modify); @@ -245,7 +255,7 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener void setFromGLTFMaterial(LLGLTFMaterial* mat); bool setFromSelection(); - void loadMaterial(const tinygltf::Model &model, const std::string & filename, S32 index, bool open_floater = true); + void loadMaterial(const LL::GLTF::Asset& asset, const std::string& filename, S32 index, bool open_floater = true); friend class LLMaterialFilePicker; @@ -256,8 +266,10 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener LLTextureCtrl* mMetallicTextureCtrl; LLTextureCtrl* mEmissiveTextureCtrl; LLTextureCtrl* mNormalTextureCtrl; + LLTextureCtrl* mSpecularTextureCtrl; LLColorSwatchCtrl* mBaseColorCtrl; LLColorSwatchCtrl* mEmissiveColorCtrl; + LLColorSwatchCtrl* mSpecularColorCtrl; // 'Default' texture, unless it's null or from inventory is the one with the fee LLUUID mBaseColorTextureUploadId; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index b2e21bea1ca..040ad52add5 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -30,7 +30,7 @@ #include "llmodelloader.h" #include "lldaeloader.h" -#include "gltf/llgltfloader.h" +#include "llgltfloader.h" #include "llfloatermodelpreview.h" #include "llagent.h" diff --git a/indra/newview/llpaneleditsky.cpp b/indra/newview/llpaneleditsky.cpp index 578206a7681..0b808b9b773 100644 --- a/indra/newview/llpaneleditsky.cpp +++ b/indra/newview/llpaneleditsky.cpp @@ -68,6 +68,7 @@ namespace const std::string FIELD_SKY_GLOW_FOCUS("glow_focus"); const std::string FIELD_SKY_GLOW_SIZE("glow_size"); const std::string FIELD_SKY_STAR_BRIGHTNESS("star_brightness"); + const std::string FIELD_SKY_SUN_BRIGHTNESS("sun_brightness"); const std::string FIELD_SKY_SUN_ROTATION("sun_rotation"); const std::string FIELD_SKY_SUN_AZIMUTH("sun_azimuth"); const std::string FIELD_SKY_SUN_ELEVATION("sun_elevation"); @@ -82,6 +83,12 @@ namespace const std::string FIELD_SKY_MOON_SCALE("moon_scale"); const std::string FIELD_SKY_MOON_BRIGHTNESS("moon_brightness"); + const std::string FIELD_SKY_HDR_MIN("hdr_min"); + const std::string FIELD_SKY_HDR_MAX("hdr_max"); + const std::string FIELD_SKY_HDR_OFFSET("hdr_offset"); + const std::string FIELD_SKY_TONEMAPPER("tonemapper"); + const std::string FIELD_SKY_TONEMAP_MIX("tonemap_mix"); + const std::string PANEL_SKY_SUN_LAYOUT("sun_layout"); const std::string PANEL_SKY_MOON_LAYOUT("moon_layout"); @@ -109,6 +116,7 @@ namespace const std::string FIELD_SKY_DENSITY_ICE_LEVEL("ice_level"); const std::string FIELD_REFLECTION_PROBE_AMBIANCE("probe_ambiance"); + const std::string FIELD_AMBIENT_SKY_SATURATION("ambient_sky_saturation"); const F32 SLIDER_SCALE_SUN_AMBIENT(3.0f); const F32 SLIDER_SCALE_BLUE_HORIZON_DENSITY(2.0f); @@ -153,6 +161,12 @@ bool LLPanelSettingsSkyAtmosTab::postBuild() getChild(FIELD_SKY_DENSITY_DROPLET_RADIUS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onDropletRadiusChanged(); }); getChild(FIELD_SKY_DENSITY_ICE_LEVEL)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onIceLevelChanged(); }); getChild(FIELD_REFLECTION_PROBE_AMBIANCE)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onReflectionProbeAmbianceChanged(); }); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onAmbientSkySaturationChanged(); }); + getChild(FIELD_SKY_TONEMAPPER)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTonemapperChanged(); }); + getChild(FIELD_SKY_TONEMAP_MIX)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTonemapMixChanged(); }); + getChild(FIELD_SKY_HDR_MIN)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onHDRMinChanged(); }); + getChild(FIELD_SKY_HDR_MAX)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onHDRMaxChanged(); }); + getChild(FIELD_SKY_HDR_OFFSET)->setCommitCallback([this](LLUICtrl*, const LLSD&) { onHDROffsetChanged(); }); refresh(); return true; @@ -176,6 +190,12 @@ void LLPanelSettingsSkyAtmosTab::setEnabled(bool enabled) getChild(FIELD_SKY_DENSITY_DROPLET_RADIUS)->setEnabled(enabled); getChild(FIELD_SKY_DENSITY_ICE_LEVEL)->setEnabled(enabled); getChild(FIELD_REFLECTION_PROBE_AMBIANCE)->setEnabled(enabled); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setEnabled(enabled); + getChild(FIELD_SKY_TONEMAPPER)->setEnabled(enabled); + getChild(FIELD_SKY_TONEMAP_MIX)->setEnabled(enabled); + getChild(FIELD_SKY_HDR_MIN)->setEnabled(enabled); + getChild(FIELD_SKY_HDR_MAX)->setEnabled(enabled); + getChild(FIELD_SKY_HDR_OFFSET)->setEnabled(enabled); } } @@ -191,6 +211,13 @@ void LLPanelSettingsSkyAtmosTab::refresh() setEnabled(getCanChangeSettings()); setAllChildrenEnabled(getCanChangeSettings()); + bool is_v2 = mSkySettings->getSkySettingVersion() >= 2; + getChild(FIELD_SKY_TONEMAPPER)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_SKY_TONEMAP_MIX)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_SKY_HDR_OFFSET)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_SKY_HDR_MIN)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_SKY_HDR_MAX)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_SKY_AMBIENT_LIGHT)->set(mSkySettings->getAmbientColor() / SLIDER_SCALE_SUN_AMBIENT); getChild(FIELD_SKY_BLUE_HORIZON)->set(mSkySettings->getBlueHorizon() / SLIDER_SCALE_BLUE_HORIZON_DENSITY); getChild(FIELD_SKY_BLUE_DENSITY)->set(mSkySettings->getBlueDensity() / SLIDER_SCALE_BLUE_HORIZON_DENSITY); @@ -215,6 +242,14 @@ void LLPanelSettingsSkyAtmosTab::refresh() getChild(FIELD_SKY_DENSITY_DROPLET_RADIUS)->setValue(droplet_radius); getChild(FIELD_SKY_DENSITY_ICE_LEVEL)->setValue(ice_level); getChild(FIELD_REFLECTION_PROBE_AMBIANCE)->setValue(rp_ambiance); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_AMBIENT_SKY_SATURATION)->setValue(mSkySettings->getAmbientSkySaturation()); + + getChild(FIELD_SKY_TONEMAPPER)->setValue((S32)mSkySettings->getTonemapper()); + getChild(FIELD_SKY_TONEMAP_MIX)->setValue(mSkySettings->getTonemapMix()); + getChild(FIELD_SKY_HDR_MIN)->setValue(mSkySettings->getHDRMin()); + getChild(FIELD_SKY_HDR_MAX)->setValue(mSkySettings->getHDRMax()); + getChild(FIELD_SKY_HDR_OFFSET)->setValue(mSkySettings->getHDROffset()); updateGammaLabel(should_auto_adjust); } @@ -333,6 +368,53 @@ void LLPanelSettingsSkyAtmosTab::onReflectionProbeAmbianceChanged() updateGammaLabel(); } +void LLPanelSettingsSkyAtmosTab::onAmbientSkySaturationChanged() +{ + if (!mSkySettings) return; + mSkySettings->setAmbientSkySaturation((F32)getChild(FIELD_AMBIENT_SKY_SATURATION)->getValue().asReal()); + mSkySettings->update(); + setIsDirty(); +} + +void LLPanelSettingsSkyAtmosTab::onTonemapperChanged() +{ + if (!mSkySettings) return; + mSkySettings->setTonemapper((U8)getChild(FIELD_SKY_TONEMAPPER)->getValue().asInteger()); + mSkySettings->update(); + setIsDirty(); +} + +void LLPanelSettingsSkyAtmosTab::onTonemapMixChanged() +{ + if (!mSkySettings) return; + mSkySettings->setTonemapMix((F32)getChild(FIELD_SKY_TONEMAP_MIX)->getValue().asReal()); + mSkySettings->update(); + setIsDirty(); +} + +void LLPanelSettingsSkyAtmosTab::onHDRMinChanged() +{ + if (!mSkySettings) return; + mSkySettings->setHDRMin((F32)getChild(FIELD_SKY_HDR_MIN)->getValue().asReal()); + mSkySettings->update(); + setIsDirty(); +} + +void LLPanelSettingsSkyAtmosTab::onHDRMaxChanged() +{ + if (!mSkySettings) return; + mSkySettings->setHDRMax((F32)getChild(FIELD_SKY_HDR_MAX)->getValue().asReal()); + mSkySettings->update(); + setIsDirty(); +} + +void LLPanelSettingsSkyAtmosTab::onHDROffsetChanged() +{ + if (!mSkySettings) return; + mSkySettings->setHDROffset((F32)getChild(FIELD_SKY_HDR_OFFSET)->getValue().asReal()); + mSkySettings->update(); + setIsDirty(); +} void LLPanelSettingsSkyAtmosTab::updateGammaLabel(bool auto_adjust) { @@ -515,6 +597,7 @@ bool LLPanelSettingsSkySunMoonTab::postBuild() getChild(FIELD_SKY_GLOW_FOCUS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onGlowChanged(); }); getChild(FIELD_SKY_GLOW_SIZE)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onGlowChanged(); }); getChild(FIELD_SKY_STAR_BRIGHTNESS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onStarBrightnessChanged(); }); + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunBrightnessChanged(); }); getChild(FIELD_SKY_SUN_ROTATION)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunRotationChanged(); }); getChild(FIELD_SKY_SUN_AZIMUTH)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunAzimElevChanged(); }); getChild(FIELD_SKY_SUN_ELEVATION)->setCommitCallback([this](LLUICtrl *, const LLSD &) { onSunAzimElevChanged(); }); @@ -549,6 +632,7 @@ void LLPanelSettingsSkySunMoonTab::setEnabled(bool enabled) getChild(FIELD_SKY_GLOW_FOCUS)->setEnabled(enabled); getChild(FIELD_SKY_GLOW_SIZE)->setEnabled(enabled); getChild(FIELD_SKY_STAR_BRIGHTNESS)->setEnabled(enabled); + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setEnabled(enabled); getChild(FIELD_SKY_SUN_SCALE)->setEnabled(enabled); getChild(FIELD_SKY_MOON_SCALE)->setEnabled(enabled); getChild(FIELD_SKY_MOON_BRIGHTNESS)->setEnabled(enabled); @@ -575,6 +659,9 @@ void LLPanelSettingsSkySunMoonTab::refresh() setAllChildrenEnabled(true); } + bool is_v2 = mSkySettings->getSkySettingVersion() > 1; + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setEnabled(is_v2 && getCanChangeSettings()); + getChild(FIELD_SKY_SUN_MOON_COLOR)->set(mSkySettings->getSunlightColor() / SLIDER_SCALE_SUN_AMBIENT); LLColor3 glow(mSkySettings->getGlow()); @@ -584,6 +671,7 @@ void LLPanelSettingsSkySunMoonTab::refresh() getChild(FIELD_SKY_GLOW_FOCUS)->setValue(glow.mV[2] / SLIDER_SCALE_GLOW_B); getChild(FIELD_SKY_STAR_BRIGHTNESS)->setValue(mSkySettings->getStarBrightness()); + getChild(FIELD_SKY_SUN_BRIGHTNESS)->setValue(mSkySettings->getSunBrightness()); getChild(FIELD_SKY_SUN_IMAGE)->setValue(mSkySettings->getSunTextureId()); getChild(FIELD_SKY_SUN_SCALE)->setValue(mSkySettings->getSunScale()); getChild(FIELD_SKY_MOON_IMAGE)->setValue(mSkySettings->getMoonTextureId()); @@ -644,6 +732,14 @@ void LLPanelSettingsSkySunMoonTab::onStarBrightnessChanged() setIsDirty(); } +void LLPanelSettingsSkySunMoonTab::onSunBrightnessChanged() +{ + if (!mSkySettings) return; + mSkySettings->setSunBrightness((F32)getChild(FIELD_SKY_SUN_BRIGHTNESS)->getValue().asReal()); + mSkySettings->update(); + setIsDirty(); +} + void LLPanelSettingsSkySunMoonTab::onSunRotationChanged() { LLQuaternion quat = getChild(FIELD_SKY_SUN_ROTATION)->getRotation(); diff --git a/indra/newview/llpaneleditsky.h b/indra/newview/llpaneleditsky.h index bab606bc8e8..e3225b3064d 100644 --- a/indra/newview/llpaneleditsky.h +++ b/indra/newview/llpaneleditsky.h @@ -80,6 +80,12 @@ class LLPanelSettingsSkyAtmosTab : public LLPanelSettingsSky void onDropletRadiusChanged(); void onIceLevelChanged(); void onReflectionProbeAmbianceChanged(); + void onAmbientSkySaturationChanged(); + void onTonemapperChanged(); + void onTonemapMixChanged(); + void onHDRMinChanged(); + void onHDRMaxChanged(); + void onHDROffsetChanged(); void updateGammaLabel(bool auto_adjust = false); }; @@ -125,6 +131,7 @@ class LLPanelSettingsSkySunMoonTab : public LLPanelSettingsSky void onSunMoonColorChanged(); void onGlowChanged(); void onStarBrightnessChanged(); + void onSunBrightnessChanged(); void onSunRotationChanged(); void onSunAzimElevChanged(); void onSunScaleChanged(); diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp index f4f3e39f177..bed89328d0c 100644 --- a/indra/newview/llreflectionmap.cpp +++ b/indra/newview/llreflectionmap.cpp @@ -50,7 +50,8 @@ LLReflectionMap::~LLReflectionMap() } } -void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic, F32 near_clip, bool useClipPlane, LLPlane clipPlane) +void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic, F32 near_clip, bool useClipPlane, LLPlane clipPlane, + const LLVector3* overrideLookDir, const LLVector3* overrideUpDir) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; if (!mCubeArray.notNull()) @@ -71,7 +72,8 @@ void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic, F32 n F32 clip = (near_clip > 0) ? near_clip : getNearClip(); bool dynamic = force_dynamic || getIsDynamic(); - gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, clip, dynamic, useClipPlane, clipPlane); + gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, clip, dynamic, useClipPlane, clipPlane, + overrideLookDir, overrideUpDir); } void LLReflectionMap::autoAdjustOrigin() diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h index 8da18e09451..a1ed596619b 100644 --- a/indra/newview/llreflectionmap.h +++ b/indra/newview/llreflectionmap.h @@ -52,7 +52,8 @@ class alignas(16) LLReflectionMap : public LLRefCount // update this environment map // resolution - size of cube map to generate - void update(U32 resolution, U32 face, bool force_dynamic = false, F32 near_clip = -1.f, bool useClipPlane = false, LLPlane clipPlane = LLPlane(LLVector3(0, 0, 0), LLVector3(0, 0, 1))); + void update(U32 resolution, U32 face, bool force_dynamic = false, F32 near_clip = -1.f, bool useClipPlane = false, LLPlane clipPlane = LLPlane(LLVector3(0, 0, 0), LLVector3(0, 0, 1)), + const LLVector3* overrideLookDir = nullptr, const LLVector3* overrideUpDir = nullptr); // for volume partition probes, try to place this probe in the best spot void autoAdjustOrigin(); diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index d2e37fb40a5..39c8f05343d 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -271,10 +271,21 @@ void LLReflectionMapManager::update() initReflectionMaps(); static LLCachedControl render_hdr(gSavedSettings, "RenderHDREnabled", true); + static LLCachedControl probe_quality(gSavedSettings, "RenderReflectionProbeQuality", 1); if (!mRenderTarget.isComplete()) { - U32 color_fmt = render_hdr ? GL_R11F_G11F_B10F : GL_RGB8; + U32 color_fmt; + if (probe_quality > 0) + { + // High quality: RGBA with alpha preservation for sky blending + color_fmt = render_hdr ? GL_RGBA16F : GL_RGBA8; + } + else + { + // Low quality: RGB only (legacy path) + color_fmt = render_hdr ? GL_R11F_G11F_B10F : GL_RGB8; + } U32 targetRes = mProbeResolution * 4; // super sample mRenderTarget.allocate(targetRes, targetRes, color_fmt, true); } @@ -287,7 +298,18 @@ void LLReflectionMapManager::update() mMipChain.resize(count); for (U32 i = 0; i < count; ++i) { - mMipChain[i].allocate(res, res, render_hdr ? GL_R11F_G11F_B10F : GL_RGB8); + U32 mip_fmt; + if (probe_quality > 0) + { + // High quality: RGBA with alpha preservation + mip_fmt = render_hdr ? GL_RGBA16F : GL_RGBA8; + } + else + { + // Low quality: RGB only (legacy path) + mip_fmt = render_hdr ? GL_R11F_G11F_B10F : GL_RGB8; + } + mMipChain[i].allocate(res, res, mip_fmt); res /= 2; } } @@ -804,7 +826,9 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face) gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, LLPipeline::RENDER_TYPE_WL_SKY, LLPipeline::RENDER_TYPE_WATER, LLPipeline::RENDER_TYPE_VOIDWATER, LLPipeline::RENDER_TYPE_CLOUDS, LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::END_RENDER_TYPES); + LLPipeline::sDefaultProbeRender = true; probe->update(mRenderTarget.getWidth(), face); + LLPipeline::sDefaultProbeRender = false; gPipeline.popRenderTypeMask(); } @@ -1293,12 +1317,22 @@ void LLReflectionMapManager::updateUniforms() gPipeline.mHeroProbeManager.updateUniforms(); // Get the hero data. - - mProbeData.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox; - mProbeData.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere; - mProbeData.heroShape = gPipeline.mHeroProbeManager.mHeroData.heroShape; - mProbeData.heroMipCount = gPipeline.mHeroProbeManager.mHeroData.heroMipCount; - mProbeData.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount; + { + const HeroProbeData& hd = gPipeline.mHeroProbeManager.mHeroData; + for (S32 i = 0; i < LL_MAX_HERO_PROBE_COUNT; ++i) + { + mProbeData.heroBox[i] = hd.heroBox[i]; + mProbeData.heroSphere[i] = hd.heroSphere[i]; + mProbeData.heroParams[i][0] = hd.heroParams[i][0]; + mProbeData.heroParams[i][1] = hd.heroParams[i][1]; + mProbeData.heroParams[i][2] = hd.heroParams[i][2]; + mProbeData.heroParams[i][3] = hd.heroParams[i][3]; + mProbeData.heroPlaneMatrix[i] = hd.heroPlaneMatrix[i]; + mProbeData.heroClipPlane[i] = hd.heroClipPlane[i]; + } + mProbeData.heroMipCount = hd.heroMipCount; + mProbeData.heroProbeCount = hd.heroProbeCount; + } //copy mProbeData into uniform buffer object if (mUBO == 0) @@ -1474,13 +1508,15 @@ void LLReflectionMapManager::initReflectionMaps() mTexture = new LLCubeMapArray(); static LLCachedControl render_hdr(gSavedSettings, "RenderHDREnabled", true); + static LLCachedControl probe_quality(gSavedSettings, "RenderReflectionProbeQuality", 1); // store mReflectionProbeCount+2 cube maps, final two cube maps are used for render target and radiance map generation // source) - mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2, true, render_hdr); + U32 components = (probe_quality > 0) ? 4 : 3; // Use 4 components (RGBA) for high quality with alpha preservation, 3 (RGB) for low quality + mTexture->allocate(mProbeResolution, components, mReflectionProbeCount + 2, true, render_hdr); mIrradianceMaps = new LLCubeMapArray(); - mIrradianceMaps->allocate(LL_IRRADIANCE_MAP_RESOLUTION, 3, mReflectionProbeCount, false, render_hdr); + mIrradianceMaps->allocate(LL_IRRADIANCE_MAP_RESOLUTION, components, mReflectionProbeCount, false, render_hdr); } } diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h index 1e1f9ad1a2a..27701d1f8be 100644 --- a/indra/newview/llreflectionmapmanager.h +++ b/indra/newview/llreflectionmapmanager.h @@ -30,6 +30,7 @@ #include "llrendertarget.h" #include "llcubemaparray.h" #include "llcubemap.h" +#include "llheroprobemanager.h" class LLSpatialGroup; class LLViewerObject; @@ -68,7 +69,7 @@ class alignas(16) LLReflectionMapManager // the box probe LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; - LLMatrix4 heroBox; + LLMatrix4 heroBox[LL_MAX_HERO_PROBE_COUNT]; // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; @@ -80,7 +81,7 @@ class alignas(16) LLReflectionMapManager // w - znear LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT]; - LLVector4 heroSphere; + LLVector4 heroSphere[LL_MAX_HERO_PROBE_COUNT]; // indices used by probe: // [i][0] - cubemap array index for this probe @@ -96,9 +97,15 @@ class alignas(16) LLReflectionMapManager // numbrer of active refmaps GLint refmapCount; - GLint heroShape; GLint heroMipCount; GLint heroProbeCount; + + // std140: arrays must start on 16-byte boundary. + // 3 ints above = 12 bytes; alignas(16) inserts 4 bytes so heroParams starts at offset 16. + // heroParams[i] = { shape, cubeIndex, 0, 0 } + alignas(16) GLint heroParams[LL_MAX_HERO_PROBE_COUNT][4]; + LLMatrix4 heroPlaneMatrix[LL_MAX_HERO_PROBE_COUNT]; + LLVector4 heroClipPlane[LL_MAX_HERO_PROBE_COUNT]; }; // allocate an environment map of the given resolution diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 1b64eab3c0a..d8b76cebe27 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -2080,6 +2080,8 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) // Update local state objectp->setRenderMaterialID(te, asset_id, false, true); tep->setGLTFMaterialOverride(preserved_override); + // Ensure render material is built with the override + objectp->initRenderMaterial(te); } else { diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 6023f6885db..f71cf066471 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -685,35 +685,52 @@ void LLSettingsVOSky::updateSettings() gSky.setMoonScale(getMoonScale()); } -void draw_color(LLShaderUniforms* shader, const LLColor3& col, S32 shader_key) +void draw_color(LLShaderUniforms* shader, const LLColor3& col, S32 shader_key, + F32 ambient_saturation, bool use_debug_desaturate) { - // always identify as a radiance pass if desaturating irradiance is disabled - static LLCachedControl desaturate_irradiance(gSavedSettings, "RenderDesaturateIrradiance", true); - LLVector4 vect4(col.mV[0], col.mV[1], col.mV[2]); - if (desaturate_irradiance && gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass()) - { // maximize and remove tinting if this is an irradiance map render pass and the parameter feeds into the sky background color - auto max_vec = [](LLVector4 col) - { - LLColor3 color(col); - F32 h, s, l; - color.calcHSL(&h, &s, &l); - - col.mV[0] = col.mV[1] = col.mV[2] = l; - return col; - }; - + if (gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass()) + { // irradiance map render pass — desaturate sky color contribution switch (shader_key) { case LLShaderMgr::BLUE_HORIZON: case LLShaderMgr::BLUE_DENSITY: - vect4 = max_vec(vect4); + { + bool should_desaturate = false; + F32 saturation = 1.0f; + + if (use_debug_desaturate) + { + // V1 path: use debug setting for full on/off desaturation + static LLCachedControl desaturate_irradiance(gSavedSettings, "RenderDesaturateIrradiance", true); + if (desaturate_irradiance) + { + should_desaturate = true; + saturation = 0.0f; + } + } + else + { + // V2 path: use per-sky ambient_saturation slider + should_desaturate = true; + saturation = ambient_saturation; + } + + if (should_desaturate) + { + LLColor3 color(vect4); + F32 h, s, l; + color.calcHSL(&h, &s, &l); + + LLVector4 desat(l, l, l, vect4.mV[3]); + vect4 = lerp(desat, vect4, saturation); + } break; } + } } - //_WARNS("RIDER") << "pushing '" << (*it).first << "' as " << vect4 << LL_ENDL; shader->uniform3fv(shader_key, LLVector3(vect4.mV)); } @@ -726,18 +743,21 @@ void LLSettingsVOSky::applyToUniforms(void* ptarget) { LLShaderUniforms* shader = &((LLShaderUniforms*)ptarget)[LLGLSLShader::SG_ANY]; - draw_color(shader, getAmbientColor(), LLShaderMgr::AMBIENT); - draw_color(shader, getBlueDensity(), LLShaderMgr::BLUE_DENSITY); - draw_color(shader, getBlueHorizon(), LLShaderMgr::BLUE_HORIZON); + F32 ambient_saturation = getAmbientSkySaturation(); + bool use_debug_desaturate = (getSkySettingVersion() < 2); + + draw_color(shader, getAmbientColor(), LLShaderMgr::AMBIENT, ambient_saturation, use_debug_desaturate); + draw_color(shader, getBlueDensity(), LLShaderMgr::BLUE_DENSITY, ambient_saturation, use_debug_desaturate); + draw_color(shader, getBlueHorizon(), LLShaderMgr::BLUE_HORIZON, ambient_saturation, use_debug_desaturate); draw_real(shader, getHazeDensity(), LLShaderMgr::HAZE_DENSITY); draw_real(shader, getHazeHorizon(), LLShaderMgr::HAZE_HORIZON); draw_real(shader, getDensityMultiplier(), LLShaderMgr::DENSITY_MULTIPLIER); draw_real(shader, getDistanceMultiplier(), LLShaderMgr::DISTANCE_MULTIPLIER); - draw_color(shader, getCloudPosDensity2(), LLShaderMgr::CLOUD_POS_DENSITY2); + draw_color(shader, getCloudPosDensity2(), LLShaderMgr::CLOUD_POS_DENSITY2, ambient_saturation, use_debug_desaturate); draw_real(shader, getCloudScale(), LLShaderMgr::CLOUD_SCALE); draw_real(shader, getCloudShadow(), LLShaderMgr::CLOUD_SHADOW); draw_real(shader, getCloudVariance(), LLShaderMgr::CLOUD_VARIANCE); - draw_color(shader, getGlow(), LLShaderMgr::GLOW); + draw_color(shader, getGlow(), LLShaderMgr::GLOW, ambient_saturation, use_debug_desaturate); draw_real(shader, getMaxY(), LLShaderMgr::MAX_Y); draw_real(shader, getMoonBrightness(), LLShaderMgr::MOON_BRIGHTNESS); draw_real(shader, getSkyMoistureLevel(), LLShaderMgr::MOISTURE_LEVEL); @@ -804,17 +824,14 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) static LLCachedControl sunlight_scale(gSavedSettings, "RenderSkySunlightScale", 1.5f); static LLCachedControl sunlight_hdr_scale(gSavedSettings, "RenderHDRSkySunlightScale", 1.5f); static LLCachedControl ambient_scale(gSavedSettings, "RenderSkyAmbientScale", 1.5f); - static LLCachedControl tonemap_mix_setting(gSavedSettings, "RenderTonemapMix", 1.f); - // sky is a "classic" sky following pre SL 7.0 shading bool classic_mode = psky->canAutoAdjust() && !should_auto_adjust(); - if (!classic_mode) - { - psky->setTonemapMix(tonemap_mix_setting); - } - - shader->uniform1f(LLShaderMgr::SKY_SUNLIGHT_SCALE, hdr ? sunlight_hdr_scale : sunlight_scale); + // Shaders normalize sun_lux: intensity = sun_lux / 100000.0 + // Reference: 100000 lux (bright sunlight) = 1.0 normalized intensity + // getSunBrightness() is version-aware and returns a PBR-compatible value for v1 skies. + F32 sun_lux = psky->getSunBrightness(); + shader->uniform1f(LLShaderMgr::SUN_LUX, sun_lux); shader->uniform1f(LLShaderMgr::SKY_AMBIENT_SCALE, ambient_scale); shader->uniform1i(LLShaderMgr::CLASSIC_MODE, classic_mode); @@ -1127,6 +1144,14 @@ void LLSettingsVOWater::applySpecial(void *ptarget, bool force) F32 eyedepth = LLViewerCamera::getInstance()->getOrigin().mV[2] - water_height; bool underwater = (eyedepth <= 0.0f); + // During water probe rendering, the reflected camera is below water but + // we're rendering the above-water scene. Force above-water fog. + if (underwater && gPipeline.mHeroProbeManager.isMirrorPass() + && gPipeline.mHeroProbeManager.mCurrentRenderingProbeIdx == 0) + { + underwater = false; + } + F32 waterFogDensity = env.getCurrentWater()->getModifiedWaterFogDensity(underwater); shader->uniform1f(LLShaderMgr::WATER_FOGDENSITY, waterFogDensity); diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index 4b312b15978..f84c53c103f 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -101,6 +101,7 @@ class LLDrawInfo final : public LLRefCount const LLMatrix4* mNormalMapMatrix = nullptr; const LLMatrix4* mTextureMatrix = nullptr; const LLMatrix4* mModelMatrix = nullptr; + LLMatrix4* mLastModelMatrix = nullptr; LLPointer mAvatar = nullptr; LLMeshSkinInfo* mSkinInfo = nullptr; diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp deleted file mode 100644 index b8cb3d712d7..00000000000 --- a/indra/newview/lltinygltfhelper.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/** - * @file lltinygltfhelper.cpp - * - * $LicenseInfo:firstyear=2022&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2022, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "lltinygltfhelper.h" - -#include "llimage.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" - -static void strip_alpha_channel(LLPointer& img) -{ - if (img->getComponents() == 4) - { - LLImageRaw* tmp = new LLImageRaw(img->getWidth(), img->getHeight(), 3); - tmp->copyUnscaled4onto3(img); - img = tmp; - } -} - -// copy red channel from src_img to dst_img -// PRECONDITIONS: -// dst_img must be 3 component -// src_img and dst_image must have the same dimensions -static void copy_red_channel(const LLPointer& src_img, LLPointer& dst_img) -{ - llassert(src_img->getWidth() == dst_img->getWidth() && src_img->getHeight() == dst_img->getHeight()); - llassert(dst_img->getComponents() == 3); - - U32 pixel_count = dst_img->getWidth() * dst_img->getHeight(); - const U8* src = src_img->getData(); - U8* dst = dst_img->getData(); - S8 src_components = src_img->getComponents(); - - for (U32 i = 0; i < pixel_count; ++i) - { - dst[i * 3] = src[i * src_components]; - } -} - -void LLTinyGLTFHelper::initFetchedTextures(tinygltf::Material& material, - LLPointer& base_color_img, - LLPointer& normal_img, - LLPointer& mr_img, - LLPointer& emissive_img, - LLPointer& occlusion_img, - LLPointer& base_color_tex, - LLPointer& normal_tex, - LLPointer& mr_tex, - LLPointer& emissive_tex) -{ - if (base_color_img) - { - base_color_tex = LLViewerTextureManager::getFetchedTexture(base_color_img, FTType::FTT_LOCAL_FILE, true); - } - - if (normal_img) - { - strip_alpha_channel(normal_img); - normal_tex = LLViewerTextureManager::getFetchedTexture(normal_img, FTType::FTT_LOCAL_FILE, true); - } - - if (mr_img) - { - strip_alpha_channel(mr_img); - - if (occlusion_img) - { - if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != material.occlusionTexture.index) - { - // occlusion is a distinct texture from pbrMetallicRoughness - // pack into mr red channel - int occlusion_idx = material.occlusionTexture.index; - int mr_idx = material.pbrMetallicRoughness.metallicRoughnessTexture.index; - if (occlusion_idx != mr_idx) - { - LLImageDataLock lockIn(occlusion_img); - LLImageDataLock lockOut(mr_img); - //scale occlusion image to match resolution of mr image - occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight()); - - copy_red_channel(occlusion_img, mr_img); - } - } - } - else if (material.occlusionTexture.index == -1) - { - // no occlusion, make sure red channel of ORM is all 255 - occlusion_img = new LLImageRaw(mr_img->getWidth(), mr_img->getHeight(), 3); - occlusion_img->clear(255, 255, 255); - copy_red_channel(occlusion_img, mr_img); - } - } - else if (occlusion_img) - { - LLImageDataSharedLock lock(occlusion_img); - //no mr but occlusion exists, make a white mr_img and copy occlusion red channel over - mr_img = new LLImageRaw(occlusion_img->getWidth(), occlusion_img->getHeight(), 3); - mr_img->clear(255, 255, 255); - copy_red_channel(occlusion_img, mr_img); - } - - if (mr_img) - { - mr_tex = LLViewerTextureManager::getFetchedTexture(mr_img, FTType::FTT_LOCAL_FILE, true); - } - - if (emissive_img) - { - strip_alpha_channel(emissive_img); - emissive_tex = LLViewerTextureManager::getFetchedTexture(emissive_img, FTType::FTT_LOCAL_FILE, true); - } -} - -LLColor4 LLTinyGLTFHelper::getColor(const std::vector& in) -{ - LLColor4 out; - for (S32 i = 0; i < llmin((S32)in.size(), 4); ++i) - { - out.mV[i] = (F32)in[i]; - } - - return out; -} - -const tinygltf::Image * LLTinyGLTFHelper::getImageFromTextureIndex(const tinygltf::Model & model, S32 texture_index) -{ - if (texture_index >= 0) - { - S32 source_idx = model.textures[texture_index].source; - if (source_idx >= 0) - { - return &(model.images[source_idx]); - } - } - - return nullptr; -} - -LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name, bool flip) -{ - const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index); - LLImageRaw* rawImage = nullptr; - - if (image != nullptr && - image->bits == 8 && - !image->image.empty() && - image->component <= 4) - { - name = image->name; - rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component); - if (flip) - { - rawImage->verticalFlip(); - } - rawImage->optimizeAwayAlpha(); - } - - return rawImage; -} - -LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, bool flip) -{ - const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index); - LLImageRaw* rawImage = nullptr; - - if (image != nullptr && - image->bits == 8 && - !image->image.empty() && - image->component <= 4) - { - rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component); - if (flip) - { - rawImage->verticalFlip(); - } - rawImage->optimizeAwayAlpha(); - } - - return rawImage; -} - -bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& model_in) -{ - std::string exten = gDirUtilp->getExtension(filename); - - if (exten == "gltf" || exten == "glb") - { - tinygltf::TinyGLTF loader; - std::string error_msg; - std::string warn_msg; - - std::string filename_lc = filename; - LLStringUtil::toLower(filename_lc); - - // Load a tinygltf model fom a file. Assumes that the input filename has already been - // been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish. - bool decode_successful = false; - if (std::string::npos == filename_lc.rfind(".gltf")) - { // file is binary - decode_successful = loader.LoadBinaryFromFile(&model_in, &error_msg, &warn_msg, filename_lc); - } - else - { // file is ascii - decode_successful = loader.LoadASCIIFromFile(&model_in, &error_msg, &warn_msg, filename_lc); - } - - if (!decode_successful) - { - LL_WARNS("GLTF") << "Cannot load, error: Failed to decode" << error_msg - << ", warning:" << warn_msg - << " file: " << filename - << LL_ENDL; - return false; - } - - if (model_in.materials.empty()) - { - // materials are missing - LL_WARNS("GLTF") << "Cannot load. File has no materials " << filename << LL_ENDL; - return false; - } - - return true; - } - - - return false; -} - -bool LLTinyGLTFHelper::saveModel(const std::string& filename, tinygltf::Model& model_in) -{ - std::string exten = gDirUtilp->getExtension(filename); - - bool success = false; - - if (exten == "gltf" || exten == "glb") - { - tinygltf::TinyGLTF writer; - - std::string filename_lc = filename; - LLStringUtil::toLower(filename_lc); - - - bool embed_images = false; - bool embed_buffers = false; - bool pretty_print = true; - bool write_binary = false; - - - if (std::string::npos == filename_lc.rfind(".gltf")) - { // file is binary - embed_images = embed_buffers = write_binary = true; - } - - success = writer.WriteGltfSceneToFile(&model_in, filename, embed_images, embed_buffers, pretty_print, write_binary); - - if (!success) - { - LL_WARNS("GLTF") << "Failed to save" << LL_ENDL; - return false; - } - } - - return success; -} - -bool LLTinyGLTFHelper::getMaterialFromModel( - const std::string& filename, - const tinygltf::Model& model_in, - S32 mat_index, - LLFetchedGLTFMaterial* material, - std::string& material_name, - bool flip) -{ - llassert(material); - - if (model_in.materials.size() <= mat_index) - { - // materials are missing - LL_WARNS("GLTF") << "Cannot load Material, Material " << mat_index << " is missing, " << filename << LL_ENDL; - return false; - } - - material->setFromModel(model_in, mat_index); - - std::string folder = gDirUtilp->getDirName(filename); - tinygltf::Material material_in = model_in.materials[mat_index]; - - material_name = material_in.name; - - // get base color texture - LLPointer base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, flip); - // get normal map - LLPointer normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index, flip); - // get metallic-roughness texture - LLPointer mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, flip); - // get emissive texture - LLPointer emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index, flip); - // get occlusion map if needed - LLPointer occlusion_img; - if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index) - { - occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index, flip); - } - - LLPointer base_color_tex; - LLPointer normal_tex; - LLPointer mr_tex; - LLPointer emissive_tex; - - // todo: pass it into local bitmaps? - LLTinyGLTFHelper::initFetchedTextures(material_in, - base_img, normal_img, mr_img, emissive_img, occlusion_img, - base_color_tex, normal_tex, mr_tex, emissive_tex); - - if (base_color_tex) - { - base_color_tex->addTextureStats(64.f * 64.f, true); - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = base_color_tex->getID(); - material->mBaseColorTexture = base_color_tex; - } - else - { - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = LLUUID::null; - material->mBaseColorTexture = nullptr; - } - - if (normal_tex) - { - normal_tex->addTextureStats(64.f * 64.f, true); - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = normal_tex->getID(); - material->mNormalTexture = normal_tex; - } - else - { - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = LLUUID::null; - material->mNormalTexture = nullptr; - } - - if (mr_tex) - { - mr_tex->addTextureStats(64.f * 64.f, true); - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = mr_tex->getID(); - material->mMetallicRoughnessTexture = mr_tex; - } - else - { - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = LLUUID::null; - material->mMetallicRoughnessTexture = nullptr; - } - - if (emissive_tex) - { - emissive_tex->addTextureStats(64.f * 64.f, true); - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = emissive_tex->getID(); - material->mEmissiveTexture = emissive_tex; - } - else - { - material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = LLUUID::null; - material->mEmissiveTexture = nullptr; - } - - return true; -} diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 5e2d91d31e8..56b1f25ecf7 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1377,6 +1377,8 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj, // Update local state hit_obj->setRenderMaterialID(hit_face, asset_id, false, true); tep->setGLTFMaterialOverride(preserved_override); + // Ensure render material is built with the override + hit_obj->initRenderMaterial(hit_face); } else { @@ -1534,6 +1536,8 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj, // Update local state hit_obj->setRenderMaterialID(te, asset_id, false, true); hit_obj->getTE(te)->setGLTFMaterialOverride(preserved_override); + // Ensure render material is built with the override + hit_obj->initRenderMaterial(te); } else { diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index af6c78d8e57..65e5c45dd97 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -50,11 +50,14 @@ #include "llquaternion.h" #include "llwindow.h" // getPixelAspectRatio() #include "lltracerecording.h" +#include "pipeline.h" #include "llenvironment.h" // System includes #include // for setprecision +extern bool gCubeSnapshot; + LLTrace::CountStatHandle<> LLViewerCamera::sVelocityStat("camera_velocity"); LLTrace::CountStatHandle<> LLViewerCamera::sAngularVelocityStat("camera_angular_velocity"); @@ -368,6 +371,20 @@ void LLViewerCamera::setPerspective(bool for_selection, proj_mat *= glm::perspective(fov_y, aspect, z_near, z_far); + if (LLPipeline::sT2xJitterEnabled && !for_selection && !gCubeSnapshot) + { + // SMAA T2x subpixel jitter (alternating ±0.25 pixels) + static const float jitters[2][2] = { + { 0.25f, -0.25f}, // Frame 0 + {-0.25f, 0.25f}, // Frame 1 + }; + U32 idx = gPipeline.mSMAAFrameIndex & 1; + float jx = jitters[idx][0] * 2.0f / (float)width; + float jy = jitters[idx][1] * 2.0f / (float)height; + proj_mat[2][0] += jx; + proj_mat[2][1] += jy; + } + gGL.loadMatrix(glm::value_ptr(proj_mat)); set_current_projection(proj_mat); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 0c93b247510..055912904da 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -830,6 +830,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "RenderUIBuffer", handleWindowResized); setting_setup_signal_listener(gSavedSettings, "RenderDepthOfField", handleReleaseGLBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderFSAAType", handleReleaseGLBufferChanged); + setting_setup_signal_listener(gSavedSettings, "RenderMotionBlur", handleReleaseGLBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderSpecularResX", handleLUTBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderSpecularResY", handleLUTBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderSpecularExponent", handleLUTBufferChanged); @@ -861,12 +862,14 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "RenderResolutionDivisor", handleRenderResolutionDivisorChanged); setting_setup_signal_listener(gSavedSettings, "RenderReflectionProbeLevel", handleReflectionProbeDetailChanged); setting_setup_signal_listener(gSavedSettings, "RenderReflectionProbeDetail", handleReflectionProbeDetailChanged); + setting_setup_signal_listener(gSavedSettings, "RenderReflectionProbeQuality", handleReflectionProbeDetailChanged); setting_setup_signal_listener(gSavedSettings, "RenderReflectionProbeCount", handleReflectionProbeCountChanged); setting_setup_signal_listener(gSavedSettings, "RenderReflectionsEnabled", handleReflectionProbeDetailChanged); #if LL_DARWIN setting_setup_signal_listener(gSavedSettings, "RenderAppleUseMultGL", handleAppleUseMultGLChanged); #endif setting_setup_signal_listener(gSavedSettings, "RenderScreenSpaceReflections", handleReflectionProbeDetailChanged); + setting_setup_signal_listener(gSavedSettings, "RenderScreenSpaceReflectionsResolutionMultiplier", handleWindowResized); setting_setup_signal_listener(gSavedSettings, "RenderMirrors", handleReflectionProbeDetailChanged); setting_setup_signal_listener(gSavedSettings, "RenderHeroProbeResolution", handleHeroProbeResolutionChanged); setting_setup_signal_listener(gSavedSettings, "RenderShadowDetail", handleSetShaderChanged); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 9dfa9a0efd2..09857e172d4 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -223,6 +223,7 @@ void display_update_camera() } LLViewerCamera::getInstance()->setFar(final_far); LLVOAvatar::sRenderDistance = llclamp(final_far, 16.f, 256.f); + LLPipeline::sT2xJitterEnabled = (LLPipeline::RenderFSAAType == 3); gViewerWindow->setup3DRender(); if (!gCubeSnapshot) @@ -1034,7 +1035,17 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) if (LLPipeline::sRenderDeferred) { + gPipeline.renderSSRTrace(); + gPipeline.renderSSRAlpha(); + gPipeline.renderSSRWater(); + gPipeline.filterSSRBuffer(); + gPipeline.renderDeferredLighting(); + + // Copy screen to scene map for SSR to trace against next frame/pass. + // Must happen after deferred lighting so the lit scene is available. + gPipeline.copyScreenSpaceReflections(&gPipeline.mRT->screen, &gPipeline.mSceneMap); + gPipeline.buildHiZBuffer(); } LLPipeline::sUnderWaterRender = false; @@ -1190,9 +1201,16 @@ void display_cube_face() display_update_camera(); { - LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Env Update"); - // update all the sky/atmospheric/water settings - LLEnvironment::instance().update(LLViewerCamera::getInstance()); + S32 probeIdx = gPipeline.mHeroProbeManager.mCurrentRenderingProbeIdx; + bool heroSkipEnv = gPipeline.mHeroProbeManager.isMirrorPass() + && probeIdx >= 0 + && gPipeline.mHeroProbeManager.mHeroShadowsComplete[probeIdx]; + if (!heroSkipEnv) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Env Update"); + // update all the sky/atmospheric/water settings + LLEnvironment::instance().update(LLViewerCamera::getInstance()); + } } LLSpatialGroup::sNoDelete = true; @@ -1204,12 +1222,33 @@ void display_cube_face() static LLCullResult result; LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; LLPipeline::sUnderWaterRender = LLViewerCamera::getInstance()->cameraUnderWater(); + + // During water probe rendering, the reflected camera is below water but + // we're rendering the above-water scene for the reflection. + if (LLPipeline::sUnderWaterRender && gPipeline.mHeroProbeManager.isMirrorPass() + && gPipeline.mHeroProbeManager.mCurrentRenderingProbeIdx == 0) + { + LLPipeline::sUnderWaterRender = false; + } + gPipeline.updateCull(*LLViewerCamera::getInstance(), result); gGL.setColorMask(true, true); glClearColor(0.f, 0.f, 0.f, 0.f); - gPipeline.generateSunShadow(*LLViewerCamera::getInstance()); + + { + S32 probeIdx = gPipeline.mHeroProbeManager.mCurrentRenderingProbeIdx; + bool heroSkipShadow = gPipeline.mHeroProbeManager.isMirrorPass() + && probeIdx >= 0 + && gPipeline.mHeroProbeManager.mHeroShadowsComplete[probeIdx]; + if (!heroSkipShadow) + { + gPipeline.generateSunShadow(*LLViewerCamera::getInstance()); + if (gPipeline.mHeroProbeManager.isMirrorPass() && probeIdx >= 0) + gPipeline.mHeroProbeManager.mHeroShadowsComplete[probeIdx] = true; + } + } glClear(GL_DEPTH_BUFFER_BIT); // | GL_STENCIL_BUFFER_BIT); @@ -1235,6 +1274,12 @@ void display_cube_face() LLPipeline::sUnderWaterRender = LLViewerCamera::getInstance()->cameraUnderWater(); + if (LLPipeline::sUnderWaterRender && gPipeline.mHeroProbeManager.isMirrorPass() + && gPipeline.mHeroProbeManager.mCurrentRenderingProbeIdx == 0) + { + LLPipeline::sUnderWaterRender = false; + } + gGL.setColorMask(true, true); gPipeline.mRT->deferredScreen.bindTarget(); @@ -1483,6 +1528,10 @@ void render_ui(F32 zoom_factor, int subfield) set_current_modelview(glm::make_mat4(gGLLastModelView)); } + // Disable T2x jitter before any projection setup in the UI path. + // The main scene projection was jittered; UI/HUD/nametags must not be. + LLPipeline::sT2xJitterEnabled = false; + if(LLSceneMonitor::getInstance()->needsUpdate()) { gGL.pushMatrix(); @@ -1495,6 +1544,10 @@ void render_ui(F32 zoom_factor, int subfield) // apply gamma correction and post effects gPipeline.renderFinalize(); + // Reload the projection matrix without jitter for HUD/nametag rendering. + // sT2xJitterEnabled is already false, so this produces an unjittered matrix. + gViewerWindow->setup3DRender(); + { LLGLState::checkStates(); diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp index 3c5c4752df6..22cfe6d4fd1 100644 --- a/indra/newview/llviewerjoint.cpp +++ b/indra/newview/llviewerjoint.cpp @@ -88,7 +88,7 @@ U32 LLViewerJoint::render( F32 pixelArea, bool first_pass, bool is_dummy ) { triangle_count += drawShape( pixelArea, first_pass, is_dummy ); } - else if (LLPipeline::sShadowRender) + else if (LLPipeline::sShadowRender || LLPipeline::sVelocityRender) { triangle_count += drawShape(pixelArea, first_pass, is_dummy ); } diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index da7ad713367..16773656add 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -178,10 +178,25 @@ void LLViewerJointMesh::uploadJointMatrices() memcpy(mat+offset*4, vector, sizeof(GLfloat)*4); } } + // Save previous frame palette before uploading current + if (mLastMatrixPaletteFrame < (S32)gFrameCount - 1) + { + memcpy(mLastMatrixPalette, mat, sizeof(GLfloat) * 45 * 4); + } + mLastMatrixPaletteFrame = gFrameCount; + stop_glerror(); if (LLGLSLShader::sCurBoundShaderPtr) { LLGLSLShader::sCurBoundShaderPtr->uniform4fv(LLViewerShaderMgr::AVATAR_MATRIX, 45, mat); + + // Upload last frame palette for motion blur + if (LLGLSLShader::sCurBoundShaderPtr == &gAvatarVelocityProgram) + { + LLGLSLShader::sCurBoundShaderPtr->uniform4fv(LLViewerShaderMgr::AVATAR_LAST_MATRIX, 45, mLastMatrixPalette); + // Save current palette as "last" for next frame + memcpy(mLastMatrixPalette, mat, sizeof(GLfloat) * 45 * 4); + } } stop_glerror(); } diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h index cfe8ecdb677..a7ae4a10508 100644 --- a/indra/newview/llviewerjointmesh.h +++ b/indra/newview/llviewerjointmesh.h @@ -68,6 +68,10 @@ class LLViewerJointMesh : public LLAvatarJointMesh, public LLViewerJoint bool isAnimatable() const override { return false; } + // Previous frame matrix palette for motion blur + F32 mLastMatrixPalette[45 * 4]; + S32 mLastMatrixPaletteFrame = -1; + private: //copy mesh into given face's vertex buffer, applying current animation pose diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 772abb03730..31f12c44075 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -51,7 +51,7 @@ #include "llpluginclassmedia.h" #include "llresourcedata.h" #include "llstatusbar.h" -#include "lltinygltfhelper.h" +#include "llgltfhelper.h" #include "lltoast.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewertexturelist.h" @@ -678,17 +678,17 @@ void do_bulk_upload(std::vector filenames, bool allow_2k, const LLU // gltf does not use normal upload procedure if (ext == "gltf" || ext == "glb") { - tinygltf::Model model; - if (LLTinyGLTFHelper::loadModel(filename, model)) + LL::GLTF::Asset asset; + if (LLGLTFHelper::loadModel(filename, asset)) { - S32 materials_in_file = static_cast(model.materials.size()); + S32 materials_in_file = static_cast(asset.mMaterials.size()); for (S32 i = 0; i < materials_in_file; i++) { // Todo: // 1. Decouple bulk upload from material editor // 2. Take into account possiblity of identical textures - LLMaterialEditor::uploadMaterialFromModel(filename, model, i, dest); + LLMaterialEditor::uploadMaterialFromModel(filename, asset, i, dest); } } } @@ -783,17 +783,17 @@ bool get_bulk_upload_expected_cost( if (ext == "gltf" || ext == "glb") { - tinygltf::Model model; + LL::GLTF::Asset asset; - if (LLTinyGLTFHelper::loadModel(filename, model)) + if (LLGLTFHelper::loadModel(filename, asset)) { - S32 materials_in_file = static_cast(model.materials.size()); + S32 materials_in_file = static_cast(asset.mMaterials.size()); for (S32 i = 0; i < materials_in_file; i++) { LLPointer material = new LLFetchedGLTFMaterial(); std::string material_name; - bool decode_successful = LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, material.get(), material_name); + bool decode_successful = LLGLTFHelper::getMaterialFromModel(filename, asset, i, material.get(), material_name); if (decode_successful) { diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 7c26cb3c9fa..a650f538492 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -5247,6 +5247,13 @@ void LLViewerObject::updateTEMaterialTextures(U8 te) mTESpecularMaps[te] = LLViewerTextureManager::getFetchedTexture(spec_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); } + // Ensure render material is built if we have a base material and override + // (texture entry may have been copied without its render material) + if (getTE(te)->getGLTFMaterial() && getTE(te)->getGLTFMaterialOverride()) + { + initRenderMaterial(te); + } + LLFetchedGLTFMaterial* mat = (LLFetchedGLTFMaterial*) getTE(te)->getGLTFRenderMaterial(); llassert(mat == nullptr || dynamic_cast(getTE(te)->getGLTFRenderMaterial()) != nullptr); LLUUID mat_id = getRenderMaterialID(te); @@ -5688,7 +5695,14 @@ S32 LLViewerObject::initRenderMaterial(U8 te) if (need_render_material) { render_material = new LLFetchedGLTFMaterial(*base_material); - if (override_material) { render_material->applyOverride(*override_material); } + if (override_material) + { + LL_WARNS("GLTF") << "initRenderMaterial: te=" << (int)te + << " specularColorFactor=" << override_material->mSpecularColorFactor + << " metallicFactor=" << override_material->mMetallicFactor + << " override_ptr=" << (void*)override_material << LL_ENDL; + render_material->applyOverride(*override_material); + } render_material->clearFetchedTextures(); } return tep->setGLTFRenderMaterial(render_material); @@ -5735,6 +5749,7 @@ S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_ma LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.first, override_mat); } } + refreshMaterials(); } return retval; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 42a587f3764..be67f2eae68 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2761,7 +2761,7 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLDataPackerB entry->setUpdateFlags(flags); return result; - } +} LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp, U32 flags) { @@ -2783,6 +2783,18 @@ void LLViewerRegion::cacheFullUpdateGLTFOverride(const LLGLTFOverrideCacheEntry } } +void LLViewerRegion::cacheGLTFOverrideSide(U32 local_id, const LLUUID& object_id, S32 side, LLGLTFMaterial* material) +{ + auto& entry = mImpl->mGLTFOverridesLLSD[local_id]; + entry.mLocalId = local_id; + if (entry.mObjectId.isNull()) + { + entry.mObjectId = object_id; + } + entry.mGLTFMaterial[side] = material; + entry.mSides[side] = LLSD(); +} + LLVOCacheEntry* LLViewerRegion::getCacheEntryForOctree(U32 local_id) { if(!sVOCacheCullingEnabled) @@ -3728,6 +3740,12 @@ bool LLViewerRegion::avatarHoverHeightEnabled() const return ( mSimulatorFeatures.has("AvatarHoverHeightEnabled") && mSimulatorFeatures["AvatarHoverHeightEnabled"].asBoolean()); } + +bool LLViewerRegion::pbrExtensionsV1Enabled() const +{ + return ( mSimulatorFeatures.has("PBRExtensionsV1") && + mSimulatorFeatures["PBRExtensionsV1"].asBoolean()); +} /* Static Functions */ void log_capabilities(const CapabilityMap &capmap) diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 974bc375b8a..fd281628ca4 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -75,6 +75,7 @@ class LLHost; class LLBBox; class LLSpatialGroup; class LLDrawable; +class LLGLTFMaterial; class LLGLTFOverrideCacheEntry; class LLViewerRegionImpl; class LLViewerOctreeGroup; @@ -351,6 +352,8 @@ class LLViewerRegion: public LLCapabilityProvider // implements this interface bool avatarHoverHeightEnabled() const; + bool pbrExtensionsV1Enabled() const; + typedef enum { CACHE_MISS_TYPE_TOTAL = 0, // total cache miss - object not in cache @@ -371,6 +374,7 @@ class LLViewerRegion: public LLCapabilityProvider // implements this interface eCacheUpdateResult cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp, U32 flags); void cacheFullUpdateGLTFOverride(const LLGLTFOverrideCacheEntry &override_data); + void cacheGLTFOverrideSide(U32 local_id, const LLUUID& object_id, S32 side, LLGLTFMaterial* material); LLVOCacheEntry* getCacheEntryForOctree(U32 local_id); LLVOCacheEntry* getCacheEntry(U32 local_id, bool valid = true); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 27865f75980..1b898bc7faf 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -99,6 +99,7 @@ LLGLSLShader gBenchmarkProgram; LLGLSLShader gReflectionProbeDisplayProgram; LLGLSLShader gCopyProgram; LLGLSLShader gCopyDepthProgram; +LLGLSLShader gHiZReduceProgram; LLGLSLShader gPBRTerrainBakeProgram; LLGLSLShader gDrawColorProgram; @@ -135,6 +136,10 @@ LLGLSLShader gImpostorProgram; LLGLSLShader gGlowProgram; LLGLSLShader gGlowExtractProgram; LLGLSLShader gPostScreenSpaceReflectionProgram; +LLGLSLShader gScreenSpaceReflTraceProgram; +LLGLSLShader gSSRAlphaProgram; +LLGLSLShader gSkinnedSSRAlphaProgram; +LLGLSLShader gSSRWaterProgram; // Deferred rendering shaders LLGLSLShader gDeferredImpostorProgram; @@ -161,7 +166,9 @@ LLGLSLShader gDeferredSunProbeProgram; LLGLSLShader gHazeProgram; LLGLSLShader gHazeWaterProgram; LLGLSLShader gDeferredBlurLightProgram; +LLGLSLShader gSSRFilterProgram; LLGLSLShader gDeferredSoftenProgram; +LLGLSLShader gDeferredSoftenCubeProgram; LLGLSLShader gDeferredShadowProgram; LLGLSLShader gDeferredSkinnedShadowProgram; LLGLSLShader gDeferredShadowCubeProgram; @@ -208,10 +215,13 @@ LLGLSLShader gFXAAProgram[4]; LLGLSLShader gSMAAEdgeDetectProgram[4]; LLGLSLShader gSMAABlendWeightsProgram[4]; LLGLSLShader gSMAANeighborhoodBlendProgram[4]; +LLGLSLShader gSMAANeighborhoodBlendT2xProgram[4]; +LLGLSLShader gSMAAResolveProgram[4]; LLGLSLShader gCASProgram; LLGLSLShader gCASLegacyGammaProgram; LLGLSLShader gDeferredPostNoDoFProgram; LLGLSLShader gDeferredPostNoDoFNoiseProgram; +LLGLSLShader gDeferredMotionBlurProgram; LLGLSLShader gDeferredWLSkyProgram; LLGLSLShader gEnvironmentMapProgram; LLGLSLShader gDeferredWLCloudProgram; @@ -240,6 +250,12 @@ LLGLSLShader gDeferredPBRAlphaProgram; LLGLSLShader gDeferredSkinnedPBRAlphaProgram; LLGLSLShader gDeferredPBRTerrainProgram[TERRAIN_PAINT_TYPE_COUNT]; +LLGLSLShader gVelocityProgram; +LLGLSLShader gVelocitySkinnedProgram; +LLGLSLShader gVelocityAlphaProgram; +LLGLSLShader gVelocityAlphaSkinnedProgram; +LLGLSLShader gAvatarVelocityProgram; + LLGLSLShader gGLTFPBRMetallicRoughnessProgram; @@ -424,6 +440,7 @@ void LLViewerShaderMgr::finalizeShaderList() mShaderList.push_back(&gHazeProgram); mShaderList.push_back(&gHazeWaterProgram); mShaderList.push_back(&gDeferredSoftenProgram); + mShaderList.push_back(&gDeferredSoftenCubeProgram); mShaderList.push_back(&gDeferredAlphaProgram); mShaderList.push_back(&gHUDAlphaProgram); mShaderList.push_back(&gDeferredAlphaImpostorProgram); @@ -790,6 +807,7 @@ std::string LLViewerShaderMgr::loadBasicShaders() shaders.push_back( make_pair( "avatar/avatarSkinV.glsl", 1 ) ); shaders.push_back( make_pair( "avatar/objectSkinV.glsl", 1 ) ); shaders.push_back( make_pair( "deferred/textureUtilV.glsl", 1 ) ); + shaders.push_back( make_pair( "deferred/velocityFuncV.glsl", 1 ) ); if (gGLManager.mGLSLVersionMajor >= 2 || gGLManager.mGLSLVersionMinor >= 30) { shaders.push_back( make_pair( "objects/indexedTextureV.glsl", 1 ) ); @@ -836,6 +854,12 @@ std::string LLViewerShaderMgr::loadBasicShaders() { attribs["REFMAP_LEVEL"] = std::to_string(probe_level); attribs["REF_SAMPLE_COUNT"] = "32"; + + S32 probe_quality = gSavedSettings.getS32("RenderReflectionProbeQuality"); + if (probe_quality > 0) + { + attribs["REFLECTION_PROBE_MED_QUALITY"] = "1"; + } } if (mirrors) @@ -931,6 +955,7 @@ bool LLViewerShaderMgr::loadShadersWater() if (success) { + bool ssr = gSavedSettings.getBOOL("RenderScreenSpaceReflections"); // load water shader gWaterProgram.mName = "Water Shader"; gWaterProgram.mFeatures.calculatesAtmospherics = true; @@ -939,6 +964,7 @@ bool LLViewerShaderMgr::loadShadersWater() gWaterProgram.mFeatures.hasSrgb = true; gWaterProgram.mFeatures.hasReflectionProbes = true; gWaterProgram.mFeatures.hasTonemap = true; + gWaterProgram.mFeatures.hasScreenSpaceReflections = ssr; gWaterProgram.mFeatures.hasShadows = use_sun_shadow; gWaterProgram.mShaderFiles.clear(); gWaterProgram.mShaderFiles.push_back(make_pair("environment/waterV.glsl", GL_VERTEX_SHADER)); @@ -954,6 +980,11 @@ bool LLViewerShaderMgr::loadShadersWater() gWaterProgram.addPermutation("HAS_SUN_SHADOW", "1"); } + if (ssr) + { + gWaterProgram.addPermutation("SSR", "1"); + } + gWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; gWaterProgram.mShaderLevel = mShaderLevel[SHADER_WATER]; success = gWaterProgram.createShader(); @@ -1089,7 +1120,9 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredMultiSpotLightProgram.unload(); gDeferredSunProgram.unload(); gDeferredBlurLightProgram.unload(); + gSSRFilterProgram.unload(); gDeferredSoftenProgram.unload(); + gDeferredSoftenCubeProgram.unload(); gDeferredShadowProgram.unload(); gDeferredSkinnedShadowProgram.unload(); gDeferredShadowCubeProgram.unload(); @@ -1137,6 +1170,8 @@ bool LLViewerShaderMgr::loadShadersDeferred() gSMAAEdgeDetectProgram[i].unload(); gSMAABlendWeightsProgram[i].unload(); gSMAANeighborhoodBlendProgram[i].unload(); + gSMAANeighborhoodBlendT2xProgram[i].unload(); + gSMAAResolveProgram[i].unload(); } gCASProgram.unload(); gCASLegacyGammaProgram.unload(); @@ -1176,6 +1211,13 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredPBRTerrainProgram[paint_type].unload(); } + gVelocityProgram.unload(); + gVelocitySkinnedProgram.unload(); + gVelocityAlphaProgram.unload(); + gVelocityAlphaSkinnedProgram.unload(); + gAvatarVelocityProgram.unload(); + gDeferredMotionBlurProgram.unload(); + return true; } @@ -1745,6 +1787,18 @@ bool LLViewerShaderMgr::loadShadersDeferred() llassert(success); } + if (success) + { + gSSRFilterProgram.mName = "SSR Filter Shader"; + gSSRFilterProgram.mFeatures.isDeferred = true; + gSSRFilterProgram.mShaderFiles.clear(); + gSSRFilterProgram.mShaderFiles.push_back(make_pair("deferred/blurLightV.glsl", GL_VERTEX_SHADER)); + gSSRFilterProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflFilterF.glsl", GL_FRAGMENT_SHADER)); + gSSRFilterProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + add_common_permutations(&gSSRFilterProgram); + success = gSSRFilterProgram.createShader(); + } + if (success) { for (int i = 0; i < 3 && success; ++i) @@ -2129,6 +2183,35 @@ bool LLViewerShaderMgr::loadShadersDeferred() llassert(success); } + if (success) + { + // Cube snapshot variant with CUBE_SNAPSHOT define + gDeferredSoftenCubeProgram.mName = "Deferred Soften Cube Shader"; + gDeferredSoftenCubeProgram.mFeatures = gDeferredSoftenProgram.mFeatures; + gDeferredSoftenCubeProgram.mShaderFiles = gDeferredSoftenProgram.mShaderFiles; + gDeferredSoftenCubeProgram.mShaderLevel = gDeferredSoftenProgram.mShaderLevel; + gDeferredSoftenCubeProgram.mShaderGroup = gDeferredSoftenProgram.mShaderGroup; + gDeferredSoftenCubeProgram.mDefines = gDeferredSoftenProgram.mDefines; + gDeferredSoftenCubeProgram.clearPermutations(); + add_common_permutations(&gDeferredSoftenCubeProgram); + + if (use_sun_shadow) + { + gDeferredSoftenCubeProgram.addPermutation("HAS_SUN_SHADOW", "1"); + } + + if (gSavedSettings.getBOOL("RenderDeferredSSAO")) + { + gDeferredSoftenCubeProgram.mShaderLevel = llmax(gDeferredSoftenCubeProgram.mShaderLevel, 2); + gDeferredSoftenCubeProgram.addPermutation("HAS_SSAO", "1"); + } + + gDeferredSoftenCubeProgram.addPermutation("CUBE_SNAPSHOT", "1"); + + success = gDeferredSoftenCubeProgram.createShader(); + llassert(success); + } + if (success) { gHazeProgram.mName = "Haze Shader"; @@ -2735,6 +2818,81 @@ bool LLViewerShaderMgr::loadShadersDeferred() gSMAANeighborhoodBlendProgram[i].unload(); } } + + // SMAA T2x variants: neighborhood blend with reprojection and temporal resolve + if (!failed) + { + int t2x_i = 0; + bool t2x_failed = false; + for (const auto& smaa_pair : quality_levels) + { + std::map t2x_defines; + if (gGLManager.mGLVersion >= 4.f) + t2x_defines.emplace("SMAA_GLSL_4", "1"); + else if (gGLManager.mGLVersion >= 3.1f) + t2x_defines.emplace("SMAA_GLSL_3", "1"); + else + t2x_defines.emplace("SMAA_GLSL_2", "1"); + t2x_defines.emplace("SMAA_PREDICATION", "0"); + t2x_defines.emplace("SMAA_REPROJECTION", "1"); + t2x_defines.emplace("SMAA_REPROJECTION_WEIGHT_SCALE", "30.0"); + t2x_defines.emplace(smaa_pair.first, "1"); + + if (success) + { + gSMAANeighborhoodBlendT2xProgram[t2x_i].mName = llformat("SMAA T2x Neighborhood Blending (%s)", smaa_pair.second.c_str()); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mFeatures.isDeferred = true; + gSMAANeighborhoodBlendT2xProgram[t2x_i].clearPermutations(); + gSMAANeighborhoodBlendT2xProgram[t2x_i].addPermutations(t2x_defines); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mShaderFiles.clear(); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAANeighborhoodBlendF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAANeighborhoodBlendV.glsl", GL_VERTEX_SHADER_ARB)); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAA.glsl", GL_FRAGMENT_SHADER_ARB)); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAA.glsl", GL_VERTEX_SHADER_ARB)); + gSMAANeighborhoodBlendT2xProgram[t2x_i].mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + success = gSMAANeighborhoodBlendT2xProgram[t2x_i].createShader(); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << gSMAANeighborhoodBlendT2xProgram[t2x_i].mName << "', disabling!" << LL_ENDL; + t2x_failed = true; + success = true; + break; + } + } + + if (success) + { + gSMAAResolveProgram[t2x_i].mName = llformat("SMAA T2x Resolve (%s)", smaa_pair.second.c_str()); + gSMAAResolveProgram[t2x_i].mFeatures.isDeferred = true; + gSMAAResolveProgram[t2x_i].clearPermutations(); + gSMAAResolveProgram[t2x_i].addPermutations(t2x_defines); + gSMAAResolveProgram[t2x_i].mShaderFiles.clear(); + gSMAAResolveProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAAResolveF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSMAAResolveProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAAResolveV.glsl", GL_VERTEX_SHADER_ARB)); + gSMAAResolveProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAA.glsl", GL_FRAGMENT_SHADER_ARB)); + gSMAAResolveProgram[t2x_i].mShaderFiles.push_back(make_pair("deferred/SMAA.glsl", GL_VERTEX_SHADER_ARB)); + gSMAAResolveProgram[t2x_i].mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + success = gSMAAResolveProgram[t2x_i].createShader(); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << gSMAAResolveProgram[t2x_i].mName << "', disabling!" << LL_ENDL; + t2x_failed = true; + success = true; + break; + } + } + ++t2x_i; + } + + if (t2x_failed) + { + for (auto j = 0; j < 4; ++j) + { + gSMAANeighborhoodBlendT2xProgram[j].unload(); + gSMAAResolveProgram[j].unload(); + } + } + } } if (success && gGLManager.mGLVersion > 4.05f) @@ -2840,6 +2998,18 @@ bool LLViewerShaderMgr::loadShadersDeferred() llassert(success); } + if (success) + { + gDeferredMotionBlurProgram.mName = "Deferred Motion Blur Shader"; + gDeferredMotionBlurProgram.mFeatures.isDeferred = true; + gDeferredMotionBlurProgram.mShaderFiles.clear(); + gDeferredMotionBlurProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); + gDeferredMotionBlurProgram.mShaderFiles.push_back(make_pair("deferred/motionBlurF.glsl", GL_FRAGMENT_SHADER)); + gDeferredMotionBlurProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + success = gDeferredMotionBlurProgram.createShader(); + llassert(success); + } + if (success) { gEnvironmentMapProgram.mName = "Environment Map Program"; @@ -2988,10 +3158,54 @@ bool LLViewerShaderMgr::loadShadersDeferred() gPostScreenSpaceReflectionProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflPostF.glsl", GL_FRAGMENT_SHADER)); gPostScreenSpaceReflectionProgram.mFeatures.hasScreenSpaceReflections = true; gPostScreenSpaceReflectionProgram.mFeatures.isDeferred = true; + gPostScreenSpaceReflectionProgram.mFeatures.hasFullGBuffer = true; gPostScreenSpaceReflectionProgram.mShaderLevel = 3; success = gPostScreenSpaceReflectionProgram.createShader(); } + if (success) { + gScreenSpaceReflTraceProgram.mName = "Screen Space Reflection Trace"; + gScreenSpaceReflTraceProgram.mShaderFiles.clear(); + gScreenSpaceReflTraceProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflPostV.glsl", GL_VERTEX_SHADER)); + gScreenSpaceReflTraceProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflTraceF.glsl", GL_FRAGMENT_SHADER)); + gScreenSpaceReflTraceProgram.mFeatures.hasScreenSpaceReflections = true; + gScreenSpaceReflTraceProgram.mFeatures.isDeferred = true; + gScreenSpaceReflTraceProgram.mFeatures.hasFullGBuffer = true; + gScreenSpaceReflTraceProgram.mShaderLevel = 3; + success = gScreenSpaceReflTraceProgram.createShader(); + } + + if (success) { + gSSRAlphaProgram.mName = "SSR Alpha"; + gSSRAlphaProgram.mShaderFiles.clear(); + gSSRAlphaProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflAlphaV.glsl", GL_VERTEX_SHADER)); + gSSRAlphaProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflAlphaF.glsl", GL_FRAGMENT_SHADER)); + gSSRAlphaProgram.mFeatures.hasScreenSpaceReflections = true; + gSSRAlphaProgram.mFeatures.isDeferred = true; + gSSRAlphaProgram.mShaderLevel = 3; + success = gSSRAlphaProgram.createShader(); + } + + if (success) + { + success = make_rigged_variant(gSSRAlphaProgram, gSkinnedSSRAlphaProgram); + } + + if (success) + { + gSSRWaterProgram.mName = "SSR Water"; + gSSRWaterProgram.mShaderFiles.clear(); + gSSRWaterProgram.mShaderFiles.push_back(make_pair("environment/waterV.glsl", GL_VERTEX_SHADER)); + gSSRWaterProgram.mShaderFiles.push_back(make_pair("deferred/screenSpaceReflWaterF.glsl", GL_FRAGMENT_SHADER)); + gSSRWaterProgram.mFeatures.calculatesAtmospherics = true; + gSSRWaterProgram.mFeatures.hasAtmospherics = true; + gSSRWaterProgram.mFeatures.hasScreenSpaceReflections = true; + gSSRWaterProgram.mFeatures.isDeferred = true; + gSSRWaterProgram.mShaderLevel = 3; + gSSRWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; + success = gSSRWaterProgram.createShader(); + } + if (success) { gDeferredBufferVisualProgram.mName = "Deferred Buffer Visualization Shader"; gDeferredBufferVisualProgram.mShaderFiles.clear(); @@ -3004,6 +3218,44 @@ bool LLViewerShaderMgr::loadShadersDeferred() success = gDeferredBufferVisualProgram.createShader(); } + if (success) + { + gVelocityProgram.mName = "Velocity Shader"; + gVelocityProgram.mFeatures.hasMotionBlur = true; + gVelocityProgram.mShaderFiles.clear(); + gVelocityProgram.mShaderFiles.push_back(make_pair("deferred/velocityV.glsl", GL_VERTEX_SHADER)); + gVelocityProgram.mShaderFiles.push_back(make_pair("deferred/velocityF.glsl", GL_FRAGMENT_SHADER)); + gVelocityProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + success = make_rigged_variant(gVelocityProgram, gVelocitySkinnedProgram); + success = success && gVelocityProgram.createShader(); + } + + if (success) + { + gVelocityAlphaProgram.mName = "Velocity Alpha Shader"; + gVelocityAlphaProgram.mFeatures.hasMotionBlur = true; + gVelocityAlphaProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels; + gVelocityAlphaProgram.mShaderFiles.clear(); + gVelocityAlphaProgram.mShaderFiles.push_back(make_pair("deferred/velocityAlphaV.glsl", GL_VERTEX_SHADER)); + gVelocityAlphaProgram.mShaderFiles.push_back(make_pair("deferred/velocityAlphaF.glsl", GL_FRAGMENT_SHADER)); + gVelocityAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + add_common_permutations(&gVelocityAlphaProgram); + success = make_rigged_variant(gVelocityAlphaProgram, gVelocityAlphaSkinnedProgram); + success = success && gVelocityAlphaProgram.createShader(); + } + + if (success) + { + gAvatarVelocityProgram.mName = "Avatar Velocity Shader"; + gAvatarVelocityProgram.mFeatures.hasSkinning = true; + gAvatarVelocityProgram.mFeatures.hasMotionBlur = true; + gAvatarVelocityProgram.mShaderFiles.clear(); + gAvatarVelocityProgram.mShaderFiles.push_back(make_pair("deferred/avatarVelocityV.glsl", GL_VERTEX_SHADER)); + gAvatarVelocityProgram.mShaderFiles.push_back(make_pair("deferred/avatarVelocityF.glsl", GL_FRAGMENT_SHADER)); + gAvatarVelocityProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + success = gAvatarVelocityProgram.createShader(); + } + return success; } @@ -3441,6 +3693,16 @@ bool LLViewerShaderMgr::loadShadersInterface() success = gCopyDepthProgram.createShader(); } + if (success) + { + gHiZReduceProgram.mName = "Hi-Z Reduce Shader"; + gHiZReduceProgram.mShaderFiles.clear(); + gHiZReduceProgram.mShaderFiles.push_back(make_pair("interface/copyV.glsl", GL_VERTEX_SHADER)); + gHiZReduceProgram.mShaderFiles.push_back(make_pair("deferred/hiZReduceF.glsl", GL_FRAGMENT_SHADER)); + gHiZReduceProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + success = gHiZReduceProgram.createShader(); + } + if (success) { gDrawColorProgram.mName = "Draw Color Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index 46da30017db..8a99d1e7218 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -174,6 +174,7 @@ extern LLGLSLShader gBenchmarkProgram; extern LLGLSLShader gReflectionProbeDisplayProgram; extern LLGLSLShader gCopyProgram; extern LLGLSLShader gCopyDepthProgram; +extern LLGLSLShader gHiZReduceProgram; extern LLGLSLShader gPBRTerrainBakeProgram; extern LLGLSLShader gDrawColorProgram; @@ -214,6 +215,12 @@ extern LLGLSLShader gImpostorProgram; // Post Process Shaders extern LLGLSLShader gPostScreenSpaceReflectionProgram; +// SSR Trace +extern LLGLSLShader gScreenSpaceReflTraceProgram; +extern LLGLSLShader gSSRAlphaProgram; +extern LLGLSLShader gSkinnedSSRAlphaProgram; +extern LLGLSLShader gSSRWaterProgram; + // Deferred rendering shaders extern LLGLSLShader gDeferredImpostorProgram; extern LLGLSLShader gDeferredDiffuseProgram; @@ -234,8 +241,10 @@ extern LLGLSLShader gDeferredSunProbeProgram; extern LLGLSLShader gHazeProgram; extern LLGLSLShader gHazeWaterProgram; extern LLGLSLShader gDeferredBlurLightProgram; +extern LLGLSLShader gSSRFilterProgram; extern LLGLSLShader gDeferredAvatarProgram; extern LLGLSLShader gDeferredSoftenProgram; +extern LLGLSLShader gDeferredSoftenCubeProgram; extern LLGLSLShader gDeferredShadowProgram; extern LLGLSLShader gDeferredShadowCubeProgram; extern LLGLSLShader gDeferredShadowAlphaMaskProgram; @@ -249,10 +258,13 @@ extern LLGLSLShader gFXAAProgram[4]; extern LLGLSLShader gSMAAEdgeDetectProgram[4]; extern LLGLSLShader gSMAABlendWeightsProgram[4]; extern LLGLSLShader gSMAANeighborhoodBlendProgram[4]; +extern LLGLSLShader gSMAANeighborhoodBlendT2xProgram[4]; +extern LLGLSLShader gSMAAResolveProgram[4]; extern LLGLSLShader gCASProgram; extern LLGLSLShader gCASLegacyGammaProgram; extern LLGLSLShader gDeferredPostNoDoFProgram; extern LLGLSLShader gDeferredPostNoDoFNoiseProgram; +extern LLGLSLShader gDeferredMotionBlurProgram; extern LLGLSLShader gDeferredPostGammaCorrectProgram; extern LLGLSLShader gLegacyPostGammaCorrectProgram; extern LLGLSLShader gDeferredPostTonemapProgram; @@ -326,4 +338,10 @@ enum TerrainPaintType : U32 TERRAIN_PAINT_TYPE_COUNT = 2, }; extern LLGLSLShader gDeferredPBRTerrainProgram[TERRAIN_PAINT_TYPE_COUNT]; + +extern LLGLSLShader gVelocityProgram; +extern LLGLSLShader gVelocitySkinnedProgram; +extern LLGLSLShader gVelocityAlphaProgram; +extern LLGLSLShader gVelocityAlphaSkinnedProgram; +extern LLGLSLShader gAvatarVelocityProgram; #endif diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 0f23596c9a8..22bb2af1867 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -103,6 +103,7 @@ F32 LLViewerTexture::sCurrentTime = 0.0f; constexpr F32 MEMORY_CHECK_WAIT_TIME = 1.0f; constexpr F32 MIN_VRAM_BUDGET = 768.f; F32 LLViewerTexture::sFreeVRAMMegabytes = MIN_VRAM_BUDGET; +F32 LLViewerTexture::sOverBudgetPct = 0.f; LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF; @@ -522,6 +523,7 @@ void LLViewerTexture::updateClass() sFreeVRAMMegabytes = target - used; F32 over_pct = (used - target) / target; + sOverBudgetPct = over_pct; bool is_sys_low = isSystemMemoryLow(); bool is_low = is_sys_low || over_pct > 0.f; @@ -3100,6 +3102,14 @@ void LLViewerLODTexture::processTextureStats() discard_level = (F32)(log(mTexelsPerImage / mMaxVirtualSize) / log_4); } + // Apply memory pressure bias directly to the discard level. + // sDesiredDiscardBias has a floor of 1.0 in V7, so subtract 1 to + // get the effective bias (0 when no pressure, up to 3 at max). + if (mBoostLevel < LLGLTexture::BOOST_SCULPTED) + { + discard_level += sDesiredDiscardBias - 1.f; + } + discard_level = floorf(discard_level); F32 min_discard = 0.f; @@ -3120,10 +3130,17 @@ void LLViewerLODTexture::processTextureStats() // S32 current_discard = getDiscardLevel(); - if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED) + if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED && current_discard >= 0) { if (current_discard < mDesiredDiscardLevel && !mForceToSaveRawImage) - { // should scale down + { // current quality exceeds what we need, scale down + scaleDown(); + } + // Memory-threshold scaleDown: when VRAM usage exceeds 92.5% of + // budget (7.5% margin), force downscale. Matches the legacy + // texmem_middle_bound_scale behaviour. + else if (sOverBudgetPct > -0.075f && sDesiredDiscardBias > 1.f) + { scaleDown(); } } diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 29376519953..ee4c937523e 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -233,6 +233,8 @@ class LLViewerTexture : public LLGLTexture // estimated free memory for textures, by bias calculation static F32 sFreeVRAMMegabytes; + // (used - target) / target — positive means over budget + static F32 sOverBudgetPct; enum EDebugTexels { diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index e4fd9478921..7dd32074cfe 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1014,6 +1014,38 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag } imagep->addTextureStats(max_vsize); + + // Derive stream priority channel from face lists. + // Map render texture channels to priority channels: + // 0 = normal, 1 = diffuse, 2 = specular, 3 = emissive + { + static const S32 render_to_priority[] = { + 1, // DIFFUSE_MAP (0) + 0, // NORMAL_MAP / ALTERNATE_DIFFUSE_MAP (1) + 2, // SPECULAR_MAP (2) + 1, // BASECOLOR_MAP (3) + 2, // METALLIC_ROUGHNESS_MAP (4) + 0, // GLTF_NORMAL_MAP (5) + 3, // EMISSIVE_MAP (6) + }; + + S32 priority_channel = 1; // default to diffuse + for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i) + { + if (imagep->getNumFaces(i) > 0) + { + priority_channel = llmin(priority_channel, render_to_priority[i]); + } + } + + static LLCachedControl channel_priority(gSavedSettings, "TextureChannelPriority", + LLVector4(10.0f, 20.0f, 40.0f, 20.0f)); + F32 factor = llmax(channel_priority().mV[priority_channel], 0.1f); + if (factor != 1.0f) + { + imagep->mMaxVirtualSize /= factor; + } + } } #if 0 diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8695b969526..651fed2db38 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -5202,6 +5202,15 @@ bool LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei F32 depth_conversion_factor_1 = (LLViewerCamera::getInstance()->getFar() + LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear()); F32 depth_conversion_factor_2 = (LLViewerCamera::getInstance()->getFar() - LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear()); + // Warmup pass: render the scene once to populate mSceneMap for SSR. + // Without this, SSR traces against an empty scene map on the first capture pass. + if (LLPipeline::sRenderDeferred && gPipeline.RenderScreenSpaceReflections) + { + gDisplaySwapBuffers = false; + gDepthDirty = true; + display(do_rebuild, scale_factor, 0, true); + } + // Subimages are in fact partial rendering of the final view. This happens when the final view is bigger than the screen. // In most common cases, scale_factor is 1 and there's no more than 1 iteration on x and y for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y) @@ -5472,7 +5481,8 @@ bool LLViewerWindow::simpleSnapshot(LLImageRaw* raw, S32 image_width, S32 image_ void display_cube_face(); -bool LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubearray, S32 cubeIndex, S32 face, F32 near_clip, bool dynamic_render, bool useCustomClipPlane, LLPlane clipPlane) +bool LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubearray, S32 cubeIndex, S32 face, F32 near_clip, bool dynamic_render, bool useCustomClipPlane, LLPlane clipPlane, + const LLVector3* overrideLookDir, const LLVector3* overrideUpDir) { // NOTE: implementation derived from LLFloater360Capture::capture360Images() and simpleSnapshot LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; @@ -5575,7 +5585,10 @@ bool LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubea int i = face; { // set up camera to look in each direction - camera->lookDir(look_dirs[i], look_upvecs[i]); + if (overrideLookDir && overrideUpDir) + camera->lookDir(*overrideLookDir, *overrideUpDir); + else + camera->lookDir(look_dirs[i], look_upvecs[i]); // turning this flag off here prohibits the screen swap // to present the new page to the viewer - this stops diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index ec28a3fc4aa..3dc097e937b 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -388,7 +388,8 @@ class LLViewerWindow : public LLWindowCallbacks // face - which cube face to update // near_clip - near clip setting to use bool cubeSnapshot(const LLVector3 &origin, LLCubeMapArray *cubearray, S32 index, S32 face, F32 near_clip, bool render_avatars, - bool customCullingPlane = false, LLPlane cullingPlane = LLPlane(LLVector3(0, 0, 0), LLVector3(0, 0, 1))); + bool customCullingPlane = false, LLPlane cullingPlane = LLPlane(LLVector3(0, 0, 0), LLVector3(0, 0, 1)), + const LLVector3* overrideLookDir = nullptr, const LLVector3* overrideUpDir = nullptr); // special implementation of simpleSnapshot for reflection maps diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2f39a76156c..95291e8c6ee 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -9993,6 +9993,12 @@ const LLVOAvatar::MatrixPaletteCache& LLVOAvatar::updateSkinInfoMatrixPalette(co { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + if (entry.mFrame > 0) + { + entry.mLastGLMp = entry.mGLMp; + entry.mLastFrame = entry.mFrame; + } + entry.mFrame = gFrameCount; //build matrix palette diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 580d6ec911b..603b7f505d6 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -907,6 +907,10 @@ class LLVOAvatar : // Float array ready to be sent to GL std::vector mGLMp; + // Previous frame's mGLMp for velocity buffer + std::vector mLastGLMp; + S32 mLastFrame = -1; + MatrixPaletteCache() : mFrame(gFrameCount - 1) { diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 3b41ccb6fc3..ec563b2fd9e 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5524,6 +5524,7 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, draw_vec.push_back(draw_info); draw_info->mTextureMatrix = tex_mat; draw_info->mModelMatrix = model_mat; + draw_info->mLastModelMatrix = &drawable->mLastVelocityMatrix; draw_info->mBump = bump; draw_info->mShiny = shiny; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index c9d53bbcbc0..12d814770f0 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -209,13 +209,10 @@ F32 LLPipeline::CameraDoFResScale; F32 LLPipeline::RenderAutoHideSurfaceAreaLimit; bool LLPipeline::RenderScreenSpaceReflections; S32 LLPipeline::RenderScreenSpaceReflectionIterations; -F32 LLPipeline::RenderScreenSpaceReflectionRayStep; -F32 LLPipeline::RenderScreenSpaceReflectionDistanceBias; -F32 LLPipeline::RenderScreenSpaceReflectionDepthRejectBias; -F32 LLPipeline::RenderScreenSpaceReflectionAdaptiveStepMultiplier; S32 LLPipeline::RenderScreenSpaceReflectionGlossySamples; S32 LLPipeline::RenderBufferVisualization; bool LLPipeline::RenderMirrors; +S32 LLPipeline::RenderMirrorCount; S32 LLPipeline::RenderHeroProbeUpdateRate; S32 LLPipeline::RenderHeroProbeConservativeUpdateMultiplier; bool LLPipeline::RenderAvatarCloth; @@ -287,6 +284,8 @@ static LLStaticHashedString sKern("kern"); static LLStaticHashedString sKernScale("kern_scale"); static LLStaticHashedString sSmaaRTMetrics("SMAA_RT_METRICS"); +static LLStaticHashedString sSSRJitterOffset("ssrJitterOffset"); + //---------------------------------------- void drawBox(const LLVector4a& c, const LLVector4a& r); @@ -320,8 +319,10 @@ bool LLPipeline::sBakeSunlight = false; bool LLPipeline::sNoAlpha = false; bool LLPipeline::sUseFarClip = true; bool LLPipeline::sShadowRender = false; +bool LLPipeline::sVelocityRender = false; bool LLPipeline::sRenderGlow = false; bool LLPipeline::sReflectionRender = false; +bool LLPipeline::sDefaultProbeRender = false; bool LLPipeline::sDistortionRender = false; bool LLPipeline::sImpostorRender = false; bool LLPipeline::sImpostorRenderAlphaDepthPass = false; @@ -333,7 +334,9 @@ bool LLPipeline::sRenderDeferred = false; bool LLPipeline::sReflectionProbesEnabled = false; S32 LLPipeline::sVisibleLightCount = 0; bool LLPipeline::sRenderingHUDs; +bool LLPipeline::sT2xJitterEnabled = false; F32 LLPipeline::sDistortionWaterClipPlaneMargin = 1.0125f; +bool LLPipeline::RenderMotionBlur = false; // EventHost API LLPipeline listener. static LLPipelineListener sPipelineListener; @@ -593,16 +596,14 @@ void LLPipeline::init() connectRefreshCachedSettingsSafe("RenderAutoHideSurfaceAreaLimit"); connectRefreshCachedSettingsSafe("RenderScreenSpaceReflections"); connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionIterations"); - connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionRayStep"); - connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionDistanceBias"); - connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionDepthRejectBias"); - connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionAdaptiveStepMultiplier"); connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionGlossySamples"); connectRefreshCachedSettingsSafe("RenderBufferVisualization"); connectRefreshCachedSettingsSafe("RenderMirrors"); + connectRefreshCachedSettingsSafe("RenderMirrorCount"); connectRefreshCachedSettingsSafe("RenderHeroProbeUpdateRate"); connectRefreshCachedSettingsSafe("RenderHeroProbeConservativeUpdateMultiplier"); connectRefreshCachedSettingsSafe("RenderAvatarCloth"); + connectRefreshCachedSettingsSafe("RenderMotionBlur"); LLPointer cntrl_ptr = gSavedSettings.getControl("CollectFontVertexBuffers"); if (cntrl_ptr.notNull()) @@ -888,15 +889,24 @@ bool LLPipeline::allocateScreenBufferInternal(U32 resX, U32 resY) if (RenderFSAAType > 0) { if (!mFXAAMap.allocate(resX, resY, GL_RGBA)) return false; - if (RenderFSAAType == 2) + if (RenderFSAAType == 2 || RenderFSAAType == 3) { if (!mSMAABlendBuffer.allocate(resX, resY, GL_RGBA, false)) return false; } + if (RenderFSAAType == 3) + { + if (!mSMAAHistory.allocate(resX, resY, GL_RGBA)) return false; + } + else + { + mSMAAHistory.release(); + } } else { mFXAAMap.release(); mSMAABlendBuffer.release(); + mSMAAHistory.release(); } //water reflection texture (always needed as scratch space whether or not transparent water is enabled) @@ -904,16 +914,56 @@ bool LLPipeline::allocateScreenBufferInternal(U32 resX, U32 resY) if(RenderScreenSpaceReflections) { - mSceneMap.allocate(resX, resY, screenFormat, true); + mSceneMap.allocate(resX, resY, screenFormat, true, LLTexUnit::TT_TEXTURE, LLTexUnit::TMG_AUTO); + + static LLCachedControl ssrScale(gSavedSettings, "RenderScreenSpaceReflectionsResolutionMultiplier", 1.0f); + F32 scale = llclamp((F32)ssrScale, 0.25f, 1.0f); + U32 ssrW = (U32)(resX * scale); + U32 ssrH = (U32)(resY * scale); + bool fullRes = (ssrW == resX && ssrH == resY); + // At full res: no own depth, share from deferred (zero-copy). + // At reduced res: allocate own depth, blit from deferred before alpha/water passes. + mSSRBuffer.allocate(ssrW, ssrH, GL_RGBA16F, !fullRes, LLTexUnit::TT_TEXTURE, LLTexUnit::TMG_AUTO); + + // Allocate per-mip temp targets for Gaussian ping-pong (one per mip level > 0) + U32 ssrMipLevels = mSSRBuffer.getMipLevels(); + if (ssrMipLevels > 1 && mSSRMipTemp.empty()) + { + mSSRMipTemp.resize(ssrMipLevels - 1); + for (U32 i = 0; i < mSSRMipTemp.size(); ++i) + { + U32 mip = i + 1; + U32 w = llmax(1U, ssrW >> mip); + U32 h = llmax(1U, ssrH >> mip); + mSSRMipTemp[i].allocate(w, h, GL_RGBA16F); + } + } + + if (fullRes) + { + mRT->deferredScreen.shareDepthBuffer(mSSRBuffer); + } } else { mSceneMap.release(); + mSSRBuffer.release(); + mSSRMipTemp.clear(); } mPostPingMap.allocate(resX, resY, GL_RGBA); mPostPongMap.allocate(resX, resY, GL_RGBA); + if (RenderMotionBlur || RenderFSAAType == 3) + { + mVelocityMap.allocate(resX, resY, GL_RG16F); + mRT->deferredScreen.shareDepthBuffer(mVelocityMap); + } + else + { + mVelocityMap.release(); + } + // The water exclusion mask needs its own depth buffer so we can take care of the problem of multiple water planes. // Should we ever make water not just a plane, it also aids with that as well as the water planes will be rendered into the mask. // Why do we do this? Because it saves us some janky logic in the exclusion shader when we generate the mask. @@ -1125,17 +1175,14 @@ void LLPipeline::refreshCachedSettings() CameraDoFResScale = gSavedSettings.getF32("CameraDoFResScale"); RenderAutoHideSurfaceAreaLimit = gSavedSettings.getF32("RenderAutoHideSurfaceAreaLimit"); RenderScreenSpaceReflections = gSavedSettings.getBOOL("RenderScreenSpaceReflections"); - RenderScreenSpaceReflectionIterations = gSavedSettings.getS32("RenderScreenSpaceReflectionIterations"); - RenderScreenSpaceReflectionRayStep = gSavedSettings.getF32("RenderScreenSpaceReflectionRayStep"); - RenderScreenSpaceReflectionDistanceBias = gSavedSettings.getF32("RenderScreenSpaceReflectionDistanceBias"); - RenderScreenSpaceReflectionDepthRejectBias = gSavedSettings.getF32("RenderScreenSpaceReflectionDepthRejectBias"); - RenderScreenSpaceReflectionAdaptiveStepMultiplier = gSavedSettings.getF32("RenderScreenSpaceReflectionAdaptiveStepMultiplier"); RenderScreenSpaceReflectionGlossySamples = gSavedSettings.getS32("RenderScreenSpaceReflectionGlossySamples"); RenderBufferVisualization = gSavedSettings.getS32("RenderBufferVisualization"); RenderMirrors = gSavedSettings.getBOOL("RenderMirrors"); + RenderMirrorCount = gSavedSettings.getS32("RenderMirrorCount"); RenderHeroProbeUpdateRate = gSavedSettings.getS32("RenderHeroProbeUpdateRate"); RenderHeroProbeConservativeUpdateMultiplier = gSavedSettings.getS32("RenderHeroProbeConservativeUpdateMultiplier"); RenderAvatarCloth = gSavedSettings.getBOOL("RenderAvatarCloth"); + RenderMotionBlur = gSavedSettings.getBOOL("RenderMotionBlur"); sReflectionProbesEnabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderReflectionsEnabled") && gSavedSettings.getBOOL("RenderReflectionsEnabled"); RenderSpotLight = nullptr; @@ -1244,6 +1291,9 @@ void LLPipeline::releaseScreenBuffers() mHeroProbeRT.screen.release(); mHeroProbeRT.deferredScreen.release(); mHeroProbeRT.deferredLight.release(); + + mSSRBuffer.release(); + mSSRMipTemp.clear(); } void LLPipeline::releaseSunShadowTarget(U32 index) @@ -4115,6 +4165,37 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera, bool do_occlusion) // Render all of our geometry that's required after our deferred pass. // This is gonna be stuff like alpha, water, etc. +void LLPipeline::renderGeomVelocity() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + LL_PROFILE_GPU_ZONE("renderGeomVelocity"); + + mVelocityMap.bindTarget(); + mVelocityMap.clear(GL_COLOR_BUFFER_BIT); + + gGL.setColorMask(true, true); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_LEQUAL); + + sVelocityRender = true; + + // Each draw pool is responsible for producing its own velocity + for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) + { + LLDrawPool* poolp = *iter; + S32 num_passes = poolp->getNumVelocityPasses(); + for (S32 i = 0; i < num_passes; ++i) + { + poolp->beginVelocityPass(i); + poolp->renderVelocity(i); + poolp->endVelocityPass(i); + } + } + + sVelocityRender = false; + + mVelocityMap.flush(); +} + void LLPipeline::renderGeomPostDeferred(LLCamera& camera) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; @@ -7161,7 +7242,7 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool static LLCachedControl use_exposure_sky_settings(gSavedSettings, "RenderUseExposureSkySettings", false); - if (use_exposure_sky_settings) + if (use_exposure_sky_settings && sky->getSkySettingVersion() > 1) { if (dynamic_exposure_enabled) { @@ -7254,18 +7335,36 @@ void LLPipeline::tonemap(LLRenderTarget* src, LLRenderTarget* dst, bool gamma_co shader->uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, (GLfloat)src->getWidth(), (GLfloat)src->getHeight()); static LLCachedControl exposure(gSavedSettings, "RenderExposure", 1.f); - - F32 e = llclamp(exposure(), 0.5f, 4.f); + static LLCachedControl tonemap_type_setting(gSavedSettings, "RenderTonemapType", 0U); + static LLCachedControl tonemap_mix_setting(gSavedSettings, "RenderTonemapMix", 1.f); + static LLCachedControl use_env_hdr_settings(gSavedSettings, "RenderHDRUseEnvironmentSettings", false); static LLStaticHashedString s_exposure("exposure"); static LLStaticHashedString tonemap_mix("tonemap_mix"); static LLStaticHashedString tonemap_type("tonemap_type"); - shader->uniform1f(s_exposure, e); + // When use_env_hdr_settings is enabled, use environment HDR settings + // Otherwise use user's debug settings + F32 e; + U32 tonemap_type_value; + F32 tonemap_mix_value; - static LLCachedControl tonemap_type_setting(gSavedSettings, "RenderTonemapType", 0U); - shader->uniform1i(tonemap_type, tonemap_type_setting); - shader->uniform1f(tonemap_mix, psky->getTonemapMix(should_auto_adjust())); + if (use_env_hdr_settings && psky->getSkySettingVersion() > 1) + { + e = llclamp(psky->getHDROffset(should_auto_adjust()), 0.5f, 4.f); + tonemap_type_value = psky->getTonemapper(); + tonemap_mix_value = psky->getTonemapMix(should_auto_adjust()); + } + else + { + e = llclamp(exposure(), 0.5f, 4.f); + tonemap_type_value = tonemap_type_setting(); + tonemap_mix_value = tonemap_mix_setting(); + } + + shader->uniform1f(s_exposure, e); + shader->uniform1i(tonemap_type, tonemap_type_value); + shader->uniform1f(tonemap_mix, tonemap_mix_value); mScreenTriangleVB->setBuffer(); mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); @@ -7304,6 +7403,313 @@ void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) dst->flush(); } +void LLPipeline::renderSSRTrace() +{ + if (!RenderScreenSpaceReflections || gCubeSnapshot) return; + if (!gScreenSpaceReflTraceProgram.isComplete()) return; + if (!mSSRBuffer.isComplete()) return; + + LL_PROFILE_GPU_ZONE("SSR trace"); + + gGL.setColorMask(true, true); + mSSRBuffer.bindTarget(); + mSSRBuffer.clear(GL_COLOR_BUFFER_BIT); + + LLGLDepthTest depth(GL_FALSE, GL_FALSE); + LLGLDisable blend(GL_BLEND); + + bindDeferredShader(gScreenSpaceReflTraceProgram); + + mScreenTriangleVB->setBuffer(); + mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + + unbindDeferredShader(gScreenSpaceReflTraceProgram); + mSSRBuffer.flush(); +} + +void LLPipeline::renderSSRAlpha() +{ + if (!RenderScreenSpaceReflections || gCubeSnapshot) return; + if (!gSSRAlphaProgram.isComplete()) return; + if (!mSSRBuffer.isComplete()) return; + + // If the SSR buffer owns its own depth (reduced resolution), blit the + // deferred depth down so alpha/water passes can depth-test correctly. + if (mSSRBuffer.getDepth() != 0 + && mSSRBuffer.getDepth() != mRT->deferredScreen.getDepth()) + { + mSSRBuffer.blitDepthFrom(mRT->deferredScreen); + } + + LL_PROFILE_GPU_ZONE("SSR alpha"); + + gGL.setColorMask(true, true); + mSSRBuffer.bindTarget(); + + // Read-only depth test: discard alpha fragments behind opaque geometry + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_LEQUAL); + LLGLDisable blend(GL_BLEND); + + // Identity texture transform for legacy materials + static const F32 sIdentityTransform[8] = { 1.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }; + + const LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + bool skipLastSkin = false; + + for (int pass = 0; pass < 2; ++pass) + { + bool rigged = (pass == 1); + + LLGLSLShader* shader = &gSSRAlphaProgram; + if (rigged && shader->mRiggedVariant) + { + shader = shader->mRiggedVariant; + } + + // Bind shader once per pass, then use fast path for re-syncing + bindDeferredShader(*shader); + shader->mCanBindFast = true; + + LLCullResult::sg_iterator begin; + LLCullResult::sg_iterator end; + + if (rigged) + { + begin = beginRiggedAlphaGroups(); + end = endRiggedAlphaGroups(); + } + else + { + begin = beginAlphaGroups(); + end = endAlphaGroups(); + } + + for (LLCullResult::sg_iterator i = begin; i != end; ++i) + { + LLSpatialGroup* group = *i; + if (!group || group->isDead()) continue; + if (!group->getSpatialPartition()->mRenderByGroup) continue; + + U32 passType = rigged ? LLRenderPass::PASS_ALPHA_RIGGED : LLRenderPass::PASS_ALPHA; + LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[passType]; + + for (auto k = draw_info.begin(); k != draw_info.end(); ++k) + { + LLDrawInfo& params = **k; + if ((bool)params.mAvatar != rigged) + { + continue; + } + + LLRenderPass::applyModelMatrix(params); + bindDeferredShaderFast(*shader); + + bool tex_setup = false; + + if (params.mGLTFMaterial) + { + // PBR: bind all material textures and uniforms + // (diffuseMap, specularMap/ORM, bumpMap, emissiveMap, + // roughnessFactor, texture transforms, etc.) + LLGLDisable cull_face(params.mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); + params.mGLTFMaterial->bind(params.mTexture); + + // Handle texture animation (LSL texture anim on PBR objects) + if (params.mTextureMatrix) + { + tex_setup = true; + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadMatrix((GLfloat*)params.mTextureMatrix->mMatrix); + } + } + else + { + // Legacy: bind diffuse texture for alpha sampling + if (params.mTexture.notNull()) + { + shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, params.mTexture); + } + else + { + shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep); + } + + // Legacy: bind white for ORM so shader reads roughness=1.0 from green channel, + // making final roughness = roughness_factor directly + shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep); + + // Legacy glossiness from specular color alpha + F32 roughness = 1.0f - params.mSpecColor.mV[3]; + shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, roughness); + + // Identity PBR texture transforms for legacy materials + shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, sIdentityTransform); + shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, sIdentityTransform); + shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, -1.0f); + + // Handle legacy texture matrix (texture animation) + if (params.mTextureMatrix) + { + tex_setup = true; + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadMatrix((GLfloat*)params.mTextureMatrix->mMatrix); + } + } + + if (rigged) + { + LLRenderPass::uploadMatrixPalette(params.mAvatar, params.mSkinInfo, + lastAvatar, lastMeshId, skipLastSkin); + } + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, + params.mStart, params.mEnd, params.mCount, params.mOffset); + + if (tex_setup) + { + gGL.getTexUnit(0)->activate(); + gGL.matrixMode(LLRender::MM_TEXTURE); + gGL.loadIdentity(); + gGL.matrixMode(LLRender::MM_MODELVIEW); + } + } + } + + unbindDeferredShader(*shader); + } + + mSSRBuffer.flush(); + + // Reset modelview to camera-only — applyModelMatrix leaves a stale + // object transform that would corrupt light volume positions in + // renderDeferredLighting (the point light vertex shader multiplies by + // modelview_projection_matrix via syncMatrices). + gGLLastMatrix = NULL; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(gGLModelView); +} + +void LLPipeline::renderSSRWater() +{ + if (!RenderScreenSpaceReflections || gCubeSnapshot) return; + if (!gSSRWaterProgram.isComplete()) return; + if (!mSSRBuffer.isComplete()) return; + + LLDrawPool* pool = findPool(LLDrawPool::POOL_WATER); + if (!pool) return; + + LL_PROFILE_GPU_ZONE("SSR water"); + + gGL.setColorMask(true, true); + mSSRBuffer.bindTarget(); + + // Read-only depth test: discard water fragments behind opaque geometry. + // Treat water as opaque in the SSR buffer. + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_LEQUAL); + LLGLDisable blend(GL_BLEND); + + static_cast(pool)->renderSSR(); + + mSSRBuffer.flush(); + + // Same matrix reset as renderSSRAlpha — water geometry may leave a stale + // modelview that would corrupt light volume positions in deferred lighting. + gGLLastMatrix = NULL; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(gGLModelView); +} + +void LLPipeline::filterSSRBuffer() +{ + if (!RenderScreenSpaceReflections || gCubeSnapshot) return; + if (!mSSRBuffer.isComplete()) return; + if (!gGaussianProgram.isComplete()) return; + + LL_PROFILE_GPU_ZONE("SSR mip chain"); + + U32 mipLevels = mSSRBuffer.getMipLevels(); + if (mipLevels <= 1) return; + if (mSSRMipTemp.empty()) return; + + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_FALSE, GL_FALSE); + + // Identity matrices for screen-space rendering (same as probe manager) + gGL.matrixMode(gGL.MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.matrixMode(gGL.MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.flush(); + + static LLStaticHashedString resScale("resScale"); + static LLStaticHashedString direction("direction"); + + static LLCachedControl ssrFilterScale(gSavedSettings, "RenderScreenSpaceReflectionsFilterScale", 2.0f); + + gGaussianProgram.bind(); + S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE); + + for (U32 m = 1; m < mipLevels && (m - 1) < mSSRMipTemp.size(); m++) + { + LL_PROFILE_GPU_ZONE("SSR mip blur"); + + LLRenderTarget& temp = mSSRMipTemp[m - 1]; + S32 srcW = llmax(1, (S32)(mSSRBuffer.getWidth() >> (m - 1))); + S32 srcH = llmax(1, (S32)(mSSRBuffer.getHeight() >> (m - 1))); + + // Horizontal pass: read SSR mip m-1, write to temp (at mip m resolution) + gGaussianProgram.uniform1f(resScale, (F32)ssrFilterScale / (F32)srcW); + gGaussianProgram.uniform2f(direction, 1.f, 0.f); + + gGL.getTexUnit(diffuseChannel)->bind(&mSSRBuffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, m - 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m - 1); + + temp.bindTarget(); + mScreenTriangleVB->setBuffer(); + mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + temp.flush(); + + // Vertical pass: read temp, write to SSR mip m (same resolution as temp) + gGaussianProgram.uniform1f(resScale, (F32)ssrFilterScale / (F32)temp.getHeight()); + gGaussianProgram.uniform2f(direction, 0.f, 1.f); + + gGL.getTexUnit(diffuseChannel)->bind(&temp); + + mSSRBuffer.bindColorMipLevel(m); + mScreenTriangleVB->setBuffer(); + mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + } + + // Restore SSR buffer state + gGL.getTexUnit(diffuseChannel)->bind(&mSSRBuffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipLevels - 1); + mSSRBuffer.resetColorMipLevel(); + + // bindColorMipLevel bypasses the RT stack (direct glBindFramebuffer), + // so we must manually unbind the FBO here to avoid corrupting GL state + // for renderDeferredLighting. + glBindFramebuffer(GL_FRAMEBUFFER, 0); + LLRenderTarget::sCurFBO = 0; + + gGaussianProgram.unbind(); + + gGL.matrixMode(gGL.MM_PROJECTION); + gGL.popMatrix(); + + gGL.matrixMode(gGL.MM_MODELVIEW); + gGL.popMatrix(); +} + void LLPipeline::copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* dst) { @@ -7323,6 +7729,7 @@ void LLPipeline::copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* gGL.getTexUnit(diff_map)->bind(src); gGL.getTexUnit(depth_map)->bind(&depth_src, true); + gGL.getTexUnit(depth_map)->setTextureFilteringOption(LLTexUnit::TFO_POINT); mScreenTriangleVB->setBuffer(); mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); @@ -7331,6 +7738,51 @@ void LLPipeline::copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* } } +void LLPipeline::buildHiZBuffer() +{ + if (!RenderScreenSpaceReflections || gCubeSnapshot) return; + + LL_PROFILE_GPU_ZONE("build Hi-Z"); + + S32 mipLevels = mSceneMap.getMipLevels(); + if (mipLevels <= 1) return; + + LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS); + + gHiZReduceProgram.bind(); + + static LLStaticHashedString sHiZSrcLevel("srcLevel"); + + // Bind mSceneMap's own depth as the read source + gHiZReduceProgram.bindTexture(LLShaderMgr::DEFERRED_DEPTH, &mSceneMap, true, LLTexUnit::TFO_POINT); + + for (S32 level = 1; level < mipLevels; level++) + { + // Restrict readable mip range to [0, level-1] to avoid feedback + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1); + + gHiZReduceProgram.uniform1i(sHiZSrcLevel, level - 1); + + mSceneMap.bindDepthMipLevel(level); + + mScreenTriangleVB->setBuffer(); + mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + } + + // Restore full mip range and mip 0 binding + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipLevels - 1); + mSceneMap.resetDepthMipLevel(); + + // bindDepthMipLevel bypasses the RT stack (direct glBindFramebuffer), + // so we must manually unbind the FBO here. + glBindFramebuffer(GL_FRAMEBUFFER, 0); + LLRenderTarget::sCurFBO = 0; + + gHiZReduceProgram.unbind(); +} + void LLPipeline::generateGlow(LLRenderTarget* src) { LL_PROFILE_GPU_ZONE("glow generate"); @@ -7584,7 +8036,7 @@ void LLPipeline::applyFXAA(LLRenderTarget* src, LLRenderTarget* dst) void LLPipeline::generateSMAABuffers(LLRenderTarget* src) { llassert(!gCubeSnapshot); - bool multisample = RenderFSAAType == 2 && gSMAAEdgeDetectProgram[0].isComplete() && mFXAAMap.isComplete() && mSMAABlendBuffer.isComplete(); + bool multisample = (RenderFSAAType == 2 || RenderFSAAType == 3) && gSMAAEdgeDetectProgram[0].isComplete() && mFXAAMap.isComplete() && mSMAABlendBuffer.isComplete(); // Present everything. if (multisample) @@ -7658,6 +8110,21 @@ void LLPipeline::generateSMAABuffers(LLRenderTarget* src) blend_weights_shader.bind(); blend_weights_shader.uniform4fv(sSmaaRTMetrics, 1, rt_metrics); + // T2x subsample indices: alternates between (1,1,1,0) and (2,2,2,0) each frame + float subsample[4] = {0.f, 0.f, 0.f, 0.f}; + if (RenderFSAAType == 3) + { + if (mSMAAFrameIndex & 1) + { + subsample[0] = 2.f; subsample[1] = 2.f; subsample[2] = 2.f; subsample[3] = 0.f; + } + else + { + subsample[0] = 1.f; subsample[1] = 1.f; subsample[2] = 1.f; subsample[3] = 0.f; + } + } + blend_weights_shader.uniform4fv(LLShaderMgr::SMAA_SUBSAMPLE_INDICES, 1, subsample); + S32 edge_tex_channel = blend_weights_shader.enableTexture(LLShaderMgr::SMAA_EDGE_TEX, mFXAAMap.getUsage()); if (edge_tex_channel > -1) { @@ -7703,9 +8170,8 @@ void LLPipeline::applySMAA(LLRenderTarget* src, LLRenderTarget* dst) { LL_PROFILE_GPU_ZONE("SMAA"); llassert(!gCubeSnapshot); - bool multisample = RenderFSAAType == 2 && gSMAAEdgeDetectProgram[0].isComplete() && mFXAAMap.isComplete() && mSMAABlendBuffer.isComplete(); + bool multisample = (RenderFSAAType == 2 || RenderFSAAType == 3) && gSMAAEdgeDetectProgram[0].isComplete() && mFXAAMap.isComplete() && mSMAABlendBuffer.isComplete(); - // Present everything. if (multisample) { static LLCachedControl aa_quality(gSavedSettings, "RenderFSAASamples", 0U); @@ -7718,15 +8184,11 @@ void LLPipeline::applySMAA(LLRenderTarget* src, LLRenderTarget* dst) LLGLDepthTest depth(GL_FALSE, GL_FALSE); - static LLCachedControl use_sample(gSavedSettings, "RenderSMAAUseSample", false); - //static LLCachedControl use_stencil(gSavedSettings, "RenderSMAAUseStencil", true); - { - //LLGLDisable stencil(GL_STENCIL_TEST); - - // Bind setup: LLRenderTarget* bound_target = dst; - LLGLSLShader& blend_shader = gSMAANeighborhoodBlendProgram[fsaa_quality]; + LLGLSLShader& blend_shader = (RenderFSAAType == 3) + ? gSMAANeighborhoodBlendT2xProgram[fsaa_quality] + : gSMAANeighborhoodBlendProgram[fsaa_quality]; bound_target->bindTarget(); bound_target->clear(GL_COLOR_BUFFER_BIT); @@ -7747,6 +8209,15 @@ void LLPipeline::applySMAA(LLRenderTarget* src, LLRenderTarget* dst) mSMAABlendBuffer.bindTexture(0, blend_channel, LLTexUnit::TFO_BILINEAR); } + if (RenderFSAAType == 3) + { + S32 vel_channel = blend_shader.enableTexture(LLShaderMgr::SMAA_VELOCITY_TEX); + if (vel_channel > -1) + { + mVelocityMap.bindTexture(0, vel_channel, LLTexUnit::TFO_BILINEAR); + } + } + mScreenTriangleVB->setBuffer(); mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); @@ -7762,6 +8233,49 @@ void LLPipeline::applySMAA(LLRenderTarget* src, LLRenderTarget* dst) } } +void LLPipeline::resolveSMAAT2x(LLRenderTarget* src, LLRenderTarget* dst) +{ + LL_PROFILE_GPU_ZONE("SMAA T2x Resolve"); + + static LLCachedControl aa_quality(gSavedSettings, "RenderFSAASamples", 0U); + U32 q = std::clamp(aa_quality(), 0U, 3U); + + dst->bindTarget(); + + LLGLSLShader& shader = gSMAAResolveProgram[q]; + shader.bind(); + + S32 cur_ch = shader.enableTexture(LLShaderMgr::SMAA_CURRENT_COLOR_TEX); + if (cur_ch > -1) + { + src->bindTexture(0, cur_ch, LLTexUnit::TFO_POINT); + } + + S32 prev_ch = shader.enableTexture(LLShaderMgr::SMAA_PREVIOUS_COLOR_TEX); + if (prev_ch > -1) + { + mSMAAHistory.bindTexture(0, prev_ch, LLTexUnit::TFO_POINT); + } + + S32 vel_ch = shader.enableTexture(LLShaderMgr::SMAA_VELOCITY_TEX); + if (vel_ch > -1) + { + mVelocityMap.bindTexture(0, vel_ch, LLTexUnit::TFO_BILINEAR); + } + + mScreenTriangleVB->setBuffer(); + mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + + shader.unbind(); + dst->flush(); + + // Save the current SMAA'd frame (not the resolved output) to history. + // The resolve blends current and previous SMAA outputs — if we stored + // the resolved result, it would create exponential decay instead of a + // true 50/50 blend between the two jitter samples. + copyRenderTarget(src, &mSMAAHistory); +} + void LLPipeline::copyRenderTarget(LLRenderTarget* src, LLRenderTarget* dst) { @@ -7804,6 +8318,28 @@ void LLPipeline::combineGlow(LLRenderTarget* src, LLRenderTarget* dst) dst->flush(); } +void LLPipeline::renderMotionBlurComposite(LLRenderTarget* src, LLRenderTarget* dst) +{ + LL_PROFILE_GPU_ZONE("motion blur"); + + dst->bindTarget(); + + gDeferredMotionBlurProgram.bind(); + gDeferredMotionBlurProgram.bindTexture(LLShaderMgr::DEFERRED_DIFFUSE, src); + gDeferredMotionBlurProgram.bindTexture(LLShaderMgr::DEFERRED_VELOCITY, &mVelocityMap); + gDeferredMotionBlurProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, + (GLfloat)src->getWidth(), (GLfloat)src->getHeight()); + + static LLCachedControl blur_strength(gSavedSettings, "RenderMotionBlurStrength", 32); + gDeferredMotionBlurProgram.uniform1i(LLShaderMgr::MOTION_BLUR_STRENGTH, blur_strength); + + mScreenTriangleVB->setBuffer(); + mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + + gDeferredMotionBlurProgram.unbind(); + dst->flush(); +} + void LLPipeline::renderDoF(LLRenderTarget* src, LLRenderTarget* dst) { LL_PROFILE_GPU_ZONE("dof"); @@ -8023,8 +8559,6 @@ void LLPipeline::renderFinalize() bool hdr = gGLManager.mGLVersion > 4.05f && has_hdr(); if (hdr) { - copyScreenSpaceReflections(&mRT->screen, &mSceneMap); - generateLuminance(&mRT->screen, &mLuminanceMap); generateExposure(&mLuminanceMap, &mExposureMap); @@ -8055,6 +8589,12 @@ void LLPipeline::renderFinalize() combineGlow(sourceBuffer, targetBuffer); std::swap(sourceBuffer, targetBuffer); + if (RenderMotionBlur && !gCubeSnapshot) + { + renderMotionBlurComposite(sourceBuffer, targetBuffer); + std::swap(sourceBuffer, targetBuffer); + } + gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); @@ -8080,6 +8620,17 @@ void LLPipeline::renderFinalize() applySMAA(sourceBuffer, targetBuffer); std::swap(sourceBuffer, targetBuffer); } + else if (RenderFSAAType == 3) + { + generateSMAABuffers(sourceBuffer); + applySMAA(sourceBuffer, targetBuffer); + std::swap(sourceBuffer, targetBuffer); + + resolveSMAAT2x(sourceBuffer, targetBuffer); + std::swap(sourceBuffer, targetBuffer); + + mSMAAFrameIndex ^= 1; + } if (RenderBufferVisualization > -1) { @@ -8110,6 +8661,22 @@ void LLPipeline::renderFinalize() } break; } + case 7: + { + if (RenderMotionBlur) + { + visualizeBuffers(&mVelocityMap, sourceBuffer, 0); + } + break; + } + case 8: + { + if (mSSRBuffer.isComplete()) + { + visualizeBuffers(&mSSRBuffer, sourceBuffer, 0); + } + break; + } default: break; } @@ -8593,7 +9160,8 @@ void LLPipeline::renderDeferredLighting() if (RenderDeferredAtmospheric) { // apply sunlight contribution - LLGLSLShader &soften_shader = gDeferredSoftenProgram; + // Use cube snapshot variant shader when rendering to reflection probes + LLGLSLShader &soften_shader = gCubeSnapshot ? gDeferredSoftenCubeProgram : gDeferredSoftenProgram; LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("renderDeferredLighting - atmospherics"); LL_PROFILE_GPU_ZONE("atmospherics"); @@ -8614,6 +9182,12 @@ void LLPipeline::renderDeferredLighting() soften_shader.uniform4fv(LLShaderMgr::WATER_WATERPLANE, 1, LLDrawPoolAlpha::sWaterPlane.mV); + // Pass default_probe_render flag when rendering to reflection probes + if (gCubeSnapshot) + { + soften_shader.uniform1i(LLShaderMgr::DEFAULT_PROBE_RENDER, LLPipeline::sDefaultProbeRender ? 1 : 0); + } + { LLGLDepthTest depth(GL_FALSE); LLGLDisable blend(GL_BLEND); @@ -8918,10 +9492,17 @@ void LLPipeline::renderDeferredLighting() LLPipeline::RENDER_TYPE_WATEREXCLUSION, END_RENDER_TYPES); + mInForwardAlphaPass = true; renderGeomPostDeferred(*LLViewerCamera::getInstance()); + mInForwardAlphaPass = false; popRenderTypeMask(); } + if ((RenderMotionBlur || RenderFSAAType == 3) && !gCubeSnapshot) + { + renderGeomVelocity(); + } + screen_target->flush(); if (!gCubeSnapshot) @@ -9350,34 +9931,111 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) setEnvMat(shader); } - // reflection probe shaders generally sample the scene map as well for SSR - channel = shader.enableTexture(LLShaderMgr::SCENE_MAP); - if (channel > -1) - { - gGL.getTexUnit(channel)->bind(&mSceneMap); - } - - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_ITR_COUNT, (GLfloat)RenderScreenSpaceReflectionIterations); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_DIST_BIAS, RenderScreenSpaceReflectionDistanceBias); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_RAY_STEP, RenderScreenSpaceReflectionRayStep); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_GLOSSY_SAMPLES, (GLfloat)RenderScreenSpaceReflectionGlossySamples); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_REJECT_BIAS, RenderScreenSpaceReflectionDepthRejectBias); - mPoissonOffset++; + if (RenderScreenSpaceReflections) + { + // Determine if this shader is performing the SSR trace (needs previous + // frame scene map + trace uniforms) or reading pre-computed SSR results + // from the SSR buffer. Forward alpha shaders also need trace bindings + // so transparent surfaces can trace their own SSR inline rather than + // reading incorrect opaque-surface SSR from the pre-computed buffer. + bool inSSRTracePass = (&shader == &gScreenSpaceReflTraceProgram) + || (&shader == &gSSRAlphaProgram) + || (&shader == &gSkinnedSSRAlphaProgram) + || (&shader == &gSSRWaterProgram) + || (mInForwardAlphaPass && &shader != &gWaterProgram); - if (mPoissonOffset > 128 - RenderScreenSpaceReflectionGlossySamples) - mPoissonOffset = 0; + channel = shader.enableTexture(LLShaderMgr::SCENE_MAP); + if (channel > -1) + { + if (inSSRTracePass) + { + // Trace pass: bind previous frame's lit scene for ray marching + gGL.getTexUnit(channel)->bind(&mSceneMap); + } + else if (mSSRBuffer.isComplete() && !gCubeSnapshot) + { + // Lighting pass: bind pre-computed SSR buffer with trilinear for mip sampling + gGL.getTexUnit(channel)->bind(&mSSRBuffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_NOISE_SINE, (GLfloat)mPoissonOffset); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_ADAPTIVE_STEP_MULT, RenderScreenSpaceReflectionAdaptiveStepMultiplier); + // Pass mip scale for roughness -> mip level mapping + static LLStaticHashedString sSsrMipScale("ssrMipScale"); + shader.uniform1f(sSsrMipScale, (float)(mSSRBuffer.getMipLevels() - 1)); + } + else + { + // Fallback / cube snapshot: bind scene map (probes should not + // see the main camera's SSR buffer) + gGL.getTexUnit(channel)->bind(&mSceneMap); + } + } + + if (inSSRTracePass) + { + // Set all SSR trace uniforms (only needed for trace shaders) + static LLCachedControl traceIterations(gSavedSettings, "RenderScreenSpaceReflectionIterations"); + static LLCachedControl traceMaxThickness(gSavedSettings, "RenderScreenSpaceReflectionMaxThickness"); + static LLCachedControl traceDepthBias(gSavedSettings, "RenderScreenSpaceReflectionDepthBias"); + static LLCachedControl traceMaxDepth(gSavedSettings, "RenderScreenSpaceReflectionMaxDepth"); + static LLCachedControl traceMaxRoughness(gSavedSettings, "RenderScreenSpaceReflectionMaxRoughness"); + + shader.uniform1i(LLShaderMgr::DEFERRED_SSR_ITR_COUNT, traceIterations); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_THICKNESS, traceMaxThickness); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_DEPTH_BIAS, traceDepthBias); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_GLOSSY_SAMPLES, (GLfloat)RenderScreenSpaceReflectionGlossySamples); + + // Only advance the Poisson noise offset for the dedicated SSR trace + // programs — not for every forward alpha bind. Otherwise the offset + // increments hundreds of times per frame (once per alpha draw call), + // with the count varying by what's visible, destabilizing the noise + // seed between frames and causing SSR to flicker. + bool isSSRTraceProgram = (&shader == &gScreenSpaceReflTraceProgram) + || (&shader == &gSSRAlphaProgram) + || (&shader == &gSkinnedSSRAlphaProgram) + || (&shader == &gSSRWaterProgram); + if (isSSRTraceProgram) + { + mPoissonOffset++; + if (mPoissonOffset > 128 - RenderScreenSpaceReflectionGlossySamples) + mPoissonOffset = 0; + } + + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_NOISE_SINE, (GLfloat)mPoissonOffset); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_Z, traceMaxDepth()); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_ROUGHNESS, traceMaxRoughness()); + + // Compute jitter compensation for SMAA T2x. + // mSceneMap was rendered with the previous frame's jittered projection. + // SSR projects positions using the current frame's projection. + // Correct the UV offset so mSceneMap lookups hit the right texels. + float jitterOffsetX = 0.f; + float jitterOffsetY = 0.f; + if (sT2xJitterEnabled) + { + static const float jitters[2][2] = { + { 0.25f, -0.25f }, + {-0.25f, 0.25f }, + }; + U32 curIdx = mSMAAFrameIndex & 1; + U32 prevIdx = curIdx ^ 1; + float width = (float)gViewerWindow->getWorldViewWidthRaw(); + float height = (float)gViewerWindow->getWorldViewHeightRaw(); + jitterOffsetX = (jitters[curIdx][0] - jitters[prevIdx][0]) / width; + jitterOffsetY = (jitters[curIdx][1] - jitters[prevIdx][1]) / height; + } + shader.uniform2f(sSSRJitterOffset, jitterOffsetX, jitterOffsetY); + + channel = shader.enableTexture(LLShaderMgr::SCENE_DEPTH); + if (channel > -1) + { + gGL.getTexUnit(channel)->bind(&mSceneMap, true); + } - channel = shader.enableTexture(LLShaderMgr::SCENE_DEPTH); - if (channel > -1) - { - gGL.getTexUnit(channel)->bind(&mSceneMap, true); + static LLStaticHashedString sHiZMipCount("hizMipCount"); + shader.uniform1i(sHiZMipCount, (GLint)mSceneMap.getMipLevels()); + } } - - } void LLPipeline::unbindReflectionProbes(LLGLSLShader& shader) diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index c051306385e..d9eeff5cc40 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -153,6 +153,11 @@ class LLPipeline void bindScreenToTexture(); void renderFinalize(); void copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* dst); + void buildHiZBuffer(); + void renderSSRTrace(); + void renderSSRAlpha(); + void renderSSRWater(); + void filterSSRBuffer(); void generateLuminance(LLRenderTarget* src, LLRenderTarget* dst); void generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history = true); void tonemap(LLRenderTarget* src, LLRenderTarget* dst, bool gamma_correct); @@ -162,6 +167,7 @@ class LLPipeline void applyFXAA(LLRenderTarget* src, LLRenderTarget* dst); void generateSMAABuffers(LLRenderTarget* src); void applySMAA(LLRenderTarget* src, LLRenderTarget* dst); + void resolveSMAAT2x(LLRenderTarget* src, LLRenderTarget* dst); void renderDoF(LLRenderTarget* src, LLRenderTarget* dst); void copyRenderTarget(LLRenderTarget* src, LLRenderTarget* dst); void combineGlow(LLRenderTarget* src, LLRenderTarget* dst); @@ -308,6 +314,8 @@ class LLPipeline void renderGeomDeferred(LLCamera& camera, bool do_occlusion = false); void renderGeomPostDeferred(LLCamera& camera); + void renderGeomVelocity(); + void renderMotionBlurComposite(LLRenderTarget* src, LLRenderTarget* dst); void renderGeomShadow(LLCamera& camera); void bindLightFunc(LLGLSLShader& shader); @@ -668,9 +676,11 @@ class LLPipeline static bool sNoAlpha; static bool sUseFarClip; static bool sShadowRender; + static bool sVelocityRender; static bool sDynamicLOD; static bool sPickAvatar; static bool sReflectionRender; + static bool sDefaultProbeRender; static bool sDistortionRender; static bool sImpostorRender; static bool sImpostorRenderAlphaDepthPass; @@ -683,6 +693,7 @@ class LLPipeline static bool sReflectionProbesEnabled; static S32 sVisibleLightCount; static bool sRenderingHUDs; + static bool sT2xJitterEnabled; static F32 sDistortionWaterClipPlaneMargin; static LLTrace::EventStatHandle sStatBatchSize; @@ -724,6 +735,12 @@ class LLPipeline // for use by SSR LLRenderTarget mSceneMap; + // Pre-computed SSR: RGB=reflection, A=fade + LLRenderTarget mSSRBuffer; + std::vector mSSRMipTemp; // Per-mip temp targets for SSR Gaussian ping-pong + + bool mInForwardAlphaPass = false; + // exposure map for getting average color in scene LLRenderTarget mLuminanceMap; LLRenderTarget mExposureMap; @@ -733,9 +750,13 @@ class LLPipeline LLRenderTarget mPostPingMap; LLRenderTarget mPostPongMap; + LLRenderTarget mVelocityMap; + // FXAA helper target LLRenderTarget mFXAAMap; LLRenderTarget mSMAABlendBuffer; + LLRenderTarget mSMAAHistory; + U32 mSMAAFrameIndex = 0; // render ui to buffer target LLRenderTarget mUIScreen; @@ -1078,16 +1099,14 @@ class LLPipeline static F32 RenderAutoHideSurfaceAreaLimit; static bool RenderScreenSpaceReflections; static S32 RenderScreenSpaceReflectionIterations; - static F32 RenderScreenSpaceReflectionRayStep; - static F32 RenderScreenSpaceReflectionDistanceBias; - static F32 RenderScreenSpaceReflectionDepthRejectBias; - static F32 RenderScreenSpaceReflectionAdaptiveStepMultiplier; static S32 RenderScreenSpaceReflectionGlossySamples; static S32 RenderBufferVisualization; static bool RenderMirrors; + static S32 RenderMirrorCount; static S32 RenderHeroProbeUpdateRate; static S32 RenderHeroProbeConservativeUpdateMultiplier; static bool RenderAvatarCloth; + static bool RenderMotionBlur; }; void render_bbox(const LLVector3 &min, const LLVector3 &max); diff --git a/indra/newview/skins/default/xui/en/floater_adjust_environment.xml b/indra/newview/skins/default/xui/en/floater_adjust_environment.xml index 889eeb53698..8ff17563c7a 100644 --- a/indra/newview/skins/default/xui/en/floater_adjust_environment.xml +++ b/indra/newview/skins/default/xui/en/floater_adjust_environment.xml @@ -5,17 +5,33 @@ save_rect="false" title="Personal Lighting" width="845" - height="280" + height="350" min_width="500" - min_height="275" + min_height="350" single_instance="true" can_resize="false"> HDR Scale: Brightness: Intensity of lighting effects such as realistically bright skies and dynamic exposure. 1.0 is the default, 0 is off, values between 0 and 1 are mixing Ambient with HDR. + + @@ -167,7 +183,7 @@ user_resize="false" visible="true" width="200" - height="150"> + height="310"> + Ambient Sky Saturation: + @@ -421,6 +457,26 @@ top_pad="5" width="130" can_edit_text="true"/> + Sun Brightness (lux): + @@ -519,4 +575,121 @@ + + + Tonemapper: + + + + + Tonemap Mix: + + HDR Offset: + + HDR Min: + + HDR Max: + + + diff --git a/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml b/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml index a29cdb5b417..62242c3d135 100644 --- a/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml +++ b/indra/newview/skins/default/xui/en/panel_settings_sky_atmos.xml @@ -10,14 +10,48 @@ HDR Scale: Brightness: Intensity of lighting effects such as realistically bright skies and dynamic exposure. 1.0 is the default, 0 is off, values between 0 and 1 are mixing Ambient with HDR. + + + + + width="470" + height="500" + follows="left|top" + orientation="vertical"> + height="240"> + height="260"> + + Ambient Sky Saturation: + + + + + + + + Tonemapper: + + + + + + + Tonemapper Mix: + + + + HDR Offset: + + + + HDR Min: + + + + HDR Max: + + + + diff --git a/indra/newview/skins/default/xui/en/panel_settings_sky_sunmoon.xml b/indra/newview/skins/default/xui/en/panel_settings_sky_sunmoon.xml index 34b48574d59..29c71cf0434 100644 --- a/indra/newview/skins/default/xui/en/panel_settings_sky_sunmoon.xml +++ b/indra/newview/skins/default/xui/en/panel_settings_sky_sunmoon.xml @@ -7,14 +7,32 @@ left="0" name="panel_settings_sky_hbodies" top="0"> - + + + height="460"> + Sun Brightness (lux): + + + Star Brightness: + height="460"> - + + + +