Fix parts render crash

Before, app crash on some machines as been reported in #15 and #16, and cannot easily be reproduced on other machines, such as a virtual box guest system, which make it very hard to trace the root cause.
Now, the offline render is totally removed from the code base, instead of offline-render to image then show the image, we can directly show the parts model in the parts tree using normal opengl widget, and the result looks better than before.
master
Jeremy Hu 2018-09-18 14:22:29 +08:00
parent 2f4102cedc
commit 1ab3d16bd4
17 changed files with 196 additions and 312 deletions

View File

@ -42,9 +42,6 @@ HEADERS += src/modelshaderprogram.h
SOURCES += src/modelmeshbinder.cpp SOURCES += src/modelmeshbinder.cpp
HEADERS += src/modelmeshbinder.h HEADERS += src/modelmeshbinder.h
SOURCES += src/modelofflinerender.cpp
HEADERS += src/modelofflinerender.h
SOURCES += src/modelwidget.cpp SOURCES += src/modelwidget.cpp
HEADERS += src/modelwidget.h HEADERS += src/modelwidget.h

View File

@ -5,7 +5,6 @@
#include "dust3dutil.h" #include "dust3dutil.h"
#include "skeletondocument.h" #include "skeletondocument.h"
#include "meshlite.h" #include "meshlite.h"
#include "modelofflinerender.h"
#include "meshutil.h" #include "meshutil.h"
#include "theme.h" #include "theme.h"
#include "positionmap.h" #include "positionmap.h"
@ -32,7 +31,7 @@ void GeneratedCacheContext::updateComponentCombinableMesh(QString componentId, v
MeshGenerator::MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread) : MeshGenerator::MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread) :
m_snapshot(snapshot), m_snapshot(snapshot),
m_mesh(nullptr), m_mesh(nullptr),
m_preview(nullptr), //m_preview(nullptr),
m_thread(thread), m_thread(thread),
m_meshResultContext(nullptr), m_meshResultContext(nullptr),
m_sharedContextWidget(nullptr), m_sharedContextWidget(nullptr),
@ -45,12 +44,8 @@ MeshGenerator::~MeshGenerator()
{ {
delete m_snapshot; delete m_snapshot;
delete m_mesh; delete m_mesh;
delete m_preview; for (const auto &partPreviewMeshIt: m_partPreviewMeshMap) {
for (const auto &partPreviewIt: m_partPreviewMap) { delete partPreviewMeshIt.second;
delete partPreviewIt.second;
}
for (const auto &render: m_partPreviewRenderMap) {
delete render.second;
} }
delete m_meshResultContext; delete m_meshResultContext;
} }
@ -65,15 +60,10 @@ void MeshGenerator::setGeneratedCacheContext(GeneratedCacheContext *cacheContext
m_cacheContext = cacheContext; m_cacheContext = cacheContext;
} }
void MeshGenerator::addPartPreviewRequirement(const QString &partId) void MeshGenerator::addPartPreviewRequirement(const QUuid &partId)
{ {
//qDebug() << "addPartPreviewRequirement:" << partId; //qDebug() << "addPartPreviewRequirement:" << partId;
m_requirePartPreviewMap.insert(partId); m_requirePreviewPartIds.insert(partId);
if (m_partPreviewRenderMap.find(partId) == m_partPreviewRenderMap.end()) {
ModelOfflineRender *render = new ModelOfflineRender(m_sharedContextWidget);
render->setRenderThread(m_thread);
m_partPreviewRenderMap[partId] = render;
}
} }
void MeshGenerator::setSharedContextWidget(QOpenGLWidget *widget) void MeshGenerator::setSharedContextWidget(QOpenGLWidget *widget)
@ -88,18 +78,21 @@ MeshLoader *MeshGenerator::takeResultMesh()
return resultMesh; return resultMesh;
} }
QImage *MeshGenerator::takePreview() MeshLoader *MeshGenerator::takePartPreviewMesh(const QUuid &partId)
{ {
QImage *resultPreview = m_preview; MeshLoader *resultMesh = m_partPreviewMeshMap[partId];
m_preview = nullptr; m_partPreviewMeshMap[partId] = nullptr;
return resultPreview; return resultMesh;
} }
QImage *MeshGenerator::takePartPreview(const QString &partId) const std::set<QUuid> &MeshGenerator::requirePreviewPartIds()
{ {
QImage *resultImage = m_partPreviewMap[partId]; return m_requirePreviewPartIds;
m_partPreviewMap[partId] = nullptr; }
return resultImage;
const std::set<QUuid> &MeshGenerator::generatedPreviewPartIds()
{
return m_generatedPreviewPartIds;
} }
MeshResultContext *MeshGenerator::takeMeshResultContext() MeshResultContext *MeshGenerator::takeMeshResultContext()
@ -360,18 +353,10 @@ void *MeshGenerator::combinePartMesh(QString partId)
} }
} }
if (m_requirePartPreviewMap.find(partId) != m_requirePartPreviewMap.end()) { if (m_requirePreviewPartIds.find(partIdNotAsString) != m_requirePreviewPartIds.end()) {
ModelOfflineRender *render = m_partPreviewRenderMap[partId];
int trimedMeshId = meshlite_trim(m_meshliteContext, meshId, 1); int trimedMeshId = meshlite_trim(m_meshliteContext, meshId, 1);
render->updateMesh(new MeshLoader(m_meshliteContext, trimedMeshId, -1, partColor, nullptr, m_smoothNormal)); m_partPreviewMeshMap[partIdNotAsString] = new MeshLoader(m_meshliteContext, trimedMeshId, -1, partColor, nullptr, m_smoothNormal);
QImage *image = new QImage(render->toImage(QSize(Theme::previewImageRenderSize, Theme::previewImageRenderSize))); m_generatedPreviewPartIds.insert(partIdNotAsString);
if (Theme::previewImageSize != Theme::previewImageRenderSize) {
int cropOffset = (Theme::previewImageRenderSize - Theme::previewImageSize) / 2;
QImage *crop = new QImage(image->copy(cropOffset, cropOffset, Theme::previewImageSize, Theme::previewImageSize));
delete image;
image = crop;
}
m_partPreviewMap[partId] = image;
} }
if (isDisabled) { if (isDisabled) {

View File

@ -9,7 +9,6 @@
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include "skeletonsnapshot.h" #include "skeletonsnapshot.h"
#include "meshloader.h" #include "meshloader.h"
#include "modelofflinerender.h"
#include "meshresultcontext.h" #include "meshresultcontext.h"
#include "positionmap.h" #include "positionmap.h"
@ -34,12 +33,13 @@ public:
MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread); MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread);
~MeshGenerator(); ~MeshGenerator();
void setSharedContextWidget(QOpenGLWidget *widget); void setSharedContextWidget(QOpenGLWidget *widget);
void addPartPreviewRequirement(const QString &partId); void addPartPreviewRequirement(const QUuid &partId);
void setGeneratedCacheContext(GeneratedCacheContext *cacheContext); void setGeneratedCacheContext(GeneratedCacheContext *cacheContext);
void setSmoothNormal(bool smoothNormal); void setSmoothNormal(bool smoothNormal);
MeshLoader *takeResultMesh(); MeshLoader *takeResultMesh();
QImage *takePreview(); MeshLoader *takePartPreviewMesh(const QUuid &partId);
QImage *takePartPreview(const QString &partId); const std::set<QUuid> &requirePreviewPartIds();
const std::set<QUuid> &generatedPreviewPartIds();
MeshResultContext *takeMeshResultContext(); MeshResultContext *takeMeshResultContext();
signals: signals:
void finished(); void finished();
@ -48,10 +48,9 @@ public slots:
private: private:
SkeletonSnapshot *m_snapshot; SkeletonSnapshot *m_snapshot;
MeshLoader *m_mesh; MeshLoader *m_mesh;
QImage *m_preview; std::map<QUuid, MeshLoader *> m_partPreviewMeshMap;
std::map<QString, QImage *> m_partPreviewMap; std::set<QUuid> m_requirePreviewPartIds;
std::set<QString> m_requirePartPreviewMap; std::set<QUuid> m_generatedPreviewPartIds;
std::map<QString, ModelOfflineRender *> m_partPreviewRenderMap;
QThread *m_thread; QThread *m_thread;
MeshResultContext *m_meshResultContext; MeshResultContext *m_meshResultContext;
QOpenGLWidget *m_sharedContextWidget; QOpenGLWidget *m_sharedContextWidget;

View File

@ -244,6 +244,15 @@ MeshLoader::MeshLoader(MeshResultContext &resultContext) :
} }
} }
MeshLoader::MeshLoader() :
m_triangleVertices(nullptr),
m_triangleVertexCount(0),
m_edgeVertices(nullptr),
m_edgeVertexCount(0),
m_textureImage(nullptr)
{
}
MeshLoader::~MeshLoader() MeshLoader::~MeshLoader()
{ {
delete[] m_triangleVertices; delete[] m_triangleVertices;

View File

@ -42,6 +42,7 @@ public:
MeshLoader(MeshResultContext &resultContext); MeshLoader(MeshResultContext &resultContext);
MeshLoader(Vertex *triangleVertices, int vertexNum); MeshLoader(Vertex *triangleVertices, int vertexNum);
MeshLoader(const MeshLoader &mesh); MeshLoader(const MeshLoader &mesh);
MeshLoader();
~MeshLoader(); ~MeshLoader();
Vertex *triangleVertices(); Vertex *triangleVertices();
int triangleVertexCount(); int triangleVertexCount();
@ -54,15 +55,15 @@ public:
void setTextureImage(QImage *textureImage); void setTextureImage(QImage *textureImage);
const QImage *textureImage(); const QImage *textureImage();
private: private:
Vertex *m_triangleVertices; Vertex *m_triangleVertices = nullptr;
int m_triangleVertexCount; int m_triangleVertexCount = 0;
Vertex *m_edgeVertices; Vertex *m_edgeVertices = nullptr;
int m_edgeVertexCount; int m_edgeVertexCount = 0;
std::vector<QVector3D> m_vertices; std::vector<QVector3D> m_vertices;
std::vector<std::vector<int>> m_faces; std::vector<std::vector<int>> m_faces;
std::vector<QVector3D> m_triangulatedVertices; std::vector<QVector3D> m_triangulatedVertices;
std::vector<TriangulatedFace> m_triangulatedFaces; std::vector<TriangulatedFace> m_triangulatedFaces;
QImage *m_textureImage; QImage *m_textureImage = nullptr;
}; };
#endif #endif

View File

@ -1,151 +0,0 @@
#include <QOpenGLFramebufferObjectFormat>
#include <QThread>
#include <QDebug>
#include "modelofflinerender.h"
ModelOfflineRender::ModelOfflineRender(QOpenGLWidget *sharedContextWidget, QScreen *targetScreen) :
QOffscreenSurface(targetScreen),
m_context(nullptr),
m_mesh(nullptr),
m_xRot(0),
m_yRot(0),
m_zRot(0)
{
create();
QOpenGLContext *current = nullptr;
if (nullptr != sharedContextWidget) {
current = sharedContextWidget->context();
current->doneCurrent();
}
m_context = new QOpenGLContext();
if (nullptr != current) {
m_context->setFormat(current->format());
m_context->setShareContext(current);
} else {
QSurfaceFormat fmt = format();
fmt.setAlphaBufferSize(8);
fmt.setSamples(4);
setFormat(fmt);
m_context->setFormat(fmt);
}
m_context->create();
if (nullptr != sharedContextWidget) {
sharedContextWidget->makeCurrent();
}
}
ModelOfflineRender::~ModelOfflineRender()
{
delete m_context;
m_context = nullptr;
destroy();
delete m_mesh;
}
void ModelOfflineRender::updateMesh(MeshLoader *mesh)
{
delete m_mesh;
m_mesh = mesh;
}
void ModelOfflineRender::setRenderThread(QThread *thread)
{
m_context->moveToThread(thread);
}
QImage ModelOfflineRender::toImage(const QSize &size)
{
QImage image;
m_context->makeCurrent(this);
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
format.setSamples(16);
QOpenGLFramebufferObject *renderFbo = new QOpenGLFramebufferObject(size, format);
renderFbo->bind();
m_context->functions()->glViewport(0, 0, size.width(), size.height());
if (nullptr != m_mesh) {
int xRot = m_xRot;
int yRot = m_yRot;
int zRot = m_zRot;
QMatrix4x4 proj;
QMatrix4x4 camera;
QMatrix4x4 world;
ModelShaderProgram *program = new ModelShaderProgram;
ModelMeshBinder meshBinder;
meshBinder.initialize();
meshBinder.hideWireframes();
program->setUniformValue(program->lightPosLoc(), QVector3D(0, 0, 70));
m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_context->functions()->glEnable(GL_DEPTH_TEST);
m_context->functions()->glEnable(GL_CULL_FACE);
m_context->functions()->glEnable(GL_LINE_SMOOTH);
camera.setToIdentity();
camera.translate(0, 0, -4.5);
world.setToIdentity();
world.rotate(xRot / 16.0f, 1, 0, 0);
world.rotate(yRot / 16.0f, 0, 1, 0);
world.rotate(zRot / 16.0f, 0, 0, 1);
proj.setToIdentity();
proj.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f);
program->bind();
program->setUniformValue(program->projMatrixLoc(), proj);
program->setUniformValue(program->mvMatrixLoc(), camera * world);
QMatrix3x3 normalMatrix = world.normalMatrix();
program->setUniformValue(program->normalMatrixLoc(), normalMatrix);
program->setUniformValue(program->textureEnabledLoc(), 0);
meshBinder.updateMesh(m_mesh);
meshBinder.paint(program);
meshBinder.cleanup();
program->release();
delete program;
m_mesh = nullptr;
}
m_context->functions()->glFlush();
image = renderFbo->toImage();
renderFbo->bindDefault();
delete renderFbo;
m_context->doneCurrent();
return image;
}
void ModelOfflineRender::setXRotation(int angle)
{
m_xRot = angle;
}
void ModelOfflineRender::setYRotation(int angle)
{
m_yRot = angle;
}
void ModelOfflineRender::setZRotation(int angle)
{
m_zRot = angle;
}

View File

@ -1,33 +0,0 @@
#ifndef MODEL_OFFLINE_RENDER_H
#define MODEL_OFFLINE_RENDER_H
#include <QOffscreenSurface>
#include <QScreen>
#include <QOpenGLFunctions>
#include <QOpenGLContext>
#include <QImage>
#include <QThread>
#include <QOpenGLWidget>
#include "modelshaderprogram.h"
#include "modelmeshbinder.h"
#include "meshloader.h"
class ModelOfflineRender : QOffscreenSurface
{
public:
ModelOfflineRender(QOpenGLWidget *sharedContextWidget = nullptr, QScreen *targetScreen = Q_NULLPTR);
~ModelOfflineRender();
void setRenderThread(QThread *thread);
void updateMesh(MeshLoader *mesh);
QImage toImage(const QSize &size);
void setXRotation(int angle);
void setYRotation(int angle);
void setZRotation(int angle);
private:
QOpenGLContext *m_context;
MeshLoader *m_mesh;
int m_xRot;
int m_yRot;
int m_zRot;
};
#endif

View File

@ -19,7 +19,9 @@ ModelWidget::ModelWidget(QWidget *parent) :
m_zRot(0), m_zRot(0),
m_program(nullptr), m_program(nullptr),
m_moveStarted(false), m_moveStarted(false),
m_graphicsFunctions(NULL) m_graphicsFunctions(NULL),
m_moveEnabled(true),
m_zoomEnabled(true)
{ {
// --transparent causes the clear color to be transparent. Therefore, on systems that // --transparent causes the clear color to be transparent. Therefore, on systems that
// support it, the widget will become transparent apart from the logo. // support it, the widget will become transparent apart from the logo.
@ -192,7 +194,7 @@ void ModelWidget::mousePressEvent(QMouseEvent *event)
shouldStartMove = true; shouldStartMove = true;
} }
} else if (event->button() == Qt::MidButton) { } else if (event->button() == Qt::MidButton) {
shouldStartMove = true; shouldStartMove = m_moveEnabled;
} }
if (shouldStartMove) { if (shouldStartMove) {
m_lastPos = event->pos(); m_lastPos = event->pos();
@ -270,6 +272,8 @@ void ModelWidget::wheelEvent(QWheelEvent *event)
return; return;
if (m_moveStarted) if (m_moveStarted)
return; return;
if (!m_zoomEnabled)
return;
qreal delta = event->delta() / 10; qreal delta = event->delta() / 10;
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) { if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
if (delta > 0) if (delta > 0)
@ -299,3 +303,12 @@ void ModelWidget::exportMeshAsObjPlusMaterials(const QString &filename)
m_meshBinder.exportMeshAsObjPlusMaterials(filename); m_meshBinder.exportMeshAsObjPlusMaterials(filename);
} }
void ModelWidget::enableMove(bool enabled)
{
m_moveEnabled = enabled;
}
void ModelWidget::enableZoom(bool enabled)
{
m_zoomEnabled = enabled;
}

View File

@ -16,19 +16,24 @@ class SkeletonGraphicsFunctions;
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions
{ {
Q_OBJECT Q_OBJECT
public: public:
ModelWidget(QWidget *parent = 0); ModelWidget(QWidget *parent = 0);
~ModelWidget(); ~ModelWidget();
static bool isTransparent()
static bool isTransparent() { return m_transparent; } {
static void setTransparent(bool t) { m_transparent = t; } return m_transparent;
}
static void setTransparent(bool t)
{
m_transparent = t;
}
void updateMesh(MeshLoader *mesh); void updateMesh(MeshLoader *mesh);
void exportMeshAsObj(const QString &filename); void exportMeshAsObj(const QString &filename);
void exportMeshAsObjPlusMaterials(const QString &filename); void exportMeshAsObjPlusMaterials(const QString &filename);
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions); void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
void toggleWireframe(); void toggleWireframe();
void enableMove(bool enabled);
void enableZoom(bool enabled);
public slots: public slots:
void setXRotation(int angle); void setXRotation(int angle);
void setYRotation(int angle); void setYRotation(int angle);
@ -39,7 +44,6 @@ signals:
void xRotationChanged(int angle); void xRotationChanged(int angle);
void yRotationChanged(int angle); void yRotationChanged(int angle);
void zRotationChanged(int angle); void zRotationChanged(int angle);
protected: protected:
void initializeGL() override; void initializeGL() override;
void paintGL() override; void paintGL() override;
@ -61,6 +65,8 @@ private:
ModelShaderProgram *m_program; ModelShaderProgram *m_program;
bool m_moveStarted; bool m_moveStarted;
SkeletonGraphicsFunctions *m_graphicsFunctions; SkeletonGraphicsFunctions *m_graphicsFunctions;
bool m_moveEnabled;
bool m_zoomEnabled;
private: private:
QPoint m_lastPos; QPoint m_lastPos;
ModelMeshBinder m_meshBinder; ModelMeshBinder m_meshBinder;

View File

@ -137,7 +137,9 @@ void SkeletonDocument::removeEdge(QUuid edgeId)
std::vector<std::vector<QUuid>> groups; std::vector<std::vector<QUuid>> groups;
splitPartByEdge(&groups, edgeId); splitPartByEdge(&groups, edgeId);
for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) { for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) {
SkeletonPart part; const auto newUuid = QUuid::createUuid();
SkeletonPart &part = partMap[newUuid];
part.id = newUuid;
part.copyAttributes(*oldPart); part.copyAttributes(*oldPart);
part.name = nextPartName; part.name = nextPartName;
for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) { for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) {
@ -157,7 +159,6 @@ void SkeletonDocument::removeEdge(QUuid edgeId)
edgeIt->second.partId = part.id; edgeIt->second.partId = part.id;
} }
} }
partMap[part.id] = part;
addPartToComponent(part.id, findComponentParentId(part.componentId)); addPartToComponent(part.id, findComponentParentId(part.componentId));
emit partAdded(part.id); emit partAdded(part.id);
} }
@ -196,7 +197,9 @@ void SkeletonDocument::removeNode(QUuid nodeId)
std::vector<std::vector<QUuid>> groups; std::vector<std::vector<QUuid>> groups;
splitPartByNode(&groups, nodeId); splitPartByNode(&groups, nodeId);
for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) { for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) {
SkeletonPart part; const auto newUuid = QUuid::createUuid();
SkeletonPart &part = partMap[newUuid];
part.id = newUuid;
part.copyAttributes(*oldPart); part.copyAttributes(*oldPart);
part.name = nextPartName; part.name = nextPartName;
for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) { for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) {
@ -216,7 +219,6 @@ void SkeletonDocument::removeNode(QUuid nodeId)
edgeIt->second.partId = part.id; edgeIt->second.partId = part.id;
} }
} }
partMap[part.id] = part;
addPartToComponent(part.id, findComponentParentId(part.componentId)); addPartToComponent(part.id, findComponentParentId(part.componentId));
emit partAdded(part.id); emit partAdded(part.id);
} }
@ -254,8 +256,9 @@ QUuid SkeletonDocument::createNode(float x, float y, float z, float radius, QUui
const SkeletonNode *fromNode = nullptr; const SkeletonNode *fromNode = nullptr;
bool newPartAdded = false; bool newPartAdded = false;
if (fromNodeId.isNull()) { if (fromNodeId.isNull()) {
SkeletonPart part; const auto newUuid = QUuid::createUuid();
partMap[part.id] = part; SkeletonPart &part = partMap[newUuid];
part.id = newUuid;
partId = part.id; partId = part.id;
emit partAdded(partId); emit partAdded(partId);
newPartAdded = true; newPartAdded = true;
@ -828,7 +831,9 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot, bool fr
std::map<QUuid, QUuid> oldNewIdMap; std::map<QUuid, QUuid> oldNewIdMap;
for (const auto &partKv: snapshot.parts) { for (const auto &partKv: snapshot.parts) {
SkeletonPart part; const auto newUuid = QUuid::createUuid();
SkeletonPart &part = partMap[newUuid];
part.id = newUuid;
oldNewIdMap[QUuid(partKv.first)] = part.id; oldNewIdMap[QUuid(partKv.first)] = part.id;
part.name = valueOfKeyInMapOrEmpty(partKv.second, "name"); part.name = valueOfKeyInMapOrEmpty(partKv.second, "name");
part.visible = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "visible")); part.visible = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "visible"));
@ -852,7 +857,6 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot, bool fr
const auto &deformWidthIt = partKv.second.find("deformWidth"); const auto &deformWidthIt = partKv.second.find("deformWidth");
if (deformWidthIt != partKv.second.end()) if (deformWidthIt != partKv.second.end())
part.setDeformWidth(deformWidthIt->second.toFloat()); part.setDeformWidth(deformWidthIt->second.toFloat());
partMap[part.id] = part;
newAddedPartIds.insert(part.id); newAddedPartIds.insert(part.id);
} }
for (const auto &nodeKv: snapshot.nodes) { for (const auto &nodeKv: snapshot.nodes) {
@ -1037,18 +1041,12 @@ void SkeletonDocument::meshReady()
MeshLoader *resultMesh = m_meshGenerator->takeResultMesh(); MeshLoader *resultMesh = m_meshGenerator->takeResultMesh();
MeshResultContext *meshResultContext = m_meshGenerator->takeMeshResultContext(); MeshResultContext *meshResultContext = m_meshGenerator->takeMeshResultContext();
QImage *resultPreview = m_meshGenerator->takePreview(); for (auto &partId: m_meshGenerator->generatedPreviewPartIds()) {
if (resultPreview) { auto part = partMap.find(partId);
preview = *resultPreview; if (part != partMap.end()) {
delete resultPreview; MeshLoader *resultPartPreviewMesh = m_meshGenerator->takePartPreviewMesh(partId);
} part->second.updatePreviewMesh(resultPartPreviewMesh);
emit partPreviewChanged(partId);
for (auto &part: partMap) {
QImage *resultPartPreview = m_meshGenerator->takePartPreview(part.first.toString());
if (resultPartPreview) {
part.second.preview = *resultPartPreview;
emit partPreviewChanged(part.first);
delete resultPartPreview;
} }
} }
@ -1134,7 +1132,7 @@ void SkeletonDocument::generateMesh()
m_meshGenerator->setSharedContextWidget(m_sharedContextWidget); m_meshGenerator->setSharedContextWidget(m_sharedContextWidget);
m_meshGenerator->moveToThread(thread); m_meshGenerator->moveToThread(thread);
for (auto &part: partMap) { for (auto &part: partMap) {
m_meshGenerator->addPartPreviewRequirement(part.first.toString()); m_meshGenerator->addPartPreviewRequirement(part.first);
} }
connect(thread, &QThread::started, m_meshGenerator, &MeshGenerator::process); connect(thread, &QThread::started, m_meshGenerator, &MeshGenerator::process);
connect(m_meshGenerator, &MeshGenerator::finished, this, &SkeletonDocument::meshReady); connect(m_meshGenerator, &MeshGenerator::finished, this, &SkeletonDocument::meshReady);

View File

@ -74,6 +74,10 @@ public:
class SkeletonPart class SkeletonPart
{ {
public: public:
~SkeletonPart()
{
delete m_previewMesh;
}
QUuid id; QUuid id;
QString name; QString name;
bool visible; bool visible;
@ -87,7 +91,6 @@ public:
bool rounded; bool rounded;
QColor color; QColor color;
bool hasColor; bool hasColor;
QImage preview;
QUuid componentId; QUuid componentId;
std::vector<QUuid> nodeIds; std::vector<QUuid> nodeIds;
bool dirty; bool dirty;
@ -157,6 +160,20 @@ public:
wrapped = other.wrapped; wrapped = other.wrapped;
componentId = other.componentId; componentId = other.componentId;
} }
void updatePreviewMesh(MeshLoader *previewMesh)
{
delete m_previewMesh;
m_previewMesh = previewMesh;
}
MeshLoader *takePreviewMesh() const
{
if (nullptr == m_previewMesh)
return nullptr;
return new MeshLoader(*m_previewMesh);
}
private:
Q_DISABLE_COPY(SkeletonPart);
MeshLoader *m_previewMesh = nullptr;
}; };
enum class SkeletonProfile enum class SkeletonProfile
@ -564,8 +581,8 @@ private: // need initialize
MeshLoader *m_resultRigWeightMesh; MeshLoader *m_resultRigWeightMesh;
std::vector<AutoRiggerBone> *m_resultRigBones; std::vector<AutoRiggerBone> *m_resultRigBones;
std::map<int, AutoRiggerVertexWeights> *m_resultRigWeights; std::map<int, AutoRiggerVertexWeights> *m_resultRigWeights;
MeshResultContext *m_riggedResultContext;
bool m_isRigObsolete; bool m_isRigObsolete;
MeshResultContext *m_riggedResultContext;
private: private:
static unsigned long m_maxSnapshot; static unsigned long m_maxSnapshot;
std::deque<SkeletonHistoryItem> m_undoItems; std::deque<SkeletonHistoryItem> m_undoItems;

View File

@ -29,7 +29,6 @@
#include "graphicscontainerwidget.h" #include "graphicscontainerwidget.h"
#include "skeletonparttreewidget.h" #include "skeletonparttreewidget.h"
#include "rigwidget.h" #include "rigwidget.h"
#include "modelofflinerender.h"
#include "markiconcreator.h" #include "markiconcreator.h"
#include "tetrapodposeeditwidget.h" #include "tetrapodposeeditwidget.h"
@ -1055,18 +1054,6 @@ void SkeletonDocumentWindow::exportObjPlusMaterialsResult()
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
} }
void SkeletonDocumentWindow::exportRenderedResult()
{
QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
tr("Image (*.png)"));
if (filename.isEmpty()) {
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
exportRenderedAsImage(filename);
QApplication::restoreOverrideCursor();
}
void SkeletonDocumentWindow::showExportPreview() void SkeletonDocumentWindow::showExportPreview()
{ {
if (nullptr == m_exportPreviewWidget) { if (nullptr == m_exportPreviewWidget) {
@ -1146,14 +1133,3 @@ void SkeletonDocumentWindow::updateRadiusLockButtonState()
else else
m_radiusLockButton->setStyleSheet("QPushButton {color: " + Theme::white.name() + "}"); m_radiusLockButton->setStyleSheet("QPushButton {color: " + Theme::white.name() + "}");
} }
void SkeletonDocumentWindow::exportRenderedAsImage(const QString &filename)
{
ModelOfflineRender offlineRender(m_modelRenderWidget);
offlineRender.setXRotation(m_modelRenderWidget->xRot());
offlineRender.setYRotation(m_modelRenderWidget->yRot());
offlineRender.setZRotation(m_modelRenderWidget->zRot());
offlineRender.updateMesh(m_document->takeResultMesh());
QImage renderedImage = offlineRender.toImage(QSize(1024, 1024));
renderedImage.save(filename);
}

View File

@ -39,7 +39,6 @@ public slots:
void exportObjResult(); void exportObjResult();
void exportObjPlusMaterialsResult(); void exportObjPlusMaterialsResult();
void exportGltfResult(); void exportGltfResult();
void exportRenderedResult();
void showExportPreview(); void showExportPreview();
void newWindow(); void newWindow();
void newDocument(); void newDocument();
@ -60,7 +59,6 @@ private:
void initLockButton(QPushButton *button); void initLockButton(QPushButton *button);
void setCurrentFilename(const QString &filename); void setCurrentFilename(const QString &filename);
void updateTitle(); void updateTitle();
void exportRenderedAsImage(const QString &filename);
private: private:
SkeletonDocument *m_document; SkeletonDocument *m_document;
bool m_firstShow; bool m_firstShow;

View File

@ -62,6 +62,42 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
connect(this, &QTreeWidget::itemCollapsed, this, &SkeletonPartTreeWidget::groupCollapsed); connect(this, &QTreeWidget::itemCollapsed, this, &SkeletonPartTreeWidget::groupCollapsed);
} }
bool SkeletonPartTreeWidget::mouseMove(QMouseEvent *event)
{
return false;
}
bool SkeletonPartTreeWidget::wheel(QWheelEvent *event)
{
return false;
}
bool SkeletonPartTreeWidget::mouseRelease(QMouseEvent *event)
{
return false;
}
bool SkeletonPartTreeWidget::mousePress(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
showContextMenu(mapFromGlobal(event->globalPos()));
return true;
}
return false;
}
bool SkeletonPartTreeWidget::mouseDoubleClick(QMouseEvent *event)
{
return false;
}
bool SkeletonPartTreeWidget::keyPress(QKeyEvent *event)
{
if (m_graphicsFunctions)
return m_graphicsFunctions->keyPress(event);
return false;
}
void SkeletonPartTreeWidget::selectComponent(QUuid componentId, bool multiple) void SkeletonPartTreeWidget::selectComponent(QUuid componentId, bool multiple)
{ {
if (multiple) { if (multiple) {
@ -194,6 +230,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
{ {
const SkeletonComponent *component = nullptr; const SkeletonComponent *component = nullptr;
const SkeletonPart *part = nullptr; const SkeletonPart *part = nullptr;
SkeletonPartWidget *partWidget = nullptr;
std::set<QUuid> unorderedComponentIds = m_selectedComponentIds; std::set<QUuid> unorderedComponentIds = m_selectedComponentIds;
if (!m_currentSelectedComponentId.isNull()) if (!m_currentSelectedComponentId.isNull())
@ -229,22 +266,38 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
} }
} }
QWidgetAction forDisplayPartImage(this);
QLabel *previewLabel = new QLabel;
previewLabel->setFixedHeight(Theme::previewImageSize);
previewLabel->setStyleSheet("QLabel {color: " + Theme::red.name() + "}");
if (nullptr != part) {
previewLabel->setPixmap(QPixmap::fromImage(part->preview));
} else if (nullptr != component) {
previewLabel->setText(component->name);
} else if (!componentIds.empty()) {
previewLabel->setText(tr("(%1 items)").arg(QString::number(componentIds.size())));
}
QHBoxLayout *layout = new QHBoxLayout; QHBoxLayout *layout = new QHBoxLayout;
QWidgetAction forDisplayPartImage(this);
layout->setAlignment(Qt::AlignCenter); layout->setAlignment(Qt::AlignCenter);
layout->addWidget(previewLabel);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0); layout->setSpacing(0);
if (nullptr != part) {
auto findItem = m_partItemMap.find(part->id);
if (findItem != m_partItemMap.end()) {
partWidget = (SkeletonPartWidget *)itemWidget(findItem->second, 0);
}
}
if (nullptr != part && nullptr != partWidget) {
ModelWidget *previewWidget = new ModelWidget;
previewWidget->enableMove(false);
previewWidget->enableZoom(false);
previewWidget->setFixedSize(Theme::previewImageSize, Theme::previewImageSize);
previewWidget->setXRotation(partWidget->previewWidget()->xRot());
previewWidget->setYRotation(partWidget->previewWidget()->yRot());
previewWidget->setZRotation(partWidget->previewWidget()->zRot());
previewWidget->updateMesh(part->takePreviewMesh());
layout->addWidget(previewWidget);
} else {
QLabel *previewLabel = new QLabel;
previewLabel->setFixedHeight(Theme::previewImageSize);
previewLabel->setStyleSheet("QLabel {color: " + Theme::red.name() + "}");
if (nullptr != component) {
previewLabel->setText(component->name);
} else if (!componentIds.empty()) {
previewLabel->setText(tr("(%1 items)").arg(QString::number(componentIds.size())));
}
layout->addWidget(previewLabel);
}
QWidget *widget = new QWidget; QWidget *widget = new QWidget;
widget->setLayout(layout); widget->setLayout(layout);
forDisplayPartImage.setDefaultWidget(widget); forDisplayPartImage.setDefaultWidget(widget);
@ -650,6 +703,7 @@ void SkeletonPartTreeWidget::addComponentChildrenToItem(QUuid componentId, QTree
item->setFlags(item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable)); item->setFlags(item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
QUuid partId = component->linkToPartId; QUuid partId = component->linkToPartId;
SkeletonPartWidget *widget = new SkeletonPartWidget(m_document, partId); SkeletonPartWidget *widget = new SkeletonPartWidget(m_document, partId);
widget->previewWidget()->setGraphicsFunctions(this);
item->setSizeHint(0, SkeletonPartWidget::preferredSize()); item->setSizeHint(0, SkeletonPartWidget::preferredSize());
setItemWidget(item, 0, widget); setItemWidget(item, 0, widget);
widget->reload(); widget->reload();

View File

@ -4,10 +4,9 @@
#include <QUuid> #include <QUuid>
#include <QMouseEvent> #include <QMouseEvent>
#include "skeletondocument.h" #include "skeletondocument.h"
#include "skeletongraphicswidget.h"
class SkeletonGraphicsFunctions; class SkeletonPartTreeWidget : public QTreeWidget, public SkeletonGraphicsFunctions
class SkeletonPartTreeWidget : public QTreeWidget
{ {
Q_OBJECT Q_OBJECT
signals: signals:
@ -74,6 +73,12 @@ protected:
virtual QSize sizeHint() const; virtual QSize sizeHint() const;
virtual void mousePressEvent(QMouseEvent *event); virtual void mousePressEvent(QMouseEvent *event);
virtual void keyPressEvent(QKeyEvent *event); virtual void keyPressEvent(QKeyEvent *event);
bool mouseMove(QMouseEvent *event);
bool wheel(QWheelEvent *event);
bool mouseRelease(QMouseEvent *event);
bool mousePress(QMouseEvent *event);
bool mouseDoubleClick(QMouseEvent *event);
bool keyPress(QKeyEvent *event);
private: private:
void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem); void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem);
void deleteItemChildren(QTreeWidgetItem *item); void deleteItemChildren(QTreeWidgetItem *item);

View File

@ -52,8 +52,10 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
m_wrapButton->setSizePolicy(retainSizePolicy); m_wrapButton->setSizePolicy(retainSizePolicy);
initButton(m_wrapButton); initButton(m_wrapButton);
m_previewLabel = new QLabel; m_previewWidget = new ModelWidget;
m_previewLabel->setFixedSize(Theme::previewImageSize, Theme::previewImageSize); m_previewWidget->enableMove(false);
m_previewWidget->enableZoom(false);
m_previewWidget->setFixedSize(Theme::previewImageSize, Theme::previewImageSize);
QWidget *hrLightWidget = new QWidget; QWidget *hrLightWidget = new QWidget;
hrLightWidget->setFixedHeight(1); hrLightWidget->setFixedHeight(1);
@ -89,7 +91,7 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
previewAndToolsLayout->setSpacing(0); previewAndToolsLayout->setSpacing(0);
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0); previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
previewAndToolsLayout->addWidget(m_visibleButton); previewAndToolsLayout->addWidget(m_visibleButton);
previewAndToolsLayout->addWidget(m_previewLabel); previewAndToolsLayout->addWidget(m_previewWidget);
previewAndToolsLayout->addLayout(toolsLayout); previewAndToolsLayout->addLayout(toolsLayout);
previewAndToolsLayout->setStretch(0, 0); previewAndToolsLayout->setStretch(0, 0);
previewAndToolsLayout->setStretch(1, 0); previewAndToolsLayout->setStretch(1, 0);
@ -229,6 +231,11 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
updateAllButtons(); updateAllButtons();
} }
ModelWidget *SkeletonPartWidget::previewWidget()
{
return m_previewWidget;
}
QSize SkeletonPartWidget::preferredSize() QSize SkeletonPartWidget::preferredSize()
{ {
return QSize(Theme::miniIconSize + Theme::previewImageSize + Theme::miniIconSize * 4 + 5 + 2, Theme::previewImageSize + 6); return QSize(Theme::miniIconSize + Theme::previewImageSize + Theme::miniIconSize * 4 + 5 + 2, Theme::previewImageSize + 6);
@ -408,7 +415,9 @@ void SkeletonPartWidget::updatePreview()
qDebug() << "Part not found:" << m_partId; qDebug() << "Part not found:" << m_partId;
return; return;
} }
m_previewLabel->setPixmap(QPixmap::fromImage(part->preview)); //m_previewLabel->setPixmap(QPixmap::fromImage(part->preview));
MeshLoader *previewMesh = part->takePreviewMesh();
m_previewWidget->updateMesh(previewMesh);
} }
void SkeletonPartWidget::updateLockButton() void SkeletonPartWidget::updateLockButton()

View File

@ -4,6 +4,7 @@
#include <QPushButton> #include <QPushButton>
#include <QMouseEvent> #include <QMouseEvent>
#include "skeletondocument.h" #include "skeletondocument.h"
#include "modelwidget.h"
class SkeletonPartWidget : public QWidget class SkeletonPartWidget : public QWidget
{ {
@ -44,6 +45,7 @@ public:
void updateWrapButton(); void updateWrapButton();
void updateCheckedState(bool checked); void updateCheckedState(bool checked);
static QSize preferredSize(); static QSize preferredSize();
ModelWidget *previewWidget();
protected: protected:
void mouseDoubleClickEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event);
public slots: public slots:
@ -53,7 +55,7 @@ private: // need initialize
const SkeletonDocument *m_document; const SkeletonDocument *m_document;
QUuid m_partId; QUuid m_partId;
private: private:
QLabel *m_previewLabel; ModelWidget *m_previewWidget;
QPushButton *m_visibleButton; QPushButton *m_visibleButton;
QPushButton *m_lockButton; QPushButton *m_lockButton;
QPushButton *m_subdivButton; QPushButton *m_subdivButton;
@ -64,7 +66,6 @@ private:
QPushButton *m_roundButton; QPushButton *m_roundButton;
QPushButton *m_colorButton; QPushButton *m_colorButton;
QPushButton *m_wrapButton; QPushButton *m_wrapButton;
QLabel *m_nameLabel;
QWidget *m_backgroundWidget; QWidget *m_backgroundWidget;
private: private:
void initToolButton(QPushButton *button); void initToolButton(QPushButton *button);