/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using UnityEditor; using UnityEngine; using ColorMapEditorType = OVRPassthroughLayer.ColorMapEditorType; [CustomPropertyDrawer(typeof(OVRPassthroughLayer.SerializedSurfaceGeometry))] class SerializedSurfaceGeometryPropertyDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // Find the SerializedProperties by name var meshFilterProperty = property.FindPropertyRelative(nameof(OVRPassthroughLayer.SerializedSurfaceGeometry.meshFilter)); var updateTransformProperty = property.FindPropertyRelative(nameof(OVRPassthroughLayer.SerializedSurfaceGeometry.updateTransform)); var propertyHeight = position.height / 2; using (new EditorGUI.PropertyScope(position, label, property)) { var surfaceGeometryPropertyPosition = new Rect(position.x, position.y, position.width, propertyHeight); EditorGUI.PropertyField(surfaceGeometryPropertyPosition, meshFilterProperty, new GUIContent("Surface Geometry", "The GameObject from which to generate surface geometry.")); var heightOffset = EditorGUI.GetPropertyHeight(updateTransformProperty) + EditorGUIUtility.standardVerticalSpacing; var updateTransformPosition = new Rect(position.x, position.y + heightOffset, position.width, propertyHeight); EditorGUI.PropertyField(updateTransformPosition, updateTransformProperty, new GUIContent("Update Transform", "When enabled, updates the mesh's transform every frame. Use this if the GameObject is dynamic.")); } } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return base.GetPropertyHeight(property, label) * 2.2f; } } [CustomEditor(typeof(OVRPassthroughLayer))] public class OVRPassthroughLayerEditor : Editor { private readonly static string[] _colorMapNames = { "None", "Color Adjustment", "Grayscale", "Grayscale to Color", "Color LUT", "Blended Color LUTs", "Custom" }; private readonly static string[] _selectableColorMapNames = { _colorMapNames[0], _colorMapNames[1], _colorMapNames[2], _colorMapNames[3], _colorMapNames[4], _colorMapNames[5] }; private ColorMapEditorType[] _colorMapTypes = { ColorMapEditorType.None, ColorMapEditorType.ColorAdjustment, ColorMapEditorType.Grayscale, ColorMapEditorType.GrayscaleToColor, ColorMapEditorType.ColorLut, ColorMapEditorType.InterpolatedColorLut, ColorMapEditorType.Custom }; private SerializedProperty _projectionSurfaces; private SerializedProperty _propProjectionSurfaceType; private SerializedProperty _propOverlayType; private SerializedProperty _propCompositionDepth; private SerializedProperty _propTextureOpacity; private SerializedProperty _propEdgeRenderingEnabled; private SerializedProperty _propEdgeColor; private SerializedProperty _propColorMapEditorContrast; private SerializedProperty _propColorMapEditorBrightness; private SerializedProperty _propColorMapEditorPosterize; private SerializedProperty _propColorMapEditorGradient; private SerializedProperty _propColorMapEditorSaturation; private SerializedProperty _propColorLutSourceTexture; private SerializedProperty _propColorLutTargetTexture; private SerializedProperty _propLutWeight; private SerializedProperty _propFlipLutY; void OnEnable() { _projectionSurfaces = serializedObject.FindProperty(nameof(OVRPassthroughLayer.serializedSurfaceGeometry)); _propProjectionSurfaceType = serializedObject.FindProperty(nameof(OVRPassthroughLayer.projectionSurfaceType)); _propOverlayType = serializedObject.FindProperty(nameof(OVRPassthroughLayer.overlayType)); _propCompositionDepth = serializedObject.FindProperty(nameof(OVRPassthroughLayer.compositionDepth)); _propTextureOpacity = serializedObject.FindProperty(nameof(OVRPassthroughLayer.textureOpacity_)); _propEdgeRenderingEnabled = serializedObject.FindProperty(nameof(OVRPassthroughLayer.edgeRenderingEnabled_)); _propEdgeColor = serializedObject.FindProperty(nameof(OVRPassthroughLayer.edgeColor_)); _propColorMapEditorContrast = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorContrast)); _propColorMapEditorBrightness = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorBrightness)); _propColorMapEditorPosterize = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorPosterize)); _propColorMapEditorSaturation = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorSaturation)); _propColorMapEditorGradient = serializedObject.FindProperty(nameof(OVRPassthroughLayer.colorMapEditorGradient)); _propColorLutSourceTexture = serializedObject.FindProperty(nameof(OVRPassthroughLayer._colorLutSourceTexture)); _propColorLutTargetTexture = serializedObject.FindProperty(nameof(OVRPassthroughLayer._colorLutTargetTexture)); _propLutWeight = serializedObject.FindProperty(nameof(OVRPassthroughLayer._lutWeight)); _propFlipLutY = serializedObject.FindProperty(nameof(OVRPassthroughLayer._flipLutY)); } public override void OnInspectorGUI() { OVRPassthroughLayer layer = (OVRPassthroughLayer)target; serializedObject.Update(); EditorGUILayout.PropertyField(_propProjectionSurfaceType, new GUIContent("Projection Surface", "The type of projection surface for this Passthrough layer")); if (layer.projectionSurfaceType == OVRPassthroughLayer.ProjectionSurfaceType.UserDefined) { EditorGUILayout.PropertyField(_projectionSurfaces, new GUIContent("Projection Surfaces")); } EditorGUILayout.Space(); EditorGUILayout.LabelField("Compositing", EditorStyles.boldLabel); EditorGUILayout.PropertyField(_propOverlayType, new GUIContent("Placement", "Whether this overlay should layer behind the scene or in front of it")); EditorGUILayout.PropertyField(_propCompositionDepth, new GUIContent("Composition Depth", "Depth value used to sort layers in the scene, smaller value appears in front")); EditorGUILayout.Space(); EditorGUILayout.LabelField("Style", EditorStyles.boldLabel); EditorGUILayout.Slider(_propTextureOpacity, 0, 1f, new GUIContent("Opacity")); EditorGUILayout.Space(); EditorGUILayout.PropertyField(_propEdgeRenderingEnabled, new GUIContent("Edge Rendering", "Highlight salient edges in the camera images in a specific color")); EditorGUILayout.PropertyField(_propEdgeColor, new GUIContent("Edge Color")); if (serializedObject.ApplyModifiedProperties()) { layer.SetStyleDirty(); } layer.textureOpacity = _propTextureOpacity.floatValue; layer.edgeRenderingEnabled = _propEdgeRenderingEnabled.boolValue; layer.edgeColor = _propEdgeColor.colorValue; EditorGUILayout.Space(); // Custom popup for color map type to control order, names, and visibility of types int colorMapTypeIndex = Array.IndexOf(_colorMapTypes, layer.colorMapEditorType); if (colorMapTypeIndex == -1) { Debug.LogWarning("Invalid color map type encountered"); colorMapTypeIndex = 0; } // Dropdown list contains "Custom" only if it is currently selected. string[] colorMapNames = layer.colorMapEditorType == ColorMapEditorType.Custom ? _colorMapNames : _selectableColorMapNames; GUIContent[] colorMapLabels = new GUIContent[colorMapNames.Length]; for (int i = 0; i < colorMapNames.Length; i++) colorMapLabels[i] = new GUIContent(colorMapNames[i]); bool modified = false; OVREditorUtil.SetupPopupField(target, new GUIContent("Color Control", "The type of color controls applied to this layer"), ref colorMapTypeIndex, colorMapLabels, ref modified); layer.colorMapEditorType = _colorMapTypes[colorMapTypeIndex]; if (layer.colorMapEditorType == ColorMapEditorType.Grayscale || layer.colorMapEditorType == ColorMapEditorType.GrayscaleToColor || layer.colorMapEditorType == ColorMapEditorType.ColorAdjustment) { EditorGUILayout.PropertyField(_propColorMapEditorContrast, new GUIContent("Contrast")); EditorGUILayout.PropertyField(_propColorMapEditorBrightness, new GUIContent("Brightness")); } if (layer.colorMapEditorType == ColorMapEditorType.Grayscale || layer.colorMapEditorType == ColorMapEditorType.GrayscaleToColor) { EditorGUILayout.PropertyField(_propColorMapEditorPosterize, new GUIContent("Posterize")); } if (layer.colorMapEditorType == ColorMapEditorType.ColorAdjustment) { EditorGUILayout.PropertyField(_propColorMapEditorSaturation, new GUIContent("Saturation")); } if (layer.colorMapEditorType == ColorMapEditorType.GrayscaleToColor) { EditorGUILayout.PropertyField(_propColorMapEditorGradient, new GUIContent("Colorize")); } if (layer.colorMapEditorType == ColorMapEditorType.ColorLut || layer.colorMapEditorType == ColorMapEditorType.InterpolatedColorLut) { var sourceLutLabel = layer.colorMapEditorType == ColorMapEditorType.ColorLut ? "LUT" : "Source LUT"; EditorGUILayout.PropertyField(_propColorLutSourceTexture, new GUIContent(sourceLutLabel)); PerformLutTextureCheck((Texture2D)_propColorLutSourceTexture.objectReferenceValue); if (layer.colorMapEditorType == ColorMapEditorType.InterpolatedColorLut) { EditorGUILayout.PropertyField(_propColorLutTargetTexture, new GUIContent("Target LUT")); PerformLutTextureCheck((Texture2D)_propColorLutTargetTexture.objectReferenceValue); } var flipLutYTooltip = "Flip LUT textures along the vertical axis on load. This is needed for LUT " + "images which have color (0, 0, 0) in the top-left corner. Some color grading systems, " + "e.g. Unity post-processing, have color (0, 0, 0) in the bottom-left corner, " + "in which case flipping is not needed."; EditorGUILayout.PropertyField(_propFlipLutY, new GUIContent("Flip Vertically", flipLutYTooltip)); var weightTooltip = layer.colorMapEditorType == ColorMapEditorType.ColorLut ? "Blend between the original colors and the specified LUT. A value of 0 leaves the colors unchanged, a value of 1 fully applies the LUT." : "Blend between the source and the target LUT. A value of 0 fully applies the source LUT and a value of 1 fully applies the target LUT."; EditorGUILayout.PropertyField(_propLutWeight, new GUIContent("Blend", weightTooltip)); } serializedObject.ApplyModifiedProperties(); } private void PerformLutTextureCheck(Texture2D texture) { if (texture != null) { if (!OVRPassthroughColorLut.IsTextureSupported(texture, out var message)) { EditorGUILayout.HelpBox(message, MessageType.Error); } CheckLutImportSettings(texture); } } private void CheckLutImportSettings(Texture lut) { if (lut != null) { var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(lut)) as TextureImporter; // Fails when using an internal texture as you can't change import settings on // builtin resources, thus the check for null if (importer != null) { bool isReadable = importer.isReadable == true; bool isUncompressed = importer.textureCompression == TextureImporterCompression.Uncompressed; bool valid = isReadable && isUncompressed; if (!valid) { string warningMessage = "" + (isReadable ? "" : "Texture is not readable. ") + (isUncompressed ? "" : "Texture is compressed."); DrawFixMeBox(warningMessage, () => SetLutImportSettings(importer)); } } } } private void SetLutImportSettings(TextureImporter importer) { importer.isReadable = true; importer.textureCompression = TextureImporterCompression.Uncompressed; importer.SaveAndReimport(); AssetDatabase.Refresh(); } private void DrawFixMeBox(string text, Action action) { EditorGUILayout.HelpBox(text, MessageType.Warning); GUILayout.Space(-32); using (new EditorGUILayout.HorizontalScope()) { GUILayout.FlexibleSpace(); if (GUILayout.Button("Fix", GUILayout.Width(60))) action(); GUILayout.Space(8); } GUILayout.Space(11); } }