mirror of
https://github.com/leozide/leocad
synced 2024-12-27 21:58:37 +01:00
2712 lines
70 KiB
C++
2712 lines
70 KiB
C++
#include "lc_global.h"
|
|
#include "lc_model.h"
|
|
#include "piece.h"
|
|
#include "camera.h"
|
|
#include "light.h"
|
|
#include "group.h"
|
|
#include "lc_mainwindow.h"
|
|
#include "lc_profile.h"
|
|
#include "lc_library.h"
|
|
#include "lc_texture.h"
|
|
#include "pieceinf.h"
|
|
#include "view.h"
|
|
#include "preview.h"
|
|
|
|
void lcModelProperties::LoadDefaults()
|
|
{
|
|
mAuthor = lcGetProfileString(LC_PROFILE_DEFAULT_AUTHOR_NAME);
|
|
|
|
mBackgroundType = (lcBackgroundType)lcGetProfileInt(LC_PROFILE_DEFAULT_BACKGROUND_TYPE);
|
|
mBackgroundSolidColor = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_BACKGROUND_COLOR));
|
|
mBackgroundGradientColor1 = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_GRADIENT_COLOR1));
|
|
mBackgroundGradientColor2 = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_GRADIENT_COLOR2));
|
|
mBackgroundImage = lcGetProfileString(LC_PROFILE_DEFAULT_BACKGROUND_TEXTURE);
|
|
mBackgroundImageTile = lcGetProfileInt(LC_PROFILE_DEFAULT_BACKGROUND_TILE);
|
|
|
|
mFogEnabled = lcGetProfileInt(LC_PROFILE_DEFAULT_FOG_ENABLED);
|
|
mFogDensity = lcGetProfileFloat(LC_PROFILE_DEFAULT_FOG_DENSITY);
|
|
mFogColor = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_FOG_COLOR));
|
|
mAmbientColor = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR));
|
|
}
|
|
|
|
void lcModelProperties::SaveDefaults()
|
|
{
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_BACKGROUND_TYPE, mBackgroundType);
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_BACKGROUND_COLOR, lcColorFromVector3(mBackgroundSolidColor));
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_GRADIENT_COLOR1, lcColorFromVector3(mBackgroundGradientColor1));
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_GRADIENT_COLOR2, lcColorFromVector3(mBackgroundGradientColor2));
|
|
lcSetProfileString(LC_PROFILE_DEFAULT_BACKGROUND_TEXTURE, mBackgroundImage);
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_BACKGROUND_TILE, mBackgroundImageTile);
|
|
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_FOG_ENABLED, mFogEnabled);
|
|
lcSetProfileFloat(LC_PROFILE_DEFAULT_FOG_DENSITY, mFogDensity);
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_FOG_COLOR, lcColorFromVector3(mFogColor));
|
|
lcSetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR, lcColorFromVector3(mAmbientColor));
|
|
}
|
|
|
|
void lcModelProperties::SaveLDraw(QTextStream& Stream) const
|
|
{
|
|
QLatin1String LineEnding("\r\n");
|
|
|
|
if (!mName.isEmpty())
|
|
Stream << QLatin1String("0 !LEOCAD MODEL NAME ") << mName << LineEnding;
|
|
|
|
if (!mAuthor.isEmpty())
|
|
Stream << QLatin1String("0 !LEOCAD MODEL AUTHOR ") << mAuthor << LineEnding;
|
|
|
|
if (!mDescription.isEmpty())
|
|
Stream << QLatin1String("0 !LEOCAD MODEL DESCRIPTION ") << mDescription << LineEnding;
|
|
|
|
if (!mComments.isEmpty())
|
|
{
|
|
QStringList Comments = mComments.split('\n');
|
|
foreach (const QString& Comment, Comments)
|
|
Stream << QLatin1String("0 !LEOCAD MODEL COMMENT ") << Comment << LineEnding;
|
|
}
|
|
|
|
for (int BackgroundIdx = 0; BackgroundIdx < LC_NUM_BACKGROUND_TYPES; BackgroundIdx++)
|
|
{
|
|
switch ((mBackgroundType + 1 + BackgroundIdx) % LC_NUM_BACKGROUND_TYPES)
|
|
{
|
|
case LC_BACKGROUND_SOLID:
|
|
Stream << QLatin1String("0 !LEOCAD MODEL BACKGROUND COLOR ") << mBackgroundSolidColor[0] << ' ' << mBackgroundSolidColor[1] << ' ' << mBackgroundSolidColor[2] << LineEnding;
|
|
break;
|
|
case LC_BACKGROUND_GRADIENT:
|
|
Stream << QLatin1String("0 !LEOCAD MODEL BACKGROUND GRADIENT ") << mBackgroundGradientColor1[0] << ' ' << mBackgroundGradientColor1[1] << ' ' << mBackgroundGradientColor1[2] << ' ' << mBackgroundGradientColor2[0] << ' ' << mBackgroundGradientColor2[1] << ' ' << mBackgroundGradientColor2[2] << LineEnding;
|
|
break;
|
|
case LC_BACKGROUND_IMAGE:
|
|
if (!mBackgroundImage.isEmpty())
|
|
{
|
|
Stream << QLatin1String("0 !LEOCAD MODEL BACKGROUND IMAGE ");
|
|
if (mBackgroundImageTile)
|
|
Stream << QLatin1String("TILE ");
|
|
Stream << QLatin1String("NAME ") << mBackgroundImage << LineEnding;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// bool mFogEnabled;
|
|
// float mFogDensity;
|
|
// lcVector3 mFogColor;
|
|
// lcVector3 mAmbientColor;
|
|
}
|
|
|
|
void lcModelProperties::ParseLDrawLine(QTextStream& Stream)
|
|
{
|
|
QString Token;
|
|
Stream >> Token;
|
|
|
|
if (Token == QLatin1String("NAME"))
|
|
Stream >> mName;
|
|
else if (Token == QLatin1String("AUTHOR"))
|
|
Stream >> mAuthor;
|
|
else if (Token == QLatin1String("DESCRIPTION"))
|
|
Stream >> mDescription;
|
|
else if (Token == QLatin1String("COMMENT"))
|
|
{
|
|
QString Comment;
|
|
Stream >> Comment;
|
|
if (!mComments.isEmpty())
|
|
mComments += '\n';
|
|
mComments += Comment;
|
|
}
|
|
else if (Token == QLatin1String("BACKGROUND"))
|
|
{
|
|
Stream >> Token;
|
|
|
|
if (Token == QLatin1String("COLOR"))
|
|
{
|
|
mBackgroundType = LC_BACKGROUND_SOLID;
|
|
Stream >> mBackgroundSolidColor[0] >> mBackgroundSolidColor[1] >> mBackgroundSolidColor[2];
|
|
}
|
|
else if (Token == QLatin1String("GRADIENT"))
|
|
{
|
|
mBackgroundType = LC_BACKGROUND_GRADIENT;
|
|
Stream >> mBackgroundGradientColor1[0] >> mBackgroundGradientColor1[1] >> mBackgroundGradientColor1[2] >> mBackgroundGradientColor2[0] >> mBackgroundGradientColor2[1] >> mBackgroundGradientColor2[2];
|
|
}
|
|
else if (Token == QLatin1String("IMAGE"))
|
|
{
|
|
Stream >> Token;
|
|
|
|
if (Token == QLatin1String("TILE"))
|
|
{
|
|
mBackgroundImageTile = true;
|
|
Stream >> Token;
|
|
}
|
|
|
|
if (Token == QLatin1String("NAME"))
|
|
mBackgroundImage = Stream.readLine();
|
|
}
|
|
}
|
|
}
|
|
|
|
lcModel::lcModel()
|
|
{
|
|
mBackgroundTexture = NULL;
|
|
mSavedHistory = NULL;
|
|
}
|
|
|
|
lcModel::~lcModel()
|
|
{
|
|
DeleteModel();
|
|
DeleteHistory();
|
|
}
|
|
|
|
void lcModel::DeleteHistory()
|
|
{
|
|
mUndoHistory.DeleteAll();
|
|
mRedoHistory.DeleteAll();
|
|
}
|
|
|
|
void lcModel::DeleteModel()
|
|
{
|
|
lcReleaseTexture(mBackgroundTexture);
|
|
mBackgroundTexture = NULL;
|
|
|
|
const lcArray<View*> Views = gMainWindow->GetViews();
|
|
|
|
for (int ViewIdx = 0; ViewIdx < Views.GetSize(); ViewIdx++)
|
|
{
|
|
View* View = Views[ViewIdx];
|
|
lcCamera* Camera = View->mCamera;
|
|
|
|
if (!Camera->IsSimple())
|
|
View->SetCamera(Camera, true);
|
|
}
|
|
|
|
mPieces.DeleteAll();
|
|
mCameras.DeleteAll();
|
|
mLights.DeleteAll();
|
|
mGroups.DeleteAll();
|
|
}
|
|
|
|
void lcModel::SaveLDraw(QTextStream& Stream) const
|
|
{
|
|
QLatin1String LineEnding("\r\n");
|
|
|
|
mProperties.SaveLDraw(Stream);
|
|
|
|
lcStep LastStep = GetLastStep();
|
|
if (mCurrentStep != LastStep)
|
|
Stream << QLatin1String("0 !LEOCAD MODEL CURRENT_STEP") << mCurrentStep << LineEnding;
|
|
|
|
lcArray<lcGroup*> CurrentGroups;
|
|
|
|
for (lcStep Step = 1; Step <= LastStep; Step++)
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->GetStepShow() != Step)
|
|
continue;
|
|
|
|
lcGroup* PieceGroup = Piece->GetGroup();
|
|
|
|
if (PieceGroup)
|
|
{
|
|
if (CurrentGroups.IsEmpty() || (!CurrentGroups.IsEmpty() && PieceGroup != CurrentGroups[CurrentGroups.GetSize() - 1]))
|
|
{
|
|
lcArray<lcGroup*> PieceParents;
|
|
|
|
for (lcGroup* Group = PieceGroup; Group; Group = Group->mGroup)
|
|
PieceParents.InsertAt(0, Group);
|
|
|
|
int FoundParent = -1;
|
|
|
|
while (!CurrentGroups.IsEmpty())
|
|
{
|
|
lcGroup* Group = CurrentGroups[CurrentGroups.GetSize() - 1];
|
|
int Index = PieceParents.FindIndex(Group);
|
|
|
|
if (Index == -1)
|
|
{
|
|
CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
|
|
Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
|
|
}
|
|
else
|
|
{
|
|
FoundParent = Index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int ParentIdx = FoundParent + 1; ParentIdx < PieceParents.GetSize(); ParentIdx++)
|
|
{
|
|
lcGroup* Group = PieceParents[ParentIdx];
|
|
CurrentGroups.Add(Group);
|
|
Stream << QLatin1String("0 !LEOCAD GROUP BEGIN ") << Group->m_strName << LineEnding;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (CurrentGroups.GetSize())
|
|
{
|
|
CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
|
|
Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
|
|
}
|
|
}
|
|
|
|
Piece->SaveLDraw(Stream);
|
|
}
|
|
|
|
if (Step != LastStep)
|
|
Stream << QLatin1String("0 STEP\r\n");
|
|
}
|
|
|
|
while (CurrentGroups.GetSize())
|
|
{
|
|
CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
|
|
Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
|
|
}
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
mCameras[CameraIdx]->SaveLDraw(Stream);
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
mLights[LightIdx]->SaveLDraw(Stream);
|
|
}
|
|
|
|
void lcModel::LoadLDraw(QTextStream& Stream)
|
|
{
|
|
lcPiece* Piece = NULL;
|
|
lcCamera* Camera = NULL;
|
|
lcLight* Light = NULL;
|
|
lcArray<lcGroup*> CurrentGroups;
|
|
int CurrentStep = 1;
|
|
|
|
while (!Stream.atEnd())
|
|
{
|
|
QString Line = Stream.readLine().trimmed();
|
|
QTextStream LineStream(&Line, QIODevice::ReadOnly);
|
|
|
|
QString Token;
|
|
LineStream >> Token;
|
|
|
|
if (Token == QLatin1String("0"))
|
|
{
|
|
LineStream >> Token;
|
|
|
|
if (Token == QLatin1String("STEP"))
|
|
{
|
|
CurrentStep++;
|
|
continue;
|
|
}
|
|
|
|
if (Token != QLatin1String("!LEOCAD"))
|
|
continue;
|
|
|
|
LineStream >> Token;
|
|
|
|
if (Token == QLatin1String("MODEL"))
|
|
{
|
|
// if (!strcmp(Tokens[3], "CURRENT_STEP") && Tokens[4])
|
|
// mCurrentStep = atoi(Tokens[4]);
|
|
|
|
mProperties.ParseLDrawLine(LineStream);
|
|
}
|
|
else if (Token == QLatin1String("PIECE"))
|
|
{
|
|
if (!Piece)
|
|
Piece = new lcPiece(NULL);
|
|
|
|
Piece->ParseLDrawLine(LineStream);
|
|
}
|
|
else if (Token == QLatin1String("CAMERA"))
|
|
{
|
|
if (!Camera)
|
|
Camera = new lcCamera(false);
|
|
|
|
if (Camera->ParseLDrawLine(LineStream))
|
|
{
|
|
Camera->CreateName(mCameras);
|
|
mCameras.Add(Camera);
|
|
Camera = NULL;
|
|
}
|
|
}
|
|
else if (Token == QLatin1String("LIGHT"))
|
|
{
|
|
}
|
|
else if (Token == QLatin1String("GROUP"))
|
|
{
|
|
LineStream >> Token;
|
|
|
|
if (Token == QLatin1String("BEGIN"))
|
|
{
|
|
QString Name = LineStream.readAll().trimmed();
|
|
QByteArray NameUtf = Name.toUtf8(); // todo: replace with qstring
|
|
lcGroup* Group = GetGroup(NameUtf.constData(), true);
|
|
if (!CurrentGroups.IsEmpty())
|
|
Group->mGroup = CurrentGroups[CurrentGroups.GetSize() - 1];
|
|
else
|
|
Group->mGroup = NULL;
|
|
CurrentGroups.Add(Group);
|
|
}
|
|
else if (Token == QLatin1String("END"))
|
|
{
|
|
if (!CurrentGroups.IsEmpty())
|
|
CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
else if (Token == QLatin1String("1"))
|
|
{
|
|
int ColorCode;
|
|
LineStream >> ColorCode;
|
|
|
|
float Matrix[12];
|
|
for (int TokenIdx = 0; TokenIdx < 12; TokenIdx++)
|
|
LineStream >> Matrix[TokenIdx];
|
|
|
|
lcMatrix44 IncludeTransform(lcVector4(Matrix[3], Matrix[6], Matrix[9], 0.0f), lcVector4(Matrix[4], Matrix[7], Matrix[10], 0.0f),
|
|
lcVector4(Matrix[5], Matrix[8], Matrix[11], 0.0f), lcVector4(Matrix[0], Matrix[1], Matrix[2], 1.0f));
|
|
|
|
QString File;
|
|
LineStream >> File;
|
|
|
|
QString PartID = File.toUpper();
|
|
if (PartID.endsWith(QLatin1String(".DAT")))
|
|
PartID = PartID.left(PartID.size() - 4);
|
|
|
|
if (!Piece)
|
|
Piece = new lcPiece(NULL);
|
|
|
|
if (!CurrentGroups.IsEmpty())
|
|
Piece->SetGroup(CurrentGroups[CurrentGroups.GetSize() - 1]);
|
|
|
|
PieceInfo* Info = lcGetPiecesLibrary()->FindPiece(PartID.toLatin1().constData(), false);
|
|
if (Info != NULL)
|
|
{
|
|
float* Matrix = IncludeTransform;
|
|
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(0.0f, 0.0f, 0.0f, 1.0f));
|
|
|
|
lcVector4 AxisAngle = lcMatrix44ToAxisAngle(Transform);
|
|
AxisAngle[3] *= LC_RTOD;
|
|
|
|
Piece->SetPieceInfo(Info);
|
|
Piece->Initialize(lcVector3(IncludeTransform[3].x, IncludeTransform[3].z, -IncludeTransform[3].y), AxisAngle, CurrentStep);
|
|
Piece->SetColorCode(ColorCode);
|
|
Piece->CreateName(mPieces);
|
|
mPieces.Add(Piece);
|
|
Piece = NULL;
|
|
continue;
|
|
}
|
|
|
|
// todo: mpd
|
|
// todo: load from disk
|
|
|
|
Info = lcGetPiecesLibrary()->FindPiece(PartID.toLatin1().constData(), true);
|
|
if (Info != NULL)
|
|
{
|
|
float* Matrix = IncludeTransform;
|
|
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(0.0f, 0.0f, 0.0f, 1.0f));
|
|
|
|
lcVector4 AxisAngle = lcMatrix44ToAxisAngle(Transform);
|
|
AxisAngle[3] *= LC_RTOD;
|
|
|
|
Piece->SetPieceInfo(Info);
|
|
Piece->Initialize(lcVector3(IncludeTransform[3].x, IncludeTransform[3].z, -IncludeTransform[3].y), AxisAngle, CurrentStep);
|
|
Piece->SetColorCode(ColorCode);
|
|
Piece->CreateName(mPieces);
|
|
mPieces.Add(Piece);
|
|
Piece = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
CalculateStep();
|
|
UpdateBackgroundTexture();
|
|
|
|
delete Piece;
|
|
delete Camera;
|
|
delete Light;
|
|
}
|
|
|
|
void lcModel::DrawBackground(lcContext* Context)
|
|
{
|
|
if (mProperties.mBackgroundType == LC_BACKGROUND_SOLID)
|
|
{
|
|
glClearColor(mProperties.mBackgroundSolidColor[0], mProperties.mBackgroundSolidColor[1], mProperties.mBackgroundSolidColor[2], 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
return;
|
|
}
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
float ViewWidth = (float)Context->GetViewportWidth();
|
|
float ViewHeight = (float)Context->GetViewportHeight();
|
|
|
|
Context->SetProjectionMatrix(lcMatrix44Ortho(0.0f, ViewWidth, 0.0f, ViewHeight, -1.0f, 1.0f));
|
|
Context->SetWorldViewMatrix(lcMatrix44Translation(lcVector3(0.375f, 0.375f, 0.0f)));
|
|
|
|
if (mProperties.mBackgroundType == LC_BACKGROUND_GRADIENT)
|
|
{
|
|
glShadeModel(GL_SMOOTH);
|
|
|
|
const lcVector3& Color1 = mProperties.mBackgroundGradientColor1;
|
|
const lcVector3& Color2 = mProperties.mBackgroundGradientColor2;
|
|
|
|
float Verts[] =
|
|
{
|
|
ViewWidth, ViewHeight, Color1[0], Color1[1], Color1[2], 1.0f,
|
|
0.0f, ViewHeight, Color1[0], Color1[1], Color1[2], 1.0f,
|
|
0.0f, 0.0f, Color2[0], Color2[1], Color2[2], 1.0f,
|
|
ViewWidth, 0.0f, Color2[0], Color2[1], Color2[2], 1.0f
|
|
};
|
|
|
|
glVertexPointer(2, GL_FLOAT, 6 * sizeof(float), Verts);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
glColorPointer(4, GL_FLOAT, 6 * sizeof(float), Verts + 2);
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
glShadeModel(GL_FLAT);
|
|
}
|
|
|
|
if (mProperties.mBackgroundType == LC_BACKGROUND_IMAGE)
|
|
{
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glBindTexture(GL_TEXTURE_2D, mBackgroundTexture->mTexture);
|
|
|
|
float TileWidth = 1.0f, TileHeight = 1.0f;
|
|
|
|
if (mProperties.mBackgroundImageTile)
|
|
{
|
|
TileWidth = ViewWidth / mBackgroundTexture->mWidth;
|
|
TileHeight = ViewHeight / mBackgroundTexture->mHeight;
|
|
}
|
|
|
|
float Verts[] =
|
|
{
|
|
0.0f, ViewHeight, 0.0f, 0.0f,
|
|
ViewWidth, ViewHeight, TileWidth, 0.0f,
|
|
ViewWidth, 0.0f, TileWidth, TileHeight,
|
|
0.0f, 0.0f, 0.0f, TileHeight
|
|
};
|
|
|
|
glVertexPointer(2, GL_FLOAT, 4 * sizeof(float), Verts);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), Verts + 2);
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
}
|
|
|
|
void lcModel::UpdateBackgroundTexture()
|
|
{
|
|
lcReleaseTexture(mBackgroundTexture);
|
|
mBackgroundTexture = NULL;
|
|
|
|
if (mProperties.mBackgroundType == LC_BACKGROUND_IMAGE)
|
|
{
|
|
mBackgroundTexture = lcLoadTexture(mProperties.mBackgroundImage, LC_TEXTURE_WRAPU | LC_TEXTURE_WRAPV);
|
|
|
|
if (!mBackgroundTexture)
|
|
mProperties.mBackgroundType = LC_BACKGROUND_SOLID;
|
|
}
|
|
}
|
|
|
|
void lcModel::RayTest(lcObjectRayTest& ObjectRayTest) const
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsVisible(mCurrentStep))
|
|
Piece->RayTest(ObjectRayTest);
|
|
}
|
|
|
|
if (ObjectRayTest.PiecesOnly)
|
|
return;
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
{
|
|
lcCamera* Camera = mCameras[CameraIdx];
|
|
|
|
if (Camera != ObjectRayTest.ViewCamera && Camera->IsVisible())
|
|
Camera->RayTest(ObjectRayTest);
|
|
}
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
if (mLights[LightIdx]->IsVisible())
|
|
mLights[LightIdx]->RayTest(ObjectRayTest);
|
|
}
|
|
|
|
void lcModel::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsVisible(mCurrentStep))
|
|
Piece->BoxTest(ObjectBoxTest);
|
|
}
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
{
|
|
lcCamera* Camera = mCameras[CameraIdx];
|
|
|
|
if (Camera != ObjectBoxTest.ViewCamera && Camera->IsVisible())
|
|
Camera->BoxTest(ObjectBoxTest);
|
|
}
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
if (mLights[LightIdx]->IsVisible())
|
|
mLights[LightIdx]->BoxTest(ObjectBoxTest);
|
|
}
|
|
|
|
void lcModel::SaveCheckpoint(const QString& Description)
|
|
{
|
|
lcModelHistoryEntry* ModelHistoryEntry = new lcModelHistoryEntry();
|
|
|
|
ModelHistoryEntry->Description = Description;
|
|
|
|
QTextStream Stream(&ModelHistoryEntry->File);
|
|
SaveLDraw(Stream);
|
|
|
|
mUndoHistory.InsertAt(0, ModelHistoryEntry);
|
|
mRedoHistory.DeleteAll();
|
|
|
|
gMainWindow->UpdateModified(IsModified());
|
|
gMainWindow->UpdateUndoRedo(mUndoHistory.GetSize() > 1 ? mUndoHistory[0]->Description : QString(), !mRedoHistory.IsEmpty() ? mRedoHistory[0]->Description : QString());
|
|
}
|
|
|
|
void lcModel::LoadCheckPoint(lcModelHistoryEntry* CheckPoint)
|
|
{
|
|
DeleteModel();
|
|
|
|
QTextStream Stream(CheckPoint->File, QIODevice::ReadOnly);
|
|
LoadLDraw(Stream);
|
|
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
gMainWindow->UpdateCameraMenu();
|
|
UpdateSelection();
|
|
gMainWindow->UpdateCurrentStep();
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::CalculateStep()
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
Piece->UpdatePosition(mCurrentStep);
|
|
|
|
if (Piece->IsSelected())
|
|
{
|
|
if (!Piece->IsVisible(mCurrentStep))
|
|
Piece->SetSelected(false);
|
|
else
|
|
SelectGroup(Piece->GetTopGroup(), true);
|
|
}
|
|
}
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
mCameras[CameraIdx]->UpdatePosition(mCurrentStep);
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
mLights[LightIdx]->UpdatePosition(mCurrentStep);
|
|
}
|
|
|
|
lcStep lcModel::GetLastStep() const
|
|
{
|
|
lcStep Step = 1;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
Step = lcMax(Step, mPieces[PieceIdx]->GetStepShow());
|
|
|
|
return Step;
|
|
}
|
|
|
|
lcGroup* lcModel::GetGroup(const char* Name, bool CreateIfMissing)
|
|
{
|
|
for (int GroupIdx = 0; GroupIdx < mGroups.GetSize(); GroupIdx++)
|
|
{
|
|
lcGroup* Group = mGroups[GroupIdx];
|
|
|
|
if (!strcmp(Group->m_strName, Name))
|
|
return Group;
|
|
}
|
|
|
|
if (CreateIfMissing)
|
|
{
|
|
lcGroup* Group = new lcGroup();
|
|
mGroups.Add(Group);
|
|
|
|
strncpy(Group->m_strName, Name, sizeof(Group->m_strName));
|
|
Group->m_strName[sizeof(Group->m_strName) - 1] = 0;
|
|
|
|
return Group;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void lcModel::RemoveEmptyGroups()
|
|
{
|
|
bool Removed;
|
|
|
|
do
|
|
{
|
|
Removed = false;
|
|
|
|
for (int GroupIdx = 0; GroupIdx < mGroups.GetSize();)
|
|
{
|
|
lcGroup* Group = mGroups[GroupIdx];
|
|
int Ref = 0;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
if (mPieces[PieceIdx]->GetGroup() == Group)
|
|
Ref++;
|
|
|
|
for (int ParentIdx = 0; ParentIdx < mGroups.GetSize(); ParentIdx++)
|
|
if (mGroups[ParentIdx]->mGroup == Group)
|
|
Ref++;
|
|
|
|
if (Ref > 1)
|
|
{
|
|
GroupIdx++;
|
|
continue;
|
|
}
|
|
|
|
if (Ref != 0)
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->GetGroup() == Group)
|
|
{
|
|
Piece->SetGroup(Group->mGroup);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int ParentIdx = 0; ParentIdx < mGroups.GetSize(); ParentIdx++)
|
|
{
|
|
if (mGroups[ParentIdx]->mGroup == Group)
|
|
{
|
|
mGroups[ParentIdx]->mGroup = Group->mGroup;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mGroups.RemoveIndex(GroupIdx);
|
|
delete Group;
|
|
Removed = true;
|
|
}
|
|
}
|
|
while (Removed);
|
|
}
|
|
|
|
lcVector3 lcModel::LockVector(const lcVector3& Vector) const
|
|
{
|
|
lcVector3 NewVector(Vector);
|
|
|
|
if (gMainWindow->GetLockX())
|
|
NewVector[0] = 0;
|
|
|
|
if (gMainWindow->GetLockY())
|
|
NewVector[1] = 0;
|
|
|
|
if (gMainWindow->GetLockZ())
|
|
NewVector[2] = 0;
|
|
|
|
return NewVector;
|
|
}
|
|
|
|
lcVector3 lcModel::SnapPosition(const lcVector3& Distance) const
|
|
{
|
|
lcVector3 NewDistance(Distance);
|
|
|
|
if (gMainWindow->GetMoveXYSnap())
|
|
{
|
|
float SnapXY = (float)gMainWindow->GetMoveXYSnap();
|
|
int i = (int)(NewDistance[0] / SnapXY);
|
|
float Leftover = NewDistance[0] - (SnapXY * i);
|
|
|
|
if (Leftover > SnapXY / 2)
|
|
{
|
|
Leftover -= SnapXY;
|
|
i++;
|
|
}
|
|
else if (Leftover < -SnapXY / 2)
|
|
{
|
|
Leftover += SnapXY;
|
|
i--;
|
|
}
|
|
|
|
NewDistance[0] = SnapXY * i;
|
|
|
|
i = (int)(NewDistance[1] / SnapXY);
|
|
Leftover = NewDistance[1] - (SnapXY * i);
|
|
|
|
if (Leftover > SnapXY / 2)
|
|
{
|
|
Leftover -= SnapXY;
|
|
i++;
|
|
}
|
|
else if (Leftover < -SnapXY / 2)
|
|
{
|
|
Leftover += SnapXY;
|
|
i--;
|
|
}
|
|
|
|
NewDistance[1] = SnapXY * i;
|
|
}
|
|
|
|
if (gMainWindow->GetMoveZSnap())
|
|
{
|
|
float SnapZ = (float)gMainWindow->GetMoveZSnap();
|
|
int i = (int)(NewDistance[2] / SnapZ);
|
|
float Leftover = NewDistance[2] - (SnapZ * i);
|
|
|
|
if (Leftover > SnapZ / 2)
|
|
{
|
|
Leftover -= SnapZ;
|
|
i++;
|
|
}
|
|
else if (Leftover < -SnapZ / 2)
|
|
{
|
|
Leftover += SnapZ;
|
|
i--;
|
|
}
|
|
|
|
NewDistance[2] = SnapZ * i;
|
|
}
|
|
|
|
return NewDistance;
|
|
}
|
|
|
|
lcVector3 lcModel::SnapRotation(const lcVector3& Angles) const
|
|
{
|
|
int AngleSnap = gMainWindow->GetAngleSnap();
|
|
lcVector3 NewAngles(Angles);
|
|
|
|
if (AngleSnap)
|
|
{
|
|
int Snap[3];
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
Snap[i] = (int)(Angles[i] / (float)AngleSnap);
|
|
|
|
NewAngles = lcVector3((float)(AngleSnap * Snap[0]), (float)(AngleSnap * Snap[1]), (float)(AngleSnap * Snap[2]));
|
|
}
|
|
|
|
return NewAngles;
|
|
}
|
|
|
|
lcMatrix44 lcModel::GetRelativeRotation() const
|
|
{
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
|
|
if (!Preferences.mForceGlobalTransforms)
|
|
{
|
|
lcObject* Focus = GetFocusObject();
|
|
|
|
if ((Focus != NULL) && Focus->IsPiece())
|
|
{
|
|
lcMatrix44 WorldMatrix = ((lcPiece*)Focus)->mModelWorld;
|
|
WorldMatrix.SetTranslation(lcVector3(0.0f, 0.0f, 0.0f));
|
|
return WorldMatrix;
|
|
}
|
|
}
|
|
|
|
return lcMatrix44Identity();
|
|
}
|
|
|
|
bool lcModel::RemoveSelectedObjects()
|
|
{
|
|
bool RemovedPiece = false;
|
|
bool RemovedCamera = false;
|
|
bool RemovedLight = false;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsSelected())
|
|
{
|
|
RemovedPiece = true;
|
|
mPieces.Remove(Piece);
|
|
delete Piece;
|
|
}
|
|
else
|
|
PieceIdx++;
|
|
}
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); )
|
|
{
|
|
lcCamera* Camera = mCameras[CameraIdx];
|
|
|
|
if (Camera->IsSelected())
|
|
{
|
|
const lcArray<View*> Views = gMainWindow->GetViews();
|
|
for (int ViewIdx = 0; ViewIdx < Views.GetSize(); ViewIdx++)
|
|
{
|
|
View* View = Views[ViewIdx];
|
|
|
|
if (Camera == View->mCamera)
|
|
View->SetCamera(Camera, true);
|
|
}
|
|
|
|
RemovedCamera = true;
|
|
mCameras.RemoveIndex(CameraIdx);
|
|
delete Camera;
|
|
}
|
|
else
|
|
CameraIdx++;
|
|
}
|
|
|
|
if (RemovedCamera)
|
|
gMainWindow->UpdateCameraMenu();
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); )
|
|
{
|
|
lcLight* Light = mLights[LightIdx];
|
|
|
|
if (Light->IsSelected())
|
|
{
|
|
RemovedLight = true;
|
|
mLights.RemoveIndex(LightIdx);
|
|
delete Light;
|
|
}
|
|
else
|
|
LightIdx++;
|
|
}
|
|
|
|
RemoveEmptyGroups();
|
|
|
|
return RemovedPiece || RemovedCamera || RemovedLight;
|
|
}
|
|
|
|
bool lcModel::MoveSelectedObjects(const lcVector3& PieceDistance, const lcVector3& ObjectDistance)
|
|
{
|
|
lcMatrix44 RelativeRotation = GetRelativeRotation();
|
|
bool Moved = false;
|
|
|
|
if (PieceDistance.LengthSquared() >= 0.001f)
|
|
{
|
|
lcVector3 TransformedPieceDistance = lcMul30(PieceDistance, RelativeRotation);
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsSelected())
|
|
{
|
|
Piece->Move(mCurrentStep, gMainWindow->GetAddKeys(), TransformedPieceDistance);
|
|
Piece->UpdatePosition(mCurrentStep);
|
|
Moved = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ObjectDistance.LengthSquared() >= 0.001f)
|
|
{
|
|
lcVector3 TransformedObjectDistance = lcMul30(ObjectDistance, RelativeRotation);
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
{
|
|
lcCamera* Camera = mCameras[CameraIdx];
|
|
|
|
if (Camera->IsSelected())
|
|
{
|
|
Camera->Move(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
|
|
Camera->UpdatePosition(mCurrentStep);
|
|
Moved = true;
|
|
}
|
|
}
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
{
|
|
lcLight* Light = mLights[LightIdx];
|
|
|
|
if (Light->IsSelected())
|
|
{
|
|
Light->Move(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
|
|
Light->UpdatePosition(mCurrentStep);
|
|
Moved = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Moved;
|
|
}
|
|
|
|
bool lcModel::RotateSelectedPieces(const lcVector3& Angles)
|
|
{
|
|
if (Angles.LengthSquared() < 0.001f)
|
|
return false;
|
|
|
|
float Bounds[6] = { FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };
|
|
lcPiece* Focus = NULL;
|
|
int SelectedCount = 0;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsSelected())
|
|
{
|
|
if (Piece->IsFocused())
|
|
Focus = Piece;
|
|
|
|
Piece->CompareBoundingBox(Bounds);
|
|
SelectedCount++;
|
|
}
|
|
}
|
|
|
|
lcVector3 Center;
|
|
|
|
if (Focus)
|
|
Center = Focus->mPosition;
|
|
else
|
|
Center = lcVector3((Bounds[0] + Bounds[3]) / 2.0f, (Bounds[1] + Bounds[4]) / 2.0f, (Bounds[2] + Bounds[5]) / 2.0f);
|
|
|
|
lcVector4 RotationQuaternion(0, 0, 0, 1);
|
|
lcVector4 WorldToFocusQuaternion, FocusToWorldQuaternion;
|
|
|
|
if (Angles[0] != 0.0f)
|
|
{
|
|
lcVector4 q = lcQuaternionRotationX(Angles[0] * LC_DTOR);
|
|
RotationQuaternion = lcQuaternionMultiply(q, RotationQuaternion);
|
|
}
|
|
|
|
if (Angles[1] != 0.0f)
|
|
{
|
|
lcVector4 q = lcQuaternionRotationY(Angles[1] * LC_DTOR);
|
|
RotationQuaternion = lcQuaternionMultiply(q, RotationQuaternion);
|
|
}
|
|
|
|
if (Angles[2] != 0.0f)
|
|
{
|
|
lcVector4 q = lcQuaternionRotationZ(Angles[2] * LC_DTOR);
|
|
RotationQuaternion = lcQuaternionMultiply(q, RotationQuaternion);
|
|
}
|
|
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
if (Preferences.mForceGlobalTransforms)
|
|
Focus = NULL;
|
|
|
|
if (Focus)
|
|
{
|
|
const lcVector4& Rotation = Focus->mRotation;
|
|
|
|
WorldToFocusQuaternion = lcQuaternionFromAxisAngle(lcVector4(Rotation[0], Rotation[1], Rotation[2], -Rotation[3] * LC_DTOR));
|
|
FocusToWorldQuaternion = lcQuaternionFromAxisAngle(lcVector4(Rotation[0], Rotation[1], Rotation[2], Rotation[3] * LC_DTOR));
|
|
|
|
RotationQuaternion = lcQuaternionMultiply(FocusToWorldQuaternion, RotationQuaternion);
|
|
}
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (!Piece->IsSelected())
|
|
continue;
|
|
|
|
const lcVector4& Rotation = Piece->mRotation;
|
|
lcVector3 Distance = Piece->mPosition - Center;
|
|
lcVector4 LocalToWorldQuaternion = lcQuaternionFromAxisAngle(lcVector4(Rotation[0], Rotation[1], Rotation[2], Rotation[3] * LC_DTOR));
|
|
lcVector4 NewRotation, NewLocalToWorldQuaternion;
|
|
|
|
if (Focus)
|
|
{
|
|
lcVector4 LocalToFocusQuaternion = lcQuaternionMultiply(WorldToFocusQuaternion, LocalToWorldQuaternion);
|
|
NewLocalToWorldQuaternion = lcQuaternionMultiply(RotationQuaternion, LocalToFocusQuaternion);
|
|
|
|
lcVector4 WorldToLocalQuaternion = lcQuaternionFromAxisAngle(lcVector4(Rotation[0], Rotation[1], Rotation[2], -Rotation[3] * LC_DTOR));
|
|
|
|
Distance = lcQuaternionMul(Distance, WorldToLocalQuaternion);
|
|
Distance = lcQuaternionMul(Distance, NewLocalToWorldQuaternion);
|
|
}
|
|
else
|
|
{
|
|
NewLocalToWorldQuaternion = lcQuaternionMultiply(RotationQuaternion, LocalToWorldQuaternion);
|
|
|
|
Distance = lcQuaternionMul(Distance, RotationQuaternion);
|
|
}
|
|
|
|
NewRotation = lcQuaternionToAxisAngle(NewLocalToWorldQuaternion);
|
|
NewRotation[3] *= LC_RTOD;
|
|
|
|
Piece->SetPosition(Center + Distance, mCurrentStep, gMainWindow->GetAddKeys());
|
|
Piece->SetRotation(NewRotation, mCurrentStep, gMainWindow->GetAddKeys());
|
|
Piece->UpdatePosition(mCurrentStep);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool lcModel::AnyPiecesSelected() const
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
if (mPieces[PieceIdx]->IsSelected())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool lcModel::AnyObjectsSelected() const
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
if (mPieces[PieceIdx]->IsSelected())
|
|
return true;
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
if (mCameras[CameraIdx]->IsSelected())
|
|
return true;
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
if (mLights[LightIdx]->IsSelected())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
lcObject* lcModel::GetFocusObject() const
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsFocused())
|
|
return Piece;
|
|
}
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
{
|
|
lcCamera* Camera = mCameras[CameraIdx];
|
|
|
|
if (Camera->IsFocused())
|
|
return Camera;
|
|
}
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
{
|
|
lcLight* Light = mLights[LightIdx];
|
|
|
|
if (Light->IsFocused())
|
|
return Light;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
lcVector3 lcModel::GetFocusOrSelectionCenter() const
|
|
{
|
|
lcVector3 Center;
|
|
|
|
if (GetFocusPosition(Center))
|
|
return Center;
|
|
|
|
GetSelectionCenter(Center);
|
|
|
|
return Center;
|
|
}
|
|
|
|
bool lcModel::GetFocusPosition(lcVector3& Position) const
|
|
{
|
|
lcObject* FocusObject = GetFocusObject();
|
|
|
|
if (FocusObject)
|
|
{
|
|
Position = FocusObject->GetSectionPosition(FocusObject->GetFocusSection());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Position = lcVector3(0.0f, 0.0f, 0.0f);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool lcModel::GetSelectionCenter(lcVector3& Center) const
|
|
{
|
|
float Bounds[6] = { FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX };
|
|
bool Selected = false;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsSelected())
|
|
{
|
|
Piece->CompareBoundingBox(Bounds);
|
|
Selected = true;
|
|
}
|
|
}
|
|
|
|
Center = lcVector3((Bounds[0] + Bounds[3]) * 0.5f, (Bounds[1] + Bounds[4]) * 0.5f, (Bounds[2] + Bounds[5]) * 0.5f);
|
|
|
|
return Selected;
|
|
}
|
|
|
|
bool lcModel::GetPiecesBoundingBox(float BoundingBox[6]) const
|
|
{
|
|
if (mPieces.IsEmpty())
|
|
return false;
|
|
|
|
BoundingBox[0] = FLT_MAX;
|
|
BoundingBox[1] = FLT_MAX;
|
|
BoundingBox[2] = FLT_MAX;
|
|
BoundingBox[3] = -FLT_MAX;
|
|
BoundingBox[4] = -FLT_MAX;
|
|
BoundingBox[5] = -FLT_MAX;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsVisible(mCurrentStep))
|
|
Piece->CompareBoundingBox(BoundingBox);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void lcModel::GetPartsList(lcArray<lcPartsListEntry>& PartsList) const
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->mPieceInfo->m_strDescription[0] == '~')
|
|
continue;
|
|
|
|
int UsedIdx;
|
|
|
|
for (UsedIdx = 0; UsedIdx < PartsList.GetSize(); UsedIdx++)
|
|
{
|
|
if (PartsList[UsedIdx].Info != Piece->mPieceInfo || PartsList[UsedIdx].ColorIndex != Piece->mColorIndex)
|
|
continue;
|
|
|
|
PartsList[UsedIdx].Count++;
|
|
break;
|
|
}
|
|
|
|
if (UsedIdx == PartsList.GetSize())
|
|
{
|
|
lcPartsListEntry& Entry = PartsList.Add();
|
|
|
|
Entry.Info = Piece->mPieceInfo;
|
|
Entry.ColorIndex = Piece->mColorIndex;
|
|
Entry.Count = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcModel::UpdateSelection() const
|
|
{
|
|
int Flags = 0;
|
|
int SelectedCount = 0;
|
|
lcObject* Focus = NULL;
|
|
|
|
if (mPieces.IsEmpty())
|
|
Flags |= LC_SEL_NO_PIECES;
|
|
else
|
|
{
|
|
lcGroup* pGroup = NULL;
|
|
bool first = true;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsSelected())
|
|
{
|
|
SelectedCount++;
|
|
|
|
if (Piece->IsFocused())
|
|
Focus = Piece;
|
|
|
|
Flags |= LC_SEL_PIECE | LC_SEL_SELECTED;
|
|
|
|
if (Piece->GetGroup() != NULL)
|
|
{
|
|
Flags |= LC_SEL_GROUPED;
|
|
if (Piece->IsFocused())
|
|
Flags |= LC_SEL_FOCUS_GROUPED;
|
|
}
|
|
|
|
if (first)
|
|
{
|
|
pGroup = Piece->GetGroup();
|
|
first = false;
|
|
}
|
|
else
|
|
{
|
|
if (pGroup != Piece->GetGroup())
|
|
Flags |= LC_SEL_CAN_GROUP;
|
|
else
|
|
if (pGroup == NULL)
|
|
Flags |= LC_SEL_CAN_GROUP;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Flags |= LC_SEL_UNSELECTED;
|
|
|
|
if (Piece->IsHidden())
|
|
Flags |= LC_SEL_HIDDEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
{
|
|
lcCamera* Camera = mCameras[CameraIdx];
|
|
|
|
if (Camera->IsSelected())
|
|
{
|
|
Flags |= LC_SEL_SELECTED;
|
|
SelectedCount++;
|
|
|
|
if (Camera->IsFocused())
|
|
Focus = Camera;
|
|
}
|
|
}
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
{
|
|
lcLight* Light = mLights[LightIdx];
|
|
|
|
if (Light->IsSelected())
|
|
{
|
|
Flags |= LC_SEL_SELECTED;
|
|
SelectedCount++;
|
|
|
|
if (Light->IsFocused())
|
|
Focus = Light;
|
|
}
|
|
}
|
|
|
|
gMainWindow->UpdateSelectedObjects(Flags, SelectedCount, Focus);
|
|
}
|
|
|
|
void lcModel::ClearSelection(bool UpdateInterface)
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
mPieces[PieceIdx]->SetSelected(false);
|
|
|
|
for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); CameraIdx++)
|
|
mCameras[CameraIdx]->SetSelected(false);
|
|
|
|
for (int LightIdx = 0; LightIdx < mLights.GetSize(); LightIdx++)
|
|
mLights[LightIdx]->SetSelected(false);
|
|
|
|
if (UpdateInterface)
|
|
{
|
|
UpdateSelection();
|
|
gMainWindow->UpdateAllViews();
|
|
gMainWindow->UpdateFocusObject(NULL);
|
|
}
|
|
}
|
|
|
|
void lcModel::SelectGroup(lcGroup* TopGroup, bool Select)
|
|
{
|
|
if (!TopGroup)
|
|
return;
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (!Piece->IsSelected() && Piece->IsVisible(mCurrentStep) && (Piece->GetTopGroup() == TopGroup))
|
|
Piece->SetSelected(Select);
|
|
}
|
|
}
|
|
|
|
void lcModel::FocusOrDeselectObject(const lcObjectSection& ObjectSection)
|
|
{
|
|
lcObject* FocusObject = GetFocusObject();
|
|
lcObject* Object = ObjectSection.Object;
|
|
lcuint32 Section = ObjectSection.Section;
|
|
|
|
if (Object)
|
|
{
|
|
bool WasSelected = Object->IsSelected();
|
|
|
|
if (!Object->IsFocused(Section))
|
|
{
|
|
if (FocusObject)
|
|
FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
|
|
|
|
Object->SetFocused(Section, true);
|
|
}
|
|
else
|
|
Object->SetSelected(Section, false);
|
|
|
|
bool IsSelected = Object->IsSelected();
|
|
|
|
if (Object->IsPiece() && (WasSelected != IsSelected))
|
|
SelectGroup(((lcPiece*)Object)->GetTopGroup(), IsSelected);
|
|
}
|
|
else
|
|
{
|
|
if (FocusObject)
|
|
FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
|
|
}
|
|
|
|
UpdateSelection();
|
|
gMainWindow->UpdateAllViews();
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
}
|
|
|
|
void lcModel::ClearSelectionAndSetFocus(lcObject* Object, lcuint32 Section)
|
|
{
|
|
ClearSelection(false);
|
|
|
|
if (Object)
|
|
{
|
|
Object->SetFocused(Section, true);
|
|
|
|
if (Object->IsPiece())
|
|
SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
|
|
}
|
|
|
|
UpdateSelection();
|
|
gMainWindow->UpdateAllViews();
|
|
gMainWindow->UpdateFocusObject(Object);
|
|
}
|
|
|
|
void lcModel::ClearSelectionAndSetFocus(const lcObjectSection& ObjectSection)
|
|
{
|
|
ClearSelectionAndSetFocus(ObjectSection.Object, ObjectSection.Section);
|
|
}
|
|
|
|
void lcModel::SetSelection(const lcArray<lcObjectSection>& ObjectSections)
|
|
{
|
|
ClearSelection(false);
|
|
AddToSelection(ObjectSections);
|
|
}
|
|
|
|
void lcModel::AddToSelection(const lcArray<lcObjectSection>& ObjectSections)
|
|
{
|
|
for (int ObjectIdx = 0; ObjectIdx < ObjectSections.GetSize(); ObjectIdx++)
|
|
{
|
|
lcObject* Object = ObjectSections[ObjectIdx].Object;
|
|
|
|
bool WasSelected = Object->IsSelected();
|
|
Object->SetSelected(ObjectSections[ObjectIdx].Section, true);
|
|
|
|
if (!WasSelected && Object->GetType() == LC_OBJECT_PIECE)
|
|
SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
|
|
}
|
|
|
|
UpdateSelection();
|
|
gMainWindow->UpdateAllViews();
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
}
|
|
|
|
void lcModel::FindPiece(bool FindFirst, bool SearchForward)
|
|
{
|
|
if (mPieces.IsEmpty())
|
|
return;
|
|
|
|
int StartIdx = mPieces.GetSize() - 1;
|
|
if (!FindFirst)
|
|
{
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
|
|
if (Piece->IsFocused() && Piece->IsVisible(mCurrentStep))
|
|
{
|
|
StartIdx = PieceIdx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CurrentIdx = StartIdx;
|
|
lcObject* Focus = NULL;
|
|
const lcSearchOptions& SearchOptions = gMainWindow->mSearchOptions;
|
|
|
|
for (;;)
|
|
{
|
|
if (SearchForward)
|
|
CurrentIdx++;
|
|
else
|
|
CurrentIdx--;
|
|
|
|
if (CurrentIdx < 0)
|
|
CurrentIdx = mPieces.GetSize() - 1;
|
|
else if (CurrentIdx >= mPieces.GetSize())
|
|
CurrentIdx = 0;
|
|
|
|
if (CurrentIdx == StartIdx)
|
|
break;
|
|
|
|
lcPiece* Current = mPieces[CurrentIdx];
|
|
|
|
if (!Current->IsVisible(mCurrentStep))
|
|
continue;
|
|
|
|
if ((!SearchOptions.MatchInfo || Current->mPieceInfo == SearchOptions.Info) &&
|
|
(!SearchOptions.MatchColor || Current->mColorIndex == SearchOptions.ColorIndex) &&
|
|
(!SearchOptions.MatchName || strcasestr(Current->GetName(), SearchOptions.Name)))
|
|
{
|
|
Focus = Current;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ClearSelectionAndSetFocus(Focus, LC_PIECE_SECTION_POSITION);
|
|
}
|
|
|
|
void lcModel::BeginMouseTool()
|
|
{
|
|
mMouseToolDistance = lcVector3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
void lcModel::EndMouseTool(lcTool Tool, bool Accept)
|
|
{
|
|
if (!Accept)
|
|
{
|
|
LoadCheckPoint(mUndoHistory[0]);
|
|
return;
|
|
}
|
|
|
|
switch (Tool)
|
|
{
|
|
case LC_TOOL_INSERT:
|
|
case LC_TOOL_LIGHT:
|
|
break;
|
|
|
|
case LC_TOOL_SPOTLIGHT:
|
|
SaveCheckpoint(tr("New SpotLight"));
|
|
break;
|
|
|
|
case LC_TOOL_CAMERA:
|
|
gMainWindow->UpdateCameraMenu();
|
|
SaveCheckpoint(tr("New Camera"));
|
|
break;
|
|
|
|
case LC_TOOL_SELECT:
|
|
break;
|
|
|
|
case LC_TOOL_MOVE:
|
|
SaveCheckpoint(tr("Move"));
|
|
break;
|
|
|
|
case LC_TOOL_ROTATE:
|
|
SaveCheckpoint(tr("Rotate"));
|
|
break;
|
|
|
|
case LC_TOOL_ERASER:
|
|
case LC_TOOL_PAINT:
|
|
break;
|
|
|
|
case LC_TOOL_ZOOM:
|
|
if (!gMainWindow->GetActiveView()->mCamera->IsSimple())
|
|
SaveCheckpoint(tr("Zoom"));
|
|
break;
|
|
|
|
case LC_TOOL_PAN:
|
|
if (!gMainWindow->GetActiveView()->mCamera->IsSimple())
|
|
SaveCheckpoint(tr("Pan"));
|
|
break;
|
|
|
|
case LC_TOOL_ROTATE_VIEW:
|
|
if (!gMainWindow->GetActiveView()->mCamera->IsSimple())
|
|
SaveCheckpoint(tr("Orbit"));
|
|
break;
|
|
|
|
case LC_TOOL_ROLL:
|
|
if (!gMainWindow->GetActiveView()->mCamera->IsSimple())
|
|
SaveCheckpoint(tr("Roll"));
|
|
break;
|
|
|
|
case LC_TOOL_ZOOM_REGION:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void lcModel::InsertPieceToolClicked(const lcVector3& Position, const lcVector4& Rotation)
|
|
{
|
|
lcPiece* Piece = new lcPiece(gMainWindow->mPreviewWidget->GetCurrentPiece());
|
|
Piece->Initialize(Position, Rotation, mCurrentStep);
|
|
Piece->SetColorIndex(gMainWindow->mColorIndex);
|
|
Piece->UpdatePosition(mCurrentStep);
|
|
Piece->CreateName(mPieces);
|
|
mPieces.Add(Piece);
|
|
|
|
ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION);
|
|
|
|
SaveCheckpoint(tr("Insert"));
|
|
}
|
|
|
|
void lcModel::PointLightToolClicked(const lcVector3& Position)
|
|
{
|
|
lcLight* Light = new lcLight(Position[0], Position[1], Position[2]);
|
|
Light->CreateName(mLights);
|
|
mLights.Add(Light);
|
|
|
|
ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_POSITION);
|
|
SaveCheckpoint(tr("New Light"));
|
|
}
|
|
|
|
void lcModel::BeginSpotLightTool(const lcVector3& Position, const lcVector3& Target)
|
|
{
|
|
lcLight* Light = new lcLight(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
|
|
mLights.Add(Light);
|
|
|
|
ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_TARGET);
|
|
}
|
|
|
|
void lcModel::UpdateSpotLightTool(const lcVector3& Target)
|
|
{
|
|
lcLight* Light = mLights[mLights.GetSize() - 1];
|
|
|
|
Light->Move(1, false, Target);
|
|
Light->UpdatePosition(1);
|
|
|
|
gMainWindow->UpdateFocusObject(Light);
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::BeginCameraTool(const lcVector3& Position, const lcVector3& Target)
|
|
{
|
|
lcCamera* Camera = new lcCamera(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
|
|
Camera->CreateName(mCameras);
|
|
mCameras.Add(Camera);
|
|
|
|
ClearSelectionAndSetFocus(Camera, LC_CAMERA_SECTION_TARGET);
|
|
}
|
|
|
|
void lcModel::UpdateCameraTool(const lcVector3& Target)
|
|
{
|
|
lcCamera* Camera = mCameras[mCameras.GetSize() - 1];
|
|
|
|
Camera->Move(1, false, Target);
|
|
Camera->UpdatePosition(1);
|
|
|
|
gMainWindow->UpdateFocusObject(Camera);
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::UpdateMoveTool(const lcVector3& Distance)
|
|
{
|
|
lcVector3 PieceDistance = LockVector(SnapPosition(Distance) - SnapPosition(mMouseToolDistance));
|
|
lcVector3 ObjectDistance = Distance - mMouseToolDistance;
|
|
|
|
MoveSelectedObjects(PieceDistance, ObjectDistance);
|
|
mMouseToolDistance = Distance;
|
|
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::UpdateRotateTool(const lcVector3& Angles)
|
|
{
|
|
lcVector3 Delta = LockVector(SnapRotation(Angles) - SnapRotation(mMouseToolDistance));
|
|
RotateSelectedPieces(Delta);
|
|
mMouseToolDistance = Angles;
|
|
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::EraserToolClicked(lcObject* Object)
|
|
{
|
|
if (!Object)
|
|
return;
|
|
|
|
switch (Object->GetType())
|
|
{
|
|
case LC_OBJECT_PIECE:
|
|
mPieces.Remove((lcPiece*)Object);
|
|
RemoveEmptyGroups();
|
|
break;
|
|
|
|
case LC_OBJECT_CAMERA:
|
|
{
|
|
const lcArray<View*> Views = gMainWindow->GetViews();
|
|
for (int ViewIdx = 0; ViewIdx < Views.GetSize(); ViewIdx++)
|
|
{
|
|
View* View = Views[ViewIdx];
|
|
lcCamera* Camera = View->mCamera;
|
|
|
|
if (Camera == Object)
|
|
View->SetCamera(Camera, true);
|
|
}
|
|
|
|
mCameras.Remove((lcCamera*)Object);
|
|
|
|
gMainWindow->UpdateCameraMenu();
|
|
}
|
|
break;
|
|
|
|
case LC_OBJECT_LIGHT:
|
|
mLights.Remove((lcLight*)Object);
|
|
break;
|
|
}
|
|
|
|
delete Object;
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
UpdateSelection();
|
|
gMainWindow->UpdateAllViews();
|
|
SaveCheckpoint(tr("Deleting"));
|
|
}
|
|
|
|
void lcModel::PaintToolClicked(lcObject* Object)
|
|
{
|
|
if (!Object || Object->GetType() != LC_OBJECT_PIECE)
|
|
return;
|
|
|
|
lcPiece* Piece = (lcPiece*)Object;
|
|
|
|
if (Piece->mColorIndex != gMainWindow->mColorIndex)
|
|
{
|
|
Piece->SetColorIndex(gMainWindow->mColorIndex);
|
|
|
|
SaveCheckpoint(tr("Painting"));
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
}
|
|
|
|
void lcModel::UpdateZoomTool(lcCamera* Camera, float Mouse)
|
|
{
|
|
Camera->Zoom(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
|
|
mMouseToolDistance.x = Mouse;
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::UpdatePanTool(lcCamera* Camera, float MouseX, float MouseY)
|
|
{
|
|
Camera->Pan(MouseX - mMouseToolDistance.x, MouseY - mMouseToolDistance.y, mCurrentStep, gMainWindow->GetAddKeys());
|
|
mMouseToolDistance.x = MouseX;
|
|
mMouseToolDistance.y = MouseY;
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::UpdateOrbitTool(lcCamera* Camera, float MouseX, float MouseY)
|
|
{
|
|
lcVector3 Center;
|
|
GetSelectionCenter(Center);
|
|
Camera->Orbit(MouseX - mMouseToolDistance.x, MouseY - mMouseToolDistance.y, Center, mCurrentStep, gMainWindow->GetAddKeys());
|
|
mMouseToolDistance.x = MouseX;
|
|
mMouseToolDistance.y = MouseY;
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::UpdateRollTool(lcCamera* Camera, float Mouse)
|
|
{
|
|
Camera->Roll(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
|
|
mMouseToolDistance.x = Mouse;
|
|
gMainWindow->UpdateAllViews();
|
|
}
|
|
|
|
void lcModel::ZoomRegionToolClicked(lcCamera* Camera, const lcVector3* Points, float RatioX, float RatioY)
|
|
{
|
|
Camera->ZoomRegion(Points, RatioX, RatioY, mCurrentStep, gMainWindow->GetAddKeys());
|
|
|
|
gMainWindow->UpdateFocusObject(GetFocusObject());
|
|
gMainWindow->UpdateAllViews();
|
|
|
|
if (!Camera->IsSimple())
|
|
SaveCheckpoint(tr("Zoom"));
|
|
}
|
|
|
|
void lcModel::Export3DStudio() const
|
|
{
|
|
if (mPieces.IsEmpty())
|
|
{
|
|
gMainWindow->DoMessageBox("Nothing to export.", LC_MB_OK | LC_MB_ICONINFORMATION);
|
|
return;
|
|
}
|
|
|
|
char FileName[LC_MAXPATH];
|
|
memset(FileName, 0, sizeof(FileName));
|
|
|
|
if (!gMainWindow->DoDialog(LC_DIALOG_EXPORT_3DSTUDIO, FileName))
|
|
return;
|
|
|
|
lcDiskFile File;
|
|
|
|
if (!File.Open(FileName, "wb"))
|
|
{
|
|
gMainWindow->DoMessageBox("Could not open file for writing.", LC_MB_OK | LC_MB_ICONERROR);
|
|
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);
|
|
}
|
|
|
|
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(mProperties.mAmbientColor, 3);
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mAmbientColor, 3);
|
|
|
|
File.WriteU16(0x1200); // CHK_SOLID_BGND
|
|
File.WriteU32(42);
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mBackgroundSolidColor, 3);
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mBackgroundSolidColor, 3);
|
|
|
|
File.WriteU16(0x1100); // CHK_BIT_MAP
|
|
QByteArray BackgroundImage = mProperties.mBackgroundImage.toLatin1();
|
|
File.WriteU32(6 + 1 + strlen(BackgroundImage.constData()));
|
|
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(mProperties.mBackgroundGradientColor1, 3);
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mBackgroundGradientColor1, 3);
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats((mProperties.mBackgroundGradientColor1 + mProperties.mBackgroundGradientColor2) / 2.0f, 3);
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats((mProperties.mBackgroundGradientColor1 + mProperties.mBackgroundGradientColor2) / 2.0f, 3);
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mBackgroundGradientColor2, 3);
|
|
|
|
File.WriteU16(0x0013); // CHK_LIN_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mBackgroundGradientColor2, 3);
|
|
|
|
if (mProperties.mBackgroundType == LC_BACKGROUND_GRADIENT)
|
|
{
|
|
File.WriteU16(0x1301); // LIB3DS_USE_V_GRADIENT
|
|
File.WriteU32(6);
|
|
}
|
|
else if (mProperties.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);
|
|
}
|
|
|
|
File.WriteU16(0x2200); // CHK_FOG
|
|
File.WriteU32(46);
|
|
|
|
File.WriteFloat(0.0f);
|
|
File.WriteFloat(0.0f);
|
|
File.WriteFloat(1000.0f);
|
|
File.WriteFloat(100.0f);
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mFogColor, 3);
|
|
|
|
File.WriteU16(0x2210); // CHK_FOG_BGND
|
|
File.WriteU32(6);
|
|
|
|
File.WriteU16(0x2302); // CHK_LAYER_FOG
|
|
File.WriteU32(40);
|
|
|
|
File.WriteFloat(0.0f);
|
|
File.WriteFloat(100.0f);
|
|
File.WriteFloat(50.0f);
|
|
File.WriteU32(0x00100000);
|
|
|
|
File.WriteU16(0x0010); // CHK_COLOR_F
|
|
File.WriteU32(18);
|
|
|
|
File.WriteFloats(mProperties.mFogColor, 3);
|
|
|
|
File.WriteU16(0x2300); // CHK_DISTANCE_CUE
|
|
File.WriteU32(28);
|
|
|
|
File.WriteFloat(0.0f);
|
|
File.WriteFloat(0.0f);
|
|
File.WriteFloat(1000.0f);
|
|
File.WriteFloat(100.0f);
|
|
|
|
File.WriteU16(0x2310); // CHK_DICHK_DCUE_BGNDSTANCE_CUE
|
|
File.WriteU32(6);
|
|
|
|
int NumPieces = 0;
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* piece = mPieces[PieceIdx];
|
|
PieceInfo* Info = piece->mPieceInfo;
|
|
lcMesh* Mesh = Info->mMesh;
|
|
|
|
if (Mesh->mIndexType == GL_UNSIGNED_INT)
|
|
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);
|
|
|
|
float* Verts = (float*)Mesh->mVertexBuffer.mData;
|
|
const lcMatrix44& ModelWorld = piece->mModelWorld;
|
|
|
|
for (int VertexIdx = 0; VertexIdx < Mesh->mNumVertices; VertexIdx++)
|
|
{
|
|
lcVector3 Pos(Verts[VertexIdx * 3], Verts[VertexIdx * 3 + 1], Verts[VertexIdx * 3 + 2]);
|
|
Pos = lcMul31(Pos, ModelWorld);
|
|
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;
|
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mNumSections; SectionIdx++)
|
|
{
|
|
lcMeshSection* Section = &Mesh->mSections[SectionIdx];
|
|
|
|
if (Section->PrimitiveType != GL_TRIANGLES)
|
|
continue;
|
|
|
|
NumTriangles += Section->NumIndices / 3;
|
|
}
|
|
|
|
File.WriteU16(NumTriangles);
|
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mNumSections; SectionIdx++)
|
|
{
|
|
lcMeshSection* Section = &Mesh->mSections[SectionIdx];
|
|
|
|
if (Section->PrimitiveType != GL_TRIANGLES)
|
|
continue;
|
|
|
|
lcuint16* Indices = (lcuint16*)Mesh->mIndexBuffer.mData + Section->IndexOffset / sizeof(lcuint16);
|
|
|
|
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;
|
|
|
|
for (int SectionIdx = 0; SectionIdx < Mesh->mNumSections; SectionIdx++)
|
|
{
|
|
lcMeshSection* Section = &Mesh->mSections[SectionIdx];
|
|
|
|
if (Section->PrimitiveType != GL_TRIANGLES)
|
|
continue;
|
|
|
|
int MaterialIndex = Section->ColorIndex == gDefaultColor ? piece->mColorIndex : 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 lcModel::ExportBrickLink() const
|
|
{
|
|
if (mPieces.IsEmpty())
|
|
{
|
|
gMainWindow->DoMessageBox("Nothing to export.", LC_MB_OK | LC_MB_ICONINFORMATION);
|
|
return;
|
|
}
|
|
|
|
char FileName[LC_MAXPATH];
|
|
memset(FileName, 0, sizeof(FileName));
|
|
|
|
if (!gMainWindow->DoDialog(LC_DIALOG_EXPORT_BRICKLINK, FileName))
|
|
return;
|
|
|
|
lcDiskFile BrickLinkFile;
|
|
char Line[1024];
|
|
|
|
if (!BrickLinkFile.Open(FileName, "wt"))
|
|
{
|
|
gMainWindow->DoMessageBox("Could not open file for writing.", LC_MB_OK | LC_MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
lcArray<lcPartsListEntry> PartsList;
|
|
GetPartsList(PartsList);
|
|
|
|
const char* OldLocale = setlocale(LC_NUMERIC, "C");
|
|
BrickLinkFile.WriteLine("<INVENTORY>\n");
|
|
|
|
for (int PieceIdx = 0; PieceIdx < PartsList.GetSize(); PieceIdx++)
|
|
{
|
|
BrickLinkFile.WriteLine(" <ITEM>\n");
|
|
BrickLinkFile.WriteLine(" <ITEMTYPE>P</ITEMTYPE>\n");
|
|
|
|
sprintf(Line, " <ITEMID>%s</ITEMID>\n", PartsList[PieceIdx].Info->m_strName);
|
|
BrickLinkFile.WriteLine(Line);
|
|
|
|
int Count = PartsList[PieceIdx].Count;
|
|
if (Count > 1)
|
|
{
|
|
sprintf(Line, " <MINQTY>%d</MINQTY>\n", Count);
|
|
BrickLinkFile.WriteLine(Line);
|
|
}
|
|
|
|
int Color = lcGetBrickLinkColor(PartsList[PieceIdx].ColorIndex);
|
|
if (Color)
|
|
{
|
|
sprintf(Line, " <COLOR>%d</COLOR>\n", Color);
|
|
BrickLinkFile.WriteLine(Line);
|
|
}
|
|
|
|
BrickLinkFile.WriteLine(" </ITEM>\n");
|
|
}
|
|
|
|
BrickLinkFile.WriteLine("</INVENTORY>\n");
|
|
|
|
setlocale(LC_NUMERIC, OldLocale);
|
|
}
|
|
|
|
void lcModel::ExportCSV() const
|
|
{
|
|
if (mPieces.IsEmpty())
|
|
{
|
|
gMainWindow->DoMessageBox("Nothing to export.", LC_MB_OK | LC_MB_ICONINFORMATION);
|
|
return;
|
|
}
|
|
|
|
char FileName[LC_MAXPATH];
|
|
memset(FileName, 0, sizeof(FileName));
|
|
|
|
if (!gMainWindow->DoDialog(LC_DIALOG_EXPORT_CSV, FileName))
|
|
return;
|
|
|
|
lcDiskFile CSVFile;
|
|
char Line[1024];
|
|
|
|
if (!CSVFile.Open(FileName, "wt"))
|
|
{
|
|
gMainWindow->DoMessageBox("Could not open file for writing.", LC_MB_OK | LC_MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
lcArray<lcPartsListEntry> PartsList;
|
|
GetPartsList(PartsList);
|
|
|
|
const char* OldLocale = setlocale(LC_NUMERIC, "C");
|
|
CSVFile.WriteLine("Part Name,Color,Quantity,Part ID,Color Code\n");
|
|
|
|
for (int PieceIdx = 0; PieceIdx < PartsList.GetSize(); PieceIdx++)
|
|
{
|
|
sprintf(Line, "\"%s\",\"%s\",%d,%s,%d\n", PartsList[PieceIdx].Info->m_strDescription, gColorList[PartsList[PieceIdx].ColorIndex].Name,
|
|
PartsList[PieceIdx].Count, PartsList[PieceIdx].Info->m_strName, gColorList[PartsList[PieceIdx].ColorIndex].Code);
|
|
CSVFile.WriteLine(Line);
|
|
}
|
|
|
|
setlocale(LC_NUMERIC, OldLocale);
|
|
}
|
|
|
|
void lcModel::ExportPOVRay() const
|
|
{
|
|
lcPOVRayDialogOptions Options;
|
|
|
|
memset(Options.FileName, 0, sizeof(Options.FileName));
|
|
strcpy(Options.POVRayPath, lcGetProfileString(LC_PROFILE_POVRAY_PATH));
|
|
strcpy(Options.LGEOPath, lcGetProfileString(LC_PROFILE_POVRAY_LGEO_PATH));
|
|
Options.Render = lcGetProfileInt(LC_PROFILE_POVRAY_RENDER);
|
|
|
|
if (!gMainWindow->DoDialog(LC_DIALOG_EXPORT_POVRAY, &Options))
|
|
return;
|
|
|
|
lcSetProfileString(LC_PROFILE_POVRAY_PATH, Options.POVRayPath);
|
|
lcSetProfileString(LC_PROFILE_POVRAY_LGEO_PATH, Options.LGEOPath);
|
|
lcSetProfileInt(LC_PROFILE_POVRAY_RENDER, Options.Render);
|
|
|
|
lcDiskFile POVFile;
|
|
|
|
if (!POVFile.Open(Options.FileName, "wt"))
|
|
{
|
|
gMainWindow->DoMessageBox("Could not open file for writing.", LC_MB_OK|LC_MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
char Line[1024];
|
|
|
|
lcPiecesLibrary* Library = lcGetPiecesLibrary();
|
|
char* PieceTable = new char[Library->mPieces.GetSize() * LC_PIECE_NAME_LEN];
|
|
int* PieceFlags = new int[Library->mPieces.GetSize()];
|
|
int NumColors = gColorList.GetSize();
|
|
char* ColorTable = new char[NumColors * LC_MAX_COLOR_NAME];
|
|
|
|
memset(PieceTable, 0, Library->mPieces.GetSize() * LC_PIECE_NAME_LEN);
|
|
memset(PieceFlags, 0, Library->mPieces.GetSize() * sizeof(int));
|
|
memset(ColorTable, 0, NumColors * LC_MAX_COLOR_NAME);
|
|
|
|
enum
|
|
{
|
|
LGEO_PIECE_LGEO = 0x01,
|
|
LGEO_PIECE_AR = 0x02,
|
|
LGEO_PIECE_SLOPE = 0x04
|
|
};
|
|
|
|
enum
|
|
{
|
|
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
|
|
};
|
|
|
|
char LGEOPath[LC_MAXPATH];
|
|
strcpy(LGEOPath, lcGetProfileString(LC_PROFILE_POVRAY_LGEO_PATH));
|
|
|
|
if (LGEOPath[0])
|
|
{
|
|
lcDiskFile TableFile, ColorFile;
|
|
char Filename[LC_MAXPATH];
|
|
|
|
int Length = strlen(LGEOPath);
|
|
|
|
if ((LGEOPath[Length - 1] != '\\') && (LGEOPath[Length - 1] != '/'))
|
|
strcat(LGEOPath, "/");
|
|
|
|
strcpy(Filename, LGEOPath);
|
|
strcat(Filename, "lg_elements.lst");
|
|
|
|
if (!TableFile.Open(Filename, "rt"))
|
|
{
|
|
delete[] PieceTable;
|
|
delete[] PieceFlags;
|
|
gMainWindow->DoMessageBox("Could not find LGEO files.", LC_MB_OK | LC_MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
while (TableFile.ReadLine(Line, sizeof(Line)))
|
|
{
|
|
char Src[1024], Dst[1024], Flags[1024];
|
|
|
|
if (*Line == ';')
|
|
continue;
|
|
|
|
if (sscanf(Line,"%s%s%s", Src, Dst, Flags) != 3)
|
|
continue;
|
|
|
|
strupr(Src);
|
|
|
|
PieceInfo* Info = Library->FindPiece(Src, false);
|
|
if (!Info)
|
|
continue;
|
|
|
|
int Index = Library->mPieces.FindIndex(Info);
|
|
|
|
if (strchr(Flags, 'L'))
|
|
{
|
|
PieceFlags[Index] |= LGEO_PIECE_LGEO;
|
|
sprintf(PieceTable + Index * LC_PIECE_NAME_LEN, "lg_%s", Dst);
|
|
}
|
|
|
|
if (strchr(Flags, 'A'))
|
|
{
|
|
PieceFlags[Index] |= LGEO_PIECE_AR;
|
|
sprintf(PieceTable + Index * LC_PIECE_NAME_LEN, "ar_%s", Dst);
|
|
}
|
|
|
|
if (strchr(Flags, 'S'))
|
|
PieceFlags[Index] |= LGEO_PIECE_SLOPE;
|
|
}
|
|
|
|
strcpy(Filename, LGEOPath);
|
|
strcat(Filename, "lg_colors.lst");
|
|
|
|
if (!ColorFile.Open(Filename, "rt"))
|
|
{
|
|
delete[] PieceTable;
|
|
delete[] PieceFlags;
|
|
gMainWindow->DoMessageBox("Could not find LGEO files.", LC_MB_OK | LC_MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
while (ColorFile.ReadLine(Line, sizeof(Line)))
|
|
{
|
|
char Name[1024], Flags[1024];
|
|
int Code;
|
|
|
|
if (*Line == ';')
|
|
continue;
|
|
|
|
if (sscanf(Line,"%d%s%s", &Code, Name, Flags) != 3)
|
|
continue;
|
|
|
|
int Color = lcGetColorIndex(Code);
|
|
if (Color >= NumColors)
|
|
continue;
|
|
|
|
strcpy(&ColorTable[Color * LC_MAX_COLOR_NAME], Name);
|
|
}
|
|
}
|
|
|
|
const char* OldLocale = setlocale(LC_NUMERIC, "C");
|
|
|
|
if (LGEOPath[0])
|
|
{
|
|
POVFile.WriteLine("#include \"lg_defs.inc\"\n#include \"lg_color.inc\"\n\n");
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* piece = mPieces[PieceIdx];
|
|
PieceInfo* Info = piece->mPieceInfo;
|
|
|
|
for (int CheckIdx = 0; CheckIdx < mPieces.GetSize(); CheckIdx++)
|
|
{
|
|
if (mPieces[CheckIdx]->mPieceInfo != Info)
|
|
continue;
|
|
|
|
if (CheckIdx != PieceIdx)
|
|
break;
|
|
|
|
int Index = Library->mPieces.FindIndex(Info);
|
|
|
|
if (PieceTable[Index * LC_PIECE_NAME_LEN])
|
|
{
|
|
sprintf(Line, "#include \"%s.inc\"\n", PieceTable + Index * LC_PIECE_NAME_LEN);
|
|
POVFile.WriteLine(Line);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
POVFile.WriteLine("\n");
|
|
}
|
|
else
|
|
POVFile.WriteLine("#include \"colors.inc\"\n\n");
|
|
|
|
for (int ColorIdx = 0; ColorIdx < gColorList.GetSize(); ColorIdx++)
|
|
{
|
|
lcColor* Color = &gColorList[ColorIdx];
|
|
|
|
if (lcIsColorTranslucent(ColorIdx))
|
|
{
|
|
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]);
|
|
}
|
|
else
|
|
{
|
|
sprintf(Line, "#declare lc_%s = texture { pigment { rgb <%f, %f, %f> } finish { ambient 0.1 phong 0.2 phong_size 20 } }\n",
|
|
Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]);
|
|
}
|
|
|
|
POVFile.WriteLine(Line);
|
|
|
|
if (!ColorTable[ColorIdx * LC_MAX_COLOR_NAME])
|
|
sprintf(&ColorTable[ColorIdx * LC_MAX_COLOR_NAME], "lc_%s", Color->SafeName);
|
|
}
|
|
|
|
POVFile.WriteLine("\n");
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
PieceInfo* Info = Piece->mPieceInfo;
|
|
int Index = Library->mPieces.FindIndex(Info);
|
|
|
|
if (PieceTable[Index * LC_PIECE_NAME_LEN])
|
|
continue;
|
|
|
|
char Name[LC_PIECE_NAME_LEN];
|
|
char* Ptr;
|
|
|
|
strcpy(Name, Info->m_strName);
|
|
while ((Ptr = strchr(Name, '-')))
|
|
*Ptr = '_';
|
|
|
|
sprintf(PieceTable + Index * LC_PIECE_NAME_LEN, "lc_%s", Name);
|
|
|
|
Info->mMesh->ExportPOVRay(POVFile, Name, ColorTable);
|
|
|
|
POVFile.WriteLine("}\n\n");
|
|
|
|
sprintf(Line, "#declare lc_%s_clear = lc_%s\n\n", Name, Name);
|
|
POVFile.WriteLine(Line);
|
|
}
|
|
|
|
lcCamera* Camera = gMainWindow->GetActiveView()->mCamera;
|
|
const lcVector3& Position = Camera->mPosition;
|
|
const lcVector3& Target = Camera->mTargetPosition;
|
|
const lcVector3& Up = Camera->mUpVector;
|
|
|
|
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",
|
|
mProperties.mBackgroundSolidColor[0], mProperties.mBackgroundSolidColor[1], mProperties.mBackgroundSolidColor[2]);
|
|
POVFile.WriteLine(Line);
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
int Index = Library->mPieces.FindIndex(Piece->mPieceInfo);
|
|
int Color;
|
|
|
|
Color = Piece->mColorIndex;
|
|
const char* Suffix = lcIsColorTranslucent(Color) ? "_clear" : "";
|
|
|
|
const float* f = Piece->mModelWorld;
|
|
|
|
if (PieceFlags[Index] & LGEO_PIECE_SLOPE)
|
|
{
|
|
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",
|
|
PieceTable + Index * LC_PIECE_NAME_LEN, Suffix, &ColorTable[Color * LC_MAX_COLOR_NAME], PieceTable + Index * LC_PIECE_NAME_LEN, &ColorTable[Color * LC_MAX_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);
|
|
}
|
|
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",
|
|
PieceTable + Index * LC_PIECE_NAME_LEN, Suffix, &ColorTable[Color * LC_MAX_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);
|
|
}
|
|
|
|
POVFile.WriteLine(Line);
|
|
}
|
|
|
|
delete[] PieceTable;
|
|
delete[] PieceFlags;
|
|
setlocale(LC_NUMERIC, OldLocale);
|
|
POVFile.Close();
|
|
|
|
if (Options.Render)
|
|
{
|
|
lcArray<String> Arguments;
|
|
char Argument[LC_MAXPATH + 32];
|
|
|
|
sprintf(Argument, "+I%s", Options.FileName);
|
|
Arguments.Add(Argument);
|
|
|
|
if (Options.LGEOPath[0])
|
|
{
|
|
sprintf(Argument, "+L%slg/", Options.LGEOPath);
|
|
Arguments.Add(Argument);
|
|
sprintf(Argument, "+L%sar/", Options.LGEOPath);
|
|
Arguments.Add(Argument);
|
|
}
|
|
|
|
sprintf(Argument, "+o%s", Options.FileName);
|
|
char* Slash1 = strrchr(Argument, '\\');
|
|
char* Slash2 = strrchr(Argument, '/');
|
|
if (Slash1 || Slash2)
|
|
{
|
|
if (Slash1 > Slash2)
|
|
*(Slash1 + 1) = 0;
|
|
else
|
|
*(Slash2 + 1) = 0;
|
|
|
|
Arguments.Add(Argument);
|
|
}
|
|
|
|
g_App->RunProcess(Options.POVRayPath, Arguments);
|
|
}
|
|
}
|
|
|
|
void lcModel::ExportWavefront() const
|
|
{
|
|
char FileName[LC_MAXPATH];
|
|
memset(FileName, 0, sizeof(FileName));
|
|
|
|
if (!gMainWindow->DoDialog(LC_DIALOG_EXPORT_WAVEFRONT, FileName))
|
|
return;
|
|
|
|
lcDiskFile OBJFile;
|
|
char Line[1024];
|
|
|
|
if (!OBJFile.Open(FileName, "wt"))
|
|
{
|
|
gMainWindow->DoMessageBox("Could not open file for writing.", LC_MB_OK|LC_MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
char buf[LC_MAXPATH], *ptr;
|
|
lcuint32 vert = 1;
|
|
|
|
const char* OldLocale = setlocale(LC_NUMERIC, "C");
|
|
|
|
OBJFile.WriteLine("# Model exported from LeoCAD\n");
|
|
|
|
strcpy(buf, FileName);
|
|
ptr = strrchr(buf, '.');
|
|
if (ptr)
|
|
*ptr = 0;
|
|
|
|
strcat(buf, ".mtl");
|
|
ptr = strrchr(buf, '\\');
|
|
if (ptr)
|
|
ptr++;
|
|
else
|
|
{
|
|
ptr = strrchr(buf, '/');
|
|
if (ptr)
|
|
ptr++;
|
|
else
|
|
ptr = buf;
|
|
}
|
|
|
|
sprintf(Line, "#\n\nmtllib %s\n\n", ptr);
|
|
OBJFile.WriteLine(Line);
|
|
|
|
FILE* mat = fopen(buf, "wt");
|
|
fputs("# Colors used by LeoCAD\n# You need to add transparency values\n#\n\n", mat);
|
|
for (int ColorIdx = 0; ColorIdx < gColorList.GetSize(); ColorIdx++)
|
|
{
|
|
lcColor* Color = &gColorList[ColorIdx];
|
|
fprintf(mat, "newmtl %s\nKd %.2f %.2f %.2f\n\n", Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]);
|
|
}
|
|
fclose(mat);
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
const lcMatrix44& ModelWorld = Piece->mModelWorld;
|
|
PieceInfo* pInfo = Piece->mPieceInfo;
|
|
float* Verts = (float*)pInfo->mMesh->mVertexBuffer.mData;
|
|
|
|
for (int i = 0; i < pInfo->mMesh->mNumVertices * 3; i += 3)
|
|
{
|
|
lcVector3 Vertex = lcMul31(lcVector3(Verts[i], Verts[i+1], Verts[i+2]), ModelWorld);
|
|
sprintf(Line, "v %.2f %.2f %.2f\n", Vertex[0], Vertex[1], Vertex[2]);
|
|
OBJFile.WriteLine(Line);
|
|
}
|
|
|
|
OBJFile.WriteLine("#\n\n");
|
|
}
|
|
|
|
for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
|
|
{
|
|
lcPiece* Piece = mPieces[PieceIdx];
|
|
PieceInfo* Info = Piece->mPieceInfo;
|
|
|
|
strcpy(buf, Piece->GetName());
|
|
for (unsigned int i = 0; i < strlen(buf); i++)
|
|
if ((buf[i] == '#') || (buf[i] == ' '))
|
|
buf[i] = '_';
|
|
|
|
sprintf(Line, "g %s\n", buf);
|
|
OBJFile.WriteLine(Line);
|
|
|
|
Info->mMesh->ExportWavefrontIndices(OBJFile, Piece->mColorCode, vert);
|
|
vert += Info->mMesh->mNumVertices;
|
|
}
|
|
|
|
setlocale(LC_NUMERIC, OldLocale);
|
|
}
|