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
parent
2f4102cedc
commit
1ab3d16bd4
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue