From deb0b6b06f45652368218d4faf0b43d8575a6861 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 14 Dec 2017 16:12:44 +0100 Subject: [PATCH] Working interleaved normals and tangents. I'm still wondering why it works with blendshapes though... --- libraries/fbx/src/FBXReader.cpp | 55 +++++++++------- libraries/fbx/src/FBXReader_Mesh.cpp | 87 ++++++++++++++++---------- libraries/model/src/model/Geometry.cpp | 6 +- 3 files changed, 89 insertions(+), 59 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e4fea00a34..041ce4ec70 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -303,8 +303,7 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { return blendshape; } - -void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { +static void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { const glm::vec3& normal = mesh.normals.at(firstIndex); glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); if (glm::length(bitangent) < EPSILON) { @@ -316,6 +315,35 @@ void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { glm::normalize(bitangent), normalizedNormal); } +static void createTangents(FBXMesh& mesh, bool generateFromTexCoords) { + mesh.tangents.resize(mesh.vertices.size()); + + // if we have a normal map (and texture coordinates), we must compute tangents + if (generateFromTexCoords && !mesh.texCoords.isEmpty()) { + foreach(const FBXMeshPart& part, mesh.parts) { + for (int i = 0; i < part.quadIndices.size(); i += 4) { + setTangents(mesh, part.quadIndices.at(i), part.quadIndices.at(i + 1)); + setTangents(mesh, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2)); + setTangents(mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3)); + setTangents(mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i)); + } + // <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0 + // This is most likely evidence of a further problem in extractMesh() + for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) { + setTangents(mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1)); + setTangents(mesh, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2)); + setTangents(mesh, part.triangleIndices.at(i + 2), part.triangleIndices.at(i)); + } + if ((part.triangleIndices.size() % 3) != 0) { + qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; + } + } + } else { + // Fill with a dummy value to force tangents to be present if there are normals + std::fill(mesh.tangents.begin(), mesh.tangents.end(), Vectors::UNIT_NEG_X); + } +} + QVector getIndices(const QVector ids, QVector modelIDs) { QVector indices; foreach (const QString& id, ids) { @@ -1570,27 +1598,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } - // if we have a normal map (and texture coordinates), we must compute tangents - if (generateTangents && !extracted.mesh.texCoords.isEmpty()) { - extracted.mesh.tangents.resize(extracted.mesh.vertices.size()); - foreach (const FBXMeshPart& part, extracted.mesh.parts) { - for (int i = 0; i < part.quadIndices.size(); i += 4) { - setTangents(extracted.mesh, part.quadIndices.at(i), part.quadIndices.at(i + 1)); - setTangents(extracted.mesh, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2)); - setTangents(extracted.mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3)); - setTangents(extracted.mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i)); - } - // <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0 - // This is most likely evidence of a further problem in extractMesh() - for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) { - setTangents(extracted.mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1)); - setTangents(extracted.mesh, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2)); - setTangents(extracted.mesh, part.triangleIndices.at(i + 2), part.triangleIndices.at(i)); - } - if ((part.triangleIndices.size() % 3) != 0){ - qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; - } - } + if (!extracted.mesh.normals.empty()) { + createTangents(extracted.mesh, generateTangents); } // find the clusters with which the mesh is associated diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 8e0b33b548..ea27934b28 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -42,6 +42,10 @@ using vec2h = glm::tvec2; +using NormalType = glm::vec3; + +#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ + class Vertex { public: int originalIndex; @@ -576,35 +580,52 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { mesh->setVertexBuffer(vbv); // evaluate all attribute channels sizes - int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3); - int tangentsSize = fbxMesh.tangents.size() * sizeof(glm::vec3); - int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3); + const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType); + const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType); + // If there are normals then there should be tangents + assert(normalsSize == tangentsSize); + const auto normalsAndTangentsSize = normalsSize + tangentsSize; + const int normalsAndTangentsStride = 2 * sizeof(NormalType); + const int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3); // Texture coordinates are stored in 2 half floats - int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h); - int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h); + const int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h); + const int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h); int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t); if (fbxMesh.clusters.size() > UINT8_MAX) { // we need 16 bits instead of just 8 for clusterIndices clusterIndicesSize *= 2; } - int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t); + const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t); - int normalsOffset = 0; - int tangentsOffset = normalsOffset + normalsSize; - int colorsOffset = tangentsOffset + tangentsSize; - int texCoordsOffset = colorsOffset + colorsSize; - int texCoords1Offset = texCoordsOffset + texCoordsSize; - int clusterIndicesOffset = texCoords1Offset + texCoords1Size; - int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; - int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize; + // Normals and tangents are interleaved + const int normalsOffset = 0; + const int tangentsOffset = normalsOffset + sizeof(NormalType); + const int colorsOffset = normalsOffset + normalsSize + tangentsSize; + const int texCoordsOffset = colorsOffset + colorsSize; + const int texCoords1Offset = texCoordsOffset + texCoordsSize; + const int clusterIndicesOffset = texCoords1Offset + texCoords1Size; + const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; + const int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize; // Copy all attribute data in a single attribute buffer auto attribBuffer = std::make_shared(); attribBuffer->resize(totalAttributeSize); - attribBuffer->setSubData(normalsOffset, normalsSize, (gpu::Byte*) fbxMesh.normals.constData()); - attribBuffer->setSubData(tangentsOffset, tangentsSize, (gpu::Byte*) fbxMesh.tangents.constData()); - attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData()); + + // Interleave normals and tangents + { + QVector normalsAndTangents; + + normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size()); + for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin(); + normalIt != fbxMesh.normals.constEnd(); + ++normalIt, ++tangentIt) { + normalsAndTangents.push_back(*normalIt); + normalsAndTangents.push_back(*tangentIt); + } + attribBuffer->setSubData(normalsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.constData()); + } + attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData()); if (texCoordsSize > 0) { QVector texCoordData; @@ -616,7 +637,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y); texCoordData.push_back(texCoordVec2h); } - attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) texCoordData.constData()); + attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData()); } if (texCoords1Size > 0) { @@ -629,7 +650,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y); texCoordData.push_back(texCoordVec2h); } - attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (gpu::Byte*) texCoordData.constData()); + attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (const gpu::Byte*) texCoordData.constData()); } if (fbxMesh.clusters.size() < UINT8_MAX) { @@ -641,31 +662,29 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { assert(fbxMesh.clusterIndices[i] <= UINT8_MAX); clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]); } - attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) clusterIndices.constData()); + attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData()); } else { - attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) fbxMesh.clusterIndices.constData()); + attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData()); } - attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (gpu::Byte*) fbxMesh.clusterWeights.constData()); + attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData()); if (normalsSize) { mesh->addAttribute(gpu::Stream::NORMAL, - model::BufferView(attribBuffer, normalsOffset, normalsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); - } - if (tangentsSize) { + model::BufferView(attribBuffer, normalsOffset, normalsAndTangentsSize, + normalsAndTangentsStride, FBX_NORMAL_ELEMENT)); mesh->addAttribute(gpu::Stream::TANGENT, - model::BufferView(attribBuffer, tangentsOffset, tangentsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); + model::BufferView(attribBuffer, tangentsOffset, normalsAndTangentsSize, + normalsAndTangentsStride, FBX_NORMAL_ELEMENT)); } if (colorsSize) { mesh->addAttribute(gpu::Stream::COLOR, - model::BufferView(attribBuffer, colorsOffset, colorsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB))); + model::BufferView(attribBuffer, colorsOffset, colorsSize, + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB))); } if (texCoordsSize) { mesh->addAttribute(gpu::Stream::TEXCOORD, - model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, - gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); + model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, + gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); } if (texCoords1Size) { mesh->addAttribute( gpu::Stream::TEXCOORD1, @@ -673,8 +692,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); } else if (texCoordsSize) { mesh->addAttribute(gpu::Stream::TEXCOORD1, - model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize, - gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); + model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize, + gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); } if (clusterIndicesSize) { diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 5627746c43..f6c17adf17 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -72,12 +72,14 @@ void Mesh::evalVertexStream() { int channelNum = 0; if (hasVertexData()) { - _vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum)); + auto stride = glm::max(_vertexFormat->getChannelStride(channelNum), _vertexBuffer._stride); + _vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, stride); channelNum++; } for (auto attrib : _attributeBuffers) { BufferView& view = attrib.second; - _vertexStream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum)); + auto stride = glm::max(_vertexFormat->getChannelStride(channelNum), view._stride); + _vertexStream.addBuffer(view._buffer, view._offset, stride); channelNum++; } }