diff --git a/common/camera.cpp b/common/camera.cpp index b28479e2..6703c008 100644 --- a/common/camera.cpp +++ b/common/camera.cpp @@ -996,6 +996,28 @@ void lcCamera::SetViewpoint(lcViewpoint Viewpoint) UpdatePosition(1); } +void lcCamera::SetViewpoint(const lcVector3& Position) +{ + mPosition = Position; + mTargetPosition = lcVector3(0, 0, 0); + + lcVector3 UpVector(0, 0, 1), FrontVector(Position), SideVector; + FrontVector.Normalize(); + if (FrontVector == UpVector) + SideVector = lcVector3(1, 0, 0); + else + SideVector = lcCross(FrontVector, UpVector); + UpVector = lcCross(SideVector, FrontVector); + UpVector.Normalize(); + mUpVector = UpVector; + + ChangeKey(mPositionKeys, mPosition, 1, false); + ChangeKey(mTargetPositionKeys, mTargetPosition, 1, false); + ChangeKey(mUpVectorKeys, mUpVector, 1, false); + + UpdatePosition(1); +} + void lcCamera::SetAngles(float Latitude, float Longitude) { mPosition = lcVector3(0, -1, 0); diff --git a/common/camera.h b/common/camera.h index 513278b7..88b03f7d 100644 --- a/common/camera.h +++ b/common/camera.h @@ -284,6 +284,7 @@ public: void MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance); void MoveRelative(const lcVector3& Distance, lcStep Step, bool AddKey); void SetViewpoint(lcViewpoint Viewpoint); + void SetViewpoint(const lcVector3& Position); void SetAngles(float Latitude, float Longitude); char m_strName[81]; diff --git a/common/lc_viewcube.cpp b/common/lc_viewcube.cpp new file mode 100644 index 00000000..996978c0 --- /dev/null +++ b/common/lc_viewcube.cpp @@ -0,0 +1,293 @@ +#include "lc_global.h" +#include "lc_viewcube.h" +#include "view.h" +#include "lc_context.h" +#include "texfont.h" + +//todo: move these and add preferences +const int ViewportSize = 100; +const float BoxSize = 10.0f; + +lcViewCube::lcViewCube(View* View) + : mView(View) +{ + mIntersectionFlags = 0; +} + +lcMatrix44 lcViewCube::GetViewMatrix() const +{ + lcMatrix44 ViewMatrix = mView->mCamera->mWorldView; + ViewMatrix.SetTranslation(lcVector3(0, 0, 0)); + return ViewMatrix; +} + +lcMatrix44 lcViewCube::GetProjectionMatrix() const +{ + return lcMatrix44Ortho(-BoxSize * 2, BoxSize * 2, -BoxSize * 2, BoxSize * 2, -50, 50); +} + +void lcViewCube::Draw() +{ + lcContext* Context = mView->mContext; + int Width = mView->mWidth; + int Height = mView->mHeight; + + Context->SetViewport(Width - ViewportSize, Height - ViewportSize, ViewportSize, ViewportSize); + + const lcVector3 BoxVerts[8] = + { + lcVector3(-BoxSize, -BoxSize, -BoxSize), lcVector3(-BoxSize, BoxSize, -BoxSize), lcVector3(BoxSize, BoxSize, -BoxSize), lcVector3(BoxSize, -BoxSize, -BoxSize), + lcVector3(-BoxSize, -BoxSize, BoxSize), lcVector3(-BoxSize, BoxSize, BoxSize), lcVector3(BoxSize, BoxSize, BoxSize), lcVector3(BoxSize, -BoxSize, BoxSize) + }; + + const GLushort BoxIndices[36 + 24] = + { + 0, 1, 2, 0, 2, 3, 7, 6, 5, 7, 5, 4, 0, 1, 5, 0, 5, 4, 2, 3, 7, 2, 7, 6, 0, 3, 7, 0, 7, 4, 1, 2, 6, 1, 6, 5, + 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->SetMaterial(LC_MATERIAL_UNLIT_COLOR); + Context->SetWorldMatrix(lcMatrix44Identity()); + Context->SetViewMatrix(GetViewMatrix()); + Context->SetProjectionMatrix(GetProjectionMatrix()); + + Context->SetVertexBufferPointer(BoxVerts); + Context->SetVertexFormatPosition(3); + Context->SetIndexBufferPointer(BoxIndices); + + Context->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + Context->DrawIndexedPrimitives(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0); + Context->SetColor(0.0f, 0.0f, 0.0f, 1.0f); + Context->DrawIndexedPrimitives(GL_LINES, 24, GL_UNSIGNED_SHORT, 36 * 2); + + glDisable(GL_DEPTH_TEST); // TODO: don't disable depth, fix z fighting + + int IntersectionType = mIntersectionFlags.count(); + if (IntersectionType == 1) + { + lcVector3 Center(0.0f, 0.0f, 0.0f), Dx(0.0f, 0.0f, 0.0f), Dy(0.0f, 0.0f, 0.0f); + int AxisIdx; + + for (AxisIdx = 0; AxisIdx < 3; AxisIdx++) + { + if (mIntersectionFlags.test(AxisIdx * 2)) + { + Center[AxisIdx] = -BoxSize; + break; + } + else if (mIntersectionFlags.test(AxisIdx * 2 + 1)) + { + Center[AxisIdx] = BoxSize; + break; + } + } + + Dx[(AxisIdx + 1) % 3] = BoxSize * 2 / 3; + Dy[(AxisIdx + 2) % 3] = BoxSize * 2 / 3; + + lcVector3 FaceVerts[] = + { + Center - Dx - Dy, Center + Dx - Dy, Center - Dx + Dy, Center + Dx + Dy + }; + + Context->SetVertexBufferPointer(FaceVerts); + Context->SetVertexFormatPosition(3); + + Context->SetColor(1.0f, 0.0f, 0.0f, 1.0f); + Context->DrawPrimitives(GL_TRIANGLE_STRIP, 0, 4); + } + else if (IntersectionType == 2) + { + lcVector3 Center(0.0f, 0.0f, 0.0f), Dx(0.0f, 0.0f, 0.0f), Dy(0.0f, 0.0f, 0.0f), Dz(0.0f, 0.0f, 0.0f); + int Corners = 0; + + for (int AxisIdx = 0; AxisIdx < 3; AxisIdx++) + { + if (mIntersectionFlags.test(AxisIdx * 2)) + { + if (!Corners++) + Dx[AxisIdx] = BoxSize * 1 / 3; + else + Dy[AxisIdx] = BoxSize * 1 / 3; + + Center[AxisIdx] = -BoxSize; + } + else if (mIntersectionFlags.test(AxisIdx * 2 + 1)) + { + if (!Corners++) + Dx[AxisIdx] = -BoxSize * 1 / 3; + else + Dy[AxisIdx] = -BoxSize * 1 / 3; + + Center[AxisIdx] = BoxSize; + } + else + Dz[AxisIdx] = BoxSize * 2 / 3; + } + + lcVector3 FaceVerts[] = + { + Center - Dz, Center + Dx - Dz, Center + Dz, Center + Dx + Dz, + Center - Dz, Center + Dy - Dz, Center + Dz, Center + Dy + Dz + }; + + Context->SetVertexBufferPointer(FaceVerts); + Context->SetVertexFormatPosition(3); + + Context->SetColor(1.0f, 0.0f, 0.0f, 1.0f); + Context->DrawPrimitives(GL_TRIANGLE_STRIP, 0, 4); + Context->DrawPrimitives(GL_TRIANGLE_STRIP, 4, 4); + } + else if (IntersectionType == 3) + { + lcVector3 Signs; + + for (int AxisIdx = 0; AxisIdx < 3; AxisIdx++) + { + if (mIntersectionFlags.test(AxisIdx * 2)) + Signs[AxisIdx] = -1.0f; + else + Signs[AxisIdx] = 1.0f; + } + + lcVector3 Center = Signs * BoxSize; + lcVector3 Dx = lcVector3(BoxSize * 1 / 3 * Signs[0], 0.0f, 0.0f); + lcVector3 Dy = lcVector3(0.0f, BoxSize * 1 / 3 * Signs[1], 0.0f); + lcVector3 Dz = lcVector3(0.0f, 0.0f, BoxSize * 1 / 3 * Signs[2]); + + lcVector3 FaceVerts[] = + { + Center - Dz, Center - Dx - Dz, Center, Center - Dx, + Center - Dz, Center - Dy - Dz, Center, Center - Dy, + Center - Dx, Center - Dy - Dx, Center, Center - Dy + }; + + Context->SetVertexBufferPointer(FaceVerts); + Context->SetVertexFormatPosition(3); + + Context->SetColor(1.0f, 0.0f, 0.0f, 1.0f); + Context->DrawPrimitives(GL_TRIANGLE_STRIP, 0, 4); + Context->DrawPrimitives(GL_TRIANGLE_STRIP, 4, 4); + Context->DrawPrimitives(GL_TRIANGLE_STRIP, 8, 4); + } + + glEnable(GL_DEPTH_TEST); + + Context->SetMaterial(LC_MATERIAL_UNLIT_TEXTURE_MODULATE); + Context->BindTexture2D(gTexFont.GetTexture()); + glEnable(GL_BLEND); + + const char* ViewNames[6] = { "Front", "Back", "Top", "Bottom", "Left", "Right" }; + + int MaxText = 0; + + for (int FaceIdx = 0; FaceIdx < 6; FaceIdx++) + { + int Width, Height; + gTexFont.GetStringDimensions(&Width, &Height, ViewNames[FaceIdx]); + if (Width > MaxText) + MaxText = Width; + if (Height > MaxText) + MaxText = Height; + } + + float Scale = (BoxSize * 2.0f - 4.0f) / (float)MaxText; + + lcMatrix44 ViewMatrices[6] = + { + lcMatrix44(lcVector4(Scale, 0.0f, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, Scale, 0.0f), lcVector4(0.0f, 1.0f, 0.0f, 0.0f), lcVector4(0.0f, -BoxSize - 0.01f, 0.0f, 1.0f)), + lcMatrix44(lcVector4(-Scale, 0.0f, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, Scale, 0.0f), lcVector4(0.0f, 1.0f, 0.0f, 0.0f), lcVector4(0.0f, BoxSize + 0.01f, 0.0f, 1.0f)), + lcMatrix44(lcVector4(Scale, 0.0f, 0.0f, 0.0f), lcVector4(0.0f, Scale, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, 1.0f, 0.0f), lcVector4(0.0f, 0.0f, BoxSize + 0.01f, 1.0f)), + lcMatrix44(lcVector4(Scale, 0.0f, 0.0f, 0.0f), lcVector4(0.0f, -Scale, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, 1.0f, 0.0f), lcVector4(0.0f, 0.0f, -BoxSize - 0.01f, 1.0f)), + lcMatrix44(lcVector4(0.0f, Scale, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, Scale, 0.0f), lcVector4(1.0f, 0.0f, 0.0f, 0.0f), lcVector4(BoxSize + 0.01f, 0.0f, 0.0f, 1.0f)), + lcMatrix44(lcVector4(0.0f, -Scale, 0.0f, 0.0f), lcVector4(0.0f, 0.0f, Scale, 0.0f), lcVector4(1.0f, 0.0f, 0.0f, 0.0f), lcVector4(-BoxSize - 0.01f, 0.0f, 0.0f, 1.0f)) + }; + + float TextBuffer[256 * 5 * 3]; + int CharsWritten = 0; + + for (int FaceIdx = 0; FaceIdx < 6; FaceIdx++) + { + const char* ViewName = ViewNames[FaceIdx]; + gTexFont.GetTriangles(ViewMatrices[FaceIdx], ViewName, TextBuffer + CharsWritten * 2 * 3 * 5); + CharsWritten += strlen(ViewName); + } + + Context->SetVertexBufferPointer(TextBuffer); + Context->SetVertexFormat(0, 3, 0, 2, 0, false); + + Context->SetColor(0.0f, 0.0f, 0.0f, 1.0f); + Context->DrawPrimitives(GL_TRIANGLES, 0, CharsWritten * 2 * 3); + + glDisable(GL_BLEND); + + Context->SetViewport(0, 0, Width, Height); +} + +bool lcViewCube::OnLeftButtonUp() +{ + if (!mIntersectionFlags.any()) + return false; + + lcVector3 Position(0.0f, 0.0f, 0.0f); + + for (int AxisIdx = 0; AxisIdx < 3; AxisIdx++) + { + if (mIntersectionFlags.test(AxisIdx * 2)) + Position[AxisIdx] = -BoxSize; + else if (mIntersectionFlags.test(AxisIdx * 2 + 1)) + Position[AxisIdx] = BoxSize; + } + + mView->SetViewpoint(Position); + + return true; +} + +bool lcViewCube::OnMouseMove() +{ + int x = mView->mInputState.x; + int y = mView->mInputState.y; + int Width = mView->mWidth; + int Height = mView->mHeight; + + if (x < Width - ViewportSize || x > Width || y < Height - ViewportSize || y > Height) + { + if (mIntersectionFlags.any()) + { + mIntersectionFlags.reset(); + mView->Redraw(); + } + + return false; + } + + x -= Width - ViewportSize; + y -= Height - ViewportSize; + + lcVector3 StartEnd[2] = { lcVector3(x, y, 0), lcVector3(x, y, 1) }; + const int Viewport[4] = { 0, 0, ViewportSize, ViewportSize }; + + lcUnprojectPoints(StartEnd, 2, GetViewMatrix(), GetProjectionMatrix(), Viewport); + std::bitset<6> IntersectionFlags; + + float Distance; + if (lcBoundingBoxRayIntersectDistance(lcVector3(-BoxSize, -BoxSize, -BoxSize), lcVector3(BoxSize, BoxSize, BoxSize), StartEnd[0], StartEnd[1], &Distance, &mIntersection)) + { + for (int AxisIdx = 0; AxisIdx < 3; AxisIdx++) + { + if (mIntersection[AxisIdx] < -BoxSize * 2 / 3) + IntersectionFlags.set(2 * AxisIdx); + else if (mIntersection[AxisIdx] > BoxSize * 2 / 3) + IntersectionFlags.set(2 * AxisIdx + 1); + } + } + + if (IntersectionFlags != mIntersectionFlags) + { + mIntersectionFlags = IntersectionFlags; + mView->Redraw(); + } + + return true; +} diff --git a/common/lc_viewcube.h b/common/lc_viewcube.h new file mode 100644 index 00000000..7b278513 --- /dev/null +++ b/common/lc_viewcube.h @@ -0,0 +1,24 @@ +#pragma once + +#include "lc_math.h" +#include + +class View; + +class lcViewCube +{ +public: + lcViewCube(View* View); + + void Draw(); + bool OnMouseMove(); + bool OnLeftButtonUp(); + +protected: + lcMatrix44 GetViewMatrix() const; + lcMatrix44 GetProjectionMatrix() const; + + View* mView; + lcVector3 mIntersection; + std::bitset<6> mIntersectionFlags; +}; diff --git a/common/texfont.cpp b/common/texfont.cpp index 6f473f2b..a7b80122 100644 --- a/common/texfont.cpp +++ b/common/texfont.cpp @@ -239,6 +239,73 @@ void TexFont::PrintText(lcContext* Context, float Left, float Top, float Z, cons delete[] Verts; } +void TexFont::GetTriangles(const lcMatrix44& Transform, const char* Text, float* Buffer) const +{ + float Width = 0.0f; + + for (const char* ch = Text; *ch; ch++) + Width += mGlyphs[*ch].width; + + float Left = -Width / 2.0f; + float Top = mFontHeight / 2.0f; + float Z = 0.0f; + + while (*Text) + { + int ch = *Text; + + lcVector3 Points[4] = + { + lcVector3(Left, Top, Z), + lcVector3(Left, Top - mFontHeight, Z), + lcVector3(Left + mGlyphs[ch].width, Top - mFontHeight, Z), + lcVector3(Left + mGlyphs[ch].width, Top, Z), + }; + + for (int PointIdx = 0; PointIdx < 4; PointIdx++) + Points[PointIdx] = lcMul31(Points[PointIdx], Transform); + + *Buffer++ = Points[0].x; + *Buffer++ = Points[0].y; + *Buffer++ = Points[0].z; + *Buffer++ = mGlyphs[ch].left; + *Buffer++ = mGlyphs[ch].top; + + *Buffer++ = Points[1].x; + *Buffer++ = Points[1].y; + *Buffer++ = Points[1].z; + *Buffer++ = mGlyphs[ch].left; + *Buffer++ = mGlyphs[ch].bottom; + + *Buffer++ = Points[2].x; + *Buffer++ = Points[2].y; + *Buffer++ = Points[2].z; + *Buffer++ = mGlyphs[ch].right; + *Buffer++ = mGlyphs[ch].bottom; + + *Buffer++ = Points[2].x; + *Buffer++ = Points[2].y; + *Buffer++ = Points[2].z; + *Buffer++ = mGlyphs[ch].right; + *Buffer++ = mGlyphs[ch].bottom; + + *Buffer++ = Points[3].x; + *Buffer++ = Points[3].y; + *Buffer++ = Points[3].z; + *Buffer++ = mGlyphs[ch].right; + *Buffer++ = mGlyphs[ch].top; + + *Buffer++ = Points[0].x; + *Buffer++ = Points[0].y; + *Buffer++ = Points[0].z; + *Buffer++ = mGlyphs[ch].left; + *Buffer++ = mGlyphs[ch].top; + + Left += mGlyphs[ch].width; + Text++; + } +} + void TexFont::GetGlyphTriangles(float Left, float Top, float Z, int Glyph, float* Buffer) const { Left -= mGlyphs[Glyph].width / 2.0f; diff --git a/common/texfont.h b/common/texfont.h index 21f53deb..7771f17a 100644 --- a/common/texfont.h +++ b/common/texfont.h @@ -20,6 +20,7 @@ public: void Release(); void PrintText(lcContext* Context, float Left, float Top, float Z, const char* Text) const; + void GetTriangles(const lcMatrix44& Transform, const char* Text, float* Buffer) const; void GetGlyphTriangles(float Left, float Top, float Z, int Glyph, float* Buffer) const; void GetStringDimensions(int* cx, int* cy, const char* Text) const; diff --git a/common/view.cpp b/common/view.cpp index 15f61d58..5c4962bd 100644 --- a/common/view.cpp +++ b/common/view.cpp @@ -13,6 +13,7 @@ lcVertexBuffer View::mRotateMoveVertexBuffer; lcIndexBuffer View::mRotateMoveIndexBuffer; View::View(lcModel* Model) + : mViewCube(this) { mModel = Model; mActiveSubmodelInstance = nullptr; @@ -314,6 +315,18 @@ void View::SetViewpoint(lcViewpoint Viewpoint) gMainWindow->UpdateCurrentCamera(-1); } +void View::SetViewpoint(const lcVector3& Position) +{ + if (!mCamera || !mCamera->IsSimple()) + mCamera = new lcCamera(true); + + mCamera->SetViewpoint(Position); + ZoomExtents(); + Redraw(); + + gMainWindow->UpdateCurrentCamera(-1); +} + void View::SetCameraAngles(float Latitude, float Longitude) { if (!mCamera || !mCamera->IsSimple()) @@ -855,6 +868,7 @@ void View::OnDraw() else if (Tool == LC_TOOL_ROTATE_VIEW && mTrackButton == LC_TRACKBUTTON_NONE) DrawRotateViewOverlay(); + mViewCube.Draw(); DrawViewport(); } @@ -1709,7 +1723,7 @@ void View::DrawAxes() mContext->SetColor(0.0f, 0.0f, 0.0f, 1.0f); mContext->DrawIndexedPrimitives(GL_LINES, 6, GL_UNSIGNED_SHORT, 0); - mContext->SetColor(0.8f, 0.0f, 0.0f, 1.0f), + mContext->SetColor(0.8f, 0.0f, 0.0f, 1.0f); mContext->DrawIndexedPrimitives(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, 6 * 2); mContext->SetColor(0.0f, 0.8f, 0.0f, 1.0f); mContext->DrawIndexedPrimitives(GL_TRIANGLES, 24, GL_UNSIGNED_SHORT, (6 + 24) * 2); @@ -2756,6 +2770,9 @@ void View::OnLeftButtonDown() void View::OnLeftButtonUp() { StopTracking(mTrackButton == LC_TRACKBUTTON_LEFT); + + if (mViewCube.OnLeftButtonUp()) + return; } void View::OnLeftButtonDoubleClick() @@ -2850,6 +2867,9 @@ void View::OnMouseMove() if (mTrackButton == LC_TRACKBUTTON_NONE) { + if (mViewCube.OnMouseMove()) + return; + UpdateTrackTool(); if (mTrackTool == LC_TRACKTOOL_INSERT) diff --git a/common/view.h b/common/view.h index a029021e..8e8662c4 100644 --- a/common/view.h +++ b/common/view.h @@ -3,6 +3,8 @@ #include "lc_glwidget.h" #include "camera.h" #include "lc_scene.h" +#include "lc_viewcube.h" +#include "lc_commands.h" enum lcTrackButton { @@ -109,6 +111,7 @@ public: void SetCamera(const char* CameraName); void SetCameraIndex(int Index); void SetViewpoint(lcViewpoint Viewpoint); + void SetViewpoint(const lcVector3& Position); void SetCameraAngles(float Latitude, float Longitude); void SetDefaultCamera(); lcMatrix44 GetProjectionMatrix() const; @@ -187,6 +190,7 @@ protected: bool mHighlight; QImage mRenderImage; std::pair mRenderFramebuffer; + lcViewCube mViewCube; lcVertexBuffer mGridBuffer; int mGridSettings[7];