A game about forced loneliness, made by TACStudios
1using System;
2using System.Runtime.CompilerServices;
3using UnityEngine;
4
5namespace UnityEditor.Rendering
6{
7 public static partial class CameraUI
8 {
9 /// <summary>
10 /// Physical camera related drawers
11 /// </summary>
12 public static partial class PhysicalCamera
13 {
14 // Saves the value of the sensor size when the user switches from "custom" size to a preset per camera.
15 // We use a ConditionalWeakTable instead of a Dictionary to avoid keeping alive (with strong references) deleted cameras
16 static ConditionalWeakTable<Camera, object> s_PerCameraSensorSizeHistory = new ConditionalWeakTable<Camera, object>();
17
18 /// <summary>Draws Body Sensor related fields on the inspector</summary>
19 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
20 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
21 public static void Drawer_PhysicalCamera_CameraBody_Sensor(ISerializedCamera p, Editor owner)
22 {
23 var cam = p.baseCameraSettings;
24 EditorGUI.BeginChangeCheck();
25
26 int oldFilmGateIndex = Array.IndexOf(Styles.apertureFormatValues, new Vector2((float)Math.Round(cam.sensorSize.vector2Value.x, 3), (float)Math.Round(cam.sensorSize.vector2Value.y, 3)));
27
28 // If it is not one of the preset sizes, set it to custom
29 oldFilmGateIndex = (oldFilmGateIndex == -1) ? Styles.customPresetIndex : oldFilmGateIndex;
30
31 // Get the new user selection
32 int newFilmGateIndex = EditorGUILayout.Popup(Styles.sensorType, oldFilmGateIndex, Styles.apertureFormatNames);
33
34 if (EditorGUI.EndChangeCheck())
35 {
36 // Retrieve the previous custom size value, if one exists for this camera
37 object previousCustomValue;
38 s_PerCameraSensorSizeHistory.TryGetValue((Camera)p.serializedObject.targetObject, out previousCustomValue);
39
40 // When switching from custom to a preset, update the last custom value (to display again, in case the user switches back to custom)
41 if (oldFilmGateIndex == Styles.customPresetIndex)
42 {
43 if (previousCustomValue == null)
44 {
45 s_PerCameraSensorSizeHistory.Add((Camera)p.serializedObject.targetObject, cam.sensorSize.vector2Value);
46 }
47 else
48 {
49 previousCustomValue = cam.sensorSize.vector2Value;
50 }
51 }
52
53 if (newFilmGateIndex < Styles.customPresetIndex)
54 {
55 cam.sensorSize.vector2Value = Styles.apertureFormatValues[newFilmGateIndex];
56 }
57 else
58 {
59 // The user switched back to custom, so display by deafulr the previous custom value
60 if (previousCustomValue != null)
61 {
62 cam.sensorSize.vector2Value = (Vector2)previousCustomValue;
63 }
64 else
65 {
66 cam.sensorSize.vector2Value = new Vector2(36.0f, 24.0f); // this is the value new cameras are created with
67 }
68 }
69 }
70
71 EditorGUILayout.PropertyField(cam.sensorSize, Styles.sensorSize);
72 }
73
74 /// <summary>Draws Gate fit related fields on the inspector</summary>
75 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
76 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
77 public static void Drawer_PhysicalCamera_CameraBody_GateFit(ISerializedCamera p, Editor owner)
78 {
79 var cam = p.baseCameraSettings;
80
81 using (var horizontal = new EditorGUILayout.HorizontalScope())
82 using (var propertyScope = new EditorGUI.PropertyScope(horizontal.rect, Styles.gateFit, cam.gateFit))
83 using (var checkScope = new EditorGUI.ChangeCheckScope())
84 {
85 int gateValue = (int)(Camera.GateFitMode)EditorGUILayout.EnumPopup(propertyScope.content, (Camera.GateFitMode)cam.gateFit.intValue);
86 if (checkScope.changed)
87 cam.gateFit.intValue = gateValue;
88 }
89 }
90
91 /// <summary>Draws Focal Length related fields on the inspector</summary>
92 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
93 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
94 public static void Drawer_PhysicalCamera_Lens_FocalLength(ISerializedCamera p, Editor owner)
95 {
96 var cam = p.baseCameraSettings;
97
98 using (var horizontal = new EditorGUILayout.HorizontalScope())
99 using (new EditorGUI.PropertyScope(horizontal.rect, Styles.focalLength, cam.focalLength))
100 using (var checkScope = new EditorGUI.ChangeCheckScope())
101 {
102 bool isPhysical = p.projectionMatrixMode.intValue == (int)CameraUI.ProjectionMatrixMode.PhysicalPropertiesBased;
103 // We need to update the focal length if the camera is physical and the FoV has changed.
104 bool focalLengthIsDirty = (s_FovChanged && isPhysical);
105
106 float sensorLength = cam.fovAxisMode.intValue == 0 ? cam.sensorSize.vector2Value.y : cam.sensorSize.vector2Value.x;
107 float focalLengthVal = focalLengthIsDirty ? Camera.FieldOfViewToFocalLength(s_FovLastValue, sensorLength) : cam.focalLength.floatValue;
108 focalLengthVal = EditorGUILayout.FloatField(Styles.focalLength, focalLengthVal);
109 if (checkScope.changed || focalLengthIsDirty)
110 cam.focalLength.floatValue = focalLengthVal;
111 }
112 }
113
114 /// <summary>Draws Lens Shift related fields on the inspector</summary>
115 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
116 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
117 public static void Drawer_PhysicalCamera_Lens_Shift(ISerializedCamera p, Editor owner)
118 {
119 EditorGUILayout.PropertyField(p.baseCameraSettings.lensShift, Styles.shift);
120 }
121
122
123 /// <summary>Draws Focus Distance related fields on the inspector</summary>
124 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
125 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
126 public static void Drawer_PhysicalCamera_FocusDistance(ISerializedCamera p, Editor owner)
127 {
128 var cam = p.baseCameraSettings;
129 EditorGUILayout.PropertyField(cam.focusDistance, Styles.focusDistance);
130 }
131
132 /// <summary>Draws ISO related fields on the inspector</summary>
133 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
134 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
135 public static void Drawer_PhysicalCamera_CameraBody_ISO(ISerializedCamera p, Editor owner)
136 {
137 var cam = p.baseCameraSettings;
138 EditorGUILayout.PropertyField(cam.iso, Styles.ISO);
139 }
140
141 static EditorPrefBoolFlags<ShutterSpeedUnit> m_ShutterSpeedState = new EditorPrefBoolFlags<ShutterSpeedUnit>($"HDRP:{nameof(CameraUI)}:ShutterSpeedState");
142
143 enum ShutterSpeedUnit
144 {
145 [InspectorName("Second")]
146 Second,
147 [InspectorName("1 \u2215 Second")] // Don't use a slash here else Unity will auto-create a submenu...
148 OneOverSecond
149 }
150
151 /// <summary>Draws Shutter Speed related fields on the inspector</summary>
152 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
153 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
154 public static void Drawer_PhysicalCamera_CameraBody_ShutterSpeed(ISerializedCamera p, Editor owner)
155 {
156 var cam = p.baseCameraSettings;
157
158 // Custom layout for shutter speed
159 const int k_UnitMenuWidth = 90;
160 const int k_OffsetPerIndent = 15;
161 const int k_LabelFieldSeparator = 2;
162 const int k_Offset = 1;
163 int oldIndentLevel = EditorGUI.indentLevel;
164
165 // Don't take into account the indentLevel when rendering the units field
166 EditorGUI.indentLevel = 0;
167
168 var lineRect = EditorGUILayout.GetControlRect();
169 var fieldRect = new Rect(k_OffsetPerIndent + k_LabelFieldSeparator + k_Offset, lineRect.y, lineRect.width - k_UnitMenuWidth, lineRect.height);
170 var unitMenu = new Rect(fieldRect.xMax + k_LabelFieldSeparator, lineRect.y, k_UnitMenuWidth - k_LabelFieldSeparator, lineRect.height);
171
172 // We cannot had the shutterSpeedState as this is not a serialized property but a global edition mode.
173 // This imply that it will never go bold nor can be reverted in prefab overrides
174
175 m_ShutterSpeedState.value = (ShutterSpeedUnit)EditorGUI.EnumPopup(unitMenu, m_ShutterSpeedState.value);
176 // Reset the indent level
177 EditorGUI.indentLevel = oldIndentLevel;
178
179 EditorGUI.BeginProperty(fieldRect, Styles.shutterSpeed, cam.shutterSpeed);
180 {
181 // if we we use (1 / second) units, then change the value for the display and then revert it back
182 if (m_ShutterSpeedState.value == ShutterSpeedUnit.OneOverSecond && cam.shutterSpeed.floatValue > 0)
183 cam.shutterSpeed.floatValue = 1.0f / cam.shutterSpeed.floatValue;
184 EditorGUI.PropertyField(fieldRect, cam.shutterSpeed, Styles.shutterSpeed);
185 if (m_ShutterSpeedState.value == ShutterSpeedUnit.OneOverSecond && cam.shutterSpeed.floatValue > 0)
186 cam.shutterSpeed.floatValue = 1.0f / cam.shutterSpeed.floatValue;
187 }
188 EditorGUI.EndProperty();
189 }
190
191 /// <summary>Draws Lens Aperture related fields on the inspector</summary>
192 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
193 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
194 public static void Drawer_PhysicalCamera_Lens_Aperture(ISerializedCamera p, Editor owner)
195 {
196 var cam = p.baseCameraSettings;
197
198 // Custom layout for aperture
199 var rect = EditorGUILayout.BeginHorizontal();
200 {
201 // Magic values/offsets to get the UI look consistent
202 const float textRectSize = 80;
203 const float textRectPaddingRight = 62;
204 const float unitRectPaddingRight = 97;
205 const float sliderPaddingLeft = 2;
206 const float sliderPaddingRight = 77;
207
208 var labelRect = rect;
209 labelRect.width = EditorGUIUtility.labelWidth;
210 labelRect.height = EditorGUIUtility.singleLineHeight;
211 EditorGUI.LabelField(labelRect, Styles.aperture);
212
213 GUI.SetNextControlName("ApertureSlider");
214 var sliderRect = rect;
215 sliderRect.x += labelRect.width + sliderPaddingLeft;
216 sliderRect.width = rect.width - labelRect.width - sliderPaddingRight;
217 float newVal = GUI.HorizontalSlider(sliderRect, cam.aperture.floatValue, Camera.kMinAperture, Camera.kMaxAperture);
218
219 // keep only 2 digits of precision, like the otehr editor fields
220 newVal = Mathf.Floor(100 * newVal) / 100.0f;
221
222 if (cam.aperture.floatValue != newVal)
223 {
224 cam.aperture.floatValue = newVal;
225 // Note: We need to move the focus when the slider changes, otherwise the textField will not update
226 GUI.FocusControl("ApertureSlider");
227 }
228
229 var unitRect = rect;
230 unitRect.x += rect.width - unitRectPaddingRight;
231 unitRect.width = textRectSize;
232 unitRect.height = EditorGUIUtility.singleLineHeight;
233 EditorGUI.LabelField(unitRect, "f /", EditorStyles.label);
234
235 var textRect = rect;
236 textRect.x = rect.width - textRectPaddingRight;
237 textRect.width = textRectSize;
238 textRect.height = EditorGUIUtility.singleLineHeight;
239 string newAperture = EditorGUI.TextField(textRect, cam.aperture.floatValue.ToString());
240 if (float.TryParse(newAperture, out float parsedValue))
241 cam.aperture.floatValue = Mathf.Clamp(parsedValue, Camera.kMinAperture, Camera.kMaxAperture);
242 }
243
244 EditorGUILayout.EndHorizontal();
245 EditorGUILayout.Space(EditorGUIUtility.singleLineHeight);
246 }
247
248 /// <summary>Draws Aperture Shape related fields on the inspector</summary>
249 /// <param name="p"><see cref="ISerializedCamera"/> The serialized camera</param>
250 /// <param name="owner"><see cref="Editor"/> The editor owner calling this drawer</param>
251 public static void Drawer_PhysicalCamera_ApertureShape(ISerializedCamera p, Editor owner)
252 {
253 var cam = p.baseCameraSettings;
254
255 EditorGUILayout.PropertyField(cam.bladeCount, Styles.bladeCount);
256
257 using (var horizontal = new EditorGUILayout.HorizontalScope())
258 using (var propertyScope = new EditorGUI.PropertyScope(horizontal.rect, Styles.curvature, cam.curvature))
259 {
260 var v = cam.curvature.vector2Value;
261
262 // The layout system breaks alignment when mixing inspector fields with custom layout'd
263 // fields as soon as a scrollbar is needed in the inspector, so we'll do the layout
264 // manually instead
265 const int kFloatFieldWidth = 50;
266 const int kSeparatorWidth = 5;
267 float indentOffset = EditorGUI.indentLevel * 15f;
268 var lineRect = EditorGUILayout.GetControlRect();
269 var labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth - indentOffset, lineRect.height);
270 var floatFieldLeft = new Rect(labelRect.xMax, lineRect.y, kFloatFieldWidth + indentOffset, lineRect.height);
271 var sliderRect = new Rect(floatFieldLeft.xMax + kSeparatorWidth - indentOffset, lineRect.y, lineRect.width - labelRect.width - kFloatFieldWidth * 2 - kSeparatorWidth * 2, lineRect.height);
272 var floatFieldRight = new Rect(sliderRect.xMax + kSeparatorWidth - indentOffset, lineRect.y, kFloatFieldWidth + indentOffset, lineRect.height);
273
274 EditorGUI.PrefixLabel(labelRect, propertyScope.content);
275 v.x = EditorGUI.FloatField(floatFieldLeft, v.x);
276 EditorGUI.MinMaxSlider(sliderRect, ref v.x, ref v.y, Camera.kMinAperture, Camera.kMaxAperture);
277 v.y = EditorGUI.FloatField(floatFieldRight, v.y);
278 cam.curvature.vector2Value = v;
279 }
280
281 EditorGUILayout.PropertyField(cam.barrelClipping, Styles.barrelClipping);
282 EditorGUILayout.PropertyField(cam.anamorphism, Styles.anamorphism);
283 }
284 }
285 }
286}