diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 0805c0198c..66cf791776 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -3,3 +3,8 @@ setup_hifi_library(Concurrent) link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) + +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index e3b1269d37..f504b3f03c 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -11,8 +11,6 @@ #include // need this include so we don't get an error looking for std::isnan -#include - #include #include #include @@ -35,6 +33,9 @@ #include "FBXBaker.h" +#include +#include + FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), @@ -80,7 +81,8 @@ void FBXBaker::bakeSourceCopy() { return; } - // enumerate the textures found in the scene and start a bake for them + // enumerate the models and textures found in the scene and start a bake for them + rewriteAndBakeSceneModels(); rewriteAndBakeSceneTextures(); if (hasErrors()) { @@ -212,7 +214,7 @@ void FBXBaker::importScene() { qCDebug(model_baking) << "Parsing" << _fbxURL; _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); - _geometry = *reader.extractFBXGeometry({}, _fbxURL.toString()); + _geometry = reader.extractFBXGeometry({}, _fbxURL.toString()); _textureContent = reader._textureContent; } @@ -278,12 +280,136 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF return urlToTexture; } +void FBXBaker::rewriteAndBakeSceneModels() { + unsigned int meshIndex = 0; + for (FBXNode& rootChild : _rootNode.children) { + if (rootChild.name == "Objects") { + for (FBXNode& objectChild : rootChild.children) { + if (objectChild.name == "Geometry") { + + // TODO Pull this out of _geometry instead so we don't have to reprocess it + auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); + auto mesh = extractedMesh.mesh; + + Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); + Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); + Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); + + int64_t numTriangles { 0 }; + for (auto& part : mesh.parts) { + Q_ASSERT(part.quadTrianglesIndices.size() % 3 == 0); + Q_ASSERT(part.triangleIndices.size() % 3 == 0); + + numTriangles += part.quadTrianglesIndices.size() / 3; + numTriangles += part.triangleIndices.size() / 3; + } + + draco::TriangleSoupMeshBuilder meshBuilder; + + meshBuilder.Start(numTriangles); + + bool hasNormals { mesh.normals.size() > 0 }; + bool hasColors { mesh.colors.size() > 0 }; + bool hasTexCoords { mesh.texCoords.size() > 0 }; + //bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + + int normalsAttributeID { -1 }; + int colorsAttributeID { -1 }; + int texCoordsAttributeID { -1 }; + //int texCoords1AttributeID { -1 }; + + const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, + 3, draco::DT_FLOAT32); + + const int faceMaterialAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, + 1, draco::DT_INT64); + + if (hasNormals) { + normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, + 3, draco::DT_FLOAT32); + } + if (hasColors) { + colorsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::COLOR, + 3, draco::DT_FLOAT32); + } + if (hasTexCoords) { + texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, + 2, draco::DT_FLOAT32); + } + + + for (auto& part : mesh.parts) { + //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); + //Q_ASSERT(part.triangleIndices % 3 == 0); + + int64_t materialID = 0; + + for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { + auto idx0 = part.quadTrianglesIndices[i]; + auto idx1 = part.quadTrianglesIndices[i + 1]; + auto idx2 = part.quadTrianglesIndices[i + 2]; + + auto face = draco::FaceIndex(i / 3); + + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); + + meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, + &mesh.vertices[idx0], &mesh.vertices[idx1], + &mesh.vertices[idx2]); + + if (hasNormals) { + meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, + &mesh.normals[idx0],&mesh.normals[idx1], + &mesh.normals[idx2]); + } + if (hasColors) { + meshBuilder.SetAttributeValuesForFace(colorsAttributeID, face, + &mesh.colors[idx0], &mesh.colors[idx1], + &mesh.colors[idx2]); + } + if (hasTexCoords) { + meshBuilder.SetAttributeValuesForFace(texCoordsAttributeID, face, + &mesh.texCoords[idx0], &mesh.texCoords[idx1], + &mesh.texCoords[idx2]); + } + } + } + + auto dracoMesh = meshBuilder.Finalize(); + + draco::Encoder encoder; + draco::EncoderBuffer buffer; + encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); + + FBXNode dracoMeshNode; + dracoMeshNode.name = "DracoMesh"; + auto value = QVariant::fromValue(QByteArray(buffer.data(), buffer.size())); + dracoMeshNode.properties.append(value); + + + QFile file("C:/Users/huffm/encodedFBX/" + this->_fbxURL.fileName() + "-" + QString::number(meshIndex) + ".drc"); + if (file.open(QIODevice::WriteOnly)) { + file.write(buffer.data(), buffer.size()); + file.close(); + } else { + qWarning() << "Failed to write to: " << file.fileName(); + + } + + objectChild.children.push_back(dracoMeshNode); + } + } + } + } +} + void FBXBaker::rewriteAndBakeSceneTextures() { using namespace image::TextureUsage; QHash textureTypes; // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID - for (const auto& material : _geometry.materials) { + for (const auto& material : _geometry->materials) { if (material.normalTexture.isBumpmap) { textureTypes[material.normalTexture.id] = BUMP_TEXTURE; } else { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 2dc31828cb..26c1ff2dcc 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -58,6 +58,7 @@ private: void loadSourceFBX(); void importScene(); + void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); void exportScene(); void removeEmbeddedMediaFolder(); @@ -73,7 +74,7 @@ private: QUrl _fbxURL; FBXNode _rootNode; - FBXGeometry _geometry; + FBXGeometry* _geometry; QHash _textureContent; QString _bakedFBXFilePath; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fc45e9a792..9f5fad7d66 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -32,6 +32,13 @@ static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; 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_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES; +static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1; + + class FBXNode; using FBXNodeList = QList; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index a600ac6bab..8a5e394da9 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -110,7 +110,7 @@ public: FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); - ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); + static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); QHash meshes; static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 4e153dfe3a..14f1c09b75 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -304,8 +304,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn if (subdata.name == "Materials") { materials = getIntVector(subdata); } else if (subdata.name == "MappingInformationType") { - if (subdata.properties.at(0) == BY_POLYGON) + if (subdata.properties.at(0) == BY_POLYGON) { isMaterialPerPolygon = true; + } } else { isMaterialPerPolygon = false; } diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 9d5d6c1aad..1022c204c5 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -6,11 +6,6 @@ link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() -add_dependency_external_projects(draco) -find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) - if (WIN32) package_libraries_for_deployment() endif ()