summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorBrad Linden <brad@lindenlab.com>2024-06-12 10:31:27 -0700
committerBrad Linden <brad@lindenlab.com>2024-06-12 10:31:27 -0700
commitd12c897bfc77a800fcec5e22a21c9d0344b0d0bc (patch)
tree18752bdf92b12fce68642c94acec2d78b9c43fbe /indra
parent9775d7ea10ff87d913b1ba361a9204f961cd9c3f (diff)
parentf0de2ba6340e0d540aa70ac0086defde52cf60af (diff)
Merge remote-tracking branch 'origin/project/gltf_development' into brad/maint-a-merge-to-gltf-dev
# Conflicts: # indra/newview/gltf/primitive.cpp
Diffstat (limited to 'indra')
-rw-r--r--indra/llrender/llglslshader.h4
-rw-r--r--indra/llrender/llrender.cpp2
-rw-r--r--indra/llrender/llshadermgr.cpp6
-rw-r--r--indra/llrender/llshadermgr.h6
-rw-r--r--indra/llrender/llvertexbuffer.cpp10
-rw-r--r--indra/llrender/llvertexbuffer.h3
-rw-r--r--indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl117
-rw-r--r--indra/newview/gltf/asset.cpp69
-rw-r--r--indra/newview/gltf/asset.h23
-rw-r--r--indra/newview/gltf/buffer_util.h37
-rw-r--r--indra/newview/gltf/primitive.cpp201
-rw-r--r--indra/newview/gltf/primitive.h3
-rw-r--r--indra/newview/gltfscenemanager.cpp70
-rw-r--r--indra/newview/llviewershadermgr.cpp10
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml9
-rw-r--r--indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml202
17 files changed, 604 insertions, 187 deletions
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 3b4224cbfd..9339e7998d 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -328,14 +328,16 @@ public:
// bit 0 = alpha mode blend (1) or opaque (0)
// bit 1 = rigged (1) or static (0)
// bit 2 = unlit (1) or lit (0)
+ // bit 3 = single (0) or multi (1) uv coordinates
struct GLTFVariant
{
constexpr static U8 ALPHA_BLEND = 1;
constexpr static U8 RIGGED = 2;
constexpr static U8 UNLIT = 4;
+ constexpr static U8 MULTI_UV = 8;
};
- constexpr static U8 NUM_GLTF_VARIANTS = 8;
+ constexpr static U8 NUM_GLTF_VARIANTS = 16;
std::vector<LLGLSLShader> mGLTFVariants;
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 51028e5667..cfefde3acc 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -1686,7 +1686,7 @@ void LLRender::flush()
if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
{
- vb->setTexCoordData(mTexcoordsp.get());
+ vb->setTexCoord0Data(mTexcoordsp.get());
}
if (attribute_mask & LLVertexBuffer::MAP_COLOR)
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index c3cb015556..634be615e6 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1178,7 +1178,13 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("texture_base_color_transform"); // (GLTF)
mReservedUniforms.push_back("texture_normal_transform"); // (GLTF)
mReservedUniforms.push_back("texture_metallic_roughness_transform"); // (GLTF)
+ mReservedUniforms.push_back("texture_occlusion_transform"); // (GLTF)
mReservedUniforms.push_back("texture_emissive_transform"); // (GLTF)
+ mReservedUniforms.push_back("base_color_texcoord"); // (GLTF)
+ mReservedUniforms.push_back("emissive_texcoord"); // (GLTF)
+ mReservedUniforms.push_back("normal_texcoord"); // (GLTF)
+ mReservedUniforms.push_back("metallic_roughness_texcoord"); // (GLTF)
+ mReservedUniforms.push_back("occlusion_texcoord"); // (GLTF)
mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF)
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 53e3d010db..c00aff3a9a 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -56,7 +56,13 @@ public:
TEXTURE_BASE_COLOR_TRANSFORM, // "texture_base_color_transform" (GLTF)
TEXTURE_NORMAL_TRANSFORM, // "texture_normal_transform" (GLTF)
TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_transform" (GLTF)
+ TEXTURE_OCCLUSION_TRANSFORM, // "texture_occlusion_transform" (GLTF)
TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF)
+ BASE_COLOR_TEXCOORD, // "base_color_texcoord" (GLTF)
+ EMISSIVE_TEXCOORD, // "emissive_texcoord" (GLTF)
+ NORMAL_TEXCOORD, // "normal_texcoord" (GLTF)
+ METALLIC_ROUGHNESS_TEXCOORD, // "metallic_roughness_texcoord" (GLTF)
+ OCCLUSION_TEXCOORD, // "occlusion_texcoord" (GLTF)
TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF)
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 5bace72ffd..f82ec30242 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -1633,7 +1633,7 @@ void LLVertexBuffer::setPositionData(const LLVector4a* data)
flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData);
}
-void LLVertexBuffer::setTexCoordData(const LLVector2* data)
+void LLVertexBuffer::setTexCoord0Data(const LLVector2* data)
{
#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
@@ -1641,6 +1641,14 @@ void LLVertexBuffer::setTexCoordData(const LLVector2* data)
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data, mMappedData);
}
+void LLVertexBuffer::setTexCoord1Data(const LLVector2* data)
+{
+#if !LL_DARWIN
+ llassert(sGLRenderBuffer == mGLBuffer);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD1], mOffsets[TYPE_TEXCOORD1] + sTypeSize[TYPE_TEXCOORD1] * getNumVerts() - 1, (U8*)data, mMappedData);
+}
+
void LLVertexBuffer::setColorData(const LLColor4U* data)
{
#if !LL_DARWIN
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index 94339191a4..66a7f2bf26 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -196,7 +196,8 @@ public:
void setTangentData(const LLVector4a* data);
void setWeight4Data(const LLVector4a* data);
void setJointData(const U64* data);
- void setTexCoordData(const LLVector2* data);
+ void setTexCoord0Data(const LLVector2* data);
+ void setTexCoord1Data(const LLVector2* data);
void setColorData(const LLColor4U* data);
void setIndexData(const U16* data);
void setIndexData(const U32* data);
diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl
index d71a3fad99..789c00259b 100644
--- a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl
+++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl
@@ -37,8 +37,8 @@ uniform sampler2D emissiveMap;
uniform vec3 emissiveColor;
in vec3 vary_position;
in vec4 vertex_color;
-in vec2 base_color_texcoord;
-in vec2 emissive_texcoord;
+in vec2 base_color_uv;
+in vec2 emissive_uv;
uniform float minimum_alpha;
void mirrorClip(vec3 pos);
@@ -59,8 +59,9 @@ uniform float roughnessFactor;
in vec3 vary_normal;
in vec3 vary_tangent;
flat in float vary_sign;
-in vec2 normal_texcoord;
-in vec2 metallic_roughness_texcoord;
+in vec2 normal_uv;
+in vec2 metallic_roughness_uv;
+in vec2 occlusion_uv;
#endif
// ==================================
@@ -164,7 +165,7 @@ void main()
vec3 pos = vary_position;
mirrorClip(pos);
- vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba;
+ vec4 basecolor = texture(diffuseMap, base_color_uv.xy).rgba;
basecolor.rgb = srgb_to_linear(basecolor.rgb);
basecolor *= vertex_color;
@@ -174,7 +175,7 @@ void main()
}
vec3 emissive = emissiveColor;
- emissive *= srgb_to_linear(texture(emissiveMap, emissive_texcoord.xy).rgb);
+ emissive *= srgb_to_linear(texture(emissiveMap, emissive_uv.xy).rgb);
// ==================================
// ==================================
@@ -184,7 +185,7 @@ void main()
// ==================================
#ifndef UNLIT
// from mikktspace.com
- vec3 vNt = texture(normalMap, normal_texcoord.xy).xyz*2.0-1.0;
+ vec3 vNt = texture(normalMap, normal_uv.xy).xyz*2.0-1.0;
float sign = vary_sign;
vec3 vN = vary_normal;
vec3 vT = vary_tangent.xyz;
@@ -198,8 +199,8 @@ void main()
// occlusion 1.0
// roughness 0.0
// metal 0.0
- vec3 orm = texture(metallicRoughnessMap, metallic_roughness_texcoord.xy).rgb;
- orm.r = texture(occlusionMap, metallic_roughness_texcoord.xy).r;
+ vec3 orm = texture(metallicRoughnessMap, metallic_roughness_uv.xy).rgb;
+ orm.r = texture(occlusionMap, occlusion_uv.xy).r;
orm.g *= roughnessFactor;
orm.b *= metallicFactor;
#endif
diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl
index f123c29101..aac3dc917f 100644
--- a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl
+++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl
@@ -33,33 +33,97 @@ uniform mat4 projection_matrix;
uniform mat3 normal_matrix;
uniform mat4 modelview_projection_matrix;
#endif
-uniform mat4 texture_matrix0;
uniform vec4[2] texture_base_color_transform;
uniform vec4[2] texture_normal_transform;
uniform vec4[2] texture_metallic_roughness_transform;
uniform vec4[2] texture_emissive_transform;
+uniform vec4[2] texture_occlusion_transform;
in vec3 position;
in vec4 diffuse_color;
in vec2 texcoord0;
-out vec2 base_color_texcoord;
-out vec2 emissive_texcoord;
+out vec2 base_color_uv;
+out vec2 emissive_uv;
out vec4 vertex_color;
out vec3 vary_position;
#ifndef UNLIT
in vec3 normal;
in vec4 tangent;
-out vec2 normal_texcoord;
-out vec2 metallic_roughness_texcoord;
+out vec2 normal_uv;
+out vec2 metallic_roughness_uv;
+out vec2 occlusion_uv;
out vec3 vary_tangent;
flat out float vary_sign;
out vec3 vary_normal;
-vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
#endif
-vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
+#ifdef MULTI_UV
+in vec2 texcoord1;
+uniform int base_color_texcoord;
+uniform int emissive_texcoord;
+#ifndef UNLIT
+uniform int normal_texcoord;
+uniform int metallic_roughness_texcoord;
+uniform int occlusion_texcoord;
+#endif
+#endif
+
+vec2 gltf_texture_transform(vec2 texcoord, vec4[2] p)
+{
+ texcoord.y = 1.0 - texcoord.y;
+
+ vec2 Scale = p[0].xy;
+ float Rotation = -p[0].z;
+ vec2 Offset = vec2(p[0].w, p[1].x);
+
+ mat3 translation = mat3(1,0,0, 0,1,0, Offset.x, Offset.y, 1);
+ mat3 rotation = mat3(
+ cos(Rotation), sin(Rotation), 0,
+ -sin(Rotation), cos(Rotation), 0,
+ 0, 0, 1);
+
+ mat3 scale = mat3(Scale.x,0,0, 0,Scale.y,0, 0,0,1);
+
+ mat3 matrix = translation * rotation * scale;
+
+ vec2 uvTransformed = ( matrix * vec3(texcoord.xy, 1) ).xy;
+
+ uvTransformed.y = 1.0 - uvTransformed.y;
+
+ return uvTransformed;
+}
+
+#ifndef UNLIT
+vec3 gltf_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform)
+{ //derived from tangent_space_transform in textureUtilV.glsl
+ vec2 weights = vec2(0, 1);
+
+ // Convert to left-handed coordinate system
+ weights.y = -weights.y;
+
+ // Apply KHR_texture_transform (rotation only)
+ float khr_rotation = khr_gltf_transform[0].z;
+ mat2 khr_rotation_mat = mat2(
+ cos(khr_rotation),-sin(khr_rotation),
+ sin(khr_rotation), cos(khr_rotation)
+ );
+ weights = khr_rotation_mat * weights;
+
+ // Convert back to right-handed coordinate system
+ weights.y = -weights.y;
+
+ // Similar to the MikkTSpace-compatible method of extracting the binormal
+ // from the normal and tangent, as seen in the fragment shader
+ vec3 vertex_binormal = vertex_tangent.w * cross(vertex_normal, vertex_tangent.xyz);
+
+ return (weights.x * vertex_binormal.xyz) + (weights.y * vertex_tangent.xyz);
+
+ return vertex_tangent.xyz;
+}
+#endif
+
#ifdef ALPHA_BLEND
@@ -136,14 +200,41 @@ void main()
gl_Position = vert;
#endif
- base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
- emissive_texcoord = texture_transform(texcoord0, texture_emissive_transform, texture_matrix0);
+ vec2 bcuv;
+ vec2 emuv;
+
+#ifdef MULTI_UV
+ vec2 uv[2];
+ uv[0] = texcoord0;
+ uv[1] = texcoord1;
+
+ bcuv = uv[base_color_texcoord];
+ emuv = uv[emissive_texcoord];
+#else
+ bcuv = texcoord0;
+ emuv = texcoord0;
+#endif
+
+ base_color_uv = gltf_texture_transform(bcuv, texture_base_color_transform);
+ emissive_uv = gltf_texture_transform(emuv, texture_emissive_transform);
#ifndef UNLIT
- normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);
- metallic_roughness_texcoord = texture_transform(texcoord0, texture_metallic_roughness_transform, texture_matrix0);
+ vec2 normuv;
+ vec2 rmuv;
+ vec2 ouv;
+#ifdef MULTI_UV
+ normuv = uv[normal_texcoord];
+ rmuv = uv[metallic_roughness_texcoord];
+ ouv = uv[occlusion_texcoord];
+#else
+ normuv = texcoord0;
+ rmuv = texcoord0;
+ ouv = texcoord0;
+#endif
+ normal_uv = gltf_texture_transform(normuv, texture_normal_transform);
+ metallic_roughness_uv = gltf_texture_transform(rmuv, texture_metallic_roughness_transform);
+ occlusion_uv = gltf_texture_transform(ouv, texture_occlusion_transform);
#endif
-
#ifndef UNLIT
#ifdef HAS_SKIN
@@ -155,7 +246,7 @@ void main()
#endif
n = normalize(n);
- vary_tangent = normalize(tangent_space_transform(vec4(t, tangent.w), n, texture_normal_transform, texture_matrix0));
+ vary_tangent = normalize(gltf_tangent_space_transform(vec4(t, tangent.w), n, texture_normal_transform));
vary_sign = tangent.w;
vary_normal = n;
#endif
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index a4efb25860..21be69aae2 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -45,7 +45,8 @@ namespace LL
namespace GLTF
{
static std::unordered_set<std::string> ExtensionsSupported = {
- "KHR_materials_unlit"
+ "KHR_materials_unlit",
+ "KHR_texture_transform"
};
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
@@ -906,6 +907,25 @@ void Material::TextureInfo::serialize(object& dst) const
{
write(mIndex, "index", dst, INVALID_INDEX);
write(mTexCoord, "texCoord", dst, 0);
+ write_extensions(dst, &mTextureTransform, "KHR_texture_transform");
+}
+
+S32 Material::TextureInfo::getTexCoord() const
+{
+ if (mTextureTransform.mPresent && mTextureTransform.mTexCoord != INVALID_INDEX)
+ {
+ return mTextureTransform.mTexCoord;
+ }
+ return mTexCoord;
+}
+
+bool Material::isMultiUV() const
+{
+ return mPbrMetallicRoughness.mBaseColorTexture.getTexCoord() != 0 ||
+ mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord() != 0 ||
+ mNormalTexture.getTexCoord() != 0 ||
+ mOcclusionTexture.getTexCoord() != 0 ||
+ mEmissiveTexture.getTexCoord() != 0;
}
const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src)
@@ -914,6 +934,7 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src)
{
copy(src, "index", mIndex);
copy(src, "texCoord", mTexCoord);
+ copy_extensions(src, "KHR_texture_transform", &mTextureTransform);
}
return *this;
@@ -931,17 +952,16 @@ bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const
void Material::OcclusionTextureInfo::serialize(object& dst) const
{
- write(mIndex, "index", dst, INVALID_INDEX);
- write(mTexCoord, "texCoord", dst, 0);
+ TextureInfo::serialize(dst);
write(mStrength, "strength", dst, 1.f);
}
const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src)
{
+ TextureInfo::operator=(src);
+
if (src.is_object())
{
- copy(src, "index", mIndex);
- copy(src, "texCoord", mTexCoord);
copy(src, "strength", mStrength);
}
@@ -950,13 +970,13 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(
void Material::NormalTextureInfo::serialize(object& dst) const
{
- write(mIndex, "index", dst, INVALID_INDEX);
- write(mTexCoord, "texCoord", dst, 0);
+ TextureInfo::serialize(dst);
write(mScale, "scale", dst, 1.f);
}
const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src)
{
+ TextureInfo::operator=(src);
if (src.is_object())
{
copy(src, "index", mIndex);
@@ -1015,6 +1035,41 @@ void Material::Unlit::serialize(object& dst) const
// no members and object has already been created, nothing to do
}
+void TextureTransform::getPacked(F32* packed) const
+{
+ packed[0] = mScale.x;
+ packed[1] = mScale.y;
+ packed[2] = mRotation;
+ packed[3] = mOffset.x;
+ packed[4] = mOffset.y;
+
+ packed[5] = packed[6] = packed[7] = 0.f;
+}
+
+
+const TextureTransform& TextureTransform::operator=(const Value& src)
+{
+ mPresent = true;
+ if (src.is_object())
+ {
+ copy(src, "offset", mOffset);
+ copy(src, "rotation", mRotation);
+ copy(src, "scale", mScale);
+ copy(src, "texCoord", mTexCoord);
+ }
+
+ return *this;
+}
+
+void TextureTransform::serialize(object& dst) const
+{
+ write(mOffset, "offset", dst, vec2(0.f, 0.f));
+ write(mRotation, "rotation", dst, 0.f);
+ write(mScale, "scale", dst, vec2(1.f, 1.f));
+ write(mTexCoord, "texCoord", dst, -1);
+}
+
+
void Material::serialize(object& dst) const
{
write(mName, "name", dst);
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index 8f28e5905f..ea3f7d480a 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -57,6 +57,21 @@ namespace LL
bool mPresent = false;
};
+ class TextureTransform : public Extension // KHR_texture_transform implementation
+ {
+ public:
+ vec2 mOffset = vec2(0.f, 0.f);
+ F32 mRotation = 0.f;
+ vec2 mScale = vec2(1.f, 1.f);
+ S32 mTexCoord = INVALID_INDEX;
+
+ // get the texture transform as a packed array of floats
+ // dst MUST point to at least 8 floats
+ void getPacked(F32* dst) const;
+
+ const TextureTransform& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
class Material
{
@@ -82,9 +97,15 @@ namespace LL
S32 mIndex = INVALID_INDEX;
S32 mTexCoord = 0;
+ TextureTransform mTextureTransform;
+
bool operator==(const TextureInfo& rhs) const;
bool operator!=(const TextureInfo& rhs) const;
+ // get the UV channel that should be used for sampling this texture
+ // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord
+ S32 getTexCoord() const;
+
const TextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -135,6 +156,8 @@ namespace LL
bool mDoubleSided = false;
Unlit mUnlit;
+ bool isMultiUV() const;
+
const Material& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index c26752a6b6..c1101818b7 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -590,8 +590,8 @@ namespace LL
// Write all extensions to dst.extensions
// Usage:
// write_extensions(dst,
- // "KHR_materials_unlit", mUnlit,
- // "KHR_materials_pbrSpecularGlossiness", mPbrSpecularGlossiness);
+ // mUnlit, "KHR_materials_unlit",
+ // mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness");
// returns true if any of the extensions are written
template<class... Types>
inline bool write_extensions(boost::json::object& dst, Types... args)
@@ -816,6 +816,39 @@ namespace LL
return true;
}
+ // vec2
+ template<>
+ inline bool copy(const Value& src, vec2& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 2)
+ {
+ std::error_code ec;
+ vec2 t;
+ t.x = arr[0].to_number<F32>(ec); if (ec) return false;
+ t.y = arr[1].to_number<F32>(ec); if (ec) return false;
+
+ dst = t;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const vec2& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.as_array();
+ arr.resize(2);
+ arr[0] = src.x;
+ arr[1] = src.y;
+
+ return true;
+ }
+
// bool
template<>
inline bool copy(const Value& src, bool& dst)
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index 197ffb68e8..dd37b5b4d0 100644
--- a/indra/newview/gltf/primitive.cpp
+++ b/indra/newview/gltf/primitive.cpp
@@ -42,13 +42,14 @@ using namespace boost::json;
// Mesh data useful for Mikktspace tangent generation (and flat normal generation)
struct MikktMesh
{
- std::vector<LLVector3> p;
- std::vector<LLVector3> n;
- std::vector<LLVector2> tc;
- std::vector<LLVector4> w;
- std::vector<LLVector4> t;
- std::vector<LLColor4U> c;
- std::vector<U64> j;
+ std::vector<LLVector3> p; //positions
+ std::vector<LLVector3> n; //normals
+ std::vector<LLVector4> t; //tangents
+ std::vector<LLVector2> tc0; //texcoords 0
+ std::vector<LLVector2> tc1; //texcoords 1
+ std::vector<LLColor4U> c; //colors
+ std::vector<LLVector4> w; //weights
+ std::vector<U64> j; //joints
// initialize from src primitive and make an unrolled triangle list
// returns false if the Primitive cannot be converted to a triangle list
@@ -57,15 +58,28 @@ struct MikktMesh
bool indexed = !prim->mIndexArray.empty();
auto vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size();
- if (prim->mMode != Primitive::Mode::TRIANGLES)
+ U32 triangle_count = 0;
+
+ if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP ||
+ prim->mMode == Primitive::Mode::TRIANGLE_FAN)
+ {
+ triangle_count = vert_count - 2;
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLES)
{
- LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32) prim->mMode << LL_ENDL;
+ triangle_count = vert_count / 3;
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32)prim->mMode << LL_ENDL;
return false;
}
+ vert_count = triangle_count * 3;
+
p.resize(vert_count);
n.resize(vert_count);
- tc.resize(vert_count);
+ tc0.resize(vert_count);
c.resize(vert_count);
bool has_normals = !prim->mNormals.empty();
@@ -78,6 +92,7 @@ struct MikktMesh
{
t.resize(vert_count);
}
+
bool rigged = !prim->mWeights.empty();
if (rigged)
{
@@ -85,23 +100,69 @@ struct MikktMesh
j.resize(vert_count);
}
- for (U32 i = 0; i < vert_count; ++i)
+ bool multi_uv = !prim->mTexCoords1.empty();
+ if (multi_uv)
{
- U32 idx = indexed ? prim->mIndexArray[i] : i;
+ tc1.resize(vert_count);
+ }
- p[i].set(prim->mPositions[idx].getF32ptr());
- tc[i].set(prim->mTexCoords[idx]);
- c[i] = prim->mColors[idx];
+ for (int tri_idx = 0; tri_idx < triangle_count; ++tri_idx)
+ {
+ U32 idx[3];
+
+ if (prim->mMode == Primitive::Mode::TRIANGLES)
+ {
+ idx[0] = tri_idx * 3;
+ idx[1] = tri_idx * 3 + 1;
+ idx[2] = tri_idx * 3 + 2;
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP)
+ {
+ idx[0] = tri_idx;
+ idx[1] = tri_idx + 1;
+ idx[2] = tri_idx + 2;
+
+ if (tri_idx % 2 != 0)
+ {
+ std::swap(idx[1], idx[2]);
+ }
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLE_FAN)
+ {
+ idx[0] = 0;
+ idx[1] = tri_idx + 1;
+ idx[2] = tri_idx + 2;
+ }
- if (has_normals)
+ if (indexed)
{
- n[i].set(prim->mNormals[idx].getF32ptr());
+ idx[0] = prim->mIndexArray[idx[0]];
+ idx[1] = prim->mIndexArray[idx[1]];
+ idx[2] = prim->mIndexArray[idx[2]];
}
- if (rigged)
+ for (U32 v = 0; v < 3; ++v)
{
- w[i].set(prim->mWeights[idx].getF32ptr());
- j[i] = prim->mJoints[idx];
+ U32 i = tri_idx * 3 + v;
+ p[i].set(prim->mPositions[idx[v]].getF32ptr());
+ tc0[i].set(prim->mTexCoords0[idx[v]]);
+ c[i] = prim->mColors[idx[v]];
+
+ if (multi_uv)
+ {
+ tc1[i].set(prim->mTexCoords1[idx[v]]);
+ }
+
+ if (has_normals)
+ {
+ n[i].set(prim->mNormals[idx[v]].getF32ptr());
+ }
+
+ if (rigged)
+ {
+ w[i].set(prim->mWeights[idx[v]].getF32ptr());
+ j[i] = prim->mJoints[idx[v]];
+ }
}
}
@@ -138,25 +199,34 @@ struct MikktMesh
void write(Primitive* prim) const
{
//re-weld
- meshopt_Stream mos[] =
+ std::vector<meshopt_Stream> mos =
{
{ &p[0], sizeof(LLVector3), sizeof(LLVector3) },
{ &n[0], sizeof(LLVector3), sizeof(LLVector3) },
{ &t[0], sizeof(LLVector4), sizeof(LLVector4) },
- { &tc[0], sizeof(LLVector2), sizeof(LLVector2) },
- { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) },
- { w.empty() ? nullptr : &w[0], sizeof(LLVector4), sizeof(LLVector4) },
- { j.empty() ? nullptr : &j[0], sizeof(U64), sizeof(U64) }
+ { &tc0[0], sizeof(LLVector2), sizeof(LLVector2) },
+ { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) }
};
+ if (!w.empty())
+ {
+ mos.push_back({ &w[0], sizeof(LLVector4), sizeof(LLVector4) });
+ mos.push_back({ &j[0], sizeof(U64), sizeof(U64) });
+ }
+
+ if (!tc1.empty())
+ {
+ mos.push_back({ &tc1[0], sizeof(LLVector2), sizeof(LLVector2) });
+ }
+
std::vector<U32> remap;
remap.resize(p.size());
- U32 stream_count = w.empty() ? 5 : 7;
+ U32 stream_count = mos.size();
- size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos, stream_count);
+ size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos.data(), stream_count);
- prim->mTexCoords.resize(vert_count);
+ prim->mTexCoords0.resize(vert_count);
prim->mNormals.resize(vert_count);
prim->mTangents.resize(vert_count);
prim->mPositions.resize(vert_count);
@@ -166,6 +236,10 @@ struct MikktMesh
prim->mWeights.resize(vert_count);
prim->mJoints.resize(vert_count);
}
+ if (!tc1.empty())
+ {
+ prim->mTexCoords1.resize(vert_count);
+ }
prim->mIndexArray.resize(remap.size());
@@ -178,7 +252,7 @@ struct MikktMesh
prim->mPositions[dst_idx].load3(p[src_idx].mV);
prim->mNormals[dst_idx].load3(n[src_idx].mV);
- prim->mTexCoords[dst_idx] = tc[src_idx];
+ prim->mTexCoords0[dst_idx] = tc0[src_idx];
prim->mTangents[dst_idx].loadua(t[src_idx].mV);
prim->mColors[dst_idx] = c[src_idx];
@@ -187,6 +261,11 @@ struct MikktMesh
prim->mWeights[dst_idx].loadua(w[src_idx].mV);
prim->mJoints[dst_idx] = j[src_idx];
}
+
+ if (!tc1.empty())
+ {
+ prim->mTexCoords1[dst_idx] = tc1[src_idx];
+ }
}
prim->mGLMode = LLRender::TRIANGLES;
@@ -210,8 +289,8 @@ struct MikktMesh
mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num)
{
- F32* uv = tc[face_num * 3 + vert_num].mV;
- return mikk::float3(uv[0], uv[1], 1.0f);
+ F32* uv = tc0[face_num * 3 + vert_num].mV;
+ return mikk::float3(uv[0], 1.f-uv[1], 1.0f);
}
mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num)
@@ -228,6 +307,14 @@ struct MikktMesh
};
+static void vertical_flip(std::vector<LLVector2>& texcoords)
+{
+ for (auto& tc : texcoords)
+ {
+ tc[1] = 1.f - tc[1];
+ }
+}
+
bool Primitive::prep(Asset& asset)
{
// allocate vertex buffer
@@ -261,7 +348,11 @@ bool Primitive::prep(Asset& asset)
}
else if (attribName == "TEXCOORD_0")
{
- copy(asset, accessor, mTexCoords);
+ copy(asset, accessor, mTexCoords0);
+ }
+ else if (attribName == "TEXCOORD_1")
+ {
+ copy(asset, accessor, mTexCoords1);
}
else if (attribName == "JOINTS_0")
{
@@ -297,24 +388,28 @@ bool Primitive::prep(Asset& asset)
mask |= LLVertexBuffer::MAP_JOINT;
}
- if (mTexCoords.empty())
+ if (mTexCoords0.empty())
{
- mTexCoords.resize(mPositions.size());
+ mTexCoords0.resize(mPositions.size());
}
- // TODO: support more than one texcoord set (or no texcoords)
mask |= LLVertexBuffer::MAP_TEXCOORD0;
+ if (!mTexCoords1.empty())
+ {
+ mask |= LLVertexBuffer::MAP_TEXCOORD1;
+ }
+
if (mColors.empty())
{
mColors.resize(mPositions.size(), LLColor4U::white);
}
+ mShaderVariant = 0;
+
// TODO: support colorless vertex buffers
mask |= LLVertexBuffer::MAP_COLOR;
- mShaderVariant = 0;
-
bool unlit = false;
// bake material basecolor into color array
@@ -332,6 +427,11 @@ bool Primitive::prep(Asset& asset)
mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
unlit = true;
}
+
+ if (material.isMultiUV())
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::MULTI_UV;
+ }
}
if (mNormals.empty() && !unlit)
@@ -437,16 +537,18 @@ bool Primitive::prep(Asset& asset)
}
// flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
- for (auto& tc : mTexCoords)
- {
- tc[1] = 1.f - tc[1];
- }
- mVertexBuffer->setTexCoordData(mTexCoords.data());
- for (auto& tc : mTexCoords)
+ vertical_flip(mTexCoords0);
+ mVertexBuffer->setTexCoord0Data(mTexCoords0.data());
+ vertical_flip(mTexCoords0);
+
+ if (!mTexCoords1.empty())
{
- tc[1] = 1.f - tc[1];
+ vertical_flip(mTexCoords1);
+ mVertexBuffer->setTexCoord1Data(mTexCoords1.data());
+ vertical_flip(mTexCoords1);
}
+
if (!mIndexArray.empty())
{
mVertexBuffer->setIndexData(mIndexArray.data());
@@ -456,10 +558,13 @@ bool Primitive::prep(Asset& asset)
mVertexBuffer->unbind();
- Material& material = asset.mMaterials[mMaterial];
- if (material.mAlphaMode == Material::AlphaMode::BLEND)
+ if (mMaterial != INVALID_INDEX)
{
- mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ Material& material = asset.mMaterials[mMaterial];
+ if (material.mAlphaMode == Material::AlphaMode::BLEND)
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ }
}
return true;
@@ -617,7 +722,7 @@ const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start,
//create a proxy LLVolumeFace for the raycast
LLVolumeFace face;
face.mPositions = mPositions.data();
- face.mTexCoords = mTexCoords.data();
+ face.mTexCoords = mTexCoords0.data();
face.mNormals = mNormals.data();
face.mTangents = mTangents.data();
face.mIndices = nullptr; // unreferenced
diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h
index f9d7c63c65..7cc05cf831 100644
--- a/indra/newview/gltf/primitive.h
+++ b/indra/newview/gltf/primitive.h
@@ -58,7 +58,8 @@ namespace LL
LLPointer<LLVertexBuffer> mVertexBuffer;
// CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code
- std::vector<LLVector2> mTexCoords;
+ std::vector<LLVector2> mTexCoords0;
+ std::vector<LLVector2> mTexCoords1;
std::vector<LLVector4a> mNormals;
std::vector<LLVector4a> mTangents;
std::vector<LLVector4a> mPositions;
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index b948b2e2d6..e01aec0497 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -552,10 +552,16 @@ void GLTFSceneManager::render(bool opaque, bool rigged, bool unlit)
void GLTFSceneManager::render(U8 variant)
{
- // 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 boundd
+ // just render the whole scene by traversing the whole scenegraph
+ // Assumes camera transform is already set and appropriate shader is already bound.
+ // Eventually we'll want a smarter render pipe that has pre-sorted the scene graph
+ // into buckets by material and shader.
+
+ // HACK -- implicitly render multi-uv variant
+ if (!(variant & LLGLSLShader::GLTFVariant::MULTI_UV))
+ {
+ render((U8) (variant | LLGLSLShader::GLTFVariant::MULTI_UV));
+ }
gGL.matrixMode(LLRender::MM_MODELVIEW);
@@ -666,11 +672,30 @@ static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info,
{
if (info.mIndex != INVALID_INDEX)
{
- LLViewerTexture* tex = asset.mImages[asset.mTextures[info.mIndex].mSource].mTexture;
+ Texture& texture = asset.mTextures[info.mIndex];
+
+ LLViewerTexture* tex = asset.mImages[texture.mSource].mTexture;
if (tex)
{
tex->addTextureStats(2048.f * 2048.f);
- LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex);
+ S32 channel = LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex);
+
+ if (channel != -1 && texture.mSampler != -1)
+ { // set sampler state
+ Sampler& sampler = asset.mSamplers[texture.mSampler];
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sampler.mWrapS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, sampler.mWrapT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sampler.mMagFilter);
+
+ // NOTE: do not set min filter. Always respect client preference for min filter
+ }
+ else
+ {
+ // set default sampler state
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
}
else
{
@@ -713,10 +738,10 @@ void GLTFSceneManager::bind(Asset& asset, Material& material)
bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep);
- F32 base_color_packed[8];
- //mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
- LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
- shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed);
+ F32 tf[8];
+ material.mPbrMetallicRoughness.mBaseColorTexture.mTextureTransform.getPacked(tf);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, tf);
+ shader->uniform1i(LLShaderMgr::BASE_COLOR_TEXCOORD, material.mPbrMetallicRoughness.mBaseColorTexture.getTexCoord());
if (!LLPipeline::sShadowRender)
{
@@ -731,20 +756,21 @@ void GLTFSceneManager::bind(Asset& asset, Material& material)
shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, material.mPbrMetallicRoughness.mMetallicFactor);
shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(material.mEmissiveFactor));
- F32 normal_packed[8];
- //mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
- LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
- shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, (F32*)normal_packed);
+ material.mNormalTexture.mTextureTransform.getPacked(tf);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, tf);
+ shader->uniform1i(LLShaderMgr::NORMAL_TEXCOORD, material.mNormalTexture.getTexCoord());
+
+ material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTextureTransform.getPacked(tf);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, tf);
+ shader->uniform1i(LLShaderMgr::METALLIC_ROUGHNESS_TEXCOORD, material.mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord());
- F32 metallic_roughness_packed[8];
- //mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
- LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
- shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, (F32*)metallic_roughness_packed);
+ material.mOcclusionTexture.mTextureTransform.getPacked(tf);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_OCCLUSION_TRANSFORM, 2, tf);
+ shader->uniform1i(LLShaderMgr::OCCLUSION_TEXCOORD, material.mOcclusionTexture.getTexCoord());
- F32 emissive_packed[8];
- //mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
- LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
- shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed);
+ material.mEmissiveTexture.mTextureTransform.getPacked(tf);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, tf);
+ shader->uniform1i(LLShaderMgr::EMISSIVE_TEXCOORD, material.mEmissiveTexture.getTexCoord());
}
}
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index d43d6fea37..5913e7ba6f 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -249,7 +249,7 @@ static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader
}
-static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool alpha_blend, bool rigged, bool unlit, bool use_sun_shadow)
+static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool alpha_blend, bool rigged, bool unlit, bool multi_uv, bool use_sun_shadow)
{
variant.mName = shader.mName.c_str();
variant.mFeatures = shader.mFeatures;
@@ -271,6 +271,11 @@ static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool
variant.addPermutation("UNLIT", "1");
}
+ if (multi_uv)
+ {
+ variant.addPermutation("MULTI_UV", "1");
+ }
+
if (alpha_blend)
{
variant.addPermutation("ALPHA_BLEND", "1");
@@ -317,8 +322,9 @@ static bool make_gltf_variants(LLGLSLShader& shader, bool use_sun_shadow)
bool alpha_blend = i & LLGLSLShader::GLTFVariant::ALPHA_BLEND;
bool rigged = i & LLGLSLShader::GLTFVariant::RIGGED;
bool unlit = i & LLGLSLShader::GLTFVariant::UNLIT;
+ bool multi_uv = i & LLGLSLShader::GLTFVariant::MULTI_UV;
- if (!make_gltf_variant(shader, shader.mGLTFVariants[i], alpha_blend, rigged, unlit, use_sun_shadow))
+ if (!make_gltf_variant(shader, shader.mGLTFVariants[i], alpha_blend, rigged, unlit, multi_uv, use_sun_shadow))
{
return false;
}
diff --git a/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml b/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml
index cbcbe418cd..9a90700056 100644
--- a/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml
+++ b/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml
@@ -3,7 +3,6 @@
border="true"
follows="top|left"
height="460"
- help_topic="panel_region_terrain_tab"
label="Terrain"
layout="topleft"
left="0"
@@ -225,13 +224,12 @@
left="0"
name="terrain_tabs"
tab_position="top"
- tab_width="100"
+ tab_width="110"
tab_padding_right="3"
top_pad="0"
width="700">
<panel
border="true"
- class="panel_settings_terrain_elevation"
filename="panel_settings_terrain_elevation.xml"
label="Elevation"
layout="topleft"
@@ -240,7 +238,6 @@
name="terrain_elevation_panel" />
<panel
border="true"
- class="panel_settings_terrain_transform"
filename="panel_settings_terrain_transform.xml"
label="Transforms"
layout="topleft"
@@ -256,8 +253,8 @@
height="20"
label="Apply"
layout="topleft"
- left="353"
+ left="351"
name="apply_btn"
- top_delta="290"
+ top_delta="310"
width="100" />
</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml b/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml
index 7052622813..0bf0d8cffc 100644
--- a/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml
+++ b/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml
@@ -10,6 +10,62 @@
<text
type="string"
length="1"
+ halign="center"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain0_label"
+ left="48"
+ top_delta="0"
+ width="62">
+ 1
+ </text>
+ <text
+ type="string"
+ length="1"
+ halign="center"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain1_label"
+ left_delta="110"
+ top_delta="0"
+ width="62">
+ 2
+ </text>
+ <text
+ type="string"
+ length="1"
+ halign="center"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain2_label"
+ left_delta="110"
+ top_delta="0"
+ width="62">
+ 3
+ </text>
+ <text
+ type="string"
+ length="1"
+ halign="center"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain3_label"
+ left_delta="110"
+ top_delta="0"
+ width="62">
+ 4
+ </text>
+ <text
+ type="string"
+ length="1"
halign="left"
valign="center"
follows="left|top"
@@ -17,19 +73,10 @@
layout="topleft"
name="terrain0ScaleU_label"
left="10"
- top_pad="3"
+ top_pad="0"
width="170">
Scale u
</text>
- <view_border
- bevel_style="none"
- follows="top|left"
- height="0"
- layout="topleft"
- left="8"
- top_pad="-2"
- name="terrain0ScaleU_horizontal"
- width="430" />
<spinner
follows="left|top"
height="19"
@@ -37,7 +84,7 @@
label="Scale u"
label_width="0"
layout="topleft"
- left="10"
+ left="46"
min_val="-100"
max_val="100"
name="terrain0ScaleU"
@@ -78,6 +125,15 @@
max_val="100"
name="terrain3ScaleU"
width="64" />
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="8"
+ name="terrainScaleU_horizontal"
+ width="432" />
<text
type="string"
length="1"
@@ -88,19 +144,10 @@
layout="topleft"
name="terrain0ScaleV_label"
left="10"
- top_pad="3"
+ top_pad="0"
width="170">
Scale v
</text>
- <view_border
- bevel_style="none"
- follows="top|left"
- height="0"
- layout="topleft"
- left="8"
- top_pad="-2"
- name="terrain0ScaleV_horizontal"
- width="430" />
<spinner
follows="left|top"
height="19"
@@ -108,7 +155,7 @@
label="Scale v"
label_width="0"
layout="topleft"
- left="10"
+ left="46"
min_val="-100"
max_val="100"
name="terrain0ScaleV"
@@ -149,6 +196,15 @@
max_val="100"
name="terrain3ScaleV"
width="64" />
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="8"
+ name="terrainScaleV_horizontal"
+ width="432" />
<text
type="string"
length="1"
@@ -159,19 +215,10 @@
layout="topleft"
name="terrain0Rotation_label"
left="10"
- top_pad="3"
+ top_pad="0"
width="170">
Rotation
</text>
- <view_border
- bevel_style="none"
- follows="top|left"
- height="0"
- layout="topleft"
- left="8"
- top_pad="-2"
- name="terrain0Rotation_horizontal"
- width="430" />
<spinner
follows="left|top"
height="19"
@@ -179,7 +226,7 @@
label="Rotation"
label_width="0"
layout="topleft"
- left="10"
+ left="46"
min_val="-360"
max_val="360"
name="terrain0Rotation"
@@ -192,8 +239,8 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-360"
+ max_val="360"
name="terrain1Rotation"
width="64" />
<spinner
@@ -204,8 +251,8 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-360"
+ max_val="360"
name="terrain2Rotation"
width="64" />
<spinner
@@ -216,10 +263,19 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-360"
+ max_val="360"
name="terrain3Rotation"
width="64" />
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="8"
+ name="terrainRotation_horizontal"
+ width="432" />
<text
type="string"
length="1"
@@ -230,19 +286,10 @@
layout="topleft"
name="terrain0OffsetU_label"
left="10"
- top_pad="3"
+ top_pad="0"
width="170">
Offset y
</text>
- <view_border
- bevel_style="none"
- follows="top|left"
- height="0"
- layout="topleft"
- left="8"
- top_pad="-2"
- name="terrain0OffsetU_horizontal"
- width="430" />
<spinner
follows="left|top"
height="19"
@@ -250,7 +297,7 @@
label="Offset u"
label_width="0"
layout="topleft"
- left="10"
+ left="46"
min_val="-999"
max_val="999"
name="terrain0OffsetU"
@@ -263,8 +310,8 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-999"
+ max_val="999"
name="terrain1OffsetU"
width="64" />
<spinner
@@ -275,8 +322,8 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-999"
+ max_val="999"
name="terrain2OffsetU"
width="64" />
<spinner
@@ -287,10 +334,19 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-999"
+ max_val="999"
name="terrain3OffsetU"
width="64" />
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="8"
+ name="terrainOffsetU_horizontal"
+ width="432" />
<text
type="string"
length="1"
@@ -301,19 +357,10 @@
layout="topleft"
name="terrain0OffsetV_label"
left="10"
- top_pad="3"
+ top_pad="0"
width="170">
Offset v
</text>
- <view_border
- bevel_style="none"
- follows="top|left"
- height="0"
- layout="topleft"
- left="8"
- top_pad="-2"
- name="terrain0OffsetV_horizontal"
- width="430" />
<spinner
follows="left|top"
height="19"
@@ -321,7 +368,7 @@
label="Offset v"
label_width="0"
layout="topleft"
- left="10"
+ left="46"
min_val="-999"
max_val="999"
name="terrain0OffsetV"
@@ -334,8 +381,8 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-999"
+ max_val="999"
name="terrain1OffsetV"
width="64" />
<spinner
@@ -346,8 +393,8 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-999"
+ max_val="999"
name="terrain2OffsetV"
width="64" />
<spinner
@@ -358,8 +405,17 @@
label_width="0"
layout="topleft"
left_delta="110"
- min_val="-100"
- max_val="100"
+ min_val="-999"
+ max_val="999"
name="terrain3OffsetV"
width="64" />
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="8"
+ name="terrainOffsetV_horizontal"
+ width="432" />
</panel>