diff --git a/common/lc_commands.cpp b/common/lc_commands.cpp index aca91c23..a40e7eef 100644 --- a/common/lc_commands.cpp +++ b/common/lc_commands.cpp @@ -956,6 +956,20 @@ lcCommand gCommands[LC_NUM_COMMANDS] = QT_TRANSLATE_NOOP("Status", "Reset the pivot point of the selected pieces to their origin"), QT_TRANSLATE_NOOP("Shortcut", "") }, + // LC_PIECE_CONTROL_POINT_INSERT + { + "Piece.ControlPoint.Insert", + QT_TRANSLATE_NOOP("Menu", "Insert Control Point"), + QT_TRANSLATE_NOOP("Status", "Insert a new control point"), + QT_TRANSLATE_NOOP("Shortcut", "") + }, + // LC_PIECE_CONTROL_POINT_REMOVE + { + "Piece.ControlPoint.Remove", + QT_TRANSLATE_NOOP("Menu", "Remove Control Point"), + QT_TRANSLATE_NOOP("Status", "Remove the currently selected control point"), + QT_TRANSLATE_NOOP("Shortcut", "") + }, // LC_PIECE_MOVE_PLUSX { "Piece.Move.PlusX", diff --git a/common/lc_commands.h b/common/lc_commands.h index 899b4228..e40b94d9 100644 --- a/common/lc_commands.h +++ b/common/lc_commands.h @@ -147,6 +147,8 @@ enum lcCommandId LC_PIECE_INSERT, LC_PIECE_DELETE, LC_PIECE_RESET_PIVOT_POINT, + LC_PIECE_CONTROL_POINT_INSERT, + LC_PIECE_CONTROL_POINT_REMOVE, LC_PIECE_MOVE_PLUSX, LC_PIECE_MOVE_MINUSX, LC_PIECE_MOVE_PLUSY, diff --git a/common/lc_mainwindow.cpp b/common/lc_mainwindow.cpp index 10c69a84..cb6f3227 100644 --- a/common/lc_mainwindow.cpp +++ b/common/lc_mainwindow.cpp @@ -1487,6 +1487,8 @@ void lcMainWindow::UpdateSelectedObjects(bool SelectionChanged) mActions[LC_PIECE_DELETE]->setEnabled(Flags & LC_SEL_SELECTED); mActions[LC_PIECE_RESET_PIVOT_POINT]->setEnabled(Flags & LC_SEL_SELECTED); mActions[LC_PIECE_ARRAY]->setEnabled(Flags & LC_SEL_PIECE); + mActions[LC_PIECE_CONTROL_POINT_INSERT]->setEnabled(Flags & LC_SEL_CAN_ADD_CONTROL_POINT); + mActions[LC_PIECE_CONTROL_POINT_REMOVE]->setEnabled(Flags & LC_SEL_CAN_REMOVE_CONTROL_POINT); mActions[LC_PIECE_HIDE_SELECTED]->setEnabled(Flags & LC_SEL_VISIBLE_SELECTED); mActions[LC_PIECE_HIDE_UNSELECTED]->setEnabled(Flags & LC_SEL_UNSELECTED); mActions[LC_PIECE_UNHIDE_SELECTED]->setEnabled(Flags & LC_SEL_HIDDEN_SELECTED); @@ -2092,6 +2094,14 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) lcGetActiveModel()->ResetSelectedPiecesPivotPoint(); break; + case LC_PIECE_CONTROL_POINT_INSERT: + lcGetActiveModel()->InsertControlPoint(); + break; + + case LC_PIECE_CONTROL_POINT_REMOVE: + lcGetActiveModel()->RemoveFocusedControlPoint(); + break; + case LC_PIECE_MOVE_PLUSX: lcGetActiveModel()->MoveSelectedObjects(mActiveView->GetMoveDirection(lcVector3(lcMax(GetMoveXYSnap(), 0.1f), 0.0f, 0.0f)), true, false, true, true); break; diff --git a/common/lc_math.h b/common/lc_math.h index fb68537a..604c1364 100644 --- a/common/lc_math.h +++ b/common/lc_math.h @@ -521,6 +521,11 @@ inline lcVector4& operator/=(lcVector4& a, float b) return a; } +inline lcVector3 lcVector3LDrawToLeoCAD(const lcVector3& Vector) +{ + return lcVector3(Vector[0], Vector[2], -Vector[1]); +} + inline lcVector3 lcVector3FromColor(lcuint32 Color) { lcVector3 v(LC_RGBA_RED(Color), LC_RGBA_GREEN(Color), LC_RGBA_BLUE(Color)); @@ -1242,6 +1247,30 @@ inline lcMatrix44 lcMatrix44Inverse(const lcMatrix44& m) #undef SWAP_ROWS } +inline lcMatrix44 lcMatrix44LeoCADToLDraw(const lcMatrix44& Matrix) +{ + lcMatrix44 m; + + m.r[0] = lcVector4(Matrix[0][0], -Matrix[2][0], Matrix[1][0], 0.0f); + m.r[1] = lcVector4(-Matrix[0][2], Matrix[2][2], -Matrix[1][2], 0.0f); + m.r[2] = lcVector4(Matrix[0][1], -Matrix[2][1], Matrix[1][1], 0.0f); + m.r[3] = lcVector4(Matrix[3][0], -Matrix[3][2], Matrix[3][1], 1.0f); + + return m; +} + +inline lcMatrix44 lcMatrix44LDrawToLeoCAD(const lcMatrix44& Matrix) +{ + lcMatrix44 m; + + m.r[0] = lcVector4(Matrix[0][0], Matrix[2][0], -Matrix[1][0], 0.0f); + m.r[1] = lcVector4(Matrix[0][2], Matrix[2][2], -Matrix[1][2], 0.0f); + m.r[2] = lcVector4(-Matrix[0][1], -Matrix[2][1], Matrix[1][1], 0.0f); + m.r[3] = lcVector4(Matrix[3][0], Matrix[3][2], -Matrix[3][1], 1.0f); + + return m; +} + inline lcVector4 lcQuaternionRotationX(float Radians) { return lcVector4(sinf(Radians / 2.0f), 0, 0, cosf(Radians / 2.0f)); @@ -1760,26 +1789,19 @@ inline bool lcSphereRayMinIntersectDistance(const lcVector3& Center, float Radiu } } -/* -float LinePointMinDistance(const Vector3& Point, const Vector3& Start, const Vector3& End) +inline float lcRayPointDistance(const lcVector3& Point, const lcVector3& Start, const lcVector3& End) { - Vector3 Dir = End - Start; + lcVector3 Dir = Point - Start; + lcVector3 RayDir = End - Start; - float t1 = Dot3(Start - Point, Dir); - float t2 = LengthSquared(Dir); + float t = lcDot(Dir, RayDir) / lcLengthSquared(RayDir); + t = lcClamp(t, 0.0f, 1.0f); - float t = -t1 / t2; + lcVector3 Closest = Start + t * RayDir; - if (t < 0.0f) - t = 0.0f; - else if (t > 1.0f) - t = 1.0f; - - Vector3 Closest = Start + t * Dir; - - return Length(Closest - Point); + return lcLength(Closest - Point); } -*/ + // Returns true if the axis aligned box intersects the volume defined by planes. inline bool lcBoundingBoxIntersectsVolume(const lcVector3& Min, const lcVector3& Max, const lcVector4 Planes[6]) { diff --git a/common/lc_model.cpp b/common/lc_model.cpp index bd4a9330..4fa14a87 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -1918,6 +1918,43 @@ void lcModel::ResetSelectedPiecesPivotPoint() gMainWindow->UpdateAllViews(); } +void lcModel::InsertControlPoint() +{ + lcObject* Focus = GetFocusObject(); + + if (!Focus || !Focus->IsPiece()) + return; + + lcPiece* Piece = (lcPiece*)Focus; + + lcVector3 Start, End; + gMainWindow->GetActiveView()->GetRayUnderPointer(Start, End); + + if (Piece->InsertControlPoint(Start, End)) + { + SaveCheckpoint("Modifying"); + gMainWindow->UpdateSelectedObjects(true); + gMainWindow->UpdateAllViews(); + } +} + +void lcModel::RemoveFocusedControlPoint() +{ + lcObject* Focus = GetFocusObject(); + + if (!Focus || !Focus->IsPiece()) + return; + + lcPiece* Piece = (lcPiece*)Focus; + + if (Piece->RemoveFocusedControlPoint()) + { + SaveCheckpoint("Modifying"); + gMainWindow->UpdateSelectedObjects(true); + gMainWindow->UpdateAllViews(); + } +} + void lcModel::ShowSelectedPiecesEarlier() { lcArray MovedPieces; @@ -2910,6 +2947,16 @@ void lcModel::GetSelectionInformation(int* Flags, lcArray& Selection, *Flags |= LC_SEL_PIECE | LC_SEL_SELECTED; + if (Piece->mPieceInfo->GetSynthInfo()) + { + *Flags |= LC_SEL_CAN_ADD_CONTROL_POINT; + + lcuint32 Section = Piece->GetFocusSection(); + + if (Section >= LC_PIECE_SECTION_CONTROL_POINT_1 && Section <= LC_PIECE_SECTION_CONTROL_POINT_8 && Piece->GetControlPoints().GetSize() > 2) + *Flags |= LC_SEL_CAN_REMOVE_CONTROL_POINT; + } + if (Piece->GetGroup() != NULL) { *Flags |= LC_SEL_GROUPED; diff --git a/common/lc_model.h b/common/lc_model.h index 50876d17..bcecd41d 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -5,17 +5,19 @@ #include "lc_math.h" #include "object.h" -#define LC_SEL_NO_PIECES 0x001 // No pieces in model -#define LC_SEL_PIECE 0x002 // At last 1 piece selected -#define LC_SEL_SELECTED 0x004 // At last 1 object selected -#define LC_SEL_UNSELECTED 0x008 // At least 1 piece unselected -#define LC_SEL_HIDDEN 0x010 // At least one piece hidden -#define LC_SEL_HIDDEN_SELECTED 0x020 // At least one piece selected is hidden -#define LC_SEL_VISIBLE_SELECTED 0x040 // At least one piece selected is not hidden -#define LC_SEL_GROUPED 0x080 // At least one piece selected is grouped -#define LC_SEL_FOCUS_GROUPED 0x100 // Focused piece is grouped -#define LC_SEL_CAN_GROUP 0x200 // Can make a new group -#define LC_SEL_MODEL_SELECTED 0x400 // At least one model reference is selected +#define LC_SEL_NO_PIECES 0x0001 // No pieces in model +#define LC_SEL_PIECE 0x0002 // At last 1 piece selected +#define LC_SEL_SELECTED 0x0004 // At last 1 object selected +#define LC_SEL_UNSELECTED 0x0008 // At least 1 piece unselected +#define LC_SEL_HIDDEN 0x0010 // At least one piece hidden +#define LC_SEL_HIDDEN_SELECTED 0x0020 // At least one piece selected is hidden +#define LC_SEL_VISIBLE_SELECTED 0x0040 // At least one piece selected is not hidden +#define LC_SEL_GROUPED 0x0080 // At least one piece selected is grouped +#define LC_SEL_FOCUS_GROUPED 0x0100 // Focused piece is grouped +#define LC_SEL_CAN_GROUP 0x0200 // Can make a new group +#define LC_SEL_MODEL_SELECTED 0x0400 // At least one model reference is selected +#define LC_SEL_CAN_ADD_CONTROL_POINT 0x0800 // Can add control points to focused piece +#define LC_SEL_CAN_REMOVE_CONTROL_POINT 0x1000 // Can remove control points from focused piece enum lcTransformType { @@ -197,6 +199,8 @@ public: void DeleteAllCameras(); void DeleteSelectedObjects(); void ResetSelectedPiecesPivotPoint(); + void InsertControlPoint(); + void RemoveFocusedControlPoint(); void ShowSelectedPiecesEarlier(); void ShowSelectedPiecesLater(); void SetPieceSteps(const QList>& PieceSteps); diff --git a/common/lc_synth.cpp b/common/lc_synth.cpp index eb8565aa..082db90a 100644 --- a/common/lc_synth.cpp +++ b/common/lc_synth.cpp @@ -56,26 +56,13 @@ void lcSynthInit() } } -inline lcMatrix44 lcMatrix44LeoCADToLDraw(const lcMatrix44& Matrix) -{ - lcMatrix44 m; - - m.r[0] = lcVector4( Matrix[0][0], -Matrix[2][0], Matrix[1][0], 0.0f); - m.r[1] = lcVector4(-Matrix[0][2], Matrix[2][2], -Matrix[1][2], 0.0f); - m.r[2] = lcVector4( Matrix[0][1], -Matrix[2][1], Matrix[1][1], 0.0f); - m.r[3] = lcVector4( Matrix[3][0], -Matrix[3][2], Matrix[3][1], 1.0f); - - return m; -} - #include "lc_file.h" -lcMesh* lcSynthCreateMesh(lcSynthInfo* SynthInfo, const lcArray& ControlPoints) +static void lcSynthEvaluate(const lcSynthInfo* SynthInfo, const lcArray& ControlPoints, lcArray& Sections, void (*SectionCallback)(const lcVector3& CurvePoint, int SegmentIndex, float t, void* Param), void* CallbackParam) { - lcArray Sections; float SectionLength = 0.0f; - for (int ControlPointIdx = 0; ControlPointIdx < ControlPoints.GetSize() - 1; ControlPointIdx++) + for (int ControlPointIdx = 0; ControlPointIdx < ControlPoints.GetSize() - 1 && Sections.GetSize() < SynthInfo->NumSections + 2; ControlPointIdx++) { lcVector3 SegmentControlPoints[4]; @@ -84,13 +71,13 @@ lcMesh* lcSynthCreateMesh(lcSynthInfo* SynthInfo, const lcArrayComponents[0].Transform); + StartTransform = lcMatrix44(lcMul(lcMatrix33(StartTransform), lcMatrix33(SynthInfo->Components[0].Transform)), StartTransform.GetTranslation()); Sections.Add(StartTransform); SectionLength = SynthInfo->Components[0].Length; } if (ControlPointIdx == ControlPoints.GetSize() - 2) - EndTransform = lcMul(EndTransform, lcMatrix44(lcVector4(1.0f, 0.0f, 0.0f, 0.0f), lcVector4(0.0f, -1.0f, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, 1.0f, 0.0f), lcVector4(0.0f, 0.0f, 0.0f, 1.0f))); + EndTransform = lcMatrix44(lcMul(lcMatrix33(EndTransform), lcMatrix33(lcVector3(1.0f, 0.0f, 0.0f), lcVector3(0.0f, -1.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f))), EndTransform.GetTranslation()); SegmentControlPoints[0] = StartTransform.GetTranslation(); SegmentControlPoints[1] = lcMul31(lcVector3(0.0f, ControlPoints[ControlPointIdx].Stiffness, 0.0f), StartTransform); @@ -159,6 +146,9 @@ lcMesh* lcSynthCreateMesh(lcSynthInfo* SynthInfo, const lcArrayNumSections + 2) break; @@ -172,16 +162,25 @@ lcMesh* lcSynthCreateMesh(lcSynthInfo* SynthInfo, const lcArrayNumSections + 2) { lcMatrix44 EndTransform = lcMatrix44LeoCADToLDraw(ControlPoints[ControlPoints.GetSize() - 1].Transform); - EndTransform = lcMul(EndTransform, lcMatrix44(lcVector4(1.0f, 0.0f, 0.0f, 0.0f), lcVector4(0.0f, -1.0f, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, 1.0f, 0.0f), lcVector4(0.0f, 0.0f, 0.0f, 1.0f))); + EndTransform = lcMatrix44(lcMul(lcMatrix33(EndTransform), lcMatrix33(lcVector3(1.0f, 0.0f, 0.0f), lcVector3(0.0f, -1.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f))), EndTransform.GetTranslation()); lcVector3 Position = lcMul31(lcVector3(0.0f, SectionLength, 0.0f), EndTransform); EndTransform.SetTranslation(Position); Sections.Add(EndTransform); + if (SectionCallback) + SectionCallback(Position, ControlPoints.GetSize() - 1, 1.0f, CallbackParam); + if (Sections.GetSize() < SynthInfo->NumSections + 1) SectionLength += SynthInfo->Components[1].Length; else SectionLength += SynthInfo->Components[2].Length; } +} + +lcMesh* lcSynthCreateMesh(const lcSynthInfo* SynthInfo, const lcArray& ControlPoints) +{ + lcArray Sections; + lcSynthEvaluate(SynthInfo, ControlPoints, Sections, NULL, NULL); // todo: rewrite this to pass the parts directly @@ -224,3 +223,58 @@ lcMesh* lcSynthCreateMesh(lcSynthInfo* SynthInfo, const lcArrayStart, SynthInsertParam->End); + if (Distance < SynthInsertParam->BestDistance) + { + SynthInsertParam->BestSegment = SegmentIndex; + SynthInsertParam->BestTime = t; + SynthInsertParam->BestDistance = Distance; + SynthInsertParam->BestPosition = lcVector3LDrawToLeoCAD(CurvePoint); + } +} + +int lcSynthInsertControlPoint(const lcSynthInfo* SynthInfo, lcArray& ControlPoints, const lcVector3& Start, const lcVector3& End) +{ + lcArray Sections; + lcSynthInsertParam SynthInsertParam; + + SynthInsertParam.Start = Start; + SynthInsertParam.End = End; + SynthInsertParam.BestSegment = -1; + SynthInsertParam.BestDistance = FLT_MAX; + + lcSynthEvaluate(SynthInfo, ControlPoints, Sections, lcSynthInsertCallback, &SynthInsertParam); + + if (SynthInsertParam.BestSegment != -1) + { + lcPieceControlPoint ControlPoint = ControlPoints[SynthInsertParam.BestSegment]; + ControlPoint.Transform.SetTranslation(SynthInsertParam.BestPosition); + + if (SynthInsertParam.BestSegment != ControlPoints.GetSize() - 1) + { + lcPieceControlPoint NextControlPoint = ControlPoints[SynthInsertParam.BestSegment + 1]; + float t = SynthInsertParam.BestTime; + + ControlPoint.Stiffness = ControlPoint.Stiffness * (1.0f - t) + NextControlPoint.Stiffness * t; + } + + ControlPoints.InsertAt(SynthInsertParam.BestSegment + 1, ControlPoint); + } + + return SynthInsertParam.BestSegment + 1; +} diff --git a/common/lc_synth.h b/common/lc_synth.h index 9d8ee0b2..a184297d 100644 --- a/common/lc_synth.h +++ b/common/lc_synth.h @@ -21,6 +21,7 @@ struct lcSynthInfo }; void lcSynthInit(); -lcMesh* lcSynthCreateMesh(lcSynthInfo* SynthInfo, const lcArray& ControlPoints); +lcMesh* lcSynthCreateMesh(const lcSynthInfo* SynthInfo, const lcArray& ControlPoints); +int lcSynthInsertControlPoint(const lcSynthInfo* SynthInfo, lcArray& ControlPoints, const lcVector3& Start, const lcVector3& End); #endif // _LC_SYNTH_H_ diff --git a/common/piece.cpp b/common/piece.cpp index a1a2bd9f..ceac409e 100644 --- a/common/piece.cpp +++ b/common/piece.cpp @@ -732,6 +732,43 @@ void lcPiece::RotatePivotPoint(const lcMatrix33& RotationMatrix) mState |= LC_PIECE_PIVOT_POINT_VALID; } +bool lcPiece::InsertControlPoint(const lcVector3& WorldStart, const lcVector3& WorldEnd) +{ + if (!mPieceInfo->GetSynthInfo()) + return false; + + lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mModelWorld); + lcVector3 Start = lcMul31(WorldStart, InverseWorldMatrix); + lcVector3 End = lcMul31(WorldEnd, InverseWorldMatrix); + + int ControlPointIndex = lcSynthInsertControlPoint(mPieceInfo->GetSynthInfo(), mControlPoints, Start, End); + if (ControlPointIndex) + { + SetFocused(GetFocusSection(), false); + SetFocused(LC_PIECE_SECTION_CONTROL_POINT_1 + ControlPointIndex, true); + UpdateMesh(); + return true; + } + + return false; +} + +bool lcPiece::RemoveFocusedControlPoint() +{ + int ControlPointIndex = GetFocusSection() - LC_PIECE_SECTION_CONTROL_POINT_1; + + 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; +} + const char* lcPiece::GetName() const { return mPieceInfo->m_strDescription; diff --git a/common/piece.h b/common/piece.h index 28420010..ee8dde19 100644 --- a/common/piece.h +++ b/common/piece.h @@ -361,6 +361,11 @@ public: mState &= ~LC_PIECE_HIDDEN; } + const lcArray& GetControlPoints() const + { + return mControlPoints; + } + const char* GetName() const; bool IsVisible(lcStep Step); void Initialize(const lcMatrix44& WorldMatrix, lcStep Step); @@ -375,6 +380,9 @@ public: void MovePivotPoint(const lcVector3& Distance); void RotatePivotPoint(const lcMatrix33& RotationMatrix); + bool InsertControlPoint(const lcVector3& WorldStart, const lcVector3& WorldEnd); + bool RemoveFocusedControlPoint(); + lcGroup* GetTopGroup(); void SetGroup(lcGroup* Group) diff --git a/common/view.cpp b/common/view.cpp index dce29a6d..21c91bcc 100644 --- a/common/view.cpp +++ b/common/view.cpp @@ -386,6 +386,11 @@ void View::ShowContextMenu() const Popup->addSeparator(); + Popup->addAction(Actions[LC_PIECE_CONTROL_POINT_INSERT]); + Popup->addAction(Actions[LC_PIECE_CONTROL_POINT_REMOVE]); + + Popup->addSeparator(); + Popup->addAction(Actions[LC_PIECE_VIEW_SELECTED_MODEL]); Popup->addAction(Actions[LC_PIECE_INLINE_SELECTED_MODELS]); Popup->addAction(Actions[LC_PIECE_MOVE_SELECTION_TO_MODEL]); @@ -486,6 +491,20 @@ lcMatrix44 View::GetPieceInsertPosition() const return lcMatrix44Translation(UnprojectPoint(lcVector3((float)mInputState.x, (float)mInputState.y, 0.9f))); } +void View::GetRayUnderPointer(lcVector3& Start, lcVector3& End) const +{ + lcVector3 StartEnd[2] = + { + lcVector3((float)mInputState.x, (float)mInputState.y, 0.0f), + lcVector3((float)mInputState.x, (float)mInputState.y, 1.0f) + }; + + UnprojectPoints(StartEnd, 2); + + Start = StartEnd[0]; + End = StartEnd[1]; +} + lcObjectSection View::FindObjectUnderPointer(bool PiecesOnly) const { lcVector3 StartEnd[2] = diff --git a/common/view.h b/common/view.h index a77fcfc4..0b40fe56 100644 --- a/common/view.h +++ b/common/view.h @@ -97,6 +97,7 @@ public: lcVector3 GetMoveDirection(const lcVector3& Direction) const; lcMatrix44 GetPieceInsertPosition() const; + void GetRayUnderPointer(lcVector3& Start, lcVector3& End) const; lcObjectSection FindObjectUnderPointer(bool PiecesOnly) const; lcArray FindObjectsInBox(float x1, float y1, float x2, float y2) const;