#pragma once class PieceInfo; #include "object.h" #include "lc_colors.h" #include "lc_math.h" #define LC_PIECE_HIDDEN 0x000001 #define LC_PIECE_PIVOT_POINT_VALID 0x000002 #define LC_PIECE_POSITION_SELECTED 0x000004 #define LC_PIECE_POSITION_FOCUSED 0x000008 #define LC_PIECE_CONTROL_POINT_1_SELECTED 0x000010 #define LC_PIECE_CONTROL_POINT_1_FOCUSED 0x000020 #define LC_PIECE_CONTROL_POINT_2_SELECTED 0x000040 #define LC_PIECE_CONTROL_POINT_2_FOCUSED 0x000080 #define LC_PIECE_CONTROL_POINT_3_SELECTED 0x000100 #define LC_PIECE_CONTROL_POINT_3_FOCUSED 0x000200 #define LC_PIECE_CONTROL_POINT_4_SELECTED 0x000400 #define LC_PIECE_CONTROL_POINT_4_FOCUSED 0x000800 #define LC_PIECE_CONTROL_POINT_5_SELECTED 0x001000 #define LC_PIECE_CONTROL_POINT_5_FOCUSED 0x002000 #define LC_PIECE_CONTROL_POINT_6_SELECTED 0x004000 #define LC_PIECE_CONTROL_POINT_6_FOCUSED 0x008000 #define LC_PIECE_CONTROL_POINT_7_SELECTED 0x010000 #define LC_PIECE_CONTROL_POINT_7_FOCUSED 0x020000 #define LC_PIECE_CONTROL_POINT_8_SELECTED 0x040000 #define LC_PIECE_CONTROL_POINT_8_FOCUSED 0x080000 #define LC_PIECE_CONTROL_POINT_9_SELECTED 0x100000 #define LC_PIECE_CONTROL_POINT_9_FOCUSED 0x200000 #define LC_PIECE_CONTROL_POINT_10_SELECTED 0x400000 #define LC_PIECE_CONTROL_POINT_10_FOCUSED 0x800000 #define LC_MAX_CONTROL_POINTS 10 #define LC_PIECE_CONTROL_POINT_SELECTION_MASK (LC_PIECE_CONTROL_POINT_1_SELECTED | LC_PIECE_CONTROL_POINT_2_SELECTED | LC_PIECE_CONTROL_POINT_3_SELECTED | LC_PIECE_CONTROL_POINT_4_SELECTED | LC_PIECE_CONTROL_POINT_5_SELECTED | LC_PIECE_CONTROL_POINT_6_SELECTED | LC_PIECE_CONTROL_POINT_7_SELECTED | LC_PIECE_CONTROL_POINT_8_SELECTED | LC_PIECE_CONTROL_POINT_9_SELECTED | LC_PIECE_CONTROL_POINT_10_SELECTED) #define LC_PIECE_CONTROL_POINT_FOCUS_MASK (LC_PIECE_CONTROL_POINT_1_FOCUSED | LC_PIECE_CONTROL_POINT_2_FOCUSED | LC_PIECE_CONTROL_POINT_3_FOCUSED | LC_PIECE_CONTROL_POINT_4_FOCUSED | LC_PIECE_CONTROL_POINT_5_FOCUSED | LC_PIECE_CONTROL_POINT_6_FOCUSED | LC_PIECE_CONTROL_POINT_7_FOCUSED | LC_PIECE_CONTROL_POINT_8_FOCUSED | LC_PIECE_CONTROL_POINT_9_FOCUSED | LC_PIECE_CONTROL_POINT_10_FOCUSED) #define LC_PIECE_SELECTION_MASK (LC_PIECE_POSITION_SELECTED | LC_PIECE_CONTROL_POINT_SELECTION_MASK) #define LC_PIECE_FOCUS_MASK (LC_PIECE_POSITION_FOCUSED | LC_PIECE_CONTROL_POINT_FOCUS_MASK) enum lcPieceSection { LC_PIECE_SECTION_POSITION, LC_PIECE_SECTION_CONTROL_POINT_FIRST, LC_PIECE_SECTION_CONTROL_POINT_1 = LC_PIECE_SECTION_CONTROL_POINT_FIRST, LC_PIECE_SECTION_CONTROL_POINT_2, LC_PIECE_SECTION_CONTROL_POINT_3, LC_PIECE_SECTION_CONTROL_POINT_4, LC_PIECE_SECTION_CONTROL_POINT_5, LC_PIECE_SECTION_CONTROL_POINT_6, LC_PIECE_SECTION_CONTROL_POINT_7, LC_PIECE_SECTION_CONTROL_POINT_8, LC_PIECE_SECTION_CONTROL_POINT_9, LC_PIECE_SECTION_CONTROL_POINT_10, LC_PIECE_SECTION_CONTROL_POINT_LAST = LC_PIECE_SECTION_CONTROL_POINT_10 }; #define LC_PIECE_SECTION_INVALID (~0U) struct lcPieceControlPoint { lcMatrix44 Transform; float Scale; }; class lcPiece : public lcObject { public: lcPiece(PieceInfo* Info); lcPiece(const lcPiece& Other); ~lcPiece(); lcPiece(lcPiece&&) = delete; lcPiece& operator=(const lcPiece&) = delete; lcPiece& operator=(lcPiece&&) = delete; bool IsSelected() const override { return (mState & LC_PIECE_SELECTION_MASK) != 0; } bool IsSelected(quint32 Section) const override { Q_UNUSED(Section); return (mState & LC_PIECE_SELECTION_MASK) != 0; } void SetSelected(bool Selected) override { if (Selected) mState |= LC_PIECE_SELECTION_MASK; else mState &= ~(LC_PIECE_SELECTION_MASK | LC_PIECE_FOCUS_MASK); } void SetSelected(quint32 Section, bool Selected) override { switch (Section) { case LC_PIECE_SECTION_POSITION: if (Selected) mState |= LC_PIECE_POSITION_SELECTED; else mState &= ~(LC_PIECE_POSITION_SELECTED | LC_PIECE_POSITION_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_1: if (Selected) mState |= LC_PIECE_CONTROL_POINT_1_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_1_SELECTED | LC_PIECE_CONTROL_POINT_1_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_2: if (Selected) mState |= LC_PIECE_CONTROL_POINT_2_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_2_SELECTED | LC_PIECE_CONTROL_POINT_2_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_3: if (Selected) mState |= LC_PIECE_CONTROL_POINT_3_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_3_SELECTED | LC_PIECE_CONTROL_POINT_3_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_4: if (Selected) mState |= LC_PIECE_CONTROL_POINT_4_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_4_SELECTED | LC_PIECE_CONTROL_POINT_4_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_5: if (Selected) mState |= LC_PIECE_CONTROL_POINT_5_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_5_SELECTED | LC_PIECE_CONTROL_POINT_5_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_6: if (Selected) mState |= LC_PIECE_CONTROL_POINT_6_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_6_SELECTED | LC_PIECE_CONTROL_POINT_6_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_7: if (Selected) mState |= LC_PIECE_CONTROL_POINT_7_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_7_SELECTED | LC_PIECE_CONTROL_POINT_7_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_8: if (Selected) mState |= LC_PIECE_CONTROL_POINT_8_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_8_SELECTED | LC_PIECE_CONTROL_POINT_8_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_9: if (Selected) mState |= LC_PIECE_CONTROL_POINT_9_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_9_SELECTED | LC_PIECE_CONTROL_POINT_9_FOCUSED); break; case LC_PIECE_SECTION_CONTROL_POINT_10: if (Selected) mState |= LC_PIECE_CONTROL_POINT_10_SELECTED; else mState &= ~(LC_PIECE_CONTROL_POINT_10_SELECTED | LC_PIECE_CONTROL_POINT_10_FOCUSED); break; } } bool IsFocused() const override { return (mState & LC_PIECE_FOCUS_MASK) != 0; } bool IsFocused(quint32 Section) const override { switch (Section) { case LC_PIECE_SECTION_POSITION: return (mState & LC_PIECE_POSITION_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_1: return (mState & LC_PIECE_CONTROL_POINT_1_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_2: return (mState & LC_PIECE_CONTROL_POINT_2_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_3: return (mState & LC_PIECE_CONTROL_POINT_3_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_4: return (mState & LC_PIECE_CONTROL_POINT_4_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_5: return (mState & LC_PIECE_CONTROL_POINT_5_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_6: return (mState & LC_PIECE_CONTROL_POINT_6_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_7: return (mState & LC_PIECE_CONTROL_POINT_7_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_8: return (mState & LC_PIECE_CONTROL_POINT_8_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_9: return (mState & LC_PIECE_CONTROL_POINT_9_FOCUSED) != 0; case LC_PIECE_SECTION_CONTROL_POINT_10: return (mState & LC_PIECE_CONTROL_POINT_10_FOCUSED) != 0; } return false; } void SetFocused(quint32 Section, bool Focused) override { switch (Section) { case LC_PIECE_SECTION_POSITION: if (Focused) mState |= (LC_PIECE_POSITION_SELECTED | LC_PIECE_POSITION_FOCUSED); else mState &= ~LC_PIECE_POSITION_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_1: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_1_SELECTED | LC_PIECE_CONTROL_POINT_1_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_1_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_2: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_2_SELECTED | LC_PIECE_CONTROL_POINT_2_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_2_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_3: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_3_SELECTED | LC_PIECE_CONTROL_POINT_3_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_3_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_4: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_4_SELECTED | LC_PIECE_CONTROL_POINT_4_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_4_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_5: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_5_SELECTED | LC_PIECE_CONTROL_POINT_5_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_5_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_6: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_6_SELECTED | LC_PIECE_CONTROL_POINT_6_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_6_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_7: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_7_SELECTED | LC_PIECE_CONTROL_POINT_7_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_7_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_8: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_8_SELECTED | LC_PIECE_CONTROL_POINT_8_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_8_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_9: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_9_SELECTED | LC_PIECE_CONTROL_POINT_9_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_9_FOCUSED; break; case LC_PIECE_SECTION_CONTROL_POINT_10: if (Focused) mState |= (LC_PIECE_CONTROL_POINT_10_SELECTED | LC_PIECE_CONTROL_POINT_10_FOCUSED); else mState &= ~LC_PIECE_CONTROL_POINT_10_FOCUSED; break; } } quint32 GetFocusSection() const override { if (mState & LC_PIECE_POSITION_FOCUSED) return LC_PIECE_SECTION_POSITION; if (mState & LC_PIECE_CONTROL_POINT_1_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_1; if (mState & LC_PIECE_CONTROL_POINT_2_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_2; if (mState & LC_PIECE_CONTROL_POINT_3_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_3; if (mState & LC_PIECE_CONTROL_POINT_4_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_4; if (mState & LC_PIECE_CONTROL_POINT_5_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_5; if (mState & LC_PIECE_CONTROL_POINT_6_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_6; if (mState & LC_PIECE_CONTROL_POINT_7_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_7; if (mState & LC_PIECE_CONTROL_POINT_8_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_8; if (mState & LC_PIECE_CONTROL_POINT_9_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_9; if (mState & LC_PIECE_CONTROL_POINT_10_FOCUSED) return LC_PIECE_SECTION_CONTROL_POINT_10; return LC_PIECE_SECTION_INVALID; } quint32 GetAllowedTransforms() const override; lcVector3 GetSectionPosition(quint32 Section) const override { switch (Section) { case LC_PIECE_SECTION_POSITION: if (mState & LC_PIECE_PIVOT_POINT_VALID) return lcMul(mPivotMatrix, mModelWorld).GetTranslation(); else return mModelWorld.GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_1: return lcMul(mControlPoints[0].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_2: return lcMul(mControlPoints[1].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_3: return lcMul(mControlPoints[2].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_4: return lcMul(mControlPoints[3].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_5: return lcMul(mControlPoints[4].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_6: return lcMul(mControlPoints[5].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_7: return lcMul(mControlPoints[6].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_8: return lcMul(mControlPoints[7].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_9: return lcMul(mControlPoints[8].Transform, mModelWorld).GetTranslation(); case LC_PIECE_SECTION_CONTROL_POINT_10: return lcMul(mControlPoints[9].Transform, mModelWorld).GetTranslation(); } return lcVector3(0.0f, 0.0f, 0.0f); } void SaveLDraw(QTextStream& Stream) const; bool ParseLDrawLine(QTextStream& Stream); void SetFileLine(int Line) { mFileLine = Line; } int GetFileLine() const { return mFileLine; } void RayTest(lcObjectRayTest& ObjectRayTest) const override; void BoxTest(lcObjectBoxTest& ObjectBoxTest) const override; void DrawInterface(lcContext* Context, const lcScene& Scene) const override; void RemoveKeyFrames() override; void AddMainModelRenderMeshes(lcScene* Scene, bool Highlight, bool Fade) const; void AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const; void SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector3& Min, lcVector3& Max) const; void SubModelAddBoundingBoxPoints(const lcMatrix44& WorldMatrix, std::vector& Points) const; void InsertTime(lcStep Start, lcStep Time); void RemoveTime(lcStep Start, lcStep Time); bool IsHidden() const { return (mState & LC_PIECE_HIDDEN) != 0; } void SetHidden(bool Hidden) { if (Hidden) mState |= LC_PIECE_HIDDEN; else mState &= ~LC_PIECE_HIDDEN; } const lcArray& GetControlPoints() const { return mControlPoints; } void SetControlPoints(const lcArray& ControlPoints) { mControlPoints = ControlPoints; UpdateMesh(); } void SetControlPointScale(int ControlPointIndex, float Scale) { mControlPoints[ControlPointIndex].Scale = Scale; UpdateMesh(); } const QString& GetID() const { return mID; } void UpdateID(); QString GetName() const override; bool IsVisible(lcStep Step) const; bool IsVisibleInSubModel() const; void GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector& ModelParts) const; void Initialize(const lcMatrix44& WorldMatrix, lcStep Step); const lcBoundingBox& GetBoundingBox() const; void CompareBoundingBox(lcVector3& Min, lcVector3& Max) const; void SetPieceInfo(PieceInfo* Info, const QString& ID, bool Wait); bool FileLoad(lcFile& file); void UpdatePosition(lcStep Step); void MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance); void Rotate(lcStep Step, bool AddKey, const lcMatrix33& RotationMatrix, const lcVector3& Center, const lcMatrix33& RotationFrame); void MovePivotPoint(const lcVector3& Distance); void RotatePivotPoint(const lcMatrix33& RotationMatrix); bool CanAddControlPoint() const; bool CanRemoveControlPoint() const; bool InsertControlPoint(const lcVector3& WorldStart, const lcVector3& WorldEnd); bool RemoveFocusedControlPoint(); void VerifyControlPoints(lcArray& ControlPoints) const; lcGroup* GetTopGroup(); void SetGroup(lcGroup* Group) { mGroup = Group; } lcGroup* GetGroup() { return mGroup; } lcStep GetStepShow() const { return mStepShow; } lcStep GetStepHide() const { return mStepHide; } void SetStepHide(lcStep Step) { if (Step < 2) Step = 2; mStepHide = Step; if (mStepHide <= mStepShow) SetStepShow(mStepHide - 1); } void SetStepShow(lcStep Step) { if (Step == LC_STEP_MAX) Step = LC_STEP_MAX - 1; if (mStepShow != Step) { mFileLine = -1; mStepShow = Step; } if (mStepHide <= mStepShow) mStepHide = mStepShow + 1; } quint32 GetColorCode() const { return mColorCode; } void SetColorCode(quint32 ColorCode) { mColorCode = ColorCode; mColorIndex = lcGetColorIndex(ColorCode); } int GetColorIndex() const { return mColorIndex; } void SetColorIndex(int ColorIndex) { mColorIndex = ColorIndex; mColorCode = lcGetColorCode(ColorIndex); } void SetPosition(const lcVector3& Position, lcStep Step, bool AddKey) { mPositionKeys.ChangeKey(Position, Step, AddKey); } void SetRotation(const lcMatrix33& Rotation, lcStep Step, bool AddKey) { mRotationKeys.ChangeKey(Rotation, Step, AddKey); } lcVector3 GetRotationCenter() const { const quint32 Section = GetFocusSection(); if (Section == LC_PIECE_SECTION_POSITION || Section == LC_PIECE_SECTION_INVALID) { if (mState & LC_PIECE_PIVOT_POINT_VALID) return lcMul31(mPivotMatrix.GetTranslation(), mModelWorld); else return mModelWorld.GetTranslation(); } else { const int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_1; if (ControlPointIndex >= 0 && ControlPointIndex < mControlPoints.GetSize()) { const lcMatrix44& Transform = mControlPoints[ControlPointIndex].Transform; return lcMul31(Transform.GetTranslation(), mModelWorld); } return mModelWorld.GetTranslation(); } } lcMatrix33 GetRelativeRotation() const { const quint32 Section = GetFocusSection(); if (Section == LC_PIECE_SECTION_POSITION || Section == LC_PIECE_SECTION_INVALID) { if (mState & LC_PIECE_PIVOT_POINT_VALID) return lcMatrix33(lcMul(mModelWorld, mPivotMatrix)); else return lcMatrix33(mModelWorld); } else { const int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_1; if (ControlPointIndex >= 0 && ControlPointIndex < mControlPoints.GetSize()) { const lcMatrix44& Transform = mControlPoints[ControlPointIndex].Transform; return lcMatrix33(lcMul(Transform, mModelWorld)); } return lcMatrix33Identity(); } } void ResetPivotPoint() { mState &= ~LC_PIECE_PIVOT_POINT_VALID; mPivotMatrix = lcMatrix44Identity(); } public: PieceInfo* mPieceInfo; lcMatrix44 mModelWorld; lcMatrix44 mPivotMatrix; protected: void UpdateMesh(); bool IsPivotPointVisible() const { return (mState & LC_PIECE_PIVOT_POINT_VALID) && IsFocused(); } bool AreControlPointsVisible() const { return IsSelected(); } lcObjectKeyArray mPositionKeys; lcObjectKeyArray mRotationKeys; int mFileLine; QString mID; lcGroup* mGroup; int mColorIndex; quint32 mColorCode; lcStep mStepShow; lcStep mStepHide; quint32 mState; lcArray mControlPoints; lcMesh* mMesh; };