Added LDD importer. Closes #29.

This commit is contained in:
Leonardo Zide 2017-06-25 18:20:34 -07:00
parent 4ef698ddf4
commit f0eb8bde96
13 changed files with 18861 additions and 0 deletions

View file

@ -45,6 +45,13 @@ lcCommand gCommands[LC_NUM_COMMANDS] =
QT_TRANSLATE_NOOP("Status", "Save a picture of the current view"),
QT_TRANSLATE_NOOP("Shortcut", "")
},
// LC_FILE_IMPORT_LDD
{
QT_TRANSLATE_NOOP("Action", "File.Import.LDD"),
QT_TRANSLATE_NOOP("Menu", "&LEGO Digital Designer..."),
QT_TRANSLATE_NOOP("Status", "Import a file in LEGO Digital Designer LXF format"),
QT_TRANSLATE_NOOP("Shortcut", "")
},
// LC_FILE_EXPORT_3DS
{
QT_TRANSLATE_NOOP("Action", "File.Export.3DS"),

View file

@ -9,6 +9,7 @@ enum lcCommandId
LC_FILE_SAVE,
LC_FILE_SAVEAS,
LC_FILE_SAVE_IMAGE,
LC_FILE_IMPORT_LDD,
LC_FILE_EXPORT_3DS,
LC_FILE_EXPORT_HTML,
LC_FILE_EXPORT_BRICKLINK,

564
common/lc_lxf.cpp Normal file
View file

@ -0,0 +1,564 @@
#include "lc_global.h"
#include "lc_lxf.h"
#include "lc_library.h"
#include "lc_application.h"
#include "lc_mainwindow.h"
#include "piece.h"
#include "lc_file.h"
// Based on https://gitlab.com/sylvainls/lxf2ldr
#define LC_READ_XML_INT(attribute, variable) \
variable = Reader.attributes().value(attribute).toInt(&Ok); \
if (!Ok) \
return false;
#define LC_READ_XML_FLOAT(attribute, variable) \
variable = Reader.attributes().value(attribute).toFloat(&Ok); \
if (!Ok) \
return false;
inline QMatrix4x4 lcLDDRotationMatrix(double ax, double ay, double az, double Angle)
{
QMatrix4x4 Matrix;
Matrix.rotate(Angle, ax, ay, az);
return Matrix;
}
struct lcLDDTransformation
{
QVector3D Translation;
QMatrix4x4 Rotation;
lcLDDTransformation(const QVector3D& vtrans, const QMatrix4x4& vrot)
: Translation(vtrans), Rotation(vrot)
{
}
lcLDDTransformation()
{
}
};
struct lcLDDSubstitute
{
QString LDrawFile;
const lcLDDTransformation* Transformation;
lcLDDSubstitute()
: Transformation(nullptr)
{
}
lcLDDSubstitute(const QString& vdatfile, const lcLDDTransformation *const vtransformation)
: LDrawFile(vdatfile), Transformation(vtransformation)
{
}
};
struct lcLDDSimpleSubstitute
{
QString LDrawFile;
bool Overwrite;
lcLDDSimpleSubstitute()
: Overwrite(false)
{
}
lcLDDSimpleSubstitute(const QString& vdatfile, bool voverwrite)
: LDrawFile(vdatfile), Overwrite(voverwrite)
{
}
};
typedef QMap<int, lcLDDSimpleSubstitute> lcLDDColorMap;
typedef QMap<QString, lcLDDSubstitute> lcLDDDecorationMap;
struct DecorMatch
{
int UseColor;
QList<lcLDDColorMap> Colors;
lcLDDDecorationMap Decorations;
DecorMatch()
: UseColor(0)
{
}
DecorMatch(int vusecolor, const QList<lcLDDColorMap>& vcolors, const lcLDDDecorationMap& vdecorations)
: UseColor(vusecolor), Colors(vcolors), Decorations(vdecorations)
{
}
};
struct lcLDDAssembly
{
int id;
QStringList Parts;
lcLDDAssembly()
{
}
lcLDDAssembly(int vid, const QStringList vparts)
: id(vid), Parts(vparts)
{
}
};
static bool lcLDDReadLDrawXML(QMap<int, int>& MaterialTable, QMap<int, QString>& BrickTable, QMap<QString, lcLDDTransformation>& TransformationTable, QMap<int, lcLDDAssembly>& AssemblyTable)
{
QString DiskFileName(lcGetPiecesLibrary()->mLibraryDir.absoluteFilePath(QLatin1String("ldraw.xml")));
QXmlStreamReader Reader;
QFile DiskFile(DiskFileName);
if (DiskFile.open(QFile::ReadOnly | QFile::Text))
Reader.setDevice(&DiskFile);
else
{
QResource Resource(QLatin1String(":/resources/ldraw.xml"));
if (Resource.isValid())
{
QByteArray Data;
if (Resource.isCompressed())
Data = qUncompress(Resource.data(), Resource.size());
else
Data = QByteArray::fromRawData((const char*)Resource.data(), Resource.size());
Reader.addData(QString::fromUtf8(Data));
}
}
if (!Reader.readNextStartElement() || Reader.name() != "LDrawMapping")
return false;
bool Ok;
while (Reader.readNextStartElement())
{
if (Reader.name() == "Material")
{
int Lego, LDraw;
LC_READ_XML_INT("lego", Lego);
LC_READ_XML_INT("ldraw", LDraw);
MaterialTable[Lego] = LDraw;
Reader.skipCurrentElement();
}
else if (Reader.name() == "Brick")
{
int Lego;
LC_READ_XML_INT("lego", Lego);
BrickTable[Lego] = Reader.attributes().value("ldraw").toString().toLower();
Reader.skipCurrentElement();
}
else if (Reader.name() == "Transformation")
{
float tx, ty, tz, ax, ay, az, Angle;
LC_READ_XML_FLOAT("tx", tx);
LC_READ_XML_FLOAT("ty", ty);
LC_READ_XML_FLOAT("tz", tz);
LC_READ_XML_FLOAT("ax", ax);
LC_READ_XML_FLOAT("ay", ay);
LC_READ_XML_FLOAT("az", az);
LC_READ_XML_FLOAT("angle", Angle);
TransformationTable[Reader.attributes().value("ldraw").toString().toLower()] = lcLDDTransformation(QVector3D(-tx, -ty, -tz), lcLDDRotationMatrix(ax, ay, az, -180.0 * Angle / M_PI));
Reader.skipCurrentElement();
}
else if (Reader.name() == "Assembly")
{
int Lego;
LC_READ_XML_INT("lego", Lego);
QStringList Parts;
while (Reader.readNextStartElement())
{
if (Reader.name() == "Part")
Parts << Reader.attributes().value("ldraw").toString().toLower();
Reader.skipCurrentElement();
}
AssemblyTable[Lego] = lcLDDAssembly(Lego, Parts);
}
else
{
Reader.skipCurrentElement();
}
}
return !Reader.hasError();
}
static QString lcLDDGetLDrawPart(int LegoID, const QMap<int, QString>& BrickTable)
{
if (BrickTable.contains(LegoID))
return BrickTable.value(LegoID);
return QString("%1.dat").arg(LegoID);
}
static const lcLDDTransformation lcLDDGetLDrawTransform(const QString& LDraw, const QMap<QString, lcLDDTransformation>& TransformationTable)
{
return TransformationTable.value(LDraw);
}
static int lcLDDGetLDrawColor(int LegoColor, const QMap<int, int>& MaterialTable)
{
if (MaterialTable.contains(LegoColor))
return MaterialTable.value(LegoColor);
return LegoColor;
}
static bool lcLDDReadDecors(QMap<int, DecorMatch>& DecorationTable)
{
lcDiskFile DiskFile(lcGetPiecesLibrary()->mLibraryDir.absoluteFilePath(QLatin1String("decors_lxf2ldr.yaml")));
lcMemFile MemFile;
lcFile* File;
if (DiskFile.Open(QIODevice::ReadOnly))
File = &DiskFile;
else
{
QResource Resource(":/resources/decors_lxf2ldr.yaml");
if (Resource.isValid())
{
QByteArray Data;
if (Resource.isCompressed())
Data = qUncompress(Resource.data(), Resource.size());
else
Data = QByteArray::fromRawData((const char*)Resource.data(), Resource.size());
MemFile.WriteBuffer(Data.constData(), Data.size());
MemFile.Seek(0, SEEK_SET);
File = &MemFile;
}
}
char Line[1024];
int CurrentLegoId = -1;
bool ParsingColors = true;
int UseColor = 0;
QList<lcLDDColorMap> Colors;
lcLDDDecorationMap Decorations;
auto AddDecor = [&]()
{
if (CurrentLegoId == -1)
return;
DecorationTable[CurrentLegoId] = DecorMatch(UseColor, Colors, Decorations);
UseColor = 0;
Colors.clear();
Decorations.clear();
CurrentLegoId = -1;
};
while (File->ReadLine(Line, sizeof(Line)))
{
char* Comment = strchr(Line, '#');
if (Comment)
*Comment = 0;
if (*Line == 0)
{
AddDecor();
continue;
}
int Indent = 0;
for (char* c = Line; *c == ' '; c++)
Indent++;
if (Indent == 0)
{
AddDecor();
if (!strchr(Line, ':'))
continue;
CurrentLegoId = atoi(Line);
}
else if (Indent == 2)
{
char* Separator = strchr(Line, ':');
if (!Separator)
continue;
*Separator = 0;
Separator++;
if (!strcmp(Line + 2, "usecolor"))
UseColor = atoi(Separator);
else if (!strcmp(Line + 2, "colors"))
ParsingColors = true;
else if (!strcmp(Line + 2, "decorations"))
ParsingColors = false;
}
else if (Indent == 4)
{
if (ParsingColors)
{
if (Line[4] == '-')
Colors << lcLDDColorMap();
}
else
{
static const QMap<QString, QMatrix4x4> RotationTable =
{
{ "x", lcLDDRotationMatrix(1, 0, 0, 90) },
{ "xx", lcLDDRotationMatrix(1, 0, 0, 180) },
{ "xxx", lcLDDRotationMatrix(1, 0, 0, -90) },
{ "y", lcLDDRotationMatrix(0, 1, 0, 90) },
{ "yy", lcLDDRotationMatrix(0, 1, 0, 180) },
{ "yyy", lcLDDRotationMatrix(0, 1, 0, -90) },
{ "y7p/6", lcLDDRotationMatrix(0, 1, 0, 210) },
{ "z", lcLDDRotationMatrix(0, 0, 1, 90) },
{ "zz", lcLDDRotationMatrix(0, 0, 1, 180) },
{ "zzz", lcLDDRotationMatrix(0, 0, 1, -90) },
};
char* Separator = strchr(Line, ':');
if (!Separator)
continue;
*Separator = 0;
Separator++;
char* Key = Line + 4;
if (Key[0] != '\'')
continue;
Key++;
char* Quote = strchr(Key, '\'');
if (!Quote)
continue;
*Quote = 0;
QStringList SubList = QString(Separator).trimmed().split(' ');
QString LDrawFile = SubList.first().toLower();
QString Rotation;
if (SubList.size() > 1)
Rotation = SubList.at(1);
if (RotationTable.contains(Rotation))
Decorations.insert(Key, lcLDDSubstitute(LDrawFile, new lcLDDTransformation(QVector3D(), RotationTable[Rotation])));
else
Decorations.insert(Key, lcLDDSubstitute(LDrawFile, 0));
}
}
else if (Indent == 6)
{
if (ParsingColors)
{
char* Separator = strchr(Line, ':');
if (!Separator)
continue;
*Separator = 0;
Separator++;
int LegoColor = atoi(Line + 4);
QStringList SimpleList = QString(Separator).trimmed().split(' ');
QString LDrawFile = SimpleList.first().toLower();
bool Overwrite = false;
if (SimpleList.size() > 1)
Overwrite = SimpleList.at(1).toUpper() == QStringLiteral("OW");
if (!Colors.empty())
Colors.last().insert(LegoColor, lcLDDSimpleSubstitute(LDrawFile, Overwrite));
}
}
}
return true;
}
static void lcLDDGetDecorationSubstitute(int lego, const QString& decorations, const QList<int>& colors, int& usecolor, lcLDDSubstitute& substitute, const QMap<int, DecorMatch>& DecorationTable)
{
if (!DecorationTable.contains(lego))
return;
const DecorMatch decor = DecorationTable.value(lego);
usecolor = decor.UseColor;
const int maxcol = qMin(colors.size(), decor.Colors.size());
for (int i = 0; i < maxcol; ++i)
{
const int curcol = colors.at(i) == 0 ? colors.first() : colors.at(i);
if (decor.Colors.at(i).contains(curcol))
{
const lcLDDSimpleSubstitute sub = decor.Colors.at(i).value(curcol);
if (substitute.LDrawFile.isEmpty() || sub.Overwrite)
substitute = lcLDDSubstitute(sub.LDrawFile, 0);
}
}
if (decor.Decorations.contains(decorations))
substitute = decor.Decorations.value(decorations);
}
bool lcImportLDDFile(const QString& FileData, lcArray<lcPiece*>& Pieces, lcArray<lcArray<lcPiece*>>& Groups)
{
QMap<int, int> MaterialTable;
QMap<int, QString> BrickTable;
QMap<QString, lcLDDTransformation> TransformationTable;
QMap<int, lcLDDAssembly> AssemblyTable;
if (!lcLDDReadLDrawXML(MaterialTable, BrickTable, TransformationTable, AssemblyTable))
{
QMessageBox::information(gMainWindow, QApplication::tr("Error"), QApplication::tr("Error loading conversion data from ldraw.xml."));
return false;
}
QMap<int, DecorMatch> DecorationTable;
lcLDDReadDecors(DecorationTable);
QXmlStreamReader Reader(FileData);
Reader.readNext();
if (!Reader.readNextStartElement() || Reader.name() != "LXFML")
return false;
lcPiecesLibrary* Library = lcGetPiecesLibrary();
bool Ok;
while (Reader.readNextStartElement())
{
if (Reader.name() == "Bricks")
{
while (Reader.readNextStartElement())
{
if (Reader.name() == "Brick")
{
int NumPieces = Pieces.GetSize();
while (Reader.readNextStartElement())
{
if (Reader.name() == "Part")
{
int DesignId;
LC_READ_XML_INT("designID", DesignId);
const QString Decoration = Reader.attributes().value("decoration").toString();
const QStringList ColorList = Reader.attributes().value("materials").toString().split(',');
QList<int> PartColors;
for (const QString& color_txt : ColorList)
{
PartColors << color_txt.toInt(&Ok);
if (!Ok)
return false;
}
QStringList Bones;
while (Reader.readNextStartElement())
{
if (Reader.name() == "Bone")
Bones << Reader.attributes().value("transformation").toString();
Reader.skipCurrentElement();
}
if (Bones.isEmpty())
return false;
QList<QVector3D> Positions;
QList<QMatrix4x4> Rotations;
for (const QString& Bone : Bones)
{
const QStringList FloatList = Bone.split(',');
if (FloatList.size() != 12)
return false;
float Matrix[12];
for (int i = 0; i < FloatList.size(); ++i)
{
Matrix[i] = FloatList.at(i).toFloat(&Ok);
if (!Ok)
return false;
}
Positions << QVector3D(Matrix[9], Matrix[10], Matrix[11]);
QMatrix4x4 Rotation(Matrix[0], Matrix[3], Matrix[6], 0, Matrix[1], Matrix[4], Matrix[7], 0, Matrix[2], Matrix[5], Matrix[8], 0, 0, 0, 0, 1);
Rotation.optimize();
Rotations << Rotation;
}
QString PartID = lcLDDGetLDrawPart(DesignId, BrickTable);
const lcLDDTransformation LDrawTransform = lcLDDGetLDrawTransform(PartID, TransformationTable);
int UseColor = 0;
lcLDDSubstitute Substitute;
lcLDDGetDecorationSubstitute(DesignId, Decoration, PartColors, UseColor, Substitute, DecorationTable);
if (!Substitute.LDrawFile.isEmpty())
PartID = Substitute.LDrawFile;
QList<int> LDrawColors;
int ColorCode = lcLDDGetLDrawColor(PartColors.first(), MaterialTable);
for (int Color : PartColors)
LDrawColors << (Color ? lcLDDGetLDrawColor(Color, MaterialTable) : ColorCode);
ColorCode = LDrawColors.at(UseColor);
const lcLDDTransformation *const SubTransform = Substitute.Transformation;
const static QMatrix4x4 Axes = lcLDDRotationMatrix(1, 0, 0, 180);
const static QVector3D Zero;
const QMatrix4x4 Rot = Rotations.first() * LDrawTransform.Rotation;
const QMatrix4x4 ldr_rot = Axes * (SubTransform ? (Rot * SubTransform->Rotation) : Rot) * Axes;
const QVector3D Move = (SubTransform ? SubTransform->Translation : Zero) + LDrawTransform.Translation;
const QVector3D ldr_pos = Axes * (Positions.first() + (Rot * Move)) * 25;
lcMatrix44 LDrawMatrix(lcVector4(ldr_rot(0, 0), ldr_rot(1, 0), ldr_rot(2, 0), 0.0f), lcVector4(ldr_rot(0, 1), ldr_rot(1, 1), ldr_rot(2, 1), 0.0f),
lcVector4(ldr_rot(0, 2), ldr_rot(1, 2), ldr_rot(2, 2), 0.0f), lcVector4(ldr_pos[0], ldr_pos[1], ldr_pos[2], 1.0f));
float* Matrix = LDrawMatrix;
lcMatrix44 Transform(lcVector4(Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f),
lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f));
PartID = PartID.toUpper();
PartID.chop(4);
PieceInfo* Info = Library->FindPiece(PartID.toLatin1().constData(), nullptr, true, false);
lcPiece* Piece = new lcPiece(nullptr);
Piece->SetPieceInfo(Info, false);
Piece->Initialize(Transform, 1);
Piece->SetColorCode(ColorCode);
Pieces.Add(Piece);
}
else
{
Reader.skipCurrentElement();
}
}
if (Pieces.GetSize() != NumPieces + 1)
{
lcArray<lcPiece*> Group;
for (int PieceIdx = NumPieces; PieceIdx < Pieces.GetSize(); PieceIdx++)
Group.Add(Pieces[PieceIdx]);
Groups.Add(Group);
}
}
else
Reader.skipCurrentElement();
}
}
else if (Reader.name() == "GroupSystems")
{
// todo: read ldd groups
Reader.skipCurrentElement();
}
else
Reader.skipCurrentElement();
}
return true;
}

6
common/lc_lxf.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include "lc_array.h"
bool lcImportLDDFile(const QString& FileData, lcArray<lcPiece*>& Pieces, lcArray<lcArray<lcPiece*>>& Groups);

View file

@ -378,6 +378,8 @@ void lcMainWindow::CreateMenus()
FileMenu->addAction(mActions[LC_FILE_SAVE]);
FileMenu->addAction(mActions[LC_FILE_SAVEAS]);
FileMenu->addAction(mActions[LC_FILE_SAVE_IMAGE]);
QMenu* ImportMenu = FileMenu->addMenu(tr("&Import"));
ImportMenu->addAction(mActions[LC_FILE_IMPORT_LDD]);
QMenu* ExportMenu = FileMenu->addMenu(tr("&Export"));
ExportMenu->addAction(mActions[LC_FILE_EXPORT_3DS]);
ExportMenu->addAction(mActions[LC_FILE_EXPORT_BRICKLINK]);
@ -1922,6 +1924,26 @@ void lcMainWindow::MergeProject()
}
}
void lcMainWindow::ImportLDD()
{
if (!SaveProjectIfModified())
return;
QString LoadFileName = QFileDialog::getOpenFileName(this, tr("Import"), QString(), tr("LEGO Diginal Designer Files (*.lxf);;All Files (*.*)"));
if (LoadFileName.isEmpty())
return;
Project* NewProject = new Project();
if (NewProject->ImportLDD(LoadFileName))
{
g_App->SetProject(NewProject);
UpdateAllViews();
}
else
delete NewProject;
}
bool lcMainWindow::SaveProject(const QString& FileName)
{
QString SaveFileName;
@ -2048,6 +2070,10 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId)
lcGetActiveProject()->SaveImage();
break;
case LC_FILE_IMPORT_LDD:
ImportLDD();
break;
case LC_FILE_EXPORT_3DS:
lcGetActiveProject()->Export3DStudio(QString());
break;

View file

@ -251,6 +251,7 @@ public:
void NewProject();
bool OpenProject(const QString& FileName);
void MergeProject();
void ImportLDD();
bool SaveProject(const QString& FileName);
bool SaveProjectIfModified();
bool SetModelFromFocus();

View file

@ -21,6 +21,7 @@
#include "lc_qeditgroupsdialog.h"
#include "lc_selectbycolordialog.h"
#include "lc_qutils.h"
#include "lc_lxf.h"
void lcModelProperties::LoadDefaults()
{
@ -944,6 +945,33 @@ bool lcModel::LoadBinary(lcFile* file)
return true;
}
bool lcModel::LoadLDD(const QString& FileData)
{
lcArray<lcPiece*> Pieces;
lcArray<lcArray<lcPiece*>> Groups;
if (!lcImportLDDFile(FileData, Pieces, Groups))
return false;
for (lcPiece* Piece : Pieces)
AddPiece(Piece);
for (const lcArray<lcPiece*>& Group : Groups)
{
lcGroup* NewGroup = AddGroup(tr("Group #"), nullptr);
for (lcPiece* Piece : Group)
Piece->SetGroup(NewGroup);
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
CalculateStep(mCurrentStep);
Library->WaitForLoadQueue();
Library->mBuffersDirty = true;
Library->UnloadUnusedParts();
return true;
}
void lcModel::Merge(lcModel* Other)
{
for (int PieceIdx = 0; PieceIdx < Other->mPieces.GetSize(); PieceIdx++)

View file

@ -198,6 +198,7 @@ public:
void SaveLDraw(QTextStream& Stream, bool SelectedOnly) const;
void LoadLDraw(QIODevice& Device, Project* Project);
bool LoadBinary(lcFile* File);
bool LoadLDD(const QString& FileData);
void SplitMPD(QIODevice& Device);
void Merge(lcModel* Other);

View file

@ -12,6 +12,7 @@
#include "lc_application.h"
#include "lc_profile.h"
#include "lc_file.h"
#include "lc_zipfile.h"
#include "lc_qimagedialog.h"
#include "lc_qmodellistdialog.h"
#include "lc_qpovraydialog.h"
@ -403,6 +404,53 @@ void Project::Merge(Project* Other)
mModified = true;
}
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;
}
void Project::GetModelParts(lcArray<lcModelPartsEntry>& ModelParts)
{
if (mModels.IsEmpty())

View file

@ -65,6 +65,7 @@ public:
bool Save(const QString& FileName);
bool Save(QTextStream& Stream);
void Merge(Project* Other);
bool ImportLDD(const QString& FileName);
void SaveImage();
void Export3DStudio(const QString& FileName);

View file

@ -93,6 +93,8 @@
<file>resources/library.zip</file>
<file>resources/ldconfig.ldr</file>
<file>resources/minifig.ini</file>
<file>resources/ldraw.xml</file>
<file>resources/decors_lxf2ldr.yaml</file>
<file>resources/leocad_fr.qm</file>
<file>resources/leocad_pt.qm</file>
</qresource>

View file

@ -0,0 +1,699 @@
---
# Format:
# ""
# legoid:
# usecolor: <index of the color used to paint the ldr part>
# colors:
# - # where
# <lego color>: <ldr part file> [OW]
# …
# decorations:
# <lxf decorations field>: <ldr part file> [rotation]
# …
# ""
#
# decorations beat colors.
#
# OW means that color beats previous colors.
#
# <rotation> is one of x, xx, xxx, y, yy, yyy, y7p/6
# meaning resp. 90° around x, 180° around x, 270° around x, … y, … z, 210° around y.
93094:
usecolor: 1
colors:
- # handle
194: 93094.dat
1: 25866c02.dat
- # stick
10066:
colors:
- # hair
- # ears
138: 10066p01.dat # dark tan
312: 10066p02.dat # dark flesh
330: 10066p03.dat # olive green
10128:
colors:
- # body
- # trousers
268: 10128p01.dat # dark purple
138: 10128p02.dat # dark tan
- # ?
- # hair
10908:
colors:
- # chin
- # face
297: 10908p01.dat # gold
310: 10908p01.dat # gold
309: 10908p03.dat # silver
315: 10908p03.dat # silver
- # eyes
1: 10908p01.dat # white
23: 10908p02.dat OW # blue
- # eyelids
# 26: p0[1234] # black
11055:
colors:
- # flag
- # face 1
- # face 2
decorations:
'57592,55510': 2335p03.dat
11459:
usecolor: 1
colors:
- # handle
312: 11459p01.dat # dark flesh
- # plunger
13787:
colors:
- # hat
37: 13787p01.dat # bright green
- # ears
13792:
colors:
- # mask
- # glass
26: 13792p01.dat # black (with stripes)
13808:
colors:
- # handle
- # body
- # mouth
26: 13808p01.dat # black
15284:
colors:
- # hair
- # visor
22: 15284p01.dat # dark pink
15400:
usecolor: 1
colors:
- # brick
199: 15400c01.dat # dark bluish grey
- # top
15427:
colors:
- # hair
- # lock
124: 15427p02.dat # magenta
322: 15427p01.dat # magenta + medium azure
decorations:
'606285': 15427p01.dat
16000:
colors:
- # arm
- # lower arm
24: 3818p01.dat # yellow
16001:
colors:
- # arm
- # lower arm
24: 3819p01.dat # yellow
16175:
colors:
- # helmet
- # hair
192: 16175p01.dat # reddish brown
312: 16175p02.dat # medium dark flesh
16178:
colors:
- # helmet
- # hair
308: 16178p01.dat # dark brown
2634:
colors:
- # frame
- # glass
42: 2634p01.dat # trans light blue
111: 2634p02.dat # trans black
30152:
usecolor: 1
colors:
- # glass
41: 30152c02.dat # trans red
40: 30152c01.dat # trans clear
- # handle
30258:
colors:
- # sign
- # front
decorations:
'93337': 15210pc0.dat zz
30261:
colors:
- # sign
- # front
decorations:
'57818': 30261p02.dat
30287:
colors:
- # hat
- # fur
1: 30287p01.dat # white
30361:
colors:
- # cylinder
- # front
decorations:
'86700': 30361dps1.dat yy
'94274': 30361dps8.dat yy
30367:
colors:
- # dome
- # large band
- # thin band
- #
- #
- #
decorations:
'0,0,94270,94271,94272': 30367cps0.dat yy
30480:
colors:
- # head
- # eyes
3: 30480ps0.dat # light yellow
30483:
colors:
- # head
- # nose
26: 30483p01.dat # black
- #
- #
30527:
colors:
- # tube
- # stud tip
26: 30527p01.dat # black
27: 30527p02.dat # dark grey
199: 30527p03.dat # dark bluish grey
- # antistud tip
26: 30527p01.dat # black
27: 30527p02.dat # dark grey
199: 30527p03.dat # dark bluish grey
45406:
colors:
- # cockpit
- # glass
111: 45406c01.dat # trans black
40: 45406c02.dat # trans clear
42: 45406c03.dat # trans light blue
54654:
usecolor: 1
colors:
- # bottom
194: 54654c02.dat # light bluish grey
199: 54654c03.dat # dark bluish grey
106: 54654c04.dat # orange
1: 54654c06.dat # white
- # top
- # tip
1: 54654c05.dat # white
54779:
usecolor: 1
colors:
- # top
194: 54779p01.dat # light bluish grey
199: 54779p02.dat # dark bluish grey
- # bottom
54923:
colors:
- # cockpit
- # glass
111: 54923c01.dat # trans black
42: 54923c02.dat # trans light blue
57028:
colors:
- # arrow
- # head
26: 57028a.dat # black
119: 57028b.dat # lime
194: 57028c.dat # grey
199: 57028c.dat # grey
208: 57028c.dat # grey
2: 57028c.dat # grey
27: 57028c.dat # grey
24: 57028d.dat # yellow
60672:
usecolor: 1
colors:
- # arm
298: 60672c01.dat # pearlescent grey
315: 60672c01.dat # pearlescent grey
309: 60672c01.dat # pearlescent grey
179: 60672c01.dat # pearlescent grey
316: 60672c01.dat # pearlescent grey
139: 60672c02.dat # pearlescent copper
- # skin
60673:
usecolor: 1
colors:
- # arm
298: 60673c01.dat # pearlescent grey
315: 60673c01.dat # pearlescent grey
309: 60673c01.dat # pearlescent grey
179: 60673c01.dat # pearlescent grey
316: 60673c01.dat # pearlescent grey
139: 60673c02.dat # pearlescent copper
- # skin
60797:
colors:
- # door
- # glass
42: 60797c01.dat # trans light blue
111: 60797c02.dat # trans black
192: 60797c03.dat # reddish brown
61406:
colors:
- # spike
- # tip
24: 61406p01.dat # yellow
194: 61406p02.dat # light bluish grey
106: 61406p03.dat # orange
119: 61406p04.dat # lime
26: 61406p05.dat # black
62537:
colors:
- # underside
- # left side
26: 62537p02.dat # black
23: 62537p01.dat # blue
- # pompoms
23: 62537p01.dat # blue # (one)
1: 62537p02.dat # white
21: 62537p03.dat # red # (one)
- # (dont work)
- # (dont work)
- # right side
21: 62537p03.dat # red
6567:
colors:
- # windscreen
- # glass
40: 6567p01.dat # trans clear
111: 6567p02.dat # trans black
42: 6567p03.dat # trans light blue
76029:
colors:
- # window
- # glass
111: 76029c01.dat # trans black
43: 76029c02.dat # trans blue
42: 76029c03.dat # trans light blue
40: 76029c04.dat # trans clear
48: 76029c05.dat # trans green
76033:
colors:
- # window
- # glass
43: 76033c01.dat # trans dark blue
42: 76033c02.dat # trans light blue
76040:
usecolor: 1
colors:
- # base
199: 76040c05.dat # dark bluish grey
27: 76040c04.dat # dark grey
192: 76040c06.dat # reddish brown
- # top
76041:
colors:
- # door
- # glass
40: 76041c01.dat # trans clear
85947:
colors:
- #
- # eye? (dont work)
- # tongue
- # "=1 eye? (dont work)"
- #
decorations:
'0,0,0,93500': 85947p01.dat
'0,0,93849,93500': 85947p01.dat
85959:
usecolor: 1
colors:
- # flame
182: 85959p01.dat # trans orange
44: 85959p02.dat # trans yellow
48: 85959p03.dat # trans green
40: 85959p04.dat # trans clear
227: 85959p05.dat # trans bright green
126: 85959p06.dat # trans purple
- # base
- #
- #
decorations:
'87354,87355,0': 85959p01.dat
'87354,0,0': 85959p01.dat
'0,87355,0': 85959p01.dat
'0,87354,87355': 85959p01.dat
90391:
colors:
- # staff
- # blade
179: 90391p02.dat # flat silver
315: 90391p01.dat # metallic silver
309: 90391p01.dat # metallic silver
90393:
colors:
- # handle
- # mike
315: 90393p01.dat # metallic silver
309: 90393p01.dat # metallic silver
9044:
colors:
- # base
- # tap
309: 9044c02.dat # chrome
315: 9044c02.dat # chrome
2: 9044c01.dat # light grey
90462:
colors:
- # wig
- # front stud
- #
decorations:
'92597': 90462bpq1.dat
92254:
colors:
- # hair
- # hat
26: 92254p01.dat # black
124: 92254p02.dat # magenta
93549:
usecolor: 1
colors:
- # bottle
40: 93549c01.dat # trans clear
- # content
93552:
colors:
- # paintbrush
- # brush tip
28: 93552p01.dat # green
- # ring
179: 93552p01.dat # flat silver
93564:
colors:
- # guitar
- # plugs
- #
- #
decorations:
'0,600099,600385': 93564p02.dat
'0,95101,96912': 93564p01.dat
93568:
colors:
- # pie
- # cream
1: 93568p01.dat # white
24: 93568p02.dat # yellow
decorations:
'97265': 93568p01.dat
95330:
colors:
- # handle
- # head
21: 95330p01.dat # red
- # blade
315: 95330p01.dat # silver
309: 95330p01.dat # silver
179: 95330p01.dat # silver
98382:
colors:
- # bear
- # sole
- # muzzle
5: 98382p01.dat # tan
- # belly?
- #
decorations:
'0,99596,73156,0': 98382p01.dat
98388:
colors:
- # bird
- # beak
124: 98388p03.dat # magenta
21: 98388p01.dat # red
24: 98388p02.dat # yellow
- #
- #
98568:
colors:
- # sword
- # blade
227: 98568p01.dat # trans bright green
179: 98568p02.dat # flat silver
143: 98568p03.dat # trans medium blue
99013:
colors:
- # blade
- # tip
26: 99013p01.dat # black
99243:
colors:
- # mask
- # feathers tips
- # right eye + jaw
- #
decorations:
'600100,600949,600950': 99243p01.dat
2431:
decorations:
'55728': 2431pc0.dat
'58858': 2431bp52.dat
'600778': 2431p01.dat
'610633': 2431p03.dat
2528:
decorations:
'601244': 2528p30.dat
3003:
decorations:
'57239,57308': 3003pe2.dat
'607795,0': 3003px1.dat
'610491,0': 3003px0.dat
'88625,88624': 3003pe3.dat
3004:
decorations:
'610489,0,0': 3004ptc1.dat
3005:
decorations:
'56918': 3005pe1.dat
'88620': 3005pe4.dat
'88627': 3005pe4.dat
'95551': 3005pf0.dat
3010:
decorations:
'618499,0': 3010pb1.dat
3039:
decorations:
'55123': 3039pce.dat
'55132': 3039pc9.dat
'55159': 3039pca.dat
'56057': 3039pc6.dat
'56571': 3039pcb.dat
'86666': 3039pcc.dat
3040:
decorations:
'55161': 3040p06.dat
3068:
decorations:
'610486': 3068bpx1.dat
'63404': 3068bp33.dat
'73023': 3068bpt3.dat
'55124': 3068bp72.dat
'55350': 3068bp72.dat
'97353': 3068bp71.dat
3069:
decorations:
'55035': 3069bpc3.dat
'55128': 3069bps1.dat
'55347': 3069bps6.dat yy
'55506': 3069bpw2.dat
'55513': 3069bp02.dat yy
'56756': 3069bp08.dat yy
'58229': 3069bp0c.dat
'60382': 3069bpc1.dat yy
'609694': 3069bpw4.dat yy
'609700': 3069bpw3.dat yy
'609713': 3069bpc7.dat yy
'62292': 3069bp01.dat
'93074': 3069bp17.dat
'55160': 3069bp80.dat
'58812': 3069bp80.dat
3070:
decorations:
'55071': 3070bp07.dat yyy
3678:
decorations:
'0,85232': 3678bp4x.dat
3846:
decorations:
'602054': 3846p4j.dat
4150:
decorations:
'55729': 4150ps4.dat
'59487': 4150p03.dat
'87078': 4150p02.dat
4492:
decorations:
'58002,58000': 4492p02.dat
12888:
decorations:
'603963,603962': 12888p01.dat
13789:
decorations:
'605406': 13789p01.dat
'605407': 13792p01.dat
14769:
decorations:
'55729': 14769p06.dat
'606885': 14769p04.dat yy
'606886': 14769p02.dat yy
'606887': 14769p01.dat yy
'87078': 14769p07.dat
15210:
decorations:
'93337': 15210pc0.dat zz
15530:
decorations:
'606112': 15530p01.dat
19727:
decorations:
'611560': 19727p01.dat
19729:
decorations:
'610378': 19729p02.dat
'611551': 19729p01.dat
30126:
decorations:
'88247': 30126p01.dat
30372:
decorations:
'58425': 30372ps0.dat
32474:
decorations:
'606117': 32474p01.dat y
44336:
decorations:
'87833': 44336p01.dat
44341:
decorations:
'87834': 44341p01.dat
44342:
decorations:
'87835': 44342p01.dat
44343:
decorations:
'87836': 44343p01.dat
44375:
decorations:
'60488': 44375p03.dat
59231:
decorations:
'59645': 59231p4h.dat
75902:
decorations:
'604009': 75902p03.dat
85984:
decorations:
'611013,0,0': 85984p70.dat
'616009,0,0': 85984pc1.dat
90397:
decorations:
'95059': 90397p02.dat
90508:
decorations:
'92125': 90508p01.dat
91884:
decorations:
'600331': 91884p04.dat
'95056': 91884p03.dat
'99762': 91884p02.dat
93551:
decorations:
'94470': 93551p01.dat
95228:
decorations:
'97373': 95228p01.dat
98138:
decorations:
'603159': 98138p07.dat y7p/6
'610549': 98138p0c.dat
'610752': 98138p0b.dat
98372:
decorations:
'99754,99590': 98372p01.dat
98384:
decorations:
'99598': 98384p01.dat
3626:
decorations:
'55196,0,0': 3626cp01.dat
'59715,0,0': 3626bp06.dat
'606096,0,0': 3626bpq3.dat
'64901,0,0': 3626bp3r.dat
'89320,0,0': 3626cp8k.dat
'93417,0,0': 3626bp61.dat
'94062,0,0': 3626bpq2.dat
'94097,0,0': 3626bpq4.dat
'99567,0,0': 3626bp05.dat
'99571,0,0': 3626cp8c.dat
'0,0,608148': 3626cp8e.dat yy
3814:
decorations:
'55059,0': 973p8z.dat
'55127,0': 973p8j.dat
'55129,0': 973p6j.dat
'55661,0': 973p8x.dat
'55662,0': 973p7q.dat
'55663,0': 973p7u.dat
'55663,93077': 973p7n.dat
'55665,0': 973p7v.dat
'56288,0': 973p1r.dat
'56289,0': 973p7h.dat
'56753,0': 973p8g.dat
'57056,0': 973p7z.dat
'57497,0': 973p2d.dat
'60153,0': 973p1j.dat
'606424,0': 973p8i.dat
'608149,0': 973p91.dat
'610390,0': 973p90.dat
'612774,612775': 973p8s.dat
'612842,612843': 973p8r.dat
'612844,612845': 973p8q.dat
'88183,0': 973pc11.dat
'89174,0': 973ps1.dat
'89309,89310': 973p7w.dat
'89311,89312': 973p7j.dat
'91323,0': 973pc23.dat
'92100,0': 973pr5.dat
'93200,0': 973p7t.dat
'93319,93338': 973p7m.dat
'93429,0': 973pc3g.dat
'96692,97941': 973p7y.dat
'99158,99157': 973p8k.dat
'99672,0': 973pc67.dat
3815:
decorations:
'606647,0': 3815p8i.dat
'95112,0': 3815pc44.dat
'99713,0': 3815pc67.dat
3816:
decorations:
'73244,0,0': 3816pba.dat
'95030,0,0': 3816pc44.dat
'99716,605096,0': 3816pc67.dat
'99716,99717,0': 3816pc67.dat
'0,602300,0': 3816paw.dat
3817:
decorations:
'606649,0,0': 3817p8h.dat
'95031,0,0': 3817pc44.dat
'99718,605097,0': 3817pc67.dat
'99718,99719,0': 3817pc67.dat
'0,602298,0': 3817paw.dat

17477
resources/ldraw.xml Normal file

File diff suppressed because it is too large Load diff