diff --git a/languages/dust3d_zh_CN.ts b/languages/dust3d_zh_CN.ts index 3d4309c9..a7e9e5aa 100644 --- a/languages/dust3d_zh_CN.ts +++ b/languages/dust3d_zh_CN.ts @@ -791,6 +791,10 @@ Tips: Transparency 透明度 + + Tile Scale + 平铺缩放 + PoseEditWidget diff --git a/resources/material-demo-model.ds3 b/resources/material-demo-model.ds3 index be193198..23a04e6d 100644 Binary files a/resources/material-demo-model.ds3 and b/resources/material-demo-model.ds3 differ diff --git a/src/document.cpp b/src/document.cpp index a2328ec2..8fee7d76 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1192,6 +1192,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId maps.push_back(textureMap); } std::map layerAttributes; + if (!qFuzzyCompare((float)layer.tileScale, (float)1.0)) + layerAttributes["tileScale"] = QString::number(layer.tileScale); layers.push_back({layerAttributes, maps}); } snapshot->materials.push_back(std::make_pair(material, layers)); @@ -1300,6 +1302,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(materialAttributes, "id"))] = newMaterialId; for (const auto &layerIt: materialIt.second) { MaterialLayer layer; + auto findTileScale = layerIt.first.find("tileScale"); + if (findTileScale != layerIt.first.end()) + layer.tileScale = findTileScale->second.toFloat(); for (const auto &mapItem: layerIt.second) { auto textureTypeString = valueOfKeyInMapOrEmpty(mapItem, "for"); auto textureType = TextureTypeFromString(textureTypeString.toUtf8().constData()); diff --git a/src/document.h b/src/document.h index ca571602..07c2c1d3 100644 --- a/src/document.h +++ b/src/document.h @@ -336,6 +336,7 @@ class MaterialLayer { public: std::vector maps; + float tileScale = 1.0; }; class Material diff --git a/src/material.cpp b/src/material.cpp index dfb0fdf93..612e2eb7 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -2,7 +2,10 @@ #include "imageforever.h" #include "util.h" -void initializeMaterialTexturesFromSnapshot(const Snapshot &snapshot, const QUuid &materialId, MaterialTextures &materialTextures) +void initializeMaterialTexturesFromSnapshot(const Snapshot &snapshot, + const QUuid &materialId, + MaterialTextures &materialTextures, + float &tileScale) { QString materialIdString = materialId.toString(); for (const auto &materialItem: snapshot.materials) { @@ -10,6 +13,9 @@ void initializeMaterialTexturesFromSnapshot(const Snapshot &snapshot, const QUui continue; for (const auto &layer: materialItem.second) { //FIXME: Only support one layer currently + auto findTileScale = layer.first.find("tileScale"); + if (findTileScale != layer.first.end()) + tileScale = findTileScale->second.toFloat(); for (const auto &mapItem: layer.second) { auto textureType = TextureTypeFromString(valueOfKeyInMapOrEmpty(mapItem, "for").toUtf8().constData()); if (textureType != TextureType::None) { diff --git a/src/material.h b/src/material.h index 08f2d13b..bbbfca97 100644 --- a/src/material.h +++ b/src/material.h @@ -10,6 +10,9 @@ struct MaterialTextures const QImage *textureImages[(int)TextureType::Count - 1] = {nullptr}; }; -void initializeMaterialTexturesFromSnapshot(const Snapshot &snapshot, const QUuid &materialId, MaterialTextures &materialTextures); +void initializeMaterialTexturesFromSnapshot(const Snapshot &snapshot, + const QUuid &materialId, + MaterialTextures &materialTextures, + float &tileScale); #endif diff --git a/src/materialeditwidget.cpp b/src/materialeditwidget.cpp index 17597edb..2655ff34 100644 --- a/src/materialeditwidget.cpp +++ b/src/materialeditwidget.cpp @@ -121,6 +121,30 @@ MaterialEditWidget::MaterialEditWidget(const Document *document, QWidget *parent QPushButton *saveButton = new QPushButton(tr("Save")); connect(saveButton, &QPushButton::clicked, this, &MaterialEditWidget::save); saveButton->setDefault(true); + + FloatNumberWidget *tileScaleWidget = new FloatNumberWidget; + tileScaleWidget->setItemName(tr("Tile Scale")); + tileScaleWidget->setRange(0.01, 1.0); + tileScaleWidget->setValue(m_layers[0].tileScale); + + m_tileScaleSlider = tileScaleWidget; + + connect(tileScaleWidget, &FloatNumberWidget::valueChanged, [=](float value) { + m_layers[0].tileScale = value; + emit layersAdjusted(); + }); + + QPushButton *tileScaleEraser = new QPushButton(QChar(fa::eraser)); + Theme::initAwesomeToolButton(tileScaleEraser); + + connect(tileScaleEraser, &QPushButton::clicked, [=]() { + tileScaleWidget->setValue(1.0); + }); + + QHBoxLayout *tileScaleLayout = new QHBoxLayout; + tileScaleLayout->addWidget(tileScaleEraser); + tileScaleLayout->addWidget(tileScaleWidget); + tileScaleLayout->addStretch(); QHBoxLayout *baseInfoLayout = new QHBoxLayout; baseInfoLayout->addWidget(new QLabel(tr("Name"))); @@ -132,6 +156,7 @@ MaterialEditWidget::MaterialEditWidget(const Document *document, QWidget *parent mainLayout->addLayout(paramtersLayout); mainLayout->addStretch(); mainLayout->addWidget(Theme::createHorizontalLineWidget()); + mainLayout->addLayout(tileScaleLayout); mainLayout->addLayout(baseInfoLayout); setLayout(mainLayout); @@ -273,6 +298,7 @@ void MaterialEditWidget::setEditMaterialLayers(std::vector layers } if (!layers.empty()) { for (const auto &layer: layers) { + m_layers[0].tileScale = layer.tileScale; for (const auto &mapItem: layer.maps) { int index = (int)mapItem.forWhat - 1; if (index >= 0 && index < (int)TextureType::Count - 1) { @@ -280,6 +306,7 @@ void MaterialEditWidget::setEditMaterialLayers(std::vector layers } } } + m_tileScaleSlider->setValue(m_layers[0].tileScale); } for (int i = 1; i < (int)TextureType::Count; i++) { updateMapButtonBackground(m_textureMapButtons[i - 1], ImageForever::get(m_layers[0].maps[i - 1].imageId)); diff --git a/src/materialeditwidget.h b/src/materialeditwidget.h index 3543e402..7f757bf6 100644 --- a/src/materialeditwidget.h +++ b/src/materialeditwidget.h @@ -8,6 +8,7 @@ #include "modelwidget.h" #include "materialpreviewsgenerator.h" #include "imagepreviewwidget.h" +#include "floatnumberwidget.h" enum class PopupWidgetType { @@ -47,6 +48,7 @@ private: const Document *m_document = nullptr; MaterialPreviewsGenerator *m_materialPreviewsGenerator = nullptr; ModelWidget *m_previewWidget = nullptr; + FloatNumberWidget *m_tileScaleSlider = nullptr; bool m_isPreviewDirty = false; bool m_closed = false; QUuid m_materialId; diff --git a/src/materialpreviewsgenerator.cpp b/src/materialpreviewsgenerator.cpp index d592132a..585c7e31 100644 --- a/src/materialpreviewsgenerator.cpp +++ b/src/materialpreviewsgenerator.cpp @@ -82,15 +82,15 @@ void MaterialPreviewsGenerator::generate() continue; for (const auto &partId: partIds) { if (TextureType::BaseColor == mapItem.forWhat) - textureGenerator->addPartColorMap(partId, image); + textureGenerator->addPartColorMap(partId, image, layer.tileScale); else if (TextureType::Normal == mapItem.forWhat) - textureGenerator->addPartNormalMap(partId, image); + textureGenerator->addPartNormalMap(partId, image, layer.tileScale); else if (TextureType::Metalness == mapItem.forWhat) - textureGenerator->addPartMetalnessMap(partId, image); + textureGenerator->addPartMetalnessMap(partId, image, layer.tileScale); else if (TextureType::Roughness == mapItem.forWhat) - textureGenerator->addPartRoughnessMap(partId, image); + textureGenerator->addPartRoughnessMap(partId, image, layer.tileScale); else if (TextureType::AmbientOcclusion == mapItem.forWhat) - textureGenerator->addPartAmbientOcclusionMap(partId, image); + textureGenerator->addPartAmbientOcclusionMap(partId, image, layer.tileScale); } } } diff --git a/src/texturegenerator.cpp b/src/texturegenerator.cpp index 666c1004..192c2534 100644 --- a/src/texturegenerator.cpp +++ b/src/texturegenerator.cpp @@ -123,39 +123,39 @@ MeshLoader *TextureGenerator::takeResultMesh() return resultMesh; } -void TextureGenerator::addPartColorMap(QUuid partId, const QImage *image) +void TextureGenerator::addPartColorMap(QUuid partId, const QImage *image, float tileScale) { if (nullptr == image) return; - m_partColorTextureMap[partId] = *image; + m_partColorTextureMap[partId] = std::make_pair(*image, tileScale); } -void TextureGenerator::addPartNormalMap(QUuid partId, const QImage *image) +void TextureGenerator::addPartNormalMap(QUuid partId, const QImage *image, float tileScale) { if (nullptr == image) return; - m_partNormalTextureMap[partId] = *image; + m_partNormalTextureMap[partId] = std::make_pair(*image, tileScale); } -void TextureGenerator::addPartMetalnessMap(QUuid partId, const QImage *image) +void TextureGenerator::addPartMetalnessMap(QUuid partId, const QImage *image, float tileScale) { if (nullptr == image) return; - m_partMetalnessTextureMap[partId] = *image; + m_partMetalnessTextureMap[partId] = std::make_pair(*image, tileScale); } -void TextureGenerator::addPartRoughnessMap(QUuid partId, const QImage *image) +void TextureGenerator::addPartRoughnessMap(QUuid partId, const QImage *image, float tileScale) { if (nullptr == image) return; - m_partRoughnessTextureMap[partId] = *image; + m_partRoughnessTextureMap[partId] = std::make_pair(*image, tileScale); } -void TextureGenerator::addPartAmbientOcclusionMap(QUuid partId, const QImage *image) +void TextureGenerator::addPartAmbientOcclusionMap(QUuid partId, const QImage *image, float tileScale) { if (nullptr == image) return; - m_partAmbientOcclusionTextureMap[partId] = *image; + m_partAmbientOcclusionTextureMap[partId] = std::make_pair(*image, tileScale); } QPainterPath TextureGenerator::expandedPainterPath(const QPainterPath &painterPath, int expandSize) @@ -188,19 +188,20 @@ void TextureGenerator::prepare() auto findUpdatedMaterialIdResult = updatedMaterialIdMap.find(bmeshNode.mirrorFromPartId.isNull() ? bmeshNode.partId : bmeshNode.mirrorFromPartId); if (findUpdatedMaterialIdResult != updatedMaterialIdMap.end()) materialId = findUpdatedMaterialIdResult->second; - initializeMaterialTexturesFromSnapshot(*m_snapshot, materialId, materialTextures); + float tileScale = 1.0; + initializeMaterialTexturesFromSnapshot(*m_snapshot, materialId, materialTextures, tileScale); const QImage *image = materialTextures.textureImages[i]; if (nullptr != image) { if (TextureType::BaseColor == forWhat) - addPartColorMap(bmeshNode.partId, image); + addPartColorMap(bmeshNode.partId, image, tileScale); else if (TextureType::Normal == forWhat) - addPartNormalMap(bmeshNode.partId, image); + addPartNormalMap(bmeshNode.partId, image, tileScale); else if (TextureType::Metalness == forWhat) - addPartMetalnessMap(bmeshNode.partId, image); + addPartMetalnessMap(bmeshNode.partId, image, tileScale); else if (TextureType::Roughness == forWhat) - addPartRoughnessMap(bmeshNode.partId, image); + addPartRoughnessMap(bmeshNode.partId, image, tileScale); else if (TextureType::AmbientOcclusion == forWhat) - addPartAmbientOcclusionMap(bmeshNode.partId, image); + addPartAmbientOcclusionMap(bmeshNode.partId, image, tileScale); } } } @@ -395,13 +396,18 @@ void TextureGenerator::generate() drawGradient(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first); } - auto drawTexture = [&](const std::map &map, QPainter &painter) { + auto drawTexture = [&](const std::map> &map, QPainter &painter) { for (const auto &it: partUvRects) { const auto &partId = it.first; const auto &rects = it.second; auto findTextureResult = map.find(partId); if (findTextureResult != map.end()) { - const auto &image = findTextureResult->second; + float tileScale = findTextureResult->second.second; + const auto &image = findTextureResult->second.first; + auto newSize = image.size() * tileScale; + QImage scaledImage = image.scaled(newSize); + auto pixmap = QPixmap::fromImage(scaledImage); + QPixmap rotatedPixmap; for (const auto &rect: rects) { QRectF translatedRect = { rect.left() * TextureGenerator::m_textureSize, @@ -409,7 +415,19 @@ void TextureGenerator::generate() rect.width() * TextureGenerator::m_textureSize, rect.height() * TextureGenerator::m_textureSize }; - painter.drawImage(translatedRect, image, QRectF(0, 0, image.width(), image.height())); + if (translatedRect.width() < translatedRect.height()) { + if (rotatedPixmap.isNull()) { + QPoint center = scaledImage.rect().center(); + QMatrix matrix; + matrix.translate(center.x(), center.y()); + matrix.rotate(90); + auto rotatedImage = scaledImage.transformed(matrix); + rotatedPixmap = QPixmap::fromImage(rotatedImage); + } + painter.drawTiledPixmap(translatedRect, rotatedPixmap); + } else { + painter.drawTiledPixmap(translatedRect, pixmap); + } } } } diff --git a/src/texturegenerator.h b/src/texturegenerator.h index be819770..9d1210da 100644 --- a/src/texturegenerator.h +++ b/src/texturegenerator.h @@ -25,11 +25,11 @@ public: QImage *takeResultTextureAmbientOcclusionImage(); Outcome *takeOutcome(); MeshLoader *takeResultMesh(); - void addPartColorMap(QUuid partId, const QImage *image); - void addPartNormalMap(QUuid partId, const QImage *image); - void addPartMetalnessMap(QUuid partId, const QImage *image); - void addPartRoughnessMap(QUuid partId, const QImage *image); - void addPartAmbientOcclusionMap(QUuid partId, const QImage *image); + void addPartColorMap(QUuid partId, const QImage *image, float tileScale); + void addPartNormalMap(QUuid partId, const QImage *image, float tileScale); + void addPartMetalnessMap(QUuid partId, const QImage *image, float tileScale); + void addPartRoughnessMap(QUuid partId, const QImage *image, float tileScale); + void addPartAmbientOcclusionMap(QUuid partId, const QImage *image, float tileScale); void generate(); signals: void finished(); @@ -53,11 +53,11 @@ private: QImage *m_resultTextureMetalnessImage; QImage *m_resultTextureAmbientOcclusionImage; MeshLoader *m_resultMesh; - std::map m_partColorTextureMap; - std::map m_partNormalTextureMap; - std::map m_partMetalnessTextureMap; - std::map m_partRoughnessTextureMap; - std::map m_partAmbientOcclusionTextureMap; + std::map> m_partColorTextureMap; + std::map> m_partNormalTextureMap; + std::map> m_partMetalnessTextureMap; + std::map> m_partRoughnessTextureMap; + std::map> m_partAmbientOcclusionTextureMap; Snapshot *m_snapshot; }; diff --git a/src/theme.cpp b/src/theme.cpp index 4cdaf45f..ce8f131f 100644 --- a/src/theme.cpp +++ b/src/theme.cpp @@ -57,10 +57,10 @@ void Theme::initAwsomeBaseSizes() Theme::miniIconSize = (int)(Theme::miniIconFontSize * 1.67); Theme::partPreviewImageSize = (Theme::miniIconSize * 3); Theme::sidebarPreferredWidth = Theme::partPreviewImageSize * 3.7; - Theme::materialPreviewImageSize = Theme::sidebarPreferredWidth * 0.4; - Theme::cutFacePreviewImageSize = Theme::materialPreviewImageSize; - Theme::posePreviewImageSize = Theme::materialPreviewImageSize; - Theme::motionPreviewImageSize = Theme::materialPreviewImageSize; + Theme::posePreviewImageSize = Theme::sidebarPreferredWidth * 0.4; + Theme::materialPreviewImageSize = Theme::posePreviewImageSize; + Theme::cutFacePreviewImageSize = Theme::posePreviewImageSize; + Theme::motionPreviewImageSize = Theme::posePreviewImageSize; Theme::normalButtonSize = Theme::toolIconSize * 2; }