SL-18095 Add tangents to mesh assets so we can calculate mikktspace tangents in the mesh's original coordinate frame.

master
Dave Parks 2022-09-12 19:48:33 -05:00
parent c5af5d10ed
commit e49d602bd9
6 changed files with 219 additions and 124 deletions

View File

@ -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);
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);
data->t[i].set(fvTangent);
data->t[i].mV[3] = fSign;
};
ms.m_setTSpace = nullptr;
MikktData data(this);
SMikkTSpaceContext ctx = { &ms, &data };
genTangSpaceDefault(&ctx);
//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<U32> 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<U32> indices;
indices.resize(mNumIndices);
//copy results back into volume
resizeVertices(vert_count);
if (!data.w.empty())
{
allocateWeights(vert_count);
if (!mNormals || !mTexCoords)
{ // can't perform this operation without normals and texture coordinates
return false;
}
allocateTangents(mNumVertices, true);
if (mMikktSpaceTangents == nullptr)
{ // make sure to generate mikkt space tangents for cache optimizing since the index buffer may change
allocateTangents(mNumVertices, true);
for (int i = 0; i < mNumIndices; ++i)
{
U32 src_idx = i;
U32 dst_idx = remap[i];
mIndices[i] = dst_idx;
SMikkTSpaceInterface ms;
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)
ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext)
{
mWeights[dst_idx].loadua(data.w[src_idx].mV);
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);
// 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;
};
ms.m_setTSpace = nullptr;
MikktData data(this);
SMikkTSpaceContext ctx = { &ms, &data };
genTangSpaceDefault(&ctx);
//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<U32> 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<U32> indices;
indices.resize(mNumIndices);
//copy results back into volume
resizeVertices(vert_count);
if (!data.w.empty())
{
allocateWeights(vert_count);
}
allocateTangents(mNumVertices, true);
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];
mMikktSpaceTangents[dst_idx].loadua(data.t[src_idx].mV);
if (mWeights)
{
mWeights[dst_idx].loadua(data.w[src_idx].mV);
}
}
}

View File

@ -2551,6 +2551,9 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
LLVolume::face_list_t remainder;
do
{
// generate tangents and cache optimize before normalizing
ret->preprocessVolumeFaces();
// Insure we do this once with the whole gang and not per-model
//
if (!normalized && !mNoNormalize)
@ -2561,10 +2564,11 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
ret->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder);
if (!mNoOptimize)
{
ret->remapVolumeFaces();
}
// remove unused/redundant vertices after normalizing
//if (!mNoOptimize)
//{
// ret->remapVolumeFaces();
//}
volume_faces = remainder.size();

View File

@ -187,6 +187,15 @@ void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remain
}
}
// generate mikkt space tangents and cache optimize
void LLModel::preprocessVolumeFaces()
{
for (auto& face : mVolumeFaces)
{
face.cacheOptimize();
}
}
// Shrink the model to fit
// on a 1x1x1 cube centered at the origin.
// The positions and extents
@ -296,6 +305,7 @@ void LLModel::normalizeVolumeFaces()
// the positions to fit within the unit cube.
LLVector4a* pos = (LLVector4a*) face.mPositions;
LLVector4a* norm = (LLVector4a*) face.mNormals;
LLVector4a* t = (LLVector4a*)face.mMikktSpaceTangents;
for (U32 j = 0; j < face.mNumVertices; ++j)
{
@ -306,6 +316,14 @@ void LLModel::normalizeVolumeFaces()
norm[j].mul(inv_scale);
norm[j].normalize3();
}
if (t)
{
F32 w = t[j].getF32ptr()[3];
t[j].mul(inv_scale);
t[j].normalize3();
t[j].getF32ptr()[3] = w;
}
}
}
@ -726,10 +744,12 @@ LLSD LLModel::writeModel(
LLSD::Binary verts(face.mNumVertices*3*2);
LLSD::Binary tc(face.mNumVertices*2*2);
LLSD::Binary normals(face.mNumVertices*3*2);
LLSD::Binary tangents(face.mNumVertices * 4 * 2);
LLSD::Binary indices(face.mNumIndices*2);
U32 vert_idx = 0;
U32 norm_idx = 0;
U32 tan_idx = 0;
U32 tc_idx = 0;
LLVector2* ftc = (LLVector2*) face.mTexCoords;
@ -782,6 +802,22 @@ LLSD LLModel::writeModel(
normals[norm_idx++] = buff[1];
}
}
if (face.mMikktSpaceTangents)
{ //normals
F32* tangent = face.mMikktSpaceTangents[j].getF32ptr();
for (U32 k = 0; k < 4; ++k)
{ //for each component
//convert to 16-bit normalized
U16 val = (U16)((tangent[k] + 1.f) * 0.5f * 65535);
U8* buff = (U8*)&val;
//write to binary buffer
tangents[tan_idx++] = buff[0];
tangents[tan_idx++] = buff[1];
}
}
//texcoord
if (face.mTexCoords)
@ -819,6 +855,11 @@ LLSD LLModel::writeModel(
mdl[model_names[idx]][i]["Normal"] = normals;
}
if (face.mMikktSpaceTangents)
{
mdl[model_names[idx]][i]["Tangent"] = tangents;
}
if (face.mTexCoords)
{
mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();

View File

@ -182,6 +182,7 @@ public:
void addFace(const LLVolumeFace& face);
void sortVolumeFacesByMaterialName();
void preprocessVolumeFaces();
void normalizeVolumeFaces();
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
void remapVolumeFaces();

View File

@ -94,6 +94,8 @@ void main()
//col = vec3(0,0,0);
//emissive = vary_tangent.xyz*0.5+0.5;
//emissive = vec3(sign*0.5+0.5);
//emissive = vNt * 0.5 + 0.5;
//emissive = tnorm*0.5+0.5;
// See: C++: addDeferredAttachments(), GLSL: softenLightF
frag_data[0] = vec4(col, 0.0); // Diffuse
frag_data[1] = vec4(emissive, vertex_color.a); // PBR sRGB Emissive

View File

@ -1308,9 +1308,10 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
// extra space for normals and text coords
S32 tc_bytes_size = ((size_vertices * sizeof(LLVector2)) + 0xF) & ~0xF;
LLVector4a* combined_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size);
LLVector4a* combined_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 3 * size_vertices + tc_bytes_size);
LLVector4a* combined_normals = combined_positions + size_vertices;
LLVector2* combined_tex_coords = (LLVector2*)(combined_normals + size_vertices);
LLVector4a* combined_tangents = combined_normals + size_vertices;
LLVector2* combined_tex_coords = (LLVector2*)(combined_tangents + size_vertices);
// copy indices and vertices into new buffers
S32 combined_positions_shift = 0;
@ -1320,6 +1321,9 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
{
const LLVolumeFace &face = base_model->getVolumeFace(face_idx);
// ensure tangents have been generated or loaded
llassert(face.mMikktSpaceTangents);
// Vertices
S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a);
LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes);
@ -1327,6 +1331,9 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
// Normals
LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes);
// Tangents
LLVector4a::memcpyNonAliased16((F32*)(combined_tangents + combined_positions_shift), (F32*)face.mMikktSpaceTangents, copy_bytes);
// Tex coords
copy_bytes = face.mNumVertices * sizeof(LLVector2);
memcpy((void*)(combined_tex_coords + combined_positions_shift), (void*)face.mTexCoords, copy_bytes);
@ -1428,9 +1435,10 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
// IV. Repack back into individual faces
LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size);
LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 3 * size_vertices + tc_bytes_size);
LLVector4a* buffer_normals = buffer_positions + size_vertices;
LLVector2* buffer_tex_coords = (LLVector2*)(buffer_normals + size_vertices);
LLVector4a* buffer_tangents = buffer_normals + size_vertices;
LLVector2* buffer_tex_coords = (LLVector2*)(buffer_tangents + size_vertices);
S32 buffer_idx_size = (size_indices * sizeof(U16) + 0xF) & ~0xF;
U16* buffer_indices = (U16*)ll_aligned_malloc_16(buffer_idx_size);
S32* old_to_new_positions_map = new S32[size_vertices];
@ -1511,6 +1519,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
// Copy vertice, normals, tcs
buffer_positions[buf_positions_copied] = combined_positions[idx];
buffer_normals[buf_positions_copied] = combined_normals[idx];
buffer_tangents[buf_positions_copied] = combined_tangents[idx];
buffer_tex_coords[buf_positions_copied] = combined_tex_coords[idx];
old_to_new_positions_map[idx] = buf_positions_copied;
@ -1549,12 +1558,13 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
{
new_face.resizeIndices(buf_indices_copied);
new_face.resizeVertices(buf_positions_copied);
new_face.allocateTangents(buf_positions_copied, true);
S32 idx_size = (buf_indices_copied * sizeof(U16) + 0xF) & ~0xF;
LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)buffer_indices, idx_size);
LLVector4a::memcpyNonAliased16((F32*)new_face.mPositions, (F32*)buffer_positions, buf_positions_copied * sizeof(LLVector4a));
LLVector4a::memcpyNonAliased16((F32*)new_face.mNormals, (F32*)buffer_normals, buf_positions_copied * sizeof(LLVector4a));
LLVector4a::memcpyNonAliased16((F32*)new_face.mMikktSpaceTangents, (F32*)buffer_tangents, buf_positions_copied * sizeof(LLVector4a));
U32 tex_size = (buf_positions_copied * sizeof(LLVector2) + 0xF)&~0xF;
LLVector4a::memcpyNonAliased16((F32*)new_face.mTexCoords, (F32*)buffer_tex_coords, tex_size);