diff options
author | hnOsmium0001 <[email protected]> | 2022-05-09 18:02:41 -0700 |
---|---|---|
committer | hnOsmium0001 <[email protected]> | 2022-05-09 18:02:41 -0700 |
commit | 30cac681caa29a89cebfb8b558ccfddf32e9bc37 (patch) | |
tree | 9566c23b09c92e442b3143a42ca3bc7b99f08575 | |
parent | e98ea6ca4c586b5e27e6acf32e526c0d6a7b04a6 (diff) |
-rw-r--r-- | source/EditorGuizmo.cpp | 242 | ||||
-rw-r--r-- | source/EditorGuizmo.hpp | 12 |
2 files changed, 153 insertions, 101 deletions
diff --git a/source/EditorGuizmo.cpp b/source/EditorGuizmo.cpp index a0ee212..3e4f890 100644 --- a/source/EditorGuizmo.cpp +++ b/source/EditorGuizmo.cpp @@ -737,13 +737,25 @@ struct Context { float mAxisFactor[3]; // bounds stretching + // NOTE: these variable only lives during the duration of a drag + /// Position in world space, of the knob on the opposite side of the knob being dragged. + /// This is the point that needs to space regardless of where anchor is placed. vec_t mBoundsPivot; + /// Position in world space, of the knob begin dragged. + /// This is the point that's being moved. vec_t mBoundsAnchor; vec_t mBoundsPlan; + /// Position in local space, of the knob on the opposite side of the knob being dragged vec_t mBoundsLocalPivot; int mBoundsBestAxis; + /// The axes that are being modified by the current operation. May contain 1 or 2 elements. + /// Unused elements are filled with -1 during the operation. int mBoundsAxis[2]; + /// The index of the corner that pivot data is fetched from (opposite side from anchor). + int mBoundsPivotCornerIndex; bool mbUsingBounds; + bool mbIsUsingBigAnchor; + /// Model matrix passed into ImGuizmo::Manipulate() matrix_t mBoundsMatrix; // @@ -1418,17 +1430,17 @@ static void DrawScaleUniveralGizmo(OPERATION op, int type) { ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal); #if 0 - if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) - { - drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f); - drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF)); - } - /* - if (!hasTranslateOnAxis || gContext.mbUsing) - { - drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); - } - */ + if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) + { + drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f); + drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF)); + } + /* + if (!hasTranslateOnAxis || gContext.mbUsing) + { + drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f); + } + */ #endif drawList->AddCircleFilled(worldDirSSpace, 12.f, colors[i + 1]); } @@ -1559,7 +1571,7 @@ static bool CanActivate() { return false; } -static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, const float* snapValues, OPERATION operation) { +static bool HandleAndDrawLocalBounds(float* bounds, matrix_t* matrix, const float* snapValues, OPERATION operation) { ImGuiIO& io = ImGui::GetIO(); ImDrawList* drawList = gContext.mDrawList; @@ -1623,32 +1635,35 @@ static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, cons axesWorldDirections[bestIndex] = tempDirection; } + matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection; for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex) { bestAxis = axes[axisIndex]; bestAxisWorldDirection = axesWorldDirections[axisIndex]; - // corners - vec_t aabb[4]; + // Corners of the plane (rectangle) containing bestAxis + vec_t corners[4]; int secondAxis = (bestAxis + 1) % 3; int thirdAxis = (bestAxis + 2) % 3; + // ImU32 col[] = { IM_COL32(255, 0, 0, 255), IM_COL32(0, 255, 0, 255), IM_COL32(0, 0, 255, 255) }; + for (int i = 0; i < 4; i++) { + corners[i].w = 0.0f; + corners[i][bestAxis] = 0.0f; + corners[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; + corners[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; - 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))]; + // ImVec2 pos = worldToPos(corners[i], boundsMVP); + // drawList->AddCircleFilled(pos, 10.0f, col[axisIndex]); } // draw bounds unsigned int anchorAlpha = gContext.mbEnable ? IM_COL32_BLACK : IM_COL32(0, 0, 0, 0x80); - 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); + ImVec2 worldBound1 = worldToPos(corners[i], boundsMVP); + ImVec2 worldBound2 = worldToPos(corners[(i + 1) % 4], boundsMVP); if (!IsInContextRect(worldBound1) || !IsInContextRect(worldBound2)) { continue; @@ -1666,7 +1681,7 @@ static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, cons // drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0, 0, 0, 0) + anchorAlpha, 3.f); drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha, 2.f); } - vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4]) * 0.5f; + vec_t midPoint = (corners[i] + corners[(i + 1) % 4]) * 0.5f; ImVec2 midBound = worldToPos(midPoint, boundsMVP); static const float AnchorBigRadius = 8.f; static const float AnchorSmallRadius = 6.f; @@ -1707,25 +1722,28 @@ static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, cons // big anchor on corners if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate()) { - gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource); - gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource); + gContext.mBoundsPivot.TransformPoint(corners[(i + 2) % 4], gContext.mModelSource); + gContext.mBoundsAnchor.TransformPoint(corners[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.mBoundsLocalPivot[secondAxis] = corners[oppositeIndex][secondAxis]; + gContext.mBoundsLocalPivot[thirdAxis] = corners[oppositeIndex][thirdAxis]; + gContext.mBoundsPivotCornerIndex = oppositeIndex; gContext.mbUsingBounds = true; gContext.mEditingID = gContext.mActualID; gContext.mBoundsMatrix = gContext.mModelSource; + + gContext.mbIsUsingBigAnchor = true; } // small anchor on middle of segment if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate()) { - vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f; + vec_t midPointOpposite = (corners[(i + 2) % 4] + corners[(i + 3) % 4]) * 0.5f; gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource); gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource); gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection); @@ -1734,20 +1752,25 @@ static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, cons gContext.mBoundsAxis[0] = indices[i % 2]; gContext.mBoundsAxis[1] = -1; + int localPivotComponentIdx = gContext.mBoundsAxis[0]; 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.mBoundsLocalPivot[localPivotComponentIdx] = corners[oppositeIndex][localPivotComponentIdx]; // bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); + gContext.mBoundsPivotCornerIndex = oppositeIndex; gContext.mbUsingBounds = true; gContext.mEditingID = gContext.mActualID; gContext.mBoundsMatrix = gContext.mModelSource; + + gContext.mbIsUsingBigAnchor = false; } } + ImGui::Text("bounds pivot: %.2f, %.2f, %.2f", gContext.mBoundsPivot.x, gContext.mBoundsPivot.y, gContext.mBoundsPivot.z); + ImGui::Text("bounds anchor: %.2f, %.2f, %.2f", gContext.mBoundsAnchor.x, gContext.mBoundsAnchor.y, gContext.mBoundsAnchor.z); + ImGui::Text("bounds plan: %.2f, %.2f, %.2f", gContext.mBoundsPlan.x, gContext.mBoundsPlan.y, gContext.mBoundsPlan.z); + ImGui::Text("bounds local pivot: %.2f, %.2f, %.2f", gContext.mBoundsLocalPivot.x, gContext.mBoundsLocalPivot.y, gContext.mBoundsLocalPivot.z); if (gContext.mbUsingBounds && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) { - 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; @@ -1756,48 +1779,68 @@ static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, cons vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs(); vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs(); + ImGui::Text("Delta: %.2f, %.2f, %.2f", deltaVector.x, deltaVector.y, deltaVector.z); + ImGui::Text("Ref: %.2f, %.2f, %.2f", referenceVector.x, referenceVector.y, referenceVector.z); + ImGui::Separator(); + // 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 axisIndex1 = gContext.mBoundsAxis[i]; - if (axisIndex1 == -1) - { + for (int axisIndex1 : gContext.mBoundsAxis) { + if (axisIndex1 == -1) { continue; } - float ratioAxis = 1.f; vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs(); + // ImGui::Text("Axisdir: %.2f, %.2f, %.2f", axisDir.x, axisDir.y, axisDir.z); - float dtAxis = axisDir.Dot(referenceVector); - float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1]; - if (dtAxis > FLT_EPSILON) - { - ratioAxis = axisDir.Dot(deltaVector) / dtAxis; - } + float refAxisComp = axisDir.Dot(referenceVector); + float deltaAxisComp = axisDir.Dot(deltaVector); + // ImGui::Text("refAxisComp: %.2f", refAxisComp); - if (snapValues) - { - float length = boundSize * ratioAxis; + float length = deltaAxisComp; + if (snapValues) { ComputeSnap(&length, snapValues[axisIndex1]); - if (boundSize > FLT_EPSILON) - { - ratioAxis = length / boundSize; - } } - scale.component[axisIndex1] *= ratioAxis; + + // ImGui::Text("axis idx %d", axisIndex1); + // TODO(hnosm): logic that mapps mouse pos to bound seems to account for translation fixup already? + bounds[axisIndex1] = -length / 2; + bounds[axisIndex1 + 3] = +length / 2; + } + + // Update corner positions, translation fixup code needs them + for (int i = 0; i < 4; i++) { + corners[i].w = 0.0f; + corners[i][bestAxis] = 0.0f; + corners[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)]; + corners[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))]; + } + + // Translation (object center) fixup - make sure pivot stays in place + // TODO(hnosm): is there a better way to write this that doesn't involve transferring a bunch of extra state from begin drag frame? + vec_t newLocalPivot; + if (gContext.mbIsUsingBigAnchor) { + newLocalPivot.Set(0.0f); + newLocalPivot[secondAxis] = corners[gContext.mBoundsPivotCornerIndex][secondAxis]; + newLocalPivot[thirdAxis] = corners[gContext.mBoundsPivotCornerIndex][thirdAxis]; + } else { + newLocalPivot.Set(0.0f); + int localPivotComponentIdx = gContext.mBoundsAxis[0]; + newLocalPivot[localPivotComponentIdx] = corners[gContext.mBoundsPivotCornerIndex][localPivotComponentIdx]; } - // transform matrix - matrix_t preScale, postScale; - preScale.Translation(-gContext.mBoundsLocalPivot); - postScale.Translation(gContext.mBoundsLocalPivot); - matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix; - *matrix = res; + vec_t delta = gContext.mBoundsLocalPivot - newLocalPivot; + vec_t oldTranslation = gContext.mBoundsMatrix.component[3]; + matrix->component[3] = oldTranslation + delta; // 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()); + ImFormatString(tmps, sizeof(tmps), + // Size of the bounds in each axis direction + "X: %.2f Y: %.2f Z:%.2f", + (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length(), + (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length(), + (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length()); drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps); drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps); } @@ -1811,6 +1854,8 @@ static void HandleAndDrawLocalBounds(const float* bounds, matrix_t* matrix, cons break; } } + + return gContext.mbUsingBounds; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2385,7 +2430,7 @@ void AllowAxisFlip(bool value) { gContext.mAllowAxisFlip = value; } -bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, const float* localBounds, const float* boundsSnap) { +bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, float* localBounds, const float* boundsSnap) { // Scale is always local or matrix will be skewed when applying world scale or oriented matrix ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); @@ -2410,15 +2455,14 @@ bool Manipulate(const float* view, const float* projection, OPERATION operation, { if (!gContext.mbUsingBounds) { - manipulated = HandleTranslation(matrix, deltaMatrix, operation, type, snap) || + manipulated |= HandleTranslation(matrix, deltaMatrix, operation, type, snap) || HandleScale(matrix, deltaMatrix, operation, type, snap) || HandleRotation(matrix, deltaMatrix, operation, type, snap); } } - if (localBounds && !gContext.mbUsing) { - HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation); + manipulated |= HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation); } gContext.mOperation = operation; @@ -2686,7 +2730,6 @@ void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU // tag faces bool boxes[27]{}; - static int overBox = -1; for (int iPass = 0; iPass < 2; iPass++) { for (int iFace = 0; iFace < 6; iFace++) @@ -2756,10 +2799,38 @@ void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU { gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, IM_COL32(0xF0, 0xA0, 0x60, 0x80)); - if (io.MouseDown[0] && !isClicking && !isDraging) { - overBox = boxCoordInt; + if (!io.MouseDown[0] && !isDraging && isClicking) + { + // apply new view direction + int cx = boxCoordInt / 9; + int cy = (boxCoordInt - cx * 9) / 3; + int cz = boxCoordInt % 3; + interpolationDir = makeVect(1.f - (float)cx, 1.f - (float)cy, 1.f - (float)cz); + interpolationDir.Normalize(); + + if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f) + { + vec_t right = viewInverse.v.right; + if (fabsf(right.x) > fabsf(right.z)) + { + right.z = 0.f; + } else + { + right.x = 0.f; + } + right.Normalize(); + interpolationUp = Cross(interpolationDir, right); + interpolationUp.Normalize(); + } else + { + interpolationUp = referenceUp; + } + interpolationFrames = 40; + isClicking = false; + } + if (io.MouseClicked[0] && !isDraging) + { isClicking = true; - isDraging = true; } } } @@ -2782,42 +2853,13 @@ void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU } isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos); - if (io.MouseDown[0] && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1])) && isClicking) + // drag view + if (!isDraging && io.MouseClicked[0] && isInside) { + isDraging = true; isClicking = false; - } - - if (!io.MouseDown[0]) + } else if (isDraging && !io.MouseDown[0]) { - if (isClicking) - { - // apply new view direction - int cx = overBox / 9; - int cy = (overBox - cx * 9) / 3; - int cz = overBox % 3; - interpolationDir = makeVect(1.f - (float)cx, 1.f - (float)cy, 1.f - (float)cz); - interpolationDir.Normalize(); - - if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f) - { - vec_t right = viewInverse.v.right; - if (fabsf(right.x) > fabsf(right.z)) - { - right.z = 0.f; - } else - { - right.x = 0.f; - } - right.Normalize(); - interpolationUp = Cross(interpolationDir, right); - interpolationUp.Normalize(); - } else - { - interpolationUp = referenceUp; - } - interpolationFrames = 40; - } - isClicking = false; isDraging = false; } diff --git a/source/EditorGuizmo.hpp b/source/EditorGuizmo.hpp index b11759c..0560050 100644 --- a/source/EditorGuizmo.hpp +++ b/source/EditorGuizmo.hpp @@ -201,7 +201,17 @@ enum MODE { WORLD }; -IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL); +IMGUI_API bool Manipulate( + const float* view, + const float* projection, + OPERATION operation, + MODE mode, + float* matrix, + float* deltaMatrix = NULL, + const float* snap = NULL, + float* localBounds = NULL, + const float* boundsSnap = NULL); + // // Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en // It seems to be a defensive patent in the US. I don't think it will bring troubles using it as |