Merge branch 'DRTVWR-559' of https://bitbucket.org/lindenlab/viewer
commit
5178e59b59
|
|
@ -122,6 +122,7 @@ LLFolderDictionary::LLFolderDictionary()
|
|||
addEntry(LLFolderType::FT_MARKETPLACE_VERSION, new FolderEntry("version", FALSE, FALSE, FALSE));
|
||||
|
||||
addEntry(LLFolderType::FT_SETTINGS, new FolderEntry("settings", TRUE, FALSE, TRUE));
|
||||
addEntry(LLFolderType::FT_MATERIAL, new FolderEntry("material", TRUE, FALSE, TRUE));
|
||||
|
||||
addEntry(LLFolderType::FT_MY_SUITCASE, new FolderEntry("suitcase", TRUE, FALSE, TRUE)); // <FS:Ansariel> OpenSim HG-support
|
||||
|
||||
|
|
|
|||
|
|
@ -2523,6 +2523,24 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
|
|||
min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
|
||||
max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
|
||||
|
||||
//unpack normalized scale/translation
|
||||
if (mdl[i].has("NormalizedScale"))
|
||||
{
|
||||
face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
face.mNormalizedScale.set(1, 1, 1);
|
||||
}
|
||||
if (mdl[i].has("NormalizedTranslation"))
|
||||
{
|
||||
face.mNormalizedTranslation.setValue(mdl[i]["NormalizedTranslation"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
face.mNormalizedTranslation.set(1, 1, 1);
|
||||
}
|
||||
|
||||
LLVector4a pos_range;
|
||||
pos_range.setSub(max_pos, min_pos);
|
||||
LLVector2 tc_range2 = max_tc - min_tc;
|
||||
|
|
@ -2573,6 +2591,7 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
if (!tangent.empty())
|
||||
{
|
||||
|
|
@ -2599,6 +2618,7 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
if (!tc.empty())
|
||||
|
|
@ -4957,7 +4977,9 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
|
|||
}
|
||||
|
||||
mOptimized = src.mOptimized;
|
||||
|
||||
mNormalizedScale = src.mNormalizedScale;
|
||||
mNormalizedTranslation = src.mNormalizedTranslation;
|
||||
|
||||
//delete
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -5501,12 +5523,19 @@ struct MikktData
|
|||
w.resize(count);
|
||||
}
|
||||
|
||||
|
||||
LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]);
|
||||
|
||||
|
||||
for (int i = 0; i < face->mNumIndices; ++i)
|
||||
{
|
||||
U32 idx = face->mIndices[i];
|
||||
|
||||
p[i].set(face->mPositions[idx].getF32ptr());
|
||||
p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents
|
||||
n[i].set(face->mNormals[idx].getF32ptr());
|
||||
n[i].scaleVec(inv_scale);
|
||||
n[i].normalize();
|
||||
tc[i].set(face->mTexCoords[idx]);
|
||||
|
||||
if (face->mWeights)
|
||||
|
|
@ -5550,10 +5579,7 @@ bool LLVolumeFace::cacheOptimize()
|
|||
ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
|
||||
{
|
||||
MikktData* data = (MikktData*)pContext->m_pUserData;
|
||||
LLVolumeFace* face = data->face;
|
||||
S32 idx = face->mIndices[iFace * 3 + iVert];
|
||||
auto& vert = face->mPositions[idx];
|
||||
F32* v = vert.getF32ptr();
|
||||
F32* v = data->p[iFace * 3 + iVert].mV;
|
||||
fvPosOut[0] = v[0];
|
||||
fvPosOut[1] = v[1];
|
||||
fvPosOut[2] = v[2];
|
||||
|
|
@ -5562,10 +5588,7 @@ bool LLVolumeFace::cacheOptimize()
|
|||
ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
|
||||
{
|
||||
MikktData* data = (MikktData*)pContext->m_pUserData;
|
||||
LLVolumeFace* face = data->face;
|
||||
S32 idx = face->mIndices[iFace * 3 + iVert];
|
||||
auto& norm = face->mNormals[idx];
|
||||
F32* n = norm.getF32ptr();
|
||||
F32* n = data->n[iFace * 3 + iVert].mV;
|
||||
fvNormOut[0] = n[0];
|
||||
fvNormOut[1] = n[1];
|
||||
fvNormOut[2] = n[2];
|
||||
|
|
@ -5574,27 +5597,16 @@ bool LLVolumeFace::cacheOptimize()
|
|||
ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
|
||||
{
|
||||
MikktData* data = (MikktData*)pContext->m_pUserData;
|
||||
LLVolumeFace* face = data->face;
|
||||
S32 idx = face->mIndices[iFace * 3 + iVert];
|
||||
auto& tc = face->mTexCoords[idx];
|
||||
fvTexcOut[0] = tc.mV[0];
|
||||
fvTexcOut[1] = tc.mV[1];
|
||||
F32* tc = data->tc[iFace * 3 + iVert].mV;
|
||||
fvTexcOut[0] = tc[0];
|
||||
fvTexcOut[1] = tc[1];
|
||||
};
|
||||
|
||||
ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
|
||||
{
|
||||
MikktData* data = (MikktData*)pContext->m_pUserData;
|
||||
LLVolumeFace* face = data->face;
|
||||
S32 i = iFace * 3 + iVert;
|
||||
S32 idx = face->mIndices[i];
|
||||
|
||||
LLVector3 p(face->mPositions[idx].getF32ptr());
|
||||
LLVector3 n(face->mNormals[idx].getF32ptr());
|
||||
LLVector3 t(fvTangent);
|
||||
|
||||
// assert that this tangent hasn't already been set
|
||||
llassert(data->t[i].magVec() < 0.1f);
|
||||
|
||||
|
||||
data->t[i].set(fvTangent);
|
||||
data->t[i].mV[3] = fSign;
|
||||
};
|
||||
|
|
@ -5654,6 +5666,24 @@ bool LLVolumeFace::cacheOptimize()
|
|||
mWeights[dst_idx].loadua(data.w[src_idx].mV);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// put back in normalized coordinate frame
|
||||
LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]);
|
||||
LLVector4a scale;
|
||||
scale.load3(mNormalizedScale.mV);
|
||||
scale.getF32ptr()[3] = 1.f;
|
||||
|
||||
for (int i = 0; i < mNumVertices; ++i)
|
||||
{
|
||||
mPositions[i].mul(inv_scale);
|
||||
mNormals[i].mul(scale);
|
||||
mNormals[i].normalize3();
|
||||
F32 w = mMikktSpaceTangents[i].getF32ptr()[3];
|
||||
mMikktSpaceTangents[i].mul(scale);
|
||||
mMikktSpaceTangents[i].normalize3();
|
||||
mMikktSpaceTangents[i].getF32ptr()[3] = w;
|
||||
}
|
||||
}
|
||||
|
||||
// cache optimize index buffer
|
||||
|
|
|
|||
|
|
@ -987,6 +987,12 @@ public:
|
|||
//whether or not face has been cache optimized
|
||||
BOOL mOptimized;
|
||||
|
||||
// if this is a mesh asset, scale and translation that were applied
|
||||
// when encoding the source mesh into a unit cube
|
||||
// used for regenerating tangents
|
||||
LLVector3 mNormalizedScale = LLVector3(1,1,1);
|
||||
LLVector3 mNormalizedTranslation;
|
||||
|
||||
private:
|
||||
BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
|
||||
BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE);
|
||||
|
|
|
|||
|
|
@ -349,6 +349,12 @@ void LLModel::normalizeVolumeFaces()
|
|||
mNormalizedScale.set(normalized_scale.getF32ptr());
|
||||
mNormalizedTranslation.set(trans.getF32ptr());
|
||||
mNormalizedTranslation *= -1.f;
|
||||
|
||||
for (auto& face : mVolumeFaces)
|
||||
{
|
||||
face.mNormalizedScale = mNormalizedScale;
|
||||
face.mNormalizedTranslation = mNormalizedTranslation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -764,7 +770,7 @@ LLSD LLModel::writeModel(
|
|||
|
||||
U32 vert_idx = 0;
|
||||
U32 norm_idx = 0;
|
||||
U32 tan_idx = 0;
|
||||
//U32 tan_idx = 0;
|
||||
U32 tc_idx = 0;
|
||||
|
||||
LLVector2* ftc = (LLVector2*) face.mTexCoords;
|
||||
|
|
@ -818,6 +824,7 @@ LLSD LLModel::writeModel(
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (face.mMikktSpaceTangents)
|
||||
{ //normals
|
||||
F32* tangent = face.mMikktSpaceTangents[j].getF32ptr();
|
||||
|
|
@ -833,6 +840,7 @@ LLSD LLModel::writeModel(
|
|||
tangents[tan_idx++] = buff[1];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//texcoord
|
||||
if (face.mTexCoords)
|
||||
|
|
@ -863,6 +871,9 @@ LLSD LLModel::writeModel(
|
|||
//write out face data
|
||||
mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue();
|
||||
mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue();
|
||||
mdl[model_names[idx]][i]["NormalizedScale"] = face.mNormalizedScale.getValue();
|
||||
mdl[model_names[idx]][i]["NormalizedTranslation"] = face.mNormalizedTranslation.getValue();
|
||||
|
||||
mdl[model_names[idx]][i]["Position"] = verts;
|
||||
|
||||
if (face.mNormals)
|
||||
|
|
|
|||
|
|
@ -980,8 +980,11 @@ BOOL LLGLSLShader::mapUniforms(const vector<LLStaticHashedString> * uniforms)
|
|||
static const GLuint BLOCKBINDING = 1; //picked by us
|
||||
//Get the index, similar to a uniform location
|
||||
GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, "ReflectionProbes");
|
||||
//Set this index to a binding index
|
||||
glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING);
|
||||
if (UBOBlockIndex != GL_INVALID_INDEX)
|
||||
{
|
||||
//Set this index to a binding index
|
||||
glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING);
|
||||
}
|
||||
}
|
||||
unbind();
|
||||
|
||||
|
|
|
|||
|
|
@ -643,6 +643,17 @@
|
|||
<key>Value</key>
|
||||
<string></string>
|
||||
</map>
|
||||
<key>PBRUploadFolder</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>All pbr uploads will be stored in this directory (UUID)</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>String</string>
|
||||
<key>Value</key>
|
||||
<string></string>
|
||||
</map>
|
||||
<key>TextureUploadFolder</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -69,18 +69,12 @@ uniform vec3 moon_dir;
|
|||
VARYING vec3 vary_position;
|
||||
VARYING vec4 vertex_color;
|
||||
VARYING vec2 vary_texcoord0;
|
||||
|
||||
#ifdef HAS_NORMAL_MAP
|
||||
VARYING vec3 vary_normal;
|
||||
VARYING vec3 vary_mat0;
|
||||
VARYING vec3 vary_mat1;
|
||||
VARYING vec3 vary_mat2;
|
||||
VARYING vec2 vary_texcoord1;
|
||||
#endif
|
||||
VARYING vec2 vary_texcoord2;
|
||||
VARYING vec3 vary_normal;
|
||||
VARYING vec3 vary_tangent;
|
||||
flat in float vary_sign;
|
||||
|
||||
#ifdef HAS_SPECULAR_MAP
|
||||
VARYING vec2 vary_texcoord2;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ALPHA_MASK
|
||||
uniform float minimum_alpha; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlphaCutoff()
|
||||
|
|
@ -204,17 +198,15 @@ void main()
|
|||
|
||||
vec3 base = vertex_color.rgb * albedo.rgb;
|
||||
|
||||
vec4 norm = texture2D(bumpMap, vary_texcoord1.xy);
|
||||
norm.xyz = normalize(norm.xyz * 2 - 1);
|
||||
vec3 vNt = texture2D(bumpMap, vary_texcoord1.xy).xyz*2.0-1.0;
|
||||
float sign = vary_sign;
|
||||
vec3 vN = vary_normal;
|
||||
vec3 vT = vary_tangent.xyz;
|
||||
|
||||
vec3 vB = sign * cross(vN, vT);
|
||||
vec3 norm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
|
||||
|
||||
vec3 tnorm = vec3(dot(norm.xyz,vary_mat0),
|
||||
dot(norm.xyz,vary_mat1),
|
||||
dot(norm.xyz,vary_mat2));
|
||||
|
||||
tnorm = normalize(tnorm.xyz);
|
||||
|
||||
tnorm *= gl_FrontFacing ? 1.0 : -1.0;
|
||||
norm.xyz = tnorm.xyz;
|
||||
norm *= gl_FrontFacing ? 1.0 : -1.0;
|
||||
|
||||
#ifdef HAS_SHADOW
|
||||
vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5;
|
||||
|
|
@ -276,20 +268,7 @@ void main()
|
|||
vec3 light = vec3(0);
|
||||
|
||||
// Punctual lights
|
||||
#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight( \
|
||||
reflect0, \
|
||||
reflect90, \
|
||||
alphaRough, \
|
||||
c_diff, \
|
||||
light_diffuse[i].rgb, \
|
||||
base.rgb, \
|
||||
pos.xyz, \
|
||||
v, \
|
||||
n, \
|
||||
light_position[i], \
|
||||
light_direction[i].xyz, \
|
||||
light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, \
|
||||
light_attenuation[i].z, light_attenuation[i].w );
|
||||
#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight( reflect0, reflect90, alphaRough, c_diff, light_diffuse[i].rgb, base.rgb, pos.xyz, v, n, light_position[i], light_direction[i].xyz, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w );
|
||||
|
||||
LIGHT_LOOP(1)
|
||||
LIGHT_LOOP(2)
|
||||
|
|
|
|||
|
|
@ -54,29 +54,20 @@ uniform float near_clip;
|
|||
ATTRIBUTE vec3 position;
|
||||
ATTRIBUTE vec4 diffuse_color;
|
||||
ATTRIBUTE vec3 normal;
|
||||
ATTRIBUTE vec2 texcoord0;
|
||||
|
||||
|
||||
#ifdef HAS_NORMAL_MAP
|
||||
ATTRIBUTE vec4 tangent;
|
||||
ATTRIBUTE vec2 texcoord0;
|
||||
ATTRIBUTE vec2 texcoord1;
|
||||
|
||||
VARYING vec3 vary_mat0;
|
||||
VARYING vec3 vary_mat1;
|
||||
VARYING vec3 vary_mat2;
|
||||
|
||||
VARYING vec2 vary_texcoord1;
|
||||
#else
|
||||
VARYING vec3 vary_normal;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SPECULAR_MAP
|
||||
ATTRIBUTE vec2 texcoord2;
|
||||
VARYING vec2 vary_texcoord2;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
VARYING vec4 vertex_color;
|
||||
VARYING vec2 vary_texcoord0;
|
||||
VARYING vec2 vary_texcoord1;
|
||||
VARYING vec2 vary_texcoord2;
|
||||
VARYING vec3 vary_normal;
|
||||
VARYING vec3 vary_tangent;
|
||||
flat out float vary_sign;
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
|
|
@ -99,41 +90,21 @@ void main()
|
|||
#endif
|
||||
|
||||
vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
|
||||
|
||||
#ifdef HAS_NORMAL_MAP
|
||||
vary_texcoord1 = (texture_matrix0 * vec4(texcoord1,0,1)).xy;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SPECULAR_MAP
|
||||
vary_texcoord2 = (texture_matrix0 * vec4(texcoord2,0,1)).xy;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SKIN
|
||||
vec3 n = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
|
||||
#ifdef HAS_NORMAL_MAP
|
||||
vec3 t = normalize((mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz);
|
||||
vec3 b = cross(n, t)*tangent.w;
|
||||
|
||||
vary_mat0 = vec3(t.x, b.x, n.x);
|
||||
vary_mat1 = vec3(t.y, b.y, n.y);
|
||||
vary_mat2 = vec3(t.z, b.z, n.z);
|
||||
#else //HAS_NORMAL_MAP
|
||||
vary_normal = n;
|
||||
#endif //HAS_NORMAL_MAP
|
||||
vec3 n = (mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz;
|
||||
vec3 t = (mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz;
|
||||
#else //HAS_SKIN
|
||||
vec3 n = normalize(normal_matrix * normal);
|
||||
#ifdef HAS_NORMAL_MAP
|
||||
vec3 t = normalize(normal_matrix * tangent.xyz);
|
||||
vec3 b = cross(n,t)*tangent.w;
|
||||
|
||||
vary_mat0 = vec3(t.x, b.x, n.x);
|
||||
vary_mat1 = vec3(t.y, b.y, n.y);
|
||||
vary_mat2 = vec3(t.z, b.z, n.z);
|
||||
#else //HAS_NORMAL_MAP
|
||||
vary_normal = n;
|
||||
#endif //HAS_NORMAL_MAP
|
||||
vec3 n = normal_matrix * normal;
|
||||
vec3 t = normal_matrix * tangent.xyz;
|
||||
#endif //HAS_SKIN
|
||||
|
||||
|
||||
vary_tangent = normalize(t);
|
||||
vary_sign = tangent.w;
|
||||
vary_normal = normalize(n);
|
||||
|
||||
vertex_color = diffuse_color;
|
||||
|
||||
#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
|
||||
|
|
|
|||
|
|
@ -644,6 +644,8 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture, BOOL ba
|
|||
params.mGroup->rebuildMesh();
|
||||
}
|
||||
|
||||
LLGLDisable cull(params.mGLTFMaterial && params.mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0);
|
||||
|
||||
LLGLEnableFunc stencil_test(GL_STENCIL_TEST, params.mSelected, &LLGLCommonFunc::selected_stencil_test);
|
||||
|
||||
params.mVertexBuffer->setBufferFast(mask);
|
||||
|
|
|
|||
|
|
@ -418,62 +418,76 @@ bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material)
|
|||
{
|
||||
bool tex_setup = false;
|
||||
|
||||
if (deferred_render && use_material && current_shader)
|
||||
if (draw->mGLTFMaterial)
|
||||
{
|
||||
if (draw->mNormalMap)
|
||||
{
|
||||
draw->mNormalMap->addTextureStats(draw->mVSize);
|
||||
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap);
|
||||
}
|
||||
|
||||
if (draw->mSpecularMap)
|
||||
{
|
||||
draw->mSpecularMap->addTextureStats(draw->mVSize);
|
||||
current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap);
|
||||
}
|
||||
if (draw->mTextureMatrix)
|
||||
{
|
||||
tex_setup = true;
|
||||
gGL.getTexUnit(0)->activate();
|
||||
gGL.matrixMode(LLRender::MM_TEXTURE);
|
||||
gGL.loadMatrix((GLfloat*)draw->mTextureMatrix->mMatrix);
|
||||
gPipeline.mTextureMatrixOps++;
|
||||
}
|
||||
}
|
||||
else if (current_shader == simple_shader || current_shader == simple_shader->mRiggedVariant)
|
||||
else
|
||||
{
|
||||
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
|
||||
current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
|
||||
}
|
||||
if (draw->mTextureList.size() > 1)
|
||||
{
|
||||
for (U32 i = 0; i < draw->mTextureList.size(); ++i)
|
||||
{
|
||||
if (draw->mTextureList[i].notNull())
|
||||
{
|
||||
gGL.getTexUnit(i)->bindFast(draw->mTextureList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //not batching textures or batch has only 1 texture -- might need a texture matrix
|
||||
if (draw->mTexture.notNull())
|
||||
{
|
||||
if (use_material)
|
||||
{
|
||||
current_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, draw->mTexture);
|
||||
}
|
||||
else
|
||||
{
|
||||
gGL.getTexUnit(0)->bindFast(draw->mTexture);
|
||||
}
|
||||
if (deferred_render && use_material && current_shader)
|
||||
{
|
||||
if (draw->mNormalMap)
|
||||
{
|
||||
draw->mNormalMap->addTextureStats(draw->mVSize);
|
||||
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, draw->mNormalMap);
|
||||
}
|
||||
|
||||
if (draw->mTextureMatrix)
|
||||
{
|
||||
tex_setup = true;
|
||||
gGL.getTexUnit(0)->activate();
|
||||
gGL.matrixMode(LLRender::MM_TEXTURE);
|
||||
gGL.loadMatrix((GLfloat*) draw->mTextureMatrix->mMatrix);
|
||||
gPipeline.mTextureMatrixOps++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gGL.getTexUnit(0)->unbindFast(LLTexUnit::TT_TEXTURE);
|
||||
}
|
||||
}
|
||||
if (draw->mSpecularMap)
|
||||
{
|
||||
draw->mSpecularMap->addTextureStats(draw->mVSize);
|
||||
current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, draw->mSpecularMap);
|
||||
}
|
||||
}
|
||||
else if (current_shader == simple_shader || current_shader == simple_shader->mRiggedVariant)
|
||||
{
|
||||
current_shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
|
||||
current_shader->bindTexture(LLShaderMgr::SPECULAR_MAP, LLViewerFetchedTexture::sWhiteImagep);
|
||||
}
|
||||
if (draw->mTextureList.size() > 1)
|
||||
{
|
||||
for (U32 i = 0; i < draw->mTextureList.size(); ++i)
|
||||
{
|
||||
if (draw->mTextureList[i].notNull())
|
||||
{
|
||||
gGL.getTexUnit(i)->bindFast(draw->mTextureList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //not batching textures or batch has only 1 texture -- might need a texture matrix
|
||||
if (draw->mTexture.notNull())
|
||||
{
|
||||
if (use_material)
|
||||
{
|
||||
current_shader->bindTexture(LLShaderMgr::DIFFUSE_MAP, draw->mTexture);
|
||||
}
|
||||
else
|
||||
{
|
||||
gGL.getTexUnit(0)->bindFast(draw->mTexture);
|
||||
}
|
||||
|
||||
if (draw->mTextureMatrix)
|
||||
{
|
||||
tex_setup = true;
|
||||
gGL.getTexUnit(0)->activate();
|
||||
gGL.matrixMode(LLRender::MM_TEXTURE);
|
||||
gGL.loadMatrix((GLfloat*)draw->mTextureMatrix->mMatrix);
|
||||
gPipeline.mTextureMatrixOps++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gGL.getTexUnit(0)->unbindFast(LLTexUnit::TT_TEXTURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tex_setup;
|
||||
}
|
||||
|
|
@ -775,36 +789,37 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged)
|
|||
{
|
||||
target_shader = fullbright_shader;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.mAvatar != nullptr)
|
||||
{
|
||||
target_shader = target_shader->mRiggedVariant;
|
||||
}
|
||||
|
||||
if (current_shader != target_shader)
|
||||
{// If we need shaders, and we're not ALREADY using the proper shader, then bind it
|
||||
// (this way we won't rebind shaders unnecessarily).
|
||||
target_shader->bind();
|
||||
}
|
||||
|
||||
LLVector4 spec_color(1, 1, 1, 1);
|
||||
F32 env_intensity = 0.0f;
|
||||
F32 brightness = 1.0f;
|
||||
if (params.mAvatar != nullptr)
|
||||
{
|
||||
target_shader = target_shader->mRiggedVariant;
|
||||
}
|
||||
|
||||
// We have a material. Supply the appropriate data here.
|
||||
if (mat && deferred_render)
|
||||
{
|
||||
spec_color = params.mSpecColor;
|
||||
env_intensity = params.mEnvIntensity;
|
||||
brightness = params.mFullbright ? 1.f : 0.f;
|
||||
}
|
||||
if (current_shader != target_shader)
|
||||
{// If we need shaders, and we're not ALREADY using the proper shader, then bind it
|
||||
// (this way we won't rebind shaders unnecessarily).
|
||||
target_shader->bind();
|
||||
}
|
||||
|
||||
if (current_shader)
|
||||
{
|
||||
current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]);
|
||||
current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env_intensity);
|
||||
current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, brightness);
|
||||
LLVector4 spec_color(1, 1, 1, 1);
|
||||
F32 env_intensity = 0.0f;
|
||||
F32 brightness = 1.0f;
|
||||
|
||||
// We have a material. Supply the appropriate data here.
|
||||
if (mat && deferred_render)
|
||||
{
|
||||
spec_color = params.mSpecColor;
|
||||
env_intensity = params.mEnvIntensity;
|
||||
brightness = params.mFullbright ? 1.f : 0.f;
|
||||
}
|
||||
|
||||
if (current_shader)
|
||||
{
|
||||
current_shader->uniform4f(LLShaderMgr::SPECULAR_COLOR, spec_color.mV[0], spec_color.mV[1], spec_color.mV[2], spec_color.mV[3]);
|
||||
current_shader->uniform1f(LLShaderMgr::ENVIRONMENT_INTENSITY, env_intensity);
|
||||
current_shader->uniform1f(LLShaderMgr::EMISSIVE_BRIGHTNESS, brightness);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.mGroup)
|
||||
|
|
|
|||
|
|
@ -624,6 +624,7 @@ BOOL LLFloaterPreference::postBuild()
|
|||
gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeMaturity, this));
|
||||
|
||||
gSavedPerAccountSettings.getControl("ModelUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeModelFolder, this));
|
||||
gSavedPerAccountSettings.getControl("PBRUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangePBRFolder, this));
|
||||
gSavedPerAccountSettings.getControl("TextureUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeTextureFolder, this));
|
||||
gSavedPerAccountSettings.getControl("SoundUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeSoundFolder, this));
|
||||
gSavedPerAccountSettings.getControl("AnimationUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeAnimationFolder, this));
|
||||
|
|
@ -1139,6 +1140,7 @@ void LLFloaterPreference::onOpen(const LLSD& key)
|
|||
onChangeMaturity();
|
||||
|
||||
onChangeModelFolder();
|
||||
onChangePBRFolder();
|
||||
onChangeTextureFolder();
|
||||
onChangeSoundFolder();
|
||||
onChangeAnimationFolder();
|
||||
|
|
@ -2799,6 +2801,14 @@ void LLFloaterPreference::onChangeModelFolder()
|
|||
}
|
||||
}
|
||||
|
||||
void LLFloaterPreference::onChangePBRFolder()
|
||||
{
|
||||
if (gInventory.isInventoryUsable())
|
||||
{
|
||||
getChild<LLTextBox>("upload_pbr")->setText(get_category_path(LLFolderType::FT_MATERIAL));
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterPreference::onChangeTextureFolder()
|
||||
{
|
||||
if (gInventory.isInventoryUsable())
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ public:
|
|||
void applyResolution();
|
||||
void onChangeMaturity();
|
||||
void onChangeModelFolder();
|
||||
void onChangePBRFolder();
|
||||
void onChangeTextureFolder();
|
||||
void onChangeSoundFolder();
|
||||
void onChangeAnimationFolder();
|
||||
|
|
|
|||
|
|
@ -696,6 +696,11 @@ const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::
|
|||
cat_id = LLUUID(gSavedPerAccountSettings.getString("AnimationUploadFolder"));
|
||||
break;
|
||||
}
|
||||
case LLFolderType::FT_MATERIAL:
|
||||
{
|
||||
cat_id = LLUUID(gSavedPerAccountSettings.getString("PBRUploadFolder"));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1754,6 +1754,10 @@ void LLInventoryPanel::fileUploadLocation(const LLSD& userdata)
|
|||
{
|
||||
gSavedPerAccountSettings.setString("AnimationUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString());
|
||||
}
|
||||
else if (param == "pbr_material")
|
||||
{
|
||||
gSavedPerAccountSettings.setString("PBRUploadFolder", LLFolderBridge::sSelf.get()->getUUID().asString());
|
||||
}
|
||||
}
|
||||
|
||||
void LLInventoryPanel::purgeSelectedItems()
|
||||
|
|
@ -2416,6 +2420,7 @@ namespace LLInitParam
|
|||
declare(LLFolderType::lookup(LLFolderType::FT_OUTBOX) , LLFolderType::FT_OUTBOX);
|
||||
declare(LLFolderType::lookup(LLFolderType::FT_BASIC_ROOT) , LLFolderType::FT_BASIC_ROOT);
|
||||
declare(LLFolderType::lookup(LLFolderType::FT_SETTINGS) , LLFolderType::FT_SETTINGS);
|
||||
declare(LLFolderType::lookup(LLFolderType::FT_MATERIAL) , LLFolderType::FT_MATERIAL);
|
||||
declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_LISTINGS) , LLFolderType::FT_MARKETPLACE_LISTINGS);
|
||||
declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_STOCK), LLFolderType::FT_MARKETPLACE_STOCK);
|
||||
declare(LLFolderType::lookup(LLFolderType::FT_MARKETPLACE_VERSION), LLFolderType::FT_MARKETPLACE_VERSION);
|
||||
|
|
|
|||
|
|
@ -761,8 +761,8 @@ bool LLMaterialEditor::saveIfNeeded()
|
|||
tid.generate(); // timestamp-based randomization + uniquification
|
||||
LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
|
||||
std::string res_desc = buildMaterialDescription();
|
||||
U32 next_owner_perm = LLPermissions::DEFAULT.getMaskNextOwner();
|
||||
LLUUID parent = gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL);
|
||||
U32 next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Uploads");
|
||||
LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL);
|
||||
const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ?
|
||||
|
||||
create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, mMaterialName, res_desc,
|
||||
|
|
|
|||
|
|
@ -2658,6 +2658,15 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
|
||||
LLModel* target_model = mModel[lod][mdl_idx];
|
||||
|
||||
// carry over normalized transform into simplified model
|
||||
for (int i = 0; i < base->getNumVolumeFaces(); ++i)
|
||||
{
|
||||
LLVolumeFace& src = base->getVolumeFace(i);
|
||||
LLVolumeFace& dst = target_model->getVolumeFace(i);
|
||||
dst.mNormalizedScale = src.mNormalizedScale;
|
||||
dst.mNormalizedTranslation = src.mNormalizedTranslation;
|
||||
}
|
||||
|
||||
S32 model_meshopt_mode = meshopt_mode;
|
||||
|
||||
// Ideally this should run not per model,
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, cons
|
|||
|
||||
void LLSettingsVOBase::createInventoryItem(const LLSettingsBase::ptr_t &settings, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback)
|
||||
{
|
||||
U32 nextOwnerPerm = LLPermissions::DEFAULT.getMaskNextOwner();
|
||||
U32 nextOwnerPerm = LLFloaterPerms::getNextOwnerPerms("Settings");
|
||||
createInventoryItem(settings, nextOwnerPerm, parent_id, settings_name, callback);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -568,6 +568,8 @@ void LLFloaterTexturePicker::draw()
|
|||
|
||||
if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID))
|
||||
{
|
||||
// TODO: Fix this! Picker is not warrantied to be connected to a selection
|
||||
// LLSelectMgr shouldn't be used in texture picker
|
||||
LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
|
||||
if (obj)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ LLViewerFolderDictionary::LLViewerFolderDictionary()
|
|||
addEntry(LLFolderType::FT_MY_OUTFITS, new ViewerFolderEntry("My Outfits", "Inv_LookFolderOpen", "Inv_LookFolderClosed", TRUE, true));
|
||||
addEntry(LLFolderType::FT_MESH, new ViewerFolderEntry("Meshes", "Inv_MeshesOpen", "Inv_MeshesClosed", FALSE, true));
|
||||
addEntry(LLFolderType::FT_SETTINGS, new ViewerFolderEntry("Settings", "Inv_SettingsOpen", "Inv_SettingsClosed", FALSE, true));
|
||||
addEntry(LLFolderType::FT_MATERIAL, new ViewerFolderEntry("Materials", "Inv_SysOpen", "Inv_SysClosed", FALSE, true));
|
||||
|
||||
bool boxes_invisible = !gSavedSettings.getBOOL("InventoryOutboxMakeVisible");
|
||||
addEntry(LLFolderType::FT_INBOX, new ViewerFolderEntry("Received Items", "Inv_InboxOpen", "Inv_InboxClosed", FALSE, true));
|
||||
|
|
|
|||
|
|
@ -5508,6 +5508,11 @@ bool can_batch_texture(LLFace* facep)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (facep->getTextureEntry()->getGLTFMaterial() != nullptr)
|
||||
{ // PBR materials break indexed texture batching
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -5814,6 +5819,14 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
|
|||
draw_info->mNormalMap = vobj->getGLTFNormalMap(te);
|
||||
draw_info->mSpecularMap = vobj->getGLTFMetallicRoughnessMap(te);
|
||||
draw_info->mEmissiveMap = vobj->getGLTFEmissiveMap(te);
|
||||
if (draw_info->mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
|
||||
{
|
||||
draw_info->mAlphaMaskCutoff = gltf_mat->mAlphaCutoff * gltf_mat->mAlbedoColor.mV[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
draw_info->mAlphaMaskCutoff = 1.f;
|
||||
}
|
||||
}
|
||||
else if (mat)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10108,9 +10108,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
|
|||
LLRenderPass::PASS_NORMMAP,
|
||||
LLRenderPass::PASS_NORMMAP_EMISSIVE,
|
||||
LLRenderPass::PASS_NORMSPEC,
|
||||
LLRenderPass::PASS_NORMSPEC_EMISSIVE,
|
||||
LLRenderPass::PASS_PBR_OPAQUE, // NOTE: Assumes PASS_PBR_OPAQUE_RIGGED is consecutive
|
||||
//LLRenderPass::PASS_PBR_OPAQUE_RIGGED,
|
||||
LLRenderPass::PASS_NORMSPEC_EMISSIVE
|
||||
};
|
||||
|
||||
LLGLEnable cull(GL_CULL_FACE);
|
||||
|
|
@ -10194,7 +10192,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
|
|||
|
||||
if (use_shader)
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow geom"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_GEOM);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow geom");
|
||||
|
||||
gDeferredShadowProgram.unbind();
|
||||
renderGeomShadow(shadow_cam);
|
||||
|
|
@ -10203,13 +10201,13 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
|
|||
}
|
||||
else
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow geom"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_GEOM);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow geom");
|
||||
|
||||
renderGeomShadow(shadow_cam);
|
||||
}
|
||||
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha");
|
||||
LL_PROFILE_GPU_ZONE("shadow alpha");
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
|
|
@ -10225,19 +10223,19 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
|
|||
LLVertexBuffer::MAP_TEXTURE_INDEX;
|
||||
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha masked"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_MASKED);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha masked");
|
||||
renderMaskedObjects(LLRenderPass::PASS_ALPHA_MASK, mask, TRUE, TRUE, rigged);
|
||||
}
|
||||
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha blend"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_BLEND);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha blend");
|
||||
LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(0.598f);
|
||||
renderAlphaObjects(mask, TRUE, TRUE, rigged);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow fullbright alpha masked"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_FULLBRIGHT_ALPHA_MASKED);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow fullbright alpha masked");
|
||||
gDeferredShadowFullbrightAlphaMaskProgram.bind(rigged);
|
||||
LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
|
||||
LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, environment.getIsSunUp() ? 1 : 0);
|
||||
|
|
@ -10246,7 +10244,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
|
|||
|
||||
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha grass"); //LL_RECORD_BLOCK_TIME(FTM_SHADOW_ALPHA_GRASS);
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("shadow alpha grass");
|
||||
gDeferredTreeShadowProgram.bind(rigged);
|
||||
if (i == 0)
|
||||
{
|
||||
|
|
@ -10259,6 +10257,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
|
|||
renderMaskedObjects(LLRenderPass::PASS_MATERIAL_ALPHA_MASK, no_idx_mask, true, false, rigged);
|
||||
renderMaskedObjects(LLRenderPass::PASS_SPECMAP_MASK, no_idx_mask, true, false, rigged);
|
||||
renderMaskedObjects(LLRenderPass::PASS_NORMMAP_MASK, no_idx_mask, true, false, rigged);
|
||||
renderMaskedObjects(LLRenderPass::PASS_PBR_OPAQUE, no_idx_mask, true, false, rigged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -764,6 +764,14 @@
|
|||
function="Inventory.FileUploadLocation"
|
||||
parameter="model" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="PBR material uploads"
|
||||
layout="topleft"
|
||||
name="PBR uploads">
|
||||
<menu_item_call.on_click
|
||||
function="Inventory.FileUploadLocation"
|
||||
parameter="pbr_material" />
|
||||
</menu_item_call>
|
||||
</menu>
|
||||
<menu_item_separator
|
||||
layout="topleft" />
|
||||
|
|
|
|||
|
|
@ -122,6 +122,31 @@
|
|||
width="370"
|
||||
word_wrap="true"/>
|
||||
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
follows="left|top"
|
||||
height="12"
|
||||
layout="topleft"
|
||||
left="37"
|
||||
name="title_pbr"
|
||||
top_pad="7"
|
||||
width="100">
|
||||
PBR Materials
|
||||
</text>
|
||||
<text
|
||||
type="string"
|
||||
use_ellipses="true"
|
||||
follows="left|top"
|
||||
height="27"
|
||||
layout="topleft"
|
||||
font.style="BOLD"
|
||||
left="37"
|
||||
name="upload_pbr"
|
||||
top_pad="5"
|
||||
width="370"
|
||||
word_wrap="true"/>
|
||||
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
|
|
|
|||
|
|
@ -937,6 +937,7 @@ If you continue to receive this message, please contact Second Life support for
|
|||
<string name="InvFolder Received Items">Received Items</string>
|
||||
<string name="InvFolder Merchant Outbox">Merchant Outbox</string>
|
||||
<string name="InvFolder Settings">Settings</string>
|
||||
<string name="InvFolder Materials">Materials</string>
|
||||
|
||||
<!-- are used for Friends and Friends/All folders in Inventory "Calling cards" folder. See EXT-694-->
|
||||
<string name="InvFolder Friends">Friends</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue