mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:49:27 +02:00
Merge pull request #11369 from huffman/feat/bake-skinning-support
Add animation support for draco encoded FBXs
This commit is contained in:
commit
1f961885cc
7 changed files with 127 additions and 43 deletions
|
@ -292,13 +292,27 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF
|
||||||
|
|
||||||
void FBXBaker::rewriteAndBakeSceneModels() {
|
void FBXBaker::rewriteAndBakeSceneModels() {
|
||||||
unsigned int meshIndex = 0;
|
unsigned int meshIndex = 0;
|
||||||
|
bool hasDeformers { false };
|
||||||
|
for (FBXNode& rootChild : _rootNode.children) {
|
||||||
|
if (rootChild.name == "Objects") {
|
||||||
|
for (FBXNode& objectChild : rootChild.children) {
|
||||||
|
if (objectChild.name == "Deformer") {
|
||||||
|
hasDeformers = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasDeformers) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (FBXNode& rootChild : _rootNode.children) {
|
for (FBXNode& rootChild : _rootNode.children) {
|
||||||
if (rootChild.name == "Objects") {
|
if (rootChild.name == "Objects") {
|
||||||
for (FBXNode& objectChild : rootChild.children) {
|
for (FBXNode& objectChild : rootChild.children) {
|
||||||
if (objectChild.name == "Geometry") {
|
if (objectChild.name == "Geometry") {
|
||||||
|
|
||||||
// TODO Pull this out of _geometry instead so we don't have to reprocess it
|
// TODO Pull this out of _geometry instead so we don't have to reprocess it
|
||||||
auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex);
|
auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false);
|
||||||
auto& mesh = extractedMesh.mesh;
|
auto& mesh = extractedMesh.mesh;
|
||||||
|
|
||||||
if (mesh.wasCompressed) {
|
if (mesh.wasCompressed) {
|
||||||
|
@ -334,15 +348,22 @@ void FBXBaker::rewriteAndBakeSceneModels() {
|
||||||
bool hasTexCoords { mesh.texCoords.size() > 0 };
|
bool hasTexCoords { mesh.texCoords.size() > 0 };
|
||||||
bool hasTexCoords1 { mesh.texCoords1.size() > 0 };
|
bool hasTexCoords1 { mesh.texCoords1.size() > 0 };
|
||||||
bool hasPerFaceMaterials { mesh.parts.size() > 1 };
|
bool hasPerFaceMaterials { mesh.parts.size() > 1 };
|
||||||
|
bool needsOriginalIndices { hasDeformers };
|
||||||
|
|
||||||
int normalsAttributeID { -1 };
|
int normalsAttributeID { -1 };
|
||||||
int colorsAttributeID { -1 };
|
int colorsAttributeID { -1 };
|
||||||
int texCoordsAttributeID { -1 };
|
int texCoordsAttributeID { -1 };
|
||||||
int texCoords1AttributeID { -1 };
|
int texCoords1AttributeID { -1 };
|
||||||
int faceMaterialAttributeID { -1 };
|
int faceMaterialAttributeID { -1 };
|
||||||
|
int originalIndexAttributeID { -1 };
|
||||||
|
|
||||||
const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION,
|
const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION,
|
||||||
3, draco::DT_FLOAT32);
|
3, draco::DT_FLOAT32);
|
||||||
|
if (needsOriginalIndices) {
|
||||||
|
originalIndexAttributeID = meshBuilder.AddAttribute(
|
||||||
|
(draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX,
|
||||||
|
1, draco::DT_INT32);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasNormals) {
|
if (hasNormals) {
|
||||||
normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL,
|
normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL,
|
||||||
|
@ -372,14 +393,14 @@ void FBXBaker::rewriteAndBakeSceneModels() {
|
||||||
draco::FaceIndex face;
|
draco::FaceIndex face;
|
||||||
for (auto& part : mesh.parts) {
|
for (auto& part : mesh.parts) {
|
||||||
const auto& matTex = extractedMesh.partMaterialTextures[partIndex];
|
const auto& matTex = extractedMesh.partMaterialTextures[partIndex];
|
||||||
|
uint16_t materialID = matTex.first;
|
||||||
|
|
||||||
auto addFace = [&](QVector<int>& indices, int index, draco::FaceIndex face) {
|
auto addFace = [&](QVector<int>& indices, int index, draco::FaceIndex face) {
|
||||||
auto idx0 = indices[index];
|
int32_t idx0 = indices[index];
|
||||||
auto idx1 = indices[index + 1];
|
int32_t idx1 = indices[index + 1];
|
||||||
auto idx2 = indices[index + 2];
|
int32_t idx2 = indices[index + 2];
|
||||||
|
|
||||||
if (hasPerFaceMaterials) {
|
if (hasPerFaceMaterials) {
|
||||||
uint16_t materialID = matTex.first;
|
|
||||||
meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID);
|
meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +408,12 @@ void FBXBaker::rewriteAndBakeSceneModels() {
|
||||||
&mesh.vertices[idx0], &mesh.vertices[idx1],
|
&mesh.vertices[idx0], &mesh.vertices[idx1],
|
||||||
&mesh.vertices[idx2]);
|
&mesh.vertices[idx2]);
|
||||||
|
|
||||||
|
if (needsOriginalIndices) {
|
||||||
|
meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face,
|
||||||
|
&mesh.originalIndices[idx0],
|
||||||
|
&mesh.originalIndices[idx1],
|
||||||
|
&mesh.originalIndices[idx2]);
|
||||||
|
}
|
||||||
if (hasNormals) {
|
if (hasNormals) {
|
||||||
meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face,
|
meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face,
|
||||||
&mesh.normals[idx0], &mesh.normals[idx1],
|
&mesh.normals[idx0], &mesh.normals[idx1],
|
||||||
|
@ -437,6 +464,10 @@ void FBXBaker::rewriteAndBakeSceneModels() {
|
||||||
dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1);
|
dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needsOriginalIndices) {
|
||||||
|
dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
draco::Encoder encoder;
|
draco::Encoder encoder;
|
||||||
|
|
||||||
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14);
|
encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14);
|
||||||
|
|
|
@ -30,14 +30,16 @@
|
||||||
|
|
||||||
static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary ";
|
static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary ";
|
||||||
static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23;
|
static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23;
|
||||||
|
static const quint32 FBX_VERSION_2015 = 7400;
|
||||||
static const quint32 FBX_VERSION_2016 = 7500;
|
static const quint32 FBX_VERSION_2016 = 7500;
|
||||||
|
|
||||||
|
|
||||||
// TODO Convert to GeometryAttribute type
|
|
||||||
static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000;
|
static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000;
|
||||||
static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES;
|
static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES;
|
||||||
static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1;
|
static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1;
|
||||||
|
static const int DRACO_ATTRIBUTE_ORIGINAL_INDEX = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 2;
|
||||||
|
|
||||||
|
static const int32_t FBX_PROPERTY_UNCOMPRESSED_FLAG = 0;
|
||||||
|
static const int32_t FBX_PROPERTY_COMPRESSED_FLAG = 1;
|
||||||
|
|
||||||
class FBXNode;
|
class FBXNode;
|
||||||
using FBXNodeList = QList<FBXNode>;
|
using FBXNodeList = QList<FBXNode>;
|
||||||
|
@ -223,6 +225,7 @@ public:
|
||||||
QVector<glm::vec2> texCoords1;
|
QVector<glm::vec2> texCoords1;
|
||||||
QVector<uint16_t> clusterIndices;
|
QVector<uint16_t> clusterIndices;
|
||||||
QVector<uint8_t> clusterWeights;
|
QVector<uint8_t> clusterWeights;
|
||||||
|
QVector<int32_t> originalIndices;
|
||||||
|
|
||||||
QVector<FBXCluster> clusters;
|
QVector<FBXCluster> clusters;
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ public:
|
||||||
|
|
||||||
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url);
|
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url);
|
||||||
|
|
||||||
static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex);
|
static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true);
|
||||||
QHash<QString, ExtractedMesh> meshes;
|
QHash<QString, ExtractedMesh> meshes;
|
||||||
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
|
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
void appendIndex(MeshData& data, QVector<int>& indices, int index, bool deduplicate) {
|
||||||
if (index >= data.polygonIndices.size()) {
|
if (index >= data.polygonIndices.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -156,12 +156,13 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<Vertex, int>::const_iterator it = data.indices.find(vertex);
|
QHash<Vertex, int>::const_iterator it = data.indices.find(vertex);
|
||||||
if (it == data.indices.constEnd()) {
|
if (!deduplicate || it == data.indices.constEnd()) {
|
||||||
int newIndex = data.extracted.mesh.vertices.size();
|
int newIndex = data.extracted.mesh.vertices.size();
|
||||||
indices.append(newIndex);
|
indices.append(newIndex);
|
||||||
data.indices.insert(vertex, newIndex);
|
data.indices.insert(vertex, newIndex);
|
||||||
data.extracted.newIndices.insert(vertexIndex, newIndex);
|
data.extracted.newIndices.insert(vertexIndex, newIndex);
|
||||||
data.extracted.mesh.vertices.append(position);
|
data.extracted.mesh.vertices.append(position);
|
||||||
|
data.extracted.mesh.originalIndices.append(vertexIndex);
|
||||||
data.extracted.mesh.normals.append(normal);
|
data.extracted.mesh.normals.append(normal);
|
||||||
data.extracted.mesh.texCoords.append(vertex.texCoord);
|
data.extracted.mesh.texCoords.append(vertex.texCoord);
|
||||||
if (hasColors) {
|
if (hasColors) {
|
||||||
|
@ -176,7 +177,7 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIndex) {
|
ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate) {
|
||||||
MeshData data;
|
MeshData data;
|
||||||
data.extracted.mesh.meshIndex = meshIndex++;
|
data.extracted.mesh.meshIndex = meshIndex++;
|
||||||
|
|
||||||
|
@ -355,7 +356,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD);
|
auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD);
|
||||||
auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1);
|
auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1);
|
||||||
auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR);
|
auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR);
|
||||||
auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID);
|
auto materialIDAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID);
|
||||||
|
auto originalIndexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_ORIGINAL_INDEX);
|
||||||
|
|
||||||
// setup extracted mesh data structures given number of points
|
// setup extracted mesh data structures given number of points
|
||||||
auto numVertices = dracoMesh->num_points();
|
auto numVertices = dracoMesh->num_points();
|
||||||
|
@ -424,7 +426,17 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
reinterpret_cast<float*>(&data.extracted.mesh.colors[i]));
|
reinterpret_cast<float*>(&data.extracted.mesh.colors[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
data.extracted.newIndices.insert(i, i);
|
if (originalIndexAttribute) {
|
||||||
|
auto mappedIndex = originalIndexAttribute->mapped_index(vertexIndex);
|
||||||
|
|
||||||
|
int32_t originalIndex;
|
||||||
|
|
||||||
|
originalIndexAttribute->ConvertValue<int32_t, 1>(mappedIndex, &originalIndex);
|
||||||
|
|
||||||
|
data.extracted.newIndices.insert(originalIndex, i);
|
||||||
|
} else {
|
||||||
|
data.extracted.newIndices.insert(i, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < dracoMesh->num_faces(); ++i) {
|
for (int i = 0; i < dracoMesh->num_faces(); ++i) {
|
||||||
|
@ -434,11 +446,11 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
|
|
||||||
uint16_t materialID { 0 };
|
uint16_t materialID { 0 };
|
||||||
|
|
||||||
if (matTexAttribute) {
|
if (materialIDAttribute) {
|
||||||
// read material ID and texture ID mappings into materials and texture vectors
|
// read material ID and texture ID mappings into materials and texture vectors
|
||||||
auto mappedIndex = matTexAttribute->mapped_index(firstCorner);
|
auto mappedIndex = materialIDAttribute->mapped_index(firstCorner);
|
||||||
|
|
||||||
matTexAttribute->ConvertValue<uint16_t, 1>(mappedIndex, &materialID);
|
materialIDAttribute->ConvertValue<uint16_t, 1>(mappedIndex, &materialID);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<int, int> materialTexture(materialID, 0);
|
QPair<int, int> materialTexture(materialID, 0);
|
||||||
|
@ -488,10 +500,10 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1];
|
FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1];
|
||||||
|
|
||||||
if (endIndex - beginIndex == 4) {
|
if (endIndex - beginIndex == 4) {
|
||||||
appendIndex(data, part.quadIndices, beginIndex++);
|
appendIndex(data, part.quadIndices, beginIndex++, deduplicate);
|
||||||
appendIndex(data, part.quadIndices, beginIndex++);
|
appendIndex(data, part.quadIndices, beginIndex++, deduplicate);
|
||||||
appendIndex(data, part.quadIndices, beginIndex++);
|
appendIndex(data, part.quadIndices, beginIndex++, deduplicate);
|
||||||
appendIndex(data, part.quadIndices, beginIndex++);
|
appendIndex(data, part.quadIndices, beginIndex++, deduplicate);
|
||||||
|
|
||||||
int quadStartIndex = part.quadIndices.size() - 4;
|
int quadStartIndex = part.quadIndices.size() - 4;
|
||||||
int i0 = part.quadIndices[quadStartIndex + 0];
|
int i0 = part.quadIndices[quadStartIndex + 0];
|
||||||
|
@ -516,9 +528,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (int nextIndex = beginIndex + 1;; ) {
|
for (int nextIndex = beginIndex + 1;; ) {
|
||||||
appendIndex(data, part.triangleIndices, beginIndex);
|
appendIndex(data, part.triangleIndices, beginIndex, deduplicate);
|
||||||
appendIndex(data, part.triangleIndices, nextIndex++);
|
appendIndex(data, part.triangleIndices, nextIndex++, deduplicate);
|
||||||
appendIndex(data, part.triangleIndices, nextIndex);
|
appendIndex(data, part.triangleIndices, nextIndex, deduplicate);
|
||||||
if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) {
|
if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,9 +48,8 @@ QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||||
QVector<T> values;
|
QVector<T> values;
|
||||||
if ((int)QSysInfo::ByteOrder == (int)in.byteOrder()) {
|
if ((int)QSysInfo::ByteOrder == (int)in.byteOrder()) {
|
||||||
values.resize(arrayLength);
|
values.resize(arrayLength);
|
||||||
const unsigned int DEFLATE_ENCODING = 1;
|
|
||||||
QByteArray arrayData;
|
QByteArray arrayData;
|
||||||
if (encoding == DEFLATE_ENCODING) {
|
if (encoding == FBX_PROPERTY_COMPRESSED_FLAG) {
|
||||||
// preface encoded data with uncompressed length
|
// preface encoded data with uncompressed length
|
||||||
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||||
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||||
|
@ -72,8 +71,7 @@ QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
values.reserve(arrayLength);
|
values.reserve(arrayLength);
|
||||||
const unsigned int DEFLATE_ENCODING = 1;
|
if (encoding == FBX_PROPERTY_COMPRESSED_FLAG) {
|
||||||
if (encoding == DEFLATE_ENCODING) {
|
|
||||||
// preface encoded data with uncompressed length
|
// preface encoded data with uncompressed length
|
||||||
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||||
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||||
|
|
|
@ -13,14 +13,45 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
template <typename T>
|
#ifdef USE_FBX_2016_FORMAT
|
||||||
void writeVector(QDataStream& out, char ch, QVector<T> list) {
|
using FBXEndOffset = int64_t;
|
||||||
out.device()->write(&ch, 1);
|
using FBXPropertyCount = uint64_t;
|
||||||
out << (int32_t)list.length();
|
using FBXListLength = uint64_t;
|
||||||
out << (int32_t)0;
|
#else
|
||||||
out << (int32_t)0;
|
using FBXEndOffset = int32_t;
|
||||||
|
using FBXPropertyCount = uint32_t;
|
||||||
|
using FBXListLength = uint32_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
out.writeBytes(reinterpret_cast<const char*>(list.constData()), list.length() * sizeof(T));
|
template <typename T>
|
||||||
|
void writeVector(QDataStream& out, char ch, QVector<T> vec) {
|
||||||
|
// Minimum number of bytes to consider compressing
|
||||||
|
const int ATTEMPT_COMPRESSION_THRESHOLD_BYTES = 2000;
|
||||||
|
|
||||||
|
out.device()->write(&ch, 1);
|
||||||
|
out << (int32_t)vec.length();
|
||||||
|
|
||||||
|
auto data { QByteArray::fromRawData((const char*)vec.constData(), vec.length() * sizeof(T)) };
|
||||||
|
|
||||||
|
if (data.size() >= ATTEMPT_COMPRESSION_THRESHOLD_BYTES) {
|
||||||
|
auto compressedDataWithLength { qCompress(data) };
|
||||||
|
|
||||||
|
// qCompress packs a length uint32 at the beginning of the buffer, but the FBX format
|
||||||
|
// does not expect it. This removes it.
|
||||||
|
auto compressedData = QByteArray::fromRawData(
|
||||||
|
compressedDataWithLength.constData() + sizeof(uint32_t), compressedDataWithLength.size() - sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (compressedData.size() < data.size()) {
|
||||||
|
out << FBX_PROPERTY_COMPRESSED_FLAG;
|
||||||
|
out << (int32_t)compressedData.size();
|
||||||
|
out.writeRawData(compressedData.constData(), compressedData.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out << FBX_PROPERTY_UNCOMPRESSED_FLAG;
|
||||||
|
out << (int32_t)0;
|
||||||
|
out.writeRawData(data.constData(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +65,11 @@ QByteArray FBXWriter::encodeFBX(const FBXNode& root) {
|
||||||
auto bytes = QByteArray(FBX_HEADER_BYTES_BEFORE_VERSION - FBX_BINARY_PROLOG.size(), '\0');
|
auto bytes = QByteArray(FBX_HEADER_BYTES_BEFORE_VERSION - FBX_BINARY_PROLOG.size(), '\0');
|
||||||
out.writeRawData(bytes, bytes.size());
|
out.writeRawData(bytes, bytes.size());
|
||||||
|
|
||||||
|
#ifdef USE_FBX_2016_FORMAT
|
||||||
out << FBX_VERSION_2016;
|
out << FBX_VERSION_2016;
|
||||||
|
#else
|
||||||
|
out << FBX_VERSION_2015;
|
||||||
|
#endif
|
||||||
|
|
||||||
for (auto& child : root.children) {
|
for (auto& child : root.children) {
|
||||||
encodeNode(out, child);
|
encodeNode(out, child);
|
||||||
|
@ -45,19 +80,17 @@ QByteArray FBXWriter::encodeFBX(const FBXNode& root) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) {
|
void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) {
|
||||||
qDebug() << "Encoding " << node.name;
|
|
||||||
|
|
||||||
auto device = out.device();
|
auto device = out.device();
|
||||||
auto nodeStartPos = device->pos();
|
auto nodeStartPos = device->pos();
|
||||||
|
|
||||||
// endOffset (temporary, updated later)
|
// endOffset (temporary, updated later)
|
||||||
out << (qint64)0;
|
out << (FBXEndOffset)0;
|
||||||
|
|
||||||
// Property count
|
// Property count
|
||||||
out << (quint64)node.properties.size();
|
out << (FBXPropertyCount)node.properties.size();
|
||||||
|
|
||||||
// Property list length (temporary, updated later)
|
// Property list length (temporary, updated later)
|
||||||
out << (quint64)0;
|
out << (FBXListLength)0;
|
||||||
|
|
||||||
out << (quint8)node.name.size();
|
out << (quint8)node.name.size();
|
||||||
out.writeRawData(node.name, node.name.size());
|
out.writeRawData(node.name, node.name.size());
|
||||||
|
@ -70,8 +103,8 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) {
|
||||||
|
|
||||||
// Go back and write property list length
|
// Go back and write property list length
|
||||||
auto nodePropertiesEndPos = device->pos();
|
auto nodePropertiesEndPos = device->pos();
|
||||||
device->seek(nodeStartPos + sizeof(qint64) + sizeof(quint64));
|
device->seek(nodeStartPos + sizeof(FBXEndOffset) + sizeof(FBXPropertyCount));
|
||||||
out << (quint64)(nodePropertiesEndPos - nodePropertiesStartPos);
|
out << (FBXListLength)(nodePropertiesEndPos - nodePropertiesStartPos);
|
||||||
|
|
||||||
device->seek(nodePropertiesEndPos);
|
device->seek(nodePropertiesEndPos);
|
||||||
|
|
||||||
|
@ -86,7 +119,7 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) {
|
||||||
// Go back and write actual endOffset
|
// Go back and write actual endOffset
|
||||||
auto nodeEndPos = device->pos();
|
auto nodeEndPos = device->pos();
|
||||||
device->seek(nodeStartPos);
|
device->seek(nodeStartPos);
|
||||||
out << (qint64)(nodeEndPos);
|
out << (FBXEndOffset)(nodeEndPos);
|
||||||
|
|
||||||
device->seek(nodeEndPos);
|
device->seek(nodeEndPos);
|
||||||
}
|
}
|
||||||
|
@ -94,8 +127,12 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) {
|
||||||
void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
|
void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
|
||||||
auto type = prop.userType();
|
auto type = prop.userType();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QVariant::Type::Bool:
|
case QMetaType::Short:
|
||||||
|
out.device()->write("Y", 1);
|
||||||
|
out << prop.value<int16_t>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QVariant::Type::Bool:
|
||||||
out.device()->write("C", 1);
|
out.device()->write("C", 1);
|
||||||
out << prop.toBool();
|
out << prop.toBool();
|
||||||
break;
|
break;
|
||||||
|
@ -215,6 +252,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
|
||||||
writeVector(out, 'b', prop.value<QVector<bool>>());
|
writeVector(out, 'b', prop.value<QVector<bool>>());
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop;
|
qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop;
|
||||||
|
throw("Unsupported property type in FBXWriter::encodeNode: " + QString::number(type) + " " + prop.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
|
|
||||||
|
//#define USE_FBX_2016_FORMAT
|
||||||
|
|
||||||
class FBXWriter {
|
class FBXWriter {
|
||||||
public:
|
public:
|
||||||
static QByteArray encodeFBX(const FBXNode& root);
|
static QByteArray encodeFBX(const FBXNode& root);
|
||||||
|
|
Loading…
Reference in a new issue