Remove materials

Material will be integrated into part property.
master
Jeremy HU 2022-10-29 10:30:22 +11:00
parent d991f0c532
commit f2fc58c699
29 changed files with 165 additions and 2382 deletions

View File

@ -145,20 +145,6 @@ SOURCES += sources/log_browser.cc
HEADERS += sources/log_browser_dialog.h HEADERS += sources/log_browser_dialog.h
SOURCES += sources/log_browser_dialog.cc SOURCES += sources/log_browser_dialog.cc
SOURCES += sources/main.cc SOURCES += sources/main.cc
HEADERS += sources/material.h
SOURCES += sources/material.cc
HEADERS += sources/material_edit_widget.h
SOURCES += sources/material_edit_widget.cc
HEADERS += sources/material_layer.h
SOURCES += sources/material_layer.cc
HEADERS += sources/material_list_widget.h
SOURCES += sources/material_list_widget.cc
HEADERS += sources/material_manage_widget.h
SOURCES += sources/material_manage_widget.cc
HEADERS += sources/material_previews_generator.h
SOURCES += sources/material_previews_generator.cc
HEADERS += sources/material_widget.h
SOURCES += sources/material_widget.cc
HEADERS += sources/mesh_generator.h HEADERS += sources/mesh_generator.h
SOURCES += sources/mesh_generator.cc SOURCES += sources/mesh_generator.cc
HEADERS += sources/mesh_preview_images_generator.h HEADERS += sources/mesh_preview_images_generator.h
@ -195,8 +181,6 @@ HEADERS += sources/skeleton_ik_mover.h
SOURCES += sources/skeleton_ik_mover.cc SOURCES += sources/skeleton_ik_mover.cc
HEADERS += sources/spinnable_toolbar_icon.h HEADERS += sources/spinnable_toolbar_icon.h
SOURCES += sources/spinnable_toolbar_icon.cc SOURCES += sources/spinnable_toolbar_icon.cc
HEADERS += sources/texture_generator.h
SOURCES += sources/texture_generator.cc
HEADERS += sources/theme.h HEADERS += sources/theme.h
SOURCES += sources/theme.cc SOURCES += sources/theme.cc
HEADERS += sources/toolbar_button.h HEADERS += sources/toolbar_button.h

View File

@ -25,7 +25,6 @@
<file>resources/toolbar_z_disabled.svg</file> <file>resources/toolbar_z_disabled.svg</file>
<file>resources/toolbar_radius.svg</file> <file>resources/toolbar_radius.svg</file>
<file>resources/toolbar_radius_disabled.svg</file> <file>resources/toolbar_radius_disabled.svg</file>
<file>resources/material-demo-model.ds3</file>
<file>resources/model-addax.ds3</file> <file>resources/model-addax.ds3</file>
<file>resources/model-banana.ds3</file> <file>resources/model-banana.ds3</file>
<file>resources/model-bicycle.ds3</file> <file>resources/model-bicycle.ds3</file>

View File

@ -1,9 +1,8 @@
#include "document.h" #include "document.h"
#include "image_forever.h" #include "image_forever.h"
#include "material_previews_generator.h"
#include "mesh_generator.h" #include "mesh_generator.h"
#include "mesh_result_post_processor.h" #include "mesh_result_post_processor.h"
#include "texture_generator.h" #include "uv_map_generator.h"
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
#include <QDebug> #include <QDebug>
@ -1423,14 +1422,6 @@ bool Document::originSettled() const
return !qFuzzyIsNull(getOriginX()) && !qFuzzyIsNull(getOriginY()) && !qFuzzyIsNull(getOriginZ()); return !qFuzzyIsNull(getOriginX()) && !qFuzzyIsNull(getOriginY()) && !qFuzzyIsNull(getOriginZ());
} }
const Material* Document::findMaterial(dust3d::Uuid materialId) const
{
auto it = materialMap.find(materialId);
if (it == materialMap.end())
return nullptr;
return &it->second;
}
void Document::setNodeCutRotation(dust3d::Uuid nodeId, float cutRotation) void Document::setNodeCutRotation(dust3d::Uuid nodeId, float cutRotation)
{ {
auto node = nodeMap.find(nodeId); auto node = nodeMap.find(nodeId);
@ -1571,8 +1562,7 @@ void Document::setEditMode(DocumentEditMode mode)
} }
void Document::toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uuid>& limitNodeIds, void Document::toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uuid>& limitNodeIds,
DocumentToSnapshotFor forWhat, DocumentToSnapshotFor forWhat) const
const std::set<dust3d::Uuid>& limitMaterialIds) const
{ {
if (DocumentToSnapshotFor::Document == forWhat || DocumentToSnapshotFor::Nodes == forWhat) { if (DocumentToSnapshotFor::Document == forWhat || DocumentToSnapshotFor::Nodes == forWhat) {
std::set<dust3d::Uuid> limitPartIds; std::set<dust3d::Uuid> limitPartIds;
@ -1640,8 +1630,6 @@ void Document::toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uui
part["hollowThickness"] = std::to_string(partIt.second.hollowThickness); part["hollowThickness"] = std::to_string(partIt.second.hollowThickness);
if (!partIt.second.name.isEmpty()) if (!partIt.second.name.isEmpty())
part["name"] = partIt.second.name.toUtf8().constData(); part["name"] = partIt.second.name.toUtf8().constData();
if (partIt.second.materialAdjusted())
part["materialId"] = partIt.second.materialId.toString();
if (partIt.second.countershaded) if (partIt.second.countershaded)
part["countershaded"] = "true"; part["countershaded"] = "true";
if (partIt.second.smooth) if (partIt.second.smooth)
@ -1722,39 +1710,6 @@ void Document::toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uui
snapshot->rootComponent["children"] = children; snapshot->rootComponent["children"] = children;
} }
} }
if (DocumentToSnapshotFor::Document == forWhat || DocumentToSnapshotFor::Materials == forWhat) {
for (const auto& materialId : materialIdList) {
if (!limitMaterialIds.empty() && limitMaterialIds.find(materialId) == limitMaterialIds.end())
continue;
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Find material failed:" << materialId;
continue;
}
auto& materialIt = *findMaterialResult;
std::map<std::string, std::string> material;
material["id"] = materialIt.second.id.toString();
material["type"] = "MetalRoughness";
if (!materialIt.second.name.isEmpty())
material["name"] = materialIt.second.name.toUtf8().constData();
std::vector<std::pair<std::map<std::string, std::string>, std::vector<std::map<std::string, std::string>>>> layers;
for (const auto& layer : materialIt.second.layers) {
std::vector<std::map<std::string, std::string>> maps;
for (const auto& mapItem : layer.maps) {
std::map<std::string, std::string> textureMap;
textureMap["for"] = TextureTypeToString(mapItem.forWhat);
textureMap["linkDataType"] = "imageId";
textureMap["linkData"] = mapItem.imageId.toString();
maps.push_back(textureMap);
}
std::map<std::string, std::string> layerAttributes;
if (!qFuzzyCompare((float)layer.tileScale, (float)1.0))
layerAttributes["tileScale"] = std::to_string(layer.tileScale);
layers.push_back({ layerAttributes, maps });
}
snapshot->materials.push_back(std::make_pair(material, layers));
}
}
if (DocumentToSnapshotFor::Document == forWhat) { if (DocumentToSnapshotFor::Document == forWhat) {
std::map<std::string, std::string> canvas; std::map<std::string, std::string> canvas;
canvas["originX"] = std::to_string(getOriginX()); canvas["originX"] = std::to_string(getOriginX());
@ -1787,49 +1742,6 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo
std::set<dust3d::Uuid> inversePartIds; std::set<dust3d::Uuid> inversePartIds;
std::map<dust3d::Uuid, dust3d::Uuid> oldNewIdMap; std::map<dust3d::Uuid, dust3d::Uuid> oldNewIdMap;
for (const auto& materialIt : snapshot.materials) {
const auto& materialAttributes = materialIt.first;
auto materialType = dust3d::String::valueOrEmpty(materialAttributes, "type");
if ("MetalRoughness" != materialType) {
qDebug() << "Unsupported material type:" << materialType;
continue;
}
dust3d::Uuid oldMaterialId = dust3d::Uuid(dust3d::String::valueOrEmpty(materialAttributes, "id"));
dust3d::Uuid newMaterialId = SnapshotSource::Import == source ? oldMaterialId : dust3d::Uuid::createUuid();
oldNewIdMap[oldMaterialId] = newMaterialId;
if (materialMap.end() == materialMap.find(newMaterialId)) {
auto& newMaterial = materialMap[newMaterialId];
newMaterial.id = newMaterialId;
newMaterial.name = dust3d::String::valueOrEmpty(materialAttributes, "name").c_str();
for (const auto& layerIt : materialIt.second) {
MaterialLayer layer;
auto findTileScale = layerIt.first.find("tileScale");
if (findTileScale != layerIt.first.end())
layer.tileScale = dust3d::String::toFloat(findTileScale->second);
for (const auto& mapItem : layerIt.second) {
auto textureTypeString = dust3d::String::valueOrEmpty(mapItem, "for");
auto textureType = dust3d::TextureTypeFromString(textureTypeString.c_str());
if (dust3d::TextureType::None == textureType) {
qDebug() << "Unsupported texture type:" << textureTypeString;
continue;
}
auto linkTypeString = dust3d::String::valueOrEmpty(mapItem, "linkDataType");
if ("imageId" != linkTypeString) {
qDebug() << "Unsupported link data type:" << linkTypeString;
continue;
}
auto imageId = dust3d::Uuid(dust3d::String::valueOrEmpty(mapItem, "linkData"));
MaterialMap materialMap;
materialMap.imageId = imageId;
materialMap.forWhat = textureType;
layer.maps.push_back(materialMap);
}
newMaterial.layers.push_back(layer);
}
materialIdList.push_back(newMaterialId);
emit materialAdded(newMaterialId);
}
}
std::map<dust3d::Uuid, dust3d::Uuid> cutFaceLinkedIdModifyMap; std::map<dust3d::Uuid, dust3d::Uuid> cutFaceLinkedIdModifyMap;
for (const auto& partKv : snapshot.parts) { for (const auto& partKv : snapshot.parts) {
const auto newUuid = dust3d::Uuid::createUuid(); const auto newUuid = dust3d::Uuid::createUuid();
@ -1895,9 +1807,6 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo
const auto& hollowThicknessIt = partKv.second.find("hollowThickness"); const auto& hollowThicknessIt = partKv.second.find("hollowThickness");
if (hollowThicknessIt != partKv.second.end()) if (hollowThicknessIt != partKv.second.end())
part.hollowThickness = dust3d::String::toFloat(hollowThicknessIt->second); part.hollowThickness = dust3d::String::toFloat(hollowThicknessIt->second);
const auto& materialIdIt = partKv.second.find("materialId");
if (materialIdIt != partKv.second.end())
part.materialId = oldNewIdMap[dust3d::Uuid(materialIdIt->second)];
part.countershaded = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "countershaded")); part.countershaded = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "countershaded"));
part.smooth = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "smooth")); part.smooth = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "smooth"));
newAddedPartIds.insert(part.id); newAddedPartIds.insert(part.id);
@ -2054,9 +1963,6 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo
for (const auto& edgeIt : newAddedEdgeIds) { for (const auto& edgeIt : newAddedEdgeIds) {
emit checkEdge(edgeIt); emit checkEdge(edgeIt);
} }
if (!snapshot.materials.empty())
emit materialListChanged();
} }
void Document::silentReset() void Document::silentReset()
@ -2068,8 +1974,6 @@ void Document::silentReset()
edgeMap.clear(); edgeMap.clear();
partMap.clear(); partMap.clear();
componentMap.clear(); componentMap.clear();
materialMap.clear();
materialIdList.clear();
rootComponent = SkeletonComponent(); rootComponent = SkeletonComponent();
} }
@ -2244,29 +2148,31 @@ void Document::generateTexture()
m_isTextureObsolete = false; m_isTextureObsolete = false;
dust3d::Snapshot* snapshot = new dust3d::Snapshot; auto object = std::make_unique<dust3d::Object>(*m_postProcessedObject);
toSnapshot(snapshot);
auto snapshot = std::make_unique<dust3d::Snapshot>();
toSnapshot(snapshot.get());
QThread* thread = new QThread; QThread* thread = new QThread;
m_textureGenerator = new TextureGenerator(*m_postProcessedObject, snapshot); m_textureGenerator = new UvMapGenerator(std::move(object), std::move(snapshot));
m_textureGenerator->moveToThread(thread); m_textureGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_textureGenerator, &TextureGenerator::process); connect(thread, &QThread::started, m_textureGenerator, &UvMapGenerator::process);
connect(m_textureGenerator, &TextureGenerator::finished, this, &Document::textureReady); connect(m_textureGenerator, &UvMapGenerator::finished, this, &Document::textureReady);
connect(m_textureGenerator, &TextureGenerator::finished, thread, &QThread::quit); connect(m_textureGenerator, &UvMapGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start(); thread->start();
} }
void Document::textureReady() void Document::textureReady()
{ {
updateTextureImage(m_textureGenerator->takeResultTextureColorImage()); updateTextureImage(m_textureGenerator->takeResultTextureColorImage().release());
updateTextureNormalImage(m_textureGenerator->takeResultTextureNormalImage()); updateTextureNormalImage(m_textureGenerator->takeResultTextureNormalImage().release());
updateTextureMetalnessImage(m_textureGenerator->takeResultTextureMetalnessImage()); updateTextureMetalnessImage(m_textureGenerator->takeResultTextureMetalnessImage().release());
updateTextureRoughnessImage(m_textureGenerator->takeResultTextureRoughnessImage()); updateTextureRoughnessImage(m_textureGenerator->takeResultTextureRoughnessImage().release());
updateTextureAmbientOcclusionImage(m_textureGenerator->takeResultTextureAmbientOcclusionImage()); updateTextureAmbientOcclusionImage(m_textureGenerator->takeResultTextureAmbientOcclusionImage().release());
delete m_resultTextureMesh; delete m_resultTextureMesh;
m_resultTextureMesh = m_textureGenerator->takeResultMesh(); m_resultTextureMesh = m_textureGenerator->takeResultMesh().release();
m_postProcessedObject->alphaEnabled = m_textureGenerator->hasTransparencySettings(); m_postProcessedObject->alphaEnabled = m_textureGenerator->hasTransparencySettings();
@ -2486,21 +2392,6 @@ void Document::setPartDeformUnified(dust3d::Uuid partId, bool unified)
emit skeletonChanged(); emit skeletonChanged();
} }
void Document::setPartMaterialId(dust3d::Uuid partId, dust3d::Uuid materialId)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.materialId == materialId)
return;
part->second.materialId = materialId;
part->second.dirty = true;
emit partMaterialIdChanged(partId);
emit textureChanged();
}
void Document::setPartRoundState(dust3d::Uuid partId, bool rounded) void Document::setPartRoundState(dust3d::Uuid partId, bool rounded)
{ {
auto part = partMap.find(partId); auto part = partMap.find(partId);
@ -2758,17 +2649,6 @@ bool Document::hasPastableNodesInClipboard() const
return false; return false;
} }
bool Document::hasPastableMaterialsInClipboard() const
{
const QClipboard* clipboard = QApplication::clipboard();
const QMimeData* mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
if (-1 != mimeData->text().indexOf("<material "))
return true;
}
return false;
}
bool Document::undoable() const bool Document::undoable() const
{ {
return m_undoItems.size() >= 2; return m_undoItems.size() >= 2;
@ -2816,126 +2696,6 @@ void Document::checkExportReadyState()
emit exportReady(); emit exportReady();
} }
void Document::addMaterial(dust3d::Uuid materialId, QString name, std::vector<MaterialLayer> layers)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult != materialMap.end()) {
qDebug() << "Material already exist:" << materialId;
return;
}
dust3d::Uuid newMaterialId = materialId;
auto& material = materialMap[newMaterialId];
material.id = newMaterialId;
material.name = name;
material.layers = layers;
material.dirty = true;
materialIdList.push_back(newMaterialId);
emit materialAdded(newMaterialId);
emit materialListChanged();
emit optionsChanged();
}
void Document::removeMaterial(dust3d::Uuid materialId)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Remove a none exist material:" << materialId;
return;
}
materialIdList.erase(std::remove(materialIdList.begin(), materialIdList.end(), materialId), materialIdList.end());
materialMap.erase(findMaterialResult);
emit materialListChanged();
emit materialRemoved(materialId);
emit optionsChanged();
}
void Document::setMaterialLayers(dust3d::Uuid materialId, std::vector<MaterialLayer> layers)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Find material failed:" << materialId;
return;
}
findMaterialResult->second.layers = layers;
findMaterialResult->second.dirty = true;
emit materialLayersChanged(materialId);
emit textureChanged();
emit optionsChanged();
}
void Document::renameMaterial(dust3d::Uuid materialId, QString name)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Find material failed:" << materialId;
return;
}
if (findMaterialResult->second.name == name)
return;
findMaterialResult->second.name = name;
emit materialNameChanged(materialId);
emit materialListChanged();
emit optionsChanged();
}
void Document::generateMaterialPreviews()
{
if (nullptr != m_materialPreviewsGenerator) {
return;
}
QThread* thread = new QThread;
m_materialPreviewsGenerator = new MaterialPreviewsGenerator();
bool hasDirtyMaterial = false;
for (auto& materialIt : materialMap) {
if (!materialIt.second.dirty)
continue;
m_materialPreviewsGenerator->addMaterial(materialIt.first, materialIt.second.layers);
materialIt.second.dirty = false;
hasDirtyMaterial = true;
}
if (!hasDirtyMaterial) {
delete m_materialPreviewsGenerator;
m_materialPreviewsGenerator = nullptr;
delete thread;
return;
}
qDebug() << "Material previews generating..";
m_materialPreviewsGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_materialPreviewsGenerator, &MaterialPreviewsGenerator::process);
connect(m_materialPreviewsGenerator, &MaterialPreviewsGenerator::finished, this, &Document::materialPreviewsReady);
connect(m_materialPreviewsGenerator, &MaterialPreviewsGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void Document::materialPreviewsReady()
{
for (const auto& materialId : m_materialPreviewsGenerator->generatedPreviewMaterialIds()) {
auto material = materialMap.find(materialId);
if (material != materialMap.end()) {
ModelMesh* resultPartPreviewMesh = m_materialPreviewsGenerator->takePreview(materialId);
material->second.updatePreviewMesh(resultPartPreviewMesh);
emit materialPreviewChanged(materialId);
}
}
delete m_materialPreviewsGenerator;
m_materialPreviewsGenerator = nullptr;
qDebug() << "Material previews generation done";
generateMaterialPreviews();
}
bool Document::isMeshGenerating() const bool Document::isMeshGenerating() const
{ {
return nullptr != m_meshGenerator; return nullptr != m_meshGenerator;

View File

@ -2,7 +2,6 @@
#define DUST3D_APPLICATION_DOCUMENT_H_ #define DUST3D_APPLICATION_DOCUMENT_H_
#include "debug.h" #include "debug.h"
#include "material_layer.h"
#include "model_mesh.h" #include "model_mesh.h"
#include "monochrome_mesh.h" #include "monochrome_mesh.h"
#include "theme.h" #include "theme.h"
@ -165,7 +164,6 @@ public:
float cutRotation; float cutRotation;
dust3d::CutFace cutFace; dust3d::CutFace cutFace;
dust3d::Uuid cutFaceLinkedId; dust3d::Uuid cutFaceLinkedId;
dust3d::Uuid materialId;
dust3d::PartTarget target; dust3d::PartTarget target;
float colorSolubility; float colorSolubility;
float metalness; float metalness;
@ -338,10 +336,6 @@ public:
{ {
return cutRotationAdjusted() || cutFaceAdjusted() || hollowThicknessAdjusted(); return cutRotationAdjusted() || cutFaceAdjusted() || hollowThicknessAdjusted();
} }
bool materialAdjusted() const
{
return !materialId.isNull();
}
bool isEditVisible() const bool isEditVisible() const
{ {
return visible && !disabled; return visible && !disabled;
@ -364,7 +358,6 @@ public:
cutFaceLinkedId = other.cutFaceLinkedId; cutFaceLinkedId = other.cutFaceLinkedId;
componentId = other.componentId; componentId = other.componentId;
dirty = other.dirty; dirty = other.dirty;
materialId = other.materialId;
target = other.target; target = other.target;
colorSolubility = other.colorSolubility; colorSolubility = other.colorSolubility;
countershaded = other.countershaded; countershaded = other.countershaded;
@ -551,8 +544,7 @@ private:
std::set<dust3d::Uuid> m_childrenIdSet; std::set<dust3d::Uuid> m_childrenIdSet;
}; };
class MaterialPreviewsGenerator; class UvMapGenerator;
class TextureGenerator;
class MeshGenerator; class MeshGenerator;
class MeshResultPostProcessor; class MeshResultPostProcessor;
@ -561,40 +553,9 @@ public:
dust3d::Snapshot snapshot; dust3d::Snapshot snapshot;
}; };
class Material {
public:
Material()
{
}
~Material()
{
delete m_previewMesh;
}
dust3d::Uuid id;
QString name;
bool dirty = true;
std::vector<MaterialLayer> layers;
void updatePreviewMesh(ModelMesh* previewMesh)
{
delete m_previewMesh;
m_previewMesh = previewMesh;
}
ModelMesh* takePreviewMesh() const
{
if (nullptr == m_previewMesh)
return nullptr;
return new ModelMesh(*m_previewMesh);
}
private:
Q_DISABLE_COPY(Material);
ModelMesh* m_previewMesh = nullptr;
};
enum class DocumentToSnapshotFor { enum class DocumentToSnapshotFor {
Document = 0, Document = 0,
Nodes, Nodes
Materials
}; };
class Document : public QObject { class Document : public QObject {
@ -618,7 +579,6 @@ signals:
void partColorStateChanged(dust3d::Uuid partId); void partColorStateChanged(dust3d::Uuid partId);
void partCutRotationChanged(dust3d::Uuid partId); void partCutRotationChanged(dust3d::Uuid partId);
void partCutFaceChanged(dust3d::Uuid partId); void partCutFaceChanged(dust3d::Uuid partId);
void partMaterialIdChanged(dust3d::Uuid partId);
void partChamferStateChanged(dust3d::Uuid partId); void partChamferStateChanged(dust3d::Uuid partId);
void partTargetChanged(dust3d::Uuid partId); void partTargetChanged(dust3d::Uuid partId);
void partColorSolubilityChanged(dust3d::Uuid partId); void partColorSolubilityChanged(dust3d::Uuid partId);
@ -638,12 +598,6 @@ signals:
void uncheckAll(); void uncheckAll();
void checkNode(dust3d::Uuid nodeId); void checkNode(dust3d::Uuid nodeId);
void checkEdge(dust3d::Uuid edgeId); void checkEdge(dust3d::Uuid edgeId);
void materialAdded(dust3d::Uuid materialId);
void materialRemoved(dust3d::Uuid materialId);
void materialListChanged();
void materialNameChanged(dust3d::Uuid materialId);
void materialLayersChanged(dust3d::Uuid materialId);
void materialPreviewChanged(dust3d::Uuid materialId);
void meshGenerating(); void meshGenerating();
void postProcessing(); void postProcessing();
void textureGenerating(); void textureGenerating();
@ -706,9 +660,6 @@ public: // need initialize
public: public:
Document(); Document();
~Document(); ~Document();
std::map<dust3d::Uuid, Material> materialMap;
std::vector<dust3d::Uuid> materialIdList;
bool undoable() const; bool undoable() const;
bool redoable() const; bool redoable() const;
bool hasPastableNodesInClipboard() const; bool hasPastableNodesInClipboard() const;
@ -717,8 +668,7 @@ public:
bool isEdgeEditable(dust3d::Uuid edgeId) const; bool isEdgeEditable(dust3d::Uuid edgeId) const;
void copyNodes(std::set<dust3d::Uuid> nodeIdSet) const; void copyNodes(std::set<dust3d::Uuid> nodeIdSet) const;
void toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uuid>& limitNodeIds = std::set<dust3d::Uuid>(), void toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uuid>& limitNodeIds = std::set<dust3d::Uuid>(),
DocumentToSnapshotFor forWhat = DocumentToSnapshotFor::Document, DocumentToSnapshotFor forWhat = DocumentToSnapshotFor::Document) const;
const std::set<dust3d::Uuid>& limitMaterialIds = std::set<dust3d::Uuid>()) const;
void fromSnapshot(const dust3d::Snapshot& snapshot); void fromSnapshot(const dust3d::Snapshot& snapshot);
enum class SnapshotSource { enum class SnapshotSource {
Unknown, Unknown,
@ -726,7 +676,6 @@ public:
Import Import
}; };
void addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSource source = SnapshotSource::Paste); void addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSource source = SnapshotSource::Paste);
const Material* findMaterial(dust3d::Uuid materialId) const;
ModelMesh* takeResultMesh(); ModelMesh* takeResultMesh();
MonochromeMesh* takeWireframeMesh(); MonochromeMesh* takeWireframeMesh();
ModelMesh* takePaintedMesh(); ModelMesh* takePaintedMesh();
@ -740,7 +689,6 @@ public:
void updateTextureMetalnessImage(QImage* image); void updateTextureMetalnessImage(QImage* image);
void updateTextureRoughnessImage(QImage* image); void updateTextureRoughnessImage(QImage* image);
void updateTextureAmbientOcclusionImage(QImage* image); void updateTextureAmbientOcclusionImage(QImage* image);
bool hasPastableMaterialsInClipboard() const;
const dust3d::Object& currentPostProcessedObject() const; const dust3d::Object& currentPostProcessedObject() const;
bool isExportReady() const; bool isExportReady() const;
bool isPostProcessResultObsolete() const; bool isPostProcessResultObsolete() const;
@ -822,8 +770,6 @@ public slots:
void textureReady(); void textureReady();
void postProcess(); void postProcess();
void postProcessedMeshResultReady(); void postProcessedMeshResultReady();
void generateMaterialPreviews();
void materialPreviewsReady();
void setPartSubdivState(dust3d::Uuid partId, bool subdived); void setPartSubdivState(dust3d::Uuid partId, bool subdived);
void setPartXmirrorState(dust3d::Uuid partId, bool mirrored); void setPartXmirrorState(dust3d::Uuid partId, bool mirrored);
void setPartDeformThickness(dust3d::Uuid partId, float thickness); void setPartDeformThickness(dust3d::Uuid partId, float thickness);
@ -834,7 +780,6 @@ public slots:
void setPartCutRotation(dust3d::Uuid partId, float cutRotation); void setPartCutRotation(dust3d::Uuid partId, float cutRotation);
void setPartCutFace(dust3d::Uuid partId, dust3d::CutFace cutFace); void setPartCutFace(dust3d::Uuid partId, dust3d::CutFace cutFace);
void setPartCutFaceLinkedId(dust3d::Uuid partId, dust3d::Uuid linkedId); void setPartCutFaceLinkedId(dust3d::Uuid partId, dust3d::Uuid linkedId);
void setPartMaterialId(dust3d::Uuid partId, dust3d::Uuid materialId);
void setPartChamferState(dust3d::Uuid partId, bool chamfered); void setPartChamferState(dust3d::Uuid partId, bool chamfered);
void setPartTarget(dust3d::Uuid partId, dust3d::PartTarget target); void setPartTarget(dust3d::Uuid partId, dust3d::PartTarget target);
void setPartColorSolubility(dust3d::Uuid partId, float solubility); void setPartColorSolubility(dust3d::Uuid partId, float solubility);
@ -852,10 +797,6 @@ public slots:
void silentReset(); void silentReset();
void toggleSmoothNormal(); void toggleSmoothNormal();
void enableWeld(bool enabled); void enableWeld(bool enabled);
void addMaterial(dust3d::Uuid materialId, QString name, std::vector<MaterialLayer>);
void removeMaterial(dust3d::Uuid materialId);
void setMaterialLayers(dust3d::Uuid materialId, std::vector<MaterialLayer> layers);
void renameMaterial(dust3d::Uuid materialId, QString name);
void removeNode(dust3d::Uuid nodeId); void removeNode(dust3d::Uuid nodeId);
void removeEdge(dust3d::Uuid edgeId); void removeEdge(dust3d::Uuid edgeId);
void removePart(dust3d::Uuid partId); void removePart(dust3d::Uuid partId);
@ -932,14 +873,13 @@ private:
int m_batchChangeRefCount = 0; int m_batchChangeRefCount = 0;
dust3d::Object* m_currentObject = nullptr; dust3d::Object* m_currentObject = nullptr;
bool m_isTextureObsolete = false; bool m_isTextureObsolete = false;
TextureGenerator* m_textureGenerator = nullptr; UvMapGenerator* m_textureGenerator = nullptr;
bool m_isPostProcessResultObsolete = false; bool m_isPostProcessResultObsolete = false;
MeshResultPostProcessor* m_postProcessor = nullptr; MeshResultPostProcessor* m_postProcessor = nullptr;
dust3d::Object* m_postProcessedObject = new dust3d::Object; dust3d::Object* m_postProcessedObject = new dust3d::Object;
ModelMesh* m_resultTextureMesh = nullptr; ModelMesh* m_resultTextureMesh = nullptr;
unsigned long long m_textureImageUpdateVersion = 0; unsigned long long m_textureImageUpdateVersion = 0;
bool m_smoothNormal = false; bool m_smoothNormal = false;
MaterialPreviewsGenerator* m_materialPreviewsGenerator = nullptr;
quint64 m_meshGenerationId = 0; quint64 m_meshGenerationId = 0;
quint64 m_nextMeshGenerationId = 0; quint64 m_nextMeshGenerationId = 0;
void* m_generatedCacheContext = nullptr; void* m_generatedCacheContext = nullptr;

View File

@ -39,17 +39,6 @@ void DocumentSaver::collectUsedResourceIds(const dust3d::Snapshot* snapshot,
dust3d::Uuid imageId = dust3d::Uuid(findImageIdString->second); dust3d::Uuid imageId = dust3d::Uuid(findImageIdString->second);
imageIds.insert(imageId); imageIds.insert(imageId);
} }
for (const auto& material : snapshot->materials) {
for (auto& layer : material.second) {
for (auto& mapItem : layer.second) {
auto findImageIdString = mapItem.find("linkData");
if (findImageIdString == mapItem.end())
continue;
dust3d::Uuid imageId = dust3d::Uuid(findImageIdString->second);
imageIds.insert(imageId);
}
}
}
} }
bool DocumentSaver::save(const QString* filename, bool DocumentSaver::save(const QString* filename,

View File

@ -10,14 +10,13 @@
#include "horizontal_line_widget.h" #include "horizontal_line_widget.h"
#include "image_forever.h" #include "image_forever.h"
#include "log_browser.h" #include "log_browser.h"
#include "material_manage_widget.h"
#include "part_manage_widget.h" #include "part_manage_widget.h"
#include "preferences.h" #include "preferences.h"
#include "skeleton_graphics_widget.h" #include "skeleton_graphics_widget.h"
#include "spinnable_toolbar_icon.h" #include "spinnable_toolbar_icon.h"
#include "texture_generator.h"
#include "theme.h" #include "theme.h"
#include "updates_check_widget.h" #include "updates_check_widget.h"
#include "uv_map_generator.h"
#include "version.h" #include "version.h"
#include <QApplication> #include <QApplication>
#include <QDesktopServices> #include <QDesktopServices>
@ -270,21 +269,6 @@ DocumentWindow::DocumentWindow()
partsDocker->setWidget(m_partManageWidget); partsDocker->setWidget(m_partManageWidget);
addDockWidget(Qt::RightDockWidgetArea, partsDocker); addDockWidget(Qt::RightDockWidgetArea, partsDocker);
QDockWidget* materialDocker = new QDockWidget(tr("Materials"), this);
materialDocker->setAllowedAreas(Qt::RightDockWidgetArea);
MaterialManageWidget* materialManageWidget = new MaterialManageWidget(m_document, materialDocker);
materialDocker->setWidget(materialManageWidget);
connect(materialManageWidget, &MaterialManageWidget::registerDialog, this, &DocumentWindow::registerDialog);
connect(materialManageWidget, &MaterialManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog);
addDockWidget(Qt::RightDockWidgetArea, materialDocker);
connect(materialDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) {
Q_UNUSED(topLevel);
for (const auto& material : m_document->materialMap)
emit m_document->materialPreviewChanged(material.first);
});
tabifyDockWidget(partsDocker, materialDocker);
partsDocker->raise(); partsDocker->raise();
QWidget* titleBarWidget = new QWidget; QWidget* titleBarWidget = new QWidget;
@ -431,13 +415,6 @@ DocumentWindow::DocumentWindow()
}); });
m_windowMenu->addAction(m_showPartsListAction); m_windowMenu->addAction(m_showPartsListAction);
m_showMaterialsAction = new QAction(tr("Materials"), this);
connect(m_showMaterialsAction, &QAction::triggered, [=]() {
materialDocker->show();
materialDocker->raise();
});
m_windowMenu->addAction(m_showMaterialsAction);
QMenu* dialogsMenu = m_windowMenu->addMenu(tr("Dialogs")); QMenu* dialogsMenu = m_windowMenu->addMenu(tr("Dialogs"));
connect(dialogsMenu, &QMenu::aboutToShow, [=]() { connect(dialogsMenu, &QMenu::aboutToShow, [=]() {
dialogsMenu->clear(); dialogsMenu->clear();
@ -667,15 +644,6 @@ DocumentWindow::DocumentWindow()
connect(m_document, &Document::zlockStateChanged, this, &DocumentWindow::updateZlockButtonState); connect(m_document, &Document::zlockStateChanged, this, &DocumentWindow::updateZlockButtonState);
connect(m_document, &Document::radiusLockStateChanged, this, &DocumentWindow::updateRadiusLockButtonState); connect(m_document, &Document::radiusLockStateChanged, this, &DocumentWindow::updateRadiusLockButtonState);
connect(m_document, &Document::materialAdded, this, [=](dust3d::Uuid materialId) {
Q_UNUSED(materialId);
m_document->generateMaterialPreviews();
});
connect(m_document, &Document::materialLayersChanged, this, [=](dust3d::Uuid materialId) {
Q_UNUSED(materialId);
m_document->generateMaterialPreviews();
});
initializeShortcuts(); initializeShortcuts();
connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady); connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady);
@ -1088,7 +1056,7 @@ void DocumentWindow::exportGlbToFilename(const QString& filename)
} }
QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::setOverrideCursor(Qt::WaitCursor);
dust3d::Object skeletonResult = m_document->currentPostProcessedObject(); dust3d::Object skeletonResult = m_document->currentPostProcessedObject();
QImage* textureMetalnessRoughnessAmbientOcclusionImage = TextureGenerator::combineMetalnessRoughnessAmbientOcclusionImages(m_document->textureMetalnessImage, QImage* textureMetalnessRoughnessAmbientOcclusionImage = UvMapGenerator::combineMetalnessRoughnessAmbientOcclusionImages(m_document->textureMetalnessImage,
m_document->textureRoughnessImage, m_document->textureRoughnessImage,
m_document->textureAmbientOcclusionImage); m_document->textureAmbientOcclusionImage);
GlbFileWriter glbFileWriter(skeletonResult, filename, GlbFileWriter glbFileWriter(skeletonResult, filename,

View File

@ -144,7 +144,6 @@ private:
QMenu* m_windowMenu = nullptr; QMenu* m_windowMenu = nullptr;
QAction* m_showPartsListAction = nullptr; QAction* m_showPartsListAction = nullptr;
QAction* m_showDebugDialogAction = nullptr; QAction* m_showDebugDialogAction = nullptr;
QAction* m_showMaterialsAction = nullptr;
QMenu* m_helpMenu = nullptr; QMenu* m_helpMenu = nullptr;
QAction* m_gotoHomepageAction = nullptr; QAction* m_gotoHomepageAction = nullptr;

View File

@ -1,34 +0,0 @@
#include "material.h"
#include "image_forever.h"
void initializeMaterialTexturesFromSnapshot(const dust3d::Snapshot& snapshot,
const dust3d::Uuid& materialId,
MaterialTextures& materialTextures,
float& tileScale)
{
auto materialIdString = materialId.toString();
for (const auto& materialItem : snapshot.materials) {
if (materialIdString != dust3d::String::valueOrEmpty(materialItem.first, "id"))
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 = dust3d::String::toFloat(findTileScale->second);
for (const auto& mapItem : layer.second) {
auto textureType = dust3d::TextureTypeFromString(dust3d::String::valueOrEmpty(mapItem, "for").c_str());
if (textureType != dust3d::TextureType::None) {
int index = (int)textureType - 1;
if (index >= 0 && index < (int)dust3d::TextureType::Count - 1) {
if ("imageId" == dust3d::String::valueOrEmpty(mapItem, "linkDataType")) {
auto imageIdString = dust3d::String::valueOrEmpty(mapItem, "linkData");
materialTextures.textureImages[index] = ImageForever::get(dust3d::Uuid(imageIdString));
}
}
}
}
break;
}
break;
}
}

View File

@ -1,18 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_H_
#define DUST3D_APPLICATION_MATERIAL_H_
#include <QImage>
#include <dust3d/base/snapshot.h>
#include <dust3d/base/texture_type.h>
#include <dust3d/base/uuid.h>
struct MaterialTextures {
const QImage* textureImages[(int)dust3d::TextureType::Count - 1] = { nullptr };
};
void initializeMaterialTexturesFromSnapshot(const dust3d::Snapshot& snapshot,
const dust3d::Uuid& materialId,
MaterialTextures& materialTextures,
float& tileScale);
#endif

View File

@ -1,335 +0,0 @@
#include "material_edit_widget.h"
#include "document.h"
#include "float_number_widget.h"
#include "horizontal_line_widget.h"
#include "image_forever.h"
#include "theme.h"
#include "version.h"
#include <QFileDialog>
#include <QFormLayout>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QThread>
#include <QVBoxLayout>
#include <QWidgetAction>
ImagePreviewWidget* MaterialEditWidget::createMapButton()
{
ImagePreviewWidget* mapButton = new ImagePreviewWidget;
mapButton->setFixedSize(Theme::partPreviewImageSize * 2, Theme::partPreviewImageSize * 2);
updateMapButtonBackground(mapButton, nullptr);
return mapButton;
}
QImage* MaterialEditWidget::pickImage()
{
QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(),
tr("Image Files (*.png *.jpg *.bmp)"))
.trimmed();
if (fileName.isEmpty())
return nullptr;
QImage* image = new QImage();
if (!image->load(fileName))
return nullptr;
return image;
}
MaterialEditWidget::MaterialEditWidget(const Document* document, QWidget* parent)
: QDialog(parent)
, m_document(document)
{
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
m_layers.resize(1);
m_previewWidget = new ModelWidget(this);
m_previewWidget->setMinimumSize(128, 128);
m_previewWidget->resize(512, 512);
m_previewWidget->move(-128, -128);
m_previewWidget->setNotGraphics(true);
QFont nameFont;
nameFont.setWeight(QFont::Light);
nameFont.setBold(false);
QGridLayout* mapLayout = new QGridLayout;
int row = 0;
int col = 0;
for (int i = 1; i < (int)dust3d::TextureType::Count; i++) {
QVBoxLayout* textureManageLayout = new QVBoxLayout;
MaterialMap item;
item.forWhat = (dust3d::TextureType)i;
m_layers[0].maps.push_back(item);
ImagePreviewWidget* imageButton = createMapButton();
connect(imageButton, &ImagePreviewWidget::clicked, [=]() {
QImage* image = pickImage();
if (nullptr == image)
return;
m_layers[0].maps[(int)i - 1].imageId = ImageForever::add(image);
updateMapButtonBackground(imageButton, image);
delete image;
emit layersAdjusted();
});
QLabel* nameLabel = new QLabel(tr(dust3d::TextureTypeToDispName(item.forWhat).c_str()));
nameLabel->setFont(nameFont);
QPushButton* eraser = new QPushButton(QChar(fa::eraser));
Theme::initAwesomeToolButton(eraser);
connect(eraser, &QPushButton::clicked, [=]() {
m_layers[0].maps[(int)i - 1].imageId = dust3d::Uuid();
updateMapButtonBackground(imageButton, nullptr);
emit layersAdjusted();
});
QHBoxLayout* textureTitleLayout = new QHBoxLayout;
textureTitleLayout->addWidget(eraser);
textureTitleLayout->addWidget(nameLabel);
textureTitleLayout->addStretch();
textureManageLayout->addWidget(imageButton);
textureManageLayout->addLayout(textureTitleLayout);
m_textureMapButtons[i - 1] = imageButton;
mapLayout->addLayout(textureManageLayout, row, col++);
if (col == 2) {
col = 0;
row++;
}
}
QVBoxLayout* rightLayout = new QVBoxLayout;
rightLayout->addStretch();
rightLayout->addLayout(mapLayout);
rightLayout->addStretch();
QHBoxLayout* paramtersLayout = new QHBoxLayout;
paramtersLayout->setContentsMargins(256, 0, 0, 0);
paramtersLayout->addStretch();
paramtersLayout->addLayout(rightLayout);
m_nameEdit = new QLineEdit;
connect(m_nameEdit, &QLineEdit::textChanged, this, [=]() {
m_unsaved = true;
updateTitle();
});
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")));
baseInfoLayout->addWidget(m_nameEdit);
baseInfoLayout->addStretch();
baseInfoLayout->addWidget(saveButton);
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->addLayout(paramtersLayout);
mainLayout->addStretch();
mainLayout->addWidget(new HorizontalLineWidget());
mainLayout->addLayout(tileScaleLayout);
mainLayout->addLayout(baseInfoLayout);
setLayout(mainLayout);
connect(this, &MaterialEditWidget::layersAdjusted, this, &MaterialEditWidget::updatePreview);
connect(this, &MaterialEditWidget::layersAdjusted, [=]() {
m_unsaved = true;
updateTitle();
});
connect(this, &MaterialEditWidget::addMaterial, document, &Document::addMaterial);
connect(this, &MaterialEditWidget::renameMaterial, document, &Document::renameMaterial);
connect(this, &MaterialEditWidget::setMaterialLayers, document, &Document::setMaterialLayers);
updatePreview();
updateTitle();
}
void MaterialEditWidget::updateMapButtonBackground(ImagePreviewWidget* button, const QImage* image)
{
if (nullptr == image)
button->updateImage(QImage());
else
button->updateImage(*image);
}
void MaterialEditWidget::reject()
{
close();
}
void MaterialEditWidget::closeEvent(QCloseEvent* event)
{
if (m_unsaved && !m_closed) {
QMessageBox::StandardButton answer = QMessageBox::question(this,
APP_NAME,
tr("Do you really want to close while there are unsaved changes?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (answer != QMessageBox::Yes) {
event->ignore();
return;
}
}
m_closed = true;
hide();
if (nullptr != m_materialPreviewsGenerator) {
event->ignore();
return;
}
event->accept();
}
QSize MaterialEditWidget::sizeHint() const
{
return QSize(0, 200);
}
MaterialEditWidget::~MaterialEditWidget()
{
Q_ASSERT(nullptr == m_materialPreviewsGenerator);
}
void MaterialEditWidget::updatePreview()
{
if (m_closed)
return;
if (nullptr != m_materialPreviewsGenerator) {
m_isPreviewDirty = true;
return;
}
m_isPreviewDirty = false;
qDebug() << "Material preview generating..";
QThread* thread = new QThread;
m_materialPreviewsGenerator = new MaterialPreviewsGenerator();
m_materialPreviewsGenerator->addMaterial(dust3d::Uuid(), m_layers);
m_materialPreviewsGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_materialPreviewsGenerator, &MaterialPreviewsGenerator::process);
connect(m_materialPreviewsGenerator, &MaterialPreviewsGenerator::finished, this, &MaterialEditWidget::previewReady);
connect(m_materialPreviewsGenerator, &MaterialPreviewsGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void MaterialEditWidget::previewReady()
{
m_previewWidget->updateMesh(m_materialPreviewsGenerator->takePreview(dust3d::Uuid()));
delete m_materialPreviewsGenerator;
m_materialPreviewsGenerator = nullptr;
qDebug() << "Material preview generation done";
if (m_closed) {
close();
return;
}
if (m_isPreviewDirty)
updatePreview();
}
void MaterialEditWidget::setEditMaterialId(dust3d::Uuid materialId)
{
if (m_materialId == materialId)
return;
m_materialId = materialId;
updateTitle();
}
void MaterialEditWidget::updateTitle()
{
if (m_materialId.isNull()) {
setWindowTitle(applicationTitle(tr("New") + (m_unsaved ? "*" : "")));
return;
}
const Material* material = m_document->findMaterial(m_materialId);
if (nullptr == material) {
qDebug() << "Find material failed:" << m_materialId;
return;
}
setWindowTitle(applicationTitle(material->name + (m_unsaved ? "*" : "")));
}
void MaterialEditWidget::setEditMaterialName(QString name)
{
m_nameEdit->setText(name);
updateTitle();
}
void MaterialEditWidget::setEditMaterialLayers(std::vector<MaterialLayer> layers)
{
for (int i = 1; i < (int)dust3d::TextureType::Count; i++) {
m_layers[0].maps[i - 1].imageId = dust3d::Uuid();
}
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)dust3d::TextureType::Count - 1) {
m_layers[0].maps[index].imageId = mapItem.imageId;
}
}
}
m_tileScaleSlider->setValue(m_layers[0].tileScale);
}
for (int i = 1; i < (int)dust3d::TextureType::Count; i++) {
updateMapButtonBackground(m_textureMapButtons[i - 1], ImageForever::get(m_layers[0].maps[i - 1].imageId));
}
updatePreview();
}
void MaterialEditWidget::clearUnsaveState()
{
m_unsaved = false;
updateTitle();
}
void MaterialEditWidget::save()
{
if (m_materialId.isNull()) {
m_materialId = dust3d::Uuid::createUuid();
emit addMaterial(m_materialId, m_nameEdit->text(), m_layers);
} else if (m_unsaved) {
emit renameMaterial(m_materialId, m_nameEdit->text());
emit setMaterialLayers(m_materialId, m_layers);
}
clearUnsaveState();
}

View File

@ -1,65 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_EDIT_WIDGET_H_
#define DUST3D_APPLICATION_MATERIAL_EDIT_WIDGET_H_
#include "float_number_widget.h"
#include "image_preview_widget.h"
#include "material_layer.h"
#include "material_previews_generator.h"
#include "model_widget.h"
#include <QCloseEvent>
#include <QDialog>
#include <QLineEdit>
#include <map>
class Document;
enum class PopupWidgetType {
PitchYawRoll,
Intersection
};
class MaterialEditWidget : public QDialog {
Q_OBJECT
signals:
void addMaterial(dust3d::Uuid materialId, QString name, std::vector<MaterialLayer> layers);
void removeMaterial(dust3d::Uuid materialId);
void setMaterialLayers(dust3d::Uuid materialId, std::vector<MaterialLayer> layers);
void renameMaterial(dust3d::Uuid materialId, QString name);
void layersAdjusted();
public:
MaterialEditWidget(const Document* document, QWidget* parent = nullptr);
~MaterialEditWidget();
public slots:
void updatePreview();
void setEditMaterialId(dust3d::Uuid materialId);
void setEditMaterialName(QString name);
void setEditMaterialLayers(std::vector<MaterialLayer> layers);
void updateTitle();
void save();
void clearUnsaveState();
void previewReady();
protected:
QSize sizeHint() const override;
void closeEvent(QCloseEvent* event) override;
void reject() override;
private:
void updateMapButtonBackground(ImagePreviewWidget* button, const QImage* image);
ImagePreviewWidget* createMapButton();
QImage* pickImage();
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;
dust3d::Uuid m_materialId;
bool m_unsaved = false;
QLineEdit* m_nameEdit = nullptr;
std::vector<MaterialLayer> m_layers;
ImagePreviewWidget* m_textureMapButtons[(int)dust3d::TextureType::Count - 1] = { nullptr };
};
#endif

View File

@ -1 +0,0 @@
#include "material_layer.h"

View File

@ -1,20 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_LAYER_H_
#define DUST3D_APPLICATION_MATERIAL_LAYER_H_
#include <dust3d/base/texture_type.h>
#include <dust3d/base/uuid.h>
#include <vector>
class MaterialMap {
public:
dust3d::TextureType forWhat;
dust3d::Uuid imageId;
};
class MaterialLayer {
public:
std::vector<MaterialMap> maps;
float tileScale = 1.0;
};
#endif

View File

@ -1,308 +0,0 @@
#include "material_list_widget.h"
#include "document.h"
#include <QApplication>
#include <QClipboard>
#include <QGuiApplication>
#include <QMenu>
#include <QXmlStreamWriter>
#include <dust3d/base/snapshot_xml.h>
MaterialListWidget::MaterialListWidget(const Document* document, QWidget* parent)
: QTreeWidget(parent)
, m_document(document)
{
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
setFocusPolicy(Qt::NoFocus);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setAutoScroll(false);
setHeaderHidden(true);
QPalette palette = this->palette();
palette.setColor(QPalette::Window, Qt::transparent);
palette.setColor(QPalette::Base, Qt::transparent);
setPalette(palette);
setStyleSheet("QTreeView {qproperty-indentation: 0;}");
setContentsMargins(0, 0, 0, 0);
connect(document, &Document::materialListChanged, this, &MaterialListWidget::reload);
connect(document, &Document::cleanup, this, &MaterialListWidget::removeAllContent);
connect(this, &MaterialListWidget::removeMaterial, document, &Document::removeMaterial);
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QTreeWidget::customContextMenuRequested, this, &MaterialListWidget::showContextMenu);
reload();
}
void MaterialListWidget::enableMultipleSelection(bool enabled)
{
m_multipleSelectionEnabled = enabled;
}
void MaterialListWidget::materialRemoved(dust3d::Uuid materialId)
{
if (m_currentSelectedMaterialId == materialId)
m_currentSelectedMaterialId = dust3d::Uuid();
m_selectedMaterialIds.erase(materialId);
m_itemMap.erase(materialId);
}
void MaterialListWidget::updateMaterialSelectState(dust3d::Uuid materialId, bool selected)
{
auto findItemResult = m_itemMap.find(materialId);
if (findItemResult == m_itemMap.end()) {
qDebug() << "Find material item failed:" << materialId;
return;
}
MaterialWidget* materialWidget = (MaterialWidget*)itemWidget(findItemResult->second.first, findItemResult->second.second);
materialWidget->updateCheckedState(selected);
}
void MaterialListWidget::selectMaterial(dust3d::Uuid materialId, bool multiple)
{
if (multiple) {
if (!m_currentSelectedMaterialId.isNull()) {
m_selectedMaterialIds.insert(m_currentSelectedMaterialId);
m_currentSelectedMaterialId = dust3d::Uuid();
}
if (m_selectedMaterialIds.find(materialId) != m_selectedMaterialIds.end()) {
updateMaterialSelectState(materialId, false);
m_selectedMaterialIds.erase(materialId);
} else {
if (m_multipleSelectionEnabled || m_selectedMaterialIds.empty()) {
updateMaterialSelectState(materialId, true);
m_selectedMaterialIds.insert(materialId);
}
}
if (m_selectedMaterialIds.size() > 1) {
return;
}
if (m_selectedMaterialIds.size() == 1)
materialId = *m_selectedMaterialIds.begin();
else {
materialId = dust3d::Uuid();
emit currentSelectedMaterialChanged(materialId);
}
}
if (!m_selectedMaterialIds.empty()) {
for (const auto& id : m_selectedMaterialIds) {
updateMaterialSelectState(id, false);
}
m_selectedMaterialIds.clear();
}
if (m_currentSelectedMaterialId != materialId) {
if (!m_currentSelectedMaterialId.isNull()) {
updateMaterialSelectState(m_currentSelectedMaterialId, false);
}
m_currentSelectedMaterialId = materialId;
if (!m_currentSelectedMaterialId.isNull()) {
updateMaterialSelectState(m_currentSelectedMaterialId, true);
}
emit currentSelectedMaterialChanged(m_currentSelectedMaterialId);
}
}
void MaterialListWidget::mousePressEvent(QMouseEvent* event)
{
QModelIndex itemIndex = indexAt(event->pos());
QTreeView::mousePressEvent(event);
if (event->button() == Qt::LeftButton) {
bool multiple = QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier);
if (itemIndex.isValid()) {
QTreeWidgetItem* item = itemFromIndex(itemIndex);
auto materialId = dust3d::Uuid(item->data(itemIndex.column(), Qt::UserRole).toString().toUtf8().constData());
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
bool startAdd = false;
bool stopAdd = false;
std::vector<dust3d::Uuid> waitQueue;
for (const auto& childId : m_document->materialIdList) {
if (m_shiftStartMaterialId == childId || materialId == childId) {
if (startAdd) {
stopAdd = true;
} else {
startAdd = true;
}
}
if (startAdd)
waitQueue.push_back(childId);
if (stopAdd)
break;
}
if (stopAdd && !waitQueue.empty()) {
if (!m_selectedMaterialIds.empty()) {
for (const auto& id : m_selectedMaterialIds) {
updateMaterialSelectState(id, false);
}
m_selectedMaterialIds.clear();
}
if (!m_currentSelectedMaterialId.isNull()) {
m_currentSelectedMaterialId = dust3d::Uuid();
}
for (const auto& waitId : waitQueue) {
selectMaterial(waitId, true);
}
}
return;
} else {
m_shiftStartMaterialId = materialId;
}
selectMaterial(materialId, multiple);
return;
}
if (!multiple)
selectMaterial(dust3d::Uuid());
}
}
bool MaterialListWidget::isMaterialSelected(dust3d::Uuid materialId)
{
return (m_currentSelectedMaterialId == materialId || m_selectedMaterialIds.find(materialId) != m_selectedMaterialIds.end());
}
void MaterialListWidget::showContextMenu(const QPoint& pos)
{
if (!m_hasContextMenu)
return;
QMenu contextMenu(this);
std::set<dust3d::Uuid> unorderedMaterialIds = m_selectedMaterialIds;
if (!m_currentSelectedMaterialId.isNull())
unorderedMaterialIds.insert(m_currentSelectedMaterialId);
std::vector<dust3d::Uuid> materialIds;
for (const auto& cand : m_document->materialIdList) {
if (unorderedMaterialIds.find(cand) != unorderedMaterialIds.end())
materialIds.push_back(cand);
}
QAction modifyAction(tr("Modify"), this);
if (materialIds.size() == 1) {
connect(&modifyAction, &QAction::triggered, this, [=]() {
emit modifyMaterial(*materialIds.begin());
});
contextMenu.addAction(&modifyAction);
}
QAction copyAction(tr("Copy"), this);
if (!materialIds.empty()) {
connect(&copyAction, &QAction::triggered, this, &MaterialListWidget::copy);
contextMenu.addAction(&copyAction);
}
QAction pasteAction(tr("Paste"), this);
if (m_document->hasPastableMaterialsInClipboard()) {
connect(&pasteAction, &QAction::triggered, m_document, &Document::paste);
contextMenu.addAction(&pasteAction);
}
QAction deleteAction(tr("Delete"), this);
if (!materialIds.empty()) {
connect(&deleteAction, &QAction::triggered, [=]() {
for (const auto& materialId : materialIds)
emit removeMaterial(materialId);
});
contextMenu.addAction(&deleteAction);
}
contextMenu.exec(mapToGlobal(pos));
}
void MaterialListWidget::resizeEvent(QResizeEvent* event)
{
QTreeWidget::resizeEvent(event);
if (calculateColumnCount() != columnCount())
reload();
}
int MaterialListWidget::calculateColumnCount()
{
if (nullptr == parentWidget())
return 0;
int columns = parentWidget()->width() / Theme::materialPreviewImageSize;
if (0 == columns)
columns = 1;
return columns;
}
void MaterialListWidget::reload()
{
removeAllContent();
int columns = calculateColumnCount();
if (0 == columns)
return;
int columnWidth = parentWidget()->width() / columns;
//qDebug() << "parentWidth:" << parentWidget()->width() << "columnWidth:" << columnWidth << "columns:" << columns;
setColumnCount(columns);
for (int i = 0; i < columns; i++)
setColumnWidth(i, columnWidth);
std::vector<dust3d::Uuid> orderedMaterialIdList = m_document->materialIdList;
std::sort(orderedMaterialIdList.begin(), orderedMaterialIdList.end(), [&](const dust3d::Uuid& firstMaterialId, const dust3d::Uuid& secondMaterialId) {
const auto* firstMaterial = m_document->findMaterial(firstMaterialId);
const auto* secondMaterial = m_document->findMaterial(secondMaterialId);
if (nullptr == firstMaterial || nullptr == secondMaterial)
return false;
return QString::compare(firstMaterial->name, secondMaterial->name, Qt::CaseInsensitive) < 0;
});
decltype(orderedMaterialIdList.size()) materialIndex = 0;
while (materialIndex < orderedMaterialIdList.size()) {
QTreeWidgetItem* item = new QTreeWidgetItem(this);
item->setFlags((item->flags() | Qt::ItemIsEnabled) & ~(Qt::ItemIsSelectable) & ~(Qt::ItemIsEditable));
for (int col = 0; col < columns && materialIndex < orderedMaterialIdList.size(); col++, materialIndex++) {
const auto& materialId = orderedMaterialIdList[materialIndex];
item->setSizeHint(col, QSize(columnWidth, MaterialWidget::preferredHeight() + 2));
item->setData(col, Qt::UserRole, QString(materialId.toString().c_str()));
MaterialWidget* widget = new MaterialWidget(m_document, materialId);
connect(widget, &MaterialWidget::modifyMaterial, this, &MaterialListWidget::modifyMaterial);
setItemWidget(item, col, widget);
widget->reload();
widget->updateCheckedState(isMaterialSelected(materialId));
m_itemMap[materialId] = std::make_pair(item, col);
}
invisibleRootItem()->addChild(item);
}
}
void MaterialListWidget::setHasContextMenu(bool hasContextMenu)
{
m_hasContextMenu = hasContextMenu;
}
void MaterialListWidget::removeAllContent()
{
m_itemMap.clear();
clear();
}
void MaterialListWidget::copy()
{
if (m_selectedMaterialIds.empty() && m_currentSelectedMaterialId.isNull())
return;
std::set<dust3d::Uuid> limitMaterialIds = m_selectedMaterialIds;
if (!m_currentSelectedMaterialId.isNull())
limitMaterialIds.insert(m_currentSelectedMaterialId);
std::set<dust3d::Uuid> emptySet;
dust3d::Snapshot snapshot;
m_document->toSnapshot(&snapshot, emptySet, DocumentToSnapshotFor::Materials,
limitMaterialIds);
std::string snapshotXml;
dust3d::saveSnapshotToXmlString(snapshot, snapshotXml);
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(snapshotXml.c_str());
}

View File

@ -1,48 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_LIST_WIDGET_H_
#define DUST3D_APPLICATION_MATERIAL_LIST_WIDGET_H_
#include "material_widget.h"
#include <QMouseEvent>
#include <QTreeWidget>
#include <map>
class Document;
class MaterialListWidget : public QTreeWidget {
Q_OBJECT
signals:
void removeMaterial(dust3d::Uuid materialId);
void modifyMaterial(dust3d::Uuid materialId);
void cornerButtonClicked(dust3d::Uuid materialId);
void currentSelectedMaterialChanged(dust3d::Uuid materialId);
public:
MaterialListWidget(const Document* document, QWidget* parent = nullptr);
bool isMaterialSelected(dust3d::Uuid materialId);
void enableMultipleSelection(bool enabled);
public slots:
void reload();
void removeAllContent();
void materialRemoved(dust3d::Uuid materialId);
void showContextMenu(const QPoint& pos);
void selectMaterial(dust3d::Uuid materialId, bool multiple = false);
void copy();
void setHasContextMenu(bool hasContextMenu);
protected:
void resizeEvent(QResizeEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
private:
int calculateColumnCount();
void updateMaterialSelectState(dust3d::Uuid materialId, bool selected);
const Document* m_document = nullptr;
std::map<dust3d::Uuid, std::pair<QTreeWidgetItem*, int>> m_itemMap;
std::set<dust3d::Uuid> m_selectedMaterialIds;
dust3d::Uuid m_currentSelectedMaterialId;
dust3d::Uuid m_shiftStartMaterialId;
bool m_hasContextMenu = true;
bool m_multipleSelectionEnabled = true;
};
#endif

View File

@ -1,64 +0,0 @@
#include "material_manage_widget.h"
#include "document.h"
#include "info_label.h"
#include "material_edit_widget.h"
#include "theme.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QVBoxLayout>
MaterialManageWidget::MaterialManageWidget(const Document* document, QWidget* parent)
: QWidget(parent)
, m_document(document)
{
QPushButton* addMaterialButton = new QPushButton(Theme::awesome()->icon(fa::plus), tr("Add Material..."));
connect(addMaterialButton, &QPushButton::clicked, this, &MaterialManageWidget::showAddMaterialDialog);
QHBoxLayout* toolsLayout = new QHBoxLayout;
toolsLayout->addWidget(addMaterialButton);
m_materialListWidget = new MaterialListWidget(document);
connect(m_materialListWidget, &MaterialListWidget::modifyMaterial, this, &MaterialManageWidget::showMaterialDialog);
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->addLayout(toolsLayout);
mainLayout->addWidget(m_materialListWidget);
setLayout(mainLayout);
}
MaterialListWidget* MaterialManageWidget::materialListWidget()
{
return m_materialListWidget;
}
QSize MaterialManageWidget::sizeHint() const
{
return QSize(Theme::sidebarPreferredWidth, 0);
}
void MaterialManageWidget::showAddMaterialDialog()
{
showMaterialDialog(dust3d::Uuid());
}
void MaterialManageWidget::showMaterialDialog(dust3d::Uuid materialId)
{
MaterialEditWidget* materialEditWidget = new MaterialEditWidget(m_document);
materialEditWidget->setAttribute(Qt::WA_DeleteOnClose);
if (!materialId.isNull()) {
const Material* material = m_document->findMaterial(materialId);
if (nullptr != material) {
materialEditWidget->setEditMaterialId(materialId);
materialEditWidget->setEditMaterialName(material->name);
materialEditWidget->setEditMaterialLayers(material->layers);
materialEditWidget->clearUnsaveState();
}
}
materialEditWidget->show();
connect(materialEditWidget, &QDialog::destroyed, [=]() {
emit unregisterDialog((QWidget*)materialEditWidget);
});
emit registerDialog((QWidget*)materialEditWidget);
}

View File

@ -1,30 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_MANAGE_WIDGET_H_
#define DUST3D_APPLICATION_MATERIAL_MANAGE_WIDGET_H_
#include "material_list_widget.h"
#include <QWidget>
class Document;
class MaterialManageWidget : public QWidget {
Q_OBJECT
signals:
void registerDialog(QWidget* widget);
void unregisterDialog(QWidget* widget);
public:
MaterialManageWidget(const Document* document, QWidget* parent = nullptr);
MaterialListWidget* materialListWidget();
protected:
virtual QSize sizeHint() const;
public slots:
void showAddMaterialDialog();
void showMaterialDialog(dust3d::Uuid materialId);
private:
const Document* m_document = nullptr;
MaterialListWidget* m_materialListWidget = nullptr;
};
#endif

View File

@ -1,131 +0,0 @@
#include "material_previews_generator.h"
#include "image_forever.h"
#include "mesh_generator.h"
#include "mesh_result_post_processor.h"
#include "texture_generator.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QFile>
#include <QGuiApplication>
#include <dust3d/base/ds3_file.h>
#include <dust3d/base/snapshot_xml.h>
MaterialPreviewsGenerator::MaterialPreviewsGenerator()
{
}
MaterialPreviewsGenerator::~MaterialPreviewsGenerator()
{
for (auto& item : m_previews) {
delete item.second;
}
}
void MaterialPreviewsGenerator::addMaterial(dust3d::Uuid materialId, const std::vector<MaterialLayer>& layers)
{
m_materials.push_back({ materialId, layers });
}
const std::set<dust3d::Uuid>& MaterialPreviewsGenerator::generatedPreviewMaterialIds()
{
return m_generatedMaterialIds;
}
ModelMesh* MaterialPreviewsGenerator::takePreview(dust3d::Uuid materialId)
{
ModelMesh* resultMesh = m_previews[materialId];
m_previews[materialId] = nullptr;
return resultMesh;
}
void MaterialPreviewsGenerator::generate()
{
dust3d::Snapshot* snapshot = new dust3d::Snapshot;
QFile file(":/resources/material-demo-model.ds3");
file.open(QFile::ReadOnly);
QByteArray fileData = file.readAll();
std::vector<dust3d::Uuid> partIds;
dust3d::Ds3FileReader ds3Reader((const std::uint8_t*)fileData.data(), fileData.size());
for (int i = 0; i < (int)ds3Reader.items().size(); ++i) {
dust3d::Ds3ReaderItem item = ds3Reader.items().at(i);
if (item.type == "model") {
std::vector<std::uint8_t> data;
ds3Reader.loadItem(item.name, &data);
std::string xmlString((char*)data.data(), data.size());
dust3d::loadSnapshotFromXmlString(snapshot, (char*)xmlString.c_str());
for (const auto& item : snapshot->parts) {
partIds.push_back(dust3d::Uuid(item.first));
}
}
}
dust3d::MeshGenerator::GeneratedCacheContext* cacheContext = new dust3d::MeshGenerator::GeneratedCacheContext();
MeshGenerator* meshGenerator = new MeshGenerator(snapshot);
meshGenerator->setGeneratedCacheContext(cacheContext);
meshGenerator->generate();
for (const auto& mirror : cacheContext->partMirrorIdMap) {
partIds.push_back(dust3d::Uuid(mirror.first));
}
dust3d::Object* object = meshGenerator->takeObject();
if (nullptr != object) {
MeshResultPostProcessor* poseProcessor = new MeshResultPostProcessor(*object);
poseProcessor->poseProcess();
delete object;
object = poseProcessor->takePostProcessedObject();
delete poseProcessor;
}
if (nullptr != object) {
for (const auto& material : m_materials) {
TextureGenerator* textureGenerator = new TextureGenerator(*object);
for (const auto& layer : material.second) {
for (const auto& mapItem : layer.maps) {
const QImage* image = ImageForever::get(mapItem.imageId);
if (nullptr == image)
continue;
for (const auto& partId : partIds) {
if (dust3d::TextureType::BaseColor == mapItem.forWhat)
textureGenerator->addPartColorMap(partId, image, layer.tileScale);
else if (dust3d::TextureType::Normal == mapItem.forWhat)
textureGenerator->addPartNormalMap(partId, image, layer.tileScale);
else if (dust3d::TextureType::Metallic == mapItem.forWhat)
textureGenerator->addPartMetalnessMap(partId, image, layer.tileScale);
else if (dust3d::TextureType::Roughness == mapItem.forWhat)
textureGenerator->addPartRoughnessMap(partId, image, layer.tileScale);
else if (dust3d::TextureType::AmbientOcclusion == mapItem.forWhat)
textureGenerator->addPartAmbientOcclusionMap(partId, image, layer.tileScale);
}
}
}
textureGenerator->generate();
ModelMesh* texturedResultMesh = textureGenerator->takeResultMesh();
if (nullptr != texturedResultMesh) {
m_previews[material.first] = new ModelMesh(*texturedResultMesh);
m_generatedMaterialIds.insert(material.first);
delete texturedResultMesh;
}
delete textureGenerator;
}
}
delete object;
delete meshGenerator;
delete cacheContext;
}
void MaterialPreviewsGenerator::process()
{
QElapsedTimer countTimeConsumed;
countTimeConsumed.start();
generate();
qDebug() << "The material previews generation took" << countTimeConsumed.elapsed() << "milliseconds";
emit finished();
}

View File

@ -1,30 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_PREVIEWS_GENERATOR_H_
#define DUST3D_APPLICATION_MATERIAL_PREVIEWS_GENERATOR_H_
#include "material_layer.h"
#include "model_mesh.h"
#include <QObject>
#include <map>
#include <vector>
class MaterialPreviewsGenerator : public QObject {
Q_OBJECT
public:
MaterialPreviewsGenerator();
~MaterialPreviewsGenerator();
void addMaterial(dust3d::Uuid materialId, const std::vector<MaterialLayer>& layers);
const std::set<dust3d::Uuid>& generatedPreviewMaterialIds();
ModelMesh* takePreview(dust3d::Uuid materialId);
void generate();
signals:
void finished();
public slots:
void process();
private:
std::vector<std::pair<dust3d::Uuid, std::vector<MaterialLayer>>> m_materials;
std::map<dust3d::Uuid, ModelMesh*> m_previews;
std::set<dust3d::Uuid> m_generatedMaterialIds;
};
#endif

View File

@ -1,98 +0,0 @@
#include "material_widget.h"
#include "document.h"
#include <QVBoxLayout>
MaterialWidget::MaterialWidget(const Document* document, dust3d::Uuid materialId)
: m_materialId(materialId)
, m_document(document)
{
setObjectName("MaterialFrame");
m_previewWidget = new ModelWidget(this);
m_previewWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
m_previewWidget->setFixedSize(Theme::materialPreviewImageSize, Theme::materialPreviewImageSize);
m_previewWidget->enableMove(false);
m_previewWidget->enableZoom(false);
m_nameLabel = new QLabel;
m_nameLabel->setAlignment(Qt::AlignCenter);
m_nameLabel->setStyleSheet("background: qlineargradient(x1:0.5 y1:-15.5, x2:0.5 y2:1, stop:0 " + Theme::white.name() + ", stop:1 #252525);");
QFont nameFont;
nameFont.setWeight(QFont::Light);
nameFont.setBold(false);
m_nameLabel->setFont(nameFont);
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addStretch();
mainLayout->addWidget(m_nameLabel);
setLayout(mainLayout);
setFixedSize(Theme::materialPreviewImageSize, MaterialWidget::preferredHeight());
connect(document, &Document::materialNameChanged, this, &MaterialWidget::updateName);
connect(document, &Document::materialPreviewChanged, this, &MaterialWidget::updatePreview);
}
void MaterialWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
m_previewWidget->move((width() - Theme::materialPreviewImageSize) / 2, 0);
}
int MaterialWidget::preferredHeight()
{
return Theme::materialPreviewImageSize;
}
void MaterialWidget::reload()
{
updatePreview(m_materialId);
updateName(m_materialId);
}
void MaterialWidget::updatePreview(dust3d::Uuid materialId)
{
if (materialId != m_materialId)
return;
const Material* material = m_document->findMaterial(m_materialId);
if (!material) {
qDebug() << "Material not found:" << m_materialId;
return;
}
ModelMesh* previewMesh = material->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh);
}
void MaterialWidget::updateName(dust3d::Uuid materialId)
{
if (materialId != m_materialId)
return;
const Material* material = m_document->findMaterial(m_materialId);
if (!material) {
qDebug() << "Material not found:" << m_materialId;
return;
}
m_nameLabel->setText(material->name);
}
void MaterialWidget::updateCheckedState(bool checked)
{
if (checked)
setStyleSheet("#MaterialFrame {border: 1px solid " + Theme::red.name() + ";}");
else
setStyleSheet("#MaterialFrame {border: 1px solid transparent;}");
}
ModelWidget* MaterialWidget::previewWidget()
{
return m_previewWidget;
}
void MaterialWidget::mouseDoubleClickEvent(QMouseEvent* event)
{
QFrame::mouseDoubleClickEvent(event);
emit modifyMaterial(m_materialId);
}

View File

@ -1,38 +0,0 @@
#ifndef DUST3D_APPLICATION_MATERIAL_WIDGET_H_
#define DUST3D_APPLICATION_MATERIAL_WIDGET_H_
#include "model_widget.h"
#include <QFrame>
#include <QIcon>
#include <QLabel>
#include <QPushButton>
class Document;
class MaterialWidget : public QFrame {
Q_OBJECT
signals:
void modifyMaterial(dust3d::Uuid materialId);
public:
MaterialWidget(const Document* document, dust3d::Uuid materialId);
static int preferredHeight();
ModelWidget* previewWidget();
protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
public slots:
void reload();
void updatePreview(dust3d::Uuid materialId);
void updateName(dust3d::Uuid materialId);
void updateCheckedState(bool checked);
private:
dust3d::Uuid m_materialId;
const Document* m_document = nullptr;
ModelWidget* m_previewWidget = nullptr;
QLabel* m_nameLabel = nullptr;
};
#endif

View File

@ -1,702 +0,0 @@
#include "texture_generator.h"
#include "debug.h"
#include "material.h"
#include "theme.h"
#include <QElapsedTimer>
#include <QGuiApplication>
#include <QPainter>
#include <QPolygon>
#include <QRadialGradient>
#include <QRegion>
#include <dust3d/base/texture_type.h>
QColor TextureGenerator::m_defaultTextureColor = Qt::transparent;
TextureGenerator::TextureGenerator(const dust3d::Object& object, dust3d::Snapshot* snapshot)
: m_snapshot(snapshot)
{
m_object = new dust3d::Object();
*m_object = object;
}
TextureGenerator::~TextureGenerator()
{
delete m_object;
delete m_resultTextureColorImage;
delete m_resultTextureNormalImage;
delete m_resultTextureRoughnessImage;
delete m_resultTextureMetalnessImage;
delete m_resultTextureAmbientOcclusionImage;
delete m_resultMesh;
delete m_snapshot;
}
QImage* TextureGenerator::takeResultTextureColorImage()
{
QImage* resultTextureColorImage = m_resultTextureColorImage;
m_resultTextureColorImage = nullptr;
return resultTextureColorImage;
}
QImage* TextureGenerator::takeResultTextureNormalImage()
{
QImage* resultTextureNormalImage = m_resultTextureNormalImage;
m_resultTextureNormalImage = nullptr;
return resultTextureNormalImage;
}
QImage* TextureGenerator::takeResultTextureRoughnessImage()
{
QImage* resultTextureRoughnessImage = m_resultTextureRoughnessImage;
m_resultTextureRoughnessImage = nullptr;
return resultTextureRoughnessImage;
}
QImage* TextureGenerator::takeResultTextureMetalnessImage()
{
QImage* resultTextureMetalnessImage = m_resultTextureMetalnessImage;
m_resultTextureMetalnessImage = nullptr;
return resultTextureMetalnessImage;
}
QImage* TextureGenerator::takeResultTextureAmbientOcclusionImage()
{
QImage* resultTextureAmbientOcclusionImage = m_resultTextureAmbientOcclusionImage;
m_resultTextureAmbientOcclusionImage = nullptr;
return resultTextureAmbientOcclusionImage;
}
dust3d::Object* TextureGenerator::takeObject()
{
dust3d::Object* object = m_object;
m_object = nullptr;
return object;
}
ModelMesh* TextureGenerator::takeResultMesh()
{
ModelMesh* resultMesh = m_resultMesh;
m_resultMesh = nullptr;
return resultMesh;
}
void TextureGenerator::addPartColorMap(dust3d::Uuid partId, const QImage* image, float tileScale)
{
if (nullptr == image)
return;
m_partColorTextureMap[partId] = std::make_pair(*image, tileScale);
}
void TextureGenerator::addPartNormalMap(dust3d::Uuid partId, const QImage* image, float tileScale)
{
if (nullptr == image)
return;
m_partNormalTextureMap[partId] = std::make_pair(*image, tileScale);
}
void TextureGenerator::addPartMetalnessMap(dust3d::Uuid partId, const QImage* image, float tileScale)
{
if (nullptr == image)
return;
m_partMetalnessTextureMap[partId] = std::make_pair(*image, tileScale);
}
void TextureGenerator::addPartRoughnessMap(dust3d::Uuid partId, const QImage* image, float tileScale)
{
if (nullptr == image)
return;
m_partRoughnessTextureMap[partId] = std::make_pair(*image, tileScale);
}
void TextureGenerator::addPartAmbientOcclusionMap(dust3d::Uuid partId, const QImage* image, float tileScale)
{
if (nullptr == image)
return;
m_partAmbientOcclusionTextureMap[partId] = std::make_pair(*image, tileScale);
}
void TextureGenerator::prepare()
{
if (nullptr == m_snapshot)
return;
std::map<dust3d::Uuid, dust3d::Uuid> updatedMaterialIdMap;
std::map<dust3d::Uuid, bool> updatedCountershadedMap;
for (const auto& partIt : m_snapshot->parts) {
dust3d::Uuid materialId;
auto materialIdIt = partIt.second.find("materialId");
if (materialIdIt != partIt.second.end())
materialId = dust3d::Uuid(materialIdIt->second);
dust3d::Uuid partId = dust3d::Uuid(partIt.first);
updatedMaterialIdMap.insert({ partId, materialId });
updatedCountershadedMap.insert({ partId,
dust3d::String::isTrue(dust3d::String::valueOrEmpty(partIt.second, "countershaded")) });
}
for (const auto& bmeshNode : m_object->nodes) {
bool countershaded = bmeshNode.countershaded;
auto findUpdatedCountershadedMap = updatedCountershadedMap.find(bmeshNode.mirrorFromPartId.isNull() ? bmeshNode.partId : bmeshNode.mirrorFromPartId);
if (findUpdatedCountershadedMap != updatedCountershadedMap.end())
countershaded = findUpdatedCountershadedMap->second;
if (countershaded)
m_countershadedPartIds.insert(bmeshNode.partId);
for (size_t i = 0; i < (int)dust3d::TextureType::Count - 1; ++i) {
dust3d::TextureType forWhat = (dust3d::TextureType)(i + 1);
MaterialTextures materialTextures;
dust3d::Uuid materialId = bmeshNode.materialId;
auto findUpdatedMaterialIdResult = updatedMaterialIdMap.find(bmeshNode.mirrorFromPartId.isNull() ? bmeshNode.partId : bmeshNode.mirrorFromPartId);
if (findUpdatedMaterialIdResult != updatedMaterialIdMap.end())
materialId = findUpdatedMaterialIdResult->second;
float tileScale = 1.0;
initializeMaterialTexturesFromSnapshot(*m_snapshot, materialId, materialTextures, tileScale);
const QImage* image = materialTextures.textureImages[i];
if (nullptr != image) {
if (dust3d::TextureType::BaseColor == forWhat)
addPartColorMap(bmeshNode.partId, image, tileScale);
else if (dust3d::TextureType::Normal == forWhat)
addPartNormalMap(bmeshNode.partId, image, tileScale);
else if (dust3d::TextureType::Metallic == forWhat)
addPartMetalnessMap(bmeshNode.partId, image, tileScale);
else if (dust3d::TextureType::Roughness == forWhat)
addPartRoughnessMap(bmeshNode.partId, image, tileScale);
else if (dust3d::TextureType::AmbientOcclusion == forWhat)
addPartAmbientOcclusionMap(bmeshNode.partId, image, tileScale);
}
}
}
}
bool TextureGenerator::hasTransparencySettings()
{
return m_hasTransparencySettings;
}
void TextureGenerator::generate()
{
m_resultMesh = new ModelMesh(*m_object);
if (nullptr == m_object->triangleVertexUvs())
return;
if (nullptr == m_object->triangleSourceNodes())
return;
if (nullptr == m_object->partUvRects())
return;
QElapsedTimer countTimeConsumed;
countTimeConsumed.start();
prepare();
bool hasNormalMap = false;
bool hasMetalnessMap = false;
bool hasRoughnessMap = false;
bool hasAmbientOcclusionMap = false;
const auto& triangleVertexUvs = *m_object->triangleVertexUvs();
const auto& triangleSourceNodes = *m_object->triangleSourceNodes();
const auto& partUvRects = *m_object->partUvRects();
const auto& triangleNormals = m_object->triangleNormals;
std::map<dust3d::Uuid, QColor> partColorMap;
std::map<std::pair<dust3d::Uuid, dust3d::Uuid>, const dust3d::ObjectNode*> nodeMap;
std::map<dust3d::Uuid, float> partColorSolubilityMap;
std::map<dust3d::Uuid, float> partMetalnessMap;
std::map<dust3d::Uuid, float> partRoughnessMap;
for (const auto& item : m_object->nodes) {
if (!m_hasTransparencySettings) {
if (!qFuzzyCompare(1.0, item.color.alpha()))
m_hasTransparencySettings = true;
}
nodeMap.insert({ { item.partId, item.nodeId }, &item });
partColorMap.insert({ item.partId, QColor(item.color.toString().c_str()) });
partColorSolubilityMap.insert({ item.partId, item.colorSolubility });
partMetalnessMap.insert({ item.partId, item.metalness });
partRoughnessMap.insert({ item.partId, item.roughness });
}
m_resultTextureColorImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32);
m_resultTextureColorImage->fill(m_hasTransparencySettings ? m_defaultTextureColor : Qt::white);
m_resultTextureNormalImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32);
m_resultTextureNormalImage->fill(QColor(128, 128, 255));
m_resultTextureMetalnessImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32);
m_resultTextureMetalnessImage->fill(Qt::black);
m_resultTextureRoughnessImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32);
m_resultTextureRoughnessImage->fill(Qt::white);
m_resultTextureAmbientOcclusionImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32);
m_resultTextureAmbientOcclusionImage->fill(Qt::white);
QColor borderColor = Qt::darkGray;
QPen pen(borderColor);
QPainter texturePainter;
texturePainter.begin(m_resultTextureColorImage);
texturePainter.setRenderHint(QPainter::Antialiasing);
texturePainter.setRenderHint(QPainter::HighQualityAntialiasing);
QPainter textureNormalPainter;
textureNormalPainter.begin(m_resultTextureNormalImage);
textureNormalPainter.setRenderHint(QPainter::Antialiasing);
textureNormalPainter.setRenderHint(QPainter::HighQualityAntialiasing);
QPainter textureMetalnessPainter;
textureMetalnessPainter.begin(m_resultTextureMetalnessImage);
textureMetalnessPainter.setRenderHint(QPainter::Antialiasing);
textureMetalnessPainter.setRenderHint(QPainter::HighQualityAntialiasing);
QPainter textureRoughnessPainter;
textureRoughnessPainter.begin(m_resultTextureRoughnessImage);
textureRoughnessPainter.setRenderHint(QPainter::Antialiasing);
textureRoughnessPainter.setRenderHint(QPainter::HighQualityAntialiasing);
QPainter textureAmbientOcclusionPainter;
textureAmbientOcclusionPainter.begin(m_resultTextureAmbientOcclusionImage);
textureAmbientOcclusionPainter.setRenderHint(QPainter::Antialiasing);
textureAmbientOcclusionPainter.setRenderHint(QPainter::HighQualityAntialiasing);
texturePainter.setPen(Qt::NoPen);
for (const auto& it : partUvRects) {
const auto& partId = it.first;
const auto& rects = it.second;
auto findSourceColorResult = partColorMap.find(partId);
if (findSourceColorResult != partColorMap.end()) {
const auto& color = findSourceColorResult->second;
QBrush brush(color);
float fillExpandSize = 2;
for (const auto& rect : rects) {
QRectF translatedRect = {
rect.left() * TextureGenerator::m_textureSize - fillExpandSize,
rect.top() * TextureGenerator::m_textureSize - fillExpandSize,
rect.width() * TextureGenerator::m_textureSize + fillExpandSize * 2,
rect.height() * TextureGenerator::m_textureSize + fillExpandSize * 2
};
texturePainter.fillRect(translatedRect, brush);
}
}
}
for (const auto& it : partUvRects) {
const auto& partId = it.first;
const auto& rects = it.second;
auto findMetalnessResult = partMetalnessMap.find(partId);
if (findMetalnessResult != partMetalnessMap.end()) {
if (qFuzzyCompare(findMetalnessResult->second, (float)0.0))
continue;
const auto& color = QColor(findMetalnessResult->second * 255,
findMetalnessResult->second * 255,
findMetalnessResult->second * 255);
QBrush brush(color);
float fillExpandSize = 2;
for (const auto& rect : rects) {
QRectF translatedRect = {
rect.left() * TextureGenerator::m_textureSize - fillExpandSize,
rect.top() * TextureGenerator::m_textureSize - fillExpandSize,
rect.width() * TextureGenerator::m_textureSize + fillExpandSize * 2,
rect.height() * TextureGenerator::m_textureSize + fillExpandSize * 2
};
textureMetalnessPainter.fillRect(translatedRect, brush);
hasMetalnessMap = true;
}
}
}
for (const auto& it : partUvRects) {
const auto& partId = it.first;
const auto& rects = it.second;
auto findRoughnessResult = partRoughnessMap.find(partId);
if (findRoughnessResult != partRoughnessMap.end()) {
if (qFuzzyCompare(findRoughnessResult->second, (float)1.0))
continue;
const auto& color = QColor(findRoughnessResult->second * 255,
findRoughnessResult->second * 255,
findRoughnessResult->second * 255);
QBrush brush(color);
float fillExpandSize = 2;
for (const auto& rect : rects) {
QRectF translatedRect = {
rect.left() * TextureGenerator::m_textureSize - fillExpandSize,
rect.top() * TextureGenerator::m_textureSize - fillExpandSize,
rect.width() * TextureGenerator::m_textureSize + fillExpandSize * 2,
rect.height() * TextureGenerator::m_textureSize + fillExpandSize * 2
};
textureRoughnessPainter.fillRect(translatedRect, brush);
hasRoughnessMap = true;
}
}
}
auto drawTexture = [&](const std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>>& map, QPainter& painter, bool useAlpha) {
for (const auto& it : partUvRects) {
const auto& partId = it.first;
const auto& rects = it.second;
float alpha = 1.0;
if (useAlpha) {
auto findSourceColorResult = partColorMap.find(partId);
if (findSourceColorResult != partColorMap.end()) {
const auto& color = findSourceColorResult->second;
alpha = color.alphaF();
}
}
auto findTextureResult = map.find(partId);
if (findTextureResult != map.end()) {
const auto& pixmap = findTextureResult->second.first;
const auto& rotatedPixmap = findTextureResult->second.second;
painter.setOpacity(alpha);
for (const auto& rect : rects) {
QRectF translatedRect = {
rect.left() * TextureGenerator::m_textureSize,
rect.top() * TextureGenerator::m_textureSize,
rect.width() * TextureGenerator::m_textureSize,
rect.height() * TextureGenerator::m_textureSize
};
if (translatedRect.width() < translatedRect.height()) {
painter.drawTiledPixmap(translatedRect, rotatedPixmap, QPointF(rect.top(), rect.left()));
} else {
painter.drawTiledPixmap(translatedRect, pixmap, QPointF(rect.left(), rect.top()));
}
}
painter.setOpacity(1.0);
}
}
};
auto convertTextureImageToPixmap = [&](const std::map<dust3d::Uuid, std::pair<QImage, float>>& sourceMap,
std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>>& targetMap) {
for (const auto& it : sourceMap) {
float tileScale = it.second.second;
const auto& image = it.second.first;
auto newSize = image.size() * tileScale;
QImage scaledImage = image.scaled(newSize);
QPoint center = scaledImage.rect().center();
QMatrix matrix;
matrix.translate(center.x(), center.y());
matrix.rotate(90);
auto rotatedImage = scaledImage.transformed(matrix).mirrored(true, false);
targetMap[it.first] = std::make_pair(QPixmap::fromImage(scaledImage),
QPixmap::fromImage(rotatedImage));
}
};
std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>> partColorTexturePixmaps;
std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>> partNormalTexturePixmaps;
std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>> partMetalnessTexturePixmaps;
std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>> partRoughnessTexturePixmaps;
std::map<dust3d::Uuid, std::pair<QPixmap, QPixmap>> partAmbientOcclusionTexturePixmaps;
convertTextureImageToPixmap(m_partColorTextureMap, partColorTexturePixmaps);
convertTextureImageToPixmap(m_partNormalTextureMap, partNormalTexturePixmaps);
convertTextureImageToPixmap(m_partMetalnessTextureMap, partMetalnessTexturePixmaps);
convertTextureImageToPixmap(m_partRoughnessTextureMap, partRoughnessTexturePixmaps);
convertTextureImageToPixmap(m_partAmbientOcclusionTextureMap, partAmbientOcclusionTexturePixmaps);
drawTexture(partColorTexturePixmaps, texturePainter, true);
drawTexture(partNormalTexturePixmaps, textureNormalPainter, false);
drawTexture(partMetalnessTexturePixmaps, textureMetalnessPainter, false);
drawTexture(partRoughnessTexturePixmaps, textureRoughnessPainter, false);
drawTexture(partAmbientOcclusionTexturePixmaps, textureAmbientOcclusionPainter, false);
auto drawBySolubility = [&](const dust3d::Uuid& partId, size_t triangleIndex, size_t firstVertexIndex, size_t secondVertexIndex,
const dust3d::Uuid& neighborPartId) {
const std::vector<dust3d::Vector2>& uv = triangleVertexUvs[triangleIndex];
const auto& allRects = partUvRects.find(partId);
if (allRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
return;
}
const auto& firstPoint = uv[firstVertexIndex];
const auto& secondPoint = uv[secondVertexIndex];
auto edgeLength = (firstPoint - secondPoint).length();
auto middlePoint = (firstPoint + secondPoint) / 2.0;
float alpha = 1.0;
const auto& findColor = partColorMap.find(partId);
if (findColor == partColorMap.end())
return;
alpha = findColor->second.alphaF();
const auto& findNeighborColorSolubility = partColorSolubilityMap.find(neighborPartId);
if (findNeighborColorSolubility == partColorSolubilityMap.end())
return;
if (qFuzzyIsNull(findNeighborColorSolubility->second))
return;
const auto& findNeighborColor = partColorMap.find(neighborPartId);
if (findNeighborColor == partColorMap.end())
return;
for (const auto& it : allRects->second) {
if (it.contains(firstPoint.x(), firstPoint.y()) || it.contains(secondPoint.x(), secondPoint.y())) {
float finalRadius = (it.width() + it.height()) * 0.5 * findNeighborColorSolubility->second;
if (finalRadius < edgeLength)
finalRadius = edgeLength;
dust3d::Rectangle fillTarget((middlePoint.x() - finalRadius),
(middlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
QRectF translatedRect = {
clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize
};
texturePainter.setOpacity(alpha);
auto findTextureResult = partColorTexturePixmaps.find(neighborPartId);
if (findTextureResult != partColorTexturePixmaps.end()) {
const auto& pixmap = findTextureResult->second.first;
const auto& rotatedPixmap = findTextureResult->second.second;
QImage tmpImage(translatedRect.width(), translatedRect.height(), QImage::Format_ARGB32);
QPixmap tmpPixmap = QPixmap::fromImage(tmpImage);
QPainter tmpPainter;
QRectF tmpImageFrame = QRectF(0, 0, translatedRect.width(), translatedRect.height());
// Fill tiled texture
tmpPainter.begin(&tmpPixmap);
tmpPainter.setOpacity(alpha);
if (it.width() < it.height()) {
tmpPainter.drawTiledPixmap(tmpImageFrame, rotatedPixmap, QPointF(translatedRect.top(), translatedRect.left()));
} else {
tmpPainter.drawTiledPixmap(tmpImageFrame, pixmap, translatedRect.topLeft());
}
tmpPainter.setOpacity(1.0);
tmpPainter.end();
// Apply gradient
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize - translatedRect.left(),
middlePoint.y() * TextureGenerator::m_textureSize - translatedRect.top()),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, findNeighborColor->second);
gradient.setColorAt(1.0, Qt::transparent);
tmpPainter.begin(&tmpPixmap);
tmpPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
tmpPainter.fillRect(tmpImageFrame, gradient);
tmpPainter.end();
texturePainter.drawPixmap(translatedRect, tmpPixmap, tmpImageFrame);
} else {
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
middlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, findNeighborColor->second);
gradient.setColorAt(1.0, Qt::transparent);
texturePainter.fillRect(translatedRect, gradient);
}
texturePainter.setOpacity(1.0);
break;
}
}
};
std::map<std::pair<size_t, size_t>, std::tuple<size_t, size_t, size_t>> halfEdgeToTriangleMap;
for (size_t i = 0; i < m_object->triangles.size(); ++i) {
const auto& triangleIndices = m_object->triangles[i];
if (triangleIndices.size() != 3) {
qDebug() << "Found invalid triangle indices";
continue;
}
for (size_t j = 0; j < triangleIndices.size(); ++j) {
size_t k = (j + 1) % triangleIndices.size();
halfEdgeToTriangleMap.insert(std::make_pair(std::make_pair(triangleIndices[j], triangleIndices[k]),
std::make_tuple(i, j, k)));
}
}
for (const auto& it : halfEdgeToTriangleMap) {
auto oppositeHalfEdge = std::make_pair(it.first.second, it.first.first);
const auto& opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
if (opposite == halfEdgeToTriangleMap.end())
continue;
const std::pair<dust3d::Uuid, dust3d::Uuid>& source = triangleSourceNodes[std::get<0>(it.second)];
const std::pair<dust3d::Uuid, dust3d::Uuid>& oppositeSource = triangleSourceNodes[std::get<0>(opposite->second)];
if (source.first == oppositeSource.first)
continue;
drawBySolubility(source.first, std::get<0>(it.second), std::get<1>(it.second), std::get<2>(it.second), oppositeSource.first);
drawBySolubility(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first);
}
// Draw belly white
texturePainter.setCompositionMode(QPainter::CompositionMode_SoftLight);
for (size_t triangleIndex = 0; triangleIndex < m_object->triangles.size(); ++triangleIndex) {
const auto& normal = triangleNormals[triangleIndex];
const std::pair<dust3d::Uuid, dust3d::Uuid>& source = triangleSourceNodes[triangleIndex];
const auto& partId = source.first;
if (m_countershadedPartIds.find(partId) == m_countershadedPartIds.end())
continue;
const auto& allRects = partUvRects.find(partId);
if (allRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
continue;
}
const auto& findObjectNode = nodeMap.find(source);
if (findObjectNode == nodeMap.end())
continue;
const dust3d::ObjectNode* objectNode = findObjectNode->second;
if (qAbs(dust3d::Vector3::dotProduct(objectNode->direction, dust3d::Vector3(0, 1, 0))) >= 0.707) {
if (dust3d::Vector3::dotProduct(normal, dust3d::Vector3(0, 0, 1)) <= 0.0)
continue;
} else {
if (dust3d::Vector3::dotProduct(normal, dust3d::Vector3(0, -1, 0)) <= 0.0)
continue;
}
const auto& triangleIndices = m_object->triangles[triangleIndex];
if (triangleIndices.size() != 3) {
qDebug() << "Found invalid triangle indices";
continue;
}
const std::vector<dust3d::Vector2>& uv = triangleVertexUvs[triangleIndex];
dust3d::Vector2 middlePoint = (uv[0] + uv[1] + uv[2]) / 3.0;
float finalRadius = ((uv[0] - uv[1]).length() + (uv[1] - uv[2]).length() + (uv[2] - uv[0]).length()) / 3.0;
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
middlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(1.0, Qt::transparent);
for (const auto& it : allRects->second) {
if (it.contains(middlePoint.x(), middlePoint.y())) {
dust3d::Rectangle fillTarget((middlePoint.x() - finalRadius),
(middlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
QRectF translatedRect = {
clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize
};
texturePainter.fillRect(translatedRect, gradient);
}
}
// Fill the neighbor halfedges
for (int i = 0; i < 3; ++i) {
int j = (i + 1) % 3;
auto oppositeHalfEdge = std::make_pair(triangleIndices[j], triangleIndices[i]);
const auto& opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
if (opposite == halfEdgeToTriangleMap.end())
continue;
auto oppositeTriangleIndex = std::get<0>(opposite->second);
const std::pair<dust3d::Uuid, dust3d::Uuid>& oppositeSource = triangleSourceNodes[oppositeTriangleIndex];
if (partId == oppositeSource.first)
continue;
const auto& oppositeAllRects = partUvRects.find(oppositeSource.first);
if (oppositeAllRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
continue;
}
const std::vector<dust3d::Vector2>& oppositeUv = triangleVertexUvs[oppositeTriangleIndex];
dust3d::Vector2 oppositeMiddlePoint = (oppositeUv[std::get<1>(opposite->second)] + oppositeUv[std::get<2>(opposite->second)]) * 0.5;
QRadialGradient oppositeGradient(QPointF(oppositeMiddlePoint.x() * TextureGenerator::m_textureSize,
oppositeMiddlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
oppositeGradient.setColorAt(0.0, Qt::white);
oppositeGradient.setColorAt(1.0, Qt::transparent);
for (const auto& it : oppositeAllRects->second) {
if (it.contains(oppositeMiddlePoint.x(), oppositeMiddlePoint.y())) {
dust3d::Rectangle fillTarget((oppositeMiddlePoint.x() - finalRadius),
(oppositeMiddlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
QRectF translatedRect = {
clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize
};
texturePainter.fillRect(translatedRect, oppositeGradient);
}
}
}
}
hasNormalMap = !m_partNormalTextureMap.empty();
if (!m_partMetalnessTextureMap.empty())
hasMetalnessMap = true;
if (!m_partRoughnessTextureMap.empty())
hasRoughnessMap = true;
hasAmbientOcclusionMap = !m_partAmbientOcclusionTextureMap.empty();
texturePainter.end();
textureNormalPainter.end();
textureMetalnessPainter.end();
textureRoughnessPainter.end();
textureAmbientOcclusionPainter.end();
if (!hasNormalMap) {
delete m_resultTextureNormalImage;
m_resultTextureNormalImage = nullptr;
}
if (!hasMetalnessMap && !hasRoughnessMap && !hasAmbientOcclusionMap) {
delete m_resultTextureMetalnessImage;
m_resultTextureMetalnessImage = nullptr;
delete m_resultTextureRoughnessImage;
m_resultTextureRoughnessImage = nullptr;
delete m_resultTextureAmbientOcclusionImage;
m_resultTextureAmbientOcclusionImage = nullptr;
}
m_resultMesh->setTextureImage(new QImage(*m_resultTextureColorImage));
if (nullptr != m_resultTextureNormalImage)
m_resultMesh->setNormalMapImage(new QImage(*m_resultTextureNormalImage));
if (hasMetalnessMap || hasRoughnessMap || hasAmbientOcclusionMap) {
m_resultMesh->setMetalnessRoughnessAmbientOcclusionMapImage(combineMetalnessRoughnessAmbientOcclusionImages(
m_resultTextureMetalnessImage,
m_resultTextureRoughnessImage,
m_resultTextureAmbientOcclusionImage));
m_resultMesh->setHasMetalnessInImage(hasMetalnessMap);
m_resultMesh->setHasRoughnessInImage(hasRoughnessMap);
m_resultMesh->setHasAmbientOcclusionInImage(hasAmbientOcclusionMap);
}
qDebug() << "The texture[" << TextureGenerator::m_textureSize << "x" << TextureGenerator::m_textureSize << "] generation took" << countTimeConsumed.elapsed() << "milliseconds";
}
QImage* TextureGenerator::combineMetalnessRoughnessAmbientOcclusionImages(QImage* metalnessImage,
QImage* roughnessImage,
QImage* ambientOcclusionImage)
{
QImage* textureMetalnessRoughnessAmbientOcclusionImage = nullptr;
if (nullptr != metalnessImage || nullptr != roughnessImage || nullptr != ambientOcclusionImage) {
int textureSize = 0;
if (nullptr != metalnessImage)
textureSize = metalnessImage->height();
if (nullptr != roughnessImage)
textureSize = roughnessImage->height();
if (nullptr != ambientOcclusionImage)
textureSize = ambientOcclusionImage->height();
if (textureSize > 0) {
textureMetalnessRoughnessAmbientOcclusionImage = new QImage(textureSize, textureSize, QImage::Format_ARGB32);
textureMetalnessRoughnessAmbientOcclusionImage->fill(QColor(255, 255, 0));
for (int row = 0; row < textureMetalnessRoughnessAmbientOcclusionImage->height(); ++row) {
for (int col = 0; col < textureMetalnessRoughnessAmbientOcclusionImage->width(); ++col) {
QColor color(255, 255, 0);
if (nullptr != metalnessImage)
color.setBlue(qGray(metalnessImage->pixel(col, row)));
if (nullptr != roughnessImage)
color.setGreen(qGray(roughnessImage->pixel(col, row)));
if (nullptr != ambientOcclusionImage)
color.setRed(qGray(ambientOcclusionImage->pixel(col, row)));
textureMetalnessRoughnessAmbientOcclusionImage->setPixelColor(col, row, color);
}
}
}
}
return textureMetalnessRoughnessAmbientOcclusionImage;
}
void TextureGenerator::process()
{
generate();
emit finished();
}

View File

@ -1,65 +0,0 @@
#ifndef DUST3D_APPLICATION_TEXTURE_GENERATOR_H_
#define DUST3D_APPLICATION_TEXTURE_GENERATOR_H_
#include "model_mesh.h"
#include <QColor>
#include <QImage>
#include <QObject>
#include <QPixmap>
#include <dust3d/base/object.h>
#include <dust3d/base/snapshot.h>
#include <vector>
class TextureGenerator : public QObject {
Q_OBJECT
public:
TextureGenerator(const dust3d::Object& object, dust3d::Snapshot* snapshot = nullptr);
~TextureGenerator();
QImage* takeResultTextureColorImage();
QImage* takeResultTextureNormalImage();
QImage* takeResultTextureRoughnessImage();
QImage* takeResultTextureMetalnessImage();
QImage* takeResultTextureAmbientOcclusionImage();
dust3d::Object* takeObject();
ModelMesh* takeResultMesh();
bool hasTransparencySettings();
void addPartColorMap(dust3d::Uuid partId, const QImage* image, float tileScale);
void addPartNormalMap(dust3d::Uuid partId, const QImage* image, float tileScale);
void addPartMetalnessMap(dust3d::Uuid partId, const QImage* image, float tileScale);
void addPartRoughnessMap(dust3d::Uuid partId, const QImage* image, float tileScale);
void addPartAmbientOcclusionMap(dust3d::Uuid partId, const QImage* image, float tileScale);
void generate();
static QImage* combineMetalnessRoughnessAmbientOcclusionImages(QImage* metalnessImage,
QImage* roughnessImage,
QImage* ambientOcclusionImage);
signals:
void finished();
public slots:
void process();
public:
static QColor m_defaultTextureColor;
private:
void prepare();
private:
dust3d::Object* m_object = nullptr;
QImage* m_resultTextureColorImage = nullptr;
QImage* m_resultTextureNormalImage = nullptr;
QImage* m_resultTextureRoughnessImage = nullptr;
QImage* m_resultTextureMetalnessImage = nullptr;
QImage* m_resultTextureAmbientOcclusionImage = nullptr;
ModelMesh* m_resultMesh = nullptr;
std::map<dust3d::Uuid, std::pair<QImage, float>> m_partColorTextureMap;
std::map<dust3d::Uuid, std::pair<QImage, float>> m_partNormalTextureMap;
std::map<dust3d::Uuid, std::pair<QImage, float>> m_partMetalnessTextureMap;
std::map<dust3d::Uuid, std::pair<QImage, float>> m_partRoughnessTextureMap;
std::map<dust3d::Uuid, std::pair<QImage, float>> m_partAmbientOcclusionTextureMap;
std::set<dust3d::Uuid> m_countershadedPartIds;
dust3d::Snapshot* m_snapshot = nullptr;
bool m_hasTransparencySettings = false;
int m_textureSize = 1024;
};
#endif

View File

@ -20,7 +20,6 @@ int Theme::toolIconSize = 0;
int Theme::miniIconFontSize = 0; int Theme::miniIconFontSize = 0;
int Theme::miniIconSize = 0; int Theme::miniIconSize = 0;
int Theme::partPreviewImageSize = 0; int Theme::partPreviewImageSize = 0;
int Theme::materialPreviewImageSize = 0;
int Theme::sidebarPreferredWidth = 0; int Theme::sidebarPreferredWidth = 0;
int Theme::previewIconBorderSize = 0; int Theme::previewIconBorderSize = 0;
int Theme::previewIconMargin = 0; int Theme::previewIconMargin = 0;
@ -57,7 +56,6 @@ void Theme::initialize()
Theme::miniIconSize = (int)(Theme::miniIconFontSize * 1.67); Theme::miniIconSize = (int)(Theme::miniIconFontSize * 1.67);
Theme::partPreviewImageSize = (Theme::miniIconSize * 2.3); Theme::partPreviewImageSize = (Theme::miniIconSize * 2.3);
Theme::sidebarPreferredWidth = Theme::partPreviewImageSize * 4.5; Theme::sidebarPreferredWidth = Theme::partPreviewImageSize * 4.5;
Theme::materialPreviewImageSize = Theme::sidebarPreferredWidth * 0.4;
Theme::previewIconBorderSize = std::max(1, Theme::partPreviewImageSize / 20); Theme::previewIconBorderSize = std::max(1, Theme::partPreviewImageSize / 20);
Theme::previewIconMargin = std::max(1, Theme::previewIconBorderSize / 2); Theme::previewIconMargin = std::max(1, Theme::previewIconBorderSize / 2);
Theme::previewIconBorderRadius = std::max(3, Theme::partPreviewImageSize / 10); Theme::previewIconBorderRadius = std::max(3, Theme::partPreviewImageSize / 10);

View File

@ -25,7 +25,6 @@ public:
static int miniIconFontSize; static int miniIconFontSize;
static int miniIconSize; static int miniIconSize;
static int partPreviewImageSize; static int partPreviewImageSize;
static int materialPreviewImageSize;
static int sidebarPreferredWidth; static int sidebarPreferredWidth;
static int previewIconBorderSize; static int previewIconBorderSize;
static int previewIconMargin; static int previewIconMargin;

View File

@ -1,7 +1,11 @@
#include "uv_map_generator.h" #include "uv_map_generator.h"
#include "image_forever.h" #include "image_forever.h"
#include <QMatrix>
#include <QPainter>
#include <dust3d/uv/uv_map_packer.h> #include <dust3d/uv/uv_map_packer.h>
size_t UvMapGenerator::m_textureSize = 1024;
UvMapGenerator::UvMapGenerator(std::unique_ptr<dust3d::Object> object, std::unique_ptr<dust3d::Snapshot> snapshot) UvMapGenerator::UvMapGenerator(std::unique_ptr<dust3d::Object> object, std::unique_ptr<dust3d::Snapshot> snapshot)
: m_object(std::move(object)) : m_object(std::move(object))
, m_snapshot(std::move(snapshot)) , m_snapshot(std::move(snapshot))
@ -14,16 +18,53 @@ void UvMapGenerator::process()
emit finished(); emit finished();
} }
std::unique_ptr<QImage> UvMapGenerator::takeResultTextureColorImage()
{
return std::move(m_textureColorImage);
}
std::unique_ptr<QImage> UvMapGenerator::takeResultTextureNormalImage()
{
return std::move(m_textureNormalImage);
}
std::unique_ptr<QImage> UvMapGenerator::takeResultTextureRoughnessImage()
{
return std::move(m_textureRoughnessImage);
}
std::unique_ptr<QImage> UvMapGenerator::takeResultTextureMetalnessImage()
{
return std::move(m_textureMetalnessImage);
}
std::unique_ptr<QImage> UvMapGenerator::takeResultTextureAmbientOcclusionImage()
{
return std::move(m_textureAmbientOcclusionImage);
}
std::unique_ptr<ModelMesh> UvMapGenerator::takeResultMesh()
{
return std::move(m_mesh);
}
std::unique_ptr<dust3d::Object> UvMapGenerator::takeObject() std::unique_ptr<dust3d::Object> UvMapGenerator::takeObject()
{ {
return std::move(m_object); return std::move(m_object);
} }
bool UvMapGenerator::hasTransparencySettings() const
{
return m_hasTransparencySettings;
}
void UvMapGenerator::generate() void UvMapGenerator::generate()
{ {
if (nullptr == m_object) if (nullptr == m_object)
return; return;
m_mesh = std::make_unique<ModelMesh>(*m_object);
if (nullptr == m_snapshot) if (nullptr == m_snapshot)
return; return;
@ -50,5 +91,78 @@ void UvMapGenerator::generate()
m_mapPacker->pack(); m_mapPacker->pack();
// TODO: m_textureColorImage = std::make_unique<QImage>(UvMapGenerator::m_textureSize, UvMapGenerator::m_textureSize, QImage::Format_ARGB32);
m_textureColorImage->fill(Qt::white);
QPainter colorTexturePainter;
colorTexturePainter.begin(m_textureColorImage.get());
colorTexturePainter.setRenderHint(QPainter::Antialiasing);
colorTexturePainter.setRenderHint(QPainter::HighQualityAntialiasing);
colorTexturePainter.setPen(Qt::NoPen);
for (const auto& layout : m_mapPacker->packedLayouts()) {
auto findPart = m_snapshot->parts.find(layout.id.toString());
if (findPart == m_snapshot->parts.end())
continue;
const auto& colorImageIdIt = findPart->second.find("colorImageId");
if (colorImageIdIt == findPart->second.end())
continue;
auto imageId = dust3d::Uuid(colorImageIdIt->second);
const QImage* image = ImageForever::get(imageId);
if (nullptr == image)
continue;
QPixmap brushPixmap;
if (layout.flipped) {
auto scaledImage = image->scaled(QSize(layout.height * UvMapGenerator::m_textureSize,
layout.width * UvMapGenerator::m_textureSize));
QPoint center = scaledImage.rect().center();
QMatrix matrix;
matrix.translate(center.x(), center.y());
matrix.rotate(90);
auto rotatedImage = scaledImage.transformed(matrix).mirrored(true, false);
brushPixmap = QPixmap::fromImage(rotatedImage);
} else {
auto scaledImage = image->scaled(QSize(layout.width * UvMapGenerator::m_textureSize,
layout.height * UvMapGenerator::m_textureSize));
brushPixmap = QPixmap::fromImage(scaledImage);
}
colorTexturePainter.drawPixmap(layout.left * UvMapGenerator::m_textureSize,
layout.top * UvMapGenerator::m_textureSize,
brushPixmap);
}
colorTexturePainter.end();
}
QImage* UvMapGenerator::combineMetalnessRoughnessAmbientOcclusionImages(QImage* metalnessImage,
QImage* roughnessImage,
QImage* ambientOcclusionImage)
{
QImage* textureMetalnessRoughnessAmbientOcclusionImage = nullptr;
if (nullptr != metalnessImage || nullptr != roughnessImage || nullptr != ambientOcclusionImage) {
int textureSize = 0;
if (nullptr != metalnessImage)
textureSize = metalnessImage->height();
if (nullptr != roughnessImage)
textureSize = roughnessImage->height();
if (nullptr != ambientOcclusionImage)
textureSize = ambientOcclusionImage->height();
if (textureSize > 0) {
textureMetalnessRoughnessAmbientOcclusionImage = new QImage(textureSize, textureSize, QImage::Format_ARGB32);
textureMetalnessRoughnessAmbientOcclusionImage->fill(QColor(255, 255, 0));
for (int row = 0; row < textureMetalnessRoughnessAmbientOcclusionImage->height(); ++row) {
for (int col = 0; col < textureMetalnessRoughnessAmbientOcclusionImage->width(); ++col) {
QColor color(255, 255, 0);
if (nullptr != metalnessImage)
color.setBlue(qGray(metalnessImage->pixel(col, row)));
if (nullptr != roughnessImage)
color.setGreen(qGray(roughnessImage->pixel(col, row)));
if (nullptr != ambientOcclusionImage)
color.setRed(qGray(ambientOcclusionImage->pixel(col, row)));
textureMetalnessRoughnessAmbientOcclusionImage->setPixelColor(col, row, color);
}
}
}
}
return textureMetalnessRoughnessAmbientOcclusionImage;
} }

View File

@ -1,6 +1,8 @@
#ifndef DUST3D_APPLICATION_UV_MAP_GENERATOR_H_ #ifndef DUST3D_APPLICATION_UV_MAP_GENERATOR_H_
#define DUST3D_APPLICATION_UV_MAP_GENERATOR_H_ #define DUST3D_APPLICATION_UV_MAP_GENERATOR_H_
#include "model_mesh.h"
#include <QImage>
#include <QObject> #include <QObject>
#include <dust3d/base/object.h> #include <dust3d/base/object.h>
#include <dust3d/base/snapshot.h> #include <dust3d/base/snapshot.h>
@ -12,7 +14,17 @@ class UvMapGenerator : public QObject {
public: public:
UvMapGenerator(std::unique_ptr<dust3d::Object> object, std::unique_ptr<dust3d::Snapshot> snapshot); UvMapGenerator(std::unique_ptr<dust3d::Object> object, std::unique_ptr<dust3d::Snapshot> snapshot);
void generate(); void generate();
std::unique_ptr<QImage> takeResultTextureColorImage();
std::unique_ptr<QImage> takeResultTextureNormalImage();
std::unique_ptr<QImage> takeResultTextureRoughnessImage();
std::unique_ptr<QImage> takeResultTextureMetalnessImage();
std::unique_ptr<QImage> takeResultTextureAmbientOcclusionImage();
std::unique_ptr<ModelMesh> takeResultMesh();
std::unique_ptr<dust3d::Object> takeObject(); std::unique_ptr<dust3d::Object> takeObject();
bool hasTransparencySettings() const;
static QImage* combineMetalnessRoughnessAmbientOcclusionImages(QImage* metalnessImage,
QImage* roughnessImage,
QImage* ambientOcclusionImage);
signals: signals:
void finished(); void finished();
public slots: public slots:
@ -22,6 +34,14 @@ private:
std::unique_ptr<dust3d::Object> m_object; std::unique_ptr<dust3d::Object> m_object;
std::unique_ptr<dust3d::Snapshot> m_snapshot; std::unique_ptr<dust3d::Snapshot> m_snapshot;
std::unique_ptr<dust3d::UvMapPacker> m_mapPacker; std::unique_ptr<dust3d::UvMapPacker> m_mapPacker;
std::unique_ptr<QImage> m_textureColorImage;
std::unique_ptr<QImage> m_textureNormalImage;
std::unique_ptr<QImage> m_textureRoughnessImage;
std::unique_ptr<QImage> m_textureMetalnessImage;
std::unique_ptr<QImage> m_textureAmbientOcclusionImage;
std::unique_ptr<ModelMesh> m_mesh;
bool m_hasTransparencySettings = false;
static size_t m_textureSize;
}; };
#endif #endif

View File

@ -61,15 +61,15 @@ void UvMapPacker::pack()
layout.id = part.id; layout.id = part.id;
layout.flipped = flipped; layout.flipped = flipped;
if (flipped) { if (flipped) {
layout.left = left; layout.left = left / m_packedTextureSize;
layout.top = top; layout.top = top / m_packedTextureSize;
layout.width = height; layout.width = height / m_packedTextureSize;
layout.height = width; layout.height = width / m_packedTextureSize;
} else { } else {
layout.left = left; layout.left = left / m_packedTextureSize;
layout.top = top; layout.top = top / m_packedTextureSize;
layout.width = width; layout.width = width / m_packedTextureSize;
layout.height = height; layout.height = height / m_packedTextureSize;
} }
if (flipped) { if (flipped) {
for (auto& it : part.localUv) { for (auto& it : part.localUv) {