CedricGuillemet / ImGuizmo

Immediate mode 3D gizmo for scene editing and other controls based on Dear Imgui
MIT License
3.12k stars 878 forks source link

Support for 2D transformations? #49

Open jdumas opened 6 years ago

jdumas commented 6 years ago

Hi there,

I'd like to use this nice little gizmo to set a 2D transformation matrix. Basically I would like to "hide" one of the axes (e.g. Z). I've tried to "squish" things down by setting the Z scaling to 0/1e-3 for either the view matrix, or the manipulated matrix ... And neither of them really works well: with the former approach, I cannot select the Y axis (only the X), but the Z axis/XZ/YZ planes are well hidden; with the latter the Z/XZ/YZ are still visible, and there is some issue with the RectTransform (#20).

Looking at the code it looks like it shouldn't be too hard to add an option to mask one axis, but I'm open to other suggestions.

CedricGuillemet commented 6 years ago

Hi Jeremie,

Look for 'belowAxisLimit' boolean. It's true when the axis is nearly colinear with the camera view direction. If you enhance the checks with a mask (something like belowAxisLimit && ((1<<currentAxis)&AxisMask) you should be able to hide the axis you don't want. AxisMask would be a combination of AxisMaskX = 1, AxisMaskY = 2, AxisMaskZ = 4 And add a function ImGuizmo::SetAxisMask(int axisMask) to save the mask that in the context

jdumas commented 6 years ago

Thanks! I've hardcoded those changes for now. I also had to fiddle around with the bestAxis in HandleAndDrawLocalBounds() to show the bounds in the plane I was interested in, even when it is viewed at a steep angle. I also modified the DrawCube() function to draw a single face at a coordinate Z=0.

Things are a bit hardcoded for now, so I can't really do a PR, but I may do that later on.

vamidi commented 5 years ago

@jdumas can you explain me how you did it? I want to try and make this (picture below) with Imguizmo where you can control a UI element within a X/Y plane https://docs.unity3d.com/uploads/Main/UI_Anchored4.gif

jdumas commented 5 years ago

In HandleAndDrawLocalBounds I have made the following modifications to enforce edits on the XY plane, even when viewed at a grazing angle:

               if ( i == 2 )
               {
                   bestDot = dt;
                   bestAxis = i;
                   bestAxisWorldDirection = dirPlaneNormalWorld;
               }

               if( i == 2 && dt >= 0.1f )
               {
                   axes[numAxes] = i;
                   axesWorldDirections[numAxes] = dirPlaneNormalWorld;
                   ++numAxes;
               }

       // ....

       for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex)
       {
           if (axisIndex == 1) { break; }

And then my modified version of DrawCube:

   void DrawCube(const float *view, const float *projection, float *matrix)
   {
      matrix_t viewInverse;
      viewInverse.Inverse(*(const matrix_t*)view);
      const matrix_t& model = *(const matrix_t*)matrix;
      matrix_t res = *(const matrix_t*)matrix * *(const matrix_t*)view * *(const matrix_t*)projection;

      for (int iFace = 0; iFace < 6; iFace++)
      {
         const int normalIndex = (iFace % 3);
         const int perpXIndex = (normalIndex + 1) % 3;
         const int perpYIndex = (normalIndex + 2) % 3;
         const float invert = (iFace > 2) ? -1.f : 1.f;

         if (normalIndex != 2) { continue; }

         const vec_t faceCoords[4] = {
            /*directionUnary[normalIndex]*/ + directionUnary[perpXIndex] + directionUnary[perpYIndex],
            /*directionUnary[normalIndex]*/ + directionUnary[perpXIndex] - directionUnary[perpYIndex],
            /*directionUnary[normalIndex]*/ - directionUnary[perpXIndex] - directionUnary[perpYIndex],
            /*directionUnary[normalIndex]*/ - directionUnary[perpXIndex] + directionUnary[perpYIndex],
         };

         // clipping
         bool skipFace = false;
         for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
         {
            vec_t camSpacePosition;
            camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, gContext.mMVP);
            // if (camSpacePosition.z < 0.001f)
            // {
            //    skipFace = true;
            //    break;
            // }
         }
         if (skipFace)
            continue;

         // 3D->2D
         ImVec2 faceCoordsScreen[4];
         for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
            faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res);

         // back face culling
         vec_t cullPos, cullNormal;
         cullPos.TransformPoint(faceCoords[0] * 0.5f * invert, model);
         cullNormal.TransformVector(directionUnary[normalIndex] * invert, model);
         float dt = Dot(Normalized(cullPos - viewInverse.v.position), Normalized(cullNormal));
         if (dt>0.f)
            continue;

         // draw face with lighter color
         gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080);
      }
   }

My version of ImGuizmo is not in sync with upstream anymore, so I may be missing other small changes, but as I recall those where the changes I had to make.

vamidi commented 5 years ago

Amazing! I will take a look at it! Thanks! @jdumas, I tried it out and I think I am doing something wrong? How does it works actually? maybe I can try messing around with variables. https://gyazo.com/8a8e9649f60de6ebeb4c31f3f3c17927

this needs to be replaced with your piece right?


                           if (dt >= bestDot)
                {
                    bestDot = dt;
                    bestAxis = i;
                    bestAxisWorldDirection = dirPlaneNormalWorld;
               }
                if (dt >= 0.1f)
                {
                    axes[numAxes] = i;
                    axesWorldDirections[numAxes] = dirPlaneNormalWorld;
                    ++numAxes;
                }```
vamidi commented 5 years ago

@jdumas based on the comment above is it still possible to provide an answer :D ? I notice if I rotate an object it starts to come, but do I need to rotate things beforehand?

Batres3 commented 9 months ago

I made a pull request implementing this as best I could, it seems to work fine if anyone wants to try it. It is on a branch of my fork of ImGuizmo.