2012-03-20 01:57:42 +01:00
|
|
|
#include "lc_global.h"
|
2012-03-29 03:10:55 +02:00
|
|
|
#include "lc_math.h"
|
2012-04-14 04:20:27 +02:00
|
|
|
#include "lc_mesh.h"
|
2011-09-07 23:06:51 +02:00
|
|
|
#include <locale.h>
|
|
|
|
#include "pieceinf.h"
|
|
|
|
#include "camera.h"
|
|
|
|
#include "project.h"
|
|
|
|
#include "image.h"
|
2013-08-16 03:25:51 +02:00
|
|
|
#include "lc_mainwindow.h"
|
2011-09-07 23:06:51 +02:00
|
|
|
#include "view.h"
|
2012-10-02 03:23:44 +02:00
|
|
|
#include "lc_library.h"
|
2011-09-07 23:06:51 +02:00
|
|
|
#include "lc_application.h"
|
2013-08-09 06:57:18 +02:00
|
|
|
#include "lc_profile.h"
|
2016-05-02 21:13:54 +02:00
|
|
|
#include "lc_file.h"
|
2017-06-26 03:20:34 +02:00
|
|
|
#include "lc_zipfile.h"
|
2016-08-01 05:44:15 +02:00
|
|
|
#include "lc_qimagedialog.h"
|
2014-12-13 00:42:09 +01:00
|
|
|
#include "lc_qmodellistdialog.h"
|
2016-08-01 05:44:15 +02:00
|
|
|
#include "lc_qpovraydialog.h"
|
2017-06-29 19:02:00 +02:00
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
|
|
#include <QtConcurrent>
|
|
|
|
#endif
|
2011-09-07 23:06:51 +02:00
|
|
|
|
|
|
|
Project::Project()
|
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
mModified = false;
|
2016-09-09 00:34:51 +02:00
|
|
|
mActiveModel = new lcModel(tr("Model #1.ldr"));
|
2015-02-22 03:39:15 +01:00
|
|
|
mActiveModel->CreatePieceInfo(this);
|
2014-12-16 00:55:17 +01:00
|
|
|
mActiveModel->SetSaved();
|
2014-12-13 00:42:09 +01:00
|
|
|
mModels.Add(mActiveModel);
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Project::~Project()
|
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
mModels.DeleteAll();
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
|
|
|
|
2014-12-13 00:42:09 +01:00
|
|
|
bool Project::IsModified() const
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2014-12-13 00:42:09 +01:00
|
|
|
if (mModified)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
if (mModels[ModelIdx]->IsModified())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Project::GetTitle() const
|
|
|
|
{
|
2015-02-22 03:39:15 +01:00
|
|
|
if (!mFileName.isEmpty())
|
|
|
|
return QFileInfo(mFileName).fileName();
|
|
|
|
|
|
|
|
return mModels.GetSize() == 1 ? tr("New Project.ldr") : tr("New Project.mpd");
|
2014-12-13 00:42:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Project::SetActiveModel(int ModelIndex)
|
|
|
|
{
|
|
|
|
if (ModelIndex < 0 || ModelIndex >= mModels.GetSize())
|
|
|
|
return;
|
|
|
|
|
2014-12-25 16:25:50 +01:00
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
2014-12-30 17:56:13 +01:00
|
|
|
mModels[ModelIdx]->SetActive(ModelIdx == ModelIndex);
|
2014-12-25 16:25:50 +01:00
|
|
|
|
2015-01-17 19:29:10 +01:00
|
|
|
lcArray<lcModel*> UpdatedModels;
|
|
|
|
UpdatedModels.AllocGrow(mModels.GetSize());
|
|
|
|
|
2015-01-02 17:44:01 +01:00
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
2015-01-17 19:29:10 +01:00
|
|
|
mModels[ModelIdx]->UpdatePieceInfo(UpdatedModels);
|
2015-01-02 17:44:01 +01:00
|
|
|
|
2014-12-13 00:42:09 +01:00
|
|
|
mActiveModel = mModels[ModelIndex];
|
2016-03-06 02:47:00 +01:00
|
|
|
gMainWindow->SetCurrentModelTab(mActiveModel);
|
2014-12-13 00:42:09 +01:00
|
|
|
mActiveModel->UpdateInterface();
|
|
|
|
}
|
|
|
|
|
2017-06-20 05:03:49 +02:00
|
|
|
void Project::SetActiveModel(const QString& ModelName)
|
|
|
|
{
|
2017-06-21 06:43:39 +02:00
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
{
|
|
|
|
if (ModelName.compare(mModels[ModelIdx]->GetName(), Qt::CaseInsensitive) == 0)
|
|
|
|
{
|
2017-06-20 05:03:49 +02:00
|
|
|
SetActiveModel(ModelIdx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
QString Project::GetNewModelName(QWidget* ParentWidget, const QString& DialogTitle, const QString& CurrentName, const QStringList& ExistingModels) const
|
2014-12-13 00:42:09 +01:00
|
|
|
{
|
2016-09-09 00:34:51 +02:00
|
|
|
QString Name = CurrentName;
|
2014-12-13 00:42:09 +01:00
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
if (Name.isEmpty())
|
2014-12-13 00:42:09 +01:00
|
|
|
{
|
2016-09-09 00:34:51 +02:00
|
|
|
const QString Prefix = tr("Model #");
|
|
|
|
int Max = 0;
|
2014-12-13 00:42:09 +01:00
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
for (int ModelIdx = 0; ModelIdx < ExistingModels.size(); ModelIdx++)
|
2014-12-13 00:42:09 +01:00
|
|
|
{
|
2016-09-09 00:34:51 +02:00
|
|
|
const QString& Name = ExistingModels[ModelIdx];
|
|
|
|
|
|
|
|
if (Name.startsWith(Prefix))
|
|
|
|
{
|
|
|
|
QString NumberString = Name.mid(Prefix.length());
|
|
|
|
QTextStream Stream(&NumberString);
|
|
|
|
int Number;
|
|
|
|
Stream >> Number;
|
|
|
|
Max = qMax(Max, Number);
|
|
|
|
}
|
2014-12-13 00:42:09 +01:00
|
|
|
}
|
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
Name = Prefix + QString::number(Max + 1) + ".ldr";
|
|
|
|
}
|
2014-12-13 00:42:09 +01:00
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
bool Ok = false;
|
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
Name = QInputDialog::getText(ParentWidget, DialogTitle, tr("Model Name:"), QLineEdit::Normal, Name, &Ok);
|
2014-12-13 00:42:09 +01:00
|
|
|
|
|
|
|
if (!Ok)
|
2016-09-09 00:34:51 +02:00
|
|
|
return QString();
|
2014-12-13 00:42:09 +01:00
|
|
|
|
|
|
|
if (Name.isEmpty())
|
2016-09-09 00:34:51 +02:00
|
|
|
{
|
|
|
|
QMessageBox::information(ParentWidget, tr("Empty Name"), tr("The model name cannot be empty."));
|
|
|
|
continue;
|
|
|
|
}
|
2014-12-13 00:42:09 +01:00
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
bool ExtensionValid = false;
|
2015-12-02 02:16:12 +01:00
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
if (Name.length() < 5)
|
|
|
|
ExtensionValid = false;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QString Extension = Name.right(4);
|
2016-09-17 22:21:29 +02:00
|
|
|
if (Extension.compare(".dat", Qt::CaseInsensitive) == 0 || Extension.compare(".ldr", Qt::CaseInsensitive) == 0 || Extension.compare(".mpd", Qt::CaseInsensitive) == 0)
|
2016-09-09 00:34:51 +02:00
|
|
|
ExtensionValid = true;
|
|
|
|
}
|
2015-12-02 02:16:12 +01:00
|
|
|
|
2016-09-09 00:34:51 +02:00
|
|
|
if (!ExtensionValid)
|
2015-12-02 02:16:12 +01:00
|
|
|
{
|
2016-09-17 22:21:29 +02:00
|
|
|
QMessageBox::information(ParentWidget, tr("Invalid Extension"), tr("The model name must end with '.ldr', '.dat' or '.mpd'."));
|
2016-09-09 00:34:51 +02:00
|
|
|
continue;
|
2015-12-02 02:16:12 +01:00
|
|
|
}
|
2016-09-09 00:34:51 +02:00
|
|
|
|
|
|
|
if (ExistingModels.contains(Name, Qt::CaseInsensitive) && Name != CurrentName)
|
|
|
|
{
|
|
|
|
QMessageBox::information(ParentWidget, tr("Duplicate Model"), tr("A model named '%1' already exists, please enter an unique name.").arg(Name));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Name;
|
|
|
|
}
|
|
|
|
|
|
|
|
lcModel* Project::CreateNewModel(bool ShowModel)
|
|
|
|
{
|
|
|
|
QStringList ModelNames;
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
ModelNames.append(mModels[ModelIdx]->GetProperties().mName);
|
|
|
|
|
|
|
|
QString Name = GetNewModelName(gMainWindow, tr("New Model"), QString(), ModelNames);
|
|
|
|
|
|
|
|
if (Name.isEmpty())
|
2017-04-14 02:26:40 +02:00
|
|
|
return nullptr;
|
2016-09-09 00:34:51 +02:00
|
|
|
|
|
|
|
mModified = true;
|
|
|
|
lcModel* Model = new lcModel(Name);
|
|
|
|
Model->CreatePieceInfo(this);
|
|
|
|
Model->SetSaved();
|
|
|
|
mModels.Add(Model);
|
|
|
|
|
|
|
|
if (ShowModel)
|
|
|
|
{
|
|
|
|
SetActiveModel(mModels.GetSize() - 1);
|
|
|
|
gMainWindow->UpdateTitle();
|
2014-12-13 00:42:09 +01:00
|
|
|
}
|
2016-09-09 00:34:51 +02:00
|
|
|
else
|
|
|
|
SetActiveModel(mModels.FindIndex(mActiveModel));
|
2015-12-02 02:16:12 +01:00
|
|
|
|
|
|
|
return Model;
|
2014-12-13 00:42:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Project::ShowModelListDialog()
|
|
|
|
{
|
|
|
|
QList<QPair<QString, lcModel*>> Models;
|
2016-09-22 17:04:51 +02:00
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0))
|
2014-12-13 00:42:09 +01:00
|
|
|
Models.reserve(mModels.GetSize());
|
2016-09-22 17:04:51 +02:00
|
|
|
#endif
|
2014-12-13 00:42:09 +01:00
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
{
|
|
|
|
lcModel* Model = mModels[ModelIdx];
|
|
|
|
Models.append(QPair<QString, lcModel*>(Model->GetProperties().mName, Model));
|
|
|
|
}
|
|
|
|
|
2015-01-26 00:04:39 +01:00
|
|
|
lcQModelListDialog Dialog(gMainWindow, Models);
|
2014-12-13 00:42:09 +01:00
|
|
|
|
|
|
|
if (Dialog.exec() != QDialog::Accepted || Models.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
lcArray<lcModel*> NewModels;
|
|
|
|
|
|
|
|
for (QList<QPair<QString, lcModel*>>::iterator it = Models.begin(); it != Models.end(); it++)
|
|
|
|
{
|
|
|
|
lcModel* Model = it->second;
|
|
|
|
|
|
|
|
if (!Model)
|
|
|
|
{
|
|
|
|
Model = new lcModel(it->first);
|
2015-02-22 03:39:15 +01:00
|
|
|
Model->CreatePieceInfo(this);
|
2014-12-16 00:55:17 +01:00
|
|
|
Model->SetSaved();
|
2014-12-13 00:42:09 +01:00
|
|
|
mModified = true;
|
|
|
|
}
|
|
|
|
else if (Model->GetProperties().mName != it->first)
|
|
|
|
{
|
|
|
|
Model->SetName(it->first);
|
2017-07-27 18:21:55 +02:00
|
|
|
lcGetPiecesLibrary()->RenamePiece(Model->GetPieceInfo(), it->first.toLatin1().constData());
|
2017-07-24 04:35:18 +02:00
|
|
|
|
|
|
|
for (lcModel* CheckModel : mModels)
|
|
|
|
CheckModel->RenamePiece(Model->GetPieceInfo());
|
|
|
|
|
2014-12-13 00:42:09 +01:00
|
|
|
mModified = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
NewModels.Add(Model);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
{
|
|
|
|
lcModel* Model = mModels[ModelIdx];
|
|
|
|
|
|
|
|
if (NewModels.FindIndex(Model) == -1)
|
|
|
|
{
|
|
|
|
delete Model;
|
|
|
|
mModified = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mModels = NewModels;
|
|
|
|
|
|
|
|
SetActiveModel(Dialog.mActiveModel);
|
|
|
|
gMainWindow->UpdateTitle();
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
bool Project::Load(const QString& FileName)
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
QFile File(FileName);
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (!File.open(QIODevice::ReadOnly))
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2015-01-26 00:04:39 +01:00
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error reading file '%1':\n%2").arg(FileName, File.errorString()));
|
2014-12-08 08:32:39 +01:00
|
|
|
return false;
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
|
|
|
|
2014-12-13 00:42:09 +01:00
|
|
|
mModels.DeleteAll();
|
2016-11-26 02:12:19 +01:00
|
|
|
mFileName = FileName;
|
2015-02-22 03:39:15 +01:00
|
|
|
QFileInfo FileInfo(FileName);
|
|
|
|
QString Extension = FileInfo.suffix().toLower();
|
2013-12-19 14:41:49 +01:00
|
|
|
|
2016-09-23 21:48:29 +02:00
|
|
|
QByteArray FileData = File.readAll();
|
|
|
|
bool LoadDAT;
|
2016-05-28 19:35:13 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (Extension == QLatin1String("dat") || Extension == QLatin1String("ldr") || Extension == QLatin1String("mpd"))
|
2016-09-23 21:48:29 +02:00
|
|
|
LoadDAT = true;
|
|
|
|
else if (Extension == QLatin1String("lcd") || Extension == QLatin1String("leocad"))
|
|
|
|
LoadDAT = false;
|
|
|
|
else
|
|
|
|
LoadDAT = memcmp(FileData, "LeoCAD ", 7);
|
|
|
|
|
|
|
|
if (LoadDAT)
|
2013-01-06 20:24:25 +01:00
|
|
|
{
|
2015-01-06 16:38:51 +01:00
|
|
|
QBuffer Buffer(&FileData);
|
|
|
|
Buffer.open(QIODevice::ReadOnly);
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-06 16:38:51 +01:00
|
|
|
while (!Buffer.atEnd())
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2014-12-24 16:52:52 +01:00
|
|
|
lcModel* Model = new lcModel(QString());
|
2016-11-26 02:12:19 +01:00
|
|
|
Model->SplitMPD(Buffer);
|
2015-03-25 16:33:58 +01:00
|
|
|
|
|
|
|
if (mModels.IsEmpty() || !Model->GetProperties().mName.isEmpty())
|
|
|
|
mModels.Add(Model);
|
|
|
|
else
|
|
|
|
delete Model;
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
2016-11-26 02:12:19 +01:00
|
|
|
|
|
|
|
Buffer.seek(0);
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
{
|
|
|
|
lcModel* Model = mModels[ModelIdx];
|
|
|
|
Model->LoadLDraw(Buffer, this);
|
|
|
|
Model->SetSaved();
|
|
|
|
}
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
lcMemFile MemFile;
|
|
|
|
MemFile.WriteBuffer(FileData.constData(), FileData.size());
|
|
|
|
MemFile.Seek(0, SEEK_SET);
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2014-12-24 16:52:52 +01:00
|
|
|
lcModel* Model = new lcModel(QString());
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (Model->LoadBinary(&MemFile))
|
2014-12-16 00:55:17 +01:00
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
mModels.Add(Model);
|
2014-12-16 00:55:17 +01:00
|
|
|
Model->SetSaved();
|
|
|
|
}
|
2014-12-08 08:32:39 +01:00
|
|
|
else
|
|
|
|
delete Model;
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (mModels.IsEmpty())
|
|
|
|
return false;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-02-22 03:39:15 +01:00
|
|
|
if (mModels.GetSize() == 1)
|
2014-12-24 16:52:52 +01:00
|
|
|
{
|
2015-02-22 03:39:15 +01:00
|
|
|
lcModel* Model = mModels[0];
|
2014-12-24 16:52:52 +01:00
|
|
|
|
|
|
|
if (Model->GetProperties().mName.isEmpty())
|
2015-02-22 03:39:15 +01:00
|
|
|
Model->SetName(FileInfo.fileName());
|
2014-12-24 16:52:52 +01:00
|
|
|
}
|
|
|
|
|
2015-02-22 03:39:15 +01:00
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
mModels[ModelIdx]->CreatePieceInfo(this);
|
|
|
|
|
2015-02-23 21:25:53 +01:00
|
|
|
lcArray<lcModel*> UpdatedModels;
|
|
|
|
UpdatedModels.AllocGrow(mModels.GetSize());
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
mModels[ModelIdx]->UpdatePieceInfo(UpdatedModels);
|
|
|
|
|
2014-12-13 00:42:09 +01:00
|
|
|
mModified = false;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
bool Project::Save(const QString& FileName)
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
QFile File(FileName);
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
2012-03-23 00:44:56 +01:00
|
|
|
{
|
2015-01-26 00:04:39 +01:00
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString()));
|
2014-12-08 08:32:39 +01:00
|
|
|
return false;
|
2012-03-23 00:44:56 +01:00
|
|
|
}
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
QTextStream Stream(&File);
|
2016-09-26 02:15:30 +02:00
|
|
|
bool Success = Save(Stream);
|
|
|
|
|
|
|
|
mFileName = FileName;
|
|
|
|
mModified = false;
|
|
|
|
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Project::Save(QTextStream& Stream)
|
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
bool MPD = mModels.GetSize() > 1;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2014-12-08 08:32:39 +01:00
|
|
|
lcModel* Model = mModels[ModelIdx];
|
2013-08-16 18:16:45 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (MPD)
|
|
|
|
Stream << QLatin1String("0 FILE ") << Model->GetProperties().mName << QLatin1String("\r\n");
|
2013-08-16 18:16:45 +02:00
|
|
|
|
2016-02-17 00:11:52 +01:00
|
|
|
Model->SaveLDraw(Stream, false);
|
2014-12-08 08:32:39 +01:00
|
|
|
Model->SetSaved();
|
2013-08-16 18:16:45 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
if (MPD)
|
2014-12-26 18:09:11 +01:00
|
|
|
Stream << QLatin1String("0 NOFILE\r\n");
|
2014-12-08 08:32:39 +01:00
|
|
|
}
|
2013-08-16 18:16:45 +02:00
|
|
|
|
2014-12-08 08:32:39 +01:00
|
|
|
return true;
|
|
|
|
}
|
2013-08-16 18:16:45 +02:00
|
|
|
|
2014-12-23 18:02:23 +01:00
|
|
|
void Project::Merge(Project* Other)
|
|
|
|
{
|
|
|
|
for (int ModelIdx = 0; ModelIdx < Other->mModels.GetSize(); ModelIdx++)
|
2015-02-22 03:39:15 +01:00
|
|
|
{
|
|
|
|
lcModel* Model = Other->mModels[ModelIdx];
|
|
|
|
QString Name = Model->GetProperties().mName;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
bool Duplicate = false;
|
|
|
|
|
|
|
|
for (int SearchIdx = 0; SearchIdx < mModels.GetSize(); SearchIdx++)
|
|
|
|
{
|
|
|
|
if (mModels[SearchIdx]->GetProperties().mName == Name)
|
|
|
|
{
|
|
|
|
Duplicate = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Duplicate)
|
|
|
|
break;
|
|
|
|
|
|
|
|
Name = tr("Merged ") + Name;
|
|
|
|
Model->SetName(Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
mModels.Add(Model);
|
|
|
|
}
|
2014-12-23 18:02:23 +01:00
|
|
|
|
|
|
|
mModified = true;
|
|
|
|
}
|
|
|
|
|
2017-06-26 03:20:34 +02:00
|
|
|
bool Project::ImportLDD(const QString& FileName)
|
|
|
|
{
|
|
|
|
lcZipFile ZipFile;
|
|
|
|
|
|
|
|
if (!ZipFile.OpenRead(FileName))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
lcMemFile XMLFile;
|
|
|
|
if (!ZipFile.ExtractFile("IMAGE100.LXFML", XMLFile))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
mModels.DeleteAll();
|
|
|
|
lcModel* Model = new lcModel(QString());
|
|
|
|
|
|
|
|
if (Model->LoadLDD(QString::fromUtf8((const char*)XMLFile.mBuffer)))
|
|
|
|
{
|
|
|
|
mModels.Add(Model);
|
|
|
|
Model->SetSaved();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
delete Model;
|
|
|
|
|
|
|
|
if (mModels.IsEmpty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mModels.GetSize() == 1)
|
|
|
|
{
|
|
|
|
lcModel* Model = mModels[0];
|
|
|
|
|
|
|
|
if (Model->GetProperties().mName.isEmpty())
|
|
|
|
Model->SetName(QFileInfo(FileName).completeBaseName());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
mModels[ModelIdx]->CreatePieceInfo(this);
|
|
|
|
|
|
|
|
lcArray<lcModel*> UpdatedModels;
|
|
|
|
UpdatedModels.AllocGrow(mModels.GetSize());
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
mModels[ModelIdx]->UpdatePieceInfo(UpdatedModels);
|
|
|
|
|
|
|
|
mModified = false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:30:12 +01:00
|
|
|
void Project::GetModelParts(lcArray<lcModelPartsEntry>& ModelParts)
|
|
|
|
{
|
|
|
|
if (mModels.IsEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int ModelIdx = 0; ModelIdx < mModels.GetSize(); ModelIdx++)
|
|
|
|
mModels[ModelIdx]->CalculateStep(LC_STEP_MAX);
|
|
|
|
|
|
|
|
mModels[0]->GetModelParts(lcMatrix44Identity(), gDefaultColor, ModelParts);
|
|
|
|
|
|
|
|
SetActiveModel(mModels.FindIndex(mActiveModel));
|
|
|
|
}
|
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
QString Project::GetExportFileName(const QString& FileName, const QString& DefaultExtension, const QString& DialogTitle, const QString& DialogFilter) const
|
|
|
|
{
|
|
|
|
if (!FileName.isEmpty())
|
|
|
|
return FileName;
|
|
|
|
|
|
|
|
QString SaveFileName;
|
|
|
|
|
|
|
|
if (!mFileName.isEmpty())
|
|
|
|
SaveFileName = mFileName;
|
|
|
|
else
|
|
|
|
SaveFileName = GetTitle();
|
|
|
|
|
|
|
|
QString Extension = QFileInfo(SaveFileName).suffix().toLower();
|
|
|
|
|
|
|
|
if (Extension.isEmpty())
|
|
|
|
SaveFileName += "." + DefaultExtension;
|
|
|
|
else if (Extension != DefaultExtension && SaveFileName.length() > 4)
|
|
|
|
{
|
|
|
|
SaveFileName = SaveFileName.left(SaveFileName.length() - Extension.length() - 1);
|
|
|
|
SaveFileName += "." + DefaultExtension;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QFileDialog::getSaveFileName(gMainWindow, DialogTitle, SaveFileName, DialogFilter);
|
|
|
|
}
|
|
|
|
|
2015-03-27 21:20:12 +01:00
|
|
|
void Project::Export3DStudio(const QString& FileName)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
|
|
|
lcArray<lcModelPartsEntry> ModelParts;
|
|
|
|
|
|
|
|
GetModelParts(ModelParts);
|
|
|
|
|
|
|
|
if (ModelParts.IsEmpty())
|
|
|
|
{
|
2015-01-30 17:30:13 +01:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export."));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
QString SaveFileName = GetExportFileName(FileName, "3ds", tr("Export 3D Studio"), tr("3DS Files (*.3ds);;All Files (*.*)"));
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
if (SaveFileName.isEmpty())
|
|
|
|
return;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile File(SaveFileName);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!File.Open(QIODevice::WriteOnly))
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2017-08-11 03:40:43 +02:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
long M3DStart = File.GetPosition();
|
|
|
|
File.WriteU16(0x4D4D); // CHK_M3DMAGIC
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
File.WriteU16(0x0002); // CHK_M3D_VERSION
|
|
|
|
File.WriteU32(10);
|
|
|
|
File.WriteU32(3);
|
|
|
|
|
|
|
|
long MDataStart = File.GetPosition();
|
|
|
|
File.WriteU16(0x3D3D); // CHK_MDATA
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
File.WriteU16(0x3D3E); // CHK_MESH_VERSION
|
|
|
|
File.WriteU32(10);
|
|
|
|
File.WriteU32(3);
|
|
|
|
|
|
|
|
const int MaterialNameLength = 11;
|
|
|
|
char MaterialName[32];
|
|
|
|
|
|
|
|
for (int ColorIdx = 0; ColorIdx < gColorList.GetSize(); ColorIdx++)
|
|
|
|
{
|
|
|
|
lcColor* Color = &gColorList[ColorIdx];
|
|
|
|
|
|
|
|
sprintf(MaterialName, "Material%03d", ColorIdx);
|
|
|
|
|
|
|
|
long MaterialStart = File.GetPosition();
|
|
|
|
File.WriteU16(0xAFFF); // CHK_MAT_ENTRY
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
File.WriteU16(0xA000); // CHK_MAT_NAME
|
|
|
|
File.WriteU32(6 + MaterialNameLength + 1);
|
|
|
|
File.WriteBuffer(MaterialName, MaterialNameLength + 1);
|
|
|
|
|
|
|
|
File.WriteU16(0xA010); // CHK_MAT_AMBIENT
|
|
|
|
File.WriteU32(24);
|
|
|
|
|
|
|
|
File.WriteU16(0x0011); // CHK_COLOR_24
|
|
|
|
File.WriteU32(9);
|
|
|
|
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[0] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[1] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[2] + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0x0012); // CHK_LIN_COLOR_24
|
|
|
|
File.WriteU32(9);
|
|
|
|
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[0] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[1] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[2] + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA020); // CHK_MAT_AMBIENT
|
|
|
|
File.WriteU32(24);
|
|
|
|
|
|
|
|
File.WriteU16(0x0011); // CHK_COLOR_24
|
|
|
|
File.WriteU32(9);
|
|
|
|
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[0] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[1] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[2] + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0x0012); // CHK_LIN_COLOR_24
|
|
|
|
File.WriteU32(9);
|
|
|
|
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[0] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[1] + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * Color->Value[2] + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA030); // CHK_MAT_SPECULAR
|
|
|
|
File.WriteU32(24);
|
|
|
|
|
|
|
|
File.WriteU16(0x0011); // CHK_COLOR_24
|
|
|
|
File.WriteU32(9);
|
|
|
|
|
|
|
|
File.WriteU8(floor(255.0 * 0.9f + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * 0.9f + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * 0.9f + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0x0012); // CHK_LIN_COLOR_24
|
|
|
|
File.WriteU32(9);
|
|
|
|
|
|
|
|
File.WriteU8(floor(255.0 * 0.9f + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * 0.9f + 0.5));
|
|
|
|
File.WriteU8(floor(255.0 * 0.9f + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA040); // CHK_MAT_SHININESS
|
|
|
|
File.WriteU32(14);
|
|
|
|
|
|
|
|
File.WriteU16(0x0030); // CHK_INT_PERCENTAGE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16((lcuint8)floor(100.0 * 0.25 + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA041); // CHK_MAT_SHIN2PCT
|
|
|
|
File.WriteU32(14);
|
|
|
|
|
|
|
|
File.WriteU16(0x0030); // CHK_INT_PERCENTAGE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16((lcuint8)floor(100.0 * 0.05 + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA050); // CHK_MAT_TRANSPARENCY
|
|
|
|
File.WriteU32(14);
|
|
|
|
|
|
|
|
File.WriteU16(0x0030); // CHK_INT_PERCENTAGE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16((lcuint8)floor(100.0 * (1.0f - Color->Value[3]) + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA052); // CHK_MAT_XPFALL
|
|
|
|
File.WriteU32(14);
|
|
|
|
|
|
|
|
File.WriteU16(0x0030); // CHK_INT_PERCENTAGE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16((lcuint8)floor(100.0 * 0.0 + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA053); // CHK_MAT_REFBLUR
|
|
|
|
File.WriteU32(14);
|
|
|
|
|
|
|
|
File.WriteU16(0x0030); // CHK_INT_PERCENTAGE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16((lcuint8)floor(100.0 * 0.2 + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA100); // CHK_MAT_SHADING
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16(3);
|
|
|
|
|
|
|
|
File.WriteU16(0xA084); // CHK_MAT_SELF_ILPCT
|
|
|
|
File.WriteU32(14);
|
|
|
|
|
|
|
|
File.WriteU16(0x0030); // CHK_INT_PERCENTAGE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16((lcuint8)floor(100.0 * 0.0 + 0.5));
|
|
|
|
|
|
|
|
File.WriteU16(0xA081); // CHK_MAT_TWO_SIDE
|
|
|
|
File.WriteU32(6);
|
|
|
|
|
|
|
|
File.WriteU16(0xA087); // CHK_MAT_WIRE_SIZE
|
|
|
|
File.WriteU32(10);
|
|
|
|
|
|
|
|
File.WriteFloat(1.0f);
|
|
|
|
|
|
|
|
long MaterialEnd = File.GetPosition();
|
|
|
|
File.Seek(MaterialStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(MaterialEnd - MaterialStart);
|
|
|
|
File.Seek(MaterialEnd, SEEK_SET);
|
|
|
|
}
|
|
|
|
|
|
|
|
const lcModelProperties& Properties = mModels[0]->GetProperties();
|
|
|
|
|
|
|
|
File.WriteU16(0x0100); // CHK_MASTER_SCALE
|
|
|
|
File.WriteU32(10);
|
|
|
|
|
|
|
|
File.WriteFloat(1.0f);
|
|
|
|
|
|
|
|
File.WriteU16(0x1400); // CHK_LO_SHADOW_BIAS
|
|
|
|
File.WriteU32(10);
|
|
|
|
|
|
|
|
File.WriteFloat(1.0f);
|
|
|
|
|
|
|
|
File.WriteU16(0x1420); // CHK_SHADOW_MAP_SIZE
|
|
|
|
File.WriteU32(8);
|
|
|
|
|
|
|
|
File.WriteS16(512);
|
|
|
|
|
|
|
|
File.WriteU16(0x1450); // CHK_SHADOW_FILTER
|
|
|
|
File.WriteU32(10);
|
|
|
|
|
|
|
|
File.WriteFloat(3.0f);
|
|
|
|
|
|
|
|
File.WriteU16(0x1460); // CHK_RAY_BIAS
|
|
|
|
File.WriteU32(10);
|
|
|
|
|
|
|
|
File.WriteFloat(1.0f);
|
|
|
|
|
|
|
|
File.WriteU16(0x1500); // CHK_O_CONSTS
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloat(0.0f);
|
|
|
|
File.WriteFloat(0.0f);
|
|
|
|
File.WriteFloat(0.0f);
|
|
|
|
|
|
|
|
File.WriteU16(0x2100); // CHK_AMBIENT_LIGHT
|
|
|
|
File.WriteU32(42);
|
|
|
|
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mAmbientColor, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mAmbientColor, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x1200); // CHK_SOLID_BGND
|
|
|
|
File.WriteU32(42);
|
|
|
|
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mBackgroundSolidColor, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mBackgroundSolidColor, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x1100); // CHK_BIT_MAP
|
|
|
|
QByteArray BackgroundImage = Properties.mBackgroundImage.toLatin1();
|
2016-03-06 21:19:02 +01:00
|
|
|
File.WriteU32(6 + 1 + (lcuint32)strlen(BackgroundImage.constData()));
|
2014-12-30 17:30:12 +01:00
|
|
|
File.WriteBuffer(BackgroundImage.constData(), strlen(BackgroundImage.constData()) + 1);
|
|
|
|
|
|
|
|
File.WriteU16(0x1300); // CHK_V_GRADIENT
|
|
|
|
File.WriteU32(118);
|
|
|
|
|
|
|
|
File.WriteFloat(1.0f);
|
|
|
|
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mBackgroundGradientColor1, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mBackgroundGradientColor1, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats((Properties.mBackgroundGradientColor1 + Properties.mBackgroundGradientColor2) / 2.0f, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats((Properties.mBackgroundGradientColor1 + Properties.mBackgroundGradientColor2) / 2.0f, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mBackgroundGradientColor2, 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
|
|
File.WriteU32(18);
|
|
|
|
|
|
|
|
File.WriteFloats(Properties.mBackgroundGradientColor2, 3);
|
|
|
|
|
|
|
|
if (Properties.mBackgroundType == LC_BACKGROUND_GRADIENT)
|
|
|
|
{
|
|
|
|
File.WriteU16(0x1301); // LIB3DS_USE_V_GRADIENT
|
|
|
|
File.WriteU32(6);
|
|
|
|
}
|
|
|
|
else if (Properties.mBackgroundType == LC_BACKGROUND_IMAGE)
|
|
|
|
{
|
|
|
|
File.WriteU16(0x1101); // LIB3DS_USE_BIT_MAP
|
|
|
|
File.WriteU32(6);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
File.WriteU16(0x1201); // LIB3DS_USE_SOLID_BGND
|
|
|
|
File.WriteU32(6);
|
|
|
|
}
|
|
|
|
|
|
|
|
int NumPieces = 0;
|
|
|
|
for (int PartIdx = 0; PartIdx < ModelParts.GetSize(); PartIdx++)
|
|
|
|
{
|
|
|
|
PieceInfo* Info = ModelParts[PartIdx].Info;
|
2014-12-31 17:38:30 +01:00
|
|
|
lcMesh* Mesh = Info->GetMesh();
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2014-12-31 17:38:30 +01:00
|
|
|
if (!Mesh || Mesh->mIndexType == GL_UNSIGNED_INT)
|
2014-12-30 17:30:12 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
long NamedObjectStart = File.GetPosition();
|
|
|
|
File.WriteU16(0x4000); // CHK_NAMED_OBJECT
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
char Name[32];
|
|
|
|
sprintf(Name, "Piece%.3d", NumPieces);
|
|
|
|
NumPieces++;
|
|
|
|
File.WriteBuffer(Name, strlen(Name) + 1);
|
|
|
|
|
|
|
|
long TriObjectStart = File.GetPosition();
|
|
|
|
File.WriteU16(0x4100); // CHK_N_TRI_OBJECT
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
File.WriteU16(0x4110); // CHK_POINT_ARRAY
|
|
|
|
File.WriteU32(8 + 12 * Mesh->mNumVertices);
|
|
|
|
|
|
|
|
File.WriteU16(Mesh->mNumVertices);
|
|
|
|
|
2017-02-18 20:12:35 +01:00
|
|
|
lcVertex* Verts = (lcVertex*)Mesh->mVertexData;
|
2014-12-30 17:30:12 +01:00
|
|
|
const lcMatrix44& ModelWorld = ModelParts[PartIdx].WorldMatrix;
|
|
|
|
|
|
|
|
for (int VertexIdx = 0; VertexIdx < Mesh->mNumVertices; VertexIdx++)
|
|
|
|
{
|
2017-02-18 20:12:35 +01:00
|
|
|
lcVector3 Pos = lcMul31(Verts[VertexIdx].Position, ModelWorld);
|
2014-12-30 17:30:12 +01:00
|
|
|
File.WriteFloat(Pos[0]);
|
|
|
|
File.WriteFloat(Pos[1]);
|
|
|
|
File.WriteFloat(Pos[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
File.WriteU16(0x4160); // CHK_MESH_MATRIX
|
|
|
|
File.WriteU32(54);
|
|
|
|
|
|
|
|
lcMatrix44 Matrix = lcMatrix44Identity();
|
|
|
|
File.WriteFloats(Matrix[0], 3);
|
|
|
|
File.WriteFloats(Matrix[1], 3);
|
|
|
|
File.WriteFloats(Matrix[2], 3);
|
|
|
|
File.WriteFloats(Matrix[3], 3);
|
|
|
|
|
|
|
|
File.WriteU16(0x4165); // CHK_MESH_COLOR
|
|
|
|
File.WriteU32(7);
|
|
|
|
|
|
|
|
File.WriteU8(0);
|
|
|
|
|
|
|
|
long FaceArrayStart = File.GetPosition();
|
|
|
|
File.WriteU16(0x4120); // CHK_FACE_ARRAY
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
int NumTriangles = 0;
|
|
|
|
|
2015-05-24 06:36:25 +02:00
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-05-24 06:36:25 +02:00
|
|
|
lcMeshSection* Section = &Mesh->mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-08-22 03:11:32 +02:00
|
|
|
if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
|
2014-12-30 17:30:12 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
NumTriangles += Section->NumIndices / 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
File.WriteU16(NumTriangles);
|
|
|
|
|
2015-05-24 06:36:25 +02:00
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-05-24 06:36:25 +02:00
|
|
|
lcMeshSection* Section = &Mesh->mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-08-22 03:11:32 +02:00
|
|
|
if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
|
2014-12-30 17:30:12 +01:00
|
|
|
continue;
|
|
|
|
|
2015-04-26 20:14:33 +02:00
|
|
|
lcuint16* Indices = (lcuint16*)Mesh->mIndexData + Section->IndexOffset / sizeof(lcuint16);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
|
|
|
for (int IndexIdx = 0; IndexIdx < Section->NumIndices; IndexIdx += 3)
|
|
|
|
{
|
|
|
|
File.WriteU16(Indices[IndexIdx + 0]);
|
|
|
|
File.WriteU16(Indices[IndexIdx + 1]);
|
|
|
|
File.WriteU16(Indices[IndexIdx + 2]);
|
|
|
|
File.WriteU16(7);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NumTriangles = 0;
|
|
|
|
|
2015-05-24 06:36:25 +02:00
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-05-24 06:36:25 +02:00
|
|
|
lcMeshSection* Section = &Mesh->mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-08-22 03:11:32 +02:00
|
|
|
if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
|
2014-12-30 17:30:12 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
int MaterialIndex = Section->ColorIndex == gDefaultColor ? ModelParts[PartIdx].ColorIndex : Section->ColorIndex;
|
|
|
|
|
|
|
|
File.WriteU16(0x4130); // CHK_MSH_MAT_GROUP
|
|
|
|
File.WriteU32(6 + MaterialNameLength + 1 + 2 + 2 * Section->NumIndices / 3);
|
|
|
|
|
|
|
|
sprintf(MaterialName, "Material%03d", MaterialIndex);
|
|
|
|
File.WriteBuffer(MaterialName, MaterialNameLength + 1);
|
|
|
|
|
|
|
|
File.WriteU16(Section->NumIndices / 3);
|
|
|
|
|
|
|
|
for (int IndexIdx = 0; IndexIdx < Section->NumIndices; IndexIdx += 3)
|
|
|
|
File.WriteU16(NumTriangles++);
|
|
|
|
}
|
|
|
|
|
|
|
|
long FaceArrayEnd = File.GetPosition();
|
|
|
|
File.Seek(FaceArrayStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(FaceArrayEnd - FaceArrayStart);
|
|
|
|
File.Seek(FaceArrayEnd, SEEK_SET);
|
|
|
|
|
|
|
|
long TriObjectEnd = File.GetPosition();
|
|
|
|
File.Seek(TriObjectStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(TriObjectEnd - TriObjectStart);
|
|
|
|
File.Seek(TriObjectEnd, SEEK_SET);
|
|
|
|
|
|
|
|
long NamedObjectEnd = File.GetPosition();
|
|
|
|
File.Seek(NamedObjectStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(NamedObjectEnd - NamedObjectStart);
|
|
|
|
File.Seek(NamedObjectEnd, SEEK_SET);
|
|
|
|
}
|
|
|
|
|
|
|
|
long MDataEnd = File.GetPosition();
|
|
|
|
File.Seek(MDataStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(MDataEnd - MDataStart);
|
|
|
|
File.Seek(MDataEnd, SEEK_SET);
|
|
|
|
|
|
|
|
long KFDataStart = File.GetPosition();
|
|
|
|
File.WriteU16(0xB000); // CHK_KFDATA
|
|
|
|
File.WriteU32(0);
|
|
|
|
|
|
|
|
File.WriteU16(0xB00A); // LIB3DS_KFHDR
|
|
|
|
File.WriteU32(6 + 2 + 1 + 4);
|
|
|
|
|
|
|
|
File.WriteS16(5);
|
|
|
|
File.WriteU8(0);
|
|
|
|
File.WriteS32(100);
|
|
|
|
|
|
|
|
long KFDataEnd = File.GetPosition();
|
|
|
|
File.Seek(KFDataStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(KFDataEnd - KFDataStart);
|
|
|
|
File.Seek(KFDataEnd, SEEK_SET);
|
|
|
|
|
|
|
|
long M3DEnd = File.GetPosition();
|
|
|
|
File.Seek(M3DStart + 2, SEEK_SET);
|
|
|
|
File.WriteU32(M3DEnd - M3DStart);
|
|
|
|
File.Seek(M3DEnd, SEEK_SET);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Project::ExportBrickLink()
|
|
|
|
{
|
2016-11-16 23:53:41 +01:00
|
|
|
lcPartsList PartsList;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
|
|
|
if (!mModels.IsEmpty())
|
|
|
|
mModels[0]->GetPartsList(gDefaultColor, PartsList);
|
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
if (PartsList.empty())
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-01-30 17:30:13 +01:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export."));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
QString SaveFileName = GetExportFileName(QString(), "xml", tr("Export BrickLink"), tr("XML Files (*.xml);;All Files (*.*)"));
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
if (SaveFileName.isEmpty())
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile BrickLinkFile(SaveFileName);
|
2014-12-30 17:30:12 +01:00
|
|
|
char Line[1024];
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!BrickLinkFile.Open(QIODevice::WriteOnly))
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-03-27 21:39:43 +01:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BrickLinkFile.WriteLine("<INVENTORY>\n");
|
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& PartIt : PartsList)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
const PieceInfo* Info = PartIt.first;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& ColorIt : PartIt.second)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2016-11-16 23:53:41 +01:00
|
|
|
BrickLinkFile.WriteLine(" <ITEM>\n");
|
|
|
|
BrickLinkFile.WriteLine(" <ITEMTYPE>P</ITEMTYPE>\n");
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-07-27 18:21:55 +02:00
|
|
|
char FileName[LC_PIECE_NAME_LEN];
|
|
|
|
strcpy(FileName, Info->mFileName);
|
|
|
|
char* Ext = strchr(FileName, '.');
|
|
|
|
if (Ext)
|
|
|
|
*Ext = 0;
|
|
|
|
|
|
|
|
sprintf(Line, " <ITEMID>%s</ITEMID>\n", FileName);
|
2014-12-30 17:30:12 +01:00
|
|
|
BrickLinkFile.WriteLine(Line);
|
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
int Count = ColorIt.second;
|
2016-11-16 23:53:41 +01:00
|
|
|
if (Count > 1)
|
|
|
|
{
|
|
|
|
sprintf(Line, " <MINQTY>%d</MINQTY>\n", Count);
|
|
|
|
BrickLinkFile.WriteLine(Line);
|
|
|
|
}
|
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
int Color = lcGetBrickLinkColor(ColorIt.first);
|
2016-11-16 23:53:41 +01:00
|
|
|
if (Color)
|
|
|
|
{
|
|
|
|
sprintf(Line, " <COLOR>%d</COLOR>\n", Color);
|
|
|
|
BrickLinkFile.WriteLine(Line);
|
|
|
|
}
|
|
|
|
|
|
|
|
BrickLinkFile.WriteLine(" </ITEM>\n");
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
BrickLinkFile.WriteLine("</INVENTORY>\n");
|
|
|
|
}
|
|
|
|
|
2017-08-11 03:40:43 +02:00
|
|
|
void Project::ExportCOLLADA()
|
|
|
|
{
|
|
|
|
lcArray<lcModelPartsEntry> ModelParts;
|
|
|
|
|
|
|
|
GetModelParts(ModelParts);
|
|
|
|
|
|
|
|
if (ModelParts.IsEmpty())
|
|
|
|
{
|
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString SaveFileName = GetExportFileName(QString(), "dae", tr("Export COLLADA"), tr("COLLADA Files (*.dae);;All Files (*.*)"));
|
|
|
|
|
|
|
|
if (SaveFileName.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QFile File(SaveFileName);
|
|
|
|
|
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream Stream(&File);
|
|
|
|
QLatin1String LineEnding("\r\n");
|
|
|
|
|
|
|
|
Stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << LineEnding;
|
|
|
|
Stream << "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">" << LineEnding;
|
|
|
|
Stream << "<asset>" << LineEnding;
|
|
|
|
Stream << "\t<created>" << QDateTime::currentDateTime().toString(Qt::ISODate) << "</created>" << LineEnding;
|
|
|
|
Stream << "\t<modified>" << QDateTime::currentDateTime().toString(Qt::ISODate) << "</modified>" << LineEnding;
|
|
|
|
// <unit name="nautical_league" meter="5556.0" />
|
|
|
|
Stream << "\t<up_axis>Z_UP</up_axis>" << LineEnding;
|
|
|
|
Stream << "</asset>" << LineEnding;
|
|
|
|
|
|
|
|
Stream << "<library_effects>" << LineEnding;
|
|
|
|
|
|
|
|
for (const lcColor& Color : gColorList)
|
|
|
|
{
|
|
|
|
const char* ColorName = Color.SafeName;
|
|
|
|
|
|
|
|
Stream << QString("<effect id=\"%1-phong\">").arg(ColorName) << LineEnding;
|
|
|
|
|
|
|
|
Stream << "<profile_COMMON>" << LineEnding;
|
|
|
|
Stream << "<technique sid=\"phong1\">" << LineEnding;
|
|
|
|
Stream << "<phong>" << LineEnding;
|
|
|
|
Stream << "<emission>" << LineEnding;
|
|
|
|
Stream << "<color>1.0 1.0 1.0 1.0</color>" << LineEnding;
|
|
|
|
Stream << "</emission>" << LineEnding;
|
|
|
|
Stream << "<ambient>" << LineEnding;
|
|
|
|
Stream << "<color>1.0 1.0 1.0 1.0</color>" << LineEnding;
|
|
|
|
Stream << "</ambient>" << LineEnding;
|
|
|
|
Stream << "<diffuse>" << LineEnding;
|
|
|
|
Stream << "<color>1.0 1.0 1.0 1.0</color>" << LineEnding;
|
|
|
|
Stream << "</diffuse>" << LineEnding;
|
|
|
|
Stream << "<specular>" << LineEnding;
|
|
|
|
Stream << "<color>1.0 1.0 1.0 1.0</color>" << LineEnding;
|
|
|
|
Stream << "</specular>" << LineEnding;
|
|
|
|
Stream << "<shininess>" << LineEnding;
|
|
|
|
Stream << "<float>20.0</float>" << LineEnding;
|
|
|
|
Stream << "</shininess>" << LineEnding;
|
|
|
|
Stream << "<reflective>" << LineEnding;
|
|
|
|
Stream << "<color>1.0 1.0 1.0 1.0</color>" << LineEnding;
|
|
|
|
Stream << "</reflective>" << LineEnding;
|
|
|
|
Stream << "<reflectivity>" << LineEnding;
|
|
|
|
Stream << "<float>0.5</float>" << LineEnding;
|
|
|
|
Stream << "</reflectivity>" << LineEnding;
|
|
|
|
Stream << "<transparent>" << LineEnding;
|
|
|
|
Stream << "<color>1.0 1.0 1.0 1.0</color>" << LineEnding;
|
|
|
|
Stream << "</transparent>" << LineEnding;
|
|
|
|
Stream << "<transparency>" << LineEnding;
|
|
|
|
Stream << "<float>1.0</float>" << LineEnding;
|
|
|
|
Stream << "</transparency>" << LineEnding;
|
|
|
|
Stream << "</phong>" << LineEnding;
|
|
|
|
Stream << "</technique>" << LineEnding;
|
|
|
|
Stream << "</profile_COMMON>" << LineEnding;
|
|
|
|
Stream << "</effect>" << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "</library_effects>" << LineEnding;
|
|
|
|
Stream << "<library_materials>" << LineEnding;
|
|
|
|
|
|
|
|
for (const lcColor& Color : gColorList)
|
|
|
|
{
|
|
|
|
const char* ColorName = Color.SafeName;
|
|
|
|
Stream << QString("<material id=\"%1-material\">").arg(ColorName) << LineEnding;
|
|
|
|
Stream << QString("<instance_effect url=\"#%1-phong\" />").arg(ColorName) << LineEnding;
|
|
|
|
Stream << "</material>" << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "</library_materials>" << LineEnding;
|
|
|
|
Stream << "<library_geometries>" << LineEnding;
|
|
|
|
QSet<PieceInfo*> AddedPieces;
|
|
|
|
|
|
|
|
for (const lcModelPartsEntry& Entry : ModelParts)
|
|
|
|
{
|
|
|
|
PieceInfo* Info = Entry.Info;
|
|
|
|
|
|
|
|
if (AddedPieces.contains(Info))
|
|
|
|
continue;
|
|
|
|
AddedPieces.insert(Info);
|
|
|
|
|
|
|
|
QString ID = QString(Info->mFileName).replace('.', '_');
|
|
|
|
lcMesh* Mesh = Info->GetMesh();
|
|
|
|
|
|
|
|
Stream << QString("\t<geometry id=\"%1\">").arg(ID) << LineEnding;
|
|
|
|
Stream << "\t\t<mesh>" << LineEnding;
|
|
|
|
|
|
|
|
Stream << QString("\t\t\t<source id=\"%1-pos\">").arg(ID) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t<float_array id=\"%1-pos-array\" count=\"%2\">").arg(ID, QString::number(Mesh->mNumVertices)) << LineEnding;
|
|
|
|
|
|
|
|
lcVertex* Verts = (lcVertex*)Mesh->mVertexData;
|
|
|
|
|
|
|
|
for (int VertexIdx = 0; VertexIdx < Mesh->mNumVertices; VertexIdx++)
|
|
|
|
{
|
|
|
|
lcVector3& Position = Verts[VertexIdx].Position;
|
|
|
|
Stream << QString("\t\t\t\t\t%1 %2 %3").arg(QString::number(Position.x), QString::number(Position.y), QString::number(Position.z)) << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "\t\t\t\t</float_array>" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t<technique_common>" << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t\t<accessor source=\"#%1-pos-array\" count=\"%2\" stride=\"3\">").arg(ID, QString::number(Mesh->mNumVertices)) << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t\t<param name=\"X\" type=\"float\" />" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t\t<param name=\"Y\" type=\"float\" />" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t\t<param name=\"Z\" type=\"float\" />" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t</accessor>" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t</technique_common>" << LineEnding;
|
|
|
|
Stream << "\t\t\t</source>" << LineEnding;
|
|
|
|
|
|
|
|
Stream << QString("\t\t\t<source id=\"%1-normal\">").arg(ID) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t<float_array id=\"%1-normal-array\" count=\"%2\">").arg(ID, QString::number(Mesh->mNumVertices)) << LineEnding;
|
|
|
|
|
|
|
|
for (int VertexIdx = 0; VertexIdx < Mesh->mNumVertices; VertexIdx++)
|
|
|
|
{
|
|
|
|
lcVector3 Normal = lcUnpackNormal(Verts[VertexIdx].Normal);
|
|
|
|
Stream << QString("\t\t\t\t\t%1 %2 %3").arg(QString::number(Normal.x), QString::number(Normal.y), QString::number(Normal.z)) << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "\t\t\t\t</float_array>" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t<technique_common>" << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t\t<accessor source=\"#%1-normal-array\" count=\"%2\" stride=\"3\">").arg(ID, QString::number(Mesh->mNumVertices)) << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t\t<param name=\"X\" type=\"float\" />" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t\t<param name=\"Y\" type=\"float\" />" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t\t<param name=\"Z\" type=\"float\" />" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t</accessor>" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t</technique_common>" << LineEnding;
|
|
|
|
Stream << "\t\t\t</source>" << LineEnding;
|
|
|
|
|
|
|
|
Stream << QString("\t\t\t<vertices id=\"%1-vertices\">").arg(ID) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t<input semantic=\"POSITION\" source=\"#%1-pos\"/>").arg(ID) << LineEnding;
|
|
|
|
Stream << "\t\t\t</vertices>" << LineEnding;
|
|
|
|
|
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
|
|
|
|
{
|
|
|
|
lcMeshSection* Section = &Mesh->mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
|
|
|
|
|
|
|
|
if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
|
|
|
|
continue;
|
|
|
|
#define IndexType lcuint16
|
|
|
|
IndexType* Indices = (IndexType*)Mesh->mIndexData + Section->IndexOffset / sizeof(IndexType);
|
|
|
|
|
|
|
|
const char* ColorName = gColorList[Section->ColorIndex].SafeName;
|
|
|
|
Stream << QString("\t\t\t<triangles count=\"%1\" material=\"%2\">").arg(QString::number(Section->NumIndices / 3), ColorName) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t<input semantic=\"VERTEX\" source=\"#%1-vertices\" offset=\"0\"/>").arg(ID) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t<input semantic=\"NORMAL\" source=\"#%1-normal\" offset=\"0\"/>").arg(ID) << LineEnding;
|
|
|
|
Stream << "\t\t\t\t<p>" << LineEnding;
|
|
|
|
|
|
|
|
for (int Idx = 0; Idx < Section->NumIndices; Idx += 3)
|
|
|
|
{
|
|
|
|
QString idx1 = QString::number(Indices[Idx + 0]);
|
|
|
|
QString idx2 = QString::number(Indices[Idx + 1]);
|
|
|
|
QString idx3 = QString::number(Indices[Idx + 2]);
|
|
|
|
|
|
|
|
Stream << QString("\t\t\t\t %1 %2 %3").arg(idx1, idx2, idx3) << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "\t\t\t\t</p>" << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "\t\t\t</triangles>" << LineEnding;
|
|
|
|
Stream << "\t\t</mesh>" << LineEnding;
|
|
|
|
Stream << "\t</geometry>" << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "</library_geometries>" << LineEnding;
|
|
|
|
Stream << "<library_visual_scenes>" << LineEnding;
|
|
|
|
Stream << "\t<visual_scene id=\"DefaultScene\">" << LineEnding;
|
|
|
|
|
|
|
|
for (const lcModelPartsEntry& Entry : ModelParts)
|
|
|
|
{
|
|
|
|
PieceInfo* Info = Entry.Info;
|
|
|
|
QString ID = QString(Info->mFileName).replace('.', '_');
|
|
|
|
|
|
|
|
Stream << "\t\t<node>" << LineEnding;
|
|
|
|
Stream << "\t\t\t<matrix>" << LineEnding;
|
|
|
|
|
|
|
|
const lcMatrix44& Matrix = Entry.WorldMatrix;
|
|
|
|
Stream << QString("\t\t\t\t%1 %2 %3 %4").arg(QString::number(Matrix[0][0]), QString::number(Matrix[1][0]), QString::number(Matrix[2][0]), QString::number(Matrix[3][0])) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t%1 %2 %3 %4").arg(QString::number(Matrix[0][1]), QString::number(Matrix[1][1]), QString::number(Matrix[2][1]), QString::number(Matrix[3][1])) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t%1 %2 %3 %4").arg(QString::number(Matrix[0][2]), QString::number(Matrix[1][2]), QString::number(Matrix[2][2]), QString::number(Matrix[3][2])) << LineEnding;
|
|
|
|
Stream << QString("\t\t\t\t%1 %2 %3 %4").arg(QString::number(Matrix[0][3]), QString::number(Matrix[1][3]), QString::number(Matrix[2][3]), QString::number(Matrix[3][3])) << LineEnding;
|
|
|
|
|
|
|
|
Stream << "\t\t\t</matrix>" << LineEnding;
|
|
|
|
Stream << QString("\t\t\t<instance_geometry url=\"#%1\">").arg(ID) << LineEnding;
|
|
|
|
Stream << "\t\t\t\t<bind_material>" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t\t<technique_common>" << LineEnding;
|
|
|
|
|
|
|
|
lcMesh* Mesh = Info->GetMesh();
|
|
|
|
|
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mLods[LC_MESH_LOD_HIGH].NumSections; SectionIdx++)
|
|
|
|
{
|
|
|
|
lcMeshSection* Section = &Mesh->mLods[LC_MESH_LOD_HIGH].Sections[SectionIdx];
|
|
|
|
|
|
|
|
if (Section->PrimitiveType != LC_MESH_TRIANGLES && Section->PrimitiveType != LC_MESH_TEXTURED_TRIANGLES)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
const char* SourceColorName = gColorList[Section->ColorIndex].SafeName;
|
|
|
|
const char* TargetColorName;
|
|
|
|
if (Section->ColorIndex == gDefaultColor)
|
|
|
|
TargetColorName = gColorList[Entry.ColorIndex].SafeName;
|
|
|
|
else
|
|
|
|
TargetColorName = gColorList[Section->ColorIndex].SafeName;
|
|
|
|
|
|
|
|
Stream << QString("\t\t\t\t\t\t<instance_material symbol=\"%1\" target=\"#%2-material\"/>").arg(SourceColorName, TargetColorName) << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "\t\t\t\t\t</technique_common>" << LineEnding;
|
|
|
|
Stream << "\t\t\t\t</bind_material>" << LineEnding;
|
|
|
|
Stream << "\t\t\t</instance_geometry>" << LineEnding;
|
|
|
|
Stream << "\t\t</node>" << LineEnding;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream << "\t</visual_scene>" << LineEnding;
|
|
|
|
Stream << "</library_visual_scenes>" << LineEnding;
|
|
|
|
Stream << "<scene>" << LineEnding;
|
|
|
|
Stream << "\t<instance_visual_scene url=\"#DefaultScene\"/>" << LineEnding;
|
|
|
|
Stream << "</scene>" << LineEnding;
|
|
|
|
|
|
|
|
Stream << "</COLLADA>" << LineEnding;
|
|
|
|
}
|
|
|
|
|
2014-12-30 17:30:12 +01:00
|
|
|
void Project::ExportCSV()
|
|
|
|
{
|
2016-11-16 23:53:41 +01:00
|
|
|
lcPartsList PartsList;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
|
|
|
if (!mModels.IsEmpty())
|
|
|
|
mModels[0]->GetPartsList(gDefaultColor, PartsList);
|
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
if (PartsList.empty())
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-01-30 17:30:13 +01:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export."));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
QString SaveFileName = GetExportFileName(QString(), "csv", tr("Export CSV"), tr("CSV Files (*.csv);;All Files (*.*)"));
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
if (SaveFileName.isEmpty())
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile CSVFile(SaveFileName);
|
2014-12-30 17:30:12 +01:00
|
|
|
char Line[1024];
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!CSVFile.Open(QIODevice::WriteOnly))
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-03-27 21:39:43 +01:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CSVFile.WriteLine("Part Name,Color,Quantity,Part ID,Color Code\n");
|
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& PartIt : PartsList)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
const PieceInfo* Info = PartIt.first;
|
2016-11-16 23:53:41 +01:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& ColorIt : PartIt.second)
|
2016-11-16 23:53:41 +01:00
|
|
|
{
|
2017-07-27 18:21:55 +02:00
|
|
|
sprintf(Line, "\"%s\",\"%s\",%d,%s,%d\n", Info->m_strDescription, gColorList[ColorIt.first].Name, ColorIt.second, Info->mFileName, gColorList[ColorIt.first].Code);
|
2016-11-16 23:53:41 +01:00
|
|
|
CSVFile.WriteLine(Line);
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
QImage Project::CreatePartsListImage(lcModel* Model, lcStep Step)
|
2017-06-29 19:02:00 +02:00
|
|
|
{
|
|
|
|
lcPartsList PartsList;
|
|
|
|
if (Step == 0)
|
|
|
|
Model->GetPartsList(gDefaultColor, PartsList);
|
|
|
|
else
|
|
|
|
Model->GetPartsListForStep(Step, gDefaultColor, PartsList);
|
|
|
|
|
|
|
|
if (PartsList.empty())
|
2017-07-03 01:10:30 +02:00
|
|
|
return QImage();
|
2017-06-29 19:02:00 +02:00
|
|
|
|
|
|
|
struct lcPartsListImage
|
|
|
|
{
|
|
|
|
QImage Thumbnail;
|
|
|
|
const PieceInfo* Info;
|
|
|
|
int ColorIndex;
|
|
|
|
int Count;
|
|
|
|
QRect Bounds;
|
2017-07-03 01:10:30 +02:00
|
|
|
QPoint Position;
|
2017-06-29 19:02:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<lcPartsListImage> Images;
|
|
|
|
|
|
|
|
for (const auto& PartIt : PartsList)
|
|
|
|
{
|
|
|
|
for (const auto& ColorIt : PartIt.second)
|
|
|
|
{
|
|
|
|
Images.push_back(lcPartsListImage());
|
|
|
|
lcPartsListImage& Image = Images.back();
|
|
|
|
Image.Info = PartIt.first;
|
|
|
|
Image.ColorIndex = ColorIt.first;
|
|
|
|
Image.Count = ColorIt.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto ImageCompare = [this](const lcPartsListImage& Image1, const lcPartsListImage& Image2)
|
|
|
|
{
|
|
|
|
if (Image1.ColorIndex != Image2.ColorIndex)
|
|
|
|
return Image1.ColorIndex < Image2.ColorIndex;
|
|
|
|
|
|
|
|
return strcmp(Image1.Info->m_strDescription, Image2.Info->m_strDescription) < 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::sort(Images.begin(), Images.end(), ImageCompare);
|
|
|
|
|
|
|
|
View* View = gMainWindow->GetActiveView();
|
|
|
|
View->MakeCurrent();
|
|
|
|
lcContext* Context = View->mContext;
|
|
|
|
const int ThumbnailWidth = 512;
|
|
|
|
const int ThumbnailHeight = 512;
|
|
|
|
|
|
|
|
if (!Context->BeginRenderToTexture(ThumbnailWidth, ThumbnailHeight))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
|
2017-07-03 01:10:30 +02:00
|
|
|
return QImage();
|
2017-06-29 19:02:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
float Aspect = (float)ThumbnailWidth / (float)ThumbnailHeight;
|
2017-07-03 01:10:30 +02:00
|
|
|
float OrthoHeight = 200.0f;
|
2017-06-30 17:22:38 +02:00
|
|
|
float OrthoWidth = OrthoHeight * Aspect;
|
2017-06-29 19:02:00 +02:00
|
|
|
|
2017-06-30 17:22:38 +02:00
|
|
|
lcMatrix44 ProjectionMatrix = lcMatrix44Ortho(-OrthoWidth, OrthoWidth, -OrthoHeight, OrthoHeight, 1.0f, 50000.0f);
|
2017-06-29 19:02:00 +02:00
|
|
|
lcMatrix44 ViewMatrix = lcMatrix44LookAt(lcVector3(-100.0f, -100.0f, 75.0f), lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f));
|
|
|
|
|
2017-06-30 17:22:38 +02:00
|
|
|
Context->SetViewport(0, 0, ThumbnailWidth, ThumbnailHeight);
|
2017-06-29 19:02:00 +02:00
|
|
|
Context->SetDefaultState();
|
|
|
|
Context->SetProjectionMatrix(ProjectionMatrix);
|
|
|
|
|
|
|
|
for (lcPartsListImage& Image : Images)
|
|
|
|
{
|
|
|
|
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
|
|
|
lcScene Scene;
|
|
|
|
Scene.Begin(ViewMatrix);
|
|
|
|
|
|
|
|
Image.Info->AddRenderMeshes(Scene, lcMatrix44Identity(), Image.ColorIndex, false, false, false);
|
|
|
|
|
|
|
|
Scene.End();
|
|
|
|
|
|
|
|
Scene.Draw(Context);
|
|
|
|
|
|
|
|
Image.Thumbnail = Context->GetRenderToTextureImage(ThumbnailWidth, ThumbnailHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
Context->EndRenderToTexture();
|
|
|
|
Context->ClearResources();
|
|
|
|
|
|
|
|
auto CalculateImageBounds = [](lcPartsListImage& Image)
|
|
|
|
{
|
|
|
|
QImage& Thumbnail = Image.Thumbnail;
|
|
|
|
int Width = Thumbnail.width();
|
|
|
|
int Height = Thumbnail.height();
|
|
|
|
|
|
|
|
int MinX = Width;
|
|
|
|
int MinY = Height;
|
|
|
|
int MaxX = 0;
|
|
|
|
int MaxY = 0;
|
|
|
|
|
|
|
|
for (int x = 0; x < Width; x++)
|
|
|
|
{
|
|
|
|
for (int y = 0; y < Height; y++)
|
|
|
|
{
|
2017-06-29 20:12:37 +02:00
|
|
|
if (qAlpha(Thumbnail.pixel(x, y)))
|
2017-06-29 19:02:00 +02:00
|
|
|
{
|
|
|
|
MinX = qMin(x, MinX);
|
|
|
|
MinY = qMin(y, MinY);
|
|
|
|
MaxX = qMax(x, MaxX);
|
|
|
|
MaxY = qMax(y, MaxY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Image.Bounds = QRect(QPoint(MinX, MinY), QPoint(MaxX, MaxY));
|
|
|
|
};
|
|
|
|
|
|
|
|
QtConcurrent::blockingMap(Images, CalculateImageBounds);
|
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
QImage DummyImage(16, 16, QImage::Format_ARGB32);
|
|
|
|
QPainter DummyPainter(&DummyImage);
|
2017-06-29 19:02:00 +02:00
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
QFont Font("helvetica", 20, QFont::Bold);
|
|
|
|
DummyPainter.setFont(Font);
|
|
|
|
QFontMetrics FontMetrics = DummyPainter.fontMetrics();
|
2017-06-30 17:22:38 +02:00
|
|
|
int Ascent = FontMetrics.ascent();
|
2017-07-03 01:10:30 +02:00
|
|
|
|
|
|
|
int CurrentHeight = 0;
|
|
|
|
int MaxWidth = 1024;
|
2017-06-29 19:02:00 +02:00
|
|
|
|
|
|
|
for (lcPartsListImage& Image : Images)
|
|
|
|
{
|
2017-07-03 01:10:30 +02:00
|
|
|
CurrentHeight = qMax(Image.Bounds.height() + Ascent, CurrentHeight);
|
|
|
|
MaxWidth = qMax(MaxWidth, Image.Bounds.height());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
int CurrentWidth = 0;
|
|
|
|
int CurrentX = 0;
|
|
|
|
int CurrentY = 0;
|
|
|
|
int ColumnWidth = 0;
|
|
|
|
int Spacing = 20;
|
|
|
|
int NextHeightIncrease = INT_MAX;
|
|
|
|
|
|
|
|
for (lcPartsListImage& Image : Images)
|
2017-06-30 17:22:38 +02:00
|
|
|
{
|
2017-07-03 01:10:30 +02:00
|
|
|
if (CurrentY + Image.Bounds.height() + Ascent > CurrentHeight)
|
|
|
|
{
|
|
|
|
int NeededSpace = Image.Bounds.height() + Ascent - (CurrentHeight - CurrentY);
|
|
|
|
NextHeightIncrease = qMin(NeededSpace, NextHeightIncrease);
|
|
|
|
|
|
|
|
CurrentY = 0;
|
|
|
|
CurrentX += ColumnWidth + Spacing;
|
|
|
|
ColumnWidth = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Image.Position = QPoint(CurrentX, CurrentY);
|
|
|
|
CurrentY += Image.Bounds.height() + Ascent + Spacing;
|
|
|
|
CurrentWidth = qMax(CurrentWidth, CurrentX + Image.Bounds.width());
|
|
|
|
ColumnWidth = qMax(ColumnWidth, Image.Bounds.width());
|
2017-06-30 17:22:38 +02:00
|
|
|
}
|
2017-06-29 19:02:00 +02:00
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
if (CurrentWidth < MaxWidth)
|
|
|
|
{
|
|
|
|
MaxWidth = CurrentWidth;
|
|
|
|
break;
|
|
|
|
}
|
2017-06-29 19:02:00 +02:00
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
CurrentHeight += NextHeightIncrease;
|
|
|
|
}
|
2017-06-29 19:02:00 +02:00
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
QImage Image(MaxWidth + 40, CurrentHeight + 40, QImage::Format_ARGB32);
|
|
|
|
Image.fill(QColor(255, 255, 255, 0));
|
|
|
|
|
|
|
|
QPainter Painter(&Image);
|
|
|
|
Painter.setFont(Font);
|
|
|
|
|
|
|
|
for (lcPartsListImage& Image : Images)
|
|
|
|
{
|
|
|
|
QPoint Position = Image.Position + QPoint(20, 20);
|
|
|
|
Painter.drawImage(Position, Image.Thumbnail, Image.Bounds);
|
|
|
|
Painter.drawText(QPoint(Position.x(), Position.y() + Image.Bounds.height() + Ascent), QString::number(Image.Count) + 'x');
|
2017-06-29 19:02:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Painter.end();
|
2017-07-03 01:10:30 +02:00
|
|
|
|
|
|
|
return Image;
|
2017-06-29 19:02:00 +02:00
|
|
|
}
|
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
void Project::CreateHTMLPieceList(QTextStream& Stream, lcModel* Model, lcStep Step, bool Images)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
std::vector<int> ColorsUsed(gColorList.GetSize(), 0);
|
2015-01-12 05:49:30 +01:00
|
|
|
int NumColors = 0;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-11-16 23:53:41 +01:00
|
|
|
lcPartsList PartsList;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (Step == 0)
|
|
|
|
Model->GetPartsList(gDefaultColor, PartsList);
|
|
|
|
else
|
|
|
|
Model->GetPartsListForStep(Step, gDefaultColor, PartsList);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& PartIt : PartsList)
|
|
|
|
for (const auto& ColorIt : PartIt.second)
|
|
|
|
ColorsUsed[ColorIt.first]++;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
Stream << QLatin1String("<br><table border=1><tr><td><center>Piece</center></td>\r\n");
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int ColorIdx = 0; ColorIdx < gColorList.GetSize(); ColorIdx++)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
if (ColorsUsed[ColorIdx])
|
|
|
|
{
|
2016-11-16 23:53:41 +01:00
|
|
|
ColorsUsed[ColorIdx] = NumColors++;
|
2015-01-12 05:49:30 +01:00
|
|
|
Stream << QString("<td><center>%1</center></td>\n").arg(gColorList[ColorIdx].Name);
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
NumColors++;
|
|
|
|
Stream << QLatin1String("</tr>\n");
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& PartIt : PartsList)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
const PieceInfo* Info = PartIt.first;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-11-16 23:53:41 +01:00
|
|
|
if (Images)
|
2017-07-27 18:21:55 +02:00
|
|
|
Stream << QString("<tr><td><IMG SRC=\"%1.png\" ALT=\"%2\"></td>\n").arg(Info->mFileName, Info->m_strDescription);
|
2016-11-16 23:53:41 +01:00
|
|
|
else
|
|
|
|
Stream << QString("<tr><td>%1</td>\r\n").arg(Info->m_strDescription);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-11-16 23:53:41 +01:00
|
|
|
int CurrentColumn = 1;
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& ColorIt : PartIt.second)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
while (CurrentColumn != ColorsUsed[ColorIt.first] + 1)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
|
|
|
Stream << QLatin1String("<td><center>-</center></td>\r\n");
|
2016-11-16 23:53:41 +01:00
|
|
|
CurrentColumn++;
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
Stream << QString("<td><center>%1</center></td>\r\n").arg(QString::number(ColorIt.second));
|
2016-11-16 23:53:41 +01:00
|
|
|
CurrentColumn++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (CurrentColumn != NumColors)
|
|
|
|
{
|
|
|
|
Stream << QLatin1String("<td><center>-</center></td>\r\n");
|
|
|
|
CurrentColumn++;
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2016-11-16 23:53:41 +01:00
|
|
|
|
|
|
|
Stream << QLatin1String("</tr>\r\n");
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
|
|
|
Stream << QLatin1String("</table>\r\n<br>");
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
void Project::ExportHTML()
|
|
|
|
{
|
|
|
|
lcHTMLDialogOptions Options;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (!mFileName.isEmpty())
|
|
|
|
Options.PathName = QFileInfo(mFileName).canonicalPath();
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
int ImageOptions = lcGetProfileInt(LC_PROFILE_HTML_IMAGE_OPTIONS);
|
|
|
|
int HTMLOptions = lcGetProfileInt(LC_PROFILE_HTML_OPTIONS);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
Options.TransparentImages = (ImageOptions & LC_IMAGE_TRANSPARENT) != 0;
|
2016-05-31 00:41:03 +02:00
|
|
|
Options.SubModels = (HTMLOptions & (LC_HTML_SUBMODELS)) != 0;
|
|
|
|
Options.CurrentOnly = (HTMLOptions & LC_HTML_CURRENT_ONLY) != 0;
|
2015-01-12 05:49:30 +01:00
|
|
|
Options.SinglePage = (HTMLOptions & LC_HTML_SINGLEPAGE) != 0;
|
|
|
|
Options.IndexPage = (HTMLOptions & LC_HTML_INDEX) != 0;
|
|
|
|
Options.StepImagesWidth = lcGetProfileInt(LC_PROFILE_HTML_IMAGE_WIDTH);
|
|
|
|
Options.StepImagesHeight = lcGetProfileInt(LC_PROFILE_HTML_IMAGE_HEIGHT);
|
|
|
|
Options.HighlightNewParts = (HTMLOptions & LC_HTML_HIGHLIGHT) != 0;
|
|
|
|
Options.PartsListStep = (HTMLOptions & LC_HTML_LISTSTEP) != 0;
|
|
|
|
Options.PartsListEnd = (HTMLOptions & LC_HTML_LISTEND) != 0;
|
|
|
|
Options.PartsListImages = (HTMLOptions & LC_HTML_IMAGES) != 0;
|
|
|
|
Options.PartImagesColor = lcGetColorIndex(lcGetProfileInt(LC_PROFILE_HTML_PARTS_COLOR));
|
|
|
|
Options.PartImagesWidth = lcGetProfileInt(LC_PROFILE_HTML_PARTS_WIDTH);
|
|
|
|
Options.PartImagesHeight = lcGetProfileInt(LC_PROFILE_HTML_PARTS_HEIGHT);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (!gMainWindow->DoDialog(LC_DIALOG_EXPORT_HTML, &Options))
|
|
|
|
return;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
HTMLOptions = 0;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.SubModels)
|
|
|
|
HTMLOptions |= LC_HTML_SUBMODELS;
|
|
|
|
if (Options.CurrentOnly)
|
|
|
|
HTMLOptions |= LC_HTML_CURRENT_ONLY;
|
2015-01-12 05:49:30 +01:00
|
|
|
if (Options.SinglePage)
|
|
|
|
HTMLOptions |= LC_HTML_SINGLEPAGE;
|
|
|
|
if (Options.IndexPage)
|
|
|
|
HTMLOptions |= LC_HTML_INDEX;
|
|
|
|
if (Options.HighlightNewParts)
|
|
|
|
HTMLOptions |= LC_HTML_HIGHLIGHT;
|
|
|
|
if (Options.PartsListStep)
|
|
|
|
HTMLOptions |= LC_HTML_LISTSTEP;
|
|
|
|
if (Options.PartsListEnd)
|
|
|
|
HTMLOptions |= LC_HTML_LISTEND;
|
|
|
|
if (Options.PartsListImages)
|
|
|
|
HTMLOptions |= LC_HTML_IMAGES;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_IMAGE_OPTIONS, Options.TransparentImages ? LC_IMAGE_TRANSPARENT : 0);
|
2015-01-12 05:49:30 +01:00
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_OPTIONS, HTMLOptions);
|
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_IMAGE_WIDTH, Options.StepImagesWidth);
|
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_IMAGE_HEIGHT, Options.StepImagesHeight);
|
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_PARTS_COLOR, lcGetColorCode(Options.PartImagesColor));
|
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_PARTS_WIDTH, Options.PartImagesWidth);
|
|
|
|
lcSetProfileInt(LC_PROFILE_HTML_PARTS_HEIGHT, Options.PartImagesHeight);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
QDir Dir(Options.PathName);
|
|
|
|
Dir.mkpath(QLatin1String("."));
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
lcArray<lcModel*> Models;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.CurrentOnly)
|
|
|
|
Models.Add(mActiveModel);
|
|
|
|
else if (Options.SubModels)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-05-31 00:41:03 +02:00
|
|
|
Models.Add(mActiveModel);
|
|
|
|
mActiveModel->GetSubModels(Models);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2016-05-31 00:41:03 +02:00
|
|
|
else
|
|
|
|
Models = mModels;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
QString ProjectTitle = GetTitle();
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
auto AddPartsListImage = [this, &ProjectTitle, &Dir](QTextStream& Stream, lcModel* Model, lcStep Step)
|
|
|
|
{
|
|
|
|
QImage Image = CreatePartsListImage(Model, Step);
|
|
|
|
|
|
|
|
if (!Image.isNull())
|
|
|
|
{
|
|
|
|
QString BaseName = ProjectTitle.left(ProjectTitle.length() - QFileInfo(ProjectTitle).suffix().length() - 1);
|
|
|
|
QString FileName = QFileInfo(Dir, BaseName + QLatin1String("-parts.png")).absoluteFilePath();
|
|
|
|
|
|
|
|
Image.save(FileName);
|
|
|
|
|
|
|
|
Stream << QString::fromLatin1("<IMG SRC=\"%1\" />\r\n").arg(FileName);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
for (int ModelIdx = 0; ModelIdx < Models.GetSize(); ModelIdx++)
|
|
|
|
{
|
|
|
|
lcModel* Model = mModels[ModelIdx];
|
|
|
|
QString BaseName = ProjectTitle.left(ProjectTitle.length() - QFileInfo(ProjectTitle).suffix().length() - 1);
|
|
|
|
lcStep LastStep = Model->GetLastStep();
|
|
|
|
QString PageTitle;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Models.GetSize() > 1)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-05-31 00:41:03 +02:00
|
|
|
BaseName += '-' + Model->GetProperties().mName;
|
|
|
|
PageTitle = Model->GetProperties().mName;
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
2016-05-31 00:41:03 +02:00
|
|
|
else
|
|
|
|
PageTitle = ProjectTitle;
|
|
|
|
BaseName.replace('#', '_');
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.SinglePage)
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2016-05-31 00:41:03 +02:00
|
|
|
QString FileName = QFileInfo(Dir, BaseName + QLatin1String(".html")).absoluteFilePath();
|
2015-01-12 05:49:30 +01:00
|
|
|
QFile File(FileName);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-01-26 00:04:39 +01:00
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString()));
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
QTextStream Stream(&File);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QString::fromLatin1("<HTML>\r\n<HEAD>\r\n<TITLE>Instructions for %1</TITLE>\r\n</HEAD>\r\n<BR>\r\n<CENTER>\r\n").arg(PageTitle);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (lcStep Step = 1; Step <= LastStep; Step++)
|
2016-05-31 00:41:03 +02:00
|
|
|
{
|
|
|
|
QString StepString = QString::fromLatin1("%1").arg(Step, 2, 10, QLatin1Char('0'));
|
|
|
|
Stream << QString::fromLatin1("<IMG SRC=\"%1-%2.png\" ALT=\"Step %3\" WIDTH=%4 HEIGHT=%5><BR><BR>\r\n").arg(BaseName, StepString, StepString, QString::number(Options.StepImagesWidth), QString::number(Options.StepImagesHeight));
|
|
|
|
|
|
|
|
if (Options.PartsListStep)
|
|
|
|
CreateHTMLPieceList(Stream, Model, Step, Options.PartsListImages);
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (Options.PartsListEnd)
|
2017-07-03 01:10:30 +02:00
|
|
|
AddPartsListImage(Stream, Model, 0);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QLatin1String("</CENTER>\n<BR><HR><BR><B><I>Created by <A HREF=\"http://www.leocad.org\">LeoCAD</A></B></I><BR></HTML>\r\n");
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
2016-05-31 00:41:03 +02:00
|
|
|
else
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.IndexPage)
|
|
|
|
{
|
|
|
|
QString FileName = QFileInfo(Dir, BaseName + QLatin1String("-index.html")).absoluteFilePath();
|
|
|
|
QFile File(FileName);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream Stream(&File);
|
|
|
|
|
|
|
|
Stream << QString::fromLatin1("<HTML>\r\n<HEAD>\r\n<TITLE>Instructions for %1</TITLE>\r\n</HEAD>\r\n<BR>\r\n<CENTER>\r\n").arg(PageTitle);
|
|
|
|
|
|
|
|
for (lcStep Step = 1; Step <= LastStep; Step++)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-%2.html\">Step %3<BR>\r\n</A>").arg(BaseName, QString("%1").arg(Step, 2, 10, QLatin1Char('0')), QString::number(Step));
|
|
|
|
|
|
|
|
if (Options.PartsListEnd)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-pieces.html\">Pieces Used</A><BR>\r\n").arg(BaseName);
|
|
|
|
|
|
|
|
Stream << QLatin1String("</CENTER>\r\n<BR><HR><BR><B><I>Created by <A HREF=\"http://www.leocad.org\">LeoCAD</A></B></I><BR></HTML>\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (lcStep Step = 1; Step <= LastStep; Step++)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-05-31 00:41:03 +02:00
|
|
|
QString StepString = QString::fromLatin1("%1").arg(Step, 2, 10, QLatin1Char('0'));
|
|
|
|
QString FileName = QFileInfo(Dir, QString("%1-%2.html").arg(BaseName, StepString)).absoluteFilePath();
|
|
|
|
QFile File(FileName);
|
|
|
|
|
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTextStream Stream(&File);
|
|
|
|
|
|
|
|
Stream << QString::fromLatin1("<HTML>\r\n<HEAD>\r\n<TITLE>%1 - Step %2</TITLE>\r\n</HEAD>\r\n<BR>\r\n<CENTER>\r\n").arg(PageTitle, QString::number(Step));
|
|
|
|
Stream << QString::fromLatin1("<IMG SRC=\"%1-%2.png\" ALT=\"Step %3\" WIDTH=%4 HEIGHT=%5><BR><BR>\r\n").arg(BaseName, StepString, StepString, QString::number(Options.StepImagesWidth), QString::number(Options.StepImagesHeight));
|
|
|
|
|
|
|
|
if (Options.PartsListStep)
|
|
|
|
CreateHTMLPieceList(Stream, Model, Step, Options.PartsListImages);
|
|
|
|
|
|
|
|
Stream << QLatin1String("</CENTER>\r\n<BR><HR><BR>");
|
|
|
|
if (Step != 1)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-%2.html\">Previous</A> ").arg(BaseName, QString("%1").arg(Step - 1, 2, 10, QLatin1Char('0')));
|
|
|
|
|
|
|
|
if (Options.IndexPage)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-index.html\">Index</A> ").arg(BaseName);
|
|
|
|
|
|
|
|
if (Step != LastStep)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-%2.html\">Next</A>").arg(BaseName, QString("%1").arg(Step + 1, 2, 10, QLatin1Char('0')));
|
|
|
|
else if (Options.PartsListEnd)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-pieces.html\">Pieces Used</A>").arg(BaseName);
|
|
|
|
|
|
|
|
Stream << QLatin1String("<BR></HTML>\r\n");
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.PartsListEnd)
|
|
|
|
{
|
|
|
|
QString FileName = QFileInfo(Dir, BaseName + QLatin1String("-pieces.html")).absoluteFilePath();
|
|
|
|
QFile File(FileName);
|
|
|
|
|
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString()));
|
|
|
|
return;
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
QTextStream Stream(&File);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QString::fromLatin1("<HTML>\r\n<HEAD>\r\n<TITLE>Pieces used by %1</TITLE>\r\n</HEAD>\r\n<BR>\r\n<CENTER>\n").arg(PageTitle);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-07-03 01:10:30 +02:00
|
|
|
AddPartsListImage(Stream, Model, 0);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QLatin1String("</CENTER>\n<BR><HR><BR>");
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-%2.html\">Previous</A> ").arg(BaseName, QString("%1").arg(LastStep, 2, 10, QLatin1Char('0')));
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.IndexPage)
|
|
|
|
Stream << QString::fromLatin1("<A HREF=\"%1-index.html\">Index</A> ").arg(BaseName);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QLatin1String("<BR></HTML>\r\n");
|
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
QString StepImageBaseName = QFileInfo(Dir, BaseName + QLatin1String("-%1.png")).absoluteFilePath();
|
2017-06-20 17:06:40 +02:00
|
|
|
Model->SaveStepImages(StepImageBaseName, true, false, Options.HighlightNewParts, Options.StepImagesWidth, Options.StepImagesHeight, 1, LastStep);
|
2016-05-31 00:41:03 +02:00
|
|
|
|
|
|
|
if (Options.PartsListImages)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-12-28 22:30:31 +01:00
|
|
|
View* View = gMainWindow->GetActiveView();
|
|
|
|
View->MakeCurrent();
|
|
|
|
lcContext* Context = View->mContext;
|
2016-05-31 00:41:03 +02:00
|
|
|
int Width = Options.PartImagesWidth;
|
|
|
|
int Height = Options.PartImagesHeight;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (!Context->BeginRenderToTexture(Width, Height))
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-05-31 00:41:03 +02:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
float aspect = (float)Width / (float)Height;
|
|
|
|
Context->SetViewport(0, 0, Width, Height);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-11-16 23:53:41 +01:00
|
|
|
lcPartsList PartsList;
|
2016-05-31 00:41:03 +02:00
|
|
|
Model->GetPartsList(gDefaultColor, PartsList);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
lcMatrix44 ProjectionMatrix = lcMatrix44Perspective(30.0f, aspect, 1.0f, 2500.0f);
|
|
|
|
lcMatrix44 ViewMatrix;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Context->SetDefaultState();
|
|
|
|
Context->SetProjectionMatrix(ProjectionMatrix);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
for (const auto& PartIt : PartsList)
|
2016-05-31 00:41:03 +02:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
const PieceInfo* Info = PartIt.first;
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
lcVector3 CameraPosition(-100.0f, -100.0f, 75.0f);
|
|
|
|
Info->ZoomExtents(ProjectionMatrix, ViewMatrix, CameraPosition);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
lcScene Scene;
|
|
|
|
Scene.Begin(ViewMatrix);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2017-06-20 03:30:54 +02:00
|
|
|
Info->AddRenderMeshes(Scene, lcMatrix44Identity(), Options.PartImagesColor, false, false, false);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Scene.End();
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2017-04-02 01:53:54 +02:00
|
|
|
Scene.Draw(Context);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2017-07-27 18:21:55 +02:00
|
|
|
QString FileName = QFileInfo(Dir, QLatin1String(Info->mFileName) + QLatin1String(".png")).absoluteFilePath();
|
2016-05-31 00:41:03 +02:00
|
|
|
if (!Context->SaveRenderToTextureImage(FileName, Width, Height))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Context->EndRenderToTexture();
|
2017-03-25 20:29:28 +01:00
|
|
|
Context->ClearResources();
|
2016-05-31 00:41:03 +02:00
|
|
|
}
|
|
|
|
}
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Models.GetSize() > 1)
|
|
|
|
{
|
|
|
|
QString BaseName = ProjectTitle.left(ProjectTitle.length() - QFileInfo(ProjectTitle).suffix().length() - 1);
|
|
|
|
QString FileName = QFileInfo(Dir, BaseName + QLatin1String("-index.html")).absoluteFilePath();
|
|
|
|
QFile File(FileName);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (!File.open(QIODevice::WriteOnly))
|
|
|
|
{
|
|
|
|
QMessageBox::warning(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, File.errorString()));
|
|
|
|
return;
|
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
QTextStream Stream(&File);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QString::fromLatin1("<HTML>\r\n<HEAD>\r\n<TITLE>Instructions for %1</TITLE>\r\n</HEAD>\r\n<BR>\r\n<CENTER>\r\n").arg(ProjectTitle);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
for (int ModelIdx = 0; ModelIdx < Models.GetSize(); ModelIdx++)
|
|
|
|
{
|
|
|
|
lcModel* Model = Models[ModelIdx];
|
2017-04-03 02:15:09 +02:00
|
|
|
BaseName = ProjectTitle.left(ProjectTitle.length() - QFileInfo(ProjectTitle).suffix().length() - 1) + '-' + Model->GetProperties().mName;
|
2016-05-31 00:41:03 +02:00
|
|
|
BaseName.replace('#', '_');
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
if (Options.SinglePage)
|
|
|
|
FileName = BaseName + QLatin1String(".html");
|
|
|
|
else
|
|
|
|
FileName = BaseName + QLatin1String("-index.html");
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-05-31 00:41:03 +02:00
|
|
|
Stream << QString::fromLatin1("<p><a href=\"%1\">%2</a>").arg(FileName, Model->GetProperties().mName);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2016-05-31 00:41:03 +02:00
|
|
|
|
|
|
|
Stream << QLatin1String("</CENTER>\n<BR><HR><BR><B><I>Created by <A HREF=\"http://www.leocad.org\">LeoCAD</A></B></I><BR></HTML>\r\n");
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-16 01:25:47 +02:00
|
|
|
struct lcColorName
|
|
|
|
{
|
|
|
|
char Name[LC_MAX_COLOR_NAME];
|
|
|
|
};
|
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
void Project::ExportPOVRay()
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
|
|
|
lcArray<lcModelPartsEntry> ModelParts;
|
|
|
|
|
|
|
|
GetModelParts(ModelParts);
|
|
|
|
|
|
|
|
if (ModelParts.IsEmpty())
|
|
|
|
{
|
2015-01-30 17:30:13 +01:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export."));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
lcQPOVRayDialog Dialog(gMainWindow);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (Dialog.exec() != QDialog::Accepted)
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile POVFile(Dialog.mFileName);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!POVFile.Open(QIODevice::WriteOnly))
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2016-08-01 05:44:15 +02:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(Dialog.mFileName));
|
2014-12-30 17:30:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
char Line[1024];
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
lcPiecesLibrary* Library = lcGetPiecesLibrary();
|
2017-04-16 00:54:17 +02:00
|
|
|
std::map<PieceInfo*, std::pair<char[LC_PIECE_NAME_LEN], int>> PieceTable;
|
2015-01-12 05:49:30 +01:00
|
|
|
int NumColors = gColorList.GetSize();
|
2017-04-16 01:25:47 +02:00
|
|
|
std::vector<lcColorName> ColorTable(NumColors);
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
LGEO_PIECE_LGEO = 0x01,
|
|
|
|
LGEO_PIECE_AR = 0x02,
|
|
|
|
LGEO_PIECE_SLOPE = 0x04
|
|
|
|
};
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
enum
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
LGEO_COLOR_SOLID = 0x01,
|
|
|
|
LGEO_COLOR_TRANSPARENT = 0x02,
|
|
|
|
LGEO_COLOR_CHROME = 0x04,
|
|
|
|
LGEO_COLOR_PEARL = 0x08,
|
|
|
|
LGEO_COLOR_METALLIC = 0x10,
|
|
|
|
LGEO_COLOR_RUBBER = 0x20,
|
|
|
|
LGEO_COLOR_GLITTER = 0x40
|
|
|
|
};
|
2014-12-30 17:30:12 +01:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (!Dialog.mLGEOPath.isEmpty())
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile TableFile(QFileInfo(QDir(Dialog.mLGEOPath), QLatin1String("lg_elements.lst")).absoluteFilePath());
|
2014-12-31 17:38:30 +01:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!TableFile.Open(QIODevice::ReadOnly))
|
2014-12-30 17:30:12 +01:00
|
|
|
{
|
2016-08-01 05:44:15 +02:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Could not find LGEO files in folder '%1'.").arg(Dialog.mLGEOPath));
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
2014-12-30 17:30:12 +01:00
|
|
|
}
|
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
while (TableFile.ReadLine(Line, sizeof(Line)))
|
2014-12-31 17:38:30 +01:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
char Src[1024], Dst[1024], Flags[1024];
|
2013-12-08 00:15:28 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (*Line == ';')
|
|
|
|
continue;
|
2013-08-16 18:16:45 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (sscanf(Line,"%s%s%s", Src, Dst, Flags) != 3)
|
|
|
|
continue;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2017-07-27 18:21:55 +02:00
|
|
|
strcat(Src, ".dat");
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2017-04-14 02:26:40 +02:00
|
|
|
PieceInfo* Info = Library->FindPiece(Src, nullptr, false, false);
|
2015-01-12 05:49:30 +01:00
|
|
|
if (!Info)
|
|
|
|
continue;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (strchr(Flags, 'L'))
|
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
std::pair<char[LC_PIECE_NAME_LEN], int>& Entry = PieceTable[Info];
|
|
|
|
Entry.second |= LGEO_PIECE_LGEO;
|
|
|
|
sprintf(Entry.first, "lg_%s", Dst);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (strchr(Flags, 'A'))
|
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
std::pair<char[LC_PIECE_NAME_LEN], int>& Entry = PieceTable[Info];
|
|
|
|
Entry.second |= LGEO_PIECE_AR;
|
|
|
|
sprintf(Entry.first, "ar_%s", Dst);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (strchr(Flags, 'S'))
|
2016-11-17 21:10:33 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
std::pair<char[LC_PIECE_NAME_LEN], int>& Entry = PieceTable[Info];
|
|
|
|
Entry.second |= LGEO_PIECE_SLOPE;
|
|
|
|
Entry.first[0] = 0;
|
2016-11-17 21:10:33 +01:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile ColorFile(QFileInfo(QDir(Dialog.mLGEOPath), QLatin1String("lg_colors.lst")).absoluteFilePath());
|
|
|
|
|
|
|
|
if (!ColorFile.Open(QIODevice::ReadOnly))
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2016-08-01 05:44:15 +02:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Could not find LGEO files in folder '%1'.").arg(Dialog.mLGEOPath));
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
while (ColorFile.ReadLine(Line, sizeof(Line)))
|
|
|
|
{
|
|
|
|
char Name[1024], Flags[1024];
|
|
|
|
int Code;
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (*Line == ';')
|
|
|
|
continue;
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (sscanf(Line,"%d%s%s", &Code, Name, Flags) != 3)
|
|
|
|
continue;
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
int Color = lcGetColorIndex(Code);
|
|
|
|
if (Color >= NumColors)
|
|
|
|
continue;
|
2014-10-12 01:26:23 +02:00
|
|
|
|
2017-04-16 01:25:47 +02:00
|
|
|
strcpy(ColorTable[Color].Name, Name);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-05-18 01:03:05 +02:00
|
|
|
}
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (!Dialog.mLGEOPath.isEmpty())
|
2014-05-18 01:03:05 +02:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
POVFile.WriteLine("#include \"lg_defs.inc\"\n#include \"lg_color.inc\"\n\n");
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int PartIdx = 0; PartIdx < ModelParts.GetSize(); PartIdx++)
|
|
|
|
{
|
|
|
|
PieceInfo* Info = ModelParts[PartIdx].Info;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int CheckIdx = 0; CheckIdx < ModelParts.GetSize(); CheckIdx++)
|
|
|
|
{
|
|
|
|
if (ModelParts[CheckIdx].Info != Info)
|
|
|
|
continue;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (CheckIdx != PartIdx)
|
|
|
|
break;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
auto Search = PieceTable.find(Info);
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
if (Search != PieceTable.end())
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
const std::pair<char[LC_PIECE_NAME_LEN], int>& Entry = Search->second;
|
|
|
|
if (Entry.first[0])
|
|
|
|
{
|
|
|
|
sprintf(Line, "#include \"%s.inc\"\n", Entry.first);
|
|
|
|
POVFile.WriteLine(Line);
|
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
break;
|
|
|
|
}
|
2011-09-07 23:06:51 +02:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
|
|
|
|
POVFile.WriteLine("\n");
|
2014-05-18 01:03:05 +02:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
else
|
|
|
|
POVFile.WriteLine("#include \"colors.inc\"\n\n");
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int ColorIdx = 0; ColorIdx < gColorList.GetSize(); ColorIdx++)
|
2011-09-07 23:06:51 +02:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
lcColor* Color = &gColorList[ColorIdx];
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (lcIsColorTranslucent(ColorIdx))
|
2013-08-31 23:58:47 +02:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
sprintf(Line, "#declare lc_%s = texture { pigment { rgb <%f, %f, %f> filter 0.9 } finish { ambient 0.3 diffuse 0.2 reflection 0.25 phong 0.3 phong_size 60 } }\n",
|
|
|
|
Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]);
|
2014-05-18 01:03:05 +02:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
else
|
2014-05-18 01:03:05 +02:00
|
|
|
{
|
2015-01-12 05:49:30 +01:00
|
|
|
sprintf(Line, "#declare lc_%s = texture { pigment { rgb <%f, %f, %f> } finish { ambient 0.1 phong 0.2 phong_size 20 } }\n",
|
2016-11-17 21:10:33 +01:00
|
|
|
Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-05-18 01:03:05 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
POVFile.WriteLine(Line);
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2017-04-16 01:25:47 +02:00
|
|
|
if (!*ColorTable[ColorIdx].Name)
|
|
|
|
sprintf(ColorTable[ColorIdx].Name, "lc_%s", Color->SafeName);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
POVFile.WriteLine("\n");
|
2013-08-31 23:58:47 +02:00
|
|
|
|
2016-11-17 21:10:33 +01:00
|
|
|
lcArray<const char*> ColorTablePointer;
|
|
|
|
ColorTablePointer.SetSize(NumColors);
|
|
|
|
for (int ColorIdx = 0; ColorIdx < NumColors; ColorIdx++)
|
2017-04-16 01:25:47 +02:00
|
|
|
ColorTablePointer[ColorIdx] = ColorTable[ColorIdx].Name;
|
2016-11-17 21:10:33 +01:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int PartIdx = 0; PartIdx < ModelParts.GetSize(); PartIdx++)
|
|
|
|
{
|
|
|
|
PieceInfo* Info = ModelParts[PartIdx].Info;
|
|
|
|
lcMesh* Mesh = Info->GetMesh();
|
2017-04-16 00:54:17 +02:00
|
|
|
std::pair<char[LC_PIECE_NAME_LEN], int>& Entry = PieceTable[Info];
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
if (!Mesh || Entry.first[0])
|
2015-01-12 05:49:30 +01:00
|
|
|
continue;
|
2011-09-07 23:06:51 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
char Name[LC_PIECE_NAME_LEN];
|
|
|
|
char* Ptr;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-07-27 18:21:55 +02:00
|
|
|
strcpy(Name, Info->mFileName);
|
2015-01-12 05:49:30 +01:00
|
|
|
while ((Ptr = strchr(Name, '-')))
|
|
|
|
*Ptr = '_';
|
2017-07-27 18:21:55 +02:00
|
|
|
while ((Ptr = strchr(Name, '.')))
|
|
|
|
*Ptr = '_';
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
sprintf(Entry.first, "lc_%s", Name);
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2016-11-17 21:10:33 +01:00
|
|
|
Mesh->ExportPOVRay(POVFile, Name, &ColorTablePointer[0]);
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
POVFile.WriteLine("}\n\n");
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
sprintf(Line, "#declare lc_%s_clear = lc_%s\n\n", Name, Name);
|
|
|
|
POVFile.WriteLine(Line);
|
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
lcCamera* Camera = gMainWindow->GetActiveView()->mCamera;
|
|
|
|
const lcVector3& Position = Camera->mPosition;
|
|
|
|
const lcVector3& Target = Camera->mTargetPosition;
|
|
|
|
const lcVector3& Up = Camera->mUpVector;
|
|
|
|
const lcModelProperties& Properties = mModels[0]->GetProperties();
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
sprintf(Line, "camera {\n sky<%1g,%1g,%1g>\n location <%1g, %1g, %1g>\n look_at <%1g, %1g, %1g>\n angle %.0f\n}\n\n",
|
|
|
|
Up[0], Up[1], Up[2], Position[1] / 25.0f, Position[0] / 25.0f, Position[2] / 25.0f, Target[1] / 25.0f, Target[0] / 25.0f, Target[2] / 25.0f, Camera->m_fovy);
|
|
|
|
POVFile.WriteLine(Line);
|
|
|
|
sprintf(Line, "background { color rgb <%1g, %1g, %1g> }\n\nlight_source { <0, 0, 20> White shadowless }\n\n",
|
|
|
|
Properties.mBackgroundSolidColor[0], Properties.mBackgroundSolidColor[1], Properties.mBackgroundSolidColor[2]);
|
|
|
|
POVFile.WriteLine(Line);
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int PartIdx = 0; PartIdx < ModelParts.GetSize(); PartIdx++)
|
|
|
|
{
|
2017-04-16 00:54:17 +02:00
|
|
|
std::pair<char[LC_PIECE_NAME_LEN], int>& Entry = PieceTable[ModelParts[PartIdx].Info];
|
2015-01-12 05:49:30 +01:00
|
|
|
int Color;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
Color = ModelParts[PartIdx].ColorIndex;
|
|
|
|
const char* Suffix = lcIsColorTranslucent(Color) ? "_clear" : "";
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
const float* f = ModelParts[PartIdx].WorldMatrix;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-04-16 00:54:17 +02:00
|
|
|
if (Entry.second & LGEO_PIECE_SLOPE)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
|
|
|
sprintf(Line, "merge {\n object {\n %s%s\n texture { %s }\n }\n"
|
|
|
|
" object {\n %s_slope\n texture { %s normal { bumps 0.3 scale 0.02 } }\n }\n"
|
|
|
|
" matrix <%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f>\n}\n",
|
2017-04-16 01:25:47 +02:00
|
|
|
Entry.first, Suffix, ColorTable[Color].Name, Entry.first, ColorTable[Color].Name,
|
2015-01-12 05:49:30 +01:00
|
|
|
-f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sprintf(Line, "object {\n %s%s\n texture { %s }\n matrix <%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f>\n}\n",
|
2017-04-16 01:25:47 +02:00
|
|
|
Entry.first, Suffix, ColorTable[Color].Name, -f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
POVFile.WriteLine(Line);
|
2014-10-13 05:43:33 +02:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
|
|
|
|
POVFile.Close();
|
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (Dialog.mRender)
|
2014-10-13 05:43:33 +02:00
|
|
|
{
|
2015-01-31 21:38:53 +01:00
|
|
|
QStringList Arguments;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
Arguments.append(QString::fromLatin1("+I%1").arg(Dialog.mFileName));
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (!Dialog.mLGEOPath.isEmpty())
|
2014-10-13 05:43:33 +02:00
|
|
|
{
|
2016-08-01 05:44:15 +02:00
|
|
|
Arguments.append(QString::fromLatin1("+L%1lg/").arg(Dialog.mLGEOPath));
|
|
|
|
Arguments.append(QString::fromLatin1("+L%1ar/").arg(Dialog.mLGEOPath));
|
2014-10-13 05:43:33 +02:00
|
|
|
}
|
|
|
|
|
2015-08-08 22:55:10 +02:00
|
|
|
Arguments.append(QString::fromLatin1("/EXIT"));
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-02-11 21:41:00 +01:00
|
|
|
#ifndef QT_NO_PROCESS
|
2016-08-01 05:44:15 +02:00
|
|
|
QProcess::execute(Dialog.mPOVRayPath, Arguments);
|
2017-02-11 21:41:00 +01:00
|
|
|
#endif
|
2014-10-13 05:43:33 +02:00
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-23 02:58:33 +01:00
|
|
|
void Project::ExportWavefront(const QString& FileName)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
|
|
|
lcArray<lcModelPartsEntry> ModelParts;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
GetModelParts(ModelParts);
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (ModelParts.IsEmpty())
|
|
|
|
{
|
2015-01-30 17:30:13 +01:00
|
|
|
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to export."));
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
QString SaveFileName = GetExportFileName(FileName, QLatin1String("obj"), tr("Export Wavefront"), tr("Wavefront Files (*.obj);;All Files (*.*)"));
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-03-27 21:39:43 +01:00
|
|
|
if (SaveFileName.isEmpty())
|
|
|
|
return;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
lcDiskFile OBJFile(SaveFileName);
|
2015-01-12 05:49:30 +01:00
|
|
|
char Line[1024];
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
if (!OBJFile.Open(QIODevice::WriteOnly))
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2015-01-30 17:30:13 +01:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(SaveFileName));
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
lcuint32 vert = 1;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
OBJFile.WriteLine("# Model exported from LeoCAD\n");
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
QFileInfo SaveInfo(SaveFileName);
|
|
|
|
QString MaterialFileName = QDir(SaveInfo.absolutePath()).absoluteFilePath(SaveInfo.completeBaseName() + QLatin1String(".mtl"));
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
sprintf(Line, "#\n\nmtllib %s\n\n", QFileInfo(MaterialFileName).fileName().toLatin1().constData());
|
|
|
|
OBJFile.WriteLine(Line);
|
|
|
|
|
|
|
|
lcDiskFile MaterialFile(MaterialFileName);
|
|
|
|
if (!MaterialFile.Open(QIODevice::WriteOnly))
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2017-05-29 22:31:24 +02:00
|
|
|
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Could not open file '%1' for writing.").arg(MaterialFileName));
|
|
|
|
return;
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-05-29 22:31:24 +02:00
|
|
|
MaterialFile.WriteLine("# Colors used by LeoCAD\n\n");
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int ColorIdx = 0; ColorIdx < gColorList.GetSize(); ColorIdx++)
|
|
|
|
{
|
|
|
|
lcColor* Color = &gColorList[ColorIdx];
|
2017-02-15 01:48:19 +01:00
|
|
|
if (Color->Translucent)
|
2017-05-29 22:31:24 +02:00
|
|
|
sprintf(Line, "newmtl %s\nKd %.2f %.2f %.2f\nD %.2f\n\n", Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2], Color->Value[3]);
|
2017-02-15 01:48:19 +01:00
|
|
|
else
|
2017-05-29 22:31:24 +02:00
|
|
|
sprintf(Line, "newmtl %s\nKd %.2f %.2f %.2f\n\n", Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]);
|
|
|
|
MaterialFile.WriteLine(Line);
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int PartIdx = 0; PartIdx < ModelParts.GetSize(); PartIdx++)
|
|
|
|
{
|
|
|
|
lcMesh* Mesh = ModelParts[PartIdx].Info->GetMesh();
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (!Mesh)
|
|
|
|
continue;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
const lcMatrix44& ModelWorld = ModelParts[PartIdx].WorldMatrix;
|
2017-02-18 20:12:35 +01:00
|
|
|
lcVertex* Verts = (lcVertex*)Mesh->mVertexData;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2017-02-18 20:12:35 +01:00
|
|
|
for (int VertexIdx = 0; VertexIdx < Mesh->mNumVertices; VertexIdx++)
|
2015-01-12 05:49:30 +01:00
|
|
|
{
|
2017-02-18 20:12:35 +01:00
|
|
|
lcVector3 Vertex = lcMul31(Verts[VertexIdx].Position, ModelWorld);
|
2015-01-12 05:49:30 +01:00
|
|
|
sprintf(Line, "v %.2f %.2f %.2f\n", Vertex[0], Vertex[1], Vertex[2]);
|
|
|
|
OBJFile.WriteLine(Line);
|
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
OBJFile.WriteLine("#\n\n");
|
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
for (int PartIdx = 0; PartIdx < ModelParts.GetSize(); PartIdx++)
|
|
|
|
{
|
|
|
|
PieceInfo* Info = ModelParts[PartIdx].Info;
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
sprintf(Line, "g Piece%.3d\n", PartIdx);
|
|
|
|
OBJFile.WriteLine(Line);
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
lcMesh* Mesh = Info->GetMesh();
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
if (Mesh)
|
|
|
|
{
|
2015-01-14 17:41:32 +01:00
|
|
|
Mesh->ExportWavefrontIndices(OBJFile, ModelParts[PartIdx].ColorIndex, vert);
|
2015-01-12 05:49:30 +01:00
|
|
|
vert += Mesh->mNumVertices;
|
2014-10-13 05:43:33 +02:00
|
|
|
}
|
|
|
|
}
|
2015-01-12 05:49:30 +01:00
|
|
|
}
|
2014-10-13 05:43:33 +02:00
|
|
|
|
2015-01-12 05:49:30 +01:00
|
|
|
void Project::SaveImage()
|
|
|
|
{
|
2016-08-01 05:44:15 +02:00
|
|
|
lcQImageDialog Dialog(gMainWindow);
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (Dialog.exec() != QDialog::Accepted)
|
2015-01-12 05:49:30 +01:00
|
|
|
return;
|
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
QString Extension = QFileInfo(Dialog.mFileName).suffix();
|
2015-01-12 05:49:30 +01:00
|
|
|
|
|
|
|
if (!Extension.isEmpty())
|
2016-08-01 05:44:15 +02:00
|
|
|
lcSetProfileString(LC_PROFILE_IMAGE_EXTENSION, Dialog.mFileName.right(Extension.length() + 1));
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2016-08-01 05:44:15 +02:00
|
|
|
if (Dialog.mStart != Dialog.mEnd)
|
|
|
|
Dialog.mFileName = Dialog.mFileName.insert(Dialog.mFileName.length() - Extension.length() - 1, QLatin1String("%1"));
|
2015-01-12 05:49:30 +01:00
|
|
|
|
2017-06-20 17:06:40 +02:00
|
|
|
mActiveModel->SaveStepImages(Dialog.mFileName, Dialog.mStart != Dialog.mEnd, false, false, Dialog.mWidth, Dialog.mHeight, Dialog.mStart, Dialog.mEnd);
|
2014-10-13 05:43:33 +02:00
|
|
|
}
|
2016-11-26 02:12:19 +01:00
|
|
|
|
|
|
|
void Project::UpdatePieceInfo(PieceInfo* Info) const
|
|
|
|
{
|
|
|
|
if (!mModels.IsEmpty())
|
|
|
|
{
|
|
|
|
lcArray<lcModel*> UpdatedModels;
|
|
|
|
mModels[0]->UpdatePieceInfo(UpdatedModels);
|
|
|
|
|
|
|
|
lcBoundingBox BoundingBox = mModels[0]->GetPieceInfo()->GetBoundingBox();
|
|
|
|
Info->SetBoundingBox(BoundingBox.Min, BoundingBox.Max);
|
|
|
|
}
|
|
|
|
}
|