SL-20606: Full GLTF material preview. Works for most materials.

master
Cosmic Linden 2023-11-13 17:26:14 -08:00
parent c2a30057f8
commit 2f18d74f9a
16 changed files with 728 additions and 103 deletions

View File

@ -65,7 +65,6 @@ class LLCamera
: public LLCoordFrame
{
public:
LLCamera(const LLCamera& rhs)
{
*this = rhs;

View File

@ -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);

View File

@ -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);

View File

@ -310,6 +310,7 @@ set(viewer_SOURCE_FILES
llgiveinventory.cpp
llglsandbox.cpp
llgltfmateriallist.cpp
llgltfmaterialpreviewmgr.cpp
llgroupactions.cpp
llgroupiconctrl.cpp
llgrouplist.cpp
@ -963,6 +964,7 @@ set(viewer_HEADER_FILES
llgesturemgr.h
llgiveinventory.h
llgltfmateriallist.h
llgltfmaterialpreviewmgr.h
llgroupactions.h
llgroupiconctrl.h
llgrouplist.h

View File

@ -37,7 +37,11 @@ void main()
{
vec4 diff = texture(diffuseRect, vary_fragcoord.xy);
#if 1
frag_color = diff;
#else
frag_color = vec4(1,0,1,1);
#endif
gl_FragDepth = texture(depthMap, vary_fragcoord.xy).r;
}

View File

@ -189,13 +189,18 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
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();
@ -210,11 +215,14 @@ 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())
@ -231,10 +239,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
}
}
if (use_fbo)
{
gPipeline.mBake.flush();
}
bake_target.flush();
gGL.flush();

View File

@ -254,55 +254,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;
}

View File

@ -50,8 +50,6 @@ public:
bool isFetching() const { return mFetching; }
LLPointer<LLViewerFetchedTexture> getUITexture();
void addTextureEntry(LLTextureEntry* te) override;
void removeTextureEntry(LLTextureEntry* te) override;
virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID& old_id, const LLUUID& new_id) override;
@ -65,9 +63,6 @@ public:
std::set<LLTextureEntry*> mTextureEntires;
// Texture used for previewing the material in the UI
LLPointer<LLViewerFetchedTexture> mPreviewTexture;
protected:
// Lifetime management

View File

@ -0,0 +1,501 @@
/**
* @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 <vector>
#include "llavatarappearancedefines.h"
#include "llenvironment.h"
#include "llselectmgr.h"
#include "llviewercamera.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;
}
}
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 (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)
{
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);
}
void LLGLTFPreviewTexture::preRender(BOOL clear_depth)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
MaterialLoadLevels current_load = get_material_load_levels(*mGLTFMaterial.get());
if (current_load < mBestLoad)
{
mShouldRender = true;
mBestLoad = current_load;
}
if (!mShouldRender) { return; }
LLViewerDynamicTexture::preRender(clear_depth);
}
namespace {
struct GLTFPreviewModel
{
GLTFPreviewModel(LLPointer<LLDrawInfo>& info, const LLMatrix4& mat)
: mDrawInfo(info)
, mModelMatrix(mat)
{
mDrawInfo->mModelMatrix = &mModelMatrix;
}
LLPointer<LLDrawInfo> mDrawInfo;
LLMatrix4 mModelMatrix; // Referenced by mDrawInfo
};
// Like LLVolumeGeometryManager::registerFace but without batching or too-many-indices/vertices checking.
std::vector<GLTFPreviewModel> 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();
}
std::vector<GLTFPreviewModel> 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(info, model_matrix);
index_offset += face.mNumIndices;
vertex_offset += face.mNumVertices;
}
buf->unmapBuffer();
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; }
LLGLDepthTest(GL_FALSE);
LLGLDisable stencil(GL_STENCIL_TEST);
LLGLDisable scissor(GL_SCISSOR_TEST);
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);
SetTemporarily<S32> sun_light_only(&LLPipeline::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.)
std::vector<GLTFPreviewModel> preview_sphere = create_preview_sphere(mGLTFMaterial, object_transform);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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;
#if 0
if (mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_OPAQUE || mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
{
// *TODO: Opaque/alpha mask rendering (gDeferredPBROpaqueProgram)
}
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 (GLTFPreviewModel& part : preview_sphere)
{
LLRenderPass::pushGLTFBatch(*part.mDrawInfo);
}
gPipeline.unbindDeferredShader(shader);
screen.flush();
}
gPipeline.copyScreenSpaceReflections(&screen, &gPipeline.mSceneMap);
gPipeline.generateLuminance(&screen, &gPipeline.mLuminanceMap);
gPipeline.generateExposure(&gPipeline.mLuminanceMap, &gPipeline.mExposureMap);
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);
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);
return TRUE;
}
void LLGLTFPreviewTexture::postRender(BOOL success)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
if (!mShouldRender) { return; }
mShouldRender = false;
LLViewerDynamicTexture::postRender(success);
}
// static
LLPointer<LLViewerTexture> LLGLTFMaterialPreviewMgr::getPreview(LLPointer<LLFetchedGLTFMaterial> &material)
{
if (!material)
{
return nullptr;
}
if (!is_material_loaded_enough_for_ui(*material))
{
return nullptr;
}
return LLGLTFPreviewTexture::create(material);
}

View File

@ -0,0 +1,82 @@
/**
* @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 { return mNeedsRender; }
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();
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 mNeedsRender = true;
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;

View File

@ -27,6 +27,9 @@
#include "llviewerprecompiledheaders.h"
#include "llreflectionmapmanager.h"
#include <bitset>
#include "llviewercamera.h"
#include "llspatialpartition.h"
#include "llviewerregion.h"
@ -1383,3 +1386,32 @@ void LLReflectionMapManager::doOcclusion()
}
}
}
void LLReflectionMapManager::forceDefaultProbeAndUpdateUniforms(bool force)
{
static std::bitset<LL_MAX_REFLECTION_PROBE_COUNT> mProbeWasOccluded;
if (force)
{
for (size_t i = 0; i < mProbes.size(); ++i)
{
auto& probe = mProbes[i];
mProbeWasOccluded[i] = probe->mOccluded;
if (probe != nullptr && probe != mDefaultProbe)
{
probe->mOccluded = true;
}
}
updateUniforms();
}
else
{
for (size_t i = 0; i < mProbes.size(); ++i)
{
auto& probe = mProbes[i];
llassert(probe->mOccluded == (probe != mDefaultProbe));
probe->mOccluded = mProbeWasOccluded[i];
}
}
}

View File

@ -106,6 +106,11 @@ 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;

View File

@ -72,6 +72,7 @@
#include "llradiogroup.h"
#include "llfloaterreg.h"
#include "llgltfmaterialpreviewmgr.h"
#include "lllocalbitmaps.h"
#include "lllocalgltfmaterials.h"
#include "llerror.h"
@ -657,6 +658,7 @@ void LLFloaterTexturePicker::draw()
if( mOwner )
{
mTexturep = NULL;
LLPointer<LLFetchedGLTFMaterial> old_material = mGLTFMaterial;
mGLTFMaterial = NULL;
if (mImageAssetID.notNull())
{
@ -664,10 +666,23 @@ 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);
}
}
}
else
{
LLPointer<LLViewerFetchedTexture> texture = NULL;
mGLTFPreview = nullptr;
if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID))
{
@ -677,7 +692,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;
}
}
@ -718,27 +733,29 @@ 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 = nullptr;
if (mGLTFMaterial)
{
texture = mGLTFMaterial->getUITexture();
preview = mGLTFPreview.get();
}
else
{
texture = mTexturep.get();
preview = mTexturep.get();
if (mTexturep)
{
// Pump the priority
mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
}
}
if( texture )
if( preview )
{
if( texture->getComponents() == 4 )
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())
{
@ -2156,48 +2173,69 @@ 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())))
{
texture = material->getUITexture();
// Only update the preview if needed, since gGLTFMaterialPreviewMgr does not cache the preview.
if (mGLTFMaterial.isNull())
{
mGLTFPreview = nullptr;
}
else
{
mGLTFPreview = gGLTFMaterialPreviewMgr.getPreview(mGLTFMaterial);
}
}
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
@ -2210,15 +2248,18 @@ 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);
if (mTexturep)
{
mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
}
}
else if (!mFallbackImage.isNull())
{

View File

@ -250,6 +250,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 +384,7 @@ protected:
LLPointer<LLViewerTexture> mTexturep;
LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial;
LLPointer<LLViewerTexture> mGLTFPreview;
LLView* mOwner;
LLUUID mImageAssetID; // Currently selected texture

View File

@ -163,6 +163,7 @@ F32 LLPipeline::CameraFocusTransitionTime;
F32 LLPipeline::CameraFNumber;
F32 LLPipeline::CameraFocalLength;
F32 LLPipeline::CameraFieldOfView;
S32 LLPipeline::RenderLocalLightCount;
F32 LLPipeline::RenderShadowNoise;
F32 LLPipeline::RenderShadowBlurSize;
F32 LLPipeline::RenderSSAOScale;
@ -200,6 +201,8 @@ S32 LLPipeline::RenderScreenSpaceReflectionGlossySamples;
S32 LLPipeline::RenderBufferVisualization;
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;
@ -521,6 +524,7 @@ void LLPipeline::init()
connectRefreshCachedSettingsSafe("CameraFNumber");
connectRefreshCachedSettingsSafe("CameraFocalLength");
connectRefreshCachedSettingsSafe("CameraFieldOfView");
connectRefreshCachedSettingsSafe("RenderLocalLightCount");
connectRefreshCachedSettingsSafe("RenderShadowNoise");
connectRefreshCachedSettingsSafe("RenderShadowBlurSize");
connectRefreshCachedSettingsSafe("RenderSSAOScale");
@ -1009,6 +1013,7 @@ void LLPipeline::refreshCachedSettings()
CameraFNumber = gSavedSettings.getF32("CameraFNumber");
CameraFocalLength = gSavedSettings.getF32("CameraFocalLength");
CameraFieldOfView = gSavedSettings.getF32("CameraFieldOfView");
RenderLocalLightCount = gSavedSettings.getS32("RenderLocalLightCount");
RenderShadowNoise = gSavedSettings.getF32("RenderShadowNoise");
RenderShadowBlurSize = gSavedSettings.getF32("RenderShadowBlurSize");
RenderSSAOScale = gSavedSettings.getF32("RenderSSAOScale");
@ -1072,7 +1077,6 @@ void LLPipeline::releaseGLBuffers()
releaseLUTBuffers();
mWaterDis.release();
mBake.release();
mSceneMap.release();
@ -1151,9 +1155,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();
@ -5227,7 +5228,7 @@ void LLPipeline::calcNearbyLights(LLCamera& camera)
return;
}
static LLCachedControl<S32> local_light_count(gSavedSettings, "RenderLocalLightCount", 256);
const S32 local_light_count = LLPipeline::RenderLocalLightCount;
if (local_light_count >= 1)
{
@ -5496,7 +5497,7 @@ void LLPipeline::setupHWLights()
mLightMovingMask = 0;
static LLCachedControl<S32> local_light_count(gSavedSettings, "RenderLocalLightCount", 256);
const S32 local_light_count = LLPipeline::RenderLocalLightCount;
if (local_light_count >= 1)
{
@ -7930,7 +7931,7 @@ void LLPipeline::renderDeferredLighting()
unbindDeferredShader(gDeferredSoftenProgram);
}
static LLCachedControl<S32> local_light_count(gSavedSettings, "RenderLocalLightCount", 256);
const S32 local_light_count = LLPipeline::RenderLocalLightCount;
if (local_light_count > 0)
{

View File

@ -694,6 +694,7 @@ public:
RenderTargetPack mMainRT;
// auxillary 512x512 render target pack
// used by reflection probes and dynamic texture bakes
RenderTargetPack mAuxillaryRT;
// currently used render target pack
@ -754,7 +755,7 @@ public:
//water distortion texture (refraction)
LLRenderTarget mWaterDis;
LLRenderTarget mBake;
static const U32 MAX_BAKE_WIDTH;
//texture for making the glow
LLRenderTarget mGlow[3];
@ -1012,6 +1013,7 @@ public:
static F32 CameraFNumber;
static F32 CameraFocalLength;
static F32 CameraFieldOfView;
static S32 RenderLocalLightCount;
static F32 RenderShadowNoise;
static F32 RenderShadowBlurSize;
static F32 RenderSSAOScale;