Merge branch 'project/gltf_development' of https://github.com/secondlife/viewer
# Conflicts: # indra/newview/gltf/primitive.cpp # indra/newview/lldynamictexture.cpp # indra/newview/llfloaterregioninfo.h # indra/newview/lltexturectrl.cpp # indra/newview/lltinygltfhelper.cpp # indra/newview/llvlcomposition.cpp # indra/newview/llvlcomposition.h # indra/newview/llvovolume.cppmaster
commit
050d2fefab
|
|
@ -2,6 +2,7 @@
|
||||||
1b68f71348ecf3983b76b40d7940da8377f049b7
|
1b68f71348ecf3983b76b40d7940da8377f049b7
|
||||||
# Trim trailing whitespace
|
# Trim trailing whitespace
|
||||||
a0b3021bdcf76859054fda8e30abb3ed47749e83
|
a0b3021bdcf76859054fda8e30abb3ed47749e83
|
||||||
|
8444cd9562a6a7b755fcb075864e205122354192
|
||||||
# Wrong line endings
|
# Wrong line endings
|
||||||
1b67dd855c41f5a0cda7ec2a68d98071986ca703
|
1b67dd855c41f5a0cda7ec2a68d98071986ca703
|
||||||
6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a
|
6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
# PBR Terrain Appearance
|
# PBR Terrain Appearance
|
||||||
|
|
||||||
## Tiling
|
## Tiling Without Texture Transforms
|
||||||
|
|
||||||
|
This section assumes the PBR terrain of the current region and adjacent regions have the default texture transforms.
|
||||||
|
|
||||||
The southwest corner of a region with PBR materials should exactly match up with the bottom left corner of the material texture(s).
|
The southwest corner of a region with PBR materials should exactly match up with the bottom left corner of the material texture(s).
|
||||||
|
|
||||||
If two adjacent regions have the same PBR terrain settings, then:
|
If two adjacent regions have the same PBR terrain settings, then there should not be seams between the two regions at their shared border.
|
||||||
|
|
||||||
- There should not be seams between the two regions at their shared border
|
The ground should not suddenly slide beneath the avatar when moving between two PBR terrain regions (except due to movement of the avatar, which is not covered by this test plan)
|
||||||
- The ground should not suddenly slide beneath the avatar when moving between regions (except due to movement of the avatar, which is not covered by this test plan)
|
|
||||||
|
|
||||||
## Feature Gating
|
## Feature Gating
|
||||||
|
|
||||||
|
|
@ -35,3 +36,17 @@ Availability of PBR textures varies by machine and graphics setting:
|
||||||
### PBR Alpha
|
### PBR Alpha
|
||||||
|
|
||||||
PBR terrain does not support materials with alpha blend or double-sided. In addition, the viewer does not make any guarantees about what will render behind the terrain if alpha is used.
|
PBR terrain does not support materials with alpha blend or double-sided. In addition, the viewer does not make any guarantees about what will render behind the terrain if alpha is used.
|
||||||
|
|
||||||
|
## PBR Terrain Texture Transforms
|
||||||
|
|
||||||
|
Like PBR materials on prims, PBR terrain repeats are based on the [KHR\_texture\_transform](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform) spec, and thus should be expected to behave the same way.
|
||||||
|
|
||||||
|
The southwest corner of a region, at z=0, is the UV origin for all texture coordinates of the whole region. Unless an offset is also applied, scale and rotation of the terrain texture transforms are relative to that point.
|
||||||
|
|
||||||
|
When an avatar faces north and looks down at flat ground, the textures of the materials should appear to face upright, unless a rotation is applied.
|
||||||
|
|
||||||
|
If triplanar mapping is enabled, and an avatar faces an axially-aligned wall, the textures of the materials should appear to face upright, unless a rotation is applied.
|
||||||
|
|
||||||
|
Textures of materials should not appear mirrored.
|
||||||
|
|
||||||
|
When triplanar mapping is enabled, rotations on the axially aligned walls should apply in the same direction as they would on flat ground.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@ All tests in this section assume the PBR terrain feature flag is enabled, and th
|
||||||
|
|
||||||
### Feature Availability
|
### Feature Availability
|
||||||
|
|
||||||
|
These features are related to UI, where the Region/Estate floater is opened to the terrain Tab.
|
||||||
|
|
||||||
|
#### Feature: PBR Terrain
|
||||||
|
|
||||||
On the client, the advanced setting `RenderTerrainPBREnabled` is the PBR terrain feature flag.
|
On the client, the advanced setting `RenderTerrainPBREnabled` is the PBR terrain feature flag.
|
||||||
|
|
||||||
The PBR terrain feature flag should be set automatically when logging in/teleporting to a new region.
|
The PBR terrain feature flag should be set automatically when logging in/teleporting to a new region.
|
||||||
|
|
@ -29,6 +33,24 @@ When the PBR terrain feature flag is enabled:
|
||||||
- The "PBR Metallic Roughness" checkbox should be visible
|
- The "PBR Metallic Roughness" checkbox should be visible
|
||||||
- The user should be able to apply PBR terrain or textures to the region, depending on if the "PBR Metallic Roughness" checkbox is checked.
|
- The user should be able to apply PBR terrain or textures to the region, depending on if the "PBR Metallic Roughness" checkbox is checked.
|
||||||
|
|
||||||
|
#### Feature: PBR Terrain Texture Transforms
|
||||||
|
|
||||||
|
On the client, the advanced setting, `RenderTerrainPBRTransformsEnabled` is the PBR terrain texture transform flag. Generally, this feature should not be expected to work correctly unless the PBR terrain feature is also enabled.
|
||||||
|
|
||||||
|
The PBR terrain texture transform flag should be set automatically when logging in/teleporting to a new region.
|
||||||
|
|
||||||
|
- The flag should be enabled on regions where the PBR terrain texture transform feature is enabled
|
||||||
|
- Otherwise the flag should be disabled
|
||||||
|
|
||||||
|
When the PBR terrain texture transform feature is enabled, the UI of the Terrain tab should be overhauled. Availability of features depends on the type of terrain.
|
||||||
|
|
||||||
|
When "PBR Metallic Roughness" is checked:
|
||||||
|
|
||||||
|
- There should be a way for the user to change the texture transforms for the terrain in the current region
|
||||||
|
- For each of the four swatches, the user can change the scale, offset, and rotation of that swatch. Nonuniform scale is allowed
|
||||||
|
|
||||||
|
When "PBR Metallic Roughness" is unchecked, the controls for texture transforms should be hidden.
|
||||||
|
|
||||||
### Current Composition Type
|
### Current Composition Type
|
||||||
|
|
||||||
When the Region/Estate floater is opened to the terrain Tab, the current terrain should be shown in the four swatches, and the "PBR Metallic Roughness" checkbox should be checked or unchecked accordingly.
|
When the Region/Estate floater is opened to the terrain Tab, the current terrain should be shown in the four swatches, and the "PBR Metallic Roughness" checkbox should be checked or unchecked accordingly.
|
||||||
|
|
|
||||||
|
|
@ -328,14 +328,16 @@ public:
|
||||||
// bit 0 = alpha mode blend (1) or opaque (0)
|
// bit 0 = alpha mode blend (1) or opaque (0)
|
||||||
// bit 1 = rigged (1) or static (0)
|
// bit 1 = rigged (1) or static (0)
|
||||||
// bit 2 = unlit (1) or lit (0)
|
// bit 2 = unlit (1) or lit (0)
|
||||||
|
// bit 3 = single (0) or multi (1) uv coordinates
|
||||||
struct GLTFVariant
|
struct GLTFVariant
|
||||||
{
|
{
|
||||||
constexpr static U8 ALPHA_BLEND = 1;
|
constexpr static U8 ALPHA_BLEND = 1;
|
||||||
constexpr static U8 RIGGED = 2;
|
constexpr static U8 RIGGED = 2;
|
||||||
constexpr static U8 UNLIT = 4;
|
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;
|
std::vector<LLGLSLShader> mGLTFVariants;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1731,7 +1731,7 @@ void LLRender::flush()
|
||||||
|
|
||||||
if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
|
if (attribute_mask & LLVertexBuffer::MAP_TEXCOORD0)
|
||||||
{
|
{
|
||||||
vb->setTexCoordData(mTexcoordsp.get());
|
vb->setTexCoord0Data(mTexcoordsp.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute_mask & LLVertexBuffer::MAP_COLOR)
|
if (attribute_mask & LLVertexBuffer::MAP_COLOR)
|
||||||
|
|
|
||||||
|
|
@ -1184,7 +1184,13 @@ void LLShaderMgr::initAttribsAndUniforms()
|
||||||
mReservedUniforms.push_back("texture_base_color_transform"); // (GLTF)
|
mReservedUniforms.push_back("texture_base_color_transform"); // (GLTF)
|
||||||
mReservedUniforms.push_back("texture_normal_transform"); // (GLTF)
|
mReservedUniforms.push_back("texture_normal_transform"); // (GLTF)
|
||||||
mReservedUniforms.push_back("texture_metallic_roughness_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("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)
|
mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,13 @@ public:
|
||||||
TEXTURE_BASE_COLOR_TRANSFORM, // "texture_base_color_transform" (GLTF)
|
TEXTURE_BASE_COLOR_TRANSFORM, // "texture_base_color_transform" (GLTF)
|
||||||
TEXTURE_NORMAL_TRANSFORM, // "texture_normal_transform" (GLTF)
|
TEXTURE_NORMAL_TRANSFORM, // "texture_normal_transform" (GLTF)
|
||||||
TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_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)
|
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)
|
TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1661,7 +1661,7 @@ void LLVertexBuffer::setPositionData(const LLVector4a* data)
|
||||||
flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData);
|
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
|
#if !LL_DARWIN
|
||||||
llassert(sGLRenderBuffer == mGLBuffer);
|
llassert(sGLRenderBuffer == mGLBuffer);
|
||||||
|
|
@ -1669,6 +1669,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);
|
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)
|
void LLVertexBuffer::setColorData(const LLColor4U* data)
|
||||||
{
|
{
|
||||||
#if !LL_DARWIN
|
#if !LL_DARWIN
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,8 @@ public:
|
||||||
void setTangentData(const LLVector4a* data);
|
void setTangentData(const LLVector4a* data);
|
||||||
void setWeight4Data(const LLVector4a* data);
|
void setWeight4Data(const LLVector4a* data);
|
||||||
void setJointData(const U64* 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 setColorData(const LLColor4U* data);
|
||||||
void setIndexData(const U16* data);
|
void setIndexData(const U16* data);
|
||||||
void setIndexData(const U32* data);
|
void setIndexData(const U32* data);
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ uniform sampler2D emissiveMap;
|
||||||
uniform vec3 emissiveColor;
|
uniform vec3 emissiveColor;
|
||||||
in vec3 vary_position;
|
in vec3 vary_position;
|
||||||
in vec4 vertex_color;
|
in vec4 vertex_color;
|
||||||
in vec2 base_color_texcoord;
|
in vec2 base_color_uv;
|
||||||
in vec2 emissive_texcoord;
|
in vec2 emissive_uv;
|
||||||
uniform float minimum_alpha;
|
uniform float minimum_alpha;
|
||||||
|
|
||||||
void mirrorClip(vec3 pos);
|
void mirrorClip(vec3 pos);
|
||||||
|
|
@ -59,8 +59,9 @@ uniform float roughnessFactor;
|
||||||
in vec3 vary_normal;
|
in vec3 vary_normal;
|
||||||
in vec3 vary_tangent;
|
in vec3 vary_tangent;
|
||||||
flat in float vary_sign;
|
flat in float vary_sign;
|
||||||
in vec2 normal_texcoord;
|
in vec2 normal_uv;
|
||||||
in vec2 metallic_roughness_texcoord;
|
in vec2 metallic_roughness_uv;
|
||||||
|
in vec2 occlusion_uv;
|
||||||
#endif
|
#endif
|
||||||
// ==================================
|
// ==================================
|
||||||
|
|
||||||
|
|
@ -164,7 +165,7 @@ void main()
|
||||||
vec3 pos = vary_position;
|
vec3 pos = vary_position;
|
||||||
mirrorClip(pos);
|
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.rgb = srgb_to_linear(basecolor.rgb);
|
||||||
basecolor *= vertex_color;
|
basecolor *= vertex_color;
|
||||||
|
|
||||||
|
|
@ -174,7 +175,7 @@ void main()
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 emissive = emissiveColor;
|
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
|
#ifndef UNLIT
|
||||||
// from mikktspace.com
|
// 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;
|
float sign = vary_sign;
|
||||||
vec3 vN = vary_normal;
|
vec3 vN = vary_normal;
|
||||||
vec3 vT = vary_tangent.xyz;
|
vec3 vT = vary_tangent.xyz;
|
||||||
|
|
@ -198,8 +199,8 @@ void main()
|
||||||
// occlusion 1.0
|
// occlusion 1.0
|
||||||
// roughness 0.0
|
// roughness 0.0
|
||||||
// metal 0.0
|
// metal 0.0
|
||||||
vec3 orm = texture(metallicRoughnessMap, metallic_roughness_texcoord.xy).rgb;
|
vec3 orm = texture(metallicRoughnessMap, metallic_roughness_uv.xy).rgb;
|
||||||
orm.r = texture(occlusionMap, metallic_roughness_texcoord.xy).r;
|
orm.r = texture(occlusionMap, occlusion_uv.xy).r;
|
||||||
orm.g *= roughnessFactor;
|
orm.g *= roughnessFactor;
|
||||||
orm.b *= metallicFactor;
|
orm.b *= metallicFactor;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -33,33 +33,97 @@ uniform mat4 projection_matrix;
|
||||||
uniform mat3 normal_matrix;
|
uniform mat3 normal_matrix;
|
||||||
uniform mat4 modelview_projection_matrix;
|
uniform mat4 modelview_projection_matrix;
|
||||||
#endif
|
#endif
|
||||||
uniform mat4 texture_matrix0;
|
|
||||||
|
|
||||||
uniform vec4[2] texture_base_color_transform;
|
uniform vec4[2] texture_base_color_transform;
|
||||||
uniform vec4[2] texture_normal_transform;
|
uniform vec4[2] texture_normal_transform;
|
||||||
uniform vec4[2] texture_metallic_roughness_transform;
|
uniform vec4[2] texture_metallic_roughness_transform;
|
||||||
uniform vec4[2] texture_emissive_transform;
|
uniform vec4[2] texture_emissive_transform;
|
||||||
|
uniform vec4[2] texture_occlusion_transform;
|
||||||
|
|
||||||
in vec3 position;
|
in vec3 position;
|
||||||
in vec4 diffuse_color;
|
in vec4 diffuse_color;
|
||||||
in vec2 texcoord0;
|
in vec2 texcoord0;
|
||||||
out vec2 base_color_texcoord;
|
out vec2 base_color_uv;
|
||||||
out vec2 emissive_texcoord;
|
out vec2 emissive_uv;
|
||||||
out vec4 vertex_color;
|
out vec4 vertex_color;
|
||||||
out vec3 vary_position;
|
out vec3 vary_position;
|
||||||
|
|
||||||
#ifndef UNLIT
|
#ifndef UNLIT
|
||||||
in vec3 normal;
|
in vec3 normal;
|
||||||
in vec4 tangent;
|
in vec4 tangent;
|
||||||
out vec2 normal_texcoord;
|
out vec2 normal_uv;
|
||||||
out vec2 metallic_roughness_texcoord;
|
out vec2 metallic_roughness_uv;
|
||||||
|
out vec2 occlusion_uv;
|
||||||
out vec3 vary_tangent;
|
out vec3 vary_tangent;
|
||||||
flat out float vary_sign;
|
flat out float vary_sign;
|
||||||
out vec3 vary_normal;
|
out vec3 vary_normal;
|
||||||
vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
|
|
||||||
#endif
|
#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
|
#ifdef ALPHA_BLEND
|
||||||
|
|
@ -136,14 +200,41 @@ void main()
|
||||||
gl_Position = vert;
|
gl_Position = vert;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
|
vec2 bcuv;
|
||||||
emissive_texcoord = texture_transform(texcoord0, texture_emissive_transform, texture_matrix0);
|
vec2 emuv;
|
||||||
|
|
||||||
#ifndef UNLIT
|
#ifdef MULTI_UV
|
||||||
normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);
|
vec2 uv[2];
|
||||||
metallic_roughness_texcoord = texture_transform(texcoord0, texture_metallic_roughness_transform, texture_matrix0);
|
uv[0] = texcoord0;
|
||||||
|
uv[1] = texcoord1;
|
||||||
|
|
||||||
|
bcuv = uv[base_color_texcoord];
|
||||||
|
emuv = uv[emissive_texcoord];
|
||||||
|
#else
|
||||||
|
bcuv = texcoord0;
|
||||||
|
emuv = texcoord0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
base_color_uv = gltf_texture_transform(bcuv, texture_base_color_transform);
|
||||||
|
emissive_uv = gltf_texture_transform(emuv, texture_emissive_transform);
|
||||||
|
|
||||||
|
#ifndef UNLIT
|
||||||
|
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
|
#ifndef UNLIT
|
||||||
#ifdef HAS_SKIN
|
#ifdef HAS_SKIN
|
||||||
|
|
@ -155,7 +246,7 @@ void main()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
n = normalize(n);
|
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_sign = tangent.w;
|
||||||
vary_normal = n;
|
vary_normal = n;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,8 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
|
||||||
|
|
||||||
mData.erase(mData.begin() + offset, mData.begin() + offset + length);
|
mData.erase(mData.begin() + offset, mData.begin() + offset + length);
|
||||||
|
|
||||||
mByteLength = mData.size();
|
llassert(mData.size() <= size_t(INT_MAX));
|
||||||
|
mByteLength = S32(mData.size());
|
||||||
|
|
||||||
for (BufferView& view : asset.mBufferViews)
|
for (BufferView& view : asset.mBufferViews)
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +142,7 @@ bool Buffer::prep(Asset& asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
mData.resize(mByteLength);
|
mData.resize(mByteLength);
|
||||||
if (!file.read((U8*)mData.data(), mData.size()))
|
if (!file.read((U8*)mData.data(), mByteLength))
|
||||||
{
|
{
|
||||||
LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL;
|
LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -189,16 +189,15 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F
|
||||||
|
|
||||||
if (mFrameTimes.size() > 1)
|
if (mFrameTimes.size() > 1)
|
||||||
{
|
{
|
||||||
|
llassert(mFrameTimes.size() <= size_t(U32_MAX));
|
||||||
|
frameIndex = U32(mFrameTimes.size()) - 2;
|
||||||
|
t = 1.f;
|
||||||
|
|
||||||
if (time > mMaxTime)
|
if (time > mMaxTime)
|
||||||
{
|
{
|
||||||
frameIndex = (U32)mFrameTimes.size() - 2;
|
|
||||||
t = 1.0f;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
frameIndex = (U32)mFrameTimes.size() - 2;
|
|
||||||
t = 1.f;
|
|
||||||
|
|
||||||
for (U32 i = 0; i < (U32)mFrameTimes.size() - 1; i++)
|
for (U32 i = 0; i < (U32)mFrameTimes.size() - 1; i++)
|
||||||
{
|
{
|
||||||
if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1])
|
if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1])
|
||||||
|
|
@ -382,7 +381,7 @@ void Skin::uploadMatrixPalette(Asset& asset)
|
||||||
glGenBuffers(1, &mUBO);
|
glGenBuffers(1, &mUBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
U32 joint_count = llmin(max_joints, mJoints.size());
|
size_t joint_count = llmin<size_t>(max_joints, mJoints.size());
|
||||||
|
|
||||||
std::vector<mat4> t_mp;
|
std::vector<mat4> t_mp;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ namespace LL
|
||||||
namespace GLTF
|
namespace GLTF
|
||||||
{
|
{
|
||||||
static std::unordered_set<std::string> ExtensionsSupported = {
|
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)
|
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(mIndex, "index", dst, INVALID_INDEX);
|
||||||
write(mTexCoord, "texCoord", dst, 0);
|
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)
|
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, "index", mIndex);
|
||||||
copy(src, "texCoord", mTexCoord);
|
copy(src, "texCoord", mTexCoord);
|
||||||
|
copy_extensions(src, "KHR_texture_transform", &mTextureTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -931,17 +952,16 @@ bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const
|
||||||
|
|
||||||
void Material::OcclusionTextureInfo::serialize(object& dst) const
|
void Material::OcclusionTextureInfo::serialize(object& dst) const
|
||||||
{
|
{
|
||||||
write(mIndex, "index", dst, INVALID_INDEX);
|
TextureInfo::serialize(dst);
|
||||||
write(mTexCoord, "texCoord", dst, 0);
|
|
||||||
write(mStrength, "strength", dst, 1.f);
|
write(mStrength, "strength", dst, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src)
|
const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src)
|
||||||
{
|
{
|
||||||
|
TextureInfo::operator=(src);
|
||||||
|
|
||||||
if (src.is_object())
|
if (src.is_object())
|
||||||
{
|
{
|
||||||
copy(src, "index", mIndex);
|
|
||||||
copy(src, "texCoord", mTexCoord);
|
|
||||||
copy(src, "strength", mStrength);
|
copy(src, "strength", mStrength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -950,13 +970,13 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(
|
||||||
|
|
||||||
void Material::NormalTextureInfo::serialize(object& dst) const
|
void Material::NormalTextureInfo::serialize(object& dst) const
|
||||||
{
|
{
|
||||||
write(mIndex, "index", dst, INVALID_INDEX);
|
TextureInfo::serialize(dst);
|
||||||
write(mTexCoord, "texCoord", dst, 0);
|
|
||||||
write(mScale, "scale", dst, 1.f);
|
write(mScale, "scale", dst, 1.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src)
|
const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src)
|
||||||
{
|
{
|
||||||
|
TextureInfo::operator=(src);
|
||||||
if (src.is_object())
|
if (src.is_object())
|
||||||
{
|
{
|
||||||
copy(src, "index", mIndex);
|
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
|
// 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
|
void Material::serialize(object& dst) const
|
||||||
{
|
{
|
||||||
write(mName, "name", dst);
|
write(mName, "name", dst);
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,21 @@ namespace LL
|
||||||
bool mPresent = false;
|
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
|
class Material
|
||||||
{
|
{
|
||||||
|
|
@ -82,9 +97,15 @@ namespace LL
|
||||||
S32 mIndex = INVALID_INDEX;
|
S32 mIndex = INVALID_INDEX;
|
||||||
S32 mTexCoord = 0;
|
S32 mTexCoord = 0;
|
||||||
|
|
||||||
|
TextureTransform mTextureTransform;
|
||||||
|
|
||||||
bool operator==(const TextureInfo& rhs) const;
|
bool operator==(const TextureInfo& rhs) const;
|
||||||
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);
|
const TextureInfo& operator=(const Value& src);
|
||||||
void serialize(boost::json::object& dst) const;
|
void serialize(boost::json::object& dst) const;
|
||||||
};
|
};
|
||||||
|
|
@ -135,6 +156,8 @@ namespace LL
|
||||||
bool mDoubleSided = false;
|
bool mDoubleSided = false;
|
||||||
Unlit mUnlit;
|
Unlit mUnlit;
|
||||||
|
|
||||||
|
bool isMultiUV() const;
|
||||||
|
|
||||||
const Material& operator=(const Value& src);
|
const Material& operator=(const Value& src);
|
||||||
void serialize(boost::json::object& dst) const;
|
void serialize(boost::json::object& dst) const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -590,8 +590,8 @@ namespace LL
|
||||||
// Write all extensions to dst.extensions
|
// Write all extensions to dst.extensions
|
||||||
// Usage:
|
// Usage:
|
||||||
// write_extensions(dst,
|
// write_extensions(dst,
|
||||||
// "KHR_materials_unlit", mUnlit,
|
// mUnlit, "KHR_materials_unlit",
|
||||||
// "KHR_materials_pbrSpecularGlossiness", mPbrSpecularGlossiness);
|
// mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness");
|
||||||
// returns true if any of the extensions are written
|
// returns true if any of the extensions are written
|
||||||
template<class... Types>
|
template<class... Types>
|
||||||
inline bool write_extensions(boost::json::object& dst, Types... args)
|
inline bool write_extensions(boost::json::object& dst, Types... args)
|
||||||
|
|
@ -816,6 +816,39 @@ namespace LL
|
||||||
return true;
|
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
|
// bool
|
||||||
template<>
|
template<>
|
||||||
inline bool copy(const Value& src, bool& dst)
|
inline bool copy(const Value& src, bool& dst)
|
||||||
|
|
|
||||||
|
|
@ -42,30 +42,45 @@ using namespace boost::json;
|
||||||
// Mesh data useful for Mikktspace tangent generation (and flat normal generation)
|
// Mesh data useful for Mikktspace tangent generation (and flat normal generation)
|
||||||
struct MikktMesh
|
struct MikktMesh
|
||||||
{
|
{
|
||||||
std::vector<LLVector3> p;
|
std::vector<LLVector3> p; //positions
|
||||||
std::vector<LLVector3> n;
|
std::vector<LLVector3> n; //normals
|
||||||
std::vector<LLVector2> tc;
|
std::vector<LLVector4> t; //tangents
|
||||||
std::vector<LLVector4> w;
|
std::vector<LLVector2> tc0; //texcoords 0
|
||||||
std::vector<LLVector4> t;
|
std::vector<LLVector2> tc1; //texcoords 1
|
||||||
std::vector<LLColor4U> c;
|
std::vector<LLColor4U> c; //colors
|
||||||
std::vector<U64> j;
|
std::vector<LLVector4> w; //weights
|
||||||
|
std::vector<U64> j; //joints
|
||||||
|
|
||||||
// initialize from src primitive and make an unrolled triangle list
|
// initialize from src primitive and make an unrolled triangle list
|
||||||
// returns false if the Primitive cannot be converted to a triangle list
|
// returns false if the Primitive cannot be converted to a triangle list
|
||||||
bool copy(const Primitive* prim)
|
bool copy(const Primitive* prim)
|
||||||
{
|
{
|
||||||
bool indexed = !prim->mIndexArray.empty();
|
bool indexed = !prim->mIndexArray.empty();
|
||||||
U32 vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size();
|
size_t vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size();
|
||||||
|
|
||||||
if (prim->mMode != Primitive::Mode::TRIANGLES)
|
size_t triangle_count = 0;
|
||||||
|
|
||||||
|
if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP ||
|
||||||
|
prim->mMode == Primitive::Mode::TRIANGLE_FAN)
|
||||||
{
|
{
|
||||||
LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32) prim->mMode << LL_ENDL;
|
triangle_count = vert_count - 2;
|
||||||
|
}
|
||||||
|
else if (prim->mMode == Primitive::Mode::TRIANGLES)
|
||||||
|
{
|
||||||
|
triangle_count = vert_count / 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32)prim->mMode << LL_ENDL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vert_count = triangle_count * 3;
|
||||||
|
llassert(vert_count <= size_t(U32_MAX)); // triangle_count will also naturally be under the limit
|
||||||
|
|
||||||
p.resize(vert_count);
|
p.resize(vert_count);
|
||||||
n.resize(vert_count);
|
n.resize(vert_count);
|
||||||
tc.resize(vert_count);
|
tc0.resize(vert_count);
|
||||||
c.resize(vert_count);
|
c.resize(vert_count);
|
||||||
|
|
||||||
bool has_normals = !prim->mNormals.empty();
|
bool has_normals = !prim->mNormals.empty();
|
||||||
|
|
@ -78,6 +93,7 @@ struct MikktMesh
|
||||||
{
|
{
|
||||||
t.resize(vert_count);
|
t.resize(vert_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rigged = !prim->mWeights.empty();
|
bool rigged = !prim->mWeights.empty();
|
||||||
if (rigged)
|
if (rigged)
|
||||||
{
|
{
|
||||||
|
|
@ -85,23 +101,69 @@ struct MikktMesh
|
||||||
j.resize(vert_count);
|
j.resize(vert_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int 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());
|
for (U32 tri_idx = 0; tri_idx < U32(triangle_count); ++tri_idx)
|
||||||
tc[i].set(prim->mTexCoords[idx]);
|
{
|
||||||
c[i] = prim->mColors[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 (indexed)
|
||||||
|
{
|
||||||
|
idx[0] = prim->mIndexArray[idx[0]];
|
||||||
|
idx[1] = prim->mIndexArray[idx[1]];
|
||||||
|
idx[2] = prim->mIndexArray[idx[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (U32 v = 0; v < 3; ++v)
|
||||||
|
{
|
||||||
|
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)
|
if (has_normals)
|
||||||
{
|
{
|
||||||
n[i].set(prim->mNormals[idx].getF32ptr());
|
n[i].set(prim->mNormals[idx[v]].getF32ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rigged)
|
if (rigged)
|
||||||
{
|
{
|
||||||
w[i].set(prim->mWeights[idx].getF32ptr());
|
w[i].set(prim->mWeights[idx[v]].getF32ptr());
|
||||||
j[i] = prim->mJoints[idx];
|
j[i] = prim->mJoints[idx[v]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,8 +172,8 @@ struct MikktMesh
|
||||||
|
|
||||||
void genNormals()
|
void genNormals()
|
||||||
{
|
{
|
||||||
U32 tri_count = p.size() / 3;
|
size_t tri_count = p.size() / 3;
|
||||||
for (U32 i = 0; i < tri_count; ++i)
|
for (size_t i = 0; i < tri_count; ++i)
|
||||||
{
|
{
|
||||||
LLVector3 v0 = p[i * 3];
|
LLVector3 v0 = p[i * 3];
|
||||||
LLVector3 v1 = p[i * 3 + 1];
|
LLVector3 v1 = p[i * 3 + 1];
|
||||||
|
|
@ -138,25 +200,34 @@ struct MikktMesh
|
||||||
void write(Primitive* prim) const
|
void write(Primitive* prim) const
|
||||||
{
|
{
|
||||||
//re-weld
|
//re-weld
|
||||||
meshopt_Stream mos[] =
|
std::vector<meshopt_Stream> mos =
|
||||||
{
|
{
|
||||||
{ &p[0], sizeof(LLVector3), sizeof(LLVector3) },
|
{ &p[0], sizeof(LLVector3), sizeof(LLVector3) },
|
||||||
{ &n[0], sizeof(LLVector3), sizeof(LLVector3) },
|
{ &n[0], sizeof(LLVector3), sizeof(LLVector3) },
|
||||||
{ &t[0], sizeof(LLVector4), sizeof(LLVector4) },
|
{ &t[0], sizeof(LLVector4), sizeof(LLVector4) },
|
||||||
{ &tc[0], sizeof(LLVector2), sizeof(LLVector2) },
|
{ &tc0[0], sizeof(LLVector2), sizeof(LLVector2) },
|
||||||
{ &c[0], sizeof(LLColor4U), sizeof(LLColor4U) },
|
{ &c[0], sizeof(LLColor4U), sizeof(LLColor4U) }
|
||||||
{ w.empty() ? nullptr : &w[0], sizeof(LLVector4), sizeof(LLVector4) },
|
|
||||||
{ j.empty() ? nullptr : &j[0], sizeof(U64), sizeof(U64) }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
std::vector<U32> remap;
|
||||||
remap.resize(p.size());
|
remap.resize(p.size());
|
||||||
|
|
||||||
U32 stream_count = w.empty() ? 5 : 7;
|
size_t 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->mNormals.resize(vert_count);
|
||||||
prim->mTangents.resize(vert_count);
|
prim->mTangents.resize(vert_count);
|
||||||
prim->mPositions.resize(vert_count);
|
prim->mPositions.resize(vert_count);
|
||||||
|
|
@ -166,6 +237,10 @@ struct MikktMesh
|
||||||
prim->mWeights.resize(vert_count);
|
prim->mWeights.resize(vert_count);
|
||||||
prim->mJoints.resize(vert_count);
|
prim->mJoints.resize(vert_count);
|
||||||
}
|
}
|
||||||
|
if (!tc1.empty())
|
||||||
|
{
|
||||||
|
prim->mTexCoords1.resize(vert_count);
|
||||||
|
}
|
||||||
|
|
||||||
prim->mIndexArray.resize(remap.size());
|
prim->mIndexArray.resize(remap.size());
|
||||||
|
|
||||||
|
|
@ -178,7 +253,7 @@ struct MikktMesh
|
||||||
|
|
||||||
prim->mPositions[dst_idx].load3(p[src_idx].mV);
|
prim->mPositions[dst_idx].load3(p[src_idx].mV);
|
||||||
prim->mNormals[dst_idx].load3(n[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->mTangents[dst_idx].loadua(t[src_idx].mV);
|
||||||
prim->mColors[dst_idx] = c[src_idx];
|
prim->mColors[dst_idx] = c[src_idx];
|
||||||
|
|
||||||
|
|
@ -187,6 +262,11 @@ struct MikktMesh
|
||||||
prim->mWeights[dst_idx].loadua(w[src_idx].mV);
|
prim->mWeights[dst_idx].loadua(w[src_idx].mV);
|
||||||
prim->mJoints[dst_idx] = j[src_idx];
|
prim->mJoints[dst_idx] = j[src_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tc1.empty())
|
||||||
|
{
|
||||||
|
prim->mTexCoords1[dst_idx] = tc1[src_idx];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prim->mGLMode = LLRender::TRIANGLES;
|
prim->mGLMode = LLRender::TRIANGLES;
|
||||||
|
|
@ -210,8 +290,8 @@ struct MikktMesh
|
||||||
|
|
||||||
mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num)
|
mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num)
|
||||||
{
|
{
|
||||||
F32* uv = tc[face_num * 3 + vert_num].mV;
|
F32* uv = tc0[face_num * 3 + vert_num].mV;
|
||||||
return mikk::float3(uv[0], uv[1], 1.0f);
|
return mikk::float3(uv[0], 1.f-uv[1], 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num)
|
mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num)
|
||||||
|
|
@ -228,6 +308,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)
|
bool Primitive::prep(Asset& asset)
|
||||||
{
|
{
|
||||||
// allocate vertex buffer
|
// allocate vertex buffer
|
||||||
|
|
@ -261,7 +349,11 @@ bool Primitive::prep(Asset& asset)
|
||||||
}
|
}
|
||||||
else if (attribName == "TEXCOORD_0")
|
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")
|
else if (attribName == "JOINTS_0")
|
||||||
{
|
{
|
||||||
|
|
@ -297,24 +389,28 @@ bool Primitive::prep(Asset& asset)
|
||||||
mask |= LLVertexBuffer::MAP_JOINT;
|
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;
|
mask |= LLVertexBuffer::MAP_TEXCOORD0;
|
||||||
|
|
||||||
|
if (!mTexCoords1.empty())
|
||||||
|
{
|
||||||
|
mask |= LLVertexBuffer::MAP_TEXCOORD1;
|
||||||
|
}
|
||||||
|
|
||||||
if (mColors.empty())
|
if (mColors.empty())
|
||||||
{
|
{
|
||||||
mColors.resize(mPositions.size(), LLColor4U::white);
|
mColors.resize(mPositions.size(), LLColor4U::white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mShaderVariant = 0;
|
||||||
|
|
||||||
// TODO: support colorless vertex buffers
|
// TODO: support colorless vertex buffers
|
||||||
mask |= LLVertexBuffer::MAP_COLOR;
|
mask |= LLVertexBuffer::MAP_COLOR;
|
||||||
|
|
||||||
mShaderVariant = 0;
|
|
||||||
|
|
||||||
bool unlit = false;
|
bool unlit = false;
|
||||||
|
|
||||||
// bake material basecolor into color array
|
// bake material basecolor into color array
|
||||||
|
|
@ -332,6 +428,11 @@ bool Primitive::prep(Asset& asset)
|
||||||
mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
|
mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
|
||||||
unlit = true;
|
unlit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (material.isMultiUV())
|
||||||
|
{
|
||||||
|
mShaderVariant |= LLGLSLShader::GLTFVariant::MULTI_UV;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mNormals.empty() && !unlit)
|
if (mNormals.empty() && !unlit)
|
||||||
|
|
@ -411,7 +512,10 @@ bool Primitive::prep(Asset& asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
mVertexBuffer = new LLVertexBuffer(mask);
|
mVertexBuffer = new LLVertexBuffer(mask);
|
||||||
mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size() * 2); // double the size of the index buffer for 32-bit indices
|
// we store these buffer sizes as S32 elsewhere
|
||||||
|
llassert(mPositions.size() <= size_t(S32_MAX));
|
||||||
|
llassert(mIndexArray.size() <= size_t(S32_MAX / 2));
|
||||||
|
mVertexBuffer->allocateBuffer(U32(mPositions.size()), U32(mIndexArray.size() * 2)); // double the size of the index buffer for 32-bit indices
|
||||||
|
|
||||||
mVertexBuffer->setBuffer();
|
mVertexBuffer->setBuffer();
|
||||||
mVertexBuffer->setPositionData(mPositions.data());
|
mVertexBuffer->setPositionData(mPositions.data());
|
||||||
|
|
@ -434,16 +538,18 @@ bool Primitive::prep(Asset& asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
|
// flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
|
||||||
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());
|
||||||
mVertexBuffer->setTexCoordData(mTexCoords.data());
|
vertical_flip(mTexCoords1);
|
||||||
for (auto& tc : mTexCoords)
|
|
||||||
{
|
|
||||||
tc[1] = 1.f - tc[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!mIndexArray.empty())
|
if (!mIndexArray.empty())
|
||||||
{
|
{
|
||||||
mVertexBuffer->setIndexData(mIndexArray.data());
|
mVertexBuffer->setIndexData(mIndexArray.data());
|
||||||
|
|
@ -453,11 +559,14 @@ bool Primitive::prep(Asset& asset)
|
||||||
|
|
||||||
mVertexBuffer->unbind();
|
mVertexBuffer->unbind();
|
||||||
|
|
||||||
|
if (mMaterial != INVALID_INDEX)
|
||||||
|
{
|
||||||
Material& material = asset.mMaterials[mMaterial];
|
Material& material = asset.mMaterials[mMaterial];
|
||||||
if (material.mAlphaMode == Material::AlphaMode::BLEND)
|
if (material.mAlphaMode == Material::AlphaMode::BLEND)
|
||||||
{
|
{
|
||||||
mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
|
mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -614,13 +723,13 @@ const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start,
|
||||||
//create a proxy LLVolumeFace for the raycast
|
//create a proxy LLVolumeFace for the raycast
|
||||||
LLVolumeFace face;
|
LLVolumeFace face;
|
||||||
face.mPositions = mPositions.data();
|
face.mPositions = mPositions.data();
|
||||||
face.mTexCoords = mTexCoords.data();
|
face.mTexCoords = mTexCoords0.data();
|
||||||
face.mNormals = mNormals.data();
|
face.mNormals = mNormals.data();
|
||||||
face.mTangents = mTangents.data();
|
face.mTangents = mTangents.data();
|
||||||
face.mIndices = nullptr; // unreferenced
|
face.mIndices = nullptr; // unreferenced
|
||||||
|
|
||||||
face.mNumIndices = (S32)mIndexArray.size();
|
face.mNumIndices = S32(mIndexArray.size());
|
||||||
face.mNumVertices = (S32)mPositions.size();
|
face.mNumVertices = S32(mPositions.size());
|
||||||
|
|
||||||
LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
|
LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
|
||||||
intersect.traverse(mOctree);
|
intersect.traverse(mOctree);
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ namespace LL
|
||||||
LLPointer<LLVertexBuffer> mVertexBuffer;
|
LLPointer<LLVertexBuffer> mVertexBuffer;
|
||||||
|
|
||||||
// CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code
|
// 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> mNormals;
|
||||||
std::vector<LLVector4a> mTangents;
|
std::vector<LLVector4a> mTangents;
|
||||||
std::vector<LLVector4a> mPositions;
|
std::vector<LLVector4a> mPositions;
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,8 @@ void GLTFSceneManager::uploadSelection()
|
||||||
LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE);
|
LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE);
|
||||||
auto& data = mUploadingAsset->mBuffers[idx].mData;
|
auto& data = mUploadingAsset->mBuffers[idx].mData;
|
||||||
|
|
||||||
cache.write((const U8*)data.data(), data.size());
|
llassert(data.size() <= size_t(S32_MAX));
|
||||||
|
cache.write((const U8 *) data.data(), S32(data.size()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#if GLTF_SIM_SUPPORT
|
#if GLTF_SIM_SUPPORT
|
||||||
|
|
@ -399,8 +400,9 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a
|
||||||
{
|
{
|
||||||
LLFileSystem file(id, asset_type, LLFileSystem::READ);
|
LLFileSystem file(id, asset_type, LLFileSystem::READ);
|
||||||
std::string data;
|
std::string data;
|
||||||
data.resize(file.getSize());
|
S32 file_size = file.getSize();
|
||||||
file.read((U8*)data.data(), data.size());
|
data.resize(file_size);
|
||||||
|
file.read((U8*)data.data(), file_size);
|
||||||
|
|
||||||
boost::json::value json = boost::json::parse(data);
|
boost::json::value json = boost::json::parse(data);
|
||||||
|
|
||||||
|
|
@ -479,7 +481,8 @@ void GLTFSceneManager::update()
|
||||||
LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE);
|
LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE);
|
||||||
|
|
||||||
LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL;
|
LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL;
|
||||||
cache.write((const U8 *) buffer.c_str(), buffer.size());
|
llassert(buffer.size() <= size_t(S32_MAX));
|
||||||
|
cache.write((const U8 *) buffer.c_str(), S32(buffer.size()));
|
||||||
|
|
||||||
mUploadingAsset = nullptr;
|
mUploadingAsset = nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -549,10 +552,16 @@ void GLTFSceneManager::render(bool opaque, bool rigged, bool unlit)
|
||||||
|
|
||||||
void GLTFSceneManager::render(U8 variant)
|
void GLTFSceneManager::render(U8 variant)
|
||||||
{
|
{
|
||||||
// for debugging, just render the whole scene as opaque
|
// just render the whole scene by traversing the whole scenegraph
|
||||||
// by traversing the whole scenegraph
|
// Assumes camera transform is already set and appropriate shader is already bound.
|
||||||
// Assumes camera transform is already set and
|
// Eventually we'll want a smarter render pipe that has pre-sorted the scene graph
|
||||||
// appropriate shader is already boundd
|
// 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);
|
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||||
|
|
||||||
|
|
@ -663,11 +672,30 @@ static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info,
|
||||||
{
|
{
|
||||||
if (info.mIndex != INVALID_INDEX)
|
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)
|
if (tex)
|
||||||
{
|
{
|
||||||
tex->addTextureStats(2048.f * 2048.f);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -710,10 +738,10 @@ void GLTFSceneManager::bind(Asset& asset, Material& material)
|
||||||
|
|
||||||
bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep);
|
bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep);
|
||||||
|
|
||||||
F32 base_color_packed[8];
|
F32 tf[8];
|
||||||
//mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
|
material.mPbrMetallicRoughness.mBaseColorTexture.mTextureTransform.getPacked(tf);
|
||||||
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
|
shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, tf);
|
||||||
shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed);
|
shader->uniform1i(LLShaderMgr::BASE_COLOR_TEXCOORD, material.mPbrMetallicRoughness.mBaseColorTexture.getTexCoord());
|
||||||
|
|
||||||
if (!LLPipeline::sShadowRender)
|
if (!LLPipeline::sShadowRender)
|
||||||
{
|
{
|
||||||
|
|
@ -728,20 +756,21 @@ void GLTFSceneManager::bind(Asset& asset, Material& material)
|
||||||
shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, material.mPbrMetallicRoughness.mMetallicFactor);
|
shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, material.mPbrMetallicRoughness.mMetallicFactor);
|
||||||
shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(material.mEmissiveFactor));
|
shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(material.mEmissiveFactor));
|
||||||
|
|
||||||
F32 normal_packed[8];
|
material.mNormalTexture.mTextureTransform.getPacked(tf);
|
||||||
//mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
|
shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, tf);
|
||||||
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
|
shader->uniform1i(LLShaderMgr::NORMAL_TEXCOORD, material.mNormalTexture.getTexCoord());
|
||||||
shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, (F32*)normal_packed);
|
|
||||||
|
|
||||||
F32 metallic_roughness_packed[8];
|
material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTextureTransform.getPacked(tf);
|
||||||
//mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
|
shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, tf);
|
||||||
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
|
shader->uniform1i(LLShaderMgr::METALLIC_ROUGHNESS_TEXCOORD, material.mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord());
|
||||||
shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, (F32*)metallic_roughness_packed);
|
|
||||||
|
|
||||||
F32 emissive_packed[8];
|
material.mOcclusionTexture.mTextureTransform.getPacked(tf);
|
||||||
//mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
|
shader->uniform4fv(LLShaderMgr::TEXTURE_OCCLUSION_TRANSFORM, 2, tf);
|
||||||
LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
|
shader->uniform1i(LLShaderMgr::OCCLUSION_TEXCOORD, material.mOcclusionTexture.getTexCoord());
|
||||||
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -220,8 +220,8 @@ bool LLViewerDynamicTexture::updateAllInstances()
|
||||||
if (dynamicTexture->needsRender())
|
if (dynamicTexture->needsRender())
|
||||||
{
|
{
|
||||||
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("needsRender");
|
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("needsRender");
|
||||||
llassert(dynamicTexture->getFullWidth() <= (S32)LLPipeline::MAX_BAKE_WIDTH);
|
llassert(dynamicTexture->getFullWidth() <= S32(LLPipeline::MAX_BAKE_WIDTH));
|
||||||
llassert(dynamicTexture->getFullHeight() <= (S32)LLPipeline::MAX_BAKE_WIDTH);
|
llassert(dynamicTexture->getFullHeight() <= S32(LLPipeline::MAX_BAKE_WIDTH));
|
||||||
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,8 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, bool
|
||||||
|
|
||||||
res /= 2;
|
res /= 2;
|
||||||
|
|
||||||
S32 mip = i - ((S32)mMipChain.size() - mips);
|
llassert(mMipChain.size() <= size_t(S32_MAX));
|
||||||
|
GLint mip = i - (S32(mMipChain.size()) - mips);
|
||||||
|
|
||||||
if (mip >= 0)
|
if (mip >= 0)
|
||||||
{
|
{
|
||||||
|
|
@ -487,7 +488,8 @@ void LLHeroProbeManager::updateUniforms()
|
||||||
mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius;
|
mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
mHeroData.heroMipCount = (GLint)mMipChain.size();
|
llassert(mMipChain.size() <= size_t(S32_MAX));
|
||||||
|
mHeroData.heroMipCount = S32(mMipChain.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLHeroProbeManager::renderDebug()
|
void LLHeroProbeManager::renderDebug()
|
||||||
|
|
|
||||||
|
|
@ -2398,7 +2398,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url)
|
||||||
message_data["region_id"].asUUID(),
|
message_data["region_id"].asUUID(),
|
||||||
position,
|
position,
|
||||||
local_bin_bucket.data(),
|
local_bin_bucket.data(),
|
||||||
local_bin_bucket.size(),
|
S32(local_bin_bucket.size()),
|
||||||
local_sender,
|
local_sender,
|
||||||
message_data["asset_id"].asUUID());
|
message_data["asset_id"].asUUID());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -957,11 +957,11 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(
|
||||||
else if (root_id.notNull())
|
else if (root_id.notNull())
|
||||||
{
|
{
|
||||||
cat_array_t* cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
|
cat_array_t* cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
|
||||||
if (cats)
|
if(cats)
|
||||||
{
|
{
|
||||||
for (auto& p_cat : *cats)
|
for (auto& p_cat : *cats)
|
||||||
{
|
{
|
||||||
if (p_cat && p_cat->getPreferredType() == preferred_type)
|
if(p_cat && p_cat->getPreferredType() == preferred_type)
|
||||||
{
|
{
|
||||||
const LLUUID& folder_id = p_cat->getUUID();
|
const LLUUID& folder_id = p_cat->getUUID();
|
||||||
if (rv.isNull() || folder_id < rv)
|
if (rv.isNull() || folder_id < rv)
|
||||||
|
|
|
||||||
|
|
@ -2768,7 +2768,7 @@ EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
|
||||||
|
|
||||||
// *HACK: In order to resolve SL-22177, we need to block drags
|
// *HACK: In order to resolve SL-22177, we need to block drags
|
||||||
// from notecards and objects onto other objects.
|
// from notecards and objects onto other objects.
|
||||||
if ((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
|
if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
|
||||||
{
|
{
|
||||||
return ACCEPT_NO;
|
return ACCEPT_NO;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,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.mName = shader.mName.c_str();
|
||||||
variant.mFeatures = shader.mFeatures;
|
variant.mFeatures = shader.mFeatures;
|
||||||
|
|
@ -275,6 +275,11 @@ static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool
|
||||||
variant.addPermutation("UNLIT", "1");
|
variant.addPermutation("UNLIT", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (multi_uv)
|
||||||
|
{
|
||||||
|
variant.addPermutation("MULTI_UV", "1");
|
||||||
|
}
|
||||||
|
|
||||||
if (alpha_blend)
|
if (alpha_blend)
|
||||||
{
|
{
|
||||||
variant.addPermutation("ALPHA_BLEND", "1");
|
variant.addPermutation("ALPHA_BLEND", "1");
|
||||||
|
|
@ -321,8 +326,9 @@ static bool make_gltf_variants(LLGLSLShader& shader, bool use_sun_shadow)
|
||||||
bool alpha_blend = i & LLGLSLShader::GLTFVariant::ALPHA_BLEND;
|
bool alpha_blend = i & LLGLSLShader::GLTFVariant::ALPHA_BLEND;
|
||||||
bool rigged = i & LLGLSLShader::GLTFVariant::RIGGED;
|
bool rigged = i & LLGLSLShader::GLTFVariant::RIGGED;
|
||||||
bool unlit = i & LLGLSLShader::GLTFVariant::UNLIT;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -428,8 +428,7 @@ const LLUUID (&LLVLComposition::getDefaultTextures())[ASSET_COUNT]
|
||||||
|
|
||||||
LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
|
LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
|
||||||
LLTerrainMaterials(),
|
LLTerrainMaterials(),
|
||||||
LLViewerLayer(width, scale),
|
LLViewerLayer(width, scale)
|
||||||
mParamsReady(false)
|
|
||||||
{
|
{
|
||||||
// Load Terrain Textures - Original ones
|
// Load Terrain Textures - Original ones
|
||||||
const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
|
const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,6 @@ public:
|
||||||
bool generateComposition();
|
bool generateComposition();
|
||||||
// Generate texture from composition values.
|
// Generate texture from composition values.
|
||||||
bool generateMinimapTileLand(const F32 x, const F32 y, const F32 width, const F32 height);
|
bool generateMinimapTileLand(const F32 x, const F32 y, const F32 width, const F32 height);
|
||||||
bool generateTexture(const F32 x, const F32 y, const F32 width, const F32 height);
|
|
||||||
|
|
||||||
// Use these as indeces ito the get/setters below that use 'corner'
|
// Use these as indeces ito the get/setters below that use 'corner'
|
||||||
enum ECorner
|
enum ECorner
|
||||||
|
|
|
||||||
|
|
@ -1982,9 +1982,8 @@ void LLVOCache::writeGenericExtrasToCache(U64 handle, const LLUUID& id, const LL
|
||||||
LLViewerRegion* pRegion = LLWorld::getInstance()->getRegionFromHandle(handle);
|
LLViewerRegion* pRegion = LLWorld::getInstance()->getRegionFromHandle(handle);
|
||||||
|
|
||||||
U32 num_entries = 0;
|
U32 num_entries = 0;
|
||||||
U32 inmem_entries = 0;
|
|
||||||
U32 skipped = 0;
|
U32 skipped = 0;
|
||||||
inmem_entries = (U32)cache_extras_entry_map.size();
|
size_t inmem_entries = cache_extras_entry_map.size();
|
||||||
for (auto [local_id, entry] : cache_extras_entry_map)
|
for (auto [local_id, entry] : cache_extras_entry_map)
|
||||||
{
|
{
|
||||||
// Only write out GLTFOverrides that we can actually apply again on import.
|
// Only write out GLTFOverrides that we can actually apply again on import.
|
||||||
|
|
|
||||||
|
|
@ -4865,6 +4865,7 @@ LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const
|
||||||
|
|
||||||
bool LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32 *face_hitp,
|
bool LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32 *face_hitp,
|
||||||
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
|
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (!mbCanSelect
|
if (!mbCanSelect
|
||||||
|| mDrawable->isDead()
|
|| mDrawable->isDead()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
border="true"
|
border="true"
|
||||||
follows="top|left"
|
follows="top|left"
|
||||||
height="460"
|
height="460"
|
||||||
help_topic="panel_region_terrain_tab"
|
|
||||||
label="Terrain"
|
label="Terrain"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="0"
|
left="0"
|
||||||
|
|
@ -225,13 +224,12 @@
|
||||||
left="0"
|
left="0"
|
||||||
name="terrain_tabs"
|
name="terrain_tabs"
|
||||||
tab_position="top"
|
tab_position="top"
|
||||||
tab_width="100"
|
tab_width="110"
|
||||||
tab_padding_right="3"
|
tab_padding_right="3"
|
||||||
top_pad="0"
|
top_pad="0"
|
||||||
width="700">
|
width="700">
|
||||||
<panel
|
<panel
|
||||||
border="true"
|
border="true"
|
||||||
class="panel_settings_terrain_elevation"
|
|
||||||
filename="panel_settings_terrain_elevation.xml"
|
filename="panel_settings_terrain_elevation.xml"
|
||||||
label="Elevation"
|
label="Elevation"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
|
|
@ -240,7 +238,6 @@
|
||||||
name="terrain_elevation_panel" />
|
name="terrain_elevation_panel" />
|
||||||
<panel
|
<panel
|
||||||
border="true"
|
border="true"
|
||||||
class="panel_settings_terrain_transform"
|
|
||||||
filename="panel_settings_terrain_transform.xml"
|
filename="panel_settings_terrain_transform.xml"
|
||||||
label="Transforms"
|
label="Transforms"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
|
|
@ -256,8 +253,8 @@
|
||||||
height="20"
|
height="20"
|
||||||
label="Apply"
|
label="Apply"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="353"
|
left="351"
|
||||||
name="apply_btn"
|
name="apply_btn"
|
||||||
top_delta="290"
|
top_delta="310"
|
||||||
width="100" />
|
width="100" />
|
||||||
</panel>
|
</panel>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,62 @@
|
||||||
left="0"
|
left="0"
|
||||||
name="panel_settings_terrain_transform"
|
name="panel_settings_terrain_transform"
|
||||||
top="0">
|
top="0">
|
||||||
|
<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
|
<text
|
||||||
type="string"
|
type="string"
|
||||||
length="1"
|
length="1"
|
||||||
|
|
@ -17,19 +73,10 @@
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="terrain0ScaleU_label"
|
name="terrain0ScaleU_label"
|
||||||
left="10"
|
left="10"
|
||||||
top_pad="3"
|
top_pad="0"
|
||||||
width="170">
|
width="170">
|
||||||
Scale u
|
Scale u
|
||||||
</text>
|
</text>
|
||||||
<view_border
|
|
||||||
bevel_style="none"
|
|
||||||
follows="top|left"
|
|
||||||
height="0"
|
|
||||||
layout="topleft"
|
|
||||||
left="8"
|
|
||||||
top_pad="-2"
|
|
||||||
name="terrain0ScaleU_horizontal"
|
|
||||||
width="430" />
|
|
||||||
<spinner
|
<spinner
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
height="19"
|
height="19"
|
||||||
|
|
@ -37,7 +84,7 @@
|
||||||
label="Scale u"
|
label="Scale u"
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="10"
|
left="46"
|
||||||
min_val="-100"
|
min_val="-100"
|
||||||
max_val="100"
|
max_val="100"
|
||||||
name="terrain0ScaleU"
|
name="terrain0ScaleU"
|
||||||
|
|
@ -78,6 +125,15 @@
|
||||||
max_val="100"
|
max_val="100"
|
||||||
name="terrain3ScaleU"
|
name="terrain3ScaleU"
|
||||||
width="64" />
|
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
|
<text
|
||||||
type="string"
|
type="string"
|
||||||
length="1"
|
length="1"
|
||||||
|
|
@ -88,19 +144,10 @@
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="terrain0ScaleV_label"
|
name="terrain0ScaleV_label"
|
||||||
left="10"
|
left="10"
|
||||||
top_pad="3"
|
top_pad="0"
|
||||||
width="170">
|
width="170">
|
||||||
Scale v
|
Scale v
|
||||||
</text>
|
</text>
|
||||||
<view_border
|
|
||||||
bevel_style="none"
|
|
||||||
follows="top|left"
|
|
||||||
height="0"
|
|
||||||
layout="topleft"
|
|
||||||
left="8"
|
|
||||||
top_pad="-2"
|
|
||||||
name="terrain0ScaleV_horizontal"
|
|
||||||
width="430" />
|
|
||||||
<spinner
|
<spinner
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
height="19"
|
height="19"
|
||||||
|
|
@ -108,7 +155,7 @@
|
||||||
label="Scale v"
|
label="Scale v"
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="10"
|
left="46"
|
||||||
min_val="-100"
|
min_val="-100"
|
||||||
max_val="100"
|
max_val="100"
|
||||||
name="terrain0ScaleV"
|
name="terrain0ScaleV"
|
||||||
|
|
@ -149,6 +196,15 @@
|
||||||
max_val="100"
|
max_val="100"
|
||||||
name="terrain3ScaleV"
|
name="terrain3ScaleV"
|
||||||
width="64" />
|
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
|
<text
|
||||||
type="string"
|
type="string"
|
||||||
length="1"
|
length="1"
|
||||||
|
|
@ -159,19 +215,10 @@
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="terrain0Rotation_label"
|
name="terrain0Rotation_label"
|
||||||
left="10"
|
left="10"
|
||||||
top_pad="3"
|
top_pad="0"
|
||||||
width="170">
|
width="170">
|
||||||
Rotation
|
Rotation
|
||||||
</text>
|
</text>
|
||||||
<view_border
|
|
||||||
bevel_style="none"
|
|
||||||
follows="top|left"
|
|
||||||
height="0"
|
|
||||||
layout="topleft"
|
|
||||||
left="8"
|
|
||||||
top_pad="-2"
|
|
||||||
name="terrain0Rotation_horizontal"
|
|
||||||
width="430" />
|
|
||||||
<spinner
|
<spinner
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
height="19"
|
height="19"
|
||||||
|
|
@ -179,7 +226,7 @@
|
||||||
label="Rotation"
|
label="Rotation"
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="10"
|
left="46"
|
||||||
min_val="-360"
|
min_val="-360"
|
||||||
max_val="360"
|
max_val="360"
|
||||||
name="terrain0Rotation"
|
name="terrain0Rotation"
|
||||||
|
|
@ -192,8 +239,8 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-360"
|
||||||
max_val="100"
|
max_val="360"
|
||||||
name="terrain1Rotation"
|
name="terrain1Rotation"
|
||||||
width="64" />
|
width="64" />
|
||||||
<spinner
|
<spinner
|
||||||
|
|
@ -204,8 +251,8 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-360"
|
||||||
max_val="100"
|
max_val="360"
|
||||||
name="terrain2Rotation"
|
name="terrain2Rotation"
|
||||||
width="64" />
|
width="64" />
|
||||||
<spinner
|
<spinner
|
||||||
|
|
@ -216,10 +263,19 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-360"
|
||||||
max_val="100"
|
max_val="360"
|
||||||
name="terrain3Rotation"
|
name="terrain3Rotation"
|
||||||
width="64" />
|
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
|
<text
|
||||||
type="string"
|
type="string"
|
||||||
length="1"
|
length="1"
|
||||||
|
|
@ -230,19 +286,10 @@
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="terrain0OffsetU_label"
|
name="terrain0OffsetU_label"
|
||||||
left="10"
|
left="10"
|
||||||
top_pad="3"
|
top_pad="0"
|
||||||
width="170">
|
width="170">
|
||||||
Offset y
|
Offset y
|
||||||
</text>
|
</text>
|
||||||
<view_border
|
|
||||||
bevel_style="none"
|
|
||||||
follows="top|left"
|
|
||||||
height="0"
|
|
||||||
layout="topleft"
|
|
||||||
left="8"
|
|
||||||
top_pad="-2"
|
|
||||||
name="terrain0OffsetU_horizontal"
|
|
||||||
width="430" />
|
|
||||||
<spinner
|
<spinner
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
height="19"
|
height="19"
|
||||||
|
|
@ -250,7 +297,7 @@
|
||||||
label="Offset u"
|
label="Offset u"
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="10"
|
left="46"
|
||||||
min_val="-999"
|
min_val="-999"
|
||||||
max_val="999"
|
max_val="999"
|
||||||
name="terrain0OffsetU"
|
name="terrain0OffsetU"
|
||||||
|
|
@ -263,8 +310,8 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-999"
|
||||||
max_val="100"
|
max_val="999"
|
||||||
name="terrain1OffsetU"
|
name="terrain1OffsetU"
|
||||||
width="64" />
|
width="64" />
|
||||||
<spinner
|
<spinner
|
||||||
|
|
@ -275,8 +322,8 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-999"
|
||||||
max_val="100"
|
max_val="999"
|
||||||
name="terrain2OffsetU"
|
name="terrain2OffsetU"
|
||||||
width="64" />
|
width="64" />
|
||||||
<spinner
|
<spinner
|
||||||
|
|
@ -287,10 +334,19 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-999"
|
||||||
max_val="100"
|
max_val="999"
|
||||||
name="terrain3OffsetU"
|
name="terrain3OffsetU"
|
||||||
width="64" />
|
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
|
<text
|
||||||
type="string"
|
type="string"
|
||||||
length="1"
|
length="1"
|
||||||
|
|
@ -301,19 +357,10 @@
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
name="terrain0OffsetV_label"
|
name="terrain0OffsetV_label"
|
||||||
left="10"
|
left="10"
|
||||||
top_pad="3"
|
top_pad="0"
|
||||||
width="170">
|
width="170">
|
||||||
Offset v
|
Offset v
|
||||||
</text>
|
</text>
|
||||||
<view_border
|
|
||||||
bevel_style="none"
|
|
||||||
follows="top|left"
|
|
||||||
height="0"
|
|
||||||
layout="topleft"
|
|
||||||
left="8"
|
|
||||||
top_pad="-2"
|
|
||||||
name="terrain0OffsetV_horizontal"
|
|
||||||
width="430" />
|
|
||||||
<spinner
|
<spinner
|
||||||
follows="left|top"
|
follows="left|top"
|
||||||
height="19"
|
height="19"
|
||||||
|
|
@ -321,7 +368,7 @@
|
||||||
label="Offset v"
|
label="Offset v"
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left="10"
|
left="46"
|
||||||
min_val="-999"
|
min_val="-999"
|
||||||
max_val="999"
|
max_val="999"
|
||||||
name="terrain0OffsetV"
|
name="terrain0OffsetV"
|
||||||
|
|
@ -334,8 +381,8 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-999"
|
||||||
max_val="100"
|
max_val="999"
|
||||||
name="terrain1OffsetV"
|
name="terrain1OffsetV"
|
||||||
width="64" />
|
width="64" />
|
||||||
<spinner
|
<spinner
|
||||||
|
|
@ -346,8 +393,8 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-999"
|
||||||
max_val="100"
|
max_val="999"
|
||||||
name="terrain2OffsetV"
|
name="terrain2OffsetV"
|
||||||
width="64" />
|
width="64" />
|
||||||
<spinner
|
<spinner
|
||||||
|
|
@ -358,8 +405,17 @@
|
||||||
label_width="0"
|
label_width="0"
|
||||||
layout="topleft"
|
layout="topleft"
|
||||||
left_delta="110"
|
left_delta="110"
|
||||||
min_val="-100"
|
min_val="-999"
|
||||||
max_val="100"
|
max_val="999"
|
||||||
name="terrain3OffsetV"
|
name="terrain3OffsetV"
|
||||||
width="64" />
|
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>
|
</panel>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue