├── .gitattributes ├── Editor.meta ├── Editor ├── Popcron.Gizmos.asmdef ├── Popcron.Gizmos.asmdef.meta ├── Updater.cs └── Updater.cs.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── Constants.cs ├── Constants.cs.meta ├── Drawer.cs ├── Drawer.cs.meta ├── Drawers.meta ├── Drawers │ ├── CubeDrawer.cs │ ├── CubeDrawer.cs.meta │ ├── LineDrawer.cs │ ├── LineDrawer.cs.meta │ ├── PolygonDrawer.cs │ ├── PolygonDrawer.cs.meta │ ├── SquareDrawer.cs │ └── SquareDrawer.cs.meta ├── Element.cs ├── Element.cs.meta ├── Gizmos.cs ├── Gizmos.cs.meta ├── GizmosInstance.cs ├── GizmosInstance.cs.meta ├── Popcron.Gizmos.Runtime.asmdef └── Popcron.Gizmos.Runtime.asmdef.meta ├── package.json └── package.json.meta /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e3441179e0f8594d906cc3b9dd01c07 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Popcron.Gizmos.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Popcron.Gizmos.Editor", 3 | "references": [], 4 | "optionalUnityReferences": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [] 15 | } -------------------------------------------------------------------------------- /Editor/Popcron.Gizmos.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 565568beab7f479429923cd5dbb37a5c 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Updater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.NetworkInformation; 5 | using System.Net.Sockets; 6 | using System.Threading.Tasks; 7 | using UnityEditor; 8 | using UnityEditor.Callbacks; 9 | using UnityEditor.PackageManager; 10 | using UnityEditor.PackageManager.Requests; 11 | using UnityEngine; 12 | using PackageInfo = UnityEditor.PackageManager.PackageInfo; 13 | using Ping = System.Net.NetworkInformation.Ping; 14 | 15 | public class Updater : Editor 16 | { 17 | public class Package 18 | { 19 | public string version; 20 | } 21 | 22 | private const string PackageName = "com.popcron.gizmos"; 23 | private const string PackageURL = "https://raw.githubusercontent.com/popcron/gizmos/master/package.json"; 24 | private const string CanUpdateKey = "Popcron.Gizmos.CanUpdate"; 25 | private const string CheckForUpdateText = "Popcron/Gizmos/Check for updates"; 26 | private const string UpdateText = "Popcron/Gizmos/Update"; 27 | 28 | private static async Task IsUpdateAvailable() 29 | { 30 | Uri uri = new ("https://www.google.com/"); 31 | IPStatus result = await Task.Run(() => IsHostReachable(uri.Host)); 32 | if (result == IPStatus.Success) 33 | { 34 | // Try to check for update 35 | WebClient wc = new WebClient(); 36 | string json = await wc.DownloadStringTaskAsync(PackageURL); 37 | string versionText = JsonUtility.FromJson(json).version; 38 | Version version = Version.Parse(versionText); 39 | Version currentVersion = await GetLocalVersion(); 40 | if (currentVersion != null) 41 | { 42 | bool updateAvailable = currentVersion.CompareTo(version) < 0; 43 | return updateAvailable; 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | 50 | private static IPStatus IsHostReachable(string ip) 51 | { 52 | if (!NetworkInterface.GetIsNetworkAvailable()) 53 | { 54 | return IPStatus.DestinationUnreachable; 55 | } 56 | 57 | Ping pinger = new(); 58 | try 59 | { 60 | // Wait at most 2 seconds for the ICMP echo reply message 61 | int timeout = 2000; 62 | PingReply reply = pinger.Send(ip, timeout); 63 | return reply.Status; 64 | } 65 | catch (PingException) 66 | { 67 | return IPStatus.DestinationUnreachable; 68 | } 69 | catch (SocketException) 70 | { 71 | return IPStatus.DestinationUnreachable; 72 | } 73 | finally 74 | { 75 | pinger.Dispose(); 76 | } 77 | } 78 | 79 | private static async Task GetLocalVersion() 80 | { 81 | ListRequest listRequest = Client.List(true); 82 | while (!listRequest.IsCompleted) 83 | { 84 | await Task.Delay(1); 85 | } 86 | 87 | foreach (PackageInfo pack in listRequest.Result) 88 | { 89 | if (pack.name == PackageName) 90 | { 91 | if (pack.source == PackageSource.Local) continue; 92 | 93 | Version localVersion = Version.Parse(pack.version); 94 | return localVersion; 95 | } 96 | } 97 | 98 | return null; 99 | } 100 | 101 | [MenuItem(CheckForUpdateText, false, 0)] 102 | [DidReloadScripts] 103 | private static async void CheckForUpdates() 104 | { 105 | //check for updates 106 | bool canUpdate = await IsUpdateAvailable(); 107 | EditorPrefs.SetBool(CanUpdateKey, canUpdate); 108 | } 109 | 110 | [MenuItem(UpdateText, false, 0)] 111 | public static void Update() 112 | { 113 | //get the manifest.json file 114 | string path = Application.dataPath; 115 | path = Directory.GetParent(path).FullName; 116 | path = Path.Combine(path, "Packages", "manifest.json"); 117 | if (File.Exists(path)) 118 | { 119 | string text = File.ReadAllText(path); 120 | int index = text.IndexOf("\"lock\""); 121 | int start = index + text.Substring(index).IndexOf("\"" + PackageName + "\""); 122 | int end = start + text.Substring(start).IndexOf("}") + 2; 123 | string entry = text.Substring(start, end - start); 124 | 125 | //doesnt end with a comma, so remove the comma at the beginning of this entry if it exists because its the last entry 126 | if (!entry.EndsWith(",")) 127 | { 128 | if (text.Substring(start - 2).Contains(",")) 129 | { 130 | //4 spaces for tabs and 3 for quote, comma and } 131 | int comma = (start - 7) + text.Substring(start - 7).IndexOf(","); 132 | text = text.Remove(comma, 1); 133 | } 134 | } 135 | 136 | text = text.Replace(entry, ""); 137 | File.WriteAllText(path, text); 138 | 139 | AssetDatabase.Refresh(); 140 | } 141 | } 142 | 143 | [MenuItem(UpdateText, true)] 144 | private static bool CanUpdate() 145 | { 146 | return EditorPrefs.GetBool(CanUpdateKey); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /Editor/Updater.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0c08407a14daf94090effbcc059b50f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Phillip DaSilva-Damaskin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2f4789f4933c29c45afd5856465bdc8e 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Ah, gl lines](https://cdn.discordapp.com/attachments/377316629220032523/576127655712391198/unknown.png) 2 | # Gizmos 3 | Used for drawing runtime gizmos in builds and editor from any context in the code. It was created when I realized that the built in unity gizmos (Gizmos.DrawWireSphere, etc) don't render in builds, so I made this utility to be able to use. 4 | 5 | *Note: Currently doesn't work if a render pipline asset is set in the graphics settings of the project in SRP, if the version of the SRP package is below 7.1.1* 6 | 7 | ## Requirements 8 | - .NET Framework 4.5 9 | - Git 10 | 11 | ## Installation 12 | To install for local use, download this repo and copy everything from this repository to `/Packages/Popcron Gizmos` folder. 13 | 14 | If using 2018.3.x or higher, you can add a new entry to the manifest.json file in your Packages folder: 15 | ```json 16 | "com.popcron.gizmos": "https://github.com/popcron/gizmos.git" 17 | ``` 18 | 19 | The package checks for updates every time a compile happens, and it will say so under the `Popcron/Gizmos/Update` menu if one is available, upon pressing it will make unity update the package to the latest version thats here on github. Though you can always edit it yourself if you'd like. 20 | 21 | ## Example 22 | ```cs 23 | using UnityEngine; 24 | using Gizmos = Popcron.Gizmos; 25 | 26 | [ExecuteAlways] 27 | public class GizmoDrawer : MonoBehaviour 28 | { 29 | public Material material = null; 30 | 31 | //this will draw in scene view and in game view, in both play mode and edit mode 32 | private void OnRenderObject() 33 | { 34 | //draw a line from the position of the object, to world center 35 | //with the color green, and dashed as well 36 | Gizmos.Line(transform.position, Vector3.one, Color.green, true); 37 | 38 | //draw a cube at the position of the object, with the correct rotation and scale 39 | Gizmos.Cube(transform.position, transform.rotation, transform.lossyScale); 40 | } 41 | 42 | private void Update() 43 | { 44 | //use custom material, if null it uses a default line material 45 | Gizmos.Material = material; 46 | 47 | //toggle gizmo drawing 48 | if (Input.GetKeyDown(KeyCode.F3)) 49 | { 50 | Gizmos.Enabled = !Gizmos.Enabled; 51 | } 52 | 53 | //can also draw from update 54 | Gizmos.Cone(transform.position, transform.rotation, 15f, 45f, Color.green); 55 | } 56 | } 57 | ``` 58 | 59 | ## Camera render filter 60 | By default, gizmos will only be drawn to the MainCamera, and the Scene view camera. If you'd like to specify other cameras to also render the gizmos, the `Gizmos.CameraFilter` predicate allows you to subscribe to a delegate where you can manually specify if a camera should be rendered to or not. 61 | 62 |
Example 63 |

64 | 65 | ```cs 66 | private void OnEnable() 67 | { 68 | //always sub in on enable, because OnEnable gets called when code gets recompiled AND on awake 69 | Gizmos.CameraFilter += cam => 70 | { 71 | return cam.name == "MyOtherCamera"; 72 | }; 73 | } 74 | ``` 75 | 76 |

77 |
78 | 79 | ## Custom drawers 80 | The ability to add custom drawers is possible. Inherit from the `Drawer` class and implement the `Draw` method. To see an example of drawing a line using a custom drawer, look at the `LineDrawer.cs` file. 81 | 82 | The `Draw` method takes in a ref parameter to a Vector3 array as the buffer, and then a params array of objects. The method expects to return the number of points allocated to the buffer. For example, if renderering a single line, allocate the two points at buffer[0] and buffer[1], and return 2. If the number returned is not the same as the amount of points actually used, then the end result of the drawn element will look incorrect and corrupt. 83 | 84 | ## API 85 | - `Gizmos.Line` = Draws a line from point a to b. Equivalent to [Gizmos.DrawLine](https://docs.unity3d.com/ScriptReference/Gizmos.DrawLine.html) 86 | - `Gizmos.Square` = Draws a 2D square in the XY plane 87 | - `Gizmos.Cube` = Draws a 3D cube in world space with orientation and scale parameters. Equivalent to [Gizmos.DrawWireCube](https://docs.unity3d.com/ScriptReference/Gizmos.DrawWireCube.html) 88 | - `Gizmos.Bounds` = Draws a representation of a Bounds object 89 | - `Gizmos.Rect` = Draws a rect object to a specific camera 90 | - `Gizmos.Cone` = Draws a cone with specified orientation, length and angle. It looks like the spotlight gizmo 91 | - `Gizmos.Sphere` = Draws a 3D sphere. Equivalent to [Gizmos.DrawWireSphere](https://docs.unity3d.com/ScriptReference/Gizmos.DrawWireSphere.html) 92 | - `Gizmos.Circle` = Draws a 2D circle that is oriented to the camera by default 93 | 94 | ## Notes 95 | The package uses the same class name as the built-in gizmo class, because of this, you will need to use an alias to point to the right class (`using Gizmos = Popcron.Gizmos`). 96 | 97 | The alternative is to make a global class with the same name that redirects all of its calls to Popcron.Gizmos. The downside to this is that you will need to be explicit when calling the UnityEngine.Gizmos class if you ever need to. Choose your poison. 98 | 99 | ## FAQ 100 | - **What namespace?** 'Popcron' 101 | - **Does it work in builds?** Yes 102 | - **Is there frustum culling?** Yes, it can be toggled with Gizmos.FrustumCulling 103 | - **It's not rendering in game!** Check if your gizmo code is in OnDrawGizmos, because this method isnt called in builds, and ensure that Gizmos.Enabled is true 104 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 53ba367669129e4468fc7226da29353d 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 34897d677d7a838499c71242100d9cac 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Popcron 2 | { 3 | public class Constants 4 | { 5 | public const string UniqueIdentifier = "Popcron.Gizmos"; 6 | public const string EnabledKey = UniqueIdentifier + ".Enabled"; 7 | } 8 | } -------------------------------------------------------------------------------- /Runtime/Constants.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f46f60d5da125c54e813c584ab67715f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Drawer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace Popcron 7 | { 8 | public abstract class Drawer 9 | { 10 | private static Dictionary typeToDrawer = null; 11 | 12 | public abstract int Draw(ref Vector3[] buffer, params object[] args); 13 | 14 | public Drawer() 15 | { 16 | 17 | } 18 | 19 | public static Drawer Get() where T : class 20 | { 21 | //find all drawers 22 | if (typeToDrawer == null) 23 | { 24 | typeToDrawer = new Dictionary(); 25 | 26 | //add defaults 27 | typeToDrawer.Add(typeof(CubeDrawer), new CubeDrawer()); 28 | typeToDrawer.Add(typeof(LineDrawer), new LineDrawer()); 29 | typeToDrawer.Add(typeof(PolygonDrawer),new PolygonDrawer()); 30 | typeToDrawer.Add(typeof(SquareDrawer), new SquareDrawer()); 31 | 32 | //find extras 33 | Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 34 | foreach (Assembly assembly in assemblies) 35 | { 36 | Type[] types = assembly.GetTypes(); 37 | foreach (Type type in types) 38 | { 39 | if (type.IsAbstract) 40 | { 41 | continue; 42 | } 43 | 44 | if (type.IsSubclassOf(typeof(Drawer)) && !typeToDrawer.ContainsKey(type)) 45 | { 46 | try 47 | { 48 | Drawer value = (Drawer)Activator.CreateInstance(type); 49 | typeToDrawer[type] = value; 50 | } 51 | catch (Exception e) 52 | { 53 | Debug.LogError($"couldnt register drawer of type {type} because {e.Message}"); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | if (typeToDrawer.TryGetValue(typeof(T), out Drawer drawer)) 61 | { 62 | return drawer; 63 | } 64 | else 65 | { 66 | return null; 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Runtime/Drawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de59f05047763564782d0004f2a4aade 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Drawers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 69d206b0dd2996643bef775b4c909d5b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Drawers/CubeDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Popcron 4 | { 5 | public class CubeDrawer : Drawer 6 | { 7 | public CubeDrawer() 8 | { 9 | 10 | } 11 | 12 | public override int Draw(ref Vector3[] buffer, params object[] values) 13 | { 14 | Vector3 position = (Vector3)values[0]; 15 | Quaternion rotation = (Quaternion)values[1]; 16 | Vector3 size = (Vector3)values[2]; 17 | 18 | size *= 0.5f; 19 | 20 | Vector3 point1 = new Vector3(position.x - size.x, position.y - size.y, position.z - size.z); 21 | Vector3 point2 = new Vector3(position.x + size.x, position.y - size.y, position.z - size.z); 22 | Vector3 point3 = new Vector3(position.x + size.x, position.y + size.y, position.z - size.z); 23 | Vector3 point4 = new Vector3(position.x - size.x, position.y + size.y, position.z - size.z); 24 | 25 | Vector3 point5 = new Vector3(position.x - size.x, position.y - size.y, position.z + size.z); 26 | Vector3 point6 = new Vector3(position.x + size.x, position.y - size.y, position.z + size.z); 27 | Vector3 point7 = new Vector3(position.x + size.x, position.y + size.y, position.z + size.z); 28 | Vector3 point8 = new Vector3(position.x - size.x, position.y + size.y, position.z + size.z); 29 | 30 | point1 = rotation * (point1 - position); 31 | point1 += position; 32 | 33 | point2 = rotation * (point2 - position); 34 | point2 += position; 35 | 36 | point3 = rotation * (point3 - position); 37 | point3 += position; 38 | 39 | point4 = rotation * (point4 - position); 40 | point4 += position; 41 | 42 | point5 = rotation * (point5 - position); 43 | point5 += position; 44 | 45 | point6 = rotation * (point6 - position); 46 | point6 += position; 47 | 48 | point7 = rotation * (point7 - position); 49 | point7 += position; 50 | 51 | point8 = rotation * (point8 - position); 52 | point8 += position; 53 | 54 | //square 55 | buffer[0] = point1; 56 | buffer[1] = point2; 57 | 58 | buffer[2] = point2; 59 | buffer[3] = point3; 60 | 61 | buffer[4] = point3; 62 | buffer[5] = point4; 63 | 64 | buffer[6] = point4; 65 | buffer[7] = point1; 66 | 67 | //other square 68 | buffer[8] = point5; 69 | buffer[9] = point6; 70 | 71 | buffer[10] = point6; 72 | buffer[11] = point7; 73 | 74 | buffer[12] = point7; 75 | buffer[13] = point8; 76 | 77 | buffer[14] = point8; 78 | buffer[15] = point5; 79 | 80 | //connectors 81 | buffer[16] = point1; 82 | buffer[17] = point5; 83 | 84 | buffer[18] = point2; 85 | buffer[19] = point6; 86 | 87 | buffer[20] = point3; 88 | buffer[21] = point7; 89 | 90 | buffer[22] = point4; 91 | buffer[23] = point8; 92 | 93 | return 24; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Runtime/Drawers/CubeDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1ad89b030c19ab44b90cabc323ed5b1f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Drawers/LineDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Popcron 4 | { 5 | public class LineDrawer : Drawer 6 | { 7 | public LineDrawer() 8 | { 9 | 10 | } 11 | 12 | public override int Draw(ref Vector3[] buffer, params object[] args) 13 | { 14 | buffer[0] = (Vector3)args[0]; 15 | buffer[1] = (Vector3)args[1]; 16 | return 2; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Runtime/Drawers/LineDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 16dfea692deae6d429e9e35de4697f2b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Drawers/PolygonDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Popcron 4 | { 5 | public class PolygonDrawer : Drawer 6 | { 7 | public PolygonDrawer() 8 | { 9 | 10 | } 11 | 12 | public override int Draw(ref Vector3[] buffer, params object[] values) 13 | { 14 | Vector3 position = (Vector3)values[0]; 15 | int points = (int)values[1]; 16 | float radius = (float)values[2]; 17 | float offset = (float)values[3]; 18 | Quaternion rotation = (Quaternion)values[4]; 19 | 20 | float step = 360f / points; 21 | offset *= Mathf.Deg2Rad; 22 | 23 | for (int i = 0; i < points; i++) 24 | { 25 | float cx = Mathf.Cos(Mathf.Deg2Rad * step * i + offset) * radius; 26 | float cy = Mathf.Sin(Mathf.Deg2Rad * step * i + offset) * radius; 27 | Vector3 current = new Vector3(cx, cy); 28 | 29 | float nx = Mathf.Cos(Mathf.Deg2Rad * step * (i + 1) + offset) * radius; 30 | float ny = Mathf.Sin(Mathf.Deg2Rad * step * (i + 1) + offset) * radius; 31 | Vector3 next = new Vector3(nx, ny); 32 | 33 | buffer[i * 2] = position + (rotation * current); 34 | buffer[(i * 2) + 1] = position + (rotation * next); 35 | } 36 | 37 | return points * 2; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Runtime/Drawers/PolygonDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ce972ff7c0e9db4abdcbda3ab990466 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Drawers/SquareDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Popcron 4 | { 5 | public class SquareDrawer : Drawer 6 | { 7 | public SquareDrawer() 8 | { 9 | 10 | } 11 | 12 | public override int Draw(ref Vector3[] buffer, params object[] values) 13 | { 14 | Vector2 position = default; 15 | if (values[0] is Vector2 p2) 16 | { 17 | position = p2; 18 | } 19 | else if (values[0] is Vector3 p3) 20 | { 21 | position = p3; 22 | } 23 | 24 | Quaternion rotation = (Quaternion)values[1]; 25 | 26 | Vector2 size = default; 27 | if (values[2] is Vector2 s2) 28 | { 29 | size = s2; 30 | } 31 | else if (values[2] is Vector3 s3) 32 | { 33 | size = s3; 34 | } 35 | 36 | size *= 0.5f; 37 | 38 | Vector2 point1 = new Vector3(position.x - size.x, position.y - size.y); 39 | Vector2 point2 = new Vector3(position.x + size.x, position.y - size.y); 40 | Vector2 point3 = new Vector3(position.x + size.x, position.y + size.y); 41 | Vector2 point4 = new Vector3(position.x - size.x, position.y + size.y); 42 | 43 | point1 = rotation * (point1 - position); 44 | point1 += position; 45 | 46 | point2 = rotation * (point2 - position); 47 | point2 += position; 48 | 49 | point3 = rotation * (point3 - position); 50 | point3 += position; 51 | 52 | point4 = rotation * (point4 - position); 53 | point4 += position; 54 | 55 | //square 56 | buffer[0] = point1; 57 | buffer[1] = point2; 58 | 59 | buffer[2] = point2; 60 | buffer[3] = point3; 61 | 62 | buffer[4] = point3; 63 | buffer[5] = point4; 64 | 65 | //loop back to start 66 | buffer[6] = point4; 67 | buffer[7] = point1; 68 | 69 | return 8; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Runtime/Drawers/SquareDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 40ab2a1a3d75983449efa80453e7164b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Element.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Popcron 5 | { 6 | internal class Element 7 | { 8 | public Vector3[] points = { }; 9 | public Color color = Color.white; 10 | public bool dashed = false; 11 | } 12 | } -------------------------------------------------------------------------------- /Runtime/Element.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1762e4c9bce946747853048b776f0017 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Gizmos.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Popcron 5 | { 6 | public class Gizmos 7 | { 8 | private static string _prefsKey = null; 9 | private static int? _bufferSize = null; 10 | private static bool? _enabled = null; 11 | private static float? _dashGap = null; 12 | private static bool? _cull = null; 13 | private static int? _pass = null; 14 | private static Vector3? _offset = null; 15 | 16 | private static Vector3[] buffer = new Vector3[BufferSize]; 17 | 18 | /// 19 | /// By default, it will always render to scene view camera and the main camera. 20 | /// Subscribing to this allows you to whitelist your custom cameras. 21 | /// 22 | public static Func CameraFilter = cam => false; 23 | 24 | private static string PrefsKey 25 | { 26 | get 27 | { 28 | if (string.IsNullOrEmpty(_prefsKey)) 29 | { 30 | _prefsKey = $"{SystemInfo.deviceUniqueIdentifier}.{Application.companyName}.{Application.productName}.{Constants.UniqueIdentifier}"; 31 | } 32 | 33 | return _prefsKey; 34 | } 35 | } 36 | 37 | /// 38 | /// The size of the total gizmos buffer. 39 | /// Default is 4096. 40 | /// 41 | public static int BufferSize 42 | { 43 | get 44 | { 45 | if (_bufferSize == null) 46 | { 47 | _bufferSize = PlayerPrefs.GetInt($"{PrefsKey}.BufferSize", 4096); 48 | } 49 | 50 | return _bufferSize.Value; 51 | } 52 | set 53 | { 54 | value = Mathf.Clamp(value, 0, int.MaxValue); 55 | if (_bufferSize != value) 56 | { 57 | _bufferSize = value; 58 | PlayerPrefs.SetInt($"{PrefsKey}.BufferSize", value); 59 | 60 | //buffer size changed, so recreate the buffer array too 61 | buffer = new Vector3[value]; 62 | } 63 | } 64 | } 65 | 66 | /// 67 | /// Toggles wether the gizmos could be drawn or not. 68 | /// 69 | public static bool Enabled 70 | { 71 | get 72 | { 73 | if (_enabled == null) 74 | { 75 | _enabled = PlayerPrefs.GetInt($"{PrefsKey}.Enabled", 1) == 1; 76 | } 77 | 78 | return _enabled.Value; 79 | } 80 | set 81 | { 82 | if (_enabled != value) 83 | { 84 | _enabled = value; 85 | PlayerPrefs.SetInt($"{PrefsKey}.Enabled", value ? 1 : 0); 86 | } 87 | } 88 | } 89 | 90 | /// 91 | /// The size of the gap when drawing dashed elements. 92 | /// Default gap size is 0.1 93 | /// 94 | public static float DashGap 95 | { 96 | get 97 | { 98 | if (_dashGap == null) 99 | { 100 | _dashGap = PlayerPrefs.GetFloat($"{PrefsKey}.DashGap", 0.1f); 101 | } 102 | 103 | return _dashGap.Value; 104 | } 105 | set 106 | { 107 | if (_dashGap != value) 108 | { 109 | _dashGap = value; 110 | PlayerPrefs.SetFloat($"{PrefsKey}.DashGap", value); 111 | } 112 | } 113 | } 114 | 115 | [Obsolete("This property is obsolete. Use FrustumCulling instead.", false)] 116 | public static bool Cull 117 | { 118 | get 119 | { 120 | return FrustumCulling; 121 | } 122 | set 123 | { 124 | FrustumCulling = value; 125 | } 126 | } 127 | 128 | [Obsolete("This property is obsolete. Subscribe to CameraFilter predicate instead and return true for your custom camera.", false)] 129 | public static Camera Camera 130 | { 131 | get => null; 132 | set 133 | { 134 | 135 | } 136 | } 137 | 138 | /// 139 | /// Should the camera not draw elements that are not visible? 140 | /// 141 | public static bool FrustumCulling 142 | { 143 | get 144 | { 145 | if (_cull == null) 146 | { 147 | _cull = PlayerPrefs.GetInt($"{PrefsKey}.FrustumCulling", 1) == 1; 148 | } 149 | 150 | return _cull.Value; 151 | } 152 | set 153 | { 154 | if (_cull != value) 155 | { 156 | _cull = value; 157 | PlayerPrefs.SetInt($"{PrefsKey}.FrustumCulling", value ? 1 : 0); 158 | } 159 | } 160 | } 161 | 162 | /// 163 | /// The material being used to render. 164 | /// 165 | public static Material Material 166 | { 167 | get => GizmosInstance.Material; 168 | set => GizmosInstance.Material = value; 169 | } 170 | 171 | /// 172 | /// Rendering pass to activate. 173 | /// 174 | public static int Pass 175 | { 176 | get 177 | { 178 | if (_pass == null) 179 | { 180 | _pass = PlayerPrefs.GetInt($"{PrefsKey}.Pass", 0); 181 | } 182 | 183 | return _pass.Value; 184 | } 185 | set 186 | { 187 | if (_pass != value) 188 | { 189 | _pass = value; 190 | PlayerPrefs.SetInt($"{PrefsKey}.Pass", value); 191 | } 192 | } 193 | } 194 | 195 | /// 196 | /// Global offset for all points. Default is (0, 0, 0). 197 | /// 198 | public static Vector3 Offset 199 | { 200 | get 201 | { 202 | const string Delim = ","; 203 | if (_offset == null) 204 | { 205 | string data = PlayerPrefs.GetString($"{PrefsKey}.Offset", 0 + Delim + 0 + Delim + 0); 206 | int indexOf = data.IndexOf(Delim); 207 | int lastIndexOf = data.LastIndexOf(Delim); 208 | if (indexOf + lastIndexOf > 0) 209 | { 210 | string[] arr = data.Split(Delim[0]); 211 | _offset = new Vector3(float.Parse(arr[0]), float.Parse(arr[1]), float.Parse(arr[2])); 212 | } 213 | else 214 | { 215 | return Vector3.zero; 216 | } 217 | } 218 | 219 | return _offset.Value; 220 | } 221 | set 222 | { 223 | const string Delim = ","; 224 | if (_offset != value) 225 | { 226 | _offset = value; 227 | PlayerPrefs.SetString($"{PrefsKey}.Offset", value.x + Delim + value.y + Delim + value.y); 228 | } 229 | } 230 | } 231 | 232 | /// 233 | /// Draws an element onto the screen. 234 | /// 235 | public static void Draw(Color? color, bool dashed, params object[] args) where T : Drawer 236 | { 237 | if (!Enabled) 238 | { 239 | return; 240 | } 241 | 242 | Drawer drawer = Drawer.Get(); 243 | if (drawer != null) 244 | { 245 | int points = drawer.Draw(ref buffer, args); 246 | 247 | //copy from buffer and add to the queue 248 | Vector3[] array = ArrayPool.Shared.Rent(points); 249 | Array.Copy(buffer, array, points); 250 | GizmosInstance.Submit(array, color, dashed); 251 | ArrayPool.Shared.Return(array); 252 | } 253 | } 254 | 255 | /// 256 | /// Draws an array of lines. Useful for things like paths. 257 | /// 258 | public static void Lines(Vector3[] lines, Color? color = null, bool dashed = false) 259 | { 260 | if (!Enabled) 261 | { 262 | return; 263 | } 264 | 265 | GizmosInstance.Submit(lines, color, dashed); 266 | } 267 | 268 | /// 269 | /// Draw line in world space. 270 | /// 271 | public static void Line(Vector3 a, Vector3 b, Color? color = null, bool dashed = false) 272 | { 273 | Draw(color, dashed, a, b); 274 | } 275 | 276 | /// 277 | /// Draw square in world space. 278 | /// 279 | public static void Square(Vector2 position, Vector2 size, Color? color = null, bool dashed = false) 280 | { 281 | Square(position, Quaternion.identity, size, color, dashed); 282 | } 283 | 284 | /// 285 | /// Draw square in world space with float diameter parameter. 286 | /// 287 | public static void Square(Vector2 position, float diameter, Color? color = null, bool dashed = false) 288 | { 289 | Square(position, Quaternion.identity, Vector2.one * diameter, color, dashed); 290 | } 291 | 292 | /// 293 | /// Draw square in world space with a rotation parameter. 294 | /// 295 | public static void Square(Vector2 position, Quaternion rotation, Vector2 size, Color? color = null, bool dashed = false) 296 | { 297 | Draw(color, dashed, position, rotation, size); 298 | } 299 | 300 | /// 301 | /// Draws a cube in world space. 302 | /// 303 | public static void Cube(Vector3 position, Quaternion rotation, Vector3 size, Color? color = null, bool dashed = false) 304 | { 305 | Draw(color, dashed, position, rotation, size); 306 | } 307 | 308 | /// 309 | /// Draws a rectangle in screen space. 310 | /// 311 | public static void Rect(Rect rect, Camera camera, Color? color = null, bool dashed = false) 312 | { 313 | rect.y = Screen.height - rect.y; 314 | Vector2 corner = camera.ScreenToWorldPoint(new Vector2(rect.x, rect.y - rect.height)); 315 | Draw(color, dashed, corner + rect.size * 0.5f, Quaternion.identity, rect.size); 316 | } 317 | 318 | /// 319 | /// Draws a representation of a bounding box. 320 | /// 321 | public static void Bounds(Bounds bounds, Color? color = null, bool dashed = false) 322 | { 323 | Draw(color, dashed, bounds.center, Quaternion.identity, bounds.size); 324 | } 325 | 326 | /// 327 | /// Draws a cone similar to the one that spot lights draw. 328 | /// 329 | public static void Cone(Vector3 position, Quaternion rotation, float length, float angle, Color? color = null, bool dashed = false, int pointsCount = 16) 330 | { 331 | //draw the end of the cone 332 | float endAngle = Mathf.Tan(angle * 0.5f * Mathf.Deg2Rad) * length; 333 | Vector3 forward = rotation * Vector3.forward; 334 | Vector3 endPosition = position + forward * length; 335 | float offset = 0f; 336 | Draw(color, dashed, endPosition, pointsCount, endAngle, offset, rotation); 337 | 338 | //draw the 4 lines 339 | for (int i = 0; i < 4; i++) 340 | { 341 | float a = i * 90f * Mathf.Deg2Rad; 342 | Vector3 point = rotation * new Vector3(Mathf.Cos(a), Mathf.Sin(a)) * endAngle; 343 | Line(position, position + point + forward * length, color, dashed); 344 | } 345 | } 346 | 347 | /// 348 | /// Draws a sphere at position with specified radius. 349 | /// 350 | public static void Sphere(Vector3 position, float radius, Color? color = null, bool dashed = false, int pointsCount = 16) 351 | { 352 | float offset = 0f; 353 | Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(0f, 0f, 0f)); 354 | Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(90f, 0f, 0f)); 355 | Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(0f, 90f, 90f)); 356 | } 357 | 358 | /// 359 | /// Draws a circle in world space and billboards towards the camera. 360 | /// 361 | public static void Circle(Vector3 position, float radius, Camera camera, Color? color = null, bool dashed = false, int pointsCount = 16) 362 | { 363 | float offset = 0f; 364 | Quaternion rotation = Quaternion.LookRotation(position - camera.transform.position); 365 | Draw(color, dashed, position, pointsCount, radius, offset, rotation); 366 | } 367 | 368 | /// 369 | /// Draws a circle in world space with a specified rotation. 370 | /// 371 | public static void Circle(Vector3 position, float radius, Quaternion rotation, Color? color = null, bool dashed = false, int pointsCount = 16) 372 | { 373 | float offset = 0f; 374 | Draw(color, dashed, position, pointsCount, radius, offset, rotation); 375 | } 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /Runtime/Gizmos.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b2033d00d6905db4088079c031b89fd5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/GizmosInstance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.Rendering; 6 | using UnityEngine.SceneManagement; 7 | 8 | #if UNITY_EDITOR 9 | using UnityEditor; 10 | using UnityEditor.SceneManagement; 11 | #endif 12 | 13 | #if !UNITY_2019_1_OR_NEWER 14 | using System; 15 | 16 | public struct ScriptableRenderContext {} 17 | 18 | public static class RenderPipelineManager 19 | { 20 | public static event Action endCameraRendering; 21 | } 22 | 23 | #endif 24 | 25 | namespace Popcron 26 | { 27 | [ExecuteInEditMode] 28 | [AddComponentMenu("")] 29 | public class GizmosInstance : MonoBehaviour 30 | { 31 | private const int DefaultQueueSize = 4096; 32 | 33 | private static GizmosInstance instance; 34 | private static bool hotReloaded = true; 35 | private static Material defaultMaterial; 36 | private static Plane[] cameraPlanes = new Plane[6]; 37 | 38 | private Material overrideMaterial; 39 | private int queueIndex = 0; 40 | private int lastFrame; 41 | private Element[] queue = new Element[DefaultQueueSize]; 42 | 43 | /// 44 | /// The material being used to render 45 | /// 46 | public static Material Material 47 | { 48 | get 49 | { 50 | GizmosInstance inst = GetOrCreate(); 51 | if (inst.overrideMaterial) 52 | { 53 | return inst.overrideMaterial; 54 | } 55 | 56 | return DefaultMaterial; 57 | } 58 | set 59 | { 60 | GizmosInstance inst = GetOrCreate(); 61 | inst.overrideMaterial = value; 62 | } 63 | } 64 | 65 | /// 66 | /// The default line renderer material 67 | /// 68 | public static Material DefaultMaterial 69 | { 70 | get 71 | { 72 | if (!defaultMaterial) 73 | { 74 | // Unity has a built-in shader that is useful for drawing 75 | // simple colored things. 76 | Shader shader = Shader.Find("Hidden/Internal-Colored"); 77 | defaultMaterial = new Material(shader) 78 | { 79 | hideFlags = HideFlags.HideAndDontSave 80 | }; 81 | 82 | // Turn on alpha blending 83 | defaultMaterial.SetInt("_SrcBlend", (int)BlendMode.SrcAlpha); 84 | defaultMaterial.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha); 85 | defaultMaterial.SetInt("_Cull", (int)CullMode.Off); 86 | defaultMaterial.SetInt("_ZWrite", 0); 87 | } 88 | 89 | return defaultMaterial; 90 | } 91 | } 92 | 93 | internal static GizmosInstance GetOrCreate() 94 | { 95 | if (hotReloaded || !instance) 96 | { 97 | bool markDirty = false; 98 | GizmosInstance[] gizmosInstances = FindObjectsOfType(); 99 | for (int i = 0; i < gizmosInstances.Length; i++) 100 | { 101 | instance = gizmosInstances[i]; 102 | 103 | //destroy any extra gizmo instances 104 | if (i > 0) 105 | { 106 | if (Application.isPlaying) 107 | { 108 | Destroy(gizmosInstances[i]); 109 | } 110 | else 111 | { 112 | DestroyImmediate(gizmosInstances[i]); 113 | markDirty = true; 114 | } 115 | } 116 | } 117 | 118 | //none were found, create a new one 119 | if (!instance) 120 | { 121 | instance = new GameObject(typeof(GizmosInstance).FullName).AddComponent(); 122 | instance.gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector; 123 | 124 | markDirty = true; 125 | } 126 | 127 | #if UNITY_EDITOR 128 | //mark scene as dirty 129 | if (markDirty && !Application.isPlaying) 130 | { 131 | Scene scene = SceneManager.GetActiveScene(); 132 | EditorSceneManager.MarkSceneDirty(scene); 133 | } 134 | #endif 135 | 136 | hotReloaded = false; 137 | } 138 | 139 | return instance; 140 | } 141 | 142 | private float CurrentTime 143 | { 144 | get 145 | { 146 | float time = 0f; 147 | if (Application.isPlaying) 148 | { 149 | time = Time.time; 150 | } 151 | else 152 | { 153 | #if UNITY_EDITOR 154 | time = (float)EditorApplication.timeSinceStartup; 155 | #endif 156 | } 157 | 158 | return time; 159 | } 160 | } 161 | 162 | /// 163 | /// Submits an array of points to draw into the queue. 164 | /// 165 | internal static void Submit(Vector3[] points, Color? color, bool dashed) 166 | { 167 | GizmosInstance inst = GetOrCreate(); 168 | 169 | //if new frame, reset index 170 | if (inst.lastFrame != Time.frameCount) 171 | { 172 | inst.lastFrame = Time.frameCount; 173 | inst.queueIndex = 0; 174 | } 175 | 176 | //excedeed the length, so make it even bigger 177 | if (inst.queueIndex >= inst.queue.Length) 178 | { 179 | Element[] bigger = new Element[inst.queue.Length + DefaultQueueSize]; 180 | for (int i = inst.queue.Length; i < bigger.Length; i++) 181 | { 182 | bigger[i] = new Element(); 183 | } 184 | 185 | Array.Copy(inst.queue, 0, bigger, 0, inst.queue.Length); 186 | inst.queue = bigger; 187 | } 188 | 189 | inst.queue[inst.queueIndex].color = color ?? Color.white; 190 | inst.queue[inst.queueIndex].points = points; 191 | inst.queue[inst.queueIndex].dashed = dashed; 192 | 193 | inst.queueIndex++; 194 | } 195 | 196 | private void OnEnable() 197 | { 198 | //populate queue with empty elements 199 | queue = new Element[DefaultQueueSize]; 200 | for (int i = 0; i < DefaultQueueSize; i++) 201 | { 202 | queue[i] = new Element(); 203 | } 204 | 205 | if (GraphicsSettings.renderPipelineAsset == null) 206 | { 207 | Camera.onPostRender += OnRendered; 208 | } 209 | else 210 | { 211 | RenderPipelineManager.endCameraRendering += OnRendered; 212 | } 213 | } 214 | 215 | private void OnDisable() 216 | { 217 | if (GraphicsSettings.renderPipelineAsset == null) 218 | { 219 | Camera.onPostRender -= OnRendered; 220 | } 221 | else 222 | { 223 | RenderPipelineManager.endCameraRendering -= OnRendered; 224 | } 225 | } 226 | 227 | private void OnRendered(ScriptableRenderContext context, Camera camera) => OnRendered(camera); 228 | 229 | private bool ShouldRenderCamera(Camera camera) 230 | { 231 | if (!camera) 232 | { 233 | return false; 234 | } 235 | 236 | //allow the scene and main camera always 237 | bool isSceneCamera = false; 238 | #if UNITY_EDITOR 239 | SceneView sceneView = SceneView.currentDrawingSceneView; 240 | if (sceneView == null) 241 | { 242 | sceneView = SceneView.lastActiveSceneView; 243 | } 244 | 245 | if (sceneView != null && sceneView.camera == camera) 246 | { 247 | isSceneCamera = true; 248 | } 249 | #endif 250 | if (isSceneCamera || camera.CompareTag("MainCamera")) 251 | { 252 | return true; 253 | } 254 | 255 | //it passed through the filter 256 | if (Gizmos.CameraFilter?.Invoke(camera) == true) 257 | { 258 | return true; 259 | } 260 | 261 | return false; 262 | } 263 | 264 | private bool IsVisibleByCamera(Element points, Camera camera) 265 | { 266 | if (!camera) 267 | { 268 | return false; 269 | } 270 | 271 | //essentially check if at least 1 point is visible by the camera 272 | for (int i = 0; i < points.points.Length; i++) 273 | { 274 | Vector3 vp = camera.WorldToViewportPoint(points.points[i], camera.stereoActiveEye); 275 | if (vp.x >= 0 && vp.x <= 1 && vp.y >= 0 && vp.y <= 1) 276 | { 277 | return true; 278 | } 279 | } 280 | 281 | return false; 282 | } 283 | 284 | private void Update() 285 | { 286 | //always render something 287 | Gizmos.Line(default, default); 288 | } 289 | 290 | private void OnRendered(Camera camera) 291 | { 292 | Material.SetPass(Gizmos.Pass); 293 | 294 | //shouldnt be rendering 295 | if (!Gizmos.Enabled) 296 | { 297 | queueIndex = 0; 298 | } 299 | 300 | //check if this camera is ok to render with 301 | if (!ShouldRenderCamera(camera)) 302 | { 303 | GL.PushMatrix(); 304 | GL.Begin(GL.LINES); 305 | 306 | //bla bla bla 307 | 308 | GL.End(); 309 | GL.PopMatrix(); 310 | return; 311 | } 312 | 313 | Vector3 offset = Gizmos.Offset; 314 | 315 | GL.PushMatrix(); 316 | GL.MultMatrix(Matrix4x4.identity); 317 | GL.Begin(GL.LINES); 318 | 319 | bool alt = CurrentTime % 1 > 0.5f; 320 | float dashGap = Mathf.Clamp(Gizmos.DashGap, 0.01f, 32f); 321 | bool frustumCull = Gizmos.FrustumCulling; 322 | List points = new List(); 323 | 324 | //draw le elements 325 | for (int e = 0; e < queueIndex; e++) 326 | { 327 | //just in case 328 | if (queue.Length <= e) 329 | { 330 | break; 331 | } 332 | 333 | Element element = queue[e]; 334 | 335 | //dont render this thingy if its not inside the frustum 336 | if (frustumCull) 337 | { 338 | if (!IsVisibleByCamera(element, camera)) 339 | { 340 | continue; 341 | } 342 | } 343 | 344 | points.Clear(); 345 | if (element.dashed) 346 | { 347 | //subdivide 348 | for (int i = 0; i < element.points.Length - 1; i++) 349 | { 350 | Vector3 pointA = element.points[i]; 351 | Vector3 pointB = element.points[i + 1]; 352 | Vector3 direction = pointB - pointA; 353 | if (direction.sqrMagnitude > dashGap * dashGap * 2f) 354 | { 355 | float magnitude = direction.magnitude; 356 | int amount = Mathf.RoundToInt(magnitude / dashGap); 357 | direction /= magnitude; 358 | 359 | for (int p = 0; p < amount - 1; p++) 360 | { 361 | if (p % 2 == (alt ? 1 : 0)) 362 | { 363 | float startLerp = p / (amount - 1f); 364 | float endLerp = (p + 1) / (amount - 1f); 365 | Vector3 start = Vector3.Lerp(pointA, pointB, startLerp); 366 | Vector3 end = Vector3.Lerp(pointA, pointB, endLerp); 367 | points.Add(start); 368 | points.Add(end); 369 | } 370 | } 371 | } 372 | else 373 | { 374 | points.Add(pointA); 375 | points.Add(pointB); 376 | } 377 | } 378 | } 379 | else 380 | { 381 | points.AddRange(element.points); 382 | } 383 | 384 | GL.Color(element.color); 385 | for (int i = 0; i < points.Count; i++) 386 | { 387 | GL.Vertex(points[i] + offset); 388 | } 389 | } 390 | 391 | GL.End(); 392 | GL.PopMatrix(); 393 | } 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /Runtime/GizmosInstance.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 80b6a0da9107f164fb1f60c74620a27d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Popcron.Gizmos.Runtime.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Popcron.Gizmos", 3 | "references": [], 4 | "optionalUnityReferences": [], 5 | "includePlatforms": [], 6 | "excludePlatforms": [], 7 | "allowUnsafeCode": false, 8 | "overrideReferences": false, 9 | "precompiledReferences": [], 10 | "autoReferenced": true, 11 | "defineConstraints": [], 12 | "versionDefines": [] 13 | } -------------------------------------------------------------------------------- /Runtime/Popcron.Gizmos.Runtime.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a8a4fedd7ec12e94b88b22a273752e03 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.popcron.gizmos", 3 | "displayName": "Popcron Gizmos", 4 | "version": "1.6.8", 5 | "unity": "2018.3", 6 | "description": "Quick GL drawing utility for debugging." 7 | } 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e2bd1a97bc7c7d44289f649757a3fe7f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------