591 lines
19 KiB
C++
591 lines
19 KiB
C++
/**
|
|
* @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);
|
|
}
|