Search Unity

Drawing lines in the editor

Discussion in 'Scripting' started by Linusmartensson, Dec 26, 2010.

  1. Linusmartensson

    Linusmartensson

    Joined:
    Dec 17, 2010
    Posts:
    4
    Hello everyone!

    So, I had three things: some left-over time, a problematic line drawing script, and a desire to get it working.
    Give and take a few hours:


    It's based on the original Drawing.DrawLine script found on the wiki, but I modified it a bit to allow it to work in editors, rather than the scene. Apparently, some issues exist that prevent it from working as expected there. Both scaling and rotating around pivots apparently have problems on non-zero pivot points.
    Whilst looking around in the forums, I noticed some people having issues with that, so I thought I'd post it up here to see if it works for everyone.

    The changes aren't all straight-forward, but it should be fairly simple to compare to the original (C# version).
    In addition, I took the liberty of fixing a few bugs and adding in some fake anti-aliasing (a 1x3 texture going transparent-white-transparent) as well as beziér curve drawing. (I also temporarily removed most of the alternative functions, since I wanted to minimize error sources)

    I'll be contributing it back to the wiki in a while, but first I'd be a very happy puppy if people could tell me whether or not it's stable, what works and what doesn't. :)


    Linus

    Edit:
    Another image, showing this in a more useful context, along with the corresponding unitypackage. :)

    You'll also notice some limitations in the shadows, that's because the lines aren't capped. I probably won't fix that at the moment.
     

    Attached Files:

    Last edited: Dec 26, 2010
  2. PaulAsh

    PaulAsh

    Joined:
    Nov 29, 2010
    Posts:
    108
    Thank god, a good method for drawing lines in editor windows!

    I'll let you know how my implementation goes.
     
  3. kenshin

    kenshin

    Joined:
    Apr 21, 2010
    Posts:
    940
    Thank's a lot for sharing Linus!
     
  4. PaulAsh

    PaulAsh

    Joined:
    Nov 29, 2010
    Posts:
    108
    working great so far, having some problems with bezierline, but I'm pretty sure its just because I'm not using it right, the DrawLine however works amazing
     
  5. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    Huge thanks!

    This would have taken me too long to do myself. Massive time saver!
     
  6. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    I got them working after a small hump: The tangents you give aren't relative to the related start/end points. So, I was doing:

    Code (csharp):
    1.  Drawing.bezierLine( startPoint, Vector2.right * 50.0f, endPoint, - Vector2.right * 50.0f, Color.gray, 1.0f, true, 8);
    But this should have been:

    Code (csharp):
    1.  
    2.  public static void bezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments);
    3.  
    4.  Drawing.bezierLine(startPoint,[B] startPoint+[/B] Vector2.right * 50.0f, endPoint, [B]endPoint- [/B]Vector2.right * 50.0f, Color.gray, 1.0f, true, 8);
    So just add the start/end "tangents" onto the start or end vectors for those fields. They're not really tangents - they're control points in a bezier of (some*) degree.

    Linus: my only real feedback at the moment is just the minor point above... the name "tangent" is slightly misleading? Also maybe call the function "DrawBezier" to be more in line with convention? Really minor crits... thanks so much for this. Lovely work!

    *2nd?
     
    Last edited: Jan 5, 2011
  7. xathos

    xathos

    Joined:
    Sep 24, 2009
    Posts:
    23
    Awesome. I've been looking for a way to manually draw lines in the editor (Unity GUI is useless to build node based extensions without it). I noticed DrawLine in the .Net API, but I wasn't sure if it would work in Unity (and I pretty much assumed that it didn't).
    I had just about resigned myself to trying to scale a 2d image of a curve using GUITexture, or using plugins to draw it (not too sure how to go about that), so this is an awesome solution!

    Thanks for sharing this.
     
    Last edited: Jan 21, 2011
  8. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    Just noticed another issue.

    When using scrollbars (vertical or horizontal), and you're not at the very top and very left of the area the lines disappear altogether. I'm not sure how you'd fix this but I'm starting to look into it.

    i.e.
    Code (csharp):
    1. scrollPos = GUI.BeginScrollView(
    2.                 new Rect(0, 0, position.width, position.height),
    3.                 scrollPos,
    4.                 new Rect(0, 0, 10000, 10000), true, true
    5.             );
    is used, and possibly the view matrix then starts drawing the lines waaay outside the actual window if the scrollPos isn't (0,0)?

    Haven't yet looked into a way to fix it but if I find anything, I'll be back.
     
  9. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    From what I can tell, GUI.matrix isn't affected at all by BeginScrollView/EndScrollView scrolling, so it's not that.
     
  10. sayezz

    sayezz

    Joined:
    Mar 9, 2011
    Posts:
    16
    Thanks! Helped me a lot! Saved me a bunch of time! Thanks!
     
  11. Flowan

    Flowan

    Joined:
    Feb 4, 2011
    Posts:
    5
    Hi I'll also worked on an NodeBasedEditor thx's for this scripts.
    I also have troubles with the lines, inside a ScrollView. If anyone of you is still working on this issue please post some of your tries. I'll try to fix this bug, any idea would be nice
     
  12. Skjalg

    Skjalg

    Joined:
    May 25, 2009
    Posts:
    211
    Whats wrong with;
    ?
     
  13. Flowan

    Flowan

    Joined:
    Feb 4, 2011
    Posts:
    5
    Hi thx for that hint!! Works create also with Handles.DrawLine. But why isn't the Drawing script it self isn't using the Handles routines??
     
  14. Eiznek

    Eiznek

    Joined:
    Jun 9, 2011
    Posts:
    374
    I had been looking around the scripting area's and such for this very exact thing. Thanks just made me very happy
     
  15. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Handles.DrawBezier() is meant for drawing bezier lines in the scene in 3d. The code OP gave is for bezier lines in editor GUI.
     
  16. Eric Molitor

    Eric Molitor

    Joined:
    Aug 1, 2011
    Posts:
    18
  17. Zogg

    Zogg

    Joined:
    Mar 28, 2009
    Posts:
    158
    I'm currently using a similar method for my generic graph editor aka "The Spaghetti Machine". The problem is that when you have a large number of spagh... err, splines, this consumes a lot of CPU time.

    The solution I found is the following : When a spline is currently changing (windows being dragged around), the spines are drawn directly on screen. Otherwise they are once drawn to a texture (with SetPixel) and then the texture is drawn to the screen at every refresh, without any new spline calculations. This way, we only need to recalculate the spline if vEndPoint - vStartPoint changes.

    Thought this might help other people encountering similar problems.

    [Edit:] I replaced the single texture per line by one or more textures, depending on the length of the spline. Each texture has a maximal size. This avoids having giant textures for long, roughly diagonal splines (with surface proportional to the square of the length).
     
    Last edited: Feb 17, 2012
  18. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    I too have run into problems trying to use this script inside a ScrollView. Anyone know what's up with that?
     
  19. typehm

    typehm

    Joined:
    Jun 24, 2011
    Posts:
    161
    This class works very well in Windows editor.But incorrect in OSX editor.

    here is the screen capture.

    on windows:


    on OSX:


    They are run the same script, but result not same.

    Does anyone has the same problem? How to solve this?
     
  20. typehm

    typehm

    Joined:
    Jun 24, 2011
    Posts:
    161
    I do some modify to the code. It works well on Mac now.

    here is the code:

    Code (csharp):
    1.  
    2. public class Drawing
    3. {
    4.  
    5.     static Texture2D _aaLineTex = null;
    6.  
    7.     static Texture2D _lineTex = null;
    8.  
    9.     static Texture2D adLineTex
    10.     {
    11.         get
    12.         {
    13.             if (!_aaLineTex)
    14.             {
    15.                 _aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, true);
    16.                 _aaLineTex.SetPixel(0, 0, new Color(1, 1, 1, 0));
    17.                 _aaLineTex.SetPixel(0, 1, Color.white);
    18.                 _aaLineTex.SetPixel(0, 2, new Color(1, 1, 1, 0));
    19.                 _aaLineTex.Apply();
    20.             }
    21.             return _aaLineTex;
    22.         }
    23.     }
    24.  
    25.     static Texture2D lineTex
    26.     {
    27.         get
    28.         {
    29.             if (!_lineTex)
    30.             {
    31.                 _lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, true);
    32.                 _lineTex.SetPixel(0, 1, Color.white);
    33.                 _lineTex.Apply();
    34.             }
    35.             return _lineTex;
    36.         }
    37.     }
    38.  
    39.  
    40.     static void DrawLineMac(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    41.     {
    42.         Color savedColor = GUI.color;
    43.         Matrix4x4 savedMatrix = GUI.matrix;
    44.        
    45.         float oldWidth = width;
    46.        
    47.         if (antiAlias) width *= 3;
    48.         float angle = Vector3.Angle(pointB - pointA, Vector2.right) * (pointA.y <= pointB.y?1:-1);
    49.         float m = (pointB - pointA).magnitude;
    50.  
    51.         if (m > 0.01f)
    52.         {
    53.             Vector3 dz = new Vector3(pointA.x, pointA.y, 0);
    54.             Vector3 offset = new Vector3((pointB.x - pointA.x) * 0.5f,
    55.                                        (pointB.y - pointA.y) * 0.5f,
    56.                                        0f);
    57.  
    58.             Vector3 tmp = Vector3.zero;
    59.  
    60.             if (antiAlias)
    61.                 tmp = new Vector3( -oldWidth * 1.5f * Mathf.Sin(angle * Mathf.Deg2Rad), oldWidth * 1.5f * Mathf.Cos(angle * Mathf.Deg2Rad) );
    62.             else
    63.                 tmp = new Vector3( -oldWidth * 0.5f * Mathf.Sin(angle * Mathf.Deg2Rad), oldWidth * 0.5f * Mathf.Cos(angle * Mathf.Deg2Rad) );
    64.  
    65.             GUI.color = color;
    66.             GUI.matrix = translationMatrix(dz) * GUI.matrix;
    67.             GUIUtility.ScaleAroundPivot(new Vector2(m, width), new Vector2(-0.5f, 0));
    68.             GUI.matrix = translationMatrix(-dz) * GUI.matrix;
    69.             GUIUtility.RotateAroundPivot(angle, Vector2.zero);
    70.             GUI.matrix = translationMatrix(dz  - tmp - offset) * GUI.matrix;
    71.  
    72.             GUI.DrawTexture(new Rect(0, 0, 1, 1), antiAlias ? adLineTex :  lineTex);
    73.         }
    74.  
    75.         GUI.matrix = savedMatrix;
    76.  
    77.         GUI.color = savedColor;
    78.     }
    79.  
    80.  
    81.     static void DrawLineWindows(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    82.     {
    83.  
    84.         Color savedColor = GUI.color;
    85.  
    86.         Matrix4x4 savedMatrix = GUI.matrix;
    87.  
    88.  
    89.  
    90.  
    91.         if (antiAlias) width *= 3;
    92.  
    93.         float angle = Vector3.Angle(pointB - pointA, Vector2.right) * (pointA.y <= pointB.y ? 1 : -1);
    94.  
    95.         float m = (pointB - pointA).magnitude;
    96.  
    97.         Vector3 dz = new Vector3(pointA.x, pointA.y, 0);
    98.  
    99.         GUI.color = color;
    100.  
    101.         GUI.matrix = translationMatrix(dz) * GUI.matrix;
    102.  
    103.         GUIUtility.ScaleAroundPivot(new Vector2(m, width), new Vector2(-0.5f, 0));
    104.  
    105.         GUI.matrix = translationMatrix(-dz) * GUI.matrix;
    106.  
    107.         GUIUtility.RotateAroundPivot(angle, new Vector2(0, 0));
    108.  
    109.         GUI.matrix = translationMatrix(dz + new Vector3(width / 2, -m / 2) * Mathf.Sin(angle * Mathf.Deg2Rad)) * GUI.matrix;
    110.  
    111.  
    112.         GUI.DrawTexture(new Rect(0, 0, 1, 1), !antiAlias ? lineTex : adLineTex);
    113.  
    114.         GUI.matrix = savedMatrix;
    115.  
    116.         GUI.color = savedColor;
    117.  
    118.     }
    119.  
    120.  
    121.  
    122.     public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    123.     {
    124.         if (Application.platform == RuntimePlatform.WindowsEditor)
    125.         {
    126.             DrawLineWindows(pointA, pointB, color, width, antiAlias);
    127.         }
    128.         else if (Application.platform == RuntimePlatform.OSXEditor)
    129.         {
    130.             DrawLineMac(pointA, pointB, color, width, antiAlias);
    131.         }
    132.     }
    133.  
    134.  
    135.  
    136.     static void bezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments)
    137.     {
    138.  
    139.         Vector2 lastV = cubeBezier(start, startTangent, end, endTangent, 0);
    140.  
    141.         for (int i = 1; i < segments; ++i)
    142.         {
    143.  
    144.             Vector2 v = cubeBezier(start, startTangent, end, endTangent, i / (float)segments);
    145.  
    146.  
    147.  
    148.             Drawing.DrawLine(
    149.  
    150.                 lastV,
    151.  
    152.                 v,
    153.  
    154.                 color, width, antiAlias);
    155.  
    156.             lastV = v;
    157.  
    158.         }
    159.  
    160.     }
    161.  
    162.  
    163.  
    164.     private static Vector2 cubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t)
    165.     {
    166.  
    167.         float rt = 1 - t;
    168.  
    169.         return rt * rt * rt * s + 3 * rt * rt * t * st + 3 * rt * t * t * et + t * t * t * e;
    170.  
    171.     }
    172.  
    173.  
    174.  
    175.     private static Matrix4x4 translationMatrix(Vector3 v)
    176.     {
    177.  
    178.         return Matrix4x4.TRS(v, Quaternion.identity, Vector3.one);
    179.  
    180.     }
    181. }
     
  21. Maelstroms

    Maelstroms

    Joined:
    Nov 26, 2009
    Posts:
    11
    Hello,

    When trying to draw a 2D line using the DrawLine function, i get this error :

    Code (csharp):
    1.  
    2. NullReferenceException: Object reference not set to an instance of an object
    3. UnityEngine.GUI.set_matrix (Matrix4x4 value)
    4. UnityEngine.GUIUtility.ScaleAroundPivot (Vector2 scale, Vector2 pivotPoint)
    5. Drawing.DrawLine (Vector2 _pointA, Vector2 _pointB, Color _color, Single _width) (at Assets/Scripts/Drawing.cs:46)
    6.  
    The 2 vectors i'm sending seem to be correct, and I can't step into ScaleAroundPivot() to check what's going on. Do you guys have any idea what I could be doing wrong ?

    I'm running Unity on a Mac btw, and my app plays on an ipad.

    Thanks in advance,

    M.
     
  22. Bovine

    Bovine

    Joined:
    Oct 13, 2010
    Posts:
    195
    Did anyone fix the issue with lines disappearing as soon as you scroll the scroll view?

    Cheers
    Bovine
     
  23. FluffyCheese

    FluffyCheese

    Joined:
    Oct 13, 2010
    Posts:
    6
    A little bit of a hack, but here is how I fixed the issue of scrolling lines disappearing

    First off, draw your lines outside the scroll area:

    Code (csharp):
    1. void OnGUI()
    2.     {
    3.         Color s = new Color(0.4f, 0.4f, 0.5f);
    4.         curveFromTo(wr, wr2, new Color(0.3f,0.7f,0.4f), s);
    5.         curveFromTo(wr2, wr3, new Color(0.7f,0.2f,0.3f), s);
    6.  
    7.         scrollPos = GUI.BeginScrollView(new Rect(0,0, position.width, position.height), scrollPos, new Rect(0,0,1000,1000));
    8.  
    9.         BeginWindows();
    10.         wr = GUI.Window(0, wr, doWindow, "hello");
    11.         wr2 = GUI.Window(1, wr2, doWindow, "world");
    12.         wr3 = GUI.Window(2, wr3, doWindow, "!");
    13.         EndWindows();
    14.  
    15.         GUI.EndScrollView();
    16.  
    17.     }
    Then simply add the offset for scrollPos to your lines

    Code (csharp):
    1. void curveFromTo(Rect wr, Rect wr2, Color color, Color shadow)
    2.     {
    3.  
    4.         wr.x -= scrollPos.x;
    5.         wr.y -= scrollPos.y;
    6.         wr2.x -= scrollPos.x;
    7.         wr2.y -= scrollPos.y;
    8.  
    9.         Drawing.bezierLine(
    10.             new Vector2(wr.x + wr.width, wr.y + 3 + wr.height / 2),
    11.             new Vector2(wr.x + wr.width + Mathf.Abs(wr2.x - (wr.x + wr.width)) / 2, wr.y + 3 + wr.height / 2),
    12.             new Vector2(wr2.x, wr2.y + 3 + wr2.height / 2),
    13.             new Vector2(wr2.x - Mathf.Abs(wr2.x - (wr.x + wr.width)) / 2, wr2.y + 3 + wr2.height / 2), shadow, 5, true,20);
    14.         Drawing.bezierLine(
    15.             new Vector2(wr.x + wr.width, wr.y + wr.height / 2),
    16.             new Vector2(wr.x + wr.width + Mathf.Abs(wr2.x - (wr.x + wr.width)) / 2, wr.y + wr.height / 2),
    17.             new Vector2(wr2.x, wr2.y + wr2.height / 2),
    18.             new Vector2(wr2.x - Mathf.Abs(wr2.x - (wr.x + wr.width)) / 2, wr2.y + wr2.height / 2), color, 2, true,20);
    19.     }
    20.  
     
  24. mudloop

    mudloop

    Joined:
    May 3, 2009
    Posts:
    1,107

    I've been using the Handles stuff in editor GUI without problems. I haven't tried beziers though, but circles, rects and lines seem to work, so I assume beziers would work as well - and they don't have the SrollView issue.

    That may be new in Unity 3.5 though.

    Unity has a bug where using Gui.RotateAroundPivot inside a scrollview clips the gui incorrectly (just filed a bug report on this, so a clean fix for the ScrollView issue doesn't seem possible when using this drawing class. FluffyCheese's solution seems good though.

    However, I'd recommend using Handles instead, as it solves the ScrollView problem.

    Edit : not 100% sure on this, but I believe they use the handles class in the animation editor, so if that's true, handles in the editor windows will probably have been functional at least since they added that.

    Edit 2 : I originally posted that I hadn't tested this on Windows - I did now and it also works fine.
     
    Last edited: Apr 4, 2012
  25. StasK

    StasK

    Joined:
    Oct 5, 2012
    Posts:
    1
    I fix Bezier drawing in code listing from post on the first page.

    So the fixed variant is listed below. But it draw lines not rightly on my PC in EditorWindow.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public static class Drawing
    5. {
    6.     static Texture2D _aaLineTex = null;
    7.  
    8.     static Texture2D _lineTex = null;
    9.  
    10.     static Texture2D adLineTex
    11.     {
    12.         get
    13.         {
    14.             if (!_aaLineTex)
    15.             {
    16.                 _aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, true);
    17.  
    18.                 _aaLineTex.SetPixel(0, 0, new Color(1, 1, 1, 0));
    19.  
    20.                 _aaLineTex.SetPixel(0, 1, Color.white);
    21.  
    22.                 _aaLineTex.SetPixel(0, 2, new Color(1, 1, 1, 0));
    23.  
    24.                 _aaLineTex.Apply();
    25.             }
    26.  
    27.             return _aaLineTex;
    28.         }
    29.     }
    30.  
    31.     static Texture2D lineTex
    32.     {
    33.         get
    34.         {
    35.  
    36.             if (!_lineTex)
    37.             {
    38.                 _lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, true);
    39.  
    40.                 _lineTex.SetPixel(0, 1, Color.white);
    41.  
    42.                 _lineTex.Apply();
    43.             }
    44.  
    45.             return _lineTex;
    46.         }
    47.     }
    48.  
    49.     static void DrawLineMac(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    50.     {
    51.         Color savedColor = GUI.color;
    52.  
    53.         Matrix4x4 savedMatrix = GUI.matrix;
    54.  
    55.         float oldWidth = width;
    56.  
    57.         if (antiAlias) width *= 3;
    58.  
    59.         float angle = Vector3.Angle(pointB - pointA, Vector2.right) * (pointA.y <= pointB.y ? 1 : -1);
    60.  
    61.         float m = (pointB - pointA).magnitude;
    62.  
    63.         if (m > 0.01f)
    64.         {
    65.  
    66.             Vector3 dz = new Vector3(pointA.x, pointA.y, 0);
    67.  
    68.             Vector3 offset = new Vector3((pointB.x - pointA.x)*0.5f, (pointB.y - pointA.y)*0.5f, 0f);
    69.  
    70.             Vector3 tmp = Vector3.zero;
    71.  
    72.             if (antiAlias)
    73.                 tmp = new Vector3(-oldWidth * 1.5f * Mathf.Sin(angle * Mathf.Deg2Rad), oldWidth * 1.5f * Mathf.Cos(angle * Mathf.Deg2Rad));
    74.             else
    75.                 tmp = new Vector3(-oldWidth * 0.5f * Mathf.Sin(angle * Mathf.Deg2Rad), oldWidth * 0.5f * Mathf.Cos(angle * Mathf.Deg2Rad));
    76.  
    77.             GUI.color = color;
    78.  
    79.             GUI.matrix = translationMatrix(dz) * GUI.matrix;
    80.  
    81.             GUIUtility.ScaleAroundPivot(new Vector2(m, width), new Vector2(-0.5f, 0));
    82.  
    83.             GUI.matrix = translationMatrix(-dz) * GUI.matrix;
    84.  
    85.             GUIUtility.RotateAroundPivot(angle, Vector2.zero);
    86.  
    87.             GUI.matrix = translationMatrix(dz - tmp - offset) * GUI.matrix;
    88.  
    89.             GUI.DrawTexture(new Rect(0, 0, 1, 1), antiAlias ? adLineTex : lineTex);
    90.         }
    91.  
    92.         GUI.matrix = savedMatrix;
    93.  
    94.         GUI.color = savedColor;
    95.     }
    96.  
    97.     static void DrawLineWindows(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    98.     {
    99.         Color savedColor = GUI.color;
    100.  
    101.         Matrix4x4 savedMatrix = GUI.matrix;
    102.  
    103.         if (antiAlias) width *= 3;
    104.  
    105.         float angle = Vector3.Angle(pointB - pointA, Vector2.right) * (pointA.y <= pointB.y ? 1 : -1);
    106.  
    107.         float m = (pointB - pointA).magnitude;
    108.  
    109.         Vector3 dz = new Vector3(pointA.x, pointA.y, 0);
    110.  
    111.         GUI.color = color;
    112.  
    113.         GUI.matrix = translationMatrix(dz) * GUI.matrix;
    114.  
    115.         GUIUtility.ScaleAroundPivot(new Vector2(m, width), new Vector2(-0.5f, 0));
    116.  
    117.         GUI.matrix = translationMatrix(-dz) * GUI.matrix;
    118.  
    119.         GUIUtility.RotateAroundPivot(angle, new Vector2(0, 0));
    120.  
    121.         GUI.matrix = translationMatrix(dz + new Vector3(width / 2, -m / 2) * Mathf.Sin(angle * Mathf.Deg2Rad)) * GUI.matrix;
    122.  
    123.         GUI.DrawTexture(new Rect(0, 0, 1, 1), !antiAlias ? lineTex : adLineTex);
    124.  
    125.         GUI.matrix = savedMatrix;
    126.  
    127.         GUI.color = savedColor;
    128.     }
    129.  
    130.     public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    131.     {
    132.         if (Application.platform == RuntimePlatform.WindowsEditor)
    133.         {
    134.             DrawLineWindows(pointA, pointB, color, width, antiAlias);
    135.         }
    136.         else if (Application.platform == RuntimePlatform.OSXEditor)
    137.         {
    138.             DrawLineMac(pointA, pointB, color, width, antiAlias);
    139.         }
    140.     }
    141.  
    142.     public static void BezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments)
    143.     {
    144.         Vector2 lastV = cubeBezier(start, startTangent, end, endTangent, 0);
    145.  
    146.         for (int i = 1; i <= segments; ++i)
    147.         {
    148.             Vector2 v = cubeBezier(start, startTangent, end, endTangent, i / (float)segments);
    149.  
    150.             Drawing.DrawLine(lastV, v, color, width, antiAlias);
    151.  
    152.             lastV = v;
    153.         }
    154.     }
    155.  
    156.     private static Vector2 cubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t)
    157.     {
    158.         float rt = 1 - t;
    159.  
    160.         return (((s * rt) * rt) * rt) + (((3 * st * rt) * rt) * t) + (((3 * et * rt) * t) * t) + (((e * t) * t) * t);
    161.     }
    162.  
    163.  
    164.     private static Matrix4x4 translationMatrix(Vector3 v)
    165.     {
    166.  
    167.         return Matrix4x4.TRS(v, Quaternion.identity, Vector3.one);
    168.  
    169.     }
    170. }
    171.  
     
  26. psase

    psase

    Joined:
    Feb 14, 2013
    Posts:
    1
    Cooooooooooooooool!!!!!!!!

    Your modified code works well in android!!

    If I use "DrawLineWindows", it cause problem... but use "DrawLineMac" it works well!!

    You save me!! very thanks!!

    (sorry. I'm not used to English...)
     
  27. everything4me

    everything4me

    Joined:
    Feb 15, 2013
    Posts:
    2
    really thank you ! I am taking a bow before you ! :)
     
  28. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    Actually the Handles.Draw methods work just fine in the OnGUI method of an editor window. For example, to draw a diagonal line across your editor window, use "Handles.DrawLine(Vector3.zero, new Vector3(this.position.width, this.position.height, 0));"

    (This is true as of Unity 3.5.6 anyway -- it may not have worked back when @AnomalusUndrdog made that comment.)
     
  29. Azis

    Azis

    Joined:
    Aug 1, 2012
    Posts:
    4
    Awesome job. Thank you for the script, it shall help me a lot!
     
  30. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    I found myself with a renewed interest in line drawing and a need to draw lines both in an EditorWindow and in the game, ideally with the same code. I knew I would be drawing a lot of lines, so I wanted something reasonably efficient. I started with the function that begins this thread, and was able to get it to run more than four times faster by calculating the matrix directly rather than with Unity helper methods, and by using Graphics.DrawTexture instead of GUI.DrawTexture. (I'm using the free version of Unity, version 4.1.5.)

    Here is a graph comparing performance of the original function with my optimized version, in a standalone Windows build. For reference, I also compared it with direct calls to Graphics.DrawTexture, to see how many DrawTexture calls it is possible to make. For large numbers of lines the optimized version is more than four times faster than the original, and comes within about 70% of the raw performance of Graphics.DrawTexture. Line width and anti-aliasing had negligible effects on performance. (This is running on my laptop, with Windows 7 64-bit, Intel Core2 Duo CPU 2.53GHz, 4G RAM, NVIDIA GeForce GT 220M.)

    $Unity DrawLine performance in standalone build.png


    Here are some scribbles showing the math. Sorry, I was too lazy to type it out and draw nice diagrams, so you get a photo of my notebook instead :)

    $Unity DrawLine math.jpg


    Here is the optimized code. Note that you should only call DrawLine when (Event.current.type == EventType.Repaint). Enjoy!

    Code (csharp):
    1.  
    2. using System.Reflection;
    3. using UnityEngine;
    4.  
    5. // Line drawing routine originally courtesy of Linusmartensson:
    6. // http://forum.unity3d.com/threads/71979-Drawing-lines-in-the-editor
    7. //
    8. // Rewritten to improve performance by Yossarian King / August 2013.
    9. //
    10. // This version produces virtually identical results to the original (tested by drawing
    11. // one over the other and observing errors of one pixel or less), but for large numbers
    12. // of lines this version is more than four times faster than the original, and comes
    13. // within about 70% of the raw performance of Graphics.DrawTexture.
    14. //
    15. // Peak performance on my laptop is around 200,000 lines per second. The laptop is
    16. // Windows 7 64-bit, Intel Core2 Duo CPU 2.53GHz, 4G RAM, NVIDIA GeForce GT 220M.
    17. // Line width and anti-aliasing had negligible impact on performance.
    18. //
    19. // For a graph of benchmark results in a standalone Windows build, see this image:
    20. // https://app.box.com/s/hyuhi565dtolqdm97e00
    21. //
    22. // For a Google spreadsheet with full benchmark results, see:
    23. // https://docs.google.com/spreadsheet/ccc?key=0AvJlJlbRO26VdHhzeHNRMVF2UHZHMXFCTVFZN011V1E&usp=sharing
    24.  
    25. public static class Drawing
    26. {
    27.     private static Texture2D aaLineTex = null;
    28.     private static Texture2D lineTex = null;
    29.     private static Material blitMaterial = null;
    30.     private static Material blendMaterial = null;
    31.     private static Rect lineRect = new Rect(0, 0, 1, 1);
    32.  
    33.     // Draw a line in screen space, suitable for use from OnGUI calls from either
    34.     // MonoBehaviour or EditorWindow. Note that this should only be called during repaint
    35.     // events, when (Event.current.type == EventType.Repaint).
    36.     //
    37.     // Works by computing a matrix that transforms a unit square -- Rect(0,0,1,1) -- into
    38.     // a scaled, rotated, and offset rectangle that corresponds to the line and its width.
    39.     // A DrawTexture call used to draw a line texture into the transformed rectangle.
    40.     //
    41.     // More specifically:
    42.     //      scale x by line length, y by line width
    43.     //      rotate around z by the angle of the line
    44.     //      offset by the position of the upper left corner of the target rectangle
    45.     //
    46.     // By working out the matrices and applying some trigonometry, the matrix calculation comes
    47.     // out pretty simple. See https://app.box.com/s/xi08ow8o8ujymazg100j for a picture of my
    48.     // notebook with the calculations.
    49.     public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width, bool antiAlias)
    50.     {
    51.         // Normally the static initializer does this, but to handle texture reinitialization
    52.         // after editor play mode stops we need this check in the Editor.
    53.         #if UNITY_EDITOR
    54.         if (!lineTex)
    55.         {
    56.             Initialize();
    57.         }
    58.         #endif
    59.  
    60.         // Note that theta = atan2(dy, dx) is the angle we want to rotate by, but instead
    61.         // of calculating the angle we just use the sine (dy/len) and cosine (dx/len).
    62.         float dx = pointB.x - pointA.x;
    63.         float dy = pointB.y - pointA.y;
    64.         float len = Mathf.Sqrt(dx * dx + dy * dy);
    65.  
    66.         // Early out on tiny lines to avoid divide by zero.
    67.         // Plus what's the point of drawing a line 1/1000th of a pixel long??
    68.         if (len < 0.001f)
    69.         {
    70.             return;
    71.         }
    72.  
    73.         // Pick texture and material (and tweak width) based on anti-alias setting.
    74.         Texture2D tex;
    75.         Material mat;
    76.         if (antiAlias)
    77.         {
    78.             // Multiplying by three is fine for anti-aliasing width-1 lines, but make a wide "fringe"
    79.             // for thicker lines, which may or may not be desirable.
    80.             width = width * 3.0f;
    81.             tex = aaLineTex;
    82.             mat = blendMaterial;
    83.         }
    84.         else
    85.         {
    86.             tex = lineTex;
    87.             mat = blitMaterial;
    88.         }
    89.  
    90.         float wdx = width * dy / len;
    91.         float wdy = width * dx / len;
    92.  
    93.         Matrix4x4 matrix = Matrix4x4.identity;
    94.         matrix.m00 = dx;
    95.         matrix.m01 = -wdx;
    96.         matrix.m03 = pointA.x + 0.5f * wdx;
    97.         matrix.m10 = dy;
    98.         matrix.m11 = wdy;
    99.         matrix.m13 = pointA.y - 0.5f * wdy;
    100.  
    101.         // Use GL matrix and Graphics.DrawTexture rather than GUI.matrix and GUI.DrawTexture,
    102.         // for better performance. (Setting GUI.matrix is slow, and GUI.DrawTexture is just a
    103.         // wrapper on Graphics.DrawTexture.)
    104.         GL.PushMatrix();
    105.         GL.MultMatrix(matrix);
    106.         Graphics.DrawTexture(lineRect, tex, lineRect, 0, 0, 0, 0, color, mat);
    107.         GL.PopMatrix();
    108.     }
    109.  
    110.     // Other than method name, DrawBezierLine is unchanged from Linusmartensson's original implementation.
    111.     public static void DrawBezierLine(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, bool antiAlias, int segments)
    112.     {
    113.         Vector2 lastV = CubeBezier(start, startTangent, end, endTangent, 0);
    114.         for (int i = 1; i < segments; ++i)
    115.         {
    116.             Vector2 v = CubeBezier(start, startTangent, end, endTangent, i/(float)segments);
    117.             Drawing.DrawLine(lastV, v, color, width, antiAlias);
    118.             lastV = v;
    119.         }
    120.     }
    121.  
    122.     private static Vector2 CubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t)
    123.     {
    124.         float rt = 1 - t;
    125.         return rt * rt * rt * s + 3 * rt * rt * t * st + 3 * rt * t * t * et + t * t * t * e;
    126.     }
    127.  
    128.     // This static initializer works for runtime, but apparently isn't called when
    129.     // Editor play mode stops, so DrawLine will re-initialize if needed.
    130.     static Drawing()
    131.     {
    132.         Initialize();
    133.     }
    134.  
    135.     private static void Initialize()
    136.     {
    137.         if (lineTex == null)
    138.         {
    139.             lineTex = new Texture2D(1, 1, TextureFormat.ARGB32, false);
    140.             lineTex.SetPixel(0, 1, Color.white);
    141.             lineTex.Apply();
    142.         }
    143.         if (aaLineTex == null)
    144.         {
    145.             // TODO: better anti-aliasing of wide lines with a larger texture? or use Graphics.DrawTexture with border settings
    146.             aaLineTex = new Texture2D(1, 3, TextureFormat.ARGB32, false);
    147.             aaLineTex.SetPixel(0, 0, new Color(1, 1, 1, 0));
    148.             aaLineTex.SetPixel(0, 1, Color.white);
    149.             aaLineTex.SetPixel(0, 2, new Color(1, 1, 1, 0));
    150.             aaLineTex.Apply();
    151.         }
    152.  
    153.         // GUI.blitMaterial and GUI.blendMaterial are used internally by GUI.DrawTexture,
    154.         // depending on the alphaBlend parameter. Use reflection to "borrow" these references.
    155.         blitMaterial = (Material)typeof(GUI).GetMethod("get_blitMaterial", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
    156.         blendMaterial = (Material)typeof(GUI).GetMethod("get_blendMaterial", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
    157.     }
    158. }
    159.  
     
    Marrt and Demigiant like this.
  31. marcelobr

    marcelobr

    Joined:
    Sep 10, 2012
    Posts:
    12
    I have a desire to create a window that,
    I want to learn as it is, could you help me?
     
  32. 2FlyDreams

    2FlyDreams

    Joined:
    Oct 18, 2013
    Posts:
    42
    Last edited: May 9, 2014
  33. dear_cj

    dear_cj

    Joined:
    Jul 17, 2013
    Posts:
    2
  34. Zaicheg

    Zaicheg

    Joined:
    Sep 30, 2009
    Posts:
    46
    How to draw these lines behind some models (models should be in the foreground)?
    Illustration: http://screencast.com/t/I3y74VETh
     
  35. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    I've only used the lines as overlays, but you could try drawing lines from the OnPreRender method.
     
  36. Zaicheg

    Zaicheg

    Joined:
    Sep 30, 2009
    Posts:
    46
    The solution for me: a separate camera, which draws items. Render it manually in OnGUI (camera.Render()) after drawing lines.

    I draw a lot of lines (200-300), it takes a lot of resources.
    Illustration (Profiler): http://screencast.com/t/lLzi65epZ
    Illustration (Game View, the red intervals between the tiles): http://screencast.com/t/khVeI0cG
    There are ways to reduce the cost?
     
  37. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Perhaps look into Vectrosity, a solution thats a bit much for simple stuff like drawing a line from point A to point B, but since you need many intensive things... it is a very powerful tool it seems, and made by a trusted member of the community. might help you save some headache in the long run:

    http://starscenesoftware.com/vectrosity.html
     
  38. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    I should have looked at profiler results when I did my benchmarking, but all I did was measure total performance, in terms of number of lines that could be drawn per second. I was able to hit over 100,000 lines per second on a laptop that's a few years old. At 8 ms for 244 lines you seem to be getting around 30,000 lines per second, which seems low but at least it's roughly the same order of magnitude. With this technique Graphics.DrawTexture is the bottleneck and you can't do much about that. Matrix4x4.get_identity is surprisingly high. You could avoid this by tweaking my code to make the "matrix" variable global to the script, and only initialize it to identity once. That should help a bit.

    Or try a different technique, such as Vectrosity suggested by MD_Reptile. Good luck!
     
  39. Assault

    Assault

    Joined:
    Aug 21, 2012
    Posts:
    60
    Whats wrong with this forum? My posts keep dissapearing?

    Anyway I noticed that the small area in the top left corner of the screen gets inverted and magnified to the top right 1/4th of the screen. Really weird!

    This happens when I do Drawing.Drawline(new Vector2(0,0), new Vector2(200,200), Color.yellow, true);

    Any ideas? I am using the optimized C# version from yoyo´s post earlier in thei thread.
     

    Attached Files:

    Last edited: Feb 19, 2014
  40. Assault

    Assault

    Joined:
    Aug 21, 2012
    Posts:
    60
    Can anyone help with the problem above, or at least verify that the latest script in this thread works for them? I have been looking for a solution to draw lines on GUI, but none of the scripts that I have found work properly. The one in this thread looked promising, but it seems it is acting totally weird!

    Im actually really surprised that Unity does have built-in support for drawling lines on the GUI..
     
  41. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    Are you modifying GUI.matrix or using GUI.Window calls? I haven't tested those scenarios extensively. I applied the above script to a project yesterday and it was working great, but note that I'm using it for drawing lines in an Editor window, not in runtime. It should work for either, but hasn't been battle-tested in runtime.
     
  42. Assault

    Assault

    Joined:
    Aug 21, 2012
    Posts:
    60
    No, I am not modifying the GUI.matrix, or using GUI.Window calls (in fact, I have never used them for anything)

    The area in the top left corner that is flipped and magnified to top right corner seems to be 50 pixels wide and 35 pixels in height.

    yoyo, could you please try if you can reproduce the described behavior in runtime?
     
  43. Assault

    Assault

    Joined:
    Aug 21, 2012
    Posts:
    60
    Also, I just noticed using the latest "optimized" version causes my whole GUI dissapear in web player =(
     
  44. sicklepickle

    sicklepickle

    Joined:
    Apr 8, 2012
    Posts:
    44
    I've been having the same issue Assault - seems it has something to do with your Camera (try disabling it, doesn't help, but does draw a second offset copy).

    In any case, this temporary modification did the trick - enough to get on with some debugging anyway.

    Code (csharp):
    1.  
    2.         //Graphics.DrawTexture(lineRect, tex, lineRect, 0, 0, 0, 0, color, mat);
    3.         GUI.color = color;
    4.         GUI.DrawTexture( lineRect, tex );
    5.  
     
  45. Fainted

    Fainted

    Joined:
    Mar 24, 2014
    Posts:
    1
    This code is a godsend, thanks a lot! However, how would I go about retrieving the midpoint (t=0.5) of calculated curves?
     
  46. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    Hi @Fainted, sorry for the super slow response here, I haven't been monitoring this forum. To get the mid-point, I would suggest separating the calculation of the segment vertices from the drawing of the line, then you could iterate through the points to find total length and mid-point. Hope that helps, let me know if this is still of interest and if you need more details.
     
  47. Gooseman_1977

    Gooseman_1977

    Joined:
    Aug 15, 2013
    Posts:
    89
    thank so much for this code! I was looking for a plugin in the asset store to do just this...
     
  48. Xelnath

    Xelnath

    Joined:
    Jan 31, 2015
    Posts:
    402
    It looks like this broke in Unity 5.
     
  49. Xelnath

    Xelnath

    Joined:
    Jan 31, 2015
    Posts:
    402
  50. yoyo

    yoyo

    Joined:
    Apr 16, 2010
    Posts:
    112
    Derp :( ... I haven't tried it in 5.x.

    @Xelnath, have you tried your code in 4.6? Do your lines draw correctly there?