summaryrefslogtreecommitdiff
path: root/indra/newview/gltf
diff options
context:
space:
mode:
authorDave Parks <davep@lindenlab.com>2024-06-11 17:10:13 -0500
committerGitHub <noreply@github.com>2024-06-11 17:10:13 -0500
commitf40fbdf4ad27a547e30781cd44cd6847d68d3300 (patch)
treeb26dd135413752a90037bd48ad2217c4dea027ef /indra/newview/gltf
parent429c92ad75fd3b3f7b9dfc52ed034b25004a3b9c (diff)
#1718 Add GLTF support for multiple texcoords (#1720)
* Fix for GLTF MeshPrimitiveModes test
Diffstat (limited to 'indra/newview/gltf')
-rw-r--r--indra/newview/gltf/asset.cpp20
-rw-r--r--indra/newview/gltf/asset.h6
-rw-r--r--indra/newview/gltf/buffer_util.h2
-rw-r--r--indra/newview/gltf/primitive.cpp201
-rw-r--r--indra/newview/gltf/primitive.h3
5 files changed, 181 insertions, 51 deletions
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 4c1da3e645..21be69aae2 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -910,6 +910,24 @@ void Material::TextureInfo::serialize(object& dst) const
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)
{
if (src.is_object())
@@ -1048,7 +1066,7 @@ 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, 0);
+ write(mTexCoord, "texCoord", dst, -1);
}
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index bca269d5dc..ea3f7d480a 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -102,6 +102,10 @@ namespace LL
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;
};
@@ -152,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 943a1748f9..c1101818b7 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -826,7 +826,7 @@ namespace LL
if (arr.size() == 2)
{
std::error_code ec;
- vec3 t;
+ 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;
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index bc333aff69..4cff0622b3 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();
U32 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 (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());
- 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 (has_normals)
+ if (tri_idx % 2 != 0)
+ {
+ std::swap(idx[1], idx[2]);
+ }
+ }
+ else if (prim->mMode == Primitive::Mode::TRIANGLE_FAN)
{
- n[i].set(prim->mNormals[idx].getF32ptr());
+ idx[0] = 0;
+ idx[1] = tri_idx + 1;
+ idx[2] = tri_idx + 2;
}
- if (rigged)
+ if (indexed)
{
- w[i].set(prim->mWeights[idx].getF32ptr());
- j[i] = prim->mJoints[idx];
+ 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)
+ {
+ 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)
@@ -434,15 +534,17 @@ 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())
{
@@ -453,10 +555,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;
@@ -614,7 +719,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;