mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 02:36:54 +02:00
Working interleaved normals and tangents. I'm still wondering why it works with blendshapes though...
This commit is contained in:
parent
5ad69afa8a
commit
deb0b6b06f
3 changed files with 89 additions and 59 deletions
|
@ -303,8 +303,7 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
|
||||||
return blendshape;
|
return blendshape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
|
||||||
void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
|
|
||||||
const glm::vec3& normal = mesh.normals.at(firstIndex);
|
const glm::vec3& normal = mesh.normals.at(firstIndex);
|
||||||
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex));
|
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex));
|
||||||
if (glm::length(bitangent) < EPSILON) {
|
if (glm::length(bitangent) < EPSILON) {
|
||||||
|
@ -316,6 +315,35 @@ void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
|
||||||
glm::normalize(bitangent), normalizedNormal);
|
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<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
|
QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
|
||||||
QVector<int> indices;
|
QVector<int> indices;
|
||||||
foreach (const QString& id, ids) {
|
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 (!extracted.mesh.normals.empty()) {
|
||||||
if (generateTangents && !extracted.mesh.texCoords.isEmpty()) {
|
createTangents(extracted.mesh, generateTangents);
|
||||||
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 ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the clusters with which the mesh is associated
|
// find the clusters with which the mesh is associated
|
||||||
|
|
|
@ -42,6 +42,10 @@
|
||||||
|
|
||||||
using vec2h = glm::tvec2<glm::detail::hdata>;
|
using vec2h = glm::tvec2<glm::detail::hdata>;
|
||||||
|
|
||||||
|
using NormalType = glm::vec3;
|
||||||
|
|
||||||
|
#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ
|
||||||
|
|
||||||
class Vertex {
|
class Vertex {
|
||||||
public:
|
public:
|
||||||
int originalIndex;
|
int originalIndex;
|
||||||
|
@ -576,35 +580,52 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
||||||
mesh->setVertexBuffer(vbv);
|
mesh->setVertexBuffer(vbv);
|
||||||
|
|
||||||
// evaluate all attribute channels sizes
|
// evaluate all attribute channels sizes
|
||||||
int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3);
|
const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType);
|
||||||
int tangentsSize = fbxMesh.tangents.size() * sizeof(glm::vec3);
|
const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType);
|
||||||
int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3);
|
// 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
|
// Texture coordinates are stored in 2 half floats
|
||||||
int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h);
|
const int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h);
|
||||||
int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h);
|
const int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h);
|
||||||
|
|
||||||
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t);
|
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t);
|
||||||
if (fbxMesh.clusters.size() > UINT8_MAX) {
|
if (fbxMesh.clusters.size() > UINT8_MAX) {
|
||||||
// we need 16 bits instead of just 8 for clusterIndices
|
// we need 16 bits instead of just 8 for clusterIndices
|
||||||
clusterIndicesSize *= 2;
|
clusterIndicesSize *= 2;
|
||||||
}
|
}
|
||||||
int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
|
const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
|
||||||
|
|
||||||
int normalsOffset = 0;
|
// Normals and tangents are interleaved
|
||||||
int tangentsOffset = normalsOffset + normalsSize;
|
const int normalsOffset = 0;
|
||||||
int colorsOffset = tangentsOffset + tangentsSize;
|
const int tangentsOffset = normalsOffset + sizeof(NormalType);
|
||||||
int texCoordsOffset = colorsOffset + colorsSize;
|
const int colorsOffset = normalsOffset + normalsSize + tangentsSize;
|
||||||
int texCoords1Offset = texCoordsOffset + texCoordsSize;
|
const int texCoordsOffset = colorsOffset + colorsSize;
|
||||||
int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
|
const int texCoords1Offset = texCoordsOffset + texCoordsSize;
|
||||||
int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
|
const int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
|
||||||
int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
|
const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
|
||||||
|
const int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
|
||||||
|
|
||||||
// Copy all attribute data in a single attribute buffer
|
// Copy all attribute data in a single attribute buffer
|
||||||
auto attribBuffer = std::make_shared<gpu::Buffer>();
|
auto attribBuffer = std::make_shared<gpu::Buffer>();
|
||||||
attribBuffer->resize(totalAttributeSize);
|
attribBuffer->resize(totalAttributeSize);
|
||||||
attribBuffer->setSubData(normalsOffset, normalsSize, (gpu::Byte*) fbxMesh.normals.constData());
|
|
||||||
attribBuffer->setSubData(tangentsOffset, tangentsSize, (gpu::Byte*) fbxMesh.tangents.constData());
|
// Interleave normals and tangents
|
||||||
attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData());
|
{
|
||||||
|
QVector<NormalType> 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) {
|
if (texCoordsSize > 0) {
|
||||||
QVector<vec2h> texCoordData;
|
QVector<vec2h> texCoordData;
|
||||||
|
@ -616,7 +637,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
||||||
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
||||||
texCoordData.push_back(texCoordVec2h);
|
texCoordData.push_back(texCoordVec2h);
|
||||||
}
|
}
|
||||||
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) texCoordData.constData());
|
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texCoords1Size > 0) {
|
if (texCoords1Size > 0) {
|
||||||
|
@ -629,7 +650,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
||||||
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
|
||||||
texCoordData.push_back(texCoordVec2h);
|
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) {
|
if (fbxMesh.clusters.size() < UINT8_MAX) {
|
||||||
|
@ -641,21 +662,19 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
||||||
assert(fbxMesh.clusterIndices[i] <= UINT8_MAX);
|
assert(fbxMesh.clusterIndices[i] <= UINT8_MAX);
|
||||||
clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]);
|
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 {
|
} 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) {
|
if (normalsSize) {
|
||||||
mesh->addAttribute(gpu::Stream::NORMAL,
|
mesh->addAttribute(gpu::Stream::NORMAL,
|
||||||
model::BufferView(attribBuffer, normalsOffset, normalsSize,
|
model::BufferView(attribBuffer, normalsOffset, normalsAndTangentsSize,
|
||||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
|
normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
|
||||||
}
|
|
||||||
if (tangentsSize) {
|
|
||||||
mesh->addAttribute(gpu::Stream::TANGENT,
|
mesh->addAttribute(gpu::Stream::TANGENT,
|
||||||
model::BufferView(attribBuffer, tangentsOffset, tangentsSize,
|
model::BufferView(attribBuffer, tangentsOffset, normalsAndTangentsSize,
|
||||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
|
normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
|
||||||
}
|
}
|
||||||
if (colorsSize) {
|
if (colorsSize) {
|
||||||
mesh->addAttribute(gpu::Stream::COLOR,
|
mesh->addAttribute(gpu::Stream::COLOR,
|
||||||
|
|
|
@ -72,12 +72,14 @@ void Mesh::evalVertexStream() {
|
||||||
|
|
||||||
int channelNum = 0;
|
int channelNum = 0;
|
||||||
if (hasVertexData()) {
|
if (hasVertexData()) {
|
||||||
_vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum));
|
auto stride = glm::max<gpu::Offset>(_vertexFormat->getChannelStride(channelNum), _vertexBuffer._stride);
|
||||||
|
_vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, stride);
|
||||||
channelNum++;
|
channelNum++;
|
||||||
}
|
}
|
||||||
for (auto attrib : _attributeBuffers) {
|
for (auto attrib : _attributeBuffers) {
|
||||||
BufferView& view = attrib.second;
|
BufferView& view = attrib.second;
|
||||||
_vertexStream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum));
|
auto stride = glm::max<gpu::Offset>(_vertexFormat->getChannelStride(channelNum), view._stride);
|
||||||
|
_vertexStream.addBuffer(view._buffer, view._offset, stride);
|
||||||
channelNum++;
|
channelNum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue