ImGuizmo
ImGuizmo copied to clipboard
ImGuizmo::IsOver() returns 'true' after hiding the gizmo and then hovering over where the central white dot used to be
Steps to reproduce:
- Add this line anywhere in the
while
loop from "main.cpp" (obviously with#include <iostream>
at the top of the file):
std::cout << "Result is: " << std::boolalpha << ImGuizmo::IsOver() << '\n';
- Replace the
ImGuizmo::Manipulate
line from here with this:
static bool isGizmoVisible = true;
if (isGizmoVisible)
ImGuizmo::Manipulate(cameraView, cameraProjection, mCurrentGizmoOperation, mCurrentGizmoMode, matrix, NULL, useSnap ? &snap[0] : NULL, boundSizing ? bounds : NULL, boundSizingSnap ? boundsSnap : NULL);
static bool canCheckIfCursorMovedAway = false;
if (canCheckIfCursorMovedAway && !ImGuizmo::IsOver())
isGizmoVisible = false;
if (ImGuizmo::IsOver())
canCheckIfCursorMovedAway = true;
- Start the application.
- Hover over the gizmo.
- Hover away from the gizmo (so that it disappears).
- Hover over where the central white dot was.
The following occurs:
It returns true
, even though the gizmo isn't even visible. It only seems to occur for the white dot, not for the other axes.
Expected result: It shouldn't return anything if the gizmo isn't drawn.
It effectively simulates the problem I'm having, where I'm clicking on an entity from my scene, a raycast shoots towards it, it hits its collision and a hit registers. That hit returns the entity id, which I can use to retrieve the entity's transform component and I can draw the gizmo at that location.
All well and good.
But if I deselect the entity by clicking somewhere else (i.e. hide the gizmo), I can't re-click it in the middle, because ImGuizmo::IsOver()
returns true
for that location. I thought this was related to the previous issue, but apparently not.
I'm checking that ...
IsOver function uses context data (view matrix, projection,...) from Manipulate to determine if the mouse pointer is over the gizmo or not. If Manipulate is not called but IsOver is, it will always use the same cached data. ImGuizmo cannot determine if the gizmo is visible or not. But it's possible on your side to add a check if (gizmoVisible && ImGuizmo::IsOver()) { ... }
I don't see a way to make it work only in ImGuizmo.
@CedricGuillemet
If Manipulate is not called but IsOver is, it will always use the same cached data.
Is there a way to clear this cached data? Or replace it with some other values? Where is it stored?
I'm thinking of a function that clears it, named something like ImGuizmo::ClearCache
.
the cached datas are useful to recontruct the ray for picking axis. A workaround would be to sent the view/projection/model matrices are parameters for IsOver. But I guess you have a boolean somewhere that is true when you want to manipulate. Why not use it with isOver? Like if (doGizmoManipulation && IsOver())
?
I'm shooting raycasts into the scene using the Bullet physics library, like so:
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS
&& !ImGui::IsAnyWindowHovered()
&& !ImGuizmo::IsOver()) // <---- here is the problem, it returns 'true' when hovering over the white dot even if the gizmo isn't drawn
{
// shoot a raycast into the world from where the mouse was clicked.
// if hit something, check if it's a valid entity and set it as the selected entity
// else if hit nothing, set the selected entity as 'entt::null'
}
If I comment out that line...
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS
&& !ImGui::IsAnyWindowHovered()
/*&& !ImGuizmo::IsOver()*/)
{
// shoot a raycast into the world [...]
}
...I can click in the middle of the entity, no problem, but now I can't use the gizmo if the selected entity is behind another entity, or inside another entity's collision, because my raycast hits that collision first, as opposed to not even firing a raycast in the first place, since IsOver()
would've stopped it.
The first snippet of code works well for entities behind other entities (or entities inside the collision of other entities), as long as you don't reselect them by clicking them in the middle. Hence my problem. Because now ALL entities can't be reselected if they're clicked in the middle (where the white dot used to be).
Do you understand what I'm saying?
A way to manually flush out that cache would be great.
PS: Why aren't the other elements returning true
? Why only the white dot?
I ended up using this as a workaround when deselecting entities:
glm::mat4 transform = scene.mRegistry.get<TransformComponent>(*pSelectedEntity).Transform; // Get a copy
transform = glm::translate(transform, glm::vec3(0.0f, 1000.0f, 0.0f));
ImGuizmo::Manipulate(glm::value_ptr(*pView), glm::value_ptr(*pProjection),
gizmoOperation, gizmoCoordSystem, glm::value_ptr(transform));
*pSelectedEntity = entt::null;
It gets a copy of the transform matrix for the selected entity (so that the original isn't affected), translates it somewhere above where it is, by 1000 units, and draws the gizmo there, right before setting it to entt::null
. Seems to fix it.
It's ugly, it works. It no longer inhibits raycasting in the middle of the damn thing just because ImGuizmo::IsOver
returns a weird result at that location. I don't know how else to influence that cached data. One thing's for sure, it makes object picking a whole lot more pleasant now. 😄