RepoUser RepoBSONFactory::makeRepoUser( const std::string &userName, const std::string &password, const std::string &firstName, const std::string &lastName, const std::string &email, const std::list<std::pair<std::string, std::string>> &roles, const std::list<std::pair<std::string, std::string>> &apiKeys, const std::vector<char> &avatar) { RepoBSONBuilder builder; RepoBSONBuilder customDataBuilder; builder.append(REPO_LABEL_ID, generateUUID()); if (!userName.empty()) builder << REPO_USER_LABEL_USER << userName; if (!password.empty()) { RepoBSONBuilder credentialsBuilder; credentialsBuilder << REPO_USER_LABEL_CLEARTEXT << password; builder << REPO_USER_LABEL_CREDENTIALS << credentialsBuilder.obj(); } if (!firstName.empty()) customDataBuilder << REPO_USER_LABEL_FIRST_NAME << firstName; if (!lastName.empty()) customDataBuilder << REPO_USER_LABEL_LAST_NAME << lastName; if (!email.empty()) customDataBuilder << REPO_USER_LABEL_EMAIL << email; if (!apiKeys.empty()) customDataBuilder.appendArrayPair(REPO_USER_LABEL_API_KEYS, apiKeys, REPO_USER_LABEL_LABEL, REPO_USER_LABEL_KEY); if (avatar.size()) { RepoBSONBuilder avatarBuilder; avatarBuilder.appendBinary(REPO_LABEL_DATA, &avatar.at(0), sizeof(avatar.at(0))*avatar.size()); customDataBuilder << REPO_LABEL_AVATAR << avatarBuilder.obj(); } builder << REPO_USER_LABEL_CUSTOM_DATA << customDataBuilder.obj(); if (roles.size()) builder.appendArrayPair(REPO_USER_LABEL_ROLES, roles, REPO_USER_LABEL_DB, REPO_USER_LABEL_ROLE); return RepoUser(builder.obj()); }
TextureNode RepoBSONFactory::makeTextureNode( const std::string &name, const char *data, const uint32_t &byteCount, const uint32_t &width, const uint32_t &height, const int &apiLevel) { RepoBSONBuilder builder; auto defaults = appendDefaults(REPO_NODE_TYPE_TEXTURE, apiLevel, generateUUID(), name); builder.appendElements(defaults); // // Width // builder << REPO_LABEL_WIDTH << width; // // Height // builder << REPO_LABEL_HEIGHT << height; // // Format TODO: replace format with MIME Type? // if (!name.empty()) { boost::filesystem::path file{ name }; std::string ext = file.extension().string(); if (!ext.empty()) builder << REPO_NODE_LABEL_EXTENSION << ext.substr(1, ext.size()); } // // Data // if (data && byteCount) builder.appendBinary( REPO_LABEL_DATA, data, byteCount); else { repoWarning << " Creating a texture node with no texture!"; } return TextureNode(builder.obj()); }
MeshNode RepoBSONFactory::makeMeshNode( const std::vector<repo_vector_t> &vertices, const std::vector<repo_face_t> &faces, const std::vector<repo_vector_t> &normals, const std::vector<std::vector<float>> &boundingBox, const std::vector<std::vector<repo_vector2d_t>> &uvChannels, const std::vector<repo_color4d_t> &colors, const std::vector<std::vector<float>> &outline, const std::string &name, const int &apiLevel) { RepoBSONBuilder builder; uint64_t bytesize = 0; //track the (approximate) size to know when we need to offload to gridFS repoUUID uniqueID = generateUUID(); auto defaults = appendDefaults(REPO_NODE_TYPE_MESH, apiLevel, generateUUID(), name, std::vector<repoUUID>(), uniqueID); bytesize += defaults.objsize(); builder.appendElements(defaults); if (!vertices.size() || !faces.size()) { repoWarning << "Creating a mesh (" << defaults.getUUIDField(REPO_NODE_LABEL_ID) << ") with no vertices/faces!"; } std::unordered_map<std::string, std::pair<std::string, std::vector<uint8_t>>> binMapping; if (boundingBox.size() > 0) { RepoBSONBuilder arrayBuilder; for (int i = 0; i < boundingBox.size(); i++) { arrayBuilder.appendArray(std::to_string(i), boundingBox[i]); bytesize += boundingBox[i].size() * sizeof(boundingBox[i][0]); } builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, arrayBuilder.obj()); } if (outline.size() > 0) { RepoBSONBuilder arrayBuilder; for (int i = 0; i < outline.size(); i++) { arrayBuilder.appendArray(boost::lexical_cast<std::string>(i), outline[i]); bytesize += outline[i].size() * sizeof(outline[i][0]); } builder.appendArray(REPO_NODE_MESH_LABEL_OUTLINE, arrayBuilder.obj()); } /* * TODO: because mongo has a stupid internal limit of 64MB, we can't store everything in a BSON * There are 2 options * 1. store binaries in memory outside of the bson and put it into GRIDFS at the point of commit * 2. leave mongo's bson, use our own/exteral library that doesn't have this limit and database handle this at the point of commit * below uses option 1, but ideally we should be doing option 2. */ if (vertices.size() > 0) { uint64_t verticesByteCount = vertices.size() * sizeof(vertices[0]); if (verticesByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_vertices"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_VERTICES] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_VERTICES].second.resize(verticesByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_VERTICES].second.data(), &vertices[0], verticesByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_VERTICES, &vertices[0], vertices.size() * sizeof(vertices[0]) ); bytesize += verticesByteCount; } } if (faces.size() > 0) { builder << REPO_NODE_MESH_LABEL_FACES_COUNT << (uint32_t)(faces.size()); // In API LEVEL 1, faces are stored as // [n1, v1, v2, ..., n2, v1, v2...] std::vector<repo_face_t>::iterator faceIt; std::vector<uint32_t> facesLevel1; for (auto &face : faces){ auto nIndices = face.size(); if (!nIndices) { repoWarning << "number of indices in this face is 0!"; } facesLevel1.push_back(nIndices); for (uint32_t ind = 0; ind < nIndices; ind++) { facesLevel1.push_back(face[ind]); } } uint64_t facesByteCount = facesLevel1.size() * sizeof(facesLevel1[0]); if (facesByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_faces"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_FACES] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_FACES].second.resize(facesByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_FACES].second.data(), &facesLevel1[0], facesByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_FACES, &facesLevel1[0], facesLevel1.size() * sizeof(facesLevel1[0]) ); bytesize += facesByteCount; } } if (normals.size() > 0) { uint64_t normalsByteCount = normals.size() * sizeof(normals[0]); if (normalsByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_normals"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_NORMALS] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_NORMALS].second.resize(normalsByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_NORMALS].second.data(), &normals[0], normalsByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_NORMALS, &normals[0], normals.size() * sizeof(normals[0])); bytesize += normalsByteCount; } } //if (!vertexHash.empty()) //{ // // TODO: Fix this call - needs to be fixed as int conversion is overloaded // //builder << REPO_NODE_LABEL_SHA256 << (long unsigned int)(vertexHash); //} //-------------------------------------------------------------------------- // Vertex colors if (colors.size()) { uint64_t colorsByteCount = colors.size() * sizeof(colors[0]); if (colorsByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_colors"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_COLORS] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_COLORS].second.resize(colorsByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_COLORS].second.data(), &colors[0], colorsByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_COLORS, &colors[0], colors.size() * sizeof(colors[0])); bytesize += colorsByteCount; } } //-------------------------------------------------------------------------- // UV channels if (uvChannels.size() > 0) { // Could be unsigned __int64 if BSON had such construct (the closest is only __int64) builder << REPO_NODE_MESH_LABEL_UV_CHANNELS_COUNT << (uint32_t)(uvChannels.size()); std::vector<repo_vector2d_t> concatenated; for (auto it = uvChannels.begin(); it != uvChannels.end(); ++it) { std::vector<repo_vector2d_t> channel = *it; std::vector<repo_vector2d_t>::iterator cit; for (cit = channel.begin(); cit != channel.end(); ++cit) { concatenated.push_back(*cit); } } uint64_t uvByteCount = concatenated.size() * sizeof(concatenated[0]); if (uvByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_uv"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS].second.resize(uvByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS].second.data(), &concatenated[0], uvByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_UV_CHANNELS, &concatenated[0], concatenated.size() * sizeof(concatenated[0])); bytesize += uvByteCount; } } return MeshNode(builder.obj(), binMapping); }
RepoNode MeshNode::cloneAndApplyTransformation( const std::vector<float> &matrix) const { std::vector<repo_vector_t> vertices = getVertices(); std::vector<repo_vector_t> normals = getNormals(); auto newBigFiles = bigFiles; RepoBSONBuilder builder; std::vector<repo_vector_t> resultVertice; std::vector<repo_vector_t> newBbox; if (vertices.size()) { resultVertice.reserve(vertices.size()); for (const repo_vector_t &v : vertices) { resultVertice.push_back(multiplyMatVec(matrix, v)); if (newBbox.size()) { if (resultVertice.back().x < newBbox[0].x) newBbox[0].x = resultVertice.back().x; if (resultVertice.back().y < newBbox[0].y) newBbox[0].y = resultVertice.back().y; if (resultVertice.back().z < newBbox[0].z) newBbox[0].z = resultVertice.back().z; if (resultVertice.back().x > newBbox[1].x) newBbox[1].x = resultVertice.back().x; if (resultVertice.back().y > newBbox[1].y) newBbox[1].y = resultVertice.back().y; if (resultVertice.back().z > newBbox[1].z) newBbox[1].z = resultVertice.back().z; } else { newBbox.push_back(resultVertice.back()); newBbox.push_back(resultVertice.back()); } } if (newBigFiles.find(REPO_NODE_MESH_LABEL_VERTICES) != newBigFiles.end()) { const uint64_t verticesByteCount = resultVertice.size() * sizeof(repo_vector_t); newBigFiles[REPO_NODE_MESH_LABEL_VERTICES].second.resize(verticesByteCount); memcpy(newBigFiles[REPO_NODE_MESH_LABEL_VERTICES].second.data(), resultVertice.data(), verticesByteCount); } else builder.appendBinary(REPO_NODE_MESH_LABEL_VERTICES, resultVertice.data(), resultVertice.size() * sizeof(repo_vector_t)); if (normals.size()) { auto matInverse = invertMat(matrix); auto worldMat = transposeMat(matInverse); std::vector<repo_vector_t> resultNormals; resultNormals.reserve(normals.size()); for (const repo_vector_t &v : normals) { auto transformedNormal = multiplyMatVecFake3x3(worldMat, v); normalize(transformedNormal); resultNormals.push_back(transformedNormal); } if (newBigFiles.find(REPO_NODE_MESH_LABEL_NORMALS) != newBigFiles.end()) { const uint64_t byteCount = resultNormals.size() * sizeof(repo_vector_t); newBigFiles[REPO_NODE_MESH_LABEL_NORMALS].second.resize(byteCount); memcpy(newBigFiles[REPO_NODE_MESH_LABEL_NORMALS].second.data(), resultNormals.data(), byteCount); } else builder.appendBinary(REPO_NODE_MESH_LABEL_NORMALS, resultNormals.data(), resultNormals.size() * sizeof(repo_vector_t)); } RepoBSONBuilder arrayBuilder, outlineBuilder; for (size_t i = 0; i < newBbox.size(); ++i) { std::vector<float> boundVec = { newBbox[i].x, newBbox[i].y, newBbox[i].z }; arrayBuilder.appendArray(std::to_string(i), boundVec); } if (newBbox[0].x > newBbox[1].x || newBbox[0].z > newBbox[1].z || newBbox[0].y > newBbox[1].y) { repoError << "New bounding box is incorrect!!!"; } builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, arrayBuilder.obj()); std::vector<float> outline0 = { newBbox[0].x, newBbox[0].y }; std::vector<float> outline1 = { newBbox[1].x, newBbox[0].y }; std::vector<float> outline2 = { newBbox[1].x, newBbox[1].y }; std::vector<float> outline3 = { newBbox[0].x, newBbox[1].y }; outlineBuilder.appendArray("0", outline0); outlineBuilder.appendArray("1", outline1); outlineBuilder.appendArray("2", outline2); outlineBuilder.appendArray("3", outline3); builder.appendArray(REPO_NODE_MESH_LABEL_OUTLINE, outlineBuilder.obj()); return MeshNode(builder.appendElementsUnique(*this), newBigFiles); } else { repoError << "Unable to apply transformation: Cannot find vertices within a mesh!"; return RepoNode(this->copy(), bigFiles); } }