Add project file(.ds3) reader and writer

master
Jeremy Hu 2018-03-15 23:40:30 +08:00
parent 130ffdae79
commit ff19efa188
16 changed files with 435 additions and 317 deletions

View File

@ -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

View File

@ -1,26 +0,0 @@
#include <QGridLayout>
#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();
}

View File

@ -1,20 +0,0 @@
#ifndef COMBINE_EDIT_WIDGET_H
#define COMBINE_EDIT_WIDGET_H
#include <QFrame>
#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

160
src/ds3file.cpp Normal file
View File

@ -0,0 +1,160 @@
#include <QFile>
#include <QTextStream>
#include <QXmlStreamReader>
#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<Ds3ReaderItem> &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;
}

67
src/ds3file.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef DS3_FILE_H
#define DS3_FILE_H
#include <QObject>
#include <QString>
#include <QByteArray>
#include <map>
/*
DUST3D 1.0 xml 12345
<?xml version="1.0" encoding="UTF-8"?>
<ds3>
<model name="ant.xml" offset="0" size="1024"/>
<asset name="ant.jpg" offset="1024" size="279306"/>
</ds3>
... 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<Ds3ReaderItem> &items();
static QString m_applicationName;
static QString m_fileFormatVersion;
static QString m_headFormat;
private:
std::map<QString, Ds3ReaderItem> m_itemsMap;
QList<Ds3ReaderItem> 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<QString, Ds3WriterItem> m_itemsMap;
QList<Ds3WriterItem> m_items;
QString m_filename;
};
#endif

View File

@ -8,17 +8,18 @@
#include <QApplication>
#include <QDesktopWidget>
#include <QStackedWidget>
#include <QXmlStreamReader>
#include <QBuffer>
#include <assert.h>
#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);
}

View File

@ -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;
};

View File

@ -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";

View File

@ -1,6 +1,7 @@
#include <QGraphicsPixmapItem>
#include <QXmlStreamWriter>
#include <QFile>
#include <QApplication>
#include <cmath>
#include <map>
#include <vector>
@ -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,6 +107,7 @@ void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event)
void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
{
QWidget::mouseDoubleClickEvent(event);
if (QApplication::keyboardModifiers() & Qt::ControlModifier)
emit changeTurnaroundTriggered();
}
@ -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<std::map<QString, QString>> pendingEdges;
std::map<QString, SkeletonEditNodeItem *> 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<QString, QString> 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<QString, QString> 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<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> 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<SkeletonEditNodeItem *>(*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<SkeletonEditEdgeItem *>(*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();
}

View File

@ -2,6 +2,9 @@
#define SKELETON_EDIT_GRAPHICS_VIEW_H
#include <QGraphicsView>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#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;

View File

@ -1,39 +0,0 @@
#include "skeletoneditwidget.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGridLayout>
#include <QGraphicsPixmapItem>
// 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);
}

View File

@ -1,26 +0,0 @@
#ifndef SKELETON_EDIT_WIDGET_H
#define SKELETON_EDIT_WIDGET_H
#include <QFrame>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QMouseEvent>
#include <QGraphicsEllipseItem>
#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

View File

@ -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();

View File

@ -13,6 +13,7 @@ struct SkeletonNode
float originY;
float originZ;
float radius;
bool processed;
int bmeshNodeId;
};

View File

@ -9,7 +9,6 @@
#include <QDesktopWidget>
#include <assert.h>
#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");
}

View File

@ -5,7 +5,6 @@
#include <QVBoxLayout>
#include <QPushButton>
#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;