diff --git a/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp b/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp index ebd0a4cc3..a70dafd2d 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp +++ b/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp @@ -893,26 +893,24 @@ TextureNode RepoBSONFactory::makeTextureNode( const uint32_t &byteCount, const uint32_t &width, const uint32_t &height, + const std::vector& parentIDs, const int &apiLevel) { RepoBSONBuilder builder; repo::lib::RepoUUID uniqueID = repo::lib::RepoUUID::createUUID(); std::unordered_map>> binMapping; - auto defaults = appendDefaults(REPO_NODE_TYPE_TEXTURE, apiLevel, uniqueID, name); + auto defaults = appendDefaults(REPO_NODE_TYPE_TEXTURE, apiLevel, uniqueID, name, parentIDs); builder.appendElements(defaults); - // + //-------------------------------------------------------------------------- // Width - // builder.append(REPO_LABEL_WIDTH, width); - // + //-------------------------------------------------------------------------- // Height - // builder.append(REPO_LABEL_HEIGHT, height); - // + //-------------------------------------------------------------------------- // Format TODO: replace format with MIME Type? - // if (!name.empty()) { boost::filesystem::path file{ name }; @@ -920,10 +918,9 @@ TextureNode RepoBSONFactory::makeTextureNode( if (!ext.empty()) builder.append(REPO_NODE_LABEL_EXTENSION, ext.substr(1, ext.size())); } - // - // Data - // + //-------------------------------------------------------------------------- + // Data if (data && byteCount) { std::string bName = uniqueID.toString() + "_data"; //inclusion of this binary exceeds the maximum, store separately diff --git a/bouncer/src/repo/core/model/bson/repo_bson_factory.h b/bouncer/src/repo/core/model/bson/repo_bson_factory.h index 2f1948470..0ca348dd1 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson_factory.h +++ b/bouncer/src/repo/core/model/bson/repo_bson_factory.h @@ -369,6 +369,7 @@ namespace repo { const uint32_t &byteCount, const uint32_t &width, const uint32_t &height, + const std::vector& parentIDs = std::vector(), const int &apiLevel = REPO_NODE_API_LEVEL_1); /** diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/geometry_collector.cpp b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/geometry_collector.cpp index 2992cd0bc..95e8761bc 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/geometry_collector.cpp +++ b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/geometry_collector.cpp @@ -15,7 +15,6 @@ * along with this program. If not, see . */ - #include "geometry_collector.h" #include @@ -25,10 +24,8 @@ using namespace repo::manipulator::modelconvertor::odaHelper; GeometryCollector::GeometryCollector() { - } - GeometryCollector::~GeometryCollector() { } @@ -64,12 +61,11 @@ repo::core::model::TransformationNode GeometryCollector::createRootNode() } void GeometryCollector::setCurrentMaterial(const repo_material_t &material, bool missingTexture) { - auto checkSum = material.checksum(); - if(idxToMat.find(checkSum) == idxToMat.end()) { - idxToMat[checkSum] = { - repo::core::model::RepoBSONFactory::makeMaterialNode(material), - createTextureNode(material.texturePath) + if (idxToMat.find(checkSum) == idxToMat.end()) { + idxToMat[checkSum] = { + repo::core::model::RepoBSONFactory::makeMaterialNode(material), + createTextureNode(material.texturePath) }; if (missingTexture) @@ -91,11 +87,9 @@ mesh_data_t GeometryCollector::createMeshEntry() { entry.layerName = nextLayer.empty() ? "UnknownLayer" : nextLayer; return entry; - } void GeometryCollector::startMeshEntry() { - nextMeshName = nextMeshName.empty() ? std::to_string(std::time(0)) : nextMeshName; nextGroupName = nextGroupName.empty() ? nextMeshName : nextGroupName; nextLayer = nextLayer.empty() ? nextMeshName : nextLayer; @@ -112,19 +106,18 @@ void GeometryCollector::startMeshEntry() { meshData[nextGroupName][nextLayer][currMat] = createMeshEntry(); } currentEntry = &meshData[nextGroupName][nextLayer][currMat]; - } void GeometryCollector::stopMeshEntry() { - if(currentEntry) - currentEntry->vToVIndex.clear(); + if (currentEntry) + currentEntry->vToVIndex.clear(); nextMeshName = ""; } void GeometryCollector::addFace( - const std::vector &vertices, + const std::vector &vertices, const repo::lib::RepoVector3D64& normal, - const std::vector& uvCoords) + const std::vector& uvCoords) { if (!meshData.size()) startMeshEntry(); @@ -140,7 +133,7 @@ void GeometryCollector::addFace( } if (faceHasUV && uvCoords.size() != vertices.size()) { - repoError << "Vertices size["<< vertices.size() << "] and UV size ["<< uvCoords.size() <<"] mismatched!"; + repoError << "Vertices size[" << vertices.size() << "] and UV size [" << uvCoords.size() << "] mismatched!"; exit(-1); } @@ -151,9 +144,9 @@ void GeometryCollector::addFace( if (currentEntry->vToVIndex.find(v) == currentEntry->vToVIndex.end()) { //..insert new vertex along with index and normal currentEntry->vToVIndex.insert( - std::pair>( - v, + v, std::pair(currentEntry->rawVertices.size(), normal)) ); vertIdx = currentEntry->rawVertices.size(); @@ -163,13 +156,13 @@ void GeometryCollector::addFace( currentEntry->uvCoords.push_back(uvCoords[i]); if (currentEntry->boundingBox.size()) { - currentEntry->boundingBox[0][0] = currentEntry->boundingBox[0][0] > v.x ? (float) v.x : currentEntry->boundingBox[0][0]; - currentEntry->boundingBox[0][1] = currentEntry->boundingBox[0][1] > v.y ? (float) v.y : currentEntry->boundingBox[0][1]; - currentEntry->boundingBox[0][2] = currentEntry->boundingBox[0][2] > v.z ? (float) v.z : currentEntry->boundingBox[0][2]; + currentEntry->boundingBox[0][0] = currentEntry->boundingBox[0][0] > v.x ? (float)v.x : currentEntry->boundingBox[0][0]; + currentEntry->boundingBox[0][1] = currentEntry->boundingBox[0][1] > v.y ? (float)v.y : currentEntry->boundingBox[0][1]; + currentEntry->boundingBox[0][2] = currentEntry->boundingBox[0][2] > v.z ? (float)v.z : currentEntry->boundingBox[0][2]; - currentEntry->boundingBox[1][0] = currentEntry->boundingBox[1][0] < v.x ? (float) v.x : currentEntry->boundingBox[1][0]; - currentEntry->boundingBox[1][1] = currentEntry->boundingBox[1][1] < v.y ? (float) v.y : currentEntry->boundingBox[1][1]; - currentEntry->boundingBox[1][2] = currentEntry->boundingBox[1][2] < v.z ? (float) v.z : currentEntry->boundingBox[1][2]; + currentEntry->boundingBox[1][0] = currentEntry->boundingBox[1][0] < v.x ? (float)v.x : currentEntry->boundingBox[1][0]; + currentEntry->boundingBox[1][1] = currentEntry->boundingBox[1][1] < v.y ? (float)v.y : currentEntry->boundingBox[1][1]; + currentEntry->boundingBox[1][2] = currentEntry->boundingBox[1][2] < v.z ? (float)v.z : currentEntry->boundingBox[1][2]; } else { currentEntry->boundingBox.push_back({ (float)v.x, (float)v.y, (float)v.z }); @@ -205,7 +198,7 @@ void GeometryCollector::addFace( if (!normalFound) { - //.. in case the normal for this point doesn't exist yet - we should add it + //.. in case the normal for this point doesn't exist yet - we should add it //.. as duplicated and add new normal and index currentEntry->vToVIndex.insert( std::pairfaces.push_back(face); } - repo::core::model::RepoNodeSet GeometryCollector::getMeshNodes(const repo::core::model::TransformationNode& root) { repo::core::model::RepoNodeSet res; auto dummyCol = std::vector(); @@ -242,13 +234,12 @@ repo::core::model::RepoNodeSet GeometryCollector::getMeshNodes(const repo::core: for (const auto &meshMatEntry : meshLayerEntry.second) { if (!meshMatEntry.second.rawVertices.size()) continue; - auto uvChannels = meshMatEntry.second.uvCoords.size() ? + auto uvChannels = meshMatEntry.second.uvCoords.size() ? std::vector>{meshMatEntry.second.uvCoords} : std::vector>(); - if (layerToTrans.find(meshLayerEntry.first) == layerToTrans.end()) { - layerToTrans[meshLayerEntry.first] = createTransNode(layerIDToName[meshLayerEntry.first], meshLayerEntry.first, rootId); + layerToTrans[meshLayerEntry.first] = createTransNode(layerIDToName[meshLayerEntry.first], meshLayerEntry.first, rootId); transNodes.insert(layerToTrans[meshLayerEntry.first]); } @@ -279,7 +270,6 @@ repo::core::model::RepoNodeSet GeometryCollector::getMeshNodes(const repo::core: { layerToTrans[meshLayerEntry.first]->getSharedID() } ); - if (idToMeta.find(meshGroupEntry.first) != idToMeta.end()) { metaNodes.insert(createMetaNode(meshGroupEntry.first, { meshNode.getSharedID() }, idToMeta[meshGroupEntry.first])); } @@ -303,14 +293,14 @@ repo::core::model::MetadataNode* GeometryCollector::createMetaNode( const repo::lib::RepoUUID &parentId, const std::unordered_map &metaValues ) { - return new repo::core::model::MetadataNode(repo::core::model::RepoBSONFactory::makeMetaDataNode(metaValues, name, {parentId})); + return new repo::core::model::MetadataNode(repo::core::model::RepoBSONFactory::makeMetaDataNode(metaValues, name, { parentId })); } repo::core::model::TransformationNode* GeometryCollector::createTransNode( const std::string &name, const std::string &id, - const repo::lib::RepoUUID &parentId) -{ + const repo::lib::RepoUUID &parentId) +{ auto transNode = new repo::core::model::TransformationNode(repo::core::model::RepoBSONFactory::makeTransformationNode(repo::lib::RepoMatrix(), name, { parentId })); if (idToMeta.find(id) != idToMeta.end()) { metaNodes.insert(createMetaNode(name, transNode->getSharedID(), idToMeta[id])); @@ -329,7 +319,6 @@ bool GeometryCollector::hasMissingTextures() } void GeometryCollector::getMaterialAndTextureNodes(repo::core::model::RepoNodeSet& materials, repo::core::model::RepoNodeSet& textures) { - materials.clear(); textures.clear(); @@ -373,8 +362,7 @@ repo::core::model::TextureNode GeometryCollector::createTextureNode(const std::s (const char*)memblock, size, 1, - 0, - REPO_NODE_API_LEVEL_1 + 0 ); delete[] memblock; diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.cpp b/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.cpp index d7d1814a3..7f29346f9 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.cpp +++ b/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.cpp @@ -15,10 +15,6 @@ * along with this program. If not, see . */ -/** -* Allows Import/Export functionality into/output Repo world using IFCOpenShell -*/ - #include "repo_model_import_3drepo.h" #include #include @@ -45,7 +41,10 @@ RepoModelImport::~RepoModelImport() { } -repo::core::model::MetadataNode* RepoModelImport::createMetadataNode(const ptree &metaTree, const std::string &parentName, const repo::lib::RepoUUID &parentID) +repo::core::model::MetadataNode* RepoModelImport::createMetadataNode( + const ptree &metaTree, + const std::string &parentName, + const repo::lib::RepoUUID &parentID) { std::vector keys, values; @@ -87,9 +86,10 @@ repo::core::model::MetadataNode* RepoModelImport::createMetadataNode(const ptree return metaNode; } -repo::core::model::MaterialNode *RepoModelImport::parseMaterial(const boost::property_tree::ptree &matTree) +void RepoModelImport::parseMaterial(const boost::property_tree::ptree &matTree) { repo_material_t repo_material; + int textureId = -1; if (matTree.find("diffuse") != matTree.not_found()) repo_material.diffuse = as_vector(matTree, "diffuse"); @@ -121,6 +121,11 @@ repo::core::model::MaterialNode *RepoModelImport::parseMaterial(const boost::pro else repo_material.shininess = 0.0f; + if (matTree.find("texture") != matTree.not_found()) + { + textureId = matTree.get("texture"); + } + repo_material.shininessStrength = 0.25f; repo_material.isWireframe = false; repo_material.isTwoSided = false; @@ -132,7 +137,53 @@ repo::core::model::MaterialNode *RepoModelImport::parseMaterial(const boost::pro materials.insert(materialNode); matNodeList.push_back(materialNode); - return materialNode; + if (textureId >= 0) + { + textureIdToParents[textureId].push_back(materialNode->getSharedID()); + } +} + +void RepoModelImport::parseTexture( + const boost::property_tree::ptree& textureTree, + char * const dataBuffer) +{ + bool fileNameOk = textureTree.find(REPO_TXTR_FNAME) != textureTree.not_found(); + bool byteCountOK = textureTree.find(REPO_TXTR_NUM_BYTES) != textureTree.not_found(); + bool widthOk = textureTree.find(REPO_TXTR_WIDTH) != textureTree.not_found(); + bool heightOk = textureTree.find(REPO_TXTR_HEIGHT) != textureTree.not_found(); + bool idOk = textureTree.find(REPO_TXTR_ID) != textureTree.not_found(); + + if (!byteCountOK || + !widthOk || + !heightOk || + !idOk) + { + repoError << "Required texture field missing. Skipping this texture."; + missingTextures = true; + return; + } + + std::string name = fileNameOk ? textureTree.get_child(REPO_TXTR_FNAME).data() : ""; + uint32_t byteCount = textureTree.get(REPO_TXTR_NUM_BYTES); + uint32_t width = textureTree.get(REPO_TXTR_WIDTH); + uint32_t height = textureTree.get(REPO_TXTR_HEIGHT); + uint32_t id = textureTree.get(REPO_TXTR_ID); + + std::vector DataStartEnd = as_vector(textureTree, REPO_TXTR_IMG_BYTES); + + char* data = &dataBuffer[DataStartEnd[0]]; + + repo::core::model::TextureNode* textureNode = + new repo::core::model::TextureNode( + repo::core::model::RepoBSONFactory::makeTextureNode( + name, + data, + byteCount, + width, + height, + textureIdToParents[id])); + + textures.insert(textureNode); } RepoModelImport::mesh_data_t RepoModelImport::createMeshRecord( @@ -141,16 +192,14 @@ RepoModelImport::mesh_data_t RepoModelImport::createMeshRecord( const repo::lib::RepoUUID &parentID, const repo::lib::RepoMatrix &trans) { - repo::core::model::MaterialNode *materialNode; - int materialID = -1; auto numIndices = mesh.get("numIndices"); auto numVertices = mesh.get("numVertices"); - //Avoid using assimp objects everywhere -> converting assimp objects into repo structs std::vector vertices; std::vector normals; + std::vector> uvChannels; std::vector faces; std::vector > boundingBox; @@ -179,24 +228,19 @@ RepoModelImport::mesh_data_t RepoModelImport::createMeshRecord( if (props->first == REPO_IMPORT_MATERIAL) { materialID = props->second.get_value(); - materialNode = matNodeList[materialID]; } - - if (props->first == REPO_IMPORT_VERTICES || props->first == REPO_IMPORT_NORMALS) + else if (props->first == REPO_IMPORT_VERTICES || props->first == REPO_IMPORT_NORMALS) { std::vector startEnd = as_vector(mesh, props->first); - double *tmpVerticesDouble = (double *)(geomBuf + startEnd[0]); - float *tmpVerticesSingle = (float *)(geomBuf + startEnd[0]); + double *tmpVerticesDouble = (double *)(dataBuffer + startEnd[0]); + float *tmpVerticesSingle = (float *)(dataBuffer + startEnd[0]); for (int i = 0; i < numVertices; i++) { if (props->first == REPO_IMPORT_VERTICES) { repo::lib::RepoVector3D64 tmpVec; - if (is32Bit) - tmpVec = { tmpVerticesSingle[i * 3], tmpVerticesSingle[i * 3 + 1], tmpVerticesSingle[i * 3 + 2] }; - else - tmpVec = { tmpVerticesDouble[i * 3] , tmpVerticesDouble[i * 3 + 1] , tmpVerticesDouble[i * 3 + 2] }; + tmpVec = { tmpVerticesDouble[i * 3] , tmpVerticesDouble[i * 3 + 1] , tmpVerticesDouble[i * 3 + 2] }; if (needTransform) tmpVec = trans * tmpVec; if (minBBox.size()) { @@ -223,12 +267,23 @@ RepoModelImport::mesh_data_t RepoModelImport::createMeshRecord( } } } - - if (props->first == REPO_IMPORT_INDICES) + else if (props->first == REPO_IMPORT_UV) + { + std::vector startEnd = as_vector(mesh, props->first); + float* tmpUVs = (float*)(dataBuffer + startEnd[0]); + std::vector uvChannelVector; + for (int i = 0; i < numVertices; i++) + { + repo::lib::RepoVector2D tmpUVVec = repo::lib::RepoVector2D(tmpUVs[i * 2], tmpUVs[i * 2 + 1]); + uvChannelVector.push_back(tmpUVVec); + } + uvChannels.push_back(uvChannelVector); + } + else if (props->first == REPO_IMPORT_INDICES) { std::vector startEnd = as_vector(mesh, REPO_IMPORT_INDICES); - uint32_t *tmpIndices = (uint32_t*)(geomBuf + startEnd[0]); + uint32_t *tmpIndices = (uint32_t*)(dataBuffer + startEnd[0]); for (int i = 0; i < numIndices; i += 3) { @@ -261,7 +316,7 @@ RepoModelImport::mesh_data_t RepoModelImport::createMeshRecord( matParents[materialID].push_back(sharedID); } - mesh_data_t result = { vertices, normals, faces, boundingBox, parentID, sharedID }; + mesh_data_t result = { vertices, normals, uvChannels, faces, boundingBox, parentID, sharedID }; return result; } @@ -278,7 +333,7 @@ void RepoModelImport::createObject(const ptree& tree) } repo::lib::RepoUUID parentSharedID = node_map[myParent]->getSharedID(); - repo::lib::RepoMatrix parentTransform = trans_map[myParent]; + repo::lib::RepoMatrix parentTransform = trans_matrix_map[myParent]; std::vector parentIDs; parentIDs.push_back(parentSharedID); @@ -292,7 +347,7 @@ void RepoModelImport::createObject(const ptree& tree) transMat = repo::lib::RepoMatrix(as_vector(tree, "transformation")); } - trans_map.push_back(parentTransform * transMat); + trans_matrix_map.push_back(parentTransform * transMat); repo::core::model::TransformationNode * transNode = new repo::core::model::TransformationNode( @@ -314,7 +369,7 @@ void RepoModelImport::createObject(const ptree& tree) if (props->first == REPO_IMPORT_GEOMETRY) { - auto mesh = createMeshRecord(props->second, transName, transID, trans_map.back()); + auto mesh = createMeshRecord(props->second, transName, transID, trans_matrix_map.back()); metaParentIDs.push_back(mesh.sharedID); meshEntries.push_back(mesh); } @@ -349,26 +404,23 @@ bool RepoModelImport::importModel(std::string filePath, uint8_t &err) repoInfo << "IMPORT [" << fileName << "]"; repoInfo << "=== IMPORTING MODEL WITH REPO IMPORTER ==="; + // Reading in file finCompressed = new std::ifstream(filePath, std::ios_base::in | std::ios::binary); - if (finCompressed) { inbuf = new boost::iostreams::filtering_streambuf(); - #ifndef REPO_BOOST_NO_GZIP inbuf->push(boost::iostreams::gzip_decompressor()); #else repoWarning << "Gzip is not compiled into Boost library, .bim imports may not work as intended"; #endif inbuf->push(*finCompressed); - fin = new std::istream(inbuf); - char fileVersion[REPO_VERSION_LENGTH + 1] = { 0 }; + // Check the BIM file format version + char fileVersion[REPO_VERSION_LENGTH + 1] = { 0 }; fin->read(fileVersion, REPO_VERSION_LENGTH); - std::string incomingVersion = fileVersion; - if (supportedFileVersions.find(incomingVersion) == supportedFileVersions.end()) { repoError << "Unsupported BIM file version: " << fileVersion; @@ -376,73 +428,95 @@ bool RepoModelImport::importModel(std::string filePath, uint8_t &err) return false; } - is32Bit = REPO_V1 == incomingVersion; - - repoInfo << "Loading BIM file [VERSION: " << incomingVersion << "] 32 bit? : " << is32Bit; + // Loading file metadata + repoInfo << "Loading BIM file [VERSION: " << incomingVersion << "]"; size_t metaSize = REPO_VERSION_LENGTH + sizeof(fileMeta); - // Size of metadata at start - fin->read((char*)&file_meta, sizeof(fileMeta)); - - repoInfo << "META size: " << metaSize; - repoInfo << "SIZE: header = " << file_meta.headerSize << " bytes, geometry = " << file_meta.geometrySize << " bytes."; - repoInfo << "SIZE ARRAY: location = " << file_meta.sizesStart << " bytes, size = " << file_meta.sizesSize << " bytes."; - repoInfo << "MAT ARRAY: location = " << file_meta.matStart << " bytes, size = " << file_meta.matSize << " bytes."; - repoInfo << "Number of parts to process : " << file_meta.numChildren; - - if (file_meta.matStart > file_meta.sizesStart) + if (incomingVersion == REPO_V3) { - skipAheadInFile(file_meta.sizesStart - metaSize); - sizes = as_vector(getNextJSON(file_meta.sizesSize)); - - skipAheadInFile(file_meta.matStart - (file_meta.sizesStart + file_meta.sizesSize)); - - boost::property_tree::ptree materials = getNextJSON(file_meta.matSize); - for (const auto& item : materials) { - parseMaterial(item.second); + fin->read((char*)&file_meta, REPO_V3_FILEMETA_BYTE_LEN); + } + else + { + fin->read((char*)&file_meta, REPO_V2_FILEMETA_BYTE_LEN); + } + repoInfo << std::left << std::setw(30) << "File meta size: " << metaSize; + repoInfo << std::left << std::setw(30) << "JSON size: " << file_meta.jsonSize << " bytes"; + repoInfo << std::left << std::setw(30) << "Data buffer size: " << file_meta.dataSize << " bytes"; + repoInfo << std::left << std::setw(30) << "\"sizes\" array start location: " << file_meta.sizesStart << " bytes"; + repoInfo << std::left << std::setw(30) << "\"sizes\" array size: " << file_meta.sizesSize << " bytes"; + repoInfo << std::left << std::setw(30) << "\"materials\" array location: " << file_meta.matStart << " bytes"; + repoInfo << std::left << std::setw(30) << "\"materials\" array size: " << file_meta.matSize << " bytes"; + repoInfo << std::left << std::setw(30) << "\"textures\" array location: " << file_meta.textureSize << " bytes"; + repoInfo << std::left << std::setw(30) << "\"textures\" array size: " << file_meta.textureStart << " bytes"; + repoInfo << std::left << std::setw(30) << "Number of parts to process:" << file_meta.numChildren; + + // Load full JSON tree + boost::property_tree::ptree jsonRoot = getNextJSON(file_meta.jsonSize); + + // Load binary data + repoInfo << "Reading data buffer"; + dataBuffer = new char[file_meta.dataSize]; + fin->read(dataBuffer, file_meta.dataSize); + + // Loading in required JSON nodes + boost::optional materialsRoot = jsonRoot.get_child_optional("materials"); + if (materialsRoot) + { + for (ptree::value_type element : materialsRoot.get()) + { + parseMaterial(element.second); } matParents.resize(materials.size()); - - skipAheadInFile(file_meta.headerSize + metaSize - (file_meta.matStart + file_meta.matSize)); + repoInfo << "Loaded: " << materials.size() << " materials"; } - else { - skipAheadInFile(file_meta.matStart - metaSize); - - boost::property_tree::ptree materialsArr = getNextJSON(file_meta.matSize); - for (const auto& item : materialsArr) { - parseMaterial(item.second); + else + { + repoError << "File " << fileName << " does not have a \"materials\" node"; + err = REPOERR_MODEL_FILE_READ; + return false; + } + boost::optional sizesRoot = jsonRoot.get_child_optional("sizes"); + if (sizesRoot) + { + sizes = as_vector(sizesRoot.get()); + } + else + { + repoError << "File " << fileName << " does not have a \"sizes\" node"; + err = REPOERR_MODEL_FILE_READ; + return false; + } + boost::optional texturesRoot = jsonRoot.get_child_optional("textures"); + if (texturesRoot) + { + for (ptree::value_type element : texturesRoot.get()) + { + parseTexture(element.second, dataBuffer); + } + repoInfo << "Loaded: " << textures.size() << " textures"; + int maxTextureId = textureIdToParents.rbegin()->first; + if (maxTextureId > (textures.size() - 1)) + { + repoError << "A material is referencing a missing texture"; + missingTextures = true; } - - matParents.resize(materials.size()); - - skipAheadInFile(file_meta.sizesStart - (file_meta.matStart + file_meta.matSize)); - sizes = as_vector(getNextJSON(file_meta.sizesSize)); - - skipAheadInFile(file_meta.headerSize + metaSize - (file_meta.sizesStart + file_meta.sizesSize)); } - repoInfo << "Reading geometry buffer"; - - geomBuf = new char[file_meta.geometrySize]; - fin->read(geomBuf, file_meta.geometrySize); - + // Clean up finCompressed->close(); delete finCompressed; - finCompressed = new std::ifstream(filePath, std::ios_base::in | std::ios::binary); - delete fin; delete inbuf; + // Reloading file, skipping to the root start. No seekg for GZIPped files. inbuf = new boost::iostreams::filtering_streambuf(); #ifndef REPO_BOOST_NO_GZIP inbuf->push(boost::iostreams::gzip_decompressor()); #endif inbuf->push(*finCompressed); - fin = new std::istream(inbuf); - - // Skip to the root start. No seekg for GZIPped files. skipAheadInFile(sizes[0]); return true; @@ -477,49 +551,41 @@ repo::core::model::RepoScene* RepoModelImport::generateRepoScene(uint8_t &errMsg // Process root node boost::property_tree::ptree root = getNextJSON(sizes[1]); std::string rootName = root.get("name", ""); - boost::optional< ptree& > rootBBOX = root.get_child_optional("bbox"); - if (!rootBBOX) { repoError << "No root bounding box specified."; errMsg = REPOERR_MODEL_FILE_READ; return nullptr; } - boost::optional< ptree& > transMatTree = root.get_child_optional("transformation"); repo::lib::RepoMatrix transMat; - if (transMatTree) { transMat = repo::lib::RepoMatrix(as_vector(root, "transformation")); } - repo::core::model::TransformationNode *rootNode = new repo::core::model::TransformationNode( repo::core::model::RepoBSONFactory::makeTransformationNode(repo::lib::RepoMatrix(), rootName, std::vector())); - node_map.push_back(rootNode); - trans_map.push_back(transMat); + trans_matrix_map.push_back(transMat); transformations.insert(rootNode); + // Process children of root node char comma; - for (long i = 0; i < file_meta.numChildren; i++) { if (i % 500 == 0 || i == file_meta.numChildren - 1) { repoInfo << "Importing " << i << " of " << file_meta.numChildren << " JSON nodes"; } - fin->read(&comma, 1); boost::property_tree::ptree jsonTree = getNextJSON(sizes[i + 2]); createObject(jsonTree); } - materials.clear(); - - repoInfo << "Attaching materials to parents"; // Attach all the parents to the materials + repoInfo << "Attaching materials to parents"; + materials.clear(); for (int i = 0; i < matNodeList.size(); i++) { repo::core::model::MaterialNode *tmpMaterial = new repo::core::model::MaterialNode(); @@ -527,22 +593,25 @@ repo::core::model::RepoScene* RepoModelImport::generateRepoScene(uint8_t &errMsg materials.insert(tmpMaterial); } + // Preparing reference files std::vector fileVect; if (!orgFile.empty()) + { fileVect.push_back(orgFile); + } + // Processing meshes repo::core::model::RepoNodeSet meshes; - if (!offset.size()) offset = { 0, 0, 0 }; - + if (!offset.size()) { offset = { 0, 0, 0 }; } for (const auto &entry : meshEntries) { std::vector vertices; std::vector> boundingBox; - + // Offsetting all the verts by the world offset to reduce the magnitude + // of their values so they can be cast to floats (widely used in 3D libs) for (const auto &v : entry.rawVertices) { repo::lib::RepoVector3D v32 = { (float)(v.x - offset[0]), (float)(v.y - offset[1]), (float)(v.z - offset[2]) }; vertices.push_back(v32); } - for (const auto &bbArr : entry.boundingBox) { std::vector bb; for (int i = 0; i < bbArr.size(); ++i) { @@ -550,22 +619,34 @@ repo::core::model::RepoScene* RepoModelImport::generateRepoScene(uint8_t &errMsg } boundingBox.push_back(bb); } - - auto mesh = repo::core::model::RepoBSONFactory::makeMeshNode(vertices, entry.faces, entry.normals, boundingBox, { entry.parent }); + auto mesh = repo::core::model::RepoBSONFactory::makeMeshNode( + vertices, + entry.faces, + entry.normals, + boundingBox, + entry.uvChannels, + std::vector(), + std::vector>(), + std::string(), + { entry.parent }); repo::core::model::RepoBSONBuilder builder; builder.append(REPO_NODE_LABEL_SHARED_ID, entry.sharedID); auto changes = builder.obj(); meshes.insert(new repo::core::model::MeshNode(mesh.cloneAndAddFields(&changes, false))); } + // Generate scene repo::core::model::RepoScene * scenePtr = new repo::core::model::RepoScene(fileVect, cameras, meshes, materials, metadata, textures, transformations); - scenePtr->setWorldOffset(offset); + if (missingTextures) + { + errMsg = REPOERR_LOAD_SCENE_MISSING_TEXTURE; + scenePtr->setMissingTexture(); + } - delete[] geomBuf; - + // Cleanup + delete[] dataBuffer; finCompressed->close(); - delete fin; delete inbuf; delete finCompressed; diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.h b/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.h index 88718e743..736519943 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.h +++ b/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_3drepo.h @@ -34,6 +34,7 @@ #include "../../../core/model/bson/repo_node_mesh.h" #include "../../../core/model/bson/repo_node_metadata.h" #include "../../../core/model/bson/repo_node_transformation.h" +#include "../../../core/model/bson/repo_node_texture.h" namespace repo { namespace manipulator { @@ -44,85 +45,124 @@ namespace repo { const char REPO_IMPORT_TYPE_BOOL = 'B'; const char REPO_IMPORT_TYPE_DATETIME = 'T'; - const std::string REPO_IMPORT_METADATA = "metadata"; - const std::string REPO_IMPORT_GEOMETRY = "geometry"; - const std::string REPO_IMPORT_MATERIAL = "material"; + // Node JSON fields + const std::string REPO_IMPORT_METADATA = "metadata"; + const std::string REPO_IMPORT_GEOMETRY = "geometry"; + const std::string REPO_IMPORT_MATERIAL = "material"; + const std::string REPO_IMPORT_VERTICES = "vertices"; + const std::string REPO_IMPORT_UV = "uv"; + const std::string REPO_IMPORT_NORMALS = "normals"; + const std::string REPO_IMPORT_INDICES = "indices"; + const std::string REPO_IMPORT_BBOX = "bbox"; + + // Texture JSON fields + const std::string REPO_TXTR_FNAME = "filename"; + const std::string REPO_TXTR_NUM_BYTES = "numImageBytes"; + const std::string REPO_TXTR_WIDTH = "width"; + const std::string REPO_TXTR_HEIGHT = "height"; + const std::string REPO_TXTR_ID = "id"; + const std::string REPO_TXTR_IMG_BYTES = "imageBytes"; - const std::string REPO_IMPORT_VERTICES = "vertices"; - const std::string REPO_IMPORT_NORMALS = "normals"; - const std::string REPO_IMPORT_INDICES = "indices"; - const std::string REPO_IMPORT_BBOX = "bbox"; - - const std::string REPO_V1 = "BIM001"; - const std::string REPO_V2 = "BIM002"; - - const std::set supportedFileVersions = { REPO_V1, REPO_V2 }; const static int REPO_VERSION_LENGTH = 6; class RepoModelImport : public AbstractModelImport { private: - std::vector node_map; - std::vector trans_map; - bool is32Bit = false; - - void createObject(const boost::property_tree::ptree& tree); + const std::string REPO_V1 = "BIM001"; + const std::string REPO_V2 = "BIM002"; + const std::string REPO_V3 = "BIM003"; + const std::set supportedFileVersions = + { + REPO_V2, + REPO_V3 + }; - char *geomBuf; - std::ifstream *finCompressed; - boost::iostreams::filtering_streambuf *inbuf; - std::istream *fin; + const int REPO_V1_FILEMETA_BYTE_LEN = 56; + const int REPO_V2_FILEMETA_BYTE_LEN = REPO_V1_FILEMETA_BYTE_LEN; + const int REPO_V3_FILEMETA_BYTE_LEN = 72; typedef struct { - int64_t headerSize; - int64_t geometrySize; - int64_t sizesStart; - int64_t sizesSize; - int64_t matStart; - int64_t matSize; - int64_t numChildren; + int64_t jsonSize = -1; //!< Size of the entire JSON segment + int64_t dataSize = -1; //!< Size of the entire binary footer segment + int64_t sizesStart = -1; //!< Starting location of the JSON sizes array from the top of file in bytes + int64_t sizesSize = -1; //!< Length of the JSON sizes array in bytes + int64_t matStart = -1; //!< Starting location of the JSON materials array from the top of the file in bytes + int64_t matSize = -1; //!< Size of the JSON materials array in bytes + int64_t numChildren = -1; //!< Number of children of the root node + int64_t textureStart = -1; //!< Starting location of the JSON textures array from the top of the file in bytes + int64_t textureSize = -1; //!< Size of the JSON textures array in bytes } fileMeta; - struct mesh_data_t { + struct mesh_data_t + { std::vector rawVertices; std::vector normals; + std::vector> uvChannels; std::vector faces; std::vector> boundingBox; repo::lib::RepoUUID parent; repo::lib::RepoUUID sharedID; }; - fileMeta file_meta; - - std::vector sizes; - - repo::core::model::MaterialNode* parseMaterial(const boost::property_tree::ptree &pt); - + void parseMaterial(const boost::property_tree::ptree& pt); + void parseTexture(const boost::property_tree::ptree& textureTree, char * dataBuffer); repo::core::model::MetadataNode* createMetadataNode(const boost::property_tree::ptree &metadata, const std::string &parentName, const repo::lib::RepoUUID &parentID); mesh_data_t createMeshRecord(const boost::property_tree::ptree &geometry, const std::string &parentName, const repo::lib::RepoUUID &parentID, const repo::lib::RepoMatrix &trans); + + /** + * @brief Creates a property tree from the current + * position in the fine input stream (fin) + * @param number of chars to read + * @return boost poperty tree + */ boost::property_tree::ptree getNextJSON(long jsonSize); void skipAheadInFile(long amount); - std::vector matNodeList; - std::vector> matParents; - - std::vector meshEntries; - repo::core::model::RepoNodeSet cameras; //!< Cameras - repo::core::model::RepoNodeSet materials; //!< Materials - repo::core::model::RepoNodeSet metadata; //!< Metadata - repo::core::model::RepoNodeSet transformations; //!< Transformations - repo::core::model::RepoNodeSet textures; + /** + * @brief Creates relevant nodes for given child + * of the root node in the BIM file + * Directly updates: + * trans_matrix_map + * node_map + * transformations + * @param tree + */ + void createObject(const boost::property_tree::ptree& tree); + // File handling variables std::string orgFile; + std::ifstream *finCompressed; + boost::iostreams::filtering_streambuf *inbuf; + std::istream *fin; + // Source file meta data storage + fileMeta file_meta; + std::vector sizes; //!< Sizes of the nodes component, used for navigation. + char *dataBuffer; + bool missingTextures = false; + + // Intermediary variables used to keep track of node hierarchy + std::vector node_map; //!< List of all transform nodes in order of decoding + std::vector trans_matrix_map; //!< List of all transformation matrices in same order as node_map + std::vector matNodeList; //!< Stores a list of materials + std::vector> matParents; //!< Stores the UUIDs of all parents of a given material node in the same order matNodeList + std::map> textureIdToParents; //!< Maps a texture to the UUID of all the parents that reference it + std::vector meshEntries; + + // Variables directly used to instantiate the RepoScene + repo::core::model::RepoNodeSet cameras; + repo::core::model::RepoNodeSet materials; + repo::core::model::RepoNodeSet metadata; + repo::core::model::RepoNodeSet transformations; + repo::core::model::RepoNodeSet textures; std::vector offset; public: /** - * Create IFCModelImport with specific settings + * Create RepoModelImport with specific settings * NOTE: The destructor will destroy the settings object referenced * in this object! * @param settings @@ -145,9 +185,10 @@ namespace repo { virtual repo::core::model::RepoScene* generateRepoScene(uint8_t &errMsg); /** - * Import model from a given file + * Import model from a given file. + * Loads material nodes. * This does not generate the Repo Scene Graph - * Use getRepoScene() to generate a Repo Scene Graph. + * Use generateRepoScene() to generate a Repo Scene Graph. * @param path to the file * @param error message if failed * @return returns true upon success diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_assimp.cpp b/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_assimp.cpp index b25d99f4e..99be048d0 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_assimp.cpp +++ b/bouncer/src/repo/manipulator/modelconvertor/import/repo_model_import_assimp.cpp @@ -826,8 +826,7 @@ repo::core::model::RepoScene* AssimpModelImport::convertAiSceneToRepoScene() (char*)texture->pcData, size, texture->mWidth, - texture->mHeight, - REPO_NODE_API_LEVEL_1)); + texture->mHeight)); free(memblock); } @@ -859,8 +858,7 @@ repo::core::model::RepoScene* AssimpModelImport::convertAiSceneToRepoScene() memblock, size, size, - 0, - REPO_NODE_API_LEVEL_1)); + 0)); if (memblock)delete[] memblock; } diff --git a/test/src/system/st_3drepobouncerClient.cpp b/test/src/system/st_3drepobouncerClient.cpp index bee75d28a..16a7d8fc8 100644 --- a/test/src/system/st_3drepobouncerClient.cpp +++ b/test/src/system/st_3drepobouncerClient.cpp @@ -123,6 +123,26 @@ static int runProcess( #endif } + +static int testUpload ( + std::string mongoDbName, + std::string projectName, + std::string fileName + ) +{ + std::string uploadCmd = produceUploadArgs( + mongoDbName, + projectName, + getDataPath(fileName)); + + int errCode = runProcess(uploadCmd); + ; + repoInfo << "Error code from bouncer client: " << errCode + << ", " << (int)errCode; + return errCode; +}; + + TEST(RepoClientTest, UploadTestInvalidDBConn) { //this ensures we can run processes @@ -134,6 +154,7 @@ TEST(RepoClientTest, UploadTestInvalidDBConn) EXPECT_EQ((int)REPOERR_AUTH_FAILED, runProcess(failToConnect)); EXPECT_FALSE(projectExists(db, "failConn")); } + TEST(RepoClientTest, UploadTestBadDBAuth) { //this ensures we can run processes @@ -239,6 +260,39 @@ TEST(RepoClientTest, UploadTestMissingNodes) EXPECT_TRUE(projectExists(db, "missing")); } +TEST(RepoClientTest, UploadTestBIM) +{ + ////this ensures we can run processes + ASSERT_TRUE(system(nullptr)); + + std::string mongoDbName = "stUpload"; + + //OK BIM003 file with textures + std::string okBim3PrjName = "okBIM3Test"; + EXPECT_EQ(REPOERR_OK, testUpload(mongoDbName, okBim3PrjName, "RepoModelImport/BrickWalls_bim3.bim")); + EXPECT_TRUE(projectExists(mongoDbName, okBim3PrjName)); + + // OK BIM002 file with no textures + std::string okBim2PrjName = "okBIM2Test"; + EXPECT_EQ(REPOERR_OK, testUpload(mongoDbName, okBim2PrjName, "RepoModelImport/cube_bim2_navis_2021_repo_4.6.1.bim")); + EXPECT_TRUE(projectExists(mongoDbName, okBim2PrjName)); + + // Spoofed BIM001 + std::string spoofedBim1PrjName = "spoofedBIM1Test"; + EXPECT_EQ(REPOERR_UNSUPPORTED_BIM_VERSION, testUpload(mongoDbName, spoofedBim1PrjName, "RepoModelImport/cube_bim1_spoofed.bim")); + EXPECT_FALSE(projectExists(mongoDbName, spoofedBim1PrjName)); + + // Corrupt BIM003 - Missing numImageBytes field + std::string corrTxtrBim3PrjName = "corruptedTextureBIM3Test"; + EXPECT_EQ(REPOERR_LOAD_SCENE_MISSING_TEXTURE, testUpload(mongoDbName, corrTxtrBim3PrjName, "RepoModelImport/BrickWalls_bim3_CorruptedTextureField.bim")); + EXPECT_TRUE(projectExists(mongoDbName, corrTxtrBim3PrjName)); + + // Corrupt BIM003 - Material references a texture id not included in file + std::string corrMatBim3PrjName = "corruptedMaterialBIM3Test"; + EXPECT_EQ(REPOERR_LOAD_SCENE_MISSING_NODES, testUpload(mongoDbName, corrMatBim3PrjName, "RepoModelImport/BrickWalls_bim3_MissingTexture.bim")); + EXPECT_TRUE(projectExists(mongoDbName, corrMatBim3PrjName)); +} + TEST(RepoClientTest, UploadTestIFC) { //this ensures we can run processes diff --git a/test/src/unit/repo/manipulator/modelconvertor/import/CMakeLists.txt b/test/src/unit/repo/manipulator/modelconvertor/import/CMakeLists.txt index 05064bde6..994c67d73 100644 --- a/test/src/unit/repo/manipulator/modelconvertor/import/CMakeLists.txt +++ b/test/src/unit/repo/manipulator/modelconvertor/import/CMakeLists.txt @@ -6,6 +6,7 @@ set(TEST_SOURCES ${TEST_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/ut_repo_model_import_3drepo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ut_repo_model_import_synchro.cpp CACHE STRING "TEST_SOURCES" FORCE) diff --git a/test/src/unit/repo/manipulator/modelconvertor/import/ut_repo_model_import_3drepo.cpp b/test/src/unit/repo/manipulator/modelconvertor/import/ut_repo_model_import_3drepo.cpp new file mode 100644 index 000000000..5cb58f187 --- /dev/null +++ b/test/src/unit/repo/manipulator/modelconvertor/import/ut_repo_model_import_3drepo.cpp @@ -0,0 +1,194 @@ +/** +* Copyright (C) 2020 3D Repo Ltd +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +#include +#include +#include +#include "../../../../repo_test_utils.h" +#include "../../../../repo_test_database_info.h" +#include "boost/filesystem.hpp" +#include "../../bouncer/src/repo/error_codes.h" + +using namespace repo::manipulator::modelconvertor; + +static bool testBIMFileImport( + std::string bimFilePath, + int expMaterialsCount, + int expTexturesCount, + int expMeshesCount, + bool checkUvs = false, + int expImpModelErrCode = REPOERR_OK, + int expGenSceneErrCode = REPOERR_OK, + bool generateScene = true, + std::string debugMeshDataFilePath = "", + std::string textureFilesDumpPath = "") +{ + ModelImportConfig config; + uint8_t impModelErrCode = 0; + auto modelConvertor = std::unique_ptr(new RepoModelImport(config)); + modelConvertor->importModel(bimFilePath, impModelErrCode); + if (impModelErrCode != expImpModelErrCode) + { + repoError << "Error from importModel(): " << (int)impModelErrCode; + return false; + } + + if(generateScene) + { + uint8_t genSceneErrCode = 0; + auto repoScene = modelConvertor->generateRepoScene(genSceneErrCode); + if (genSceneErrCode != expGenSceneErrCode) + { + repoError << "Error from generateRepoScene(): " << (int)genSceneErrCode; + return false; + } + + auto materials = repoScene->getAllMaterials(repo::core::model::RepoScene::GraphType::DEFAULT); + auto textures = repoScene->getAllTextures(repo::core::model::RepoScene::GraphType::DEFAULT); + auto meshes = repoScene->getAllMeshes(repo::core::model::RepoScene::GraphType::DEFAULT); + + if(checkUvs) + { + for (auto const mesh : meshes) + { + auto meshNode = static_cast(mesh); + std::vector uvs = meshNode->getUVChannels(); + std::vector vertices = meshNode->getVertices(); + if(uvs.size() != vertices.size()) + { + repoError << "UV count mesh is incorrect. Expected " << vertices.size() << ", found : " << uvs.size(); + return false; + } + } + } + + if(debugMeshDataFilePath != "") + { + // Print out the mesh debug text file + std::ofstream stream; + stream.open(debugMeshDataFilePath); + if (stream) + { + for (auto const mesh : meshes) + { + auto meshNode = static_cast(mesh); + std::vector triangularFaces = meshNode->getFaces(); + std::vector vertices = meshNode->getVertices(); + std::vector normals = meshNode->getNormals(); + std::vector uvs = meshNode->getUVChannels(); + char lastsep = ','; + stream << "------mesh------" << std::endl; + // Set this to the parent node id for an easier debugging life + stream << mesh->getName() << std::endl; + for (auto vertIt = vertices.begin(); vertIt != vertices.end(); ++vertIt) + { + if (vertIt == (vertices.end() - 1)) { lastsep = '\n'; } + stream << vertIt->x << ","; + stream << vertIt->y << ","; + stream << vertIt->z << lastsep; + } + lastsep = ','; + for (auto normIt = normals.begin(); normIt != normals.end(); ++normIt) + { + if (normIt == (normals.end() - 1)) { lastsep = '\n'; } + stream << normIt->x << ","; + stream << normIt->y << ","; + stream << normIt->z << lastsep; + } + lastsep = ','; + for (auto faceIt = triangularFaces.begin(); faceIt != triangularFaces.end(); ++faceIt) + { + if (faceIt == (triangularFaces.end() - 1)) { lastsep = '\n'; } + stream << faceIt->at(0) << ","; + stream << faceIt->at(1) << ","; + stream << faceIt->at(2) << lastsep; + } + lastsep = ','; + for (auto uvIt = uvs.begin(); uvIt != uvs.end(); ++uvIt) + { + if (uvIt== (uvs.end() - 1)) { lastsep = '\n'; } + stream << uvIt->x << ","; + stream << uvIt->y << lastsep; + } + } + } + stream.close(); + } + + if(textureFilesDumpPath != "") + { + for (auto const img : textures) + { + auto textureNode = static_cast(img); + std::vector imgData = textureNode->getRawData(); + boost::filesystem::path dirPath(textureFilesDumpPath); + boost::filesystem::path fileName( "DUMP_" + textureNode->getName()); + auto fullPath = dirPath / fileName; + std::ofstream stream; + stream.open(fullPath.string(), std::ios::binary); + if (stream) + { + stream.write(&imgData[0], imgData.size()); + } + stream.close(); + } + } + + bool materialsOk = materials.size() == expMaterialsCount; + if (!materialsOk) { repoError << "Expected " << expMaterialsCount << " materials, found " << materials.size(); } + bool texturesOk = textures.size() == expTexturesCount; + if (!materialsOk) { repoError << "Expected " << expTexturesCount << " textures, found " << textures.size(); } + bool meshesOk = meshes.size() == expMeshesCount; + if (!materialsOk) { repoError << "Expected " << expMeshesCount << " meshes, found " << meshes.size(); } + if (!repoScene->isOK()) { repoError << "Scene is not healthy"; } + + bool scenePassed = materialsOk && texturesOk && meshesOk && repoScene->isOK(); + repoInfo << "Generated scene passed: " << std::boolalpha << scenePassed; + return scenePassed; + } + + return true; +}; + +TEST(RepoModelImport, BIM002ValidFile) +{ + EXPECT_TRUE(testBIMFileImport(getDataPath("RepoModelImport/cube_bim2_navis_2021_repo_4.6.1.bim"), 3, 0, 4)); +} + +TEST(RepoModelImport, BIM003ValidFile) +{ + EXPECT_TRUE(testBIMFileImport(getDataPath("RepoModelImport/BrickWalls_bim3.bim"), 3, 2, 6, true)); +} + +TEST(RepoModelImport, BIM001ValidFile) +{ + EXPECT_TRUE(testBIMFileImport( + getDataPath("RepoModelImport/cube_bim1_spoofed.bim"), 0, 0, 0, false, REPOERR_UNSUPPORTED_BIM_VERSION, REPOERR_OK, false)); +} + +TEST(RepoModelImport, BIM003MissingTextureFields) +{ + EXPECT_FALSE(testBIMFileImport( + getDataPath("RepoModelImport/BrickWalls_bim3_CorruptedTextureField.bim"), 3, 1, 6, true, REPOERR_OK, REPOERR_LOAD_SCENE_MISSING_TEXTURE)); +} + +TEST(RepoModelImport, BIM003MissingReferencedTexture) +{ + EXPECT_FALSE(testBIMFileImport( + getDataPath("RepoModelImport/BrickWalls_bim3_MissingTexture.bim"), 3, 2, 6, true, REPOERR_OK, REPOERR_LOAD_SCENE_MISSING_TEXTURE)); +} + diff --git a/test/src/unit/repo/manipulator/modeloptimizer/ut_repo_optimizer_multipart.cpp b/test/src/unit/repo/manipulator/modeloptimizer/ut_repo_optimizer_multipart.cpp index 9cec736c6..99f1c48c1 100644 --- a/test/src/unit/repo/manipulator/modeloptimizer/ut_repo_optimizer_multipart.cpp +++ b/test/src/unit/repo/manipulator/modeloptimizer/ut_repo_optimizer_multipart.cpp @@ -55,7 +55,9 @@ repo::core::model::MeshNode* createRandomMesh(const bool hasUV, const std::vecto std::vector faces; for (int i = 0; i < nVertices; ++i) { - vertices.push_back({ (float)std::rand(), (float)std::rand(), (float)std::rand() }); + vertices.push_back({static_cast(std::rand()), + static_cast(std::rand()), + static_cast(std::rand())}); } for (int i = 0; i < nFaces; ++i) { diff --git a/test/src/unit/repo_test_utils.h b/test/src/unit/repo_test_utils.h index a32931fec..9d690387a 100644 --- a/test/src/unit/repo_test_utils.h +++ b/test/src/unit/repo_test_utils.h @@ -196,4 +196,4 @@ static std::string getRandomString(const uint32_t &iLen) } return sStr; -} +} \ No newline at end of file