From 50b4732f1b0898769400bfa1b19a4dcc710ed890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sat, 14 Jan 2017 13:37:39 -0800 Subject: [PATCH] Updated ImGuizmo. --- 3rdparty/ocornut-imgui/widgets/gizmo.h | 4 +- 3rdparty/ocornut-imgui/widgets/gizmo.inl | 275 +++++++++++++++++++---- 2 files changed, 237 insertions(+), 42 deletions(-) diff --git a/3rdparty/ocornut-imgui/widgets/gizmo.h b/3rdparty/ocornut-imgui/widgets/gizmo.h index 7b398fefe..caac98694 100644 --- a/3rdparty/ocornut-imgui/widgets/gizmo.h +++ b/3rdparty/ocornut-imgui/widgets/gizmo.h @@ -143,7 +143,7 @@ namespace ImGuizmo { TRANSLATE, ROTATE, - SCALE + SCALE, }; enum MODE @@ -152,5 +152,5 @@ namespace ImGuizmo WORLD }; - void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix = 0, float *snap = 0); + void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix = 0, float *snap = 0, float *localBounds = NULL, float *boundsSnap = NULL); }; diff --git a/3rdparty/ocornut-imgui/widgets/gizmo.inl b/3rdparty/ocornut-imgui/widgets/gizmo.inl index 0ae83f530..d9342bb2b 100644 --- a/3rdparty/ocornut-imgui/widgets/gizmo.inl +++ b/3rdparty/ocornut-imgui/widgets/gizmo.inl @@ -89,7 +89,7 @@ namespace ImGuizmo float LengthSq() const { return (x*x + y*y + z*z); }; vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); } vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); } - + vec_t Abs() const; void Cross(const vec_t& v) { vec_t res; @@ -136,7 +136,7 @@ namespace ImGuizmo vec_t vec_t::operator - (const vec_t& v) const { return makeVect(x - v.x, y - v.y, z - v.z, w - v.w); } vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); } vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); } - + vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); } ImVec2 operator+ (const ImVec2& a, const ImVec2& b) { return ImVec2(a.x + b.x, a.y + b.y); } vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; } @@ -178,6 +178,7 @@ namespace ImGuizmo { vec_t right, up, dir, position; } v; + vec_t component[4]; }; matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); } @@ -239,7 +240,6 @@ namespace ImGuizmo } float Inverse(const matrix_t &srcMatrix, bool affine = false); - float Inverse(bool affine = false); void SetToIdentity() { v.right.Set(1.f, 0.f, 0.f, 0.f); @@ -481,11 +481,12 @@ namespace ImGuizmo SCALE_Y, SCALE_Z, SCALE_XYZ, + BOUNDS }; struct Context { - Context() : mbUsing(false), mbEnable(true) + Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false) { } @@ -540,6 +541,17 @@ namespace ImGuizmo bool mBelowPlaneLimit[3]; float mAxisFactor[3]; + // bounds stretching + vec_t mBoundsPivot; + vec_t mBoundsAnchor; + vec_t mBoundsPlan; + vec_t mBoundsLocalPivot; + int mBoundsBestAxis; + int mBoundsAxis[2]; + bool mbUsingBounds; + matrix_t mBoundsMatrix; + + // int mCurrentOperation; }; @@ -625,7 +637,7 @@ namespace ImGuizmo bool IsUsing() { - return gContext.mbUsing; + return gContext.mbUsing||gContext.mbUsingBounds; } bool IsOver() @@ -636,8 +648,11 @@ namespace ImGuizmo void Enable(bool enable) { gContext.mbEnable = enable; - if (!enable) - gContext.mbUsing = false; + if (!enable) + { + gContext.mbUsing = false; + gContext.mbUsingBounds = false; + } } static float GetUniform(const vec_t& position, const matrix_t& mat) @@ -770,22 +785,22 @@ namespace ImGuizmo } } - static void ComputeSnap(float*value, float *snap) + static void ComputeSnap(float*value, float snap) { - if (*snap <= FLT_EPSILON) + if (snap <= FLT_EPSILON) return; - float modulo = fmodf(*value, *snap); - float moduloRatio = fabsf(modulo) / *snap; + float modulo = fmodf(*value, snap); + float moduloRatio = fabsf(modulo) / snap; if (moduloRatio < snapTension) *value -= modulo; else if (moduloRatio >(1.f - snapTension)) - *value = *value - modulo + *snap * ((*value<0.f) ? -1.f : 1.f); + *value = *value - modulo + snap * ((*value<0.f) ? -1.f : 1.f); } static void ComputeSnap(vec_t& value, float *snap) { for (int i = 0; i < 3; i++) { - ComputeSnap(&value[i], &snap[i]); + ComputeSnap(&value[i], snap[i]); } } @@ -996,6 +1011,178 @@ namespace ImGuizmo } } + static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues) + { + ImGuiIO& io = ImGui::GetIO(); + ImDrawList* drawList = gContext.mDrawList; + + // compute best projection axis + vec_t bestAxisWorldDirection; + int bestAxis = gContext.mBoundsBestAxis; + if (!gContext.mbUsingBounds) + { + + float bestDot = 0.f; + for (unsigned int i = 0; i < 3; i++) + { + vec_t dirPlaneNormalWorld; + dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource); + dirPlaneNormalWorld.Normalize(); + + float dt = Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld); + if (fabsf(dt) >= bestDot) + { + bestDot = fabsf(dt); + bestAxis = i; + bestAxisWorldDirection = dirPlaneNormalWorld; + } + } + } + + // corners + vec_t aabb[4]; + + int secondAxis = (bestAxis + 1) % 3; + int thirdAxis = (bestAxis + 2) % 3; + + for (int i = 0; i < 4; i++) + { + aabb[i][3] = aabb[i][bestAxis] = 0.f; + aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; + aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; + } + + // draw bounds + unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000; + + matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; + for (int i = 0; i < 4;i++) + { + ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP); + ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP); + float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2)); + int stepCount = (int)(boundDistance / 10.f); + float stepLength = 1.f / (float)stepCount; + for (int j = 0; j < stepCount; j++) + { + float t1 = (float)j * stepLength; + float t2 = (float)j * stepLength + stepLength * 0.5f; + ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1)); + ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2)); + drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 3.f); + } + vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f; + ImVec2 midBound = worldToPos(midPoint, boundsMVP); + static const float AnchorBigRadius = 8.f; + static const float AnchorSmallRadius = 6.f; + bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius); + bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius); + + + unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); + unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha); + + drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor); + drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor); + int oppositeIndex = (i + 2) % 4; + // big anchor on corners + if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && io.MouseDown[0]) + { + gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource); + gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); + gContext.mBoundsBestAxis = bestAxis; + gContext.mBoundsAxis[0] = secondAxis; + gContext.mBoundsAxis[1] = thirdAxis; + + gContext.mBoundsLocalPivot.Set(0.f); + gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis]; + gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis]; + + gContext.mbUsingBounds = true; + gContext.mBoundsMatrix = gContext.mModelSource; + } + // small anchor on middle of segment + if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && io.MouseDown[0]) + { + vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; + gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); + gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); + gContext.mBoundsBestAxis = bestAxis; + int indices[] = { secondAxis , thirdAxis }; + gContext.mBoundsAxis[0] = indices[i%2]; + gContext.mBoundsAxis[1] = -1; + + gContext.mBoundsLocalPivot.Set(0.f); + gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); + + gContext.mbUsingBounds = true; + gContext.mBoundsMatrix = gContext.mModelSource; + } + } + + if (gContext.mbUsingBounds) + { + matrix_t scale; + scale.SetToIdentity(); + + // compute projected mouse position on plan + const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan); + vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len; + + // compute a reference and delta vectors base on mouse move + vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); + vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs(); + + // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length + for (int i = 0; i < 2; i++) + { + int axisIndex = gContext.mBoundsAxis[i]; + if (axisIndex == -1) + continue; + + float ratioAxis = 1.f; + vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex].Abs(); + + float dtAxis = axisDir.Dot(referenceVector); + float boundSize = bounds[axisIndex + 3] - bounds[axisIndex]; + if (dtAxis > FLT_EPSILON) + ratioAxis = axisDir.Dot(deltaVector) / dtAxis; + + if (snapValues) + { + float length = boundSize * ratioAxis; + ComputeSnap(&length, snapValues[axisIndex]); + if (boundSize > FLT_EPSILON) + ratioAxis = length / boundSize; + } + scale.component[axisIndex] *= ratioAxis; + } + + // transform matrix + matrix_t preScale, postScale; + preScale.Translation(-gContext.mBoundsLocalPivot); + postScale.Translation(gContext.mBoundsLocalPivot); + matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix; + *matrix = res; + + // info text + char tmps[512]; + ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); + ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f" + , (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length() + , (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length() + , (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length() + ); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps); + drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps); + } + + if (!io.MouseDown[0]) + gContext.mbUsingBounds = false; + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -1311,7 +1498,7 @@ namespace ImGuizmo if (snap) { float snapInRadian = snap[0] * DEG2RAD; - ComputeSnap(&gContext.mRotationAngle, &snapInRadian); + ComputeSnap(&gContext.mRotationAngle, snapInRadian); } vec_t rotationAxisLocalSpace; @@ -1393,7 +1580,7 @@ namespace ImGuizmo mat.v.position.Set(translation[0], translation[1], translation[2], 1.f); } - void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix, float *snap) + void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix, float *snap, float *localBounds, float *boundsSnap) { ComputeContext(view, projection, matrix, mode); @@ -1411,32 +1598,41 @@ namespace ImGuizmo int type = NONE; if (gContext.mbEnable) { - switch (operation) - { - case ROTATE: - HandleRotation(matrix, deltaMatrix, type, snap); - break; - case TRANSLATE: - HandleTranslation(matrix, deltaMatrix, type, snap); - break; - case SCALE: - HandleScale(matrix, deltaMatrix, type, snap); - break; - } + if (!gContext.mbUsingBounds) + { + switch (operation) + { + case ROTATE: + HandleRotation(matrix, deltaMatrix, type, snap); + break; + case TRANSLATE: + HandleTranslation(matrix, deltaMatrix, type, snap); + break; + case SCALE: + HandleScale(matrix, deltaMatrix, type, snap); + break; + } + } } - switch (operation) - { - case ROTATE: - DrawRotationGizmo(type); - break; - case TRANSLATE: - DrawTranslationGizmo(type); - break; - case SCALE: - DrawScaleGizmo(type); - break; - } + if (localBounds && !gContext.mbUsing) + HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap); + + if (!gContext.mbUsingBounds) + { + switch (operation) + { + case ROTATE: + DrawRotationGizmo(type); + break; + case TRANSLATE: + DrawTranslationGizmo(type); + break; + case SCALE: + DrawScaleGizmo(type); + break; + } + } } void DrawCube(const float *view, const float *projection, float *matrix) @@ -1492,4 +1688,3 @@ namespace ImGuizmo } } }; -