Search Unity

RuntimePanelUtils.ScreenToPanel() seems to always return the input vector unchanged

Discussion in 'UI Toolkit' started by Ryuuguu, Sep 20, 2020.

  1. Ryuuguu

    Ryuuguu

    Joined:
    Apr 14, 2007
    Posts:
    391
    I trying to use RuntimePanelUtils.ScreenToPanel() with

    panel = GetComponent<UIDocument>().rootVisualElement.panel;
    Debug.Log( RuntimePanelUtils.ScreenToPanel(panel, Vector2.one*100f));


    I run this but change the panelSettings between runs.
    I changed the scaleMode and the scale mode parameters but it always returns (100,100).
    I also set a VisualElement.transform.position to this result from the function and it moves around the screen. I would expect the debug output to change and the element to stay in the same place.

    Am I calling this wrong?

    Code (CSharp):
    1. void Start()
    2.     {
    3.         root = GetComponent<UIDocument>().rootVisualElement;
    4.         var panel = root.panel;
    5.      
    6.         var corner = Screen.safeArea.max;
    7.         root.styleSheets.Add(Resources.Load<StyleSheet>("HexGrid_Style"));
    8.         var quickToolVisualTree = Resources.Load<VisualTreeAsset>("HexGrid_Main");
    9.         quickToolVisualTree.CloneTree(root);
    10.         Debug.Log("safeSize: " + corner);
    11.         Debug.Log("0,0 : " + RuntimePanelUtils.ScreenToPanel(panel, Vector2.zero));
    12.         Debug.Log("100,100 : " +
    13.                   RuntimePanelUtils.ScreenToPanel(panel, Vector2.one*100f));
    14.         Debug.Log("root scale: " + root.transform.scale);
    15.      
    16.         NewAt(Vector3.one * 100,Vector3.one * 20);
    17.         NewAt(RuntimePanelUtils.ScreenToPanel(panel,corner)-(Vector2.one * 30)
    18.             ,Vector3.one * 20);
    19.     }
    20.  
    21.     public void NewAt(Vector3 location, Vector3 scale) {
    22.         var ve =   new UitHex();
    23.         ve.EnableInClassList("HexGrid-Hex", true);
    24.         UitHexGrid.SetupHex(ve,location,scale);
    25.         root.Add(ve);
    26.     }
     
  2. Ryuuguu

    Ryuuguu

    Joined:
    Apr 14, 2007
    Posts:
    391
    I checked the Code behind RuntimePanelUtils.ScreenToPanel() and wrote my own based on Package code. Most of the work is done in ResolveScale which cut'n'pasted from PanelSettings.ResolveScale() where is an internal method. this works fine.

    Code (CSharp):
    1.  
    2. public class TestPanelScreen : MonoBehaviour {
    3.     private VisualElement root;
    4.  
    5.     // Start is called before the first frame update
    6.     void Start() {
    7.         var camara = Camera.main;
    8.         root = GetComponent<UIDocument>().rootVisualElement;
    9.  
    10.        
    11.         var corner = Screen.safeArea.max;
    12.         root.styleSheets.Add(Resources.Load<StyleSheet>("HexGrid_Style"));
    13.         var quickToolVisualTree = Resources.Load<VisualTreeAsset>("HexGrid_Main");
    14.         quickToolVisualTree.CloneTree(root);
    15.         var panel = root.panel;
    16.         var scale = ResolveScale(GetComponent<UIDocument>().panelSettings,
    17.             new Rect(0, 0, Screen.width, Screen.height),
    18.             Screen.dpi);
    19.         Debug.Log("safeSize: " + corner);
    20.         Debug.Log("0,0 : " + ScreenToPanel(scale, Vector2.zero));
    21.         Debug.Log("100,100 : " +
    22.                   ScreenToPanel(scale, Vector2.one * 100f));
    23.         Debug.Log("root scale: " + root.transform.scale);
    24.         Debug.Log("panel scale: " + panel.visualTree.transform.scale);
    25.         Debug.Log("hacked scale: " + panel.visualTree.transform.scale);
    26.  
    27.         NewAt(Vector3.one * 100, Vector3.one * 20);
    28.         NewAt(ScreenToPanel(scale, corner) - (Vector2.one * 30)
    29.             , Vector3.one * 20);
    30.     }
    31.  
    32.     public Vector2 ScreenToPanel(float scale, Vector2 screen) {
    33.        return screen*scale;
    34.     }
    35.    
    36.     public void NewAt(Vector3 location, Vector3 scale) {
    37.         var ve = new UitHex();
    38.         ve.EnableInClassList("HexGrid-Hex", true);
    39.         UitHexGrid.SetupHex(ve, location, scale);
    40.         root.Add(ve);
    41.     }
    42.  
    43.     float ResolveScale(PanelSettings ps, Rect targetRect, float screenDpi) {
    44.         // Calculate scaling
    45.         float resolvedScale = 1.0f;
    46.         switch (ps.scaleMode) {
    47.             case PanelScaleModes.ConstantPixelSize:
    48.                 break;
    49.             case PanelScaleModes.ConstantPhysicalSize: {
    50.                 var dpi = screenDpi == 0.0f ? ps.fallbackDpi : screenDpi;
    51.                 if (dpi != 0.0f)
    52.                     resolvedScale = ps.referenceDpi / dpi;
    53.                 }
    54.                 break;
    55.             case PanelScaleModes.ScaleWithScreenSize:
    56.                 if (ps.referenceResolution.x * ps.referenceResolution.y != 0) {
    57.                     var refSize = (Vector2) ps.referenceResolution;
    58.                     var sizeRatio = new Vector2(targetRect.width / refSize.x, targetRect.height / refSize.y);
    59.                     var denominator = 0.0f;
    60.                     switch (ps.screenMatchMode) {
    61.                         case PanelScreenMatchModes.Expand:
    62.                             denominator = Mathf.Min(sizeRatio.x, sizeRatio.y);
    63.                             break;
    64.                         case PanelScreenMatchModes.Shrink:
    65.                             denominator = Mathf.Max(sizeRatio.x, sizeRatio.y);
    66.                             break;
    67.                         default: // PanelScreenMatchModes.MatchWidthOrHeight:
    68.                             var widthHeightRatio = Mathf.Clamp01(ps.match);
    69.                             denominator = Mathf.Lerp(sizeRatio.x, sizeRatio.y, widthHeightRatio);
    70.                             break;
    71.                     }
    72.                     if (denominator != 0.0f)
    73.                         resolvedScale = 1.0f / denominator;
    74.                 }
    75.                 break;
    76.         }
    77.         return resolvedScale;
    78.     }
    79. }
    80.  
     
  3. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    ScreenToPanel will return the input value for all panels by default. The scaling parameters in the PanelSettings do not affect this. They will affect a the transform.scale of global root visual Element (the parent of your UIDocument's rootVisualElement)

    The ScreenToPanel is used by the EventSystem to translate mouse click coordinates to the local panel space (which is not the space of the UIDocument's rootVisualElement). This is only useful when you set your panel to render to a texture, then map that texture on a mesh inside your scene. the ScreenToPanel should then convert "screen" coordinates to the u,v coordinates in the target texture, usually by doing a rayCast.

    There are samples in the com.unity.ui package, In the Package Manager window, select the com.unity.ui Package, then in the Samples section, click Import. You can then select the Window>UI Toolkit>Examples>Rendering>RenderTexture (Runtime) to see this custom ScreenToPanel in action

    In VisualElement's world, the transform is akin to the transform property in css. It's a local transformation applied to the element, after it's layout has been computed. Setting transform.position to (100,100) will translate the element from it's computed layout value.

    What are you trying to do exactly? If you want to know how many tiles will fit the screen, use:

    myUIDocument.RegisterCallback<GeometryChangedEvent>( (evt) => Debug.Log("Size:" + evt.newRect));
     
    Last edited: Sep 22, 2020
  4. Ryuuguu

    Ryuuguu

    Joined:
    Apr 14, 2007
    Posts:
    391
    Thanks.
    I was trying to find the size of my UIDocument's root VisualElement in its own Pixels ( not screen pixels).
    GetComponent<UIDocument>().rootVisualElement.RegisterCallback<GeometryChangedEvent>( (evt) => Debug.Log("Size:" + evt.newRect));

    using GetComponent<UIDocument>().rootVisualElement.hierarchy.parent.panel with RuntimePanelUtils.ScreenToPanel() and Screen.safeArea.max also worked.
     
  5. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    Since Layout computation is async, visualElement.layout value will contain NaN values at first. To get notified as soon as its ready, registering to GeometryChangedEvent is the way to go. After that, rootVisualElement.layout will contain the values you seek.
     
    Ryuuguu likes this.