From ff19efa1881e2465810978ababa23475a11a131c Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Thu, 15 Mar 2018 23:40:30 +0800 Subject: [PATCH] Add project file(.ds3) reader and writer --- dust3d.pro | 7 +- src/combineeditwidget.cpp | 26 ----- src/combineeditwidget.h | 20 ---- src/ds3file.cpp | 160 ++++++++++++++++++++++++++ src/ds3file.h | 67 +++++++++++ src/mainwindow.cpp | 189 ++++++++++++++----------------- src/mainwindow.h | 11 +- src/modelingwidget.cpp | 4 +- src/skeletoneditgraphicsview.cpp | 147 +++++++++++++----------- src/skeletoneditgraphicsview.h | 12 +- src/skeletoneditwidget.cpp | 39 ------- src/skeletoneditwidget.h | 26 ----- src/skeletontomesh.cpp | 1 + src/skeletontomesh.h | 1 + src/skeletonwidget.cpp | 38 +++---- src/skeletonwidget.h | 4 +- 16 files changed, 435 insertions(+), 317 deletions(-) delete mode 100644 src/combineeditwidget.cpp delete mode 100644 src/combineeditwidget.h create mode 100644 src/ds3file.cpp create mode 100644 src/ds3file.h delete mode 100644 src/skeletoneditwidget.cpp delete mode 100644 src/skeletoneditwidget.h diff --git a/dust3d.pro b/dust3d.pro index ae42fd28..38a9bfc4 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -10,9 +10,6 @@ HEADERS += src/mainwindow.h SOURCES += src/modelingwidget.cpp HEADERS += src/modelingwidget.h -SOURCES += src/skeletoneditwidget.cpp -HEADERS += src/skeletoneditwidget.h - SOURCES += src/skeletoneditgraphicsview.cpp HEADERS += src/skeletoneditgraphicsview.h @@ -31,8 +28,8 @@ HEADERS += src/turnaroundloader.h SOURCES += src/skeletonwidget.cpp HEADERS += src/skeletonwidget.h -SOURCES += src/combineeditwidget.cpp -HEADERS += src/combineeditwidget.h +SOURCES += src/ds3file.cpp +HEADERS += src/ds3file.h SOURCES += src/theme.cpp HEADERS += src/theme.h diff --git a/src/combineeditwidget.cpp b/src/combineeditwidget.cpp deleted file mode 100644 index 9b5e29fb..00000000 --- a/src/combineeditwidget.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include "combineeditwidget.h" - -CombineEditWidget::CombineEditWidget(QWidget *parent) : - QFrame(parent) -{ - m_modelingWidget = new ModelingWidget(this); - - QGridLayout *mainLayout = new QGridLayout; - mainLayout->addWidget(m_modelingWidget, 0, 0, 1, 1); - - setLayout(mainLayout); -} - -ModelingWidget *CombineEditWidget::modelingWidget() -{ - return m_modelingWidget; -} - -void CombineEditWidget::resizeEvent(QResizeEvent *event) -{ - QFrame::resizeEvent(event); - emit sizeChanged(); -} - - diff --git a/src/combineeditwidget.h b/src/combineeditwidget.h deleted file mode 100644 index 80af8403..00000000 --- a/src/combineeditwidget.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef COMBINE_EDIT_WIDGET_H -#define COMBINE_EDIT_WIDGET_H -#include -#include "modelingwidget.h" - -class CombineEditWidget : public QFrame -{ - Q_OBJECT -signals: - void sizeChanged(); -public: - CombineEditWidget(QWidget *parent = 0); - ModelingWidget *modelingWidget(); -protected: - void resizeEvent(QResizeEvent *event); -private: - ModelingWidget *m_modelingWidget; -}; - -#endif diff --git a/src/ds3file.cpp b/src/ds3file.cpp new file mode 100644 index 00000000..60938b0a --- /dev/null +++ b/src/ds3file.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include "ds3file.h" + +QString Ds3FileReader::m_applicationName = QString("DUST3D"); +QString Ds3FileReader::m_fileFormatVersion = QString("1.0"); +QString Ds3FileReader::m_headFormat = QString("xml"); + +QString Ds3FileReader::readFirstLine() +{ + QString firstLine; + QFile file(m_filename); + if (file.open(QIODevice::ReadOnly)) { + QTextStream in(&file); + if (!in.atEnd()) { + firstLine = in.readLine(); + } + file.close(); + } + return firstLine; +} + +Ds3FileReader::Ds3FileReader(const QString &filename) : + m_headerIsGood(false), + m_binaryOffset(0) +{ + m_filename = filename; + QString firstLine = readFirstLine(); + QStringList tokens = firstLine.split(" "); + if (tokens.length() < 4) { + return; + } + if (tokens[0] != Ds3FileReader::m_applicationName) { + return; + } + if (tokens[1] != Ds3FileReader::m_fileFormatVersion) { + return; + } + if (tokens[2] != Ds3FileReader::m_headFormat) { + return; + } + m_binaryOffset = tokens[3].toLongLong(); + QFile file(m_filename); + if (!file.open(QIODevice::ReadOnly)) { + return; + } + QString header = QString::fromUtf8(file.read(m_binaryOffset).constData()).mid(firstLine.size()).trimmed(); + QXmlStreamReader xml(header); + bool ds3TagEntered = false; + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + if (xml.name() == "ds3") { + ds3TagEntered = true; + m_headerIsGood = true; + } else { + if (ds3TagEntered) { + Ds3ReaderItem readerItem; + readerItem.type = xml.name().toString().trimmed(); + readerItem.name = xml.attributes().value("name").toString().trimmed(); + readerItem.offset = xml.attributes().value("offset").toLongLong(); + readerItem.size = xml.attributes().value("size").toLongLong(); + m_items.push_back(readerItem); + m_itemsMap[readerItem.name] = readerItem; + } + } + } else if (xml.isEndElement()) { + if (xml.name() == "ds3") { + ds3TagEntered = false; + } + } + } +} + +void Ds3FileReader::loadItem(const QString &name, QByteArray *byteArray) +{ + byteArray->clear(); + if (!m_headerIsGood) + return; + if (m_itemsMap.find(name) == m_itemsMap.end()) { + return; + } + Ds3ReaderItem readerItem = m_itemsMap[name]; + QFile file(m_filename); + if (!file.open(QIODevice::ReadOnly)) { + return; + } + if (!file.seek(m_binaryOffset + readerItem.offset)) { + return; + } + *byteArray = file.read(readerItem.size); +} + +const QList &Ds3FileReader::items() +{ + return m_items; +} + +bool Ds3FileWriter::add(const QString &name, const QString &type, const QByteArray *byteArray) +{ + if (m_itemsMap.find(name) != m_itemsMap.end()) { + return false; + } + Ds3WriterItem writerItem; + writerItem.type = type; + writerItem.name = name; + writerItem.byteArray = byteArray; + m_itemsMap[name] = writerItem; + m_items.push_back(writerItem); + return true; +} + +bool Ds3FileWriter::save(const QString &filename) +{ + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + + QByteArray headerXml; + { + QXmlStreamWriter stream(&headerXml); + stream.setAutoFormatting(true); + stream.writeStartDocument(); + stream.writeStartElement("ds3"); + + long long offset = 0; + for (int i = 0; i < m_items.size(); i++) { + Ds3WriterItem *writerItem = &m_items[i]; + stream.writeStartElement(writerItem->type); + stream.writeAttribute("name", QString("%1").arg(writerItem->name)); + stream.writeAttribute("offset", QString("%1").arg(offset)); + stream.writeAttribute("size", QString("%1").arg(writerItem->byteArray->size())); + offset += writerItem->byteArray->size(); + stream.writeEndElement(); + } + + stream.writeEndElement(); + stream.writeEndDocument(); + } + char firstLine[1024]; + int firstLineSizeExcludeSizeSelf = sprintf(firstLine, "%s %s %s ", + Ds3FileReader::m_applicationName.toUtf8().constData(), + Ds3FileReader::m_fileFormatVersion.toUtf8().constData(), + Ds3FileReader::m_headFormat.toUtf8().constData()); + unsigned int headerSize = firstLineSizeExcludeSizeSelf + 12 + headerXml.size(); + char headerSizeString[100] = {0}; + sprintf(headerSizeString, "%010u\r\n", headerSize); + file.write(firstLine, firstLineSizeExcludeSizeSelf); + file.write(headerSizeString, strlen(headerSizeString)); + file.write(headerXml); + for (int i = 0; i < m_items.size(); i++) { + Ds3WriterItem *writerItem = &m_items[i]; + file.write(*writerItem->byteArray); + } + + return true; +} + diff --git a/src/ds3file.h b/src/ds3file.h new file mode 100644 index 00000000..4976e0d9 --- /dev/null +++ b/src/ds3file.h @@ -0,0 +1,67 @@ +#ifndef DS3_FILE_H +#define DS3_FILE_H +#include +#include +#include +#include + +/* +DUST3D 1.0 xml 12345 + + + + + +... Binary content ... +*/ + +class Ds3ReaderItem +{ +public: + QString type; + QString name; + long long offset; + long long size; +}; + +class Ds3FileReader : public QObject +{ + Q_OBJECT +public: + Ds3FileReader(const QString &filename); + void loadItem(const QString &name, QByteArray *byteArray); + const QList &items(); + static QString m_applicationName; + static QString m_fileFormatVersion; + static QString m_headFormat; +private: + std::map m_itemsMap; + QList m_items; + QString m_filename; +private: + QString readFirstLine(); + bool m_headerIsGood; + long long m_binaryOffset; +}; + +class Ds3WriterItem +{ +public: + QString type; + QString name; + const QByteArray *byteArray; +}; + +class Ds3FileWriter : public QObject +{ + Q_OBJECT +public: + bool add(const QString &name, const QString &type, const QByteArray *byteArray); + bool save(const QString &filename); +private: + std::map m_itemsMap; + QList m_items; + QString m_filename; +}; + +#endif diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b2c25a78..5c6e03ca 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -8,17 +8,18 @@ #include #include #include +#include +#include #include #include "mainwindow.h" #include "skeletonwidget.h" -#include "combineeditwidget.h" #include "theme.h" +#include "ds3file.h" MainWindow::MainWindow() { - m_partsPageButton = new QPushButton("Parts"); - m_combinePageButton = new QPushButton("Combine"); - m_motionPageButton = new QPushButton("Motion"); + m_modelPageButton = new QPushButton("Model"); + m_sharePageButton = new QPushButton("Share"); QWidget *hrWidget = new QWidget; hrWidget->setFixedHeight(2); @@ -27,25 +28,22 @@ MainWindow::MainWindow() hrWidget->setContentsMargins(0, 0, 0, 0); QButtonGroup *pageButtonGroup = new QButtonGroup; - pageButtonGroup->addButton(m_partsPageButton); - pageButtonGroup->addButton(m_combinePageButton); - pageButtonGroup->addButton(m_motionPageButton); + pageButtonGroup->addButton(m_modelPageButton); + pageButtonGroup->addButton(m_sharePageButton); - m_partsPageButton->setCheckable(true); - m_combinePageButton->setCheckable(true); - m_motionPageButton->setCheckable(true); + m_modelPageButton->setCheckable(true); + m_sharePageButton->setCheckable(true); pageButtonGroup->setExclusive(true); - m_combinePageButton->setChecked(true); + m_modelPageButton->setChecked(true); QHBoxLayout *topButtonsLayout = new QHBoxLayout; topButtonsLayout->setContentsMargins(0, 0, 0, 0); topButtonsLayout->setSpacing(0); topButtonsLayout->addStretch(); - topButtonsLayout->addWidget(m_partsPageButton); - topButtonsLayout->addWidget(m_combinePageButton); - topButtonsLayout->addWidget(m_motionPageButton); + topButtonsLayout->addWidget(m_modelPageButton); + topButtonsLayout->addWidget(m_sharePageButton); topButtonsLayout->addStretch(); QVBoxLayout *topLayout = new QVBoxLayout; @@ -54,86 +52,46 @@ MainWindow::MainWindow() topLayout->addLayout(topButtonsLayout); topLayout->addWidget(hrWidget); - QVBoxLayout *partsRightLayout = new QVBoxLayout; + QVBoxLayout *modelRightLayout = new QVBoxLayout; - partsRightLayout->addSpacing(20); + modelRightLayout->addSpacing(20); QPushButton *changeTurnaroundButton = new QPushButton(" Change Turnaround "); - partsRightLayout->addWidget(changeTurnaroundButton); + modelRightLayout->addWidget(changeTurnaroundButton); - QPushButton *exportPartsModelButton = new QPushButton(" Export Model(.obj) "); - partsRightLayout->addWidget(exportPartsModelButton); + QPushButton *exportPartsModelButton = new QPushButton(" Export "); + modelRightLayout->addWidget(exportPartsModelButton); - QPushButton *newPartsButton = new QPushButton(" New Parts "); - partsRightLayout->addWidget(newPartsButton); + QPushButton *newModelButton = new QPushButton(" New "); + modelRightLayout->addWidget(newModelButton); - QPushButton *loadPartsButton = new QPushButton(" Load Parts "); - partsRightLayout->addWidget(loadPartsButton); + QPushButton *loadModelButton = new QPushButton(" Load "); + modelRightLayout->addWidget(loadModelButton); - QPushButton *savePartsButton = new QPushButton(" Save Parts "); - partsRightLayout->addWidget(savePartsButton); + QPushButton *saveModelButton = new QPushButton(" Save "); + modelRightLayout->addWidget(saveModelButton); - QPushButton *savePartsAsButton = new QPushButton(" Save Parts as "); - partsRightLayout->addWidget(savePartsAsButton); - savePartsAsButton->hide(); + QPushButton *saveModelAsButton = new QPushButton(" Save as "); + modelRightLayout->addWidget(saveModelAsButton); + saveModelAsButton->hide(); - partsRightLayout->addStretch(); + modelRightLayout->addStretch(); SkeletonWidget *skeletonWidget = new SkeletonWidget(this); m_skeletonWidget = skeletonWidget; - QHBoxLayout *partsPageLayout = new QHBoxLayout; - partsPageLayout->addWidget(skeletonWidget); - partsPageLayout->addLayout(partsRightLayout); + QHBoxLayout *modelPageLayout = new QHBoxLayout; + modelPageLayout->addWidget(skeletonWidget); + modelPageLayout->addLayout(modelRightLayout); - QWidget *partsPageWidget = new QWidget; - partsPageWidget->setLayout(partsPageLayout); + QWidget *modelPageWidget = new QWidget; + modelPageWidget->setLayout(modelPageLayout); - QVBoxLayout *combineRightLayout = new QVBoxLayout; - - combineRightLayout->addSpacing(20); - - QPushButton *importPartsToCombineButton = new QPushButton(" Import Parts "); - combineRightLayout->addWidget(importPartsToCombineButton); - - QPushButton *exportCombineModelButton = new QPushButton(" Export Model(.obj) "); - combineRightLayout->addWidget(exportCombineModelButton); - - QPushButton *newCombineButton = new QPushButton(" New Combine "); - combineRightLayout->addWidget(newCombineButton); - - QPushButton *loadCombineButton = new QPushButton(" Load Combine "); - combineRightLayout->addWidget(loadCombineButton); - - QPushButton *saveCombineButton = new QPushButton(" Save Combine "); - combineRightLayout->addWidget(saveCombineButton); - - QPushButton *saveCombineAsButton = new QPushButton(" Save Combine as "); - combineRightLayout->addWidget(saveCombineAsButton); - saveCombineAsButton->hide(); - - combineRightLayout->addStretch(); - - combineRightLayout->setSizeConstraint(QLayout::SetMinimumSize); - - CombineEditWidget *combineEditWidget = new CombineEditWidget(); - - QHBoxLayout *combinePageLayout = new QHBoxLayout; - combinePageLayout->addSpacing(10); - combinePageLayout->addWidget(combineEditWidget); - combinePageLayout->addStretch(); - combinePageLayout->addLayout(combineRightLayout); - combinePageLayout->addSpacing(10); - - QWidget *combinePageWidget = new QWidget; - combinePageWidget->setLayout(combinePageLayout); - - QWidget *motionPageWidget = new QWidget; + QWidget *sharePageWidget = new QWidget; QStackedWidget *stackedWidget = new QStackedWidget; - stackedWidget->addWidget(partsPageWidget); - stackedWidget->addWidget(combinePageWidget); - stackedWidget->addWidget(motionPageWidget); + stackedWidget->addWidget(modelPageWidget); + stackedWidget->addWidget(sharePageWidget); m_stackedWidget = stackedWidget; @@ -152,60 +110,85 @@ MainWindow::MainWindow() connectResult = connect(changeTurnaroundButton, SIGNAL(clicked()), skeletonWidget, SLOT(changeTurnaround())); assert(connectResult); - connectResult = connect(m_partsPageButton, SIGNAL(clicked()), this, SLOT(updatePageButtons())); + connectResult = connect(m_modelPageButton, SIGNAL(clicked()), this, SLOT(updatePageButtons())); assert(connectResult); - connectResult = connect(m_combinePageButton, SIGNAL(clicked()), this, SLOT(updatePageButtons())); + connectResult = connect(m_sharePageButton, SIGNAL(clicked()), this, SLOT(updatePageButtons())); assert(connectResult); - connectResult = connect(m_motionPageButton, SIGNAL(clicked()), this, SLOT(updatePageButtons())); + connectResult = connect(saveModelButton, SIGNAL(clicked()), this, SLOT(saveModel())); assert(connectResult); - connectResult = connect(savePartsButton, SIGNAL(clicked()), this, SLOT(saveParts())); - assert(connectResult); - - connectResult = connect(loadPartsButton, SIGNAL(clicked()), this, SLOT(loadParts())); + connectResult = connect(loadModelButton, SIGNAL(clicked()), this, SLOT(loadModel())); assert(connectResult); updatePageButtons(); } -void MainWindow::loadParts() +void MainWindow::loadModel() { QString filename = QFileDialog::getOpenFileName(this, - tr("Load Parts"), ".", - tr("Xml files (*.xml)")); + tr("Load Model"), ".", + tr("Dust 3D Project (*.ds3)")); if (filename.isEmpty()) return; - m_skeletonWidget->graphicsView()->loadFromXml(filename); + Ds3FileReader ds3Reader(filename); + for (int i = 0; i < ds3Reader.items().size(); ++i) { + Ds3ReaderItem item = ds3Reader.items().at(i); + if (item.type == "model") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + QXmlStreamReader xmlReader(data); + m_skeletonWidget->graphicsView()->loadFromXmlStream(xmlReader); + } else if (item.type == "asset") { + if (item.name == "canvas.png") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + QImage image = QImage::fromData(data, "PNG"); + m_skeletonWidget->graphicsView()->updateBackgroundImage(image); + } + } + } } -void MainWindow::saveParts() +void MainWindow::saveModel() { - if (m_savePartsAs.isEmpty()) { - m_savePartsAs = QFileDialog::getSaveFileName(this, - tr("Save Parts"), ".", - tr("Xml files (*.xml)")); - if (m_savePartsAs.isEmpty()) { + if (m_saveModelAs.isEmpty()) { + m_saveModelAs = QFileDialog::getSaveFileName(this, + tr("Save Model"), ".", + tr("Dust 3D Project (*.ds3)")); + if (m_saveModelAs.isEmpty()) { return; } } - m_skeletonWidget->graphicsView()->saveToXml(m_savePartsAs); + + Ds3FileWriter ds3Writer; + + QByteArray modelXml; + QXmlStreamWriter stream(&modelXml); + m_skeletonWidget->graphicsView()->saveToXmlStream(&stream); + if (modelXml.size() > 0) + ds3Writer.add("model1.xml", "model", &modelXml); + + QByteArray imageByteArray; + QBuffer pngBuffer(&imageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + m_skeletonWidget->graphicsView()->backgroundImage().save(&pngBuffer, "PNG"); + if (imageByteArray.size() > 0) + ds3Writer.add("canvas.png", "asset", &imageByteArray); + + ds3Writer.save(m_saveModelAs); } void MainWindow::updatePageButtons() { - if (m_partsPageButton->isChecked()) { + if (m_modelPageButton->isChecked()) { m_stackedWidget->setCurrentIndex(0); } - if (m_combinePageButton->isChecked()) { + if (m_sharePageButton->isChecked()) { m_stackedWidget->setCurrentIndex(1); } - if (m_motionPageButton->isChecked()) { - m_stackedWidget->setCurrentIndex(2); - } - m_partsPageButton->setStyleSheet(m_partsPageButton->isChecked() ? Theme::tabButtonSelectedStylesheet : Theme::tabButtonStylesheet); - m_combinePageButton->setStyleSheet(m_combinePageButton->isChecked() ? Theme::tabButtonSelectedStylesheet : Theme::tabButtonStylesheet); - m_motionPageButton->setStyleSheet(m_motionPageButton->isChecked() ? Theme::tabButtonSelectedStylesheet : Theme::tabButtonStylesheet); + m_modelPageButton->setStyleSheet(m_modelPageButton->isChecked() ? Theme::tabButtonSelectedStylesheet : Theme::tabButtonStylesheet); + m_sharePageButton->setStyleSheet(m_sharePageButton->isChecked() ? Theme::tabButtonSelectedStylesheet : Theme::tabButtonStylesheet); } diff --git a/src/mainwindow.h b/src/mainwindow.h index b845588f..2a6dbfa3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -14,14 +14,13 @@ public: MainWindow(); public slots: void updatePageButtons(); - void saveParts(); - void loadParts(); + void saveModel(); + void loadModel(); private: - QPushButton *m_partsPageButton; - QPushButton *m_combinePageButton; - QPushButton *m_motionPageButton; + QPushButton *m_modelPageButton; + QPushButton *m_sharePageButton; QStackedWidget *m_stackedWidget; - QString m_savePartsAs; + QString m_saveModelAs; SkeletonWidget *m_skeletonWidget; }; diff --git a/src/modelingwidget.cpp b/src/modelingwidget.cpp index 6fe7cdce..794e0437 100644 --- a/src/modelingwidget.cpp +++ b/src/modelingwidget.cpp @@ -108,7 +108,7 @@ static const char *fragmentShaderSourceCore = "void main() {\n" " highp vec3 L = normalize(lightPos - vert);\n" " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n" - " highp vec3 color = vec3(0.99, 0.4, 0.13);\n" + " highp vec3 color = vec3(1.0, 1.0, 1.0);\n" " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n" " fragColor = vec4(col, 1.0);\n" "}\n"; @@ -134,7 +134,7 @@ static const char *fragmentShaderSource = "void main() {\n" " highp vec3 L = normalize(lightPos - vert);\n" " highp float NL = max(dot(normalize(vertNormal), L), 0.0);\n" - " highp vec3 color = vec3(0.99, 0.4, 0.13);\n" + " highp vec3 color = vec3(1.0, 1.0, 1.0);\n" " highp vec3 col = clamp(color * 0.2 + color * 0.8 * NL, 0.0, 1.0);\n" " gl_FragColor = vec4(col, 1.0);\n" "}\n"; diff --git a/src/skeletoneditgraphicsview.cpp b/src/skeletoneditgraphicsview.cpp index e9dac1d5..7c02636d 100644 --- a/src/skeletoneditgraphicsview.cpp +++ b/src/skeletoneditgraphicsview.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,7 @@ void SkeletonEditGraphicsView::toggleAddNodeMode() void SkeletonEditGraphicsView::applyAddNodeMode() { m_pendingNodeItem->setVisible(m_inAddNodeMode); - m_pendingEdgeItem->setVisible(m_inAddNodeMode); + m_pendingEdgeItem->setVisible(m_inAddNodeMode && m_nextStartNodeItem); setMouseTracking(true); } @@ -93,6 +94,10 @@ void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event) if (m_lastHoverNodeItem) { setNextStartNodeItem(m_lastHoverNodeItem->master()); m_lastHoverNodeItem = NULL; + } else { + if (m_nextStartNodeItem) { + setNextStartNodeItem(NULL); + } } } } @@ -102,7 +107,8 @@ void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event) void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) { QWidget::mouseDoubleClickEvent(event); - emit changeTurnaroundTriggered(); + if (QApplication::keyboardModifiers() & Qt::ControlModifier) + emit changeTurnaroundTriggered(); } float SkeletonEditGraphicsView::findXForSlave(float x) @@ -110,6 +116,21 @@ float SkeletonEditGraphicsView::findXForSlave(float x) return x - m_backgroundItem->boundingRect().width() / 4; } +void SkeletonEditGraphicsView::keyPressEvent(QKeyEvent *event) +{ + QWidget::keyPressEvent(event); + if (!m_backgroundLoaded) + return; + if (event->key() == Qt::Key_A) + toggleAddNodeMode(); +} + +void SkeletonEditGraphicsView::resizeEvent(QResizeEvent *event) +{ + QFrame::resizeEvent(event); + emit sizeChanged(); +} + void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event) { QWidget::mouseReleaseEvent(event); @@ -135,8 +156,6 @@ void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event) emit nodesChanged(); } m_isMovingNodeItem = false; - } else if (event->button() == Qt::RightButton) { - toggleAddNodeMode(); } } @@ -313,6 +332,16 @@ void SkeletonEditGraphicsView::updateBackgroundImage(const QImage &image) } } +QPixmap SkeletonEditGraphicsView::backgroundImage() +{ + return m_backgroundItem->pixmap(); +} + +bool SkeletonEditGraphicsView::hasBackgroundImage() +{ + return m_backgroundLoaded; +} + void SkeletonEditGraphicsView::adjustItems(QSizeF oldSceneSize, QSizeF newSceneSize) { if (oldSceneSize == newSceneSize) @@ -338,13 +367,8 @@ void SkeletonEditGraphicsView::adjustItems(QSizeF oldSceneSize, QSizeF newSceneS } } -void SkeletonEditGraphicsView::loadFromXml(const QString &filename) +void SkeletonEditGraphicsView::loadFromXmlStream(QXmlStreamReader &reader) { - QFile file(filename); - if (!file.open(QFile::ReadOnly | QFile::Text)) { - return; - } - float radiusMul = 1.0; float xMul = 1.0; float yMul = radiusMul; @@ -353,33 +377,34 @@ void SkeletonEditGraphicsView::loadFromXml(const QString &filename) std::vector> pendingEdges; std::map addedNodeMapById; - QXmlStreamReader xml; - xml.setDevice(&file); - while (!xml.atEnd()) { - xml.readNext(); - printf("tokenString:%s\n", xml.tokenString().toUtf8().constData()); - if (xml.isStartElement()) { - printf("name:%s\n", xml.name().toUtf8().constData()); - if (xml.name() == "canvas") { - QString canvasWidth = xml.attributes().value("width").toString(); - QString canvasHeight = xml.attributes().value("height").toString(); + while (!reader.atEnd()) { + reader.readNext(); + if (reader.isStartElement()) { + if (reader.name() == "canvas") { + QString canvasWidth = reader.attributes().value("width").toString(); + QString canvasHeight = reader.attributes().value("height").toString(); float canvasHeightWidth = canvasWidth.toFloat(); float canvasHeightVal = canvasHeight.toFloat(); + if (!hasBackgroundImage()) { + QPixmap emptyImage((int)canvasHeightWidth, (int)canvasHeightVal); + emptyImage.fill(QWidget::palette().color(QWidget::backgroundRole())); + updateBackgroundImage(emptyImage.toImage()); + } if (canvasHeightVal > 0) radiusMul = (float)scene()->sceneRect().height() / canvasHeightVal; if (canvasHeightWidth > 0) xMul = (float)scene()->sceneRect().width() / canvasHeightWidth; yMul = radiusMul; - } else if (xml.name() == "origin") { + } else if (reader.name() == "origin") { if (pendingNodes.size() > 0) { - pendingNodes[pendingNodes.size() - 1]["x"] = QString("%1").arg(xml.attributes().value("x").toString().toFloat() * xMul); - pendingNodes[pendingNodes.size() - 1]["y"] = QString("%1").arg(xml.attributes().value("y").toString().toFloat() * yMul); + pendingNodes[pendingNodes.size() - 1]["x"] = QString("%1").arg(reader.attributes().value("x").toString().toFloat() * xMul); + pendingNodes[pendingNodes.size() - 1]["y"] = QString("%1").arg(reader.attributes().value("y").toString().toFloat() * yMul); } - } else if (xml.name() == "node") { - QString nodeId = xml.attributes().value("id").toString(); - QString nodeType = xml.attributes().value("type").toString(); - QString nodePairId = xml.attributes().value("pair").toString(); - QString nodeRadius = xml.attributes().value("radius").toString(); + } else if (reader.name() == "node") { + QString nodeId = reader.attributes().value("id").toString(); + QString nodeType = reader.attributes().value("type").toString(); + QString nodePairId = reader.attributes().value("pair").toString(); + QString nodeRadius = reader.attributes().value("radius").toString(); std::map pendingNode; pendingNode["id"] = nodeId; pendingNode["type"] = nodeType; @@ -388,10 +413,10 @@ void SkeletonEditGraphicsView::loadFromXml(const QString &filename) pendingNode["x"] = "0"; pendingNode["y"] = "0"; pendingNodes.push_back(pendingNode); - } else if (xml.name() == "edge") { - QString edgeId = xml.attributes().value("id").toString(); - QString edgeFromNodeId = xml.attributes().value("from").toString(); - QString edgeToNodeId = xml.attributes().value("to").toString(); + } else if (reader.name() == "edge") { + QString edgeId = reader.attributes().value("id").toString(); + QString edgeFromNodeId = reader.attributes().value("from").toString(); + QString edgeToNodeId = reader.attributes().value("to").toString(); if (!edgeFromNodeId.isEmpty() && !edgeToNodeId.isEmpty()) { std::map pendingEdge; pendingEdge["id"] = edgeId; @@ -431,18 +456,14 @@ void SkeletonEditGraphicsView::loadFromXml(const QString &filename) emit nodesChanged(); } -void SkeletonEditGraphicsView::saveToXml(const QString &filename) +void SkeletonEditGraphicsView::saveToXmlStream(QXmlStreamWriter *writer) { - QFile file(filename); - file.open(QIODevice::WriteOnly); + writer->setAutoFormatting(true); + writer->writeStartDocument(); - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument(); - - stream.writeStartElement("canvas"); - stream.writeAttribute("width", QString("%1").arg(scene()->sceneRect().width())); - stream.writeAttribute("height", QString("%1").arg(scene()->sceneRect().height())); + writer->writeStartElement("canvas"); + writer->writeAttribute("width", QString("%1").arg(scene()->sceneRect().width())); + writer->writeAttribute("height", QString("%1").arg(scene()->sceneRect().height())); QList::iterator it; QList list = scene()->items(); @@ -455,40 +476,40 @@ void SkeletonEditGraphicsView::saveToXml(const QString &filename) nextNodeId++; } } - stream.writeStartElement("nodes"); + writer->writeStartElement("nodes"); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "node") { SkeletonEditNodeItem *nodeItem = static_cast(*it); - stream.writeStartElement("node"); - stream.writeAttribute("id", QString("node%1").arg(nodeIdMap[nodeItem])); - stream.writeAttribute("type", nodeItem->isMaster() ? "master" : "slave"); - stream.writeAttribute("pair", QString("node%1").arg(nodeIdMap[nodeItem->pair()])); - stream.writeAttribute("radius", QString("%1").arg(nodeItem->radius())); - stream.writeStartElement("origin"); + writer->writeStartElement("node"); + writer->writeAttribute("id", QString("node%1").arg(nodeIdMap[nodeItem])); + writer->writeAttribute("type", nodeItem->isMaster() ? "master" : "slave"); + writer->writeAttribute("pair", QString("node%1").arg(nodeIdMap[nodeItem->pair()])); + writer->writeAttribute("radius", QString("%1").arg(nodeItem->radius())); + writer->writeStartElement("origin"); QPointF origin = nodeItem->origin(); - stream.writeAttribute("x", QString("%1").arg(origin.x())); - stream.writeAttribute("y", QString("%1").arg(origin.y())); - stream.writeEndElement(); - stream.writeEndElement(); + writer->writeAttribute("x", QString("%1").arg(origin.x())); + writer->writeAttribute("y", QString("%1").arg(origin.y())); + writer->writeEndElement(); + writer->writeEndElement(); } } - stream.writeEndElement(); - stream.writeStartElement("edges"); + writer->writeEndElement(); + writer->writeStartElement("edges"); int nextEdgeId = 1; for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); - stream.writeStartElement("edge"); - stream.writeAttribute("id", QString("edge%1").arg(nextEdgeId)); - stream.writeAttribute("from", QString("node%1").arg(nodeIdMap[edgeItem->firstNode()])); - stream.writeAttribute("to", QString("node%1").arg(nodeIdMap[edgeItem->secondNode()])); - stream.writeEndElement(); + writer->writeStartElement("edge"); + writer->writeAttribute("id", QString("edge%1").arg(nextEdgeId)); + writer->writeAttribute("from", QString("node%1").arg(nodeIdMap[edgeItem->firstNode()])); + writer->writeAttribute("to", QString("node%1").arg(nodeIdMap[edgeItem->secondNode()])); + writer->writeEndElement(); nextEdgeId++; } } - stream.writeEndElement(); + writer->writeEndElement(); - stream.writeEndElement(); - stream.writeEndDocument(); + writer->writeEndElement(); + writer->writeEndDocument(); } diff --git a/src/skeletoneditgraphicsview.h b/src/skeletoneditgraphicsview.h index ffd90e95..e0351605 100644 --- a/src/skeletoneditgraphicsview.h +++ b/src/skeletoneditgraphicsview.h @@ -2,6 +2,9 @@ #define SKELETON_EDIT_GRAPHICS_VIEW_H #include #include +#include +#include +#include #include "skeletoneditnodeitem.h" #include "skeletoneditedgeitem.h" @@ -9,6 +12,7 @@ class SkeletonEditGraphicsView : public QGraphicsView { Q_OBJECT signals: + void sizeChanged(); void nodesChanged(); void changeTurnaroundTriggered(); public slots: @@ -17,14 +21,18 @@ public slots: public: SkeletonEditGraphicsView(QWidget *parent = 0); void updateBackgroundImage(const QImage &image); - void saveToXml(const QString &filename); - void loadFromXml(const QString &filename); + void saveToXmlStream(QXmlStreamWriter *writer); + void loadFromXmlStream(QXmlStreamReader &reader); + bool hasBackgroundImage(); + QPixmap backgroundImage(); protected: void mouseMoveEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); void mouseReleaseEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void resizeEvent(QResizeEvent *event); private: QGraphicsPixmapItem *m_backgroundItem; QGraphicsEllipseItem *m_pendingNodeItem; diff --git a/src/skeletoneditwidget.cpp b/src/skeletoneditwidget.cpp deleted file mode 100644 index 4f9195ed..00000000 --- a/src/skeletoneditwidget.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "skeletoneditwidget.h" -#include -#include -#include -#include - -// Modifed from http://doc.qt.io/qt-5/qtwidgets-graphicsview-chip-view-cpp.html - -SkeletonEditWidget::SkeletonEditWidget(QWidget *parent) : - QFrame(parent) -{ - m_graphicsView = new SkeletonEditGraphicsView(this); - m_graphicsView->setRenderHint(QPainter::Antialiasing, false); - m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - m_graphicsView->setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern)); - - QGridLayout *mainLayout = new QGridLayout; - mainLayout->addWidget(m_graphicsView, 0, 0, 1, 1); - - setLayout(mainLayout); -} - -SkeletonEditGraphicsView *SkeletonEditWidget::graphicsView() -{ - return m_graphicsView; -} - -void SkeletonEditWidget::resizeEvent(QResizeEvent *event) -{ - QFrame::resizeEvent(event); - emit sizeChanged(); -} - -void SkeletonEditWidget::mouseMoveEvent(QMouseEvent *event) -{ - QFrame::mouseMoveEvent(event); -} diff --git a/src/skeletoneditwidget.h b/src/skeletoneditwidget.h deleted file mode 100644 index 8038c4f1..00000000 --- a/src/skeletoneditwidget.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SKELETON_EDIT_WIDGET_H -#define SKELETON_EDIT_WIDGET_H -#include -#include -#include -#include -#include -#include -#include "skeletoneditgraphicsview.h" - -class SkeletonEditWidget : public QFrame -{ - Q_OBJECT -signals: - void sizeChanged(); -public: - SkeletonEditWidget(QWidget *parent = 0); - SkeletonEditGraphicsView *graphicsView(); -protected: - void resizeEvent(QResizeEvent *event); - void mouseMoveEvent(QMouseEvent *event); -private: - SkeletonEditGraphicsView *m_graphicsView; -}; - -#endif diff --git a/src/skeletontomesh.cpp b/src/skeletontomesh.cpp index 06a02f4a..15b2d58f 100644 --- a/src/skeletontomesh.cpp +++ b/src/skeletontomesh.cpp @@ -34,6 +34,7 @@ SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) : node.originX = origin.x(); node.originY = origin.y(); node.originZ = nodeItem->slave()->origin().x(); + node.processed = false; node.bmeshNodeId = -1; node.radius = nodeItem->radius(); diff --git a/src/skeletontomesh.h b/src/skeletontomesh.h index 51397bb7..036290a8 100644 --- a/src/skeletontomesh.h +++ b/src/skeletontomesh.h @@ -13,6 +13,7 @@ struct SkeletonNode float originY; float originZ; float radius; + bool processed; int bmeshNodeId; }; diff --git a/src/skeletonwidget.cpp b/src/skeletonwidget.cpp index b398719f..d683c8c4 100644 --- a/src/skeletonwidget.cpp +++ b/src/skeletonwidget.cpp @@ -9,7 +9,6 @@ #include #include #include "skeletonwidget.h" -#include "skeletoneditwidget.h" #include "meshlite.h" #include "skeletontomesh.h" #include "turnaroundloader.h" @@ -24,7 +23,12 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) : QHBoxLayout *topLayout = new QHBoxLayout; topLayout->addStretch(); - m_skeletonEditWidget = new SkeletonEditWidget; + m_graphicsView = new SkeletonEditGraphicsView(this); + m_graphicsView->setRenderHint(QPainter::Antialiasing, false); + m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + m_graphicsView->setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern)); m_modelingWidget = new ModelingWidget(this); m_modelingWidget->setMinimumSize(128, 128); @@ -59,7 +63,7 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) : QHBoxLayout *middleLayout = new QHBoxLayout; middleLayout->addLayout(leftLayout); - middleLayout->addWidget(m_skeletonEditWidget); + middleLayout->addWidget(m_graphicsView); middleLayout->addLayout(rightLayout); QVBoxLayout *mainLayout = new QVBoxLayout; @@ -73,19 +77,19 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) : bool connectResult; - connectResult = connect(addAction, SIGNAL(triggered(bool)), m_skeletonEditWidget->graphicsView(), SLOT(turnOnAddNodeMode())); + connectResult = connect(addAction, SIGNAL(triggered(bool)), m_graphicsView, SLOT(turnOnAddNodeMode())); assert(connectResult); - connectResult = connectResult = connect(selectAction, SIGNAL(triggered(bool)), m_skeletonEditWidget->graphicsView(), SLOT(turnOffAddNodeMode())); + connectResult = connectResult = connect(selectAction, SIGNAL(triggered(bool)), m_graphicsView, SLOT(turnOffAddNodeMode())); assert(connectResult); - connectResult = connect(m_skeletonEditWidget->graphicsView(), SIGNAL(nodesChanged()), this, SLOT(skeletonChanged())); + connectResult = connect(m_graphicsView, SIGNAL(nodesChanged()), this, SLOT(skeletonChanged())); assert(connectResult); - connectResult = connect(m_skeletonEditWidget, SIGNAL(sizeChanged()), this, SLOT(turnaroundChanged())); + connectResult = connect(m_graphicsView, SIGNAL(sizeChanged()), this, SLOT(turnaroundChanged())); assert(connectResult); - connectResult = connect(m_skeletonEditWidget->graphicsView(), SIGNAL(changeTurnaroundTriggered()), this, SLOT(changeTurnaround())); + connectResult = connect(m_graphicsView, SIGNAL(changeTurnaroundTriggered()), this, SLOT(changeTurnaround())); assert(connectResult); //connectResult = connect(clipButton, SIGNAL(clicked()), this, SLOT(saveClip())); @@ -94,7 +98,7 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) : SkeletonEditGraphicsView *SkeletonWidget::graphicsView() { - return m_skeletonEditWidget->graphicsView(); + return m_graphicsView; } void SkeletonWidget::showModelingWidgetAtCorner() @@ -130,7 +134,7 @@ void SkeletonWidget::skeletonChanged() m_skeletonDirty = false; QThread *thread = new QThread; - m_skeletonToMesh = new SkeletonToMesh(m_skeletonEditWidget->graphicsView()); + m_skeletonToMesh = new SkeletonToMesh(m_graphicsView); m_skeletonToMesh->moveToThread(thread); connect(thread, SIGNAL(started()), m_skeletonToMesh, SLOT(process())); connect(m_skeletonToMesh, SIGNAL(finished()), this, SLOT(meshReady())); @@ -153,7 +157,7 @@ void SkeletonWidget::turnaroundChanged() QThread *thread = new QThread; m_turnaroundLoader = new TurnaroundLoader(m_turnaroundFilename, - m_skeletonEditWidget->rect().size()); + m_graphicsView->rect().size()); m_turnaroundLoader->moveToThread(thread); connect(thread, SIGNAL(started()), m_turnaroundLoader, SLOT(process())); connect(m_turnaroundLoader, SIGNAL(finished()), this, SLOT(turnaroundImageReady())); @@ -166,7 +170,7 @@ void SkeletonWidget::turnaroundImageReady() { QImage *backgroundImage = m_turnaroundLoader->takeResultImage(); if (backgroundImage && backgroundImage->width() > 0 && backgroundImage->height() > 0) { - m_skeletonEditWidget->graphicsView()->updateBackgroundImage(*backgroundImage); + m_graphicsView->updateBackgroundImage(*backgroundImage); } delete backgroundImage; delete m_turnaroundLoader; @@ -187,13 +191,3 @@ void SkeletonWidget::changeTurnaround() turnaroundChanged(); } -void SkeletonWidget::saveClip() -{ - QImage image = m_modelingWidget->grabFramebuffer(); - //QTableWidgetItem *item = new QTableWidgetItem; - //item->setSizeHint(QSize(32, 32)); - //item->setIcon(QPixmap::fromImage(image.scaled(32, 32))); - //m_clipTableWidget->insertRow(m_clipTableWidget->rowCount()); - //m_clipTableWidget->setItem(m_clipTableWidget->rowCount() - 1, 0, item); - //image.save("/Users/jeremy/Repositories/dust3d/gl.png"); -} diff --git a/src/skeletonwidget.h b/src/skeletonwidget.h index 3e04d17d..ed56eec1 100644 --- a/src/skeletonwidget.h +++ b/src/skeletonwidget.h @@ -5,7 +5,6 @@ #include #include #include "modelingwidget.h" -#include "skeletoneditwidget.h" #include "skeletontomesh.h" #include "turnaroundloader.h" #include "skeletoneditgraphicsview.h" @@ -22,11 +21,10 @@ public slots: void turnaroundChanged(); void turnaroundImageReady(); void changeTurnaround(); - void saveClip(); void showModelingWidgetAtCorner(); private: ModelingWidget *m_modelingWidget; - SkeletonEditWidget *m_skeletonEditWidget; + SkeletonEditGraphicsView *m_graphicsView; SkeletonToMesh *m_skeletonToMesh; bool m_skeletonDirty; TurnaroundLoader *m_turnaroundLoader;