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
HEADERS += src/modelmeshbinder.h
SOURCES += src/modelofflinerender.cpp
HEADERS += src/modelofflinerender.h
SOURCES += src/modelwidget.cpp
HEADERS += src/modelwidget.h

View File

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

View File

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

View File

@ -42,6 +42,7 @@ public:
MeshLoader(MeshResultContext &resultContext);
MeshLoader(Vertex *triangleVertices, int vertexNum);
MeshLoader(const MeshLoader &mesh);
MeshLoader();
~MeshLoader();
Vertex *triangleVertices();
int triangleVertexCount();
@ -54,15 +55,15 @@ public:
void setTextureImage(QImage *textureImage);
const QImage *textureImage();
private:
Vertex *m_triangleVertices;
int m_triangleVertexCount;
Vertex *m_edgeVertices;
int m_edgeVertexCount;
Vertex *m_triangleVertices = nullptr;
int m_triangleVertexCount = 0;
Vertex *m_edgeVertices = nullptr;
int m_edgeVertexCount = 0;
std::vector<QVector3D> m_vertices;
std::vector<std::vector<int>> m_faces;
std::vector<QVector3D> m_triangulatedVertices;
std::vector<TriangulatedFace> m_triangulatedFaces;
QImage *m_textureImage;
QImage *m_textureImage = nullptr;
};
#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_program(nullptr),
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
// support it, the widget will become transparent apart from the logo.
@ -192,7 +194,7 @@ void ModelWidget::mousePressEvent(QMouseEvent *event)
shouldStartMove = true;
}
} else if (event->button() == Qt::MidButton) {
shouldStartMove = true;
shouldStartMove = m_moveEnabled;
}
if (shouldStartMove) {
m_lastPos = event->pos();
@ -270,6 +272,8 @@ void ModelWidget::wheelEvent(QWheelEvent *event)
return;
if (m_moveStarted)
return;
if (!m_zoomEnabled)
return;
qreal delta = event->delta() / 10;
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
if (delta > 0)
@ -299,3 +303,12 @@ void ModelWidget::exportMeshAsObjPlusMaterials(const QString &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
{
Q_OBJECT
public:
ModelWidget(QWidget *parent = 0);
~ModelWidget();
static bool isTransparent() { return m_transparent; }
static void setTransparent(bool t) { m_transparent = t; }
static bool isTransparent()
{
return m_transparent;
}
static void setTransparent(bool t)
{
m_transparent = t;
}
void updateMesh(MeshLoader *mesh);
void exportMeshAsObj(const QString &filename);
void exportMeshAsObjPlusMaterials(const QString &filename);
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
void toggleWireframe();
void enableMove(bool enabled);
void enableZoom(bool enabled);
public slots:
void setXRotation(int angle);
void setYRotation(int angle);
@ -39,7 +44,6 @@ signals:
void xRotationChanged(int angle);
void yRotationChanged(int angle);
void zRotationChanged(int angle);
protected:
void initializeGL() override;
void paintGL() override;
@ -61,6 +65,8 @@ private:
ModelShaderProgram *m_program;
bool m_moveStarted;
SkeletonGraphicsFunctions *m_graphicsFunctions;
bool m_moveEnabled;
bool m_zoomEnabled;
private:
QPoint m_lastPos;
ModelMeshBinder m_meshBinder;

View File

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

View File

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

View File

@ -29,7 +29,6 @@
#include "graphicscontainerwidget.h"
#include "skeletonparttreewidget.h"
#include "rigwidget.h"
#include "modelofflinerender.h"
#include "markiconcreator.h"
#include "tetrapodposeeditwidget.h"
@ -1055,18 +1054,6 @@ void SkeletonDocumentWindow::exportObjPlusMaterialsResult()
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()
{
if (nullptr == m_exportPreviewWidget) {
@ -1146,14 +1133,3 @@ void SkeletonDocumentWindow::updateRadiusLockButtonState()
else
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 exportObjPlusMaterialsResult();
void exportGltfResult();
void exportRenderedResult();
void showExportPreview();
void newWindow();
void newDocument();
@ -60,7 +59,6 @@ private:
void initLockButton(QPushButton *button);
void setCurrentFilename(const QString &filename);
void updateTitle();
void exportRenderedAsImage(const QString &filename);
private:
SkeletonDocument *m_document;
bool m_firstShow;

View File

@ -62,6 +62,42 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
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)
{
if (multiple) {
@ -194,6 +230,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
{
const SkeletonComponent *component = nullptr;
const SkeletonPart *part = nullptr;
SkeletonPartWidget *partWidget = nullptr;
std::set<QUuid> unorderedComponentIds = m_selectedComponentIds;
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;
QWidgetAction forDisplayPartImage(this);
layout->setAlignment(Qt::AlignCenter);
layout->addWidget(previewLabel);
layout->setContentsMargins(0, 0, 0, 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;
widget->setLayout(layout);
forDisplayPartImage.setDefaultWidget(widget);
@ -650,6 +703,7 @@ void SkeletonPartTreeWidget::addComponentChildrenToItem(QUuid componentId, QTree
item->setFlags(item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
QUuid partId = component->linkToPartId;
SkeletonPartWidget *widget = new SkeletonPartWidget(m_document, partId);
widget->previewWidget()->setGraphicsFunctions(this);
item->setSizeHint(0, SkeletonPartWidget::preferredSize());
setItemWidget(item, 0, widget);
widget->reload();

View File

@ -4,10 +4,9 @@
#include <QUuid>
#include <QMouseEvent>
#include "skeletondocument.h"
#include "skeletongraphicswidget.h"
class SkeletonGraphicsFunctions;
class SkeletonPartTreeWidget : public QTreeWidget
class SkeletonPartTreeWidget : public QTreeWidget, public SkeletonGraphicsFunctions
{
Q_OBJECT
signals:
@ -74,6 +73,12 @@ protected:
virtual QSize sizeHint() const;
virtual void mousePressEvent(QMouseEvent *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:
void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem);
void deleteItemChildren(QTreeWidgetItem *item);

View File

@ -52,8 +52,10 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
m_wrapButton->setSizePolicy(retainSizePolicy);
initButton(m_wrapButton);
m_previewLabel = new QLabel;
m_previewLabel->setFixedSize(Theme::previewImageSize, Theme::previewImageSize);
m_previewWidget = new ModelWidget;
m_previewWidget->enableMove(false);
m_previewWidget->enableZoom(false);
m_previewWidget->setFixedSize(Theme::previewImageSize, Theme::previewImageSize);
QWidget *hrLightWidget = new QWidget;
hrLightWidget->setFixedHeight(1);
@ -89,7 +91,7 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
previewAndToolsLayout->setSpacing(0);
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
previewAndToolsLayout->addWidget(m_visibleButton);
previewAndToolsLayout->addWidget(m_previewLabel);
previewAndToolsLayout->addWidget(m_previewWidget);
previewAndToolsLayout->addLayout(toolsLayout);
previewAndToolsLayout->setStretch(0, 0);
previewAndToolsLayout->setStretch(1, 0);
@ -229,6 +231,11 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
updateAllButtons();
}
ModelWidget *SkeletonPartWidget::previewWidget()
{
return m_previewWidget;
}
QSize SkeletonPartWidget::preferredSize()
{
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;
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()

View File

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