Updated ImGuizmo.

This commit is contained in:
Branimir Karadžić 2018-05-18 21:45:26 -07:00
parent a0975b5f41
commit 54e183806b
2 changed files with 1990 additions and 1830 deletions

View File

@ -1,5 +1,5 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.04 WIP
// v 1.61 WIP
//
// The MIT License(MIT)
//
@ -105,18 +105,21 @@ void EditTransform(const Camera& camera, matrix_t& matrix)
namespace ImGuizmo
{
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
IMGUI_API void SetDrawlist();
// call BeginFrame right after ImGui_XXXX_NewFrame();
void BeginFrame();
IMGUI_API void BeginFrame();
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
bool IsOver();
IMGUI_API bool IsOver();
// return true if mouse IsOver or if the gizmo is in moving state
bool IsUsing();
IMGUI_API bool IsUsing();
// enable/disable the gizmo. Stay in the state until next call to Enable.
// gizmo is rendered with gray half transparent color when disabled
void Enable(bool enable);
IMGUI_API void Enable(bool enable);
// helper functions for manualy editing translation/rotation/scale with an input float
// translation, rotation and scale float points to 3 floats each
@ -130,13 +133,16 @@ namespace ImGuizmo
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
//
// These functions have some numerical stability issues for now. Use with caution.
void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale);
void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix);
IMGUI_API void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale);
IMGUI_API void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix);
void SetRect(float x, float y, float width, float height);
IMGUI_API void SetRect(float x, float y, float width, float height);
// default is false
IMGUI_API void SetOrthographic(bool isOrthographic);
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
void DrawCube(const float *view, const float *projection, float *matrix);
IMGUI_API void DrawCube(const float *view, const float *projection, const float *matrix);
IMGUI_API void DrawGrid(const float *view, const float *projection, const float *matrix, const float gridSize);
// call it when you want a gizmo
// Needs view and projection matrices.
@ -147,6 +153,7 @@ namespace ImGuizmo
TRANSLATE,
ROTATE,
SCALE,
BOUNDS,
};
enum MODE
@ -155,5 +162,5 @@ namespace ImGuizmo
WORLD
};
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);
IMGUI_API 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);
};

View File

@ -30,7 +30,7 @@ namespace ImGuizmo
static const float ZPI = 3.14159265358979323846f;
static const float RAD2DEG = (180.f / ZPI);
static const float DEG2RAD = (ZPI / 180.f);
static const float gGizmoSizeClipSpace = 0.1f;
const float screenRotateSize = 0.06f;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -140,6 +140,7 @@ namespace ImGuizmo
};
vec_t makeVect(float _x, float _y, float _z = 0.f, float _w = 0.f) { vec_t res; res.x = _x; res.y = _y; res.z = _z; res.w = _w; return res; }
vec_t makeVect(ImVec2 v) { vec_t res; res.x = v.x; res.y = v.y; res.z = 0.f; res.w = 0.f; return res; }
vec_t vec_t::operator * (float f) const { return makeVect(x * f, y * f, z * f, w *f); }
vec_t vec_t::operator - () const { return makeVect(-x, -y, -z, -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); }
@ -477,9 +478,9 @@ namespace ImGuizmo
MOVE_X,
MOVE_Y,
MOVE_Z,
MOVE_XY,
MOVE_XZ,
MOVE_YZ,
MOVE_ZX,
MOVE_XY,
MOVE_SCREEN,
ROTATE_X,
ROTATE_Y,
@ -488,8 +489,7 @@ namespace ImGuizmo
SCALE_X,
SCALE_Y,
SCALE_Z,
SCALE_XYZ,
BOUNDS
SCALE_XYZ
};
struct Context
@ -569,6 +569,9 @@ namespace ImGuizmo
float mHeight = 0.f;
float mXMax = 0.f;
float mYMax = 0.f;
float mDisplayRatio = 1.f;
bool mIsOrthographic = false;
};
static Context gContext;
@ -580,15 +583,16 @@ namespace ImGuizmo
static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 };
// Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F
static const ImU32 planeBorderColor[3] = { 0xFFAA0000, 0xFF0000AA, 0xFF00AA00 };
static const ImU32 planeColor[3] = { 0x610000AA, 0x6100AA00, 0x61AA0000 };
static const ImU32 selectionColor = 0x8A1080FF;
static const ImU32 inactiveColor = 0x99999999;
static const ImU32 translationLineColor = 0xAAAAAAAA;
static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "X : %5.3f Y : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" };
static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f",
"Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f",
"X : %5.3f Y : %5.3f Z : %5.3f" };
static const char *scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" };
static const char *rotationInfoMask[] = { "X : %5.2f deg %5.2f rad", "Y : %5.2f deg %5.2f rad", "Z : %5.2f deg %5.2f rad", "Screen : %5.2f deg %5.2f rad" };
static const int translationInfoIndex[] = { 0,0,0, 1,0,0, 2,0,0, 0,1,0, 1,2,0, 0,2,1, 0,1,2 };
static const int translationInfoIndex[] = { 0,0,0, 1,0,0, 2,0,0, 1,2,0, 0,2,0, 0,1,0, 0,1,2 };
static const float quadMin = 0.5f;
static const float quadMax = 0.8f;
static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin };
@ -633,6 +637,62 @@ namespace ImGuizmo
rayDir = Normalized(rayEnd - rayOrigin);
}
static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end)
{
vec_t startOfSegment = start;
startOfSegment.TransformPoint(gContext.mMVP);
if (fabsf(startOfSegment.w)> FLT_EPSILON) // check for axis aligned with camera direction
startOfSegment *= 1.f / startOfSegment.w;
vec_t endOfSegment = end;
endOfSegment.TransformPoint(gContext.mMVP);
if (fabsf(endOfSegment.w)> FLT_EPSILON) // check for axis aligned with camera direction
endOfSegment *= 1.f / endOfSegment.w;
vec_t clipSpaceAxis = endOfSegment - startOfSegment;
clipSpaceAxis.y /= gContext.mDisplayRatio;
float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x*clipSpaceAxis.x + clipSpaceAxis.y*clipSpaceAxis.y);
return segmentLengthInClipSpace;
}
static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB)
{
vec_t pts[] = { ptO, ptA, ptB };
for (unsigned int i = 0; i < 3; i++)
{
pts[i].TransformPoint(gContext.mMVP);
if (fabsf(pts[i].w)> FLT_EPSILON) // check for axis aligned with camera direction
pts[i] *= 1.f / pts[i].w;
}
vec_t segA = pts[1] - pts[0];
vec_t segB = pts[2] - pts[0];
segA.y /= gContext.mDisplayRatio;
segB.y /= gContext.mDisplayRatio;
vec_t segAOrtho = makeVect(-segA.y, segA.x);
segAOrtho.Normalize();
float dt = segAOrtho.Dot3(segB);
float surface = sqrtf(segA.x*segA.x + segA.y*segA.y) * fabsf(dt);
return surface;
}
inline vec_t PointOnSegment(const vec_t & point, const vec_t & vertPos1, const vec_t & vertPos2)
{
vec_t c = point - vertPos1;
vec_t V;
V.Normalize(vertPos2 - vertPos1);
float d = (vertPos2 - vertPos1).Length();
float t = V.Dot3(c);
if (t < 0.f)
return vertPos1;
if (t > d)
return vertPos2;
return vertPos1 + V * t;
}
static float IntersectRayPlane(const vec_t & rOrigin, const vec_t& rVector, const vec_t& plan)
{
float numer = plan.Dot3(rOrigin) - plan.w;
@ -657,6 +717,12 @@ namespace ImGuizmo
gContext.mHeight = height;
gContext.mXMax = gContext.mX + gContext.mWidth;
gContext.mYMax = gContext.mY + gContext.mXMax;
gContext.mDisplayRatio = width / height;
}
IMGUI_API void SetOrthographic(bool isOrthographic)
{
gContext.mIsOrthographic = isOrthographic;
}
void SetDrawlist()
@ -734,7 +800,16 @@ namespace ImGuizmo
gContext.mCameraEye = viewInverse.v.position;
gContext.mCameraRight = viewInverse.v.right;
gContext.mCameraUp = viewInverse.v.up;
gContext.mScreenFactor = 0.1f * GetUniform(gContext.mModel.v.position, gContext.mViewProjection);
// compute scale from the size of camera right vector projected on screen at the matrix position
vec_t pointRight = viewInverse.v.right;
pointRight.TransformPoint(gContext.mViewProjection);
gContext.mScreenFactor = gGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w);
vec_t rightViewInverse = viewInverse.v.right;
rightViewInverse.TransformVector(gContext.mModelInverse);
float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse);
gContext.mScreenFactor = gGizmoSizeClipSpace / rightLength;
ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP);
gContext.mScreenSquareCenter = centerSSpace;
@ -754,9 +829,8 @@ namespace ImGuizmo
colors[0] = (type == MOVE_SCREEN) ? selectionColor : 0xFFFFFFFF;
for (int i = 0; i < 3; i++)
{
int colorPlaneIndex = (i + 2) % 3;
colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i];
colors[i + 4] = (type == (int)(MOVE_XY + i)) ? selectionColor : planeColor[colorPlaneIndex];
colors[i + 4] = (type == (int)(MOVE_YZ + i)) ? selectionColor : planeColor[i];
colors[i + 4] = (type == MOVE_SCREEN) ? selectionColor : colors[i + 4];
}
break;
@ -770,6 +844,8 @@ namespace ImGuizmo
for (int i = 0; i < 3; i++)
colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i];
break;
case BOUNDS:
break;
}
}
else
@ -779,11 +855,11 @@ namespace ImGuizmo
}
}
static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit)
static void ComputeTripodAxisAndVisibility(int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit)
{
const int planNormal = (axisIndex + 2) % 3;
dirPlaneX = directionUnary[axisIndex];
dirPlaneY = directionUnary[(axisIndex + 1) % 3];
dirAxis = directionUnary[axisIndex];
dirPlaneX = directionUnary[(axisIndex + 1) % 3];
dirPlaneY = directionUnary[(axisIndex + 2) % 3];
if (gContext.mbUsing)
{
@ -796,34 +872,34 @@ namespace ImGuizmo
}
else
{
vec_t dirPlaneNormalWorld;
dirPlaneNormalWorld.TransformVector(directionUnary[planNormal], gContext.mModel);
dirPlaneNormalWorld.Normalize();
// new method
float lenDir = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis);
float lenDirMinus = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirAxis);
vec_t dirPlaneXWorld(dirPlaneX);
dirPlaneXWorld.TransformVector(gContext.mModel);
dirPlaneXWorld.Normalize();
float lenDirPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneX);
float lenDirMinusPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneX);
vec_t dirPlaneYWorld(dirPlaneY);
dirPlaneYWorld.TransformVector(gContext.mModel);
dirPlaneYWorld.Normalize();
float lenDirPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneY);
float lenDirMinusPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneY);
vec_t cameraEyeToGizmo = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
float dotCameraDirX = cameraEyeToGizmo.Dot3(dirPlaneXWorld);
float dotCameraDirY = cameraEyeToGizmo.Dot3(dirPlaneYWorld);
// compute factor values
float mulAxisX = (dotCameraDirX > 0.f) ? -1.f : 1.f;
float mulAxisY = (dotCameraDirY > 0.f) ? -1.f : 1.f;
float mulAxis = (lenDir < lenDirMinus && fabsf(lenDir - lenDirMinus) > FLT_EPSILON) ? -1.f : 1.f;
float mulAxisX = (lenDirPlaneX < lenDirMinusPlaneX && fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f;
float mulAxisY = (lenDirPlaneY < lenDirMinusPlaneY && fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f;
dirAxis *= mulAxis;
dirPlaneX *= mulAxisX;
dirPlaneY *= mulAxisY;
belowAxisLimit = fabsf(dotCameraDirX) < angleLimit;
belowPlaneLimit = (fabsf(cameraEyeToGizmo.Dot3(dirPlaneNormalWorld)) > planeLimit);
// for axis
float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor);
float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor);
belowPlaneLimit = (paraSurf > 0.0025f);
belowAxisLimit = (axisLengthInClipSpace > 0.02f);
// and store values
gContext.mAxisFactor[axisIndex] = mulAxisX;
gContext.mAxisFactor[axisIndex] = mulAxis;
gContext.mAxisFactor[(axisIndex + 1) % 3] = mulAxisY;
gContext.mAxisFactor[(axisIndex + 2) % 3] = mulAxisY;
gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit;
gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit;
}
@ -870,10 +946,22 @@ namespace ImGuizmo
ImU32 colors[7];
ComputeColors(colors, type, ROTATE);
vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
vec_t cameraToModelNormalized;
if (gContext.mIsOrthographic)
{
matrix_t viewInverse;
viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat);
cameraToModelNormalized = viewInverse.v.dir;
}
else
{
cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
}
cameraToModelNormalized.TransformVector(gContext.mModelInverse);
gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight;
for (int axis = 0; axis < 3; axis++)
{
ImVec2 circlePos[halfCircleSegmentCount];
@ -948,16 +1036,16 @@ namespace ImGuizmo
for (unsigned int i = 0; i < 3; i++)
{
vec_t dirPlaneX, dirPlaneY;
vec_t dirPlaneX, dirPlaneY, dirAxis;
bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
// draw axis
if (belowAxisLimit)
{
ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpaceNoScale = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpace = worldToPos((dirPlaneX * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP);
ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpace = worldToPos((dirAxis * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP);
if (gContext.mbUsing)
{
@ -969,7 +1057,7 @@ namespace ImGuizmo
drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]);
if (gContext.mAxisFactor[i] < 0.f)
DrawHatchedAxis(dirPlaneX * scaleDisplay[i]);
DrawHatchedAxis(dirAxis * scaleDisplay[i]);
}
}
@ -1014,14 +1102,14 @@ namespace ImGuizmo
bool belowPlaneLimit = false;
for (unsigned int i = 0; i < 3; ++i)
{
vec_t dirPlaneX, dirPlaneY;
ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
vec_t dirPlaneX, dirPlaneY, dirAxis;
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
// draw axis
if (belowAxisLimit)
{
ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpace = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP);
ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP);
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
@ -1038,7 +1126,7 @@ namespace ImGuizmo
// Arrow head end
if (gContext.mAxisFactor[i] < 0.f)
DrawHatchedAxis(dirPlaneX);
DrawHatchedAxis(dirAxis);
}
// draw plane
@ -1050,7 +1138,7 @@ namespace ImGuizmo
vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
}
drawList->AddPolyline(screenQuadPts, 4, planeBorderColor[i], true, 1.0f);
drawList->AddPolyline(screenQuadPts, 4, directionColor[i], true, 1.0f);
drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
}
}
@ -1084,7 +1172,7 @@ namespace ImGuizmo
return false;
}
static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues)
static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues, OPERATION operation)
{
ImGuiIO& io = ImGui::GetIO();
ImDrawList* drawList = gContext.mDrawList;
@ -1188,7 +1276,8 @@ namespace ImGuizmo
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);
//drawList->AddLine(worldBoundSS1, worldBoundSS2, 0x000000 + anchorAlpha, 3.f);
drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 2.f);
}
vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f;
ImVec2 midBound = worldToPos(midPoint, boundsMVP);
@ -1197,12 +1286,31 @@ namespace ImGuizmo
bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
int type = NONE;
vec_t gizmoHitProportion;
switch (operation)
{
case TRANSLATE: type = GetMoveType(&gizmoHitProportion); break;
case ROTATE: type = GetRotateType(); break;
case SCALE: type = GetScaleType(); break;
case BOUNDS: break;
}
if (type != NONE)
{
overBigAnchor = false;
overSmallAnchor = false;
}
unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor);
drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor);
drawList->AddCircleFilled(worldBound1, AnchorBigRadius, 0xFF000000);
drawList->AddCircleFilled(worldBound1, AnchorBigRadius-1.2f, bigAnchorColor);
drawList->AddCircleFilled(midBound, AnchorSmallRadius, 0xFF000000);
drawList->AddCircleFilled(midBound, AnchorSmallRadius-1.2f, smallAnchorColor);
int oppositeIndex = (i + 2) % 4;
// big anchor on corners
if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate())
@ -1319,24 +1427,23 @@ namespace ImGuizmo
io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y)
type = SCALE_XYZ;
const vec_t direction[3] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir };
// compute
for (unsigned int i = 0; i < 3 && type == NONE; i++)
{
vec_t dirPlaneX, dirPlaneY;
vec_t dirPlaneX, dirPlaneY, dirAxis;
bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
dirPlaneX.TransformVector(gContext.mModel);
dirPlaneY.TransformVector(gContext.mModel);
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
const int planNormal = (i + 2) % 3;
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, direction[planNormal]));
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
if (belowAxisLimit && dy > -0.1f && dy < 0.1f && dx > 0.1f && dx < 1.f)
const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection);
const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection);
const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection);
vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
type = SCALE_X + i;
}
return type;
@ -1364,9 +1471,15 @@ namespace ImGuizmo
if (Dot(Normalized(localPos), gContext.mRayVector) > FLT_EPSILON)
continue;
vec_t idealPosOnCircle = Normalized(localPos);
idealPosOnCircle.TransformVector(gContext.mModelInverse);
ImVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * gContext.mScreenFactor, gContext.mMVP);
float distance = localPos.Length() / gContext.mScreenFactor;
if (distance > 0.9f && distance < 1.1f)
//gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, 0xFFFFFFFF);
ImVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos;
float distance = makeVect(distanceOnScreen).Length();
if (distance < 8.f) // pixel size
type = ROTATE_X + i;
}
@ -1383,29 +1496,32 @@ namespace ImGuizmo
io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y)
type = MOVE_SCREEN;
const vec_t direction[3] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir };
// compute
for (unsigned int i = 0; i < 3 && type == NONE; i++)
{
vec_t dirPlaneX, dirPlaneY;
vec_t dirPlaneX, dirPlaneY, dirAxis;
bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
dirAxis.TransformVector(gContext.mModel);
dirPlaneX.TransformVector(gContext.mModel);
dirPlaneY.TransformVector(gContext.mModel);
const int planNormal = (i + 2) % 3;
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, direction[planNormal]));
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection);
const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection);
const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection);
vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
type = MOVE_X + i;
const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
if (belowAxisLimit && dy > -0.1f && dy < 0.1f && dx > 0.1f && dx < 1.f)
type = MOVE_X + i;
if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3])
type = MOVE_XY + i;
type = MOVE_YZ + i;
if (gizmoHitProportion)
*gizmoHitProportion = makeVect(dx, dy, 0.f);
@ -1422,9 +1538,11 @@ namespace ImGuizmo
if (gContext.mbUsing)
{
ImGui::CaptureMouseFromApp();
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
const float len = fabsf(IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan)); // near plan
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
// compute delta
vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
vec_t delta = newOrigin - gContext.mModel.v.position;
@ -1480,12 +1598,25 @@ namespace ImGuizmo
// find new possible way to move
vec_t gizmoHitProportion;
type = GetMoveType(&gizmoHitProportion);
if (CanActivate() && type != NONE)
if(type != NONE)
{
ImGui::CaptureMouseFromApp();
}
if (CanActivate() && type != NONE)
{
gContext.mbUsing = true;
gContext.mCurrentOperation = type;
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, -gContext.mCameraDir };
vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
-gContext.mCameraDir };
vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
for (unsigned int i = 0; i < 3; i++)
{
vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized);
movePlanNormal[i].Cross(orthoVector);
movePlanNormal[i].Normalize();
}
// pickup plan
gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MOVE_X]);
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
@ -1505,9 +1636,12 @@ namespace ImGuizmo
{
// find new possible way to scale
type = GetScaleType();
if (CanActivate() && type != NONE)
if(type != NONE)
{
ImGui::CaptureMouseFromApp();
}
if (CanActivate() && type != NONE)
{
gContext.mbUsing = true;
gContext.mCurrentOperation = type;
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir };
@ -1591,6 +1725,11 @@ namespace ImGuizmo
{
type = GetRotateType();
if(type != NONE)
{
ImGui::CaptureMouseFromApp();
}
if (type == ROTATE_SCREEN)
{
applyRotationLocaly = true;
@ -1598,7 +1737,6 @@ namespace ImGuizmo
if (CanActivate() && type != NONE)
{
ImGui::CaptureMouseFromApp();
gContext.mbUsing = true;
gContext.mCurrentOperation = type;
const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
@ -1740,12 +1878,14 @@ namespace ImGuizmo
case SCALE:
HandleScale(matrix, deltaMatrix, type, snap);
break;
case BOUNDS:
break;
}
}
}
if (localBounds && !gContext.mbUsing)
HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap);
HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation);
if (!gContext.mbUsingBounds)
{
@ -1760,11 +1900,13 @@ namespace ImGuizmo
case SCALE:
DrawScaleGizmo(type);
break;
case BOUNDS:
break;
}
}
}
void DrawCube(const float *view, const float *projection, float *matrix)
void DrawCube(const float *view, const float *projection, const float *matrix)
{
matrix_t viewInverse;
viewInverse.Inverse(*(matrix_t*)view);
@ -1816,5 +1958,16 @@ namespace ImGuizmo
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080);
}
}
void DrawGrid(const float *view, const float *projection, const float *matrix, const float gridSize)
{
matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection;
for (float f = -gridSize; f <= gridSize; f += 1.f)
{
gContext.mDrawList->AddLine(worldToPos(makeVect(f, 0.f, -gridSize), res), worldToPos(makeVect(f, 0.f, gridSize), res), 0xFF808080);
gContext.mDrawList->AddLine(worldToPos(makeVect(-gridSize, 0.f, f), res), worldToPos(makeVect(gridSize, 0.f, f), res), 0xFF808080);
}
}
};