Add silhouette image generator

master
huxingyi 2020-12-13 22:51:45 +09:30
parent 86e2075142
commit 52252b70af
7 changed files with 266 additions and 2 deletions

View File

@ -538,6 +538,9 @@ HEADERS += src/motionbuilder.h
SOURCES += src/statusbarlabel.cpp SOURCES += src/statusbarlabel.cpp
HEADERS += src/statusbarlabel.h HEADERS += src/statusbarlabel.h
SOURCES += src/silhouetteimagegenerator.cpp
HEADERS += src/silhouetteimagegenerator.h
SOURCES += src/main.cpp SOURCES += src/main.cpp
HEADERS += src/version.h HEADERS += src/version.h

View File

@ -52,6 +52,7 @@
#include "objectxml.h" #include "objectxml.h"
#include "rigxml.h" #include "rigxml.h"
#include "statusbarlabel.h" #include "statusbarlabel.h"
#include "silhouetteimagegenerator.h"
int DocumentWindow::m_autoRecovered = false; int DocumentWindow::m_autoRecovered = false;
@ -80,6 +81,22 @@ const std::map<DocumentWindow *, QUuid> &DocumentWindow::documentWindows()
return g_documentWindows; return g_documentWindows;
} }
DocumentWindow::GraphicsViewEditTarget DocumentWindow::graphicsViewEditTarget()
{
return m_graphicsViewEditTarget;
}
void DocumentWindow::updateGraphicsViewEditTarget(GraphicsViewEditTarget target)
{
if (m_graphicsViewEditTarget == target)
return;
m_graphicsViewEditTarget = target;
if (GraphicsViewEditTarget::Bone == m_graphicsViewEditTarget)
generateSilhouetteImage();
emit graphicsViewEditTargetChanged();
}
Document *DocumentWindow::document() Document *DocumentWindow::document()
{ {
return m_document; return m_document;
@ -520,12 +537,12 @@ DocumentWindow::DocumentWindow() :
connect(boneLabel, &StatusBarLabel::clicked, this, [=]() { connect(boneLabel, &StatusBarLabel::clicked, this, [=]() {
boneLabel->setSelected(true); boneLabel->setSelected(true);
shapeLabel->setSelected(false); shapeLabel->setSelected(false);
// TODO: updateGraphicsViewEditTarget(GraphicsViewEditTarget::Bone);
}); });
connect(shapeLabel, &StatusBarLabel::clicked, this, [=]() { connect(shapeLabel, &StatusBarLabel::clicked, this, [=]() {
shapeLabel->setSelected(true); shapeLabel->setSelected(true);
boneLabel->setSelected(false); boneLabel->setSelected(false);
// TODO: updateGraphicsViewEditTarget(GraphicsViewEditTarget::Shape);
}); });
/////////////////////// Status Bar End //////////////////////////// /////////////////////// Status Bar End ////////////////////////////
@ -1354,6 +1371,9 @@ DocumentWindow::DocumentWindow() :
connect(m_document, &Document::scriptChanged, m_document, &Document::runScript); connect(m_document, &Document::scriptChanged, m_document, &Document::runScript);
connect(m_document, &Document::scriptModifiedFromExternal, m_document, &Document::runScript); connect(m_document, &Document::scriptModifiedFromExternal, m_document, &Document::runScript);
connect(m_document, &Document::skeletonChanged, this, &DocumentWindow::generateSilhouetteImage);
connect(m_graphicsWidget, &SkeletonGraphicsWidget::loadedTurnaroundImageChanged, this, &DocumentWindow::generateSilhouetteImage);
initShortCuts(this, m_graphicsWidget); initShortCuts(this, m_graphicsWidget);
connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady); connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady);
@ -2634,3 +2654,47 @@ ModelWidget *DocumentWindow::modelWidget()
{ {
return m_modelRenderWidget; return m_modelRenderWidget;
} }
void DocumentWindow::generateSilhouetteImage()
{
if (GraphicsViewEditTarget::Bone != m_graphicsViewEditTarget ||
nullptr != m_silhouetteImageGenerator) {
m_isSilhouetteImageObsolete = true;
return;
}
m_isSilhouetteImageObsolete = false;
const QImage *loadedTurnaroundImage = m_graphicsWidget->loadedTurnaroundImage();
if (nullptr == loadedTurnaroundImage) {
return;
}
QThread *thread = new QThread;
Snapshot *snapshot = new Snapshot;
m_document->toSnapshot(snapshot);
m_silhouetteImageGenerator = new SilhouetteImageGenerator(loadedTurnaroundImage->width(),
loadedTurnaroundImage->height(), snapshot);
m_silhouetteImageGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_silhouetteImageGenerator, &SilhouetteImageGenerator::process);
connect(m_silhouetteImageGenerator, &SilhouetteImageGenerator::finished, this, &DocumentWindow::silhouetteImageReady);
connect(m_silhouetteImageGenerator, &SilhouetteImageGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void DocumentWindow::silhouetteImageReady()
{
QImage *image = m_silhouetteImageGenerator->takeResultImage();
if (nullptr != image)
image->save("test.png");
delete image;
delete m_silhouetteImageGenerator;
m_silhouetteImageGenerator = nullptr;
if (m_isSilhouetteImageObsolete)
generateSilhouetteImage();
}

View File

@ -24,6 +24,7 @@
class SkeletonGraphicsWidget; class SkeletonGraphicsWidget;
class PartTreeWidget; class PartTreeWidget;
class SpinnableAwesomeButton; class SpinnableAwesomeButton;
class SilhouetteImageGenerator;
class DocumentWindow : public QMainWindow class DocumentWindow : public QMainWindow
{ {
@ -33,11 +34,19 @@ signals:
void uninialized(); void uninialized();
void waitingExportFinished(const QString &filename, bool isSuccessful); void waitingExportFinished(const QString &filename, bool isSuccessful);
void mouseTargetVertexPositionChanged(const QVector3D &position); void mouseTargetVertexPositionChanged(const QVector3D &position);
void graphicsViewEditTargetChanged();
public: public:
enum class GraphicsViewEditTarget
{
Shape,
Bone
};
DocumentWindow(); DocumentWindow();
~DocumentWindow(); ~DocumentWindow();
Document *document(); Document *document();
ModelWidget *modelWidget(); ModelWidget *modelWidget();
GraphicsViewEditTarget graphicsViewEditTarget();
static DocumentWindow *createDocumentWindow(); static DocumentWindow *createDocumentWindow();
static const std::map<DocumentWindow *, QUuid> &documentWindows(); static const std::map<DocumentWindow *, QUuid> &documentWindows();
static void showAcknowlegements(); static void showAcknowlegements();
@ -105,6 +114,9 @@ public slots:
void generatePartPreviewImages(); void generatePartPreviewImages();
void partPreviewImagesReady(); void partPreviewImagesReady();
void updateRegenerateIcon(); void updateRegenerateIcon();
void updateGraphicsViewEditTarget(GraphicsViewEditTarget target);
void generateSilhouetteImage();
void silhouetteImageReady();
private: private:
void initLockButton(QPushButton *button); void initLockButton(QPushButton *button);
void setCurrentFilename(const QString &filename); void setCurrentFilename(const QString &filename);
@ -242,6 +254,10 @@ private:
SpinnableAwesomeButton *m_regenerateButton = nullptr; SpinnableAwesomeButton *m_regenerateButton = nullptr;
QWidget *m_paintWidget = nullptr; QWidget *m_paintWidget = nullptr;
GraphicsViewEditTarget m_graphicsViewEditTarget = GraphicsViewEditTarget::Shape;
bool m_isSilhouetteImageObsolete = false;
SilhouetteImageGenerator *m_silhouetteImageGenerator = nullptr;
public: public:
static int m_autoRecovered; static int m_autoRecovered;
}; };

View File

@ -0,0 +1,146 @@
#include <QPainter>
#include <QBrush>
#include "silhouetteimagegenerator.h"
#include "util.h"
SilhouetteImageGenerator::SilhouetteImageGenerator(int width, int height, Snapshot *snapshot) :
m_width(width),
m_height(height),
m_snapshot(snapshot)
{
}
SilhouetteImageGenerator::~SilhouetteImageGenerator()
{
delete m_snapshot;
delete m_resultImage;
}
QImage *SilhouetteImageGenerator::takeResultImage()
{
QImage *resultImage = m_resultImage;
m_resultImage = nullptr;
return resultImage;
}
void SilhouetteImageGenerator::generate()
{
if (m_width <= 0 || m_height <= 0 || nullptr == m_snapshot)
return;
//float originX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
//float originY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
//float originZ = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
delete m_resultImage;
m_resultImage = new QImage(m_width, m_height, QImage::Format_ARGB32);
m_resultImage->fill(QColor(0xE1, 0xD2, 0xBD));
struct NodeInfo
{
QVector3D position;
float radius;
};
std::map<QString, NodeInfo> nodePositionMap;
for (const auto &node: m_snapshot->nodes) {
NodeInfo nodeInfo;
nodeInfo.position = QVector3D(valueOfKeyInMapOrEmpty(node.second, "x").toFloat(),
valueOfKeyInMapOrEmpty(node.second, "y").toFloat(),
valueOfKeyInMapOrEmpty(node.second, "z").toFloat());
nodeInfo.radius = valueOfKeyInMapOrEmpty(node.second, "radius").toFloat();
nodePositionMap.insert({node.first, nodeInfo});
}
QPainter painter;
painter.begin(m_resultImage);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setPen(Qt::NoPen);
QBrush brush;
brush.setColor(QColor(0x88, 0x80, 0x73));
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
for (const auto &it: nodePositionMap) {
const auto &nodeInfo = it.second;
painter.drawEllipse((nodeInfo.position.x() - nodeInfo.radius) * m_height,
(nodeInfo.position.y() - nodeInfo.radius) * m_height,
nodeInfo.radius * m_height * 2.0,
nodeInfo.radius * m_height * 2.0);
painter.drawEllipse((nodeInfo.position.z() - nodeInfo.radius) * m_height,
(nodeInfo.position.y() - nodeInfo.radius) * m_height,
nodeInfo.radius * m_height * 2.0,
nodeInfo.radius * m_height * 2.0);
}
for (int round = 0; round < 2; ++round) {
if (1 == round)
painter.setCompositionMode(QPainter::CompositionMode_Multiply);
for (const auto &edge: m_snapshot->edges) {
QString partId = valueOfKeyInMapOrEmpty(edge.second, "partId");
QString fromNodeId = valueOfKeyInMapOrEmpty(edge.second, "from");
QString toNodeId = valueOfKeyInMapOrEmpty(edge.second, "to");
const auto &fromNodeInfo = nodePositionMap[fromNodeId];
const auto &toNodeInfo = nodePositionMap[toNodeId];
{
QVector3D pointerOut = QVector3D(0.0, 0.0, 1.0);
QVector3D direction = (QVector3D(toNodeInfo.position.x(), toNodeInfo.position.y(), 0.0) -
QVector3D(fromNodeInfo.position.x(), fromNodeInfo.position.y(), 0.0)).normalized();
QVector3D fromBaseDirection = QVector3D::crossProduct(pointerOut, direction);
QVector3D fromBaseRadius = fromBaseDirection * fromNodeInfo.radius;
QVector3D fromBaseFirstPoint = QVector3D(fromNodeInfo.position.x(), fromNodeInfo.position.y(), 0.0) - fromBaseRadius;
QVector3D fromBaseSecondPoint = QVector3D(fromNodeInfo.position.x(), fromNodeInfo.position.y(), 0.0) + fromBaseRadius;
QVector3D tobaseDirection = -fromBaseDirection;
QVector3D toBaseRadius = tobaseDirection * toNodeInfo.radius;
QVector3D toBaseFirstPoint = QVector3D(toNodeInfo.position.x(), toNodeInfo.position.y(), 0.0) - toBaseRadius;
QVector3D toBaseSecondPoint = QVector3D(toNodeInfo.position.x(), toNodeInfo.position.y(), 0.0) + toBaseRadius;
QPolygon polygon;
polygon.append(QPoint(fromBaseFirstPoint.x() * m_height, fromBaseFirstPoint.y() * m_height));
polygon.append(QPoint(fromBaseSecondPoint.x() * m_height, fromBaseSecondPoint.y() * m_height));
polygon.append(QPoint(toBaseFirstPoint.x() * m_height, toBaseFirstPoint.y() * m_height));
polygon.append(QPoint(toBaseSecondPoint.x() * m_height, toBaseSecondPoint.y() * m_height));
QPainterPath path;
path.addPolygon(polygon);
painter.fillPath(path, brush);
}
{
QVector3D pointerOut = QVector3D(0.0, 0.0, 1.0);
QVector3D direction = (QVector3D(toNodeInfo.position.z(), toNodeInfo.position.y(), 0.0) -
QVector3D(fromNodeInfo.position.z(), fromNodeInfo.position.y(), 0.0)).normalized();
QVector3D fromBaseDirection = QVector3D::crossProduct(pointerOut, direction);
QVector3D fromBaseRadius = fromBaseDirection * fromNodeInfo.radius;
QVector3D fromBaseFirstPoint = QVector3D(fromNodeInfo.position.z(), fromNodeInfo.position.y(), 0.0) - fromBaseRadius;
QVector3D fromBaseSecondPoint = QVector3D(fromNodeInfo.position.z(), fromNodeInfo.position.y(), 0.0) + fromBaseRadius;
QVector3D tobaseDirection = -fromBaseDirection;
QVector3D toBaseRadius = tobaseDirection * toNodeInfo.radius;
QVector3D toBaseFirstPoint = QVector3D(toNodeInfo.position.z(), toNodeInfo.position.y(), 0.0) - toBaseRadius;
QVector3D toBaseSecondPoint = QVector3D(toNodeInfo.position.z(), toNodeInfo.position.y(), 0.0) + toBaseRadius;
QPolygon polygon;
polygon.append(QPoint(fromBaseFirstPoint.x() * m_height, fromBaseFirstPoint.y() * m_height));
polygon.append(QPoint(fromBaseSecondPoint.x() * m_height, fromBaseSecondPoint.y() * m_height));
polygon.append(QPoint(toBaseFirstPoint.x() * m_height, toBaseFirstPoint.y() * m_height));
polygon.append(QPoint(toBaseSecondPoint.x() * m_height, toBaseSecondPoint.y() * m_height));
QPainterPath path;
path.addPolygon(polygon);
painter.fillPath(path, brush);
}
}
}
painter.end();
}
void SilhouetteImageGenerator::process()
{
generate();
emit finished();
}

View File

@ -0,0 +1,26 @@
#ifndef DUST3D_SILHOUETTE_IMAGE_GENERATOR_H
#define DUST3D_SILHOUETTE_IMAGE_GENERATOR_H
#include <QObject>
#include <QImage>
#include "snapshot.h"
class SilhouetteImageGenerator : public QObject
{
Q_OBJECT
public:
SilhouetteImageGenerator(int width, int height, Snapshot *snapshot);
~SilhouetteImageGenerator();
QImage *takeResultImage();
void generate();
signals:
void finished();
public slots:
void process();
private:
int m_width = 0;
int m_height = 0;
QImage *m_resultImage = nullptr;
Snapshot *m_snapshot = nullptr;
};
#endif

View File

@ -761,11 +761,18 @@ void SkeletonGraphicsWidget::turnaroundImageReady()
delete m_turnaroundLoader; delete m_turnaroundLoader;
m_turnaroundLoader = nullptr; m_turnaroundLoader = nullptr;
emit loadedTurnaroundImageChanged();
if (m_turnaroundChanged) { if (m_turnaroundChanged) {
updateTurnaround(); updateTurnaround();
} }
} }
const QImage *SkeletonGraphicsWidget::loadedTurnaroundImage() const
{
return m_backgroundImage;
}
void SkeletonGraphicsWidget::updateCursor() void SkeletonGraphicsWidget::updateCursor()
{ {
if (SkeletonDocumentEditMode::Add != m_document->editMode) { if (SkeletonDocumentEditMode::Add != m_document->editMode) {

View File

@ -516,6 +516,7 @@ signals:
void shortcutToggleRotation(); void shortcutToggleRotation();
void createGriddedPartsFromNodes(const std::set<QUuid> &nodeIds); void createGriddedPartsFromNodes(const std::set<QUuid> &nodeIds);
void addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize); void addPartByPolygons(const QPolygonF &mainProfile, const QPolygonF &sideProfile, const QSizeF &canvasSize);
void loadedTurnaroundImageChanged();
public: public:
SkeletonGraphicsWidget(const SkeletonDocument *document); SkeletonGraphicsWidget(const SkeletonDocument *document);
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap; std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
@ -547,6 +548,7 @@ public:
void setMainProfileOnly(bool mainProfileOnly); void setMainProfileOnly(bool mainProfileOnly);
bool inputWheelEventFromOtherWidget(QWheelEvent *event); bool inputWheelEventFromOtherWidget(QWheelEvent *event);
bool rotated(); bool rotated();
const QImage *loadedTurnaroundImage() const;
protected: protected:
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;