diff options
Diffstat (limited to 'indra')
176 files changed, 9318 insertions, 1269 deletions
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 9f79c13a97..873253b419 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -60,6 +60,12 @@ if(WINDOWS) nghttp2.dll libhunspell.dll uriparser.dll + Iex-3_2.dll + IlmThread-3_2.dll + Imath-3_1.dll + OpenEXR-3_2.dll + OpenEXRCore-3_2.dll + OpenEXRUtil-3_2.dll ) # ICU4C (same filenames for 32 and 64 bit builds) diff --git a/indra/cmake/OpenEXR.cmake b/indra/cmake/OpenEXR.cmake new file mode 100644 index 0000000000..ee21fac541 --- /dev/null +++ b/indra/cmake/OpenEXR.cmake @@ -0,0 +1,18 @@ +# -*- cmake -*- + +include(Prebuilt) + +include_guard() +add_library( ll::openexr INTERFACE IMPORTED ) + +if(USE_CONAN ) + target_link_libraries( ll::openexr INTERFACE CONAN_PKG::openexr ) + return() +endif() + +use_prebuilt_binary(openexr) + +target_link_libraries( ll::openexr INTERFACE Iex-3_2 IlmThread-3_2 Imath-3_1 OpenEXR-3_2 OpenEXRCore-3_2 OpenEXRUtil-3_2) + +target_include_directories( ll::openexr SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/OpenEXR ${LIBS_PREBUILT_DIR}/include/Imath) + diff --git a/indra/llcommon/indra_constants.cpp b/indra/llcommon/indra_constants.cpp index 9a0c565b06..39e232e480 100644 --- a/indra/llcommon/indra_constants.cpp +++ b/indra/llcommon/indra_constants.cpp @@ -89,3 +89,4 @@ const LLUUID IMG_USE_BAKED_AUX1 ("9742065b-19b5-297c-858a-29711d539043"); const LLUUID IMG_USE_BAKED_AUX2 ("03642e83-2bd1-4eb9-34b4-4c47ed586d2d"); const LLUUID IMG_USE_BAKED_AUX3 ("edd51b77-fc10-ce7a-4b3d-011dfc349e4f"); +const LLUUID BLANK_MATERIAL_ASSET_ID ("968cbad0-4dad-d64e-71b5-72bf13ad051a"); diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index a16cfac2b9..a4b2efef98 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -236,6 +236,8 @@ LL_COMMON_API extern const LLUUID DEFAULT_OBJECT_SPECULAR; LL_COMMON_API extern const LLUUID DEFAULT_OBJECT_NORMAL; LL_COMMON_API extern const LLUUID BLANK_OBJECT_NORMAL; +LL_COMMON_API extern const LLUUID BLANK_MATERIAL_ASSET_ID; + // radius within which a chat message is fully audible const F32 CHAT_NORMAL_RADIUS = 20.f; diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 031471d1fe..16609b60be 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -31,6 +31,7 @@ #include "llmath.h" #include "v4coloru.h" +#include "v3color.h" #include "llimagebmp.h" #include "llimagetga.h" @@ -989,6 +990,28 @@ void LLImageRaw::verticalFlip() } +bool LLImageRaw::checkHasTransparentPixels() +{ + if (getComponents() != 4) + { + return false; + } + + U8* data = getData(); + U32 pixels = getWidth() * getHeight(); + + // check alpha channel for all 255 + for (U32 i = 0; i < pixels; ++i) + { + if (data[i * 4 + 3] != 255) + { + return true; + } + } + + return false; +} + bool LLImageRaw::optimizeAwayAlpha() { if (getComponents() == 4) @@ -1026,6 +1049,34 @@ bool LLImageRaw::optimizeAwayAlpha() return false; } +bool LLImageRaw::makeAlpha() +{ + if (getComponents() == 3) + { + U8* data = getData(); + U32 pixels = getWidth() * getHeight(); + + // alpha channel doesn't exist, make a new copy of data with alpha channel + U8* new_data = (U8*) ll_aligned_malloc_16(getWidth() * getHeight() * 4); + + for (U32 i = 0; i < pixels; ++i) + { + U32 di = i * 4; + U32 si = i * 3; + for (U32 j = 0; j < 3; ++j) + { + new_data[di+j] = data[si+j]; + } + } + + setDataAndSize(new_data, getWidth(), getHeight(), 3); + + return true; + } + + return false; +} + void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image) { // Find new sizes @@ -1110,7 +1161,7 @@ void LLImageRaw::composite( LLImageRaw* src ) return; } - llassert(3 == src->getComponents()); + llassert((3 == src->getComponents()) || (4 == src->getComponents())); llassert(3 == dst->getComponents()); if( 3 == dst->getComponents() ) @@ -1268,6 +1319,30 @@ void LLImageRaw::fill( const LLColor4U& color ) } } +void LLImageRaw::tint( const LLColor3& color ) +{ + llassert( (3 == getComponents()) || (4 == getComponents()) ); + if (isBufferInvalid()) + { + LL_WARNS() << "Invalid image buffer" << LL_ENDL; + return; + } + + S32 pixels = getWidth() * getHeight(); + const S32 components = getComponents(); + U8* data = getData(); + for( S32 i = 0; i < pixels; i++ ) + { + const float c0 = data[0] * color.mV[0]; + const float c1 = data[1] * color.mV[1]; + const float c2 = data[2] * color.mV[2]; + data[0] = llclamp((U8)c0, 0, 255); + data[1] = llclamp((U8)c1, 0, 255); + data[2] = llclamp((U8)c2, 0, 255); + data += components; + } +} + LLPointer<LLImageRaw> LLImageRaw::duplicate() { if(getNumRefs() < 2) diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 8f9e1b3c54..93b58b2356 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -33,7 +33,7 @@ #include "lltrace.h" const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2 -const S32 MAX_IMAGE_MIP = 11; // 2048x2048 +const S32 MAX_IMAGE_MIP = 12; // 4096x4096 // *TODO : Use MAX_IMAGE_MIP as max discard level and modify j2c management so that the number // of levels is read from the header's file, not inferred from its size. @@ -44,7 +44,7 @@ const S32 MAX_DISCARD_LEVEL = 5; // and declared right here. Some come from the JPEG2000 spec, some conventions specific to SL. const S32 MAX_DECOMPOSITION_LEVELS = 32; // Number of decomposition levels cannot exceed 32 according to jpeg2000 spec const S32 MIN_DECOMPOSITION_LEVELS = 5; // the SL viewer will *crash* trying to decode images with fewer than 5 decomposition levels (unless image is small that is) -const S32 MAX_PRECINCT_SIZE = 2048; // No reason to be bigger than MAX_IMAGE_SIZE +const S32 MAX_PRECINCT_SIZE = 4096; // No reason to be bigger than MAX_IMAGE_SIZE const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec @@ -52,11 +52,11 @@ const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after hea const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit) const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2 -const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048 +const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 4096 const S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE; const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE; const S32 MAX_IMAGE_COMPONENTS = 8; -const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 * 2048 * 8 = 16 MB +const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //4096 * 4096 * 8 = 128 MB // Note! These CANNOT be changed without modifying simulator code // *TODO: change both to 1024 when SIM texture fetching is deprecated @@ -71,6 +71,7 @@ const S32 HTTP_PACKET_SIZE = 1496; class LLImageFormatted; class LLImageRaw; class LLColor4U; +class LLColor3; typedef enum e_image_codec { @@ -209,9 +210,13 @@ public: void verticalFlip(); + // Returns true if the image is not fully opaque + bool checkHasTransparentPixels(); // if the alpha channel is all 100% opaque, delete it // returns true if alpha channel was deleted bool optimizeAwayAlpha(); + // Create an alpha channel if this image doesn't have one + bool makeAlpha(); static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE); static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE); @@ -225,6 +230,9 @@ public: // Fill the buffer with a constant color void fill( const LLColor4U& color ); + // Multiply this raw image by the given color + void tint( const LLColor3& color ); + // Copy operations //duplicate this raw image if refCount > 1. diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index c3cd7262fb..6688dadd3a 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -407,7 +407,6 @@ LLSettingsSky::LLSettingsSky(const LLSD &data) : mNextRainbowTextureId(), mNextHaloTextureId() { - mCanAutoAdjust = !data.has(SETTING_REFLECTION_PROBE_AMBIANCE); } LLSettingsSky::LLSettingsSky(): @@ -430,8 +429,6 @@ void LLSettingsSky::replaceSettings(LLSD settings) mNextBloomTextureId.setNull(); mNextRainbowTextureId.setNull(); mNextHaloTextureId.setNull(); - - mCanAutoAdjust = !settings.has(SETTING_REFLECTION_PROBE_AMBIANCE); } void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother) @@ -444,7 +441,6 @@ void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother) mNextBloomTextureId = pother->mNextBloomTextureId; mNextRainbowTextureId = pother->mNextRainbowTextureId; mNextHaloTextureId = pother->mNextHaloTextureId; - mCanAutoAdjust = pother->mCanAutoAdjust; } void LLSettingsSky::blend(const LLSettingsBase::ptr_t &end, F64 blendf) @@ -1146,7 +1142,6 @@ void LLSettingsSky::setSkyIceLevel(F32 ice_level) void LLSettingsSky::setReflectionProbeAmbiance(F32 ambiance) { - mCanAutoAdjust = false; // we've now touched this sky in a "new" way, it can no longer auto adjust setValue(SETTING_REFLECTION_PROBE_AMBIANCE, ambiance); } @@ -1448,24 +1443,6 @@ F32 LLSettingsSky::getReflectionProbeAmbiance(bool auto_adjust) const return mSettings[SETTING_REFLECTION_PROBE_AMBIANCE].asReal(); } -F32 LLSettingsSky::getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust) const -{ -#if 0 - // feed cloud shadow back into reflection probe ambiance to mimic pre-reflection-probe behavior - // without brightening dark/interior spaces - F32 probe_ambiance = getReflectionProbeAmbiance(auto_adjust); - - if (probe_ambiance > 0.f && probe_ambiance < 1.f) - { - probe_ambiance += (1.f - probe_ambiance) * getCloudShadow() * cloud_shadow_scale; - } - - return probe_ambiance; -#else - return getReflectionProbeAmbiance(auto_adjust); -#endif -} - F32 LLSettingsSky::getSkyBottomRadius() const { return mSettings[SETTING_SKY_BOTTOM_RADIUS].asReal(); @@ -1810,3 +1787,8 @@ LLUUID LLSettingsSky::getNextBloomTextureId() const return mNextBloomTextureId; } +// if true, this sky is a candidate for auto-adjustment +bool LLSettingsSky::canAutoAdjust() const +{ + return !mSettings.has(SETTING_REFLECTION_PROBE_AMBIANCE); +} diff --git a/indra/llinventory/llsettingssky.h b/indra/llinventory/llsettingssky.h index 7ba7a9ba06..222ab040cb 100644 --- a/indra/llinventory/llsettingssky.h +++ b/indra/llinventory/llsettingssky.h @@ -139,10 +139,6 @@ public: // auto_adjust - if true and canAutoAdjust() is true, return 1.0 F32 getReflectionProbeAmbiance(bool auto_adjust = false) const; - // get the probe ambiance setting to use for rendering (adjusted by cloud shadow, aka cloud coverage) - // auto_adjust - if true and canAutoAdjust() is true, return 1.0 - F32 getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust = false) const; - // Return first (only) profile layer represented in LLSD LLSD getRayleighConfig() const; LLSD getMieConfig() const; @@ -340,7 +336,7 @@ public: virtual void updateSettings() SETTINGS_OVERRIDE; // if true, this sky is a candidate for auto-adjustment - bool canAutoAdjust() const { return mCanAutoAdjust; } + bool canAutoAdjust() const; protected: static const std::string SETTING_LEGACY_EAST_ANGLE; @@ -385,9 +381,6 @@ private: mutable LLColor4 mTotalAmbient; mutable LLColor4 mHazeColor; - // if true, this sky is a candidate for auto adjustment - bool mCanAutoAdjust = true; - typedef std::map<std::string, S32> mapNameToUniformId_t; static mapNameToUniformId_t sNameToUniformMapping; diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h index c4d04f5d02..e1d3536f66 100644 --- a/indra/llmath/llcamera.h +++ b/indra/llmath/llcamera.h @@ -65,7 +65,6 @@ class LLCamera : public LLCoordFrame { public: - LLCamera(const LLCamera& rhs) { *this = rhs; diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index e694732da2..2f25a4359d 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -45,13 +45,13 @@ #include "llmatrix3a.h" #include "lloctree.h" #include "llvolume.h" -#include "llvolumeoctree.h" #include "llstl.h" #include "llsdserialize.h" #include "llvector4a.h" #include "llmatrix4a.h" #include "llmeshoptimizer.h" #include "lltimer.h" +#include "llvolumeoctree.h" #include "mikktspace/mikktspace.h" #include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file @@ -377,77 +377,6 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons } } -class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*> -{ -public: - const LLVolumeFace* mFace; - - LLVolumeOctreeRebound(const LLVolumeFace* face) - { - mFace = face; - } - - virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) - { //this is a depth first traversal, so it's safe to assum all children have complete - //bounding data - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - - LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); - - LLVector4a& min = node->mExtents[0]; - LLVector4a& max = node->mExtents[1]; - - if (!branch->isEmpty()) - { //node has data, find AABB that binds data set - const LLVolumeTriangle* tri = *(branch->getDataBegin()); - - //initialize min/max to first available vertex - min = *(tri->mV[0]); - max = *(tri->mV[0]); - - for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) - { //for each triangle in node - - //stretch by triangles in node - tri = *iter; - - min.setMin(min, *tri->mV[0]); - min.setMin(min, *tri->mV[1]); - min.setMin(min, *tri->mV[2]); - - max.setMax(max, *tri->mV[0]); - max.setMax(max, *tri->mV[1]); - max.setMax(max, *tri->mV[2]); - } - } - else if (branch->getChildCount() > 0) - { //no data, but child nodes exist - LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0); - - //initialize min/max to extents of first child - min = child->mExtents[0]; - max = child->mExtents[1]; - } - else - { - llassert(!branch->isLeaf()); // Empty leaf - } - - for (S32 i = 0; i < branch->getChildCount(); ++i) - { //stretch by child extents - LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); - min.setMin(min, child->mExtents[0]); - max.setMax(max, child->mExtents[1]); - } - - node->mBounds[0].setAdd(min, max); - node->mBounds[0].mul(0.5f); - - node->mBounds[1].setSub(max,min); - node->mBounds[1].mul(0.5f); - } -}; - //------------------------------------------------------------------- // statics //------------------------------------------------------------------- @@ -5509,7 +5438,6 @@ struct MikktData } }; - bool LLVolumeFace::cacheOptimize(bool gen_tangents) { //optimize for vertex cache according to Forsyth method: LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; @@ -5687,8 +5615,7 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe llassert(mNumIndices % 3 == 0); - mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL); - new LLVolumeOctreeListener(mOctree); + mOctree = new LLVolumeOctree(center, size); const U32 num_triangles = mNumIndices / 3; // Initialize all the triangles we need mOctreeTriangles = new LLVolumeTriangle[num_triangles]; @@ -5743,7 +5670,7 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe while (!mOctree->balance()) { } //calculate AABB for each node - LLVolumeOctreeRebound rebound(this); + LLVolumeOctreeRebound rebound; rebound.traverse(mOctree); if (gDebugGL) @@ -5756,12 +5683,12 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe void LLVolumeFace::destroyOctree() { delete mOctree; - mOctree = NULL; + mOctree = nullptr; delete[] mOctreeTriangles; - mOctreeTriangles = NULL; + mOctreeTriangles = nullptr; } -const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const +const LLVolumeOctree* LLVolumeFace::getOctree() const { return mOctree; } @@ -6476,9 +6403,6 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) return TRUE; } -void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, - const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); - void LLVolumeFace::createTangents() { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; @@ -6496,7 +6420,7 @@ void LLVolumeFace::createTangents() (*ptr++).clear(); } - CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents); + LLCalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents); //normalize normals for (U32 i = 0; i < mNumVertices; i++) @@ -7206,7 +7130,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) } //adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html -void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, +void LLCalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index afed98ff36..cca284d9bc 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -41,6 +41,7 @@ template <class T, typename T_PTR> class LLOctreeNode; class LLVolumeFace; class LLVolume; class LLVolumeTriangle; +class LLVolumeOctree; #include "lluuid.h" #include "v4color.h" @@ -913,7 +914,7 @@ public: void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); void destroyOctree(); // Get a reference to the octree, which may be null - const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const; + const LLVolumeOctree* getOctree() const; enum { @@ -987,7 +988,7 @@ public: LLVector3 mNormalizedScale = LLVector3(1,1,1); private: - LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree; + LLVolumeOctree* mOctree; LLVolumeTriangle* mOctreeTriangles; BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE); @@ -1142,6 +1143,8 @@ public: std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); +void LLCalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); + BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size); BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size); BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size); diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp index 6894d04d3c..95c7cb0b5c 100644 --- a/indra/llmath/llvolumeoctree.cpp +++ b/indra/llmath/llvolumeoctree.cpp @@ -92,15 +92,15 @@ void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTria } LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, - const LLVolumeFace* face, F32* closest_t, + LLVolumeFace* face, F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) - : mFace(face), - mStart(start), + : mStart(start), mDir(dir), mIntersection(intersection), mTexCoord(tex_coord), mNormal(normal), mTangent(tangent), + mFace(face), mClosestT(closest_t), mHitFace(false) { @@ -139,7 +139,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL { *mClosestT = t; mHitFace = true; - + mHitTriangle = tri; if (mIntersection != NULL) { LLVector4a intersect = mDir; diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index d65bca5e52..d6f536b9ca 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -112,7 +112,6 @@ public: class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*> { public: - const LLVolumeFace* mFace; LLVector4a mStart; LLVector4a mDir; LLVector4a mEnd; @@ -121,10 +120,13 @@ public: LLVector4a* mNormal; LLVector4a* mTangent; F32* mClosestT; + LLVolumeFace* mFace; bool mHitFace; + const LLVolumeTriangle* mHitTriangle = nullptr; - LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, - const LLVolumeFace* face, F32* closest_t, + LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, + LLVolumeFace* face, + F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node); @@ -137,4 +139,91 @@ class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle, LLVolum virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch); }; +class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*> +{ +public: + LLVolumeOctreeRebound() + { + } + + virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch) + { //this is a depth first traversal, so it's safe to assum all children have complete + //bounding data + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + + LLVolumeOctreeListener* node = (LLVolumeOctreeListener*)branch->getListener(0); + + LLVector4a& min = node->mExtents[0]; + LLVector4a& max = node->mExtents[1]; + + if (!branch->isEmpty()) + { //node has data, find AABB that binds data set + const LLVolumeTriangle* tri = *(branch->getDataBegin()); + + //initialize min/max to first available vertex + min = *(tri->mV[0]); + max = *(tri->mV[0]); + + for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) + { //for each triangle in node + + //stretch by triangles in node + tri = *iter; + + min.setMin(min, *tri->mV[0]); + min.setMin(min, *tri->mV[1]); + min.setMin(min, *tri->mV[2]); + + max.setMax(max, *tri->mV[0]); + max.setMax(max, *tri->mV[1]); + max.setMax(max, *tri->mV[2]); + } + } + else if (branch->getChildCount() > 0) + { //no data, but child nodes exist + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*)branch->getChild(0)->getListener(0); + + //initialize min/max to extents of first child + min = child->mExtents[0]; + max = child->mExtents[1]; + } + else + { + llassert(!branch->isLeaf()); // Empty leaf + } + + for (S32 i = 0; i < branch->getChildCount(); ++i) + { //stretch by child extents + LLVolumeOctreeListener* child = (LLVolumeOctreeListener*)branch->getChild(i)->getListener(0); + min.setMin(min, child->mExtents[0]); + max.setMax(max, child->mExtents[1]); + } + + node->mBounds[0].setAdd(min, max); + node->mBounds[0].mul(0.5f); + + node->mBounds[1].setSub(max, min); + node->mBounds[1].mul(0.5f); + } +}; + +class LLVolumeOctree : public LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>, public LLRefCount +{ +public: + LLVolumeOctree(const LLVector4a& center, const LLVector4a& size) + : + LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, nullptr), + LLRefCount() + { + new LLVolumeOctreeListener(this); + } + + LLVolumeOctree() + : LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(LLVector4a::getZero(), LLVector4a(1.f,1.f,1.f), nullptr), + LLRefCount() + { + new LLVolumeOctreeListener(this); + } +}; + #endif diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 756d0b5db8..f5966b71de 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -582,7 +582,7 @@ void LLPluginProcessParent::idle(void) params.args.add("-e"); params.args.add("tell application \"Terminal\""); params.args.add("-e"); - params.args.add(STRINGIZE("set win to do script \"gdb -pid " + params.args.add(STRINGIZE("set win to do script \"lldb -pid " << mProcess->getProcessID() << "\"")); params.args.add("-e"); params.args.add("do script \"continue\" in win"); diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h index 02f62fb08c..d14ae6970b 100644 --- a/indra/llprimitive/llgltfmaterial.h +++ b/indra/llprimitive/llgltfmaterial.h @@ -129,6 +129,17 @@ public: bool mOverrideDoubleSided = false; bool mOverrideAlphaMode = false; + // *TODO: If/when we implement additional GLTF extensions, they may not be + // compatible with our GLTF terrain implementation. We may want to disallow + // materials with some features from being set on terrain, if their + // implementation on terrain is not compliant with the spec: + // - KHR_materials_transmission: Probably OK? + // - KHR_materials_ior: Probably OK? + // - KHR_materials_volume: Likely incompatible, as our terrain + // heightmaps cannot currently be described as finite enclosed + // volumes. + // See also LLPanelRegionTerrainInfo::validateMaterials + // get a UUID based on a hash of this LLGLTFMaterial LLUUID getHash() const; diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 904747af2d..53d09df618 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1934,6 +1934,19 @@ void LLReflectionProbeParams::setIsDynamic(bool is_dynamic) } } + +void LLReflectionProbeParams::setIsMirror(bool is_mirror) +{ + if (is_mirror) + { + mFlags |= FLAG_MIRROR; + } + else + { + mFlags &= ~FLAG_MIRROR; + } +} + //============================================================================ LLFlexibleObjectData::LLFlexibleObjectData() { diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 0b7dbd703a..5622483861 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -186,6 +186,7 @@ public: { FLAG_BOX_VOLUME = 0x01, // use a box influence volume FLAG_DYNAMIC = 0x02, // render dynamic objects (avatars) into this Reflection Probe + FLAG_MIRROR = 0x04, // This probe is used for reflections on realtime mirrors. }; protected: @@ -209,11 +210,13 @@ public: void setClipDistance(F32 distance) { mClipDistance = llclamp(distance, REFLECTION_PROBE_MIN_CLIP_DISTANCE, REFLECTION_PROBE_MAX_CLIP_DISTANCE); } void setIsBox(bool is_box); void setIsDynamic(bool is_dynamic); + void setIsMirror(bool is_mirror); F32 getAmbiance() const { return mAmbiance; } F32 getClipDistance() const { return mClipDistance; } bool getIsBox() const { return (mFlags & FLAG_BOX_VOLUME) != 0; } bool getIsDynamic() const { return (mFlags & FLAG_DYNAMIC) != 0; } + bool getIsMirror() const { return (mFlags & FLAG_MIRROR) != 0; } }; //------------------------------------------------- diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index 71caff1686..ee2a4c769a 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -685,6 +685,7 @@ S32 LLTextureEntry::setMaterialParams(const LLMaterialPtr pMaterialParams) mMaterialUpdatePending = true; } mMaterial = pMaterialParams; + return TEM_CHANGE_TEXTURE; } diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index f5f2c0172d..0935147688 100644 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -134,7 +134,7 @@ public: S32 setGlow(F32 glow); S32 setMaterialID(const LLMaterialID& pMaterialID); S32 setMaterialParams(const LLMaterialPtr pMaterialParams); - + virtual const LLUUID &getID() const { return mID; } const LLColor4 &getColor() const { return mColor; } const F32 getAlpha() const { return mColor.mV[VALPHA]; } diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index ccfb8f69be..be5ad08fbe 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1184,12 +1184,14 @@ S32 LLGLSLShader::getTextureChannel(S32 uniform) const S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; - + if (uniform < 0 || uniform >= (S32)mTexture.size()) { LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL; return -1; } + + S32 index = mTexture[uniform]; if (index != -1) { diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 43d095f73a..e8529ebadc 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -56,6 +56,8 @@ public: bool hasAlphaMask = false; bool hasReflectionProbes = false; bool attachNothing = false; + bool hasHeroProbes = false; + bool isPBRTerrain = false; }; // ============= Structure for caching shader uniforms =============== diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 24849d1d1b..1cc8fbe523 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -42,7 +42,7 @@ class LLGLTexture : public LLTexture public: enum { - MAX_IMAGE_SIZE_DEFAULT = 1024, + MAX_IMAGE_SIZE_DEFAULT = 2048, INVALID_DISCARD_LEVEL = 0x7fff }; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 88c48e5166..9cd7527d3e 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -567,4 +567,31 @@ bool LLRenderTarget::isBoundInStack() const return cur == this; } +void LLRenderTarget::swapFBORefs(LLRenderTarget& other) +{ + // Must be initialized + llassert(mFBO); + llassert(other.mFBO); + // Must be unbound + // *NOTE: mPreviousRT can be non-null even if this target is unbound - presumably for debugging purposes? + llassert(sCurFBO != mFBO); + llassert(sCurFBO != other.mFBO); + llassert(!isBoundInStack()); + llassert(!other.isBoundInStack()); + + // Must be same type + llassert(sUseFBO == other.sUseFBO); + llassert(mResX == other.mResX); + llassert(mResY == other.mResY); + llassert(mInternalFormat == other.mInternalFormat); + llassert(mTex.size() == other.mTex.size()); + llassert(mDepth == other.mDepth); + llassert(mUseDepth == other.mUseDepth); + llassert(mGenerateMipMaps == other.mGenerateMipMaps); + llassert(mMipLevels == other.mMipLevels); + llassert(mUsage == other.mUsage); + + std::swap(mFBO, other.mFBO); + std::swap(mTex, other.mTex); +} diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index 9fcea35e3d..da401572d1 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -169,6 +169,9 @@ public: static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; } + // *HACK + void swapFBORefs(LLRenderTarget& other); + protected: U32 mResX; U32 mResY; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index f78be910d2..6cfe065355 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -44,6 +44,7 @@ using std::make_pair; using std::string; LLShaderMgr * LLShaderMgr::sInstance = NULL; +bool LLShaderMgr::sMirrorsEnabled = false; LLShaderMgr::LLShaderMgr() { @@ -183,7 +184,13 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) // Attach Fragment Shader Features Next /////////////////////////////////////// -// NOTE order of shader object attaching is VERY IMPORTANT!!! + // NOTE order of shader object attaching is VERY IMPORTANT!!! + + if (!shader->attachFragmentObject("deferred/globalF.glsl")) + { + return FALSE; + } + if (features->hasSrgb || features->hasAtmospherics || features->calculatesAtmospherics || features->isDeferred) { if (!shader->attachFragmentObject("environment/srgbF.glsl")) @@ -277,6 +284,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) } } + if (features->isPBRTerrain) + { + if (!shader->attachFragmentObject("deferred/pbrterrainUtilF.glsl")) + { + return FALSE; + } + } + // NOTE order of shader object attaching is VERY IMPORTANT!!! if (features->hasAtmospherics) { @@ -572,21 +587,38 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev } else { - //set version to 1.40 - shader_code_text[shader_code_count++] = strdup("#version 140\n"); - //some implementations of GLSL 1.30 require integer precision be explicitly declared - extra_code_text[extra_code_count++] = strdup("precision mediump int;\n"); - extra_code_text[extra_code_count++] = strdup("precision highp float;\n"); + if (type == GL_GEOMETRY_SHADER) + { + //set version to 1.50 + shader_code_text[shader_code_count++] = strdup("#version 150\n"); + //some implementations of GLSL 1.30 require integer precision be explicitly declared + extra_code_text[extra_code_count++] = strdup("precision mediump int;\n"); + extra_code_text[extra_code_count++] = strdup("precision highp float;\n"); + } + else + { + //set version to 1.40 + shader_code_text[shader_code_count++] = strdup("#version 140\n"); + //some implementations of GLSL 1.30 require integer precision be explicitly declared + extra_code_text[extra_code_count++] = strdup("precision mediump int;\n"); + extra_code_text[extra_code_count++] = strdup("precision highp float;\n"); + } } extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_130 1\n"); } + if (sMirrorsEnabled) + { + extra_code_text[extra_code_count++] = strdup("#define HERO_PROBES 1\n"); + } + // Use alpha float to store bit flags // See: C++: addDeferredAttachment(), shader: frag_data[2] extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_SKIP_ATMOS 0.0 \n"); // atmo kill extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_ATMOS 0.34\n"); // bit 0 extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_PBR 0.67\n"); // bit 1 + extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_HDRI 1.0\n"); // bit 2 extra_code_text[extra_code_count++] = strdup("#define GET_GBUFFER_FLAG(flag) (abs(norm.w-flag)< 0.1)\n"); if (defines) @@ -1192,6 +1224,9 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("emissiveColor"); mReservedUniforms.push_back("metallicFactor"); mReservedUniforms.push_back("roughnessFactor"); + mReservedUniforms.push_back("mirror_flag"); + mReservedUniforms.push_back("clipPlane"); + mReservedUniforms.push_back("clipSign"); mReservedUniforms.push_back("diffuseMap"); mReservedUniforms.push_back("altDiffuseMap"); @@ -1204,6 +1239,7 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("sceneDepth"); mReservedUniforms.push_back("reflectionProbes"); mReservedUniforms.push_back("irradianceProbes"); + mReservedUniforms.push_back("heroProbes"); mReservedUniforms.push_back("cloud_noise_texture"); mReservedUniforms.push_back("cloud_noise_texture_next"); mReservedUniforms.push_back("fullbright"); @@ -1374,8 +1410,32 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("detail_1"); mReservedUniforms.push_back("detail_2"); mReservedUniforms.push_back("detail_3"); + mReservedUniforms.push_back("alpha_ramp"); + mReservedUniforms.push_back("detail_0_base_color"); + mReservedUniforms.push_back("detail_1_base_color"); + mReservedUniforms.push_back("detail_2_base_color"); + mReservedUniforms.push_back("detail_3_base_color"); + mReservedUniforms.push_back("detail_0_normal"); + mReservedUniforms.push_back("detail_1_normal"); + mReservedUniforms.push_back("detail_2_normal"); + mReservedUniforms.push_back("detail_3_normal"); + mReservedUniforms.push_back("detail_0_metallic_roughness"); + mReservedUniforms.push_back("detail_1_metallic_roughness"); + mReservedUniforms.push_back("detail_2_metallic_roughness"); + mReservedUniforms.push_back("detail_3_metallic_roughness"); + mReservedUniforms.push_back("detail_0_emissive"); + mReservedUniforms.push_back("detail_1_emissive"); + mReservedUniforms.push_back("detail_2_emissive"); + mReservedUniforms.push_back("detail_3_emissive"); + + mReservedUniforms.push_back("baseColorFactors"); + mReservedUniforms.push_back("metallicFactors"); + mReservedUniforms.push_back("roughnessFactors"); + mReservedUniforms.push_back("emissiveColors"); + mReservedUniforms.push_back("minimum_alphas"); + mReservedUniforms.push_back("origin"); mReservedUniforms.push_back("display_gamma"); @@ -1397,6 +1457,7 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("cloud_variance"); mReservedUniforms.push_back("reflection_probe_ambiance"); mReservedUniforms.push_back("max_probe_lod"); + mReservedUniforms.push_back("probe_strength"); mReservedUniforms.push_back("sh_input_r"); mReservedUniforms.push_back("sh_input_g"); @@ -1407,6 +1468,8 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("sun_up_factor"); mReservedUniforms.push_back("moonlight_color"); + mReservedUniforms.push_back("debug_normal_draw_length"); + llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS); std::set<std::string> dupe_check; diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 79a24773e1..eb13418cd7 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -85,6 +85,9 @@ public: EMISSIVE_COLOR, // "emissiveColor" METALLIC_FACTOR, // "metallicFactor" ROUGHNESS_FACTOR, // "roughnessFactor" + MIRROR_FLAG, // "mirror_flag" + CLIP_PLANE, // "clipPlane" + CLIP_SIGN, // "clipSign" DIFFUSE_MAP, // "diffuseMap" ALTERNATE_DIFFUSE_MAP, // "altDiffuseMap" SPECULAR_MAP, // "specularMap" @@ -96,6 +99,7 @@ public: SCENE_DEPTH, // "sceneDepth" REFLECTION_PROBES, // "reflectionProbes" IRRADIANCE_PROBES, // "irradianceProbes" + HERO_PROBE, // "heroProbes" CLOUD_NOISE_MAP, // "cloud_noise_texture" CLOUD_NOISE_MAP_NEXT, // "cloud_noise_texture_next" FULLBRIGHT, // "fullbright" @@ -251,8 +255,32 @@ public: TERRAIN_DETAIL1, // "detail_1" TERRAIN_DETAIL2, // "detail_2" TERRAIN_DETAIL3, // "detail_3" + TERRAIN_ALPHARAMP, // "alpha_ramp" + TERRAIN_DETAIL0_BASE_COLOR, // "detail_0_base_color" (GLTF) + TERRAIN_DETAIL1_BASE_COLOR, // "detail_1_base_color" (GLTF) + TERRAIN_DETAIL2_BASE_COLOR, // "detail_2_base_color" (GLTF) + TERRAIN_DETAIL3_BASE_COLOR, // "detail_3_base_color" (GLTF) + TERRAIN_DETAIL0_NORMAL, // "detail_0_normal" (GLTF) + TERRAIN_DETAIL1_NORMAL, // "detail_1_normal" (GLTF) + TERRAIN_DETAIL2_NORMAL, // "detail_2_normal" (GLTF) + TERRAIN_DETAIL3_NORMAL, // "detail_3_normal" (GLTF) + TERRAIN_DETAIL0_METALLIC_ROUGHNESS, // "detail_0_metallic_roughness" (GLTF) + TERRAIN_DETAIL1_METALLIC_ROUGHNESS, // "detail_1_metallic_roughness" (GLTF) + TERRAIN_DETAIL2_METALLIC_ROUGHNESS, // "detail_2_metallic_roughness" (GLTF) + TERRAIN_DETAIL3_METALLIC_ROUGHNESS, // "detail_3_metallic_roughness" (GLTF) + TERRAIN_DETAIL0_EMISSIVE, // "detail_0_emissive" (GLTF) + TERRAIN_DETAIL1_EMISSIVE, // "detail_1_emissive" (GLTF) + TERRAIN_DETAIL2_EMISSIVE, // "detail_2_emissive" (GLTF) + TERRAIN_DETAIL3_EMISSIVE, // "detail_3_emissive" (GLTF) + + TERRAIN_BASE_COLOR_FACTORS, // "baseColorFactors" (GLTF) + TERRAIN_METALLIC_FACTORS, // "metallicFactors" (GLTF) + TERRAIN_ROUGHNESS_FACTORS, // "roughnessFactors" (GLTF) + TERRAIN_EMISSIVE_COLORS, // "emissiveColors" (GLTF) + TERRAIN_MINIMUM_ALPHAS, // "minimum_alphas" (GLTF) + SHINY_ORIGIN, // "origin" DISPLAY_GAMMA, // "display_gamma" @@ -279,6 +307,7 @@ public: REFLECTION_PROBE_AMBIANCE, // "reflection_probe_ambiance" REFLECTION_PROBE_MAX_LOD, // "max_probe_lod" + REFLECTION_PROBE_STRENGTH, // "probe_strength" SH_INPUT_L1R, // "sh_input_r" SH_INPUT_L1G, // "sh_input_g" SH_INPUT_L1B, // "sh_input_b" @@ -287,6 +316,9 @@ public: WATER_EDGE_FACTOR, // "water_edge" SUN_UP_FACTOR, // "sun_up_factor" MOONLIGHT_COLOR, // "moonlight_color" + + DEBUG_NORMAL_DRAW_LENGTH, // "debug_normal_draw_length" + END_RESERVED_UNIFORMS } eGLSLReservedUniforms; // clang-format on @@ -336,6 +368,7 @@ public: bool mShaderCacheInitialized = false; bool mShaderCacheEnabled = false; std::string mShaderCacheDir; + static bool sMirrorsEnabled; protected: diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index de27636c33..dda3c1532d 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -657,7 +657,7 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto U16 idx = indicesp[i]; gGL.vertex3fv(pos[idx].getF32ptr()); } -} + } gGL.end(); gGL.flush(); } @@ -1319,6 +1319,10 @@ bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, U32 index, { return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index, count); } +bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector4a>& strider, U32 index, S32 count) +{ + return VertexBufferStrider<LLVector4a, TYPE_NORMAL>::get(*this, strider, index, count); +} bool LLVertexBuffer::getTangentStrider(LLStrider<LLVector3>& strider, U32 index, S32 count) { return VertexBufferStrider<LLVector3,TYPE_TANGENT>::get(*this, strider, index, count); diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index f2d146d4bb..cc59e322ed 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -180,6 +180,7 @@ public: bool getTexCoord1Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1); bool getTexCoord2Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1); bool getNormalStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1); + bool getNormalStrider(LLStrider<LLVector4a>& strider, U32 index = 0, S32 count = -1); bool getTangentStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1); bool getTangentStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1); bool getColorStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1); @@ -187,10 +188,6 @@ public: bool getWeightStrider(LLStrider<F32>& strider, U32 index=0, S32 count = -1); bool getWeight4Strider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1); bool getClothWeightStrider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1); - bool getBasecolorTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1); - bool getNormalTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1); - bool getMetallicRoughnessTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1); - bool getEmissiveTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1); void setPositionData(const LLVector4a* data); void setTexCoordData(const LLVector2* data); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 39f6117c69..011dccb910 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -34,6 +34,7 @@ include(LLWindow) include(NDOF) include(NVAPI) include(OPENAL) +include(OpenEXR) include(OpenGL) include(OpenSSL) include(PNG) @@ -72,8 +73,10 @@ if (NOT HAVOK_TPV) endif() endif (NOT HAVOK_TPV) - set(viewer_SOURCE_FILES + gltfscenemanager.cpp + gltf/asset.cpp + gltf/primitive.cpp groupchatlistener.cpp llaccountingcostmanager.cpp llaisapi.cpp @@ -312,6 +315,7 @@ set(viewer_SOURCE_FILES llgiveinventory.cpp llglsandbox.cpp llgltfmateriallist.cpp + llgltfmaterialpreviewmgr.cpp llgroupactions.cpp llgroupiconctrl.cpp llgrouplist.cpp @@ -522,6 +526,7 @@ set(viewer_SOURCE_FILES llrecentpeople.cpp llreflectionmap.cpp llreflectionmapmanager.cpp + llheroprobemanager.cpp llregioninfomodel.cpp llregionposition.cpp llremoteparcelrequest.cpp @@ -725,7 +730,10 @@ set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake + gltfscenemanager.h groupchatlistener.h + gltf/asset.h + gltf/primitive.h llaccountingcost.h llaccountingcostmanager.h llaisapi.h @@ -967,6 +975,7 @@ set(viewer_HEADER_FILES llgesturemgr.h llgiveinventory.h llgltfmateriallist.h + llgltfmaterialpreviewmgr.h llgroupactions.h llgroupiconctrl.h llgrouplist.h @@ -1163,6 +1172,7 @@ set(viewer_HEADER_FILES llrecentpeople.h llreflectionmap.h llreflectionmapmanager.h + llheroprobemanager.h llregioninfomodel.h llregionposition.h llremoteparcelrequest.h @@ -1740,6 +1750,12 @@ if (WINDOWS) media_plugin_cef media_plugin_libvlc media_plugin_example + ${SHARED_LIB_STAGING_DIR}/Iex-3_2.dll + ${SHARED_LIB_STAGING_DIR}/IlmThread-3_2.dll + ${SHARED_LIB_STAGING_DIR}/Imath-3_1.dll + ${SHARED_LIB_STAGING_DIR}/OpenEXR-3_2.dll + ${SHARED_LIB_STAGING_DIR}/OpenEXRCore-3_2.dll + ${SHARED_LIB_STAGING_DIR}/OpenEXRUtil-3_2.dll ) if (ADDRESS_SIZE EQUAL 64) @@ -1933,6 +1949,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ll::bugsplat ll::tracy ll::icu4c + ll::openexr ) if( TARGET ll::intel_memops ) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2f7c256b49..e42906e78b 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8732,7 +8732,7 @@ <key>OctreeAlphaDistanceFactor</key> <map> <key>Comment</key> - <string>Multiplier on alpha object distance for determining octree node size </string> + <string>Multiplier on alpha object distance for determining octree node size. First two parameters are currently unused. Third parameter is distance at which to perform detailed alpha sorting.</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -8741,7 +8741,7 @@ <array> <real>0.1</real> <real>0.0</real> - <real>0.0</real> + <real>64.0</real> </array> </map> @@ -8861,17 +8861,6 @@ <key>Value</key> <real>32.0</real> </map> - <key>RenderCloudShadowAmbianceFactor</key> - <map> - <key>Comment</key> - <string>Amount that cloud shadow (aka cloud coverage) contributes to reflection probe ambiance</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>0.1</real> - </map> <key>RenderCPUBasis</key> <map> <key>Comment</key> @@ -9186,6 +9175,17 @@ <real>0.00</real> </array> </map> + <key>RenderMirrors</key> + <map> + <key>Comment</key> + <string>Renders realtime mirrors.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RenderScreenSpaceReflections</key> <map> <key>Comment</key> @@ -9296,6 +9296,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>RenderDesaturateIrradiance</key> + <map> + <key>Comment</key> + <string>Desaturate irradiance to remove blue tint</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>RenderDebugAlphaMask</key> <map> <key>Comment</key> @@ -9362,6 +9373,50 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderHDRIExposure</key> + <map> + <key>Comment</key> + <string>Exposure adjustment of HDRI when previewing an HDRI. Units are EV. Sane values would be -10 to 10.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>RenderHDRIRotation</key> + <map> + <key>Comment</key> + <string>Rotation (in degrees) of environment when previewing an HDRI.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>RenderHDRISplitScreen</key> + <map> + <key>Comment</key> + <string>What percentage of screen to render using HDRI vs EEP sky.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>RenderHDRIIrradianceOnly</key> + <map> + <key>Comment</key> + <string>Only use HDRI sky for irradiance map when RenderHDRISplitScreen is 0</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RenderMaxOpenGLVersion</key> <map> <key>Comment</key> @@ -9395,6 +9450,17 @@ <key>Value</key> <integer>16</integer> </map> + <key>RenderMaxTextureResolution</key> + <map> + <key>Comment</key> + <string>Maximum texture resolution to download for non-boosted textures.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>2048</integer> + </map> <key>RenderDebugTextureBind</key> <map> <key>Comment</key> @@ -10390,6 +10456,50 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderHeroProbeResolution</key> + <map> + <key>Comment</key> + <string>Resolution to render hero probes used for mirrors, water, etc.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>1024</integer> + </map> + <key>RenderHeroProbeDistance</key> + <map> + <key>Comment</key> + <string>Distance in meters for hero probes to render out to.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>8</real> + </map> + <key>RenderHeroProbeUpdateRate</key> + <map> + <key>Comment</key> + <string>How many frames to wait for until it's time to render the probe. E.g., every other frame (1), every two frames (2), every three frames (3) etc.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>2</integer> + </map> + <key>RenderHeroProbeConservativeUpdateMultiplier</key> + <map> + <key>Comment</key> + <string>How many probe updates to wait until it's time to update faces that are not directly facing the camera. Acts as a multiplier. E.g., frames to the periphery of the camera updating once every 3 updates, vs ones directly facing the camera updating every update.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>16</integer> + </map> <key>RenderReflectionProbeVolumes</key> <map> <key>Comment</key> @@ -10756,6 +10866,17 @@ <key>Value</key> <real>0.5</real> </map> + <key>RenderDiffuseLuminanceScale</key> + <map> + <key>Comment</key> + <string>Luminance adjustment for diffuse surfaces to aid auto-exposure behavior</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.5</real> + </map> <key>RenderShaderLODThreshold</key> <map> <key>Comment</key> @@ -10814,7 +10935,7 @@ <key>RenderTerrainScale</key> <map> <key>Comment</key> - <string>Terrain detail texture scale</string> + <string>Terrain detail texture scale (meters)</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -10822,6 +10943,83 @@ <key>Value</key> <real>12.0</real> </map> + <key>RenderTerrainPBREnabled</key> + <map> + <key>Comment</key> + <string>EXPERIMENTAL: Enable PBR Terrain features.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>RenderTerrainPBRForce</key> + <map> + <key>Comment</key> + <string>Force-load PBR terrain if enabled</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>RenderTerrainPBRDetail</key> + <map> + <key>Comment</key> + <string>Detail level for PBR terrain. 0 is full detail. Negative values drop rendering features, in accordance with the GLTF specification when possible, which reduces the number of texture binds.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>RenderTerrainPBRScale</key> + <map> + <key>Comment</key> + <string>PBR terrain detail texture scale (meters)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>8.0</real> + </map> + <key>RenderTerrainPBRPlanarSampleCount</key> + <map> + <key>Comment</key> + <string>How many UV planes to sample PBR terrain textures from. 1 is "flat", 3 is triplanar mapping (aka box mapping)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <real>3</real> + </map> + <key>RenderTerrainPBRTriplanarBlendFactor</key> + <map> + <key>Comment</key> + <string>Higher values create sharper transitions, but are more likely to produce artifacts.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>8.0</real> + </map> + <key>RenderTerrainPBRNormalsEnabled</key> + <map> + <key>Comment</key> + <string>EXPERIMENTAL: Change normal gen for PBR Terrain.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RenderTrackerBeacon</key> <map> <key>Comment</key> @@ -13752,6 +13950,17 @@ <key>Value</key> <integer>2</integer> </map> + <key>UIPreviewMaterial</key> + <map> + <key>Comment</key> + <string>Whether or not PBR material swatch is enabled</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <real>0</real> + </map> <key>UIResizeBarHeight</key> <map> <key>Comment</key> @@ -15667,7 +15876,7 @@ <key>Type</key> <string>S32</string> <key>Value</key> - <integer>2048</integer> + <integer>1024</integer> </map> <key>max_texture_dimension_Y</key> <map> @@ -15678,7 +15887,7 @@ <key>Type</key> <string>S32</string> <key>Value</key> - <integer>2048</integer> + <integer>1024</integer> </map> <!-- End of back compatibility settings --> <key>teleport_offer_invitation_max_length</key> @@ -16554,6 +16763,50 @@ <key>Value</key> <integer>0</integer> </map> + <key>LocalTerrainAsset1</key> + <map> + <key>Comment</key> + <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>00000000-0000-0000-0000-000000000000</string> + </map> + <key>LocalTerrainAsset2</key> + <map> + <key>Comment</key> + <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>00000000-0000-0000-0000-000000000000</string> + </map> + <key>LocalTerrainAsset3</key> + <map> + <key>Comment</key> + <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>00000000-0000-0000-0000-000000000000</string> + </map> + <key>LocalTerrainAsset4</key> + <map> + <key>Comment</key> + <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>00000000-0000-0000-0000-000000000000</string> + </map> <key>PathfindingRetrieveNeighboringRegion</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl index 83b0ba096c..43863dd37a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl @@ -26,6 +26,7 @@ uniform mat3 normal_matrix; uniform mat4 texture_matrix0; uniform mat4 modelview_projection_matrix; +uniform mat4 modelview_matrix; in vec3 position; in vec3 normal; @@ -35,10 +36,12 @@ in vec2 texcoord0; out vec3 vary_normal; out vec4 vertex_color; out vec2 vary_texcoord0; +out vec3 vary_position; void main() { //transform vertex + vary_position = (modelview_matrix * vec4(position.xyz, 1.0)).xyz; gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl index 6ebe4ce251..c0607d0149 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl @@ -33,11 +33,15 @@ uniform float minimum_alpha; in vec3 vary_normal; in vec2 vary_texcoord0; +in vec3 vary_position; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 pos); void main() { + mirrorClip(vary_position); + vec4 diff = texture(diffuseMap, vary_texcoord0.xy); if (diff.a < minimum_alpha) diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl index bc0c11ec46..5cc2846156 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl @@ -35,6 +35,7 @@ in vec4 weight; out vec3 vary_normal; out vec2 vary_texcoord0; +out vec3 vary_position; void main() { @@ -57,6 +58,7 @@ void main() vary_normal = norm; + vary_position = pos.xyz; gl_Position = projection_matrix * pos; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl index 35f483f633..a22c174349 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl @@ -37,11 +37,15 @@ in vec3 vary_mat2; in vec4 vertex_color; in vec2 vary_texcoord0; +in vec3 vary_position; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 pos); void main() { + mirrorClip(vary_position); + vec4 col = texture(diffuseMap, vary_texcoord0.xy); if(col.a < minimum_alpha) diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl index 3af2eab1e4..74319349f6 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl @@ -23,6 +23,7 @@ * $/LicenseInfo$ */ +uniform mat4 modelview_matrix; uniform mat3 normal_matrix; uniform mat4 texture_matrix0; uniform mat4 modelview_projection_matrix; @@ -38,11 +39,11 @@ out vec3 vary_mat1; out vec3 vary_mat2; out vec4 vertex_color; out vec2 vary_texcoord0; +out vec3 vary_position; #ifdef HAS_SKIN mat4 getObjectSkinnedTransform(); uniform mat4 projection_matrix; -uniform mat4 modelview_matrix; #endif void main() @@ -52,11 +53,13 @@ void main() mat4 mat = getObjectSkinnedTransform(); mat = modelview_matrix * mat; vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz; + vary_position = pos; gl_Position = projection_matrix*vec4(pos, 1.0); vec3 n = normalize((mat * vec4(normal.xyz+position.xyz, 1.0)).xyz-pos.xyz); vec3 t = normalize((mat * vec4(tangent.xyz+position.xyz, 1.0)).xyz-pos.xyz); #else + vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz; gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); vec3 n = normalize(normal_matrix * normal); vec3 t = normalize(normal_matrix * tangent.xyz); diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl index 6f3b94f734..e8ead91504 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl @@ -31,14 +31,20 @@ uniform float minimum_alpha; uniform sampler2D diffuseMap; +in vec3 vary_position; + in vec3 vary_normal; in vec4 vertex_color; in vec2 vary_texcoord0; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 pos); + void main() { + mirrorClip(vary_position); + vec4 col = texture(diffuseMap, vary_texcoord0.xy) * vertex_color; if (col.a < minimum_alpha) diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl index e5f2af2c53..a07c892d8e 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl @@ -28,6 +28,7 @@ out vec4 frag_data[4]; in vec3 vary_normal; +in vec3 vary_position; uniform float minimum_alpha; @@ -36,8 +37,12 @@ in vec2 vary_texcoord0; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 pos); + void main() { + mirrorClip(vary_position); + vec4 col = diffuseLookup(vary_texcoord0.xy) * vertex_color; if (col.a < minimum_alpha) diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl index 68fb8bf499..76776ede2c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl @@ -32,11 +32,14 @@ uniform sampler2D diffuseMap; in vec3 vary_normal; in vec4 vertex_color; in vec2 vary_texcoord0; +in vec3 vary_position; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 pos); void main() { + mirrorClip(vary_position); vec3 col = vertex_color.rgb * texture(diffuseMap, vary_texcoord0.xy).rgb; frag_data[0] = vec4(col, 0.0); frag_data[1] = vertex_color.aaaa; // spec diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl index 93d561504e..b983acf657 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl @@ -30,12 +30,15 @@ out vec4 frag_data[4]; in vec3 vary_normal; in vec4 vertex_color; in vec2 vary_texcoord0; +in vec3 vary_position; +void mirrorClip(vec3 pos); vec2 encode_normal(vec3 n); vec3 linear_to_srgb(vec3 c); void main() { + mirrorClip(vary_position); vec3 col = vertex_color.rgb * diffuseLookup(vary_texcoord0.xy).rgb; vec3 spec; diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl index 2402cc3b70..64230dc680 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl @@ -36,13 +36,16 @@ out vec3 vary_normal; out vec4 vertex_color; out vec2 vary_texcoord0; +out vec3 vary_position; void passTextureIndex(); +uniform mat4 modelview_matrix; + #ifdef HAS_SKIN mat4 getObjectSkinnedTransform(); uniform mat4 projection_matrix; -uniform mat4 modelview_matrix; + #endif void main() @@ -51,9 +54,11 @@ void main() mat4 mat = getObjectSkinnedTransform(); mat = modelview_matrix * mat; vec4 pos = mat * vec4(position.xyz, 1.0); + vary_position = pos.xyz; gl_Position = projection_matrix * pos; vary_normal = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz); #else + vary_position = (modelview_matrix * vec4(position.xyz, 1.0)).xyz; gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); vary_normal = normalize(normal_matrix * normal); #endif diff --git a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl index 9ac4ceb37e..eff7221ae7 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl @@ -28,7 +28,9 @@ out vec4 frag_color; uniform sampler2D emissiveRect; +#ifdef USE_LAST_EXPOSURE uniform sampler2D exposureMap; +#endif uniform float dt; uniform vec2 noiseVec; @@ -51,10 +53,12 @@ void main() L /= max_L; L = pow(L, 2.0); float s = mix(dynamic_exposure_params.z, dynamic_exposure_params.y, L); - + +#ifdef USE_LAST_EXPOSURE float prev = texture(exposureMap, vec2(0.5,0.5)).r; s = mix(prev, s, min(dt*2.0*abs(prev-s), 0.04)); +#endif frag_color = max(vec4(s, s, s, dt), vec4(0.0)); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl index a6fab10791..52dfed06ae 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl @@ -50,9 +50,11 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou vec4 applySkyAndWaterFog(vec3 pos, vec3 additive, vec3 atten, vec4 color); #endif +void mirrorClip(vec3 pos); + void main() { - + mirrorClip(vary_position); #ifdef IS_ALPHA waterClip(vary_position.xyz); #endif diff --git a/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl b/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl new file mode 100644 index 0000000000..7e3e7d9271 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl @@ -0,0 +1,45 @@ +/** + * @file class1/deferred/globalF.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$ + */ + + + // Global helper functions included in every fragment shader + // DO NOT declare sampler uniforms here as OS X doesn't compile + // them out + +uniform float mirror_flag; +uniform vec4 clipPlane; +uniform float clipSign; + +void mirrorClip(vec3 pos) +{ + if (mirror_flag > 0) + { + if ((dot(pos.xyz, clipPlane.xyz) + clipPlane.w) < 0.0) + { + discard; + } + } +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl index c8afacf9bb..95b2f80e06 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl @@ -34,6 +34,8 @@ in vec2 vary_fragcoord; uniform sampler2D diffuseRect; uniform sampler2D emissiveRect; +uniform sampler2D normalMap; +uniform float diffuse_luminance_scale; float lum(vec3 col) { @@ -45,7 +47,21 @@ void main() { vec2 tc = vary_fragcoord*0.6+0.2; tc.y -= 0.1; // HACK - nudge exposure sample down a little bit to favor ground over sky - vec3 c = texture(diffuseRect, tc).rgb + texture(emissiveRect, tc).rgb; + vec3 c = texture(diffuseRect, tc).rgb; + + vec4 norm = texture(normalMap, tc); + + if (!GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_HDRI) && + !GET_GBUFFER_FLAG(GBUFFER_FLAG_SKIP_ATMOS)) + { + // Apply the diffuse luminance scale to objects but not the sky + // Prevents underexposing when looking at bright environments + // while still allowing for realistically bright skies. + c *= diffuse_luminance_scale; + } + + c += texture(emissiveRect, tc).rgb; + float L = lum(c); frag_color = vec4(max(L, 0.0)); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl index 7cdddfe8db..ddf878ae60 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl @@ -28,25 +28,18 @@ #define DIFFUSE_ALPHA_MODE_MASK 2 #define DIFFUSE_ALPHA_MODE_EMISSIVE 3 -#ifdef HAS_SKIN uniform mat4 modelview_matrix; uniform mat4 projection_matrix; +uniform mat4 modelview_projection_matrix; + +#ifdef HAS_SKIN mat4 getObjectSkinnedTransform(); #else uniform mat3 normal_matrix; -uniform mat4 modelview_projection_matrix; -#endif - -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) - -#if !defined(HAS_SKIN) -uniform mat4 modelview_matrix; #endif out vec3 vary_position; -#endif - uniform mat4 texture_matrix0; in vec3 position; @@ -85,9 +78,7 @@ void main() vec3 pos = (mat*vec4(position.xyz,1.0)).xyz; -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) vary_position = pos; -#endif gl_Position = projection_matrix*vec4(pos,1.0); @@ -133,10 +124,8 @@ void main() vertex_color = diffuse_color; -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) #if !defined(HAS_SKIN) vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz; #endif -#endif } diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl index faa273b834..0683236460 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl @@ -58,10 +58,17 @@ vec2 encode_normal(vec3 n); vec3 linear_to_srgb(vec3 c); vec3 srgb_to_linear(vec3 c); +uniform vec4 clipPlane; +uniform float clipSign; + +void mirrorClip(vec3 pos); + uniform mat3 normal_matrix; void main() { + mirrorClip(vary_position); + vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba; if (basecolor.a < minimum_alpha) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl index 160ae7a215..5e0141910b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl @@ -28,8 +28,9 @@ //deferred opaque implementation -#ifdef HAS_SKIN uniform mat4 modelview_matrix; + +#ifdef HAS_SKIN uniform mat4 projection_matrix; mat4 getObjectSkinnedTransform(); #else @@ -59,6 +60,7 @@ out vec4 vertex_color; out vec3 vary_tangent; flat out float vary_sign; out vec3 vary_normal; +out vec3 vary_position; vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform); vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform); @@ -71,10 +73,11 @@ void main() mat = modelview_matrix * mat; vec3 pos = (mat*vec4(position.xyz,1.0)).xyz; - + vary_position = pos; gl_Position = projection_matrix*vec4(pos,1.0); #else + vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz; //transform vertex gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); #endif diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl new file mode 100644 index 0000000000..57c0a6024f --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl @@ -0,0 +1,348 @@ +/** + * @file class1\deferred\terrainF.glsl + * + * $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$ + */ + +/*[EXTRA_CODE_HERE]*/ + +#define TERRAIN_PBR_DETAIL_EMISSIVE 0 +#define TERRAIN_PBR_DETAIL_OCCLUSION -1 +#define TERRAIN_PBR_DETAIL_NORMAL -2 +#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3 + +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 +#define TerrainCoord vec4[2] +#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 +#define TerrainCoord vec2 +#endif + +#define MIX_X 1 << 3 +#define MIX_Y 1 << 4 +#define MIX_Z 1 << 5 +#define MIX_W 1 << 6 + +struct TerrainMix +{ + vec4 weight; + int type; +}; + +TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal); + +struct PBRMix +{ + vec4 col; // RGB color with alpha, linear space +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + vec3 orm; // Occlusion, roughness, metallic +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + vec2 rm; // Roughness, metallic +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + vec3 vNt; // Unpacked normal texture sample, vector +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + vec3 emissive; // RGB emissive color, linear space +#endif +}; + +PBRMix init_pbr_mix(); + +PBRMix terrain_sample_and_multiply_pbr( + TerrainCoord terrain_coord + , sampler2D tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , sampler2D tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , sampler2D tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , sampler2D tex_emissive +#endif + , vec4 factor_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , vec3 factor_orm +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , vec2 factor_rm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , vec3 factor_emissive +#endif + ); + +PBRMix mix_pbr(PBRMix mix1, PBRMix mix2, float mix2_weight); + +out vec4 frag_data[4]; + +uniform sampler2D alpha_ramp; + +// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures +uniform sampler2D detail_0_base_color; +uniform sampler2D detail_1_base_color; +uniform sampler2D detail_2_base_color; +uniform sampler2D detail_3_base_color; +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) +uniform sampler2D detail_0_normal; +uniform sampler2D detail_1_normal; +uniform sampler2D detail_2_normal; +uniform sampler2D detail_3_normal; +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) +uniform sampler2D detail_0_metallic_roughness; +uniform sampler2D detail_1_metallic_roughness; +uniform sampler2D detail_2_metallic_roughness; +uniform sampler2D detail_3_metallic_roughness; +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) +uniform sampler2D detail_0_emissive; +uniform sampler2D detail_1_emissive; +uniform sampler2D detail_2_emissive; +uniform sampler2D detail_3_emissive; +#endif + +uniform vec4[4] baseColorFactors; // See also vertex_color in pbropaqueV.glsl +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) +uniform vec4 metallicFactors; +uniform vec4 roughnessFactors; +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) +uniform vec3[4] emissiveColors; +#endif +uniform vec4 minimum_alphas; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlphaCutoff() + +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 +in vec4[2] vary_coords; +#endif +in vec3 vary_position; +in vec3 vary_normal; +in vec3 vary_tangent; +flat in float vary_sign; +in vec4 vary_texcoord0; +in vec4 vary_texcoord1; + +vec2 encode_normal(vec3 n); +void mirrorClip(vec3 position); + +float terrain_mix(TerrainMix tm, vec4 tms4); + +void main() +{ + // Make sure we clip the terrain if we're in a mirror. + mirrorClip(vary_position); + +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + TerrainCoord terrain_texcoord = vary_coords; +#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 + TerrainCoord terrain_texcoord = vary_texcoord0.xy; +#endif + + float alpha1 = texture(alpha_ramp, vary_texcoord0.zw).a; + float alpha2 = texture(alpha_ramp,vary_texcoord1.xy).a; + float alphaFinal = texture(alpha_ramp, vary_texcoord1.zw).a; + + TerrainMix tm = get_terrain_mix_weights(alpha1, alpha2, alphaFinal); + +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + // RGB = Occlusion, Roughness, Metal + // default values, see LLViewerTexture::sDefaultPBRORMImagep + // occlusion 1.0 + // roughness 0.0 + // metal 0.0 + vec3[4] orm_factors; + orm_factors[0] = vec3(1.0, roughnessFactors.x, metallicFactors.x); + orm_factors[1] = vec3(1.0, roughnessFactors.y, metallicFactors.y); + orm_factors[2] = vec3(1.0, roughnessFactors.z, metallicFactors.z); + orm_factors[3] = vec3(1.0, roughnessFactors.w, metallicFactors.w); +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + vec2[4] rm_factors; + rm_factors[0] = vec2(roughnessFactors.x, metallicFactors.x); + rm_factors[1] = vec2(roughnessFactors.y, metallicFactors.y); + rm_factors[2] = vec2(roughnessFactors.z, metallicFactors.z); + rm_factors[3] = vec2(roughnessFactors.w, metallicFactors.w); +#endif + + PBRMix mix = init_pbr_mix(); + PBRMix mix2; + switch (tm.type & MIX_X) + { + case MIX_X: + mix2 = terrain_sample_and_multiply_pbr( + terrain_texcoord + , detail_0_base_color +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , detail_0_metallic_roughness +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , detail_0_normal +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , detail_0_emissive +#endif + , baseColorFactors[0] +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , orm_factors[0] +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , rm_factors[0] +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , emissiveColors[0] +#endif + ); + mix = mix_pbr(mix, mix2, tm.weight.x); + break; + default: + break; + } + switch (tm.type & MIX_Y) + { + case MIX_Y: + mix2 = terrain_sample_and_multiply_pbr( + terrain_texcoord + , detail_1_base_color +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , detail_1_metallic_roughness +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , detail_1_normal +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , detail_1_emissive +#endif + , baseColorFactors[1] +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , orm_factors[1] +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , rm_factors[1] +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , emissiveColors[1] +#endif + ); + mix = mix_pbr(mix, mix2, tm.weight.y); + break; + default: + break; + } + switch (tm.type & MIX_Z) + { + case MIX_Z: + mix2 = terrain_sample_and_multiply_pbr( + terrain_texcoord + , detail_2_base_color +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , detail_2_metallic_roughness +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , detail_2_normal +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , detail_2_emissive +#endif + , baseColorFactors[2] +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , orm_factors[2] +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , rm_factors[2] +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , emissiveColors[2] +#endif + ); + mix = mix_pbr(mix, mix2, tm.weight.z); + break; + default: + break; + } + switch (tm.type & MIX_W) + { + case MIX_W: + mix2 = terrain_sample_and_multiply_pbr( + terrain_texcoord + , detail_3_base_color +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , detail_3_metallic_roughness +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , detail_3_normal +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , detail_3_emissive +#endif + , baseColorFactors[3] +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , orm_factors[3] +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , rm_factors[3] +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , emissiveColors[3] +#endif + ); + mix = mix_pbr(mix, mix2, tm.weight.w); + break; + default: + break; + } + + float minimum_alpha = terrain_mix(tm, minimum_alphas); + if (mix.col.a < minimum_alpha) + { + discard; + } + float base_color_factor_alpha = terrain_mix(tm, vec4(baseColorFactors[0].z, baseColorFactors[1].z, baseColorFactors[2].z, baseColorFactors[3].z)); + +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + // from mikktspace.com + vec3 vNt = mix.vNt; + vec3 vN = vary_normal; + vec3 vT = vary_tangent.xyz; + + vec3 vB = vary_sign * cross(vN, vT); + vec3 tnorm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); + + tnorm *= gl_FrontFacing ? 1.0 : -1.0; +#else + vec3 tnorm = vary_normal; + tnorm *= gl_FrontFacing ? 1.0 : -1.0; +#endif + + +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) +#define emissive mix.emissive +#else +#define emissive vec3(0) +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) +#define orm mix.orm +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) +#define orm vec3(1.0, mix.rm) +#else +// Matte plastic potato terrain +#define orm vec3(1.0, 1.0, 0.0) +#endif + frag_data[0] = max(vec4(mix.col.xyz, 0.0), vec4(0)); // Diffuse + frag_data[1] = max(vec4(orm.rgb, base_color_factor_alpha), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal. + frag_data[2] = max(vec4(encode_normal(tnorm), base_color_factor_alpha, GBUFFER_FLAG_HAS_PBR), vec4(0)); // normal, environment intensity, flags + frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl new file mode 100644 index 0000000000..935c3f9301 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl @@ -0,0 +1,473 @@ +/** + * @file class1\deferred\pbrterrainUtilF.glsl + * + * $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$ + */ + +/*[EXTRA_CODE_HERE]*/ + +/** + * Triplanar mapping implementation adapted from Inigo Quilez' example shader, + * MIT license. + * https://www.shadertoy.com/view/MtsGWH + * Copyright © 2015 Inigo Quilez + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: The above copyright + * notice and this permission notice shall be included in all copies or + * substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", + * WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define TERRAIN_PBR_DETAIL_EMISSIVE 0 +#define TERRAIN_PBR_DETAIL_OCCLUSION -1 +#define TERRAIN_PBR_DETAIL_NORMAL -2 +#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3 + +in vec3 vary_vertex_normal; + +vec3 srgb_to_linear(vec3 c); + +// A relatively agressive threshold for terrain material mixing sampling +// cutoff. This ensures that only one or two materials are used in most places, +// making PBR terrain blending more performant. Should be greater than 0 to work. +#define TERRAIN_RAMP_MIX_THRESHOLD 0.1 +// A small threshold for triplanar mapping sampling cutoff. This and +// TERRAIN_TRIPLANAR_BLEND_FACTOR together ensures that only one or two samples +// per texture are used in most places, making triplanar mapping more +// performant. Should be greater than 0 to work. +// There's also an artistic design choice in the use of these factors, and the +// use of triplanar generally. Don't take these triplanar constants for granted. +#define TERRAIN_TRIPLANAR_MIX_THRESHOLD 0.01 + +#define SAMPLE_X 1 << 0 +#define SAMPLE_Y 1 << 1 +#define SAMPLE_Z 1 << 2 +#define MIX_X 1 << 3 +#define MIX_Y 1 << 4 +#define MIX_Z 1 << 5 +#define MIX_W 1 << 6 + +struct PBRMix +{ + vec4 col; // RGB color with alpha, linear space +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + vec3 orm; // Occlusion, roughness, metallic +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + vec2 rm; // Roughness, metallic +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + vec3 vNt; // Unpacked normal texture sample, vector +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + vec3 emissive; // RGB emissive color, linear space +#endif +}; + +PBRMix init_pbr_mix() +{ + PBRMix mix; + mix.col = vec4(0); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + mix.orm = vec3(0); +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + mix.rm = vec2(0); +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix.vNt = vec3(0); +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + mix.emissive = vec3(0); +#endif + return mix; +} + +// Usage example, for two weights: +// vec2 weights = ... // Weights must add up to 1 +// PBRMix mix = init_pbr_mix(); +// PBRMix mix1 = ... +// mix = mix_pbr(mix, mix1, weights.x); +// PBRMix mix2 = ... +// mix = mix_pbr(mix, mix2, weights.y); +PBRMix mix_pbr(PBRMix mix1, PBRMix mix2, float mix2_weight) +{ + PBRMix mix; + mix.col = mix1.col + (mix2.col * mix2_weight); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + mix.orm = mix1.orm + (mix2.orm * mix2_weight); +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + mix.rm = mix1.rm + (mix2.rm * mix2_weight); +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix.vNt = mix1.vNt + (mix2.vNt * mix2_weight); +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + mix.emissive = mix1.emissive + (mix2.emissive * mix2_weight); +#endif + return mix; +} + +PBRMix sample_pbr( + vec2 uv + , sampler2D tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , sampler2D tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , sampler2D tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , sampler2D tex_emissive +#endif + ) +{ + PBRMix mix; + mix.col = texture(tex_col, uv); + mix.col.rgb = srgb_to_linear(mix.col.rgb); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + mix.orm = texture(tex_orm, uv).xyz; +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + mix.rm = texture(tex_orm, uv).yz; +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix.vNt = texture(tex_vNt, uv).xyz*2.0-1.0; +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + mix.emissive = srgb_to_linear(texture(tex_emissive, uv).xyz); +#endif + return mix; +} + +struct TerrainTriplanar +{ + vec3 weight; + int type; +}; + +struct TerrainMix +{ + vec4 weight; + int type; +}; + +#define TerrainMixSample vec4[4] +#define TerrainMixSample3 vec3[4] + +TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal) +{ + TerrainMix tm; + vec4 sample_x = vec4(1,0,0,0); + vec4 sample_y = vec4(0,1,0,0); + vec4 sample_z = vec4(0,0,1,0); + vec4 sample_w = vec4(0,0,0,1); + + tm.weight = mix( mix(sample_w, sample_z, alpha2), mix(sample_y, sample_x, alpha1), alphaFinal ); + tm.weight -= TERRAIN_RAMP_MIX_THRESHOLD; + ivec4 usage = max(ivec4(0), ivec4(ceil(tm.weight))); + // Prevent negative weights and keep weights balanced + tm.weight = tm.weight*vec4(usage); + tm.weight /= (tm.weight.x + tm.weight.y + tm.weight.z + tm.weight.w); + + tm.type = (usage.x * MIX_X) | + (usage.y * MIX_Y) | + (usage.z * MIX_Z) | + (usage.w * MIX_W); + return tm; +} + +TerrainTriplanar _t_triplanar() +{ + float sharpness = TERRAIN_TRIPLANAR_BLEND_FACTOR; + float threshold = TERRAIN_TRIPLANAR_MIX_THRESHOLD; + vec3 weight_signed = pow(abs(vary_vertex_normal), vec3(sharpness)); + weight_signed /= (weight_signed.x + weight_signed.y + weight_signed.z); + weight_signed -= vec3(threshold); + TerrainTriplanar tw; + // *NOTE: Make sure the threshold doesn't affect the materials + tw.weight = max(vec3(0), weight_signed); + tw.weight /= (tw.weight.x + tw.weight.y + tw.weight.z); + ivec3 usage = ivec3(round(max(vec3(0), sign(weight_signed)))); + tw.type = ((usage.x) * SAMPLE_X) | + ((usage.y) * SAMPLE_Y) | + ((usage.z) * SAMPLE_Z); + return tw; +} + +// Assume weights add to 1 +float terrain_mix(TerrainMix tm, vec4 tms4) +{ + return (tm.weight.x * tms4[0]) + + (tm.weight.y * tms4[1]) + + (tm.weight.z * tms4[2]) + + (tm.weight.w * tms4[3]); +} + +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 +// Triplanar mapping + +// Pre-transformed texture coordinates for each axial uv slice (Packing: xy, yz, (-x)z, unused) +#define TerrainCoord vec4[2] + +vec2 _t_uv(vec2 uv_unflipped, float sign_or_zero) +{ + // Handle case where sign is 0 + float sign = (2.0*sign_or_zero) + 1.0; + sign /= abs(sign); + // If the vertex normal is negative, flip the texture back + // right-side up. + vec2 uv = uv_unflipped * vec2(sign, 1); + return uv; +} + +vec3 _t_normal_post_1(vec3 vNt0, float sign_or_zero) +{ + // Assume normal is unpacked + vec3 vNt1 = vNt0; + // Get sign + float sign = sign_or_zero; + // Handle case where sign is 0 + sign = (2.0*sign) + 1.0; + sign /= abs(sign); + // If the sign is negative, rotate normal by 180 degrees + vNt1.xy = (min(0, sign) * vNt1.xy) + (min(0, -sign) * -vNt1.xy); + return vNt1; +} + +// Triplanar-specific normal texture fixes +vec3 _t_normal_post_x(vec3 vNt0) +{ + vec3 vNt_x = _t_normal_post_1(vNt0, sign(vary_vertex_normal.x)); + // *HACK: Transform normals according to orientation of the UVs + vNt_x.xy = vec2(-vNt_x.y, vNt_x.x); + return vNt_x; +} +vec3 _t_normal_post_y(vec3 vNt0) +{ + vec3 vNt_y = _t_normal_post_1(vNt0, sign(vary_vertex_normal.y)); + // *HACK: Transform normals according to orientation of the UVs + vNt_y.xy = -vNt_y.xy; + return vNt_y; +} +vec3 _t_normal_post_z(vec3 vNt0) +{ + vec3 vNt_z = _t_normal_post_1(vNt0, sign(vary_vertex_normal.z)); + return vNt_z; +} + +PBRMix terrain_sample_pbr( + TerrainCoord terrain_coord + , TerrainTriplanar tw + , sampler2D tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , sampler2D tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , sampler2D tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , sampler2D tex_emissive +#endif + ) +{ + PBRMix mix = init_pbr_mix(); + +#define get_uv_x() _t_uv(terrain_coord[0].zw, sign(vary_vertex_normal.x)) +#define get_uv_y() _t_uv(terrain_coord[1].xy, sign(vary_vertex_normal.y)) +#define get_uv_z() _t_uv(terrain_coord[0].xy, sign(vary_vertex_normal.z)) + switch (tw.type & SAMPLE_X) + { + case SAMPLE_X: + PBRMix mix_x = sample_pbr( + get_uv_x() + , tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , tex_emissive +#endif + ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + // Triplanar-specific normal texture fix + mix_x.vNt = _t_normal_post_x(mix_x.vNt); +#endif + mix = mix_pbr(mix, mix_x, tw.weight.x); + break; + default: + break; + } + + switch (tw.type & SAMPLE_Y) + { + case SAMPLE_Y: + PBRMix mix_y = sample_pbr( + get_uv_y() + , tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , tex_emissive +#endif + ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + // Triplanar-specific normal texture fix + mix_y.vNt = _t_normal_post_y(mix_y.vNt); +#endif + mix = mix_pbr(mix, mix_y, tw.weight.y); + break; + default: + break; + } + + switch (tw.type & SAMPLE_Z) + { + case SAMPLE_Z: + PBRMix mix_z = sample_pbr( + get_uv_z() + , tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , tex_emissive +#endif + ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + // Triplanar-specific normal texture fix + // *NOTE: Bottom face has not been tested + mix_z.vNt = _t_normal_post_z(mix_z.vNt); +#endif + mix = mix_pbr(mix, mix_z, tw.weight.z); + break; + default: + break; + } + + return mix; +} + +#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 + +#define TerrainCoord vec2 + +#define terrain_sample_pbr sample_pbr + +#endif + +PBRMix multiply_factors_pbr( + PBRMix mix_in + , vec4 factor_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , vec3 factor_orm +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , vec2 factor_rm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , vec3 factor_emissive +#endif + ) +{ + PBRMix mix = mix_in; + mix.col *= factor_col; +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + mix.orm *= factor_orm; +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + mix.rm *= factor_rm; +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + mix.emissive *= factor_emissive; +#endif + return mix; +} + +PBRMix terrain_sample_and_multiply_pbr( + TerrainCoord terrain_coord + , sampler2D tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , sampler2D tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , sampler2D tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , sampler2D tex_emissive +#endif + , vec4 factor_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , vec3 factor_orm +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , vec2 factor_rm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , vec3 factor_emissive +#endif + ) +{ + PBRMix mix = terrain_sample_pbr( + terrain_coord +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + , _t_triplanar() +#endif + , tex_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , tex_orm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + , tex_vNt +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , tex_emissive +#endif + ); + + mix = multiply_factors_pbr(mix + , factor_col +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION) + , factor_orm +#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + , factor_rm +#endif +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) + , factor_emissive +#endif + ); + + return mix; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl new file mode 100644 index 0000000000..489fc26e3f --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl @@ -0,0 +1,96 @@ +/** + * @file class1\environment\pbrterrainV.glsl + * + * $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$ + */ + +uniform mat3 normal_matrix; +uniform mat4 texture_matrix0; +uniform mat4 modelview_matrix; +uniform mat4 modelview_projection_matrix; + +in vec3 position; +in vec3 normal; +in vec4 tangent; +in vec4 diffuse_color; +in vec2 texcoord1; + +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 +out vec4[2] vary_coords; +#endif +out vec3 vary_vertex_normal; // Used by pbrterrainUtilF.glsl +out vec3 vary_normal; +out vec3 vary_tangent; +flat out float vary_sign; +out vec4 vary_texcoord0; +out vec4 vary_texcoord1; +out vec3 vary_position; + +// *HACK: tangent_space_transform should use texture_normal_transform, or maybe +// we shouldn't use tangent_space_transform at all. See the call to +// tangent_space_transform below. +uniform vec4[2] texture_base_color_transform; + +vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform); +vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform); + +void main() +{ + //transform vertex + gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); + vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz; + + vec3 n = normal_matrix * normal; + vary_vertex_normal = normal; + vec3 t = normal_matrix * tangent.xyz; + + vary_tangent = normalize(t); + // *TODO: Decide if we want this. It may be better to just calculate the + // tangents on-the-fly in the fragment shader, due to the subtleties of the + // effect of triplanar mapping on UVs. + // *HACK: Should be using texture_normal_transform here. The KHR texture + // transform spec requires handling texture transforms separately for each + // individual texture. + vary_tangent = normalize(tangent_space_transform(vec4(t, tangent.w), n, texture_base_color_transform, texture_matrix0)); + vary_sign = tangent.w; + vary_normal = normalize(n); + + // Transform and pass tex coords + // *HACK: texture_base_color_transform is used for all of these here, but + // the KHR texture transform spec requires handling texture transforms + // separately for each individual texture. +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + // xy + vary_coords[0].xy = texture_transform(position.xy, texture_base_color_transform, texture_matrix0); + // yz + vary_coords[0].zw = texture_transform(position.yz, texture_base_color_transform, texture_matrix0); + // (-x)z + vary_coords[1].xy = texture_transform(position.xz * vec2(-1, 1), texture_base_color_transform, texture_matrix0); +#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 + vary_texcoord0.xy = texture_transform(position.xy, texture_base_color_transform, texture_matrix0); +#endif + + vec4 tc = vec4(texcoord1,0,1); + vary_texcoord0.zw = tc.xy; + vary_texcoord1.xy = tc.xy-vec2(2.0, 0.0); + vary_texcoord1.zw = tc.xy-vec2(1.0, 0.0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl index 3443785e1a..1629ed91c8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl @@ -97,6 +97,7 @@ vec3 toneMapACES_Hill(vec3 color) uniform float exposure; uniform float gamma; +uniform float aces_mix; vec3 toneMap(vec3 color) { @@ -106,7 +107,7 @@ vec3 toneMap(vec3 color) color *= exposure * exp_scale; // mix ACES and Linear here as a compromise to avoid over-darkening legacy content - color = mix(toneMapACES_Hill(color), color, 0.3); + color = mix(toneMapACES_Hill(color), color, aces_mix); #endif return color; @@ -152,6 +153,15 @@ float noise(vec2 x) { //============================= +void debugExposure(inout vec3 color) +{ + float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r; + exp_scale *= 0.5; + if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1) + { + color = vec3(1,0,0); + } +} vec3 legacyGamma(vec3 color) { @@ -180,7 +190,8 @@ void main() vec3 seed = (diff.rgb+vec3(1.0))*vec3(tc.xy, tc.x+tc.y); vec3 nz = vec3(noise(seed.rg), noise(seed.gb), noise(seed.rb)); diff.rgb += nz*0.003; - + + //debugExposure(diff.rgb); frag_color = max(diff, vec4(0)); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl index 9d9ba49d82..785c748234 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl @@ -27,6 +27,15 @@ in vec3 vary_HazeColor; in float vary_LightNormPosDot; +#ifdef HAS_HDRI +in vec4 vary_position; +in vec3 vary_rel_pos; +uniform float sky_hdr_scale; +uniform float hdri_split_screen; +uniform mat3 env_mat; +uniform sampler2D environmentMap; +#endif + uniform sampler2D rainbow_map; uniform sampler2D halo_map; @@ -37,6 +46,9 @@ uniform float ice_level; out vec4 frag_data[4]; vec3 srgb_to_linear(vec3 c); +vec3 linear_to_srgb(vec3 c); + +#define PI 3.14159265 ///////////////////////////////////////////////////////////////////////// // The fragment shader for the sky @@ -71,24 +83,42 @@ vec3 halo22(float d) void main() { - // Potential Fill-rate optimization. Add cloud calculation - // back in and output alpha of 0 (so that alpha culling kills - // the fragment) if the sky wouldn't show up because the clouds - // are fully opaque. - - vec3 color = vary_HazeColor; - - float rel_pos_lightnorm = vary_LightNormPosDot; - float optic_d = rel_pos_lightnorm; - vec3 halo_22 = halo22(optic_d); - color.rgb += rainbow(optic_d); - color.rgb += halo_22; - color.rgb *= 2.; - color.rgb = clamp(color.rgb, vec3(0), vec3(5)); + vec3 color; +#ifdef HAS_HDRI + vec3 frag_coord = vary_position.xyz/vary_position.w; + if (-frag_coord.x > ((1.0-hdri_split_screen)*2.0-1.0)) + { + vec3 pos = normalize(vary_rel_pos); + pos = env_mat * pos; + vec2 texCoord = vec2(atan(pos.z, pos.x) + PI, acos(pos.y)) / vec2(2.0 * PI, PI); + color = textureLod(environmentMap, texCoord.xy, 0).rgb * sky_hdr_scale; + color = min(color, vec3(8192*8192*16)); // stupidly large value arrived at by binary search -- avoids framebuffer corruption from some HDRIs + + frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_HAS_HDRI); + } + else +#endif + { + // Potential Fill-rate optimization. Add cloud calculation + // back in and output alpha of 0 (so that alpha culling kills + // the fragment) if the sky wouldn't show up because the clouds + // are fully opaque. + + color = vary_HazeColor; + + float rel_pos_lightnorm = vary_LightNormPosDot; + float optic_d = rel_pos_lightnorm; + vec3 halo_22 = halo22(optic_d); + color.rgb += rainbow(optic_d); + color.rgb += halo_22; + color.rgb *= 2.; + color.rgb = clamp(color.rgb, vec3(0), vec3(5)); + + frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_SKIP_ATMOS); + } frag_data[0] = vec4(0); frag_data[1] = vec4(0); - frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_SKIP_ATMOS); //1.0 in norm.w masks off fog frag_data[3] = vec4(color.rgb, 1.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl index 17ce2dee5b..24d2db2183 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl @@ -35,6 +35,11 @@ in vec3 position; out vec3 vary_HazeColor; out float vary_LightNormPosDot; +#ifdef HAS_HDRI +out vec4 vary_position; +out vec3 vary_rel_pos; +#endif + // Inputs uniform vec3 camPosLocal; @@ -72,6 +77,11 @@ void main() // Get relative position vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0); +#ifdef HAS_HDRI + vary_rel_pos = rel_pos; + vary_position = pos; +#endif + // Adj position vector to clamp altitude if (rel_pos.y > 0.) { @@ -98,7 +108,7 @@ void main() vec3 light_atten = (blue_density + vec3(haze_density * 0.25)) * (density_multiplier * max_y); // Calculate relative weights - vec3 combined_haze = abs(blue_density) + vec3(abs(haze_density)); + vec3 combined_haze = max(abs(blue_density) + vec3(abs(haze_density)), vec3(1e-6)); vec3 blue_weight = blue_density / combined_haze; vec3 haze_weight = haze_density / combined_haze; diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl index f6d509e2c6..2dba7cb9d9 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl @@ -39,9 +39,11 @@ in vec4 vary_texcoord0; in vec4 vary_texcoord1; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 position); void main() { + mirrorClip(pos); /// Note: This should duplicate the blending functionality currently used for the terrain rendering. vec4 color0 = texture(detail_0, vary_texcoord0.xy); diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl index f6d3b59e85..8e1e4b54d5 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl @@ -25,12 +25,12 @@ uniform mat3 normal_matrix; uniform mat4 texture_matrix0; +uniform mat4 modelview_matrix; uniform mat4 modelview_projection_matrix; in vec3 position; in vec3 normal; in vec4 diffuse_color; -in vec2 texcoord0; in vec2 texcoord1; out vec3 pos; @@ -41,18 +41,16 @@ out vec4 vary_texcoord1; uniform vec4 object_plane_s; uniform vec4 object_plane_t; -vec4 texgen_object(vec4 vpos, vec4 tc, mat4 mat, vec4 tp0, vec4 tp1) +vec2 texgen_object(vec4 vpos, mat4 mat, vec4 tp0, vec4 tp1) { vec4 tcoord; tcoord.x = dot(vpos, tp0); tcoord.y = dot(vpos, tp1); - tcoord.z = tc.z; - tcoord.w = tc.w; tcoord = mat * tcoord; - return tcoord; + return tcoord.xy; } void main() @@ -62,12 +60,12 @@ void main() vec4 t_pos = modelview_projection_matrix * pre_pos; gl_Position = t_pos; - pos = t_pos.xyz; + pos = (modelview_matrix*pre_pos).xyz; vary_normal = normalize(normal_matrix * normal); // Transform and pass tex coords - vary_texcoord0.xy = texgen_object(vec4(position, 1.0), vec4(texcoord0,0,1), texture_matrix0, object_plane_s, object_plane_t).xy; + vary_texcoord0.xy = texgen_object(vec4(position, 1.0), texture_matrix0, object_plane_s, object_plane_t); vec4 t = vec4(texcoord1,0,1); diff --git a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl index 636dfed4ba..732333311c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl @@ -65,7 +65,7 @@ vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl // Apply texture animation first to avoid shearing and other artifacts texcoord = (sl_animation_transform * vec4(texcoord, 0, 1)).xy; // Convert to left-handed coordinate system. The offset of 1 is necessary - // for rotations to be applied correctly. + // for rotation and scale to be applied correctly. texcoord.y = 1.0 - texcoord.y; texcoord = khr_texture_transform(texcoord, khr_gltf_transform[0].xy, khr_gltf_transform[0].z, khr_gltf_transform[1].xy); // Convert back to right-handed coordinate system diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl index e2d87e68fa..f108faf283 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl @@ -32,13 +32,16 @@ uniform sampler2D diffuseMap; in vec4 vertex_color; in vec3 vary_normal; in vec2 vary_texcoord0; +in vec3 vary_position; uniform float minimum_alpha; vec2 encode_normal(vec3 n); +void mirrorClip(vec3 pos); void main() { + mirrorClip(vary_position); vec4 col = texture(diffuseMap, vary_texcoord0.xy); if (col.a < minimum_alpha) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl index ce8a10712c..c84fccd4c6 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl @@ -24,6 +24,7 @@ */ uniform mat4 texture_matrix0; +uniform mat4 modelview_matrix; uniform mat4 modelview_projection_matrix; uniform mat3 normal_matrix; @@ -34,11 +35,14 @@ in vec2 texcoord0; out vec3 vary_normal; out vec4 vertex_color; out vec2 vary_texcoord0; +out vec3 vary_position; void main() { //transform vertex gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0); + vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz; + vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; vary_normal = normalize(normal_matrix * normal); diff --git a/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl new file mode 100644 index 0000000000..388042e7e0 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl @@ -0,0 +1,33 @@ +/** + * @file normaldebugF.glsl + * + * $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$ + */ + +out vec4 frag_color; + +in vec4 vertex_color; + +void main() +{ + frag_color = max(vertex_color, vec4(0)); +} diff --git a/indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl new file mode 100644 index 0000000000..51d05cd507 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl @@ -0,0 +1,76 @@ +/** + * @file normaldebugG.glsl + * + * $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$ + */ + +// *NOTE: Geometry shaders have a reputation for being slow. Consider using +// compute shaders instead, which have a reputation for being fast. This +// geometry shader in particular seems to run fine on my machine, but I won't +// vouch for this in performance-critical areas. +// -Cosmic,2023-09-28 + +out vec4 vertex_color; + +in vec4 normal_g[]; +#ifdef HAS_ATTRIBUTE_TANGENT +in vec4 tangent_g[]; +#endif + +layout(triangles) in; +#ifdef HAS_ATTRIBUTE_TANGENT +layout(line_strip, max_vertices = 12) out; +#else +layout(line_strip, max_vertices = 6) out; +#endif + +void triangle_normal_debug(int i) +{ + // Normal + vec4 normal_color = vec4(1.0, 1.0, 0.0, 1.0); + gl_Position = gl_in[i].gl_Position; + vertex_color = normal_color; + EmitVertex(); + gl_Position = normal_g[i]; + vertex_color = normal_color; + EmitVertex(); + EndPrimitive(); + +#ifdef HAS_ATTRIBUTE_TANGENT + // Tangent + vec4 tangent_color = vec4(0.0, 1.0, 1.0, 1.0); + gl_Position = gl_in[i].gl_Position; + vertex_color = tangent_color; + EmitVertex(); + gl_Position = tangent_g[i]; + vertex_color = tangent_color; + EmitVertex(); + EndPrimitive(); +#endif +} + +void main() +{ + triangle_normal_debug(0); + triangle_normal_debug(1); + triangle_normal_debug(2); +} diff --git a/indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl new file mode 100644 index 0000000000..b198bc2485 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl @@ -0,0 +1,74 @@ +/** + * @file normaldebugV.glsl + * + * $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$ + */ + +in vec3 position; +in vec3 normal; +out vec4 normal_g; +#ifdef HAS_ATTRIBUTE_TANGENT +in vec4 tangent; +out vec4 tangent_g; +#endif + +uniform float debug_normal_draw_length; + +#ifdef HAS_SKIN +mat4 getObjectSkinnedTransform(); +#else +uniform mat3 normal_matrix; +#endif +uniform mat4 projection_matrix; +uniform mat4 modelview_matrix; + +// *NOTE: Should use the modelview_projection_matrix here in the non-skinned +// case for efficiency, but opting for the simplier implementation for now as +// this is debug code. Also, the skinned version hasn't beeen tested yet. +// world_pos = mat * vec4(position.xyz, 1.0) +vec4 get_screen_normal(vec3 position, vec4 world_pos, vec3 normal, mat4 mat) +{ + vec4 world_norm = mat * vec4((position + normal), 1.0); + world_norm.xyz -= world_pos.xyz; + world_norm.xyz = debug_normal_draw_length * normalize(world_norm.xyz); + world_norm.xyz += world_pos.xyz; + return projection_matrix * world_norm; +} + +void main() +{ +#ifdef HAS_SKIN + mat4 mat = getObjectSkinnedTransform(); + mat = modelview_matrix * mat; +#else +#define mat modelview_matrix +#endif + + vec4 world_pos = mat * vec4(position.xyz,1.0); + + gl_Position = projection_matrix * world_pos; + normal_g = get_screen_normal(position.xyz, world_pos, normal.xyz, mat); +#ifdef HAS_ATTRIBUTE_TANGENT + tangent_g = get_screen_normal(position.xyz, world_pos, tangent.xyz, mat); +#endif +} + diff --git a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl index 9ecdf0bf77..c1ed1bfe6e 100644 --- a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl @@ -38,6 +38,7 @@ in vec3 vary_dir; uniform float mipLevel; uniform int u_width; uniform float max_probe_lod; +uniform float probe_strength; // ============================================================================================================= @@ -129,7 +130,7 @@ vec4 prefilterEnvMap(vec3 R) float totalWeight = 0.0; float envMapDim = float(textureSize(reflectionProbes, 0).s); float roughness = mipLevel/max_probe_lod; - int numSamples = max(int(32*roughness), 1); + int numSamples = max(int(PROBE_FILTER_SAMPLES*roughness), 1); float numMips = max_probe_lod+1; @@ -163,5 +164,6 @@ void main() { vec3 N = normalize(vary_dir); frag_color = max(prefilterEnvMap(N), vec4(0)); + frag_color.a *= probe_strength; } // ============================================================================================================= diff --git a/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl b/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl index 67c99530e3..142f2a5d71 100644 --- a/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl @@ -30,9 +30,13 @@ uniform sampler2D texture1; in vec2 vary_texcoord0; in vec2 vary_texcoord1; +in vec3 vary_position; + +void mirrorClip(vec3 pos); void main() { + mirrorClip(vary_position); float tex0 = texture(texture0, vary_texcoord0.xy).a; float tex1 = texture(texture1, vary_texcoord1.xy).a; diff --git a/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl b/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl index 7d5417919e..b8a02fbdec 100644 --- a/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl @@ -23,6 +23,7 @@ * $/LicenseInfo$ */ +uniform mat4 modelview_matrix; uniform mat4 texture_matrix0; uniform mat4 modelview_projection_matrix; @@ -32,11 +33,11 @@ in vec2 texcoord1; out vec2 vary_texcoord0; out vec2 vary_texcoord1; +out vec3 vary_position; #ifdef HAS_SKIN mat4 getObjectSkinnedTransform(); uniform mat4 projection_matrix; -uniform mat4 modelview_matrix; #endif void main() @@ -46,8 +47,10 @@ void main() mat4 mat = getObjectSkinnedTransform(); mat = modelview_matrix * mat; vec4 pos = mat * vec4(position.xyz, 1.0); + vary_position = pos.xyz; gl_Position = projection_matrix * pos; #else + vary_position = (modelview_matrix * vec4(position.xyz, 1.0)).xyz; gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0); #endif vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy; diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl index a1da4b1f9a..40b7f9cf0e 100644 --- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl +++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl @@ -66,7 +66,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou // I had thought blue_density and haze_density should have equal weighting, // but attenuation due to haze_density tends to seem too strong - vec3 combined_haze = blue_density + vec3(haze_density); + vec3 combined_haze = max(blue_density + vec3(haze_density), vec3(1e-6)); vec3 blue_weight = blue_density / combined_haze; vec3 haze_weight = vec3(haze_density) / combined_haze; @@ -98,7 +98,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou haze_glow = max(haze_glow, .001); // set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) haze_glow *= glow.x; // higher glow.x gives dimmer glow (because next step is 1 / "angle") - haze_glow = pow(haze_glow, glow.z); + haze_glow = clamp(pow(haze_glow, glow.z), -10, 10); // glow.z should be negative, so we're doing a sort of (1 / "angle") function // add "minimum anti-solar illumination" diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index acd32a81b3..4aef22c296 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -78,6 +78,8 @@ float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); float getAmbientClamp(); +void mirrorClip(vec3 pos); + void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); @@ -167,6 +169,8 @@ vec3 calcPointLightOrSpotLight(vec3 light_col, vec3 diffuse, vec3 v, vec3 n, vec void main() { + mirrorClip(vary_position); + vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5; vec4 pos = vec4(vary_position, 1.0); diff --git a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl index 003dd05e6f..059c2a64ce 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl @@ -90,6 +90,7 @@ 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 mirrorClip(vec3 pos); void waterClip(vec3 pos); void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor); @@ -156,6 +157,8 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, void main() { + mirrorClip(vary_position); + vec3 color = vec3(0,0,0); vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir; diff --git a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl index 8430cca325..c382a9fbc0 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl @@ -53,8 +53,11 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); +void mirrorClip(vec3 pos); + void main() { + mirrorClip(vary_position); #ifdef HAS_DIFFUSE_LOOKUP vec4 color = diffuseLookup(vary_texcoord0.xy); #else diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl index ec1e49eeb4..0476b98e10 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl @@ -45,6 +45,13 @@ void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float vec3 srgb_to_linear(vec3 cs); vec3 linear_to_srgb(vec3 cs); +uniform mat4 modelview_matrix; +uniform mat3 normal_matrix; + +in vec3 vary_position; + +void mirrorClip(vec3 pos); + #if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) out vec4 frag_color; @@ -66,12 +73,12 @@ uniform vec4 morphFactor; uniform vec3 camPosLocal; uniform mat3 env_mat; +uniform float is_mirror; + uniform vec3 sun_dir; uniform vec3 moon_dir; in vec2 vary_fragcoord; -in vec3 vary_position; - uniform mat4 proj_mat; uniform mat4 inv_proj; uniform vec2 screen_res; @@ -285,12 +292,12 @@ float getShadow(vec3 pos, vec3 norm) void main() { + mirrorClip(vary_position); waterClip(); // diffcol == diffuse map combined with vertex color vec4 diffcol = texture(diffuseMap, vary_texcoord0.xy); diffcol.rgb *= vertex_color.rgb; - alphaMask(diffcol.a); // spec == specular map combined with specular color @@ -407,9 +414,12 @@ void main() #else // mode is not DIFFUSE_ALPHA_MODE_BLEND, encode to gbuffer // deferred path // See: C++: addDeferredAttachment(), shader: softenLightF.glsl + + float flag = GBUFFER_FLAG_HAS_ATMOS; + frag_data[0] = vec4(diffcol.rgb, emissive); // gbuffer is sRGB for legacy materials frag_data[1] = vec4(spec.rgb, glossiness); // XYZ = Specular color. W = Specular exponent. - frag_data[2] = vec4(encode_normal(norm), env, GBUFFER_FLAG_HAS_ATMOS);; // XY = Normal. Z = Env. intensity. W = 1 skip atmos (mask off fog) + frag_data[2] = vec4(encode_normal(norm), env, flag);; // XY = Normal. Z = Env. intensity. W = 1 skip atmos (mask off fog) frag_data[3] = vec4(0); #endif } diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl index 906e66ecc8..90c84cc428 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl @@ -31,6 +31,7 @@ float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, uniform samplerCubeArray reflectionProbes; uniform samplerCubeArray irradianceProbes; + uniform sampler2D sceneMap; uniform int cube_snapshot; uniform float max_probe_lod; @@ -47,6 +48,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; // list of bounding spheres for reflection probes sorted by distance to camera (closest first) vec4 refSphere[MAX_REFMAP_COUNT]; // extra parameters @@ -55,6 +57,7 @@ layout (std140) uniform ReflectionProbes // z - fade in // w - znear vec4 refParams[MAX_REFMAP_COUNT]; + vec4 heroSphere; // 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 @@ -70,6 +73,10 @@ layout (std140) uniform ReflectionProbes // number of reflection probes present in refSphere int refmapCount; + + int heroShape; + int heroMipCount; + int heroProbeCount; }; // Inputs @@ -365,11 +372,11 @@ return texCUBE(envMap, ReflDirectionWS); // i - probe index in refBox/refSphere // d - distance to nearest wall in clip space // scale - scale of box, default 1.0 -vec3 boxIntersect(vec3 origin, vec3 dir, int i, out float d, float scale) +vec3 boxIntersect(vec3 origin, vec3 dir, mat4 i, out float d, float scale) { // Intersection with OBB convert to unit box space // Transform in local unit parallax cube space (scaled and rotated) - mat4 clipToLocal = refBox[i]; + mat4 clipToLocal = i; vec3 RayLS = mat3(clipToLocal) * dir; vec3 PositionLS = (clipToLocal * vec4(origin, 1.0)).xyz; @@ -388,7 +395,7 @@ vec3 boxIntersect(vec3 origin, vec3 dir, int i, out float d, float scale) return IntersectPositionCS; } -vec3 boxIntersect(vec3 origin, vec3 dir, int i, out float d) +vec3 boxIntersect(vec3 origin, vec3 dir, mat4 i, out float d) { return boxIntersect(origin, dir, i, d, 1.0); } @@ -443,9 +450,9 @@ void boxIntersectionDebug( in vec3 ro, in vec3 p, vec3 boxSize, inout vec4 col) } -void boxIntersectDebug(vec3 origin, vec3 pos, int i, inout vec4 col) +void boxIntersectDebug(vec3 origin, vec3 pos, mat4 i, inout vec4 col) { - mat4 clipToLocal = refBox[i]; + mat4 clipToLocal = i; // transform into unit cube space origin = (clipToLocal * vec4(origin, 1.0)).xyz; @@ -462,7 +469,7 @@ void boxIntersectDebug(vec3 origin, vec3 pos, int i, inout vec4 col) // r - radius of probe influence volume // i - index of probe in refSphere // dw - distance weight -float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, int i, out float dw) +float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, vec4 i, out float dw) { float r1 = r * 0.5; // 50% of radius (outer sphere to start interpolating down) vec3 delta = pos.xyz - origin; @@ -471,7 +478,7 @@ float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, int i, out float dw float atten = 1.0 - max(d2 - r1, 0.0) / max((r - r1), 0.001); float w = 1.0 / d2; - w *= refParams[i].z; + w *= i.z; dw = w * atten * max(r, 1.0)*4; @@ -497,7 +504,7 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, if (refIndex[i].w < 0) { // box probe float d = 0; - v = boxIntersect(pos, dir, i, d); + v = boxIntersect(pos, dir, refBox[i], d); w = max(d, 0.001); } @@ -511,7 +518,7 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, refIndex[i].w < 1 ? 4096.0*4096.0 : // <== effectively disable parallax correction for automatically placed probes to keep from bombing the world with obvious spheres rr); - w = sphereWeight(pos, dir, refSphere[i].xyz, r, i, dw); + w = sphereWeight(pos, dir, refSphere[i].xyz, r, refParams[i], dw); } v -= c; @@ -537,7 +544,7 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int if (refIndex[i].w < 0) { float d = 0.0; - v = boxIntersect(pos, dir, i, d, 3.0); + v = boxIntersect(pos, dir, refBox[i], d, 3.0); w = max(d, 0.001); } else @@ -551,7 +558,7 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int refIndex[i].w < 1 ? 4096.0*4096.0 : // <== effectively disable parallax correction for automatically placed probes to keep from bombing the world with obvious spheres rr); - w = sphereWeight(pos, dir, refSphere[i].xyz, r, i, dw); + w = sphereWeight(pos, dir, refSphere[i].xyz, r, refParams[i], dw); } v -= c; @@ -681,6 +688,49 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit) return col[1]+col[0]; } +#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 + { + float r = heroSphere.w; + + w = sphereWeight(pos, refnormpersp, heroSphere.xyz, r, vec4(1), dw); + } + + 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); +} + +#else + +void tapHeroProbe(inout vec3 glossenv, vec3 pos, vec3 norm, float glossiness) +{ +} + +#endif + + + void doProbeSample(inout vec3 ambenv, inout vec3 glossenv, vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit) { @@ -712,6 +762,8 @@ void doProbeSample(inout vec3 ambenv, inout vec3 glossenv, glossenv = mix(glossenv, ssr.rgb, ssr.a); } #endif + + tapHeroProbe(glossenv, pos, norm, glossiness); } void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, @@ -747,7 +799,7 @@ void debugTapRefMap(vec3 pos, vec3 dir, float depth, int i, inout vec4 col) { if (refIndex[i].w < 0) { - boxIntersectDebug(origin, pos, i, col); + boxIntersectDebug(origin, pos, refBox[i], col); } else { @@ -799,6 +851,7 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout { float lod = (1.0-glossiness)*reflection_lods; glossenv = sampleProbes(pos, normalize(refnormpersp), lod); + } if (envIntensity > 0.0) @@ -826,6 +879,9 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout } #endif + tapHeroProbe(glossenv, pos, norm, glossiness); + tapHeroProbe(legacyenv, pos, norm, 1.0); + glossenv = clamp(glossenv, vec3(0), vec3(10)); } diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 5e8fe9301a..9b9739ba22 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -50,6 +50,7 @@ uniform float ssao_irradiance_max; #endif // Inputs +uniform vec4 clipPlane; uniform mat3 env_mat; uniform mat3 ssao_effect_mat; uniform vec3 sun_dir; @@ -178,7 +179,7 @@ void main() float gloss = 1.0 - perceptualRoughness; sampleReflectionProbes(irradiance, radiance, tc, pos.xyz, norm.xyz, gloss, false, amblit_linear); - + adjustIrradiance(irradiance, ambocc); vec3 diffuseColor; @@ -188,9 +189,14 @@ void main() 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); } - else if (!GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_ATMOS)) + else if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_HDRI)) + { + // actual HDRI sky, just copy color value + color = texture(emissiveRect, tc).rgb; + } + else if (GET_GBUFFER_FLAG(GBUFFER_FLAG_SKIP_ATMOS)) { - //should only be true of WL sky, just port over base color value + //should only be true of WL sky, port over base color value and scale for fake HDR color = texture(emissiveRect, tc).rgb; color = srgb_to_linear(color); color *= sky_hdr_scale; @@ -209,7 +215,7 @@ void main() vec3 legacyenv = vec3(0); sampleReflectionProbesLegacy(irradiance, glossenv, legacyenv, tc, pos.xyz, norm.xyz, spec.a, envIntensity, false, amblit_linear); - + adjustIrradiance(irradiance, ambocc); // apply lambertian IBL only (see pbrIbl) @@ -244,6 +250,7 @@ void main() // add radiance map applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz); + } color.rgb = mix(color.rgb, baseColor.rgb, baseColor.a); diff --git a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl index 223e55eb69..ef086fc3be 100644 --- a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl @@ -55,9 +55,11 @@ in vec4 view; in vec3 vary_position; vec4 applyWaterFogViewLinearNoClip(vec3 pos, vec4 color); +void mirrorClip(vec3 position); void main() { + mirrorClip(vary_position); vec4 color; //get detail normals diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl index b364e454e8..b2a81aa025 100644 --- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl @@ -35,6 +35,8 @@ vec3 scaleSoftClipFragLinear(vec3 l); void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive); vec4 applyWaterFogViewLinear(vec3 pos, vec4 color); +void mirrorClip(vec3 pos); + // PBR interface vec2 BRDF(float NoV, float roughness); @@ -129,6 +131,7 @@ vec3 getPositionWithNDC(vec3 ndc); void main() { + mirrorClip(vary_position); vN = vary_normal; vT = vary_tangent; vB = cross(vN, vT); diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index f92d9a2a0e..3e1e235fcd 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 59 +version 60 // 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 @@ -48,6 +48,8 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 2 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTreeLODFactor 1 1.0 RenderVBOEnable 1 1 RenderVBOMappingDisable 1 1 @@ -94,6 +96,8 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 +RenderTerrainPBRDetail 1 -4 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTreeLODFactor 1 0 RenderVolumeLODFactor 1 1.125 RenderDeferredSSAO 1 0 @@ -103,6 +107,11 @@ WLSkyDetail 1 96 RenderFSAASamples 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 256 +RenderHeroProbeDistance 1 4 +RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeConservativeUpdateMultiplier 1 16 // // Medium Low Graphics Settings @@ -123,6 +132,8 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 +RenderTerrainPBRDetail 1 -1 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.125 RenderDeferredSSAO 1 0 @@ -132,6 +143,11 @@ WLSkyDetail 1 96 RenderFSAASamples 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 256 +RenderHeroProbeDistance 1 6 +RenderHeroProbeUpdateRate 1 3 +RenderHeroProbeConservativeUpdateMultiplier 1 16 // // Medium Graphics Settings (standard) @@ -150,6 +166,8 @@ RenderLocalLightCount 1 512 RenderTransparentWater 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.25 RenderDeferredSSAO 1 0 @@ -161,6 +179,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 1 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 512 +RenderHeroProbeDistance 1 6 +RenderHeroProbeUpdateRate 1 3 +RenderHeroProbeConservativeUpdateMultiplier 1 16 // // Medium High Graphics Settings @@ -179,6 +202,8 @@ RenderLocalLightCount 1 1024 RenderTransparentWater 1 1 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.375 RenderDeferredSSAO 1 0 @@ -190,6 +215,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 2 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 512 +RenderHeroProbeDistance 1 6 +RenderHeroProbeUpdateRate 1 2 +RenderHeroProbeConservativeUpdateMultiplier 1 8 // // High Graphics Settings (SSAO + sun shadows) @@ -208,6 +238,8 @@ RenderLocalLightCount 1 2048 RenderTransparentWater 1 1 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.5 RenderDeferredSSAO 1 1 @@ -219,6 +251,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 3 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 1024 +RenderHeroProbeDistance 1 8 +RenderHeroProbeUpdateRate 1 2 +RenderHeroProbeConservativeUpdateMultiplier 1 8 // // High Ultra Graphics Settings (deferred + SSAO + all shadows) @@ -236,6 +273,8 @@ RenderMaxPartCount 1 4096 RenderLocalLightCount 1 4096 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.75 @@ -248,6 +287,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 3 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 1024 +RenderHeroProbeDistance 1 16 +RenderHeroProbeUpdateRate 1 1 +RenderHeroProbeConservativeUpdateMultiplier 1 4 // // Ultra graphics (REALLY PURTY!) @@ -264,6 +308,8 @@ RenderLocalLightCount 1 8192 RenderMaxPartCount 1 8192 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderVolumeLODFactor 1 2.0 @@ -277,6 +323,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 3 +RenderMirrors 1 1 +RenderHeroProbeResolution 1 2048 +RenderHeroProbeDistance 1 16 +RenderHeroProbeUpdateRate 1 1 +RenderHeroProbeConservativeUpdateMultiplier 1 4 // // Class Unknown Hardware (unknown) @@ -285,6 +336,7 @@ list Unknown RenderShadowDetail 1 0 RenderDeferredSSAO 1 0 RenderUseAdvancedAtmospherics 1 0 +RenderMirrors 1 0 // // VRAM > 512MB @@ -306,6 +358,7 @@ RenderTransparentWater 1 0 RenderDeferredSSAO 0 0 RenderShadowDetail 0 0 RenderReflectionProbeDetail 0 -1 +RenderMirrors 0 0 list Intel RenderAnisotropic 1 0 @@ -320,4 +373,7 @@ list GL3 RenderFSAASamples 0 0 RenderReflectionsEnabled 0 0 RenderReflectionProbeDetail 0 0 +RenderMirrors 0 0 +list TexUnit16orLess +RenderTerrainPBRDetail 1 -1 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 40aaccc8cb..2ffefadcc1 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 56 +version 57 // 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 @@ -45,6 +45,8 @@ RenderObjectBump 1 1 RenderLocalLightCount 1 4096 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderVBOEnable 1 1 @@ -89,6 +91,8 @@ RenderLocalLightCount 1 8 RenderMaxPartCount 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 +RenderTerrainPBRDetail 1 -4 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTransparentWater 1 0 RenderTreeLODFactor 1 0 RenderVolumeLODFactor 1 1.125 @@ -101,6 +105,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 256 +RenderHeroProbeDistance 1 4 +RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeConservativeUpdateMultiplier 1 16 // // Medium Low Graphics Settings @@ -118,6 +127,8 @@ RenderMaxPartCount 1 2048 RenderLocalLightCount 1 256 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 +RenderTerrainPBRDetail 1 -1 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.125 @@ -130,6 +141,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 256 +RenderHeroProbeDistance 1 6 +RenderHeroProbeUpdateRate 1 3 +RenderHeroProbeConservativeUpdateMultiplier 1 16 // // Medium Graphics Settings (standard) @@ -147,6 +163,8 @@ RenderMaxPartCount 1 4096 RenderLocalLightCount 1 512 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.25 @@ -159,6 +177,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 512 +RenderHeroProbeDistance 1 6 +RenderHeroProbeUpdateRate 1 3 +RenderHeroProbeConservativeUpdateMultiplier 1 16 // // Medium High Graphics Settings @@ -176,6 +199,8 @@ RenderMaxPartCount 1 4096 RenderLocalLightCount 1 1024 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 1 RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.375 @@ -188,6 +213,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 512 +RenderHeroProbeDistance 1 6 +RenderHeroProbeUpdateRate 1 2 +RenderHeroProbeConservativeUpdateMultiplier 1 8 // // High Graphics Settings (SSAO + sun shadows) @@ -205,6 +235,8 @@ RenderMaxPartCount 1 4096 RenderLocalLightCount 1 2048 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.5 @@ -217,6 +249,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 1 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 1024 +RenderHeroProbeDistance 1 8 +RenderHeroProbeUpdateRate 1 2 +RenderHeroProbeConservativeUpdateMultiplier 1 8 // // High Ultra Graphics Settings (SSAO + all shadows) @@ -234,6 +271,8 @@ RenderMaxPartCount 1 4096 RenderLocalLightCount 1 4096 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTransparentWater 1 1 RenderTreeLODFactor 1 0.5 RenderVolumeLODFactor 1 1.75 @@ -246,6 +285,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 2 +RenderMirrors 1 0 +RenderHeroProbeResolution 1 1024 +RenderHeroProbeDistance 1 16 +RenderHeroProbeUpdateRate 1 1 +RenderHeroProbeConservativeUpdateMultiplier 1 4 // // Ultra graphics (REALLY PURTY!) @@ -262,6 +306,8 @@ RenderLocalLightCount 1 8192 RenderMaxPartCount 1 8192 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 +RenderTerrainPBRDetail 1 0 +RenderTerrainPBRPlanarSampleCount 1 3 RenderTransparentWater 1 1 RenderTreeLODFactor 1 1.0 RenderVolumeLODFactor 1 2.0 @@ -275,6 +321,11 @@ RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 3 +RenderMirrors 1 1 +RenderHeroProbeResolution 1 2048 +RenderHeroProbeDistance 1 16 +RenderHeroProbeUpdateRate 1 1 +RenderHeroProbeConservativeUpdateMultiplier 1 4 // // Class Unknown Hardware (unknown) @@ -283,6 +334,7 @@ list Unknown RenderShadowDetail 1 0 RenderDeferredSSAO 1 0 RenderUseAdvancedAtmospherics 1 0 +RenderMirrors 1 0 // @@ -304,10 +356,14 @@ RenderTerrainDetail 1 0 RenderDeferredSSAO 0 0 RenderUseAdvancedAtmospherics 0 0 RenderShadowDetail 0 0 +RenderMirrors 0 0 list TexUnit8orLess RenderDeferredSSAO 0 0 +list TexUnit16orLess +RenderTerrainPBRDetail 1 -1 + list AMD RenderDeferredSSAO 1 0 @@ -319,3 +375,4 @@ list GL3 RenderFSAASamples 0 0 RenderReflectionProbeDetail 0 0 RenderReflectionsEnabled 0 0 +RenderMirrors 0 0 diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp new file mode 100644 index 0000000000..092b6e5d4b --- /dev/null +++ b/indra/newview/gltf/asset.cpp @@ -0,0 +1,211 @@ +/** + * @file asset.cpp + * @brief LL GLTF Implementation + * + * $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$ + */ + +#include "../llviewerprecompiledheaders.h" + +#include "asset.h" +#include "llvolumeoctree.h" + +using namespace LL::GLTF; + +void Scene::updateTransforms(Asset& asset) +{ + LLMatrix4a identity; + identity.setIdentity(); + for (auto& nodeIndex : mNodes) + { + Node& node = asset.mNodes[nodeIndex]; + node.updateTransforms(asset, identity); + } +} + +void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +{ + for (auto& nodeIndex : mNodes) + { + Node& node = asset.mNodes[nodeIndex]; + node.updateRenderTransforms(asset, modelview); + } +} + +void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +{ + matMul(mMatrix, modelview, mRenderMatrix); + + for (auto& childIndex : mChildren) + { + Node& child = asset.mNodes[childIndex]; + child.updateRenderTransforms(asset, mRenderMatrix); + } +} + +LLMatrix4a inverse(const LLMatrix4a& mat); + +void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix) +{ + matMul(mMatrix, parentMatrix, mAssetMatrix); + mAssetMatrixInv = inverse(mAssetMatrix); + + for (auto& childIndex : mChildren) + { + Node& child = asset.mNodes[childIndex]; + child.updateTransforms(asset, mAssetMatrix); + } +} + +void Asset::updateTransforms() +{ + for (auto& scene : mScenes) + { + scene.updateTransforms(*this); + } +} + +void Asset::updateRenderTransforms(const LLMatrix4a& modelview) +{ +#if 0 + // traverse hierarchy and update render transforms from scratch + for (auto& scene : mScenes) + { + scene.updateRenderTransforms(*this, modelview); + } +#else + // use mAssetMatrix to update render transforms from node list + for (auto& node : mNodes) + { + if (node.mMesh != INVALID_INDEX) + { + matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); + } + } + +#endif + +} + +S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector4a* normal, // return the surface normal at the intersection point + LLVector4a* tangent, // return the surface tangent at the intersection point + S32* primitive_hitp +) +{ + S32 node_hit = -1; + S32 primitive_hit = -1; + + LLVector4a local_start; + LLVector4a asset_end = end; + LLVector4a local_end; + LLVector4a p; + + + for (auto& node : mNodes) + { + if (node.mMesh != INVALID_INDEX) + { + + bool newHit = false; + + // transform start and end to this node's local space + node.mAssetMatrixInv.affineTransform(start, local_start); + node.mAssetMatrixInv.affineTransform(asset_end, local_end); + + Mesh& mesh = mMeshes[node.mMesh]; + for (auto& primitive : mesh.mPrimitives) + { + const LLVolumeTriangle* tri = primitive.lineSegmentIntersect(local_start, local_end, &p, tex_coord, normal, tangent); + if (tri) + { + newHit = true; + local_end = p; + + // pointer math to get the node index + node_hit = &node - &mNodes[0]; + llassert(&mNodes[node_hit] == &node); + + //pointer math to get the primitive index + primitive_hit = &primitive - &mesh.mPrimitives[0]; + llassert(&mesh.mPrimitives[primitive_hit] == &primitive); + } + } + + if (newHit) + { + // shorten line segment on hit + node.mAssetMatrix.affineTransform(p, asset_end); + + // transform results back to asset space + if (intersection) + { + *intersection = asset_end; + } + + if (normal || tangent) + { + LLMatrix4 normalMatrix(node.mAssetMatrixInv.getF32ptr()); + + normalMatrix.transpose(); + + LLMatrix4a norm_mat; + norm_mat.loadu((F32*)normalMatrix.mMatrix); + + if (normal) + { + LLVector4a n = *normal; + F32 w = n.getF32ptr()[3]; + n.getF32ptr()[3] = 0.0f; + + norm_mat.affineTransform(n, *normal); + normal->getF32ptr()[3] = w; + } + + if (tangent) + { + LLVector4a t = *tangent; + F32 w = t.getF32ptr()[3]; + t.getF32ptr()[3] = 0.0f; + + norm_mat.affineTransform(t, *tangent); + tangent->getF32ptr()[3] = w; + } + } + } + } + } + + if (node_hit != -1) + { + if (primitive_hitp) + { + *primitive_hitp = primitive_hit; + } + } + + return node_hit; +} + + diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h new file mode 100644 index 0000000000..0f1d4e8993 --- /dev/null +++ b/indra/newview/gltf/asset.h @@ -0,0 +1,445 @@ +#pragma once + +/** + * @file asset.h + * @brief LL GLTF Implementation + * + * $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$ + */ + +#include "llvertexbuffer.h" +#include "llvolumeoctree.h" +#include "../lltinygltfhelper.h" +#include "primitive.h" + +// LL GLTF Implementation +namespace LL +{ + namespace GLTF + { + constexpr S32 INVALID_INDEX = -1; + + class Asset; + + class Buffer + { + public: + std::vector<U8> mData; + std::string mName; + std::string mUri; + + const Buffer& operator=(const tinygltf::Buffer& src) + { + mData = src.data; + mName = src.name; + mUri = src.uri; + return *this; + } + }; + + class BufferView + { + public: + S32 mBuffer = INVALID_INDEX; + S32 mByteLength; + S32 mByteOffset; + S32 mByteStride; + S32 mTarget; + S32 mComponentType; + + std::string mName; + + const BufferView& operator=(const tinygltf::BufferView& src) + { + mBuffer = src.buffer; + mByteLength = src.byteLength; + mByteOffset = src.byteOffset; + mByteStride = src.byteStride; + mTarget = src.target; + mName = src.name; + return *this; + } + }; + + class Accessor + { + public: + S32 mBufferView = INVALID_INDEX; + S32 mByteOffset; + S32 mComponentType; + S32 mCount; + std::vector<double> mMax; + std::vector<double> mMin; + S32 mType; + bool mNormalized; + std::string mName; + + const Accessor& operator=(const tinygltf::Accessor& src) + { + mBufferView = src.bufferView; + mByteOffset = src.byteOffset; + mComponentType = src.componentType; + mCount = src.count; + mType = src.type; + mNormalized = src.normalized; + mName = src.name; + mMax = src.maxValues; + mMin = src.maxValues; + + return *this; + } + }; + + class Material + { + public: + // use LLFetchedGLTFMaterial for now, but eventually we'll want to use + // a more flexible GLTF material implementation instead of the fixed packing + // version we use for sharable GLTF material assets + LLPointer<LLFetchedGLTFMaterial> mMaterial; + std::string mName; + + const Material& operator=(const tinygltf::Material& src) + { + mName = src.name; + return *this; + } + + void allocateGLResources(Asset& asset) + { + // allocate material + mMaterial = new LLFetchedGLTFMaterial(); + } + }; + + class Mesh + { + public: + std::vector<Primitive> mPrimitives; + std::vector<double> mWeights; + std::string mName; + + const Mesh& operator=(const tinygltf::Mesh& src) + { + mPrimitives.resize(src.primitives.size()); + for (U32 i = 0; i < src.primitives.size(); ++i) + { + mPrimitives[i] = src.primitives[i]; + } + + mWeights = src.weights; + mName = src.name; + + return *this; + } + + void allocateGLResources(Asset& asset) + { + for (auto& primitive : mPrimitives) + { + primitive.allocateGLResources(asset); + } + } + + }; + + class Node + { + public: + LLMatrix4a mMatrix; //local transform + LLMatrix4a mRenderMatrix; //transform for rendering + LLMatrix4a mAssetMatrix; //transform from local to asset space + LLMatrix4a mAssetMatrixInv; //transform from asset to local space + + std::vector<S32> mChildren; + S32 mMesh = INVALID_INDEX; + std::string mName; + + const Node& operator=(const tinygltf::Node& src) + { + F32* dstMatrix = mMatrix.getF32ptr(); + + if (src.matrix.size() != 16) + { + mMatrix.setIdentity(); + } + else + { + for (U32 i = 0; i < 16; ++i) + { + dstMatrix[i] = (F32)src.matrix[i]; + } + } + + mChildren = src.children; + mMesh = src.mesh; + mName = src.name; + + return *this; + } + + // Set mRenderMatrix to a transform that can be used for the current render pass + // modelview -- parent's render matrix + void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); + + // update mAssetMatrix and mAssetMatrixInv + void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix); + + }; + + class Scene + { + public: + std::vector<S32> mNodes; + std::string mName; + + const Scene& operator=(const tinygltf::Scene& src) + { + mNodes = src.nodes; + mName = src.name; + + return *this; + } + + void updateTransforms(Asset& asset); + void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); + + }; + + class Texture + { + public: + S32 mSampler = INVALID_INDEX; + S32 mSource = INVALID_INDEX; + std::string mName; + + const Texture& operator=(const tinygltf::Texture& src) + { + mSampler = src.sampler; + mSource = src.source; + mName = src.name; + + return *this; + } + }; + + class Sampler + { + public: + S32 mMagFilter; + S32 mMinFilter; + S32 mWrapS; + S32 mWrapT; + std::string mName; + + const Sampler& operator=(const tinygltf::Sampler& src) + { + mMagFilter = src.magFilter; + mMinFilter = src.minFilter; + mWrapS = src.wrapS; + mWrapT = src.wrapT; + mName = src.name; + + return *this; + } + }; + + class Image + { + public: + std::string mName; + std::string mUri; + std::string mMimeType; + std::vector<U8> mData; + S32 mWidth; + S32 mHeight; + S32 mComponent; + S32 mBits; + LLPointer<LLViewerFetchedTexture> mTexture; + + const Image& operator=(const tinygltf::Image& src) + { + mName = src.name; + mUri = src.uri; + mMimeType = src.mimeType; + mData = src.image; + mWidth = src.width; + mHeight = src.height; + mComponent = src.component; + mBits = src.bits; + + return *this; + } + + void allocateGLResources() + { + // allocate texture + + } + }; + + // C++ representation of a GLTF Asset + class Asset : public LLRefCount + { + public: + std::vector<Scene> mScenes; + std::vector<Node> mNodes; + std::vector<Mesh> mMeshes; + std::vector<Material> mMaterials; + std::vector<Buffer> mBuffers; + std::vector<BufferView> mBufferViews; + std::vector<Texture> mTextures; + std::vector<Sampler> mSamplers; + std::vector<Image> mImages; + std::vector<Accessor> mAccessors; + + void allocateGLResources(const std::string& filename, const tinygltf::Model& model) + { + for (auto& mesh : mMeshes) + { + mesh.allocateGLResources(*this); + } + + for (auto& image : mImages) + { + image.allocateGLResources(); + } + + for (U32 i = 0; i < mMaterials.size(); ++i) + { + mMaterials[i].allocateGLResources(*this); + LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); + } + } + + // update asset-to-node and node-to-asset transforms + void updateTransforms(); + + // update node render transforms + void updateRenderTransforms(const LLMatrix4a& modelview); + + void renderOpaque() + { + for (auto& node : mNodes) + { + if (node.mMesh != INVALID_INDEX) + { + Mesh& mesh = mMeshes[node.mMesh]; + for (auto& primitive : mesh.mPrimitives) + { + gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + if (primitive.mMaterial != INVALID_INDEX) + { + Material& material = mMaterials[primitive.mMaterial]; + material.mMaterial->bind(); + } + primitive.mVertexBuffer->setBuffer(); + if (primitive.mVertexBuffer->getNumIndices() > 0) + { + primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0); + } + else + { + primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts()); + } + } + } + } + } + + // return the index of the node that the line segment intersects with, or -1 if no hit + // input and output values must be in this asset's local coordinate frame + S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection = nullptr, // return the intersection point + LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point + LLVector4a* normal = nullptr, // return the surface normal at the intersection point + LLVector4a* tangent = nullptr, // return the surface tangent at the intersection point + S32* primitive_hitp = nullptr // return the index of the primitive that was hit + ); + + const Asset& operator=(const tinygltf::Model& src) + { + mScenes.resize(src.scenes.size()); + for (U32 i = 0; i < src.scenes.size(); ++i) + { + mScenes[i] = src.scenes[i]; + } + + mNodes.resize(src.nodes.size()); + for (U32 i = 0; i < src.nodes.size(); ++i) + { + mNodes[i] = src.nodes[i]; + } + + mMeshes.resize(src.meshes.size()); + for (U32 i = 0; i < src.meshes.size(); ++i) + { + mMeshes[i] = src.meshes[i]; + } + + mMaterials.resize(src.materials.size()); + for (U32 i = 0; i < src.materials.size(); ++i) + { + mMaterials[i] = src.materials[i]; + } + + mBuffers.resize(src.buffers.size()); + for (U32 i = 0; i < src.buffers.size(); ++i) + { + mBuffers[i] = src.buffers[i]; + } + + mBufferViews.resize(src.bufferViews.size()); + for (U32 i = 0; i < src.bufferViews.size(); ++i) + { + mBufferViews[i] = src.bufferViews[i]; + } + + mTextures.resize(src.textures.size()); + for (U32 i = 0; i < src.textures.size(); ++i) + { + mTextures[i] = src.textures[i]; + } + + mSamplers.resize(src.samplers.size()); + for (U32 i = 0; i < src.samplers.size(); ++i) + { + mSamplers[i] = src.samplers[i]; + } + + mImages.resize(src.images.size()); + for (U32 i = 0; i < src.images.size(); ++i) + { + mImages[i] = src.images[i]; + } + + mAccessors.resize(src.accessors.size()); + for (U32 i = 0; i < src.accessors.size(); ++i) + { + mAccessors[i] = src.accessors[i]; + } + + return *this; + } + }; + } +} diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp new file mode 100644 index 0000000000..71654dcfdd --- /dev/null +++ b/indra/newview/gltf/primitive.cpp @@ -0,0 +1,484 @@ +/** + * @file primitive.cpp + * @brief LL GLTF Implementation + * + * $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$ + */ + +#include "../llviewerprecompiledheaders.h" + +#include "asset.h" +#include "../lltinygltfhelper.h" + +using namespace LL::GLTF; + +#ifdef _MSC_VER +#define LL_FUNCSIG __FUNCSIG__ +#else +#define LL_FUNCSIG __PRETTY_FUNCTION__ +#endif + +// copy one vec3 from src to dst +template<class S, class T> +void copyVec2(S* src, T& dst) +{ + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; +} + +// copy one vec3 from src to dst +template<class S, class T> +void copyVec3(S* src, T& dst) +{ + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; +} + +// copy one vec4 from src to dst +template<class S, class T> +void copyVec4(S* src, T& dst) +{ + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; +} + +template<> +void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst) +{ + dst.set(src[0], src[1]); +} + +template<> +void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) +{ + dst.load3(src); +} + +template<> +void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst) +{ + dst.set(src[0], src[1], src[2], 255); +} + +template<> +void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst) +{ + dst.loadua(src); +} + +// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy +template<class S, class T> +void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count) +{ + for (S32 i = 0; i < count; ++i) + { + copyVec2(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } +} + +// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy +template<class S, class T> +void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count) +{ + for (S32 i = 0; i < count; ++i) + { + copyVec3(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } +} + +// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy +template<class S, class T> +void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count) +{ + for (S32 i = 0; i < count; ++i) + { + copyVec3(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } +} + +template<class S, class T> +void copyAttributeArray(Asset& asset, const Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) +{ + if (accessor.mType == TINYGLTF_TYPE_VEC2) + { + S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride; + copyVec2((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == TINYGLTF_TYPE_VEC3) + { + S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride; + copyVec3((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == TINYGLTF_TYPE_VEC4) + { + S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; + copyVec4((S*)src, dst, stride, accessor.mCount); + } + else + { + LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL; + } +} + +template <class T> +void Primitive::copyAttribute(Asset& asset, S32 accessorIdx, LLStrider<T>& dst) +{ + const Accessor& accessor = asset.mAccessors[accessorIdx]; + const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; + const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; + + if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT) + { + copyAttributeArray(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + copyAttributeArray(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + copyAttributeArray(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); + } + else + + { + LL_ERRS() << "Unsupported component type" << LL_ENDL; + } +} + +void Primitive::allocateGLResources(Asset& asset) +{ + // allocate vertex buffer + // We diverge from the intent of the GLTF format here to work with our existing render pipeline + // GLTF wants us to copy the buffer views into GPU storage as is and build render commands that source that data. + // For our engine, though, it's better to rearrange the buffers at load time into a layout that's more consistent. + // The GLTF native approach undoubtedly works well if you can count on VAOs, but VAOs perform much worse with our scenes. + + // get the number of vertices + U32 numVertices = 0; + for (auto& it : mAttributes) + { + const Accessor& accessor = asset.mAccessors[it.second]; + numVertices = accessor.mCount; + break; + } + + // get the number of indices + U32 numIndices = 0; + if (mIndices != INVALID_INDEX) + { + const Accessor& accessor = asset.mAccessors[mIndices]; + numIndices = accessor.mCount; + } + + // create vertex buffer + mVertexBuffer = new LLVertexBuffer(ATTRIBUTE_MASK); + mVertexBuffer->allocateBuffer(numVertices, numIndices); + + bool needs_color = true; + bool needs_texcoord = true; + bool needs_normal = true; + bool needs_tangent = true; + + // load vertex data + for (auto& it : mAttributes) + { + const std::string& attribName = it.first; + + // load vertex data + if (attribName == "POSITION") + { + // load position data + LLStrider<LLVector4a> dst; + mVertexBuffer->getVertexStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "NORMAL") + { + needs_normal = false; + // load normal data + LLStrider<LLVector4a> dst; + mVertexBuffer->getNormalStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "TANGENT") + { + needs_tangent = false; + // load tangent data + + LLStrider<LLVector4a> dst; + mVertexBuffer->getTangentStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "COLOR_0") + { + needs_color = false; + // load color data + + LLStrider<LLColor4U> dst; + mVertexBuffer->getColorStrider(dst); + + copyAttribute(asset, it.second, dst); + } + else if (attribName == "TEXCOORD_0") + { + needs_texcoord = false; + // load texcoord data + LLStrider<LLVector2> dst; + mVertexBuffer->getTexCoord0Strider(dst); + + LLStrider<LLVector2> tc = dst; + copyAttribute(asset, it.second, dst); + + // convert to OpenGL coordinate space + for (U32 i = 0; i < numVertices; ++i) + { + tc->mV[1] = 1.0f - tc->mV[1];; + tc++; + } + } + } + + // copy index buffer + if (mIndices != INVALID_INDEX) + { + const Accessor& accessor = asset.mAccessors[mIndices]; + const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; + const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + + const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; + + LLStrider<U16> dst; + mVertexBuffer->getIndexStrider(dst); + mIndexArray.resize(numIndices); + + if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + for (U32 i = 0; i < numIndices; ++i) + { + *(dst++) = (U16) * (U32*)src; + src += sizeof(U32); + } + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + for (U32 i = 0; i < numIndices; ++i) + { + *(dst++) = *(U16*)src; + src += sizeof(U16); + } + } + else + { + LL_ERRS("GLTF") << "Unsupported component type for indices" << LL_ENDL; + } + + U16* idx = (U16*)mVertexBuffer->getMappedIndices(); + for (U32 i = 0; i < numIndices; ++i) + { + mIndexArray[i] = idx[i]; + } + } + + // fill in default values for missing attributes + if (needs_color) + { // set default color + LLStrider<LLColor4U> dst; + mVertexBuffer->getColorStrider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLColor4U(255, 255, 255, 255); + } + } + + if (needs_texcoord) + { // set default texcoord + LLStrider<LLVector2> dst; + mVertexBuffer->getTexCoord0Strider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLVector2(0.0f, 0.0f); + } + } + + if (needs_normal) + { // set default normal + LLStrider<LLVector4a> dst; + mVertexBuffer->getNormalStrider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLVector4a(0.0f, 0.0f, 1.0f, 0.0f); + } + } + + if (needs_tangent) + { // TODO: generate tangents if needed + LLStrider<LLVector4a> dst; + mVertexBuffer->getTangentStrider(dst); + for (U32 i = 0; i < numVertices; ++i) + { + *(dst++) = LLVector4a(1.0f, 0.0f, 0.0f, 1.0f); + } + } + + mPositions.resize(numVertices); + mTexCoords.resize(numVertices); + mNormals.resize(numVertices); + mTangents.resize(numVertices); + + LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); + LLVector2* tc = (LLVector2*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TEXCOORD0)); + LLVector4a* norm = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_NORMAL)); + LLVector4a* tangent = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TANGENT)); + for (U32 i = 0; i < numVertices; ++i) + { + mPositions[i] = pos[i]; + mTexCoords[i] = tc[i]; + mNormals[i] = norm[i]; + mTangents[i] = tangent[i]; + } + createOctree(); + + mVertexBuffer->unmapBuffer(); +} + +void Primitive::createOctree() +{ + // create octree + mOctree = new LLVolumeOctree(); + + if (mMode == TINYGLTF_MODE_TRIANGLES) + { + F32 scaler = 0.25f; + + const U32 num_triangles = mVertexBuffer->getNumIndices() / 3; + // Initialize all the triangles we need + mOctreeTriangles.resize(num_triangles); + + LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); + U16* indices = (U16*)mVertexBuffer->getMappedIndices(); + + for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) + { //for each triangle + const U32 index = triangle_index * 3; + LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; + const LLVector4a& v0 = pos[indices[index]]; + const LLVector4a& v1 = pos[indices[index + 1]]; + const LLVector4a& v2 = pos[indices[index + 2]]; + + //store pointers to vertex data + tri->mV[0] = &v0; + tri->mV[1] = &v1; + tri->mV[2] = &v2; + + //store indices + tri->mIndex[0] = indices[index]; + tri->mIndex[1] = indices[index + 1]; + tri->mIndex[2] = indices[index + 2]; + + //get minimum point + LLVector4a min = v0; + min.setMin(min, v1); + min.setMin(min, v2); + + //get maximum point + LLVector4a max = v0; + max.setMax(max, v1); + max.setMax(max, v2); + + //compute center + LLVector4a center; + center.setAdd(min, max); + center.mul(0.5f); + + tri->mPositionGroup = center; + + //compute "radius" + LLVector4a size; + size.setSub(max, min); + + tri->mRadius = size.getLength3().getF32() * scaler; + + //insert + mOctree->insert(tri); + } + } + else + { + LL_ERRS() << "Unsupported Primitive mode" << LL_ENDL; + } + + //remove unneeded octree layers + while (!mOctree->balance()) {} + + //calculate AABB for each node + LLVolumeOctreeRebound rebound; + rebound.traverse(mOctree); +} + +const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out) +{ + if (mOctree.isNull()) + { + return nullptr; + } + + LLVector4a dir; + dir.setSub(end, start); + + F32 closest_t = 2.f; // must be larger than 1 + + //create a proxy LLVolumeFace for the raycast + LLVolumeFace face; + face.mPositions = mPositions.data(); + face.mTexCoords = mTexCoords.data(); + face.mNormals = mNormals.data(); + face.mTangents = mTangents.data(); + face.mIndices = mIndexArray.data(); + + face.mNumIndices = mIndexArray.size(); + face.mNumVertices = mPositions.size(); + + LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out); + intersect.traverse(mOctree); + + // null out proxy data so it doesn't get freed + face.mPositions = face.mNormals = face.mTangents = nullptr; + face.mIndices = nullptr; + face.mTexCoords = nullptr; + + return intersect.mHitTriangle; +} + +Primitive::~Primitive() +{ + mOctree = nullptr; +} + diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h new file mode 100644 index 0000000000..7c47d9dac5 --- /dev/null +++ b/indra/newview/gltf/primitive.h @@ -0,0 +1,140 @@ +#pragma once + +/** + * @file primitive.h + * @brief LL GLTF Implementation + * + * $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$ + */ + +#include "llvertexbuffer.h" +#include "llvolumeoctree.h" + +// LL GLTF Implementation +namespace LL +{ + namespace GLTF + { + class Asset; + + constexpr U32 ATTRIBUTE_MASK = + LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_TANGENT | + LLVertexBuffer::MAP_COLOR; + + class Primitive + { + public: + ~Primitive(); + + // GPU copy of mesh data + LLPointer<LLVertexBuffer> mVertexBuffer; + + // CPU copy of mesh data + std::vector<LLVector2> mTexCoords; + std::vector<LLVector4a> mNormals; + std::vector<LLVector4a> mTangents; + std::vector<LLVector4a> mPositions; + std::vector<U16> mIndexArray; + + // raycast acceleration structure + LLPointer<LLVolumeOctree> mOctree; + std::vector<LLVolumeTriangle> mOctreeTriangles; + + S32 mMaterial = -1; + U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles + U32 mGLMode = LLRender::TRIANGLES; + S32 mIndices = -1; + std::unordered_map<std::string, int> mAttributes; + + // copy the attribute in the given BufferView to the given destination + // assumes destination has enough storage for the attribute + template<class T> + void copyAttribute(Asset& asset, S32 bufferViewIdx, LLStrider<T>& dst); + + // create octree based on vertex buffer + // must be called before buffer is unmapped and after buffer is populated with good data + void createOctree(); + + //get the LLVolumeTriangle that intersects with the given line segment at the point + //closest to start. Moves end to the point of intersection. Returns nullptr if no intersection. + //Line segment must be in the same coordinate frame as this Primitive + const LLVolumeTriangle* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + LLVector4a* intersection = NULL, // return the intersection point + LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point + LLVector4a* normal = NULL, // return the surface normal at the intersection point + LLVector4a* tangent = NULL // return the surface tangent at the intersection point + ); + + const Primitive& operator=(const tinygltf::Primitive& src) + { + // load material + mMaterial = src.material; + + // load mode + mMode = src.mode; + + // load indices + mIndices = src.indices; + + // load attributes + for (auto& it : src.attributes) + { + mAttributes[it.first] = it.second; + } + + switch (mMode) + { + case TINYGLTF_MODE_POINTS: + mGLMode = LLRender::POINTS; + break; + case TINYGLTF_MODE_LINE: + mGLMode = LLRender::LINES; + break; + case TINYGLTF_MODE_LINE_LOOP: + mGLMode = LLRender::LINE_LOOP; + break; + case TINYGLTF_MODE_LINE_STRIP: + mGLMode = LLRender::LINE_STRIP; + break; + case TINYGLTF_MODE_TRIANGLES: + mGLMode = LLRender::TRIANGLES; + break; + case TINYGLTF_MODE_TRIANGLE_STRIP: + mGLMode = LLRender::TRIANGLE_STRIP; + break; + case TINYGLTF_MODE_TRIANGLE_FAN: + mGLMode = LLRender::TRIANGLE_FAN; + break; + default: + mGLMode = GL_TRIANGLES; + } + + return *this; + } + + void allocateGLResources(Asset& asset); + }; + } +} diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp new file mode 100644 index 0000000000..6c44b83646 --- /dev/null +++ b/indra/newview/gltfscenemanager.cpp @@ -0,0 +1,470 @@ +/** + * @file gltfscenemanager.cpp + * @brief Builds menus out of items. + * + * $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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "gltfscenemanager.h" +#include "llviewermenufile.h" +#include "llappviewer.h" +#include "lltinygltfhelper.h" +#include "llvertexbuffer.h" +#include "llselectmgr.h" +#include "llagent.h" +#include "llnotificationsutil.h" +#include "llvoavatarself.h" +#include "llvolumeoctree.h" +#include "gltf/asset.h" +#include "pipeline.h" +#include "llviewershadermgr.h" + + +using namespace LL; + +// temporary location of LL GLTF Implementation +using namespace LL::GLTF; + +void GLTFSceneManager::load() +{ + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + + if (obj) + { + // Load a scene from disk + LLFilePickerReplyThread::startPicker( + [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) + { + if (LLAppViewer::instance()->quitRequested()) + { + return; + } + if (filenames.size() > 0) + { + GLTFSceneManager::instance().load(filenames[0]); + } + }, + LLFilePicker::FFLOAD_GLTF, + true); + } + else + { + LLNotificationsUtil::add("GLTFPreviewSelection"); + } +} + +void GLTFSceneManager::load(const std::string& filename) +{ + tinygltf::Model model; + LLTinyGLTFHelper::loadModel(filename, model); + + LLPointer<Asset> asset = new Asset(); + *asset = model; + + asset->allocateGLResources(filename, model); + asset->updateTransforms(); + + // hang the asset off the currently selected object, or off of the avatar if no object is selected + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + + if (obj) + { // assign to self avatar + obj->mGLTFAsset = asset; + mObjects.push_back(obj); + } +} + +GLTFSceneManager::~GLTFSceneManager() +{ + mObjects.clear(); +} + +LLMatrix4a getAssetToAgentTransform(LLViewerObject* obj) +{ + LLMatrix4 root; + root.initScale(obj->getScale()); + root.rotate(obj->getRenderRotation()); + root.translate(obj->getPositionAgent()); + + LLMatrix4a mat; + mat.loadu((F32*) root.mMatrix); + + return mat; +} + +LLMatrix4a getAgentToAssetTransform(LLViewerObject* obj) +{ + LLMatrix4 root; + LLVector3 scale = obj->getScale(); + scale.mV[0] = 1.f / scale.mV[0]; + scale.mV[1] = 1.f / scale.mV[1]; + scale.mV[2] = 1.f / scale.mV[2]; + + root.translate(-obj->getPositionAgent()); + root.rotate(~obj->getRenderRotation()); + + LLMatrix4 scale_mat; + scale_mat.initScale(scale); + + root *= scale_mat; + + + LLMatrix4a mat; + mat.loadu((F32*) root.mMatrix); + + return mat; +} + +void GLTFSceneManager::renderOpaque() +{ + // for debugging, just render the whole scene as opaque + // by traversing the whole scenegraph + // Assumes camera transform is already set and + // appropriate shader is already bound + + gGL.matrixMode(LLRender::MM_MODELVIEW); + + for (U32 i = 0; i < mObjects.size(); ++i) + { + if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr) + { + mObjects.erase(mObjects.begin() + i); + --i; + continue; + } + + Asset* asset = mObjects[i]->mGLTFAsset; + + gGL.pushMatrix(); + + LLMatrix4a mat = getAssetToAgentTransform(mObjects[i]); + + LLMatrix4a modelview; + modelview.loadu(gGLModelView); + + matMul(mat, modelview, modelview); + + asset->updateRenderTransforms(modelview); + asset->renderOpaque(); + + gGL.popMatrix(); + } +} + +LLMatrix4a inverse(const LLMatrix4a& mat) +{ + glh::matrix4f m((F32*)mat.mMatrix); + m = m.inverse(); + LLMatrix4a ret; + ret.loadu(m.m); + return ret; +} + +bool GLTFSceneManager::lineSegmentIntersect(LLVOVolume* obj, Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32* node_hit, S32* primitive_hit, + LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) + +{ + // line segment intersection test + // start and end should be in agent space + // volume space and asset space should be the same coordinate frame + // results should be transformed back to agent space + + bool ret = false; + + LLVector4a local_start; + LLVector4a local_end; + + LLMatrix4a asset_to_agent = getAssetToAgentTransform(obj); + LLMatrix4a agent_to_asset = inverse(asset_to_agent); + + agent_to_asset.affineTransform(start, local_start); + agent_to_asset.affineTransform(end, local_end); + + LLVector4a p; + LLVector4a n; + LLVector2 tc; + LLVector4a tn; + + if (intersection != NULL) + { + p = *intersection; + } + + if (tex_coord != NULL) + { + tc = *tex_coord; + } + + if (normal != NULL) + { + n = *normal; + } + + if (tangent != NULL) + { + tn = *tangent; + } + + S32 hit_node_index = asset->lineSegmentIntersect(local_start, local_end, &p, &tc, &n, &tn, primitive_hit); + + if (hit_node_index >= 0) + { + local_end = p; + if (node_hit != NULL) + { + *node_hit = hit_node_index; + } + + if (intersection != NULL) + { + asset_to_agent.affineTransform(p, *intersection); + } + + if (normal != NULL) + { + LLVector3 v_n(n.getF32ptr()); + normal->load3(obj->volumeDirectionToAgent(v_n).mV); + (*normal).normalize3fast(); + } + + if (tangent != NULL) + { + LLVector3 v_tn(tn.getF32ptr()); + + LLVector4a trans_tangent; + trans_tangent.load3(obj->volumeDirectionToAgent(v_tn).mV); + + LLVector4Logical mask; + mask.clear(); + mask.setElement<3>(); + + tangent->setSelectWithMask(mask, tn, trans_tangent); + (*tangent).normalize3fast(); + } + + if (tex_coord != NULL) + { + *tex_coord = tc; + } + + ret = true; + } + + return ret; +} + +LLDrawable* GLTFSceneManager::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + BOOL pick_transparent, + BOOL pick_rigged, + BOOL pick_unselectable, + BOOL pick_reflection_probe, + S32* node_hit, // return the index of the node that was hit + S32* primitive_hit, // return the index of the primitive that was hit + LLVector4a* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector4a* normal, // return the surface normal at the intersection point + LLVector4a* tangent) // return the surface tangent at the intersection point +{ + LLDrawable* drawable = nullptr; + + LLVector4a local_end = end; + LLVector4a position; + + for (U32 i = 0; i < mObjects.size(); ++i) + { + if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr || !mObjects[i]->getVolume()) + { + mObjects.erase(mObjects.begin() + i); + --i; + continue; + } + + // temporary debug -- always double check objects that have GLTF scenes hanging off of them even if the ray doesn't intersect the object bounds + if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset, start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent)) + { + local_end = position; + if (intersection) + { + *intersection = position; + } + drawable = mObjects[i]->mDrawable; + } + } + + return drawable; +} + +void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size); + +extern LLVector4a gDebugRaycastStart; +extern LLVector4a gDebugRaycastEnd; + +void renderOctreeRaycast(const LLVector4a& start, const LLVector4a& end, const LLVolumeOctree* octree); + +void renderAssetDebug(LLViewerObject* obj, Asset* asset) +{ + // render debug + // assumes appropriate shader is already bound + // assumes modelview matrix is already set + + gGL.pushMatrix(); + + // get raycast in asset space + LLMatrix4a agent_to_asset = getAgentToAssetTransform(obj); + + LLVector4a start; + LLVector4a end; + + agent_to_asset.affineTransform(gDebugRaycastStart, start); + agent_to_asset.affineTransform(gDebugRaycastEnd, end); + + + for (auto& node : asset->mNodes) + { + Mesh& mesh = asset->mMeshes[node.mMesh]; + + if (node.mMesh != INVALID_INDEX) + { + gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + + // draw bounding box of mesh primitives + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES)) + { + gGL.color3f(0.f, 1.f, 1.f); + + for (auto& primitive : mesh.mPrimitives) + { + auto* listener = (LLVolumeOctreeListener*) primitive.mOctree->getListener(0); + + LLVector4a center = listener->mBounds[0]; + LLVector4a size = listener->mBounds[1]; + + drawBoxOutline(center, size); + } + } + +#if 0 + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) + { + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + // convert raycast to node local space + LLVector4a local_start; + LLVector4a local_end; + + node.mAssetMatrixInv.affineTransform(start, local_start); + node.mAssetMatrixInv.affineTransform(end, local_end); + + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mOctree.notNull()) + { + renderOctreeRaycast(local_start, local_end, primitive.mOctree); + } + } + + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +#endif + } + } + + gGL.popMatrix(); +} + +void GLTFSceneManager::renderDebug() +{ + if (!gPipeline.hasRenderDebugMask( + LLPipeline::RENDER_DEBUG_BBOXES | + LLPipeline::RENDER_DEBUG_RAYCAST)) + { + return; + } + + gDebugProgram.bind(); + + LLGLDisable cullface(GL_CULL_FACE); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gPipeline.disableLights(); + + for (auto& obj : mObjects) + { + if (obj->isDead() || obj->mGLTFAsset == nullptr) + { + continue; + } + + Asset* asset = obj->mGLTFAsset; + + + LLMatrix4a mat = getAssetToAgentTransform(obj); + + LLMatrix4a modelview; + modelview.loadu(gGLModelView); + + matMul(mat, modelview, modelview); + + asset->updateRenderTransforms(modelview); + renderAssetDebug(obj, asset); + } + + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) + { + S32 node_hit = -1; + S32 primitive_hit = -1; + LLVector4a intersection; + + LLDrawable* drawable = lineSegmentIntersect(gDebugRaycastStart, gDebugRaycastEnd, TRUE, TRUE, TRUE, TRUE, &node_hit, &primitive_hit, &intersection, nullptr, nullptr, nullptr); + + if (drawable) + { + gGL.pushMatrix(); + Asset* asset = drawable->getVObj()->mGLTFAsset; + Node* node = &asset->mNodes[node_hit]; + Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit]; + + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + gGL.color3f(1, 0, 1); + drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f)); + + gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix); + + + + auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0); + drawBoxOutline(listener->mBounds[0], listener->mBounds[1]); + + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.popMatrix(); + } + } + gDebugProgram.unbind(); +} + diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h new file mode 100644 index 0000000000..50e1dd93da --- /dev/null +++ b/indra/newview/gltfscenemanager.h @@ -0,0 +1,64 @@ +#pragma once + +/** + * @file gltfscenemanager.h + * @brief Builds menus out of items. + * + * $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$ + */ + +#include "llsingleton.h" +#include "llviewerobject.h" + +namespace LL +{ + class GLTFSceneManager : public LLSimpleton<GLTFSceneManager> + { + public: + ~GLTFSceneManager(); + // load GLTF file from disk + void load(); // open filepicker to choose asset + void load(const std::string& filename); // load asset from filename + void renderOpaque(); + + LLDrawable* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + BOOL pick_transparent, + BOOL pick_rigged, + BOOL pick_unselectable, + BOOL pick_reflection_probe, + S32* node_hit, // return the index of the node that was hit + S32* primitive_hit, // return the index of the primitive that was hit + LLVector4a* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector4a* normal, // return the surface normal at the intersection point + LLVector4a* tangent); // return the surface tangent at the intersection point + + bool lineSegmentIntersect(LLVOVolume* obj, GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32* face_hitp, S32* primitive_hitp, + LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); + + void renderDebug(); + + std::vector<LLPointer<LLViewerObject>> mObjects; + }; +} + + diff --git a/indra/newview/llagentbenefits.cpp b/indra/newview/llagentbenefits.cpp index 2d219735a0..29b0a4612f 100644 --- a/indra/newview/llagentbenefits.cpp +++ b/indra/newview/llagentbenefits.cpp @@ -25,6 +25,7 @@ #include "llviewerprecompiledheaders.h" #include "llagentbenefits.h" +#include "llviewertexture.h" LLAgentBenefits::LLAgentBenefits(): m_initalized(false), @@ -95,6 +96,26 @@ bool LLAgentBenefits::init(const LLSD& benefits_sd) return false; } + if (benefits_sd.has("large_texture_upload_cost")) + { + LLSD large_texture_cost = benefits_sd.get("large_texture_upload_cost"); + if (large_texture_cost.isArray()) + { + LLSD::array_const_iterator end = large_texture_cost.endArray(); + LLSD::array_const_iterator it = large_texture_cost.beginArray(); + for (; it != end; ++it) + { + m_2k_texture_upload_cost.push_back(it->asInteger()); + } + std::sort(m_2k_texture_upload_cost.begin(), m_2k_texture_upload_cost.end()); + } + } + + if (m_2k_texture_upload_cost.empty()) + { + m_2k_texture_upload_cost.push_back(m_texture_upload_cost); + } + // FIXME PREMIUM - either use this field or get rid of it m_initalized = true; return true; @@ -140,12 +161,68 @@ S32 LLAgentBenefits::getTextureUploadCost() const return m_texture_upload_cost; } +S32 LLAgentBenefits::getTextureUploadCost(const LLViewerTexture* tex) const +{ + if (tex) + { + S32 area = tex->getFullHeight() * tex->getFullWidth(); + if (area >= MIN_2K_TEXTURE_AREA) + { + return get2KTextureUploadCost(area); + } + else + { + return getTextureUploadCost(); + } + } + return getTextureUploadCost(); +} + +S32 LLAgentBenefits::getTextureUploadCost(const LLImageBase* tex) const +{ + if (tex) + { + S32 area = tex->getHeight() * tex->getWidth(); + if (area >= MIN_2K_TEXTURE_AREA) + { + return get2KTextureUploadCost(area); + } + else + { + return getTextureUploadCost(); + } + } + return getTextureUploadCost(); +} + +S32 LLAgentBenefits::get2KTextureUploadCost(S32 area) const +{ + if (m_2k_texture_upload_cost.empty()) + { + return m_texture_upload_cost; + } + const S32 TEXTURE_SEGMENTS = 1024; + if (m_2k_texture_upload_cost.size() == TEXTURE_SEGMENTS) + { + S32 index = (S32)llceil(sqrt((F32)area)); + // 1..1024 pixels uses m_texture_upload_cost + // 1025..2048 uses m_2k_texture_upload_cost + // Translate 1025..2048 to 0..1023 of the + // cost array + const S32 PIXELS_TO_2K_ARRAY_TRANLATE = 1025; + index -= PIXELS_TO_2K_ARRAY_TRANLATE; + index = llclamp(index, 0, TEXTURE_SEGMENTS - 1); + return m_2k_texture_upload_cost[index]; + } + return m_2k_texture_upload_cost[0]; +} + bool LLAgentBenefits::findUploadCost(LLAssetType::EType& asset_type, S32& cost) const { bool succ = false; if (asset_type == LLAssetType::AT_TEXTURE) { - cost = getTextureUploadCost(); + cost = getTextureUploadCost(); succ = true; } else if (asset_type == LLAssetType::AT_SOUND) diff --git a/indra/newview/llagentbenefits.h b/indra/newview/llagentbenefits.h index 48aa6bd869..962d0f9371 100644 --- a/indra/newview/llagentbenefits.h +++ b/indra/newview/llagentbenefits.h @@ -30,9 +30,14 @@ #include "llsd.h" #include "llassettype.h" +class LLViewerTexture; +class LLImageBase; + class LLAgentBenefits { public: + static constexpr S32 MIN_2K_TEXTURE_AREA = 1024 * 1024 + 1; + LLAgentBenefits(); ~LLAgentBenefits(); LOG_CLASS(LLAgentBenefits); @@ -47,6 +52,9 @@ public: S32 getPicksLimit() const; S32 getSoundUploadCost() const; S32 getTextureUploadCost() const; + S32 getTextureUploadCost(const LLViewerTexture* tex) const; + S32 getTextureUploadCost(const LLImageBase* tex) const; + S32 get2KTextureUploadCost(S32 area) const; bool findUploadCost(LLAssetType::EType& asset_type, S32& cost) const; @@ -59,6 +67,7 @@ private: S32 m_picks_limit; S32 m_sound_upload_cost; S32 m_texture_upload_cost; + std::vector<S32> m_2k_texture_upload_cost; bool m_initalized; }; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6e176183d8..46b95601af 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -229,6 +229,7 @@ #include "pipeline.h" #include "llgesturemgr.h" #include "llsky.h" +#include "llvlcomposition.h" #include "llvlmanager.h" #include "llviewercamera.h" #include "lldrawpoolbump.h" @@ -239,6 +240,8 @@ #include "llavatariconctrl.h" #include "llgroupiconctrl.h" #include "llviewerassetstats.h" +#include "gltfscenemanager.h" + #include "workqueue.h" using namespace LL; @@ -1279,6 +1282,8 @@ bool LLAppViewer::init() LLWorld::createInstance(); LLSelectMgr::createInstance(); LLViewerCamera::createInstance(); + LL::GLTFSceneManager::createInstance(); + #if LL_WINDOWS if (!mSecondInstance) @@ -2154,7 +2159,7 @@ bool LLAppViewer::cleanup() ll_close_fail_log(); LLError::LLCallStacks::cleanup(); - + LL::GLTFSceneManager::deleteSingleton(); LLEnvironment::deleteSingleton(); LLSelectMgr::deleteSingleton(); LLViewerEventRecorder::deleteSingleton(); diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 970e8c8b2a..fc182fc2e2 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -286,6 +286,7 @@ public: ANIMATED_CHILD = 0x01000000, ACTIVE_CHILD = 0x02000000, FOR_UNLOAD = 0x04000000, //should be unload from memory + MIRROR = 0x08000000, // Used as a mirror, needs a hero probe position to be calculated. } EDrawableFlags; public: diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 50210b06c4..556760632a 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -567,14 +567,19 @@ void LLRenderPass::pushRiggedMaskBatches(U32 type, bool texture, bool batch_text void LLRenderPass::applyModelMatrix(const LLDrawInfo& params) { - if (params.mModelMatrix != gGLLastMatrix) + applyModelMatrix(params.mModelMatrix); +} + +void LLRenderPass::applyModelMatrix(const LLMatrix4* model_matrix) +{ + if (model_matrix != gGLLastMatrix) { - gGLLastMatrix = params.mModelMatrix; + gGLLastMatrix = model_matrix; gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.loadMatrix(gGLModelView); - if (params.mModelMatrix) + if (model_matrix) { - gGL.multMatrix((GLfloat*) params.mModelMatrix->mMatrix); + gGL.multMatrix((GLfloat*) model_matrix->mMatrix); } gPipeline.mMatrixOpCount++; } @@ -746,6 +751,7 @@ void LLRenderPass::pushUntexturedGLTFBatches(U32 type) } } +// static void LLRenderPass::pushGLTFBatch(LLDrawInfo& params) { auto& mat = params.mGLTFMaterial; @@ -764,6 +770,7 @@ void LLRenderPass::pushGLTFBatch(LLDrawInfo& params) teardown_texture_matrix(params); } +// static void LLRenderPass::pushUntexturedGLTFBatch(LLDrawInfo& params) { auto& mat = params.mGLTFMaterial; @@ -825,6 +832,7 @@ void LLRenderPass::pushUntexturedRiggedGLTFBatches(U32 type) } +// static void LLRenderPass::pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId) { if (params.mAvatar.notNull() && (lastAvatar != params.mAvatar || lastMeshId != params.mSkinInfo->mHash)) @@ -837,6 +845,7 @@ void LLRenderPass::pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvat pushGLTFBatch(params); } +// static void LLRenderPass::pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId) { if (params.mAvatar.notNull() && (lastAvatar != params.mAvatar || lastMeshId != params.mSkinInfo->mHash)) diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index 0925a01439..c69f386c6d 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -58,9 +58,9 @@ public: POOL_SIMPLE, POOL_FULLBRIGHT, POOL_BUMP, - POOL_TERRAIN, POOL_MATERIALS, POOL_GLTF_PBR, + POOL_TERRAIN, POOL_GRASS, POOL_GLTF_PBR_ALPHA_MASK, POOL_TREE, @@ -349,8 +349,8 @@ public: void resetDrawOrders() { } static void applyModelMatrix(const LLDrawInfo& params); - // Use before a non-GLTF batch if it is interleaved with GLTF batches that share the same shader - static void resetGLTFTextureTransform(); + // For rendering that doesn't use LLDrawInfo for some reason + static void applyModelMatrix(const LLMatrix4* model_matrix); void pushBatches(U32 type, bool texture = true, bool batch_textures = false); void pushUntexturedBatches(U32 type); @@ -374,10 +374,10 @@ public: void pushUntexturedRiggedGLTFBatches(U32 type); // push a single GLTF draw call - void pushGLTFBatch(LLDrawInfo& params); - void pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId); - void pushUntexturedGLTFBatch(LLDrawInfo& params); - void pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId); + static void pushGLTFBatch(LLDrawInfo& params); + static void pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId); + static void pushUntexturedGLTFBatch(LLDrawInfo& params); + static void pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId); void pushMaskBatches(U32 type, bool texture = true, bool batch_textures = false); void pushRiggedMaskBatches(U32 type, bool texture = true, bool batch_textures = false); diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp index 86b790e2c5..a32382af92 100644 --- a/indra/newview/lldrawpoolpbropaque.cpp +++ b/indra/newview/lldrawpoolpbropaque.cpp @@ -30,6 +30,7 @@ #include "lldrawpoolpbropaque.h" #include "llviewershadermgr.h" #include "pipeline.h" +#include "gltfscenemanager.h" LLDrawPoolGLTFPBR::LLDrawPoolGLTFPBR(U32 type) : LLRenderPass(type) @@ -54,8 +55,11 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass) llassert(!LLPipeline::sRenderingHUDs); gDeferredPBROpaqueProgram.bind(); + + LL::GLTFSceneManager::instance().renderOpaque(); pushGLTFBatches(mRenderType); + gDeferredPBROpaqueProgram.bind(true); pushRiggedGLTFBatches(mRenderType + 1); } diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 77189dceae..2d198c5b4b 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -54,8 +54,9 @@ const F32 DETAIL_SCALE = 1.f/16.f; int DebugDetailMap = 0; -S32 LLDrawPoolTerrain::sDetailMode = 1; +S32 LLDrawPoolTerrain::sPBRDetailMode = 0; F32 LLDrawPoolTerrain::sDetailScale = DETAIL_SCALE; +F32 LLDrawPoolTerrain::sPBRDetailScale = DETAIL_SCALE; static LLGLSLShader* sShader = NULL; static LLTrace::BlockTimerStatHandle FTM_SHADOW_TERRAIN("Terrain Shadow"); @@ -66,7 +67,8 @@ LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerTexture *texturep) : { // Hack! sDetailScale = 1.f/gSavedSettings.getF32("RenderTerrainScale"); - sDetailMode = gSavedSettings.getS32("RenderTerrainDetail"); + sPBRDetailScale = 1.f/gSavedSettings.getF32("RenderTerrainPBRScale"); + sPBRDetailMode = gSavedSettings.getS32("RenderTerrainPBRDetail"); mAlphaRampImagep = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD); //gGL.getTexUnit(0)->bind(mAlphaRampImagep.get()); @@ -105,13 +107,7 @@ U32 LLDrawPoolTerrain::getVertexDataMask() void LLDrawPoolTerrain::prerender() { - sDetailMode = gSavedSettings.getS32("RenderTerrainDetail"); -} - -//static -S32 LLDrawPoolTerrain::getDetailMode() -{ - return sDetailMode; + sPBRDetailMode = gSavedSettings.getS32("RenderTerrainPBRDetail"); } void LLDrawPoolTerrain::boostTerrainDetailTextures() @@ -121,8 +117,38 @@ void LLDrawPoolTerrain::boostTerrainDetailTextures() LLVLComposition *compp = regionp->getComposition(); for (S32 i = 0; i < 4; i++) { - compp->mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN); - compp->mDetailTextures[i]->addTextureStats(1024.f * 1024.f); + constexpr LLGLTexture::EBoostLevel level = LLGLTexture::BOOST_TERRAIN; + constexpr float stats = 1024.f * 1024.f; + + LLPointer<LLViewerFetchedTexture>& tex = compp->mDetailTextures[i]; + llassert(tex.notNull()); + tex->setBoostLevel(level); + tex->addTextureStats(stats); + + LLPointer<LLFetchedGLTFMaterial>& fetched_material = compp->mDetailMaterials[i]; + if (fetched_material) + { + if (fetched_material->mBaseColorTexture) + { + fetched_material->mBaseColorTexture->setBoostLevel(level); + fetched_material->mBaseColorTexture->addTextureStats(stats); + } + if (fetched_material->mNormalTexture) + { + fetched_material->mNormalTexture->setBoostLevel(level); + fetched_material->mNormalTexture->addTextureStats(stats); + } + if (fetched_material->mMetallicRoughnessTexture) + { + fetched_material->mMetallicRoughnessTexture->setBoostLevel(level); + fetched_material->mMetallicRoughnessTexture->addTextureStats(stats); + } + if (fetched_material->mEmissiveTexture) + { + fetched_material->mEmissiveTexture->setBoostLevel(level); + fetched_material->mEmissiveTexture->addTextureStats(stats); + } + } } } @@ -130,10 +156,6 @@ void LLDrawPoolTerrain::beginDeferredPass(S32 pass) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_TERRAIN); LLFacePool::beginRenderPass(pass); - - sShader = &gDeferredTerrainProgram; - - sShader->bind(); } void LLDrawPoolTerrain::endDeferredPass(S32 pass) @@ -204,19 +226,8 @@ void LLDrawPoolTerrain::drawLoop() { LLFace *facep = *iter; - LLMatrix4* model_matrix = &(facep->getDrawable()->getRegion()->mRenderMatrix); - - if (model_matrix != gGLLastMatrix) - { - llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); - gGLLastMatrix = model_matrix; - gGL.loadMatrix(gGLModelView); - if (model_matrix) - { - gGL.multMatrix((GLfloat*) model_matrix->mMatrix); - } - gPipeline.mMatrixOpCount++; - } + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); + LLRenderPass::applyModelMatrix(&facep->getDrawable()->getRegion()->mRenderMatrix); facep->renderIndexed(); } @@ -225,9 +236,34 @@ void LLDrawPoolTerrain::drawLoop() void LLDrawPoolTerrain::renderFullShader() { + const BOOL use_local_materials = gLocalTerrainMaterials.materialsReady(true, false); + // Hack! Get the region that this draw pool is rendering from! + LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion(); + LLVLComposition *compp = regionp->getComposition(); + const BOOL use_textures = !use_local_materials && (compp->getMaterialType() == LLTerrainMaterials::Type::TEXTURE); + + if (use_textures) + { + // Use textures + sShader = &gDeferredTerrainProgram; + sShader->bind(); + renderFullShaderTextures(); + } + else + { + // Use materials + sShader = &gDeferredPBRTerrainProgram; + sShader->bind(); + renderFullShaderPBR(use_local_materials); + } +} + +void LLDrawPoolTerrain::renderFullShaderTextures() +{ // Hack! Get the region that this draw pool is rendering from! LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion(); LLVLComposition *compp = regionp->getComposition(); + LLViewerTexture *detail_texture0p = compp->mDetailTextures[0]; LLViewerTexture *detail_texture1p = compp->mDetailTextures[1]; LLViewerTexture *detail_texture2p = compp->mDetailTextures[2]; @@ -322,6 +358,236 @@ void LLDrawPoolTerrain::renderFullShader() gGL.getTexUnit(detail0)->activate(); } +// *TODO: Investigate use of bindFast for PBR terrain textures +void LLDrawPoolTerrain::renderFullShaderPBR(BOOL local_materials) +{ + // Hack! Get the region that this draw pool is rendering from! + LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion(); + LLVLComposition *compp = regionp->getComposition(); + LLPointer<LLFetchedGLTFMaterial> (*fetched_materials)[LLVLComposition::ASSET_COUNT] = &compp->mDetailMaterials; + + constexpr U32 terrain_material_count = LLVLComposition::ASSET_COUNT; +#ifdef SHOW_ASSERT + constexpr U32 shader_material_count = 1 + LLViewerShaderMgr::TERRAIN_DETAIL3_BASE_COLOR - LLViewerShaderMgr::TERRAIN_DETAIL0_BASE_COLOR; + llassert(shader_material_count == terrain_material_count); +#endif + + if (local_materials) + { + // Override region terrain with the global local override terrain + fetched_materials = &gLocalTerrainMaterials.mDetailMaterials; + } + const LLGLTFMaterial* materials[terrain_material_count]; + for (U32 i = 0; i < terrain_material_count; ++i) + { + materials[i] = (*fetched_materials)[i].get(); + if (!materials[i]) { materials[i] = &LLGLTFMaterial::sDefault; } + } + + S32 detail_basecolor[terrain_material_count]; + S32 detail_normal[terrain_material_count]; + S32 detail_metalrough[terrain_material_count]; + S32 detail_emissive[terrain_material_count]; + + for (U32 i = 0; i < terrain_material_count; ++i) + { + LLViewerTexture* detail_basecolor_texturep = nullptr; + LLViewerTexture* detail_normal_texturep = nullptr; + LLViewerTexture* detail_metalrough_texturep = nullptr; + LLViewerTexture* detail_emissive_texturep = nullptr; + + const LLFetchedGLTFMaterial* fetched_material = (*fetched_materials)[i].get(); + if (fetched_material) + { + detail_basecolor_texturep = fetched_material->mBaseColorTexture; + detail_normal_texturep = fetched_material->mNormalTexture; + detail_metalrough_texturep = fetched_material->mMetallicRoughnessTexture; + detail_emissive_texturep = fetched_material->mEmissiveTexture; + } + + detail_basecolor[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_BASE_COLOR + i); + if (detail_basecolor_texturep) + { + gGL.getTexUnit(detail_basecolor[i])->bind(detail_basecolor_texturep); + } + else + { + gGL.getTexUnit(detail_basecolor[i])->bind(LLViewerFetchedTexture::sWhiteImagep); + } + gGL.getTexUnit(detail_basecolor[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP); + gGL.getTexUnit(detail_basecolor[i])->activate(); + + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_NORMAL) + { + detail_normal[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_NORMAL + i); + if (detail_normal_texturep) + { + gGL.getTexUnit(detail_normal[i])->bind(detail_normal_texturep); + } + else + { + gGL.getTexUnit(detail_normal[i])->bind(LLViewerFetchedTexture::sFlatNormalImagep); + } + gGL.getTexUnit(detail_normal[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP); + gGL.getTexUnit(detail_normal[i])->activate(); + } + + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + { + detail_metalrough[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_METALLIC_ROUGHNESS + i); + if (detail_metalrough_texturep) + { + gGL.getTexUnit(detail_metalrough[i])->bind(detail_metalrough_texturep); + } + else + { + gGL.getTexUnit(detail_metalrough[i])->bind(LLViewerFetchedTexture::sWhiteImagep); + } + gGL.getTexUnit(detail_metalrough[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP); + gGL.getTexUnit(detail_metalrough[i])->activate(); + } + + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE) + { + detail_emissive[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_EMISSIVE + i); + if (detail_emissive_texturep) + { + gGL.getTexUnit(detail_emissive[i])->bind(detail_emissive_texturep); + } + else + { + gGL.getTexUnit(detail_emissive[i])->bind(LLViewerFetchedTexture::sWhiteImagep); + } + gGL.getTexUnit(detail_emissive[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP); + gGL.getTexUnit(detail_emissive[i])->activate(); + } + } + + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + llassert(shader); + + + // *TODO: Figure out why this offset is *sometimes* producing seams at the + // region edge, and repeat jumps when crossing regions, when + // RenderTerrainPBRScale is not a factor of the region scale. + LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal(); + F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sPBRDetailScale)*sPBRDetailScale; + F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sPBRDetailScale)*sPBRDetailScale; + + LLGLTFMaterial::TextureTransform base_color_transform; + base_color_transform.mScale = LLVector2(sPBRDetailScale, sPBRDetailScale); + base_color_transform.mOffset = LLVector2(offset_x, offset_y); + F32 base_color_packed[8]; + base_color_transform.getPacked(base_color_packed); + // *HACK: Use the same texture repeats for all PBR terrain textures for now + // (not compliant with KHR texture transform spec) + shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed); + + LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater(); + + // + // Alpha Ramp + // + S32 alpha_ramp = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); + gGL.getTexUnit(alpha_ramp)->bind(m2DAlphaRampImagep); + gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + + // + // GLTF uniforms + // + + LLColor4 base_color_factors[terrain_material_count]; + F32 metallic_factors[terrain_material_count]; + F32 roughness_factors[terrain_material_count]; + LLColor3 emissive_colors[terrain_material_count]; + F32 minimum_alphas[terrain_material_count]; + for (U32 i = 0; i < terrain_material_count; ++i) + { + const LLGLTFMaterial* material = materials[i]; + + base_color_factors[i] = material->mBaseColor; + metallic_factors[i] = material->mMetallicFactor; + roughness_factors[i] = material->mRoughnessFactor; + emissive_colors[i] = material->mEmissiveColor; + // glTF 2.0 Specification 3.9.4. Alpha Coverage + // mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK + // Use 0 here due to GLTF terrain blending (LLGLTFMaterial::bind uses + // -1 for easier debugging) + F32 min_alpha = -0.0f; + if (material->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK) + { + // dividing the alpha cutoff by transparency here allows the shader to compare against + // the alpha value of the texture without needing the transparency value + min_alpha = material->mAlphaCutoff/material->mBaseColor.mV[3]; + } + minimum_alphas[i] = min_alpha; + } + shader->uniform4fv(LLShaderMgr::TERRAIN_BASE_COLOR_FACTORS, terrain_material_count, (F32*)base_color_factors); + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + { + shader->uniform4f(LLShaderMgr::TERRAIN_METALLIC_FACTORS, metallic_factors[0], metallic_factors[1], metallic_factors[2], metallic_factors[3]); + shader->uniform4f(LLShaderMgr::TERRAIN_ROUGHNESS_FACTORS, roughness_factors[0], roughness_factors[1], roughness_factors[2], roughness_factors[3]); + } + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE) + { + shader->uniform3fv(LLShaderMgr::TERRAIN_EMISSIVE_COLORS, terrain_material_count, (F32*)emissive_colors); + } + shader->uniform4f(LLShaderMgr::TERRAIN_MINIMUM_ALPHAS, minimum_alphas[0], minimum_alphas[1], minimum_alphas[2], minimum_alphas[3]); + + // GL_BLEND disabled by default + drawLoop(); + + // Disable multitexture + + sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); + + gGL.getTexUnit(alpha_ramp)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(alpha_ramp)->disable(); + gGL.getTexUnit(alpha_ramp)->activate(); + + for (U32 i = 0; i < terrain_material_count; ++i) + { + sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_BASE_COLOR + i); + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_NORMAL) + { + sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_NORMAL + i); + } + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + { + sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_METALLIC_ROUGHNESS + i); + } + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE) + { + sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_EMISSIVE + i); + } + + gGL.getTexUnit(detail_basecolor[i])->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(detail_basecolor[i])->disable(); + gGL.getTexUnit(detail_basecolor[i])->activate(); + + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_NORMAL) + { + gGL.getTexUnit(detail_normal[i])->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(detail_normal[i])->disable(); + gGL.getTexUnit(detail_normal[i])->activate(); + } + + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS) + { + gGL.getTexUnit(detail_metalrough[i])->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(detail_metalrough[i])->disable(); + gGL.getTexUnit(detail_metalrough[i])->activate(); + } + + if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE) + { + gGL.getTexUnit(detail_emissive[i])->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(detail_emissive[i])->disable(); + gGL.getTexUnit(detail_emissive[i])->activate(); + } + } +} + void LLDrawPoolTerrain::hilightParcelOwners() { { //use fullbright shader for highlighting diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h index 03bef31541..13f031c8e7 100644 --- a/indra/newview/lldrawpoolterrain.h +++ b/indra/newview/lldrawpoolterrain.h @@ -37,13 +37,12 @@ public: { VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TANGENT | // Only PBR terrain uses this currently LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1 }; virtual U32 getVertexDataMask(); - static S32 getDetailMode(); - LLDrawPoolTerrain(LLViewerTexture *texturep); virtual ~LLDrawPoolTerrain(); @@ -67,8 +66,9 @@ public: LLPointer<LLViewerTexture> m2DAlphaRampImagep; LLPointer<LLViewerTexture> mAlphaNoiseImagep; - static S32 sDetailMode; - static F32 sDetailScale; // meters per texture + static S32 sPBRDetailMode; + static F32 sDetailScale; // textures per meter + static F32 sPBRDetailScale; // textures per meter protected: void boostTerrainDetailTextures(); @@ -79,6 +79,8 @@ protected: void renderFull2TU(); void renderFull4TU(); void renderFullShader(); + void renderFullShaderTextures(); + void renderFullShaderPBR(BOOL local_materials = false); void drawLoop(); private: diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 9dcbc48697..50c4a2c1b3 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -85,17 +85,8 @@ void LLDrawPoolTree::renderDeferred(S32 pass) { LLMatrix4* model_matrix = &(face->getDrawable()->getRegion()->mRenderMatrix); - if (model_matrix != gGLLastMatrix) - { - gGLLastMatrix = model_matrix; - gGL.loadMatrix(gGLModelView); - if (model_matrix) - { - llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); - gGL.multMatrix((GLfloat*)model_matrix->mMatrix); - } - gPipeline.mMatrixOpCount++; - } + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); + LLRenderPass::applyModelMatrix(model_matrix); buff->setBuffer(); buff->drawRange(LLRender::TRIANGLES, 0, buff->getNumVerts() - 1, buff->getNumIndices(), 0); diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index b14235f25c..68be354533 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -44,6 +44,7 @@ #include "llsky.h" #include "llvowlsky.h" #include "llsettingsvo.h" +#include "llviewercontrol.h" extern BOOL gCubeSnapshot; @@ -127,6 +128,19 @@ void LLDrawPoolWLSky::renderDome(const LLVector3& camPosLocal, F32 camHeightLoca gGL.popMatrix(); } +extern LLPointer<LLImageGL> gEXRImage; + +static bool use_hdri_sky() +{ + static LLCachedControl<F32> hdri_split(gSavedSettings, "RenderHDRISplitScreen", 1.f); + static LLCachedControl<bool> irradiance_only(gSavedSettings, "RenderHDRIIrradianceOnly", false); + + return gCubeSnapshot && (!irradiance_only || !gPipeline.mReflectionMapManager.isRadiancePass()) ? gEXRImage.notNull() : // always use HDRI for reflection probes when available + gEXRImage.notNull() ? hdri_split > 0.f : // fallback to EEP sky when split screen is zero + false; // no HDRI available, always use EEP sky + +} + void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 camHeightLocal) const { if (!gSky.mVOSkyp) @@ -138,9 +152,34 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)) { - LLGLSPipelineDepthTestSkyBox sky(true, true); + if (use_hdri_sky()) + { + sky_shader = &gEnvironmentMapProgram; + sky_shader->bind(); + S32 idx = sky_shader->enableTexture(LLShaderMgr::ENVIRONMENT_MAP); + if (idx > -1) + { + gGL.getTexUnit(idx)->bind(gEXRImage); + } + + static LLCachedControl<F32> hdri_exposure(gSavedSettings, "RenderHDRIExposure", 0.0f); + static LLCachedControl<F32> hdri_rotation(gSavedSettings, "RenderHDRIRotation", 0.f); + static LLCachedControl<F32> hdri_split(gSavedSettings, "RenderHDRISplitScreen", 1.f); + static LLStaticHashedString hdri_split_screen("hdri_split_screen"); - sky_shader->bind(); + LLMatrix3 rot; + rot.setRot(0.f, hdri_rotation*DEG_TO_RAD, 0.f); + + sky_shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, powf(2.f, hdri_exposure)); + sky_shader->uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, GL_FALSE, (F32*) rot.mMatrix); + sky_shader->uniform1f(hdri_split_screen, gCubeSnapshot ? 1.f : hdri_split); + } + else + { + sky_shader->bind(); + } + + LLGLSPipelineDepthTestSkyBox sky(true, true); sky_shader->uniform1i(LLShaderMgr::CUBE_SNAPSHOT, gCubeSnapshot ? 1 : 0); @@ -180,7 +219,7 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca void LLDrawPoolWLSky::renderStarsDeferred(const LLVector3& camPosLocal) const { - if (!gSky.mVOSkyp) + if (!gSky.mVOSkyp || use_hdri_sky()) { return; } @@ -251,6 +290,11 @@ void LLDrawPoolWLSky::renderStarsDeferred(const LLVector3& camPosLocal) const void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32 camHeightLocal, LLGLSLShader* cloudshader) const { + if (use_hdri_sky()) + { + return; + } + if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS) && gSky.mVOSkyp && gSky.mVOSkyp->getCloudNoiseTex()) { LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); @@ -310,7 +354,7 @@ void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32 void LLDrawPoolWLSky::renderHeavenlyBodies() { - if (!gSky.mVOSkyp) return; + if (!gSky.mVOSkyp || use_hdri_sky()) return; LLGLSPipelineBlendSkyBox gls_skybox(true, true); // SL-14113 we need moon to write to depth to clip stars behind @@ -438,8 +482,6 @@ void LLDrawPoolWLSky::renderDeferred(S32 pass) const F32 camHeightLocal = LLEnvironment::instance().getCamHeight(); - gGL.setColorMask(true, false); - LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin(); if (gPipeline.canUseWindLightShaders()) @@ -456,7 +498,6 @@ void LLDrawPoolWLSky::renderDeferred(S32 pass) renderSkyCloudsDeferred(origin, camHeightLocal, cloud_shader); } } - gGL.setColorMask(true, true); } diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 425acd3392..24818241ab 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -118,6 +118,8 @@ BOOL LLViewerDynamicTexture::render() //----------------------------------------------------------------------------- void LLViewerDynamicTexture::preRender(BOOL clear_depth) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + //use the bottom left corner mOrigin.set(0, 0); @@ -181,19 +183,26 @@ void LLViewerDynamicTexture::postRender(BOOL success) //----------------------------------------------------------------------------- BOOL LLViewerDynamicTexture::updateAllInstances() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + sNumRenders = 0; if (gGLManager.mIsDisabled) { return TRUE; } - bool use_fbo = gPipeline.mBake.isComplete() && !gGLManager.mIsAMD; + LLRenderTarget& bake_target = gPipeline.mAuxillaryRT.deferredScreen; - if (use_fbo) - { - gPipeline.mBake.bindTarget(); - gPipeline.mBake.clear(); - } + if (!bake_target.isComplete()) + { + llassert(false); + return FALSE; + } + llassert(bake_target.getWidth() >= LLPipeline::MAX_BAKE_WIDTH); + llassert(bake_target.getHeight() >= LLPipeline::MAX_BAKE_WIDTH); + + bake_target.bindTarget(); + bake_target.clear(); LLGLSLShader::unbind(); LLVertexBuffer::unbind(); @@ -208,11 +217,13 @@ BOOL LLViewerDynamicTexture::updateAllInstances() LLViewerDynamicTexture *dynamicTexture = *iter; if (dynamicTexture->needsRender()) { + llassert(dynamicTexture->getFullWidth() <= LLPipeline::MAX_BAKE_WIDTH); + llassert(dynamicTexture->getFullHeight() <= LLPipeline::MAX_BAKE_WIDTH); + glClear(GL_DEPTH_BUFFER_BIT); - gDepthDirty = TRUE; gGL.color4f(1,1,1,1); - dynamicTexture->setBoundTarget(use_fbo ? &gPipeline.mBake : nullptr); + dynamicTexture->setBoundTarget(&bake_target); dynamicTexture->preRender(); // Must be called outside of startRender() result = FALSE; if (dynamicTexture->render()) @@ -229,10 +240,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances() } } - if (use_fbo) - { - gPipeline.mBake.flush(); - } + bake_target.flush(); gGL.flush(); diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 60c2682078..0b535e15b0 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -1675,8 +1675,6 @@ void LLEnvironment::update(const LLViewerCamera * cam) updateSettingsUniforms(); - // *TODO: potential optimization - this block may only need to be - // executed some of the time. For example for water shaders only. { LLViewerShaderMgr::shader_iter shaders_iter, end_shaders; end_shaders = LLViewerShaderMgr::instance()->endShaders(); @@ -1687,6 +1685,10 @@ void LLEnvironment::update(const LLViewerCamera * cam) || shaders_iter->mShaderGroup == LLGLSLShader::SG_WATER)) { shaders_iter->mUniformsDirty = TRUE; + if (shaders_iter->mRiggedVariant) + { + shaders_iter->mRiggedVariant->mUniformsDirty = TRUE; + } } } } @@ -1768,8 +1770,10 @@ void LLEnvironment::updateGLVariablesForSettings(LLShaderUniforms* uniforms, con case LLSD::TypeArray: { LLVector4 vect4(value); + // always identify as a radiance pass if desaturating irradiance is disabled + static LLCachedControl<bool> desaturate_irradiance(gSavedSettings, "RenderDesaturateIrradiance", true); - if (gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass()) + 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) { @@ -2964,7 +2968,7 @@ void LLEnvironment::DayTransition::animate() // pause probe updates and reset reflection maps on sky change - gPipeline.mReflectionMapManager.pause(); + gPipeline.mReflectionMapManager.pause(mTransitionTime); gPipeline.mReflectionMapManager.reset(); mSky = mStartSky->buildClone(); @@ -3567,7 +3571,7 @@ namespace mInjectedSky->setSource(target_sky); // clear reflection probes and pause updates during sky change - gPipeline.mReflectionMapManager.pause(); + gPipeline.mReflectionMapManager.pause(transition); gPipeline.mReflectionMapManager.reset(); mBlenderSky = std::make_shared<LLSettingsBlenderTimeDelta>(target_sky, start_sky, psky, transition); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index c1776705f9..69e43bb458 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1889,7 +1889,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, F32* normals = (F32*) norm.get(); LLVector4a* src = vf.mNormals; LLVector4a* end = src+num_vertices; - + while (src < end) { LLVector4a normal; diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index f482d5a37d..9fff505c2a 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -664,6 +664,10 @@ void LLFeatureManager::applyBaseMasks() { maskFeatures("TexUnit8orLess"); } + if (gGLManager.mNumTextureImageUnits <= 16) + { + maskFeatures("TexUnit16orLess"); + } if (gGLManager.mVRAM > 512) { maskFeatures("VRAMGT512"); diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp index 46b9dffae9..1ca7ffaec1 100644 --- a/indra/newview/llfetchedgltfmaterial.cpp +++ b/indra/newview/llfetchedgltfmaterial.cpp @@ -37,8 +37,6 @@ LLFetchedGLTFMaterial::LLFetchedGLTFMaterial() : LLGLTFMaterial() , mExpectedFlusTime(0.f) - , mActive(true) - , mFetching(false) { } @@ -78,7 +76,14 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex) { // dividing the alpha cutoff by transparency here allows the shader to compare against // the alpha value of the texture without needing the transparency value - min_alpha = mAlphaCutoff/mBaseColor.mV[3]; + if (mBaseColor.mV[3] > 0.f) + { + min_alpha = mAlphaCutoff / mBaseColor.mV[3]; + } + else + { + min_alpha = 1024.f; + } } shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha); } @@ -242,10 +247,11 @@ void LLFetchedGLTFMaterial::onMaterialComplete(std::function<void()> material_co materialCompleteCallbacks.push_back(material_complete); } -void LLFetchedGLTFMaterial::materialComplete() +void LLFetchedGLTFMaterial::materialComplete(bool success) { llassert(mFetching); mFetching = false; + mFetchSuccess = success; for (std::function<void()> material_complete : materialCompleteCallbacks) { @@ -254,55 +260,3 @@ void LLFetchedGLTFMaterial::materialComplete() materialCompleteCallbacks.clear(); materialCompleteCallbacks.shrink_to_fit(); } - -LLPointer<LLViewerFetchedTexture> LLFetchedGLTFMaterial::getUITexture() -{ - if (mFetching) - { - return nullptr; - } - - auto fetch_texture_for_ui = [](LLPointer<LLViewerFetchedTexture>& img, const LLUUID& id) - { - if (id.notNull()) - { - if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) - { - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); - if (obj) - { - LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(id); - img = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL; - } - - } - else - { - img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - } - } - if (img) - { - img->setBoostLevel(LLGLTexture::BOOST_PREVIEW); - img->forceToSaveRawImage(0); - } - }; - - fetch_texture_for_ui(mBaseColorTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]); - fetch_texture_for_ui(mNormalTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]); - fetch_texture_for_ui(mMetallicRoughnessTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]); - fetch_texture_for_ui(mEmissiveTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]); - - if ((mBaseColorTexture && (mBaseColorTexture->getRawImageLevel() != 0)) || - (mNormalTexture && (mNormalTexture->getRawImageLevel() != 0)) || - (mMetallicRoughnessTexture && (mMetallicRoughnessTexture->getRawImageLevel() != 0)) || - (mEmissiveTexture && (mEmissiveTexture->getRawImageLevel() != 0))) - { - return nullptr; - } - - // *HACK: Use one of the PBR texture components as the preview texture for now - mPreviewTexture = mBaseColorTexture; - - return mPreviewTexture; -} diff --git a/indra/newview/llfetchedgltfmaterial.h b/indra/newview/llfetchedgltfmaterial.h index a9e539633d..42c835a416 100644 --- a/indra/newview/llfetchedgltfmaterial.h +++ b/indra/newview/llfetchedgltfmaterial.h @@ -40,6 +40,8 @@ public: virtual ~LLFetchedGLTFMaterial(); LLFetchedGLTFMaterial& operator=(const LLFetchedGLTFMaterial& rhs); + // LLGLTFMaterial::operator== is defined, but LLFetchedGLTFMaterial::operator== is not. + bool operator==(const LLGLTFMaterial& rhs) const = delete; // If this material is loaded, fire the given function void onMaterialComplete(std::function<void()> material_complete); @@ -49,8 +51,7 @@ public: void bind(LLViewerTexture* media_tex = nullptr); bool isFetching() const { return mFetching; } - - LLPointer<LLViewerFetchedTexture> getUITexture(); + bool isLoaded() const { return !mFetching && mFetchSuccess; } void addTextureEntry(LLTextureEntry* te) override; void removeTextureEntry(LLTextureEntry* te) override; @@ -65,18 +66,16 @@ public: std::set<LLTextureEntry*> mTextureEntires; - // Texture used for previewing the material in the UI - LLPointer<LLViewerFetchedTexture> mPreviewTexture; - protected: // Lifetime management void materialBegin(); - void materialComplete(); + void materialComplete(bool success); F64 mExpectedFlusTime; // since epoch in seconds - bool mActive; - bool mFetching; + bool mActive = true; + bool mFetching = false; + bool mFetchSuccess = false; std::vector<std::function<void()>> materialCompleteCallbacks; }; diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 4ad136e13a..b82172c506 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -61,6 +61,7 @@ LLFilePicker LLFilePicker::sInstance; #define RAW_FILTER L"RAW files (*.raw)\0*.raw\0" #define MODEL_FILTER L"Model files (*.dae)\0*.dae\0" #define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0" +#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0" #define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0" #define SCRIPT_FILTER L"Script files (*.lsl)\0*.lsl\0" #define DICTIONARY_FILTER L"Dictionary files (*.dic; *.xcu)\0*.dic;*.xcu\0" @@ -228,6 +229,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter) IMAGE_FILTER \ L"\0"; break; + case FFLOAD_HDRI: + mOFN.lpstrFilter = HDRI_FILTER \ + L"\0"; + break; case FFLOAD_SCRIPT: mOFN.lpstrFilter = SCRIPT_FILTER \ L"\0"; @@ -663,6 +668,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF allowedv->push_back("gltf"); allowedv->push_back("glb"); break; + case FFLOAD_HDRI: + allowedv->push_back("exr"); case FFLOAD_COLLADA: allowedv->push_back("dae"); break; diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 38daff9937..891c0c0482 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -89,6 +89,7 @@ public: FFLOAD_EXE = 14, // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin FFLOAD_MATERIAL = 15, FFLOAD_MATERIAL_TEXTURE = 16, + FFLOAD_HDRI = 17, }; enum ESaveFilter diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index ba0f97e2e1..2a5106f79e 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -34,6 +34,7 @@ #include "llimagepng.h" #include "llagent.h" +#include "llagentbenefits.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcombobox.h" @@ -144,6 +145,15 @@ BOOL LLFloaterImagePreview::postBuild() return TRUE; } + +//----------------------------------------------------------------------------- +// getExpectedUploadCost() +//----------------------------------------------------------------------------- +S32 LLFloaterImagePreview::getExpectedUploadCost() const +{ + return LLAgentBenefitsMgr::current().getTextureUploadCost(mRawImagep); +} + //----------------------------------------------------------------------------- // LLFloaterImagePreview() //----------------------------------------------------------------------------- @@ -378,7 +388,7 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename) return false; } - raw_image->biasedScaleToPowerOfTwo(1024); + raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); mRawImagep = raw_image; return true; diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h index d034622c35..426dd926ae 100644 --- a/indra/newview/llfloaterimagepreview.h +++ b/indra/newview/llfloaterimagepreview.h @@ -48,17 +48,17 @@ protected: public: LLImagePreviewSculpted(S32 width, S32 height); - /*virtual*/ S8 getType() const ; + S8 getType() const override; void setPreviewTarget(LLImageRaw *imagep, F32 distance); void setTexture(U32 name) { mTextureName = name; } - BOOL render(); + BOOL render() override; void refresh(); void rotate(F32 yaw_radians, F32 pitch_radians); void zoom(F32 zoom_amt); void pan(F32 right, F32 up); - virtual BOOL needsRender() { return mNeedsUpdate; } + virtual BOOL needsRender() override { return mNeedsUpdate; } protected: BOOL mNeedsUpdate; @@ -81,18 +81,18 @@ protected: public: LLImagePreviewAvatar(S32 width, S32 height); - /*virtual*/ S8 getType() const ; + S8 getType() const override; void setPreviewTarget(const std::string& joint_name, const std::string& mesh_name, LLImageRaw* imagep, F32 distance, BOOL male); void setTexture(U32 name) { mTextureName = name; } void clearPreviewTexture(const std::string& mesh_name); - BOOL render(); + BOOL render() override; void refresh(); void rotate(F32 yaw_radians, F32 pitch_radians); void zoom(F32 zoom_amt); void pan(F32 right, F32 up); - virtual BOOL needsRender() { return mNeedsUpdate; } + virtual BOOL needsRender() override { return mNeedsUpdate; } protected: BOOL mNeedsUpdate; @@ -113,12 +113,14 @@ public: LLFloaterImagePreview(const std::string& filename); virtual ~LLFloaterImagePreview(); - virtual BOOL postBuild(); + BOOL postBuild() override; + + S32 getExpectedUploadCost() const override; - BOOL handleMouseDown(S32 x, S32 y, MASK mask); - BOOL handleMouseUp(S32 x, S32 y, MASK mask); - BOOL handleHover(S32 x, S32 y, MASK mask); - BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + BOOL handleMouseUp(S32 x, S32 y, MASK mask) override; + BOOL handleHover(S32 x, S32 y, MASK mask) override; + BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) override; static void onMouseCaptureLostImagePreview(LLMouseHandler*); @@ -126,7 +128,7 @@ public: protected: static void onPreviewTypeCommit(LLUICtrl*,void*); - void draw(); + void draw() override; bool loadImage(const std::string& filename); LLPointer<LLImageRaw> mRawImagep; diff --git a/indra/newview/llfloaternamedesc.h b/indra/newview/llfloaternamedesc.h index 589f470e82..cf3b512fab 100644 --- a/indra/newview/llfloaternamedesc.h +++ b/indra/newview/llfloaternamedesc.h @@ -47,7 +47,7 @@ public: void onBtnCancel(); void doCommit(); - S32 getExpectedUploadCost() const; + virtual S32 getExpectedUploadCost() const; protected: virtual void onCommit(); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 187ac9d323..9ce7a07ee4 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -62,6 +62,7 @@ #include "llfloaterreg.h" #include "llfloaterregiondebugconsole.h" #include "llfloatertelehub.h" +#include "llgltfmateriallist.h" #include "llinventorymodel.h" #include "lllineeditor.h" #include "llnamelistctrl.h" @@ -86,7 +87,6 @@ #include "llviewerstats.h" #include "llviewertexteditor.h" #include "llviewerwindow.h" -#include "llvlcomposition.h" #include "lltrans.h" #include "llagentui.h" #include "llmeshrepository.h" @@ -100,7 +100,6 @@ #include "llavatarnamecache.h" #include "llenvironment.h" -const S32 TERRAIN_TEXTURE_COUNT = 4; const S32 CORNER_COUNT = 4; const U32 MAX_LISTED_NAMES = 100; @@ -340,7 +339,6 @@ void LLFloaterRegionInfo::onRegionChanged() } } -// static void LLFloaterRegionInfo::requestRegionInfo() { LLTabContainer* tab = findChild<LLTabContainer>("region_panels"); @@ -605,6 +603,16 @@ LLPanelRegionEnvironment* LLFloaterRegionInfo::getPanelEnvironment() return panel; } +LLTerrainMaterials::Type material_type_from_ctrl(LLCheckBoxCtrl* ctrl) +{ + return ctrl->get() ? LLTerrainMaterials::Type::PBR : LLTerrainMaterials::Type::TEXTURE; +} + +void material_type_to_ctrl(LLCheckBoxCtrl* ctrl, LLTerrainMaterials::Type new_type) +{ + ctrl->set(new_type == LLTerrainMaterials::Type::PBR); +} + // static LLPanelRegionTerrainInfo* LLFloaterRegionInfo::getPanelRegionTerrain() { @@ -1305,18 +1313,25 @@ void LLPanelRegionDebugInfo::onClickDebugConsole(void* data) LLFloaterReg::showInstance("region_debug_console"); } -BOOL LLPanelRegionTerrainInfo::validateTextureSizes() +bool LLPanelRegionTerrainInfo::validateTextureSizes() { - static const S32 MAX_TERRAIN_TEXTURE_SIZE = 1024; - for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i) + if (mMaterialTypeCtrl) + { + const LLTerrainMaterials::Type material_type = material_type_from_ctrl(mMaterialTypeCtrl); + const bool is_material_selected = material_type == LLTerrainMaterials::Type::PBR; + if (is_material_selected) { return true; } + } + + bool valid = true; + static LLCachedControl<U32> max_texture_resolution(gSavedSettings, "RenderMaxTextureResolution", 2048); + const S32 max_terrain_texture_size = (S32)max_texture_resolution; + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) { - std::string buffer; - buffer = llformat("texture_detail_%d", i); - LLTextureCtrl* texture_ctrl = getChild<LLTextureCtrl>(buffer); + LLTextureCtrl* texture_ctrl = mTextureDetailCtrl[i]; if (!texture_ctrl) continue; LLUUID image_asset_id = texture_ctrl->getImageAssetID(); - LLViewerTexture* img = LLViewerTextureManager::getFetchedTexture(image_asset_id); + LLViewerFetchedTexture* img = LLViewerTextureManager::getFetchedTexture(image_asset_id); S32 components = img->getComponents(); // Must ask for highest resolution version's width. JC S32 width = img->getFullWidth(); @@ -1324,31 +1339,133 @@ BOOL LLPanelRegionTerrainInfo::validateTextureSizes() //LL_INFOS() << "texture detail " << i << " is " << width << "x" << height << "x" << components << LL_ENDL; - if (components != 3) + if (components != 3 && components != 4) { LLSD args; args["TEXTURE_NUM"] = i+1; args["TEXTURE_BIT_DEPTH"] = llformat("%d",components * 8); - args["MAX_SIZE"] = MAX_TERRAIN_TEXTURE_SIZE; + args["MAX_SIZE"] = max_terrain_texture_size; LLNotificationsUtil::add("InvalidTerrainBitDepth", args); - return FALSE; + valid = false; + continue; } - if (width > MAX_TERRAIN_TEXTURE_SIZE || height > MAX_TERRAIN_TEXTURE_SIZE) + if (components == 4) + { + if (!img->hasSavedRawImage()) + { + // Raw image isn't loaded yet + // Assume it's invalid due to presence of alpha channel + LLSD args; + args["TEXTURE_NUM"] = i+1; + args["TEXTURE_BIT_DEPTH"] = llformat("%d",components * 8); + LLNotificationsUtil::add("InvalidTerrainAlphaNotFullyLoaded", args); + valid = false; + } + else + { + // Slower path: Calculate alpha from raw image pixels (not needed + // for GLTF materials, which use alphaMode to determine + // transparency) + // Raw image is pretty much guaranteed to be saved due to the texture swatches + LLImageRaw* raw = img->getSavedRawImage(); + if (raw->checkHasTransparentPixels()) + { + LLSD args; + args["TEXTURE_NUM"] = i+1; + LLNotificationsUtil::add("InvalidTerrainAlpha", args); + valid = false; + } + LL_WARNS() << "Terrain texture image in slot " << i << " with ID " << image_asset_id << " has alpha channel, but pixels are opaque. Is alpha being optimized away in the texture uploader?" << LL_ENDL; + } + } + + if (width > max_terrain_texture_size || height > max_terrain_texture_size) { LLSD args; args["TEXTURE_NUM"] = i+1; args["TEXTURE_SIZE_X"] = width; args["TEXTURE_SIZE_Y"] = height; - args["MAX_SIZE"] = MAX_TERRAIN_TEXTURE_SIZE; + args["MAX_SIZE"] = max_terrain_texture_size; LLNotificationsUtil::add("InvalidTerrainSize", args); - return FALSE; - + valid = false; } } - return TRUE; + return valid; +} + +bool LLPanelRegionTerrainInfo::validateMaterials() +{ + if (mMaterialTypeCtrl) + { + const LLTerrainMaterials::Type material_type = material_type_from_ctrl(mMaterialTypeCtrl); + const bool is_texture_selected = material_type == LLTerrainMaterials::Type::TEXTURE; + if (is_texture_selected) { return true; } + } + + // *TODO: If/when we implement additional GLTF extensions, they may not be + // compatible with our GLTF terrain implementation. We may want to disallow + // materials with some features from being set on terrain, if their + // implementation on terrain is not compliant with the spec: + // - KHR_materials_transmission: Probably OK? + // - KHR_materials_ior: Probably OK? + // - KHR_materials_volume: Likely incompatible, as our terrain + // heightmaps cannot currently be described as finite enclosed + // volumes. + // See also LLGLTFMaterial +#ifdef LL_WINDOWS + llassert(sizeof(LLGLTFMaterial) == 232); +#endif + + bool valid = true; + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLTextureCtrl* material_ctrl = mMaterialDetailCtrl[i]; + if (!material_ctrl) { continue; } + + const LLUUID& material_asset_id = material_ctrl->getImageAssetID(); + llassert(material_asset_id.notNull()); + if (material_asset_id.isNull()) { return false; } + const LLFetchedGLTFMaterial* material = gGLTFMaterialList.getMaterial(material_asset_id); + if (!material->isLoaded()) + { + if (material->isFetching()) + { + LLSD args; + args["MATERIAL_NUM"] = i + 1; + LLNotificationsUtil::add("InvalidTerrainMaterialNotLoaded", args); + } + else // Loading failed + { + LLSD args; + args["MATERIAL_NUM"] = i + 1; + LLNotificationsUtil::add("InvalidTerrainMaterialLoadFailed", args); + } + valid = false; + continue; + } + + if (material->mDoubleSided) + { + LLSD args; + args["MATERIAL_NUM"] = i + 1; + LLNotificationsUtil::add("InvalidTerrainMaterialDoubleSided", args); + valid = false; + } + if (material->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_OPAQUE && material->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_MASK) + { + LLSD args; + args["MATERIAL_NUM"] = i + 1; + const char* alpha_mode = material->getAlphaMode(); + args["MATERIAL_ALPHA_MODE"] = alpha_mode; + LLNotificationsUtil::add("InvalidTerrainMaterialAlphaMode", args); + valid = false; + } + } + + return valid; } BOOL LLPanelRegionTerrainInfo::validateTextureHeights() @@ -1370,6 +1487,21 @@ BOOL LLPanelRegionTerrainInfo::validateTextureHeights() ///////////////////////////////////////////////////////////////////////////// // LLPanelRegionTerrainInfo ///////////////////////////////////////////////////////////////////////////// + +LLPanelRegionTerrainInfo::LLPanelRegionTerrainInfo() +: LLPanelRegionInfo() +{ + const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures(); + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + mLastSetTextures[i] = default_textures[i]; + } + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + mLastSetMaterials[i] = BLANK_MATERIAL_ASSET_ID; + } +} + // Initialize statics BOOL LLPanelRegionTerrainInfo::postBuild() @@ -1380,11 +1512,22 @@ BOOL LLPanelRegionTerrainInfo::postBuild() initCtrl("terrain_raise_spin"); initCtrl("terrain_lower_spin"); + mMaterialTypeCtrl = findChild<LLCheckBoxCtrl>("terrain_material_type"); + if (mMaterialTypeCtrl) { mMaterialTypeCtrl->setCommitCallback(boost::bind(&LLPanelRegionTerrainInfo::onSelectMaterialType, this)); } + std::string buffer; - for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i) + + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) { buffer = llformat("texture_detail_%d", i); initCtrl(buffer); + mTextureDetailCtrl[i] = findChild<LLTextureCtrl>(buffer); + } + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + buffer = llformat("material_detail_%d", i); + initCtrl(buffer); + mMaterialDetailCtrl[i] = findChild<LLTextureCtrl>(buffer); } for(S32 i = 0; i < CORNER_COUNT; ++i) @@ -1405,6 +1548,57 @@ BOOL LLPanelRegionTerrainInfo::postBuild() return LLPanelRegionInfo::postBuild(); } +void LLPanelRegionTerrainInfo::onSelectMaterialType() +{ + updateForMaterialType(); + onChangeAnything(); +} + +void LLPanelRegionTerrainInfo::updateForMaterialType() +{ + if (!mMaterialTypeCtrl) { return; } + const LLTerrainMaterials::Type material_type = material_type_from_ctrl(mMaterialTypeCtrl); + const bool show_texture_controls = material_type == LLTerrainMaterials::Type::TEXTURE; + const bool show_material_controls = material_type == LLTerrainMaterials::Type::PBR; + + // Toggle visibility of correct swatches + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLTextureCtrl* texture_ctrl = mTextureDetailCtrl[i]; + if (texture_ctrl) + { + texture_ctrl->setVisible(show_texture_controls); + } + } + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLTextureCtrl* material_ctrl = mMaterialDetailCtrl[i]; + if (material_ctrl) + { + material_ctrl->setVisible(show_material_controls); + } + } + + // Toggle visibility of labels + LLUICtrl* texture_label = findChild<LLUICtrl>("detail_texture_text"); + if (texture_label) { texture_label->setVisible(show_texture_controls); } + LLUICtrl* material_label = findChild<LLUICtrl>("detail_material_text"); + if (material_label) { material_label->setVisible(show_material_controls); } + + // Toggle visibility of documentation labels for terrain blending ranges + const std::vector<std::string> doc_suffixes { "5", "10", "11" }; + std::string buffer; + for (const std::string& suffix : doc_suffixes) + { + buffer = "height_text_lbl" + suffix; + LLUICtrl* texture_doc_label = findChild<LLUICtrl>(buffer); + if (texture_doc_label) { texture_doc_label->setVisible(show_texture_controls); } + buffer += "_material"; + LLUICtrl* material_doc_label = findChild<LLUICtrl>(buffer); + if (material_doc_label) { material_doc_label->setVisible(show_material_controls); } + } +} + // virtual bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) { @@ -1421,21 +1615,99 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) getChild<LLUICtrl>("region_text")->setValue(LLSD(region->getName())); LLVLComposition* compp = region->getComposition(); - LLTextureCtrl* texture_ctrl; - std::string buffer; - for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i) - { - buffer = llformat("texture_detail_%d", i); - texture_ctrl = getChild<LLTextureCtrl>(buffer); - if(texture_ctrl) - { - LL_DEBUGS() << "Detail Texture " << i << ": " - << compp->getDetailTextureID(i) << LL_ENDL; - LLUUID tmp_id(compp->getDetailTextureID(i)); - texture_ctrl->setImageAssetID(tmp_id); - } - } + static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false); + + const bool textures_ready = compp->texturesReady(false, false); + const bool materials_ready = feature_pbr_terrain_enabled && compp->materialsReady(false, false); + + bool set_texture_swatches; + bool set_material_swatches; + bool reset_texture_swatches; + bool reset_material_swatches; + LLTerrainMaterials::Type material_type; + if (!textures_ready && !materials_ready) + { + // Are these 4 texture IDs or 4 material IDs? Who knows! Let's set + // the IDs on both pickers for now. + material_type = LLTerrainMaterials::Type::TEXTURE; + set_texture_swatches = true; + set_material_swatches = true; + reset_texture_swatches = false; + reset_material_swatches = false; + } + else + { + material_type = compp->getMaterialType(); + set_texture_swatches = material_type == LLTerrainMaterials::Type::TEXTURE; + set_material_swatches = !set_texture_swatches; + reset_texture_swatches = !set_texture_swatches; + reset_material_swatches = !set_material_swatches; + } + + if (mMaterialTypeCtrl) + { + material_type_to_ctrl(mMaterialTypeCtrl, material_type); + updateForMaterialType(); + mMaterialTypeCtrl->setVisible(feature_pbr_terrain_enabled); + } + + if (set_texture_swatches) + { + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLTextureCtrl* asset_ctrl = mTextureDetailCtrl[i]; + if(asset_ctrl) + { + LL_DEBUGS("Terrain", "Texture") << "Detail Texture " << i << ": " + << compp->getDetailAssetID(i) << LL_ENDL; + LLUUID tmp_id(compp->getDetailAssetID(i)); + asset_ctrl->setImageAssetID(tmp_id); + } + } + } + if (set_material_swatches) + { + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLTextureCtrl* asset_ctrl = mMaterialDetailCtrl[i]; + if(asset_ctrl) + { + LL_DEBUGS("Terrain", "Material") << "Detail Material " << i << ": " + << compp->getDetailAssetID(i) << LL_ENDL; + LLUUID tmp_id(compp->getDetailAssetID(i)); + asset_ctrl->setImageAssetID(tmp_id); + } + } + } + if (reset_texture_swatches) + { + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LL_DEBUGS("Terrain", "Texture") << "Reset Texture swatch " << i + << LL_ENDL; + LLTextureCtrl* asset_ctrl = mTextureDetailCtrl[i]; + if(asset_ctrl) + { + asset_ctrl->setImageAssetID(mLastSetTextures[i]); + } + } + } + if (reset_material_swatches) + { + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LL_DEBUGS("Terrain", "Material") << "Reset Material swatch " << i + << LL_ENDL; + LLTextureCtrl* asset_ctrl = mMaterialDetailCtrl[i]; + if(asset_ctrl) + { + asset_ctrl->setImageAssetID(mLastSetMaterials[i]); + } + } + } + + std::string buffer; for(S32 i = 0; i < CORNER_COUNT; ++i) { buffer = llformat("height_start_spin_%d", i); @@ -1449,6 +1721,9 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) LL_DEBUGS() << "no region set" << LL_ENDL; getChild<LLUICtrl>("region_text")->setValue(LLSD("")); } + + // Update visibility of terrain swatches, etc + refresh(); getChildView("download_raw_btn")->setEnabled(owner_or_god); getChildView("upload_raw_btn")->setEnabled(owner_or_god); @@ -1462,21 +1737,6 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) BOOL LLPanelRegionTerrainInfo::sendUpdate() { LL_INFOS() << "LLPanelRegionTerrainInfo::sendUpdate" << LL_ENDL; - std::string buffer; - strings_t strings; - LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); - - // update the model - LLRegionInfoModel& region_info = LLRegionInfoModel::instance(); - region_info.mWaterHeight = (F32) getChild<LLUICtrl>("water_height_spin")->getValue().asReal(); - region_info.mTerrainRaiseLimit = (F32) getChild<LLUICtrl>("terrain_raise_spin")->getValue().asReal(); - region_info.mTerrainLowerLimit = (F32) getChild<LLUICtrl>("terrain_lower_spin")->getValue().asReal(); - - // and sync the region with it - region_info.sendRegionTerrain(invoice); - - // ======================================= - // Assemble and send texturedetail message // Make sure user hasn't chosen wacky textures. if (!validateTextureSizes()) @@ -1484,6 +1744,12 @@ BOOL LLPanelRegionTerrainInfo::sendUpdate() return FALSE; } + // Prevent applying unsupported alpha blend/double-sided materials + if (!validateMaterials()) + { + return FALSE; + } + // Check if terrain Elevation Ranges are correct if (gSavedSettings.getBOOL("RegionCheckTextureHeights") && !validateTextureHeights()) { @@ -1499,22 +1765,57 @@ BOOL LLPanelRegionTerrainInfo::sendUpdate() } } - LLTextureCtrl* texture_ctrl; + std::string buffer; + strings_t strings; + LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); + + // update the model + LLRegionInfoModel& region_info = LLRegionInfoModel::instance(); + region_info.mWaterHeight = (F32) getChild<LLUICtrl>("water_height_spin")->getValue().asReal(); + region_info.mTerrainRaiseLimit = (F32) getChild<LLUICtrl>("terrain_raise_spin")->getValue().asReal(); + region_info.mTerrainLowerLimit = (F32) getChild<LLUICtrl>("terrain_lower_spin")->getValue().asReal(); + + // and sync the region with it + region_info.sendRegionTerrain(invoice); + + // ======================================= + // Assemble and send texturedetail message + std::string id_str; LLMessageSystem* msg = gMessageSystem; - for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i) - { - buffer = llformat("texture_detail_%d", i); - texture_ctrl = getChild<LLTextureCtrl>(buffer); - if(texture_ctrl) - { - LLUUID tmp_id(texture_ctrl->getImageAssetID()); - tmp_id.toString(id_str); - buffer = llformat("%d %s", i, id_str.c_str()); - strings.push_back(buffer); - } - } + // Send either material IDs instead of texture IDs depending on + // material_type - they both occupy the same slot. + const LLTerrainMaterials::Type material_type = mMaterialTypeCtrl ? material_type_from_ctrl(mMaterialTypeCtrl) : LLTerrainMaterials::Type::TEXTURE; + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLTextureCtrl* asset_ctrl; + if (material_type == LLTerrainMaterials::Type::PBR) + { + asset_ctrl = mMaterialDetailCtrl[i]; + } + else + { + asset_ctrl = mTextureDetailCtrl[i]; + } + + if (!asset_ctrl) { continue; } + + LLUUID tmp_id(asset_ctrl->getImageAssetID()); + tmp_id.toString(id_str); + buffer = llformat("%d %s", i, id_str.c_str()); + strings.push_back(buffer); + + // Store asset for later terrain editing + if (material_type == LLTerrainMaterials::Type::PBR) + { + mLastSetMaterials[i] = tmp_id; + } + else + { + mLastSetTextures[i] = tmp_id; + } + } sendEstateOwnerMessage(msg, "texturedetail", invoice, strings); strings.clear(); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 3eb39b250f..da17212016 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -36,6 +36,7 @@ #include "llpanel.h" #include "llextendedstatus.h" #include "llpanelenvironment.h" +#include "llvlcomposition.h" #include "lleventcoro.h" @@ -55,6 +56,7 @@ class LLRadioGroup; class LLSliderCtrl; class LLSpinCtrl; class LLTextBox; +class LLTextureCtrl; class LLPanelRegionGeneralInfo; class LLPanelRegionDebugInfo; @@ -75,9 +77,9 @@ class LLFloaterRegionInfo : public LLFloater public: - /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void onClose(bool app_quitting); - /*virtual*/ BOOL postBuild(); + void onOpen(const LLSD& key) override; + void onClose(bool app_quitting) override; + BOOL postBuild() override; static void processEstateOwnerRequest(LLMessageSystem* msg, void**); @@ -98,7 +100,7 @@ public: static LLPanelRegionEnvironment* getPanelEnvironment(); // from LLPanel - virtual void refresh(); + void refresh() override; void onRegionChanged(); void requestRegionInfo(); @@ -144,7 +146,7 @@ public: virtual bool refreshFromRegion(LLViewerRegion* region); virtual bool estateUpdate(LLMessageSystem* msg) { return true; } - virtual BOOL postBuild(); + BOOL postBuild() override; virtual void updateChild(LLUICtrl* child_ctrl); void enableButton(const std::string& btn_name, BOOL enable = TRUE); @@ -184,16 +186,15 @@ public: : LLPanelRegionInfo() {} ~LLPanelRegionGeneralInfo() {} - virtual bool refreshFromRegion(LLViewerRegion* region); + bool refreshFromRegion(LLViewerRegion* region) override; - // LLPanel - virtual BOOL postBuild(); + BOOL postBuild() override; void onBtnSet(); void setObjBonusFactor(F32 object_bonus_factor) {mObjBonusFactor = object_bonus_factor;} protected: - virtual BOOL sendUpdate(); + BOOL sendUpdate() override; void onClickKick(); void onKickCommit(const uuid_vec_t& ids); static void onClickKickAll(void* userdata); @@ -214,13 +215,13 @@ public: LLPanelRegionDebugInfo() : LLPanelRegionInfo(), mTargetAvatar() {} ~LLPanelRegionDebugInfo() {} - // LLPanel - virtual BOOL postBuild(); + + BOOL postBuild() override; - virtual bool refreshFromRegion(LLViewerRegion* region); + bool refreshFromRegion(LLViewerRegion* region) override; protected: - virtual BOOL sendUpdate(); + BOOL sendUpdate() override; void onClickChooseAvatar(); void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); @@ -244,20 +245,22 @@ class LLPanelRegionTerrainInfo : public LLPanelRegionInfo LOG_CLASS(LLPanelRegionTerrainInfo); public: - LLPanelRegionTerrainInfo() : LLPanelRegionInfo() {} + LLPanelRegionTerrainInfo(); ~LLPanelRegionTerrainInfo() {} - virtual BOOL postBuild(); // LLPanel + BOOL postBuild() override; - virtual bool refreshFromRegion(LLViewerRegion* region); // refresh local settings from region update from simulator + bool refreshFromRegion(LLViewerRegion* region) override; // refresh local settings from region update from simulator void setEnvControls(bool available); // Whether environment settings are available for this region - BOOL validateTextureSizes(); + bool validateTextureSizes(); + bool validateMaterials(); BOOL validateTextureHeights(); //static void onChangeAnything(LLUICtrl* ctrl, void* userData); // callback for any change, to enable commit button - virtual BOOL sendUpdate(); + void onSelectMaterialType(); + void updateForMaterialType(); static void onClickDownloadRaw(void*); static void onClickUploadRaw(void*); @@ -265,9 +268,17 @@ public: bool callbackBakeTerrain(const LLSD& notification, const LLSD& response); bool callbackTextureHeights(const LLSD& notification, const LLSD& response); +protected: + BOOL sendUpdate() override; + private: bool mConfirmedTextureHeights; bool mAskedTextureHeights; + LLCheckBoxCtrl* mMaterialTypeCtrl = nullptr; + LLTextureCtrl* mTextureDetailCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLTextureCtrl* mMaterialDetailCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLUUID mLastSetTextures[LLTerrainMaterials::ASSET_COUNT]; + LLUUID mLastSetMaterials[LLTerrainMaterials::ASSET_COUNT]; }; ///////////////////////////////////////////////////////////////////////////// @@ -303,13 +314,12 @@ public: static void updateEstateName(const std::string& name); static void updateEstateOwnerName(const std::string& name); - virtual bool refreshFromRegion(LLViewerRegion* region); - virtual bool estateUpdate(LLMessageSystem* msg); + bool refreshFromRegion(LLViewerRegion* region) override; + bool estateUpdate(LLMessageSystem* msg) override; - // LLPanel - virtual BOOL postBuild(); - virtual void updateChild(LLUICtrl* child_ctrl); - virtual void refresh(); + BOOL postBuild() override; + void updateChild(LLUICtrl* child_ctrl) override; + void refresh() override; void refreshFromEstate(); @@ -319,7 +329,7 @@ public: void setOwnerName(const std::string& name); protected: - virtual BOOL sendUpdate(); + BOOL sendUpdate() override; // confirmation dialog callback bool callbackChangeLindenEstate(const LLSD& notification, const LLSD& response); @@ -339,17 +349,16 @@ public: LLPanelEstateCovenant(); ~LLPanelEstateCovenant() {} - // LLPanel - virtual BOOL postBuild(); - virtual void updateChild(LLUICtrl* child_ctrl); - virtual bool refreshFromRegion(LLViewerRegion* region); - virtual bool estateUpdate(LLMessageSystem* msg); + BOOL postBuild() override; + void updateChild(LLUICtrl* child_ctrl) override; + bool refreshFromRegion(LLViewerRegion* region) override; + bool estateUpdate(LLMessageSystem* msg) override; // LLView overrides BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, - std::string& tooltip_msg); + std::string& tooltip_msg) override; static bool confirmChangeCovenantCallback(const LLSD& notification, const LLSD& response); static void resetCovenantID(void* userdata); static bool confirmResetCovenantCallback(const LLSD& notification, const LLSD& response); @@ -382,7 +391,7 @@ public: } EAssetStatus; protected: - virtual BOOL sendUpdate(); + BOOL sendUpdate() override; LLTextBox* mEstateNameText; LLTextBox* mEstateOwnerText; LLTextBox* mLastModifiedText; @@ -401,16 +410,19 @@ class LLPanelRegionExperiences : public LLPanelRegionInfo public: LLPanelRegionExperiences(){} - /*virtual*/ BOOL postBuild(); - virtual BOOL sendUpdate(); + BOOL postBuild() override; static bool experienceCoreConfirm(const LLSD& notification, const LLSD& response); static void sendEstateExperienceDelta(U32 flags, const LLUUID& agent_id); static void infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content); - bool refreshFromRegion(LLViewerRegion* region); + bool refreshFromRegion(LLViewerRegion* region) override; void sendPurchaseRequest()const; void processResponse( const LLSD& content ); + +protected: + BOOL sendUpdate() override; + private: void refreshRegionExperiences(); @@ -435,8 +447,8 @@ class LLPanelEstateAccess : public LLPanelRegionInfo public: LLPanelEstateAccess(); - virtual BOOL postBuild(); - virtual void updateChild(LLUICtrl* child_ctrl); + BOOL postBuild() override; + void updateChild(LLUICtrl* child_ctrl) override; void updateControls(LLViewerRegion* region); void updateLists(); @@ -444,7 +456,7 @@ public: void setPendingUpdate(bool pending) { mPendingUpdate = pending; } bool getPendingUpdate() { return mPendingUpdate; } - virtual bool refreshFromRegion(LLViewerRegion* region); + bool refreshFromRegion(LLViewerRegion* region) override; private: void onClickAddAllowedAgent(); diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp index a1d77779dd..e97eb552cf 100644 --- a/indra/newview/llgltfmateriallist.cpp +++ b/indra/newview/llgltfmateriallist.cpp @@ -58,8 +58,6 @@ LLGLTFMaterialList::modify_queue_t LLGLTFMaterialList::sModifyQueue; LLGLTFMaterialList::apply_queue_t LLGLTFMaterialList::sApplyQueue; LLSD LLGLTFMaterialList::sUpdates; -const LLUUID LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID("968cbad0-4dad-d64e-71b5-72bf13ad051a"); - #ifdef SHOW_ASSERT // return true if given data is (probably) valid update message for ModifyMaterialParams capability static bool is_valid_update(const LLSD& data) @@ -476,7 +474,7 @@ void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::ETyp if (status != LL_ERR_NOERR) { LL_WARNS("GLTF") << "Error getting material asset data: " << LLAssetStorage::getErrorString(status) << " (" << status << ")" << LL_ENDL; - asset_data->mMaterial->materialComplete(); + asset_data->mMaterial->materialComplete(false); delete asset_data; } else @@ -561,7 +559,7 @@ void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::ETyp LL_DEBUGS("GLTF") << "Failed to get material " << id << LL_ENDL; } - asset_data->mMaterial->materialComplete(); + asset_data->mMaterial->materialComplete(true); delete asset_data; }); diff --git a/indra/newview/llgltfmateriallist.h b/indra/newview/llgltfmateriallist.h index f1c4ce20f9..6eab86ff0a 100644 --- a/indra/newview/llgltfmateriallist.h +++ b/indra/newview/llgltfmateriallist.h @@ -40,8 +40,6 @@ class LLGLTFOverrideCacheEntry; class LLGLTFMaterialList { public: - static const LLUUID BLANK_MATERIAL_ASSET_ID; - LLGLTFMaterialList() {} diff --git a/indra/newview/llgltfmaterialpreviewmgr.cpp b/indra/newview/llgltfmaterialpreviewmgr.cpp new file mode 100644 index 0000000000..1c39f1ae7d --- /dev/null +++ b/indra/newview/llgltfmaterialpreviewmgr.cpp @@ -0,0 +1,590 @@ +/** + * @file llgltfmaterialpreviewmgr.cpp + * + * $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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llgltfmaterialpreviewmgr.h" + +#include <memory> +#include <vector> + +#include "llavatarappearancedefines.h" +#include "llenvironment.h" +#include "llselectmgr.h" +#include "llviewercamera.h" +#include "llviewercontrol.h" +#include "llviewerobject.h" +#include "llviewershadermgr.h" +#include "llviewertexturelist.h" +#include "llviewerwindow.h" +#include "llvolumemgr.h" +#include "pipeline.h" + +LLGLTFMaterialPreviewMgr gGLTFMaterialPreviewMgr; + +namespace +{ + constexpr S32 FULLY_LOADED = 0; + constexpr S32 NOT_LOADED = 99; +}; + +LLGLTFPreviewTexture::MaterialLoadLevels::MaterialLoadLevels() +{ + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + levels[i] = NOT_LOADED; + } +} + +bool LLGLTFPreviewTexture::MaterialLoadLevels::isFullyLoaded() +{ + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + if (levels[i] != FULLY_LOADED) { return false; } + } + return true; +} + +S32& LLGLTFPreviewTexture::MaterialLoadLevels::operator[](size_t i) +{ + llassert(i >= 0 && i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT); + return levels[i]; +} + +const S32& LLGLTFPreviewTexture::MaterialLoadLevels::operator[](size_t i) const +{ + llassert(i >= 0 && i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT); + return levels[i]; +} + +bool LLGLTFPreviewTexture::MaterialLoadLevels::operator<(const MaterialLoadLevels& other) const +{ + bool less = false; + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + if (((*this)[i] > other[i])) { return false; } + less = less || ((*this)[i] < other[i]); + } + return less; +} + +bool LLGLTFPreviewTexture::MaterialLoadLevels::operator>(const MaterialLoadLevels& other) const +{ + bool great = false; + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + if (((*this)[i] < other[i])) { return false; } + great = great || ((*this)[i] > other[i]); + } + return great; +} + +namespace +{ + void fetch_texture_for_ui(LLPointer<LLViewerFetchedTexture>& img, const LLUUID& id) + { + if (!img && id.notNull()) + { + if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) + { + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + if (obj) + { + LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(id); + img = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL; + } + } + else + { + img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + } + } + if (img) + { + img->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + img->forceToSaveRawImage(0); + } + }; + + // *NOTE: Does not use the same conventions as texture discard level. Lower is better. + S32 get_texture_load_level(const LLPointer<LLViewerFetchedTexture>& texture) + { + if (!texture) { return FULLY_LOADED; } + const S32 raw_level = texture->getDiscardLevel(); + if (raw_level < 0) { return NOT_LOADED; } + return raw_level; + } + + LLGLTFPreviewTexture::MaterialLoadLevels get_material_load_levels(LLFetchedGLTFMaterial& material) + { + llassert(!material.isFetching()); + + using MaterialTextures = LLPointer<LLViewerFetchedTexture>*[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT]; + + MaterialTextures textures; + + textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = &material.mBaseColorTexture; + 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; + + LLGLTFPreviewTexture::MaterialLoadLevels levels; + + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + fetch_texture_for_ui(*textures[i], material.mTextureId[i]); + levels[i] = get_texture_load_level(*textures[i]); + } + + return levels; + } + + // Is the material loaded enough to start rendering a preview? + bool is_material_loaded_enough_for_ui(LLFetchedGLTFMaterial& material) + { + if (material.isFetching()) + { + return false; + } + + LLGLTFPreviewTexture::MaterialLoadLevels levels = get_material_load_levels(material); + + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + if (levels[i] == NOT_LOADED) + { + return false; + } + } + + return true; + } + +}; // namespace + +LLGLTFPreviewTexture::LLGLTFPreviewTexture(LLPointer<LLFetchedGLTFMaterial> material, S32 width) + : LLViewerDynamicTexture(width, width, 4, EOrder::ORDER_MIDDLE, FALSE) + , mGLTFMaterial(material) +{ +} + +// static +LLPointer<LLGLTFPreviewTexture> LLGLTFPreviewTexture::create(LLPointer<LLFetchedGLTFMaterial> material) +{ + return new LLGLTFPreviewTexture(material, LLPipeline::MAX_BAKE_WIDTH); +} + +BOOL LLGLTFPreviewTexture::needsRender() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + if (!mShouldRender && mBestLoad.isFullyLoaded()) { return false; } + MaterialLoadLevels current_load = get_material_load_levels(*mGLTFMaterial.get()); + if (current_load < mBestLoad) + { + mShouldRender = true; + mBestLoad = current_load; + return true; + } + return false; +} + +void LLGLTFPreviewTexture::preRender(BOOL clear_depth) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + llassert(mShouldRender); + if (!mShouldRender) { return; } + + LLViewerDynamicTexture::preRender(clear_depth); +} + + +namespace { + +struct GLTFPreviewModel +{ + GLTFPreviewModel(LLPointer<LLDrawInfo>& info, const LLMatrix4& mat) + : mDrawInfo(info) + , mModelMatrix(mat) + { + mDrawInfo->mModelMatrix = &mModelMatrix; + } + GLTFPreviewModel(GLTFPreviewModel&) = delete; + ~GLTFPreviewModel() + { + // No model matrix necromancy + llassert(gGLLastMatrix != &mModelMatrix); + gGLLastMatrix = nullptr; + } + LLPointer<LLDrawInfo> mDrawInfo; + LLMatrix4 mModelMatrix; // Referenced by mDrawInfo +}; + +using PreviewSpherePart = std::unique_ptr<GLTFPreviewModel>; +using PreviewSphere = std::vector<PreviewSpherePart>; + +// Like LLVolumeGeometryManager::registerFace but without batching or too-many-indices/vertices checking. +PreviewSphere create_preview_sphere(LLPointer<LLFetchedGLTFMaterial>& material, const LLMatrix4& model_matrix) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + const LLColor4U vertex_color(material->mBaseColor); + + LLPrimitive prim; + prim.init_primitive(LL_PCODE_VOLUME); + LLVolumeParams params; + params.setType(LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE); + params.setBeginAndEndS(0.f, 1.f); + params.setBeginAndEndT(0.f, 1.f); + params.setRatio(1, 1); + params.setShear(0, 0); + constexpr auto MAX_LOD = LLVolumeLODGroup::NUM_LODS - 1; + prim.setVolume(params, MAX_LOD); + + LLVolume* volume = prim.getVolume(); + llassert(volume); + for (LLVolumeFace& face : volume->getVolumeFaces()) + { + face.createTangents(); + } + + PreviewSphere preview_sphere; + preview_sphere.reserve(volume->getNumFaces()); + + LLPointer<LLVertexBuffer> buf = new LLVertexBuffer( + LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_TANGENT + ); + U32 nv = 0; + U32 ni = 0; + for (LLVolumeFace& face : volume->getVolumeFaces()) + { + nv += face.mNumVertices; + ni += face.mNumIndices; + } + buf->allocateBuffer(nv, ni); + + // UV hacks + // Higher factor helps to see more details on the preview sphere + const LLVector2 uv_factor(2.0f, 2.0f); + // Offset places center of material in center of view + const LLVector2 uv_offset(-0.5f, -0.5f); + + LLStrider<U16> indices; + LLStrider<LLVector4a> positions; + LLStrider<LLVector4a> normals; + LLStrider<LLVector2> texcoords; + LLStrider<LLColor4U> colors; + LLStrider<LLVector4a> tangents; + buf->getIndexStrider(indices); + buf->getVertexStrider(positions); + buf->getNormalStrider(normals); + buf->getTexCoord0Strider(texcoords); + buf->getColorStrider(colors); + buf->getTangentStrider(tangents); + U32 index_offset = 0; + U32 vertex_offset = 0; + for (const LLVolumeFace& face : volume->getVolumeFaces()) + { + for (S32 i = 0; i < face.mNumIndices; ++i) + { + *indices++ = face.mIndices[i] + vertex_offset; + } + for (S32 v = 0; v < face.mNumVertices; ++v) + { + *positions++ = face.mPositions[v]; + *normals++ = face.mNormals[v]; + LLVector2 uv(face.mTexCoords[v]); + uv.scaleVec(uv_factor); + uv += uv_offset; + *texcoords++ = uv; + *colors++ = vertex_color; + *tangents++ = face.mTangents[v]; + } + + constexpr LLViewerTexture* no_media = nullptr; + LLPointer<LLDrawInfo> info = new LLDrawInfo(U16(vertex_offset), U16(vertex_offset + face.mNumVertices - 1), face.mNumIndices, index_offset, no_media, buf.get()); + info->mGLTFMaterial = material; + preview_sphere.emplace_back(std::make_unique<GLTFPreviewModel>(info, model_matrix)); + index_offset += face.mNumIndices; + vertex_offset += face.mNumVertices; + } + + buf->unmapBuffer(); + + return preview_sphere; +} + +void set_preview_sphere_material(PreviewSphere& preview_sphere, LLPointer<LLFetchedGLTFMaterial>& material) +{ + llassert(!preview_sphere.empty()); + if (preview_sphere.empty()) { return; } + + const LLColor4U vertex_color(material->mBaseColor); + + // See comments about unmapBuffer in llvertexbuffer.h + for (PreviewSpherePart& part : preview_sphere) + { + LLDrawInfo* info = part->mDrawInfo.get(); + info->mGLTFMaterial = material; + LLVertexBuffer* buf = info->mVertexBuffer.get(); + LLStrider<LLColor4U> colors; + const S32 count = info->mEnd - info->mStart + 1; + buf->getColorStrider(colors, info->mStart, count); + for (S32 i = 0; i < count; ++i) + { + *colors++ = vertex_color; + } + buf->unmapBuffer(); + } +} + +PreviewSphere& get_preview_sphere(LLPointer<LLFetchedGLTFMaterial>& material, const LLMatrix4& model_matrix) +{ + static PreviewSphere preview_sphere; + if (preview_sphere.empty()) + { + preview_sphere = create_preview_sphere(material, model_matrix); + } + else + { + set_preview_sphere_material(preview_sphere, material); + } + return preview_sphere; +} + +// Final, direct modifications to shader constants, just before render +void fixup_shader_constants(LLGLSLShader& shader) +{ + // Sunlight intensity of 0 no matter what + shader.uniform1i(LLShaderMgr::SUN_UP_FACTOR, 1); + shader.uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, LLColor3::white.mV); + shader.uniform1f(LLShaderMgr::DENSITY_MULTIPLIER, 0.0f); + + // Ignore sun shadow (if enabled) + for (U32 i = 0; i < 6; i++) + { + const S32 channel = shader.getTextureChannel(LLShaderMgr::DEFERRED_SHADOW0+i); + if (channel != -1) + { + gGL.getTexUnit(channel)->bind(LLViewerFetchedTexture::sWhiteImagep, TRUE); + } + } +} + +// Set a variable to a value temporarily, and restor the variable's old value +// when this object leaves scope. +template<typename T> +struct SetTemporarily +{ + T* mRef; + T mOldVal; + SetTemporarily(T* var, T temp_val) + { + mRef = var; + mOldVal = *mRef; + *mRef = temp_val; + } + ~SetTemporarily() + { + *mRef = mOldVal; + } +}; + +}; // namespace + +BOOL LLGLTFPreviewTexture::render() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + if (!mShouldRender) { return FALSE; } + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + LLGLDepthTest(GL_FALSE); + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + SetTemporarily<bool> no_dof(&LLPipeline::RenderDepthOfField, false); + SetTemporarily<bool> no_glow(&LLPipeline::sRenderGlow, false); + SetTemporarily<bool> no_ssr(&LLPipeline::RenderScreenSpaceReflections, false); + SetTemporarily<U32> no_fxaa(&LLPipeline::RenderFSAASamples, U32(0)); + SetTemporarily<LLPipeline::RenderTargetPack*> use_auxiliary_render_target(&gPipeline.mRT, &gPipeline.mAuxillaryRT); + + LLVector3 light_dir3(1.0f, 1.0f, 1.0f); + light_dir3.normalize(); + const LLVector4 light_dir = LLVector4(light_dir3, 0); + const S32 old_local_light_count = gSavedSettings.get<S32>("RenderLocalLightCount"); + gSavedSettings.set<S32>("RenderLocalLightCount", 0); + + gPipeline.mReflectionMapManager.forceDefaultProbeAndUpdateUniforms(); + + LLViewerCamera camera; + + // Calculate the object distance at which the object of a given radius will + // span the partial width of the screen given by fill_ratio. + // Assume the primitive has a scale of 1 (this is the default). + constexpr F32 fill_ratio = 0.8f; + constexpr F32 object_radius = 0.5f; + const F32 object_distance = (object_radius / fill_ratio) * tan(camera.getDefaultFOV()); + // Negative coordinate shows the textures on the sphere right-side up, when + // combined with the UV hacks in create_preview_sphere + const LLVector3 object_position(0.0, -object_distance, 0.0); + LLMatrix4 object_transform; + object_transform.translate(object_position); + + // Set up camera and viewport + const LLVector3 origin(0.0, 0.0, 0.0); + camera.lookAt(origin, object_position); + camera.setAspect(mFullHeight / mFullWidth); + const LLRect texture_rect(0, mFullHeight, mFullWidth, 0); + camera.setPerspective(NOT_FOR_SELECTION, texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight(), FALSE, camera.getNear(), MAX_FAR_CLIP*2.f); + + // Generate sphere object on-the-fly. Discard afterwards. (Vertex buffer is + // discarded, but the sphere should be cached in LLVolumeMgr.) + PreviewSphere& preview_sphere = get_preview_sphere(mGLTFMaterial, object_transform); + + gPipeline.setupHWLights(); + glh::matrix4f mat = copy_matrix(gGLModelView); + glh::vec4f transformed_light_dir(light_dir.mV); + mat.mult_matrix_vec(transformed_light_dir); + SetTemporarily<LLVector4> force_sun_direction_high_graphics(&gPipeline.mTransformedSunDir, LLVector4(transformed_light_dir.v)); + // Override lights to ensure the sun is always shining from a certain direction (low graphics) + // See also force_sun_direction_high_graphics and fixup_shader_constants + { + LLLightState* light = gGL.getLight(0); + light->setPosition(light_dir); + constexpr bool sun_up = true; + light->setSunPrimary(sun_up); + } + + LLRenderTarget& screen = gPipeline.mAuxillaryRT.screen; + + // *HACK: Force reset of the model matrix + gGLLastMatrix = nullptr; + +#if 0 + if (mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_OPAQUE || mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK) + { + // *TODO: Opaque/alpha mask rendering + } + else +#endif + { + // Alpha blend rendering + + screen.bindTarget(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + LLGLSLShader& shader = gDeferredPBRAlphaProgram; + + gPipeline.bindDeferredShader(shader); + fixup_shader_constants(shader); + + for (PreviewSpherePart& part : preview_sphere) + { + LLRenderPass::pushGLTFBatch(*part->mDrawInfo); + } + + gPipeline.unbindDeferredShader(shader); + + screen.flush(); + } + + // *HACK: Hide mExposureMap from generateExposure + gPipeline.mExposureMap.swapFBORefs(gPipeline.mLastExposure); + + gPipeline.copyScreenSpaceReflections(&screen, &gPipeline.mSceneMap); + gPipeline.generateLuminance(&screen, &gPipeline.mLuminanceMap); + gPipeline.generateExposure(&gPipeline.mLuminanceMap, &gPipeline.mExposureMap, /*use_history = */ false); + gPipeline.gammaCorrect(&screen, &gPipeline.mPostMap); + LLVertexBuffer::unbind(); + gPipeline.generateGlow(&gPipeline.mPostMap); + gPipeline.combineGlow(&gPipeline.mPostMap, &screen); + gPipeline.renderDoF(&screen, &gPipeline.mPostMap); + gPipeline.applyFXAA(&gPipeline.mPostMap, &screen); + + // *HACK: Restore mExposureMap (it will be consumed by generateExposure next frame) + gPipeline.mExposureMap.swapFBORefs(gPipeline.mLastExposure); + + // Final render + + gDeferredPostNoDoFProgram.bind(); + + // From LLPipeline::renderFinalize: "Whatever is last in the above post processing chain should _always_ be rendered directly here. If not, expect problems." + gDeferredPostNoDoFProgram.bindTexture(LLShaderMgr::DEFERRED_DIFFUSE, &screen); + gDeferredPostNoDoFProgram.bindTexture(LLShaderMgr::DEFERRED_DEPTH, mBoundTarget, true); + + { + LLGLDepthTest depth_test(GL_TRUE, GL_TRUE, GL_ALWAYS); + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + } + + gDeferredPostNoDoFProgram.unbind(); + + // Clean up + gPipeline.setupHWLights(); + gPipeline.mReflectionMapManager.forceDefaultProbeAndUpdateUniforms(false); + gSavedSettings.set<S32>("RenderLocalLightCount", old_local_light_count); + + return TRUE; +} + +void LLGLTFPreviewTexture::postRender(BOOL success) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + + if (!mShouldRender) { return; } + mShouldRender = false; + + LLViewerDynamicTexture::postRender(success); +} + +LLPointer<LLViewerTexture> LLGLTFMaterialPreviewMgr::getPreview(LLPointer<LLFetchedGLTFMaterial> &material) +{ + if (!material) + { + return nullptr; + } + + static LLCachedControl<bool> sUIPreviewMaterial(gSavedSettings, "UIPreviewMaterial", false); + if (!sUIPreviewMaterial) + { + fetch_texture_for_ui(material->mBaseColorTexture, material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]); + return material->mBaseColorTexture; + } + + if (!is_material_loaded_enough_for_ui(*material)) + { + return nullptr; + } + + return LLGLTFPreviewTexture::create(material); +} diff --git a/indra/newview/llgltfmaterialpreviewmgr.h b/indra/newview/llgltfmaterialpreviewmgr.h new file mode 100644 index 0000000000..981c8b0592 --- /dev/null +++ b/indra/newview/llgltfmaterialpreviewmgr.h @@ -0,0 +1,78 @@ +/** + * @file llgltfmaterialpreviewmgr.h + * + * $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 "lldrawpool.h" +#include "lldynamictexture.h" +#include "llfetchedgltfmaterial.h" +#include "llsingleton.h" +#include "lltexture.h" + +class LLGLTFPreviewTexture : public LLViewerDynamicTexture +{ +protected: + LLGLTFPreviewTexture(LLPointer<LLFetchedGLTFMaterial> material, S32 width); + +public: + // Width scales with size of material's textures + static LLPointer<LLGLTFPreviewTexture> create(LLPointer<LLFetchedGLTFMaterial> material); + + BOOL needsRender() override; + void preRender(BOOL clear_depth = TRUE) override; + BOOL render() override; + void postRender(BOOL success) override; + + struct MaterialLoadLevels + { + S32 levels[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT]; + + MaterialLoadLevels(); + bool isFullyLoaded(); + S32& operator[](size_t i); + const S32& operator[](size_t i) const; + // Less is better + // Returns false if lhs is not strictly less or equal for all levels + bool operator<(const MaterialLoadLevels& other) const; + // Less is better + // Returns false if lhs is not strictly greater or equal for all levels + bool operator>(const MaterialLoadLevels& other) const; + }; + +private: + LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial; + bool mShouldRender = true; + MaterialLoadLevels mBestLoad; +}; + +class LLGLTFMaterialPreviewMgr +{ + public: + // Returns null if the material is not loaded yet. + // *NOTE: User should cache the texture if the same material is being previewed + LLPointer<LLViewerTexture> getPreview(LLPointer<LLFetchedGLTFMaterial> &material); +}; + +extern LLGLTFMaterialPreviewMgr gGLTFMaterialPreviewMgr; diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp new file mode 100644 index 0000000000..dd29b416fb --- /dev/null +++ b/indra/newview/llheroprobemanager.cpp @@ -0,0 +1,607 @@ +/** + * @file LLHeroProbeManager.cpp + * @brief LLHeroProbeManager class implementation + * + * $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 "llheroprobemanager.h" +#include "llreflectionmapmanager.h" +#include "llviewercamera.h" +#include "llspatialpartition.h" +#include "llviewerregion.h" +#include "pipeline.h" +#include "llviewershadermgr.h" +#include "llviewercontrol.h" +#include "llenvironment.h" +#include "llstartup.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llviewerwindow.h" +#include "llviewerjoystick.h" +#include "llviewermediafocus.h" + +extern BOOL gCubeSnapshot; +extern BOOL gTeleportDisplay; + +// get the next highest power of two of v (or v if v is already a power of two) +//defined in llvertexbuffer.cpp +extern U32 nhpo2(U32 v); + +static void touch_default_probe(LLReflectionMap* probe) +{ + if (LLViewerCamera::getInstance()) + { + LLVector3 origin = LLViewerCamera::getInstance()->getOrigin(); + origin.mV[2] += 64.f; + + probe->mOrigin.load3(origin.mV); + } +} + +LLHeroProbeManager::LLHeroProbeManager() +{ +} + +LLHeroProbeManager::~LLHeroProbeManager() +{ + cleanup(); + + mHeroVOList.clear(); + mNearestHero = nullptr; +} + +// helper class to seed octree with probes +void LLHeroProbeManager::update() +{ + if (!LLPipeline::RenderMirrors || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE) + { + return; + } + + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + llassert(!gCubeSnapshot); // assert a snapshot is not in progress + if (LLAppViewer::instance()->logoutRequestSent()) + { + return; + } + + initReflectionMaps(); + + if (!mRenderTarget.isComplete()) + { + U32 color_fmt = GL_RGBA16F; + mRenderTarget.allocate(mProbeResolution, mProbeResolution, color_fmt, true); + } + + if (mMipChain.empty()) + { + U32 res = mProbeResolution; + U32 count = log2((F32)res) + 0.5f; + + mMipChain.resize(count); + for (int i = 0; i < count; ++i) + { + mMipChain[i].allocate(res, res, GL_RGBA16F); + res /= 2; + } + } + + llassert(mProbes[0] == mDefaultProbe); + + LLVector4a probe_pos; + LLVector3 camera_pos = LLViewerCamera::instance().mOrigin; + F32 near_clip = 0.1f; + if (mHeroVOList.size() > 0) + { + // Find our nearest hero candidate. + + float last_distance = 99999.f; + + for (auto vo : mHeroVOList) + { + if (vo && !vo->isDead() && vo->mDrawable.notNull()) + { + float distance = (LLViewerCamera::instance().getOrigin() - vo->getPositionAgent()).magVec(); + if (distance < last_distance) + { + mNearestHero = vo; + last_distance = distance; + } + } + else + { + unregisterViewerObject(vo); + } + } + + if (mNearestHero != nullptr && !mNearestHero->isDead() && mNearestHero->mDrawable.notNull()) + { + 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; + + mCurrentClipPlane.setVec(hero_pos, face_normal); + mMirrorPosition = hero_pos; + mMirrorNormal = face_normal; + + + probe_pos.load3(point.mV); + + // Collect the list of faces that need updating based upon the camera's rotation. + LLVector3 cam_direction = LLVector3(0, 0, 1) * LLViewerCamera::instance().getQuaternion(); + cam_direction.normalize(); + + 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) + }; + + for (int i = 0; i < 6; i++) + { + float shouldUpdate = fminf(1, (fmaxf(-1, cam_direction * cubeFaces[i]) * 0.5 + 0.5)); + + int updateRate = ceilf((1 - shouldUpdate) * gPipeline.RenderHeroProbeConservativeUpdateMultiplier); + + // Chances are this is a face that's non-visible to the camera when it's being reflected. + // Set it to 0. It will be skipped below. + if (updateRate == gPipeline.RenderHeroProbeConservativeUpdateMultiplier) + updateRate = 0; + + mFaceUpdateList[i] = updateRate; + } + } + else + { + mNearestHero = nullptr; + } + + mHeroProbeStrength = 1; + } + else + { + probe_pos.load3(camera_pos.mV); + } + + + static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1); + static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3); + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime"); + // Probe 0 is always our mirror probe. + mProbes[0]->mOrigin = probe_pos; + + bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass(); + + gPipeline.mReflectionMapManager.mRadiancePass = true; + mRenderingMirror = true; + for (U32 j = 0; j < mProbes.size(); j++) + { + for (U32 i = 0; i < 6; ++i) + { + if (mFaceUpdateList[i] > 0 && mCurrentProbeUpdateFrame % mFaceUpdateList[i] == 0) + { + updateProbeFace(mProbes[j], i, near_clip); + mCurrentProbeUpdateFrame = 0; + } + } + generateRadiance(mProbes[j]); + } + mRenderingMirror = false; + + gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass; + + mProbes[0]->mViewerObject = mNearestHero; + mProbes[0]->autoAdjustOrigin(); + } + + mCurrentProbeUpdateFrame++; +} + +// Do the reflection map update render passes. +// For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated +// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate +// a simple mip chain (not convolution filter). +// At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe +// The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain. +// At the end of these passes, a radiance map is generated for this probe and placed into the radiance cube map array at the index for this probe. +// In effect this simulates single-bounce lighting. +void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, F32 near_clip) +{ + // hacky hot-swap of camera specific render targets + gPipeline.mRT = &gPipeline.mHeroProbeRT; + + probe->update(mRenderTarget.getWidth(), face, true, near_clip); + + gPipeline.mRT = &gPipeline.mMainRT; + + S32 sourceIdx = mReflectionProbeCount; + + + // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes. + sourceIdx += 1; + + gGL.setColorMask(true, true); + LLGLDepthTest depth(GL_FALSE, GL_FALSE); + LLGLDisable cull(GL_CULL_FACE); + LLGLDisable blend(GL_BLEND); + + // downsample to placeholder map + { + gGL.matrixMode(gGL.MM_MODELVIEW); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.matrixMode(gGL.MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + + gGL.flush(); + U32 res = mProbeResolution * 2; + + static LLStaticHashedString resScale("resScale"); + static LLStaticHashedString direction("direction"); + static LLStaticHashedString znear("znear"); + static LLStaticHashedString zfar("zfar"); + + LLRenderTarget *screen_rt = &gPipeline.mHeroProbeRT.screen; + LLRenderTarget *depth_rt = &gPipeline.mHeroProbeRT.deferredScreen; + + // perform a gaussian blur on the super sampled render before downsampling + { + gGaussianProgram.bind(); + gGaussianProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2)); + S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE); + + // horizontal + gGaussianProgram.uniform2f(direction, 1.f, 0.f); + gGL.getTexUnit(diffuseChannel)->bind(screen_rt); + mRenderTarget.bindTarget(); + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + mRenderTarget.flush(); + + // vertical + gGaussianProgram.uniform2f(direction, 0.f, 1.f); + gGL.getTexUnit(diffuseChannel)->bind(&mRenderTarget); + screen_rt->bindTarget(); + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + screen_rt->flush(); + gGaussianProgram.unbind(); + } + + S32 mips = log2((F32)mProbeResolution) + 0.5f; + + gReflectionMipProgram.bind(); + S32 diffuseChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE); + S32 depthChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_TEXTURE); + + for (int i = 0; i < mMipChain.size(); ++i) + { + LL_PROFILE_GPU_ZONE("probe mip"); + mMipChain[i].bindTarget(); + if (i == 0) + { + gGL.getTexUnit(diffuseChannel)->bind(screen_rt); + } + else + { + gGL.getTexUnit(diffuseChannel)->bind(&(mMipChain[i - 1])); + } + + gGL.getTexUnit(depthChannel)->bind(depth_rt, true); + + gReflectionMipProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2)); + gReflectionMipProgram.uniform1f(znear, probe->getNearClip()); + gReflectionMipProgram.uniform1f(zfar, MAX_FAR_CLIP); + + gPipeline.mScreenTriangleVB->setBuffer(); + gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); + + res /= 2; + + S32 mip = i - (mMipChain.size() - mips); + + if (mip >= 0) + { + LL_PROFILE_GPU_ZONE("probe mip copy"); + mTexture->bind(0); + + glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, sourceIdx * 6 + face, 0, 0, res, res); + + mTexture->unbind(); + } + mMipChain[i].flush(); + } + + gGL.popMatrix(); + gGL.matrixMode(gGL.MM_MODELVIEW); + gGL.popMatrix(); + + gGL.getTexUnit(diffuseChannel)->unbind(LLTexUnit::TT_TEXTURE); + gReflectionMipProgram.unbind(); + } +} + +// Separate out radiance generation as a separate stage. +// This is to better enable independent control over how we generate radiance vs. having it coupled with processing the final face of the probe. +// Useful when we may not always be rendering a full set of faces of the probe. +void LLHeroProbeManager::generateRadiance(LLReflectionMap* probe) +{ + S32 sourceIdx = mReflectionProbeCount; + + // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes. + sourceIdx += 1; + { + mMipChain[0].bindTarget(); + static LLStaticHashedString sSourceIdx("sourceIdx"); + + { + + + // generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map) + gHeroRadianceGenProgram.bind(); + mVertexBuffer->setBuffer(); + + S32 channel = gHeroRadianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY); + mTexture->bind(channel); + gHeroRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx); + gHeroRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD); + gHeroRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, mHeroProbeStrength); + + U32 res = mMipChain[0].getWidth(); + + for (int i = 0; i < mMipChain.size() / 4; ++i) + { + LL_PROFILE_GPU_ZONE("probe radiance gen"); + static LLStaticHashedString sMipLevel("mipLevel"); + static LLStaticHashedString sRoughness("roughness"); + static LLStaticHashedString sWidth("u_width"); + static LLStaticHashedString sStrength("probe_strength"); + + gHeroRadianceGenProgram.uniform1f(sRoughness, (F32) i / (F32) (mMipChain.size() - 1)); + gHeroRadianceGenProgram.uniform1f(sMipLevel, i); + gHeroRadianceGenProgram.uniform1i(sWidth, mProbeResolution); + gHeroRadianceGenProgram.uniform1f(sStrength, 1); + + for (int cf = 0; cf < 6; ++cf) + { // for each cube face + LLCoordFrame frame; + frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]); + + F32 mat[16]; + frame.getOpenGLRotation(mat); + gGL.loadMatrix(mat); + + mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4); + + glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res); + } + + if (i != mMipChain.size() - 1) + { + res /= 2; + glViewport(0, 0, res, res); + } + } + + gHeroRadianceGenProgram.unbind(); + } + + mMipChain[0].flush(); + } +} + +void LLHeroProbeManager::updateUniforms() +{ + if (!gPipeline.RenderMirrors) + { + return; + } + + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + + LLMatrix4a modelview; + modelview.loadu(gGLModelView); + LLVector4a oa; // scratch space for transformed origin + oa.set(0, 0, 0, 0); + mHeroData.heroProbeCount = 1; + + if (mNearestHero != nullptr && !mNearestHero->isDead()) + { + if (mNearestHero->getReflectionProbeIsBox()) + { + LLVector3 s = mNearestHero->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f)); + mProbes[0]->mRadius = s.magVec(); + } + else + { + mProbes[0]->mRadius = mNearestHero->getScale().mV[0] * 0.5f; + } + + modelview.affineTransform(mProbes[0]->mOrigin, oa); + mHeroData.heroShape = 0; + if (!mProbes[0]->getBox(mHeroData.heroBox)) + { + mHeroData.heroShape = 1; + } + + mHeroData.heroSphere.set(oa.getF32ptr()); + mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius; + } + + mHeroData.heroMipCount = mMipChain.size(); +} + +void LLHeroProbeManager::renderDebug() +{ + gDebugProgram.bind(); + + for (auto& probe : mProbes) + { + renderReflectionProbe(probe); + } + + gDebugProgram.unbind(); +} + + +void LLHeroProbeManager::initReflectionMaps() +{ + U32 count = LL_MAX_HERO_PROBE_COUNT; + + if ((mTexture.isNull() || mReflectionProbeCount != count || mReset) && LLPipeline::RenderMirrors) + { + + if (mReset) + { + cleanup(); + } + + mReset = false; + mReflectionProbeCount = count; + mProbeResolution = gSavedSettings.getS32("RenderHeroProbeResolution"); + mMaxProbeLOD = log2f(mProbeResolution) - 1.f; // number of mips - 1 + + mTexture = new LLCubeMapArray(); + + // 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); + + if (mDefaultProbe.isNull()) + { + llassert(mProbes.empty()); // default probe MUST be the first probe created + mDefaultProbe = new LLReflectionMap(); + mProbes.push_back(mDefaultProbe); + } + + 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); + } + + if (mVertexBuffer.isNull()) + { + U32 mask = LLVertexBuffer::MAP_VERTEX; + LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask); + buff->allocateBuffer(4, 0); + + LLStrider<LLVector3> v; + + buff->getVertexStrider(v); + + v[0] = LLVector3(-1, -1, -1); + v[1] = LLVector3(1, -1, -1); + v[2] = LLVector3(-1, 1, -1); + v[3] = LLVector3(1, 1, -1); + + buff->unmapBuffer(); + + mVertexBuffer = buff; + } +} + +void LLHeroProbeManager::cleanup() +{ + mVertexBuffer = nullptr; + mRenderTarget.release(); + mHeroRenderTarget.release(); + + mMipChain.clear(); + + mTexture = nullptr; + + mProbes.clear(); + + mReflectionMaps.clear(); + + mDefaultProbe = nullptr; + mUpdatingProbe = nullptr; + /* + */ +} + +void LLHeroProbeManager::doOcclusion() +{ + LLVector4a eye; + eye.load3(LLViewerCamera::instance().getOrigin().mV); + + for (auto& probe : mProbes) + { + if (probe != nullptr && probe != mDefaultProbe) + { + probe->doOcclusion(eye); + } + } +} + +void LLHeroProbeManager::reset() +{ + mReset = true; +} + +bool LLHeroProbeManager::registerViewerObject(LLVOVolume* drawablep) +{ + llassert(drawablep != nullptr); + + if (std::find(mHeroVOList.begin(), mHeroVOList.end(), drawablep) == mHeroVOList.end()) + { + // Probe isn't in our list for consideration. Add it. + mHeroVOList.push_back(drawablep); + return true; + } + + return false; +} + +void LLHeroProbeManager::unregisterViewerObject(LLVOVolume* drawablep) +{ + std::vector<LLPointer<LLVOVolume>>::iterator found_itr = std::find(mHeroVOList.begin(), mHeroVOList.end(), drawablep); + if (found_itr != mHeroVOList.end()) + { + mHeroVOList.erase(found_itr); + } +} diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h new file mode 100644 index 0000000000..d5e720e8e8 --- /dev/null +++ b/indra/newview/llheroprobemanager.h @@ -0,0 +1,156 @@ +/** + * @file llheroprobemanager.h + * @brief LLHeroProbeManager class declaration + * + * $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$ + */ + +#pragma once + +#include "llreflectionmap.h" +#include "llrendertarget.h" +#include "llcubemaparray.h" +#include "llcubemap.h" +#include "lldrawable.h" + +class LLSpatialGroup; +class LLViewerObject; + +// number of reflection probes to keep in vram +#define LL_MAX_HERO_PROBE_COUNT 2 + +struct HeroProbeData +{ + LLMatrix4 heroBox; + LLVector4 heroSphere; + GLint heroShape; + GLint heroMipCount; + GLint heroProbeCount; +}; + +class alignas(16) LLHeroProbeManager +{ + LL_ALIGN_NEW +public: + enum class DetailLevel + { + STATIC_ONLY = 0, + STATIC_AND_DYNAMIC, + REALTIME = 2 + }; + + // allocate an environment map of the given resolution + LLHeroProbeManager(); + ~LLHeroProbeManager(); + + // release any GL state + void cleanup(); + + // maintain reflection probes + void update(); + + // debug display, called from llspatialpartition if reflection + // probe debug display is active + void renderDebug(); + + // call once at startup to allocate cubemap arrays + void initReflectionMaps(); + + // perform occlusion culling on all active reflection probes + void doOcclusion(); + + void reset(); + + bool registerViewerObject(LLVOVolume *drawablep); + void unregisterViewerObject(LLVOVolume* drawablep); + + bool isMirrorPass() const { return mRenderingMirror; } + + LLVector3 mMirrorPosition; + LLVector3 mMirrorNormal; + HeroProbeData mHeroData; + +private: + friend class LLPipeline; + friend class LLReflectionMapManager; + + // update UBO used for rendering (call only once per render pipe flush) + void updateUniforms(); + + // bind UBO used for rendering + + // render target for cube snapshots + // used to generate mipmaps without doing a copy-to-texture + LLRenderTarget mRenderTarget; + + LLRenderTarget mHeroRenderTarget; + + std::vector<LLRenderTarget> mMipChain; + + // storage for reflection probe radiance maps (plus two scratch space cubemaps) + LLPointer<LLCubeMapArray> mTexture; + + // vertex buffer for pushing verts to filter shaders + LLPointer<LLVertexBuffer> mVertexBuffer; + + LLPlane mCurrentClipPlane; + + + // update the specified face of the specified probe + void updateProbeFace(LLReflectionMap* probe, U32 face, F32 near_clip); + void generateRadiance(LLReflectionMap *probe); + + // list of active reflection maps + std::vector<LLPointer<LLReflectionMap>> mProbes; + + // list of maps being used for rendering + std::vector<LLReflectionMap*> mReflectionMaps; + + LLReflectionMap* mUpdatingProbe = nullptr; + + LLPointer<LLReflectionMap> mDefaultProbe; // default reflection probe to fall back to for pixels with no probe influences (should always be at cube index 0) + + // number of reflection probes to use for rendering + U32 mReflectionProbeCount; + + // resolution of reflection probes + U32 mProbeResolution = 1024; + + // maximum LoD of reflection probes (mip levels - 1) + F32 mMaxProbeLOD = 6.f; + + F32 mHeroProbeStrength = 1.f; + bool mIsInTransition = false; + + // if true, reset all probe render state on the next update (for teleports and sky changes) + bool mReset = false; + + bool mRenderingMirror = false; + std::map<int, int> mFaceUpdateList; + + U32 mCurrentProbeUpdateFrame = 0; + + std::vector<LLPointer<LLVOVolume>> mHeroVOList; + LLPointer<LLVOVolume> mNearestHero; + +}; + diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index b7828db3a9..84446b626a 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -489,11 +489,10 @@ BOOL LLMaterialEditor::postBuild() } else { - S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); - getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost)); - getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost)); - getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost)); - getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost)); + getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched))); + getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched))); + getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched))); + getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched))); } boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void* userData) @@ -842,25 +841,24 @@ void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag) setCanSave(false); } - S32 upload_texture_count = 0; - if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId()) + mExpectedUploadCost = 0; + if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId() && mBaseColorFetched) { - upload_texture_count++; + mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched); } - if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId()) + if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId() && mMetallicRoughnessFetched) { - upload_texture_count++; + mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched); } - if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId()) + if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId() && mEmissiveFetched) { - upload_texture_count++; + mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched); } - if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId()) + if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId() && mNormalFetched) { - upload_texture_count++; + mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched); } - mExpectedUploadCost = upload_texture_count * LLAgentBenefitsMgr::current().getTextureUploadCost(); getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost)); } @@ -1870,7 +1868,7 @@ static void pack_textures( if (normal_img) { // create a losslessly compressed version of the normal map - normal_j2c = LLViewerTextureList::convertToUploadFile(normal_img, 1024, false, true); + normal_j2c = LLViewerTextureList::convertToUploadFile(normal_img, 2048, false, true); LL_DEBUGS("MaterialEditor") << "Normal: " << normal_j2c->getDataSize() << LL_ENDL; } @@ -2124,7 +2122,7 @@ bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<Pe // Look for the item to base permissions off of item_out = nullptr; - const bool blank_material = func.mMaterialId == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + const bool blank_material = func.mMaterialId == BLANK_MATERIAL_ASSET_ID; if (!blank_material) { LLAssetIDMatchesWithPerms item_has_perms(func.mMaterialId, ops); @@ -3495,8 +3493,7 @@ void LLMaterialEditor::saveTexture(LLImageJ2C* img, const std::string& name, con std::string buffer; buffer.assign((const char*) img->getData(), img->getDataSize()); - U32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); - + U32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(img); LLSD key = getKey(); std::function<bool(LLUUID itemId, LLSD response, std::string reason)> failed_upload([key](LLUUID assetId, LLSD response, std::string reason) { diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index a527ebe47f..5ee25b76a2 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -328,7 +328,7 @@ BOOL LLPanelFace::postBuild() if (pbr_ctrl) { pbr_ctrl->setDefaultImageAssetID(LLUUID::null); - pbr_ctrl->setBlankImageAssetID(LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID); + pbr_ctrl->setBlankImageAssetID(BLANK_MATERIAL_ASSET_ID); pbr_ctrl->setCommitCallback(boost::bind(&LLPanelFace::onCommitPbr, this, _2)); pbr_ctrl->setOnCancelCallback(boost::bind(&LLPanelFace::onCancelPbr, this, _2)); pbr_ctrl->setOnSelectCallback(boost::bind(&LLPanelFace::onSelectPbr, this, _2)); @@ -1793,6 +1793,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild<LLUICtrl>("shinyOffsetV")->setValue(offset_y); getChild<LLUICtrl>("glossiness")->setValue(material->getSpecularLightExponent()); getChild<LLUICtrl>("environment")->setValue(material->getEnvironmentIntensity()); + getChild<LLUICtrl>("mirror")->setValue(material->getEnvironmentIntensity()); updateShinyControls(!material->getSpecularID().isNull(), true); } @@ -2028,7 +2029,7 @@ void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, { mVOInventoryListener = nullptr; } - if (!identical_pbr || pbr_id.isNull() || pbr_id == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID) + if (!identical_pbr || pbr_id.isNull() || pbr_id == BLANK_MATERIAL_ASSET_ID) { mAgentInventoryListener = nullptr; } diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index a64b85815f..05860b995f 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -145,6 +145,7 @@ protected: void sendTexGen(); // applies and sends bump map void sendShiny(U32 shininess); // applies and sends shininess void sendFullbright(); // applies and sends full bright + void sendGlow(); void alignTestureLayer(); @@ -234,7 +235,7 @@ protected: static void onCommitShiny( LLUICtrl* ctrl, void* userdata); static void onCommitAlphaMode( LLUICtrl* ctrl, void* userdata); static void onCommitFullbright( LLUICtrl* ctrl, void* userdata); - static void onCommitGlow( LLUICtrl* ctrl, void *userdata); + static void onCommitGlow( LLUICtrl* ctrl, void *userdata); static void onCommitPlanarAlign( LLUICtrl* ctrl, void* userdata); static void onCommitRepeatsPerMeter( LLUICtrl* ctrl, void* userinfo); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 20241aac24..2db431d165 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -276,14 +276,12 @@ BOOL LLPanelMainInventory::postBuild() initListCommandsHandlers(); - const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()); const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost()); const std::string animation_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getAnimationUploadCost()); LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get(); if (menu) { - menu->getChild<LLMenuItemGL>("Upload Image")->setLabelArg("[COST]", texture_upload_cost_str); menu->getChild<LLMenuItemGL>("Upload Sound")->setLabelArg("[COST]", sound_upload_cost_str); menu->getChild<LLMenuItemGL>("Upload Animation")->setLabelArg("[COST]", animation_upload_cost_str); } @@ -2229,11 +2227,9 @@ void LLPanelMainInventory::setUploadCostIfNeeded() LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get(); if(mNeedUploadCost && menu) { - const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()); const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost()); const std::string animation_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getAnimationUploadCost()); - menu->getChild<LLView>("Upload Image")->setLabelArg("[COST]", texture_upload_cost_str); menu->getChild<LLView>("Upload Sound")->setLabelArg("[COST]", sound_upload_cost_str); menu->getChild<LLView>("Upload Animation")->setLabelArg("[COST]", animation_upload_cost_str); } diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 595609b4de..8d8263448d 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -150,7 +150,7 @@ BOOL LLPanelVolume::postBuild() // REFLECTION PROBE Parameters { childSetCommitCallback("Reflection Probe", onCommitIsReflectionProbe, this); - childSetCommitCallback("Probe Dynamic", onCommitProbe, this); + childSetCommitCallback("Probe Update Type", onCommitProbe, this); childSetCommitCallback("Probe Volume Type", onCommitProbe, this); childSetCommitCallback("Probe Ambiance", onCommitProbe, this); childSetCommitCallback("Probe Near Clip", onCommitProbe, this); @@ -304,7 +304,7 @@ void LLPanelVolume::getState( ) getChildView("select_single")->setVisible(true); getChildView("select_single")->setEnabled(true); } - + // Light properties BOOL is_light = volobjp && volobjp->getIsLight(); getChild<LLUICtrl>("Light Checkbox Ctrl")->setValue(is_light); @@ -392,17 +392,25 @@ void LLPanelVolume::getState( ) bool probe_enabled = is_probe && editable && single_volume; + bool mirrors_enabled = LLPipeline::RenderMirrors; + + getChildView("Probe Update Type")->setVisible(mirrors_enabled); + getChildView("Probe Update Label")->setVisible(mirrors_enabled); + getChildView("Probe Dynamic")->setVisible(!mirrors_enabled); + getChildView("Probe Dynamic")->setEnabled(probe_enabled); + getChildView("Probe Update Type")->setEnabled(probe_enabled); getChildView("Probe Volume Type")->setEnabled(probe_enabled); getChildView("Probe Ambiance")->setEnabled(probe_enabled); getChildView("Probe Near Clip")->setEnabled(probe_enabled); + getChildView("Probe Update Label")->setEnabled(probe_enabled); if (!probe_enabled) { getChild<LLComboBox>("Probe Volume Type", true)->clear(); getChild<LLSpinCtrl>("Probe Ambiance", true)->clear(); getChild<LLSpinCtrl>("Probe Near Clip", true)->clear(); - getChild<LLCheckBoxCtrl>("Probe Dynamic", true)->clear(); + getChild<LLComboBox>("Probe Update Type", true)->clear(); } else { @@ -416,10 +424,28 @@ void LLPanelVolume::getState( ) volume_type = "Sphere"; } + std::string update_type; + if (volobjp->getReflectionProbeIsDynamic()) + { + update_type = "Dynamic"; + } + else if (volobjp->getReflectionProbeIsMirror()) + { + update_type = "Mirror"; + + } + else + { + update_type = "Static"; + } + + getChildView("Probe Ambiance")->setEnabled(update_type != "Mirror"); + getChildView("Probe Near Clip")->setEnabled(update_type != "Mirror"); + getChild<LLComboBox>("Probe Volume Type", true)->setValue(volume_type); getChild<LLSpinCtrl>("Probe Ambiance", true)->setValue(volobjp->getReflectionProbeAmbiance()); getChild<LLSpinCtrl>("Probe Near Clip", true)->setValue(volobjp->getReflectionProbeNearClip()); - getChild<LLCheckBoxCtrl>("Probe Dynamic", true)->setValue(volobjp->getReflectionProbeIsDynamic()); + getChild<LLComboBox>("Probe Update Type", true)->setValue(update_type); } // Animated Mesh @@ -706,7 +732,7 @@ void LLPanelVolume::clearCtrls() getChildView("Reflection Probe")->setEnabled(false);; getChildView("Probe Volume Type")->setEnabled(false); - getChildView("Probe Dynamic")->setEnabled(false); + getChildView("Probe Update Type")->setEnabled(false); getChildView("Probe Ambiance")->setEnabled(false); getChildView("Probe Near Clip")->setEnabled(false); getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(false); @@ -1399,7 +1425,14 @@ void LLPanelVolume::onCommitProbe(LLUICtrl* ctrl, void* userdata) volobjp->setReflectionProbeAmbiance((F32)self->getChild<LLUICtrl>("Probe Ambiance")->getValue().asReal()); volobjp->setReflectionProbeNearClip((F32)self->getChild<LLUICtrl>("Probe Near Clip")->getValue().asReal()); - volobjp->setReflectionProbeIsDynamic(self->getChild<LLUICtrl>("Probe Dynamic")->getValue().asBoolean()); + + std::string update_type = self->getChild<LLUICtrl>("Probe Update Type")->getValue().asString(); + + volobjp->setReflectionProbeIsDynamic(update_type == "Dynamic"); + volobjp->setReflectionProbeIsMirror(update_type == "Mirror"); + + self->getChildView("Probe Ambiance")->setEnabled(update_type != "Mirror"); + self->getChildView("Probe Near Clip")->setEnabled(update_type != "Mirror"); std::string shape_type = self->getChild<LLUICtrl>("Probe Volume Type")->getValue().asString(); diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index 01b7ebb75c..aafefa918f 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -57,7 +57,7 @@ public: void refresh(); void sendIsLight(); - + // when an object is becoming a refleciton probe, present a dialog asking for confirmation // otherwise, send the reflection probe update immediately void sendIsReflectionProbe(); diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp index a26445b4bc..30a61144b9 100644 --- a/indra/newview/llreflectionmap.cpp +++ b/indra/newview/llreflectionmap.cpp @@ -49,7 +49,7 @@ LLReflectionMap::~LLReflectionMap() } } -void LLReflectionMap::update(U32 resolution, U32 face) +void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic, F32 near_clip, bool useClipPlane, LLPlane clipPlane) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; mLastUpdateTime = gFrameTimeSeconds; @@ -63,7 +63,10 @@ void LLReflectionMap::update(U32 resolution, U32 face) { resolution /= 2; } - gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, getNearClip(), getIsDynamic()); + + F32 clip = (near_clip > 0) ? near_clip : getNearClip(); + + gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, clip, getIsDynamic() || force_dynamic, useClipPlane, clipPlane); } void LLReflectionMap::autoAdjustOrigin() @@ -163,7 +166,7 @@ void LLReflectionMap::autoAdjustOrigin() } } - else if (mViewerObject) + else if (mViewerObject && !mViewerObject->isDead()) { mPriority = 1; mOrigin.load3(mViewerObject->getPositionAgent().mV); diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h index 7ea0fe6187..9e888f20d0 100644 --- a/indra/newview/llreflectionmap.h +++ b/indra/newview/llreflectionmap.h @@ -36,6 +36,15 @@ class alignas(16) LLReflectionMap : public LLRefCount { LL_ALIGN_NEW public: + + enum class ProbeType + { + ALL = 0, + RADIANCE, + IRRADIANCE, + REFLECTION + }; + // allocate an environment map of the given resolution LLReflectionMap(); @@ -43,7 +52,7 @@ public: // update this environment map // resolution - size of cube map to generate - void update(U32 resolution, U32 face); + 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))); // for volume partition probes, try to place this probe in the best spot void autoAdjustOrigin(); @@ -127,5 +136,7 @@ public: GLuint mOcclusionQuery = 0; bool mOccluded = false; U32 mOcclusionPendingFrames = 0; + + ProbeType mType; }; diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index 69674417c1..3e4992e4e7 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -27,6 +27,9 @@ #include "llviewerprecompiledheaders.h" #include "llreflectionmapmanager.h" + +#include <vector> + #include "llviewercamera.h" #include "llspatialpartition.h" #include "llviewerregion.h" @@ -35,6 +38,126 @@ #include "llviewercontrol.h" #include "llenvironment.h" #include "llstartup.h" +#include "llviewermenufile.h" +#include "llnotificationsutil.h" + + +// load an OpenEXR image from a file +#define IMATH_HALF_NO_LOOKUP_TABLE 1 +#include <ImfInputFile.h> +#include <ImfArray.h> +#include <ImfHeader.h> +#include <ImfFrameBuffer.h> +#include <iostream> + +LLPointer<LLImageGL> gEXRImage; + +void load_exr(const std::string& filename) +{ + // reset reflection maps when previewing a new HDRI + gPipeline.mReflectionMapManager.reset(); + gPipeline.mReflectionMapManager.initReflectionMaps(); + + try { + Imf::InputFile file(filename.c_str()); + Imath::Box2i dw = file.header().dataWindow(); + int width = dw.max.x - dw.min.x + 1; + int height = dw.max.y - dw.min.y + 1; + + Imf::Array2D<Imath::half> rPixels; + Imf::Array2D<Imath::half> gPixels; + Imf::Array2D<Imath::half> bPixels; + + rPixels.resizeErase(height, width); + gPixels.resizeErase(height, width); + bPixels.resizeErase(height, width); + + Imf::FrameBuffer frameBuffer; + + frameBuffer.insert("R", // name + Imf::Slice(Imf::HALF, // type + (char*)(&rPixels[0][0] - // base + dw.min.x - + dw.min.y * width), + sizeof(rPixels[0][0]) * 1, // xStride + sizeof(rPixels[0][0]) * width, // yStride + 1, 1, // x/y sampling + 0.0)); // fillValue + + frameBuffer.insert("G", // name + Imf::Slice(Imf::HALF, // type + (char*)(&gPixels[0][0] - // base + dw.min.x - + dw.min.y * width), + sizeof(gPixels[0][0]) * 1, // xStride + sizeof(gPixels[0][0]) * width, // yStride + 1, 1, // x/y sampling + 0.0)); // fillValue + + frameBuffer.insert("B", // name + Imf::Slice(Imf::HALF, // type + (char*)(&bPixels[0][0] - // base + dw.min.x - + dw.min.y * width), + sizeof(bPixels[0][0]) * 1, // xStride + sizeof(bPixels[0][0]) * width, // yStride + 1, 1, // x/y sampling + FLT_MAX)); // fillValue + + file.setFrameBuffer(frameBuffer); + file.readPixels(dw.min.y, dw.max.y); + + U32 texName = 0; + LLImageGL::generateTextures(1, &texName); + + gEXRImage = new LLImageGL(texName, 4, GL_TEXTURE_2D, GL_RGB16F, GL_RGB16F, GL_FLOAT, LLTexUnit::TAM_CLAMP); + gEXRImage->setHasMipMaps(TRUE); + gEXRImage->setUseMipMaps(TRUE); + gEXRImage->setFilteringOption(LLTexUnit::TFO_TRILINEAR); + + gGL.getTexUnit(0)->bind(gEXRImage); + + std::vector<F32> data(width * height * 3); + for (int i = 0; i < width * height; ++i) + { + data[i * 3 + 0] = rPixels[i / width][i % width]; + data[i * 3 + 1] = gPixels[i / width][i % width]; + data[i * 3 + 2] = bPixels[i / width][i % width]; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data.data()); + + glGenerateMipmap(GL_TEXTURE_2D); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + } + catch (const std::exception& e) { + LLSD notif_args; + notif_args["WHAT"] = filename; + notif_args["REASON"] = e.what(); + LLNotificationsUtil::add("CannotLoad", notif_args); + return; + } +} + +void hdri_preview() +{ + LLFilePickerReplyThread::startPicker( + [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) + { + if (LLAppViewer::instance()->quitRequested()) + { + return; + } + if (filenames.size() > 0) + { + load_exr(filenames[0]); + } + }, + LLFilePicker::FFLOAD_HDRI, + true); +} extern BOOL gCubeSnapshot; extern BOOL gTeleportDisplay; @@ -130,6 +253,11 @@ void LLReflectionMapManager::update() return; } + if (mPaused && gFrameTimeSeconds > mResumeTime) + { + resume(); + } + initReflectionMaps(); if (!mRenderTarget.isComplete()) @@ -306,8 +434,8 @@ void LLReflectionMapManager::update() } } - if (realtime && - closestDynamic == nullptr && + if (realtime && + closestDynamic == nullptr && probe->mCubeIndex != -1 && probe->getIsDynamic()) { @@ -322,7 +450,7 @@ void LLReflectionMapManager::update() // should do a full irradiance pass on "odd" frames and a radiance pass on "even" frames closestDynamic->autoAdjustOrigin(); - // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set + // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set // lighting values etc bool radiance_pass = isRadiancePass(); mRadiancePass = mRealtimeRadiancePass; @@ -573,7 +701,7 @@ void LLReflectionMapManager::doProbeUpdate() // Do the reflection map update render passes. // For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated -// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate +// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate // a simple mip chain (not convolution filter). // At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe // The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain. @@ -734,6 +862,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face) mTexture->bind(channel); gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx); gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD); + gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, 1.f); U32 res = mMipChain[0].getWidth(); @@ -827,9 +956,10 @@ void LLReflectionMapManager::reset() mReset = true; } -void LLReflectionMapManager::pause() +void LLReflectionMapManager::pause(F32 duration) { mPaused = true; + mResumeTime = gFrameTimeSeconds + duration; } void LLReflectionMapManager::resume() @@ -898,33 +1028,41 @@ void LLReflectionMapManager::updateUniforms() // see class3/deferred/reflectionProbeF.glsl struct ReflectionProbeData { - // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of + // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of // the box probe - LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; + LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT]; + + LLMatrix4 heroBox; // for sphere probes, origin (xyz) and radius (w) of refmaps in clip space - LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; + LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT]; - // extra parameters + // extra parameters // x - irradiance scale // y - radiance scale // z - fade in // w - znear LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT]; + LLVector4 heroSphere; + // indices used by probe: // [i][0] - cubemap array index for this probe // [i][1] - index into "refNeighbor" for probes that intersect this probe // [i][2] - number of probes that intersect this probe, or -1 for no neighbors // [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes) - GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4]; + GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4]; // list of neighbor indices - GLint refNeighbor[4096]; + GLint refNeighbor[4096]; GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth // numbrer of active refmaps - GLint refmapCount; + GLint refmapCount; + + GLint heroShape; + GLint heroMipCount; + GLint heroProbeCount; }; mReflectionMaps.resize(mReflectionProbeCount); @@ -954,9 +1092,8 @@ void LLReflectionMapManager::updateUniforms() LLEnvironment& environment = LLEnvironment::instance(); LLSettingsSky::ptr_t psky = environment.getCurrentSky(); - static LLCachedControl<F32> cloud_shadow_scale(gSavedSettings, "RenderCloudShadowAmbianceFactor", 0.125f); static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true); - F32 minimum_ambiance = psky->getTotalReflectionProbeAmbiance(cloud_shadow_scale, should_auto_adjust); + F32 minimum_ambiance = psky->getReflectionProbeAmbiance(should_auto_adjust); bool is_ambiance_pass = gCubeSnapshot && !isRadiancePass(); F32 ambscale = is_ambiance_pass ? 0.f : 1.f; @@ -1012,7 +1149,6 @@ void LLReflectionMapManager::updateUniforms() { refmap->mRadius = refmap->mViewerObject->getScale().mV[0] * 0.5f; } - } modelview.affineTransform(refmap->mOrigin, oa); rpd.refSphere[count].set(oa.getF32ptr()); @@ -1115,6 +1251,16 @@ void LLReflectionMapManager::updateUniforms() rpd.refmapCount = count; + gPipeline.mHeroProbeManager.updateUniforms(); + + // Get the hero data. + + rpd.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox; + rpd.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere; + rpd.heroShape = gPipeline.mHeroProbeManager.mHeroData.heroShape; + rpd.heroMipCount = gPipeline.mHeroProbeManager.mHeroData.heroMipCount; + rpd.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount; + //copy rpd into uniform buffer object if (mUBO == 0) { @@ -1152,7 +1298,7 @@ void LLReflectionMapManager::setUniforms() } if (mUBO == 0) - { + { updateUniforms(); } glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO); @@ -1262,6 +1408,8 @@ void LLReflectionMapManager::initReflectionMaps() if (mTexture.isNull() || mReflectionProbeCount != count || mReset) { + gEXRImage = nullptr; + mReset = false; mReflectionProbeCount = count; mProbeResolution = nhpo2(llclamp(gSavedSettings.getU32("RenderReflectionProbeResolution"), (U32)64, (U32)512)); @@ -1319,7 +1467,6 @@ void LLReflectionMapManager::initReflectionMaps() mDefaultProbe->mComplete = default_complete; touch_default_probe(mDefaultProbe); - } if (mVertexBuffer.isNull()) @@ -1343,8 +1490,8 @@ void LLReflectionMapManager::initReflectionMaps() } } -void LLReflectionMapManager::cleanup() -{ +void LLReflectionMapManager::cleanup() +{ mVertexBuffer = nullptr; mRenderTarget.release(); @@ -1383,3 +1530,39 @@ void LLReflectionMapManager::doOcclusion() } } } + +void LLReflectionMapManager::forceDefaultProbeAndUpdateUniforms(bool force) +{ + static std::vector<bool> mProbeWasOccluded; + + if (force) + { + llassert(mProbeWasOccluded.empty()); + + for (size_t i = 0; i < mProbes.size(); ++i) + { + auto& probe = mProbes[i]; + mProbeWasOccluded.push_back(probe->mOccluded); + if (probe != nullptr && probe != mDefaultProbe) + { + probe->mOccluded = true; + } + } + + updateUniforms(); + } + else + { + llassert(mProbes.size() == mProbeWasOccluded.size()); + + const size_t n = llmin(mProbes.size(), mProbeWasOccluded.size()); + for (size_t i = 0; i < n; ++i) + { + auto& probe = mProbes[i]; + llassert(probe->mOccluded == (probe != mDefaultProbe)); + probe->mOccluded = mProbeWasOccluded[i]; + } + mProbeWasOccluded.clear(); + mProbeWasOccluded.shrink_to_fit(); + } +} diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h index b77a33da89..5c0651bc24 100644 --- a/indra/newview/llreflectionmapmanager.h +++ b/indra/newview/llreflectionmapmanager.h @@ -43,21 +43,23 @@ class LLViewerObject; // reflection probe mininum scale #define LL_REFLECTION_PROBE_MINIMUM_SCALE 1.f; +void renderReflectionProbe(LLReflectionMap* probe); + class alignas(16) LLReflectionMapManager { LL_ALIGN_NEW public: - enum class DetailLevel + enum class DetailLevel { STATIC_ONLY = 0, STATIC_AND_DYNAMIC, REALTIME = 2 }; - // allocate an environment map of the given resolution + // allocate an environment map of the given resolution LLReflectionMapManager(); - // release any GL state + // release any GL state void cleanup(); // maintain reflection probes @@ -85,7 +87,8 @@ public: void reset(); // pause all updates other than the default probe - void pause(); + // duration - number of seconds to pause (default 10) + void pause(F32 duration = 10.f); // unpause (see pause) void resume(); @@ -106,8 +109,14 @@ public: // perform occlusion culling on all active reflection probes void doOcclusion(); + // *HACK: "cull" all reflection probes except the default one. Only call + // this if you don't intend to call updateUniforms directly. Call again + // with false when done. + void forceDefaultProbeAndUpdateUniforms(bool force = true); + private: friend class LLPipeline; + friend class LLHeroProbeManager; // initialize mCubeFree array to default values void initCubeFree(); @@ -119,7 +128,7 @@ private: // returns -1 if allocation failed S32 allocateCubeIndex(); - // update the neighbors of the given probe + // update the neighbors of the given probe void updateNeighbors(LLReflectionMap* probe); // update UBO used for rendering (call only once per render pipe flush) @@ -200,5 +209,6 @@ private: // if true, only update the default probe bool mPaused = false; + F32 mResumeTime = 0.f; }; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index b50c0f60bd..471601f16f 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1782,7 +1782,7 @@ bool LLObjectSelection::applyRestrictedPbrMaterialToTEs(LLViewerInventoryItem* i LLUUID asset_id = item->getAssetUUID(); if (asset_id.isNull()) { - asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + asset_id = BLANK_MATERIAL_ASSET_ID; } bool material_copied_all_faces = true; @@ -1987,7 +1987,7 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) asset_id = mItem->getAssetUUID(); if (asset_id.isNull()) { - asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + asset_id = BLANK_MATERIAL_ASSET_ID; } } diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index c07c939862..ff9332aeee 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -718,11 +718,11 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); // TODO -- make these getters return vec3s - LLVector3 sunDiffuse = LLVector3(psky->getSunlightColor().mV); - LLVector3 moonDiffuse = LLVector3(psky->getMoonlightColor().mV); + LLVector3 sun_light_color = LLVector3(psky->getSunlightColor().mV); + LLVector3 moon_light_color = LLVector3(psky->getMoonlightColor().mV); - shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sunDiffuse); - shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, moonDiffuse); + shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_light_color); + shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, moon_light_color); shader->uniform3fv(LLShaderMgr::CLOUD_COLOR, LLVector3(psky->getCloudColor().mV)); @@ -745,8 +745,7 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) shader->uniform1f(LLShaderMgr::SKY_SUNLIGHT_SCALE, sunlight_scale); shader->uniform1f(LLShaderMgr::SKY_AMBIENT_SCALE, ambient_scale); - static LLCachedControl<F32> cloud_shadow_scale(gSavedSettings, "RenderCloudShadowAmbianceFactor", 0.125f); - F32 probe_ambiance = getTotalReflectionProbeAmbiance(cloud_shadow_scale); + F32 probe_ambiance = getReflectionProbeAmbiance(); if (irradiance_pass) { // during an irradiance map update, disable ambient lighting (direct lighting only) and desaturate sky color (avoid tinting the world blue) @@ -765,9 +764,9 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, auto_adjust_hdr_scale); LLColor3 blue_horizon = getBlueHorizon() * auto_adjust_blue_horizon_scale; LLColor3 blue_density = getBlueDensity() * auto_adjust_blue_density_scale; - LLColor3 sun_diffuse = getSunDiffuse() * auto_adjust_sun_color_scale; + sun_light_color = sun_light_color * auto_adjust_sun_color_scale; - shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_diffuse.mV); + shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_light_color.mV); shader->uniform3fv(LLShaderMgr::BLUE_DENSITY, blue_density.mV); shader->uniform3fv(LLShaderMgr::BLUE_HORIZON, blue_horizon.mV); @@ -1009,6 +1008,7 @@ void LLSettingsVOWater::applySpecial(void *ptarget, bool force) glh::matrix4f mat(modelView); glh::matrix4f invtrans = mat.inverse().transpose(); + invtrans.m[3] = invtrans.m[7] = invtrans.m[11] = 0.f; glh::vec3f enorm; glh::vec3f ep; invtrans.mult_matrix_vec(norm, enorm); @@ -1017,12 +1017,29 @@ void LLSettingsVOWater::applySpecial(void *ptarget, bool force) LLVector4 waterPlane(enorm.v[0], enorm.v[1], enorm.v[2], -ep.dot(enorm)); + norm = glh::vec3f(gPipeline.mHeroProbeManager.mMirrorNormal.mV); + p = glh::vec3f(gPipeline.mHeroProbeManager.mMirrorPosition.mV); + invtrans.mult_matrix_vec(norm, enorm); + enorm.normalize(); + mat.mult_matrix_vec(p, ep); + + LLVector4 mirrorPlane(enorm.v[0], enorm.v[1], enorm.v[2], -ep.dot(enorm)); + LLDrawPoolAlpha::sWaterPlane = waterPlane; shader->uniform4fv(LLShaderMgr::WATER_WATERPLANE, waterPlane.mV); - + shader->uniform4fv(LLShaderMgr::CLIP_PLANE, mirrorPlane.mV); LLVector4 light_direction = env.getClampedLightNorm(); + if (gPipeline.mHeroProbeManager.isMirrorPass()) + { + shader->uniform1f(LLShaderMgr::MIRROR_FLAG, 1); + } + else + { + shader->uniform1f(LLShaderMgr::MIRROR_FLAG, 0); + } + F32 waterFogKS = 1.f / llmax(light_direction.mV[2], WATER_FOG_LIGHT_CLAMP); shader->uniform1f(LLShaderMgr::WATER_FOGKS, waterFogKS); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index faf6f15015..f3b5bb0565 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -47,6 +47,7 @@ #include "pipeline.h" #include "llmeshrepository.h" #include "llrender.h" +#include "lldrawpool.h" #include "lloctree.h" #include "llphysicsshapebuilderutil.h" #include "llvoavatar.h" @@ -1990,7 +1991,11 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE) drawBoxOutline(pos,size); } } - +// *TODO: LLDrawables which are not part of LLVOVolumes fall into a different +// code path which uses a shader - it was tested to be faster than mapping a +// vertex buffer in the terrain case. Consider using it for LLVOVolumes as well +// to simplify and speed up this debug code. Alternatively, a compute shader is +// likely faster. -Cosmic,2023-09-28 void renderNormals(LLDrawable *drawablep) { if (!drawablep->isVisible()) @@ -1998,11 +2003,13 @@ void renderNormals(LLDrawable *drawablep) LLVertexBuffer::unbind(); + LLViewerObject* obj = drawablep->getVObj(); LLVOVolume *vol = drawablep->getVOVolume(); - if (vol) + if (obj) { - LLVolume *volume = vol->getVolume(); + LLGLEnable blend(GL_BLEND); + LLGLDepthTest gl_depth(GL_TRUE, GL_FALSE); // Drawable's normals & tangents are stored in model space, i.e. before any scaling is applied. // @@ -2011,68 +2018,136 @@ void renderNormals(LLDrawable *drawablep) // transform. We get that effect here by pre-applying the inverse scale (twice, because // one forward scale will be re-applied via the MVP in the vertex shader) - LLVector3 scale_v3 = vol->getScale(); - float scale_len = scale_v3.length(); - LLVector4a obj_scale(scale_v3.mV[VX], scale_v3.mV[VY], scale_v3.mV[VZ]); - obj_scale.normalize3(); + LLVector4a inv_scale; + float scale_len; + if (vol) + { + LLVector3 scale_v3 = vol->getScale(); + LLVector4a obj_scale(scale_v3.mV[VX], scale_v3.mV[VY], scale_v3.mV[VZ]); + obj_scale.normalize3(); - // Normals &tangent line segments get scaled along with the object. Divide by scale length - // to keep the as-viewed lengths (relatively) constant with the debug setting length - float draw_length = gSavedSettings.getF32("RenderDebugNormalScale") / scale_len; + // Create inverse-scale vector for normals + inv_scale.set(1.0 / scale_v3.mV[VX], 1.0 / scale_v3.mV[VY], 1.0 / scale_v3.mV[VZ], 0.0); + inv_scale.mul(inv_scale); // Squared, to apply inverse scale twice - // Create inverse-scale vector for normals - LLVector4a inv_scale(1.0 / scale_v3.mV[VX], 1.0 / scale_v3.mV[VY], 1.0 / scale_v3.mV[VZ]); - inv_scale.mul(inv_scale); // Squared, to apply inverse scale twice - inv_scale.normalize3fast(); + inv_scale.normalize3fast(); + scale_len = scale_v3.length(); + } + else + { + inv_scale.set(1.0, 1.0, 1.0, 0.0); + scale_len = 1.0; + } gGL.pushMatrix(); - gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix); + if (vol) + { + gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix); + } gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) + // Normals &tangent line segments get scaled along with the object. Divide by scale length + // to keep the as-viewed lengths (relatively) constant with the debug setting length + float draw_length = gSavedSettings.getF32("RenderDebugNormalScale") / scale_len; + + std::vector<LLVolumeFace>* faces = nullptr; + std::vector<LLFace*>* drawable_faces = nullptr; + if (vol) { - const LLVolumeFace &face = volume->getVolumeFace(i); + LLVolume* volume = vol->getVolume(); + faces = &volume->getVolumeFaces(); + } + else + { + drawable_faces = &drawablep->getFaces(); + } - gGL.flush(); - gGL.diffuseColor4f(1, 1, 0, 1); - gGL.begin(LLRender::LINES); - for (S32 j = 0; j < face.mNumVertices; ++j) - { - LLVector4a n, p; + if (faces) + { + for (auto it = faces->begin(); it != faces->end(); ++it) + { + const LLVolumeFace& face = *it; - n.setMul(face.mNormals[j], 1.0); - n.mul(inv_scale); // Pre-scale normal, so it's left with an inverse-transpose xform after MVP - n.normalize3fast(); - n.mul(draw_length); - p.setAdd(face.mPositions[j], n); + gGL.flush(); + gGL.diffuseColor4f(1, 1, 0, 1); + gGL.begin(LLRender::LINES); + for (S32 j = 0; j < face.mNumVertices; ++j) + { + LLVector4a n, p; - gGL.vertex3fv(face.mPositions[j].getF32ptr()); - gGL.vertex3fv(p.getF32ptr()); - } - gGL.end(); + n.setMul(face.mNormals[j], 1.0); + n.mul(inv_scale); // Pre-scale normal, so it's left with an inverse-transpose xform after MVP + n.normalize3fast(); + n.mul(draw_length); + p.setAdd(face.mPositions[j], n); + + gGL.vertex3fv(face.mPositions[j].getF32ptr()); + gGL.vertex3fv(p.getF32ptr()); + } + gGL.end(); - // Tangents are simple vectors and do not require reorientation via pre-scaling - if (face.mTangents) + // Tangents are simple vectors and do not require reorientation via pre-scaling + if (face.mTangents) + { + gGL.flush(); + gGL.diffuseColor4f(0, 1, 1, 1); + gGL.begin(LLRender::LINES); + for (S32 j = 0; j < face.mNumVertices; ++j) + { + LLVector4a t, p; + + t.setMul(face.mTangents[j], 1.0f); + t.normalize3fast(); + t.mul(draw_length); + p.setAdd(face.mPositions[j], t); + + gGL.vertex3fv(face.mPositions[j].getF32ptr()); + gGL.vertex3fv(p.getF32ptr()); + } + gGL.end(); + } + } + } + else if (drawable_faces) + { + // *HACK: Prepare to restore previous shader as other debug code depends on a simpler shader being present + llassert(LLGLSLShader::sCurBoundShaderPtr == &gDebugProgram); + LLGLSLShader* prev_shader = LLGLSLShader::sCurBoundShaderPtr; + for (auto it = drawable_faces->begin(); it != drawable_faces->end(); ++it) { - gGL.flush(); - gGL.diffuseColor4f(0, 1, 1, 1); - gGL.begin(LLRender::LINES); - for (S32 j = 0; j < face.mNumVertices; ++j) + LLFace* facep = *it; + LLFace& face = **it; + LLVertexBuffer* buf = face.getVertexBuffer(); + if (!buf) { continue; } + U32 mask_vn = LLVertexBuffer::TYPE_VERTEX | LLVertexBuffer::TYPE_NORMAL; + if ((buf->getTypeMask() & mask_vn) != mask_vn) { continue; } + + LLGLSLShader* shader; + if ((buf->getTypeMask() & LLVertexBuffer::TYPE_TANGENT) != LLVertexBuffer::TYPE_TANGENT) { - LLVector4a t, p; + shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_DEFAULT]; + } + else + { + shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_WITH_TANGENTS]; + } + shader->bind(); - t.setMul(face.mTangents[j], 1.0f); - t.normalize3fast(); - t.mul(draw_length); - p.setAdd(face.mPositions[j], t); + shader->uniform1f(LLShaderMgr::DEBUG_NORMAL_DRAW_LENGTH, draw_length); - gGL.vertex3fv(face.mPositions[j].getF32ptr()); - gGL.vertex3fv(p.getF32ptr()); - } - gGL.end(); + LLRenderPass::applyModelMatrix(&facep->getDrawable()->getRegion()->mRenderMatrix); + + buf->setBuffer(); + // *NOTE: The render type in the vertex shader is TRIANGLES, but gets converted to LINES in the geometry shader + // *NOTE: For terrain normal debug, this seems to also include vertices for water, which is technically not part of the terrain. Should fix that at some point. + buf->drawRange(LLRender::TRIANGLES, face.getGeomIndex(), face.getGeomIndex() + face.getGeomCount()-1, face.getIndicesCount(), face.getIndicesStart()); } - } + if (prev_shader) + { + prev_shader->bind(); + } + } gGL.popMatrix(); } @@ -2791,10 +2866,8 @@ void renderLights(LLDrawable* drawablep) class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect { public: - - LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t) - : LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL) + : LLOctreeTriangleRayIntersect(start, dir, nullptr, closest_t, NULL, NULL, NULL, NULL) { } @@ -2818,7 +2891,7 @@ public: size.set(vl->mBounds[1].getF32ptr()); } - drawBoxOutline(center, size); + drawBoxOutline(center, size); for (U32 i = 0; i < 2; i++) { @@ -2862,6 +2935,13 @@ public: } }; +void renderOctreeRaycast(const LLVector4a& start, const LLVector4a& end, const LLVolumeOctree* octree) +{ + F32 t = 1.f; + LLRenderOctreeRaycast render(start, end, &t); + render.traverse(octree); +} + void renderRaycast(LLDrawable* drawablep) { if (drawablep->getNumFaces()) @@ -2919,29 +2999,22 @@ void renderRaycast(LLDrawable* drawablep) dir.setSub(end, start); gGL.flush(); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); { //render face positions - LLVertexBuffer::unbind(); - gGL.diffuseColor4f(0,1,1,0.5f); - glVertexPointer(3, GL_FLOAT, sizeof(LLVector4a), face.mPositions); - gGL.syncMatrices(); - glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices); + //gGL.diffuseColor4f(0,1,1,0.5f); + //LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mPositions, nullptr, face.mNumIndices, face.mIndices); } if (!volume->isUnique()) { - F32 t = 1.f; - if (!face.getOctree()) { ((LLVolumeFace*) &face)->createOctree(); } - LLRenderOctreeRaycast render(start, dir, &t); - - render.traverse(face.getOctree()); + renderOctreeRaycast(start, end, face.getOctree()); } gGL.popMatrix(); diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index 1418499f8b..804afe6266 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -643,17 +643,18 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent) } } -BOOL LLSurface::idleUpdate(F32 max_update_time) +template<bool PBR> +bool LLSurface::idleUpdate(F32 max_update_time) { if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN)) { - return FALSE; + return false; } // Perform idle time update of non-critical stuff. // In this case, texture and normal updates. LLTimer update_timer; - BOOL did_update = FALSE; + bool did_update = false; // If the Z height data has changed, we need to rebuild our // property line vertex arrays. @@ -669,13 +670,13 @@ BOOL LLSurface::idleUpdate(F32 max_update_time) { std::set<LLSurfacePatch *>::iterator curiter = iter++; LLSurfacePatch *patchp = *curiter; - patchp->updateNormals(); + patchp->updateNormals<PBR>(); patchp->updateVerticalStats(); if (max_update_time == 0.f || update_timer.getElapsedTimeF32() < max_update_time) { if (patchp->updateTexture()) { - did_update = TRUE; + did_update = true; patchp->clearDirty(); mDirtyPatchList.erase(curiter); } @@ -691,6 +692,9 @@ BOOL LLSurface::idleUpdate(F32 max_update_time) return did_update; } +template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time); +template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time); + void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch) { diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h index 33a64ae7d5..b7b47d2a1c 100644 --- a/indra/newview/llsurface.h +++ b/indra/newview/llsurface.h @@ -112,7 +112,8 @@ public: LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const; // Update methods (called during idle, normally) - BOOL idleUpdate(F32 max_update_time); + template<bool PBR> + bool idleUpdate(F32 max_update_time); BOOL containsPosition(const LLVector3 &position); @@ -224,6 +225,9 @@ private: static S32 sTextureSize; // Size of the surface texture }; +extern template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time); +extern template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time); + // . __. diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index 449d3d95c8..92b1273041 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -221,7 +221,9 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 *vertex = pos_agent-mVObjp->getRegion()->getOriginAgent(); LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent(); - LLVector3 tex_pos = rel_pos * (1.f/surface_stride); + // *NOTE: Only PBR terrain uses the UVs right now. Texture terrain just ignores it. + // *NOTE: In the future, UVs and horizontal position will no longer have a 1:1 relationship for PBR terrain + LLVector3 tex_pos = rel_pos; tex0->mV[0] = tex_pos.mV[0]; tex0->mV[1] = tex_pos.mV[1]; tex1->mV[0] = mSurfacep->getRegion()->getCompositionXY(llfloor(mOriginRegion.mV[0])+x, llfloor(mOriginRegion.mV[1])+y); @@ -241,7 +243,8 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 } -void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride) +template<> +void LLSurfacePatch::calcNormal</*PBR=*/false>(const U32 x, const U32 y, const U32 stride) { U32 patch_width = mSurfacep->mPVArray.mPatchWidth; U32 surface_stride = mSurfacep->getGridsPerEdge(); @@ -354,6 +357,166 @@ void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride) *(mDataNorm + surface_stride * y + x) = normal; } +template<> +void LLSurfacePatch::calcNormal</*PBR=*/true>(const U32 x, const U32 y, const U32 stride) +{ + llassert(mDataNorm); + constexpr U32 index = 0; + + const U32 surface_stride = mSurfacep->getGridsPerEdge(); + LLVector3& normal_out = *(mDataNorm + surface_stride * y + x); + calcNormalFlat(normal_out, x, y, index); +} + +// Calculate the flat normal of a triangle whose least coordinate is specified by the given x,y values. +// If index = 0, calculate the normal of the first triangle, otherwise calculate the normal of the second. +void LLSurfacePatch::calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index) +{ + llassert(index == 0 || index == 1); + + U32 patch_width = mSurfacep->mPVArray.mPatchWidth; + U32 surface_stride = mSurfacep->getGridsPerEdge(); + + // Vertex stride is always 1 because we want the flat surface of the current triangle face + constexpr U32 stride = 1; + + const F32 mpg = mSurfacep->getMetersPerGrid() * stride; + + S32 poffsets[2][2][2]; + poffsets[0][0][0] = x; + poffsets[0][0][1] = y; + + poffsets[0][1][0] = x; + poffsets[0][1][1] = y + stride; + + poffsets[1][0][0] = x + stride; + poffsets[1][0][1] = y; + + poffsets[1][1][0] = x + stride; + poffsets[1][1][1] = y + stride; + + const LLSurfacePatch *ppatches[2][2]; + + // LLVector3 p1, p2, p3, p4; + + ppatches[0][0] = this; + ppatches[0][1] = this; + ppatches[1][0] = this; + ppatches[1][1] = this; + + U32 i, j; + for (i = 0; i < 2; i++) + { + for (j = 0; j < 2; j++) + { + if (poffsets[i][j][0] < 0) + { + if (!ppatches[i][j]->getNeighborPatch(WEST)) + { + poffsets[i][j][0] = 0; + } + else + { + poffsets[i][j][0] += patch_width; + ppatches[i][j] = ppatches[i][j]->getNeighborPatch(WEST); + } + } + if (poffsets[i][j][1] < 0) + { + if (!ppatches[i][j]->getNeighborPatch(SOUTH)) + { + poffsets[i][j][1] = 0; + } + else + { + poffsets[i][j][1] += patch_width; + ppatches[i][j] = ppatches[i][j]->getNeighborPatch(SOUTH); + } + } + if (poffsets[i][j][0] >= (S32)patch_width) + { + if (!ppatches[i][j]->getNeighborPatch(EAST)) + { + poffsets[i][j][0] = patch_width - 1; + } + else + { + poffsets[i][j][0] -= patch_width; + ppatches[i][j] = ppatches[i][j]->getNeighborPatch(EAST); + } + } + if (poffsets[i][j][1] >= (S32)patch_width) + { + if (!ppatches[i][j]->getNeighborPatch(NORTH)) + { + poffsets[i][j][1] = patch_width - 1; + } + else + { + poffsets[i][j][1] -= patch_width; + ppatches[i][j] = ppatches[i][j]->getNeighborPatch(NORTH); + } + } + } + } + + LLVector3 p00(-mpg,-mpg, + *(ppatches[0][0]->mDataZ + + poffsets[0][0][0] + + poffsets[0][0][1]*surface_stride)); + LLVector3 p01(-mpg,+mpg, + *(ppatches[0][1]->mDataZ + + poffsets[0][1][0] + + poffsets[0][1][1]*surface_stride)); + LLVector3 p10(+mpg,-mpg, + *(ppatches[1][0]->mDataZ + + poffsets[1][0][0] + + poffsets[1][0][1]*surface_stride)); + LLVector3 p11(+mpg,+mpg, + *(ppatches[1][1]->mDataZ + + poffsets[1][1][0] + + poffsets[1][1][1]*surface_stride)); + + // Triangle index / coordinate convention + // for a single surface patch + // + // p01 p11 + // + // ^ ._____. + // | |\ | + // | | \ 1 | + // | | \ | + // | 0 \ | + // y |____\| + // + // p00 x ---> p10 + // + // (z up / out of the screen due to right-handed coordinate system) + + LLVector3 normal; + if (index == 0) + { + LLVector3 c1 = p10 - p00; + LLVector3 c2 = p01 - p00; + + normal = c1; + normal %= c2; + normal.normVec(); + } + else // index == 1 + { + LLVector3 c1 = p11 - p01; + LLVector3 c2 = p11 - p10; + + normal = c1; + normal %= c2; + normal.normVec(); + } + + llassert(&normal_out); + normal_out = normal; +} + const LLVector3 &LLSurfacePatch::getNormal(const U32 x, const U32 y) const { U32 surface_stride = mSurfacep->getGridsPerEdge(); @@ -451,6 +614,7 @@ void LLSurfacePatch::updateVerticalStats() } +template<bool PBR> void LLSurfacePatch::updateNormals() { if (mSurfacep->mType == 'w') @@ -468,9 +632,9 @@ void LLSurfacePatch::updateNormals() { for (j = 0; j <= grids_per_patch_edge; j++) { - calcNormal(grids_per_patch_edge, j, 2); - calcNormal(grids_per_patch_edge - 1, j, 2); - calcNormal(grids_per_patch_edge - 2, j, 2); + calcNormal<PBR>(grids_per_patch_edge, j, 2); + calcNormal<PBR>(grids_per_patch_edge - 1, j, 2); + calcNormal<PBR>(grids_per_patch_edge - 2, j, 2); } dirty_patch = TRUE; @@ -481,9 +645,9 @@ void LLSurfacePatch::updateNormals() { for (i = 0; i <= grids_per_patch_edge; i++) { - calcNormal(i, grids_per_patch_edge, 2); - calcNormal(i, grids_per_patch_edge - 1, 2); - calcNormal(i, grids_per_patch_edge - 2, 2); + calcNormal<PBR>(i, grids_per_patch_edge, 2); + calcNormal<PBR>(i, grids_per_patch_edge - 1, 2); + calcNormal<PBR>(i, grids_per_patch_edge - 2, 2); } dirty_patch = TRUE; @@ -494,8 +658,8 @@ void LLSurfacePatch::updateNormals() { for (j = 0; j < grids_per_patch_edge; j++) { - calcNormal(0, j, 2); - calcNormal(1, j, 2); + calcNormal<PBR>(0, j, 2); + calcNormal<PBR>(1, j, 2); } dirty_patch = TRUE; } @@ -505,8 +669,8 @@ void LLSurfacePatch::updateNormals() { for (i = 0; i < grids_per_patch_edge; i++) { - calcNormal(i, 0, 2); - calcNormal(i, 1, 2); + calcNormal<PBR>(i, 0, 2); + calcNormal<PBR>(i, 1, 2); } dirty_patch = TRUE; } @@ -582,10 +746,10 @@ void LLSurfacePatch::updateNormals() // We've got a northeast patch in the same surface. // The z and normals will be handled by that patch. } - calcNormal(grids_per_patch_edge, grids_per_patch_edge, 2); - calcNormal(grids_per_patch_edge, grids_per_patch_edge - 1, 2); - calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge, 2); - calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2); + calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge, 2); + calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge - 1, 2); + calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge, 2); + calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2); dirty_patch = TRUE; } @@ -596,7 +760,7 @@ void LLSurfacePatch::updateNormals() { for (i=2; i < grids_per_patch_edge - 2; i++) { - calcNormal(i, j, 2); + calcNormal<PBR>(i, j, 2); } } dirty_patch = TRUE; @@ -613,6 +777,9 @@ void LLSurfacePatch::updateNormals() } } +template void LLSurfacePatch::updateNormals</*PBR=*/false>(); +template void LLSurfacePatch::updateNormals</*PBR=*/true>(); + void LLSurfacePatch::updateEastEdge() { U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge(); @@ -739,7 +906,7 @@ void LLSurfacePatch::updateGL() updateCompositionStats(); F32 tex_patch_size = meters_per_grid*grids_per_patch_edge; - if (comp->generateTexture((F32)origin_region[VX], (F32)origin_region[VY], + if (comp->generateMinimapTileLand((F32)origin_region[VX], (F32)origin_region[VY], tex_patch_size, tex_patch_size)) { mSTexUpdate = FALSE; diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h index 8c8f501dce..ec3864ce44 100644 --- a/indra/newview/llsurfacepatch.h +++ b/indra/newview/llsurfacepatch.h @@ -77,6 +77,7 @@ public: void updateVerticalStats(); void updateCompositionStats(); + template<bool PBR> void updateNormals(); void updateEastEdge(); @@ -102,9 +103,18 @@ public: LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset. LLVector2 getTexCoords(const U32 x, const U32 y) const; + // Per-vertex normals + // *TODO: PBR=true is a test implementation solely for proof-of-concept. + // Final implementation would likely be very different and may not even use + // this function. If we decide to keep calcNormalFlat, remove index as it + // is a debug parameter for testing. + template<bool PBR> void calcNormal(const U32 x, const U32 y, const U32 stride); const LLVector3 &getNormal(const U32 x, const U32 y) const; + // Per-triangle normals for flat edges + void calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index /* 0 or 1 */); + void eval(const U32 x, const U32 y, const U32 stride, LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1); @@ -181,5 +191,8 @@ protected: LLSurface *mSurfacep; // Pointer to "parent" surface }; +extern template void LLSurfacePatch::updateNormals</*PBR=*/false>(); +extern template void LLSurfacePatch::updateNormals</*PBR=*/true>(); + #endif // LL_LLSURFACEPATCH_H diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 62d3fa28bf..91c2755ea5 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -72,6 +72,7 @@ #include "llradiogroup.h" #include "llfloaterreg.h" +#include "llgltfmaterialpreviewmgr.h" #include "lllocalbitmaps.h" #include "lllocalgltfmaterials.h" #include "llerror.h" @@ -229,7 +230,7 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID()); if (mInventoryPickType == PICK_MATERIAL - && mImageAssetID == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID + && mImageAssetID == BLANK_MATERIAL_ASSET_ID && itemp && itemp->getAssetUUID().isNull()) { item_id = inv_view->getUUID(); @@ -272,7 +273,7 @@ void LLFloaterTexturePicker::setImageIDFromItem(const LLInventoryItem* itemp, bo if (mInventoryPickType == PICK_MATERIAL && asset_id.isNull()) { // If an inventory item has a null asset, consider it a valid blank material(gltf) - asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + asset_id = BLANK_MATERIAL_ASSET_ID; } setImageID(asset_id, set_selection); } @@ -537,6 +538,8 @@ void LLFloaterTexturePicker::onClose(bool app_quitting) } stopUsingPipette(); sLastPickerMode = mModeSelector->getValue().asInteger(); + // *NOTE: Vertex buffer for sphere preview is still cached + mGLTFPreview = nullptr; } // virtual @@ -659,6 +662,7 @@ void LLFloaterTexturePicker::draw() if( mOwner ) { mTexturep = NULL; + LLPointer<LLFetchedGLTFMaterial> old_material = mGLTFMaterial; mGLTFMaterial = NULL; if (mImageAssetID.notNull()) { @@ -666,10 +670,27 @@ void LLFloaterTexturePicker::draw() { mGLTFMaterial = (LLFetchedGLTFMaterial*) gGLTFMaterialList.getMaterial(mImageAssetID); llassert(mGLTFMaterial == nullptr || dynamic_cast<LLFetchedGLTFMaterial*>(gGLTFMaterialList.getMaterial(mImageAssetID)) != nullptr); + if (mGLTFPreview.isNull() || mGLTFMaterial.isNull() || (old_material.notNull() && (old_material.get() != mGLTFMaterial.get()))) + { + // Only update the preview if needed, since gGLTFMaterialPreviewMgr does not cache the preview. + if (mGLTFMaterial.isNull()) + { + mGLTFPreview = nullptr; + } + else + { + mGLTFPreview = gGLTFMaterialPreviewMgr.getPreview(mGLTFMaterial); + } + } + if (mGLTFPreview) + { + mGLTFPreview->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + } } else { LLPointer<LLViewerFetchedTexture> texture = NULL; + mGLTFPreview = nullptr; if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID)) { @@ -679,7 +700,7 @@ void LLFloaterTexturePicker::draw() if (obj) { LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(mImageAssetID); - texture = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL; + texture = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL; } } @@ -720,27 +741,25 @@ void LLFloaterTexturePicker::draw() // If the floater is focused, don't apply its alpha to the texture (STORM-677). const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); - LLViewerTexture* texture = nullptr; + LLViewerTexture* preview; if (mGLTFMaterial) { - texture = mGLTFMaterial->getUITexture(); + preview = mGLTFPreview.get(); } else { - texture = mTexturep.get(); + preview = mTexturep.get(); } - if( texture ) + if( preview ) { - if( texture->getComponents() == 4 ) + preview->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); + if( preview->getComponents() == 4 ) { gl_rect_2d_checkerboard( interior, alpha ); } - gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), texture, UI_VERTEX_COLOR % alpha ); - - // Pump the priority - texture->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); + gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), preview, UI_VERTEX_COLOR % alpha ); } else if (!mFallbackImage.isNull()) { @@ -794,7 +813,7 @@ const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL co } LLUUID loockup_id = asset_id; - if (mInventoryPickType == PICK_MATERIAL && loockup_id == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID) + if (mInventoryPickType == PICK_MATERIAL && loockup_id == BLANK_MATERIAL_ASSET_ID) { // default asset id means we are looking for an inventory item with a default asset UUID (null) loockup_id = LLUUID::null; @@ -889,7 +908,7 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op) LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID()); if (mInventoryPickType == PICK_MATERIAL - && mImageAssetID == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID + && mImageAssetID == BLANK_MATERIAL_ASSET_ID && itemp && itemp->getAssetUUID().isNull()) { inventory_id = inv_view->getUUID(); @@ -1639,7 +1658,7 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p) mShowLoadingPlaceholder( TRUE ), mOpenTexPreview(false), mBakeTextureEnabled(true), - mInventoryPickType(PICK_TEXTURE), + mInventoryPickType(p.pick_type), mImageAssetID(p.image_id), mDefaultImageAssetID(p.default_image_id), mDefaultImageName(p.default_image_name), @@ -1743,6 +1762,19 @@ void LLTextureCtrl::setFilterPermissionMasks(PermissionMask mask) setDnDFilterPermMask(mask); } +void LLTextureCtrl::onVisibilityChange(BOOL new_visibility) +{ + if (!new_visibility) + { + // *NOTE: Vertex buffer for sphere preview is still cached + mGLTFPreview = nullptr; + } + else + { + llassert(!mGLTFPreview); + } +} + void LLTextureCtrl::setVisible( BOOL visible ) { if( !visible ) @@ -2157,48 +2189,73 @@ void LLTextureCtrl::draw() { mBorder->setKeyboardFocusHighlight(hasFocus()); + LLPointer<LLViewerTexture> preview = NULL; + if (!mValid) { mTexturep = NULL; + mGLTFMaterial = NULL; + mGLTFPreview = NULL; } else if (!mImageAssetID.isNull()) { - LLPointer<LLViewerFetchedTexture> texture = NULL; - if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID)) { LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); if (obj) { LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(mImageAssetID); - texture = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL; + mTexturep = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL; + mGLTFMaterial = NULL; + mGLTFPreview = NULL; + + preview = mTexturep; } } - if (texture.isNull()) + if (preview.isNull()) { + LLPointer<LLFetchedGLTFMaterial> old_material = mGLTFMaterial; + mGLTFMaterial = NULL; + mTexturep = NULL; if (mInventoryPickType == PICK_MATERIAL) { - LLPointer<LLFetchedGLTFMaterial> material = gGLTFMaterialList.getMaterial(mImageAssetID); - if (material) + mGLTFMaterial = gGLTFMaterialList.getMaterial(mImageAssetID); + if (mGLTFPreview.isNull() || mGLTFMaterial.isNull() || (old_material.notNull() && (old_material.get() != mGLTFMaterial.get()))) + { + // Only update the preview if needed, since gGLTFMaterialPreviewMgr does not cache the preview. + if (mGLTFMaterial.isNull()) + { + mGLTFPreview = nullptr; + } + else + { + mGLTFPreview = gGLTFMaterialPreviewMgr.getPreview(mGLTFMaterial); + } + } + if (mGLTFPreview) { - texture = material->getUITexture(); + mGLTFPreview->setBoostLevel(LLGLTexture::BOOST_PREVIEW); } + + preview = mGLTFPreview; } else { - texture = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - texture->setBoostLevel(LLGLTexture::BOOST_PREVIEW); - texture->forceToSaveRawImage(0); + mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + mTexturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + mTexturep->forceToSaveRawImage(0); + + preview = mTexturep; } } - - mTexturep = texture; } else//mImageAssetID == LLUUID::null { mTexturep = NULL; + mGLTFMaterial = NULL; + mGLTFPreview = NULL; } // Border @@ -2211,15 +2268,15 @@ void LLTextureCtrl::draw() // If we're in a focused floater, don't apply the floater's alpha to the texture (STORM-677). const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); - if( mTexturep ) + if( preview ) { - if( mTexturep->getComponents() == 4 ) + if( preview->getComponents() == 4 ) { gl_rect_2d_checkerboard( interior, alpha ); } - gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); - mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); + gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), preview, UI_VERTEX_COLOR % alpha); + preview->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) ); } else if (!mFallbackImage.isNull()) { @@ -2338,7 +2395,7 @@ BOOL LLTextureCtrl::doDrop(LLInventoryItem* item) if (mInventoryPickType == PICK_MATERIAL && asset_id.isNull()) { // If an inventory material has a null asset, consider it a valid blank material(gltf) - asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + asset_id = BLANK_MATERIAL_ASSET_ID; } setImageAssetID(asset_id); @@ -2366,6 +2423,16 @@ LLSD LLTextureCtrl::getValue() const return LLSD(getImageAssetID()); } +namespace LLInitParam +{ + void TypeValues<EPickInventoryType>::declareValues() + { + declare("texture_material", PICK_TEXTURE_MATERIAL); + declare("texture", PICK_TEXTURE); + declare("material", PICK_MATERIAL); + } +} + diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index cb6ce636e0..f193b0c083 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -64,13 +64,6 @@ bool get_is_predefined_texture(LLUUID asset_id); LLUUID get_copy_free_item_by_asset_id(LLUUID image_id, bool no_trans_perm = false); bool get_can_copy_texture(LLUUID image_id); -enum LLPickerSource -{ - PICKER_INVENTORY, - PICKER_LOCAL, - PICKER_BAKE, - PICKER_UNKNOWN, // on cancel, default ids -}; typedef enum e_pick_inventory_type { @@ -79,6 +72,23 @@ typedef enum e_pick_inventory_type PICK_MATERIAL = 2, } EPickInventoryType; +namespace LLInitParam +{ + template<> + struct TypeValues<EPickInventoryType> : public TypeValuesHelper<EPickInventoryType> + { + static void declareValues(); + }; +} + +enum LLPickerSource +{ + PICKER_INVENTORY, + PICKER_LOCAL, + PICKER_BAKE, + PICKER_UNKNOWN, // on cancel, default ids +}; + ////////////////////////////////////////////////////////////////////////////////////////// // LLTextureCtrl @@ -100,6 +110,7 @@ public: Optional<LLUUID> image_id; Optional<LLUUID> default_image_id; Optional<std::string> default_image_name; + Optional<EPickInventoryType> pick_type; Optional<bool> allow_no_texture; Optional<bool> can_apply_immediately; Optional<bool> no_commit_on_selection; // alternative mode: commit occurs and the widget gets dirty @@ -117,6 +128,7 @@ public: : image_id("image"), default_image_id("default_image_id"), default_image_name("default_image_name"), + pick_type("pick_type", PICK_TEXTURE), allow_no_texture("allow_no_texture", false), can_apply_immediately("can_apply_immediately"), no_commit_on_selection("no_commit_on_selection", false), @@ -136,26 +148,28 @@ public: // LLView interface - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, void *cargo_data, - EAcceptance *accept, - std::string& tooltip_msg); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleUnicodeCharHere(llwchar uni_char); + BOOL handleMouseDown(S32 x, S32 y, MASK mask) override; + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, + BOOL drop, EDragAndDropType cargo_type, void *cargo_data, + EAcceptance *accept, + std::string& tooltip_msg) override; + BOOL handleHover(S32 x, S32 y, MASK mask) override; + BOOL handleUnicodeCharHere(llwchar uni_char) override; + + void draw() override; + void setVisible( BOOL visible ) override; + void setEnabled( BOOL enabled ) override; - virtual void draw(); - virtual void setVisible( BOOL visible ); - virtual void setEnabled( BOOL enabled ); + void onVisibilityChange(BOOL new_visibility) override; - void setValid(BOOL valid); + void setValid(BOOL valid); - // LLUICtrl interface - virtual void clear(); + // LLUICtrl interface + void clear() override; - // Takes a UUID, wraps get/setImageAssetID - virtual void setValue(const LLSD& value); - virtual LLSD getValue() const; + // Takes a UUID, wraps get/setImageAssetID + void setValue(const LLSD& value) override; + LLSD getValue() const override; // LLTextureCtrl interface void showPicker(BOOL take_focus); @@ -250,6 +264,8 @@ private: commit_callback_t mOnCloseCallback; texture_selected_callback mOnTextureSelectedCallback; LLPointer<LLViewerFetchedTexture> mTexturep; + LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial; + LLPointer<LLViewerTexture> mGLTFPreview; LLUIColor mBorderColor; LLUUID mImageItemID; LLUUID mImageAssetID; @@ -382,6 +398,7 @@ protected: LLPointer<LLViewerTexture> mTexturep; LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial; + LLPointer<LLViewerTexture> mGLTFPreview; LLView* mOwner; LLUUID mImageAssetID; // Currently selected texture diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp index 5b75db37d0..40e6f285a8 100644 --- a/indra/newview/lltinygltfhelper.cpp +++ b/indra/newview/lltinygltfhelper.cpp @@ -147,7 +147,7 @@ const tinygltf::Image * LLTinyGLTFHelper::getImageFromTextureIndex(const tinyglt return nullptr; } -LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name) +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; @@ -159,14 +159,17 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny { name = image->name; rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component); - rawImage->verticalFlip(); + if (flip) + { + rawImage->verticalFlip(); + } rawImage->optimizeAwayAlpha(); } return rawImage; } -LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index) +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; @@ -177,7 +180,10 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny image->component <= 4) { rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component); - rawImage->verticalFlip(); + if (flip) + { + rawImage->verticalFlip(); + } rawImage->optimizeAwayAlpha(); } @@ -237,7 +243,8 @@ bool LLTinyGLTFHelper::getMaterialFromModel( const tinygltf::Model& model_in, S32 mat_index, LLFetchedGLTFMaterial* material, - std::string& material_name) + std::string& material_name, + bool flip) { llassert(material); @@ -256,18 +263,18 @@ bool LLTinyGLTFHelper::getMaterialFromModel( material_name = material_in.name; // get base color texture - LLPointer<LLImageRaw> base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index); + LLPointer<LLImageRaw> base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, flip); // get normal map - LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index); + LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index, flip); // get metallic-roughness texture - LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index); + LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, flip); // get emissive texture - LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index); + LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index, flip); // get occlusion map if needed LLPointer<LLImageRaw> occlusion_img; if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index) { - occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index); + occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index, flip); } LLPointer<LLViewerFetchedTexture> base_color_tex; diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/lltinygltfhelper.h index 256f6c854f..da505b41e9 100644 --- a/indra/newview/lltinygltfhelper.h +++ b/indra/newview/lltinygltfhelper.h @@ -38,10 +38,8 @@ namespace LLTinyGLTFHelper { LLColor4 getColor(const std::vector<double>& 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); - LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index); - - LLImageRaw* getTexture(const std::string& folder, 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); bool loadModel(const std::string& filename, tinygltf::Model& model_out); @@ -50,7 +48,8 @@ namespace LLTinyGLTFHelper const tinygltf::Model& model, S32 mat_index, LLFetchedGLTFMaterial* material, - std::string& material_name); + std::string& material_name, + bool flip = true); void initFetchedTextures(tinygltf::Material& material, LLPointer<LLImageRaw>& base_color_img, diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index bfa9386cd4..293c47bf73 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1077,51 +1077,71 @@ void set_texture_to_material(LLViewerObject* hit_obj, LLGLTFMaterial::TextureInfo drop_channel) { LLTextureEntry* te = hit_obj->getTE(hit_face); - if (te) + if (!te) { - LLPointer<LLGLTFMaterial> material = te->getGLTFMaterialOverride(); + return; + } - // make a copy to not invalidate existing - // material for multiple objects - if (material.isNull()) - { - // Start with a material override which does not make any changes - material = new LLGLTFMaterial(); - } - else - { - material = new LLGLTFMaterial(*material); - } + const LLUUID base_mat_id = hit_obj->getRenderMaterialID(hit_face); + if (base_mat_id.isNull()) + { + return; + } - switch (drop_channel) - { - case LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR: - default: - { - material->setBaseColorId(asset_id); - } - break; + if (hit_obj->isInventoryDirty() && hit_obj->hasInventoryListeners()) + { + hit_obj->requestInventory(); + return; + } - case LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS: - { - material->setOcclusionRoughnessMetallicId(asset_id); - } - break; + LLViewerInventoryItem* mat_item = hit_obj->getInventoryItemByAsset(base_mat_id); + if (mat_item && !mat_item->getPermissions().allowModifyBy(gAgentID)) + { + return; + } - case LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE: - { - material->setEmissiveId(asset_id); - } - break; + LLPointer<LLGLTFMaterial> material = te->getGLTFMaterialOverride(); - case LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL: - { - material->setNormalId(asset_id); - } - break; - } - LLGLTFMaterialList::queueModify(hit_obj, hit_face, material); + // make a copy to not invalidate existing + // material for multiple objects + if (material.isNull()) + { + // Start with a material override which does not make any changes + material = new LLGLTFMaterial(); + } + else + { + material = new LLGLTFMaterial(*material); + } + + switch (drop_channel) + { + case LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR: + default: + { + material->setBaseColorId(asset_id); + } + break; + + case LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS: + { + material->setOcclusionRoughnessMetallicId(asset_id); + } + break; + + case LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE: + { + material->setEmissiveId(asset_id); + } + break; + + case LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL: + { + material->setNormalId(asset_id); + } + break; } + LLGLTFMaterialList::queueModify(hit_obj, hit_face, material); } void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj, @@ -1262,7 +1282,7 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj, if (asset_id.isNull()) { // use blank material - asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + asset_id = BLANK_MATERIAL_ASSET_ID; } hit_obj->setRenderMaterialID(hit_face, asset_id); @@ -1298,7 +1318,7 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj, if (asset_id.isNull()) { // use blank material - asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + asset_id = BLANK_MATERIAL_ASSET_ID; } hit_obj->setRenderMaterialIDs(asset_id); diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index e2e321af0d..882e08935c 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -156,9 +156,9 @@ LLSD LLResourceUploadInfo::generatePostBody() body["next_owner_mask"] = LLSD::Integer(mNextOwnerPerms); body["group_mask"] = LLSD::Integer(mGroupPerms); body["everyone_mask"] = LLSD::Integer(mEveryonePerms); + body["expected_upload_cost"] = mExpectedUploadCost; return body; - } void LLResourceUploadInfo::logPreparedUpload() diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index 4134e35f87..9ba42e4f47 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -764,6 +764,12 @@ BOOL LLViewerCamera::cameraUnderWater() const { LLViewerRegion* regionp = LLWorld::instance().getRegionFromPosAgent(getOrigin()); + if (gPipeline.mHeroProbeManager.isMirrorPass()) + { + // TODO: figure out how to handle this case + return FALSE; + } + if (!regionp) { regionp = gAgent.getRegion(); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 7738cb904e..1fc51ac23c 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -54,6 +54,7 @@ #include "llvotree.h" #include "llvovolume.h" #include "llworld.h" +#include "llvlcomposition.h" #include "pipeline.h" #include "llviewerjoystick.h" #include "llviewerobjectlist.h" @@ -117,12 +118,25 @@ static bool handleRenderFarClipChanged(const LLSD& newvalue) return false; } -static bool handleTerrainDetailChanged(const LLSD& newvalue) +static bool handleTerrainScaleChanged(const LLSD& newvalue) { - LLDrawPoolTerrain::sDetailMode = newvalue.asInteger(); + F64 scale = newvalue.asReal(); + if (scale != 0.0) + { + LLDrawPoolTerrain::sDetailScale = F32(1.0 / scale); + } return true; } +static bool handlePBRTerrainScaleChanged(const LLSD& newvalue) +{ + F64 scale = newvalue.asReal(); + if (scale != 0.0) + { + LLDrawPoolTerrain::sPBRDetailScale = F32(1.0 / scale); + } + return true; +} static bool handleDebugAvatarJointsChanged(const LLSD& newvalue) { @@ -423,6 +437,19 @@ static bool handleReflectionProbeDetailChanged(const LLSD& newvalue) gPipeline.createGLBuffers(); LLViewerShaderMgr::instance()->setShaders(); gPipeline.mReflectionMapManager.reset(); + gPipeline.mHeroProbeManager.reset(); + } + return true; +} + +static bool handleHeroProbeResolutionChanged(const LLSD &newvalue) +{ + if (gPipeline.isInit()) + { + LLPipeline::refreshCachedSettings(); + gPipeline.mHeroProbeManager.reset(); + gPipeline.releaseGLBuffers(); + gPipeline.createGLBuffers(); } return true; } @@ -650,6 +677,16 @@ void handleFPSTuningStrategyChanged(const LLSD& newValue) const auto newval = gSavedSettings.getU32("TuningFPSStrategy"); LLPerfStats::tunables.userFPSTuningStrategy = newval; } + +void handleLocalTerrainChanged(const LLSD& newValue) +{ + for (U32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + const auto setting = gSavedSettings.getString(std::string("LocalTerrainAsset") + std::to_string(i + 1)); + const LLUUID materialID(setting); + gLocalTerrainMaterials.setDetailAssetID(i, materialID); + } +} //////////////////////////////////////////////////////////////////////////// LLPointer<LLControlVariable> setting_get_control(LLControlGroup& group, const std::string& setting) @@ -684,7 +721,11 @@ void settings_setup_listeners() { setting_setup_signal_listener(gSavedSettings, "FirstPersonAvatarVisible", handleRenderAvatarMouselookChanged); setting_setup_signal_listener(gSavedSettings, "RenderFarClip", handleRenderFarClipChanged); - setting_setup_signal_listener(gSavedSettings, "RenderTerrainDetail", handleTerrainDetailChanged); + setting_setup_signal_listener(gSavedSettings, "RenderTerrainScale", handleTerrainScaleChanged); + setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRScale", handlePBRTerrainScaleChanged); + setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRDetail", handleSetShaderChanged); + setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRPlanarSampleCount", handleSetShaderChanged); + setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRTriplanarBlendFactor", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "OctreeStaticObjectSizeFactor", handleRepartition); setting_setup_signal_listener(gSavedSettings, "OctreeDistanceFactor", handleRepartition); setting_setup_signal_listener(gSavedSettings, "OctreeMaxNodeCapacity", handleRepartition); @@ -724,6 +765,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "RenderReflectionProbeDetail", handleReflectionProbeDetailChanged); setting_setup_signal_listener(gSavedSettings, "RenderReflectionsEnabled", handleReflectionProbeDetailChanged); setting_setup_signal_listener(gSavedSettings, "RenderScreenSpaceReflections", handleReflectionProbeDetailChanged); + setting_setup_signal_listener(gSavedSettings, "RenderHeroProbeResolution", handleHeroProbeResolutionChanged); setting_setup_signal_listener(gSavedSettings, "RenderShadowDetail", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "RenderDeferredSSAO", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "RenderPerformanceTest", handleRenderPerfTestChanged); @@ -830,6 +872,10 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorFarAwayDistance", handleUserImpostorDistanceChanged); setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); + setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset1", handleLocalTerrainChanged); + setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset2", handleLocalTerrainChanged); + setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset3", handleLocalTerrainChanged); + setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset4", handleLocalTerrainChanged); setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged); } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index e1d6f71cce..2a77146101 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -656,6 +656,14 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if (!gDisconnected) { + // Render mirrors and associated hero probes before we render the rest of the scene. + // This ensures the scene state in the hero probes are exactly the same as the rest of the scene before we render it. + if (gPipeline.RenderMirrors && !gSnapshot && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0)) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Update hero probes"); + gPipeline.mHeroProbeManager.update(); + } + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("display - 1"); LLAppViewer::instance()->pingMainloopTimeout("Display:Update"); if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) @@ -696,7 +704,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) } gPipeline.updateGL(); - + stop_glerror(); LLAppViewer::instance()->pingMainloopTimeout("Display:Cull"); @@ -1064,7 +1072,7 @@ void display_cube_face() LLSpatialGroup::sNoDelete = TRUE; S32 occlusion = LLPipeline::sUseOcclusion; - LLPipeline::sUseOcclusion = 0; // occlusion data is from main camera point of view, don't read or write it during cube snapshots + LLPipeline::sUseOcclusion = 1; // occlusion data is from main camera point of view, don't read or write it during cube snapshots //gDepthDirty = TRUE; //let "real" render pipe know it can't trust the depth buffer for occlusion data static LLCullResult result; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 94efab3f4b..4cf948f20d 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -140,6 +140,7 @@ #include <boost/algorithm/string.hpp> #include "llcleanup.h" #include "llviewershadermgr.h" +#include "gltfscenemanager.h" using namespace LLAvatarAppearanceDefines; @@ -527,10 +528,8 @@ void init_menus() LLGridManager::getInstance()->isInProductionGrid()); // *TODO:Also fix cost in llfolderview.cpp for Inventory menus - const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()); const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost()); const std::string animation_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getAnimationUploadCost()); - gMenuHolder->childSetLabelArg("Upload Image", "[COST]", texture_upload_cost_str); gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", sound_upload_cost_str); gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", animation_upload_cost_str); @@ -2148,6 +2147,20 @@ class LLAdvancedPurgeShaderCache : public view_listener_t } }; +///////////////////// +// REBUILD TERRAIN // +///////////////////// + + +class LLAdvancedRebuildTerrain : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gPipeline.rebuildTerrain(); + return true; + } +}; + //////////////////// // EVENT Recorder // /////////////////// @@ -7896,6 +7909,30 @@ class LLAdvancedClickRenderBenchmark: public view_listener_t } }; +void hdri_preview(); + +class LLAdvancedClickHDRIPreview: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools) + LLFloaterReg::showInstance("env_adjust_snapshot"); + hdri_preview(); + return true; + } +}; + + +class LLAdvancedClickGLTFScenePreview : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools) + LL::GLTFSceneManager::instance().load(); + return true; + } +}; + // these are used in the gl menus to set control values that require shader recompilation class LLToggleShaderControl : public view_listener_t { @@ -9243,6 +9280,8 @@ void LLUploadCostCalculator::calculateCost(const std::string& asset_type_str) if (asset_type_str == "texture") { + // This use minimal texture cost to allow bulk and + // texture upload menu options to be visible upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); } else if (asset_type_str == "animation") @@ -9540,7 +9579,10 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedClickRenderShadowOption(), "Advanced.ClickRenderShadowOption"); view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile"); view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark"); + view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview"); + view_listener_t::addMenu(new LLAdvancedClickGLTFScenePreview(), "Advanced.ClickGLTFScenePreview"); view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache"); + view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); #ifdef TOGGLE_HACKED_GODLIKE_VIEWER view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode"); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 5461e0f362..240c756411 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -546,21 +546,39 @@ void do_bulk_upload(std::vector<std::string> filenames, const LLSD& notification std::string ext = gDirUtilp->getExtension(filename); LLAssetType::EType asset_type; U32 codec; - S32 expected_upload_cost; - if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec) && - LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost)) - { - LLResourceUploadInfo::ptr_t uploadInfo(new LLNewFileResourceUploadInfo( - filename, - asset_name, - asset_name, 0, - LLFolderType::FT_NONE, LLInventoryType::IT_NONE, - LLFloaterPerms::getNextOwnerPerms("Uploads"), - LLFloaterPerms::getGroupPerms("Uploads"), - LLFloaterPerms::getEveryonePerms("Uploads"), - expected_upload_cost)); - - upload_new_resource(uploadInfo); + S32 expected_upload_cost = 0; + + if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec)) + { + bool resource_upload = false; + if (asset_type == LLAssetType::AT_TEXTURE) + { + LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec); + if (gDirUtilp->fileExists(filename) && image_frmted->load(filename)) + { + expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(image_frmted); + resource_upload = true; + } + } + else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost)) + { + resource_upload = true; + } + + if (resource_upload) + { + LLResourceUploadInfo::ptr_t uploadInfo(new LLNewFileResourceUploadInfo( + filename, + asset_name, + asset_name, 0, + LLFolderType::FT_NONE, LLInventoryType::IT_NONE, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost)); + + upload_new_resource(uploadInfo); + } } // gltf does not use normal upload procedure @@ -602,17 +620,26 @@ bool get_bulk_upload_expected_cost(const std::vector<std::string>& filenames, S3 U32 codec; S32 cost; - if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec) && - LLAgentBenefitsMgr::current().findUploadCost(asset_type, cost)) + if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec)) { - total_cost += cost; - file_count++; - } + if (asset_type == LLAssetType::AT_TEXTURE) + { + LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec); + if (gDirUtilp->fileExists(filename) && image_frmted->load(filename)) + { + total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(image_frmted); + file_count++; + } + } + else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, cost)) + { + total_cost += cost; + file_count++; + } + } if (ext == "gltf" || ext == "glb") { - S32 texture_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); - tinygltf::Model model; if (LLTinyGLTFHelper::loadModel(filename, model)) @@ -629,24 +656,22 @@ bool get_bulk_upload_expected_cost(const std::vector<std::string>& filenames, S3 { // Todo: make it account for possibility of same texture in different // materials and even in scope of same material - S32 texture_count = 0; - if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].notNull()) + if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].notNull() && material->mBaseColorTexture) { - texture_count++; + total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mBaseColorTexture); } - if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].notNull()) + if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].notNull() && material->mMetallicRoughnessTexture) { - texture_count++; + total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mMetallicRoughnessTexture); } - if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].notNull()) + if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].notNull() && material->mNormalTexture) { - texture_count++; + total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mNormalTexture); } - if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].notNull()) + if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].notNull() && material->mEmissiveTexture) { - texture_count++; + total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mEmissiveTexture); } - total_cost += texture_count * texture_upload_cost; file_count++; } } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index f1fac106c7..cee00db8eb 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1529,12 +1529,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, std::string temp_string; mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, temp_string, block_num ); - + LLColor4U coloru; mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num); - + // alpha was flipped so that it zero encoded better coloru.mV[3] = 255 - coloru.mV[3]; + mText->setColor(LLColor4(coloru)); mText->setString(temp_string); @@ -1914,6 +1915,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, { std::string temp_string; dp->unpackString(temp_string, "Text"); + LLColor4U coloru; dp->unpackBinaryDataFixed(coloru.mV, 4, "Color"); coloru.mV[3] = 255 - coloru.mV[3]; @@ -5359,7 +5361,6 @@ S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright) return retval; } - S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags) { // this might need work for media type diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 80da7b2f73..3d6903d177 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -45,6 +45,7 @@ #include "llbbox.h" #include "llrigginginfo.h" #include "llreflectionmap.h" +#include "gltf/asset.h" class LLAgent; // TODO: Get rid of this. class LLAudioSource; @@ -722,6 +723,8 @@ public: F32 mPhysicsDensity; F32 mPhysicsRestitution; + // Associated GLTF Asset + LLPointer<LL::GLTF::Asset> mGLTFAsset; // Pipeline classes LLPointer<LLDrawable> mDrawable; @@ -944,6 +947,7 @@ public: // reflection probe state bool mIsReflectionProbe = false; // if true, this object should register itself with LLReflectionProbeManager LLPointer<LLReflectionMap> mReflectionProbe = nullptr; // reflection probe coupled to this viewer object. If not null, should be deregistered when this object is destroyed + bool mIsHeroProbe = false; // This is a special case for mirrors and other high resolution probes. // the amount of GPU time (in ms) it took to render this object according to LLPipeline::profileAvatar // -1.f if no profile data available diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index c4a4e6613c..160e2f7e5c 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1113,6 +1113,11 @@ void LLViewerRegion::dirtyHeights() } } +void LLViewerRegion::dirtyAllPatches() +{ + getLand().dirtyAllPatches(); +} + //physically delete the cache entry void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering) { @@ -1613,7 +1618,19 @@ void LLViewerRegion::idleUpdate(F32 max_update_time) mLastUpdate = LLViewerOctreeEntryData::getCurrentFrame(); - mImpl->mLandp->idleUpdate(max_update_time); + static LLCachedControl<bool> pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false); + static LLCachedControl<bool> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", false); + bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR); + bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals; + + if (!pbr_land) + { + mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time); + } + else + { + mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time); + } if (mParcelOverlay) { @@ -1914,7 +1931,21 @@ LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* o // As above, but forcibly do the update. void LLViewerRegion::forceUpdate() { - mImpl->mLandp->idleUpdate(0.f); + constexpr F32 max_update_time = 0.f; + + static LLCachedControl<bool> pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false); + static LLCachedControl<bool> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", false); + bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR); + bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals; + + if (!pbr_land) + { + mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time); + } + else + { + mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time); + } if (mParcelOverlay) { @@ -2415,7 +2446,47 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features) mSimulatorFeatures = sim_features; setSimulatorFeaturesReceived(true); - + + // if region has MaxTextureResolution, set max_texture_dimension settings, otherwise use default + if (mSimulatorFeatures.has("MaxTextureResolution")) + { + S32 max_texture_resolution = mSimulatorFeatures["MaxTextureResolution"].asInteger(); + gSavedSettings.setS32("max_texture_dimension_X", max_texture_resolution); + gSavedSettings.setS32("max_texture_dimension_Y", max_texture_resolution); + } + else + { + gSavedSettings.setS32("max_texture_dimension_X", 1024); + gSavedSettings.setS32("max_texture_dimension_Y", 1024); + } + + bool mirrors_enabled = false; + if (mSimulatorFeatures.has("MirrorsEnabled")) + { + mirrors_enabled = mSimulatorFeatures["MirrorsEnabled"].asBoolean(); + } + + gSavedSettings.setBOOL("RenderMirrors", mirrors_enabled); + + if (mSimulatorFeatures.has("PBRTerrainEnabled")) + { + bool enabled = mSimulatorFeatures["PBRTerrainEnabled"]; + gSavedSettings.setBOOL("RenderTerrainPBREnabled", enabled); + } + else + { + gSavedSettings.setBOOL("RenderTerrainPBREnabled", false); + } + + if (mSimulatorFeatures.has("PBRMaterialSwatchEnabled")) + { + bool enabled = mSimulatorFeatures["PBRMaterialSwatchEnabled"]; + gSavedSettings.setBOOL("UIPreviewMaterial", enabled); + } + else + { + gSavedSettings.setBOOL("UIPreviewMaterial", false); + } } //this is called when the parent is not cacheable. @@ -2974,20 +3045,20 @@ void LLViewerRegion::unpackRegionHandshake() // Get the 4 textures for land msg->getUUID("RegionInfo", "TerrainDetail0", tmp_id); - changed |= (tmp_id != compp->getDetailTextureID(0)); - compp->setDetailTextureID(0, tmp_id); + changed |= (tmp_id != compp->getDetailAssetID(0)); + compp->setDetailAssetID(0, tmp_id); msg->getUUID("RegionInfo", "TerrainDetail1", tmp_id); - changed |= (tmp_id != compp->getDetailTextureID(1)); - compp->setDetailTextureID(1, tmp_id); + changed |= (tmp_id != compp->getDetailAssetID(1)); + compp->setDetailAssetID(1, tmp_id); msg->getUUID("RegionInfo", "TerrainDetail2", tmp_id); - changed |= (tmp_id != compp->getDetailTextureID(2)); - compp->setDetailTextureID(2, tmp_id); + changed |= (tmp_id != compp->getDetailAssetID(2)); + compp->setDetailAssetID(2, tmp_id); msg->getUUID("RegionInfo", "TerrainDetail3", tmp_id); - changed |= (tmp_id != compp->getDetailTextureID(3)); - compp->setDetailTextureID(3, tmp_id); + changed |= (tmp_id != compp->getDetailAssetID(3)); + compp->setDetailAssetID(3, tmp_id); // Get the start altitude and range values for land textures F32 tmp_f32; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 622490c881..1c2ff9bc97 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -163,6 +163,9 @@ public: // Call this whenever you change the height data in the region. // (Automatically called by LLSurfacePatch's update routine) void dirtyHeights(); + // Call this whenever you want to force all terrain to rebuild. + // (For example, if a global terrain config option has changed) + void dirtyAllPatches(); LLViewerParcelOverlay *getParcelOverlay() const { return mParcelOverlay; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 3225299493..65b1fee54b 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -69,6 +69,14 @@ bool LLViewerShaderMgr::sSkipReload = false; LLVector4 gShinyOrigin; +S32 clamp_terrain_mapping(S32 mapping) +{ + // 1 = "flat", 2 not implemented, 3 = triplanar mapping + mapping = llclamp(mapping, 1, 3); + if (mapping == 2) { mapping = 1; } + return mapping; +} + //utility shaders LLGLSLShader gOcclusionProgram; LLGLSLShader gSkinnedOcclusionProgram; @@ -76,13 +84,16 @@ LLGLSLShader gOcclusionCubeProgram; LLGLSLShader gGlowCombineProgram; LLGLSLShader gReflectionMipProgram; LLGLSLShader gGaussianProgram; -LLGLSLShader gRadianceGenProgram; +LLGLSLShader gRadianceGenProgram; +LLGLSLShader gHeroRadianceGenProgram; LLGLSLShader gIrradianceGenProgram; LLGLSLShader gGlowCombineFXAAProgram; LLGLSLShader gTwoTextureCompareProgram; LLGLSLShader gOneTextureFilterProgram; LLGLSLShader gDebugProgram; LLGLSLShader gSkinnedDebugProgram; +LLGLSLShader gNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT]; +LLGLSLShader gSkinnedNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT]; LLGLSLShader gClipProgram; LLGLSLShader gAlphaMaskProgram; LLGLSLShader gBenchmarkProgram; @@ -95,7 +106,6 @@ LLGLSLShader gObjectPreviewProgram; LLGLSLShader gSkinnedObjectPreviewProgram; LLGLSLShader gPhysicsPreviewProgram; LLGLSLShader gObjectFullbrightAlphaMaskProgram; -LLGLSLShader gSkinnedObjectFullbrightAlphaMaskProgram; LLGLSLShader gObjectBumpProgram; LLGLSLShader gSkinnedObjectBumpProgram; LLGLSLShader gObjectAlphaMaskNoColorProgram; @@ -186,10 +196,12 @@ LLGLSLShader gDeferredPostGammaCorrectProgram; LLGLSLShader gNoPostGammaCorrectProgram; LLGLSLShader gLegacyPostGammaCorrectProgram; LLGLSLShader gExposureProgram; +LLGLSLShader gExposureProgramNoFade; LLGLSLShader gLuminanceProgram; LLGLSLShader gFXAAProgram; LLGLSLShader gDeferredPostNoDoFProgram; LLGLSLShader gDeferredWLSkyProgram; +LLGLSLShader gEnvironmentMapProgram; LLGLSLShader gDeferredWLCloudProgram; LLGLSLShader gDeferredWLSunProgram; LLGLSLShader gDeferredWLMoonProgram; @@ -214,9 +226,10 @@ LLGLSLShader gDeferredSkinnedPBROpaqueProgram; LLGLSLShader gHUDPBRAlphaProgram; LLGLSLShader gDeferredPBRAlphaProgram; LLGLSLShader gDeferredSkinnedPBRAlphaProgram; +LLGLSLShader gDeferredPBRTerrainProgram; //helper for making a rigged variant of a given shader -bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader) +static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader) { riggedShader.mName = llformat("Skinned %s", shader.mName.c_str()); riggedShader.mFeatures = shader.mFeatures; @@ -231,64 +244,100 @@ bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader) return riggedShader.createShader(NULL, NULL); } +#ifdef SHOW_ASSERT +// return true if there are no redundant shaders in the given vector +// also checks for redundant variants +static bool no_redundant_shaders(const std::vector<LLGLSLShader*>& shaders) +{ + std::set<std::string> names; + for (LLGLSLShader* shader : shaders) + { + if (names.find(shader->mName) != names.end()) + { + LL_WARNS("Shader") << "Redundant shader: " << shader->mName << LL_ENDL; + return false; + } + names.insert(shader->mName); + + if (shader->mRiggedVariant) + { + if (names.find(shader->mRiggedVariant->mName) != names.end()) + { + LL_WARNS("Shader") << "Redundant shader: " << shader->mRiggedVariant->mName << LL_ENDL; + return false; + } + names.insert(shader->mRiggedVariant->mName); + } + } + return true; +} +#endif + + LLViewerShaderMgr::LLViewerShaderMgr() : mShaderLevel(SHADER_COUNT, 0), mMaxAvatarShaderLevel(0) { +} + +LLViewerShaderMgr::~LLViewerShaderMgr() +{ + mShaderLevel.clear(); + mShaderList.clear(); +} + +void LLViewerShaderMgr::finalizeShaderList() +{ //ONLY shaders that need WL Param management should be added here - mShaderList.push_back(&gAvatarProgram); - mShaderList.push_back(&gWaterProgram); - mShaderList.push_back(&gWaterEdgeProgram); - mShaderList.push_back(&gAvatarEyeballProgram); - mShaderList.push_back(&gImpostorProgram); - mShaderList.push_back(&gObjectBumpProgram); - mShaderList.push_back(&gSkinnedObjectBumpProgram); - mShaderList.push_back(&gObjectFullbrightAlphaMaskProgram); - mShaderList.push_back(&gSkinnedObjectFullbrightAlphaMaskProgram); - mShaderList.push_back(&gObjectAlphaMaskNoColorProgram); - mShaderList.push_back(&gUnderWaterProgram); - mShaderList.push_back(&gDeferredSunProgram); + mShaderList.push_back(&gAvatarProgram); + mShaderList.push_back(&gWaterProgram); + mShaderList.push_back(&gWaterEdgeProgram); + mShaderList.push_back(&gAvatarEyeballProgram); + mShaderList.push_back(&gImpostorProgram); + mShaderList.push_back(&gObjectBumpProgram); + mShaderList.push_back(&gObjectFullbrightAlphaMaskProgram); + mShaderList.push_back(&gObjectAlphaMaskNoColorProgram); + mShaderList.push_back(&gUnderWaterProgram); + mShaderList.push_back(&gDeferredSunProgram); mShaderList.push_back(&gHazeProgram); mShaderList.push_back(&gHazeWaterProgram); - mShaderList.push_back(&gDeferredSoftenProgram); - mShaderList.push_back(&gDeferredAlphaProgram); + mShaderList.push_back(&gDeferredSoftenProgram); + mShaderList.push_back(&gDeferredAlphaProgram); mShaderList.push_back(&gHUDAlphaProgram); - mShaderList.push_back(&gDeferredSkinnedAlphaProgram); - mShaderList.push_back(&gDeferredAlphaImpostorProgram); - mShaderList.push_back(&gDeferredSkinnedAlphaImpostorProgram); - mShaderList.push_back(&gDeferredFullbrightProgram); + mShaderList.push_back(&gDeferredAlphaImpostorProgram); + mShaderList.push_back(&gDeferredFullbrightProgram); mShaderList.push_back(&gHUDFullbrightProgram); - mShaderList.push_back(&gDeferredFullbrightAlphaMaskProgram); + mShaderList.push_back(&gDeferredFullbrightAlphaMaskProgram); mShaderList.push_back(&gHUDFullbrightAlphaMaskProgram); mShaderList.push_back(&gDeferredFullbrightAlphaMaskAlphaProgram); mShaderList.push_back(&gHUDFullbrightAlphaMaskAlphaProgram); - mShaderList.push_back(&gDeferredFullbrightShinyProgram); + mShaderList.push_back(&gDeferredFullbrightShinyProgram); mShaderList.push_back(&gHUDFullbrightShinyProgram); - mShaderList.push_back(&gDeferredSkinnedFullbrightShinyProgram); - mShaderList.push_back(&gDeferredSkinnedFullbrightProgram); - mShaderList.push_back(&gDeferredSkinnedFullbrightAlphaMaskProgram); - mShaderList.push_back(&gDeferredSkinnedFullbrightAlphaMaskAlphaProgram); - mShaderList.push_back(&gDeferredEmissiveProgram); - mShaderList.push_back(&gDeferredSkinnedEmissiveProgram); - mShaderList.push_back(&gDeferredAvatarEyesProgram); + mShaderList.push_back(&gDeferredEmissiveProgram); + mShaderList.push_back(&gDeferredAvatarEyesProgram); mShaderList.push_back(&gDeferredAvatarAlphaProgram); - mShaderList.push_back(&gDeferredWLSkyProgram); - mShaderList.push_back(&gDeferredWLCloudProgram); + mShaderList.push_back(&gEnvironmentMapProgram); + mShaderList.push_back(&gDeferredWLSkyProgram); + mShaderList.push_back(&gDeferredWLCloudProgram); mShaderList.push_back(&gDeferredWLMoonProgram); mShaderList.push_back(&gDeferredWLSunProgram); mShaderList.push_back(&gDeferredPBRAlphaProgram); mShaderList.push_back(&gHUDPBRAlphaProgram); - mShaderList.push_back(&gDeferredSkinnedPBRAlphaProgram); mShaderList.push_back(&gDeferredPostGammaCorrectProgram); // for gamma mShaderList.push_back(&gNoPostGammaCorrectProgram); mShaderList.push_back(&gLegacyPostGammaCorrectProgram); - -} - -LLViewerShaderMgr::~LLViewerShaderMgr() -{ - mShaderLevel.clear(); - mShaderList.clear(); + mShaderList.push_back(&gDeferredDiffuseProgram); + mShaderList.push_back(&gDeferredBumpProgram); + mShaderList.push_back(&gDeferredPBROpaqueProgram); + mShaderList.push_back(&gDeferredAvatarProgram); + mShaderList.push_back(&gDeferredTerrainProgram); + mShaderList.push_back(&gDeferredPBRTerrainProgram); + mShaderList.push_back(&gDeferredDiffuseAlphaMaskProgram); + mShaderList.push_back(&gDeferredNonIndexedDiffuseAlphaMaskProgram); + mShaderList.push_back(&gDeferredTreeProgram); + + // make sure there are no redundancies + llassert(no_redundant_shaders(mShaderList)); } // static @@ -343,6 +392,10 @@ void LLViewerShaderMgr::setShaders() return; } + mShaderList.clear(); + + LLShaderMgr::sMirrorsEnabled = LLPipeline::RenderMirrors; + if (!gGLManager.mHasRequirements) { // Viewer will show 'hardware requirements' warning later @@ -524,6 +577,8 @@ void LLViewerShaderMgr::setShaders() } gPipeline.createGLBuffers(); + finalizeShaderList(); + reentrance = false; } @@ -623,6 +678,16 @@ std::string LLViewerShaderMgr::loadBasicShaders() attribs["REF_SAMPLE_COUNT"] = "32"; } + { // PBR terrain + const S32 mapping = clamp_terrain_mapping(gSavedSettings.getS32("RenderTerrainPBRPlanarSampleCount")); + attribs["TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT"] = llformat("%d", mapping); + const F32 triplanar_factor = gSavedSettings.getF32("RenderTerrainPBRTriplanarBlendFactor"); + attribs["TERRAIN_TRIPLANAR_BLEND_FACTOR"] = llformat("%.2f", triplanar_factor); + S32 detail = gSavedSettings.getS32("RenderTerrainPBRDetail"); + detail = llclamp(detail, TERRAIN_PBR_DETAIL_MIN, TERRAIN_PBR_DETAIL_MAX); + attribs["TERRAIN_PBR_DETAIL"] = llformat("%d", detail); + } + LLGLSLShader::sGlobalDefines = attribs; // We no longer have to bind the shaders to global glhandles, they are automatically added to a map now. @@ -658,8 +723,10 @@ std::string LLViewerShaderMgr::loadBasicShaders() index_channels.push_back(-1); shaders.push_back( make_pair( "environment/encodeNormF.glsl", mShaderLevel[SHADER_ENVIRONMENT] ) ); index_channels.push_back(-1); shaders.push_back( make_pair( "environment/srgbF.glsl", mShaderLevel[SHADER_ENVIRONMENT] ) ); index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/deferredUtil.glsl", 1) ); + index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/globalF.glsl", 1)); index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/shadowUtil.glsl", 1) ); index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/aoUtil.glsl", 1) ); + index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/pbrterrainUtilF.glsl", 1) ); index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/reflectionProbeF.glsl", has_reflection_probes ? 3 : 2) ); index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/screenSpaceReflUtil.glsl", ssr ? 3 : 1) ); index_channels.push_back(-1); shaders.push_back( make_pair( "lighting/lightNonIndexedF.glsl", mShaderLevel[SHADER_LIGHTING] ) ); @@ -917,11 +984,13 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredCoFProgram.unload(); gDeferredDoFCombineProgram.unload(); gExposureProgram.unload(); + gExposureProgramNoFade.unload(); gLuminanceProgram.unload(); gDeferredPostGammaCorrectProgram.unload(); gNoPostGammaCorrectProgram.unload(); gLegacyPostGammaCorrectProgram.unload(); gFXAAProgram.unload(); + gEnvironmentMapProgram.unload(); gDeferredWLSkyProgram.unload(); gDeferredWLCloudProgram.unload(); gDeferredWLSunProgram.unload(); @@ -951,6 +1020,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredSkinnedPBROpaqueProgram.unload(); gDeferredPBRAlphaProgram.unload(); gDeferredSkinnedPBRAlphaProgram.unload(); + gDeferredPBRTerrainProgram.unload(); return TRUE; } @@ -1044,9 +1114,17 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() { if (success) { - mShaderList.push_back(&gDeferredMaterialProgram[i]); + bool has_skin = i & 0x10; - gDeferredMaterialProgram[i].mName = llformat("Deferred Material Shader %d", i); + if (!has_skin) + { + mShaderList.push_back(&gDeferredMaterialProgram[i]); + gDeferredMaterialProgram[i].mName = llformat("Material Shader %d", i); + } + else + { + gDeferredMaterialProgram[i].mName = llformat("Skinned Material Shader %d", i); + } U32 alpha_mode = i & 0x3; @@ -1083,7 +1161,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredMaterialProgram[i].addPermutation("HAS_SUN_SHADOW", "1"); } - bool has_skin = i & 0x10; + gDeferredMaterialProgram[i].mFeatures.hasSrgb = true; gDeferredMaterialProgram[i].mFeatures.encodesNormal = true; gDeferredMaterialProgram[i].mFeatures.calculatesAtmospherics = true; @@ -1242,6 +1320,34 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() success = shader->createShader(NULL, NULL); llassert(success); } + + if (success) + { + S32 detail = gSavedSettings.getS32("RenderTerrainPBRDetail"); + detail = llclamp(detail, TERRAIN_PBR_DETAIL_MIN, TERRAIN_PBR_DETAIL_MAX); + const S32 mapping = clamp_terrain_mapping(gSavedSettings.getS32("RenderTerrainPBRPlanarSampleCount")); + gDeferredPBRTerrainProgram.mName = llformat("Deferred PBR Terrain Shader %d %s", + detail, + (mapping == 1 ? "flat" : "triplanar")); + gDeferredPBRTerrainProgram.mFeatures.encodesNormal = true; + gDeferredPBRTerrainProgram.mFeatures.hasSrgb = true; + gDeferredPBRTerrainProgram.mFeatures.isAlphaLighting = true; + gDeferredPBRTerrainProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels + gDeferredPBRTerrainProgram.mFeatures.calculatesAtmospherics = true; + gDeferredPBRTerrainProgram.mFeatures.hasAtmospherics = true; + gDeferredPBRTerrainProgram.mFeatures.hasGamma = true; + gDeferredPBRTerrainProgram.mFeatures.hasTransport = true; + gDeferredPBRTerrainProgram.mFeatures.isPBRTerrain = true; + + gDeferredPBRTerrainProgram.mShaderFiles.clear(); + gDeferredPBRTerrainProgram.mShaderFiles.push_back(make_pair("deferred/pbrterrainV.glsl", GL_VERTEX_SHADER)); + gDeferredPBRTerrainProgram.mShaderFiles.push_back(make_pair("deferred/pbrterrainF.glsl", GL_FRAGMENT_SHADER)); + gDeferredPBRTerrainProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + gDeferredPBRTerrainProgram.addPermutation("TERRAIN_PBR_DETAIL", llformat("%d", detail)); + gDeferredPBRTerrainProgram.addPermutation("TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT", llformat("%d", mapping)); + success = gDeferredPBRTerrainProgram.createShader(NULL, NULL); + llassert(success); + } if (success) { @@ -1955,8 +2061,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredTerrainProgram.mName = "Deferred Terrain Shader"; gDeferredTerrainProgram.mFeatures.encodesNormal = true; gDeferredTerrainProgram.mFeatures.hasSrgb = true; - gDeferredTerrainProgram.mFeatures.calculatesLighting = false; - gDeferredTerrainProgram.mFeatures.hasLighting = false; gDeferredTerrainProgram.mFeatures.isAlphaLighting = true; gDeferredTerrainProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels gDeferredTerrainProgram.mFeatures.calculatesAtmospherics = true; @@ -2029,6 +2133,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gExposureProgram.mFeatures.isDeferred = true; gExposureProgram.mShaderFiles.clear(); gExposureProgram.clearPermutations(); + gExposureProgram.addPermutation("USE_LAST_EXPOSURE", "1"); gExposureProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gExposureProgram.mShaderFiles.push_back(make_pair("deferred/exposureF.glsl", GL_FRAGMENT_SHADER)); gExposureProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; @@ -2038,6 +2143,20 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { + gExposureProgramNoFade.mName = "Exposure (no fade)"; + gExposureProgramNoFade.mFeatures.hasSrgb = true; + gExposureProgramNoFade.mFeatures.isDeferred = true; + gExposureProgramNoFade.mShaderFiles.clear(); + gExposureProgramNoFade.clearPermutations(); + gExposureProgramNoFade.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); + gExposureProgramNoFade.mShaderFiles.push_back(make_pair("deferred/exposureF.glsl", GL_FRAGMENT_SHADER)); + gExposureProgramNoFade.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + success = gExposureProgramNoFade.createShader(NULL, NULL); + llassert(success); + } + + if (success) + { gLuminanceProgram.mName = "Luminance"; gLuminanceProgram.mShaderFiles.clear(); gLuminanceProgram.clearPermutations(); @@ -2153,6 +2272,26 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() llassert(success); } + if (success) + { + gEnvironmentMapProgram.mName = "Environment Map Program"; + gEnvironmentMapProgram.mShaderFiles.clear(); + gEnvironmentMapProgram.mFeatures.calculatesAtmospherics = true; + gEnvironmentMapProgram.mFeatures.hasAtmospherics = true; + gEnvironmentMapProgram.mFeatures.hasGamma = true; + gEnvironmentMapProgram.mFeatures.hasSrgb = true; + + gEnvironmentMapProgram.clearPermutations(); + gEnvironmentMapProgram.addPermutation("HAS_HDRI", "1"); + gEnvironmentMapProgram.mShaderFiles.push_back(make_pair("deferred/skyV.glsl", GL_VERTEX_SHADER)); + gEnvironmentMapProgram.mShaderFiles.push_back(make_pair("deferred/skyF.glsl", GL_FRAGMENT_SHADER)); + gEnvironmentMapProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + gEnvironmentMapProgram.mShaderGroup = LLGLSLShader::SG_SKY; + + success = gEnvironmentMapProgram.createShader(NULL, NULL); + llassert(success); + } + if (success) { gDeferredWLSkyProgram.mName = "Deferred Windlight Sky Shader"; @@ -2643,6 +2782,33 @@ BOOL LLViewerShaderMgr::loadShadersInterface() if (success) { + for (S32 variant = 0; variant < NORMAL_DEBUG_SHADER_COUNT; ++variant) + { + LLGLSLShader& shader = gNormalDebugProgram[variant]; + LLGLSLShader& skinned_shader = gSkinnedNormalDebugProgram[variant]; + shader.mName = "Normal Debug Shader"; + shader.mShaderFiles.clear(); + shader.mShaderFiles.push_back(make_pair("interface/normaldebugV.glsl", GL_VERTEX_SHADER)); + // *NOTE: Geometry shaders have a reputation for being slow. + // Consider using compute shaders instead, which have a reputation + // for being fast. This geometry shader in particular seems to run + // fine on my machine, but I won't vouch for this in + // performance-critical areas. -Cosmic,2023-09-28 + shader.mShaderFiles.push_back(make_pair("interface/normaldebugG.glsl", GL_GEOMETRY_SHADER)); + shader.mShaderFiles.push_back(make_pair("interface/normaldebugF.glsl", GL_FRAGMENT_SHADER)); + shader.mRiggedVariant = &skinned_shader; + shader.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + if (variant == NORMAL_DEBUG_SHADER_WITH_TANGENTS) + { + shader.addPermutation("HAS_ATTRIBUTE_TANGENT", "1"); + } + success = make_rigged_variant(shader, skinned_shader); + success = success && shader.createShader(NULL, NULL); + } + } + + if (success) + { gClipProgram.mName = "Clip Shader"; gClipProgram.mShaderFiles.clear(); gClipProgram.mShaderFiles.push_back(make_pair("interface/clipV.glsl", GL_VERTEX_SHADER)); @@ -2744,8 +2910,21 @@ BOOL LLViewerShaderMgr::loadShadersInterface() gRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenV.glsl", GL_VERTEX_SHADER)); gRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenF.glsl", GL_FRAGMENT_SHADER)); gRadianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + gRadianceGenProgram.addPermutation("PROBE_FILTER_SAMPLES", "32"); success = gRadianceGenProgram.createShader(NULL, NULL); } + + if (success && gGLManager.mHasCubeMapArray) + { + gHeroRadianceGenProgram.mName = "Hero Radiance Gen Shader"; + gHeroRadianceGenProgram.mShaderFiles.clear(); + gHeroRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenV.glsl", GL_VERTEX_SHADER)); + gHeroRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenF.glsl", GL_FRAGMENT_SHADER)); + gHeroRadianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + gHeroRadianceGenProgram.addPermutation("HERO_PROBES", "1"); + gHeroRadianceGenProgram.addPermutation("PROBE_FILTER_SAMPLES", "4"); + success = gHeroRadianceGenProgram.createShader(NULL, NULL); + } if (success && gGLManager.mHasCubeMapArray) { diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index 04da7e48ae..c51f583ebc 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -41,6 +41,10 @@ public: LLViewerShaderMgr(); /* virtual */ ~LLViewerShaderMgr(); + // Add shaders to mShaderList for later uniform propagation + // Will assert on redundant shader entries in debug builds + void finalizeShaderList(); + // singleton pattern implementation static LLViewerShaderMgr * instance(); static void releaseInstance(); @@ -153,9 +157,18 @@ extern LLGLSLShader gGlowCombineProgram; extern LLGLSLShader gReflectionMipProgram; extern LLGLSLShader gGaussianProgram; extern LLGLSLShader gRadianceGenProgram; +extern LLGLSLShader gHeroRadianceGenProgram; extern LLGLSLShader gIrradianceGenProgram; extern LLGLSLShader gGlowCombineFXAAProgram; extern LLGLSLShader gDebugProgram; +enum NormalDebugShaderVariant : S32 +{ + NORMAL_DEBUG_SHADER_DEFAULT, + NORMAL_DEBUG_SHADER_WITH_TANGENTS, + NORMAL_DEBUG_SHADER_COUNT +}; +extern LLGLSLShader gNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT]; +extern LLGLSLShader gSkinnedNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT]; extern LLGLSLShader gClipProgram; extern LLGLSLShader gBenchmarkProgram; extern LLGLSLShader gReflectionProbeDisplayProgram; @@ -171,7 +184,6 @@ extern LLGLSLShader gOneTextureFilterProgram; //object shaders extern LLGLSLShader gObjectPreviewProgram; extern LLGLSLShader gPhysicsPreviewProgram; -extern LLGLSLShader gSkinnedObjectFullbrightAlphaMaskProgram; extern LLGLSLShader gObjectBumpProgram; extern LLGLSLShader gSkinnedObjectBumpProgram; extern LLGLSLShader gObjectAlphaMaskNoColorProgram; @@ -237,6 +249,7 @@ extern LLGLSLShader gDeferredPostGammaCorrectProgram; extern LLGLSLShader gNoPostGammaCorrectProgram; extern LLGLSLShader gLegacyPostGammaCorrectProgram; extern LLGLSLShader gExposureProgram; +extern LLGLSLShader gExposureProgramNoFade; extern LLGLSLShader gLuminanceProgram; extern LLGLSLShader gDeferredAvatarShadowProgram; extern LLGLSLShader gDeferredAvatarAlphaShadowProgram; @@ -253,6 +266,7 @@ extern LLGLSLShader gHUDFullbrightAlphaMaskAlphaProgram; extern LLGLSLShader gDeferredEmissiveProgram; extern LLGLSLShader gDeferredAvatarEyesProgram; extern LLGLSLShader gDeferredAvatarAlphaProgram; +extern LLGLSLShader gEnvironmentMapProgram; extern LLGLSLShader gDeferredWLSkyProgram; extern LLGLSLShader gDeferredWLCloudProgram; extern LLGLSLShader gDeferredWLSunProgram; @@ -272,4 +286,20 @@ extern LLGLSLShader gPBRGlowProgram; extern LLGLSLShader gDeferredPBROpaqueProgram; extern LLGLSLShader gDeferredPBRAlphaProgram; extern LLGLSLShader gHUDPBRAlphaProgram; + +// Encodes detail level for dropping textures, in accordance with the GLTF spec where possible +// 0 is highest detail, -1 drops emissive, etc +// Dropping metallic roughness is off-spec - Reserve for potato machines as needed +// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures +enum TerrainPBRDetail : S32 +{ + TERRAIN_PBR_DETAIL_MAX = 0, + TERRAIN_PBR_DETAIL_EMISSIVE = 0, + TERRAIN_PBR_DETAIL_OCCLUSION = -1, + TERRAIN_PBR_DETAIL_NORMAL = -2, + TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS = -3, + TERRAIN_PBR_DETAIL_BASE_COLOR = -4, + TERRAIN_PBR_DETAIL_MIN = -4, +}; +extern LLGLSLShader gDeferredPBRTerrainProgram; #endif diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 04ef441a69..6b496cf658 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1699,7 +1699,7 @@ void LLViewerFetchedTexture::processTextureStats() { if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) { - mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048 + mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096 } else { @@ -1712,7 +1712,7 @@ void LLViewerFetchedTexture::processTextureStats() } else { - U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048 + U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096 if(!mKnownDrawWidth || !mKnownDrawHeight || mFullWidth <= mKnownDrawWidth || mFullHeight <= mKnownDrawHeight) { if (mFullWidth > desired_size || mFullHeight > desired_size) @@ -3079,7 +3079,15 @@ void LLViewerLODTexture::processTextureStats() updateVirtualSize(); static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes", false); - + + { // restrict texture resolution to download based on RenderMaxTextureResolution + static LLCachedControl<U32> max_texture_resolution(gSavedSettings, "RenderMaxTextureResolution", 2048); + // sanity clamp debug setting to avoid settings hack shenanigans + F32 tex_res = (F32)llclamp((S32)max_texture_resolution, 512, 2048); + tex_res *= tex_res; + mMaxVirtualSize = llmin(mMaxVirtualSize, tex_res); + } + if (textures_fullres) { mDesiredDiscardLevel = 0; @@ -3089,7 +3097,7 @@ void LLViewerLODTexture::processTextureStats() { mDesiredDiscardLevel = 0; if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) - mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048 + mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096 } else if (mBoostLevel < LLGLTexture::BOOST_HIGH && mMaxVirtualSize <= 10.f) { @@ -3134,7 +3142,7 @@ void LLViewerLODTexture::processTextureStats() discard_level = floorf(discard_level); F32 min_discard = 0.f; - U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048 + U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096 if (mBoostLevel <= LLGLTexture::BOOST_SCULPTED) { desired_size = DESIRED_NORMAL_TEXTURE_SIZE; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 37e64dfc17..1459686f10 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -5311,7 +5311,7 @@ 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 LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubearray, S32 cubeIndex, S32 face, F32 near_clip, bool dynamic_render, bool useCustomClipPlane, LLPlane clipPlane) { // NOTE: implementation derived from LLFloater360Capture::capture360Images() and simpleSnapshot LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; @@ -5342,6 +5342,14 @@ BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubea camera->setOrigin(origin); camera->setNear(near_clip); + LLPlane previousClipPlane; + + if (useCustomClipPlane) + { + previousClipPlane = camera->getUserClipPlane(); + camera->setUserClipPlane(clipPlane); + } + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // stencil buffer is deprecated | GL_STENCIL_BUFFER_BIT); U32 dynamic_render_types[] = { @@ -5448,6 +5456,11 @@ BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubea gPipeline.resetDrawOrders(); mWorldViewRectRaw = window_rect; + + if (useCustomClipPlane) + { + camera->setUserClipPlane(previousClipPlane); + } // restore original view/camera/avatar settings settings *camera = saved_camera; diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index ccef006a07..ad634503ba 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -374,7 +374,8 @@ public: // index - cube index in the array to use (cube index, not face-layer) // 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 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))); // special implementation of simpleSnapshot for reflection maps diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 001fab7755..6c0691c6a9 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -34,41 +34,319 @@ #include "lltextureview.h" #include "llviewertexture.h" #include "llviewertexturelist.h" +#include "llfetchedgltfmaterial.h" +#include "llgltfmateriallist.h" #include "llviewerregion.h" #include "noise.h" #include "llregionhandle.h" // for from_region_handle #include "llviewercontrol.h" +extern LLColor4U MAX_WATER_COLOR; -F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac) +static const U32 BASE_SIZE = 128; + +namespace +{ + F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac) + { + // Not sure if this is the right math... + // Take weighted average of all four points (bilinear interpolation) + F32 result; + + const F32 inv_x_frac = 1.f - x_frac; + const F32 inv_y_frac = 1.f - y_frac; + result = inv_x_frac*inv_y_frac*v00 + + x_frac*inv_y_frac*v10 + + inv_x_frac*y_frac*v01 + + x_frac*y_frac*v11; + + return result; + } + + void unboost_minimap_texture(LLPointer<LLViewerFetchedTexture>& tex) + { + if (!tex) { return; } + tex->setBoostLevel(LLGLTexture::BOOST_NONE); + tex->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1); + + if (tex->getTextureState() == LLGLTexture::NO_DELETE) + { + tex->forceActive(); + } + } + + void unboost_minimap_material(LLPointer<LLFetchedGLTFMaterial>& mat) + { + if (!mat) { return; } + unboost_minimap_texture(mat->mBaseColorTexture); + unboost_minimap_texture(mat->mNormalTexture); + unboost_minimap_texture(mat->mMetallicRoughnessTexture); + unboost_minimap_texture(mat->mEmissiveTexture); + } +}; + +LLTerrainMaterials::LLTerrainMaterials() { - // Not sure if this is the right math... - // Take weighted average of all four points (bilinear interpolation) - F32 result; - - const F32 inv_x_frac = 1.f - x_frac; - const F32 inv_y_frac = 1.f - y_frac; - result = inv_x_frac*inv_y_frac*v00 - + x_frac*inv_y_frac*v10 - + inv_x_frac*y_frac*v01 - + x_frac*y_frac*v11; - - return result; + for (S32 i = 0; i < ASSET_COUNT; ++i) + { + mMaterialTexturesSet[i] = false; + } } +LLTerrainMaterials::~LLTerrainMaterials() +{ + for (S32 i = 0; i < ASSET_COUNT; ++i) + { + unboost_minimap_texture(mDetailTextures[i]); + unboost_minimap_material(mDetailMaterials[i]); + } +} -LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) : - LLViewerLayer(width, scale), - mParamsReady(FALSE) +BOOL LLTerrainMaterials::generateMaterials() { - mSurfacep = surfacep; + if (texturesReady(true, true)) + { + return TRUE; + } + + if (materialsReady(true, true)) + { + return TRUE; + } + + return FALSE; +} + +LLUUID LLTerrainMaterials::getDetailAssetID(S32 asset) +{ + llassert(mDetailTextures[asset] && mDetailMaterials[asset]); + // Assume both the the material and texture were fetched in the same way + // using the same UUID. However, we may not know at this point which one + // will load. + return mDetailTextures[asset] ? mDetailTextures[asset]->getID() : LLUUID::null; +} + +LLPointer<LLViewerFetchedTexture> fetch_terrain_texture(const LLUUID& id) +{ + if (id.isNull()) + { + return nullptr; + } + + LLPointer<LLViewerFetchedTexture> tex = LLViewerTextureManager::getFetchedTexture(id); + tex->setNoDelete(); + return tex; +} + +void LLTerrainMaterials::setDetailAssetID(S32 asset, const LLUUID& id) +{ + // *NOTE: If there were multiple terrain swatches using the same asset + // ID, the asset still in use will be temporarily unboosted. + // It will be boosted again during terrain rendering. + unboost_minimap_texture(mDetailTextures[asset]); + unboost_minimap_material(mDetailMaterials[asset]); + + // This is terrain texture, but we are not setting it as BOOST_TERRAIN + // since we will be manipulating it later as needed. + mDetailTextures[asset] = fetch_terrain_texture(id); + LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[asset]; + mat = id.isNull() ? nullptr : gGLTFMaterialList.getMaterial(id); + mMaterialTexturesSet[asset] = false; +} + +LLTerrainMaterials::Type LLTerrainMaterials::getMaterialType() +{ + LL_PROFILE_ZONE_SCOPED; + const BOOL use_textures = texturesReady(false, false) || !materialsReady(false, false); + return use_textures ? Type::TEXTURE : Type::PBR; +} + +bool LLTerrainMaterials::texturesReady(bool boost, bool strict) +{ + bool ready[ASSET_COUNT]; + // *NOTE: Calls to textureReady may boost textures. Do not early-return. + for (S32 i = 0; i < ASSET_COUNT; i++) + { + ready[i] = mDetailTextures[i].notNull() && textureReady(mDetailTextures[i], boost); + } + + bool one_ready = false; + for (S32 i = 0; i < ASSET_COUNT; i++) + { + const bool current_ready = ready[i]; + one_ready = one_ready || current_ready; + if (!current_ready && strict) + { + return false; + } + } + return one_ready; +} + +bool LLTerrainMaterials::materialsReady(bool boost, bool strict) +{ + bool ready[ASSET_COUNT]; + // *NOTE: Calls to materialReady may boost materials/textures. Do not early-return. + for (S32 i = 0; i < ASSET_COUNT; i++) + { + ready[i] = materialReady(mDetailMaterials[i], mMaterialTexturesSet[i], boost, strict); + } + +#if 1 + static LLCachedControl<bool> sRenderTerrainPBREnabled(gSavedSettings, "RenderTerrainPBREnabled", false); + static LLCachedControl<bool> sRenderTerrainPBRForce(gSavedSettings, "RenderTerrainPBRForce", false); + if (sRenderTerrainPBREnabled && sRenderTerrainPBRForce) + { + bool defined = true; + for (S32 i = 0; i < ASSET_COUNT; i++) + { + if (!mDetailMaterials[i]) + { + defined = false; + break; + } + } + if (defined) + { + return true; + } + } +#endif + + bool one_ready = false; + for (S32 i = 0; i < ASSET_COUNT; i++) + { + const bool current_ready = ready[i]; + one_ready = one_ready || current_ready; + if (!current_ready && strict) + { + return false; + } + } + return one_ready; +} + +// Boost the texture loading priority +// Return true when ready to use (i.e. texture is sufficiently loaded) +// static +bool LLTerrainMaterials::textureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost) +{ + llassert(tex); + if (!tex) { return false; } + + if (tex->getDiscardLevel() < 0) + { + if (boost) + { + tex->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail + tex->addTextureStats(BASE_SIZE*BASE_SIZE); + } + return false; + } + if ((tex->getDiscardLevel() != 0 && + (tex->getWidth() < BASE_SIZE || + tex->getHeight() < BASE_SIZE))) + { + if (boost) + { + S32 width = tex->getFullWidth(); + S32 height = tex->getFullHeight(); + S32 min_dim = llmin(width, height); + S32 ddiscard = 0; + while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) + { + ddiscard++; + min_dim /= 2; + } + tex->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail + tex->setMinDiscardLevel(ddiscard); + tex->addTextureStats(BASE_SIZE*BASE_SIZE); // priority + } + return false; + } + if (tex->getComponents() == 0) + { + return false; + } + return true; +} + +// Boost the loading priority of every known texture in the material +// Return true when ready to use +// static +bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bool &textures_set, bool boost, bool strict) +{ + if (!mat || !mat->isLoaded()) + { + return false; + } + + // Material is loaded, but textures may not be + if (!textures_set) + { + textures_set = true; + // *NOTE: These can sometimes be set to to nullptr due to + // updateTEMaterialTextures. For the sake of robustness, we emulate + // that fetching behavior by setting textures of null IDs to nullptr. + mat->mBaseColorTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]); + mat->mNormalTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]); + mat->mMetallicRoughnessTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]); + mat->mEmissiveTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]); + } + + // *NOTE: Calls to textureReady may boost textures. Do not early-return. + bool ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT]; + ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].isNull() || textureReady(mat->mBaseColorTexture, boost); + ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].isNull() || textureReady(mat->mNormalTexture, boost); + ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].isNull() || + textureReady(mat->mMetallicRoughnessTexture, boost); + ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].isNull() || textureReady(mat->mEmissiveTexture, boost); + + if (strict) + { + for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) + { + if (!ready[i]) + { + return false; + } + } + } + + return true; +} + +// static +const LLUUID (&LLVLComposition::getDefaultTextures())[ASSET_COUNT] +{ + const static LLUUID default_textures[LLVLComposition::ASSET_COUNT] = + { + TERRAIN_DIRT_DETAIL, + TERRAIN_GRASS_DETAIL, + TERRAIN_MOUNTAIN_DETAIL, + TERRAIN_ROCK_DETAIL + }; + return default_textures; +} + +LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) : + LLTerrainMaterials(), + LLViewerLayer(width, scale) +{ // Load Terrain Textures - Original ones - setDetailTextureID(0, TERRAIN_DIRT_DETAIL); - setDetailTextureID(1, TERRAIN_GRASS_DETAIL); - setDetailTextureID(2, TERRAIN_MOUNTAIN_DETAIL); - setDetailTextureID(3, TERRAIN_ROCK_DETAIL); + const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures(); + for (S32 i = 0; i < ASSET_COUNT; ++i) + { + setDetailAssetID(i, default_textures[i]); + } + + mSurfacep = surfacep; // Initialize the texture matrix to defaults. for (S32 i = 0; i < CORNER_COUNT; ++i) @@ -76,14 +354,12 @@ LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 mStartHeight[i] = gSavedSettings.getF32("TerrainColorStartHeight"); mHeightRange[i] = gSavedSettings.getF32("TerrainColorHeightRange"); } - mTexScaleX = 16.f; - mTexScaleY = 16.f; - mTexturesLoaded = FALSE; } LLVLComposition::~LLVLComposition() { + LLTerrainMaterials::~LLTerrainMaterials(); } @@ -92,20 +368,6 @@ void LLVLComposition::setSurface(LLSurface *surfacep) mSurfacep = surfacep; } - -void LLVLComposition::setDetailTextureID(S32 corner, const LLUUID& id) -{ - if(id.isNull()) - { - return; - } - // This is terrain texture, but we are not setting it as BOOST_TERRAIN - // since we will be manipulating it later as needed. - mDetailTextures[corner] = LLViewerTextureManager::getFetchedTexture(id); - mDetailTextures[corner]->setNoDelete() ; - mRawImages[corner] = NULL; -} - BOOL LLVLComposition::generateHeights(const F32 x, const F32 y, const F32 width, const F32 height) { @@ -149,10 +411,6 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y, const F32 noise_magnitude = 2.f; // Degree to which noise modulates composition layer (versus // simple height) - // Heights map into textures as 0-1 = first, 1-2 = second, etc. - // So we need to compress heights into this range. - const S32 NUM_TEXTURES = 4; - const F32 xyScaleInv = (1.f / xyScale); const F32 zScaleInv = (1.f / zScale); @@ -199,7 +457,7 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y, twiddle += turbulence2(vec, 2)*slope_squared; // High frequency component twiddle *= noise_magnitude; - F32 scaled_noisy_height = (height + twiddle - start_height) * F32(NUM_TEXTURES) / height_range; + F32 scaled_noisy_height = (height + twiddle - start_height) * F32(ASSET_COUNT) / height_range; scaled_noisy_height = llmax(0.f, scaled_noisy_height); scaled_noisy_height = llmin(3.f, scaled_noisy_height); @@ -209,49 +467,20 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y, return TRUE; } -static const U32 BASE_SIZE = 128; +LLTerrainMaterials gLocalTerrainMaterials; BOOL LLVLComposition::generateComposition() { - if (!mParamsReady) { // All the parameters haven't been set yet (we haven't gotten the message from the sim) return FALSE; } - for (S32 i = 0; i < 4; i++) - { - if (mDetailTextures[i]->getDiscardLevel() < 0) - { - mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail - mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE); - return FALSE; - } - if ((mDetailTextures[i]->getDiscardLevel() != 0 && - (mDetailTextures[i]->getWidth() < BASE_SIZE || - mDetailTextures[i]->getHeight() < BASE_SIZE))) - { - S32 width = mDetailTextures[i]->getFullWidth(); - S32 height = mDetailTextures[i]->getFullHeight(); - S32 min_dim = llmin(width, height); - S32 ddiscard = 0; - while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) - { - ddiscard++; - min_dim /= 2; - } - mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail - mDetailTextures[i]->setMinDiscardLevel(ddiscard); - mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE); // priority - return FALSE; - } - } - - return TRUE; + return LLTerrainMaterials::generateMaterials(); } -BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, +BOOL LLVLComposition::generateMinimapTileLand(const F32 x, const F32 y, const F32 width, const F32 height) { LL_PROFILE_ZONE_SCOPED @@ -259,8 +488,6 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, llassert(x >= 0.f); llassert(y >= 0.f); - LLTimer gen_timer; - /////////////////////////// // // Generate raw data arrays for surface textures @@ -268,52 +495,198 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, // // These have already been validated by generateComposition. - U8* st_data[4]; - S32 st_data_size[4]; // for debugging - - for (S32 i = 0; i < 4; i++) + U8* st_data[ASSET_COUNT]; + S32 st_data_size[ASSET_COUNT]; // for debugging + + const bool use_textures = getMaterialType() != LLTerrainMaterials::Type::PBR; + if (use_textures) + { + if (!texturesReady(true, true)) { return FALSE; } + } + else + { + if (!materialsReady(true, true)) { return FALSE; } + } + + for (S32 i = 0; i < ASSET_COUNT; i++) { if (mRawImages[i].isNull()) { // Read back a raw image for this discard level, if it exists - S32 min_dim = llmin(mDetailTextures[i]->getFullWidth(), mDetailTextures[i]->getFullHeight()); - S32 ddiscard = 0; - while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) - { - ddiscard++; - min_dim /= 2; + LLViewerFetchedTexture* tex; + LLViewerFetchedTexture* tex_emissive; // Can be null + bool has_base_color_factor; + bool has_emissive_factor; + bool has_alpha; + LLColor3 base_color_factor; + LLColor3 emissive_factor; + if (use_textures) + { + tex = mDetailTextures[i]; + tex_emissive = nullptr; + has_base_color_factor = false; + has_emissive_factor = false; + has_alpha = false; + llassert(tex); + } + else + { + tex = mDetailMaterials[i]->mBaseColorTexture; + tex_emissive = mDetailMaterials[i]->mEmissiveTexture; + base_color_factor = LLColor3(mDetailMaterials[i]->mBaseColor); + // *HACK: Treat alpha as black + base_color_factor *= (mDetailMaterials[i]->mBaseColor.mV[VW]); + emissive_factor = mDetailMaterials[i]->mEmissiveColor; + has_base_color_factor = (base_color_factor.mV[VX] != 1.f || + base_color_factor.mV[VY] != 1.f || + base_color_factor.mV[VZ] != 1.f); + has_emissive_factor = (emissive_factor.mV[VX] != 1.f || + emissive_factor.mV[VY] != 1.f || + emissive_factor.mV[VZ] != 1.f); + has_alpha = mDetailMaterials[i]->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_OPAQUE; + } + + if (!tex) { tex = LLViewerFetchedTexture::sWhiteImagep; } + // tex_emissive can be null, and then will be ignored + + S32 ddiscard = 0; + { + S32 min_dim = llmin(tex->getFullWidth(), tex->getFullHeight()); + while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL) + { + ddiscard++; + min_dim /= 2; + } + } + + S32 ddiscard_emissive = 0; + if (tex_emissive) + { + S32 min_dim_emissive = llmin(tex_emissive->getFullWidth(), tex_emissive->getFullHeight()); + while (min_dim_emissive > BASE_SIZE && ddiscard_emissive < MAX_DISCARD_LEVEL) + { + ddiscard_emissive++; + min_dim_emissive /= 2; + } } - BOOL delete_raw = (mDetailTextures[i]->reloadRawImage(ddiscard) != NULL) ; - if(mDetailTextures[i]->getRawImageLevel() != ddiscard)//raw iamge is not ready, will enter here again later. + // *NOTE: It is probably safe to call destroyRawImage no matter + // what, as LLViewerFetchedTexture::mRawImage is managed by + // LLPointer and not modified with the rare exception of + // icons (see BOOST_ICON). Nevertheless, gate this fix for now, as + // it may have unintended consequences on texture loading. + // We may want to also set the boost level in setDetailAssetID, but + // that is not guaranteed to work if a texture is loaded on an object + // before being loaded as terrain, so we will need this fix + // regardless. + static LLCachedControl<bool> sRenderTerrainPBREnabled(gSavedSettings, "RenderTerrainPBREnabled", false); + BOOL delete_raw = (tex->reloadRawImage(ddiscard) != NULL || sRenderTerrainPBREnabled); + BOOL delete_raw_emissive = (tex_emissive && + (tex_emissive->reloadRawImage(ddiscard_emissive) != NULL || sRenderTerrainPBREnabled)); + + if(tex->getRawImageLevel() != ddiscard) { - if (mDetailTextures[i]->getFetchPriority() <= 0.0f && !mDetailTextures[i]->hasSavedRawImage()) + // Raw image is not ready, will enter here again later. + if (tex->getFetchPriority() <= 0.0f && !tex->hasSavedRawImage()) { - mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_MAP); - mDetailTextures[i]->forceToRefetchTexture(ddiscard); + tex->setBoostLevel(LLGLTexture::BOOST_TERRAIN); + tex->forceToRefetchTexture(ddiscard); } if(delete_raw) { - mDetailTextures[i]->destroyRawImage() ; + tex->destroyRawImage() ; } - LL_DEBUGS("Terrain") << "cached raw data for terrain detail texture is not ready yet: " << mDetailTextures[i]->getID() << " Discard: " << ddiscard << LL_ENDL; return FALSE; } + if (tex_emissive) + { + if(tex_emissive->getRawImageLevel() != ddiscard_emissive) + { + // Raw image is not ready, will enter here again later. + if (tex_emissive->getFetchPriority() <= 0.0f && !tex_emissive->hasSavedRawImage()) + { + tex_emissive->setBoostLevel(LLGLTexture::BOOST_TERRAIN); + tex_emissive->forceToRefetchTexture(ddiscard_emissive); + } + + if(delete_raw_emissive) + { + tex_emissive->destroyRawImage() ; + } + return FALSE; + } + } - mRawImages[i] = mDetailTextures[i]->getRawImage() ; + mRawImages[i] = tex->getRawImage() ; if(delete_raw) { - mDetailTextures[i]->destroyRawImage() ; + tex->destroyRawImage() ; } - if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE || - mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE || - mDetailTextures[i]->getComponents() != 3) + + // *TODO: This isn't quite right for PBR: + // 1) It does not convert the color images from SRGB to linear + // before mixing (which will always require copying the image). + // 2) It mixes emissive and base color before mixing terrain + // materials, but it should be the other way around + // 3) The composite function used to put emissive into base color + // is not an alpha blend. + // Long-term, we should consider a method that is more + // maintainable. Shaders, perhaps? Bake shaders to textures? + LLPointer<LLImageRaw> raw_emissive; + if (tex_emissive) + { + raw_emissive = tex_emissive->getRawImage(); + if (has_emissive_factor || + tex_emissive->getWidth(tex_emissive->getRawImageLevel()) != BASE_SIZE || + tex_emissive->getHeight(tex_emissive->getRawImageLevel()) != BASE_SIZE || + tex_emissive->getComponents() != 4) + { + LLPointer<LLImageRaw> newraw_emissive = new LLImageRaw(BASE_SIZE, BASE_SIZE, 4); + // Copy RGB, leave alpha alone (set to opaque by default) + newraw_emissive->copy(mRawImages[i]); + if (has_emissive_factor) + { + newraw_emissive->tint(emissive_factor); + } + raw_emissive = newraw_emissive; + } + } + if (has_base_color_factor || + raw_emissive || + has_alpha || + tex->getWidth(tex->getRawImageLevel()) != BASE_SIZE || + tex->getHeight(tex->getRawImageLevel()) != BASE_SIZE || + tex->getComponents() != 3) { LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3); + if (has_alpha) + { + // Approximate the water underneath terrain alpha with solid water color + newraw->clear( + MAX_WATER_COLOR.mV[VX], + MAX_WATER_COLOR.mV[VY], + MAX_WATER_COLOR.mV[VZ], + 255); + } newraw->composite(mRawImages[i]); + if (has_base_color_factor) + { + newraw->tint(base_color_factor); + } + // Apply emissive texture + if (raw_emissive) + { + newraw->composite(raw_emissive); + } + mRawImages[i] = newraw; // deletes old } + + if (delete_raw_emissive) + { + tex_emissive->destroyRawImage(); + } } st_data[i] = mRawImages[i]->getData(); st_data_size[i] = mRawImages[i]->getDataSize(); @@ -333,12 +706,12 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, if (x_end > mWidth) { - LL_WARNS("Terrain") << "x end > width" << LL_ENDL; + llassert(false); x_end = mWidth; } if (y_end > mWidth) { - LL_WARNS("Terrain") << "y end > width" << LL_ENDL; + llassert(false); y_end = mWidth; } @@ -368,7 +741,7 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, if (tex_comps != st_comps) { - LL_WARNS("Terrain") << "Base texture comps != input texture comps" << LL_ENDL; + llassert(false); return FALSE; } @@ -459,29 +832,34 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, } texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin); - for (S32 i = 0; i < 4; i++) - { - // Un-boost detatil textures (will get re-boosted if rendering in high detail) - mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_NONE); - mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1); - } + // Un-boost detail textures (will get re-boosted if rendering in high detail) + for (S32 i = 0; i < ASSET_COUNT; i++) + { + unboost_minimap_texture(mDetailTextures[i]); + } + + // Un-boost textures for each detail material (will get re-boosted if rendering in high detail) + for (S32 i = 0; i < ASSET_COUNT; i++) + { + unboost_minimap_material(mDetailMaterials[i]); + } return TRUE; } -LLUUID LLVLComposition::getDetailTextureID(S32 corner) -{ - return mDetailTextures[corner]->getID(); -} - -LLViewerFetchedTexture* LLVLComposition::getDetailTexture(S32 corner) +F32 LLVLComposition::getStartHeight(S32 corner) { - return mDetailTextures[corner]; + return mStartHeight[corner]; } -F32 LLVLComposition::getStartHeight(S32 corner) +void LLVLComposition::setDetailAssetID(S32 asset, const LLUUID& id) { - return mStartHeight[corner]; + if (id.isNull()) + { + return; + } + LLTerrainMaterials::setDetailAssetID(asset, id); + mRawImages[asset] = NULL; } void LLVLComposition::setStartHeight(S32 corner, const F32 start_height) diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 2dd04ac5a5..7397ff1e8d 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -28,13 +28,65 @@ #define LL_LLVLCOMPOSITION_H #include "llviewerlayer.h" -#include "llviewertexture.h" +#include "llpointer.h" + +#include "llimage.h" class LLSurface; -class LLVLComposition : public LLViewerLayer +class LLViewerFetchedTexture; +class LLFetchedGLTFMaterial; + +class LLTerrainMaterials { public: + friend class LLDrawPoolTerrain; + + LLTerrainMaterials(); + virtual ~LLTerrainMaterials(); + + // Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc. + // So we need to compress heights into this range. + static const S32 ASSET_COUNT = 4; + + enum class Type + { + TEXTURE, + PBR, + COUNT + }; + + BOOL generateMaterials(); + + virtual LLUUID getDetailAssetID(S32 asset); + virtual void setDetailAssetID(S32 asset, const LLUUID& id); + Type getMaterialType(); + bool texturesReady(bool boost, bool strict); + // strict = true -> all materials must be sufficiently loaded + // strict = false -> at least one material must be loaded + bool materialsReady(bool boost, bool strict); + +protected: + static bool textureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost); + // strict = true -> all materials must be sufficiently loaded + // strict = false -> at least one material must be loaded + static bool materialReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict); + LLPointer<LLViewerFetchedTexture> mDetailTextures[ASSET_COUNT]; + LLPointer<LLFetchedGLTFMaterial> mDetailMaterials[ASSET_COUNT]; + bool mMaterialTexturesSet[ASSET_COUNT]; +}; + +// Local materials to override all regions +extern LLTerrainMaterials gLocalTerrainMaterials; + +class LLVLComposition : public LLTerrainMaterials, public LLViewerLayer +{ +public: + // Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc. + // So we need to compress heights into this range. + static const S32 ASSET_COUNT = 4; + static const LLUUID (&getDefaultTextures())[ASSET_COUNT]; + LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale); /*virtual*/ ~LLVLComposition(); @@ -44,7 +96,7 @@ public: BOOL generateHeights(const F32 x, const F32 y, const F32 width, const F32 height); BOOL generateComposition(); // Generate texture from composition values. - BOOL generateTexture(const F32 x, const F32 y, const F32 width, const F32 height); + BOOL generateMinimapTileLand(const F32 x, const F32 y, const F32 width, const F32 height); // Use these as indeces ito the get/setters below that use 'corner' enum ECorner @@ -55,12 +107,11 @@ public: NORTHEAST = 3, CORNER_COUNT = 4 }; - LLUUID getDetailTextureID(S32 corner); - LLViewerFetchedTexture* getDetailTexture(S32 corner); + + void setDetailAssetID(S32 asset, const LLUUID& id) override; F32 getStartHeight(S32 corner); F32 getHeightRange(S32 corner); - void setDetailTextureID(S32 corner, const LLUUID& id); void setStartHeight(S32 corner, F32 start_height); void setHeightRange(S32 corner, F32 range); @@ -68,19 +119,21 @@ public: friend class LLDrawPoolTerrain; void setParamsReady() { mParamsReady = TRUE; } BOOL getParamsReady() const { return mParamsReady; } + protected: - BOOL mParamsReady; + static bool textureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost = false); + static bool materialReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost = false); + + BOOL mParamsReady = FALSE; LLSurface *mSurfacep; - BOOL mTexturesLoaded; - LLPointer<LLViewerFetchedTexture> mDetailTextures[CORNER_COUNT]; - LLPointer<LLImageRaw> mRawImages[CORNER_COUNT]; + LLPointer<LLImageRaw> mRawImages[LLTerrainMaterials::ASSET_COUNT]; F32 mStartHeight[CORNER_COUNT]; F32 mHeightRange[CORNER_COUNT]; - F32 mTexScaleX; - F32 mTexScaleY; + F32 mTexScaleX = 16.f; + F32 mTexScaleY = 16.f; }; #endif //LL_LLVLCOMPOSITION_H diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 9579a5e4b1..2ba7a76b08 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4689,7 +4689,7 @@ bool LLVOAvatar::updateCharacter(LLAgent &agent) mSpeed = speed; // update animations - if (!visible) + if (!visible && !isSelf()) // NOTE: never do a "hidden update" for self avatar as it interrupts controller processing { updateMotions(LLCharacter::HIDDEN_UPDATE); } @@ -5241,9 +5241,6 @@ U32 LLVOAvatar::renderRigid() return 0; } - bool should_alpha_mask = shouldAlphaMask(); - LLGLState test(GL_ALPHA_TEST, should_alpha_mask); - if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar()) { LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 7b24b9ee02..75a17cebe9 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1062,7 +1062,7 @@ void LLVOAvatarSelf::updateAttachmentVisibility(U32 camera_mode) switch (camera_mode) { case CAMERA_MODE_MOUSELOOK: - if (LLVOAvatar::sVisibleInFirstPerson && attachment->getVisibleInFirstPerson()) + if ((LLVOAvatar::sVisibleInFirstPerson && attachment->getVisibleInFirstPerson()) || gPipeline.mHeroProbeManager.isMirrorPass()) { attachment->setAttachmentVisibility(TRUE); } diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 15fabf0414..69b9476d38 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -39,6 +39,7 @@ #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llvlcomposition.h" +#include "llvolume.h" #include "llvovolume.h" #include "pipeline.h" #include "llspatialpartition.h" @@ -213,6 +214,7 @@ BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable) void LLVOSurfacePatch::updateFaceSize(S32 idx) { + LL_PROFILE_ZONE_SCOPED; if (idx != 0) { LL_WARNS() << "Terrain partition requested invalid face!!!" << LL_ENDL; @@ -241,48 +243,50 @@ BOOL LLVOSurfacePatch::updateLOD() return TRUE; } -void LLVOSurfacePatch::getGeometry(LLStrider<LLVector3> &verticesp, - LLStrider<LLVector3> &normalsp, - LLStrider<LLVector2> &texCoords0p, - LLStrider<LLVector2> &texCoords1p, - LLStrider<U16> &indicesp) +void LLVOSurfacePatch::getTerrainGeometry(LLStrider<LLVector3> &verticesp, + LLStrider<LLVector3> &normalsp, + LLStrider<LLVector2> &texCoords0p, + LLStrider<LLVector2> &texCoords1p, + LLStrider<U16> &indicesp) { LLFace* facep = mDrawable->getFace(0); - if (facep) - { - U32 index_offset = facep->getGeomIndex(); - - updateMainGeometry(facep, - verticesp, - normalsp, - texCoords0p, - texCoords1p, - indicesp, - index_offset); - updateNorthGeometry(facep, - verticesp, - normalsp, - texCoords0p, - texCoords1p, - indicesp, - index_offset); - updateEastGeometry(facep, - verticesp, - normalsp, - texCoords0p, - texCoords1p, - indicesp, - index_offset); - } + if (!facep) + { + return; + } + + U32 index_offset = facep->getGeomIndex(); + + updateMainGeometry(facep, + verticesp, + normalsp, + texCoords0p, + texCoords1p, + indicesp, + index_offset); + updateNorthGeometry(facep, + verticesp, + normalsp, + texCoords0p, + texCoords1p, + indicesp, + index_offset); + updateEastGeometry(facep, + verticesp, + normalsp, + texCoords0p, + texCoords1p, + indicesp, + index_offset); } void LLVOSurfacePatch::updateMainGeometry(LLFace *facep, - LLStrider<LLVector3> &verticesp, - LLStrider<LLVector3> &normalsp, - LLStrider<LLVector2> &texCoords0p, - LLStrider<LLVector2> &texCoords1p, - LLStrider<U16> &indicesp, - U32 &index_offset) + LLStrider<LLVector3> &verticesp, + LLStrider<LLVector3> &normalsp, + LLStrider<LLVector2> &texCoords0p, + LLStrider<LLVector2> &texCoords1p, + LLStrider<U16> &indicesp, + U32 &index_offset) { S32 i, j, x, y; @@ -381,12 +385,12 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep, void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, - LLStrider<LLVector3> &verticesp, - LLStrider<LLVector3> &normalsp, - LLStrider<LLVector2> &texCoords0p, - LLStrider<LLVector2> &texCoords1p, - LLStrider<U16> &indicesp, - U32 &index_offset) + LLStrider<LLVector3> &verticesp, + LLStrider<LLVector3> &normalsp, + LLStrider<LLVector2> &texCoords0p, + LLStrider<LLVector2> &texCoords1p, + LLStrider<U16> &indicesp, + U32 &index_offset) { S32 i, x, y; @@ -571,12 +575,12 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, } void LLVOSurfacePatch::updateEastGeometry(LLFace *facep, - LLStrider<LLVector3> &verticesp, - LLStrider<LLVector3> &normalsp, - LLStrider<LLVector2> &texCoords0p, - LLStrider<LLVector2> &texCoords1p, - LLStrider<U16> &indicesp, - U32 &index_offset) + LLStrider<LLVector3> &verticesp, + LLStrider<LLVector3> &normalsp, + LLStrider<LLVector2> &texCoords0p, + LLStrider<LLVector2> &texCoords1p, + LLStrider<U16> &indicesp, + U32 &index_offset) { S32 i, x, y; @@ -982,6 +986,49 @@ LLTerrainPartition::LLTerrainPartition(LLViewerRegion* regionp) mPartitionType = LLViewerRegion::PARTITION_TERRAIN; } +// Do not add vertices; honor strict vertex count specified by strider_vertex_count +void gen_terrain_tangents(U16 strider_vertex_count, + U32 strider_index_count, + LLStrider<LLVector3> &verticesp, + LLStrider<LLVector3> &normalsp, + LLStrider<LLVector4a> &tangentsp, + LLStrider<LLVector2> &texCoords0p, + LLStrider<U16> &indicesp) +{ + LL_PROFILE_ZONE_SCOPED + + LLVector4a *vertices = new LLVector4a[strider_vertex_count]; + LLVector4a *normals = new LLVector4a[strider_vertex_count]; + LLVector4a *tangents = new LLVector4a[strider_vertex_count]; + std::vector<LLVector2> texcoords(strider_vertex_count); + std::vector<U16> indices(strider_index_count); + + for (U16 v = 0; v < strider_vertex_count; ++v) + { + F32 *vert = verticesp[v].mV; + vertices[v] = LLVector4a(vert[0], vert[1], vert[2], 1.f); + F32 *n = normalsp[v].mV; + normals[v] = LLVector4a(n[0], n[1], n[2], 1.f); + tangents[v] = tangentsp[v]; + texcoords[v] = texCoords0p[v]; + } + for (U32 i = 0; i < strider_index_count; ++i) + { + indices[i] = indicesp[i]; + } + + LLCalculateTangentArray(strider_vertex_count, vertices, normals, texcoords.data(), strider_index_count / 3, indices.data(), tangents); + + for (U16 v = 0; v < strider_vertex_count; ++v) + { + tangentsp[v] = tangents[v]; + } + + delete[] vertices; + delete[] normals; + delete[] tangents; +} + void LLTerrainPartition::getGeometry(LLSpatialGroup* group) { LL_PROFILE_ZONE_SCOPED; @@ -989,35 +1036,57 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group) LLVertexBuffer* buffer = group->mVertexBuffer; //get vertex buffer striders - LLStrider<LLVector3> vertices; - LLStrider<LLVector3> normals; - LLStrider<LLVector2> texcoords2; - LLStrider<LLVector2> texcoords; - LLStrider<U16> indices; - - llassert_always(buffer->getVertexStrider(vertices)); - llassert_always(buffer->getNormalStrider(normals)); - llassert_always(buffer->getTexCoord0Strider(texcoords)); - llassert_always(buffer->getTexCoord1Strider(texcoords2)); - llassert_always(buffer->getIndexStrider(indices)); - - U32 indices_index = 0; - U32 index_offset = 0; - - for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i) - { - LLFace* facep = *i; - - facep->setIndicesIndex(indices_index); - facep->setGeomIndex(index_offset); - facep->setVertexBuffer(buffer); - - LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject(); - patchp->getGeometry(vertices, normals, texcoords, texcoords2, indices); - - indices_index += facep->getIndicesCount(); - index_offset += facep->getGeomCount(); - } + LLStrider<LLVector3> vertices_start; + LLStrider<LLVector3> normals_start; + LLStrider<LLVector4a> tangents_start; + LLStrider<LLVector2> texcoords_start; + LLStrider<LLVector2> texcoords2_start; + LLStrider<U16> indices_start; + + llassert_always(buffer->getVertexStrider(vertices_start)); + llassert_always(buffer->getNormalStrider(normals_start)); + llassert_always(buffer->getTangentStrider(tangents_start)); + llassert_always(buffer->getTexCoord0Strider(texcoords_start)); + llassert_always(buffer->getTexCoord1Strider(texcoords2_start)); + llassert_always(buffer->getIndexStrider(indices_start)); + + U32 indices_index = 0; + U32 index_offset = 0; + + { + LLStrider<LLVector3> vertices = vertices_start; + LLStrider<LLVector3> normals = normals_start; + LLStrider<LLVector2> texcoords = texcoords_start; + LLStrider<LLVector2> texcoords2 = texcoords2_start; + LLStrider<U16> indices = indices_start; + + for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i) + { + LLFace* facep = *i; + + facep->setIndicesIndex(indices_index); + facep->setGeomIndex(index_offset); + facep->setVertexBuffer(buffer); + + LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject(); + patchp->getTerrainGeometry(vertices, normals, texcoords, texcoords2, indices); + + indices_index += facep->getIndicesCount(); + index_offset += facep->getGeomCount(); + } + } + + const bool has_tangents = tangents_start.get() != nullptr; + if (has_tangents) + { + LLStrider<LLVector3> vertices = vertices_start; + LLStrider<LLVector3> normals = normals_start; + LLStrider<LLVector4a> tangents = tangents_start; + LLStrider<LLVector2> texcoords = texcoords_start; + LLStrider<U16> indices = indices_start; + + gen_terrain_tangents(index_offset, indices_index, vertices, normals, tangents, texcoords, indices); + } buffer->unmapBuffer(); mFaceList.clear(); diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h index aed67162d1..a3dcb945d1 100644 --- a/indra/newview/llvosurfacepatch.h +++ b/indra/newview/llvosurfacepatch.h @@ -63,11 +63,11 @@ public: /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ BOOL updateLOD(); /*virtual*/ void updateFaceSize(S32 idx); - void getGeometry(LLStrider<LLVector3> &verticesp, - LLStrider<LLVector3> &normalsp, - LLStrider<LLVector2> &texCoords0p, - LLStrider<LLVector2> &texCoords1p, - LLStrider<U16> &indicesp); + void getTerrainGeometry(LLStrider<LLVector3> &verticesp, + LLStrider<LLVector3> &normalsp, + LLStrider<LLVector2> &texCoords0p, + LLStrider<LLVector2> &texCoords1p, + LLStrider<U16> &indicesp); /*virtual*/ void updateTextures(); /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 57a7d6a90b..bcecd3a7fe 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -296,6 +296,11 @@ void LLVOVolume::markDead() { mLightTexture->removeVolume(LLRender::LIGHT_TEX, this); } + + if (mIsHeroProbe) + { + gPipeline.mHeroProbeManager.unregisterViewerObject(this); + } } LLViewerObject::markDead(); @@ -996,7 +1001,7 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline) { updateReflectionProbePtr(); } - + updateRadius(); bool force_update = true; // avoid non-alpha mDistance update being optimized away mDrawable->updateDistance(*LLViewerCamera::getInstance(), force_update); @@ -3408,6 +3413,22 @@ bool LLVOVolume::setReflectionProbeIsDynamic(bool is_dynamic) return false; } +bool LLVOVolume::setReflectionProbeIsMirror(bool is_mirror) +{ + LLReflectionProbeParams *param_block = (LLReflectionProbeParams *) getParameterEntry(LLNetworkData::PARAMS_REFLECTION_PROBE); + if (param_block) + { + if (param_block->getIsMirror() != is_mirror) + { + param_block->setIsMirror(is_mirror); + parameterChanged(LLNetworkData::PARAMS_REFLECTION_PROBE, true); + return true; + } + } + + return false; +} + F32 LLVOVolume::getReflectionProbeAmbiance() const { const LLReflectionProbeParams* param_block = (const LLReflectionProbeParams*)getParameterEntry(LLNetworkData::PARAMS_REFLECTION_PROBE); @@ -3456,6 +3477,18 @@ bool LLVOVolume::getReflectionProbeIsDynamic() const return false; } +bool LLVOVolume::getReflectionProbeIsMirror() const +{ + const LLReflectionProbeParams *param_block = + (const LLReflectionProbeParams *) getParameterEntry(LLNetworkData::PARAMS_REFLECTION_PROBE); + if (param_block) + { + return param_block->getIsMirror(); + } + + return false; +} + U32 LLVOVolume::getVolumeInterfaceID() const { if (mVolumeImpl) @@ -4375,14 +4408,30 @@ void LLVOVolume::updateReflectionProbePtr() { if (isReflectionProbe()) { - if (mReflectionProbe.isNull()) + if (mReflectionProbe.isNull() && !getReflectionProbeIsMirror()) { mReflectionProbe = gPipeline.mReflectionMapManager.registerViewerObject(this); } + else if (mReflectionProbe.isNull() && getReflectionProbeIsMirror()) + { + // Geenz: This is a special case - what we want here is a hero probe. + // What we want to do here is instantiate a hero probe from the hero probe manager. + + if (!mIsHeroProbe) + mIsHeroProbe = gPipeline.mHeroProbeManager.registerViewerObject(this); + } } - else if (mReflectionProbe.notNull()) + else if (mReflectionProbe.notNull() || getReflectionProbeIsMirror()) { - mReflectionProbe = nullptr; + if (mReflectionProbe.notNull()) + { + mReflectionProbe = nullptr; + } + + if (getReflectionProbeIsMirror()) + { + gPipeline.mHeroProbeManager.unregisterViewerObject(this); + } } } @@ -4557,7 +4606,7 @@ LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32 *face_hitp, - LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) + LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) { if (!mbCanSelect @@ -4765,7 +4814,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& } } } - + return ret; } diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index aadc1fbcf3..cac09609b3 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -301,12 +301,14 @@ public: bool setReflectionProbeNearClip(F32 near_clip); bool setReflectionProbeIsBox(bool is_box); bool setReflectionProbeIsDynamic(bool is_dynamic); + bool setReflectionProbeIsMirror(bool is_mirror); BOOL isReflectionProbe() const override; F32 getReflectionProbeAmbiance() const; F32 getReflectionProbeNearClip() const; bool getReflectionProbeIsBox() const; bool getReflectionProbeIsDynamic() const; + bool getReflectionProbeIsMirror() const; // Flexible Objects U32 getVolumeInterfaceID() const; diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index b816f9a3b5..c4f9be5fcd 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -411,34 +411,20 @@ private: return parseValues(status_string, "", param); } - /** - * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map. - * @param key_pfx Used to describe a given key in log messages. At top - * level, pass "". When parsing an options array, pass the top-level key - * name of the array plus the index of the array entry; to this we'll - * append the subkey of interest. - * @param param XMLRPC_VALUE iterator. At top level, pass - * XMLRPC_RequestGetData(XMLRPC_REQUEST). - */ - LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param) + LLSD parseValue(std::string& status_string, const std::string& key, const std::string& key_pfx, XMLRPC_VALUE param) { - LLSD responses; - for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current; - current = XMLRPC_VectorNext(param)) + LLSD response; + + XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(param); + switch (type) { - std::string key(XMLRPC_GetValueID(current)); - LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL; - XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current); - switch (type) - { case xmlrpc_type_empty: LL_INFOS("LLXMLRPCListener") << "Empty result for key " << key_pfx << key << LL_ENDL; - responses.insert(key, LLSD()); break; case xmlrpc_type_base64: { - S32 len = XMLRPC_GetValueStringLen(current); - const char* buf = XMLRPC_GetValueBase64(current); + S32 len = XMLRPC_GetValueStringLen(param); + const char* buf = XMLRPC_GetValueBase64(param); if ((len > 0) && buf) { // During implementation this code was not tested @@ -449,49 +435,44 @@ private: LLSD::Binary data; data.resize(len); memcpy((void*)&data[0], (void*)buf, len); - responses.insert(key, data); + response = data; } else { LL_WARNS("LLXMLRPCListener") << "Potentially malformed xmlrpc_type_base64 for key " << key_pfx << key << LL_ENDL; - responses.insert(key, LLSD()); } break; } case xmlrpc_type_boolean: { - LLSD::Boolean val(XMLRPC_GetValueBoolean(current)); - LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; - responses.insert(key, val); + response = LLSD::Boolean(XMLRPC_GetValueBoolean(param)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL; break; } case xmlrpc_type_datetime: { - std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(current)); + std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(param)); LL_DEBUGS("LLXMLRPCListener") << "val: " << iso8601_date << LL_ENDL; - responses.insert(key, LLSD::Date(iso8601_date)); + response = LLSD::Date(iso8601_date); break; } case xmlrpc_type_double: { - LLSD::Real val(XMLRPC_GetValueDouble(current)); - LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; - responses.insert(key, val); + response = LLSD::Real(XMLRPC_GetValueDouble(param)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL; break; } case xmlrpc_type_int: { - LLSD::Integer val(XMLRPC_GetValueInt(current)); - LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; - responses.insert(key, val); + response = LLSD::Integer(XMLRPC_GetValueInt(param)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL; break; } case xmlrpc_type_string: { - LLSD::String val(XMLRPC_GetValueString(current)); - LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL; - responses.insert(key, val); + response = LLSD::String(XMLRPC_GetValueString(param)); + LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL; break; } case xmlrpc_type_mixed: @@ -501,8 +482,8 @@ private: // recursively parsing each submap and collecting them. LLSD array; int i = 0; // for descriptive purposes - for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row; - row = XMLRPC_VectorNext(current), ++i) + for (XMLRPC_VALUE row = XMLRPC_VectorRewind(param); row; + row = XMLRPC_VectorNext(param), ++i) { // Recursive call. For the lower-level key_pfx, if 'key' // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the @@ -510,21 +491,21 @@ private: // "foo[0]:bar", and so forth. // Parse the scalar subkey/value pairs from this array // entry into a temp submap. Collect such submaps in 'array'. - array.append(parseValues(status_string, + + array.append(parseValue(status_string, "", STRINGIZE(key_pfx << key << '[' << i << "]:"), row)); } // Having collected an 'array' of 'submap's, insert that whole // 'array' as the value of this 'key'. - responses.insert(key, array); + response = array; break; } case xmlrpc_type_struct: { - LLSD submap = parseValues(status_string, + response = parseValues(status_string, STRINGIZE(key_pfx << key << ':'), - current); - responses.insert(key, submap); + param); break; } case xmlrpc_type_none: // Not expected @@ -532,9 +513,30 @@ private: // whoops - unrecognized type LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key " << key_pfx << key << LL_ENDL; - responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>')); + response = STRINGIZE("<bad XMLRPC type " << type << '>'); status_string = "BadType"; - } + } + return response; + } + + /** + * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map. + * @param key_pfx Used to describe a given key in log messages. At top + * level, pass "". When parsing an options array, pass the top-level key + * name of the array plus the index of the array entry; to this we'll + * append the subkey of interest. + * @param param XMLRPC_VALUE iterator. At top level, pass + * XMLRPC_RequestGetData(XMLRPC_REQUEST). + */ + LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param) + { + LLSD responses; + for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current; + current = XMLRPC_VectorNext(param)) + { + std::string key(XMLRPC_GetValueID(current)); + LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL; + responses.insert(key, parseValue(status_string, key, key_pfx, current)); } return responses; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index f5f2ba52bf..ea40365c97 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -112,6 +112,7 @@ #include "llscenemonitor.h" #include "llprogressview.h" #include "llcleanup.h" +#include "gltfscenemanager.h" #include "llenvironment.h" #include "llsettingsvo.h" @@ -198,8 +199,13 @@ F32 LLPipeline::RenderScreenSpaceReflectionDepthRejectBias; F32 LLPipeline::RenderScreenSpaceReflectionAdaptiveStepMultiplier; S32 LLPipeline::RenderScreenSpaceReflectionGlossySamples; S32 LLPipeline::RenderBufferVisualization; +bool LLPipeline::RenderMirrors; +S32 LLPipeline::RenderHeroProbeUpdateRate; +S32 LLPipeline::RenderHeroProbeConservativeUpdateMultiplier; LLTrace::EventStatHandle<S64> LLPipeline::sStatBatchSize("renderbatchsize"); +const U32 LLPipeline::MAX_BAKE_WIDTH = 512; + const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f; const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f; const F32 ALPHA_BLEND_CUTOFF = 0.598f; @@ -555,6 +561,9 @@ void LLPipeline::init() connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionAdaptiveStepMultiplier"); connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionGlossySamples"); connectRefreshCachedSettingsSafe("RenderBufferVisualization"); + connectRefreshCachedSettingsSafe("RenderMirrors"); + connectRefreshCachedSettingsSafe("RenderHeroProbeUpdateRate"); + connectRefreshCachedSettingsSafe("RenderHeroProbeConservativeUpdateMultiplier"); gSavedSettings.getControl("RenderAutoHideSurfaceAreaLimit")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); } @@ -638,6 +647,7 @@ void LLPipeline::cleanup() mCubeVB = NULL; mReflectionMapManager.cleanup(); + mHeroProbeManager.cleanup(); } //============================================================================ @@ -764,13 +774,31 @@ LLPipeline::eFBOStatus LLPipeline::doAllocateScreenBuffer(U32 resX, U32 resY) bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; - if (mRT == &mMainRT && sReflectionProbesEnabled) + if (mRT == &mMainRT) { // hacky -- allocate auxillary buffer + gCubeSnapshot = TRUE; mReflectionMapManager.initReflectionMaps(); + mHeroProbeManager.initReflectionMaps(); + + if (sReflectionProbesEnabled) + { + gCubeSnapshot = TRUE; + mReflectionMapManager.initReflectionMaps(); + } + mRT = &mAuxillaryRT; U32 res = mReflectionMapManager.mProbeResolution * 4; //multiply by 4 because probes will be 16x super sampled allocateScreenBuffer(res, res, samples); + + if (RenderMirrors) + { + mHeroProbeManager.initReflectionMaps(); + res = mHeroProbeManager.mProbeResolution; // We also scale the hero probe RT to the probe res since we don't super sample it. + mRT = &mHeroProbeRT; + allocateScreenBuffer(res, res, samples); + } + mRT = &mMainRT; gCubeSnapshot = FALSE; } @@ -1043,6 +1071,15 @@ void LLPipeline::refreshCachedSettings() RenderScreenSpaceReflectionAdaptiveStepMultiplier = gSavedSettings.getF32("RenderScreenSpaceReflectionAdaptiveStepMultiplier"); RenderScreenSpaceReflectionGlossySamples = gSavedSettings.getS32("RenderScreenSpaceReflectionGlossySamples"); RenderBufferVisualization = gSavedSettings.getS32("RenderBufferVisualization"); + if (gSavedSettings.getBOOL("RenderMirrors") != (BOOL)RenderMirrors) + { + RenderMirrors = gSavedSettings.getBOOL("RenderMirrors"); + LLViewerShaderMgr::instance()->clearShaderCache(); + LLViewerShaderMgr::instance()->setShaders(); + } + RenderHeroProbeUpdateRate = gSavedSettings.getS32("RenderHeroProbeUpdateRate"); + RenderHeroProbeConservativeUpdateMultiplier = gSavedSettings.getS32("RenderHeroProbeConservativeUpdateMultiplier"); + sReflectionProbesEnabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderReflectionsEnabled") && gSavedSettings.getBOOL("RenderReflectionsEnabled"); RenderSpotLight = nullptr; @@ -1072,7 +1109,6 @@ void LLPipeline::releaseGLBuffers() releaseLUTBuffers(); mWaterDis.release(); - mBake.release(); mSceneMap.release(); @@ -1118,6 +1154,12 @@ void LLPipeline::releaseScreenBuffers() mRT->fxaaBuffer.release(); mRT->deferredScreen.release(); mRT->deferredLight.release(); + + mHeroProbeRT.uiScreen.release(); + mHeroProbeRT.screen.release(); + mHeroProbeRT.fxaaBuffer.release(); + mHeroProbeRT.deferredScreen.release(); + mHeroProbeRT.deferredLight.release(); } void LLPipeline::releaseSunShadowTarget(U32 index) @@ -1151,9 +1193,6 @@ void LLPipeline::createGLBuffers() stop_glerror(); assertInitialized(); - // Use FBO for bake tex - mBake.allocate(512, 512, GL_RGBA, true); // SL-12781 Build > Upload > Model; 3D Preview - stop_glerror(); GLuint resX = gViewerWindow->getWorldViewWidthRaw(); @@ -2400,6 +2439,26 @@ void LLPipeline::doOcclusion(LLCamera& camera) gGL.setColorMask(true, true); } + + if (sReflectionProbesEnabled && sUseOcclusion > 1 && !LLPipeline::sShadowRender && !gCubeSnapshot) + { + gGL.setColorMask(false, false); + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + LLGLDisable cull(GL_CULL_FACE); + + gOcclusionCubeProgram.bind(); + + if (mCubeVB.isNull()) + { //cube VB will be used for issuing occlusion queries + mCubeVB = ll_create_cube_vb(LLVertexBuffer::MAP_VERTEX); + } + mCubeVB->setBuffer(); + + mHeroProbeManager.doOcclusion(); + gOcclusionCubeProgram.unbind(); + + gGL.setColorMask(true, true); + } if (LLPipeline::sUseOcclusion > 1 && (sCull->hasOcclusionGroups() || LLVOCachePartition::sNeedsOcclusionCheck)) @@ -3777,6 +3836,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera, bool do_occlusion) { //update reflection probe uniform mReflectionMapManager.updateUniforms(); + mHeroProbeManager.updateUniforms(); } U32 cur_type = 0; @@ -4478,6 +4538,8 @@ void LLPipeline::renderDebug() } } + LL::GLTFSceneManager::instance().renderDebug(); + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION)) { //render visible selected group occlusion geometry gDebugProgram.bind(); @@ -6289,6 +6351,15 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, } } } + + S32 node_hit = -1; + S32 primitive_hit = -1; + LLDrawable* hit = LL::GLTFSceneManager::instance().lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, &node_hit, &primitive_hit, &position, tex_coord, normal, tangent); + if (hit) + { + drawable = hit; + local_end = position; + } if (!sPickAvatar) { @@ -6505,6 +6576,11 @@ void LLPipeline::renderGLTFObjects(U32 type, bool texture, bool rigged) gGL.loadMatrix(gGLModelView); gGLLastMatrix = NULL; + + if (!rigged) + { + LL::GLTFSceneManager::instance().renderOpaque(); + } } // Currently only used for shadows -Cosmic,2023-04-19 @@ -6542,7 +6618,7 @@ void LLPipeline::renderAlphaObjects(bool rigged) LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up); LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width); LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(ALPHA_BLEND_CUTOFF); - mSimplePool->pushRiggedGLTFBatch(*pparams, lastAvatar, lastMeshId); + LLRenderPass::pushRiggedGLTFBatch(*pparams, lastAvatar, lastMeshId); } else { @@ -6568,7 +6644,7 @@ void LLPipeline::renderAlphaObjects(bool rigged) LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up); LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width); LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(ALPHA_BLEND_CUTOFF); - mSimplePool->pushGLTFBatch(*pparams); + LLRenderPass::pushGLTFBatch(*pparams); } else { @@ -6713,6 +6789,8 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst) gLuminanceProgram.bind(); + static LLCachedControl<F32> diffuse_luminance_scale(gSavedSettings, "RenderDiffuseLuminanceScale", 1.0f); + S32 channel = 0; channel = gLuminanceProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE); if (channel > -1) @@ -6726,6 +6804,16 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst) mGlow[1].bindTexture(0, channel); } + channel = gLuminanceProgram.enableTexture(LLShaderMgr::DEFERRED_NORMAL); + if (channel > -1) + { + // bind the normal map to get the environment mask + mRT->deferredScreen.bindTexture(2, channel, LLTexUnit::TFO_POINT); + } + + static LLStaticHashedString diffuse_luminance_scale_s("diffuse_luminance_scale"); + gLuminanceProgram.uniform1f(diffuse_luminance_scale_s, diffuse_luminance_scale); + mScreenTriangleVB->setBuffer(); mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); dst->flush(); @@ -6736,11 +6824,12 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst) } } -void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) { +void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history) { // exposure sample { LL_PROFILE_GPU_ZONE("exposure sample"); + if (use_history) { // copy last frame's exposure into mLastExposure mLastExposure.bindTarget(); @@ -6757,55 +6846,73 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) { LLGLDepthTest depth(GL_FALSE, GL_FALSE); - gExposureProgram.bind(); + LLGLSLShader* shader; + if (use_history) + { + shader = &gExposureProgram; + } + else + { + shader = &gExposureProgramNoFade; + } - S32 channel = gExposureProgram.enableTexture(LLShaderMgr::DEFERRED_EMISSIVE); + shader->bind(); + + S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_EMISSIVE); if (channel > -1) { - mLuminanceMap.bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR); + src->bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR); } - channel = gExposureProgram.enableTexture(LLShaderMgr::EXPOSURE_MAP); - if (channel > -1) + if (use_history) { - mLastExposure.bindTexture(0, channel); + channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP); + if (channel > -1) + { + mLastExposure.bindTexture(0, channel); + } } static LLStaticHashedString dt("dt"); static LLStaticHashedString noiseVec("noiseVec"); static LLStaticHashedString dynamic_exposure_params("dynamic_exposure_params"); static LLCachedControl<F32> dynamic_exposure_coefficient(gSavedSettings, "RenderDynamicExposureCoefficient", 0.175f); - static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true); + static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true); - LLSettingsSky::ptr_t sky = LLEnvironment::instance().getCurrentSky(); + LLSettingsSky::ptr_t sky = LLEnvironment::instance().getCurrentSky(); - F32 probe_ambiance = LLEnvironment::instance().getCurrentSky()->getReflectionProbeAmbiance(should_auto_adjust); - F32 exp_min = 1.f; - F32 exp_max = 1.f; - - if (probe_ambiance > 0.f) - { - F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma())*2.f; + F32 probe_ambiance = LLEnvironment::instance().getCurrentSky()->getReflectionProbeAmbiance(should_auto_adjust); + F32 exp_min = 1.f; + F32 exp_max = 1.f; - if (hdr_scale > 1.f) - { - exp_min = 1.f / hdr_scale; - exp_max = hdr_scale; - } - } - gExposureProgram.uniform1f(dt, gFrameIntervalSeconds); - gExposureProgram.uniform2f(noiseVec, ll_frand() * 2.0 - 1.0, ll_frand() * 2.0 - 1.0); - gExposureProgram.uniform3f(dynamic_exposure_params, dynamic_exposure_coefficient, exp_min, exp_max); + if (probe_ambiance > 0.f) + { + F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma()) * 2.f; + + if (hdr_scale > 1.f) + { + exp_min = 1.f / hdr_scale; + exp_max = hdr_scale; + } + } + shader->uniform1f(dt, gFrameIntervalSeconds); + shader->uniform2f(noiseVec, ll_frand() * 2.0 - 1.0, ll_frand() * 2.0 - 1.0); + shader->uniform3f(dynamic_exposure_params, dynamic_exposure_coefficient, exp_min, exp_max); mScreenTriangleVB->setBuffer(); mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); - gGL.getTexUnit(channel)->unbind(mLastExposure.getUsage()); - gExposureProgram.unbind(); + if (use_history) + { + gGL.getTexUnit(channel)->unbind(mLastExposure.getUsage()); + } + shader->unbind(); dst->flush(); } } +extern LLPointer<LLImageGL> gEXRImage; + void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) { dst->bindTarget(); // gamma correct lighting @@ -6842,8 +6949,10 @@ void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) { F32 e = llclamp(exposure(), 0.5f, 4.f); static LLStaticHashedString s_exposure("exposure"); + static LLStaticHashedString aces_mix("aces_mix"); shader.uniform1f(s_exposure, e); + shader.uniform1f(aces_mix, gEXRImage.notNull() ? 0.f : 0.3f); mScreenTriangleVB->setBuffer(); mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3); @@ -8215,6 +8324,7 @@ void LLPipeline::renderDeferredLighting() LLPipeline::RENDER_TYPE_CONTROL_AV, LLPipeline::RENDER_TYPE_ALPHA_MASK, LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK, + LLPipeline::RENDER_TYPE_TERRAIN, LLPipeline::RENDER_TYPE_WATER, END_RENDER_TYPES); @@ -8618,6 +8728,17 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) mReflectionMapManager.mIrradianceMaps->bind(channel); bound = true; } + + if (RenderMirrors) + { + channel = shader.enableTexture(LLShaderMgr::HERO_PROBE, LLTexUnit::TT_CUBE_MAP_ARRAY); + if (channel > -1 && mHeroProbeManager.mTexture.notNull()) + { + mHeroProbeManager.mTexture->bind(channel); + bound = true; + } + } + if (bound) { @@ -10832,3 +10953,12 @@ void LLPipeline::rebuildDrawInfo() } } +void LLPipeline::rebuildTerrain() +{ + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + region->dirtyAllPatches(); + } +} diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 88a7eab813..817750b4a1 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -39,6 +39,7 @@ #include "lldrawable.h" #include "llrendertarget.h" #include "llreflectionmapmanager.h" +#include "llheroprobemanager.h" #include <stack> @@ -133,6 +134,8 @@ public: // rebuild all LLVOVolume render batches void rebuildDrawInfo(); + // Rebuild all terrain + void rebuildTerrain(); // Clear LLFace mVertexBuffer pointers void resetVertexBuffers(LLDrawable* drawable); @@ -151,7 +154,7 @@ public: void renderFinalize(); void copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* dst); void generateLuminance(LLRenderTarget* src, LLRenderTarget* dst); - void generateExposure(LLRenderTarget* src, LLRenderTarget* dst); + void generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history = true); void gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst); void generateGlow(LLRenderTarget* src); void applyFXAA(LLRenderTarget* src, LLRenderTarget* dst); @@ -458,6 +461,7 @@ public: void handleShadowDetailChanged(); LLReflectionMapManager mReflectionMapManager; + LLHeroProbeManager mHeroProbeManager; private: void unloadShaders(); @@ -694,8 +698,12 @@ public: RenderTargetPack mMainRT; // auxillary 512x512 render target pack + // used by reflection probes and dynamic texture bakes RenderTargetPack mAuxillaryRT; + // Auxillary render target pack scaled to the hero probe's per-face size. + RenderTargetPack mHeroProbeRT; + // currently used render target pack RenderTargetPack* mRT; @@ -754,7 +762,7 @@ public: //water distortion texture (refraction) LLRenderTarget mWaterDis; - LLRenderTarget mBake; + static const U32 MAX_BAKE_WIDTH; //texture for making the glow LLRenderTarget mGlow[3]; @@ -1047,6 +1055,9 @@ public: static F32 RenderScreenSpaceReflectionAdaptiveStepMultiplier; static S32 RenderScreenSpaceReflectionGlossySamples; static S32 RenderBufferVisualization; + static bool RenderMirrors; + static S32 RenderHeroProbeUpdateRate; + static S32 RenderHeroProbeConservativeUpdateMultiplier; }; void render_bbox(const LLVector3 &min, const LLVector3 &max); diff --git a/indra/newview/skins/default/xui/de/panel_region_terrain.xml b/indra/newview/skins/default/xui/de/panel_region_terrain.xml index 42ba5b5269..7738427fe3 100644 --- a/indra/newview/skins/default/xui/de/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/de/panel_region_terrain.xml @@ -10,7 +10,7 @@ <spinner label="Obere Terraingrenze" name="terrain_raise_spin"/> <spinner label="Untere Terraingrenze" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Terraintexturen (erfordert 24-Bit-.tga-Dateien mit einer Größe von 1024x1024) + Terraintexturen </text> <text name="height_text_lbl"> 1 (niedrig) diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index bc237322af..94c889f4e4 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -335,21 +335,9 @@ name="FSAADisabled" value="0" /> <combo_box.item - label="2x" - name="2x" + label="FXAA" + name="FXAA" value="2" /> - <combo_box.item - label="4x" - name="4x" - value="4" /> - <combo_box.item - label="8x" - name="8x" - value="8" /> - <combo_box.item - label="16x" - name="16x" - value="16" /> </combo_box> <text type="string" @@ -357,44 +345,23 @@ follows="left|top" height="16" layout="topleft" - left_pad="10" - name="antialiasing restart" - top_delta="0" - width="130"> - (requires restart) - </text> - <view_border - bevel_style="in" - height="322" - layout="topleft" - left="385" - name="vert_border" - top="16" - width="0"/> - <text - type="string" - length="1" - follows="left|top" - height="16" - layout="topleft" name="MeshText" - top_delta="20" - left="400" - top="21" + top_delta="16" + left="10" width="128"> - Mesh + Mesh </text> <slider control_name="RenderTerrainLODFactor" - follows="left|top" + follows="topleft" height="16" increment="0.125" initial_value="160" label="Terrain Mesh Detail:" label_width="185" layout="topleft" - left="420" + left="30" min_val="1" max_val="2" name="TerrainMeshDetail" @@ -408,7 +375,7 @@ <text type="string" length="1" - follows="left|top" + follows="topleft" height="16" layout="topleft" name="TerrainMeshDetailText" @@ -416,19 +383,19 @@ top_delta="0" left_delta="304" width="65"> - Low + Low </text> <slider control_name="RenderTreeLODFactor" - follows="left|top" + follows="topleft" height="16" increment="0.125" initial_value="160" label="Trees:" label_width="185" layout="topleft" - left="420" + left="30" name="TreeMeshDetail" show_text="false" top_delta="16" @@ -447,7 +414,7 @@ top_delta="0" left_delta="304" width="65"> - Low + Low </text> <slider @@ -459,7 +426,7 @@ label="Objects:" label_width="185" layout="topleft" - left="420" + left="30" min_val="0" max_val="4" name="ObjectMeshDetail" @@ -480,7 +447,7 @@ top_delta="0" left_delta="304" width="65"> - Low + Low </text> <slider @@ -491,7 +458,7 @@ label="Flexiprims:" label_width="185" layout="topleft" - left="420" + left="30" name="FlexibleMeshDetail" show_text="false" top_delta="16" @@ -510,8 +477,28 @@ top_delta="0" left_delta="304" width="65"> - Low + Low </text> + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left_pad="10" + name="antialiasing restart" + top_delta="0" + width="130"> + (requires restart) + </text> + <view_border + bevel_style="in" + height="322" + layout="topleft" + left="385" + name="vert_border" + top="16" + width="0"/> <text type="string" @@ -520,7 +507,7 @@ height="16" layout="topleft" name="ShadersText" - top_delta="20" + top_delta="-10" left="400" width="128"> Shaders @@ -534,7 +521,7 @@ layout="topleft" left="420" name="TransparentWater" - top_delta="16" + top_delta="18" width="300"> <check_box.commit_callback function="Pref.RenderOptionUpdate" /> @@ -555,7 +542,7 @@ max_val="128" name="SkyMeshDetail" show_text="false" - top_delta="16" + top_delta="22" width="260"> <slider.commit_callback function="Pref.UpdateSliderText" @@ -583,7 +570,7 @@ layout="topleft" left="420" name="UseSSAO" - top_delta="16" + top_delta="22" width="240"> <check_box.commit_callback function="Pref.RenderOptionUpdate" /> @@ -597,7 +584,7 @@ layout="topleft" left="420" name="UseDoF" - top_delta="16" + top_delta="20" width="240"> <check_box.commit_callback function="Pref.RenderOptionUpdate" /> @@ -612,7 +599,7 @@ left="420" name="RenderShadowDetailText" text_readonly_color="LabelDisabledColor" - top_delta="16" + top_delta="22" width="128"> Shadows: </text> @@ -646,7 +633,7 @@ layout="topleft" left="420" name="ScreenSpaceReflections" - top_delta="16" + top_delta="24" width="240"> <check_box.commit_callback function="Pref.RenderOptionUpdate" /> @@ -661,7 +648,7 @@ left="420" name="ReflectionDetailText" text_readonly_color="LabelDisabledColor" - top_delta="16" + top_delta="22" width="128"> Reflection Detail: </text> @@ -697,7 +684,7 @@ left="420" name="ReflectionProbeText" text_readonly_color="LabelDisabledColor" - top_delta="16" + top_delta="22" width="128"> Reflection Coverage: </text> @@ -743,10 +730,106 @@ max_val="1.5" name="RenderExposure" show_text="true" - top_delta="20" + top_delta="24" width="260"> </slider> + <!-- Mirror settings. --> + <check_box + control_name="RenderMirrors" + height="16" + initial_value="false" + label="Mirrors" + layout="topleft" + left="420" + name="Mirrors" + top_delta="24" + width="240"> + <check_box.commit_callback + function="Pref.RenderOptionUpdate" /> + </check_box> + + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="420" + name="MirrorResolutionText" + text_readonly_color="LabelDisabledColor" + top_delta="22" + width="128"> + Mirror Resolution: + </text> + + <combo_box + control_name="RenderHeroProbeResolution" + height="18" + layout="topleft" + left_delta="130" + top_delta="0" + name="MirrorResolution" + width="150"> + <combo_box.item + label="256" + name="0" + value="256"/> + <combo_box.item + label="512" + name="1" + value="512"/> + <combo_box.item + label="1024" + name="2" + value="1024"/> + <combo_box.item + label="2048" + name="3" + value="2048"/> + </combo_box> + + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="420" + name="HeroProbeUpdateText" + text_readonly_color="LabelDisabledColor" + top_delta="22" + width="128"> + Mirror Update Rate: + </text> + + <combo_box + control_name="RenderHeroProbeUpdateRate" + height="18" + layout="topleft" + left_delta="130" + top_delta="0" + name="HeroProbeUpdateRate" + width="150"> + <combo_box.item + label="Every Frame" + name="0" + value="1"/> + <combo_box.item + label="Every 2nd Frame" + name="1" + value="2"/> + <combo_box.item + label="Every 3rd Frame" + name="2" + value="3"/> + <combo_box.item + label="Every 4th Frame" + name="3" + value="4"/> + </combo_box> + <!-- End of mirror settings --> + <!-- End of Advanced Settings block --> <view_border bevel_style="in" diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index 490b516ac6..3ee4a354d4 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -2410,7 +2410,6 @@ even though the user gets a free copy. name="object_horizontal" top_pad="10" width="278" /> - <check_box height="16" label="Light" @@ -2560,16 +2559,52 @@ even though the user gets a free copy. name="Box" value="Box"/> </combo_box> + <check_box - height="16" - label="Dynamic" + height="16" + label="Dynamic" + layout="topleft" + left="10" + name="Probe Dynamic" + tool_tip="When enabled, Avatars will appear in reflections within this probe's influence volume." + bottom_delta="19" + width="60" /> + + <text + bottom_delta="-8" + type="string" + length="1" + follows="left|top" + height="10" layout="topleft" left="10" - name="Probe Dynamic" - tool_tip="When enabled, Avatars will appear in reflections within this probe's influence volume." - bottom_delta="19" - width="60" /> - <spinner bottom_delta="19" + name="Probe Update Label" + text_readonly_color="LabelDisabledColor" + width="100"> + Probe Update + </text> + <combo_box + height="19" + top_delta="0" + left="144" + follows="left|top" + name="Probe Update Type" + tool_tip="Determines how the probe updates. Static updates the slowest and without avatars. Dynamic updates more frequently, with avatars visible in the probes. Mirror turns this probe into a realtime planar projected mirror probe, but does not calculate ambiance." + width="108"> + <combo_box.item + label="Static" + name="Static" + value="Static" /> + <combo_box.item + label="Dynamic" + name="Dynamic" + value="Dynamic"/> + <combo_box.item + label="Mirror" + name="Mirror" + value="Mirror"/> + </combo_box> + <spinner bottom_delta="17" decimal_digits="3" follows="left|top" height="16" diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml index 13064db712..ae96289f36 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml @@ -13,7 +13,7 @@ name="upload" tear_off="true"> <menu_item_call - label="Image (L$[COST])..." + label="Image..." layout="topleft" name="Upload Image" shortcut="control|U"> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index d580369ab2..c2f04231fc 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1620,7 +1620,7 @@ function="World.EnvPreset" name="Upload" tear_off="true"> <menu_item_call - label="Image (L$[COST])..." + label="Image..." layout="topleft" name="Upload Image" shortcut="control|U"> @@ -2840,6 +2840,18 @@ function="World.EnvPreset" <menu_item_call.on_click function="Advanced.ClickRenderBenchmark" /> </menu_item_call> + <menu_item_call + label="HDRI Preview" + name="HDRI Preview"> + <menu_item_call.on_click + function="Advanced.ClickHDRIPreview" /> + </menu_item_call> + <menu_item_call + label="GLTF Scene Preview" + name="GLTF Scene Preview"> + <menu_item_call.on_click + function="Advanced.ClickGLTFScenePreview" /> + </menu_item_call> </menu> <menu create_jump_keys="true" @@ -3319,6 +3331,13 @@ function="World.EnvPreset" <menu_item_call.on_click function="Advanced.ClearShaderCache" /> </menu_item_call> + <menu_item_call + enabled="true" + label="Rebuild Terrain" + name="Rebuild Terrain"> + <menu_item_call.on_click + function="Advanced.RebuildTerrain" /> + </menu_item_call> <menu_item_separator /> <menu_item_call enabled="true" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 3ca284b0fa..8015c7d875 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4002,10 +4002,39 @@ Are you sure you want to return objects owned by [USER_NAME]? icon="alertmodal.tga" name="InvalidTerrainBitDepth" type="alertmodal"> + <unique combine="cancel_old" /> Couldn't set region textures: Terrain texture [TEXTURE_NUM] has an invalid bit depth of [TEXTURE_BIT_DEPTH]. -Replace texture [TEXTURE_NUM] with a 24-bit [MAX_SIZE]x[MAX_SIZE] or smaller image then click "Apply" again. +Replace texture [TEXTURE_NUM] with an RGB [MAX_SIZE]x[MAX_SIZE] or smaller image then click "Apply" again. + <tag>fail</tag> + </notification> + + <notification + icon="alertmodal.tga" + name="InvalidTerrainAlphaNotFullyLoaded" + type="alertmodal"> + <unique combine="cancel_old" /> +Couldn't set region textures: +Terrain texture [TEXTURE_NUM] is not fully loaded, but is assumed to contain transparency due to a bit depth of [TEXTURE_BIT_DEPTH]. Transparency is not currently supported for terrain textures. + +If texture [TEXTURE_NUM] is opaque, wait for the texture to fully load and then click "Apply" again. + +Alpha is only supported for terrain materials (PBR Metallic Roughness), when alphaMode="MASK" and doubleSided=false. + <tag>fail</tag> + </notification> + + <notification + icon="alertmodal.tga" + name="InvalidTerrainAlpha" + type="alertmodal"> + <unique combine="cancel_old" /> +Couldn't set region textures: +Terrain texture [TEXTURE_NUM] contains transparency. Transparency is not currently supported for terrain textures. + +Replace texture [TEXTURE_NUM] with an opaque RGB image, then click "Apply" again. + +Alpha is only supported for terrain materials (PBR Metallic Roughness), when alphaMode="MASK" and doubleSided=false. <tag>fail</tag> </notification> @@ -4013,10 +4042,55 @@ Replace texture [TEXTURE_NUM] with a 24-bit [MAX_SIZE]x[MAX_SIZE] or smaller ima icon="alertmodal.tga" name="InvalidTerrainSize" type="alertmodal"> + <unique combine="cancel_old" /> Couldn't set region textures: Terrain texture [TEXTURE_NUM] is too large at [TEXTURE_SIZE_X]x[TEXTURE_SIZE_Y]. -Replace texture [TEXTURE_NUM] with a 24-bit [MAX_SIZE]x[MAX_SIZE] or smaller image then click "Apply" again. +Replace texture [TEXTURE_NUM] with an RGB [MAX_SIZE]x[MAX_SIZE] or smaller image then click "Apply" again. + </notification> + + <notification + icon="alertmodal.tga" + name="InvalidTerrainMaterialNotLoaded" + type="alertmodal"> + <unique combine="cancel_old" /> +Couldn't set region materials: +Terrain material [MATERIAL_NUM] is not loaded. + +Wait for the material to load, or replace material [MATERIAL_NUM] with a valid material. + </notification> + + <notification + icon="alertmodal.tga" + name="InvalidTerrainMaterialLoadFailed" + type="alertmodal"> + <unique combine="cancel_old" /> +Couldn't set region materials: +Terrain material [MATERIAL_NUM] failed to load. + +Replace material [MATERIAL_NUM] with a valid material. + </notification> + + <notification + icon="alertmodal.tga" + name="InvalidTerrainMaterialDoubleSided" + type="alertmodal"> + <unique combine="cancel_old" /> +Couldn't set region materials: +Terrain material [MATERIAL_NUM] is double-sided. Double-sided materials are not currently supported for PBR terrain. + +Replace material [MATERIAL_NUM] with a material with doubleSided=false. + </notification> + + <notification + icon="alertmodal.tga" + name="InvalidTerrainMaterialAlphaMode" + type="alertmodal"> + <unique combine="cancel_old" /> +Couldn't set region materials: +Terrain material [MATERIAL_NUM] is using the unsupported alphaMode="[MATERIAL_ALPHA_MODE]". + +Replace material [MATERIAL_NUM] with a material with alphaMode="OPAQUE" or alphaMode="MASK". </notification> <notification @@ -9253,6 +9327,15 @@ Unable to upload texture: '[NAME]' </notification> <notification + icon="alertmodal.tga" + name="CannotLoad" + type="alertmodal"> + Unable to load [WHAT]. + [REASON] + <tag>fail</tag> + </notification> + + <notification icon="alertmodal.tga" name="CannotUploadMaterial" type="alertmodal"> @@ -12278,5 +12361,16 @@ Would you like to save them first? name="okignore" yestext="OK"/> </notification> - + + <notification + icon="alertmodal.tga" + name="GLTFPreviewSelection" + type="alert"> + You must select an object to act as a handle to the GLTF asset you are previewing. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_region_terrain.xml b/indra/newview/skins/default/xui/en/panel_region_terrain.xml index ad41691323..73e0a1000f 100644 --- a/indra/newview/skins/default/xui/en/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/en/panel_region_terrain.xml @@ -79,26 +79,55 @@ <text type="string" length="1" + halign="left" + valign="center" follows="left|top" height="20" layout="topleft" - left="10" name="detail_texture_text" - top="110" - width="300"> - Terrain Textures (requires 1024x1024, 24 bit .tga files) + left="10" + top="105" + width="170"> + Terrain Textures </text> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="detail_material_text" + left="10" + top="105" + width="170"> + Terrain Materials + </text> + <check_box + height="20" + halign="left" + valign="center" + follows="left|top" + layout="topleft" + top_delta="1" + left_delta="180" + label="PBR Metallic Roughness" + name="terrain_material_type" + tool_tip="If checked, use PBR Metallic Roughness materials for terrain. Otherwise, use textures." + left_pad="2" + width="200" /> <texture_picker follows="left|top" height="100" layout="topleft" - left_delta="0" + left="10" name="texture_detail_0" default_image_id="0bc58228-74a0-7e83-89bc-5c23464bcec5" - top_delta="20" + top_delta="30" width="100" /> <texture_picker - follows="left|top" + follows="top" height="100" layout="topleft" left_pad="10" @@ -124,6 +153,50 @@ default_image_id="53a2f406-4895-1d13-d541-d2e3b86bc19c" top_delta="0" width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="100" + layout="topleft" + left="10" + name="material_detail_0" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="100" + layout="topleft" + left_pad="10" + name="material_detail_1" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="100" + layout="topleft" + left_pad="10" + name="material_detail_2" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="100" + layout="topleft" + left_pad="10" + name="material_detail_3" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> <text type="string" length="1" @@ -185,6 +258,19 @@ Texture Elevation Ranges </text> <text + visible="false" + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl5_material" + top_delta="0" + width="300"> + Material Elevation Ranges + </text> + <text follows="left|top" height="20" layout="topleft" @@ -196,6 +282,18 @@ These values represent the blend range for the textures above. </text> <text + visible="false" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl10_material" + top_delta="0" + width="200" + word_wrap="true"> + These values represent the blend range for the materials above. + </text> + <text follows="left|top" height="60" layout="topleft" @@ -207,6 +305,18 @@ Measured in meters, the LOW value is the MAXIMUM height of Texture #1, and the HIGH value is the MINIMUM height of Texture #4. </text> <text + visible="false" + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + name="height_text_lbl11_material" + top_delta="0" + width="200" + word_wrap="true"> + Measured in meters, the LOW value is the MAXIMUM height of Material #1, and the HIGH value is the MINIMUM height of Material #4. + </text> + <text follows="left|top" height="20" layout="topleft" diff --git a/indra/newview/skins/default/xui/es/panel_region_terrain.xml b/indra/newview/skins/default/xui/es/panel_region_terrain.xml index 9aba5299cb..5e6e89893e 100644 --- a/indra/newview/skins/default/xui/es/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/es/panel_region_terrain.xml @@ -12,7 +12,7 @@ del terreno" name="terrain_raise_spin"/> <spinner bottom_delta="-34" label="Límite de bajada del terreno" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Texturas del terreno (requiere archivos .tga de 1024x1024, 24 bits) + Texturas del terreno </text> <text name="height_text_lbl"> 1 (bajo) diff --git a/indra/newview/skins/default/xui/fr/panel_region_terrain.xml b/indra/newview/skins/default/xui/fr/panel_region_terrain.xml index bbab00ca24..d99948804a 100644 --- a/indra/newview/skins/default/xui/fr/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/fr/panel_region_terrain.xml @@ -12,7 +12,7 @@ terrain" name="terrain_raise_spin"/> <spinner bottom_delta="-34" label="Limite d'abaissement du terrain" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Textures du terrain (fichiers .tga 1024 x 1024, 24 bit requis) + Textures du terrain </text> <text name="height_text_lbl"> 1 (Bas) diff --git a/indra/newview/skins/default/xui/it/panel_region_terrain.xml b/indra/newview/skins/default/xui/it/panel_region_terrain.xml index e08c55f63b..0583b4e02e 100644 --- a/indra/newview/skins/default/xui/it/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/it/panel_region_terrain.xml @@ -12,7 +12,7 @@ terreno" name="terrain_raise_spin"/> <spinner bottom_delta="-34" label="Limite di abbassamento del terreno" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Texture terreno (richiede file 1024x1024, 24 bit .tga) + Texture terreno </text> <text name="height_text_lbl"> 1 (basso) diff --git a/indra/newview/skins/default/xui/ja/panel_region_terrain.xml b/indra/newview/skins/default/xui/ja/panel_region_terrain.xml index 5470bd6e3b..11e8d0d169 100644 --- a/indra/newview/skins/default/xui/ja/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/ja/panel_region_terrain.xml @@ -10,7 +10,7 @@ <spinner label="地形の上昇限度" name="terrain_raise_spin"/> <spinner label="地形の下降限度" name="terrain_lower_spin"/> <text name="detail_texture_text"> - 地形テクスチャ(1024✕1024 の 24 bit .tga ファイル) + 地形テクスチャ </text> <text name="height_text_lbl"> 1(低) diff --git a/indra/newview/skins/default/xui/pl/panel_region_terrain.xml b/indra/newview/skins/default/xui/pl/panel_region_terrain.xml index 2d4286334f..c1dde04ff0 100644 --- a/indra/newview/skins/default/xui/pl/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/pl/panel_region_terrain.xml @@ -7,7 +7,7 @@ <spinner label="Górny limit terenu" name="terrain_raise_spin" /> <spinner label="Dolny limit terenu" name="terrain_lower_spin" /> <text name="detail_texture_text"> - Tekstury terenu (1024x1024, 24 bitowy plik .tga) + Tekstury terenu </text> <text name="height_text_lbl"> 1 (Nisko) diff --git a/indra/newview/skins/default/xui/pt/panel_region_terrain.xml b/indra/newview/skins/default/xui/pt/panel_region_terrain.xml index 1d312aeed9..92d6859882 100644 --- a/indra/newview/skins/default/xui/pt/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/pt/panel_region_terrain.xml @@ -12,7 +12,7 @@ terreno" name="terrain_raise_spin"/> <spinner bottom_delta="-34" label="Limite mais baixo do terreno" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Texturas de terreno (exige arquivos .tga 1024x1024, 24 bit) + Texturas de terreno </text> <text name="height_text_lbl"> 1 (Baixo) diff --git a/indra/newview/skins/default/xui/ru/panel_region_terrain.xml b/indra/newview/skins/default/xui/ru/panel_region_terrain.xml index 76b4f513a8..2752d409cb 100644 --- a/indra/newview/skins/default/xui/ru/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/ru/panel_region_terrain.xml @@ -10,7 +10,7 @@ <spinner label="Верх. точка ландшафта" name="terrain_raise_spin"/> <spinner label="Ниж. точка ландшафта" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Текстуры ландшафта (требования: 1024x1024, 24-битные, TGA) + Текстуры ландшафта </text> <text name="height_text_lbl"> 1 (Низ) diff --git a/indra/newview/skins/default/xui/tr/panel_region_terrain.xml b/indra/newview/skins/default/xui/tr/panel_region_terrain.xml index e25047301d..00560d4c5b 100644 --- a/indra/newview/skins/default/xui/tr/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/tr/panel_region_terrain.xml @@ -10,7 +10,7 @@ <spinner label="Yüzey Yükslt. Limiti" name="terrain_raise_spin"/> <spinner label="Yüzey Alçatma Limiti" name="terrain_lower_spin"/> <text name="detail_texture_text"> - Yüzey Dokuları (1024x1024, 24 bit .tga dosyalar gerektirir) + Yüzey Dokuları </text> <text name="height_text_lbl"> 1 (Düşük) diff --git a/indra/newview/skins/default/xui/zh/panel_region_terrain.xml b/indra/newview/skins/default/xui/zh/panel_region_terrain.xml index 81bce46876..8490eeaafc 100644 --- a/indra/newview/skins/default/xui/zh/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/zh/panel_region_terrain.xml @@ -10,7 +10,7 @@ <spinner label="地形提升限制" name="terrain_raise_spin"/> <spinner label="地形降低限制" name="terrain_lower_spin"/> <text name="detail_texture_text"> - 地形材質(須 1024x1024,24 位元 .tga 檔格式) + 地形材質 </text> <text name="height_text_lbl"> 1(低) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 4de4dc8fc5..d51d86aa3c 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -587,6 +587,14 @@ class Windows_x86_64_Manifest(ViewerManifest): self.path("libcrypto-1_1-x64.dll") self.path("libssl-1_1-x64.dll") + # OpenEXR + self.path("Iex-3_2.dll") + self.path("IlmThread-3_2.dll") + self.path("Imath-3_1.dll") + self.path("OpenEXR-3_2.dll") + self.path("OpenEXRCore-3_2.dll") + self.path("OpenEXRUtil-3_2.dll") + # HTTP/2 self.path("nghttp2.dll") |