aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/EditorGuizmo.cpp242
-rw-r--r--source/EditorGuizmo.hpp12
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