diff options
Diffstat (limited to 'indra/newview/gltf')
-rw-r--r-- | indra/newview/gltf/asset.cpp | 69 | ||||
-rw-r--r-- | indra/newview/gltf/asset.h | 23 | ||||
-rw-r--r-- | indra/newview/gltf/buffer_util.h | 37 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.cpp | 201 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.h | 3 |
5 files changed, 275 insertions, 58 deletions
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index a4efb25860..21be69aae2 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -45,7 +45,8 @@ namespace LL namespace GLTF { static std::unordered_set<std::string> ExtensionsSupported = { - "KHR_materials_unlit" + "KHR_materials_unlit", + "KHR_texture_transform" }; Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode) @@ -906,6 +907,25 @@ void Material::TextureInfo::serialize(object& dst) const { write(mIndex, "index", dst, INVALID_INDEX); write(mTexCoord, "texCoord", dst, 0); + write_extensions(dst, &mTextureTransform, "KHR_texture_transform"); +} + +S32 Material::TextureInfo::getTexCoord() const +{ + if (mTextureTransform.mPresent && mTextureTransform.mTexCoord != INVALID_INDEX) + { + return mTextureTransform.mTexCoord; + } + return mTexCoord; +} + +bool Material::isMultiUV() const +{ + return mPbrMetallicRoughness.mBaseColorTexture.getTexCoord() != 0 || + mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord() != 0 || + mNormalTexture.getTexCoord() != 0 || + mOcclusionTexture.getTexCoord() != 0 || + mEmissiveTexture.getTexCoord() != 0; } const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) @@ -914,6 +934,7 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) { copy(src, "index", mIndex); copy(src, "texCoord", mTexCoord); + copy_extensions(src, "KHR_texture_transform", &mTextureTransform); } return *this; @@ -931,17 +952,16 @@ bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const void Material::OcclusionTextureInfo::serialize(object& dst) const { - write(mIndex, "index", dst, INVALID_INDEX); - write(mTexCoord, "texCoord", dst, 0); + TextureInfo::serialize(dst); write(mStrength, "strength", dst, 1.f); } const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src) { + TextureInfo::operator=(src); + if (src.is_object()) { - copy(src, "index", mIndex); - copy(src, "texCoord", mTexCoord); copy(src, "strength", mStrength); } @@ -950,13 +970,13 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=( void Material::NormalTextureInfo::serialize(object& dst) const { - write(mIndex, "index", dst, INVALID_INDEX); - write(mTexCoord, "texCoord", dst, 0); + TextureInfo::serialize(dst); write(mScale, "scale", dst, 1.f); } const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src) { + TextureInfo::operator=(src); if (src.is_object()) { copy(src, "index", mIndex); @@ -1015,6 +1035,41 @@ void Material::Unlit::serialize(object& dst) const // no members and object has already been created, nothing to do } +void TextureTransform::getPacked(F32* packed) const +{ + packed[0] = mScale.x; + packed[1] = mScale.y; + packed[2] = mRotation; + packed[3] = mOffset.x; + packed[4] = mOffset.y; + + packed[5] = packed[6] = packed[7] = 0.f; +} + + +const TextureTransform& TextureTransform::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) + { + copy(src, "offset", mOffset); + copy(src, "rotation", mRotation); + copy(src, "scale", mScale); + copy(src, "texCoord", mTexCoord); + } + + return *this; +} + +void TextureTransform::serialize(object& dst) const +{ + write(mOffset, "offset", dst, vec2(0.f, 0.f)); + write(mRotation, "rotation", dst, 0.f); + write(mScale, "scale", dst, vec2(1.f, 1.f)); + write(mTexCoord, "texCoord", dst, -1); +} + + void Material::serialize(object& dst) const { write(mName, "name", dst); diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index 8f28e5905f..ea3f7d480a 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -57,6 +57,21 @@ namespace LL bool mPresent = false; }; + class TextureTransform : public Extension // KHR_texture_transform implementation + { + public: + vec2 mOffset = vec2(0.f, 0.f); + F32 mRotation = 0.f; + vec2 mScale = vec2(1.f, 1.f); + S32 mTexCoord = INVALID_INDEX; + + // get the texture transform as a packed array of floats + // dst MUST point to at least 8 floats + void getPacked(F32* dst) const; + + const TextureTransform& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; class Material { @@ -82,9 +97,15 @@ namespace LL S32 mIndex = INVALID_INDEX; S32 mTexCoord = 0; + TextureTransform mTextureTransform; + bool operator==(const TextureInfo& rhs) const; bool operator!=(const TextureInfo& rhs) const; + // get the UV channel that should be used for sampling this texture + // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord + S32 getTexCoord() const; + const TextureInfo& operator=(const Value& src); void serialize(boost::json::object& dst) const; }; @@ -135,6 +156,8 @@ namespace LL bool mDoubleSided = false; Unlit mUnlit; + bool isMultiUV() const; + const Material& operator=(const Value& src); void serialize(boost::json::object& dst) const; }; diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h index c26752a6b6..c1101818b7 100644 --- a/indra/newview/gltf/buffer_util.h +++ b/indra/newview/gltf/buffer_util.h @@ -590,8 +590,8 @@ namespace LL // Write all extensions to dst.extensions // Usage: // write_extensions(dst, - // "KHR_materials_unlit", mUnlit, - // "KHR_materials_pbrSpecularGlossiness", mPbrSpecularGlossiness); + // mUnlit, "KHR_materials_unlit", + // mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness"); // returns true if any of the extensions are written template<class... Types> inline bool write_extensions(boost::json::object& dst, Types... args) @@ -816,6 +816,39 @@ namespace LL return true; } + // vec2 + template<> + inline bool copy(const Value& src, vec2& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 2) + { + std::error_code ec; + vec2 t; + t.x = arr[0].to_number<F32>(ec); if (ec) return false; + t.y = arr[1].to_number<F32>(ec); if (ec) return false; + + dst = t; + return true; + } + } + return false; + } + + template<> + inline bool write(const vec2& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.as_array(); + arr.resize(2); + arr[0] = src.x; + arr[1] = src.y; + + return true; + } + // bool template<> inline bool copy(const Value& src, bool& dst) diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index 197ffb68e8..dd37b5b4d0 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -42,13 +42,14 @@ using namespace boost::json; // Mesh data useful for Mikktspace tangent generation (and flat normal generation) struct MikktMesh { - std::vector<LLVector3> p; - std::vector<LLVector3> n; - std::vector<LLVector2> tc; - std::vector<LLVector4> w; - std::vector<LLVector4> t; - std::vector<LLColor4U> c; - std::vector<U64> j; + std::vector<LLVector3> p; //positions + std::vector<LLVector3> n; //normals + std::vector<LLVector4> t; //tangents + std::vector<LLVector2> tc0; //texcoords 0 + std::vector<LLVector2> tc1; //texcoords 1 + std::vector<LLColor4U> c; //colors + std::vector<LLVector4> w; //weights + std::vector<U64> j; //joints // initialize from src primitive and make an unrolled triangle list // returns false if the Primitive cannot be converted to a triangle list @@ -57,15 +58,28 @@ struct MikktMesh bool indexed = !prim->mIndexArray.empty(); auto vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size(); - if (prim->mMode != Primitive::Mode::TRIANGLES) + U32 triangle_count = 0; + + if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP || + prim->mMode == Primitive::Mode::TRIANGLE_FAN) + { + triangle_count = vert_count - 2; + } + else if (prim->mMode == Primitive::Mode::TRIANGLES) { - LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32) prim->mMode << LL_ENDL; + triangle_count = vert_count / 3; + } + else + { + LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32)prim->mMode << LL_ENDL; return false; } + vert_count = triangle_count * 3; + p.resize(vert_count); n.resize(vert_count); - tc.resize(vert_count); + tc0.resize(vert_count); c.resize(vert_count); bool has_normals = !prim->mNormals.empty(); @@ -78,6 +92,7 @@ struct MikktMesh { t.resize(vert_count); } + bool rigged = !prim->mWeights.empty(); if (rigged) { @@ -85,23 +100,69 @@ struct MikktMesh j.resize(vert_count); } - for (U32 i = 0; i < vert_count; ++i) + bool multi_uv = !prim->mTexCoords1.empty(); + if (multi_uv) { - U32 idx = indexed ? prim->mIndexArray[i] : i; + tc1.resize(vert_count); + } - p[i].set(prim->mPositions[idx].getF32ptr()); - tc[i].set(prim->mTexCoords[idx]); - c[i] = prim->mColors[idx]; + for (int tri_idx = 0; tri_idx < triangle_count; ++tri_idx) + { + U32 idx[3]; + + if (prim->mMode == Primitive::Mode::TRIANGLES) + { + idx[0] = tri_idx * 3; + idx[1] = tri_idx * 3 + 1; + idx[2] = tri_idx * 3 + 2; + } + else if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP) + { + idx[0] = tri_idx; + idx[1] = tri_idx + 1; + idx[2] = tri_idx + 2; + + if (tri_idx % 2 != 0) + { + std::swap(idx[1], idx[2]); + } + } + else if (prim->mMode == Primitive::Mode::TRIANGLE_FAN) + { + idx[0] = 0; + idx[1] = tri_idx + 1; + idx[2] = tri_idx + 2; + } - if (has_normals) + if (indexed) { - n[i].set(prim->mNormals[idx].getF32ptr()); + idx[0] = prim->mIndexArray[idx[0]]; + idx[1] = prim->mIndexArray[idx[1]]; + idx[2] = prim->mIndexArray[idx[2]]; } - if (rigged) + for (U32 v = 0; v < 3; ++v) { - w[i].set(prim->mWeights[idx].getF32ptr()); - j[i] = prim->mJoints[idx]; + U32 i = tri_idx * 3 + v; + p[i].set(prim->mPositions[idx[v]].getF32ptr()); + tc0[i].set(prim->mTexCoords0[idx[v]]); + c[i] = prim->mColors[idx[v]]; + + if (multi_uv) + { + tc1[i].set(prim->mTexCoords1[idx[v]]); + } + + if (has_normals) + { + n[i].set(prim->mNormals[idx[v]].getF32ptr()); + } + + if (rigged) + { + w[i].set(prim->mWeights[idx[v]].getF32ptr()); + j[i] = prim->mJoints[idx[v]]; + } } } @@ -138,25 +199,34 @@ struct MikktMesh void write(Primitive* prim) const { //re-weld - meshopt_Stream mos[] = + std::vector<meshopt_Stream> mos = { { &p[0], sizeof(LLVector3), sizeof(LLVector3) }, { &n[0], sizeof(LLVector3), sizeof(LLVector3) }, { &t[0], sizeof(LLVector4), sizeof(LLVector4) }, - { &tc[0], sizeof(LLVector2), sizeof(LLVector2) }, - { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) }, - { w.empty() ? nullptr : &w[0], sizeof(LLVector4), sizeof(LLVector4) }, - { j.empty() ? nullptr : &j[0], sizeof(U64), sizeof(U64) } + { &tc0[0], sizeof(LLVector2), sizeof(LLVector2) }, + { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) } }; + if (!w.empty()) + { + mos.push_back({ &w[0], sizeof(LLVector4), sizeof(LLVector4) }); + mos.push_back({ &j[0], sizeof(U64), sizeof(U64) }); + } + + if (!tc1.empty()) + { + mos.push_back({ &tc1[0], sizeof(LLVector2), sizeof(LLVector2) }); + } + std::vector<U32> remap; remap.resize(p.size()); - U32 stream_count = w.empty() ? 5 : 7; + U32 stream_count = mos.size(); - size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos, stream_count); + size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos.data(), stream_count); - prim->mTexCoords.resize(vert_count); + prim->mTexCoords0.resize(vert_count); prim->mNormals.resize(vert_count); prim->mTangents.resize(vert_count); prim->mPositions.resize(vert_count); @@ -166,6 +236,10 @@ struct MikktMesh prim->mWeights.resize(vert_count); prim->mJoints.resize(vert_count); } + if (!tc1.empty()) + { + prim->mTexCoords1.resize(vert_count); + } prim->mIndexArray.resize(remap.size()); @@ -178,7 +252,7 @@ struct MikktMesh prim->mPositions[dst_idx].load3(p[src_idx].mV); prim->mNormals[dst_idx].load3(n[src_idx].mV); - prim->mTexCoords[dst_idx] = tc[src_idx]; + prim->mTexCoords0[dst_idx] = tc0[src_idx]; prim->mTangents[dst_idx].loadua(t[src_idx].mV); prim->mColors[dst_idx] = c[src_idx]; @@ -187,6 +261,11 @@ struct MikktMesh prim->mWeights[dst_idx].loadua(w[src_idx].mV); prim->mJoints[dst_idx] = j[src_idx]; } + + if (!tc1.empty()) + { + prim->mTexCoords1[dst_idx] = tc1[src_idx]; + } } prim->mGLMode = LLRender::TRIANGLES; @@ -210,8 +289,8 @@ struct MikktMesh mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num) { - F32* uv = tc[face_num * 3 + vert_num].mV; - return mikk::float3(uv[0], uv[1], 1.0f); + F32* uv = tc0[face_num * 3 + vert_num].mV; + return mikk::float3(uv[0], 1.f-uv[1], 1.0f); } mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num) @@ -228,6 +307,14 @@ struct MikktMesh }; +static void vertical_flip(std::vector<LLVector2>& texcoords) +{ + for (auto& tc : texcoords) + { + tc[1] = 1.f - tc[1]; + } +} + bool Primitive::prep(Asset& asset) { // allocate vertex buffer @@ -261,7 +348,11 @@ bool Primitive::prep(Asset& asset) } else if (attribName == "TEXCOORD_0") { - copy(asset, accessor, mTexCoords); + copy(asset, accessor, mTexCoords0); + } + else if (attribName == "TEXCOORD_1") + { + copy(asset, accessor, mTexCoords1); } else if (attribName == "JOINTS_0") { @@ -297,24 +388,28 @@ bool Primitive::prep(Asset& asset) mask |= LLVertexBuffer::MAP_JOINT; } - if (mTexCoords.empty()) + if (mTexCoords0.empty()) { - mTexCoords.resize(mPositions.size()); + mTexCoords0.resize(mPositions.size()); } - // TODO: support more than one texcoord set (or no texcoords) mask |= LLVertexBuffer::MAP_TEXCOORD0; + if (!mTexCoords1.empty()) + { + mask |= LLVertexBuffer::MAP_TEXCOORD1; + } + if (mColors.empty()) { mColors.resize(mPositions.size(), LLColor4U::white); } + mShaderVariant = 0; + // TODO: support colorless vertex buffers mask |= LLVertexBuffer::MAP_COLOR; - mShaderVariant = 0; - bool unlit = false; // bake material basecolor into color array @@ -332,6 +427,11 @@ bool Primitive::prep(Asset& asset) mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT; unlit = true; } + + if (material.isMultiUV()) + { + mShaderVariant |= LLGLSLShader::GLTFVariant::MULTI_UV; + } } if (mNormals.empty() && !unlit) @@ -437,16 +537,18 @@ bool Primitive::prep(Asset& asset) } // flip texcoord y, upload, then flip back (keep the off-spec data in vram only) - for (auto& tc : mTexCoords) - { - tc[1] = 1.f - tc[1]; - } - mVertexBuffer->setTexCoordData(mTexCoords.data()); - for (auto& tc : mTexCoords) + vertical_flip(mTexCoords0); + mVertexBuffer->setTexCoord0Data(mTexCoords0.data()); + vertical_flip(mTexCoords0); + + if (!mTexCoords1.empty()) { - tc[1] = 1.f - tc[1]; + vertical_flip(mTexCoords1); + mVertexBuffer->setTexCoord1Data(mTexCoords1.data()); + vertical_flip(mTexCoords1); } + if (!mIndexArray.empty()) { mVertexBuffer->setIndexData(mIndexArray.data()); @@ -456,10 +558,13 @@ bool Primitive::prep(Asset& asset) mVertexBuffer->unbind(); - Material& material = asset.mMaterials[mMaterial]; - if (material.mAlphaMode == Material::AlphaMode::BLEND) + if (mMaterial != INVALID_INDEX) { - mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + Material& material = asset.mMaterials[mMaterial]; + if (material.mAlphaMode == Material::AlphaMode::BLEND) + { + mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + } } return true; @@ -617,7 +722,7 @@ const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, //create a proxy LLVolumeFace for the raycast LLVolumeFace face; face.mPositions = mPositions.data(); - face.mTexCoords = mTexCoords.data(); + face.mTexCoords = mTexCoords0.data(); face.mNormals = mNormals.data(); face.mTangents = mTangents.data(); face.mIndices = nullptr; // unreferenced diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index f9d7c63c65..7cc05cf831 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -58,7 +58,8 @@ namespace LL LLPointer<LLVertexBuffer> mVertexBuffer; // CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code - std::vector<LLVector2> mTexCoords; + std::vector<LLVector2> mTexCoords0; + std::vector<LLVector2> mTexCoords1; std::vector<LLVector4a> mNormals; std::vector<LLVector4a> mTangents; std::vector<LLVector4a> mPositions; |