parent
d991f0c532
commit
f2fc58c699
|
@ -145,20 +145,6 @@ SOURCES += sources/log_browser.cc
|
|||
HEADERS += sources/log_browser_dialog.h
|
||||
SOURCES += sources/log_browser_dialog.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
|
||||
SOURCES += sources/mesh_generator.cc
|
||||
HEADERS += sources/mesh_preview_images_generator.h
|
||||
|
@ -195,8 +181,6 @@ HEADERS += sources/skeleton_ik_mover.h
|
|||
SOURCES += sources/skeleton_ik_mover.cc
|
||||
HEADERS += sources/spinnable_toolbar_icon.h
|
||||
SOURCES += sources/spinnable_toolbar_icon.cc
|
||||
HEADERS += sources/texture_generator.h
|
||||
SOURCES += sources/texture_generator.cc
|
||||
HEADERS += sources/theme.h
|
||||
SOURCES += sources/theme.cc
|
||||
HEADERS += sources/toolbar_button.h
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
<file>resources/toolbar_z_disabled.svg</file>
|
||||
<file>resources/toolbar_radius.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-banana.ds3</file>
|
||||
<file>resources/model-bicycle.ds3</file>
|
||||
|
|
Binary file not shown.
|
@ -1,9 +1,8 @@
|
|||
#include "document.h"
|
||||
#include "image_forever.h"
|
||||
#include "material_previews_generator.h"
|
||||
#include "mesh_generator.h"
|
||||
#include "mesh_result_post_processor.h"
|
||||
#include "texture_generator.h"
|
||||
#include "uv_map_generator.h"
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
|
@ -1423,14 +1422,6 @@ bool Document::originSettled() const
|
|||
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)
|
||||
{
|
||||
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,
|
||||
DocumentToSnapshotFor forWhat,
|
||||
const std::set<dust3d::Uuid>& limitMaterialIds) const
|
||||
DocumentToSnapshotFor forWhat) const
|
||||
{
|
||||
if (DocumentToSnapshotFor::Document == forWhat || DocumentToSnapshotFor::Nodes == forWhat) {
|
||||
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);
|
||||
if (!partIt.second.name.isEmpty())
|
||||
part["name"] = partIt.second.name.toUtf8().constData();
|
||||
if (partIt.second.materialAdjusted())
|
||||
part["materialId"] = partIt.second.materialId.toString();
|
||||
if (partIt.second.countershaded)
|
||||
part["countershaded"] = "true";
|
||||
if (partIt.second.smooth)
|
||||
|
@ -1722,39 +1710,6 @@ void Document::toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uui
|
|||
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) {
|
||||
std::map<std::string, std::string> canvas;
|
||||
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::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;
|
||||
for (const auto& partKv : snapshot.parts) {
|
||||
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");
|
||||
if (hollowThicknessIt != partKv.second.end())
|
||||
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.smooth = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "smooth"));
|
||||
newAddedPartIds.insert(part.id);
|
||||
|
@ -2054,9 +1963,6 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo
|
|||
for (const auto& edgeIt : newAddedEdgeIds) {
|
||||
emit checkEdge(edgeIt);
|
||||
}
|
||||
|
||||
if (!snapshot.materials.empty())
|
||||
emit materialListChanged();
|
||||
}
|
||||
|
||||
void Document::silentReset()
|
||||
|
@ -2068,8 +1974,6 @@ void Document::silentReset()
|
|||
edgeMap.clear();
|
||||
partMap.clear();
|
||||
componentMap.clear();
|
||||
materialMap.clear();
|
||||
materialIdList.clear();
|
||||
rootComponent = SkeletonComponent();
|
||||
}
|
||||
|
||||
|
@ -2244,29 +2148,31 @@ void Document::generateTexture()
|
|||
|
||||
m_isTextureObsolete = false;
|
||||
|
||||
dust3d::Snapshot* snapshot = new dust3d::Snapshot;
|
||||
toSnapshot(snapshot);
|
||||
auto object = std::make_unique<dust3d::Object>(*m_postProcessedObject);
|
||||
|
||||
auto snapshot = std::make_unique<dust3d::Snapshot>();
|
||||
toSnapshot(snapshot.get());
|
||||
|
||||
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);
|
||||
connect(thread, &QThread::started, m_textureGenerator, &TextureGenerator::process);
|
||||
connect(m_textureGenerator, &TextureGenerator::finished, this, &Document::textureReady);
|
||||
connect(m_textureGenerator, &TextureGenerator::finished, thread, &QThread::quit);
|
||||
connect(thread, &QThread::started, m_textureGenerator, &UvMapGenerator::process);
|
||||
connect(m_textureGenerator, &UvMapGenerator::finished, this, &Document::textureReady);
|
||||
connect(m_textureGenerator, &UvMapGenerator::finished, thread, &QThread::quit);
|
||||
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void Document::textureReady()
|
||||
{
|
||||
updateTextureImage(m_textureGenerator->takeResultTextureColorImage());
|
||||
updateTextureNormalImage(m_textureGenerator->takeResultTextureNormalImage());
|
||||
updateTextureMetalnessImage(m_textureGenerator->takeResultTextureMetalnessImage());
|
||||
updateTextureRoughnessImage(m_textureGenerator->takeResultTextureRoughnessImage());
|
||||
updateTextureAmbientOcclusionImage(m_textureGenerator->takeResultTextureAmbientOcclusionImage());
|
||||
updateTextureImage(m_textureGenerator->takeResultTextureColorImage().release());
|
||||
updateTextureNormalImage(m_textureGenerator->takeResultTextureNormalImage().release());
|
||||
updateTextureMetalnessImage(m_textureGenerator->takeResultTextureMetalnessImage().release());
|
||||
updateTextureRoughnessImage(m_textureGenerator->takeResultTextureRoughnessImage().release());
|
||||
updateTextureAmbientOcclusionImage(m_textureGenerator->takeResultTextureAmbientOcclusionImage().release());
|
||||
|
||||
delete m_resultTextureMesh;
|
||||
m_resultTextureMesh = m_textureGenerator->takeResultMesh();
|
||||
m_resultTextureMesh = m_textureGenerator->takeResultMesh().release();
|
||||
|
||||
m_postProcessedObject->alphaEnabled = m_textureGenerator->hasTransparencySettings();
|
||||
|
||||
|
@ -2486,21 +2392,6 @@ void Document::setPartDeformUnified(dust3d::Uuid partId, bool unified)
|
|||
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)
|
||||
{
|
||||
auto part = partMap.find(partId);
|
||||
|
@ -2758,17 +2649,6 @@ bool Document::hasPastableNodesInClipboard() const
|
|||
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
|
||||
{
|
||||
return m_undoItems.size() >= 2;
|
||||
|
@ -2816,126 +2696,6 @@ void Document::checkExportReadyState()
|
|||
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
|
||||
{
|
||||
return nullptr != m_meshGenerator;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define DUST3D_APPLICATION_DOCUMENT_H_
|
||||
|
||||
#include "debug.h"
|
||||
#include "material_layer.h"
|
||||
#include "model_mesh.h"
|
||||
#include "monochrome_mesh.h"
|
||||
#include "theme.h"
|
||||
|
@ -165,7 +164,6 @@ public:
|
|||
float cutRotation;
|
||||
dust3d::CutFace cutFace;
|
||||
dust3d::Uuid cutFaceLinkedId;
|
||||
dust3d::Uuid materialId;
|
||||
dust3d::PartTarget target;
|
||||
float colorSolubility;
|
||||
float metalness;
|
||||
|
@ -338,10 +336,6 @@ public:
|
|||
{
|
||||
return cutRotationAdjusted() || cutFaceAdjusted() || hollowThicknessAdjusted();
|
||||
}
|
||||
bool materialAdjusted() const
|
||||
{
|
||||
return !materialId.isNull();
|
||||
}
|
||||
bool isEditVisible() const
|
||||
{
|
||||
return visible && !disabled;
|
||||
|
@ -364,7 +358,6 @@ public:
|
|||
cutFaceLinkedId = other.cutFaceLinkedId;
|
||||
componentId = other.componentId;
|
||||
dirty = other.dirty;
|
||||
materialId = other.materialId;
|
||||
target = other.target;
|
||||
colorSolubility = other.colorSolubility;
|
||||
countershaded = other.countershaded;
|
||||
|
@ -551,8 +544,7 @@ private:
|
|||
std::set<dust3d::Uuid> m_childrenIdSet;
|
||||
};
|
||||
|
||||
class MaterialPreviewsGenerator;
|
||||
class TextureGenerator;
|
||||
class UvMapGenerator;
|
||||
class MeshGenerator;
|
||||
class MeshResultPostProcessor;
|
||||
|
||||
|
@ -561,40 +553,9 @@ public:
|
|||
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 {
|
||||
Document = 0,
|
||||
Nodes,
|
||||
Materials
|
||||
Nodes
|
||||
};
|
||||
|
||||
class Document : public QObject {
|
||||
|
@ -618,7 +579,6 @@ signals:
|
|||
void partColorStateChanged(dust3d::Uuid partId);
|
||||
void partCutRotationChanged(dust3d::Uuid partId);
|
||||
void partCutFaceChanged(dust3d::Uuid partId);
|
||||
void partMaterialIdChanged(dust3d::Uuid partId);
|
||||
void partChamferStateChanged(dust3d::Uuid partId);
|
||||
void partTargetChanged(dust3d::Uuid partId);
|
||||
void partColorSolubilityChanged(dust3d::Uuid partId);
|
||||
|
@ -638,12 +598,6 @@ signals:
|
|||
void uncheckAll();
|
||||
void checkNode(dust3d::Uuid nodeId);
|
||||
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 postProcessing();
|
||||
void textureGenerating();
|
||||
|
@ -706,9 +660,6 @@ public: // need initialize
|
|||
public:
|
||||
Document();
|
||||
~Document();
|
||||
std::map<dust3d::Uuid, Material> materialMap;
|
||||
std::vector<dust3d::Uuid> materialIdList;
|
||||
|
||||
bool undoable() const;
|
||||
bool redoable() const;
|
||||
bool hasPastableNodesInClipboard() const;
|
||||
|
@ -717,8 +668,7 @@ public:
|
|||
bool isEdgeEditable(dust3d::Uuid edgeId) const;
|
||||
void copyNodes(std::set<dust3d::Uuid> nodeIdSet) const;
|
||||
void toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uuid>& limitNodeIds = std::set<dust3d::Uuid>(),
|
||||
DocumentToSnapshotFor forWhat = DocumentToSnapshotFor::Document,
|
||||
const std::set<dust3d::Uuid>& limitMaterialIds = std::set<dust3d::Uuid>()) const;
|
||||
DocumentToSnapshotFor forWhat = DocumentToSnapshotFor::Document) const;
|
||||
void fromSnapshot(const dust3d::Snapshot& snapshot);
|
||||
enum class SnapshotSource {
|
||||
Unknown,
|
||||
|
@ -726,7 +676,6 @@ public:
|
|||
Import
|
||||
};
|
||||
void addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSource source = SnapshotSource::Paste);
|
||||
const Material* findMaterial(dust3d::Uuid materialId) const;
|
||||
ModelMesh* takeResultMesh();
|
||||
MonochromeMesh* takeWireframeMesh();
|
||||
ModelMesh* takePaintedMesh();
|
||||
|
@ -740,7 +689,6 @@ public:
|
|||
void updateTextureMetalnessImage(QImage* image);
|
||||
void updateTextureRoughnessImage(QImage* image);
|
||||
void updateTextureAmbientOcclusionImage(QImage* image);
|
||||
bool hasPastableMaterialsInClipboard() const;
|
||||
const dust3d::Object& currentPostProcessedObject() const;
|
||||
bool isExportReady() const;
|
||||
bool isPostProcessResultObsolete() const;
|
||||
|
@ -822,8 +770,6 @@ public slots:
|
|||
void textureReady();
|
||||
void postProcess();
|
||||
void postProcessedMeshResultReady();
|
||||
void generateMaterialPreviews();
|
||||
void materialPreviewsReady();
|
||||
void setPartSubdivState(dust3d::Uuid partId, bool subdived);
|
||||
void setPartXmirrorState(dust3d::Uuid partId, bool mirrored);
|
||||
void setPartDeformThickness(dust3d::Uuid partId, float thickness);
|
||||
|
@ -834,7 +780,6 @@ public slots:
|
|||
void setPartCutRotation(dust3d::Uuid partId, float cutRotation);
|
||||
void setPartCutFace(dust3d::Uuid partId, dust3d::CutFace cutFace);
|
||||
void setPartCutFaceLinkedId(dust3d::Uuid partId, dust3d::Uuid linkedId);
|
||||
void setPartMaterialId(dust3d::Uuid partId, dust3d::Uuid materialId);
|
||||
void setPartChamferState(dust3d::Uuid partId, bool chamfered);
|
||||
void setPartTarget(dust3d::Uuid partId, dust3d::PartTarget target);
|
||||
void setPartColorSolubility(dust3d::Uuid partId, float solubility);
|
||||
|
@ -852,10 +797,6 @@ public slots:
|
|||
void silentReset();
|
||||
void toggleSmoothNormal();
|
||||
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 removeEdge(dust3d::Uuid edgeId);
|
||||
void removePart(dust3d::Uuid partId);
|
||||
|
@ -932,14 +873,13 @@ private:
|
|||
int m_batchChangeRefCount = 0;
|
||||
dust3d::Object* m_currentObject = nullptr;
|
||||
bool m_isTextureObsolete = false;
|
||||
TextureGenerator* m_textureGenerator = nullptr;
|
||||
UvMapGenerator* m_textureGenerator = nullptr;
|
||||
bool m_isPostProcessResultObsolete = false;
|
||||
MeshResultPostProcessor* m_postProcessor = nullptr;
|
||||
dust3d::Object* m_postProcessedObject = new dust3d::Object;
|
||||
ModelMesh* m_resultTextureMesh = nullptr;
|
||||
unsigned long long m_textureImageUpdateVersion = 0;
|
||||
bool m_smoothNormal = false;
|
||||
MaterialPreviewsGenerator* m_materialPreviewsGenerator = nullptr;
|
||||
quint64 m_meshGenerationId = 0;
|
||||
quint64 m_nextMeshGenerationId = 0;
|
||||
void* m_generatedCacheContext = nullptr;
|
||||
|
|
|
@ -39,17 +39,6 @@ void DocumentSaver::collectUsedResourceIds(const dust3d::Snapshot* snapshot,
|
|||
dust3d::Uuid imageId = dust3d::Uuid(findImageIdString->second);
|
||||
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,
|
||||
|
|
|
@ -10,14 +10,13 @@
|
|||
#include "horizontal_line_widget.h"
|
||||
#include "image_forever.h"
|
||||
#include "log_browser.h"
|
||||
#include "material_manage_widget.h"
|
||||
#include "part_manage_widget.h"
|
||||
#include "preferences.h"
|
||||
#include "skeleton_graphics_widget.h"
|
||||
#include "spinnable_toolbar_icon.h"
|
||||
#include "texture_generator.h"
|
||||
#include "theme.h"
|
||||
#include "updates_check_widget.h"
|
||||
#include "uv_map_generator.h"
|
||||
#include "version.h"
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
|
@ -270,21 +269,6 @@ DocumentWindow::DocumentWindow()
|
|||
partsDocker->setWidget(m_partManageWidget);
|
||||
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();
|
||||
|
||||
QWidget* titleBarWidget = new QWidget;
|
||||
|
@ -431,13 +415,6 @@ DocumentWindow::DocumentWindow()
|
|||
});
|
||||
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"));
|
||||
connect(dialogsMenu, &QMenu::aboutToShow, [=]() {
|
||||
dialogsMenu->clear();
|
||||
|
@ -667,15 +644,6 @@ DocumentWindow::DocumentWindow()
|
|||
connect(m_document, &Document::zlockStateChanged, this, &DocumentWindow::updateZlockButtonState);
|
||||
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();
|
||||
|
||||
connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady);
|
||||
|
@ -1088,7 +1056,7 @@ void DocumentWindow::exportGlbToFilename(const QString& filename)
|
|||
}
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
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->textureAmbientOcclusionImage);
|
||||
GlbFileWriter glbFileWriter(skeletonResult, filename,
|
||||
|
|
|
@ -144,7 +144,6 @@ private:
|
|||
QMenu* m_windowMenu = nullptr;
|
||||
QAction* m_showPartsListAction = nullptr;
|
||||
QAction* m_showDebugDialogAction = nullptr;
|
||||
QAction* m_showMaterialsAction = nullptr;
|
||||
|
||||
QMenu* m_helpMenu = 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::miniIconSize = 0;
|
||||
int Theme::partPreviewImageSize = 0;
|
||||
int Theme::materialPreviewImageSize = 0;
|
||||
int Theme::sidebarPreferredWidth = 0;
|
||||
int Theme::previewIconBorderSize = 0;
|
||||
int Theme::previewIconMargin = 0;
|
||||
|
@ -57,7 +56,6 @@ void Theme::initialize()
|
|||
Theme::miniIconSize = (int)(Theme::miniIconFontSize * 1.67);
|
||||
Theme::partPreviewImageSize = (Theme::miniIconSize * 2.3);
|
||||
Theme::sidebarPreferredWidth = Theme::partPreviewImageSize * 4.5;
|
||||
Theme::materialPreviewImageSize = Theme::sidebarPreferredWidth * 0.4;
|
||||
Theme::previewIconBorderSize = std::max(1, Theme::partPreviewImageSize / 20);
|
||||
Theme::previewIconMargin = std::max(1, Theme::previewIconBorderSize / 2);
|
||||
Theme::previewIconBorderRadius = std::max(3, Theme::partPreviewImageSize / 10);
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
static int miniIconFontSize;
|
||||
static int miniIconSize;
|
||||
static int partPreviewImageSize;
|
||||
static int materialPreviewImageSize;
|
||||
static int sidebarPreferredWidth;
|
||||
static int previewIconBorderSize;
|
||||
static int previewIconMargin;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#include "uv_map_generator.h"
|
||||
#include "image_forever.h"
|
||||
#include <QMatrix>
|
||||
#include <QPainter>
|
||||
#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)
|
||||
: m_object(std::move(object))
|
||||
, m_snapshot(std::move(snapshot))
|
||||
|
@ -14,16 +18,53 @@ void UvMapGenerator::process()
|
|||
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()
|
||||
{
|
||||
return std::move(m_object);
|
||||
}
|
||||
|
||||
bool UvMapGenerator::hasTransparencySettings() const
|
||||
{
|
||||
return m_hasTransparencySettings;
|
||||
}
|
||||
|
||||
void UvMapGenerator::generate()
|
||||
{
|
||||
if (nullptr == m_object)
|
||||
return;
|
||||
|
||||
m_mesh = std::make_unique<ModelMesh>(*m_object);
|
||||
|
||||
if (nullptr == m_snapshot)
|
||||
return;
|
||||
|
||||
|
@ -50,5 +91,78 @@ void UvMapGenerator::generate()
|
|||
|
||||
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_
|
||||
#define DUST3D_APPLICATION_UV_MAP_GENERATOR_H_
|
||||
|
||||
#include "model_mesh.h"
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
#include <dust3d/base/object.h>
|
||||
#include <dust3d/base/snapshot.h>
|
||||
|
@ -12,7 +14,17 @@ class UvMapGenerator : public QObject {
|
|||
public:
|
||||
UvMapGenerator(std::unique_ptr<dust3d::Object> object, std::unique_ptr<dust3d::Snapshot> snapshot);
|
||||
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();
|
||||
bool hasTransparencySettings() const;
|
||||
static QImage* combineMetalnessRoughnessAmbientOcclusionImages(QImage* metalnessImage,
|
||||
QImage* roughnessImage,
|
||||
QImage* ambientOcclusionImage);
|
||||
signals:
|
||||
void finished();
|
||||
public slots:
|
||||
|
@ -22,6 +34,14 @@ private:
|
|||
std::unique_ptr<dust3d::Object> m_object;
|
||||
std::unique_ptr<dust3d::Snapshot> m_snapshot;
|
||||
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
|
||||
|
|
|
@ -61,15 +61,15 @@ void UvMapPacker::pack()
|
|||
layout.id = part.id;
|
||||
layout.flipped = flipped;
|
||||
if (flipped) {
|
||||
layout.left = left;
|
||||
layout.top = top;
|
||||
layout.width = height;
|
||||
layout.height = width;
|
||||
layout.left = left / m_packedTextureSize;
|
||||
layout.top = top / m_packedTextureSize;
|
||||
layout.width = height / m_packedTextureSize;
|
||||
layout.height = width / m_packedTextureSize;
|
||||
} else {
|
||||
layout.left = left;
|
||||
layout.top = top;
|
||||
layout.width = width;
|
||||
layout.height = height;
|
||||
layout.left = left / m_packedTextureSize;
|
||||
layout.top = top / m_packedTextureSize;
|
||||
layout.width = width / m_packedTextureSize;
|
||||
layout.height = height / m_packedTextureSize;
|
||||
}
|
||||
if (flipped) {
|
||||
for (auto& it : part.localUv) {
|
||||
|
|
Loading…
Reference in New Issue