Source code
Revision control
Copy as Markdown
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "assimputils.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/Logger.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/importerdesc.h>
#include <QtCore/qstring.h>
#include <QtCore/QHash>
#include <QtCore/QSet>
QT_BEGIN_NAMESPACE
namespace
{
struct SubsetEntryData {
QString name;
int indexLength;
int indexOffset;
quint32 lightmapWidth;
quint32 lightmapHeight;
QVector<QSSGMesh::Mesh::Lod> lods;
};
struct IntVector4D {
qint32 x = 0;
qint32 y = 0;
qint32 z = 0;
qint32 w = 0;
};
struct VertexAttributeData {
QVector3D position;
QVector3D normal;
QVector3D uv0;
QVector3D uv1;
QVector3D tangent;
QVector3D binormal;
QVector4D color;
};
struct VertexAttributeDataExt {
VertexAttributeData aData;
IntVector4D boneIndexes;
QVector4D boneWeights;
QVector<VertexAttributeData> targetAData;
};
struct VertexDataRequirments {
bool needsPositionData = false;
bool needsNormalData = false;
bool needsTangentData = false;
bool needsVertexColorData = false;
unsigned uv0Components = 0;
unsigned uv1Components = 0;
bool needsUV0Data = false;
bool needsUV1Data = false;
bool needsBones = false;
bool useFloatJointIndices = false;
quint32 numMorphTargets = 0;
// All the target mesh will have the same components
// Target texture coords will be recored as 3 components.
// even if we are using just 2 components now.
bool needsTargetPositionData = false;
bool needsTargetNormalData = false;
bool needsTargetTangentData = false;
bool needsTargetVertexColorData = false;
bool needsTargetUV0Data = false;
bool needsTargetUV1Data = false;
void collectRequirmentsForMesh(const aiMesh *mesh) {
uv0Components = qMax(mesh->mNumUVComponents[0], uv0Components);
uv1Components = qMax(mesh->mNumUVComponents[1], uv1Components);
needsUV0Data |= mesh->HasTextureCoords(0);
needsUV1Data |= mesh->HasTextureCoords(1);
needsPositionData |= mesh->HasPositions();
needsNormalData |= mesh->HasNormals();
needsTangentData |= mesh->HasTangentsAndBitangents();
needsVertexColorData |=mesh->HasVertexColors(0);
needsBones |= mesh->HasBones();
numMorphTargets = mesh->mNumAnimMeshes;
if (numMorphTargets && mesh->mAnimMeshes) {
for (uint i = 0; i < numMorphTargets; ++i) {
auto animMesh = mesh->mAnimMeshes[i];
needsTargetPositionData |= animMesh->HasPositions();
needsTargetNormalData |= animMesh->HasNormals();
needsTargetTangentData |= animMesh->HasTangentsAndBitangents();
needsTargetVertexColorData |= animMesh->HasVertexColors(0);
needsTargetUV0Data |= animMesh->HasTextureCoords(0);
needsTargetUV1Data |= animMesh->HasTextureCoords(1);
}
}
}
};
QVector<VertexAttributeDataExt> getVertexAttributeData(const aiMesh *mesh, const VertexDataRequirments &requirments)
{
QVector<VertexAttributeDataExt> vertexAttributes;
vertexAttributes.resize(mesh->mNumVertices);
// Positions
if (mesh->HasPositions()) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto vertex = mesh->mVertices[index];
vertexAttributes[index].aData.position = QVector3D(vertex.x, vertex.y, vertex.z);
}
}
// Normals
if (mesh->HasNormals()) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto normal = mesh->mNormals[index];
vertexAttributes[index].aData.normal = QVector3D(normal.x, normal.y, normal.z);
}
}
// UV0
if (mesh->HasTextureCoords(0)) {
const auto texCoords = mesh->mTextureCoords[0];
if (requirments.uv0Components == 2) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto uv = texCoords[index];
vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, 0.0f);
}
} else if (requirments.uv0Components == 3) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto uv = texCoords[index];
vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, uv.z);
}
}
}
// UV1
if (mesh->HasTextureCoords(1)) {
const auto texCoords = mesh->mTextureCoords[1];
if (requirments.uv1Components == 2) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto uv = texCoords[index];
vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, 0.0f);
}
} else if (requirments.uv1Components == 3) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto uv = texCoords[index];
vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, uv.z);
}
}
}
// Tangents and Binormals
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto tangent = mesh->mTangents[index];
const auto binormal = mesh->mBitangents[index];
vertexAttributes[index].aData.tangent = QVector3D(tangent.x, tangent.y, tangent.z);
vertexAttributes[index].aData.binormal = QVector3D(binormal.x, binormal.y, binormal.z);
}
}
// Vertex Colors
if (mesh->HasVertexColors(0)) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
const auto color = mesh->mColors[0][index];
vertexAttributes[index].aData.color = QVector4D(color.r, color.g, color.b, color.a);
}
}
// Bones + Weights
if (mesh->HasBones()) {
for (uint i = 0; i < mesh->mNumBones; ++i) {
const uint vId = i;
for (uint j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
quint32 vertexId = mesh->mBones[i]->mWeights[j].mVertexId;
float weight = mesh->mBones[i]->mWeights[j].mWeight;
// skip a bone transform having small weight
if (weight <= 0.01f)
continue;
// if any vertex has more weights than 4, it will be ignored
if (vertexAttributes[vertexId].boneWeights.x() == 0.0f) {
vertexAttributes[vertexId].boneIndexes.x = qint32(vId);
vertexAttributes[vertexId].boneWeights.setX(weight);
} else if (vertexAttributes[vertexId].boneWeights.y() == 0.0f) {
vertexAttributes[vertexId].boneIndexes.y = qint32(vId);
vertexAttributes[vertexId].boneWeights.setY(weight);
} else if (vertexAttributes[vertexId].boneWeights.z() == 0.0f) {
vertexAttributes[vertexId].boneIndexes.z = qint32(vId);
vertexAttributes[vertexId].boneWeights.setZ(weight);
} else if (vertexAttributes[vertexId].boneWeights.w() == 0.0f) {
vertexAttributes[vertexId].boneIndexes.w = qint32(vId);
vertexAttributes[vertexId].boneWeights.setW(weight);
} else {
qWarning("vertexId %d has already 4 weights and index %d's weight %f will be ignored.", vertexId, vId, weight);
}
}
}
}
// Morph Targets
if (requirments.numMorphTargets > 0) {
for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
vertexAttributes[index].targetAData.resize(requirments.numMorphTargets);
for (uint i = 0; i < requirments.numMorphTargets; ++i) {
if (i >= mesh->mNumAnimMeshes)
continue;
auto animMesh = mesh->mAnimMeshes[i];
if (animMesh->HasPositions()) {
const auto vertex = animMesh->mVertices[index];
vertexAttributes[index].targetAData[i].position = QVector3D(vertex.x, vertex.y, vertex.z);
}
if (animMesh->HasNormals()) {
const auto normal = animMesh->mNormals[index];
vertexAttributes[index].targetAData[i].normal = QVector3D(normal.x, normal.y, normal.z);
}
if (animMesh->HasTangentsAndBitangents()) {
const auto tangent = animMesh->mTangents[index];
const auto binormal = animMesh->mBitangents[index];
vertexAttributes[index].targetAData[i].tangent = QVector3D(tangent.x, tangent.y, tangent.z);
vertexAttributes[index].targetAData[i].binormal = QVector3D(binormal.x, binormal.y, binormal.z);
}
if (animMesh->HasTextureCoords(0)) {
const auto texCoords = animMesh->mTextureCoords[0];
const auto uv = texCoords[index];
vertexAttributes[index].targetAData[i].uv0 = QVector3D(uv.x, uv.y, uv.z);
}
if (animMesh->HasTextureCoords(1)) {
const auto texCoords = animMesh->mTextureCoords[1];
const auto uv = texCoords[index];
vertexAttributes[index].targetAData[i].uv1 = QVector3D(uv.x, uv.y, uv.z);
}
if (animMesh->HasVertexColors(0)) {
const auto color = animMesh->mColors[0][index];
vertexAttributes[index].targetAData[i].color = QVector4D(color.r, color.g, color.b, color.a);
}
}
}
}
return vertexAttributes;
}
struct VertexBufferData {
QByteArray positionData;
QByteArray normalData;
QByteArray uv0Data;
QByteArray uv1Data;
QByteArray tangentData;
QByteArray binormalData;
QByteArray vertexColorData;
};
struct VertexBufferDataExt {
VertexBufferData vData;
QByteArray boneIndexData;
QByteArray boneWeightData;
QVector<VertexBufferData> targetVData;
void addVertexAttributeData(const VertexAttributeDataExt &vertex, const VertexDataRequirments &requirments)
{
// Position
if (requirments.needsPositionData)
vData.positionData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.position), sizeof(QVector3D));
// Normal
if (requirments.needsNormalData)
vData.normalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.normal), sizeof(QVector3D));
// UV0
if (requirments.needsUV0Data) {
if (requirments.uv0Components == 2) {
const QVector2D uv(vertex.aData.uv0.x(), vertex.aData.uv0.y());
vData.uv0Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&uv), sizeof(QVector2D));
} else {
vData.uv0Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.uv0), sizeof(QVector3D));
}
}
// UV1
if (requirments.needsUV1Data) {
if (requirments.uv1Components == 2) {
const QVector2D uv(vertex.aData.uv1.x(), vertex.aData.uv1.y());
vData.uv1Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&uv), sizeof(QVector2D));
} else {
vData.uv1Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.uv1), sizeof(QVector3D));
}
}
// Tangent
// Binormal
if (requirments.needsTangentData) {
vData.tangentData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.tangent), sizeof(QVector3D));
vData.binormalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.binormal), sizeof(QVector3D));
}
// Color
if (requirments.needsVertexColorData)
vData.vertexColorData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.color), sizeof(QVector4D));
// Bone Indexes
// Bone Weights
if (requirments.needsBones) {
if (requirments.useFloatJointIndices) {
const QVector4D fBoneIndex(float(vertex.boneIndexes.x), float(vertex.boneIndexes.y), float(vertex.boneIndexes.z), float(vertex.boneIndexes.w));
boneIndexData += QByteArray::fromRawData(reinterpret_cast<const char *>(&fBoneIndex), sizeof(QVector4D));
} else {
boneIndexData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.boneIndexes), sizeof(IntVector4D));
}
boneWeightData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.boneWeights), sizeof(QVector4D));
}
// Morph Targets
for (uint i = 0; i < requirments.numMorphTargets; ++i) {
if (requirments.needsTargetPositionData) {
targetVData[i].positionData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].position), sizeof(QVector3D));
targetVData[i].positionData.append(sizeof(float), '\0');
}
if (requirments.needsTargetNormalData) {
targetVData[i].normalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].normal), sizeof(QVector3D));
targetVData[i].normalData.append(sizeof(float), '\0');
}
if (requirments.needsTargetTangentData) {
targetVData[i].tangentData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].tangent), sizeof(QVector3D));
targetVData[i].tangentData.append(sizeof(float), '\0');
targetVData[i].binormalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].binormal), sizeof(QVector3D));
targetVData[i].binormalData.append(sizeof(float), '\0');
}
if (requirments.needsTargetUV0Data) {
targetVData[i].uv0Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].uv0), sizeof(QVector3D));
targetVData[i].uv0Data.append(sizeof(float), '\0');
}
if (requirments.needsTargetUV1Data) {
targetVData[i].uv1Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].uv1), sizeof(QVector3D));
targetVData[i].uv1Data.append(sizeof(float), '\0');
}
if (requirments.needsTargetVertexColorData) {
targetVData[i].vertexColorData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].color), sizeof(QVector4D));
}
}
}
QVector<QSSGMesh::AssetVertexEntry> createEntries(const VertexDataRequirments &requirments) {
QVector<QSSGMesh::AssetVertexEntry> entries;
if (vData.positionData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getPositionAttrName(),
vData.positionData,
QSSGMesh::Mesh::ComponentType::Float32,
3
});
}
if (vData.normalData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getNormalAttrName(),
vData.normalData,
QSSGMesh::Mesh::ComponentType::Float32,
3
});
}
if (vData.uv0Data.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getUV0AttrName(),
vData.uv0Data,
QSSGMesh::Mesh::ComponentType::Float32,
requirments.uv0Components
});
}
if (vData.uv1Data.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getUV1AttrName(),
vData.uv1Data,
QSSGMesh::Mesh::ComponentType::Float32,
requirments.uv1Components
});
}
if (vData.tangentData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getTexTanAttrName(),
vData.tangentData,
QSSGMesh::Mesh::ComponentType::Float32,
3
});
}
if (vData.binormalData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getTexBinormalAttrName(),
vData.binormalData,
QSSGMesh::Mesh::ComponentType::Float32,
3
});
}
if (vData.vertexColorData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getColorAttrName(),
vData.vertexColorData,
QSSGMesh::Mesh::ComponentType::Float32,
4
});
}
if (boneIndexData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getJointAttrName(),
boneIndexData,
requirments.useFloatJointIndices ? QSSGMesh::Mesh::ComponentType::Float32 : QSSGMesh::Mesh::ComponentType::Int32,
4
});
entries.append({
QSSGMesh::MeshInternal::getWeightAttrName(),
boneWeightData,
QSSGMesh::Mesh::ComponentType::Float32,
4
});
}
for (int i = 0; i < int(requirments.numMorphTargets); ++i) {
if (targetVData[i].positionData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getPositionAttrName(),
targetVData[i].positionData,
QSSGMesh::Mesh::ComponentType::Float32,
3,
i
});
}
if (targetVData[i].normalData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getNormalAttrName(),
targetVData[i].normalData,
QSSGMesh::Mesh::ComponentType::Float32,
3,
i
});
}
if (targetVData[i].tangentData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getTexTanAttrName(),
targetVData[i].tangentData,
QSSGMesh::Mesh::ComponentType::Float32,
3,
i
});
}
if (targetVData[i].binormalData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getTexBinormalAttrName(),
targetVData[i].binormalData,
QSSGMesh::Mesh::ComponentType::Float32,
3,
i
});
}
if (targetVData[i].uv0Data.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getUV0AttrName(),
targetVData[i].uv0Data,
QSSGMesh::Mesh::ComponentType::Float32,
3,
i
});
}
if (targetVData[i].uv1Data.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getUV1AttrName(),
targetVData[i].uv1Data,
QSSGMesh::Mesh::ComponentType::Float32,
3,
i
});
}
if (targetVData[i].vertexColorData.size() > 0) {
entries.append({
QSSGMesh::MeshInternal::getColorAttrName(),
targetVData[i].vertexColorData,
QSSGMesh::Mesh::ComponentType::Float32,
4,
i
});
}
}
return entries;
}
};
QVector<QSSGMesh::MeshLevelOfDetail> generateMeshLevelsOfDetail(QVector<VertexAttributeDataExt> &vertexAttributes,
const QVector<quint32> &indexes,
float normalMergeAngle = 60.0f,
float normalSplitAngle = 25.0f)
{
QVector<QVector3D> positions;
positions.reserve(vertexAttributes.size());
QVector<QVector3D> normals;
normals.reserve(vertexAttributes.size());
for (const auto &vertex : std::as_const(vertexAttributes)) {
positions.append(vertex.aData.position);
normals.append(vertex.aData.normal);
}
QVector<QSSGMesh::MeshVertexSplit> splitVertices;
auto lods = QSSGMesh::generateMeshLevelsOfDetail(positions, normals, indexes, splitVertices, normalMergeAngle, normalSplitAngle);
// Append the new vertices created by normal splitting, in order, so the
// LOD index values pointing past the original vertex count stay valid.
for (const auto &split : std::as_const(splitVertices)) {
auto newVertex = vertexAttributes[split.sourceIndex];
newVertex.aData.normal = split.normal;
vertexAttributes.append(newVertex);
}
return lods;
}
}
QSSGMesh::Mesh AssimpUtils::generateMeshData(const aiScene &scene,
const MeshList &meshes,
bool useFloatJointIndices,
bool generateLevelsOfDetail,
float normalMergeAngle,
float normalSplitAngle,
QString &errorString)
{
Q_UNUSED(errorString);
// All Mesh subsets are stored in the same Vertex Buffer so we need to make
// sure that all attributes from each subset have common data by potentially
// adding placeholder data or doing conversions as necessary.
// So we need to walk through each subset first and see what the requirments are
VertexDataRequirments requirments;
requirments.useFloatJointIndices = useFloatJointIndices;
for (const auto *mesh : meshes)
requirments.collectRequirmentsForMesh(mesh);
// This is the actual data we will pass to the QSSGMesh that will get filled by
// each of the subset meshes
QByteArray indexBufferData;
VertexBufferDataExt vertexBufferData;
QVector<SubsetEntryData> subsetData;
// Since the vertex data of subsets are stored one after the other, the values in
// the index buffer need to be augmented to reflect this offset. baseIndex is used
// to track the new 0 value of a subset by keeping track of the current vertex
// count as each new subset is added
quint32 baseIndex = 0;
// Always use 32-bit indices. Metal has a requirement of 4 byte alignment
// for index buffer offsets, and we cannot risk hitting that.
const QSSGMesh::Mesh::ComponentType indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
for (const auto *mesh : meshes) {
// Get the index values for just this mesh
// The index values should be relative to this meshes
// vertices and will later need to be corrected using
// baseIndex to be relative to our combined vertex data
QVector<quint32> indexes;
indexes.reserve(mesh->mNumFaces * 3);
for (unsigned int faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
const auto face = mesh->mFaces[faceIndex];
// Faces should always have 3 indices
Q_ASSERT(face.mNumIndices == 3);
// Index data for now is relative to the local vertex locations
// This must be corrected for later to be global
indexes.append(quint32(face.mIndices[0]));
indexes.append(quint32(face.mIndices[1]));
indexes.append(quint32(face.mIndices[2]));
}
// Get the Vertex Attribute Data for this mesh
auto vertexAttributes = getVertexAttributeData(mesh, requirments);
// Starting point for index buffer offsets
quint32 baseIndexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
QVector<quint32> lodIndexes;
QVector<QSSGMesh::Mesh::Lod> meshLods;
// Generate Automatic Mesh Levels of Detail
if (generateLevelsOfDetail) {
// Returns a list of lod pairs <distance, lodIndexList> sorted from smallest
// to largest as this is how they are stored in the index buffer. We still need to
// populate meshLods with push_front though because subset lod data is sorted from
// highest detail to lowest
auto lods = generateMeshLevelsOfDetail(vertexAttributes, indexes, normalMergeAngle, normalSplitAngle);
for (const auto &lodEntry : std::as_const(lods)) {
QSSGMesh::Mesh::Lod lod;
lod.offset = baseIndexOffset;
lod.count = lodEntry.indexes.size();
lod.distance = lodEntry.distance;
meshLods.push_front(lod);
baseIndexOffset += lod.count;
// Optimize the vertex cache for this lod level
auto currentLodIndexes = lodEntry.indexes;
QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(), currentLodIndexes.size(), vertexAttributes.size());
lodIndexes += currentLodIndexes;
}
}
// Write the results to the Global Index/Vertex/SubsetData buffers
// Optimize the vertex chache for the original index values
QSSGMesh::optimizeVertexCache(indexes.data(), indexes.data(), indexes.size(), vertexAttributes.size());
// Write Index Buffer Data
QVector<quint32> combinedIndexValues = lodIndexes + indexes;
// Set the absolute index relative to the larger vertex buffer
for (auto &index : combinedIndexValues)
index += baseIndex;
indexBufferData += QByteArray(reinterpret_cast<const char *>(combinedIndexValues.constData()),
combinedIndexValues.size() * QSSGMesh::MeshInternal::byteSizeForComponentType(indexType));
// Index Data is setup such that LOD indexes will come first
// from lowest quality to original
// | LOD3 | LOD2 | LOD1 | Original |
// If there were no LOD levels then indexOffset just points to that here
// baseIndexOffset has already been calculated to be correct at this point
SubsetEntryData subsetEntry;
subsetEntry.indexOffset = baseIndexOffset; // baseIndexOffset will be after lod indexes if available
subsetEntry.indexLength = indexes.size(); // Yes, only original index values, because this is for the non-lod indexes
subsetEntry.name = QString::fromUtf8(scene.mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
subsetEntry.lightmapWidth = 0;
subsetEntry.lightmapHeight = 0;
subsetEntry.lods = meshLods;
subsetData.append(subsetEntry);
// Fill the rest of the vertex data
baseIndex += vertexAttributes.size(); // Final count of vertices added
// Increase target buffers before adding data
vertexBufferData.targetVData.resize(requirments.numMorphTargets);
for (const auto &vertex : std::as_const(vertexAttributes))
vertexBufferData.addVertexAttributeData(vertex, requirments);
}
// Now that we have all the data for the mesh, generate the entries list
QVector<QSSGMesh::AssetVertexEntry> entries = vertexBufferData.createEntries(requirments);
QVector<QSSGMesh::AssetMeshSubset> subsets;
for (const SubsetEntryData &subset : subsetData) {
subsets.append({
subset.name,
quint32(subset.indexLength),
quint32(subset.indexOffset),
0, // the builder will calculate the bounds from the position data
subset.lightmapWidth,
subset.lightmapHeight,
subset.lods
});
}
auto numTargetComponents = [](VertexDataRequirments req) {
int num = 0;
if (req.needsTargetPositionData)
++num;
if (req.needsTargetNormalData)
++num;
if (req.needsTargetTangentData)
num += 2; // tangent and binormal
if (req.needsTargetVertexColorData)
++num;
if (req.needsTargetUV0Data)
++num;
if (req.needsTargetUV1Data)
++num;
return num;
};
QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromAssetData(entries,
indexBufferData,
indexType,
subsets,
requirments.numMorphTargets,
numTargetComponents(requirments));
return mesh;
}
QT_END_NAMESPACE