Files
2025-07-04 20:33:06 +03:00

325 lines
14 KiB
C#

/*
* 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);
}
}