diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 041ce4ec70..a5d15350d5 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -316,10 +316,10 @@ static void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { } 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()) { + mesh.tangents.resize(mesh.vertices.size()); + 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)); @@ -338,9 +338,6 @@ static void createTangents(FBXMesh& mesh, bool generateFromTexCoords) { 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); } } @@ -1598,9 +1595,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } - if (!extracted.mesh.normals.empty()) { - createTangents(extracted.mesh, generateTangents); - } + createTangents(extracted.mesh, generateTangents); // find the clusters with which the mesh is associated QVector clusterIDs; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 843a874a62..34d63b098a 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -33,6 +33,15 @@ class QIODevice; class FBXNode; +#define FBX_PACK_NORMALS 1 + +#if FBX_PACK_NORMALS +using NormalType = glm::uint32; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2 +#else +using NormalType = glm::vec3; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ +#endif /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing @@ -114,6 +123,8 @@ public: QHash meshes; static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); + static glm::vec3 normalizeDirForPacking(const glm::vec3& dir); + FBXTexture getTexture(const QString& textureID); QHash _textureNames; diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index ea27934b28..e684332738 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -42,10 +42,6 @@ using vec2h = glm::tvec2; -using NormalType = glm::vec3; - -#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ - class Vertex { public: int originalIndex; @@ -551,6 +547,14 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn return data.extracted; } +glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) { + auto maxCoord = glm::max(fabsf(dir.x), glm::max(fabsf(dir.y), fabsf(dir.z))); + if (maxCoord > 1e-6f) { + return dir / maxCoord; + } + return dir; +} + void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); @@ -579,6 +583,12 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { gpu::BufferView vbv(vb, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); mesh->setVertexBuffer(vbv); + if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) { + // Fill with a dummy value to force tangents to be present if there are normals + fbxMesh.tangents.reserve(fbxMesh.normals.size()); + std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X); + } + // evaluate all attribute channels sizes const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType); const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType); @@ -620,8 +630,17 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin(); normalIt != fbxMesh.normals.constEnd(); ++normalIt, ++tangentIt) { - normalsAndTangents.push_back(*normalIt); - normalsAndTangents.push_back(*tangentIt); +#if FBX_PACK_NORMALS + const auto normal = normalizeDirForPacking(*normalIt); + const auto tangent = normalizeDirForPacking(*tangentIt); + const auto packedNormal = glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f)); + const auto packedTangent = glm::packSnorm3x10_1x2(glm::vec4(tangent, 0.0f)); +#else + const auto packedNormal = *normalIt; + const auto packedTangent = *tangentIt; +#endif + normalsAndTangents.push_back(packedNormal); + normalsAndTangents.push_back(packedTangent); } attribBuffer->setSubData(normalsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.constData()); } diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 7614238a74..c4b6b39723 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -38,7 +38,7 @@ const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; const Element Element::VEC2F_XY{ VEC2, FLOAT, XY }; const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ }; const Element Element::VEC4F_XYZW{ VEC4, FLOAT, XYZW }; -const Element Element::VEC4F_W2XYZ10{ VEC4, NINT2_10_10_10, XYZW }; +const Element Element::VEC4F_NORMALIZED_XYZ10W2{ VEC4, NINT2_10_10_10, XYZW }; const Element Element::INDEX_UINT16 { SCALAR, UINT16, INDEX }; const Element Element::INDEX_INT32 { SCALAR, INT32, INDEX }; const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 979b67f728..17102d0415 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -329,7 +329,7 @@ public: static const Element VEC2F_XY; static const Element VEC3F_XYZ; static const Element VEC4F_XYZW; - static const Element VEC4F_W2XYZ10; + static const Element VEC4F_NORMALIZED_XYZ10W2; static const Element INDEX_UINT16; static const Element INDEX_INT32; static const Element PART_DRAWCALL; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 1ea3e1a705..96451fa23d 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -518,7 +518,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { ModelPointer model = _model.lock(); if (model) { batch.setInputBuffer(0, model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputBuffer(1, model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(NormalType)); batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } else { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index c4bc435691..d918970af5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -315,10 +317,21 @@ bool Model::updateGeometry() { // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? auto buffer = std::make_shared(); if (!mesh.blendshapes.isEmpty()) { - buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); + buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normals.size() * sizeof(NormalType)); + buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData()); +#if FBX_PACK_NORMALS + std::vector packedNormals; + packedNormals.reserve(mesh.normals.size()); + for (auto normal : mesh.normals) { + normal = FBXReader::normalizeDirForPacking(normal); + packedNormals.push_back(glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f))); + } + const auto normalsData = packedNormals.data(); +#else + const auto normalsData = mesh.normals.constData(); +#endif buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsData); } _blendedVertexBuffers.push_back(buffer); } @@ -1183,6 +1196,9 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo _appliedBlendNumber = blendNumber; const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; +#if FBX_PACK_NORMALS + std::vector packedNormals; +#endif for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.blendshapes.isEmpty()) { @@ -1190,9 +1206,20 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo } gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3)); - buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3)); + const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3); + buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3)); +#if FBX_PACK_NORMALS + packedNormals.clear(); + packedNormals.reserve(normals.size()); + for (auto normal : normals) { + normal = FBXReader::normalizeDirForPacking(normal); + packedNormals.push_back(glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f))); + } + const auto normalsData = packedNormals.data(); +#else + const auto normalsData = mesh.normals.constData(); +#endif + buffer->setSubData(verticesSize, normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsData + index*sizeof(NormalType)); index += mesh.vertices.size(); }