void ModelBaker::outputUnbakedFST() { // Output an unbaked FST file in the original output folder to make it easier for FSTBaker to rebake this model // TODO: Consider a more robust method that does not depend on FSTBaker navigating to a hardcoded relative path QString outputFSTFilename = _modelURL.fileName(); auto extensionStart = outputFSTFilename.indexOf("."); if (extensionStart != -1) { outputFSTFilename.resize(extensionStart); } outputFSTFilename += FST_EXTENSION; QString outputFSTURL = _originalOutputDir + "/" + outputFSTFilename; hifi::VariantHash outputMapping; outputMapping[FST_VERSION_FIELD] = FST_VERSION; outputMapping[FILENAME_FIELD] = _modelURL.fileName(); outputMapping[COMMENT_FIELD] = "This FST file was generated by Oven for use during rebaking. It is not part of the original model. This file's existence is subject to change."; hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); QFile fstOutputFile { outputFSTURL }; if (fstOutputFile.exists()) { handleWarning("The file '" + outputFSTURL + "' already exists. Should that be baked instead of '" + _modelURL.toString() + "'?"); return; } if (!fstOutputFile.open(QIODevice::WriteOnly)) { handleWarning("Failed to open file '" + outputFSTURL + "' for writing. Rebaking may fail on the associated model."); return; } if (fstOutputFile.write(fstOut) == -1) { handleWarning("Failed to write to file '" + outputFSTURL + "'. Rebaking may fail on the associated model."); } }
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setupSceneStateTransitions(); connect(&scene, SIGNAL(error(QString)), this, SLOT(handleError(QString))); connect(&scene, SIGNAL(warning(QString)), this, SLOT(handleWarning(QString))); connect(&scene, SIGNAL(notice(QString)), this, SLOT(handleNotice(QString))); connect(&score, SIGNAL(error(QString)), this, SLOT(handleError(QString))); connect(&score, SIGNAL(warning(QString)), this, SLOT(handleWarning(QString))); connect(&score, SIGNAL(notice(QString)), this, SLOT(handleNotice(QString))); connect(&scene, SIGNAL(userAction(UserAction)), &score, SLOT(userAction(UserAction))); connect(&score, SIGNAL(changed(ScoreChange)), &scene, SLOT(scoreChange(ScoreChange))); connect((ui->VoiceList), SIGNAL(userAction(UserAction)), &score, SLOT(userAction(UserAction))); connect(&score, SIGNAL(changed(ScoreChange)), (ui->VoiceList), SLOT(scoreChange(ScoreChange))); connect(ui->properSpinBox, SIGNAL(userAction(UserAction)), &score, SLOT(userAction(UserAction))); connect(&score, SIGNAL(changed(ScoreChange)), ui->properSpinBox, SLOT(scoreChanged(ScoreChange))); connect((ui->VoiceList), SIGNAL(voiceCheckedSignal(IdType,bool)), &scene, SLOT(voiceVisible(IdType,bool))); connect((ui->VoiceList), SIGNAL(voiceSelectedSignal(IdType)), &scene, SLOT(selectVoice(IdType))); ui->systemView->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); ui->systemView->setScene(&scene); }
void ModelBaker::handleFinishedMaterialBaker() { auto baker = qobject_cast<MaterialBaker*>(sender()); if (baker) { if (!baker->hasErrors()) { // this MaterialBaker is done and everything went according to plan qCDebug(model_baking) << "Adding baked material to FST mapping " << baker->getBakedMaterialData(); QString relativeBakedMaterialURL = _modelURL.fileName(); auto baseName = relativeBakedMaterialURL.left(relativeBakedMaterialURL.lastIndexOf('.')); relativeBakedMaterialURL = baseName + BAKED_MATERIAL_EXTENSION; auto materialResource = baker->getNetworkMaterialResource(); if (materialResource) { for (auto materialName : materialResource->parsedMaterials.names) { QJsonObject json; json[QString("mat::" + QString(materialName.c_str()))] = relativeBakedMaterialURL + "#" + materialName.c_str(); _materialMappingJSON.push_back(json); } } } else { // this material failed to bake - this doesn't fail the entire bake but we need to add the errors from // the material to our warnings _warningList << baker->getWarnings(); } } else { handleWarning("Failed to bake the materials for model with URL " + _modelURL.toString()); } bakeMaterialMap(); }
bool TestCaseSandboxResultDecoderImpl::decode() { switch(channel->readByte()) { case startCmd: handleStartCase(); break; case endCmd: handleEndCase(); return true; case errorCmd: handleError(); break; case failureCmd: handleAssertionFailure(); break; case infoCmd: handleInfo(); break; case warningCmd: handleWarning(); break; default: handleInternalError(); } return false; }
QString ModelBaker::compressTexture(QString modelTextureFileName, image::TextureUsage::Type textureType) { QFileInfo modelTextureFileInfo{ modelTextureFileName.replace("\\", "/") }; if (modelTextureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { // re-baking a model that already references baked textures // this is an error - return from here handleError("Cannot re-bake a file that already references compressed textures"); return QString::null; } if (!image::getSupportedFormats().contains(modelTextureFileInfo.suffix())) { // this is a texture format we don't bake, skip it handleWarning(modelTextureFileName + " is not a bakeable texture format"); return QString::null; } // make sure this texture points to something and isn't one we've already re-mapped QString textureChild { QString::null }; if (!modelTextureFileInfo.filePath().isEmpty()) { // check if this was an embedded texture that we already have in-memory content for QByteArray textureContent; // figure out the URL to this texture, embedded or external if (!modelTextureFileInfo.filePath().isEmpty()) { textureContent = _textureContentMap.value(modelTextureFileName.toLocal8Bit()); } auto urlToTexture = getTextureURL(modelTextureFileInfo, modelTextureFileName, !textureContent.isNull()); QString bakedTextureFileName; if (_remappedTexturePaths.contains(urlToTexture)) { bakedTextureFileName = _remappedTexturePaths[urlToTexture]; } else { // construct the new baked texture file name and file path // ensuring that the baked texture will have a unique name // even if there was another texture with the same name at a different path bakedTextureFileName = createBakedTextureFileName(modelTextureFileInfo); _remappedTexturePaths[urlToTexture] = bakedTextureFileName; } qCDebug(model_baking).noquote() << "Re-mapping" << modelTextureFileName << "to" << bakedTextureFileName; QString bakedTextureFilePath{ _bakedOutputDir + "/" + bakedTextureFileName }; textureChild = bakedTextureFileName; if (!_bakingTextures.contains(urlToTexture)) { _outputFiles.push_back(bakedTextureFilePath); // bake this texture asynchronously bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent); } } return textureChild; }
SelectWnd::SelectWnd(LXQt::Settings* settings, QWidget *parent) : QWidget(parent), mSettings(settings) { setupUi(this); warningLabel->hide(); mModel = new XCursorThemeModel(this); int size = style()->pixelMetric(QStyle::PM_LargeIconSize); lbThemes->setModel(mModel); lbThemes->setItemDelegate(new ItemDelegate(this)); lbThemes->setIconSize(QSize(size, size)); lbThemes->setSelectionMode(QAbstractItemView::SingleSelection); // Make sure we find out about selection changes connect(lbThemes->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), SLOT(currentChanged(const QModelIndex &, const QModelIndex &))); // display/hide warning label connect(mModel, SIGNAL(modelReset()), this, SLOT(handleWarning())); connect(mModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), this, SLOT(handleWarning())); connect(mModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(handleWarning())); connect(warningLabel, SIGNAL(showDirInfo()), this, SLOT(showDirInfo())); // Disable the install button if we can't install new themes to ~/.icons, // or Xcursor isn't set up to look for cursor themes there btInstall->setEnabled(mModel->searchPaths().contains(HOME_ICON_DIR) && iconsIsWritable()); // TODO/FIXME: btInstall functionality btInstall->hide(); btRemove->hide(); //QTimer::singleShot(0, this, SLOT(setCurrent())); handleWarning(); }
void ModelBaker::handleFinishedMaterialMapBaker() { auto baker = qobject_cast<MaterialBaker*>(sender()); if (baker) { if (!baker->hasErrors()) { // this MaterialBaker is done and everything went according to plan qCDebug(model_baking) << "Adding baked material to FST mapping " << baker->getBakedMaterialData(); QString materialName; { auto materialResource = baker->getNetworkMaterialResource(); if (materialResource) { auto url = materialResource->getURL(); if (!url.isEmpty()) { QString urlString = url.toDisplayString(); auto index = urlString.lastIndexOf("#"); if (index != -1) { materialName = urlString.right(urlString.length() - index); } } } } QJsonObject json; json[QString(_materialMapping.front().first.c_str())] = baker->getMaterialData() + BAKED_MATERIAL_EXTENSION + materialName; _materialMappingJSON.push_back(json); } else { // this material failed to bake - this doesn't fail the entire bake but we need to add the errors from // the material to our warnings _warningList << baker->getWarnings(); } } else { handleWarning("Failed to bake the materialMap for model with URL " + _modelURL.toString() + " and mapping target " + _materialMapping.front().first.c_str()); } _materialMapping.erase(_materialMapping.begin()); bakeMaterialMap(); }
bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback) { if (mesh.wasCompressed) { handleError("Cannot re-bake a file that contains compressed mesh"); return false; } 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) { if ((part.quadTrianglesIndices.size() % 3) != 0 || (part.triangleIndices.size() % 3) != 0) { handleWarning("Found a mesh part with invalid index data, skipping"); continue; } numTriangles += part.quadTrianglesIndices.size() / 3; numTriangles += part.triangleIndices.size() / 3; } if (numTriangles == 0) { return false; } 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 }; bool hasPerFaceMaterials = (materialIDCallback) ? (mesh.parts.size() > 1 || materialIDCallback(0) != 0 ) : true; bool needsOriginalIndices{ hasDeformers }; int normalsAttributeID { -1 }; int colorsAttributeID { -1 }; int texCoordsAttributeID { -1 }; int texCoords1AttributeID { -1 }; int faceMaterialAttributeID { -1 }; int originalIndexAttributeID { -1 }; const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32); if (needsOriginalIndices) { originalIndexAttributeID = meshBuilder.AddAttribute( (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX, 1, draco::DT_INT32); } 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); } if (hasTexCoords1) { texCoords1AttributeID = meshBuilder.AddAttribute( (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_TEX_COORD_1, 2, draco::DT_FLOAT32); } if (hasPerFaceMaterials) { faceMaterialAttributeID = meshBuilder.AddAttribute( (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, 1, draco::DT_UINT16); } auto partIndex = 0; draco::FaceIndex face; uint16_t materialID; for (auto& part : mesh.parts) { materialID = (materialIDCallback) ? materialIDCallback(partIndex) : partIndex; auto addFace = [&](QVector<int>& indices, int index, draco::FaceIndex face) { int32_t idx0 = indices[index]; int32_t idx1 = indices[index + 1]; int32_t idx2 = indices[index + 2]; if (hasPerFaceMaterials) { meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); } meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, &mesh.vertices[idx0], &mesh.vertices[idx1], &mesh.vertices[idx2]); if (needsOriginalIndices) { meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face, &mesh.originalIndices[idx0], &mesh.originalIndices[idx1], &mesh.originalIndices[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]); } if (hasTexCoords1) { meshBuilder.SetAttributeValuesForFace(texCoords1AttributeID, face, &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], &mesh.texCoords1[idx2]); } }; for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { addFace(part.quadTrianglesIndices, i, face++); } for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { addFace(part.triangleIndices, i, face++); } partIndex++; } auto dracoMesh = meshBuilder.Finalize(); if (!dracoMesh) { handleWarning("Failed to finalize the baking of a draco Geometry node"); return false; } // we need to modify unique attribute IDs for custom attributes // so the attributes are easily retrievable on the other side if (hasPerFaceMaterials) { dracoMesh->attribute(faceMaterialAttributeID)->set_unique_id(DRACO_ATTRIBUTE_MATERIAL_ID); } if (hasTexCoords1) { 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; encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14); encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 12); encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10); encoder.SetSpeedOptions(0, 5); draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); FBXNode dracoNode; dracoNode.name = "DracoMesh"; auto value = QVariant::fromValue(QByteArray(buffer.data(), (int)buffer.size())); dracoNode.properties.append(value); dracoMeshNode = dracoNode; // Mesh compression successful return true return true; }