Working packing of normals and tangents in GL_INT_10_10_10_2_REV format. Need to check this with all available 3D data input formats

This commit is contained in:
Olivier Prat 2017-12-14 17:57:34 +01:00
parent deb0b6b06f
commit f38e473218
7 changed files with 75 additions and 23 deletions

View file

@ -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<QString> clusterIDs;

View file

@ -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<QString, ExtractedMesh> meshes;
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
static glm::vec3 normalizeDirForPacking(const glm::vec3& dir);
FBXTexture getTexture(const QString& textureID);
QHash<QString, QString> _textureNames;

View file

@ -42,10 +42,6 @@
using vec2h = glm::tvec2<glm::detail::hdata>;
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());
}

View file

@ -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 };

View file

@ -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;

View file

@ -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);

View file

@ -26,6 +26,8 @@
#include <GLMHelpers.h>
#include <model-networking/SimpleMeshProxy.h>
#include <glm/gtc/packing.hpp>
#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<gpu::Buffer>();
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<NormalType> 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<NormalType> 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();
}