Skip to content

Commit 9fcc5ae

Browse files
author
Takayuki MATSUOKA
committed
Add FoV button to the toolbar of Scene View
1 parent 684d282 commit 9fcc5ae

File tree

8 files changed

+190
-58
lines changed

8 files changed

+190
-58
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using UnityEditor;
2+
using UnityEngine;
3+
using System;
4+
using System.Reflection;
5+
6+
#if !UNITY_EDITOR
7+
#error This script must be placed under "Editor/" directory.
8+
#endif
9+
10+
namespace UTJ.UnityEditor.Extension.SceneViewFovControl {
11+
12+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEngine/UnityEngine/GUIClip.cs
13+
static class GUIClip {
14+
const BindingFlags nonPublicStatic = BindingFlags.NonPublic | BindingFlags.Static;
15+
16+
static readonly Type typeGuiClip = typeof(UnityEngine.Transform).Assembly.GetType("UnityEngine.GUIClip");
17+
static readonly MethodInfo mi_GetTopRect = typeGuiClip.GetMethod("GetTopRect", nonPublicStatic);
18+
static readonly MethodInfo mi_Push = typeGuiClip.GetMethod("Push", nonPublicStatic);
19+
static readonly MethodInfo mi_Pop = typeGuiClip.GetMethod("Pop", nonPublicStatic);
20+
21+
public static Rect GetTopRect() {
22+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEngine/UnityEngine/GUIClip.cs#L76
23+
return (Rect) mi_GetTopRect.Invoke(null, null);
24+
}
25+
26+
public static void Push(Rect rect, Vector2 v0, Vector2 v1, bool b) {
27+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEngine/UnityEngine/GUIClip.cs#L65
28+
mi_Push.Invoke(null, new object[] { rect, v0, v1, b });
29+
}
30+
31+
public static void Pop() {
32+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEngine/UnityEngine/GUIClip.cs#L73
33+
mi_Pop.Invoke(null, null);
34+
}
35+
}
36+
37+
} // namespace

Assets/SceneViewFovControl/Editor/GUIClipExtension.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using UnityEditor;
2+
using UnityEngine;
3+
using System;
4+
using System.Reflection;
5+
6+
#if !UNITY_EDITOR
7+
#error This script must be placed under "Editor/" directory.
8+
#endif
9+
10+
namespace UTJ.UnityEditor.Extension.SceneViewFovControl {
11+
12+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs
13+
static class SceneViewHiddenApiExtension {
14+
const BindingFlags nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
15+
16+
static readonly Type typeSceneView = typeof(global::UnityEditor.SceneView);
17+
static readonly MethodInfo mi_UseSceneFiltering = typeSceneView.GetMethod("UseSceneFiltering", nonPublicInstance);
18+
static readonly MethodInfo mi_SceneCameraRendersIntoRT = typeSceneView.GetMethod("SceneCameraRendersIntoRT", nonPublicInstance);
19+
static readonly FieldInfo fi_m_GizmosContent = typeSceneView.GetField("m_GizmosContent", nonPublicInstance);
20+
static readonly FieldInfo fi_m_RenderDocContent = typeSceneView.GetField("m_RenderDocContent", nonPublicInstance);
21+
22+
public static bool UseSceneFiltering(this SceneView sceneView) {
23+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs#L1033
24+
return (bool) mi_UseSceneFiltering.Invoke(sceneView, null);
25+
}
26+
27+
public static bool SceneCameraRendersIntoRT(this SceneView sceneView) {
28+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs#L1200
29+
return (bool) mi_SceneCameraRendersIntoRT.Invoke(sceneView, null);
30+
}
31+
32+
public static GUIContent Get_m_GizmosContent(this SceneView sceneView) {
33+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs#L215
34+
return fi_m_GizmosContent.GetValue(sceneView) as GUIContent;
35+
}
36+
37+
public static GUIContent Get_m_RenderDocContent(this SceneView sceneView) {
38+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs#L219
39+
return fi_m_RenderDocContent.GetValue(sceneView) as GUIContent;
40+
}
41+
42+
static Rect[] PopGUIClips(this SceneView sceneView) {
43+
int count = 1;
44+
if(!sceneView.UseSceneFiltering() && sceneView.SceneCameraRendersIntoRT()) {
45+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs#L1255
46+
++count;
47+
}
48+
Rect[] guiClips = new Rect[count];
49+
for(int i = 0; i < count; ++i) {
50+
guiClips[i] = GUIClip.GetTopRect();
51+
GUIClip.Pop();
52+
}
53+
return guiClips;
54+
}
55+
56+
static void PushGUIClips(this SceneView sceneView, Rect[] guiClips) {
57+
for(int i = guiClips.Length-1; i >= 0; --i) {
58+
GUIClip.Push(guiClips[i], Vector2.zero, Vector2.zero, false);
59+
}
60+
}
61+
62+
const float LeftMargin = 251f;
63+
const float TopOffset = -1f;
64+
const float MinGuiContentWidth = 16f;
65+
const float RightOffset = 300.0f;
66+
67+
// https://github.com/MattRix/UnityDecompiled/blob/753fde37d331b2100f93cc5f9eb343f1dcff5eee/UnityEditor/UnityEditor/SceneView.cs#L810
68+
//
69+
// If we can put the following function after above ^ line, there's no problem.
70+
// But since we don't have any hook in the toolbar, the following function emulates it.
71+
// note : This code doesn't coexistent with other user toolbar extensions such as "Scene View Bookmarks" [1]
72+
//
73+
// [1] Scene View Bookmarks https://www.assetstore.unity3d.com/en/#!/content/22302
74+
public static void DoToolbarRightSideGUI(this SceneView sceneView, GUIContent content, GUIStyle style, Action<Rect> action) {
75+
Vector2 size = style.CalcSize(content);
76+
float wb = sceneView.position.width - RightOffset;
77+
float lx = wb - size.x;
78+
if(lx < LeftMargin) {
79+
lx = LeftMargin;
80+
size.x = wb - lx;
81+
}
82+
if(size.x > MinGuiContentWidth) {
83+
var rect = new Rect(lx, TopOffset, size.x, size.y);
84+
var active = RenderTexture.active;
85+
RenderTexture.active = null;
86+
Rect[] guiClips = sceneView.PopGUIClips();
87+
88+
action(rect);
89+
90+
sceneView.PushGUIClips(guiClips);
91+
RenderTexture.active = active;
92+
}
93+
}
94+
}
95+
96+
}

Assets/SceneViewFovControl/Editor/SceneViewExtension.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/SceneViewFovControl/Editor/SceneViewFovControl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace UTJ.UnityEditor.Extension.SceneViewFovControl {
1313
public static class SceneViewFovControl
1414
{
1515
static Dictionary<int, Status> statuses = new Dictionary<int, Status>();
16-
static public bool EnableFlag = false;
16+
static bool EnableFlag = false;
1717

1818
static SceneViewFovControl() {
1919
Enable(true);

Assets/SceneViewFovControl/Editor/Settings.cs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ static class Settings {
1111
static SettingsData data = new SettingsData();
1212
static SettingsData loadedData = new SettingsData();
1313

14-
public const string VersionString = "0.1.4";
14+
public const string VersionString = "0.1.5";
1515
public const string MenuItemName = "Edit/Scene View FoV Settings";
1616
public const string EditorPrefsKey = "UTJ.UnityEditor.Extension.SceneViewFovControl";
1717

@@ -23,9 +23,6 @@ static class Settings {
2323
public const float MinFovMax = 160.0f;
2424
public const float MaxFovMin = 1.0f;
2525
public const float MaxFovMax = 160.0f;
26-
public const float ButtonShowingDurationInSeconds = 2.0f;
27-
public const float MinButtonShowingDurationInSeconds = 0.0f;
28-
public const float MaxButtonShowingDurationInSeconds = 5.0f;
2926

3027
public const string WindowTitle = "FoV Control";
3128

@@ -92,8 +89,6 @@ public class SettingsData {
9289
public float MinFov;
9390
public float MaxFov;
9491

95-
public float ButtonShowingDurationInSeconds;
96-
9792
public SettingsData Clone() {
9893
return (SettingsData) this.MemberwiseClone();
9994
}
@@ -110,24 +105,6 @@ public void Reset() {
110105
FovQuickMultiplier = 5.0f;
111106
MinFov = 2.0f;
112107
MaxFov = 160.0f;
113-
114-
ButtonShowingDurationInSeconds = Settings.ButtonShowingDurationInSeconds;
115-
}
116-
117-
public bool AlwaysShowResetButton {
118-
get {
119-
return ButtonShowingDurationInSeconds == Settings.MaxButtonShowingDurationInSeconds;
120-
}
121-
}
122-
123-
public bool NeverShowResetButton {
124-
get {
125-
return ButtonShowingDurationInSeconds == Settings.MinButtonShowingDurationInSeconds;
126-
}
127-
}
128-
129-
public bool Dirty {
130-
get; set;
131108
}
132109
}
133110
}

Assets/SceneViewFovControl/Editor/SettingsGui.cs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,34 +56,13 @@ void OnGUI() {
5656

5757
GUILayout.Space(8);
5858

59-
if(d.AlwaysShowResetButton) {
60-
GUILayout.Label("<Reset Scene FoV> Button Showing Duration (sec): Infinite");
61-
} else if(d.NeverShowResetButton) {
62-
GUILayout.Label("<Reset Scene FoV> Button Showing Duration (sec): Never");
63-
} else {
64-
GUILayout.Label("<Reset Scene FoV> Button Showing Duration (sec):" + d.ButtonShowingDurationInSeconds);
65-
}
66-
d.ButtonShowingDurationInSeconds = GUILayout.HorizontalSlider(d.ButtonShowingDurationInSeconds, Settings.MinButtonShowingDurationInSeconds, Settings.MaxButtonShowingDurationInSeconds);
67-
6859
if(d.MaxFov < d.MinFov) {
6960
d.MinFov = d.MaxFov;
7061
}
7162

7263
GUILayout.Space(20);
7364

74-
if(SceneViewFovControl.EnableFlag) {
75-
if(GUILayout.Button("Disable")) {
76-
SceneViewFovControl.Enable(false);
77-
}
78-
} else {
79-
if(GUILayout.Button("Enable")) {
80-
SceneViewFovControl.Enable(true);
81-
}
82-
}
83-
84-
GUILayout.Space(20);
85-
86-
EditorGUILayout.BeginHorizontal();
65+
global::UnityEditor.EditorGUILayout.BeginHorizontal();
8766
{
8867
if(GUILayout.Button("Save")) {
8968
Settings.Save();
@@ -108,7 +87,7 @@ void OnGUI() {
10887
this.Close();
10988
}
11089
}
111-
EditorGUILayout.EndHorizontal();
90+
global::UnityEditor.EditorGUILayout.EndHorizontal();
11291
}
11392
}
11493

Assets/SceneViewFovControl/Editor/Status.cs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using UnityEngine;
1+
using UnityEngine;
22
using UnityEditor;
33

44
#if !UNITY_EDITOR
@@ -10,14 +10,21 @@ namespace UTJ.UnityEditor.Extension.SceneViewFovControl {
1010
class Status {
1111
float fov = 0.0f;
1212
bool reset = false;
13-
double showResetButtonTime = 0.0;
13+
bool autoFov = true;
14+
float lastOnSceneGuiFov = 0.0f;
15+
16+
const string ButtonStringFovAuto = "FoV:Auto";
17+
const string ButtonStringFovUser = "FoV:{0:0.00}";
18+
19+
static readonly GUIContent ButtonContentFovAuto = new GUIContent(ButtonStringFovAuto);
20+
GUIContent ButtonContentFovUser = new GUIContent(ButtonStringFovUser);
1421

1522
public void OnScene(SceneView sceneView) {
1623
if(sceneView == null || sceneView.camera == null) {
1724
return;
1825
}
1926

20-
if(sceneView.in2DMode) {
27+
if(sceneView.in2DMode || autoFov) {
2128
return;
2229
}
2330

@@ -54,21 +61,33 @@ public void OnScene(SceneView sceneView) {
5461
}
5562
fov += deltaFov;
5663
fov = Mathf.Clamp(fov, settings.MinFov, settings.MaxFov);
57-
showResetButtonTime = EditorApplication.timeSinceStartup + settings.ButtonShowingDurationInSeconds;
5864
}
5965

6066
camera.fieldOfView = fov;
6167
}
6268

6369
public void OnSceneGUI(SceneView sceneView) {
64-
var settings = Settings.Data;
65-
if(EditorApplication.timeSinceStartup < showResetButtonTime || settings.AlwaysShowResetButton) {
66-
Handles.BeginGUI();
67-
if (GUI.Button(new Rect(10, 10, 160, 32), "Reset SceneView FoV\n" + fov)) {
68-
reset = true;
70+
var content = ButtonContentFovUser;
71+
if(autoFov) {
72+
content = ButtonContentFovAuto;
73+
} else {
74+
if(lastOnSceneGuiFov != fov) {
75+
lastOnSceneGuiFov = fov;
76+
ButtonContentFovUser = new GUIContent(string.Format(ButtonStringFovUser, fov));
6977
}
70-
Handles.EndGUI();
78+
content = ButtonContentFovUser;
7179
}
80+
81+
GUIStyle style = EditorStyles.toolbarButton;
82+
sceneView.DoToolbarRightSideGUI(content, style, (rect) => {
83+
if (GUI.Button(rect, content, style)) {
84+
autoFov = !autoFov;
85+
if(!autoFov) {
86+
reset = true;
87+
}
88+
}
89+
});
90+
7291
}
7392
}
7493

0 commit comments

Comments
 (0)