From 8ad7240a3bb626ebaabcc81fb8155a8cbb5adf39 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Thu, 8 Sep 2022 10:06:53 -0500 Subject: SL-18095 WIP -- Add Mikktspace tangent generation for PBR materials and switch to per-pixel binormal generation. Still bugged with some test content. --- indra/llmath/llvolume.cpp | 272 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 240 insertions(+), 32 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 4a069b0f63..539db9d0e1 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -32,6 +32,7 @@ #include #endif #include +#include #include "llerror.h" @@ -52,6 +53,9 @@ #include "llmeshoptimizer.h" #include "lltimer.h" +#include "mikktspace/mikktspace.h" +#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file + #define DEBUG_SILHOUETTE_BINORMALS 0 #define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette #define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette @@ -2096,9 +2100,9 @@ void LLVolume::regen() createVolumeFaces(); } -void LLVolume::genTangents(S32 face) +void LLVolume::genTangents(S32 face, bool mikktspace) { - mVolumeFaces[face].createTangents(); + mVolumeFaces[face].createTangents(mikktspace); } LLVolume::~LLVolume() @@ -4797,6 +4801,17 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mTangents = NULL; } + if (src.mMikktSpaceTangents) + { + allocateTangents(src.mNumVertices, true); + LLVector4a::memcpyNonAliased16((F32*)mMikktSpaceTangents, (F32*)src.mMikktSpaceTangents, vert_size); + } + else + { + ll_aligned_free_16(mMikktSpaceTangents); + mMikktSpaceTangents = nullptr; + } + if (src.mWeights) { llassert(!mWeights); // don't orphan an old alloc here accidentally @@ -4867,6 +4882,8 @@ void LLVolumeFace::freeData() mIndices = NULL; ll_aligned_free_16(mTangents); mTangents = NULL; + ll_aligned_free_16(mMikktSpaceTangents); + mMikktSpaceTangents = nullptr; ll_aligned_free_16(mWeights); mWeights = NULL; @@ -4988,6 +5005,9 @@ void LLVolumeFace::remap() ll_aligned_free_16(mTangents); mTangents = NULL; + ll_aligned_free_16(mMikktSpaceTangents); + mMikktSpaceTangents = nullptr; + // Assign new values mIndices = remap_indices; mPositions = remap_positions; @@ -5514,6 +5534,12 @@ bool LLVolumeFace::cacheOptimize() } } + llassert(mTangents == nullptr); // cache optimize called too late, tangents already generated + llassert(mMikktSpaceTangents == nullptr); + + // ===================================================================================== + // DEPRECATED -- cacheOptimize should always be called before tangents are generated + // ===================================================================================== LLVector4a* binorm = NULL; if (mTangents) { @@ -5526,11 +5552,11 @@ bool LLVolumeFace::cacheOptimize() return false; } } + // ===================================================================================== - //allocate mapping of old indices to new indices + //allocate mapping of old indices to new indices std::vector new_idx; - - try + try { new_idx.resize(mNumVertices, -1); } @@ -5673,6 +5699,7 @@ void LLVolumeFace::swapData(LLVolumeFace& rhs) llswap(rhs.mPositions, mPositions); llswap(rhs.mNormals, mNormals); llswap(rhs.mTangents, mTangents); + llswap(rhs.mMikktSpaceTangents, mMikktSpaceTangents); llswap(rhs.mTexCoords, mTexCoords); llswap(rhs.mIndices,mIndices); llswap(rhs.mNumVertices, mNumVertices); @@ -6380,37 +6407,217 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); -void LLVolumeFace::createTangents() + +// data structures for tangent generation + +// key for summing tangents +// We will blend tangents wherever a common position and normal is found +struct MikktKey { - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME + // Position + LLVector3 p; + // Normal + LLVector3 n; - if (!mTangents) - { - allocateTangents(mNumVertices); + bool operator==(const MikktKey& rhs) const { return p == rhs.p && n == rhs.n; } +}; - //generate tangents - //LLVector4a* pos = mPositions; - //LLVector2* tc = (LLVector2*) mTexCoords; - LLVector4a* binorm = (LLVector4a*) mTangents; +// sum of tangents and list of signs and index array indices for a given position and normal combination +// sign must be kept separate from summed tangent because a single position and normal may have a different +// tangent facing where UV seams exist +struct MikktTangent +{ + // tangent vector + LLVector3 t; + // signs + std::vector s; + // indices (in index array) + std::vector i; +}; - LLVector4a* end = mTangents+mNumVertices; - while (binorm < end) - { - (*binorm++).clear(); - } +// hash function for MikktTangent +namespace boost +{ + template <> + struct hash + { + std::size_t operator()(LLVector3 const& k) const + { + size_t seed = 0; + boost::hash_combine(seed, k.mV[0]); + boost::hash_combine(seed, k.mV[1]); + boost::hash_combine(seed, k.mV[2]); + return seed; + } + }; + + template <> + struct hash + { + std::size_t operator()(MikktKey const& k) const + { + size_t seed = 0; + boost::hash_combine(seed, k.p); + boost::hash_combine(seed, k.n); + return seed; + } + }; +} + +// boost adapter +namespace std +{ + template<> + struct hash + { + std::size_t operator()(MikktKey const& k) const + { + return boost::hash()(k); + } + }; +} + +struct MikktData +{ + LLVolumeFace* face; + std::unordered_map tangents; +}; - binorm = mTangents; - CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices/3, mIndices, mTangents); +void LLVolumeFace::createTangents(bool mikktspace) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - //normalize tangents - for (U32 i = 0; i < mNumVertices; i++) - { - //binorm[i].normalize3fast(); - //bump map/planar projection code requires normals to be normalized - mNormals[i].normalize3fast(); - } - } + auto& tangents = mikktspace ? mMikktSpaceTangents : mTangents; + + if (!tangents) + { + allocateTangents(mNumVertices, mikktspace); + + if (mikktspace) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_VOLUME("mikktspace"); + SMikkTSpaceInterface ms; + + ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + return face->mNumIndices / 3; + }; + + ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) + { + return 3; + }; + + ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& vert = face->mPositions[idx]; + F32* v = vert.getF32ptr(); + fvPosOut[0] = v[0]; + fvPosOut[1] = v[1]; + fvPosOut[2] = v[2]; + }; + + ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& norm = face->mNormals[idx]; + F32* n = norm.getF32ptr(); + fvNormOut[0] = n[0]; + fvNormOut[1] = n[1]; + fvNormOut[2] = n[2]; + }; + + ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& tc = face->mTexCoords[idx]; + fvTexcOut[0] = tc.mV[0]; + fvTexcOut[1] = tc.mV[1]; + }; + + ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 i = iFace * 3 + iVert; + S32 idx = face->mIndices[i]; + + LLVector3 p(face->mPositions[idx].getF32ptr()); + LLVector3 n(face->mNormals[idx].getF32ptr()); + LLVector3 t(fvTangent); + + MikktKey key = { p, n }; + + MikktTangent& mt = data->tangents[key]; + mt.t += t; + mt.s.push_back(fSign); + mt.i.push_back(i); + }; + + ms.m_setTSpace = nullptr; + + MikktData data; + data.face = this; + + SMikkTSpaceContext ctx = { &ms, &data }; + + genTangSpaceDefault(&ctx); + + for (U32 i = 0; i < mNumVertices; ++i) + { + MikktKey key = { LLVector3(mPositions[i].getF32ptr()), LLVector3(mNormals[i].getF32ptr()) }; + MikktTangent& t = data.tangents[key]; + + //set tangent + mMikktSpaceTangents[i].load3(t.t.mV); + mMikktSpaceTangents[i].normalize3fast(); + + //set sign + F32 sign = 0.f; + for (int j = 0; j < t.i.size(); ++j) + { + if (mIndices[t.i[j]] == i) + { + sign = t.s[j]; + break; + } + } + + llassert(sign != 0.f); + mMikktSpaceTangents[i].getF32ptr()[3] = sign; + } + } + else + { + //generate tangents + LLVector4a* ptr = (LLVector4a*)tangents; + + LLVector4a* end = mTangents + mNumVertices; + while (ptr < end) + { + (*ptr++).clear(); + } + + CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, tangents); + } + + //normalize normals + for (U32 i = 0; i < mNumVertices; i++) + { + //bump map/planar projection code requires normals to be normalized + mNormals[i].normalize3fast(); + } + } } void LLVolumeFace::resizeVertices(S32 num_verts) @@ -6511,10 +6718,11 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con mNumVertices++; } -void LLVolumeFace::allocateTangents(S32 num_verts) +void LLVolumeFace::allocateTangents(S32 num_verts, bool mikktspace) { - ll_aligned_free_16(mTangents); - mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + auto& buff = mikktspace ? mMikktSpaceTangents : mTangents; + ll_aligned_free_16(buff); + buff = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); } void LLVolumeFace::allocateWeights(S32 num_verts) -- cgit v1.2.3 From c822da9fe644e4a420caabb30a25b487ce75c099 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 9 Sep 2022 20:56:22 -0500 Subject: SL-18095 WIP -- Allow mikktspace generator to add more vertices (skip re-welding step for now). --- indra/llmath/llvolume.cpp | 551 ++++++++++++---------------------------------- 1 file changed, 138 insertions(+), 413 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 539db9d0e1..0ce1577d00 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2102,7 +2102,12 @@ void LLVolume::regen() void LLVolume::genTangents(S32 face, bool mikktspace) { - mVolumeFaces[face].createTangents(mikktspace); + // generate legacy tangents for the specified face + // if mikktspace is true, only generate tangents if mikktspace tangents are not present (handles the case for non-mesh prims) + if (!mikktspace || mVolumeFaces[face].mMikktSpaceTangents == nullptr) + { + mVolumeFaces[face].createTangents(); + } } LLVolume::~LLVolume() @@ -5373,252 +5378,155 @@ public: } }; +// data structures for tangent generation -bool LLVolumeFace::cacheOptimize() -{ //optimize for vertex cache according to Forsyth method: - // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html - - llassert(!mOptimized); - mOptimized = TRUE; - - LLVCacheLRU cache; - - if (mNumVertices < 3 || mNumIndices < 3) - { //nothing to do - return true; - } +struct MikktData +{ + LLVolumeFace* face; + std::vector p; + std::vector n; + std::vector tc; + std::vector w; + std::vector t; + + MikktData(LLVolumeFace* f) + : face(f) + { + U32 count = face->mNumIndices; - //mapping of vertices to triangles and indices - std::vector vertex_data; + p.resize(count); + n.resize(count); + tc.resize(count); + t.resize(count); - //mapping of triangles do vertices - std::vector triangle_data; + if (face->mWeights) + { + w.resize(count); + } - try - { - triangle_data.resize(mNumIndices / 3); - vertex_data.resize(mNumVertices); + for (int i = 0; i < face->mNumIndices; ++i) + { + U32 idx = face->mIndices[i]; - for (U32 i = 0; i < mNumIndices; i++) - { //populate vertex data and triangle data arrays - U16 idx = mIndices[i]; - U32 tri_idx = i / 3; + p[i].set(face->mPositions[idx].getF32ptr()); + n[i].set(face->mNormals[idx].getF32ptr()); + tc[i].set(face->mTexCoords[idx]); - vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx])); - vertex_data[idx].mIdx = idx; - triangle_data[tri_idx].mVertex[i % 3] = &(vertex_data[idx]); + if (face->mWeights) + { + w[i].set(face->mWeights[idx].getF32ptr()); + } } } - catch (std::bad_alloc&) - { - // resize or push_back failed - LL_WARNS("LLVOLUME") << "Resize for " << mNumVertices << " vertices failed" << LL_ENDL; - return false; - } - - /*F32 pre_acmr = 1.f; - //measure cache misses from before rebuild - { - LLVCacheFIFO test_cache; - for (U32 i = 0; i < mNumIndices; ++i) - { - test_cache.addVertex(&vertex_data[mIndices[i]]); - } - - for (U32 i = 0; i < mNumVertices; i++) - { - vertex_data[i].mCacheTag = -1; - } - - pre_acmr = (F32) test_cache.mMisses/(mNumIndices/3); - }*/ - - for (U32 i = 0; i < mNumVertices; i++) - { //initialize score values (no cache -- might try a fifo cache here) - LLVCacheVertexData& data = vertex_data[i]; - - data.mScore = find_vertex_score(data); - data.mActiveTriangles = data.mTriangles.size(); +}; - for (U32 j = 0; j < data.mActiveTriangles; ++j) - { - data.mTriangles[j]->mScore += data.mScore; - } - } - //sort triangle data by score - std::sort(triangle_data.begin(), triangle_data.end()); +bool LLVolumeFace::cacheOptimize() +{ //optimize for vertex cache according to Forsyth method: + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + llassert(!mOptimized); + mOptimized = TRUE; - std::vector new_indices; + allocateTangents(mNumVertices, true); - LLVCacheTriangleData* tri; + SMikkTSpaceInterface ms; - //prime pump by adding first triangle to cache; - tri = &(triangle_data[0]); - cache.addTriangle(tri); - new_indices.push_back(tri->mVertex[0]->mIdx); - new_indices.push_back(tri->mVertex[1]->mIdx); - new_indices.push_back(tri->mVertex[2]->mIdx); - tri->complete(); + ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + return face->mNumIndices / 3; + }; - U32 breaks = 0; - for (U32 i = 1; i < mNumIndices/3; ++i) - { - cache.updateScores(); - tri = cache.mBestTriangle; - if (!tri) - { - breaks++; - for (U32 j = 0; j < triangle_data.size(); ++j) - { - if (triangle_data[j].mActive) - { - tri = &(triangle_data[j]); - break; - } - } - } - - cache.addTriangle(tri); - new_indices.push_back(tri->mVertex[0]->mIdx); - new_indices.push_back(tri->mVertex[1]->mIdx); - new_indices.push_back(tri->mVertex[2]->mIdx); - tri->complete(); - } + ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) + { + return 3; + }; - for (U32 i = 0; i < mNumIndices; ++i) - { - mIndices[i] = new_indices[i]; - } + ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& vert = face->mPositions[idx]; + F32* v = vert.getF32ptr(); + fvPosOut[0] = v[0]; + fvPosOut[1] = v[1]; + fvPosOut[2] = v[2]; + }; - /*F32 post_acmr = 1.f; - //measure cache misses from after rebuild - { - LLVCacheFIFO test_cache; - for (U32 i = 0; i < mNumVertices; i++) - { - vertex_data[i].mCacheTag = -1; - } + ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& norm = face->mNormals[idx]; + F32* n = norm.getF32ptr(); + fvNormOut[0] = n[0]; + fvNormOut[1] = n[1]; + fvNormOut[2] = n[2]; + }; - for (U32 i = 0; i < mNumIndices; ++i) - { - test_cache.addVertex(&vertex_data[mIndices[i]]); - } - - post_acmr = (F32) test_cache.mMisses/(mNumIndices/3); - }*/ + ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& tc = face->mTexCoords[idx]; + fvTexcOut[0] = tc.mV[0]; + fvTexcOut[1] = tc.mV[1]; + }; - //optimize for pre-TnL cache - - //allocate space for new buffer - S32 num_verts = mNumVertices; - S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF; - LLVector4a* pos = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+size); - if (pos == NULL) - { - LL_WARNS("LLVOLUME") << "Allocation of positions vector[" << sizeof(LLVector4a) * 2 * num_verts + size << "] failed. " << LL_ENDL; - return false; - } - LLVector4a* norm = pos + num_verts; - LLVector2* tc = (LLVector2*) (norm + num_verts); + ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 i = iFace * 3 + iVert; + S32 idx = face->mIndices[i]; - LLVector4a* wght = NULL; - if (mWeights) - { - wght = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); - if (wght == NULL) - { - ll_aligned_free<64>(pos); - LL_WARNS("LLVOLUME") << "Allocation of weights[" << sizeof(LLVector4a) * num_verts << "] failed" << LL_ENDL; - return false; - } - } + LLVector3 p(face->mPositions[idx].getF32ptr()); + LLVector3 n(face->mNormals[idx].getF32ptr()); + LLVector3 t(fvTangent); - llassert(mTangents == nullptr); // cache optimize called too late, tangents already generated - llassert(mMikktSpaceTangents == nullptr); + data->t[i].set(fvTangent); + data->t[i].mV[3] = fSign; + }; - // ===================================================================================== - // DEPRECATED -- cacheOptimize should always be called before tangents are generated - // ===================================================================================== - LLVector4a* binorm = NULL; - if (mTangents) - { - binorm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); - if (binorm == NULL) - { - ll_aligned_free<64>(pos); - ll_aligned_free_16(wght); - LL_WARNS("LLVOLUME") << "Allocation of binormals[" << sizeof(LLVector4a)*num_verts << "] failed" << LL_ENDL; - return false; - } - } - // ===================================================================================== + ms.m_setTSpace = nullptr; - //allocate mapping of old indices to new indices - std::vector new_idx; - try - { - new_idx.resize(mNumVertices, -1); - } - catch (std::bad_alloc&) - { - ll_aligned_free<64>(pos); - ll_aligned_free_16(wght); - ll_aligned_free_16(binorm); - LL_WARNS("LLVOLUME") << "Resize failed: " << mNumVertices << LL_ENDL; - return false; - } + MikktData data(this); - S32 cur_idx = 0; - for (U32 i = 0; i < mNumIndices; ++i) - { - U16 idx = mIndices[i]; - if (new_idx[idx] == -1) - { //this vertex hasn't been added yet - new_idx[idx] = cur_idx; + SMikkTSpaceContext ctx = { &ms, &data }; - //copy vertex data - pos[cur_idx] = mPositions[idx]; - norm[cur_idx] = mNormals[idx]; - tc[cur_idx] = mTexCoords[idx]; - if (mWeights) - { - wght[cur_idx] = mWeights[idx]; - } - if (mTangents) - { - binorm[cur_idx] = mTangents[idx]; - } + genTangSpaceDefault(&ctx); + + resizeVertices(data.p.size()); + resizeIndices(data.p.size()); + + if (!data.w.empty()) + { + allocateWeights(data.w.size()); + } - cur_idx++; - } - } + allocateTangents(mNumVertices, true); - for (U32 i = 0; i < mNumIndices; ++i) - { - mIndices[i] = new_idx[mIndices[i]]; - } - - ll_aligned_free<64>(mPositions); - // DO NOT free mNormals and mTexCoords as they are part of mPositions buffer - ll_aligned_free_16(mWeights); - ll_aligned_free_16(mTangents); -#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS - ll_aligned_free_16(mJointIndices); - ll_aligned_free_16(mJustWeights); - mJustWeights = NULL; - mJointIndices = NULL; // filled in later as necessary by skinning code for acceleration -#endif + for (int i = 0; i < mNumIndices; ++i) + { + mIndices[i] = i; - mPositions = pos; - mNormals = norm; - mTexCoords = tc; - mWeights = wght; - mTangents = binorm; + mPositions[i].load3(data.p[i].mV); + mNormals[i].load3(data.n[i].mV); + mTexCoords[i] = data.tc[i]; + + mMikktSpaceTangents[i].loadua(data.t[i].mV); - //std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks); - //LL_INFOS() << result << LL_ENDL; + if (mWeights) + { + mWeights[i].loadua(data.w[i].mV); + } + } + return true; } @@ -6407,209 +6315,25 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent); - -// data structures for tangent generation - -// key for summing tangents -// We will blend tangents wherever a common position and normal is found -struct MikktKey -{ - // Position - LLVector3 p; - // Normal - LLVector3 n; - - bool operator==(const MikktKey& rhs) const { return p == rhs.p && n == rhs.n; } -}; - -// sum of tangents and list of signs and index array indices for a given position and normal combination -// sign must be kept separate from summed tangent because a single position and normal may have a different -// tangent facing where UV seams exist -struct MikktTangent -{ - // tangent vector - LLVector3 t; - // signs - std::vector s; - // indices (in index array) - std::vector i; -}; - -// hash function for MikktTangent -namespace boost -{ - template <> - struct hash - { - std::size_t operator()(LLVector3 const& k) const - { - size_t seed = 0; - boost::hash_combine(seed, k.mV[0]); - boost::hash_combine(seed, k.mV[1]); - boost::hash_combine(seed, k.mV[2]); - return seed; - } - }; - - template <> - struct hash - { - std::size_t operator()(MikktKey const& k) const - { - size_t seed = 0; - boost::hash_combine(seed, k.p); - boost::hash_combine(seed, k.n); - return seed; - } - }; -} - -// boost adapter -namespace std -{ - template<> - struct hash - { - std::size_t operator()(MikktKey const& k) const - { - return boost::hash()(k); - } - }; -} - -struct MikktData -{ - LLVolumeFace* face; - std::unordered_map tangents; -}; - - -void LLVolumeFace::createTangents(bool mikktspace) +void LLVolumeFace::createTangents() { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - auto& tangents = mikktspace ? mMikktSpaceTangents : mTangents; - - if (!tangents) + + if (!mTangents) { - allocateTangents(mNumVertices, mikktspace); + allocateTangents(mNumVertices); + + //generate tangents + LLVector4a* ptr = (LLVector4a*)mTangents; - if (mikktspace) + LLVector4a* end = mTangents + mNumVertices; + while (ptr < end) { - LL_PROFILE_ZONE_NAMED_CATEGORY_VOLUME("mikktspace"); - SMikkTSpaceInterface ms; - - ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - return face->mNumIndices / 3; - }; - - ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) - { - return 3; - }; - - ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& vert = face->mPositions[idx]; - F32* v = vert.getF32ptr(); - fvPosOut[0] = v[0]; - fvPosOut[1] = v[1]; - fvPosOut[2] = v[2]; - }; - - ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& norm = face->mNormals[idx]; - F32* n = norm.getF32ptr(); - fvNormOut[0] = n[0]; - fvNormOut[1] = n[1]; - fvNormOut[2] = n[2]; - }; - - ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& tc = face->mTexCoords[idx]; - fvTexcOut[0] = tc.mV[0]; - fvTexcOut[1] = tc.mV[1]; - }; - - ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 i = iFace * 3 + iVert; - S32 idx = face->mIndices[i]; - - LLVector3 p(face->mPositions[idx].getF32ptr()); - LLVector3 n(face->mNormals[idx].getF32ptr()); - LLVector3 t(fvTangent); - - MikktKey key = { p, n }; - - MikktTangent& mt = data->tangents[key]; - mt.t += t; - mt.s.push_back(fSign); - mt.i.push_back(i); - }; - - ms.m_setTSpace = nullptr; - - MikktData data; - data.face = this; - - SMikkTSpaceContext ctx = { &ms, &data }; - - genTangSpaceDefault(&ctx); - - for (U32 i = 0; i < mNumVertices; ++i) - { - MikktKey key = { LLVector3(mPositions[i].getF32ptr()), LLVector3(mNormals[i].getF32ptr()) }; - MikktTangent& t = data.tangents[key]; - - //set tangent - mMikktSpaceTangents[i].load3(t.t.mV); - mMikktSpaceTangents[i].normalize3fast(); - - //set sign - F32 sign = 0.f; - for (int j = 0; j < t.i.size(); ++j) - { - if (mIndices[t.i[j]] == i) - { - sign = t.s[j]; - break; - } - } - - llassert(sign != 0.f); - mMikktSpaceTangents[i].getF32ptr()[3] = sign; - } + (*ptr++).clear(); } - else - { - //generate tangents - LLVector4a* ptr = (LLVector4a*)tangents; - - LLVector4a* end = mTangents + mNumVertices; - while (ptr < end) - { - (*ptr++).clear(); - } - CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, tangents); - } + CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents); //normalize normals for (U32 i = 0; i < mNumVertices; i++) @@ -6618,6 +6342,7 @@ void LLVolumeFace::createTangents(bool mikktspace) mNormals[i].normalize3fast(); } } + } void LLVolumeFace::resizeVertices(S32 num_verts) -- cgit v1.2.3 From d7c77e5c64ddfef44b3e47f4c7ae6943ff0e860a Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 12 Sep 2022 11:48:16 -0500 Subject: SL-18095 WIP -- Use meshoptimizer to re-weld and cache optimize after generating tangents --- indra/llmath/llvolume.cpp | 56 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 11 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 0ce1577d00..fca9471f14 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -56,6 +56,8 @@ #include "mikktspace/mikktspace.h" #include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file +#include "meshoptimizer/meshoptimizer.h" + #define DEBUG_SILHOUETTE_BINORMALS 0 #define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette #define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette @@ -5499,34 +5501,66 @@ bool LLVolumeFace::cacheOptimize() SMikkTSpaceContext ctx = { &ms, &data }; genTangSpaceDefault(&ctx); - - resizeVertices(data.p.size()); - resizeIndices(data.p.size()); - + + //re-weld + meshopt_Stream mos[] = + { + { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) }, + { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) }, + { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) } + }; + + std::vector remap; + remap.resize(data.p.size()); + + U32 stream_count = data.w.empty() ? 4 : 5; + + U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count); + + std::vector indices; + indices.resize(mNumIndices); + + //copy results back into volume + resizeVertices(vert_count); + if (!data.w.empty()) { - allocateWeights(data.w.size()); + allocateWeights(vert_count); } allocateTangents(mNumVertices, true); for (int i = 0; i < mNumIndices; ++i) { - mIndices[i] = i; + U32 src_idx = i; + U32 dst_idx = remap[i]; + mIndices[i] = dst_idx; - mPositions[i].load3(data.p[i].mV); - mNormals[i].load3(data.n[i].mV); - mTexCoords[i] = data.tc[i]; + mPositions[dst_idx].load3(data.p[src_idx].mV); + mNormals[dst_idx].load3(data.n[src_idx].mV); + mTexCoords[dst_idx] = data.tc[src_idx]; - mMikktSpaceTangents[i].loadua(data.t[i].mV); + mMikktSpaceTangents[dst_idx].loadua(data.t[src_idx].mV); if (mWeights) { - mWeights[i].loadua(data.w[i].mV); + mWeights[dst_idx].loadua(data.w[src_idx].mV); } } + // cache optimize index buffer + + // meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy + U16* src_indices = mIndices; + mIndices = nullptr; + resizeIndices(mNumIndices); + + meshopt_optimizeVertexCache(mIndices, src_indices, mNumIndices, mNumVertices); + ll_aligned_free_16(src_indices); + return true; } -- cgit v1.2.3 From e49d602bd99f5a3b1257ba1bc7ded133eab1eb1c Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 12 Sep 2022 19:48:33 -0500 Subject: SL-18095 Add tangents to mesh assets so we can calculate mikktspace tangents in the mesh's original coordinate frame. --- indra/llmath/llvolume.cpp | 233 +++++++++++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 98 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index fca9471f14..563a325f03 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2431,11 +2431,10 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) LLSD::Binary pos = mdl[i]["Position"]; LLSD::Binary norm = mdl[i]["Normal"]; + LLSD::Binary tangent = mdl[i]["Tangent"]; LLSD::Binary tc = mdl[i]["TexCoord0"]; LLSD::Binary idx = mdl[i]["TriangleList"]; - - //copy out indices S32 num_indices = idx.size() / 2; face.resizeIndices(num_indices); @@ -2534,6 +2533,33 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } } + { + if (!tangent.empty()) + { + face.allocateTangents(face.mNumVertices, true); + U16* t = (U16*)&(tangent[0]); + + // store incoming tangents in mMikktSpaceTangents + // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the CLTF shaders to + // maintain compliance with the GLTF spec + LLVector4a* t_out = face.mMikktSpaceTangents; + + for (U32 j = 0; j < num_verts; ++j) + { + t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]); + t_out->div(65535.f); + t_out->mul(2.f); + t_out->sub(1.f); + + F32* tp = t_out->getF32ptr(); + tp[3] = tp[3] < 0.f ? -1.f : 1.f; + + t_out++; + t += 4; + } + } + } + { if (!tc.empty()) { @@ -5429,124 +5455,135 @@ bool LLVolumeFace::cacheOptimize() llassert(!mOptimized); mOptimized = TRUE; - allocateTangents(mNumVertices, true); + if (!mNormals || !mTexCoords) + { // can't perform this operation without normals and texture coordinates + return false; + } - SMikkTSpaceInterface ms; + if (mMikktSpaceTangents == nullptr) + { // make sure to generate mikkt space tangents for cache optimizing since the index buffer may change + allocateTangents(mNumVertices, true); - ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - return face->mNumIndices / 3; - }; + SMikkTSpaceInterface ms; - ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) - { - return 3; - }; + ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + return face->mNumIndices / 3; + }; - ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& vert = face->mPositions[idx]; - F32* v = vert.getF32ptr(); - fvPosOut[0] = v[0]; - fvPosOut[1] = v[1]; - fvPosOut[2] = v[2]; - }; - - ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& norm = face->mNormals[idx]; - F32* n = norm.getF32ptr(); - fvNormOut[0] = n[0]; - fvNormOut[1] = n[1]; - fvNormOut[2] = n[2]; - }; - - ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& tc = face->mTexCoords[idx]; - fvTexcOut[0] = tc.mV[0]; - fvTexcOut[1] = tc.mV[1]; - }; - - ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) - { - MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 i = iFace * 3 + iVert; - S32 idx = face->mIndices[i]; + ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) + { + return 3; + }; - LLVector3 p(face->mPositions[idx].getF32ptr()); - LLVector3 n(face->mNormals[idx].getF32ptr()); - LLVector3 t(fvTangent); + ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& vert = face->mPositions[idx]; + F32* v = vert.getF32ptr(); + fvPosOut[0] = v[0]; + fvPosOut[1] = v[1]; + fvPosOut[2] = v[2]; + }; + + ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& norm = face->mNormals[idx]; + F32* n = norm.getF32ptr(); + fvNormOut[0] = n[0]; + fvNormOut[1] = n[1]; + fvNormOut[2] = n[2]; + }; + + ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 idx = face->mIndices[iFace * 3 + iVert]; + auto& tc = face->mTexCoords[idx]; + fvTexcOut[0] = tc.mV[0]; + fvTexcOut[1] = tc.mV[1]; + }; + + ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) + { + MikktData* data = (MikktData*)pContext->m_pUserData; + LLVolumeFace* face = data->face; + S32 i = iFace * 3 + iVert; + S32 idx = face->mIndices[i]; - data->t[i].set(fvTangent); - data->t[i].mV[3] = fSign; - }; + LLVector3 p(face->mPositions[idx].getF32ptr()); + LLVector3 n(face->mNormals[idx].getF32ptr()); + LLVector3 t(fvTangent); - ms.m_setTSpace = nullptr; + // assert that this tangent hasn't already been set + llassert(data->t[i].magVec() < 0.1f); - MikktData data(this); + data->t[i].set(fvTangent); + data->t[i].mV[3] = fSign; + }; - SMikkTSpaceContext ctx = { &ms, &data }; + ms.m_setTSpace = nullptr; - genTangSpaceDefault(&ctx); + MikktData data(this); - //re-weld - meshopt_Stream mos[] = - { - { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) }, - { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) }, - { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) }, - { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) }, - { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) } - }; + SMikkTSpaceContext ctx = { &ms, &data }; - std::vector remap; - remap.resize(data.p.size()); + genTangSpaceDefault(&ctx); - U32 stream_count = data.w.empty() ? 4 : 5; + //re-weld + meshopt_Stream mos[] = + { + { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) }, + { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) }, + { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) } + }; - U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count); + std::vector remap; + remap.resize(data.p.size()); - std::vector indices; - indices.resize(mNumIndices); + U32 stream_count = data.w.empty() ? 4 : 5; - //copy results back into volume - resizeVertices(vert_count); + U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count); - if (!data.w.empty()) - { - allocateWeights(vert_count); - } + std::vector indices; + indices.resize(mNumIndices); - allocateTangents(mNumVertices, true); + //copy results back into volume + resizeVertices(vert_count); - for (int i = 0; i < mNumIndices; ++i) - { - U32 src_idx = i; - U32 dst_idx = remap[i]; - mIndices[i] = dst_idx; + if (!data.w.empty()) + { + allocateWeights(vert_count); + } - mPositions[dst_idx].load3(data.p[src_idx].mV); - mNormals[dst_idx].load3(data.n[src_idx].mV); - mTexCoords[dst_idx] = data.tc[src_idx]; - - mMikktSpaceTangents[dst_idx].loadua(data.t[src_idx].mV); + allocateTangents(mNumVertices, true); - if (mWeights) + for (int i = 0; i < mNumIndices; ++i) { - mWeights[dst_idx].loadua(data.w[src_idx].mV); + U32 src_idx = i; + U32 dst_idx = remap[i]; + mIndices[i] = dst_idx; + + mPositions[dst_idx].load3(data.p[src_idx].mV); + mNormals[dst_idx].load3(data.n[src_idx].mV); + mTexCoords[dst_idx] = data.tc[src_idx]; + + mMikktSpaceTangents[dst_idx].loadua(data.t[src_idx].mV); + + if (mWeights) + { + mWeights[dst_idx].loadua(data.w[src_idx].mV); + } } } -- cgit v1.2.3 From 82ab5f9765ad76c73d1d7ddd5716b22d6b92bf62 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Thu, 15 Sep 2022 17:23:34 -0500 Subject: SL-18156 WIP -- Add NormalizedScale/NormalizedTranslation to mesh assets to recover mesh's original coordinate frame when generating tangents post download. --- indra/llmath/llvolume.cpp | 78 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 24 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 563a325f03..ae753fc0f3 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2483,6 +2483,24 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]); max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]); + //unpack normalized scale/translation + if (mdl[i].has("NormalizedScale")) + { + face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]); + } + else + { + face.mNormalizedScale.set(1, 1, 1); + } + if (mdl[i].has("NormalizedTranslation")) + { + face.mNormalizedTranslation.setValue(mdl[i]["NormalizedTranslation"]); + } + else + { + face.mNormalizedTranslation.set(1, 1, 1); + } + LLVector4a pos_range; pos_range.setSub(max_pos, min_pos); LLVector2 tc_range2 = max_tc - min_tc; @@ -2533,6 +2551,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } } +#if 0 { if (!tangent.empty()) { @@ -2559,6 +2578,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } } } +#endif { if (!tc.empty()) @@ -4888,7 +4908,9 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) } mOptimized = src.mOptimized; - + mNormalizedScale = src.mNormalizedScale; + mNormalizedTranslation = src.mNormalizedTranslation; + //delete return *this; } @@ -5432,12 +5454,19 @@ struct MikktData w.resize(count); } + + LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]); + + for (int i = 0; i < face->mNumIndices; ++i) { U32 idx = face->mIndices[i]; p[i].set(face->mPositions[idx].getF32ptr()); + p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents n[i].set(face->mNormals[idx].getF32ptr()); + n[i].scaleVec(inv_scale); + n[i].normalize(); tc[i].set(face->mTexCoords[idx]); if (face->mWeights) @@ -5481,10 +5510,7 @@ bool LLVolumeFace::cacheOptimize() ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) { MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& vert = face->mPositions[idx]; - F32* v = vert.getF32ptr(); + F32* v = data->p[iFace * 3 + iVert].mV; fvPosOut[0] = v[0]; fvPosOut[1] = v[1]; fvPosOut[2] = v[2]; @@ -5493,10 +5519,7 @@ bool LLVolumeFace::cacheOptimize() ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert) { MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& norm = face->mNormals[idx]; - F32* n = norm.getF32ptr(); + F32* n = data->n[iFace * 3 + iVert].mV; fvNormOut[0] = n[0]; fvNormOut[1] = n[1]; fvNormOut[2] = n[2]; @@ -5505,27 +5528,16 @@ bool LLVolumeFace::cacheOptimize() ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert) { MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; - S32 idx = face->mIndices[iFace * 3 + iVert]; - auto& tc = face->mTexCoords[idx]; - fvTexcOut[0] = tc.mV[0]; - fvTexcOut[1] = tc.mV[1]; + F32* tc = data->tc[iFace * 3 + iVert].mV; + fvTexcOut[0] = tc[0]; + fvTexcOut[1] = tc[1]; }; ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) { MikktData* data = (MikktData*)pContext->m_pUserData; - LLVolumeFace* face = data->face; S32 i = iFace * 3 + iVert; - S32 idx = face->mIndices[i]; - - LLVector3 p(face->mPositions[idx].getF32ptr()); - LLVector3 n(face->mNormals[idx].getF32ptr()); - LLVector3 t(fvTangent); - - // assert that this tangent hasn't already been set - llassert(data->t[i].magVec() < 0.1f); - + data->t[i].set(fvTangent); data->t[i].mV[3] = fSign; }; @@ -5585,6 +5597,24 @@ bool LLVolumeFace::cacheOptimize() mWeights[dst_idx].loadua(data.w[src_idx].mV); } } + + + // put back in normalized coordinate frame + LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); + LLVector4a scale; + scale.load3(mNormalizedScale.mV); + scale.getF32ptr()[3] = 1.f; + + for (int i = 0; i < mNumVertices; ++i) + { + mPositions[i].mul(inv_scale); + mNormals[i].mul(scale); + mNormals[i].normalize3(); + F32 w = mMikktSpaceTangents[i].getF32ptr()[3]; + mMikktSpaceTangents[i].mul(scale); + mMikktSpaceTangents[i].normalize3(); + mMikktSpaceTangents[i].getF32ptr()[3] = w; + } } // cache optimize index buffer -- cgit v1.2.3 From 8dc59e5ef37836b15d478fb0d04e3043a9f986de Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 16 Sep 2022 16:25:26 -0500 Subject: SL-18128 Clear out much OpenGL cruft and switch to core profile on AMD --- indra/llmath/llvolume.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index ae753fc0f3..559d790d77 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2783,7 +2783,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } } - if (!cacheOptimize()) + if (!cacheOptimize(true)) { // Out of memory? LL_WARNS() << "Failed to optimize!" << LL_ENDL; @@ -2824,11 +2824,11 @@ void LLVolume::copyVolumeFaces(const LLVolume* volume) mSculptLevel = 0; } -bool LLVolume::cacheOptimize() +bool LLVolume::cacheOptimize(bool gen_tangents) { for (S32 i = 0; i < mVolumeFaces.size(); ++i) { - if (!mVolumeFaces[i].cacheOptimize()) + if (!mVolumeFaces[i].cacheOptimize(gen_tangents)) { return false; } @@ -5478,18 +5478,13 @@ struct MikktData }; -bool LLVolumeFace::cacheOptimize() +bool LLVolumeFace::cacheOptimize(bool gen_tangents) { //optimize for vertex cache according to Forsyth method: LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; llassert(!mOptimized); mOptimized = TRUE; - if (!mNormals || !mTexCoords) - { // can't perform this operation without normals and texture coordinates - return false; - } - - if (mMikktSpaceTangents == nullptr) + if (mMikktSpaceTangents == nullptr && gen_tangents && mNormals && mTexCoords) { // make sure to generate mikkt space tangents for cache optimizing since the index buffer may change allocateTangents(mNumVertices, true); -- cgit v1.2.3 From 75de4d32768bb1359611dc7cd963c9a12f94423d Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 23 Sep 2022 12:53:24 -0500 Subject: SL-18156 Cleanup of MikktSpace integration, apply MikktSpace tangents to all meshes. --- indra/llmath/llvolume.cpp | 77 +++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 49 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 454969b912..21df7f2b2d 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2102,14 +2102,11 @@ void LLVolume::regen() createVolumeFaces(); } -void LLVolume::genTangents(S32 face, bool mikktspace) +void LLVolume::genTangents(S32 face) { // generate legacy tangents for the specified face - // if mikktspace is true, only generate tangents if mikktspace tangents are not present (handles the case for non-mesh prims) - if (!mikktspace || mVolumeFaces[face].mMikktSpaceTangents == nullptr) - { - mVolumeFaces[face].createTangents(); - } + llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents + mVolumeFaces[face].createTangents(); } LLVolume::~LLVolume() @@ -2492,15 +2489,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) { face.mNormalizedScale.set(1, 1, 1); } - if (mdl[i].has("NormalizedTranslation")) - { - face.mNormalizedTranslation.setValue(mdl[i]["NormalizedTranslation"]); - } - else - { - face.mNormalizedTranslation.set(1, 1, 1); - } - + LLVector4a pos_range; pos_range.setSub(max_pos, min_pos); LLVector2 tc_range2 = max_tc - min_tc; @@ -2551,17 +2540,16 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } } -#if 0 +#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents { if (!tangent.empty()) { - face.allocateTangents(face.mNumVertices, true); + face.allocateTangents(face.mNumVertices); U16* t = (U16*)&(tangent[0]); - // store incoming tangents in mMikktSpaceTangents - // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the CLTF shaders to + // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to // maintain compliance with the GLTF spec - LLVector4a* t_out = face.mMikktSpaceTangents; + LLVector4a* t_out = face.mTangents; for (U32 j = 0; j < num_verts; ++j) { @@ -4111,7 +4099,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en { if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them { - genTangents(i); + genTangents(i); } if (isUnique()) @@ -4854,15 +4842,15 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mTangents = NULL; } - if (src.mMikktSpaceTangents) + if (src.mTangents) { - allocateTangents(src.mNumVertices, true); - LLVector4a::memcpyNonAliased16((F32*)mMikktSpaceTangents, (F32*)src.mMikktSpaceTangents, vert_size); + allocateTangents(src.mNumVertices); + LLVector4a::memcpyNonAliased16((F32*)mTangents, (F32*)src.mTangents, vert_size); } else { - ll_aligned_free_16(mMikktSpaceTangents); - mMikktSpaceTangents = nullptr; + ll_aligned_free_16(mTangents); + mTangents = nullptr; } if (src.mWeights) @@ -4909,8 +4897,7 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mOptimized = src.mOptimized; mNormalizedScale = src.mNormalizedScale; - mNormalizedTranslation = src.mNormalizedTranslation; - + //delete return *this; } @@ -4937,8 +4924,6 @@ void LLVolumeFace::freeData() mIndices = NULL; ll_aligned_free_16(mTangents); mTangents = NULL; - ll_aligned_free_16(mMikktSpaceTangents); - mMikktSpaceTangents = nullptr; ll_aligned_free_16(mWeights); mWeights = NULL; @@ -5060,9 +5045,6 @@ void LLVolumeFace::remap() ll_aligned_free_16(mTangents); mTangents = NULL; - ll_aligned_free_16(mMikktSpaceTangents); - mMikktSpaceTangents = nullptr; - // Assign new values mIndices = remap_indices; mPositions = remap_positions; @@ -5484,10 +5466,10 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) llassert(!mOptimized); mOptimized = TRUE; - if (mMikktSpaceTangents == nullptr && gen_tangents && mNormals && mTexCoords) - { // make sure to generate mikkt space tangents for cache optimizing since the index buffer may change - allocateTangents(mNumVertices, true); - + if (gen_tangents && mNormals && mTexCoords) + { // generate mikkt space tangents before cache optimizing since the index buffer may change + // a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh + // and is executed on a background thread SMikkTSpaceInterface ms; ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext) @@ -5573,7 +5555,7 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) allocateWeights(vert_count); } - allocateTangents(mNumVertices, true); + allocateTangents(mNumVertices); for (int i = 0; i < mNumIndices; ++i) { @@ -5585,7 +5567,7 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) mNormals[dst_idx].load3(data.n[src_idx].mV); mTexCoords[dst_idx] = data.tc[src_idx]; - mMikktSpaceTangents[dst_idx].loadua(data.t[src_idx].mV); + mTangents[dst_idx].loadua(data.t[src_idx].mV); if (mWeights) { @@ -5605,10 +5587,10 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) mPositions[i].mul(inv_scale); mNormals[i].mul(scale); mNormals[i].normalize3(); - F32 w = mMikktSpaceTangents[i].getF32ptr()[3]; - mMikktSpaceTangents[i].mul(scale); - mMikktSpaceTangents[i].normalize3(); - mMikktSpaceTangents[i].getF32ptr()[3] = w; + F32 w = mTangents[i].getF32ptr()[3]; + mTangents[i].mul(scale); + mTangents[i].normalize3(); + mTangents[i].getF32ptr()[3] = w; } } @@ -5703,7 +5685,6 @@ void LLVolumeFace::swapData(LLVolumeFace& rhs) llswap(rhs.mPositions, mPositions); llswap(rhs.mNormals, mNormals); llswap(rhs.mTangents, mTangents); - llswap(rhs.mMikktSpaceTangents, mMikktSpaceTangents); llswap(rhs.mTexCoords, mTexCoords); llswap(rhs.mIndices,mIndices); llswap(rhs.mNumVertices, mNumVertices); @@ -6415,7 +6396,6 @@ void LLVolumeFace::createTangents() { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - if (!mTangents) { allocateTangents(mNumVertices); @@ -6539,11 +6519,10 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con mNumVertices++; } -void LLVolumeFace::allocateTangents(S32 num_verts, bool mikktspace) +void LLVolumeFace::allocateTangents(S32 num_verts) { - auto& buff = mikktspace ? mMikktSpaceTangents : mTangents; - ll_aligned_free_16(buff); - buff = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + ll_aligned_free_16(mTangents); + mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); } void LLVolumeFace::allocateWeights(S32 num_verts) -- cgit v1.2.3 From 3377b19de43dfe0922174240a7844ba8f82eba23 Mon Sep 17 00:00:00 2001 From: Rye Mutt Date: Mon, 6 Mar 2023 10:15:38 -0500 Subject: Fix double copy of tangents array (#107) --- indra/llmath/llvolume.cpp | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 9919af5368..7a694ab10c 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -4863,17 +4863,6 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mTangents = NULL; } - if (src.mTangents) - { - allocateTangents(src.mNumVertices); - LLVector4a::memcpyNonAliased16((F32*)mTangents, (F32*)src.mTangents, vert_size); - } - else - { - ll_aligned_free_16(mTangents); - mTangents = nullptr; - } - if (src.mWeights) { llassert(!mWeights); // don't orphan an old alloc here accidentally -- cgit v1.2.3 From 6c5b9076704865e96adb5b4a25a518633da1174e Mon Sep 17 00:00:00 2001 From: RunitaiLinden Date: Thu, 13 Apr 2023 19:41:11 -0500 Subject: SL-19358 Fix for explody meshes. --- indra/llmath/llvolume.cpp | 51 +++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 22 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 7a694ab10c..2a906c8d41 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5574,37 +5574,44 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count); - std::vector indices; - indices.resize(mNumIndices); + if (vert_count < 65535) + { + std::vector indices; + indices.resize(mNumIndices); - //copy results back into volume - resizeVertices(vert_count); + //copy results back into volume + resizeVertices(vert_count); - if (!data.w.empty()) - { - allocateWeights(vert_count); - } + if (!data.w.empty()) + { + allocateWeights(vert_count); + } - allocateTangents(mNumVertices); + allocateTangents(mNumVertices); - for (int i = 0; i < mNumIndices; ++i) - { - U32 src_idx = i; - U32 dst_idx = remap[i]; - mIndices[i] = dst_idx; + for (int i = 0; i < mNumIndices; ++i) + { + U32 src_idx = i; + U32 dst_idx = remap[i]; + mIndices[i] = dst_idx; - mPositions[dst_idx].load3(data.p[src_idx].mV); - mNormals[dst_idx].load3(data.n[src_idx].mV); - mTexCoords[dst_idx] = data.tc[src_idx]; + mPositions[dst_idx].load3(data.p[src_idx].mV); + mNormals[dst_idx].load3(data.n[src_idx].mV); + mTexCoords[dst_idx] = data.tc[src_idx]; - mTangents[dst_idx].loadua(data.t[src_idx].mV); + mTangents[dst_idx].loadua(data.t[src_idx].mV); - if (mWeights) - { - mWeights[dst_idx].loadua(data.w[src_idx].mV); + if (mWeights) + { + mWeights[dst_idx].loadua(data.w[src_idx].mV); + } } } - + else + { + // blew past the max vertex size limit, use legacy tangent generation which never adds verts + createTangents(); + } // put back in normalized coordinate frame LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); -- cgit v1.2.3 From 1cadfd44fe5ddafaf3ba3560bd8ec8e1351b347d Mon Sep 17 00:00:00 2001 From: RunitaiLinden Date: Wed, 3 May 2023 17:30:02 -0500 Subject: DRTVWR-559 WIP - optimize ARC calculation et al. --- indra/llmath/llvolume.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 2a906c8d41..b6cdcb2736 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -3351,12 +3351,12 @@ BOOL LLVolume::isFlat(S32 face) bool LLVolumeParams::isSculpt() const { - return mSculptID.notNull(); + return (mSculptType & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_NONE; } bool LLVolumeParams::isMeshSculpt() const { - return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH); + return (mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH; } bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const @@ -3771,6 +3771,7 @@ bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 h void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts) { //attempt to approximate the number of triangles that will result from generating a volume LoD set for the //supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; F32 detail[] = {1.f, 1.5f, 2.5f, 4.f}; for (S32 i = 0; i < 4; i++) { -- cgit v1.2.3 From ca84e0e0c0b4b2844f478a549cb2cb1fbb52c898 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 22 Jun 2023 22:09:42 +0300 Subject: SL-5161 Avatars should stay hidden longer if they are waiting for meshes or skin data --- indra/llmath/llvolume.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 91e463cc32..0e3792fda3 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2056,7 +2056,8 @@ LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const BOOL ge mDetail = detail; mSculptLevel = -2; mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims - mIsMeshAssetLoaded = FALSE; + mIsMeshAssetLoaded = false; + mIsMeshAssetUnavaliable = false; mLODScaleBias.setVec(1,1,1); mHullPoints = NULL; mHullIndices = NULL; @@ -2804,14 +2805,32 @@ bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl) } -BOOL LLVolume::isMeshAssetLoaded() +bool LLVolume::isMeshAssetLoaded() { return mIsMeshAssetLoaded; } -void LLVolume::setMeshAssetLoaded(BOOL loaded) +void LLVolume::setMeshAssetLoaded(bool loaded) { mIsMeshAssetLoaded = loaded; + if (loaded) + { + mIsMeshAssetUnavaliable = false; + } +} + +void LLVolume::setMeshAssetUnavaliable(bool unavaliable) +{ + // Don't set it if at least one lod loaded + if (!mIsMeshAssetLoaded) + { + mIsMeshAssetUnavaliable = unavaliable; + } +} + +bool LLVolume::isMeshAssetUnavaliable() +{ + return mIsMeshAssetUnavaliable; } void LLVolume::copyFacesTo(std::vector &faces) const -- cgit v1.2.3 From a280bacfea5453df3453322223b677adc2780511 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 11 Sep 2023 17:11:03 -0700 Subject: SL-20157: Fix scale of avatar mesh parts when mikktspace tangent generation fails --- indra/llmath/llvolume.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'indra/llmath/llvolume.cpp') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 0e3792fda3..6d36daa92a 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5626,29 +5626,29 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) mWeights[dst_idx].loadua(data.w[src_idx].mV); } } + + // put back in normalized coordinate frame + LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); + LLVector4a scale; + scale.load3(mNormalizedScale.mV); + scale.getF32ptr()[3] = 1.f; + + for (int i = 0; i < mNumVertices; ++i) + { + mPositions[i].mul(inv_scale); + mNormals[i].mul(scale); + mNormals[i].normalize3(); + F32 w = mTangents[i].getF32ptr()[3]; + mTangents[i].mul(scale); + mTangents[i].normalize3(); + mTangents[i].getF32ptr()[3] = w; + } } else { // blew past the max vertex size limit, use legacy tangent generation which never adds verts createTangents(); } - - // put back in normalized coordinate frame - LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); - LLVector4a scale; - scale.load3(mNormalizedScale.mV); - scale.getF32ptr()[3] = 1.f; - - for (int i = 0; i < mNumVertices; ++i) - { - mPositions[i].mul(inv_scale); - mNormals[i].mul(scale); - mNormals[i].normalize3(); - F32 w = mTangents[i].getF32ptr()[3]; - mTangents[i].mul(scale); - mTangents[i].normalize3(); - mTangents[i].getF32ptr()[3] = w; - } } // cache optimize index buffer -- cgit v1.2.3