mirror of
https://github.com/leozide/leocad
synced 2024-12-25 21:58:23 +01:00
1010 lines
28 KiB
C++
1010 lines
28 KiB
C++
#include "lc_global.h"
|
|
#include "lc_mesh.h"
|
|
#include "lc_colors.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "pieceinf.h"
|
|
#include "piece.h"
|
|
#include "group.h"
|
|
#include "lc_file.h"
|
|
#include "lc_application.h"
|
|
#include "lc_library.h"
|
|
#include "lc_context.h"
|
|
#include "lc_scene.h"
|
|
#include "lc_qutils.h"
|
|
#include "lc_synth.h"
|
|
|
|
constexpr float LC_PIECE_CONTROL_POINT_SIZE = 10.0f;
|
|
|
|
lcPiece::lcPiece(PieceInfo* Info)
|
|
: lcObject(lcObjectType::Piece)
|
|
{
|
|
mMesh = nullptr;
|
|
SetPieceInfo(Info, QString(), true);
|
|
mFocusedSection = LC_PIECE_SECTION_INVALID;
|
|
mColorIndex = gDefaultColor;
|
|
mColorCode = 16;
|
|
mStepShow = 1;
|
|
mStepHide = LC_STEP_MAX;
|
|
mGroup = nullptr;
|
|
mFileLine = -1;
|
|
mPivotMatrix = lcMatrix44Identity();
|
|
}
|
|
|
|
lcPiece::lcPiece(const lcPiece& Other)
|
|
: lcObject(lcObjectType::Piece)
|
|
{
|
|
mMesh = nullptr;
|
|
SetPieceInfo(Other.mPieceInfo, Other.mID, true);
|
|
mHidden = Other.mHidden;
|
|
mSelected = Other.mSelected;
|
|
mFocusedSection = LC_PIECE_SECTION_INVALID;
|
|
mColorIndex = Other.mColorIndex;
|
|
mColorCode = Other.mColorCode;
|
|
mStepShow = Other.mStepShow;
|
|
mStepHide = Other.mStepHide;
|
|
mGroup = Other.mGroup;
|
|
mFileLine = -1;
|
|
|
|
mPivotMatrix = Other.mPivotMatrix;
|
|
mPivotPointValid = Other.mPivotPointValid;
|
|
|
|
mPositionKeys = Other.mPositionKeys;
|
|
mRotationKeys = Other.mRotationKeys;
|
|
mControlPoints = Other.mControlPoints;
|
|
|
|
UpdateMesh();
|
|
}
|
|
|
|
lcPiece::~lcPiece()
|
|
{
|
|
if (mPieceInfo)
|
|
{
|
|
lcPiecesLibrary* Library = lcGetPiecesLibrary();
|
|
Library->ReleasePieceInfo(mPieceInfo);
|
|
}
|
|
|
|
delete mMesh;
|
|
}
|
|
|
|
void lcPiece::SetPieceInfo(PieceInfo* Info, const QString& ID, bool Wait)
|
|
{
|
|
lcPiecesLibrary* Library = lcGetPiecesLibrary();
|
|
|
|
mPieceInfo = Info;
|
|
if (mPieceInfo)
|
|
Library->LoadPieceInfo(mPieceInfo, Wait, true);
|
|
|
|
if (!ID.isEmpty())
|
|
mID = ID;
|
|
else if (mPieceInfo)
|
|
mID = mPieceInfo->mFileName;
|
|
else
|
|
mID.clear();
|
|
|
|
mControlPoints.RemoveAll();
|
|
delete mMesh;
|
|
mMesh = nullptr;
|
|
|
|
const lcSynthInfo* SynthInfo = mPieceInfo ? mPieceInfo->GetSynthInfo() : nullptr;
|
|
|
|
if (SynthInfo)
|
|
{
|
|
SynthInfo->GetDefaultControlPoints(mControlPoints);
|
|
UpdateMesh();
|
|
}
|
|
}
|
|
|
|
void lcPiece::UpdateID()
|
|
{
|
|
mID = mPieceInfo->mFileName;
|
|
}
|
|
|
|
void lcPiece::SaveLDraw(QTextStream& Stream) const
|
|
{
|
|
const QLatin1String LineEnding("\r\n");
|
|
|
|
if (mStepHide != LC_STEP_MAX)
|
|
Stream << QLatin1String("0 !LEOCAD PIECE STEP_HIDE ") << mStepHide << LineEnding;
|
|
|
|
if (IsHidden())
|
|
Stream << QLatin1String("0 !LEOCAD PIECE HIDDEN") << LineEnding;
|
|
|
|
if (mPivotPointValid)
|
|
{
|
|
const float* PivotMatrix = mPivotMatrix;
|
|
const float PivotNumbers[12] = { PivotMatrix[12], -PivotMatrix[14], PivotMatrix[13], PivotMatrix[0], -PivotMatrix[8], PivotMatrix[4], -PivotMatrix[2], PivotMatrix[10], -PivotMatrix[6], PivotMatrix[1], -PivotMatrix[9], PivotMatrix[5] };
|
|
|
|
Stream << QLatin1String("0 !LEOCAD PIECE PIVOT ");
|
|
|
|
for (int NumberIdx = 0; NumberIdx < 12; NumberIdx++)
|
|
Stream << ' ' << lcFormatValue(PivotNumbers[NumberIdx], NumberIdx < 3 ? 4 : 6);
|
|
|
|
Stream << LineEnding;
|
|
}
|
|
|
|
if (mPositionKeys.GetSize() > 1)
|
|
mPositionKeys.SaveKeysLDraw(Stream, "PIECE POSITION_KEY ");
|
|
|
|
if (mRotationKeys.GetSize() > 1)
|
|
mRotationKeys.SaveKeysLDraw(Stream, "PIECE ROTATION_KEY ");
|
|
|
|
Stream << "1 " << mColorCode << ' ';
|
|
|
|
const float* Matrix = mModelWorld;
|
|
const float Numbers[12] = { Matrix[12], -Matrix[14], Matrix[13], Matrix[0], -Matrix[8], Matrix[4], -Matrix[2], Matrix[10], -Matrix[6], Matrix[1], -Matrix[9], Matrix[5] };
|
|
|
|
for (int NumberIdx = 0; NumberIdx < 12; NumberIdx++)
|
|
Stream << lcFormatValue(Numbers[NumberIdx], NumberIdx < 3 ? 4 : 6) << ' ';
|
|
|
|
Stream << mID << LineEnding;
|
|
}
|
|
|
|
bool lcPiece::ParseLDrawLine(QTextStream& Stream)
|
|
{
|
|
while (!Stream.atEnd())
|
|
{
|
|
QString Token;
|
|
Stream >> Token;
|
|
|
|
if (Token == QLatin1String("STEP_HIDE"))
|
|
Stream >> mStepHide;
|
|
else if (Token == QLatin1String("HIDDEN"))
|
|
SetHidden(true);
|
|
else if (Token == QLatin1String("PIVOT"))
|
|
{
|
|
float PivotNumbers[12];
|
|
for (int TokenIdx = 0; TokenIdx < 12; TokenIdx++)
|
|
Stream >> PivotNumbers[TokenIdx];
|
|
|
|
const lcMatrix44 PivotMatrix(lcVector4( PivotNumbers[3], PivotNumbers[9], -PivotNumbers[6], 0.0f), lcVector4(PivotNumbers[5], PivotNumbers[11], -PivotNumbers[8], 0.0f),
|
|
lcVector4(-PivotNumbers[4], -PivotNumbers[10], PivotNumbers[7], 0.0f), lcVector4(PivotNumbers[0], PivotNumbers[2], -PivotNumbers[1], 1.0f));
|
|
|
|
mPivotMatrix = PivotMatrix;
|
|
mPivotPointValid = true;
|
|
}
|
|
else if (Token == QLatin1String("POSITION_KEY"))
|
|
mPositionKeys.LoadKeysLDraw(Stream);
|
|
else if (Token == QLatin1String("ROTATION_KEY"))
|
|
mRotationKeys.LoadKeysLDraw(Stream);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool lcPiece::FileLoad(lcFile& file)
|
|
{
|
|
quint8 version, ch;
|
|
|
|
version = file.ReadU8();
|
|
|
|
if (version > 12) // LeoCAD 0.80
|
|
return false;
|
|
|
|
const float PositionScale = (version < 12) ? 25.0f : 1.0f;
|
|
|
|
if (version > 8)
|
|
{
|
|
if (file.ReadU8() != 1)
|
|
return false;
|
|
|
|
quint16 time;
|
|
float param[4];
|
|
quint8 type;
|
|
quint32 n;
|
|
|
|
file.ReadU32(&n, 1);
|
|
while (n--)
|
|
{
|
|
file.ReadU16(&time, 1);
|
|
file.ReadFloats(param, 4);
|
|
file.ReadU8(&type, 1);
|
|
|
|
if (type == 0)
|
|
mPositionKeys.ChangeKey(lcVector3(param[0], param[1], param[2]) * PositionScale, time, true);
|
|
else if (type == 1)
|
|
mRotationKeys.ChangeKey(lcMatrix33FromAxisAngle(lcVector3(param[0], param[1], param[2]), param[3] * LC_DTOR), time, true);
|
|
}
|
|
|
|
file.ReadU32(&n, 1);
|
|
while (n--)
|
|
{
|
|
file.ReadU16(&time, 1);
|
|
file.ReadFloats(param, 4);
|
|
file.ReadU8(&type, 1);
|
|
}
|
|
}
|
|
|
|
if (version < 9)
|
|
{
|
|
quint16 time;
|
|
quint8 type;
|
|
|
|
if (version > 5)
|
|
{
|
|
quint32 keys;
|
|
float param[4];
|
|
|
|
file.ReadU32(&keys, 1);
|
|
while (keys--)
|
|
{
|
|
file.ReadFloats(param, 4);
|
|
file.ReadU16(&time, 1);
|
|
file.ReadU8(&type, 1);
|
|
|
|
if (type == 0)
|
|
mPositionKeys.ChangeKey(lcVector3(param[0], param[1], param[2]) * PositionScale, time, true);
|
|
else if (type == 1)
|
|
mRotationKeys.ChangeKey(lcMatrix33FromAxisAngle(lcVector3(param[0], param[1], param[2]), param[3] * LC_DTOR), time, true);
|
|
}
|
|
|
|
file.ReadU32(&keys, 1);
|
|
while (keys--)
|
|
{
|
|
file.ReadFloats(param, 4);
|
|
file.ReadU16(&time, 1);
|
|
file.ReadU8(&type, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (version > 2)
|
|
{
|
|
file.ReadU8(&ch, 1);
|
|
|
|
while (ch--)
|
|
{
|
|
lcMatrix44 ModelWorld;
|
|
|
|
if (version > 3)
|
|
{
|
|
file.ReadFloats(ModelWorld, 16);
|
|
}
|
|
else
|
|
{
|
|
lcVector3 Translation;
|
|
float Rotation[3];
|
|
file.ReadFloats(Translation, 3);
|
|
file.ReadFloats(Rotation, 3);
|
|
ModelWorld = lcMatrix44Translation(Translation);
|
|
ModelWorld = lcMul(lcMatrix44RotationZ(Rotation[2] * LC_DTOR), lcMul(lcMatrix44RotationY(Rotation[1] * LC_DTOR), lcMul(lcMatrix44RotationX(Rotation[0] * LC_DTOR), ModelWorld)));
|
|
}
|
|
|
|
quint8 b;
|
|
file.ReadU8(&b, 1);
|
|
time = b;
|
|
|
|
mPositionKeys.ChangeKey(ModelWorld.GetTranslation() * PositionScale, 1, true);
|
|
mRotationKeys.ChangeKey(lcMatrix33(ModelWorld), time, true);
|
|
|
|
qint32 bl;
|
|
file.ReadS32(&bl, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lcVector3 Translation;
|
|
float Rotation[3];
|
|
file.ReadFloats(Translation, 3);
|
|
file.ReadFloats(Rotation, 3);
|
|
lcMatrix44 ModelWorld = lcMatrix44Translation(Translation);
|
|
ModelWorld = lcMul(lcMatrix44RotationZ(Rotation[2] * LC_DTOR), lcMul(lcMatrix44RotationY(Rotation[1] * LC_DTOR), lcMul(lcMatrix44RotationX(Rotation[0] * LC_DTOR), ModelWorld)));
|
|
|
|
mPositionKeys.ChangeKey(lcVector3(ModelWorld.r[3][0], ModelWorld.r[3][1], ModelWorld.r[3][2]) * PositionScale, 1, true);
|
|
mRotationKeys.ChangeKey(lcMatrix33(ModelWorld), 1, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Common to all versions.
|
|
char name[LC_PIECE_NAME_LEN];
|
|
if (version < 10)
|
|
{
|
|
memset(name, 0, LC_PIECE_NAME_LEN);
|
|
file.ReadBuffer(name, 9);
|
|
}
|
|
else
|
|
file.ReadBuffer(name, LC_PIECE_NAME_LEN);
|
|
strcat(name, ".dat");
|
|
|
|
PieceInfo* pInfo = lcGetPiecesLibrary()->FindPiece(name, nullptr, true, false);
|
|
SetPieceInfo(pInfo, QString(), true);
|
|
|
|
// 11 (0.77)
|
|
if (version < 11)
|
|
{
|
|
quint8 Color;
|
|
|
|
file.ReadU8(&Color, 1);
|
|
|
|
if (version < 5)
|
|
mColorCode = lcGetColorCodeFromOriginalColor(Color);
|
|
else
|
|
mColorCode = lcGetColorCodeFromExtendedColor(Color);
|
|
}
|
|
else
|
|
file.ReadU32(&mColorCode, 1);
|
|
mColorIndex = lcGetColorIndex(mColorCode);
|
|
|
|
quint8 Step;
|
|
file.ReadU8(&Step, 1);
|
|
mStepShow = Step;
|
|
if (version > 1)
|
|
{
|
|
file.ReadU8(&Step, 1);
|
|
mStepHide = Step == 255 ? LC_STEP_MAX : Step;
|
|
}
|
|
else
|
|
mStepHide = LC_STEP_MAX;
|
|
|
|
if (version > 5)
|
|
{
|
|
file.ReadU16(); // m_nFrameShow
|
|
file.ReadU16(); // m_nFrameHide
|
|
|
|
if (version > 7)
|
|
{
|
|
quint8 Hidden;
|
|
file.ReadU8(&Hidden, 1);
|
|
if (Hidden & 1)
|
|
mHidden = true;
|
|
file.ReadU8(&ch, 1);
|
|
file.Seek(ch, SEEK_CUR);
|
|
}
|
|
else
|
|
{
|
|
qint32 hide;
|
|
file.ReadS32(&hide, 1);
|
|
if (hide != 0)
|
|
mHidden = true;
|
|
file.Seek(81, SEEK_CUR);
|
|
}
|
|
|
|
// 7 (0.64)
|
|
qint32 i = -1;
|
|
if (version > 6)
|
|
file.ReadS32(&i, 1);
|
|
mGroup = (lcGroup*)(quintptr)i;
|
|
}
|
|
else
|
|
{
|
|
file.ReadU8(&ch, 1);
|
|
if (ch == 0)
|
|
mGroup = (lcGroup*)-1;
|
|
else
|
|
mGroup = (lcGroup*)(quintptr)ch;
|
|
|
|
file.ReadU8(&ch, 1);
|
|
if (ch & 0x01)
|
|
mHidden = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void lcPiece::Initialize(const lcMatrix44& WorldMatrix, lcStep Step)
|
|
{
|
|
mStepShow = Step;
|
|
|
|
if (mPositionKeys.IsEmpty())
|
|
mPositionKeys.ChangeKey(WorldMatrix.GetTranslation(), 1, true);
|
|
if (mRotationKeys.IsEmpty())
|
|
mRotationKeys.ChangeKey(lcMatrix33(WorldMatrix), 1, true);
|
|
|
|
UpdatePosition(Step);
|
|
}
|
|
|
|
void lcPiece::InsertTime(lcStep Start, lcStep Time)
|
|
{
|
|
if (mStepShow >= Start)
|
|
{
|
|
if (mStepShow < LC_STEP_MAX - Time)
|
|
mStepShow += Time;
|
|
else
|
|
mStepShow = LC_STEP_MAX;
|
|
}
|
|
|
|
if (mStepHide >= Start)
|
|
{
|
|
if (mStepHide < LC_STEP_MAX - Time)
|
|
mStepHide += Time;
|
|
else
|
|
mStepHide = LC_STEP_MAX;
|
|
}
|
|
|
|
if (mStepShow >= mStepHide)
|
|
{
|
|
if (mStepShow != LC_STEP_MAX)
|
|
mStepHide = mStepShow + 1;
|
|
else
|
|
{
|
|
mStepShow = LC_STEP_MAX - 1;
|
|
mStepHide = LC_STEP_MAX;
|
|
}
|
|
}
|
|
|
|
mPositionKeys.InsertTime(Start, Time);
|
|
mRotationKeys.InsertTime(Start, Time);
|
|
}
|
|
|
|
void lcPiece::RemoveTime(lcStep Start, lcStep Time)
|
|
{
|
|
if (mStepShow >= Start)
|
|
{
|
|
if (mStepShow > Time)
|
|
mStepShow -= Time;
|
|
else
|
|
mStepShow = 1;
|
|
}
|
|
|
|
if (mStepHide != LC_STEP_MAX)
|
|
{
|
|
if (mStepHide > Time)
|
|
mStepHide -= Time;
|
|
else
|
|
mStepHide = 1;
|
|
}
|
|
|
|
if (mStepShow >= mStepHide)
|
|
{
|
|
if (mStepShow != LC_STEP_MAX)
|
|
mStepHide = mStepShow + 1;
|
|
else
|
|
{
|
|
mStepShow = LC_STEP_MAX - 1;
|
|
mStepHide = LC_STEP_MAX;
|
|
}
|
|
}
|
|
|
|
mPositionKeys.RemoveTime(Start, Time);
|
|
mRotationKeys.RemoveTime(Start, Time);
|
|
}
|
|
|
|
void lcPiece::RayTest(lcObjectRayTest& ObjectRayTest) const
|
|
{
|
|
const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mModelWorld);
|
|
const lcVector3 Start = lcMul31(ObjectRayTest.Start, InverseWorldMatrix);
|
|
const lcVector3 End = lcMul31(ObjectRayTest.End, InverseWorldMatrix);
|
|
|
|
if (mMesh)
|
|
{
|
|
if (mMesh->MinIntersectDist(Start, End, ObjectRayTest.Distance, ObjectRayTest.PieceInfoRayTest.Plane))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcPiece*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_PIECE_SECTION_POSITION;
|
|
ObjectRayTest.PieceInfoRayTest.Transform = mModelWorld;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mPieceInfo->MinIntersectDist(Start, End, ObjectRayTest.Distance, ObjectRayTest.PieceInfoRayTest))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcPiece*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_PIECE_SECTION_POSITION;
|
|
ObjectRayTest.PieceInfoRayTest.Transform = lcMul(ObjectRayTest.PieceInfoRayTest.Transform, mModelWorld);
|
|
}
|
|
}
|
|
|
|
if (AreControlPointsVisible())
|
|
{
|
|
const lcVector3 Min(-LC_PIECE_CONTROL_POINT_SIZE, -LC_PIECE_CONTROL_POINT_SIZE, -LC_PIECE_CONTROL_POINT_SIZE);
|
|
const lcVector3 Max(LC_PIECE_CONTROL_POINT_SIZE, LC_PIECE_CONTROL_POINT_SIZE, LC_PIECE_CONTROL_POINT_SIZE);
|
|
|
|
for (int ControlPointIdx = 0; ControlPointIdx < mControlPoints.GetSize(); ControlPointIdx++)
|
|
{
|
|
const lcMatrix44 InverseTransform = lcMatrix44AffineInverse(mControlPoints[ControlPointIdx].Transform);
|
|
const lcVector3 PointStart = lcMul31(Start, InverseTransform);
|
|
const lcVector3 PointEnd = lcMul31(End, InverseTransform);
|
|
|
|
float Distance;
|
|
lcVector3 Plane;
|
|
|
|
if (lcBoundingBoxRayIntersectDistance(Min, Max, PointStart, PointEnd, &Distance, nullptr, &Plane))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcPiece*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_PIECE_SECTION_CONTROL_POINT_FIRST + ControlPointIdx;
|
|
ObjectRayTest.Distance = Distance;
|
|
ObjectRayTest.PieceInfoRayTest.Plane = Plane;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcPiece::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
|
|
{
|
|
if (mPieceInfo->BoxTest(mModelWorld, ObjectBoxTest.Planes))
|
|
ObjectBoxTest.Objects.Add(const_cast<lcPiece*>(this));
|
|
}
|
|
|
|
void lcPiece::DrawInterface(lcContext* Context, const lcScene& Scene) const
|
|
{
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
const float LineWidth = Preferences.mLineWidth;
|
|
|
|
Context->SetLineWidth(2.0f * LineWidth);
|
|
|
|
const lcBoundingBox& BoundingBox = GetBoundingBox();
|
|
const lcVector3& Min = BoundingBox.Min;
|
|
const lcVector3& Max = BoundingBox.Max;
|
|
lcVector3 Edge((Max - Min) * 0.33f);
|
|
|
|
const float LineVerts[48][3] =
|
|
{
|
|
{ Max[0], Max[1], Max[2] }, { Max[0] - Edge[0], Max[1], Max[2] },
|
|
{ Max[0], Max[1], Max[2] }, { Max[0], Max[1] - Edge[1], Max[2] },
|
|
{ Max[0], Max[1], Max[2] }, { Max[0], Max[1], Max[2] - Edge[2] },
|
|
|
|
{ Min[0], Max[1], Max[2] }, { Min[0] + Edge[0], Max[1], Max[2] },
|
|
{ Min[0], Max[1], Max[2] }, { Min[0], Max[1] - Edge[1], Max[2] },
|
|
{ Min[0], Max[1], Max[2] }, { Min[0], Max[1], Max[2] - Edge[2] },
|
|
|
|
{ Max[0], Min[1], Max[2] }, { Max[0] - Edge[0], Min[1], Max[2] },
|
|
{ Max[0], Min[1], Max[2] }, { Max[0], Min[1] + Edge[1], Max[2] },
|
|
{ Max[0], Min[1], Max[2] }, { Max[0], Min[1], Max[2] - Edge[2] },
|
|
|
|
{ Min[0], Min[1], Max[2] }, { Min[0] + Edge[0], Min[1], Max[2] },
|
|
{ Min[0], Min[1], Max[2] }, { Min[0], Min[1] + Edge[1], Max[2] },
|
|
{ Min[0], Min[1], Max[2] }, { Min[0], Min[1], Max[2] - Edge[2] },
|
|
|
|
{ Max[0], Max[1], Min[2] }, { Max[0] - Edge[0], Max[1], Min[2] },
|
|
{ Max[0], Max[1], Min[2] }, { Max[0], Max[1] - Edge[1], Min[2] },
|
|
{ Max[0], Max[1], Min[2] }, { Max[0], Max[1], Min[2] + Edge[2] },
|
|
|
|
{ Min[0], Max[1], Min[2] }, { Min[0] + Edge[0], Max[1], Min[2] },
|
|
{ Min[0], Max[1], Min[2] }, { Min[0], Max[1] - Edge[1], Min[2] },
|
|
{ Min[0], Max[1], Min[2] }, { Min[0], Max[1], Min[2] + Edge[2] },
|
|
|
|
{ Max[0], Min[1], Min[2] }, { Max[0] - Edge[0], Min[1], Min[2] },
|
|
{ Max[0], Min[1], Min[2] }, { Max[0], Min[1] + Edge[1], Min[2] },
|
|
{ Max[0], Min[1], Min[2] }, { Max[0], Min[1], Min[2] + Edge[2] },
|
|
|
|
{ Min[0], Min[1], Min[2] }, { Min[0] + Edge[0], Min[1], Min[2] },
|
|
{ Min[0], Min[1], Min[2] }, { Min[0], Min[1] + Edge[1], Min[2] },
|
|
{ Min[0], Min[1], Min[2] }, { Min[0], Min[1], Min[2] + Edge[2] },
|
|
};
|
|
|
|
const lcMatrix44 WorldMatrix = Scene.ApplyActiveSubmodelTransform(mModelWorld);
|
|
Context->SetMaterial(lcMaterialType::UnlitColor);
|
|
Context->SetWorldMatrix(WorldMatrix);
|
|
|
|
if (IsFocused(LC_PIECE_SECTION_POSITION))
|
|
{
|
|
const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor);
|
|
Context->SetColor(FocusedColor);
|
|
}
|
|
else
|
|
{
|
|
const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor);
|
|
Context->SetColor(SelectedColor);
|
|
}
|
|
|
|
Context->SetVertexBufferPointer(LineVerts);
|
|
Context->SetVertexFormatPosition(3);
|
|
|
|
Context->DrawPrimitives(GL_LINES, 0, 48);
|
|
|
|
if (IsPivotPointVisible())
|
|
{
|
|
constexpr float Size = 5.0f;
|
|
constexpr float Verts[8 * 3] =
|
|
{
|
|
-Size, -Size, -Size, -Size, Size, -Size, Size, Size, -Size, Size, -Size, -Size,
|
|
-Size, -Size, Size, -Size, Size, Size, Size, Size, Size, Size, -Size, Size
|
|
};
|
|
|
|
const GLushort Indices[24] =
|
|
{
|
|
0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7
|
|
};
|
|
|
|
Context->SetWorldMatrix(lcMul(mPivotMatrix, WorldMatrix));
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, 24, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
|
|
if (!mControlPoints.IsEmpty() && AreControlPointsVisible())
|
|
{
|
|
float Verts[8 * 3];
|
|
float* CurVert = Verts;
|
|
|
|
lcVector3 CubeMin(-LC_PIECE_CONTROL_POINT_SIZE, -LC_PIECE_CONTROL_POINT_SIZE, -LC_PIECE_CONTROL_POINT_SIZE);
|
|
lcVector3 CubeMax(LC_PIECE_CONTROL_POINT_SIZE, LC_PIECE_CONTROL_POINT_SIZE, LC_PIECE_CONTROL_POINT_SIZE);
|
|
|
|
*CurVert++ = CubeMin[0]; *CurVert++ = CubeMin[1]; *CurVert++ = CubeMin[2];
|
|
*CurVert++ = CubeMin[0]; *CurVert++ = CubeMax[1]; *CurVert++ = CubeMin[2];
|
|
*CurVert++ = CubeMax[0]; *CurVert++ = CubeMax[1]; *CurVert++ = CubeMin[2];
|
|
*CurVert++ = CubeMax[0]; *CurVert++ = CubeMin[1]; *CurVert++ = CubeMin[2];
|
|
*CurVert++ = CubeMin[0]; *CurVert++ = CubeMin[1]; *CurVert++ = CubeMax[2];
|
|
*CurVert++ = CubeMin[0]; *CurVert++ = CubeMax[1]; *CurVert++ = CubeMax[2];
|
|
*CurVert++ = CubeMax[0]; *CurVert++ = CubeMax[1]; *CurVert++ = CubeMax[2];
|
|
*CurVert++ = CubeMax[0]; *CurVert++ = CubeMin[1]; *CurVert++ = CubeMax[2];
|
|
|
|
const GLushort Indices[36] =
|
|
{
|
|
0, 1, 2, 0, 2, 3, 7, 6, 5, 7, 5, 4, 5, 1, 0, 4, 5, 0,
|
|
7, 3, 2, 6, 7, 2, 0, 3, 7, 0, 7, 4, 6, 2, 1, 5, 6, 1
|
|
};
|
|
|
|
Context->EnableColorBlend(true);
|
|
Context->EnableCullFace(true);
|
|
|
|
const lcVector4 ControlPointColor = lcVector4FromColor(Preferences.mControlPointColor);
|
|
const lcVector4 ControlPointFocusedColor = lcVector4FromColor(Preferences.mControlPointFocusedColor);
|
|
|
|
for (int ControlPointIdx = 0; ControlPointIdx < mControlPoints.GetSize(); ControlPointIdx++)
|
|
{
|
|
Context->SetWorldMatrix(lcMul(mControlPoints[ControlPointIdx].Transform, WorldMatrix));
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
if (IsFocused(LC_PIECE_SECTION_CONTROL_POINT_FIRST + ControlPointIdx))
|
|
Context->SetColor(ControlPointFocusedColor);
|
|
else
|
|
Context->SetColor(ControlPointColor);
|
|
|
|
Context->DrawIndexedPrimitives(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
|
|
Context->EnableCullFace(false);
|
|
Context->EnableColorBlend(false);
|
|
}
|
|
}
|
|
|
|
void lcPiece::RemoveKeyFrames()
|
|
{
|
|
mPositionKeys.RemoveAll();
|
|
mPositionKeys.ChangeKey(mModelWorld.GetTranslation(), 1, true);
|
|
|
|
mRotationKeys.RemoveAll();
|
|
mRotationKeys.ChangeKey(lcMatrix33(mModelWorld), 1, true);
|
|
}
|
|
|
|
void lcPiece::AddMainModelRenderMeshes(lcScene* Scene, bool Highlight, bool Fade) const
|
|
{
|
|
lcRenderMeshState RenderMeshState = lcRenderMeshState::Default;
|
|
bool ParentActive = false;
|
|
|
|
if (Highlight)
|
|
RenderMeshState = lcRenderMeshState::Highlighted;
|
|
|
|
if (Fade)
|
|
RenderMeshState = lcRenderMeshState::Faded;
|
|
|
|
if (Scene->GetDrawInterface())
|
|
{
|
|
const lcPiece* ActiveSubmodelInstance = Scene->GetActiveSubmodelInstance();
|
|
|
|
if (!ActiveSubmodelInstance)
|
|
RenderMeshState = IsFocused() ? lcRenderMeshState::Focused : (IsSelected() ? lcRenderMeshState::Selected : RenderMeshState);
|
|
else if (ActiveSubmodelInstance == this)
|
|
ParentActive = true;
|
|
else
|
|
RenderMeshState = lcRenderMeshState::Faded;
|
|
}
|
|
|
|
if (!mMesh)
|
|
mPieceInfo->AddRenderMeshes(Scene, mModelWorld, mColorIndex, RenderMeshState, ParentActive);
|
|
else
|
|
Scene->AddMesh(mMesh, mModelWorld, mColorIndex, RenderMeshState);
|
|
|
|
if (RenderMeshState == lcRenderMeshState::Focused || RenderMeshState == lcRenderMeshState::Selected)
|
|
Scene->AddInterfaceObject(this);
|
|
}
|
|
|
|
void lcPiece::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const
|
|
{
|
|
int ColorIndex = mColorIndex;
|
|
|
|
if (ColorIndex == gDefaultColor)
|
|
ColorIndex = DefaultColorIndex;
|
|
|
|
const lcPiece* ActiveSubmodelInstance = Scene->GetActiveSubmodelInstance();
|
|
|
|
if (ActiveSubmodelInstance == this)
|
|
RenderMeshState = lcRenderMeshState::Default;
|
|
else if (ParentActive)
|
|
RenderMeshState = IsFocused() ? lcRenderMeshState::Focused : (IsSelected() ? lcRenderMeshState::Selected : lcRenderMeshState::Default);
|
|
|
|
if (!mMesh)
|
|
mPieceInfo->AddRenderMeshes(Scene, lcMul(mModelWorld, WorldMatrix), ColorIndex, RenderMeshState, ActiveSubmodelInstance == this);
|
|
else
|
|
Scene->AddMesh(mMesh, lcMul(mModelWorld, WorldMatrix), ColorIndex, RenderMeshState);
|
|
|
|
if (ParentActive && (RenderMeshState == lcRenderMeshState::Focused || RenderMeshState == lcRenderMeshState::Selected))
|
|
Scene->AddInterfaceObject(this);
|
|
}
|
|
|
|
void lcPiece::SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector3& Min, lcVector3& Max) const
|
|
{
|
|
mPieceInfo->CompareBoundingBox(lcMul(mModelWorld, WorldMatrix), Min, Max);
|
|
}
|
|
|
|
void lcPiece::SubModelAddBoundingBoxPoints(const lcMatrix44& WorldMatrix, std::vector<lcVector3>& Points) const
|
|
{
|
|
if (!mMesh)
|
|
mPieceInfo->AddSubModelBoundingBoxPoints(lcMul(mModelWorld, WorldMatrix), Points);
|
|
else
|
|
{
|
|
lcVector3 BoxPoints[8];
|
|
|
|
lcGetBoxCorners(mMesh->mBoundingBox, BoxPoints);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
Points.emplace_back(lcMul31(BoxPoints[i], mModelWorld));
|
|
}
|
|
}
|
|
|
|
void lcPiece::MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance)
|
|
{
|
|
const quint32 Section = GetFocusSection();
|
|
|
|
if (Section == LC_PIECE_SECTION_POSITION || Section == LC_PIECE_SECTION_INVALID)
|
|
{
|
|
const lcVector3 Position = mModelWorld.GetTranslation() + Distance;
|
|
|
|
SetPosition(Position, Step, AddKey);
|
|
|
|
mModelWorld.SetTranslation(Position);
|
|
}
|
|
else
|
|
{
|
|
const int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_FIRST;
|
|
|
|
if (ControlPointIndex >= 0 && ControlPointIndex < mControlPoints.GetSize())
|
|
{
|
|
const lcMatrix33 InverseWorldMatrix = lcMatrix33AffineInverse(lcMatrix33(mModelWorld));
|
|
lcMatrix44& Transform = mControlPoints[ControlPointIndex].Transform;
|
|
|
|
Transform.SetTranslation(Transform.GetTranslation() + lcMul(Distance, InverseWorldMatrix));
|
|
}
|
|
|
|
UpdateMesh();
|
|
}
|
|
}
|
|
|
|
void lcPiece::Rotate(lcStep Step, bool AddKey, const lcMatrix33& RotationMatrix, const lcVector3& Center, const lcMatrix33& RotationFrame)
|
|
{
|
|
const quint32 Section = GetFocusSection();
|
|
|
|
if (Section == LC_PIECE_SECTION_POSITION || Section == LC_PIECE_SECTION_INVALID)
|
|
{
|
|
lcVector3 Distance = mModelWorld.GetTranslation() - Center;
|
|
const lcMatrix33 LocalToWorldMatrix = lcMatrix33(mModelWorld);
|
|
|
|
const lcMatrix33 LocalToFocusMatrix = lcMul(LocalToWorldMatrix, RotationFrame);
|
|
lcMatrix33 NewLocalToWorldMatrix = lcMul(LocalToFocusMatrix, RotationMatrix);
|
|
|
|
const lcMatrix33 WorldToLocalMatrix = lcMatrix33AffineInverse(LocalToWorldMatrix);
|
|
|
|
Distance = lcMul(Distance, WorldToLocalMatrix);
|
|
Distance = lcMul(Distance, NewLocalToWorldMatrix);
|
|
|
|
NewLocalToWorldMatrix.Orthonormalize();
|
|
|
|
SetPosition(Center + Distance, Step, AddKey);
|
|
SetRotation(NewLocalToWorldMatrix, Step, AddKey);
|
|
}
|
|
else
|
|
{
|
|
const int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_FIRST;
|
|
|
|
if (ControlPointIndex >= 0 && ControlPointIndex < mControlPoints.GetSize())
|
|
{
|
|
lcMatrix44& Transform = mControlPoints[ControlPointIndex].Transform;
|
|
const lcMatrix33 PieceWorldMatrix(mModelWorld);
|
|
const lcMatrix33 LocalToWorldMatrix = lcMul(lcMatrix33(Transform), PieceWorldMatrix);
|
|
|
|
const lcMatrix33 LocalToFocusMatrix = lcMul(LocalToWorldMatrix, RotationFrame);
|
|
lcMatrix33 NewLocalToWorldMatrix = lcMul(lcMul(LocalToFocusMatrix, RotationMatrix), lcMatrix33AffineInverse(PieceWorldMatrix));
|
|
|
|
NewLocalToWorldMatrix.Orthonormalize();
|
|
Transform = lcMatrix44(NewLocalToWorldMatrix, Transform.GetTranslation());
|
|
}
|
|
|
|
UpdateMesh();
|
|
}
|
|
}
|
|
|
|
void lcPiece::MovePivotPoint(const lcVector3& Distance)
|
|
{
|
|
if (!IsFocused(LC_PIECE_SECTION_POSITION))
|
|
return;
|
|
|
|
mPivotMatrix.SetTranslation(mPivotMatrix.GetTranslation() + lcMul30(Distance, lcMatrix44AffineInverse(mModelWorld)));
|
|
mPivotPointValid = true;
|
|
}
|
|
|
|
void lcPiece::RotatePivotPoint(const lcMatrix33& RotationMatrix)
|
|
{
|
|
if (!IsFocused(LC_PIECE_SECTION_POSITION))
|
|
return;
|
|
|
|
lcMatrix33 NewPivotRotationMatrix = lcMul(RotationMatrix, lcMatrix33(mPivotMatrix));
|
|
NewPivotRotationMatrix.Orthonormalize();
|
|
|
|
mPivotMatrix = lcMatrix44(NewPivotRotationMatrix, mPivotMatrix.GetTranslation());
|
|
mPivotPointValid = true;
|
|
}
|
|
|
|
quint32 lcPiece::GetAllowedTransforms() const
|
|
{
|
|
constexpr quint32 Move = LC_OBJECT_TRANSFORM_MOVE_X | LC_OBJECT_TRANSFORM_MOVE_Y | LC_OBJECT_TRANSFORM_MOVE_Z;
|
|
constexpr quint32 Rotate = LC_OBJECT_TRANSFORM_ROTATE_X | LC_OBJECT_TRANSFORM_ROTATE_Y | LC_OBJECT_TRANSFORM_ROTATE_Z;
|
|
const quint32 Section = GetFocusSection();
|
|
|
|
if (Section == LC_PIECE_SECTION_POSITION || Section == LC_PIECE_SECTION_INVALID)
|
|
return Move | Rotate;
|
|
|
|
const lcSynthInfo* SynthInfo = mPieceInfo->GetSynthInfo();
|
|
|
|
if (SynthInfo)
|
|
{
|
|
if (SynthInfo->IsUnidirectional())
|
|
return LC_OBJECT_TRANSFORM_MOVE_Z;
|
|
|
|
if (SynthInfo->IsCurve())
|
|
return Move | Rotate | LC_OBJECT_TRANSFORM_SCALE_X;
|
|
|
|
if (SynthInfo->IsNondirectional())
|
|
return Move;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool lcPiece::CanAddControlPoint() const
|
|
{
|
|
if (mControlPoints.GetSize() >= LC_MAX_CONTROL_POINTS)
|
|
return false;
|
|
|
|
const lcSynthInfo* SynthInfo = mPieceInfo->GetSynthInfo();
|
|
return SynthInfo && SynthInfo->CanAddControlPoints();
|
|
}
|
|
|
|
bool lcPiece::CanRemoveControlPoint() const
|
|
{
|
|
const quint32 Section = GetFocusSection();
|
|
return Section >= LC_PIECE_SECTION_CONTROL_POINT_FIRST && Section <= LC_PIECE_SECTION_CONTROL_POINT_LAST && mControlPoints.GetSize() > 2;
|
|
}
|
|
|
|
bool lcPiece::InsertControlPoint(const lcVector3& WorldStart, const lcVector3& WorldEnd)
|
|
{
|
|
if (!CanAddControlPoint())
|
|
return false;
|
|
|
|
const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mModelWorld);
|
|
const lcVector3 Start = lcMul31(WorldStart, InverseWorldMatrix);
|
|
const lcVector3 End = lcMul31(WorldEnd, InverseWorldMatrix);
|
|
|
|
const lcSynthInfo* SynthInfo = mPieceInfo->GetSynthInfo();
|
|
const int ControlPointIndex = SynthInfo->InsertControlPoint(mControlPoints, Start, End);
|
|
|
|
if (ControlPointIndex)
|
|
{
|
|
SetFocused(GetFocusSection(), false);
|
|
SetFocused(LC_PIECE_SECTION_CONTROL_POINT_FIRST + ControlPointIndex, true);
|
|
UpdateMesh();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool lcPiece::RemoveFocusedControlPoint()
|
|
{
|
|
const int ControlPointIndex = GetFocusSection() - LC_PIECE_SECTION_CONTROL_POINT_FIRST;
|
|
|
|
if (ControlPointIndex < 0 || ControlPointIndex >= mControlPoints.GetSize() || mControlPoints.GetSize() <= 2)
|
|
return false;
|
|
|
|
SetFocused(GetFocusSection(), false);
|
|
SetFocused(LC_PIECE_SECTION_POSITION, true);
|
|
mControlPoints.RemoveIndex(ControlPointIndex);
|
|
|
|
UpdateMesh();
|
|
|
|
return true;
|
|
}
|
|
|
|
void lcPiece::VerifyControlPoints(lcArray<lcPieceControlPoint>& ControlPoints) const
|
|
{
|
|
const lcSynthInfo* SynthInfo = mPieceInfo->GetSynthInfo();
|
|
|
|
if (!SynthInfo)
|
|
{
|
|
ControlPoints.RemoveAll();
|
|
}
|
|
else
|
|
{
|
|
if (ControlPoints.GetSize() > LC_MAX_CONTROL_POINTS)
|
|
ControlPoints.SetSize(LC_MAX_CONTROL_POINTS);
|
|
|
|
SynthInfo->VerifyControlPoints(ControlPoints);
|
|
}
|
|
}
|
|
|
|
QString lcPiece::GetName() const
|
|
{
|
|
return QString::fromLatin1(mPieceInfo->m_strDescription);
|
|
}
|
|
|
|
bool lcPiece::IsVisible(lcStep Step) const
|
|
{
|
|
return !mHidden && (mStepShow <= Step) && (mStepHide > Step || mStepHide == LC_STEP_MAX);
|
|
}
|
|
|
|
bool lcPiece::IsVisibleInSubModel() const
|
|
{
|
|
return (mStepHide == LC_STEP_MAX) && !mHidden;
|
|
}
|
|
|
|
void lcPiece::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector<lcModelPartsEntry>& ModelParts) const
|
|
{
|
|
if (!IsVisibleInSubModel())
|
|
return;
|
|
|
|
int ColorIndex = mColorIndex;
|
|
|
|
if (ColorIndex == gDefaultColor)
|
|
ColorIndex = DefaultColorIndex;
|
|
|
|
if (!mMesh)
|
|
mPieceInfo->GetModelParts(lcMul(mModelWorld, WorldMatrix), ColorIndex, ModelParts);
|
|
else
|
|
ModelParts.emplace_back(lcModelPartsEntry{ lcMul(mModelWorld, WorldMatrix), mPieceInfo, mMesh, ColorIndex });
|
|
}
|
|
|
|
const lcBoundingBox& lcPiece::GetBoundingBox() const
|
|
{
|
|
if (!mMesh)
|
|
return mPieceInfo->GetBoundingBox();
|
|
else
|
|
return mMesh->mBoundingBox;
|
|
}
|
|
|
|
void lcPiece::CompareBoundingBox(lcVector3& Min, lcVector3& Max) const
|
|
{
|
|
if (!mMesh)
|
|
mPieceInfo->CompareBoundingBox(mModelWorld, Min, Max);
|
|
else
|
|
{
|
|
lcVector3 Points[8];
|
|
|
|
lcGetBoxCorners(mMesh->mBoundingBox, Points);
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
const lcVector3 Point = lcMul31(Points[i], mModelWorld);
|
|
|
|
Min = lcMin(Point, Min);
|
|
Max = lcMax(Point, Max);
|
|
}
|
|
}
|
|
}
|
|
|
|
lcGroup* lcPiece::GetTopGroup()
|
|
{
|
|
return mGroup ? mGroup->GetTopGroup() : nullptr;
|
|
}
|
|
|
|
void lcPiece::UpdatePosition(lcStep Step)
|
|
{
|
|
const lcVector3 Position = mPositionKeys.CalculateKey(Step);
|
|
const lcMatrix33 Rotation = mRotationKeys.CalculateKey(Step);
|
|
|
|
mModelWorld = lcMatrix44(Rotation, Position);
|
|
}
|
|
|
|
void lcPiece::UpdateMesh()
|
|
{
|
|
delete mMesh;
|
|
const lcSynthInfo* SynthInfo = mPieceInfo->GetSynthInfo();
|
|
mMesh = SynthInfo ? SynthInfo->CreateMesh(mControlPoints) : nullptr;
|
|
}
|