parent
d991f0c532
commit
f2fc58c699
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Binary file not shown.
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -1 +0,0 @@
|
||||||
#include "material_layer.h"
|
|
|
@ -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
|
|
|
@ -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(©Action, &QAction::triggered, this, &MaterialListWidget::copy);
|
|
||||||
contextMenu.addAction(©Action);
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue