Search Unity

  1. We are migrating the Unity Forums to Unity Discussions by the end of July. Read our announcement for more information and let us know if you have any questions.
    Dismiss Notice
  2. Dismiss Notice

Bug Mouse Position in Editor Window incorrect.

Discussion in 'Scripting' started by TwoBitMachines, Apr 27, 2024.

  1. TwoBitMachines

    TwoBitMachines

    Joined:
    Oct 5, 2016
    Posts:
    46
    Hello.

    In my editor window I need to somehow recreate world coordinates, but since the coordinate system is made for GUI, I had to transform it. It works as intended, but the mouse position has an incorrect position.

    So first, this the transformation I applied to the Editor Window:


    Code (CSharp):
    1.  Matrix4x4 guiMatrix = Matrix4x4.identity;
    2. guiMatrix *= Matrix4x4.Translate(new Vector3((int) Screen.width * 0.5f, Screen.height, 0f));
    3. guiMatrix *= Matrix4x4.Scale(new Vector3(totalScale, -totalScale, 1f));
    4. guiMatrix *= Matrix4x4.Translate(new Vector3(-1f + offset.x, -18f + offset.y, 0f));
    5.                         GUI.matrix = guiMatrix;
    As mentioned, this works. I can use negative numbers, the center position of the coordinate system is no longer the upper left corner. However, whenever I use Event.current.mousePosition, there seems to be a weird offset that changes based on the zoom and offset position of the new editor window. I think this error also occurs without the editor window being transformed. The error is about 0.005 and it causes my window handles to miss detection. Any ideas how to fix this? Or if there is a better way to go about this?

    Thanks
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    7,019
    Screen.width/height should not be used when dealing with windows. This is the size of the screen after all.

    In an EditorWindow you have the position property. This may include the toolbar though, so height may include that.

    And then you have EditorGUIUtility with methods GUIToScreenPoint/Rect and vice versa. If you work with Handles, then HandleUtility has a couple specific/similar/same conversion methods.
     
    Bunny83 and TwoBitMachines like this.
  3. TwoBitMachines

    TwoBitMachines

    Joined:
    Oct 5, 2016
    Posts:
    46
    Hey, thanks for the suggestions. I tried playing around with it some more, but no matter what I do,
    Event.current.mousePosition always returns with a bit of an offset. Even if I remove the matrix transformation, the offset is still there. Is there any other way of retrieving the mouse position?

    Edit: I noticed the error offset gets worse the closer the mouse position is to the top of the window. It's more accurate the lower the mouse position is in the window.
     
    Last edited: Apr 27, 2024
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,219
    You really should never mess with the GUI.matrix. Unity's UI support for custom matrix transformations is bad, especially with things like the tab / tool bar. You should keep the GUI space as it is. It just creates countless of issues. What exactly do you want to render in the window? You can render custom things with the GL class and setup custom view / model matrices for that.

    Keep in mind that every window has it's "rendering" origin like every device context at the bottom left corner. However GUI space has the origin at the top left. Since this is usually offset to exlude the header / tab-bar, custom matrix manipulations in most cases do not what you think they do ^^.

    GUI.matrix is only for GUI stuff and that's inherently 2d. As I said, when you want to do some custom drawing you can setup a GL.Viewport inside the window. This is still tricky to get right since the y axis is reversed.

    You can have a look at my old UVViewer editor window. The whole UI is made with the layout system of the IMGUI system. I used GetRect to reserve a GUI rect for the drawing area (and copy it into the m_ViewPort field).

    With this method I setup a pixel viewport inside the given GUI rect. Of course you probably want to setup an actual perspective view when you want to render 3d stuff. Though the viewport is the first step to define the area inside the window to draw to.

    As I said, you should not mess with the GUI.matrix so all events stay actually consistent with the window. You can / have to translate your GUI positions yourself according to your view / projection.
     
    TwoBitMachines likes this.
  5. TwoBitMachines

    TwoBitMachines

    Joined:
    Oct 5, 2016
    Posts:
    46
    Hey, thanks for the reply. I basically need to render a mesh (2D only) into the window editor. I really need the use of negative numbers. As it is now, it works perfectly, except for the whole mouse position issue. I am actually using GL for this. I'm still new to this class though, so I don't know it's full capabilities. You are saying I can set up a model matrices for this. That sounds interesting. Do you know where I can look more into this?

    I just want to draw a mesh into the editor and have the ability to manipulate its vertices with the Handles class. I already have this entire system working just fine in the Scene View. But I would like to have a dedicated window editor for this.

    I will look into your project. Hopefully I can get some clues here. Thank you for the information.

    Edit: Alright, I was able to set up an ortho projection matrix. I am able to render the mesh into the window editor successfully without applying matrix transformation to the window editor. The main issue I believe was using Screen.Height when I should have been using position.height!


    Code (CSharp):
    1. Rect viewportRect = new Rect(1, 2, position.width, position.height);
    2. float width = position.width / zoomFactor;
    3. float height = position.height / zoomFactor;
    4. float left = -width / 2f + offset.x;
    5. float right = width / 2f + offset.x;
    6. float bottom = -height / 2f + offset.y;
    7. float top = height / 2f + offset.y;
    8.  
    9. Vector2 mousePosition = Event.current.mousePosition;
    10. mousePosition.y = position.height - mousePosition.y;
    11. mousePosition /= zoomFactor;
    12. mousePosition.x += left;
    13. mousePosition.y += bottom;
    14. Events.mousePosition = mousePosition;
    15.  
    16. if (Event.current.type == EventType.Repaint)
    17. {
    18. GL.Viewport(viewportRect);
    19.  
    20. Matrix4x4 projectionMatrix = Matrix4x4.Ortho(left, right, bottom, top, -1f, 1f);
    21. GL.LoadProjectionMatrix(projectionMatrix);
    22.            
    23. GLDraw.DrawMesh(main.parentMesh.colors, main.parentMesh.triangles, main.parentMesh.vertices, GL_Material);
    24.                            
    25. }
    26.  
     
    Last edited: Apr 28, 2024
  6. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    7,019
    You could simply embed a preview scene in your editor. Behaves like normal scene view for the most part. I believe this is what the Mesh importer preview for models and animations are built with.
     
    TwoBitMachines likes this.
  7. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,219
    Yes you can. The GL class in Unity is an abstract implementation of classical OpenGL immediate mode. Though while OpenGL has several different matrices (modelview, projection, texture, color), in Unity we only really have the modelview matrix (view and model matrix at the same time, just classic OpenGL) and projection matrix. The GL class only directly exposes the modelview matrix. The projection matrix can be loaded / replaced through one of the "Load" methods (LoadOrtho, LoadPixelMatrix, LoadProjectionMatrix).

    Though instead of manually drawing individual vertices and triangles, you can also manually render a camera to your window. Though the Unity editor is a bit strange with the camera rect. For reasons unknown custom secondary cameras are sill limited and somewhat linked to the size of the gameview when you render them directly in a IMGUI window. Though the easiest workaround is to use a RenderTexture on the camera and set it up to have the desired size and then just draw the texture in the editor window. This seems to be the most reliable way.

    Note when you create temporary cameras and stuff like that (just as the scene view does) you should use
    EditorUtility.CreateGameObjectWithHideFlags
    and pass
    HideFlags.HideAndDontSave
    as hideflags. This avoids getting the scene "dirty" so it doesn't show up as "unaved" as objects that are marked as don't save are not saved into the scene. Though when creating a normal GO, it immediately dirties the scene, even when you set the hideflags afterwards. Usually editor windows (such as the sceneview) would create their personal camera object in Awake / OnEnable and destroy them (DestroyImmediate) in OnDestroy / OnDisable.

    When you have an actual camera, you can also use all the conversion methods that the camera provides based on the used projection. That's what a camera IS after all. A camera is just a collection of parameters and settings.
     
    TwoBitMachines likes this.