Repository: Unity-Technologies/UnityCsReference Branch: master Commit: 59b03b8a0f17 Files: 4921 Total size: 46.4 MB Directory structure: gitextract_76xfp494/ ├── Editor/ │ ├── IncrementalBuildPipeline/ │ │ ├── BeeBuildProgramCommon.Data/ │ │ │ ├── BeeBuildProgramCommon.Data.gen.csproj │ │ │ └── Data.cs │ │ ├── PlayerBuildProgramLibrary.Data/ │ │ │ ├── Data.cs │ │ │ └── PlayerBuildProgramLibrary.Data.gen.csproj │ │ └── ScriptCompilationBuildProgram.Data/ │ │ ├── Data.cs │ │ └── ScriptCompilationBuildProgram.Data.gen.csproj │ └── Mono/ │ ├── 2D/ │ │ ├── Common/ │ │ │ ├── ScriptBindings/ │ │ │ │ └── SpriteEditorExtension.bindings.cs │ │ │ ├── SpriteEditorUtility.cs │ │ │ ├── TexturePlatformSettingsController.cs │ │ │ ├── TexturePlatformSettingsFormatHelper.cs │ │ │ └── TexturePlatformSettingsView.cs │ │ ├── Interface/ │ │ │ ├── IEvent.cs │ │ │ └── ITexturePlatformSetting.cs │ │ └── SpriteAtlas/ │ │ ├── EditorSpriteAtlas.bindings.cs │ │ ├── EditorSpritePacking.bindings.cs │ │ ├── SpriteAtlasAsset.bindings.cs │ │ ├── SpriteAtlasImporter.bindings.cs │ │ ├── SpriteAtlasImporterInspector.cs │ │ └── SpriteAtlasInspector.cs │ ├── Accessibility/ │ │ └── UserAccessibilitySettings.cs │ ├── Animation/ │ │ ├── AnimationClipSettings.bindings.cs │ │ ├── AnimationClipStats.bindings.cs │ │ ├── AnimationMode.bindings.cs │ │ ├── AnimationUtility.bindings.cs │ │ ├── AnimationWindow/ │ │ │ ├── AddCurvesPopup.cs │ │ │ ├── AddCurvesPopupHierarchy.cs │ │ │ ├── AddCurvesPopupHierarchyBuilder.cs │ │ │ ├── AddCurvesPopupHierarchyDataSource.cs │ │ │ ├── AddCurvesPopupHierarchyGUI.cs │ │ │ ├── AnimEditor.cs │ │ │ ├── AnimEditorOverlay.cs │ │ │ ├── AnimationClipSelectionItem.cs │ │ │ ├── AnimationContextualPropertyMenu.cs │ │ │ ├── AnimationKeyTime.cs │ │ │ ├── AnimationRecording.cs │ │ │ ├── AnimationWindow.cs │ │ │ ├── AnimationWindowClipPopup.cs │ │ │ ├── AnimationWindowClipboard.cs │ │ │ ├── AnimationWindowControl.cs │ │ │ ├── AnimationWindowControllerAttribute.cs │ │ │ ├── AnimationWindowCurve.cs │ │ │ ├── AnimationWindowEvent.cs │ │ │ ├── AnimationWindowEventInspector.cs │ │ │ ├── AnimationWindowHierarchy.cs │ │ │ ├── AnimationWindowHierarchyDataSource.cs │ │ │ ├── AnimationWindowHierarchyGUI.cs │ │ │ ├── AnimationWindowHierarchyNode.cs │ │ │ ├── AnimationWindowKeySelection.cs │ │ │ ├── AnimationWindowKeyframe.cs │ │ │ ├── AnimationWindowManipulator.cs │ │ │ ├── AnimationWindowOptions.cs │ │ │ ├── AnimationWindowSelectionItem.cs │ │ │ ├── AnimationWindowState.cs │ │ │ ├── AnimationWindowStyles.cs │ │ │ ├── AnimationWindowUtility.cs │ │ │ ├── ControlPointRenderer.cs │ │ │ ├── CurveBindingUtility.cs │ │ │ ├── CurveEditor.cs │ │ │ ├── CurveEditorRectangleTool.cs │ │ │ ├── CurveEditorSelection.cs │ │ │ ├── CurveEditorSettings.cs │ │ │ ├── CurveEditorWindow.cs │ │ │ ├── CurveMenuManager.cs │ │ │ ├── CurveRenderer/ │ │ │ │ ├── BoolCurveRenderer.cs │ │ │ │ ├── CurveRenderer.cs │ │ │ │ ├── EulerCurveCombinedRenderer.cs │ │ │ │ ├── EulerCurveRenderer.cs │ │ │ │ ├── IntCurveRenderer.cs │ │ │ │ └── NormalCurveRenderer.cs │ │ │ ├── Deprecated/ │ │ │ │ ├── AnimationEventTimeline.cs │ │ │ │ ├── EditorGUIExt.cs │ │ │ │ └── UtilityClasses.cs │ │ │ ├── DopeLine.cs │ │ │ ├── DopeSheetEditor.cs │ │ │ ├── DopeSheetEditorRectangleTool.cs │ │ │ ├── GameObjectSelectionItem.cs │ │ │ ├── IAnimationContextualResponder.cs │ │ │ ├── IAnimationRecordingState.cs │ │ │ ├── IAnimationWindowControl.cs │ │ │ ├── IAnimationWindowController.cs │ │ │ ├── MinMaxCurveEditorWindow.cs │ │ │ ├── RectangleTool.cs │ │ │ └── RotationCurveInterpolation.cs │ │ ├── AnimatorController.cs │ │ ├── BlendTree.cs │ │ ├── EditorCurveBinding.bindings.cs │ │ ├── GameObjectRecorder.bindings.cs │ │ ├── MaterialAnimationUtility.cs │ │ ├── MecanimUtilities.cs │ │ ├── SerializedStringTable.cs │ │ ├── StateMachine.cs │ │ ├── TickHandler.cs │ │ ├── TickStyle.cs │ │ ├── TimeArea.cs │ │ ├── TransitionPreview.cs │ │ └── ZoomableArea.cs │ ├── AnimationCurvePreviewCache.bindings.cs │ ├── AnimatorController.bindings.cs │ ├── AnimatorControllerLayer.bindings.cs │ ├── Annotation/ │ │ ├── AnnotationUtility.bindings.cs │ │ ├── AnnotationWindow.cs │ │ ├── GizmoInfo.cs │ │ ├── GizmoUtility.cs │ │ ├── LayerVisibilityWindow.cs │ │ ├── SceneFXWindow.cs │ │ ├── SceneRenderModeWindow.cs │ │ └── SceneViewCameraWindow.cs │ ├── ArrayUtility.cs │ ├── AssemblyHelper.cs │ ├── AssemblyInfo/ │ │ └── AssemblyInfo.cs │ ├── AssemblyReloadEvents.cs │ ├── AssemblyValidation.cs │ ├── AssetDatabase/ │ │ ├── AssetDatabase.cs │ │ ├── AssetDatabase.deprecated.cs │ │ ├── AssetDatabaseSearching.cs │ │ ├── AssetImportInProgressProxy.bindings.cs │ │ ├── AssetMoveInfo.cs │ │ ├── AssetPreview.bindings.cs │ │ └── AssetsModifiedProcessor.cs │ ├── AssetDeleteResult.cs │ ├── AssetModificationProcessor.cs │ ├── AssetMoveResult.cs │ ├── AssetPipeline/ │ │ ├── AssemblyDefinitionImporter.cs │ │ ├── AssemblyDefinitionReferenceImporter.cs │ │ ├── AssetImportContext.bindings.cs │ │ ├── AssetImporter.bindings.cs │ │ ├── BumpMapSettings.bindings.cs │ │ ├── CameraDescription.bindings.cs │ │ ├── ComputeShaderImporter.bindings.cs │ │ ├── IHVImageFormatImporter.bindings.cs │ │ ├── ImportLog.bindings.cs │ │ ├── LightDescription.bindings.cs │ │ ├── LocalCacheServer.cs │ │ ├── MaterialDescription.bindings.cs │ │ ├── ShaderImporter.bindings.cs │ │ ├── ShaderIncludeImporter.bindings.cs │ │ ├── SpeedTree/ │ │ │ ├── SpeedTree9Importer.cs │ │ │ ├── SpeedTree9ImporterEditor.cs │ │ │ ├── SpeedTree9ImporterMaterialEditor.cs │ │ │ ├── SpeedTree9ImporterModelEditor.cs │ │ │ ├── SpeedTree9ImporterWindEditor.cs │ │ │ ├── SpeedTree9Reader.cs │ │ │ ├── SpeedTreeImporterCommon.cs │ │ │ ├── SpeedTreeImporterOutputData.cs │ │ │ └── SpeedTreeImporterSettings.cs │ │ ├── SpeedTreeImporter.bindings.cs │ │ ├── TextureGenerator.bindings.cs │ │ ├── TextureImporter.bindings.cs │ │ ├── TextureImporterEnums.cs │ │ ├── TextureImporterTypes.bindings.cs │ │ └── TextureUtil.bindings.cs │ ├── AssetPostprocessor.cs │ ├── AssetPreviewUpdater.cs │ ├── AssetStore/ │ │ ├── AssetStoreAsset.cs │ │ ├── AssetStoreClient.cs │ │ ├── AssetStoreContext.cs │ │ ├── AssetStorePreviewManager.cs │ │ ├── AssetStoreWindow.cs │ │ └── Json.cs │ ├── AssetStore.bindings.cs │ ├── AssetStoreCachePathManager.bindings.cs │ ├── AssetStoreCachePathManager.cs │ ├── AssetStoreContext.bindings.cs │ ├── AssetStoreToolUtils.bindings.cs │ ├── AssetStoreUtils.bindings.cs │ ├── AssetsMenuUtility.bindings.cs │ ├── AsyncHTTPClient.bindings.cs │ ├── AsyncHTTPClient.cs │ ├── AttributeHelper.cs │ ├── Audio/ │ │ ├── Analytics/ │ │ │ ├── AudioAnalytics.cs │ │ │ ├── AudioRandomContainerBuildAnalyticsEvent.cs │ │ │ └── AudioRandomContainerQuitAnalyticsEvent.cs │ │ ├── AudioContainerListDragAndDropManipulator.cs │ │ ├── AudioContainerWindow.cs │ │ ├── AudioContainerWindowState.cs │ │ ├── Bindings/ │ │ │ └── AudioUtil.bindings.cs │ │ ├── Effects/ │ │ │ ├── AudioCurveRendering.cs │ │ │ ├── AudioMixerEffectPlugin.cs │ │ │ ├── Complex.cs │ │ │ ├── DuckVolumeGUI.cs │ │ │ ├── IAudioEffectPlugin.cs │ │ │ ├── IAudioEffectPluginGUI.cs │ │ │ └── ParamEQGUI.cs │ │ ├── Mixer/ │ │ │ ├── AudioMixerDescription.cs │ │ │ ├── Bindings/ │ │ │ │ ├── AudioMixerController.cs │ │ │ │ └── AudioMixerGroup.cs │ │ │ └── GUI/ │ │ │ ├── AudioMixerChannelStripView.cs │ │ │ ├── AudioMixerColorCodes.cs │ │ │ ├── AudioMixerDrawUtils.cs │ │ │ ├── AudioMixerEffectGUI.cs │ │ │ ├── AudioMixerEffectView.cs │ │ │ ├── AudioMixerExposedParameterView.cs │ │ │ ├── AudioMixerExposedParametersPopup.cs │ │ │ ├── AudioMixerGroupTreeView.cs │ │ │ ├── AudioMixerGroupViewList.cs │ │ │ ├── AudioMixerSelection.cs │ │ │ ├── AudioMixerSnapshotView.cs │ │ │ ├── AudioMixerUtility.cs │ │ │ ├── AudioMixerWindow.cs │ │ │ ├── AudioMixersTreeView.cs │ │ │ ├── ReorderableListWithRenameAndScrollView.cs │ │ │ └── TreeViewForAudioMixerGroups.cs │ │ ├── StreamedAudioClipPreview.cs │ │ ├── UIElements/ │ │ │ ├── AudioContainerElementClipField.cs │ │ │ ├── AudioLevelMeter.cs │ │ │ ├── AudioRandomRangeSliderTracker.cs │ │ │ ├── OnAudioFilterReadLevelMeter.cs │ │ │ └── Tickmarks.cs │ │ ├── UIToolkitUtilities.cs │ │ ├── WaveformPreview.cs │ │ ├── WaveformPreviewFactory.cs │ │ └── WaveformStreamer.bindings.cs │ ├── AvatarUtility.bindings.cs │ ├── BaseBuildTarget.cs │ ├── BlendTree.bindings.cs │ ├── BlendTreePreviewUtility.bindings.cs │ ├── BrokenPrefabAsset.bindings.cs │ ├── BrokenPrefabAssetEditor.cs │ ├── BugReportingTools.bindings.cs │ ├── BuildPipeline/ │ │ ├── Android/ │ │ │ └── AndroidPostGenerateGradleProject.cs │ │ ├── AssemblyStripper.cs │ │ ├── AssemblyTypeInfoGenerator.cs │ │ ├── BuildFailedException.cs │ │ ├── BuildPipelineInterfaces.cs │ │ ├── BuildPlatform.cs │ │ ├── BuildPlayerContext.cs │ │ ├── DataBuildDirtyTracker.cs │ │ ├── DesktopStandaloneBuildWindowExtension.cs │ │ ├── DesktopStandalonePostProcessor.cs │ │ ├── DesktopStandaloneUserBuildSettings.cs │ │ ├── IPostprocessLaunch.cs │ │ ├── Il2Cpp/ │ │ │ └── IL2CPPUtils.cs │ │ ├── NamedBuildTarget.cs │ │ ├── OSArchitecture.cs │ │ ├── PostprocessBuildPlayer.cs │ │ ├── RenderPipeline/ │ │ │ ├── EnsureSinglePipelineOnBuild.cs │ │ │ ├── RenderPipelineBuildProcessor.cs │ │ │ └── RenderPipelineGlobalSettingsStripper.cs │ │ ├── RuntimeClassMetadata.cs │ │ ├── RuntimeClassMetadataUtils.bindings.cs │ │ └── UnityLinker/ │ │ └── UnityLinkerBuildPipelineData.cs │ ├── BuildPipeline.bindings.cs │ ├── BuildPipelineExperimental.cs │ ├── BuildPlayerDataExtractor.cs │ ├── BuildPlayerSceneTreeView.cs │ ├── BuildPlayerWindow.cs │ ├── BuildPlayerWindowBuildMethods.cs │ ├── BuildProfile/ │ │ ├── BuildProfile.cs │ │ ├── BuildProfileAPI.cs │ │ ├── BuildProfileCLI.cs │ │ ├── BuildProfileContext.cs │ │ ├── BuildProfileCreate.cs │ │ ├── BuildProfileGraphicsSettings.cs │ │ ├── BuildProfileGraphicsSettingsEditor.cs │ │ ├── BuildProfileModuleUtil.cs │ │ ├── BuildProfilePackageAddInfo.cs │ │ ├── BuildProfilePlatformSettingsBase.cs │ │ ├── BuildProfilePlayerSettings.cs │ │ ├── BuildProfileQualitySettings.cs │ │ ├── BuildProfileQualitySettingsEditor.cs │ │ ├── BuildProfileRenameOverlay.cs │ │ ├── BuildProfileSceneListTreeView.cs │ │ ├── BuildProfileState.cs │ │ ├── Events.cs │ │ └── SharedPlatformSettings.cs │ ├── BuildTarget.cs │ ├── BuildTargetConverter.cs │ ├── BuildTargetDiscovery.bindings.cs │ ├── BuildTargetGroup.cs │ ├── CSPreProcess.cs │ ├── Callbacks.cs │ ├── Camera/ │ │ ├── BuiltinBakedReflectionSystem.bindings.cs │ │ ├── CameraProjectionCache.cs │ │ ├── EditorCameraUtils.bindings.cs │ │ ├── IScriptableBakedReflectionSystem.cs │ │ ├── IScriptableBakedReflectionSystemStageNotifier.cs │ │ ├── SceneStateHash.cs │ │ ├── ScriptableBakedReflectionSystem.cs │ │ ├── ScriptableBakedReflectionSystemSettings.cs │ │ └── ScriptableBakedReflectionSystemWrapper.bindings.cs │ ├── Categorize.cs │ ├── ChangeTrackerHandle.bindings.cs │ ├── Clipboard/ │ │ ├── Clipboard.cs │ │ ├── ClipboardContextMenu.cs │ │ ├── ClipboardParser.cs │ │ └── ClipboardState.cs │ ├── ClipboardUtility.cs │ ├── CloudBuild/ │ │ └── CloudBuild.cs │ ├── CodeEditor/ │ │ ├── CodeEditor.cs │ │ ├── CodeEditorAnalytics.cs │ │ ├── CodeEditorProjectSync.cs │ │ ├── DefaultExternalCodeEditor.cs │ │ ├── ExternalEditor.bindings.cs │ │ ├── IExternalCodeEditor.cs │ │ └── SyncVS.cs │ ├── Collab/ │ │ ├── CollabToUVCSBridge.cs │ │ └── IVersionControl.cs │ ├── CollectImportedDependenciesAttribute.cs │ ├── Commands/ │ │ ├── CommandService.cs │ │ └── GOCreationCommands.cs │ ├── CompilationPipeline.bindings.cs │ ├── ComponentUtility.bindings.cs │ ├── ComponentUtility.cs │ ├── ConsoleWindow.cs │ ├── ConsoleWindowUtility.cs │ ├── ContainerWindow.bindings.cs │ ├── ContainerWindow.cs │ ├── ContextMenuUtility.cs │ ├── CustomEditorAttributes.bindings.cs │ ├── CustomEditorAttributes.cs │ ├── CustomInspectorStubs.cs │ ├── CutBoard.cs │ ├── DataMode.cs │ ├── DefaultAsset.bindings.cs │ ├── Delayer.cs │ ├── DeploymentTargets/ │ │ ├── DefaultDeploymentTargetsExtension.cs │ │ ├── DeploymentTargetLogger.cs │ │ ├── DeploymentTargetManager.cs │ │ └── IDeploymentTargetsExtension.cs │ ├── DisplayUtility.cs │ ├── DragAndDrop.bindings.cs │ ├── DrivenPropertyManagerInternal.bindings.cs │ ├── DrivenRectTransformUndo.cs │ ├── DropInfo.cs │ ├── DynamicHints/ │ │ ├── DynamicHintContent.cs │ │ └── DynamicHintUtility.cs │ ├── EditorApplication.bindings.cs │ ├── EditorApplication.cs │ ├── EditorApplication.deprecated.cs │ ├── EditorAssemblies.bindings.cs │ ├── EditorAssemblies.cs │ ├── EditorBuildSettings.bindings.cs │ ├── EditorConnectionInternal.bindings.cs │ ├── EditorGUI.EnumMaskField.deprecated.cs │ ├── EditorGUI.RenderPipeline.cs │ ├── EditorGUI.cs │ ├── EditorGUILayout.RenderPipeline.cs │ ├── EditorGUILayout.cs │ ├── EditorGUIUtility.bindings.cs │ ├── EditorGUIUtility.cs │ ├── EditorGraphicsSettings.bindings.cs │ ├── EditorGraphicsSettings.cs │ ├── EditorHeaderItemAttribute.cs │ ├── EditorMode/ │ │ ├── MenuService.cs │ │ └── ModeService.cs │ ├── EditorPrefs.bindings.cs │ ├── EditorResources.bindings.cs │ ├── EditorResources.cs │ ├── EditorSceneManager.bindings.cs │ ├── EditorSceneManager.bindings.deprecated.cs │ ├── EditorSceneManager.cs │ ├── EditorSerializationUtility.bindings.cs │ ├── EditorSettings.bindings.cs │ ├── EditorUserBuildSettings.bindings.cs │ ├── EditorUserBuildSettings.deprecated.cs │ ├── EditorUserBuildSettingsEmbeddedCommon.bindings.cs │ ├── EditorUserBuildSettingsEmbeddedLinux.deprecated.cs │ ├── EditorUserBuildSettingsQNX.bindings.cs │ ├── EditorUserBuildSettingsQNX.deprecated.cs │ ├── EditorUserBuildSettingsUtils.cs │ ├── EditorUserSettings.bindings.cs │ ├── EditorUtility.bindings.cs │ ├── EditorUtility.cs │ ├── EditorWindow.bindings.cs │ ├── EditorWindow.cs │ ├── EnumDataUtility.cs │ ├── EventWithPerformanceTracker.cs │ ├── ExportPackageOptions.cs │ ├── ExternalPlayModeView/ │ │ ├── ExternalPlayModeView.bindings.cs │ │ └── ExternalPlayModeView.cs │ ├── FileUtil.bindings.cs │ ├── FileUtil.cs │ ├── FlagSet.cs │ ├── GI/ │ │ ├── DeviceContext.bindings.cs │ │ ├── InputExtraction.bindings.cs │ │ ├── InputExtraction.cs │ │ ├── IntegrationContext.bindings.cs │ │ ├── InteractiveLightBaking.bindings.cs │ │ ├── LightBaker.bindings.cs │ │ ├── LightProbeVisualization.bindings.cs │ │ ├── LightingDataAsset.bindings.cs │ │ ├── LightmapEditorSettings.bindings.cs │ │ ├── LightmapEditorSettingsDeprecated.cs │ │ ├── LightmapParameters.bindings.cs │ │ ├── LightmapSnapshot.deprecated.cs │ │ ├── LightmapVisualization.bindings.cs │ │ ├── Lightmapping.bindings.cs │ │ ├── Lightmapping.deprecated.cs │ │ ├── PostProcessing.bindings.cs │ │ ├── ProbeIntegrator.bindings.cs │ │ ├── ProgressState.bindings.cs │ │ ├── RadeonRaysDeviceContext.bindings.cs │ │ ├── RadeonRaysLightBaker.bindings.cs │ │ ├── UnityComputeBake.cs │ │ ├── WintermuteDeviceContext.bindings.cs │ │ ├── WintermuteLightBaker.bindings.cs │ │ └── World.bindings.cs │ ├── GUI/ │ │ ├── AboutWindow.cs │ │ ├── AngularDial.cs │ │ ├── AnimatedValues.cs │ │ ├── AppStatusBar.cs │ │ ├── AssetPopupBackend.cs │ │ ├── AssetSaveDialog.cs │ │ ├── BumpMapSettingsFixingWindow.cs │ │ ├── ButtonWithAnimatedIcon.cs │ │ ├── CacheServerToggle.cs │ │ ├── CacheServerWindow.cs │ │ ├── CallbackController.cs │ │ ├── ColorMutator.cs │ │ ├── ColorPicker.cs │ │ ├── ColorPicker.deprecated.cs │ │ ├── ColumnView.cs │ │ ├── CreateAssetUtility.cs │ │ ├── DockArea.cs │ │ ├── DragRect.cs │ │ ├── EditorApplicationLayout.cs │ │ ├── EditorCache.cs │ │ ├── EditorGUIContents.cs │ │ ├── EditorGUIInternal.cs │ │ ├── EditorStyles.cs │ │ ├── EditorUpdateWindow.cs │ │ ├── ExposablePopupMenu.cs │ │ ├── FallbackEditorWindow.cs │ │ ├── FlexibleMenu/ │ │ │ ├── FlexibleMenu.cs │ │ │ ├── FlexibleMenuModifyItemUI.cs │ │ │ └── IFlexibleMenuItemProvider.cs │ │ ├── FlowLayout.cs │ │ ├── FoldoutHeader.cs │ │ ├── GenericMenu.cs │ │ ├── GradientEditor.cs │ │ ├── GradientField.cs │ │ ├── GradientPicker.cs │ │ ├── HexColorTextField.cs │ │ ├── IApplyRevertPropertyContextMenuItemProvider.cs │ │ ├── InternalEditorGUI.cs │ │ ├── InternalEditorGUILayout.cs │ │ ├── Knob.cs │ │ ├── LazyLoadReferenceField.cs │ │ ├── ListViewElement.cs │ │ ├── ListViewGUI.cs │ │ ├── ListViewGUILayout.cs │ │ ├── ListViewOptions.cs │ │ ├── ListViewShared.cs │ │ ├── ListViewState.cs │ │ ├── MainView.cs │ │ ├── ManagedDebuggerToggle.cs │ │ ├── ManagedDebuggerWindow.cs │ │ ├── MaskFieldGUI.cs │ │ ├── ObjectField.cs │ │ ├── PackageExport.cs │ │ ├── PackageExportTreeView.cs │ │ ├── PackageImport.cs │ │ ├── PackageImportTreeView.cs │ │ ├── PaneDragTab.cs │ │ ├── PingData.cs │ │ ├── PopupLocation.cs │ │ ├── PopupLocationHelper.cs │ │ ├── PopupWindow.cs │ │ ├── PopupWindowWithoutFocus.cs │ │ ├── PreviewResizer.cs │ │ ├── RenameOverlay.cs │ │ ├── ReorderableList.cs │ │ ├── ScalableGUIContent.cs │ │ ├── ScreenShotting.cs │ │ ├── SearchField.cs │ │ ├── SliderWithTexture.cs │ │ ├── SplitView.cs │ │ ├── Splitter.cs │ │ ├── StructPropertyGUI.cs │ │ ├── SubToolbar.cs │ │ ├── TargetChoiceHandler.cs │ │ ├── TextFieldDropDown.cs │ │ ├── Toolbars/ │ │ │ ├── EditorToolbar.cs │ │ │ ├── EditorToolbarElementAttribute.cs │ │ │ ├── EditorToolbarManager.cs │ │ │ ├── EditorToolbarUtility.cs │ │ │ ├── MainToolbarImguiContainer.cs │ │ │ └── Toolbar.cs │ │ ├── TreeView/ │ │ │ ├── AssetOrGameObjectTreeViewDragging.cs │ │ │ ├── AssetsTreeViewDataSource.cs │ │ │ ├── AssetsTreeViewGUI.cs │ │ │ ├── GameObjectTreeViewDataSource.cs │ │ │ ├── GameObjectTreeViewGUI.cs │ │ │ ├── GameObjectTreeViewItem.cs │ │ │ ├── ITreeViewDataSource.cs │ │ │ ├── ITreeViewDragging.cs │ │ │ ├── ITreeViewGUI.cs │ │ │ ├── LazyTreeViewDataSource.cs │ │ │ ├── MultiColumnHeader.cs │ │ │ ├── MultiColumnHeaderDefaults.cs │ │ │ ├── MultiColumnHeaderState.cs │ │ │ ├── SubSceneGUI.cs │ │ │ ├── ToggleTreeView.cs │ │ │ ├── TreeViewControl/ │ │ │ │ ├── TreeViewControl.cs │ │ │ │ ├── TreeViewControlDataSource.cs │ │ │ │ ├── TreeViewControlDefaults.cs │ │ │ │ ├── TreeViewControlDragging.cs │ │ │ │ ├── TreeViewControlGUI.cs │ │ │ │ └── TreeViewOld.cs │ │ │ ├── TreeViewController.cs │ │ │ ├── TreeViewDataSource.cs │ │ │ ├── TreeViewDragging.cs │ │ │ ├── TreeViewExpandAnimator.cs │ │ │ ├── TreeViewGUI.cs │ │ │ ├── TreeViewGUIWithCustomItemHeights.cs │ │ │ ├── TreeViewItem.cs │ │ │ ├── TreeViewTests/ │ │ │ │ ├── TreeViewTest.cs │ │ │ │ ├── TreeViewTestBackEnd.cs │ │ │ │ ├── TreeViewTestDataSource.cs │ │ │ │ ├── TreeViewTestDragging.cs │ │ │ │ ├── TreeViewTestGUI.cs │ │ │ │ ├── TreeViewTestGUICustom.cs │ │ │ │ ├── TreeViewTestLazyDataSource.cs │ │ │ │ ├── TreeViewTestWindow.cs │ │ │ │ └── TreeViewTestWithCustomHeight.cs │ │ │ └── TreeViewUtililty.cs │ │ ├── VUMeter.cs │ │ ├── VerticalGrid.cs │ │ └── WindowLayout.cs │ ├── GUID.bindings.cs │ ├── GUIDebugger/ │ │ ├── BaseInspectView.cs │ │ ├── ElementHighlighter.cs │ │ ├── GUIClipInspectView.cs │ │ ├── GUILayoutInspectView.cs │ │ ├── GUINamedControlInspectView.cs │ │ ├── GUIPropertyInspectView.cs │ │ ├── GUIViewDebuggerHelper.bindings.cs │ │ ├── GUIViewDebuggerWindow.cs │ │ ├── StyleDrawInspectView.cs │ │ ├── StylePicker.cs │ │ └── UnifiedInspectView.cs │ ├── GUIView.bindings.cs │ ├── GUIView.cs │ ├── GameObjectChangeTracker.bindings.cs │ ├── GameObjectUtility.bindings.cs │ ├── GameObjectUtility.deprecated.cs │ ├── GameView/ │ │ ├── GameView.cs │ │ ├── GameViewSize.cs │ │ ├── GameViewSizeGroup.cs │ │ ├── GameViewSizeMenu.cs │ │ ├── GameViewSizes.cs │ │ ├── GameViewSizesMenuItemProvider.cs │ │ ├── GameViewSizesMenuModifyItemUI.cs │ │ ├── GameviewGUI.cs │ │ ├── IGameViewOnPlayMenuUser.cs │ │ └── IGameViewSizeMenuUser.cs │ ├── GenerateIconsWithMipLevels.cs │ ├── Gizmos/ │ │ └── DrawGizmo.cs │ ├── GlobalObjectId.bindings.cs │ ├── GradientPreviewCache.bindings.cs │ ├── Graphics/ │ │ ├── Analytics/ │ │ │ ├── GraphicsToolLifetimeAnalytic.cs │ │ │ └── GraphicsToolUsageAnalytic.cs │ │ ├── EditorMaterialUtility.bindings.cs │ │ ├── GraphicsStateCollectionImporter.cs │ │ ├── RenderPipelineGlobalSettingsPostprocessor.cs │ │ ├── RenderPipelineGraphicsSettingsEditorUtility.cs │ │ ├── RenderingLayersLimitSettings.cs │ │ ├── ShaderCompilerData.cs │ │ ├── StaticBatchingEditorHelper.cs │ │ └── VulkanDeviceFilterListsEditor.cs │ ├── Grids/ │ │ ├── EditorSnap.cs │ │ ├── GridShortcuts.cs │ │ ├── GridSnapping.cs │ │ └── SnapSettings.cs │ ├── Handles/ │ │ ├── ArcHandle.cs │ │ ├── BoneHandle.cs │ │ ├── BoundsHandle/ │ │ │ ├── BoxBoundsHandle.cs │ │ │ ├── CapsuleBoundsHandle.cs │ │ │ ├── PrimitiveBoundsHandle.cs │ │ │ └── SphereBoundsHandle.cs │ │ ├── Button.cs │ │ ├── ConeFrustrumHandle.cs │ │ ├── ConeHandle.cs │ │ ├── Disc.cs │ │ ├── FreeMove.cs │ │ ├── FreeRotate.cs │ │ ├── HandleUtility.bindings.cs │ │ ├── HandleUtility.cs │ │ ├── Handles.bindings.cs │ │ ├── Handles.cs │ │ ├── PositionHandle.cs │ │ ├── RadiusHandle.cs │ │ ├── RectHandle.cs │ │ ├── RotationHandle.cs │ │ ├── ScaleHandle.cs │ │ ├── SimpleRadiusHandle.cs │ │ ├── Slider1D.cs │ │ ├── Slider2D.cs │ │ ├── SliderScale.cs │ │ ├── TransformHandle.cs │ │ └── VertexSnapping.cs │ ├── Hardware.bindings.cs │ ├── Help.bindings.cs │ ├── Help.cs │ ├── HierarchyProperty.bindings.cs │ ├── HomeWindow.bindings.cs │ ├── HostView.cs │ ├── HyperLinkClickedEventArgs.cs │ ├── IAudioPlatformProperties.cs │ ├── IBuildPlatformProperties.cs │ ├── IBuildTarget.cs │ ├── ICleanuppable.cs │ ├── IDerivedBuildTarget.cs │ ├── IDerivedBuildTargetProvider.cs │ ├── IDropArea.cs │ ├── IGraphicsPlatformProperties.cs │ ├── IHasCustomMenu.cs │ ├── IIconPlatformProperties.cs │ ├── IInsightsPlatformProperties.cs │ ├── IPlatformProperties.cs │ ├── IPlayerConnectionPlatformProperties.cs │ ├── ISubtargetPlatformProperties.cs │ ├── IUIPlatformProperties.cs │ ├── IVRPlatformProperties.cs │ ├── IconSelector.cs │ ├── ImportSettings/ │ │ ├── AnimationClipInfoProperties.cs │ │ ├── BaseSpeedTreeImporterTabUI.cs │ │ ├── DesktopPluginImporterExtension.cs │ │ ├── EditorPluginImporterExtension.cs │ │ ├── ExposeTransformEditor.cs │ │ ├── IHVImageFormatImporterInspector.cs │ │ ├── ImportSettingsInternalID.cs │ │ ├── SpeedTreeImporterInspector.cs │ │ ├── SpeedTreeImporterMaterialEditor.cs │ │ ├── SpeedTreeImporterModelEditor.cs │ │ ├── TextureImportPlatformSettings.cs │ │ ├── TextureImportValidFormats.cs │ │ └── TextureImporterInspector.cs │ ├── InSceneAssetUtility.bindings.cs │ ├── Inspector/ │ │ ├── AimConstraintEditor.cs │ │ ├── AnimationClipEditor.cs │ │ ├── AnimationEditor.cs │ │ ├── AnimatorInspector.cs │ │ ├── AnimatorOverrideControllerInspector.cs │ │ ├── AssemblyDefinitionImporterInspector.cs │ │ ├── AssemblyDefinitionReferenceImporterInspector.cs │ │ ├── AssetBundleNameGUI.cs │ │ ├── AudioChorusFilterEditor.cs │ │ ├── AudioClipInspector.cs │ │ ├── AudioDistortionFilterInspector.cs │ │ ├── AudioEchoFilterInspector.cs │ │ ├── AudioFilterGUI.cs │ │ ├── AudioHighPassFilterInspector.cs │ │ ├── AudioLowPassFilterInspector.cs │ │ ├── AudioManagerInspector.cs │ │ ├── AudioMixerControllerInspector.cs │ │ ├── AudioMixerGroupEditor.cs │ │ ├── AudioRandomContainerInspector.cs │ │ ├── AudioReverbFilterEditor.cs │ │ ├── AudioReverbZoneEditor.cs │ │ ├── AudioSourceInspector.cs │ │ ├── AutodeskInteractiveShaderGUI.cs │ │ ├── Avatar/ │ │ │ ├── AvatarAutoMapper.cs │ │ │ ├── AvatarBipedMapper.cs │ │ │ ├── AvatarControl.cs │ │ │ ├── AvatarEditor.cs │ │ │ ├── AvatarMappingEditor.cs │ │ │ ├── AvatarMuscleEditor.cs │ │ │ ├── AvatarSetupTool.cs │ │ │ └── AvatarSkeletonDrawer.cs │ │ ├── AvatarMaskInspector.cs │ │ ├── AvatarMaskUtility.cs │ │ ├── AvatarPreview.cs │ │ ├── AvatarPreviewSelection.cs │ │ ├── BillboardAssetInspector.cs │ │ ├── BillboardRendererInspector.cs │ │ ├── BlendTreeInspector.cs │ │ ├── CameraEditor.cs │ │ ├── CameraEditorUtils.cs │ │ ├── CameraOverlay.cs │ │ ├── CanvasEditor.cs │ │ ├── CanvasRendererEditor.cs │ │ ├── ColliderEditorBase.cs │ │ ├── ColorPresetLibraryInspector.cs │ │ ├── ComputeShaderImporterInspector.cs │ │ ├── ComputeShaderInspector.cs │ │ ├── ConstrainProportionsTransformScale.cs │ │ ├── ConstraintEditorBase.cs │ │ ├── Core/ │ │ │ ├── AddComponent/ │ │ │ │ ├── AddComponentDataSource.cs │ │ │ │ ├── AddComponentGUI.cs │ │ │ │ ├── AddComponentWindow.cs │ │ │ │ ├── ComponentDropdownItem.cs │ │ │ │ └── NewScriptDropdownItem.cs │ │ │ ├── AdvancedDropdown/ │ │ │ │ ├── AdvancedDropdownDataSource.cs │ │ │ │ ├── AdvancedDropdownGUI.cs │ │ │ │ ├── AdvancedDropdownItem.cs │ │ │ │ ├── AdvancedDropdownState.cs │ │ │ │ ├── AdvancedDropdownWindow.cs │ │ │ │ ├── DataSources/ │ │ │ │ │ ├── CallbackDataSource.cs │ │ │ │ │ ├── MultiLevelDataSource.cs │ │ │ │ │ ├── MultiselectDataSource.cs │ │ │ │ │ └── SimpleDataSource.cs │ │ │ │ └── EditorGUI/ │ │ │ │ ├── AdvancedDropdown.cs │ │ │ │ ├── EditorGUIAdvancedDropdown.cs │ │ │ │ └── StatelessAdvancedDropdown.cs │ │ │ ├── CustomEditor.cs │ │ │ ├── GUI/ │ │ │ │ └── TypeSelectionList.cs │ │ │ ├── GenericInspector.cs │ │ │ ├── InspectorPreviewWindow.cs │ │ │ ├── InspectorWindow.cs │ │ │ ├── PreviewWindow.cs │ │ │ ├── PropertyEditor.cs │ │ │ ├── RootEditor.cs │ │ │ ├── ScriptAttributeGUI/ │ │ │ │ ├── CustomPropertyDrawerAttribute.cs │ │ │ │ ├── DecoratorDrawer.cs │ │ │ │ ├── GUIDrawer.cs │ │ │ │ ├── Implementations/ │ │ │ │ │ ├── DecoratorDrawers.cs │ │ │ │ │ ├── ExposedReferenceDrawer.cs │ │ │ │ │ ├── ExposedReferenceObject.cs │ │ │ │ │ └── PropertyDrawers.cs │ │ │ │ ├── PropertyDrawer.cs │ │ │ │ ├── PropertyHandler.cs │ │ │ │ ├── PropertyTrait.cs │ │ │ │ └── ScriptAttributeUtility.cs │ │ │ ├── ScriptBindings/ │ │ │ │ └── Editor.bindings.cs │ │ │ └── Utils/ │ │ │ ├── InspectorWindowUtils.cs │ │ │ └── PropertyDrawerCache.cs │ │ ├── CubemapArrayInspector.cs │ │ ├── CubemapInspector.cs │ │ ├── CubemapPreview.cs │ │ ├── CurvePresetLibraryInspector.cs │ │ ├── CustomPreviewAttribute.cs │ │ ├── CustomRenderTextureEditor.cs │ │ ├── DirectorEditor.cs │ │ ├── DoubleCurvePresetLibraryInspector.cs │ │ ├── EditMode.cs │ │ ├── Editor.cs │ │ ├── EditorDragging.cs │ │ ├── EditorElementUpdater.cs │ │ ├── EditorSettingsInspector.cs │ │ ├── Enlighten/ │ │ │ └── LightmapParameters.cs │ │ ├── FontInspector.cs │ │ ├── GameObjectInspector.cs │ │ ├── GenericPresetLibraryInspector.cs │ │ ├── GradientPresetLibraryInspector.cs │ │ ├── GraphicsSettingsInspector.cs │ │ ├── GraphicsSettingsInspectors/ │ │ │ ├── GraphicsSettingsElement.cs │ │ │ ├── GraphicsSettingsInspectorTierSettings.cs │ │ │ ├── GraphicsSettingsInspectorUtility.cs │ │ │ ├── RenderPipelineGlobalSettingsAssetProcessor.cs │ │ │ └── RenderPipelineGraphicsSettingsPropertyDrawer.cs │ │ ├── IEditorElement.cs │ │ ├── LODGroupEditor.cs │ │ ├── LODGroupGUI.cs │ │ ├── LabelGUI.cs │ │ ├── LayoutDropdownWindow.cs │ │ ├── LegacyIlluminShaderGUI.cs │ │ ├── LightEditor.cs │ │ ├── LightProbeGroupInspector.cs │ │ ├── LightProbeProxyVolumeEditor.cs │ │ ├── LightProbesInspector.cs │ │ ├── LightingSettingsEditor.cs │ │ ├── LineRendererCurveEditor.cs │ │ ├── LineRendererEditor.cs │ │ ├── LineRendererEditorSettings.cs │ │ ├── LineRendererPositionsView.cs │ │ ├── LineRendererToolModes.cs │ │ ├── LookAtConstraintEditor.cs │ │ ├── MaskFieldDropdown.cs │ │ ├── MaterialEditor.cs │ │ ├── MaterialEditorGUIHelpers.cs │ │ ├── MaterialPropertyDrawer.cs │ │ ├── MemorySettingsEditor.cs │ │ ├── MeshPreview.cs │ │ ├── MeshRendererEditor.cs │ │ ├── MinMaxCurvePropertyDrawer.cs │ │ ├── MinMaxGradientPropertyDrawer.cs │ │ ├── ModelInspector.cs │ │ ├── MonoScriptInspector.cs │ │ ├── NavMeshAgentInspector.cs │ │ ├── NavMeshObstacleInspector.cs │ │ ├── NotSupportedOnRenderPipelineInspector.cs │ │ ├── OcclusionAreaEditor.cs │ │ ├── OcclusionPortalEditor.cs │ │ ├── OffMeshLinkInspector.deprecated.cs │ │ ├── ParentConstraintEditor.cs │ │ ├── ParticleSystemForceFieldInspector.cs │ │ ├── PlayerSettingsEditor/ │ │ │ ├── PlayerSettingsEditor.cs │ │ │ ├── PlayerSettingsIconsEditor.cs │ │ │ ├── PlayerSettingsSplashScreenEditor.bindings.cs │ │ │ ├── PlayerSettingsSplashScreenEditor.cs │ │ │ ├── WebTemplate.cs │ │ │ └── WebTemplateManagerBase.cs │ │ ├── PlayerSettingsSectionAttribute.cs │ │ ├── PositionConstraintEditor.cs │ │ ├── PreviewRenderUtility.cs │ │ ├── ProjectSettingsBaseEditor.cs │ │ ├── QualitySettingsEditor.cs │ │ ├── RayTracingShaderInspector.cs │ │ ├── RectHandles.cs │ │ ├── RectTransformEditor.cs │ │ ├── RectTransformSnapping.cs │ │ ├── ReflectionProbeEditor.cs │ │ ├── RenderPipelineAssetSelector.cs │ │ ├── RenderPipelineEditorUtility.cs │ │ ├── RenderSettingsInspector.cs │ │ ├── RenderTextureEditor.cs │ │ ├── RendererEditorBase.cs │ │ ├── RendererLightingSettings.cs │ │ ├── ReorderableListWrapper.cs │ │ ├── RotationConstraintEditor.cs │ │ ├── ScaleConstraintEditor.cs │ │ ├── ScriptExecutionOrderInspector.cs │ │ ├── ScriptableObjectAssetEditor.cs │ │ ├── ScriptableRenderPipelineExtensionAttribute.deprecated.cs │ │ ├── ShaderGUI.cs │ │ ├── ShaderImporterInspector.cs │ │ ├── ShaderIncludePathAttribute.cs │ │ ├── ShaderInspector.cs │ │ ├── ShaderVariantCollectionInspector.cs │ │ ├── ShadowCascadeSplitGUI.cs │ │ ├── SkinnedMeshRendererEditor.cs │ │ ├── SkyboxPanoramicShaderGUI.cs │ │ ├── SkyboxProceduralShaderGUI.cs │ │ ├── SortingGroupEditor.cs │ │ ├── SortingLayerEditorUtility.cs │ │ ├── SpeedTree8ShaderGUI.cs │ │ ├── SpeedTree9ShaderGUI.cs │ │ ├── SpeedTreeMaterialInspector.cs │ │ ├── SpriteFrameInspector.cs │ │ ├── SpriteRendererEditor.cs │ │ ├── StandardParticlesShaderGUI.cs │ │ ├── StandardShaderGUI.cs │ │ ├── StreamingControllerInspector.cs │ │ ├── TabbedEditor.cs │ │ ├── TagManagerInspector.cs │ │ ├── TextMeshInspector.cs │ │ ├── Texture2DArrayInspector.cs │ │ ├── Texture2DArrayPreview.cs │ │ ├── Texture3DInspector.cs │ │ ├── Texture3DPreview.cs │ │ ├── TextureInspector.cs │ │ ├── TimeControl.cs │ │ ├── TimeManagerInspector.cs │ │ ├── TimelineControl.cs │ │ ├── TrailRendererEditor.cs │ │ ├── TransformInspector.cs │ │ ├── TransformRotationGUI.cs │ │ ├── TransformUtils.cs │ │ ├── UNetBehaviourInspector.cs │ │ ├── UnityEventDrawer.cs │ │ ├── VersionControlSettingsInspector.cs │ │ ├── VisualElements/ │ │ │ ├── ClippingPlanes.cs │ │ │ ├── MinMaxGradientField.cs │ │ │ ├── ObjectFieldWithPrompt.cs │ │ │ ├── ProjectSettings/ │ │ │ │ ├── BuiltInShaderElement.cs │ │ │ │ ├── ProjectSettingsElementWithSO.cs │ │ │ │ ├── ProjectSettingsScopes.cs │ │ │ │ ├── ProjectSettingsSection.cs │ │ │ │ ├── ProjectSettingsTitleBar.cs │ │ │ │ ├── TabButton.cs │ │ │ │ └── TabbedView.cs │ │ │ └── RenderingLayerMaskField.cs │ │ ├── WebCamTextureInspector.cs │ │ └── WindInspector.cs │ ├── InspectorUtility.cs │ ├── InteractionContext.bindings.cs │ ├── Internal/ │ │ └── MonoScripts.cs │ ├── InternalEditorUtility.bindings.cs │ ├── InternalEditorUtility.cs │ ├── InternalMeshUtil.bindings.cs │ ├── LODUtility.bindings.cs │ ├── LogEntries.bindings.cs │ ├── Macros/ │ │ ├── MacroEvaluator.cs │ │ └── MethodEvaluator.cs │ ├── MaterialProperty.cs │ ├── MaterialProperty.deprecated.cs │ ├── Media/ │ │ └── Bindings/ │ │ ├── MediaDecoder.bindings.cs │ │ └── MediaEncoder.bindings.cs │ ├── MemorySettings.bindings.cs │ ├── Menu.bindings.cs │ ├── MenuCommand.cs │ ├── MenuItem.cs │ ├── MeshUtility.bindings.cs │ ├── ModuleMetadata.bindings.cs │ ├── Modules/ │ │ ├── BeeBuildPostprocessor.cs │ │ ├── DefaultBuildProfileExtension.cs │ │ ├── DefaultBuildWindowExtension.cs │ │ ├── DefaultCompilationExtension.cs │ │ ├── DefaultPlatformSupportModule.cs │ │ ├── DefaultPlayerSettingsEditorExtension.cs │ │ ├── DefaultPluginImporterExtension.cs │ │ ├── DefaultTextureImportSettingsExtension.cs │ │ ├── DerivedBuildTargetExtensionsProvider.cs │ │ ├── IDerivedBuildTargetExtensions.cs │ │ ├── IPostStrippingModuleAdder.cs │ │ ├── IPreStrippingModuleAdder.cs │ │ ├── ModuleManager.cs │ │ ├── PlatformSupportModule.cs │ │ └── PostStrippingModuleAdder.cs │ ├── MonoCecil/ │ │ ├── FileOpenInfo.cs │ │ ├── IFileOpenInfo.cs │ │ ├── IMonoCecilHelper.cs │ │ └── MonoCecilHelper.cs │ ├── MonoScript.bindings.cs │ ├── MuscleClipUtility.bindings.cs │ ├── Networking/ │ │ └── PlayerConnection/ │ │ ├── AttachToPlayerGUI.cs │ │ ├── ConnectionDropDown.cs │ │ └── EditorConnection.cs │ ├── OSUtil.bindings.cs │ ├── ObjectFactory.bindings.cs │ ├── ObjectListArea.cs │ ├── ObjectListGroup.cs │ ├── ObjectListLocalGroup.cs │ ├── ObjectNames.bindings.cs │ ├── ObjectNames.cs │ ├── ObjectPool/ │ │ └── PoolManager.cs │ ├── ObjectSelector.cs │ ├── ObjectTreeForSelector.cs │ ├── OrderedCallbackCollection.cs │ ├── Overlays/ │ │ ├── ICreateToolbar.cs │ │ ├── IMGUIOverlay.cs │ │ ├── Overlay.cs │ │ ├── OverlayAttribute.cs │ │ ├── OverlayCanvas.cs │ │ ├── OverlayCanvasesData.cs │ │ ├── OverlayContainer.cs │ │ ├── OverlayContainerDropZone.cs │ │ ├── OverlayContainerInsertDropZone.cs │ │ ├── OverlayDockArea.cs │ │ ├── OverlayDragger.cs │ │ ├── OverlayDropZone.cs │ │ ├── OverlayDropZoneBase.cs │ │ ├── OverlayGhostDropZone.cs │ │ ├── OverlayInsertIndicator.cs │ │ ├── OverlayMenuItem.cs │ │ ├── OverlayPlacement.cs │ │ ├── OverlayPopup.cs │ │ ├── OverlayPopupWindow.cs │ │ ├── OverlayPreset.cs │ │ ├── OverlayPresetManager.cs │ │ ├── OverlayResizer.cs │ │ ├── OverlayToolbar.cs │ │ ├── OverlayUtilities.cs │ │ ├── SaveOverlayPreset.cs │ │ ├── ToolbarDropZone.cs │ │ └── ToolbarOverlay.cs │ ├── PackageManagerUtilityInternal.cs │ ├── PackageUtility.bindings.cs │ ├── Performance.bindings.cs │ ├── PerformanceTools/ │ │ ├── FrameDebugger.bindings.cs │ │ ├── FrameDebugger.cs │ │ ├── FrameDebuggerData.cs │ │ ├── FrameDebuggerEventDetailsView.cs │ │ ├── FrameDebuggerEventDisplayData.cs │ │ ├── FrameDebuggerHelper.cs │ │ ├── FrameDebuggerStyles.cs │ │ ├── FrameDebuggerToolbarView.cs │ │ └── FrameDebuggerTreeView.cs │ ├── Picking/ │ │ ├── PickingIncludeExcludeList.cs │ │ └── PickingObject.cs │ ├── PlatformSupport/ │ │ ├── PlatformIconField.cs │ │ ├── PlayerSettingsPlatformIcons.bindings.cs │ │ ├── ProvisioningProfile.cs │ │ ├── ProvisioningProfileGUI.cs │ │ └── ReorderableTextureList.cs │ ├── PlayModeView/ │ │ ├── PlayModeAnalytics.cs │ │ ├── PlayModeView.cs │ │ └── PlayModeWindow.cs │ ├── Playables/ │ │ ├── PlayableOutputEditorExtensions.cs │ │ └── Playables.bindings.cs │ ├── PlayerConnectionLogReceiver.cs │ ├── PlayerPrefsSettings.cs │ ├── PlayerSettings.bindings.cs │ ├── PlayerSettings.deprecated.cs │ ├── PlayerSettingsAndroid.bindings.cs │ ├── PlayerSettingsDefaultTextureCompressionHandler.cs │ ├── PlayerSettingsEmbeddedLinux.bindings.cs │ ├── PlayerSettingsFacebook.bindings.cs │ ├── PlayerSettingsIOS.bindings.cs │ ├── PlayerSettingsLumin.bindings.cs │ ├── PlayerSettingsMacOS.bindings.cs │ ├── PlayerSettingsPS4.bindings.cs │ ├── PlayerSettingsQNX.bindings.cs │ ├── PlayerSettingsSplashScreen.bindings.cs │ ├── PlayerSettingsSplashScreen.cs │ ├── PlayerSettingsSwitch.bindings.cs │ ├── PlayerSettingsSwitch.deprecated.bindings.cs │ ├── PlayerSettingsTVOS.bindings.cs │ ├── PlayerSettingsVisionOS.bindings.cs │ ├── PlayerSettingsVulkan.bindings.cs │ ├── PlayerSettingsWSA.bindings.cs │ ├── PlayerSettingsWSA.cs │ ├── PlayerSettingsWebGL.bindings.cs │ ├── PlayerSettingsXboxOne.bindings.cs │ ├── PluginDesc.cs │ ├── Plugins/ │ │ └── PluginsHelper.cs │ ├── PointCreator.cs │ ├── PointEditor.cs │ ├── PolygonEditor.bindings.cs │ ├── PostprocessScene.cs │ ├── Prefabs/ │ │ ├── PrefabFamilyPopup.cs │ │ ├── PrefabImporter.bindings.cs │ │ ├── PrefabImporterEditor.cs │ │ ├── PrefabInstanceChangedListener.cs │ │ ├── PrefabOverrides/ │ │ │ ├── PrefabOverride.cs │ │ │ ├── PrefabOverridesTreeView.cs │ │ │ ├── PrefabOverridesUtility.cs │ │ │ ├── PrefabOverridesWindow.cs │ │ │ └── TransformVisitor.cs │ │ ├── PrefabReplaceUtility.cs │ │ ├── PrefabUtility.bindings.cs │ │ ├── PrefabUtility.cs │ │ └── PropertyModification.bindings.cs │ ├── PreferencesWindow/ │ │ ├── AssetPipelinePreferences.cs │ │ ├── CollectionsPreferences.cs │ │ ├── PreferencesItem.cs │ │ └── PreferencesSettingsProviders.cs │ ├── PresetLibraries/ │ │ ├── ColorPresetLibrary.cs │ │ ├── CurvePresetLibrary.cs │ │ ├── CurvePresetsContentsForPopupWindow.cs │ │ ├── DoubleCurvePresetLibrary.cs │ │ ├── DoubleCurvePresetsContentsForPopupWindow.cs │ │ ├── GradientPresetLibrary.cs │ │ ├── PopupWindowContentForNewLibrary.cs │ │ ├── PresetLibrary.cs │ │ ├── PresetLibraryEditor.cs │ │ ├── PresetLibraryEditorMenu.cs │ │ ├── PresetLibraryManager.cs │ │ └── ScriptableObjectSaveLoadHelper.cs │ ├── Progress/ │ │ ├── AssemblyInfo.cs │ │ ├── Progress.bindings.cs │ │ ├── Progress.cs │ │ └── ProgressOrderComparer.cs │ ├── ProgressScope.bindings.cs │ ├── ProgressScope.cs │ ├── ProjectBrowser/ │ │ ├── AssetClipboardUtility.cs │ │ ├── CachedFilteredHierachy.cs │ │ ├── GlobSearchUtilities.cs │ │ ├── ProjectBrowser.cs │ │ ├── ProjectBrowserColumnOne.cs │ │ ├── ProjectBrowserPopups.cs │ │ ├── ProjectWindowUtil.cs │ │ ├── SavedSearchFilter.cs │ │ ├── SearchFilter.cs │ │ └── SearchableEditorWindow.cs │ ├── ProjectTemplateWindow.cs │ ├── RegistryUtil.bindings.cs │ ├── RemoteInput/ │ │ └── Remoting.bindings.cs │ ├── RemoveLegacyMenuItems.cs │ ├── RenderDoc/ │ │ ├── RenderDoc.bindings.cs │ │ └── RenderDocUtil.cs │ ├── RenderPipelineGlobalSettingsEditor.cs │ ├── RenderPipelineGraphicsSettingsCollectionPropertyDrawer.cs │ ├── RenderPipelineGraphicsSettingsContextMenu.cs │ ├── RenderPipelineResourcesEditorUtils.cs │ ├── RuntimeInitializeOnLoadManager.bindings.cs │ ├── SJSON.bindings.cs │ ├── SaveAssetsProcessor.cs │ ├── SavedGUIState.bindings.cs │ ├── SceneHierarchy.cs │ ├── SceneHierarchyHooks.cs │ ├── SceneHierarchySortingWindow.cs │ ├── SceneHierarchyStageHandling.cs │ ├── SceneHierarchyWindow.cs │ ├── SceneManagement/ │ │ ├── EditorSceneManager.cs │ │ ├── SceneSetup.cs │ │ └── StageManager/ │ │ ├── AssetEvents.cs │ │ ├── AvatarConfigurationStage.cs │ │ ├── BreadcrumbBar.cs │ │ ├── MainStage.cs │ │ ├── MainStageHierarchyState.cs │ │ ├── PrefabStage/ │ │ │ ├── PrefabStage.cs │ │ │ ├── PrefabStage.deprecated.cs │ │ │ └── PrefabStageUtility.cs │ │ ├── PrefabStageHierarchyState.cs │ │ ├── PreviewSceneStage.cs │ │ ├── SceneViewCameraState.cs │ │ ├── Stage.cs │ │ ├── StageNavigationHistory.cs │ │ ├── StageNavigationManager.cs │ │ ├── StageUtility.bindings.cs │ │ └── StageUtility.cs │ ├── SceneModeWindows/ │ │ ├── DefaultLightingExplorerExtension.cs │ │ ├── LightingExplorerExtensionAttribute.deprecated.cs │ │ ├── LightingExplorerTab.cs │ │ ├── LightingExplorerWindow.cs │ │ ├── LightingWindow.cs │ │ ├── LightingWindowBakeSettings.cs │ │ ├── LightingWindowEnvironmentTab.cs │ │ ├── LightingWindowLightingTab.cs │ │ ├── LightingWindowLightmapPreviewTab.cs │ │ ├── LightingWindowTab.cs │ │ ├── LightmapPreviewWindow.cs │ │ ├── OcclusionCullingWindow.cs │ │ └── SceneModeUtility.cs │ ├── SceneObjectIdentifier.bindings.cs │ ├── SceneView/ │ │ ├── CameraFlyModeContext.cs │ │ ├── PickingShortcutContext.cs │ │ ├── RectSelection.cs │ │ ├── SceneOrientationGizmo.cs │ │ ├── SceneView.cs │ │ ├── SceneViewGrid.cs │ │ ├── SceneViewMotion.cs │ │ ├── SceneViewOverlay.cs │ │ ├── SceneViewOverlays.cs │ │ ├── SceneViewPicking.cs │ │ ├── SceneViewPiercingMenu.cs │ │ ├── SceneViewStageHandling.cs │ │ ├── SceneViewToolbarStyles.cs │ │ ├── SceneViewToolbars.cs │ │ ├── SceneViewViewpoint.cs │ │ └── Viewpoint/ │ │ ├── CameraViewpoint.cs │ │ ├── ViewpointAPI.cs │ │ └── ViewpointProxyTypeCache.cs │ ├── SceneVisibility/ │ │ ├── SceneVisibilityHierarchyGUI.cs │ │ ├── SceneVisibilityManager.cs │ │ └── SceneVisibilityState.bindings.cs │ ├── ScriptEditorUtility.cs │ ├── ScriptReloadProperties.cs │ ├── ScriptableSingleton.cs │ ├── ScriptableSingletonDictionary.cs │ ├── ScriptableWizard.cs │ ├── Scripting/ │ │ ├── APIUpdater/ │ │ │ ├── APIUpdaterAssemblyHelper.cs │ │ │ ├── APIUpdaterHelper.cs │ │ │ ├── APIUpdaterLogger.cs │ │ │ ├── APIUpdaterManager.bindings.cs │ │ │ └── AssemblyDependencyGraph.cs │ │ ├── AsyncInstantiateManager.cs │ │ ├── Compilers/ │ │ │ ├── CommandLineFormatter.cs │ │ │ ├── CompilerBase.cs │ │ │ ├── CompilerOutputParserBase.cs │ │ │ ├── CompilerSpecificReponseFiles.cs │ │ │ ├── Il2CppOutputParser.cs │ │ │ ├── MicrosoftCSharpCompilerOutputParser.cs │ │ │ ├── MicrosoftCSharpResponseFileProvider.cs │ │ │ ├── MicrosoftResponseFileParser.cs │ │ │ ├── ResponseFileProvider.cs │ │ │ ├── ScriptCompilerBase.cs │ │ │ └── UWPReferences.cs │ │ ├── ManagedDebugger.bindings.cs │ │ ├── NativeClassExtensionUtilities.cs │ │ ├── ScriptCompilation/ │ │ │ ├── AssemblyBuilder.cs │ │ │ ├── AssemblyDefinitionException.cs │ │ │ ├── AssemblyFlags.cs │ │ │ ├── AssemblyGraphBuilder.cs │ │ │ ├── AssemblyGraphBuilderFactory.cs │ │ │ ├── AssetPath.cs │ │ │ ├── AssetPathMetaData.cs │ │ │ ├── AssetPathVersionMetaData.cs │ │ │ ├── AutoReferencedPackageAssemblies.cs │ │ │ ├── BeeDriver/ │ │ │ │ ├── BeeScriptCompilation.cs │ │ │ │ ├── PotentiallyUpdatableErrorMessages.cs │ │ │ │ ├── UnityBeeDriver.cs │ │ │ │ ├── UnityBeeDriverProfilerSession.cs │ │ │ │ ├── UnityScriptUpdater.cs │ │ │ │ ├── UnityScriptUpdaterConsentAPI.cs │ │ │ │ └── UnitySourceFileUpdatersResultHandler.cs │ │ │ ├── CompilationPipeline.cs │ │ │ ├── CompilationPipelineCommonHelper.cs │ │ │ ├── CompilationSetupErrorsTracker.cs │ │ │ ├── CompilerMessage.cs │ │ │ ├── CustomScriptAssembly.cs │ │ │ ├── CustomScriptAssemblyReference.cs │ │ │ ├── DefineConstraintsHelper.cs │ │ │ ├── EditorBuildRules.cs │ │ │ ├── EditorCompilation.cs │ │ │ ├── EditorCompilationInterface.cs │ │ │ ├── EditorScriptCompilationOptions.cs │ │ │ ├── EnumerableExtensions.cs │ │ │ ├── ExpressionNotValidException.cs │ │ │ ├── ExpressionTypeFactory.cs │ │ │ ├── ExpressionTypeKey.cs │ │ │ ├── GUIDReference.cs │ │ │ ├── ILPostProcessingProgram.cs │ │ │ ├── IVersion.cs │ │ │ ├── LoadingAssemblyDefinition.cs │ │ │ ├── MonoLibraryHelpers.cs │ │ │ ├── PathMultidimensionalDivisionTree.cs │ │ │ ├── PlatformSupportModuleHelpers.cs │ │ │ ├── PostProcessorOutputParser.cs │ │ │ ├── PrecompiledAssembly.cs │ │ │ ├── RoslynAnalyzers.cs │ │ │ ├── SafeMode.cs │ │ │ ├── ScriptAssembly.cs │ │ │ ├── SemVersion.cs │ │ │ ├── TargetAssembly.cs │ │ │ ├── TestRunnerHelpers.cs │ │ │ ├── UnityCodeGenHelpers.cs │ │ │ ├── UnitySpecificCompilerMessageProcessor.cs │ │ │ ├── UnityVersion.cs │ │ │ ├── Utility.cs │ │ │ ├── VersionDefineExpression.cs │ │ │ ├── VersionDefinesConsoleLogs.bindings.cs │ │ │ ├── VersionRanges.cs │ │ │ ├── VersionRangesEvaluators.cs │ │ │ └── VersionRangesFactory.cs │ │ └── ScriptCompilers.cs │ ├── ScriptingDefinesHelper.cs │ ├── Search/ │ │ ├── AdvancedObjectSelectorAttribute.cs │ │ ├── LegacyImplementations.cs │ │ ├── ObjectSelector.Deprecated.cs │ │ ├── ObjectSelectorSearch.cs │ │ ├── OpenSearchHelper.cs │ │ ├── Project.Deprecated.cs │ │ ├── ProjectSearch.cs │ │ ├── Scene.Deprecated.cs │ │ ├── SceneSearch.cs │ │ └── SearchService.cs │ ├── SearchUtility.cs │ ├── Selection/ │ │ ├── ActiveEditorTracker.bindings.cs │ │ ├── Selection.bindings.cs │ │ └── Selection.cs │ ├── SelectionCommands/ │ │ └── SelectionCommands.cs │ ├── SerializationDebug.bindings.cs │ ├── SerializedObject.bindings.cs │ ├── SerializedProperty/ │ │ ├── SerializedPropertyFilters.cs │ │ ├── SerializedPropertyTable.cs │ │ └── SerializedPropertyTreeView.cs │ ├── SerializedProperty.bindings.cs │ ├── SerializedPropertyExtensions.cs │ ├── Settings/ │ │ ├── Providers/ │ │ │ └── AssetSettingsProvider.cs │ │ ├── RenderPipelines/ │ │ │ └── RenderPipelineGraphicsSettingsManager.cs │ │ ├── SettingsProvider.cs │ │ ├── SettingsProviderAttribute.cs │ │ ├── SettingsService.cs │ │ ├── SettingsTreeView.cs │ │ └── SettingsWindow.cs │ ├── Settings.cs │ ├── SettingsWindow/ │ │ ├── FogEditor.cs │ │ ├── LightingEditor.cs │ │ └── OtherRenderingEditor.cs │ ├── ShaderUtil.bindings.cs │ ├── ShaderUtil.bindings.deprecated.cs │ ├── ShaderUtil.cs │ ├── Shaders/ │ │ ├── MaterialHierarchyPopup.cs │ │ ├── ShaderKeywordFilterAttributes.cs │ │ ├── ShaderKeywordFilterConstraintAttributes.cs │ │ ├── ShaderKeywordFilterData.cs │ │ └── ShaderKeywordFilterUtil.cs │ ├── SpeedTreeMaterialFixer.cs │ ├── SplashScreenLogo.cs │ ├── SpritePacker.bindings.cs │ ├── Sprites/ │ │ ├── SpriteUtility.cs │ │ └── SpriteUtilityWindow.cs │ ├── SpritesEditor.bindings.cs │ ├── StateMachine.bindings.cs │ ├── StateMachineBehaviourContext.bindings.cs │ ├── StaticEditorFlags.cs │ ├── StaticOcclusionCulling.bindings.cs │ ├── TagManager.bindings.cs │ ├── TerrainEditor/ │ │ └── TerrainInspectorUtil.bindings.cs │ ├── Text/ │ │ ├── BlurryTextMappingTable.cs │ │ ├── EditorFontAssetFactory.cs │ │ └── EditorTextSettings.cs │ ├── Tools/ │ │ ├── BuiltinTools.cs │ │ ├── EditorAction.cs │ │ ├── EditorActionTool.cs │ │ ├── EditorTool.cs │ │ ├── EditorToolAttributes.cs │ │ ├── EditorToolCache.cs │ │ ├── EditorToolContext.cs │ │ ├── EditorToolGUI.cs │ │ ├── EditorToolManager.cs │ │ ├── EditorToolSettingsOverlay.cs │ │ ├── EditorToolUtility.cs │ │ ├── EditorTools.deprecated.cs │ │ ├── GameObjectToolContext.cs │ │ ├── IEditor.cs │ │ ├── PrimitiveColliderTool.cs │ │ ├── ToolEntry.cs │ │ ├── ToolManager.cs │ │ ├── ToolShortcutContext.cs │ │ ├── ToolVariantPrefs.cs │ │ ├── Tools.cs │ │ └── TransformManipulator.cs │ ├── TooltipView/ │ │ └── TooltipView.cs │ ├── Tuple.cs │ ├── Tutorial/ │ │ ├── Highlighter.bindings.cs │ │ └── Highlighter.cs │ ├── TypeCache.bindings.cs │ ├── TypeCache.cs │ ├── TypeSystem/ │ │ ├── UnityType.bindings.cs │ │ └── UnityType.cs │ ├── UIElements/ │ │ ├── BaseLiveReloadAssetTracker.cs │ │ ├── Bindings/ │ │ │ ├── BindingStyleHelpers.cs │ │ │ ├── BindingsInterface.cs │ │ │ └── SerializedPropertyBindingEvents.cs │ │ ├── Controls/ │ │ │ ├── ColorField.cs │ │ │ ├── CurveField.cs │ │ │ ├── EnumFlagsField.cs │ │ │ ├── GradientField.cs │ │ │ ├── LayerField.cs │ │ │ ├── LayerMaskField.cs │ │ │ ├── Mask64Field.cs │ │ │ ├── MaskField.cs │ │ │ ├── ObjectField.cs │ │ │ ├── PropertyField.cs │ │ │ ├── TagField.cs │ │ │ └── Toolbar/ │ │ │ ├── IToolbarMenuElement.cs │ │ │ ├── SearchFieldBase.cs │ │ │ ├── Toolbar.cs │ │ │ ├── ToolbarBreadcrumbs.cs │ │ │ ├── ToolbarButton.cs │ │ │ ├── ToolbarMenu.cs │ │ │ ├── ToolbarPopupSearchField.cs │ │ │ ├── ToolbarSearchField.cs │ │ │ ├── ToolbarSpacer.cs │ │ │ └── ToolbarToggle.cs │ │ ├── DefaultMainToolbar.cs │ │ ├── Drawers/ │ │ │ └── Internal/ │ │ │ ├── DropdownOptionListItem.cs │ │ │ └── UnityEventItem.cs │ │ ├── EditorFocusMonitor.cs │ │ ├── EditorMenuExtensions.cs │ │ ├── EditorWindowPersistentViewData.cs │ │ ├── GenericDropdownMenuWindowContent.cs │ │ ├── Inspector/ │ │ │ ├── EditorElement.cs │ │ │ ├── IEditorElementDecorator.cs │ │ │ └── InspectorElement.cs │ │ ├── SerializableJsonDictionary.cs │ │ ├── StyleSheets/ │ │ │ ├── StyleSheetEditor.cs │ │ │ ├── StyleSheetImportErrors.cs │ │ │ ├── StyleSheetImportGlossary.cs │ │ │ ├── StyleSheetImporter.cs │ │ │ ├── StyleSheetImporterImpl.cs │ │ │ ├── StyleSheetResourceUtil.cs │ │ │ ├── ThemeAssetDefinitionState.cs │ │ │ ├── ThemeRegistry.cs │ │ │ ├── ThemeStyleSheetImporter.cs │ │ │ ├── ThemeStyleSheetImporterEditor.cs │ │ │ └── URIHelpers.cs │ │ ├── UIBuildAnalyticsEvent.cs │ │ └── UIElementsEditorUtility.cs │ ├── Undo/ │ │ ├── EditorObjectChangeEvents.cs │ │ ├── Undo.bindings.cs │ │ ├── UndoHistoryWindow.cs │ │ ├── UndoSerializationWindow.cs │ │ └── UndoWindow.cs │ ├── Undo.cs │ ├── UnityConnect/ │ │ ├── CloudProjectSettings.cs │ │ ├── CloudProjectSettingsEventManager.cs │ │ ├── CoppaCompliance.cs │ │ ├── Network/ │ │ │ ├── OrganizationRequestResponse.cs │ │ │ ├── ProjectRequestResponse.cs │ │ │ ├── UnityConnectRequests.cs │ │ │ ├── UnityConnectWebRequestException.cs │ │ │ ├── UnityConnectWebRequestUtils.cs │ │ │ └── UserRequestResponse.cs │ │ ├── ServiceToken/ │ │ │ ├── Caching/ │ │ │ │ ├── GenesisAndServiceTokenCaching.cs │ │ │ │ ├── IGenesisAndServiceTokenCaching.cs │ │ │ │ ├── JsonWebToken.cs │ │ │ │ └── Tokens.cs │ │ │ ├── ServiceToken.cs │ │ │ └── TokenExchange/ │ │ │ ├── ITokenExchange.cs │ │ │ ├── Model/ │ │ │ │ ├── TokenExchangeRequest.cs │ │ │ │ └── TokenExchangeResponse.cs │ │ │ └── TokenExchange.cs │ │ ├── Services/ │ │ │ ├── EditorProjectAccess.bindings.cs │ │ │ ├── ServicesConfiguration.cs │ │ │ └── ServicesUtils.cs │ │ ├── UnityConnect.bindings.cs │ │ ├── Uri/ │ │ │ ├── IUnityConnectRequestUriProvider.cs │ │ │ └── UnityConnectRequestUriProvider.cs │ │ └── Utils/ │ │ └── AsyncUtils.cs │ ├── UnityStats.bindings.cs │ ├── Unsupported.bindings.cs │ ├── Unwrapping.bindings.cs │ ├── Utils/ │ │ ├── AssemblyReferenceChecker.cs │ │ ├── DirectoryExtensions.cs │ │ ├── EditorExtensionMethods.cs │ │ ├── EnumUtility.cs │ │ ├── IDeviceUtils.cs │ │ ├── IconUtility.bindings.cs │ │ ├── LightProbeGroupSelection.cs │ │ ├── ManagedProgram.cs │ │ ├── MathUtils.cs │ │ ├── MenuUtils.cs │ │ ├── MetroCertificatePasswordWindow.cs │ │ ├── MetroCreateTestCertificateWindow.cs │ │ ├── MonoInstallationFinder.cs │ │ ├── NetCoreProgram.cs │ │ ├── NetCoreRunProgram.cs │ │ ├── NetStandardFinder.cs │ │ ├── Paths.cs │ │ ├── PerformanceChecks.cs │ │ ├── Pram.cs │ │ ├── ProcessOutputStreamReader.cs │ │ ├── Program.cs │ │ ├── ProgressBarUtils.cs │ │ ├── SearchUtils.cs │ │ ├── SimpleProfiler.cs │ │ ├── StateCache.cs │ │ ├── TickTimerHelper.cs │ │ ├── TimeAgo.cs │ │ ├── TimeHelper.cs │ │ ├── UnityEventTools.cs │ │ └── WebURLs.bindings.cs │ ├── VersionControl/ │ │ ├── Common/ │ │ │ ├── IIconOverlayExtension.cs │ │ │ ├── IInspectorWindowExtension.cs │ │ │ ├── IPopupMenuExtension.cs │ │ │ ├── ISettingsInspectorExtension.cs │ │ │ ├── VCAsset.cs │ │ │ ├── VCAssetList.cs │ │ │ ├── VCAssetModificationHooks.cs │ │ │ ├── VCChangeSet.cs │ │ │ ├── VCChangeSets.cs │ │ │ ├── VCMessage.cs │ │ │ ├── VCProvider.cs │ │ │ ├── VCTask.cs │ │ │ ├── VCUtils.cs │ │ │ ├── VersionControlAttribute.cs │ │ │ ├── VersionControlDescriptor.cs │ │ │ ├── VersionControlManager.cs │ │ │ └── VersionControlObject.cs │ │ ├── UI/ │ │ │ ├── VCListControl.cs │ │ │ ├── VCListItem.cs │ │ │ ├── VCMenuChange.cs │ │ │ ├── VCMenuPending.cs │ │ │ ├── VCMenuProject.cs │ │ │ ├── VCOverlay.cs │ │ │ ├── VCProjectHooks.cs │ │ │ ├── VCWindowChange.cs │ │ │ ├── VCWindowCheckoutFailure.cs │ │ │ ├── VCWindowPending.cs │ │ │ ├── VCWindowResolve.cs │ │ │ └── VCWindowRevert.cs │ │ ├── VCAsset.bindings.cs │ │ ├── VCChangeSet.bindings.cs │ │ ├── VCCustomCommand.bindings.cs │ │ ├── VCMessage.bindings.cs │ │ ├── VCPlugin.bindings.cs │ │ ├── VCProvider.bindings.cs │ │ ├── VCTask.bindings.cs │ │ └── VersionControlManager.bindings.cs │ ├── VersionControlSettings.bindings.cs │ ├── VertexChannelCompressionFlags.cs │ ├── View.cs │ ├── VisualStudioIntegration/ │ │ └── FileIO.cs │ ├── VisualStudioUtil.bindings.cs │ ├── WindowAction.cs │ └── WindowBackendManager.cs ├── External/ │ ├── JsonParsers/ │ │ └── MiniJson/ │ │ └── MiniJSON.cs │ ├── NiceIO/ │ │ └── NiceIO.cs │ ├── baselib/ │ │ └── baselib/ │ │ └── CSharp/ │ │ ├── BaselibNativeLibrary.cs │ │ ├── BindingsUnity/ │ │ │ ├── Baselib_DynamicLibrary.gen.binding.cs │ │ │ ├── Baselib_ErrorCode.gen.binding.cs │ │ │ ├── Baselib_ErrorState.gen.binding.cs │ │ │ ├── Baselib_FileIO.gen.binding.cs │ │ │ ├── Baselib_HostnameLookup.gen.binding.cs │ │ │ ├── Baselib_Memory.gen.binding.cs │ │ │ ├── Baselib_NetworkAddress.gen.binding.cs │ │ │ ├── Baselib_RegisteredNetwork.gen.binding.cs │ │ │ ├── Baselib_Socket.gen.binding.cs │ │ │ ├── Baselib_SourceLocation.gen.binding.cs │ │ │ ├── Baselib_SystemFutex.gen.binding.cs │ │ │ ├── Baselib_SystemSemaphore.gen.binding.cs │ │ │ ├── Baselib_Thread.gen.binding.cs │ │ │ ├── Baselib_ThreadLocalStorage.gen.binding.cs │ │ │ ├── Baselib_Timer.gen.binding.cs │ │ │ └── Baselib_WakeupFallbackStrategy.gen.binding.cs │ │ ├── Error.cs │ │ └── ManualBindings.cs │ ├── il2cpp/ │ │ └── builds/ │ │ ├── Il2CppEditorIntegration/ │ │ │ └── SharedWithEditor.cs │ │ └── LinkerEditorIntegration/ │ │ └── SharedWithEditor.cs │ └── unitytls/ │ └── builds/ │ └── CSharp/ │ ├── BindingsUnity/ │ │ └── TLSAgent.gen.bindings.cs │ └── UnityTLSNativeLibrary.cs ├── LICENSE.md ├── Modules/ │ ├── AI/ │ │ ├── AssemblyInfo.cs │ │ ├── Builder/ │ │ │ └── NavMeshBuilder.bindings.cs │ │ ├── Components/ │ │ │ ├── NavMeshAgent.bindings.cs │ │ │ ├── NavMeshObstacle.bindings.cs │ │ │ ├── OffMeshLink.bindings.cs │ │ │ └── OffMeshLink.deprecated.cs │ │ ├── NavMesh/ │ │ │ ├── NavMesh.bindings.cs │ │ │ └── NavMesh.deprecated.cs │ │ ├── NavMeshExperimental.bindings.cs │ │ ├── NavMeshPath.bindings.cs │ │ └── Public/ │ │ ├── NavMeshBindingTypes.bindings.cs │ │ └── NavMeshBuildSettings.bindings.cs │ ├── AIEditor/ │ │ ├── Builder/ │ │ │ └── NavMeshBuilderEditor.deprecated.cs │ │ ├── Utilities/ │ │ │ ├── NavMeshEditorHelpers.bindings.cs │ │ │ └── NavMeshEditorHelpers.cs │ │ └── Visualization/ │ │ └── NavMeshVisualizationSettings.bindings.cs │ ├── AR/ │ │ └── ARCore/ │ │ └── ScriptBindings/ │ │ └── ARCore.bindings.cs │ ├── Accessibility/ │ │ ├── AssemblyInfo.cs │ │ ├── Bindings/ │ │ │ ├── AccessibilityAction.bindings.cs │ │ │ ├── AccessibilityManager.bindings.cs │ │ │ ├── AccessibilityNodeData.bindings.cs │ │ │ ├── AccessibilityNodeManager.bindings.cs │ │ │ ├── AccessibilityNotificationContext.bindings.cs │ │ │ └── AccessibilitySettings.bindings.cs │ │ ├── Managed/ │ │ │ ├── AccessibilitySettings.cs │ │ │ ├── AssistiveSupport.cs │ │ │ ├── Hierarchy/ │ │ │ │ ├── AccessibilityHierarchy.cs │ │ │ │ └── AccessibilityNode.cs │ │ │ ├── IAccessibilityNotificationDispatcher.cs │ │ │ └── Services/ │ │ │ ├── AccessibilityHierarchyService.cs │ │ │ ├── IService.cs │ │ │ └── ServiceManager.cs │ │ └── VisionUtility.cs │ ├── AccessibilityEditor/ │ │ └── Managed/ │ │ ├── AccessibilityEditor.cs │ │ ├── AccessibilityHierarchyTreeView.cs │ │ ├── AccessibilityHierarchyViewModel.cs │ │ ├── AccessibilityHierarchyViewer.cs │ │ ├── AccessibilityHierarchyViewerWindow.cs │ │ ├── SearchableLabel.cs │ │ └── TreeViewSearchBar.cs │ ├── AdaptivePerformance/ │ │ └── Editor/ │ │ └── AdaptivePerformanceInstaller.cs │ ├── AndroidJNI/ │ │ ├── AndroidApplication.bindings.cs │ │ ├── AndroidApplicationExitInfo.cs │ │ ├── AndroidAssetPacks.bindings.cs │ │ ├── AndroidDevice.bindings.cs │ │ ├── AndroidDiagnosticsReporting.cs │ │ ├── AndroidGame.bindings.cs │ │ ├── AndroidGame.cs │ │ ├── AndroidInsets.bindings.cs │ │ ├── AndroidJNI.bindings.cs │ │ ├── AndroidJNISafe.cs │ │ ├── AndroidJava.cs │ │ ├── AndroidPermissions.cs │ │ ├── AssemblyInfo.cs │ │ └── Configuration/ │ │ ├── AndroidColorModeHdr.cs │ │ ├── AndroidColorModeWideColorGamut.cs │ │ ├── AndroidConfiguration.cs │ │ ├── AndroidHardwareKeyboardHidden.cs │ │ ├── AndroidKeyboard.cs │ │ ├── AndroidKeyboardHidden.cs │ │ ├── AndroidNavigation.cs │ │ ├── AndroidNavigationHidden.cs │ │ ├── AndroidOrientation.cs │ │ ├── AndroidScreenLayoutDirection.cs │ │ ├── AndroidScreenLayoutLong.cs │ │ ├── AndroidScreenLayoutRound.cs │ │ ├── AndroidScreenLayoutSize.cs │ │ ├── AndroidTouchScreen.cs │ │ ├── AndroidUiModeNight.cs │ │ └── AndroidUiModeType.cs │ ├── Animation/ │ │ ├── Managed/ │ │ │ ├── Animation.deprecated.cs │ │ │ ├── AnimationPlayableBinding.cs │ │ │ ├── AnimationPlayableUtilities.cs │ │ │ ├── Animator.deprecated.cs │ │ │ ├── DiscreteEvaluationAttribute.cs │ │ │ ├── IAnimationClipSource.cs │ │ │ ├── IAnimationJob.cs │ │ │ ├── IAnimationJobPlayable.cs │ │ │ ├── IAnimationPreviewable.cs │ │ │ ├── IAnimationWindowPreview.cs │ │ │ ├── NotKeyableAttribute.cs │ │ │ ├── ProcessAnimationJobStruct.cs │ │ │ └── StateMachineBehaviour.cs │ │ └── ScriptBindings/ │ │ ├── AimConstraint.bindings.cs │ │ ├── Animation.bindings.cs │ │ ├── AnimationClip.bindings.cs │ │ ├── AnimationClipPlayable.bindings.cs │ │ ├── AnimationHumanStream.bindings.cs │ │ ├── AnimationLayerMixerPlayable.bindings.cs │ │ ├── AnimationMixerPlayable.bindings.cs │ │ ├── AnimationMotionXToDeltaPlayable.bindings.cs │ │ ├── AnimationOffsetPlayable.bindings.cs │ │ ├── AnimationPlayableExtensions.bindings.cs │ │ ├── AnimationPlayableGraphExtensions.bindings.cs │ │ ├── AnimationPlayableOutput.bindings.cs │ │ ├── AnimationPlayableOutputExtensions.bindings.cs │ │ ├── AnimationPosePlayable.bindings.cs │ │ ├── AnimationRemoveScalePlayable.bindings.cs │ │ ├── AnimationScriptPlayable.bindings.cs │ │ ├── AnimationStream.bindings.cs │ │ ├── AnimationStreamHandles.bindings.cs │ │ ├── Animator.bindings.cs │ │ ├── AnimatorControllerParameter.bindings.cs │ │ ├── AnimatorControllerPlayable.bindings.cs │ │ ├── AnimatorJobExtensions.bindings.cs │ │ ├── AnimatorOverrideController.bindings.cs │ │ ├── AnimatorUtility.bindings.cs │ │ ├── Avatar.bindings.cs │ │ ├── AvatarBuilder.bindings.cs │ │ ├── AvatarMask.bindings.cs │ │ ├── BoundProperty.bindings.cs │ │ ├── Constraint.bindings.cs │ │ ├── GenericBinding.bindings.cs │ │ ├── HumanPoseHandler.bindings.cs │ │ ├── HumanTrait.bindings.cs │ │ ├── LookAtConstraint.bindings.cs │ │ ├── Motion.bindings.cs │ │ ├── MuscleHandle.bindings.cs │ │ ├── ParentConstraint.bindings.cs │ │ └── RuntimeAnimatorController.bindings.cs │ ├── AssetBundle/ │ │ └── Managed/ │ │ ├── AssemblyInfo.cs │ │ ├── AssetBundle.bindings.cs │ │ ├── AssetBundle.deprecated.cs │ │ ├── AssetBundleCreateRequest.bindings.cs │ │ ├── AssetBundleLoadingCache.bindings.cs │ │ ├── AssetBundleManifest.bindings.cs │ │ ├── AssetBundleRecompressOperation.bindings.cs │ │ ├── AssetBundleRequest.bindings.cs │ │ ├── AssetBundleUnloadOperation.bindings.cs │ │ └── AssetBundleUtility.bindings.cs │ ├── AssetDatabase/ │ │ └── Editor/ │ │ ├── Public/ │ │ │ └── PreviewImporter.bindings.cs │ │ ├── ScriptBindings/ │ │ │ ├── ArtifactInfo.binding.cs │ │ │ ├── AssetDatabase.bindings.cs │ │ │ ├── AssetDatabaseExperimental.bindings.cs │ │ │ ├── AssetOrigin.binding.cs │ │ │ └── CacheServer.bindings.cs │ │ └── V2/ │ │ ├── Managed/ │ │ │ ├── ArtifactDifferenceReporter.cs │ │ │ ├── ArtifactDifferenceReporterTesting.cs │ │ │ ├── AssetImportWorkerPostProcessorHelper.cs │ │ │ ├── AssetPostprocessorStaticVariableIgnoreAttribute.cs │ │ │ └── ImportActivityWindow.cs │ │ └── MultiArtifactTestImporter.bindings.cs │ ├── AssetPipelineEditor/ │ │ ├── AssetPostprocessors/ │ │ │ ├── FBXMaterialDescriptionPreprocessor.cs │ │ │ ├── ModelImporterPostProcessor.cs │ │ │ ├── SketchupMaterialDescriptionPreprocessor.cs │ │ │ └── ThreeDSMaterialDescriptionPreprocessor.cs │ │ ├── ImportSettings/ │ │ │ ├── AssetImporterEditor.bindings.cs │ │ │ ├── AssetImporterEditor.cs │ │ │ ├── AssetImporterTabbedEditor.cs │ │ │ ├── AudioImporterInspector.cs │ │ │ ├── BaseAssetImporterTabUI.cs │ │ │ ├── ModelImporterClipEditor.cs │ │ │ ├── ModelImporterEditor.cs │ │ │ ├── ModelImporterMaterialEditor.cs │ │ │ ├── ModelImporterModelEditor.cs │ │ │ ├── ModelImporterRigEditor.cs │ │ │ ├── PluginImporterInspector.cs │ │ │ ├── ScriptedImporterEditor.cs │ │ │ └── VideoClipImporterInspector.cs │ │ └── Public/ │ │ ├── AndroidAssetPackImporter.bindings.cs │ │ ├── AudioImporter.bindings.cs │ │ ├── CompiledAssemblies.bindings.cs │ │ ├── ModelImporting/ │ │ │ └── ModelImporter.bindings.cs │ │ ├── MonoImporter.bindings.cs │ │ ├── MovieImporter.deprecated.cs │ │ ├── NativeFormatImporterUtility.bindings.cs │ │ ├── PluginImporter.bindings.cs │ │ ├── RoslynAdditionalFiles.binding.cs │ │ ├── RoslynAnalyzerConfig.binding.cs │ │ ├── RuleSetCache.bindings.cs │ │ ├── ScriptedImporter.cs │ │ ├── TextScriptImporter.bindings.cs │ │ └── VideoImporter.bindings.cs │ ├── Audio/ │ │ └── Public/ │ │ ├── Managed/ │ │ │ ├── Audio.deprecated.cs │ │ │ ├── IHandle.cs │ │ │ └── IValidatable.cs │ │ └── ScriptBindings/ │ │ ├── Audio.bindings.cs │ │ ├── AudioClipExtensions.bindings.cs │ │ ├── AudioClipPlayable.bindings.cs │ │ ├── AudioMixer.bindings.cs │ │ ├── AudioMixerGroup.bindings.cs │ │ ├── AudioMixerPlayable.bindings.cs │ │ ├── AudioMixerSnapshot.bindings.cs │ │ ├── AudioPlayableBinding.cs │ │ ├── AudioPlayableGraphExtensions.bindings.cs │ │ ├── AudioPlayableOutput.bindings.cs │ │ ├── AudioRandomContainer.bindings.cs │ │ ├── AudioRenderer.bindings.cs │ │ ├── AudioSampleProvider.bindings.cs │ │ ├── AudioSampleProviderExtensions.bindings.cs │ │ ├── AudioSourceExtensions.bindings.cs │ │ ├── ExposeDSPGraph.cs │ │ ├── ExposeVivox.cs │ │ ├── MovieTexture.deprecated.cs │ │ └── UnityEngineWebCamTexture.bindings.cs │ ├── AudioEditor/ │ │ └── ScriptBindings/ │ │ ├── AudioMixerController.bindings.cs │ │ ├── AudioMixerDescription.bindings.cs │ │ ├── AudioMixerEffectController.bindings.cs │ │ ├── AudioMixerGroupController.bindings.cs │ │ └── AudioMixerSnapshotController.bindings.cs │ ├── BuildPipeline/ │ │ └── Editor/ │ │ ├── Managed/ │ │ │ ├── BuildArchiveImporter.cs │ │ │ ├── BuildCompression.deprecated.cs │ │ │ ├── BuildDefines.cs │ │ │ ├── BuildInstructionImporter.cs │ │ │ ├── BuildMetaDataImporter.cs │ │ │ ├── BuildOutput.cs │ │ │ ├── BuildPlayerDataGenerator.cs │ │ │ ├── BuildReferenceMap.bindings.cs │ │ │ ├── BuildSettings.cs │ │ │ ├── BuildTargetSelection.cs │ │ │ ├── BuildUsageCache.bindings.cs │ │ │ ├── BuildUsageTagGlobal.cs │ │ │ ├── BuildUsageTagSet.bindings.cs │ │ │ ├── ContentBuildInterface.bindings.cs │ │ │ ├── ContentBuildInterface.deprecated.cs │ │ │ ├── ContentBuildInterfaceProfile.bindings.cs │ │ │ ├── GameManagerDependencyInfo.cs │ │ │ ├── ObjectIdentifier.bindings.cs │ │ │ ├── PlayerBuildInterface.bindings.cs │ │ │ ├── ResourceFile.cs │ │ │ ├── SceneDependencyInfo.cs │ │ │ ├── TypeDB.bindings.cs │ │ │ ├── UnifiedBuildPipelineInternalApi.bindings.cs │ │ │ └── WriteCommand.cs │ │ ├── Shared/ │ │ │ └── ReferencesArtifactGenerator.bindings.cs │ │ └── Ucbp/ │ │ └── BuildPipelineContext.bindings.cs │ ├── BuildProfileEditor/ │ │ ├── ActiveBuildProfilerListener.cs │ │ ├── AssemblyInfo.cs │ │ ├── AssetImportOverridesWindow.cs │ │ ├── BuildAutomation/ │ │ │ ├── BuildAutomation.cs │ │ │ ├── BuildAutomationModalWindow.cs │ │ │ ├── BuildAutomationSettings.cs │ │ │ └── BuildAutomationSettingsEditor.cs │ │ ├── BuildProfileEditor.cs │ │ ├── BuildProfilePlayerSettingsEditor.cs │ │ ├── BuildProfileWindow.cs │ │ ├── Elements/ │ │ │ ├── BuildProfileBootstrapView.cs │ │ │ ├── BuildProfileCard.cs │ │ │ ├── BuildProfileGraphicsSettingsOverridesView.cs │ │ │ ├── BuildProfileListEditableLabel.cs │ │ │ ├── BuildProfileListLabel.cs │ │ │ ├── BuildProfileQualitySettingsOverridesView.cs │ │ │ ├── BuildProfileSceneList.cs │ │ │ ├── DropdownButton.cs │ │ │ ├── PlatformListView.cs │ │ │ ├── PlatformPackageEntry.cs │ │ │ ├── PlatformPackageItem.cs │ │ │ └── PreconfiguredSettingsItem.cs │ │ ├── Events.cs │ │ ├── Handlers/ │ │ │ ├── BuildProfileContextMenu.cs │ │ │ ├── BuildProfileDataSource.cs │ │ │ └── BuildProfileWindowSelection.cs │ │ ├── PlatformDiscoveryWindow.cs │ │ ├── TrText.cs │ │ └── Util.cs │ ├── BuildReportingEditor/ │ │ ├── BuildReportRestService.bindings.cs │ │ └── Managed/ │ │ ├── BuildFile.cs │ │ ├── BuildReport.bindings.cs │ │ ├── BuildReportRestService.cs │ │ ├── BuildResult.cs │ │ ├── BuildStep.cs │ │ ├── BuildStepMessage.cs │ │ ├── BuildSummary.cs │ │ ├── BuildType.cs │ │ ├── CommonRoles.bindings.cs │ │ ├── PackedAssetInfo.cs │ │ ├── PackedAssets.bindings.cs │ │ ├── ScenesUsingAsset.cs │ │ ├── ScenesUsingAssets.bindings.cs │ │ ├── ScopedBuildStep.cs │ │ ├── StrippingInfo.cs │ │ └── StrippingInfoWithSizeAnalysis.cs │ ├── Cloth/ │ │ └── Cloth.bindings.cs │ ├── ClothEditor/ │ │ ├── ClothInspector.cs │ │ └── ScriptBindings/ │ │ └── ClothUtilities.bindings.cs │ ├── CloudServicesSettingsEditor/ │ │ ├── Ads/ │ │ │ └── ScriptBindings/ │ │ │ └── AdvertisementSettings.bindings.cs │ │ ├── Analytics/ │ │ │ └── ScriptBindings/ │ │ │ └── AnalyticsSettings.bindings.cs │ │ ├── CrashReporting/ │ │ │ ├── Managed/ │ │ │ │ └── CrashReporting.cs │ │ │ └── ScriptBindings/ │ │ │ └── CrashReportingSettings.bindings.cs │ │ ├── PerformanceReporting/ │ │ │ └── ScriptBindings/ │ │ │ └── PerformanceReportingSettings.bindings.cs │ │ └── Purchasing/ │ │ └── ScriptBindings/ │ │ └── PurchasingSettings.bindings.cs │ ├── ClusterInput/ │ │ └── ClusterInput.bindings.cs │ ├── ClusterRenderer/ │ │ ├── ClusterRenderer.bindings.cs │ │ └── ClusterSerialization.bindings.cs │ ├── ContentLoad/ │ │ └── Public/ │ │ └── ContentLoad.bindings.cs │ ├── CrashReporting/ │ │ └── CrashReporter.bindings.cs │ ├── DSPGraph/ │ │ └── Public/ │ │ └── ScriptBindings/ │ │ ├── AudioHandle.bindings.cs │ │ ├── AudioMemoryManager.bindings.cs │ │ ├── AudioOutputHookManager.bindings.cs │ │ ├── DSPCommandBlock.bindings.cs │ │ ├── DSPGraph.bindings.cs │ │ ├── DSPNodeUpdateRequest.bindings.cs │ │ ├── DSPSampleProvider.bindings.cs │ │ ├── ExecuteContext.bindings.cs │ │ └── ExposeDSPGraph.cs │ ├── DeviceSimulatorEditor/ │ │ ├── AssemblyInfo.cs │ │ ├── DeviceInfo/ │ │ │ ├── DeviceInfo.cs │ │ │ ├── DeviceInfoAsset.cs │ │ │ ├── DeviceInfoImporter.cs │ │ │ ├── DeviceInfoImporterEditor.cs │ │ │ └── DeviceLoader.cs │ │ ├── DeviceListPopup.cs │ │ ├── DevicePackage.cs │ │ ├── DeviceSimulatorMain.cs │ │ ├── Input/ │ │ │ ├── InputManagerBackend.cs │ │ │ └── TouchEventManipulator.cs │ │ ├── Plugins/ │ │ │ ├── ApplicationPlugin.cs │ │ │ ├── DeviceSimulator.cs │ │ │ ├── DeviceSimulatorPlugin.cs │ │ │ └── PluginController.cs │ │ ├── Shims/ │ │ │ ├── ApplicationSimulation.cs │ │ │ ├── ScreenSimulation.cs │ │ │ └── SystemInfoSimulation.cs │ │ ├── SimulatorPlayerSettings.cs │ │ ├── SimulatorState.cs │ │ ├── SimulatorUtilities.cs │ │ ├── SimulatorWindow.cs │ │ ├── UserInterfaceController.cs │ │ └── VisualElements/ │ │ └── DeviceView.cs │ ├── DiagnosticsEditor/ │ │ ├── DiagnosticsConsoleMessage.cs │ │ └── DiagnosticsPreferences.cs │ ├── Director/ │ │ ├── AssemblyInfo.cs │ │ └── ScriptBindings/ │ │ ├── DataPlayable.bindings.cs │ │ ├── DataPlayable.cs │ │ ├── DataPlayableBinding.cs │ │ ├── DataPlayableOutput.bindings.cs │ │ ├── DataPlayableOutputExtensions.bindings.cs │ │ ├── IDataPlayer.cs │ │ ├── PlayableDirector.bindings.cs │ │ └── PlayableSystems.bindings.cs │ ├── EditorToolbar/ │ │ ├── Controls/ │ │ │ ├── AIDropdownContent.cs │ │ │ ├── ComponentToolContextButton.cs │ │ │ ├── EditorToolbarButton.cs │ │ │ ├── EditorToolbarDropdown.cs │ │ │ ├── EditorToolbarDropdownToggle.cs │ │ │ ├── EditorToolbarFloatField.cs │ │ │ ├── EditorToolbarIcon.cs │ │ │ ├── EditorToolbarToggle.cs │ │ │ ├── LastCustomToolButton.cs │ │ │ ├── LoadingSpinner.cs │ │ │ ├── ToolButton.cs │ │ │ └── ToolContextButton.cs │ │ └── ToolbarElements/ │ │ ├── AIDropdown.cs │ │ ├── AIDropdownConfig.cs │ │ ├── AccountDropdown.cs │ │ ├── BuiltinToolSettings.cs │ │ ├── CloudButton.cs │ │ ├── EditorToolsToolbar.cs │ │ ├── LayersDropdown.cs │ │ ├── LayoutDropdown.cs │ │ ├── ModesDropdown.cs │ │ ├── OverlayMenu.cs │ │ ├── PackageManagerButton.cs │ │ ├── PlayModeButtons.cs │ │ ├── SearchButton.cs │ │ ├── SnapSettings.cs │ │ ├── StoreButton.cs │ │ └── UndoButton.cs │ ├── EmbreeEditor/ │ │ └── Embree.bindings.cs │ ├── GameCenter/ │ │ ├── Managed/ │ │ │ ├── LocalService.cs │ │ │ └── NetworkServices.cs │ │ └── Public/ │ │ └── GameCenterServices.bindings.cs │ ├── GenericRemoteEditor/ │ │ └── Public/ │ │ └── GenericRemote.bindings.cs │ ├── GraphViewEditor/ │ │ ├── AssemblyInfo.cs │ │ ├── Capabilities.cs │ │ ├── Decorators/ │ │ │ └── GridBackground.cs │ │ ├── Direction.cs │ │ ├── EdgeControl.cs │ │ ├── Elements/ │ │ │ ├── Blackboard/ │ │ │ │ ├── Blackboard.cs │ │ │ │ ├── BlackboardField.cs │ │ │ │ ├── BlackboardRow.cs │ │ │ │ └── BlackboardSection.cs │ │ │ ├── Edge.cs │ │ │ ├── ElementResizer.cs │ │ │ ├── GraphElement.cs │ │ │ ├── GraphElementScopeExtensions.cs │ │ │ ├── Group.cs │ │ │ ├── GroupDropArea.cs │ │ │ ├── IResizable.cs │ │ │ ├── Line2.cs │ │ │ ├── MiniMap.cs │ │ │ ├── Node.cs │ │ │ ├── Pill.cs │ │ │ ├── Placemat/ │ │ │ │ ├── Placemat.cs │ │ │ │ └── PlacematContainer.cs │ │ │ ├── Port.cs │ │ │ ├── ResizableElement.cs │ │ │ ├── Scope.cs │ │ │ ├── ScopeContentContainer.cs │ │ │ ├── StackNode.cs │ │ │ ├── StackNodeDropTarget.cs │ │ │ ├── StackNodeSeparator.cs │ │ │ ├── StickyNote.cs │ │ │ └── TokenNode.cs │ │ ├── ExperimentalNamespaceRelic.cs │ │ ├── ICollectibleElement.cs │ │ ├── IDroppable.cs │ │ ├── IInsertLocation.cs │ │ ├── ISelectable.cs │ │ ├── ISelection.cs │ │ ├── IconBadge.cs │ │ ├── LineView.cs │ │ ├── Manipulators/ │ │ │ ├── ClickSelector.cs │ │ │ ├── ContentDragger.cs │ │ │ ├── Dragger.cs │ │ │ ├── EdgeConnector.cs │ │ │ ├── EdgeDragHelper.cs │ │ │ ├── EdgeManipulator.cs │ │ │ ├── FreehandSelector.cs │ │ │ ├── Inserter.cs │ │ │ ├── RectangleSelector.cs │ │ │ ├── Resizer.cs │ │ │ ├── SelectionDragger.cs │ │ │ ├── SelectionDropper.cs │ │ │ ├── ShortcutHandler.cs │ │ │ ├── SnapService.cs │ │ │ ├── Snapper.cs │ │ │ └── Zoomer.cs │ │ ├── NodeAdapter.cs │ │ ├── NodeSearch/ │ │ │ ├── SearchTree.cs │ │ │ └── SearchWindow.cs │ │ ├── Orientation.cs │ │ ├── Utils/ │ │ │ ├── GraphViewTemplateDescriptor.cs │ │ │ ├── RectUtils.cs │ │ │ └── VisualElementAttacher.cs │ │ ├── Views/ │ │ │ └── GraphView.cs │ │ └── Windows/ │ │ ├── GraphViewBlackboardWindow.cs │ │ ├── GraphViewEditorWindow.cs │ │ ├── GraphViewMinimapWindow.cs │ │ ├── GraphViewTemplateWindow.cs │ │ └── GraphViewToolWindow.cs │ ├── Grid/ │ │ ├── Managed/ │ │ │ └── Grid.cs │ │ └── ScriptBindings/ │ │ ├── Grid.bindings.cs │ │ └── GridLayout.bindings.cs │ ├── GridAndSnap/ │ │ ├── GridSettingsWindow.cs │ │ ├── LinkedVector3Field.cs │ │ └── SnapSettingsWindow.cs │ ├── GridEditor/ │ │ └── Managed/ │ │ └── GridEditor.cs │ ├── HierarchyCore/ │ │ ├── Managed/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── HierarchyFlattenedNodeChildren.cs │ │ │ ├── HierarchyNodeChildren.cs │ │ │ ├── HierarchyNodeChildrenAlloc.cs │ │ │ ├── HierarchyNodeChildrenFixed.cs │ │ │ ├── HierarchyNodeMapUnmanaged.cs │ │ │ ├── HierarchyNodeTypeHandlerBase.cs │ │ │ ├── HierarchyNodeTypeHandlerBaseEnumerable.cs │ │ │ ├── HierarchyPropertyString.cs │ │ │ ├── HierarchyPropertyUnmanaged.cs │ │ │ ├── HierarchySearch.cs │ │ │ ├── HierarchyViewNodesEnumerable.cs │ │ │ ├── IHierarchyProperty.cs │ │ │ └── NativeSparseArray.cs │ │ └── ScriptBindings/ │ │ ├── Hierarchy.bindings.cs │ │ ├── HierarchyCommandList.bindings.cs │ │ ├── HierarchyFlattened.bindings.cs │ │ ├── HierarchyFlattenedNode.bindings.cs │ │ ├── HierarchyNode.bindings.cs │ │ ├── HierarchyNodeFlags.bindings.cs │ │ ├── HierarchyNodeType.bindings.cs │ │ ├── HierarchyPropertyDescriptor.bindings.cs │ │ ├── HierarchyPropertyId.bindings.cs │ │ ├── HierarchyPropertyStorageType.bindings.cs │ │ ├── HierarchySearch.bindings.cs │ │ ├── HierarchyTestsHelper.bindings.cs │ │ └── HierarchyViewModel.bindings.cs │ ├── IMGUI/ │ │ ├── AssemblyInfo.cs │ │ ├── DrawStates.cs │ │ ├── Event.bindings.cs │ │ ├── Event.cs │ │ ├── EventCommandNames.cs │ │ ├── EventEnums.cs │ │ ├── EventInterests.cs │ │ ├── FriendAttributes.cs │ │ ├── GUI.bindings.cs │ │ ├── GUI.cs │ │ ├── GUIClip.bindings.cs │ │ ├── GUIContent.cs │ │ ├── GUIDebugger.bindings.cs │ │ ├── GUIElement.deprecated.cs │ │ ├── GUIEnums.cs │ │ ├── GUILayer.deprecated.cs │ │ ├── GUILayout.cs │ │ ├── GUILayoutOption.cs │ │ ├── GUILayoutUtility.bindings.cs │ │ ├── GUILayoutUtility.cs │ │ ├── GUISkin.bindings.cs │ │ ├── GUISkin.cs │ │ ├── GUIStateObjects.cs │ │ ├── GUIStyle.bindings.cs │ │ ├── GUIStyle.cs │ │ ├── GUITargetAttribute.cs │ │ ├── GUITexture.deprecated.cs │ │ ├── GUIUtility.bindings.cs │ │ ├── GUIUtility.cs │ │ ├── IMGUITextHandle.cs │ │ ├── LayoutEntry.cs │ │ ├── LayoutGroup.cs │ │ ├── ObjectGUIState.bindings.cs │ │ ├── RuntimeTextSettings.cs │ │ ├── ScrollViewState.cs │ │ ├── SliderHandler.cs │ │ ├── TextEditingUtilities.cs │ │ ├── TextEditor.cs │ │ └── TextSelectingUtilities.cs │ ├── Identifiers/ │ │ └── Identifiers.bindings.cs │ ├── ImageConversion/ │ │ └── ScriptBindings/ │ │ ├── AssemblyInfo.cs │ │ └── ImageConversion.bindings.cs │ ├── Input/ │ │ └── Private/ │ │ ├── Input.cs │ │ └── InputModule.bindings.cs │ ├── InputForUI/ │ │ ├── AssemblyInfo.cs │ │ ├── Events/ │ │ │ ├── CommandEvent.cs │ │ │ ├── Event.cs │ │ │ ├── EventModifiers.cs │ │ │ ├── EventSource.cs │ │ │ ├── IEventProperties.cs │ │ │ ├── IMECompositionEvent.cs │ │ │ ├── KeyEvent.cs │ │ │ ├── NavigationEvent.cs │ │ │ ├── PointerEvent.cs │ │ │ └── TextInputEvent.cs │ │ ├── Provider/ │ │ │ ├── EventProvider.cs │ │ │ ├── IEventProviderImpl.cs │ │ │ ├── InputEventPartialProvider.cs │ │ │ ├── InputManagerProvider.cs │ │ │ ├── NavigationEventRepeatHelper.cs │ │ │ └── PointerState.cs │ │ └── Sanitizer/ │ │ └── EventSanitizer.cs │ ├── InputLegacy/ │ │ ├── AndroidInput.bindings.cs │ │ ├── Input.bindings.cs │ │ └── MouseEvents.cs │ ├── JSONSerialize/ │ │ └── Public/ │ │ └── JsonUtility.bindings.cs │ ├── JSONSerializeEditor/ │ │ └── EditorJsonUtility.bindings.cs │ ├── Licensing/ │ │ ├── Public/ │ │ │ └── LicensingUtility.bindings.cs │ │ └── UI/ │ │ ├── Data/ │ │ │ └── Events/ │ │ │ ├── Base/ │ │ │ │ ├── Notification.cs │ │ │ │ ├── NotificationReasons.cs │ │ │ │ ├── NotificationType.cs │ │ │ │ └── NotificationWithDetails.cs │ │ │ ├── BorrowFeatureStatusNotification.cs │ │ │ ├── LicenseExpiredNotification.cs │ │ │ ├── LicenseOfflineValidityEndingNotification.cs │ │ │ └── LicenseUpdateNotification.cs │ │ ├── Events/ │ │ │ ├── Buttons/ │ │ │ │ ├── CloseButton.cs │ │ │ │ ├── CloseProjectButton.cs │ │ │ │ ├── EventsButtonFactory.cs │ │ │ │ ├── EventsButtonType.cs │ │ │ │ ├── IEventsButtonFactory.cs │ │ │ │ ├── ManageLicenseButton.cs │ │ │ │ ├── OkButton.cs │ │ │ │ ├── OpenUnityHubButton.cs │ │ │ │ ├── SaveAndQuitButton.cs │ │ │ │ ├── TemplateEventsButton.cs │ │ │ │ └── UpdateLicenseButton.cs │ │ │ ├── Handlers/ │ │ │ │ ├── INotificationHandler.cs │ │ │ │ ├── LicenseExpiredHandler.cs │ │ │ │ ├── LicenseOfflineValidityEndingHandler.cs │ │ │ │ └── LicenseUpdateHandler.cs │ │ │ ├── ILicenseNotificationHandlerFactory.cs │ │ │ ├── IModalWrapper.cs │ │ │ ├── INotificationDispatcher.cs │ │ │ ├── LicenseNotificationDispatcher.cs │ │ │ ├── LicenseNotificationHandlerFactory.cs │ │ │ ├── ModalWrapper.cs │ │ │ ├── Text/ │ │ │ │ └── LicenseTrStrings.cs │ │ │ └── Windows/ │ │ │ ├── LicenseExpiredWindow.cs │ │ │ ├── LicenseExpiredWindowContents.cs │ │ │ ├── LicenseOfflineValidityEndedWindow.cs │ │ │ ├── LicenseOfflineValidityEndedWindowContents.cs │ │ │ ├── LicenseOfflineValidityEndingWindow.cs │ │ │ ├── LicenseOfflineValidityEndingWindowContents.cs │ │ │ ├── LicenseRemovedWindow.cs │ │ │ ├── LicenseRemovedWindowContents.cs │ │ │ ├── LicenseReturnedWindow.cs │ │ │ ├── LicenseReturnedWindowContents.cs │ │ │ ├── LicenseRevokedWindow.cs │ │ │ ├── LicenseRevokedWindowContents.cs │ │ │ ├── TemplateLicenseEventWindow.cs │ │ │ └── TemplateLicenseEventWindowContents.cs │ │ ├── Helper/ │ │ │ ├── Constants.cs │ │ │ ├── ILicenseLogger.cs │ │ │ ├── LicenseLogger.cs │ │ │ └── Utils.cs │ │ ├── INativeApiWrapper.cs │ │ ├── LicenseManagedWrapper.cs │ │ └── NativeApiWrapper.cs │ ├── Localization/ │ │ └── Public/ │ │ └── LocalizationAsset.bindings.cs │ ├── LocalizationEditor/ │ │ ├── LocalizationAttribute.cs │ │ ├── LocalizationDatabase.bindings.cs │ │ ├── LocalizationDatabase.cs │ │ └── LocalizedEditorFontManager.cs │ ├── Marshalling/ │ │ ├── MarshallingTests.bindings.cs │ │ └── MarshallingTests2.bindings.cs │ ├── MeshLODGenerator/ │ │ └── ScriptBindings/ │ │ └── MeshLODUtility.bindings.cs │ ├── Multiplayer/ │ │ └── Managed/ │ │ ├── AssemblyInfo.cs │ │ ├── MultiplayerManager.bindings.cs │ │ ├── MultiplayerRole.cs │ │ └── MultiplayerRolesData.bindings.cs │ ├── MultiplayerEditor/ │ │ └── Managed/ │ │ ├── AssemblyInfo.cs │ │ ├── EditorMultiplayerManager.bindings.cs │ │ ├── MultiplayerRoleDropdown.cs │ │ └── MultiplayerRolesDataEditor.cs │ ├── PackageManager/ │ │ └── Editor/ │ │ └── Managed/ │ │ ├── AssetStoreInfo.cs │ │ ├── AuthorInfo.cs │ │ ├── BuildUtilities.cs │ │ ├── CacheRootConfig.cs │ │ ├── Client.cs │ │ ├── ConfigSource.cs │ │ ├── DependencyInfo.cs │ │ ├── DownloadProgress.cs │ │ ├── EditorCompatibilityInfo.cs │ │ ├── EditorCompatibilityLevel.cs │ │ ├── EntitlementLicensingModel.cs │ │ ├── EntitlementsInfo.cs │ │ ├── Error.cs │ │ ├── ErrorCode.cs │ │ ├── Events.cs │ │ ├── GitInfo.cs │ │ ├── IShouldIncludeInBuildCallback.cs │ │ ├── LogLevel.cs │ │ ├── NativeEnumExtensions.cs │ │ ├── NativeErrorCode.cs │ │ ├── NativeStatusCode.cs │ │ ├── OperationStatus.cs │ │ ├── PackOperationResult.cs │ │ ├── PackageCollection.cs │ │ ├── PackageCompliance.cs │ │ ├── PackageComplianceStatus.cs │ │ ├── PackageInfo.cs │ │ ├── PackageManager.bindings.cs │ │ ├── PackageProgress.cs │ │ ├── PackageRegistrationDiffEventArgs.cs │ │ ├── PackageSource.cs │ │ ├── PackageValidation.cs │ │ ├── ProgressState.cs │ │ ├── ProgressUpdateEventArgs.cs │ │ ├── RegistryCompliance.cs │ │ ├── RegistryComplianceStatus.cs │ │ ├── RegistryInfo.cs │ │ ├── RepositoryInfo.cs │ │ ├── RequestProgress.cs │ │ ├── Requests/ │ │ │ ├── AddAndRemoveRequest.cs │ │ │ ├── AddRequest.cs │ │ │ ├── AddScopedRegistryRequest.cs │ │ │ ├── ClearCacheRequest.cs │ │ │ ├── ClearCacheRootRequest.cs │ │ │ ├── EmbedRequest.cs │ │ │ ├── GetCacheRootRequest.cs │ │ │ ├── GetRegistriesRequest.cs │ │ │ ├── ListBuiltInPackagesRequest.cs │ │ │ ├── ListRequest.cs │ │ │ ├── PackRequest.cs │ │ │ ├── RemoveRequest.cs │ │ │ ├── RemoveScopedRegistryRequest.cs │ │ │ ├── Request.cs │ │ │ ├── Requests.bindings.cs │ │ │ ├── ResetToEditorDefaultsRequest.cs │ │ │ ├── SearchRequest.cs │ │ │ ├── SetCacheRootRequest.cs │ │ │ └── UpdateScopedRegistryRequest.cs │ │ ├── ResolutionStrategy.cs │ │ ├── Search/ │ │ │ ├── SearchCapabilities.cs │ │ │ └── SearchOrderBy.cs │ │ ├── SemVersionHelper.cs │ │ ├── SignatureInfo.cs │ │ ├── SignatureStatus.cs │ │ ├── StatusCode.cs │ │ ├── UnityLifecycleInfo.cs │ │ ├── UpdateScopedRegistryOptions.cs │ │ ├── VersionsInfo.cs │ │ └── Violation.cs │ ├── PackageManagerUI/ │ │ └── Editor/ │ │ ├── Extensions/ │ │ │ ├── BaseDropdownItem.cs │ │ │ ├── DetailsExtension.cs │ │ │ ├── ExtendableToolbarMenu.cs │ │ │ ├── ExtensionManager.cs │ │ │ ├── InputDropdownArgs.cs │ │ │ ├── Interfaces/ │ │ │ │ ├── IDetailsExtension.cs │ │ │ │ ├── IExtension.cs │ │ │ │ ├── IMenu.cs │ │ │ │ ├── IMenuDropdownItem.cs │ │ │ │ ├── IPackageActionButton.cs │ │ │ │ ├── IPackageActionDropdownItem.cs │ │ │ │ ├── IPackageActionMenu.cs │ │ │ │ ├── IPackageSelectionChangedHandler.cs │ │ │ │ ├── IWindow.cs │ │ │ │ ├── IWindowCreatedHandler.cs │ │ │ │ └── IWindowDestroyHandler.cs │ │ │ ├── MenuDropdownItem.cs │ │ │ ├── PackageActionDropdownItem.cs │ │ │ ├── PackageExtensionAction.cs │ │ │ └── PackageSelectionArgs.cs │ │ ├── External/ │ │ │ ├── EditorGameServiceExtension.cs │ │ │ ├── SemVersionExtension.cs │ │ │ └── ServicesTab/ │ │ │ ├── ServiceGroupingsWrapper.cs │ │ │ └── ServicesTabConfiguration.cs │ │ ├── Services/ │ │ │ ├── Analytics/ │ │ │ │ ├── AssetSelectionWindowAnalytics.cs │ │ │ │ ├── PackageCacheManagementAnalytics.cs │ │ │ │ ├── PackageManagerDialogAnalytics.cs │ │ │ │ ├── PackageManagerFiltersAnalytics.cs │ │ │ │ ├── PackageManagerOperationErrorAnalytics.cs │ │ │ │ └── PackageManagerWindowAnalytics.cs │ │ │ ├── AssetStore/ │ │ │ │ ├── Asset.cs │ │ │ │ ├── AssetSelectionHandler.cs │ │ │ │ ├── AssetStoreCache.cs │ │ │ │ ├── AssetStoreClientV2.cs │ │ │ │ ├── AssetStoreDownloadInfo.cs │ │ │ │ ├── AssetStoreDownloadManager.cs │ │ │ │ ├── AssetStoreDownloadOperation.cs │ │ │ │ ├── AssetStoreImportedPackage.cs │ │ │ │ ├── AssetStoreListOperation.cs │ │ │ │ ├── AssetStoreLocalInfo.cs │ │ │ │ ├── AssetStoreOAuth.cs │ │ │ │ ├── AssetStorePackageFactory.cs │ │ │ │ ├── AssetStorePackageInstaller.cs │ │ │ │ ├── AssetStorePackageVersion.cs │ │ │ │ ├── AssetStoreProductInfo.cs │ │ │ │ ├── AssetStorePurchaseInfo.cs │ │ │ │ ├── AssetStorePurchases.cs │ │ │ │ ├── AssetStoreRestAPI.cs │ │ │ │ ├── AssetStoreUpdateInfo.cs │ │ │ │ ├── AssetStoreUtils.cs │ │ │ │ ├── AssetStoreVersionList.cs │ │ │ │ ├── JsonParser.cs │ │ │ │ └── LocalInfoHandler.cs │ │ │ ├── Common/ │ │ │ │ ├── BackgroundFetchHandler.cs │ │ │ │ ├── BasePackageVersion.cs │ │ │ │ ├── BaseVersionList.cs │ │ │ │ ├── DictionaryExtensions.cs │ │ │ │ ├── DownloadState.cs │ │ │ │ ├── FetchStatusTracker.cs │ │ │ │ ├── Icon.cs │ │ │ │ ├── OperationFactory.cs │ │ │ │ ├── Package.cs │ │ │ │ ├── PackageComplianceExtension.cs │ │ │ │ ├── PackageInfoExtension.cs │ │ │ │ ├── PackageManagerPrefs.cs │ │ │ │ ├── PlaceholderPackageVersion.cs │ │ │ │ ├── PlaceholderVersionList.cs │ │ │ │ ├── PlayModeDownload.cs │ │ │ │ ├── Product.cs │ │ │ │ ├── RegistryComplianceExtension.cs │ │ │ │ ├── RegistryInfoExtension.cs │ │ │ │ ├── ResourceLoader.cs │ │ │ │ ├── UIError.cs │ │ │ │ ├── UIErrorCode.cs │ │ │ │ ├── UniqueIdMapper.cs │ │ │ │ └── ViolationExtension.cs │ │ │ ├── EntitlementsErrorAndDeprecationChecker.cs │ │ │ ├── Interfaces/ │ │ │ │ ├── IOperation.cs │ │ │ │ ├── IPackage.cs │ │ │ │ ├── IPackageVersion.cs │ │ │ │ ├── IPage.cs │ │ │ │ ├── IProduct.cs │ │ │ │ ├── IVersionList.cs │ │ │ │ └── IVisualStateList.cs │ │ │ ├── Packages/ │ │ │ │ ├── Actions/ │ │ │ │ │ ├── AddAction.cs │ │ │ │ │ ├── CancelDownloadAction.cs │ │ │ │ │ ├── DeselectAction.cs │ │ │ │ │ ├── DisableCondition.cs │ │ │ │ │ ├── DownloadActionBase.cs │ │ │ │ │ ├── DownloadNewAction.cs │ │ │ │ │ ├── DownloadUpdateAction.cs │ │ │ │ │ ├── GitUpdateAction.cs │ │ │ │ │ ├── ImportActionBase.cs │ │ │ │ │ ├── ImportNewAction.cs │ │ │ │ │ ├── ImportUpdateAction.cs │ │ │ │ │ ├── PackageAction.cs │ │ │ │ │ ├── PauseDownloadAction.cs │ │ │ │ │ ├── ReDownloadAction.cs │ │ │ │ │ ├── ReImportAction.cs │ │ │ │ │ ├── RemoveAction.cs │ │ │ │ │ ├── RemoveCustomAction.cs │ │ │ │ │ ├── RemoveImportedAction.cs │ │ │ │ │ ├── ResetAction.cs │ │ │ │ │ ├── ResumeDownloadAction.cs │ │ │ │ │ ├── SignInAction.cs │ │ │ │ │ ├── UnlockAction.cs │ │ │ │ │ ├── UpdateAction.cs │ │ │ │ │ ├── UpdateActionBase.cs │ │ │ │ │ └── VersionHistoryUpdateAction.cs │ │ │ │ ├── PackageDatabase.cs │ │ │ │ ├── PackageImage.cs │ │ │ │ ├── PackageLink/ │ │ │ │ │ ├── PackageLink.cs │ │ │ │ │ ├── PackageLinkFactory.cs │ │ │ │ │ ├── PackageUpmChangelogLink.cs │ │ │ │ │ ├── PackageUpmDocumentationLink.cs │ │ │ │ │ ├── PackageUpmLicenseLink.cs │ │ │ │ │ └── PackageUpmVersionHistoryChangelogLink.cs │ │ │ │ ├── PackageOperationDispatcher.cs │ │ │ │ ├── PackageProgress.cs │ │ │ │ ├── PackageSample.cs │ │ │ │ ├── PackageSizeInfo.cs │ │ │ │ ├── PackageState.cs │ │ │ │ ├── PackageTag.cs │ │ │ │ └── RegistryType.cs │ │ │ ├── Pages/ │ │ │ │ ├── BasePage.cs │ │ │ │ ├── PageCapability.cs │ │ │ │ ├── PageFactory.cs │ │ │ │ ├── PageFilters.cs │ │ │ │ ├── PageManager.cs │ │ │ │ ├── PageRefreshHandler.cs │ │ │ │ ├── PageSelection.cs │ │ │ │ ├── PageSortOption.cs │ │ │ │ ├── PageTypes/ │ │ │ │ │ ├── BuiltInPage.cs │ │ │ │ │ ├── ExtensionPage.cs │ │ │ │ │ ├── InProjectNonCompliancePage.cs │ │ │ │ │ ├── InProjectPage.cs │ │ │ │ │ ├── InProjectUpdatesPage.cs │ │ │ │ │ ├── MyAssetsPage.cs │ │ │ │ │ ├── MyRegistriesPage.cs │ │ │ │ │ ├── ScopedRegistryPage.cs │ │ │ │ │ └── UnityRegistryPage.cs │ │ │ │ ├── PaginatedVisualStateList.cs │ │ │ │ ├── RefreshOptions.cs │ │ │ │ ├── SimplePage.cs │ │ │ │ ├── VisualState.cs │ │ │ │ └── VisualStateList.cs │ │ │ ├── ProjectSettings/ │ │ │ │ ├── PackageManagerProjectSettings.cs │ │ │ │ ├── PackageManagerProjectSettingsProvider.cs │ │ │ │ ├── RegistryInfoDraft.cs │ │ │ │ ├── RegistryInfoDraftData.cs │ │ │ │ └── RegistryInfoOriginalData.cs │ │ │ ├── Proxies/ │ │ │ │ ├── ApplicationProxy.cs │ │ │ │ ├── AssetDatabaseProxy.cs │ │ │ │ ├── AssetStoreCachePathProxy.cs │ │ │ │ ├── ClientProxy.cs │ │ │ │ ├── DateTimeProxy.cs │ │ │ │ ├── EditorAnalyticsProxy.cs │ │ │ │ ├── HttpClientFactory.cs │ │ │ │ ├── IOProxy.cs │ │ │ │ ├── PackageManagerProjectSettingsProxy.cs │ │ │ │ ├── SelectionProxy.cs │ │ │ │ ├── UnityConnectProxy.cs │ │ │ │ └── UnityOAuthProxy.cs │ │ │ ├── ServicesContainer.cs │ │ │ ├── Upm/ │ │ │ │ ├── UpmAddAndRemoveOperation.cs │ │ │ │ ├── UpmAddOperation.cs │ │ │ │ ├── UpmAddRegistryOperation.cs │ │ │ │ ├── UpmBaseOperation.cs │ │ │ │ ├── UpmCache.cs │ │ │ │ ├── UpmCacheRootClient.cs │ │ │ │ ├── UpmClearCacheRootOperation.cs │ │ │ │ ├── UpmClient.cs │ │ │ │ ├── UpmGetCacheRootOperation.cs │ │ │ │ ├── UpmGetRegistriesOperation.cs │ │ │ │ ├── UpmListOperation.cs │ │ │ │ ├── UpmPackageData.cs │ │ │ │ ├── UpmPackageFactory.cs │ │ │ │ ├── UpmPackageVersion.cs │ │ │ │ ├── UpmRegistryClient.cs │ │ │ │ ├── UpmRemoveOperation.cs │ │ │ │ ├── UpmRemoveRegistryOperation.cs │ │ │ │ ├── UpmSearchOperation.cs │ │ │ │ ├── UpmSetCacheRootOperation.cs │ │ │ │ ├── UpmUpdateRegistryOperation.cs │ │ │ │ └── UpmVersionList.cs │ │ │ └── UserSettings/ │ │ │ └── PackageManagerUserSettingsProvider.cs │ │ └── UI/ │ │ ├── AddPackageByNameDropdown.cs │ │ ├── Common/ │ │ │ ├── Alert.cs │ │ │ ├── BaseDropdownButton.cs │ │ │ ├── DelayedSelectionHandler.cs │ │ │ ├── DropdownButton.cs │ │ │ ├── IOUtils.cs │ │ │ ├── IncorrectFieldTypeException.cs │ │ │ ├── InspectorSelectionHandler.cs │ │ │ ├── LoadingSpinner.cs │ │ │ ├── ProgressBar.cs │ │ │ ├── SelectableLabel.cs │ │ │ ├── Tabs/ │ │ │ │ ├── BaseTabElement.cs │ │ │ │ ├── BaseTabView.cs │ │ │ │ ├── ITabElement.cs │ │ │ │ └── ITabView.cs │ │ │ ├── TagLabelList.cs │ │ │ ├── TextFieldPlaceholder.cs │ │ │ ├── ToolbarWindowMenu.cs │ │ │ ├── UIUtils.cs │ │ │ ├── VisualElementCache.cs │ │ │ └── VisualElementExtensions.cs │ │ ├── DropdownContainer.cs │ │ ├── DropdownContent.cs │ │ ├── DropdownElement.cs │ │ ├── Filters/ │ │ │ ├── AssetStoreFiltersWindow.cs │ │ │ └── UpmFiltersWindow.cs │ │ ├── Generic/ │ │ │ └── HelpBoxWithOptionalReadMore.cs │ │ ├── GenericInputDropdown.cs │ │ ├── InProgressDropdown.cs │ │ ├── InProgressView.cs │ │ ├── Interfaces/ │ │ │ ├── IPackageListView.cs │ │ │ ├── IPackageManagerExtension.cs │ │ │ └── ISelectableItem.cs │ │ ├── MultiSelect/ │ │ │ ├── CheckUpdateFoldout.cs │ │ │ ├── DownloadFoldoutGroup.cs │ │ │ ├── DownloadUpdateFoldoutGroup.cs │ │ │ ├── IMultiSelectFoldoutElement.cs │ │ │ ├── InstallFoldoutGroup.cs │ │ │ ├── MultiSelectDetails.cs │ │ │ ├── MultiSelectFoldout.cs │ │ │ ├── MultiSelectFoldoutGroup.cs │ │ │ ├── MultiSelectItem.cs │ │ │ ├── NoActionsFoldout.cs │ │ │ ├── RemoveFoldoutGroup.cs │ │ │ ├── RemoveImportedFoldoutGroup.cs │ │ │ ├── UnlockFoldout.cs │ │ │ ├── UpdateFoldout.cs │ │ │ └── UpdateFoldoutGroup.cs │ │ ├── PackageDetails.cs │ │ ├── PackageDetailsBody.cs │ │ ├── PackageDetailsHeader.cs │ │ ├── PackageDetailsLinks.cs │ │ ├── PackageDetailsTabs/ │ │ │ ├── PackageDetailsDependenciesTab.cs │ │ │ ├── PackageDetailsDescriptionTab.cs │ │ │ ├── PackageDetailsFeatureDependenciesTab.cs │ │ │ ├── PackageDetailsImagesTab.cs │ │ │ ├── PackageDetailsImportedAssetsTab.cs │ │ │ ├── PackageDetailsOverviewTab.cs │ │ │ ├── PackageDetailsOverviewTabContent.cs │ │ │ ├── PackageDetailsReleasesTab.cs │ │ │ ├── PackageDetailsSampleItem.cs │ │ │ ├── PackageDetailsSamplesTab.cs │ │ │ ├── PackageDetailsTabElement.cs │ │ │ ├── PackageDetailsTabView.cs │ │ │ ├── PackageDetailsVersionHistoryItem.cs │ │ │ ├── PackageDetailsVersionsTab.cs │ │ │ ├── SignInDetails.cs │ │ │ └── SortedColumn.cs │ │ ├── PackageGroup.cs │ │ ├── PackageHelpBox/ │ │ │ ├── DeprecatedPackageHelpBox.cs │ │ │ ├── DeprecatedVersionHelpBox.cs │ │ │ ├── DisabledPackageHelpBox.cs │ │ │ ├── HiddenProductHelpBox.cs │ │ │ ├── NonCompliantPackageHelpBox.cs │ │ │ ├── PackageBaseHelpBox.cs │ │ │ ├── ScopedRegistryHelpBox.cs │ │ │ └── VersionTagHelpBox.cs │ │ ├── PackageItem.cs │ │ ├── PackageLinks/ │ │ │ ├── PackageLinkButton.cs │ │ │ └── PackageQuickStartButton.cs │ │ ├── PackageList.cs │ │ ├── PackageListScrollView.cs │ │ ├── PackageListView.cs │ │ ├── PackageLoadBar.cs │ │ ├── PackageManagerExtensions.cs │ │ ├── PackageManagerFiltersWindow.cs │ │ ├── PackageManagerToolbar.cs │ │ ├── PackageManagerWindow.cs │ │ ├── PackageManagerWindowRoot.cs │ │ ├── PackageManifest.cs │ │ ├── PackageManifestImporterEditor.cs │ │ ├── PackagePlatformList.cs │ │ ├── PackageReleaseDetailsItem.cs │ │ ├── PackageSampleItemLowWidth.cs │ │ ├── PackageSearchBar.cs │ │ ├── PackageSelectionEditor.cs │ │ ├── PackageSelectionObject.cs │ │ ├── PackageStatusBar.cs │ │ ├── PackageTagLabel/ │ │ │ ├── PackageAssetStoreTagLabel.cs │ │ │ ├── PackageBaseTagLabel.cs │ │ │ ├── PackageDeprecatedTagLabel.cs │ │ │ ├── PackageDynamicTagLabel.cs │ │ │ └── PackageSimpleTagLabel.cs │ │ ├── PartiallyNonCompliantRegistryMessage.cs │ │ ├── RegistryItem.cs │ │ ├── ScopedRegistriesSettings.cs │ │ ├── ScopedRegistryAddedPopup.cs │ │ ├── SelectionWindow/ │ │ │ ├── SelectionWindow.cs │ │ │ ├── SelectionWindowData.cs │ │ │ ├── SelectionWindowFooter.cs │ │ │ ├── SelectionWindowHeader.cs │ │ │ ├── SelectionWindowProxy.cs │ │ │ ├── SelectionWindowRoot.cs │ │ │ ├── SelectionWindowRow.cs │ │ │ └── SelectionWindowTreeView.cs │ │ ├── Sidebar/ │ │ │ ├── Sidebar.cs │ │ │ └── SidebarRow.cs │ │ ├── SignInBar.cs │ │ ├── ToolBar/ │ │ │ ├── Interfaces/ │ │ │ │ └── IPackageToolBarButton.cs │ │ │ ├── LegacyFormatDropdownButton.cs │ │ │ ├── PackageToolBar.cs │ │ │ ├── PackageToolBarButtonSingleAction.cs │ │ │ └── PackageToolBarError.cs │ │ └── VersionInfoIcon.cs │ ├── ParticleSystem/ │ │ ├── Managed/ │ │ │ ├── IJobParticleSystem.cs │ │ │ ├── ParticleSystem.deprecated.cs │ │ │ ├── ParticleSystemEnums.cs │ │ │ ├── ParticleSystemJobStructs.cs │ │ │ ├── ParticleSystemRenderer.deprecated.cs │ │ │ └── ParticleSystemStructs.cs │ │ └── ScriptBindings/ │ │ ├── ParticleSystem.bindings.cs │ │ ├── ParticleSystemForceField.bindings.cs │ │ ├── ParticleSystemModules.bindings.cs │ │ └── ParticleSystemRenderer.bindings.cs │ ├── ParticleSystemEditor/ │ │ ├── ParticleEffectUI.cs │ │ ├── ParticleSystemClipboard.cs │ │ ├── ParticleSystemCurveEditor.cs │ │ ├── ParticleSystemEditor.cs │ │ ├── ParticleSystemModules/ │ │ │ ├── ClampVelocityModuleUI.cs │ │ │ ├── CollisionModuleUI.cs │ │ │ ├── ColorByVelocityModuleUI.cs │ │ │ ├── ColorModuleUI.cs │ │ │ ├── CustomDataModuleUI.cs │ │ │ ├── EmissionModuleUI.cs │ │ │ ├── ExternalForcesModuleUI.cs │ │ │ ├── ForceModuleUI.cs │ │ │ ├── InheritVelocityModuleUI.cs │ │ │ ├── InitialModuleUI.cs │ │ │ ├── LifetimeByEmitterSpeedModuleUI.cs │ │ │ ├── LightsModuleUI.cs │ │ │ ├── ModuleUI.cs │ │ │ ├── ModuleUIHelpers.cs │ │ │ ├── NoiseModuleUI.cs │ │ │ ├── RendererModuleUI.cs │ │ │ ├── RotationByVelocityModuleUI.cs │ │ │ ├── RotationModuleUI.cs │ │ │ ├── SerializedModuleUI.cs │ │ │ ├── ShapeModuleUI.cs │ │ │ ├── SizeByVelocityModuleUI.cs │ │ │ ├── SizeModuleUI.cs │ │ │ ├── SubModuleUI.cs │ │ │ ├── TrailModuleUI.cs │ │ │ ├── TriggerModuleUI.cs │ │ │ ├── UVModuleUI.cs │ │ │ └── VelocityModuleUI.cs │ │ ├── ParticleSystemStyles.cs │ │ ├── ParticleSystemUI.cs │ │ ├── ParticleSystemWindow.cs │ │ ├── ScriptBindings/ │ │ │ └── ParticleSystemEditor.bindings.cs │ │ ├── SerializedMinMaxColor.cs │ │ ├── SerializedMinMaxCurve.cs │ │ └── SerializedMinMaxGradient.cs │ ├── PerformanceReporting/ │ │ └── ScriptBindings/ │ │ └── PerformanceReporting.bindings.cs │ ├── Physics/ │ │ ├── AssemblyInfo.cs │ │ ├── Managed/ │ │ │ ├── Collision.cs │ │ │ ├── CollisionDetectionMode.cs │ │ │ ├── ForceMode.cs │ │ │ ├── JointConstraints.cs │ │ │ └── WheelFrictionCurve.cs │ │ └── ScriptBindings/ │ │ ├── Articulations.bindings.cs │ │ ├── Articulations.deprecated.cs │ │ ├── BoxCollider.bindings.cs │ │ ├── CapsuleCollider.bindings.cs │ │ ├── CharacterController.bindings.cs │ │ ├── CharacterJoint.bindings.cs │ │ ├── Collider.bindings.cs │ │ ├── ConfigurableJoint.bindings.cs │ │ ├── ConstantForce.bindings.cs │ │ ├── ContactModification.bindings.cs │ │ ├── FixedJoint.bindings.cs │ │ ├── HingeJoint.bindings.cs │ │ ├── ImmediatePhysics.bindings.cs │ │ ├── Joint.bindings.cs │ │ ├── MeshCollider.bindings.cs │ │ ├── Physics.bindings.cs │ │ ├── Physics.deprecated.cs │ │ ├── PhysicsContact.bindings.cs │ │ ├── PhysicsContact.deprecated.cs │ │ ├── PhysicsGeometry.bindings.cs │ │ ├── PhysicsMaterial.bindings.cs │ │ ├── PhysicsMaterial.deprecated.cs │ │ ├── PhysicsScene.bindings.cs │ │ ├── QueryCommand.bindings.cs │ │ ├── QueryCommand.deprecated.cs │ │ ├── RaycastHit.bindings.cs │ │ ├── Rigidbody.bindings.cs │ │ ├── Rigidbody.deprecated.cs │ │ ├── SphereCollider.bindings.cs │ │ └── SpringJoint.bindings.cs │ ├── Physics2D/ │ │ └── ScriptBindings/ │ │ ├── Physics2D.bindings.cs │ │ └── Physics2D.deprecated.cs │ ├── Physics2DEditor/ │ │ └── Managed/ │ │ ├── Colliders/ │ │ │ ├── BoxCollider2DEditor.cs │ │ │ ├── BoxCollider2DTool.cs │ │ │ ├── CapsuleCollider2DEditor.cs │ │ │ ├── CapsuleCollider2DTool.cs │ │ │ ├── CircleCollider2DEditor.cs │ │ │ ├── CircleCollider2DTool.cs │ │ │ ├── Collider2DEditorBase.cs │ │ │ ├── Collider2DToolBase.cs │ │ │ ├── CompositeCollider2DEditor.cs │ │ │ ├── CustomCollider2DEditor.cs │ │ │ ├── EdgeCollider2DEditor.cs │ │ │ ├── EdgeCollider2DTool.cs │ │ │ ├── EditableLineHandle2D.cs │ │ │ ├── EditablePath2D.cs │ │ │ ├── EditablePath2DTool.cs │ │ │ ├── PolygonCollider2DEditor.cs │ │ │ ├── PolygonCollider2DTool.cs │ │ │ └── PrimitiveCollider2DTool.cs │ │ ├── Effectors/ │ │ │ ├── AreaEffector2DEditor.cs │ │ │ ├── BuoyancyEffector2DEditor.cs │ │ │ ├── Effector2DEditor.cs │ │ │ ├── PlatformEffector2DEditor.cs │ │ │ ├── PointEffector2DEditor.cs │ │ │ └── SurfaceEffector2DEditor.cs │ │ ├── Joints/ │ │ │ ├── AnchoredJoint2DEditor.cs │ │ │ ├── DistanceJoint2DEditor.cs │ │ │ ├── HingeJoint2DEditor.cs │ │ │ ├── HingeJoint2DTool.cs │ │ │ ├── Joint2DEditor.cs │ │ │ ├── JointAngularLimitHandle2D.cs │ │ │ ├── RelativeJoint2DEditor.cs │ │ │ ├── SliderJoint2DEditor.cs │ │ │ ├── SpringJoint2DEditor.cs │ │ │ ├── TargetJoint2DEditor.cs │ │ │ └── WheelJoint2DEditor.cs │ │ ├── Menu/ │ │ │ └── Physics2DMenuItem.cs │ │ ├── Rigidbody/ │ │ │ └── Rigidbody2DEditor.cs │ │ └── Settings/ │ │ ├── LayerCollisionMatrix2D.cs │ │ ├── Physics2DPreferences.cs │ │ └── Physics2DSettingsEditor.cs │ ├── PhysicsEditor/ │ │ ├── ArticulationBodyEditor.cs │ │ ├── ArticulationBodyEditorCommon.cs │ │ ├── ArticulationBodyJointLimitTool.cs │ │ ├── ArticulatonBodyAnchorTransformTool.cs │ │ ├── BoxColliderEditor.cs │ │ ├── CapsuleColliderEditor.cs │ │ ├── CharacterControllerEditor.cs │ │ ├── CharacterJointEditor.cs │ │ ├── Collider3DEditorBase.cs │ │ ├── ColliderEditorUtility.cs │ │ ├── ConfigurableJointEditor.cs │ │ ├── HingeJointEditor.cs │ │ ├── JointAngularLimitHandle.cs │ │ ├── JointEditor.cs │ │ ├── MeshColliderEditor.cs │ │ ├── PhysicsDebugWindow.cs │ │ ├── PhysicsDebugWindowContactsTab.cs │ │ ├── PhysicsDebugWindowInfoTab.cs │ │ ├── PhysicsDebugWindowInternal.cs │ │ ├── PhysicsDebugWindowQueries.cs │ │ ├── PhysicsManagerInspector.cs │ │ ├── PhysicsManagerInspectorBridge.cs │ │ ├── PhysicsMaterialEditor.cs │ │ ├── RagdollBuilder.cs │ │ ├── RagdollBuilderWindow.cs │ │ ├── RigidbodyEditor.cs │ │ ├── ScriptBindings/ │ │ │ ├── PhysicsDebug.bindings.cs │ │ │ └── PhysicsDebug.deprecated.cs │ │ ├── SphereColliderEditor.cs │ │ ├── TerrainColliderEditor.cs │ │ └── WheelColliderEditor.cs │ ├── PresetsEditor/ │ │ ├── AddPresetTypeWindow/ │ │ │ ├── AddPresetTypeDataSource.cs │ │ │ ├── AddPresetTypeGUI.cs │ │ │ ├── AddPresetTypeWindow.cs │ │ │ └── PresetTypeDropdownItem.cs │ │ ├── PresetEditorHelper.cs │ │ ├── PresetManagerPostProcessor.cs │ │ └── Public/ │ │ ├── Preset.bindings.cs │ │ ├── PresetManager.bindings.cs │ │ └── PresetType.bindings.cs │ ├── PresetsUIEditor/ │ │ ├── DefaultPresetReorderableList.cs │ │ ├── PresetEditor.cs │ │ ├── PresetManagerEditor.cs │ │ ├── PresetSearchProvider.cs │ │ └── PresetSelector.cs │ ├── ProfilerEditor/ │ │ ├── MemoryProfiler/ │ │ │ ├── ArrayEntries.cs │ │ │ ├── BlockSection.cs │ │ │ ├── ChapterSection.cs │ │ │ ├── MemoryProfilerCompilationGuard.cs │ │ │ ├── MemorySnapshot.cs │ │ │ ├── MemorySnapshotFileReader.cs │ │ │ └── MemorySnapshotFileWriter.bindings.cs │ │ ├── ProfilerOutOfProcess/ │ │ │ └── ProfilerRoleProvider.cs │ │ ├── ProfilerWindow/ │ │ │ ├── Analytics/ │ │ │ │ ├── EditorAnalyticsService.cs │ │ │ │ ├── IAnalyticsService.cs │ │ │ │ └── ProfilerWindowAnalytics.cs │ │ │ ├── Bottlenecks/ │ │ │ │ ├── Chart/ │ │ │ │ │ ├── BottlenecksChartViewController.cs │ │ │ │ │ ├── Components/ │ │ │ │ │ │ ├── BlocksGraphView/ │ │ │ │ │ │ │ ├── BlocksGraphView.cs │ │ │ │ │ │ │ └── BlocksGraphViewMeshBuilder.cs │ │ │ │ │ │ └── GraphView.cs │ │ │ │ │ ├── Data/ │ │ │ │ │ │ ├── BottlenecksChartViewModel.cs │ │ │ │ │ │ └── BottlenecksChartViewModelBuilder.cs │ │ │ │ │ └── Tooltip/ │ │ │ │ │ └── BottlenecksChartTooltipViewController.cs │ │ │ │ ├── Data/ │ │ │ │ │ ├── IProfilerCaptureDataService.cs │ │ │ │ │ ├── IProfilerPersistentSettingsService.cs │ │ │ │ │ ├── LegacyGlobalProfilerPersistentSettingsService.cs │ │ │ │ │ └── LegacySingletonProfilerCaptureDataService.cs │ │ │ │ └── Details/ │ │ │ │ ├── BottlenecksDetailsViewController.cs │ │ │ │ ├── Data/ │ │ │ │ │ ├── BottlenecksDetailsViewModel.cs │ │ │ │ │ └── BottlenecksDetailsViewModelBuilder.cs │ │ │ │ └── Shared/ │ │ │ │ └── TimeFormatterUtility.cs │ │ │ ├── Chart.cs │ │ │ ├── IProfilerWindowController.cs │ │ │ ├── LegacyProfilerAreaUtility.cs │ │ │ ├── ModuleEditor/ │ │ │ │ ├── Data/ │ │ │ │ │ ├── CounterCollector.cs │ │ │ │ │ └── ModuleData.cs │ │ │ │ ├── ModuleDetailsViewController.cs │ │ │ │ ├── ModuleEditorWindow.cs │ │ │ │ ├── ModuleListViewController.cs │ │ │ │ ├── ViewController.cs │ │ │ │ └── Views/ │ │ │ │ └── DragIndicator.cs │ │ │ ├── ProfilerCategoryActivator.cs │ │ │ ├── ProfilerChart.cs │ │ │ ├── ProfilerColors.cs │ │ │ ├── ProfilerCounterData.cs │ │ │ ├── ProfilerCounterDescriptor.cs │ │ │ ├── ProfilerDetailedCallsView.cs │ │ │ ├── ProfilerDetailedObjectsView.cs │ │ │ ├── ProfilerDetailedView.cs │ │ │ ├── ProfilerEditorUtility.cs │ │ │ ├── ProfilerFrameDataTreeView.cs │ │ │ ├── ProfilerFrameHierarchyView.cs │ │ │ ├── ProfilerFrameViewBase.cs │ │ │ ├── ProfilerModuleTypeValidator.cs │ │ │ ├── ProfilerModules/ │ │ │ │ ├── Audio/ │ │ │ │ │ ├── AudioProfilerModule.cs │ │ │ │ │ └── AudioProfilerView.cs │ │ │ │ ├── Box2D/ │ │ │ │ │ └── Physics2DProfilerModule.cs │ │ │ │ ├── CPU/ │ │ │ │ │ ├── CPUProfilerModule.cs │ │ │ │ │ ├── FlowIndicatorDrawer.cs │ │ │ │ │ ├── FlowLinesDrawer.cs │ │ │ │ │ ├── ProfilerFrameTimingUtility.cs │ │ │ │ │ └── ProfilerTimelineGUI.cs │ │ │ │ ├── CPUorGPUProfilerModule.cs │ │ │ │ ├── DynamicProfilerModule.cs │ │ │ │ ├── FileIO/ │ │ │ │ │ ├── AssetLoadingProfilerModule.cs │ │ │ │ │ ├── AssetLoadingProfilerView.cs │ │ │ │ │ ├── AssetMarkerTreeView.cs │ │ │ │ │ ├── FileAccessTreeView.cs │ │ │ │ │ ├── FileIOProfilerModule.cs │ │ │ │ │ ├── FileIOProfilerView.cs │ │ │ │ │ ├── FileSummaryTreeView.cs │ │ │ │ │ └── LoadingProfilerViewBase.cs │ │ │ │ ├── GPU/ │ │ │ │ │ └── GPUProfilerModule.cs │ │ │ │ ├── GlobalIllumination/ │ │ │ │ │ └── GlobalIlluminationProfilerModule.cs │ │ │ │ ├── LegacyDetailsViewController.cs │ │ │ │ ├── Memory/ │ │ │ │ │ ├── MemoryElement.cs │ │ │ │ │ ├── MemoryElementDataManager.cs │ │ │ │ │ ├── MemoryProfilerModule.cs │ │ │ │ │ ├── MemorySnapshot.cs │ │ │ │ │ ├── MemoryTreeList.cs │ │ │ │ │ ├── ObjectMemoryInfo.cs │ │ │ │ │ └── ObjectMemoryStackInfo.cs │ │ │ │ ├── PhysX/ │ │ │ │ │ └── PhysicsProfilerModule.cs │ │ │ │ ├── ProfilerModule.cs │ │ │ │ ├── ProfilerModuleBase.cs │ │ │ │ ├── ProfilerModuleChartType.cs │ │ │ │ ├── ProfilerModuleMetadataAttribute.cs │ │ │ │ ├── ProfilerModuleViewController.cs │ │ │ │ ├── Rendering/ │ │ │ │ │ └── RenderingProfilerModule.cs │ │ │ │ ├── SampleSelection.cs │ │ │ │ ├── StandardDetailsViewController.cs │ │ │ │ ├── SwitchableLegacyDetailsViewController.cs │ │ │ │ ├── Video/ │ │ │ │ │ └── VideoProfilerModule.cs │ │ │ │ ├── VirtualTexturing/ │ │ │ │ │ ├── VirtualTexturingProfilerModule.cs │ │ │ │ │ └── VirtualTexturingProfilerView.cs │ │ │ │ └── uGui/ │ │ │ │ ├── UIDetailsProfilerModule.cs │ │ │ │ ├── UIProfilerModule.cs │ │ │ │ ├── UISystemProfiler.cs │ │ │ │ ├── UISystemProfilerChart.cs │ │ │ │ ├── UISystemProfilerThumbnailService.cs │ │ │ │ └── UISystemProfilerTreeView.cs │ │ │ ├── ProfilerModulesDropdownWindow.cs │ │ │ ├── ProfilerWindow.cs │ │ │ ├── UIElements/ │ │ │ │ ├── MemoryUsageBreakdown.cs │ │ │ │ ├── MemoryUsageBreakdownElement.cs │ │ │ │ └── SelectableLabel.cs │ │ │ ├── Utilities/ │ │ │ │ └── UIUtility.cs │ │ │ └── ViewControllerSystem/ │ │ │ ├── InvalidViewDefinedInUxmlException.cs │ │ │ ├── ViewController.cs │ │ │ └── ViewControllerUtility.cs │ │ └── Public/ │ │ ├── FrameDataView.bindings.cs │ │ ├── HierarchyFrameDataView.bindings.cs │ │ ├── NativeProfilerTimeline.bindings.cs │ │ ├── ProfilerAPI.bindings.cs │ │ ├── ProfilerFrameDataIterator.bindings.cs │ │ ├── ProfilerProperty.bindings.cs │ │ ├── ProfilerSettings.cs │ │ ├── ProfilerSettingsProvider.cs │ │ ├── ProfilingSessionMetaData.bindings.cs │ │ └── RawFrameDataView.bindings.cs │ ├── Progress/ │ │ ├── ProgressWindow.cs │ │ └── VisualProgressItem.cs │ ├── Properties/ │ │ └── Runtime/ │ │ ├── Algorithms/ │ │ │ ├── PropertyContainer+Accept.cs │ │ │ ├── PropertyContainer+GetProperty.cs │ │ │ ├── PropertyContainer+GetValue.cs │ │ │ ├── PropertyContainer+Path.cs │ │ │ ├── PropertyContainer+SetValue.cs │ │ │ ├── PropertyContainer.cs │ │ │ └── VisitReturnCode.cs │ │ ├── AssemblyInfo.cs │ │ ├── Attributes.cs │ │ ├── Exceptions.cs │ │ ├── Properties/ │ │ │ ├── AttributesScope.cs │ │ │ ├── DelegateProperty.cs │ │ │ ├── ICollectionElementProperty.cs │ │ │ ├── Internal/ │ │ │ │ └── IAttributes.cs │ │ │ ├── Property.cs │ │ │ ├── PropertyPath.cs │ │ │ └── ReflectedMemberProperty.cs │ │ ├── PropertyBags/ │ │ │ ├── ArrayPropertyBag.cs │ │ │ ├── ContainerPropertyBag.cs │ │ │ ├── DictionaryPropertyBag.cs │ │ │ ├── HashSetPropertyBag.cs │ │ │ ├── IPropertyBag.cs │ │ │ ├── IndexedCollectionPropertyBag.cs │ │ │ ├── Internal/ │ │ │ │ ├── PropertiesInitialization.cs │ │ │ │ └── PropertyBagStore.cs │ │ │ ├── KeyValueCollectionPropertyBag.cs │ │ │ ├── KeyValuePairPropertyBag.cs │ │ │ ├── ListPropertyBag.cs │ │ │ ├── PropertyBag+Accept.cs │ │ │ ├── PropertyBag+Registration.cs │ │ │ ├── PropertyBag+TypeConstruction.cs │ │ │ ├── PropertyBag.cs │ │ │ ├── PropertyCollection.cs │ │ │ └── SetPropertyBag.cs │ │ ├── PropertyVisitors/ │ │ │ ├── Adapters/ │ │ │ │ ├── IExcludePropertyAdapter.cs │ │ │ │ ├── IVisitPrimitivesPropertyAdapter.cs │ │ │ │ └── IVisitPropertyAdapter.cs │ │ │ ├── ConcreteTypeVisitor.cs │ │ │ ├── ExcludeContext.cs │ │ │ ├── IAccept.cs │ │ │ ├── IVisitor.cs │ │ │ ├── Internal/ │ │ │ │ └── ReadOnlyAdapterCollection.cs │ │ │ ├── PathVisitor.cs │ │ │ ├── PropertyVisitor.cs │ │ │ └── VisitContext.cs │ │ ├── Reflection/ │ │ │ └── Internal/ │ │ │ ├── ReflectedPropertyBag.cs │ │ │ ├── ReflectedPropertyBagProvider.cs │ │ │ └── ReflectionUtilities.cs │ │ └── Utility/ │ │ ├── TypeConversion.cs │ │ ├── TypeTraits.cs │ │ └── TypeUtility.cs │ ├── PropertiesEditor/ │ │ └── Module/ │ │ ├── AssemblyInfo.cs │ │ └── PropertiesEditorInitialization.cs │ ├── QuickSearch/ │ │ └── Editor/ │ │ ├── FuzzySearch.cs │ │ ├── ISearchView.cs │ │ ├── Indexing/ │ │ │ ├── AssetIndexer.cs │ │ │ ├── CustomIndexers.cs │ │ │ ├── IndexManager.cs │ │ │ ├── IndexerExtensions.cs │ │ │ ├── ObjectIndexer.cs │ │ │ ├── SearchDatabase.cs │ │ │ ├── SearchDatabaseException.cs │ │ │ ├── SearchDatabaseImporter.cs │ │ │ ├── SearchDocumentList.cs │ │ │ ├── SearchDocumentListTable.cs │ │ │ ├── SearchIndexComparer.cs │ │ │ ├── SearchIndexEntryHeapEnumerator.cs │ │ │ ├── SearchIndexEntryImporter.cs │ │ │ ├── SearchIndexer.cs │ │ │ ├── SearchIndexerQuery.cs │ │ │ ├── SearchNativeList.cs │ │ │ ├── SearchNativeReadOnlyArray.cs │ │ │ ├── SearchResultCollection.cs │ │ │ └── SearchTask.cs │ │ ├── PropertyDatabase/ │ │ │ ├── DefaultPropertyDatabaseSerializers.cs │ │ │ ├── PropertyDatabase.cs │ │ │ ├── PropertyDatabaseRecord.cs │ │ │ ├── PropertyDatabaseSerializer.cs │ │ │ ├── PropertyDatabaseStore.cs │ │ │ └── PropertyStringTable.cs │ │ ├── Providers/ │ │ │ ├── AdbProvider.cs │ │ │ ├── AssetProvider.cs │ │ │ ├── AssetStoreProvider.cs │ │ │ ├── BasePerformanceProvider.cs │ │ │ ├── Calculator.cs │ │ │ ├── FindProvider.cs │ │ │ ├── LogProvider.cs │ │ │ ├── MenuProvider.cs │ │ │ ├── ObjectQueryEngine.cs │ │ │ ├── PackageManagerProvider.cs │ │ │ ├── PerformanceProvider.cs │ │ │ ├── ProfilerMarkersProvider.cs │ │ │ ├── SceneProvider.cs │ │ │ ├── SceneQueryEngine.cs │ │ │ ├── SearchServiceProvider.cs │ │ │ ├── SettingsProvider.cs │ │ │ └── StaticMethodProvider.cs │ │ ├── QueryBuilder/ │ │ │ ├── Blocks/ │ │ │ │ ├── QueryAddNewBlock.cs │ │ │ │ ├── QueryAreaBlock.cs │ │ │ │ ├── QueryBlockEditor.cs │ │ │ │ ├── QueryExpressionBlock.cs │ │ │ │ ├── QueryFilterBlock.cs │ │ │ │ ├── QueryListBlock.cs │ │ │ │ ├── QueryListBlockAttribute.cs │ │ │ │ ├── QueryTextFieldBlock.cs │ │ │ │ └── QueryWordBlock.cs │ │ │ ├── IBlockEditor.cs │ │ │ ├── IQuerySource.cs │ │ │ ├── QueryBlock.cs │ │ │ ├── QueryBuilder.cs │ │ │ ├── QueryBuilderHelpers.cs │ │ │ ├── QueryEnginePropositionsExtensions.cs │ │ │ ├── QueryMarker.cs │ │ │ └── QuerySelector.cs │ │ ├── QueryEngine/ │ │ │ ├── AggregateEnumerable.cs │ │ │ ├── DefaultNestedQueryAggregators.cs │ │ │ ├── DefaultQueryFilterHandlers.cs │ │ │ ├── DefaultQueryHandler.cs │ │ │ ├── EnumerableCreator.cs │ │ │ ├── IInstruction.cs │ │ │ ├── IntersectionEnumerable.cs │ │ │ ├── NestedQueryEnumerable.cs │ │ │ ├── NestedQueryHandler.cs │ │ │ ├── ParsedQuery.cs │ │ │ ├── Query.Deprecated.cs │ │ │ ├── QueryEngine.Deprecated.cs │ │ │ ├── QueryEngine.cs │ │ │ ├── QueryEngineAttributes.cs │ │ │ ├── QueryEngineUtils.cs │ │ │ ├── QueryError.cs │ │ │ ├── QueryFilterOperations.cs │ │ │ ├── QueryFilterOperators.cs │ │ │ ├── QueryFilters.cs │ │ │ ├── QueryGraph.cs │ │ │ ├── QueryNodes.cs │ │ │ ├── QueryToggle.cs │ │ │ ├── QueryToken.cs │ │ │ ├── QueryTokenizer.cs │ │ │ ├── UnionEnumerable.cs │ │ │ └── WhereEnumerable.cs │ │ ├── SearchAction.cs │ │ ├── SearchAnalytics.cs │ │ ├── SearchContext.cs │ │ ├── SearchEngines/ │ │ │ ├── AdvancedObjectSelector.cs │ │ │ ├── DefaultAdvancedObjectSelector.cs │ │ │ └── SearchEngines.cs │ │ ├── SearchEnumerator.cs │ │ ├── SearchExpression/ │ │ │ ├── Evaluators/ │ │ │ │ ├── AggregateEvaluator.cs │ │ │ │ ├── AliasEvaluators.cs │ │ │ │ ├── CompareEvaluators.cs │ │ │ │ ├── ContextEvaluators.cs │ │ │ │ ├── ControlFlowEvaluators.cs │ │ │ │ ├── ConverterEvaluators.cs │ │ │ │ ├── EnvarEvaluators.cs │ │ │ │ ├── ExceptEvaluator.cs │ │ │ │ ├── GroupByEvaluator.cs │ │ │ │ ├── IntersectEvaluator.cs │ │ │ │ ├── MapEvaluator.cs │ │ │ │ ├── MathEvaluators.cs │ │ │ │ ├── PrimitiveEvaluators.cs │ │ │ │ ├── PrintEvaluator.cs │ │ │ │ ├── QueryEvaluator.cs │ │ │ │ ├── RandomEvaluator.cs │ │ │ │ ├── SelectEvaluator.cs │ │ │ │ ├── SortEvaluator.cs │ │ │ │ └── UnionEvaluator.cs │ │ │ ├── Parsers/ │ │ │ │ ├── AliasExpressionParser.cs │ │ │ │ ├── ConstantExpressionParser.cs │ │ │ │ ├── ExpandExpressionParser.cs │ │ │ │ ├── NamedExpressionParser.cs │ │ │ │ ├── QueryExpressionParser.cs │ │ │ │ ├── SelectorExpressionParser.cs │ │ │ │ └── SetExpressionParser.cs │ │ │ ├── ReflectionUtils.cs │ │ │ ├── ResultIterators.cs │ │ │ ├── SearchExpression.cs │ │ │ ├── SearchExpressionContext.cs │ │ │ ├── SearchExpressionEvaluator.cs │ │ │ ├── SearchExpressionParser.cs │ │ │ ├── SearchExpressionRuntime.cs │ │ │ ├── SearchExpressionValidator.cs │ │ │ ├── SearchItemQueryEngine.cs │ │ │ └── TaskEvaluatorManager.cs │ │ ├── SearchFilterEnumTypes.cs │ │ ├── SearchFlags.cs │ │ ├── SearchItem.cs │ │ ├── SearchList.cs │ │ ├── SearchMonitor.cs │ │ ├── SearchPreviewManager.cs │ │ ├── SearchProvider.cs │ │ ├── SearchQuery/ │ │ │ ├── SearchQuery.cs │ │ │ ├── SearchQueryAsset.cs │ │ │ ├── SearchQueryEditor.cs │ │ │ └── SearchTemplateAttribute.cs │ │ ├── SearchQueryError.cs │ │ ├── SearchSelection.cs │ │ ├── SearchService.cs │ │ ├── SearchSession.cs │ │ ├── SearchSettings.cs │ │ ├── SearchUtils.cs │ │ ├── Selectors/ │ │ │ ├── AssetSelectors.cs │ │ │ ├── ItemSelectors.cs │ │ │ ├── MaterialSelectors.cs │ │ │ ├── PropertySelectors.cs │ │ │ ├── SceneSelectors.cs │ │ │ ├── SearchColumn.cs │ │ │ └── SearchSelector.cs │ │ ├── Table/ │ │ │ ├── ColumnEditor.cs │ │ │ ├── ColumnSelector.cs │ │ │ ├── ITableView.cs │ │ │ ├── SearchReport.cs │ │ │ └── SearchTable.cs │ │ ├── Transactions/ │ │ │ ├── TimeRange.cs │ │ │ ├── Transaction.cs │ │ │ ├── TransactionManager.cs │ │ │ ├── TransactionUtils.cs │ │ │ └── TransactionViewer.cs │ │ ├── UI/ │ │ │ ├── Dispatcher.cs │ │ │ ├── IResultView.cs │ │ │ ├── Icons.cs │ │ │ ├── ObjectField.cs │ │ │ ├── SearchContextPropertyDrawer.cs │ │ │ ├── SearchEventManager.cs │ │ │ ├── SearchPickerWindow.cs │ │ │ ├── SearchProposition.cs │ │ │ ├── SearchViewState.cs │ │ │ └── UndoManager.cs │ │ ├── UITK/ │ │ │ ├── GridView.cs │ │ │ ├── ISearchPanel.cs │ │ │ ├── RenamableLabel.cs │ │ │ ├── SearchAutoCompleteWindow.cs │ │ │ ├── SearchBaseCollectionView.cs │ │ │ ├── SearchDetailView.cs │ │ │ ├── SearchElement.cs │ │ │ ├── SearchEmptyView.cs │ │ │ ├── SearchField.cs │ │ │ ├── SearchGlobalEventHandlerManager.cs │ │ │ ├── SearchGridView.cs │ │ │ ├── SearchGroupBar.cs │ │ │ ├── SearchItemEditorElement.cs │ │ │ ├── SearchListView.cs │ │ │ ├── SearchQueryBuilderView.cs │ │ │ ├── SearchQueryListView.cs │ │ │ ├── SearchQueryPanelView.cs │ │ │ ├── SearchStatusBar.cs │ │ │ ├── SearchTableView.cs │ │ │ ├── SearchTableViewCell.cs │ │ │ ├── SearchTableViewColumn.cs │ │ │ ├── SearchTableViewColumnSorter.cs │ │ │ ├── SearchToolbar.cs │ │ │ ├── SearchView.cs │ │ │ ├── SearchViewItem.cs │ │ │ └── SearchWindow.cs │ │ └── Utilities/ │ │ ├── BinarySearchFinder.cs │ │ ├── DebugTimer.cs │ │ ├── HashingUtils.cs │ │ ├── IStringView.cs │ │ ├── RaceConditionDetector.cs │ │ ├── RetriableOperation.cs │ │ ├── SearchDisposableTracker.cs │ │ ├── StringView.cs │ │ ├── SubsetStringView.cs │ │ ├── TryConvert.cs │ │ └── Utils.cs │ ├── SafeMode/ │ │ └── SafeModeToolbarWindow.cs │ ├── SceneTemplateEditor/ │ │ ├── DependencyListView.cs │ │ ├── GridView.cs │ │ ├── ListSelectionWindow.cs │ │ ├── ReferenceUtils.cs │ │ ├── ScenePicker.cs │ │ ├── SceneTemplateAnalytics.cs │ │ ├── SceneTemplateAsset.cs │ │ ├── SceneTemplateAssetInspectorWindow.cs │ │ ├── SceneTemplateDialog.cs │ │ ├── SceneTemplatePipeline.cs │ │ ├── SceneTemplatePreferences.cs │ │ ├── SceneTemplatePreviewArea.cs │ │ ├── SceneTemplateProjectSettings.cs │ │ ├── SceneTemplateService.cs │ │ ├── SceneTemplateUtils.cs │ │ ├── SnapshotUtils.cs │ │ ├── Styles.cs │ │ └── VisualSplitter.cs │ ├── SceneView/ │ │ ├── CameraOverlay.cs │ │ ├── DropdownToggle.cs │ │ ├── SceneViewLightingOverlays.cs │ │ └── SceneViewToolbarElements.cs │ ├── ScreenCapture/ │ │ └── ScriptBindings/ │ │ └── ScreenCapture.bindings.cs │ ├── ShaderFoundry/ │ │ └── ScriptBindings/ │ │ ├── AssemblyInfo.cs │ │ ├── Block.bindings.cs │ │ ├── BlockConstructorGenerator.bindings.cs │ │ ├── BlockLinkOverride.bindings.cs │ │ ├── BlockLinker.bindings.cs │ │ ├── BlockSequenceElement.bindings.cs │ │ ├── BlockShader.bindings.cs │ │ ├── BlockShaderInterface.bindings.cs │ │ ├── BlockVariable.bindings.cs │ │ ├── CopyRule.bindings.cs │ │ ├── CustomizationPoint.bindings.cs │ │ ├── CustomizationPointImplementation.bindings.cs │ │ ├── DataType.bindings.cs │ │ ├── DataTypeStatic.cs │ │ ├── DefaultContainer.cs │ │ ├── DefineDescriptor.bindings.cs │ │ ├── EqualityChecks.cs │ │ ├── FoundryAPIAttribute.cs │ │ ├── FoundryHandle.bindings.cs │ │ ├── FunctionParameter.bindings.cs │ │ ├── HandleList.bindings.cs │ │ ├── ITemplateGenerator.cs │ │ ├── ITemplateLinker.cs │ │ ├── IncludeDescriptor.bindings.cs │ │ ├── InterfaceRegistrationStatement.bindings.cs │ │ ├── InternalType.cs │ │ ├── KeywordDescriptor.bindings.cs │ │ ├── Namespace.bindings.cs │ │ ├── PackageRequirement.bindings.cs │ │ ├── PassStageType.bindings.cs │ │ ├── PragmaDescriptor.bindings.cs │ │ ├── PublicType.cs │ │ ├── RegisterTemplatesWithInterface.bindings.cs │ │ ├── RenderStateDescriptor.bindings.cs │ │ ├── ShaderAttribute.bindings.cs │ │ ├── ShaderAttributeParameter.bindings.cs │ │ ├── ShaderBuilder.cs │ │ ├── ShaderContainer.bindings.cs │ │ ├── ShaderCustomEditor.bindings.cs │ │ ├── ShaderDependency.bindings.cs │ │ ├── ShaderFunction.bindings.cs │ │ ├── ShaderType.bindings.cs │ │ ├── StageDescription.bindings.cs │ │ ├── StructField.bindings.cs │ │ ├── TagDescriptor.bindings.cs │ │ ├── Template.bindings.cs │ │ ├── TemplateInstance.bindings.cs │ │ ├── TemplatePass.bindings.cs │ │ └── Utilities.cs │ ├── SharedInternals/ │ │ ├── Attributes.cs │ │ ├── BindingsAttributes.cs │ │ └── UnityString.cs │ ├── ShortcutManager/ │ │ └── ShortcutManagerWindowView.cs │ ├── ShortcutManagerEditor/ │ │ ├── BindingValidator.cs │ │ ├── ConflictResolver.cs │ │ ├── ConflictResolverWindow.cs │ │ ├── ContextManager.cs │ │ ├── DeleteShortcutProfileWindow.cs │ │ ├── Directory.cs │ │ ├── Discovery.cs │ │ ├── DiscoveryInvalidShortcutReporter.cs │ │ ├── FormerlyPrefKeyAsAttribute.cs │ │ ├── IConflictResolver.cs │ │ ├── IDirectory.cs │ │ ├── IDiscovery.cs │ │ ├── IShortcutContext.cs │ │ ├── KeyCombination.cs │ │ ├── PriorityContextAttribute.cs │ │ ├── PromptWindow.cs │ │ ├── ReserveModifiersAttribute.cs │ │ ├── ShortcutAttribute.cs │ │ ├── ShortcutAttributeDiscoveryProvider.cs │ │ ├── ShortcutBinding.cs │ │ ├── ShortcutController.cs │ │ ├── ShortcutEntry.cs │ │ ├── ShortcutHelperBar.cs │ │ ├── ShortcutHelperBarUtility.cs │ │ ├── ShortcutManager.cs │ │ ├── ShortcutManagerWindow.cs │ │ ├── ShortcutManagerWindowViewController.cs │ │ ├── ShortcutProfile.cs │ │ ├── ShortcutProfileManager.cs │ │ ├── ShortcutProfileStore.cs │ │ └── Trigger.cs │ ├── SketchUpEditor/ │ │ └── Mono/ │ │ ├── SketchUp.bindings.cs │ │ ├── SketchUpImportDlg.cs │ │ ├── SketchUpImporterEditor.cs │ │ └── SketchUpImporterModelEditor.cs │ ├── SpriteMask/ │ │ └── Public/ │ │ └── ScriptBindings/ │ │ └── SpriteMask.bindings.cs │ ├── SpriteMaskEditor/ │ │ └── Editor/ │ │ └── Managed/ │ │ └── SpriteMaskEditor.cs │ ├── SpriteShape/ │ │ └── Public/ │ │ └── ScriptBindings/ │ │ ├── SpriteShapeRenderer.bindings.cs │ │ └── SpriteShapeUtility.bindings.cs │ ├── SpriteShapeEditor/ │ │ └── Managed/ │ │ └── SpriteShapeRendererEditor.cs │ ├── Streaming/ │ │ └── ScriptBindings/ │ │ └── StreamingController.bindings.cs │ ├── StyleSheetsEditor/ │ │ ├── Converters/ │ │ │ ├── ConverterUtils.cs │ │ │ ├── GUISkinCompare.cs │ │ │ ├── GUISkinToStyleSheet.cs │ │ │ ├── GUIStyleExtensions.cs │ │ │ ├── Graph.cs │ │ │ ├── StyleCatalogToSkin.cs │ │ │ ├── StyleSheetBuilderHelper.cs │ │ │ ├── StyleSheetCache.cs │ │ │ ├── StyleSheetResolver.cs │ │ │ ├── StyleSheetSplitter.cs │ │ │ └── StyleSheetToUss.cs │ │ ├── StyleCatalog.cs │ │ └── StylePainter.cs │ ├── Substance/ │ │ ├── SubstanceUtility.bindings.cs │ │ └── SubstanceUtility.cs │ ├── Subsystems/ │ │ ├── Example/ │ │ │ ├── ExampleSubsystem.bindings.cs │ │ │ └── ExampleSubsystemDescriptor.bindings.cs │ │ ├── ISubsystem.cs │ │ ├── ISubsystemDescriptor.cs │ │ ├── IntegratedSubsystem.bindings.cs │ │ ├── IntegratedSubsystem.deprecated.cs │ │ ├── IntegratedSubsystemDescriptor.bindings.cs │ │ ├── Subsystem.deprecated.cs │ │ ├── SubsystemDescriptor.deprecated.cs │ │ ├── SubsystemDescriptorStore.bindings.cs │ │ ├── SubsystemDescriptorStore.cs │ │ ├── SubsystemDescriptorStore.deprecated.cs │ │ ├── SubsystemDescriptorWithProvider.cs │ │ ├── SubsystemManager.bindings.cs │ │ ├── SubsystemManager.cs │ │ ├── SubsystemManager.deprecated.cs │ │ ├── SubsystemProvider.cs │ │ ├── SubsystemProxy.cs │ │ └── SubsystemWithProvider.cs │ ├── Terrain/ │ │ └── Public/ │ │ ├── BrushTransform.cs │ │ ├── PaintContext.cs │ │ ├── SpeedTreeWind.bindings.cs │ │ ├── Terrain.bindings.cs │ │ ├── Terrain.deprecated.cs │ │ ├── TerrainCallbacks.cs │ │ ├── TerrainData.GPUCopy.cs │ │ ├── TerrainData.bindings.cs │ │ ├── TerrainLayer.bindings.cs │ │ ├── TerrainPaintUtility.cs │ │ └── TerrainUtility.cs │ ├── TerrainEditor/ │ │ ├── AssemblyInfo.cs │ │ ├── Brush/ │ │ │ ├── Brush.cs │ │ │ ├── BrushEditor.cs │ │ │ └── BrushList.cs │ │ ├── HeightmapFilters.cs │ │ ├── Overlays/ │ │ │ ├── BrushAttributes.cs │ │ │ ├── BrushesOverlay.cs │ │ │ ├── CondensedSlider.cs │ │ │ ├── TerrainTransientToolbarOverlay.cs │ │ │ └── ToolEditors.cs │ │ ├── PaintTools/ │ │ │ ├── CreateTerrainTool.cs │ │ │ ├── PaintDetailsTool.cs │ │ │ ├── PaintDetailsToolUtility.cs │ │ │ ├── PaintHeightTool.cs │ │ │ ├── PaintHolesTool.cs │ │ │ ├── PaintTextureTool.cs │ │ │ ├── PaintTreesTool.cs │ │ │ ├── SetHeightTool.cs │ │ │ ├── SmoothHeightTool.cs │ │ │ ├── StampTool.cs │ │ │ ├── TerrainPaintTool.cs │ │ │ └── TerrainPaintToolUtility.cs │ │ ├── TerrainInspector.cs │ │ ├── TerrainWizards.cs │ │ ├── TreeAOImporter.cs │ │ └── Utilities/ │ │ ├── TerrainEditorUtility.cs │ │ ├── TerrainLayerInspector.cs │ │ ├── TerrainLayerUtility.cs │ │ ├── TerrainMenus.cs │ │ └── TerrainPaintUtilityEditor.cs │ ├── TerrainPhysics/ │ │ └── TerrainPhysics.bindings.cs │ ├── TextCoreFontEngine/ │ │ └── Managed/ │ │ ├── AssemblyInfo.cs │ │ ├── FaceInfo.cs │ │ ├── FontEngine.bindings.cs │ │ ├── FontEngineMarshallingCommon.cs │ │ ├── FontFeatureCommon.cs │ │ ├── FontFeatureCommonGPOS.cs │ │ ├── FontFeatureCommonGSUB.cs │ │ └── Glyph.cs │ ├── TextCoreFontEngineEditor/ │ │ └── Managed/ │ │ ├── AssemblyInfo.cs │ │ └── FontEngineEditor.bindings.cs │ ├── TextCoreTextEngine/ │ │ └── Managed/ │ │ ├── ATGMeshInfo.cs │ │ ├── AssemblyInfo.cs │ │ ├── Character.cs │ │ ├── ColorUtilities.cs │ │ ├── FastAction.cs │ │ ├── FontFeatureCommon.cs │ │ ├── FontFeatureTable.cs │ │ ├── LineInfo.cs │ │ ├── LinkInfo.cs │ │ ├── MaterialManager.cs │ │ ├── MaterialReference.cs │ │ ├── MaterialReferenceManager.cs │ │ ├── MeshInfo.bindings.cs │ │ ├── MeshInfo.cs │ │ ├── NativeTextElementInfo.cs │ │ ├── NativeTextInfo.cs │ │ ├── TextAssets/ │ │ │ ├── FontAsset/ │ │ │ │ ├── FontAssetAtlasPopulation.cs │ │ │ │ ├── FontAssetFactory.cs │ │ │ │ ├── FontAssetFontFeatures.cs │ │ │ │ └── NativeFontAsset.cs │ │ │ ├── FontAsset.cs │ │ │ ├── FontAssetUtilities.cs │ │ │ ├── SpriteAsset.cs │ │ │ ├── SpriteCharacter.cs │ │ │ ├── SpriteGlyph.cs │ │ │ ├── TextAsset.cs │ │ │ ├── TextColorGradient.cs │ │ │ ├── TextSettings.cs │ │ │ ├── TextStyle.cs │ │ │ └── TextStyleSheet.cs │ │ ├── TextCoreVertex.bindings.cs │ │ ├── TextElement.cs │ │ ├── TextElementInfo.cs │ │ ├── TextEventManager.cs │ │ ├── TextGenerator/ │ │ │ ├── NativeTextGenerationSettings.bindings.cs │ │ │ ├── RichTextTagParser.cs │ │ │ ├── TextGenerationSettings.cs │ │ │ ├── TextGeneratorCommon.cs │ │ │ ├── TextGeneratorHtmlTagValidation.cs │ │ │ ├── TextGeneratorInternal.cs │ │ │ ├── TextGeneratorLayout.cs │ │ │ ├── TextGeneratorParsing.cs │ │ │ ├── TextGeneratorPreferredValues.cs │ │ │ ├── TextGeneratorPrepare.cs │ │ │ ├── TextGeneratorUtilities.cs │ │ │ ├── TextLib.bindings.cs │ │ │ └── TextSelectionService.bindings.cs │ │ ├── TextGenerator.cs │ │ ├── TextHandle.cs │ │ ├── TextHandlePermanentCache.cs │ │ ├── TextHandleTemporaryCache.cs │ │ ├── TextInfo.cs │ │ ├── TextMarkupTagsCommon.cs │ │ ├── TextProcessingCommon.cs │ │ ├── TextProcessingStacks.cs │ │ ├── TextResourcesManager.cs │ │ ├── TextShaderUtilities.cs │ │ ├── TextUtilities.cs │ │ └── UnicodeLineBreakingRules.cs │ ├── TextCoreTextEngineEditor/ │ │ └── Managed/ │ │ ├── AssemblyInfo.cs │ │ ├── EditorShaderUtilities.cs │ │ ├── FontAssetCreationMenu.cs │ │ ├── FontAssetCreatorWindow.cs │ │ ├── FontAssetEditor.cs │ │ ├── FontAssetEditorUtilities.cs │ │ ├── ICUDataAssetUtilities.cs │ │ ├── PropertyDrawers/ │ │ │ ├── CharacterPropertyDrawer.cs │ │ │ ├── GlyphMetricsPropertyDrawer.cs │ │ │ ├── GlyphPairAdjustmentRecordPropertyDrawer.cs │ │ │ ├── GlyphPropertyDrawer.cs │ │ │ ├── GlyphRectPropertyDrawer.cs │ │ │ ├── LigatureSubstitutionRecordPropertyDrawer.cs │ │ │ ├── MarkToBaseAdjustmentRecordPropertyDrawer.cs │ │ │ ├── MarkToMarkAdjustmentRecordPropertyDrawer.cs │ │ │ ├── SpriteCharacterPropertyDrawer.cs │ │ │ ├── SpriteGlyphPropertyDrawer.cs │ │ │ ├── TextCorePropertyDrawerUtilities.cs │ │ │ └── UnicodeLineBreakingRulesPropertyDrawer.cs │ │ ├── SerializedPropertyHolder.cs │ │ ├── SpriteAssetCreationMenu.cs │ │ ├── SpriteAssetImportFormats.cs │ │ ├── SpriteAssetImporter.cs │ │ ├── TextAssetPostProcessor.cs │ │ ├── TextColorGradientAssetCreationMenu.cs │ │ ├── TextColorGradientEditor.cs │ │ ├── TextCoreContextMenuItems.cs │ │ ├── TextCoreEditorUtilities.cs │ │ ├── TextCorePreBuildProcessor.cs │ │ ├── TextCoreShaderGUI.cs │ │ ├── TextCoreShaderGUIBitmap.cs │ │ ├── TextCoreShaderGUISDF.cs │ │ ├── TextEditorResourceManager.cs │ │ ├── TextSettingsCreationMenu.cs │ │ ├── TextSettingsEditor.cs │ │ ├── TextSpriteAssetEditor.cs │ │ ├── TextStyleAssetCreationMenu.cs │ │ └── TextStyleSheetEditor.cs │ ├── TextRendering/ │ │ ├── FontStyle.cs │ │ ├── GUIText.deprecated.cs │ │ ├── TextGenerator.cs │ │ └── TextRendering.bindings.cs │ ├── TextRenderingEditor/ │ │ ├── TextRenderingCommands.cs │ │ ├── TrueTypeFontImporter.bindings.cs │ │ └── TrueTypeFontImporterInspector.cs │ ├── Tilemap/ │ │ ├── Managed/ │ │ │ ├── CustomGridBrushAttribute.cs │ │ │ ├── GridBrushBase.cs │ │ │ ├── ITilemap.cs │ │ │ ├── Tile.cs │ │ │ ├── TileBase.cs │ │ │ └── Tilemap.cs │ │ └── ScriptBindings/ │ │ └── Tilemap.bindings.cs │ ├── TilemapEditor/ │ │ └── Editor/ │ │ └── Managed/ │ │ ├── EditorPreviewTilemap.cs │ │ ├── Grid/ │ │ │ └── GridPalette.cs │ │ ├── TileBaseEditor.cs │ │ ├── TileEditor.cs │ │ ├── TilemapCollider2DEditor.cs │ │ ├── TilemapEditor.cs │ │ └── TilemapRendererEditor.cs │ ├── TreeEditor/ │ │ ├── Includes/ │ │ │ ├── Perlin.cs │ │ │ ├── RingLoop.cs │ │ │ ├── SplineNode.cs │ │ │ ├── TextureAtlas.cs │ │ │ ├── TreeAOSphere.cs │ │ │ ├── TreeAttribute.cs │ │ │ ├── TreeGroup.cs │ │ │ ├── TreeGroupBranch.cs │ │ │ ├── TreeGroupLeaf.cs │ │ │ ├── TreeGroupRoot.cs │ │ │ ├── TreeMaterial.cs │ │ │ ├── TreeNode.cs │ │ │ ├── TreeSpline.cs │ │ │ ├── TreeTriangle.cs │ │ │ └── TreeVertex.cs │ │ ├── TreeData.cs │ │ ├── TreeEditor.cs │ │ └── TreeEditorHelper.cs │ ├── UI/ │ │ └── ScriptBindings/ │ │ ├── CanvasGroup.bindings.cs │ │ ├── CanvasRenderer.bindings.cs │ │ ├── RectTransformUtil.bindings.cs │ │ ├── RectTransformUtility.cs │ │ └── UICanvas.bindings.cs │ ├── UIAutomationEditor/ │ │ ├── AutomatedWindow.cs │ │ ├── ClickOverTime.cs │ │ ├── DragOverTime.cs │ │ ├── EventUtility.cs │ │ ├── FakeCursor.cs │ │ ├── KeyInputOverTime.cs │ │ └── TestEditorWindow.cs │ ├── UIBuilder/ │ │ └── Editor/ │ │ ├── Builder/ │ │ │ ├── Builder.cs │ │ │ ├── BuilderBindingsCache.cs │ │ │ ├── BuilderEditorInitialization.cs │ │ │ ├── BuilderExternalPackages.cs │ │ │ ├── BuilderPane.cs │ │ │ ├── BuilderPaneContent.cs │ │ │ ├── BuilderPaneWindow.cs │ │ │ ├── BuilderSelection.cs │ │ │ ├── Controllers/ │ │ │ │ └── UxmlBatchedChangesController.cs │ │ │ ├── Document/ │ │ │ │ ├── BuilderDocument.cs │ │ │ │ ├── BuilderDocumentOpenUSS.cs │ │ │ │ ├── BuilderDocumentOpenUXML.cs │ │ │ │ ├── BuilderDocumentSettings.cs │ │ │ │ └── BuilderUXMLFileSettings.cs │ │ │ ├── Draggers/ │ │ │ │ ├── BuilderClassDragger.cs │ │ │ │ ├── BuilderDragger.cs │ │ │ │ ├── BuilderExplorerDragger.cs │ │ │ │ ├── BuilderHierarchyDragger.cs │ │ │ │ ├── BuilderLibraryDragger.cs │ │ │ │ ├── BuilderStyleSheetsDragger.cs │ │ │ │ └── BuilderViewportDragger.cs │ │ │ ├── Explorer/ │ │ │ │ ├── BuilderExplorer.cs │ │ │ │ ├── BuilderExplorerItem.cs │ │ │ │ ├── BuilderHierarchy.cs │ │ │ │ ├── BuilderHierarchyUtilities.cs │ │ │ │ ├── BuilderHierarchyWindow.cs │ │ │ │ ├── BuilderNewSelectorField.cs │ │ │ │ ├── BuilderStyleSheets.cs │ │ │ │ ├── BuilderStyleSheetsContextMenu.cs │ │ │ │ ├── BuilderStyleSheetsUtilities.cs │ │ │ │ └── BuilderStyleSheetsWindow.cs │ │ │ ├── Inspector/ │ │ │ │ ├── Binding/ │ │ │ │ │ ├── BindingCompatibilityStatus.cs │ │ │ │ │ ├── BindingConverterGroupDetailsView.cs │ │ │ │ │ ├── BindingConverterGroupViewItem.cs │ │ │ │ │ ├── BindingConvertersField.cs │ │ │ │ │ ├── BuilderBindingPropertyDrawers.cs │ │ │ │ │ ├── BuilderBindingUtility.cs │ │ │ │ │ ├── BuilderBindingUxmlAtttributesView.cs │ │ │ │ │ ├── BuilderBindingView.cs │ │ │ │ │ ├── BuilderBindingWindow.cs │ │ │ │ │ ├── BuilderDataSourceAndPathView.cs │ │ │ │ │ ├── BuilderDataSourcePathCompleter.cs │ │ │ │ │ ├── BuilderPropertyPathInfoDetailsView.cs │ │ │ │ │ ├── BuilderPropertyPathInfoViewItem.cs │ │ │ │ │ └── ShowOnlyCompatibleResultsToggle.cs │ │ │ │ ├── BuilderInspector.cs │ │ │ │ ├── BuilderInspectorAttributes.cs │ │ │ │ ├── BuilderInspectorCanvas.cs │ │ │ │ ├── BuilderInspectorHeader.cs │ │ │ │ ├── BuilderInspectorInheritedStyles.cs │ │ │ │ ├── BuilderInspectorLocalStyles.cs │ │ │ │ ├── BuilderInspectorMatchingSelectors.cs │ │ │ │ ├── BuilderInspectorPreview.cs │ │ │ │ ├── BuilderInspectorPreviewWindow.cs │ │ │ │ ├── BuilderInspectorStyleFields+Transitions.cs │ │ │ │ ├── BuilderInspectorStyleFields.cs │ │ │ │ ├── BuilderInspectorStyleSheet.cs │ │ │ │ ├── BuilderInspectorVariables.cs │ │ │ │ ├── BuilderInspectorVariablesListItem.cs │ │ │ │ ├── BuilderInspectorWindow.cs │ │ │ │ ├── BuilderNewClassWindow.cs │ │ │ │ ├── BuilderStyleRow.cs │ │ │ │ ├── BuilderStyleSheetsNewSelectorHelpTips.cs │ │ │ │ └── IBuilderInspectorSection.cs │ │ │ ├── Library/ │ │ │ │ ├── BuilderLibrary.cs │ │ │ │ ├── BuilderLibraryContent.cs │ │ │ │ ├── BuilderLibraryPlainView.cs │ │ │ │ ├── BuilderLibraryProjectScanner.cs │ │ │ │ ├── BuilderLibraryTreeItem.cs │ │ │ │ ├── BuilderLibraryTreeView.cs │ │ │ │ ├── BuilderLibraryView.cs │ │ │ │ └── BuilderLibraryWindow.cs │ │ │ ├── Manipulators/ │ │ │ │ ├── BuilderAnchorer.cs │ │ │ │ ├── BuilderManipulator.cs │ │ │ │ ├── BuilderMover.cs │ │ │ │ ├── BuilderParentTracker.cs │ │ │ │ ├── BuilderPlacementIndicator.cs │ │ │ │ ├── BuilderResizer.cs │ │ │ │ ├── BuilderSelectionIndicator.cs │ │ │ │ ├── BuilderTracker.cs │ │ │ │ └── BuilderTransformer.cs │ │ │ ├── Previews/ │ │ │ │ ├── BuilderCodePreview.cs │ │ │ │ ├── BuilderPreviewWindow.cs │ │ │ │ ├── BuilderTooltipPreview.cs │ │ │ │ ├── BuilderUssPreview.cs │ │ │ │ ├── BuilderUssPreviewWindow.cs │ │ │ │ ├── BuilderUxmlPreview.cs │ │ │ │ └── BuilderUxmlPreviewWindow.cs │ │ │ ├── Toolbar/ │ │ │ │ └── BuilderToolbar.cs │ │ │ ├── Utilities/ │ │ │ │ ├── BuilderAnalyticsUtility.cs │ │ │ │ ├── BuilderAssetModificationProcessor.cs │ │ │ │ ├── BuilderAssetPostprocessor.cs │ │ │ │ ├── BuilderAssetUtilities.cs │ │ │ │ ├── BuilderCommandHandler.cs │ │ │ │ ├── BuilderConstants.cs │ │ │ │ ├── BuilderDialogsUtility.cs │ │ │ │ ├── BuilderEditorUtility.cs │ │ │ │ ├── BuilderElementContextMenu.cs │ │ │ │ ├── BuilderInspectorUtilities.cs │ │ │ │ ├── BuilderNameUtilities.cs │ │ │ │ ├── BuilderPackageUtilities.cs │ │ │ │ ├── BuilderPlacementUtilities.cs │ │ │ │ ├── BuilderProjectSettings.cs │ │ │ │ ├── BuilderSharedStyles.cs │ │ │ │ └── BuilderStyleUtilities.cs │ │ │ ├── UxmlAttributesView/ │ │ │ │ ├── BuilderUxmlAttributesView.cs │ │ │ │ ├── Fields/ │ │ │ │ │ ├── BuilderTypeField.cs │ │ │ │ │ ├── BuilderUxmlAssetAttributeField.cs │ │ │ │ │ ├── BuilderUxmlAttributeFieldFactory.cs │ │ │ │ │ ├── BuilderUxmlAttributeFieldFactoryRegistry.cs │ │ │ │ │ ├── BuilderUxmlEnumAttributeField.cs │ │ │ │ │ ├── BuilderUxmlImageAttributeField.cs │ │ │ │ │ ├── BuilderUxmlIntAttributeField.cs │ │ │ │ │ ├── BuilderUxmlStringAttributeField.cs │ │ │ │ │ ├── BuilderUxmlTypeAttributeField.cs │ │ │ │ │ └── UxmlAttributeFieldPropertyDrawers.cs │ │ │ │ └── IBatchedUxmlChangesListener.cs │ │ │ └── Viewport/ │ │ │ ├── BuilderCanvas.cs │ │ │ ├── BuilderCanvasStyleControls.cs │ │ │ ├── BuilderInPlaceTextEditingUtilities.cs │ │ │ ├── BuilderNotifications.cs │ │ │ ├── BuilderPanner.cs │ │ │ ├── BuilderViewport.cs │ │ │ ├── BuilderViewportWindow.cs │ │ │ ├── BuilderVisualTreeStyleUpdaterTraversal.cs │ │ │ ├── BuilderZoomer.cs │ │ │ └── IBuilderViewportWindow.cs │ │ ├── UIElementsViewImporterEditor.cs │ │ └── Utilities/ │ │ ├── Background/ │ │ │ └── CheckerboardBackground.cs │ │ ├── CategoryDropdownField/ │ │ │ ├── CategoryDropdownContent.cs │ │ │ ├── CategoryDropdownField+WindowContent.cs │ │ │ └── CategoryDropdownField.cs │ │ ├── ElementHierarchyView/ │ │ │ ├── BaseOverlayPainter.cs │ │ │ ├── ElementHierarchyView.cs │ │ │ ├── HighlightOverlayPainter.cs │ │ │ ├── LayoutOverlayPainter.cs │ │ │ ├── OverlayPainterHelperElement.cs │ │ │ └── RepaintOverlayPainter.cs │ │ ├── FoldoutField/ │ │ │ ├── FoldoutColorField.cs │ │ │ ├── FoldoutField.cs │ │ │ ├── FoldoutNumberField.cs │ │ │ └── FoldoutTransitionField.cs │ │ ├── FoldoutWithCheckbox/ │ │ │ └── FoldoutWithCheckbox.cs │ │ ├── FontStyleStrip/ │ │ │ └── FontStyleStrip.cs │ │ ├── HelpBox/ │ │ │ └── HelpBox.cs │ │ ├── Importers/ │ │ │ ├── BuilderStyleSheetImporter.cs │ │ │ └── BuilderVisualTreeAssetImporter.cs │ │ ├── LibraryFoldout/ │ │ │ └── LibraryFoldout.cs │ │ ├── ModalPopup/ │ │ │ └── ModalPopup.cs │ │ ├── MultiTypeField/ │ │ │ └── MultiTypeField.cs │ │ ├── PercentSlider/ │ │ │ └── PercentSlider.cs │ │ ├── PersistedFoldout/ │ │ │ ├── BuilderCategoryPersistedFoldout.cs │ │ │ └── PersistedFoldout.cs │ │ ├── ReflectionExtensions/ │ │ │ └── ReflectionExtensions.cs │ │ ├── Selector/ │ │ │ └── SelectorUtility.cs │ │ ├── StringExtensions/ │ │ │ └── StringExtensions.cs │ │ ├── StyleField/ │ │ │ ├── AngleStyleField.cs │ │ │ ├── AssetReferenceStyleField.cs │ │ │ ├── BackgroundPositionDimensionStyleField.cs │ │ │ ├── BackgroundPositionStyleField.cs │ │ │ ├── BackgroundRepeatStyleField.cs │ │ │ ├── BackgroundSizeStyleField.cs │ │ │ ├── BorderBoxModelView.cs │ │ │ ├── BoxModelElement.cs │ │ │ ├── BoxModelStyleField.cs │ │ │ ├── BoxType.cs │ │ │ ├── CursorStyleField.cs │ │ │ ├── DimensionStyleField.cs │ │ │ ├── FieldSearchCompleter.cs │ │ │ ├── FieldStatusIndicator.cs │ │ │ ├── FieldValueInfo.cs │ │ │ ├── FontDefinitionStyleField.cs │ │ │ ├── ImageStyleField.cs │ │ │ ├── IntegerStyleField.cs │ │ │ ├── NumericStyleField.cs │ │ │ ├── PositionAnchorPoint.cs │ │ │ ├── PositionAnchors.cs │ │ │ ├── PositionSection.cs │ │ │ ├── PositionStyleField.cs │ │ │ ├── RotateStyleField.cs │ │ │ ├── ScaleStyleField.cs │ │ │ ├── SpacingBoxModelView.cs │ │ │ ├── StyleField.cs │ │ │ ├── StyleFieldConstants.cs │ │ │ ├── StyleFieldPopup.cs │ │ │ ├── StyleFieldPopupWindow.cs │ │ │ ├── StyleVariableUtilities.cs │ │ │ ├── TextAutoSizeStyleField.cs │ │ │ ├── TextShadowStyleField.cs │ │ │ ├── TransformOriginSelector.cs │ │ │ ├── TransformOriginStyleField.cs │ │ │ ├── TranslateStyleField.cs │ │ │ ├── USSVariablesStyleField.cs │ │ │ ├── VariableCompleter.cs │ │ │ ├── VariableEditingHandler.cs │ │ │ ├── VariableField.cs │ │ │ ├── VariableInfo.cs │ │ │ ├── VariableInfoTooltip.cs │ │ │ └── VariableInfoView.cs │ │ ├── StyleSheetExtensions/ │ │ │ ├── StyleComplexSelectorExtensions.cs │ │ │ ├── StylePropertyExtensions.cs │ │ │ ├── StylePropertyManipulator.cs │ │ │ ├── StyleRuleExtensions.cs │ │ │ ├── StyleSheetExtensions.cs │ │ │ ├── StyleSheetToUss.cs │ │ │ ├── StyleSheetUtilities.cs │ │ │ └── StyleValueHandleExtensions.cs │ │ ├── TextAlignStrip/ │ │ │ └── TextAlignStrip.cs │ │ ├── Transitions/ │ │ │ ├── BuilderTransition.cs │ │ │ ├── BuilderTransitionData.cs │ │ │ ├── TransitionChangeType.cs │ │ │ ├── TransitionEvents.cs │ │ │ ├── TransitionListView.cs │ │ │ ├── TransitionPropertyDropdownContent.cs │ │ │ ├── TransitionsExtensions.cs │ │ │ └── UIStyleValue.cs │ │ ├── TypeExtensions/ │ │ │ └── TypeExtensions.cs │ │ ├── TypeField/ │ │ │ └── BuilderAttributeTypeName.cs │ │ ├── UnityUIBuilderSelectionMarker/ │ │ │ └── UnityUIBuilderSelectionMarker.cs │ │ ├── VisualElementExtensions/ │ │ │ ├── VersionChangeTypeUtility.cs │ │ │ └── VisualElementExtensions.cs │ │ └── VisualTreeAssetExtensions/ │ │ ├── IUxmlFactoryExtensions.cs │ │ ├── TemplateAssetExtensions.cs │ │ ├── ValueToUXML.cs │ │ ├── VisualElementAssetExtensions.cs │ │ ├── VisualTreeAssetExtensions.cs │ │ ├── VisualTreeAssetLinkedCloneTree.cs │ │ ├── VisualTreeAssetToUXML.cs │ │ └── VisualTreeAssetUtilities.cs │ ├── UIElements/ │ │ ├── AssemblyInfo.cs │ │ ├── Core/ │ │ │ ├── AlignmentUtils.cs │ │ │ ├── Atlas.cs │ │ │ ├── BackgroundPosition.PropertyBag.cs │ │ │ ├── BackgroundPosition.cs │ │ │ ├── BackgroundPropertyHelper.cs │ │ │ ├── BackgroundRepeat.PropertyBag.cs │ │ │ ├── BackgroundRepeat.cs │ │ │ ├── BackgroundSize.PropertyBag.cs │ │ │ ├── BackgroundSize.cs │ │ │ ├── BindableElement.cs │ │ │ ├── Bindings/ │ │ │ │ ├── Binding.Factory.cs │ │ │ │ ├── Binding.cs │ │ │ │ ├── BindingActivationContext.cs │ │ │ │ ├── BindingContext.cs │ │ │ │ ├── BindingResult.cs │ │ │ │ ├── BindingTarget.cs │ │ │ │ ├── BindingUpdater.cs │ │ │ │ ├── ConverterGroup.cs │ │ │ │ ├── ConverterGroups.cs │ │ │ │ ├── CustomBinding.Factory.cs │ │ │ │ ├── CustomBinding.cs │ │ │ │ ├── DataBinding.Factory.cs │ │ │ │ ├── DataBinding.cs │ │ │ │ ├── DataBindingManager.cs │ │ │ │ ├── DataBindingUtility.cs │ │ │ │ ├── DataSourceContext.cs │ │ │ │ ├── IDataSourceProvider.cs │ │ │ │ ├── IDataSourceViewHashProvider.cs │ │ │ │ ├── INotifyBindablePropertyChanged.cs │ │ │ │ ├── Utils/ │ │ │ │ │ ├── AutoCompletePathVisitor.cs │ │ │ │ │ └── TypePathVisitor.cs │ │ │ │ ├── VisualTreeBindingsUpdater.cs │ │ │ │ └── VisualTreeDataBindingUpdater.cs │ │ │ ├── ClampedDragger.cs │ │ │ ├── ClickDetector.cs │ │ │ ├── Clickable.cs │ │ │ ├── Collections/ │ │ │ │ ├── Controllers/ │ │ │ │ │ ├── BaseListViewController.cs │ │ │ │ │ ├── BaseTreeViewController.cs │ │ │ │ │ ├── CollectionViewController.cs │ │ │ │ │ ├── DefaultMultiColumnTreeViewController.cs │ │ │ │ │ ├── DefaultTreeViewController.cs │ │ │ │ │ ├── IDefaultTreeViewController.cs │ │ │ │ │ ├── ISerializedObjectList.cs │ │ │ │ │ ├── ListViewController.cs │ │ │ │ │ ├── MultiColumnListViewController.cs │ │ │ │ │ ├── MultiColumnTreeViewController.cs │ │ │ │ │ ├── TreeDataController.cs │ │ │ │ │ └── TreeViewController.cs │ │ │ │ ├── DictionaryExtensions.cs │ │ │ │ └── Virtualization/ │ │ │ │ ├── CollectionVirtualizationController.cs │ │ │ │ ├── DynamicHeightVirtualizationController.cs │ │ │ │ ├── FixedHeightVirtualizationController.cs │ │ │ │ ├── ReusableCollectionItem.cs │ │ │ │ ├── ReusableListViewItem.cs │ │ │ │ ├── ReusableMultiColumnListViewItem.cs │ │ │ │ ├── ReusableMultiColumnTreeViewItem.cs │ │ │ │ ├── ReusableTreeViewItem.cs │ │ │ │ └── VerticalVirtualizationController.cs │ │ │ ├── ContextualMenuManager.cs │ │ │ ├── ContextualMenuManipulator.cs │ │ │ ├── Controls/ │ │ │ │ ├── BaseBoolField.cs │ │ │ │ ├── BaseCompositeField.cs │ │ │ │ ├── BaseListView.cs │ │ │ │ ├── BasePopupField.cs │ │ │ │ ├── BaseSlider.cs │ │ │ │ ├── BaseTreeView.cs │ │ │ │ ├── BaseVerticalCollectionView.cs │ │ │ │ ├── Binding.cs │ │ │ │ ├── BoundsField.cs │ │ │ │ ├── BoundsIntField.cs │ │ │ │ ├── Box.cs │ │ │ │ ├── Button.cs │ │ │ │ ├── ButtonStripField.cs │ │ │ │ ├── CompoundFields.cs │ │ │ │ ├── DoubleField.cs │ │ │ │ ├── DropdownField.cs │ │ │ │ ├── EnumField.cs │ │ │ │ ├── FloatField.cs │ │ │ │ ├── Foldout.cs │ │ │ │ ├── GenericDropdownMenu.cs │ │ │ │ ├── GroupBox.cs │ │ │ │ ├── Hash128Field.cs │ │ │ │ ├── HelpBox.cs │ │ │ │ ├── IMixedValueSupport.cs │ │ │ │ ├── INotifyValueChanged.cs │ │ │ │ ├── Image.cs │ │ │ │ ├── InputField/ │ │ │ │ │ ├── BaseField.cs │ │ │ │ │ ├── KeyboardTextEditor.cs │ │ │ │ │ ├── TextEditor.cs │ │ │ │ │ ├── TextField.cs │ │ │ │ │ ├── TextInputFieldBase.cs │ │ │ │ │ └── TouchScreenTextEditor.cs │ │ │ │ ├── IntegerField.cs │ │ │ │ ├── Label.cs │ │ │ │ ├── ListView.cs │ │ │ │ ├── LongField.cs │ │ │ │ ├── MinMaxSlider.cs │ │ │ │ ├── MultiColumn/ │ │ │ │ │ ├── Column.cs │ │ │ │ │ ├── ColumnLayout.cs │ │ │ │ │ ├── ColumnMover.cs │ │ │ │ │ ├── ColumnResizer.cs │ │ │ │ │ ├── Columns.cs │ │ │ │ │ ├── MultiColumnCollectionHeader.cs │ │ │ │ │ ├── MultiColumnController.cs │ │ │ │ │ ├── MultiColumnHeaderColumn.cs │ │ │ │ │ ├── MultiColumnHeaderColumnResizeHandle.cs │ │ │ │ │ ├── MultiColumnListView.cs │ │ │ │ │ ├── MultiColumnTreeView.cs │ │ │ │ │ ├── SortColumnDescription.cs │ │ │ │ │ └── SortColumnDescriptions.cs │ │ │ │ ├── PopupField.cs │ │ │ │ ├── PopupWindow.cs │ │ │ │ ├── ProgressBar.cs │ │ │ │ ├── RadioButton.cs │ │ │ │ ├── RadioButtonGroup.cs │ │ │ │ ├── RepeatButton.cs │ │ │ │ ├── ScrollView.cs │ │ │ │ ├── Scroller.cs │ │ │ │ ├── Slider.cs │ │ │ │ ├── SliderInt.cs │ │ │ │ ├── Tab.cs │ │ │ │ ├── TabDragger.cs │ │ │ │ ├── TabView.cs │ │ │ │ ├── TextValueField.cs │ │ │ │ ├── Toggle.cs │ │ │ │ ├── ToggleButtonGroup/ │ │ │ │ │ ├── ToggleButtonGroup.cs │ │ │ │ │ ├── ToggleButtonGroupState.cs │ │ │ │ │ └── ToggleButtonGroupStatePropertiesAttribute.cs │ │ │ │ ├── TreeView.cs │ │ │ │ ├── TreeViewHelpers.cs │ │ │ │ ├── TreeViewItemData.cs │ │ │ │ ├── TwoPaneSplitView.cs │ │ │ │ ├── TwoPaneSplitViewResizer.cs │ │ │ │ ├── UnsignedIntegerField.cs │ │ │ │ └── UnsignedLongField.cs │ │ │ ├── Conversions/ │ │ │ │ └── SetValueVisitor.cs │ │ │ ├── CountingBloomFilter.cs │ │ │ ├── Cursor.PropertyBag.cs │ │ │ ├── Cursor.cs │ │ │ ├── DisposeHelper.cs │ │ │ ├── DragAndDrop/ │ │ │ │ ├── BaseReorderableDragAndDropController.cs │ │ │ │ ├── DragAndDropUtility.cs │ │ │ │ ├── DragEventsProcessor.cs │ │ │ │ ├── ICollectionDragAndDropController.cs │ │ │ │ ├── IDragAndDrop.cs │ │ │ │ ├── IDragAndDropArgs.cs │ │ │ │ ├── IDragAndDropController.cs │ │ │ │ ├── IReorderable.cs │ │ │ │ ├── ListViewDragger.cs │ │ │ │ ├── ListViewDraggerAnimated.cs │ │ │ │ ├── ListViewReorderableDragAndDropController.cs │ │ │ │ └── TreeViewReorderableDragAndDropController.cs │ │ │ ├── DropdownMenu.cs │ │ │ ├── DropdownUtility.cs │ │ │ ├── EasingCurves.cs │ │ │ ├── EventDispatcher.cs │ │ │ ├── Events/ │ │ │ │ ├── CaptureEvents.cs │ │ │ │ ├── ChangeEvent.cs │ │ │ │ ├── CommandEvents.cs │ │ │ │ ├── DebuggerEventDispatchUtilities.cs │ │ │ │ ├── DragAndDropEvents.cs │ │ │ │ ├── ElementUnderPointer.cs │ │ │ │ ├── EventBase.cs │ │ │ │ ├── EventCallback.cs │ │ │ │ ├── EventCallbackRegistry.cs │ │ │ │ ├── EventDebugger/ │ │ │ │ │ ├── EventDebugger.cs │ │ │ │ │ ├── EventDebuggerEventRecord.cs │ │ │ │ │ ├── EventDebuggerTrace.cs │ │ │ │ │ └── GlobalCallbackRegistry.cs │ │ │ │ ├── EventDispatchUtilities.cs │ │ │ │ ├── EventHandler.cs │ │ │ │ ├── FocusEvents.cs │ │ │ │ ├── IMEEvent.cs │ │ │ │ ├── InputEvents.cs │ │ │ │ ├── KeyboardEvents.cs │ │ │ │ ├── LayoutEvents.cs │ │ │ │ ├── LinkTagEvent.cs │ │ │ │ ├── MouseEvents.cs │ │ │ │ ├── MouseEventsHelper.cs │ │ │ │ ├── NavigationEvents.cs │ │ │ │ ├── PanelEvents.cs │ │ │ │ ├── PointerDeviceState.cs │ │ │ │ ├── PointerEvents.cs │ │ │ │ ├── PropagationPaths.cs │ │ │ │ ├── StyleEvents.cs │ │ │ │ ├── TooltipEvent.cs │ │ │ │ ├── TransitionEvents.cs │ │ │ │ └── UIEvent.cs │ │ │ ├── FieldMouseDragger.cs │ │ │ ├── FilterFunctionDefinition.cs │ │ │ ├── FilterFunctionDefinitionUtils.cs │ │ │ ├── FocusController.cs │ │ │ ├── GameObjects/ │ │ │ │ ├── DefaultEventSystem.InputForUIProcessor.cs │ │ │ │ ├── DefaultEventSystem.LegacyInputProcessor.cs │ │ │ │ ├── DefaultEventSystem.cs │ │ │ │ ├── DocumentPicker.cs │ │ │ │ ├── DynamicAtlasSettings.cs │ │ │ │ ├── NavigateFocusRing.cs │ │ │ │ ├── PanelInputConfiguration.cs │ │ │ │ ├── PanelSettings.cs │ │ │ │ ├── RuntimeEventDispatcher.cs │ │ │ │ ├── RuntimePanel.cs │ │ │ │ ├── RuntimePanelUtils.cs │ │ │ │ ├── ScreenRaycaster.cs │ │ │ │ ├── UIDocument.cs │ │ │ │ ├── UIDocumentHierarchyUtil.cs │ │ │ │ └── WorldSpaceInput.cs │ │ │ ├── GroupBoxUtility.cs │ │ │ ├── HierarchyTraversal.cs │ │ │ ├── IEnumerableUtils.cs │ │ │ ├── IGroupBox.cs │ │ │ ├── IGroupManager.cs │ │ │ ├── ILiveReloadAssetTracker.cs │ │ │ ├── ILiveReloadSystem.cs │ │ │ ├── IMGUIContainer.cs │ │ │ ├── ISerializableJsonDictionary.cs │ │ │ ├── ITransform.cs │ │ │ ├── ImmediateModeElement.cs │ │ │ ├── InteractionEnums.cs │ │ │ ├── KeyboardNavigationManipulator.cs │ │ │ ├── Layout/ │ │ │ │ ├── LayoutConfig.cs │ │ │ │ ├── LayoutHandle.cs │ │ │ │ ├── LayoutManager.cs │ │ │ │ ├── LayoutNode+Computed.cs │ │ │ │ ├── LayoutNode+Hierarchy.cs │ │ │ │ ├── LayoutNode+Style.cs │ │ │ │ ├── LayoutNode.cs │ │ │ │ ├── LayoutProcessor.cs │ │ │ │ ├── Model/ │ │ │ │ │ ├── Components/ │ │ │ │ │ │ ├── LayoutCacheData.cs │ │ │ │ │ │ ├── LayoutComputedData.cs │ │ │ │ │ │ ├── LayoutConfigData.cs │ │ │ │ │ │ ├── LayoutDataAccess.cs │ │ │ │ │ │ ├── LayoutDataStore.cs │ │ │ │ │ │ └── LayoutNodeData.cs │ │ │ │ │ ├── FixedBuffer.cs │ │ │ │ │ ├── LayoutAlign.cs │ │ │ │ │ ├── LayoutDefaults.cs │ │ │ │ │ ├── LayoutDimension.cs │ │ │ │ │ ├── LayoutDirection.cs │ │ │ │ │ ├── LayoutDisplay.cs │ │ │ │ │ ├── LayoutEdge.cs │ │ │ │ │ ├── LayoutFlexDirection.cs │ │ │ │ │ ├── LayoutJustify.cs │ │ │ │ │ ├── LayoutList.cs │ │ │ │ │ ├── LayoutLogLevel.cs │ │ │ │ │ ├── LayoutMeasureMode.cs │ │ │ │ │ ├── LayoutNodeType.cs │ │ │ │ │ ├── LayoutOverflow.cs │ │ │ │ │ ├── LayoutPositionType.cs │ │ │ │ │ ├── LayoutPrintOptions.cs │ │ │ │ │ ├── LayoutSize.cs │ │ │ │ │ ├── LayoutUnit.cs │ │ │ │ │ ├── LayoutValue.cs │ │ │ │ │ └── LayoutWrap.cs │ │ │ │ └── Native/ │ │ │ │ ├── LayoutNative.bindings.cs │ │ │ │ └── LayoutProcessorNative.cs │ │ │ ├── ManipulatorActivationFilter.cs │ │ │ ├── Manipulators.cs │ │ │ ├── MouseButton.cs │ │ │ ├── MouseCaptureController.cs │ │ │ ├── MouseManipulator.cs │ │ │ ├── Native/ │ │ │ │ ├── Renderer/ │ │ │ │ │ ├── MeshWriteDataInterface.bindings.cs │ │ │ │ │ ├── UIPainter2D.bindings.cs │ │ │ │ │ ├── UIRMeshBuilder.bindings.cs │ │ │ │ │ ├── UIRenderer.bindings.cs │ │ │ │ │ └── UIRendererJobProcessor.bindings.cs │ │ │ │ ├── TextNative.bindings.cs │ │ │ │ └── UIElementsUtility.bindings.cs │ │ │ ├── ObjectPool.cs │ │ │ ├── Panel.cs │ │ │ ├── PenButton.cs │ │ │ ├── PointerCaptureController.cs │ │ │ ├── PointerManipulator.cs │ │ │ ├── ProjectionUtils.cs │ │ │ ├── Renderer/ │ │ │ │ ├── GCHandlePool.cs │ │ │ │ ├── LayoutDebuggerVisualElement.cs │ │ │ │ ├── SafeHandleAccess.cs │ │ │ │ ├── UIRAllocator2D.cs │ │ │ │ ├── UIRAtlasAllocator.cs │ │ │ │ ├── UIRAtlasManager.cs │ │ │ │ ├── UIRCommandList.cs │ │ │ │ ├── UIRCommandManipulator.cs │ │ │ │ ├── UIRDetachedAllocator.cs │ │ │ │ ├── UIRDynamicAtlasPage.cs │ │ │ │ ├── UIRElementBuilder.cs │ │ │ │ ├── UIREntryPool.cs │ │ │ │ ├── UIREntryPreProcessor.cs │ │ │ │ ├── UIREntryProcessor.cs │ │ │ │ ├── UIREntryRecorder.cs │ │ │ │ ├── UIRGradientSettingsAtlas.cs │ │ │ │ ├── UIRImplicitPool.cs │ │ │ │ ├── UIRJobManager.cs │ │ │ │ ├── UIRJobMerger.cs │ │ │ │ ├── UIRLayoutUpdater.cs │ │ │ │ ├── UIRLinkedPool.cs │ │ │ │ ├── UIRMeshGenerationContext.cs │ │ │ │ ├── UIRMeshGenerationDeferrer.cs │ │ │ │ ├── UIRMeshGenerationNode.cs │ │ │ │ ├── UIRMeshGenerator.cs │ │ │ │ ├── UIRNativeList.cs │ │ │ │ ├── UIRNativePagedList.cs │ │ │ │ ├── UIROpacityIdAccelerator.cs │ │ │ │ ├── UIRPainter2D.cs │ │ │ │ ├── UIRRenderData.cs │ │ │ │ ├── UIRRenderEvents.cs │ │ │ │ ├── UIRRenderTree.cs │ │ │ │ ├── UIRRenderTreeCompositor.cs │ │ │ │ ├── UIRRenderTreeManager.cs │ │ │ │ ├── UIRRepaintUpdater.cs │ │ │ │ ├── UIRShaderInfoStorage.cs │ │ │ │ ├── UIRShaders.cs │ │ │ │ ├── UIRTempAllocator.cs │ │ │ │ ├── UIRTempMeshAllocator.cs │ │ │ │ ├── UIRTextCoreSettings.cs │ │ │ │ ├── UIRTextureBlitter.cs │ │ │ │ ├── UIRTextureRegistry.cs │ │ │ │ ├── UIRTextureSlotManager.cs │ │ │ │ ├── UIRUtility.cs │ │ │ │ ├── UIRVEShaderInfoAllocator.cs │ │ │ │ ├── UIRVectorImageManager.cs │ │ │ │ ├── UIRVisualChangesProcessor.cs │ │ │ │ └── UIRenderer/ │ │ │ │ ├── UIRenderDevice.cs │ │ │ │ ├── UIRenderDeviceAllocator.cs │ │ │ │ └── UIRenderers.cs │ │ │ ├── Scheduler.cs │ │ │ ├── Spacing.cs │ │ │ ├── StringUtils.cs │ │ │ ├── Style/ │ │ │ │ ├── Angle.PropertyBag.cs │ │ │ │ ├── Angle.cs │ │ │ │ ├── Background.PropertyBag.cs │ │ │ │ ├── Background.cs │ │ │ │ ├── ComputedStyle.cs │ │ │ │ ├── ComputedTransitions.cs │ │ │ │ ├── CustomStyle.cs │ │ │ │ ├── EasingFunction.PropertyBag.cs │ │ │ │ ├── EasingFunction.cs │ │ │ │ ├── FilterFunction.PropertyBag.cs │ │ │ │ ├── FilterFunction.cs │ │ │ │ ├── FontDefinition.PropertyBag.cs │ │ │ │ ├── FontDefinition.cs │ │ │ │ ├── Generated/ │ │ │ │ │ ├── ComputedStyle.cs │ │ │ │ │ ├── IResolvedStyle.cs │ │ │ │ │ ├── IStyle.cs │ │ │ │ │ ├── InitialStyle.cs │ │ │ │ │ ├── InlineStyleAccess.cs │ │ │ │ │ ├── InlineStyleAccessPropertyBag.cs │ │ │ │ │ ├── ResolvedStyleAccess.cs │ │ │ │ │ ├── ResolvedStyleAccessPropertyBag.cs │ │ │ │ │ ├── ResolvedStyleProperties.cs │ │ │ │ │ ├── ShorthandApplicator.cs │ │ │ │ │ ├── StyleDataStructs.cs │ │ │ │ │ ├── StyleDebug.cs │ │ │ │ │ ├── StyleDiff.cs │ │ │ │ │ ├── StyleProperties.cs │ │ │ │ │ ├── StylePropertyCache.cs │ │ │ │ │ ├── StylePropertyEnums.cs │ │ │ │ │ └── StylePropertyUtil.cs │ │ │ │ ├── IResolvedStyle.cs │ │ │ │ ├── IStyle.cs │ │ │ │ ├── InlineStyleAccess.PropertyBag.cs │ │ │ │ ├── InlineStyleAccess.cs │ │ │ │ ├── Length.PropertyBag.cs │ │ │ │ ├── Length.cs │ │ │ │ ├── ResolvedStyleAccess.PropertyBag.cs │ │ │ │ ├── ResolvedStyleAccess.cs │ │ │ │ ├── Rotate.PropertyBag.cs │ │ │ │ ├── Rotate.cs │ │ │ │ ├── Scale.PropertyBag.cs │ │ │ │ ├── Scale.cs │ │ │ │ ├── StyleBackground.cs │ │ │ │ ├── StyleBackgroundPosition.cs │ │ │ │ ├── StyleBackgroundRepeat.cs │ │ │ │ ├── StyleBackgroundSize.cs │ │ │ │ ├── StyleColor.cs │ │ │ │ ├── StyleCursor.cs │ │ │ │ ├── StyleDataRef.cs │ │ │ │ ├── StyleDebug.cs │ │ │ │ ├── StyleDiff.cs │ │ │ │ ├── StyleEnum.cs │ │ │ │ ├── StyleFloat.cs │ │ │ │ ├── StyleFont.cs │ │ │ │ ├── StyleFontDefinition.cs │ │ │ │ ├── StyleInt.cs │ │ │ │ ├── StyleLength.cs │ │ │ │ ├── StyleList.cs │ │ │ │ ├── StyleMaterial.cs │ │ │ │ ├── StylePropertyUtil.cs │ │ │ │ ├── StyleRotate.cs │ │ │ │ ├── StyleScale.cs │ │ │ │ ├── StyleTextShadow.cs │ │ │ │ ├── StyleTransformOrigin.cs │ │ │ │ ├── StyleTranslate.cs │ │ │ │ ├── StyleTypes.cs │ │ │ │ ├── StyleValue.PropertyBag.cs │ │ │ │ ├── TimeValue.PropertyBag.cs │ │ │ │ ├── TimeValue.cs │ │ │ │ ├── TransformOrigin.PropertyBag.cs │ │ │ │ ├── TransformOrigin.cs │ │ │ │ ├── Translate.PropertyBag.cs │ │ │ │ └── Translate.cs │ │ │ ├── StyleEnums.cs │ │ │ ├── StylePropertyAnimation.cs │ │ │ ├── StylePropertyAnimationSystem.cs │ │ │ ├── StylePropertyName.PropertyBag.cs │ │ │ ├── StylePropertyName.cs │ │ │ ├── StyleSheets/ │ │ │ │ ├── CSSSpec.cs │ │ │ │ ├── Dimension.cs │ │ │ │ ├── MatchedRulesExtractor.cs │ │ │ │ ├── ScalableImage.cs │ │ │ │ ├── StyleComplexSelector.cs │ │ │ │ ├── StyleProperty.cs │ │ │ │ ├── StylePropertyReader.cs │ │ │ │ ├── StylePropertyReaderHelper.cs │ │ │ │ ├── StyleRule.cs │ │ │ │ ├── StyleSelector.cs │ │ │ │ ├── StyleSelectorHelper.cs │ │ │ │ ├── StyleSelectorPart.cs │ │ │ │ ├── StyleSelectorRelationship.cs │ │ │ │ ├── StyleSelectorType.cs │ │ │ │ ├── StyleSheet.cs │ │ │ │ ├── StyleSheetApplicator.cs │ │ │ │ ├── StyleSheetBuilder.cs │ │ │ │ ├── StyleSheetCache.cs │ │ │ │ ├── StyleSheetColor.cs │ │ │ │ ├── StyleSheetExtensions.cs │ │ │ │ ├── StyleSheetUtility.cs │ │ │ │ ├── StyleValue.cs │ │ │ │ ├── StyleValueFunction.cs │ │ │ │ ├── StyleValueHandle.cs │ │ │ │ ├── StyleValueKeyword.cs │ │ │ │ ├── StyleValueType.cs │ │ │ │ ├── StyleVariable.cs │ │ │ │ ├── Syntax/ │ │ │ │ │ ├── StyleSyntaxExpression.cs │ │ │ │ │ ├── StyleSyntaxParser.cs │ │ │ │ │ └── StyleSyntaxToken.cs │ │ │ │ ├── ThemeStyleSheet.cs │ │ │ │ └── Validation/ │ │ │ │ ├── StyleMatcher.cs │ │ │ │ ├── StylePropertyCache.cs │ │ │ │ ├── StylePropertyValueParser.cs │ │ │ │ └── StyleValidator.cs │ │ │ ├── StyleTextAutoSize.cs │ │ │ ├── TemplateContainer.cs │ │ │ ├── Text/ │ │ │ │ ├── ATGTextEventHandler.cs │ │ │ │ ├── ATGTextHandle.cs │ │ │ │ ├── ATGTextJobSystem.cs │ │ │ │ ├── PanelTextSettings.cs │ │ │ │ ├── TextEditingManipulator.cs │ │ │ │ ├── TextEventHandler.cs │ │ │ │ ├── TextJobSystem.cs │ │ │ │ ├── TextSelectingManipulator.cs │ │ │ │ ├── TextUtilities.cs │ │ │ │ ├── UITKTextHandle.cs │ │ │ │ └── UITKTextJobSystem.cs │ │ │ ├── TextAutoSize.PropertyBag.cs │ │ │ ├── TextAutoSize.cs │ │ │ ├── TextElement.cs │ │ │ ├── TextElementEdition.cs │ │ │ ├── TextElementExperimental.cs │ │ │ ├── TextElementSelection.cs │ │ │ ├── TextShadow.PropertyBag.cs │ │ │ ├── TextShadow.cs │ │ │ ├── Transitions.cs │ │ │ ├── UIElementsBridge.cs │ │ │ ├── UIElementsInitialization.cs │ │ │ ├── UIElementsRuntimeUtility.cs │ │ │ ├── UIElementsUtility.cs │ │ │ ├── UIToolkitInputConfiguration.cs │ │ │ ├── UQuery.cs │ │ │ ├── UXML/ │ │ │ │ ├── FieldRenderingAttributes.cs │ │ │ │ ├── IUxmlAttributes.cs │ │ │ │ ├── OrphanFactories.cs │ │ │ │ ├── TemplateAsset.cs │ │ │ │ ├── UxmlAssetAttributeDescription.cs │ │ │ │ ├── UxmlAttributeDescription.cs │ │ │ │ ├── UxmlAttributes.cs │ │ │ │ ├── UxmlChildElementDescription.cs │ │ │ │ ├── UxmlDescriptionRegistry.cs │ │ │ │ ├── UxmlFactory.cs │ │ │ │ ├── UxmlImageAttributeDescription.cs │ │ │ │ ├── UxmlObjectAsset.cs │ │ │ │ ├── UxmlObjectFactoryRegistry.cs │ │ │ │ ├── UxmlSerializedData.cs │ │ │ │ ├── UxmlTypeRestriction.cs │ │ │ │ ├── UxmlUtility.cs │ │ │ │ ├── VisualElementAsset.cs │ │ │ │ ├── VisualElementFactoryRegistry.cs │ │ │ │ ├── VisualTreeAsset.cs │ │ │ │ └── VisualTreeAssetUtilities.cs │ │ │ ├── UpgradeConstants.cs │ │ │ ├── ValueAnimation.cs │ │ │ ├── VectorImage.cs │ │ │ ├── VisualElement.cs │ │ │ ├── VisualElementAnimation.cs │ │ │ ├── VisualElementBindableProperties.cs │ │ │ ├── VisualElementDataBinding.cs │ │ │ ├── VisualElementEventInterests.cs │ │ │ ├── VisualElementExperimentalFeatures.cs │ │ │ ├── VisualElementFocusRing.cs │ │ │ ├── VisualElementHierarchy.cs │ │ │ ├── VisualElementMathUtils.cs │ │ │ ├── VisualElementScheduler.cs │ │ │ ├── VisualElementStyleAccess.cs │ │ │ ├── VisualElementStyleSheetSet.cs │ │ │ ├── VisualElementTooltip.cs │ │ │ ├── VisualElementTypeData.cs │ │ │ ├── VisualElementUtils.cs │ │ │ ├── VisualTreeAnimationUpdater.cs │ │ │ ├── VisualTreeHierarchyFlagsUpdater.cs │ │ │ ├── VisualTreeHierarchyTracker.cs │ │ │ ├── VisualTreeStyleUpdater.cs │ │ │ ├── VisualTreeUpdater.cs │ │ │ ├── VisualTreeViewDataUpdater.cs │ │ │ └── WorldSpaceData.cs │ │ ├── InputSystem/ │ │ │ ├── InputSystemEventSystem.cs │ │ │ └── InputWrapper.cs │ │ ├── Managed/ │ │ │ ├── Utility/ │ │ │ │ └── ChunkAllocatingArray.cs │ │ │ ├── VisualNode.cs │ │ │ ├── VisualNodeChildren.cs │ │ │ ├── VisualNodeClassList.cs │ │ │ └── VisualPanel.cs │ │ └── ScriptBindings/ │ │ ├── VisualManager.bindings.cs │ │ ├── VisualNodeChildrenData.bindings.cs │ │ ├── VisualNodeClassData.bindings.cs │ │ ├── VisualNodeClassNameStore.bindings.cs │ │ ├── VisualNodeData.bindings.cs │ │ ├── VisualNodeHandle.bindings.cs │ │ ├── VisualNodeImguiData.bindings.cs │ │ ├── VisualNodeProperty.bindings.cs │ │ ├── VisualNodePropertyStore.bindings.cs │ │ ├── VisualNodePseudoStateData.bindings.cs │ │ ├── VisualNodeRenderData.bindings.cs │ │ ├── VisualNodeTextData.bindings.cs │ │ ├── VisualPanelData.bindings.cs │ │ └── VisualPanelHandle.bindings.cs │ ├── UIElementsEditor/ │ │ ├── ATGAnalytics.cs │ │ ├── AssemblyInfo.cs │ │ ├── Bindings/ │ │ │ ├── BaseListViewSerializedObjectBinding.cs │ │ │ ├── DefaultSerializedObjectBindingImplementation.cs │ │ │ ├── EditorListViewController.cs │ │ │ ├── EditorMultiColumnListViewController.cs │ │ │ ├── ListViewSerializedObjectBinding.cs │ │ │ ├── MultiColumnListViewSerializedObjectBinding.cs │ │ │ ├── SerializedDefaultEnumBinding.cs │ │ │ ├── SerializedIsExpandedBinding.cs │ │ │ ├── SerializedManagedEnumBinding.cs │ │ │ ├── SerializedObjectBinding.cs │ │ │ ├── SerializedObjectBindingBase.cs │ │ │ ├── SerializedObjectBindingContext.cs │ │ │ ├── SerializedObjectBindingContextUpdater.cs │ │ │ ├── SerializedObjectBindingPropertyToBaseField.cs │ │ │ ├── SerializedObjectBindingToBaseField.cs │ │ │ ├── SerializedObjectReferenceBinding.cs │ │ │ ├── SerializedObjectStringConversionBinding.cs │ │ │ └── SerializedPropertyHelper.cs │ │ ├── Debugger/ │ │ │ ├── BoxModelView.cs │ │ │ ├── DebuggerSearchBar.cs │ │ │ ├── DebuggerTreeView.cs │ │ │ ├── EditorWindowExtension.cs │ │ │ ├── Events/ │ │ │ │ ├── CodeLine.cs │ │ │ │ ├── EventLog.cs │ │ │ │ ├── EventLogLine.cs │ │ │ │ ├── EventTypeSearchField.cs │ │ │ │ ├── IRegisteredCallbackLine.cs │ │ │ │ ├── RegisterCallbackLines/ │ │ │ │ │ ├── CallbackInfo.cs │ │ │ │ │ ├── CodeLineInfo.cs │ │ │ │ │ └── TitleInfo.cs │ │ │ │ └── UIElementsEventDebugger.cs │ │ │ ├── LayoutDebugger/ │ │ │ │ ├── LayoutPanelDebuggerImpl.cs │ │ │ │ ├── UILayoutDebugger.cs │ │ │ │ ├── UILayoutDebuggerHistogram.cs │ │ │ │ └── UILayoutDebuggerWindow.cs │ │ │ ├── OtherDebbugerField.cs │ │ │ ├── OverlayPainter.cs │ │ │ ├── PanelDebugger.cs │ │ │ ├── StyleLengthField.cs │ │ │ ├── StylePropertyDebugger.cs │ │ │ ├── StylesDebugger.cs │ │ │ ├── TextureAtlas/ │ │ │ │ ├── TextureAtlasViewer.cs │ │ │ │ └── TextureAtlasViewerWindow.cs │ │ │ ├── UIElementsDebugger.cs │ │ │ ├── UIR/ │ │ │ │ ├── Debugger/ │ │ │ │ │ ├── AllocatorDebugger.cs │ │ │ │ │ └── UIRDebugger.cs │ │ │ │ └── UIRDebugUtility.cs │ │ │ └── UxmlExporter.cs │ │ ├── Delegates/ │ │ │ ├── EditorDelegateRegistration.cs │ │ │ └── SerializedPropertyDelegates.cs │ │ ├── EditorContextualMenuManager.cs │ │ ├── EditorCursor.cs │ │ ├── EditorDragAndDrop.cs │ │ ├── EditorMonitor.cs │ │ ├── EditorPanel.cs │ │ ├── EditorUIElementsBridge.cs │ │ ├── FilterDefinitionPropertyDrawer.cs │ │ ├── GameObjects/ │ │ │ ├── Inspector/ │ │ │ │ ├── PanelInputConfigurationEditor.cs │ │ │ │ ├── PanelSettingsInspector.cs │ │ │ │ ├── UIDocumentInspector.cs │ │ │ │ └── UIRendererEditor.cs │ │ │ ├── LiveReloadTrackerCreator.cs │ │ │ ├── PanelSettingsCreator/ │ │ │ │ └── PanelSettingsCreator.cs │ │ │ ├── PlayModeMenuItems.cs │ │ │ └── UIDocumentHierarchyWatcher.cs │ │ ├── Legacy/ │ │ │ └── ProgressBar.cs │ │ ├── PanelDebug.cs │ │ ├── RetainedMode.cs │ │ ├── StringBuilderPool.cs │ │ ├── Text/ │ │ │ ├── PanelTextSettingsCreationMenu.cs │ │ │ ├── PanelTextSettingsEditor.cs │ │ │ ├── PanelTextSettingsImporter.cs │ │ │ └── TextInfoOverlay.cs │ │ ├── Tooltip.cs │ │ ├── UIElementsEditorInitialization.cs │ │ ├── UIElementsEditorRuntimeUtility.cs │ │ ├── UIElementsEditorWindowCreator/ │ │ │ ├── CSharpTemplateCreator.cs │ │ │ ├── UIElementsEditorWindowCreator.cs │ │ │ ├── UssTemplateCreator.cs │ │ │ └── UxmlTemplateCreator.cs │ │ ├── UIElementsViewImporter.cs │ │ ├── UIToolkitProjectSettings.cs │ │ ├── UIToolkitSettingsProvider.cs │ │ ├── UXML/ │ │ │ ├── UxmlAttributeComparison.cs │ │ │ ├── UxmlAttributeConverter.cs │ │ │ ├── UxmlSerializedDataCreator.cs │ │ │ ├── UxmlSerializedDataDescription.cs │ │ │ ├── UxmlSerializedDataRegistry.cs │ │ │ └── UxmlSerializer.cs │ │ ├── UXMLAssetAttributeCache.cs │ │ ├── UXMLEditorFactories.cs │ │ ├── UXMLFactoryPreserver.cs │ │ ├── UXMLSchemaGenerator.cs │ │ ├── UxmlCodeDependencies.cs │ │ ├── VisualTreeAssetChangeTrackerUpdater.cs │ │ ├── VisualTreeAssetEditor.cs │ │ ├── VisualTreeEditorUpdater.cs │ │ └── WindowBackends/ │ │ ├── DefaultEditorWindowBackend.cs │ │ └── DefaultWindowBackend.cs │ ├── UIElementsSamplesEditor/ │ │ ├── ElementSnippet.cs │ │ ├── Snippets/ │ │ │ ├── BoundsFieldSnippet.cs │ │ │ ├── BoundsIntFieldSnippet.cs │ │ │ ├── ButtonSnippet.cs │ │ │ ├── ColorFieldSnippet.cs │ │ │ ├── CurveFieldSnippet.cs │ │ │ ├── DropdownFieldSnippet.cs │ │ │ ├── EnumFieldSnippet.cs │ │ │ ├── EnumFlagsFieldSnippet.cs │ │ │ ├── FloatFieldSnippet.cs │ │ │ ├── GradientFieldSnippet.cs │ │ │ ├── HelpBoxSnippet.cs │ │ │ ├── IntegerFieldSnippet.cs │ │ │ ├── LabelSnippet.cs │ │ │ ├── LayerFieldSnippet.cs │ │ │ ├── LayerMaskFieldSnippet.cs │ │ │ ├── ListViewSnippet.cs │ │ │ ├── LongFieldSnippet.cs │ │ │ ├── Mask64FieldSnippet.cs │ │ │ ├── MaskFieldSnippet.cs │ │ │ ├── MinMaxSliderSnippet.cs │ │ │ ├── MultiColumnListViewSnippet.cs │ │ │ ├── MultiColumnTreeViewSnippet.cs │ │ │ ├── ObjectFieldSnippet.cs │ │ │ ├── PopupFieldSnippet.cs │ │ │ ├── RadioButtonGroupSnippet.cs │ │ │ ├── RadioButtonSnippet.cs │ │ │ ├── RectFieldSnippet.cs │ │ │ ├── RectIntFieldSnippet.cs │ │ │ ├── ScrollerSnippet.cs │ │ │ ├── SliderIntSnippet.cs │ │ │ ├── SliderSnippet.cs │ │ │ ├── TabViewSnippet.cs │ │ │ ├── TagFieldSnippet.cs │ │ │ ├── TextFieldSnippet.cs │ │ │ ├── ToggleButtonGroupSnippet.cs │ │ │ ├── ToggleSnippet.cs │ │ │ ├── TreeViewSnippet.cs │ │ │ ├── UnsignedIntegerFieldSnippet.cs │ │ │ ├── UnsignedLongFieldSnippet.cs │ │ │ ├── Vector2FieldSnippet.cs │ │ │ ├── Vector2IntFieldSnippet.cs │ │ │ ├── Vector3FieldSnippet.cs │ │ │ ├── Vector3IntFieldSnippet.cs │ │ │ └── Vector4FieldSnippet.cs │ │ ├── StylesExplorer.cs │ │ ├── UIElementsSamples.cs │ │ └── UIElementsSnippetAsset.cs │ ├── UMPE/ │ │ └── Editor/ │ │ ├── ChannelClient.cs │ │ ├── ChannelService.cs │ │ ├── DataService.cs │ │ ├── EventService.cs │ │ ├── RoleProviderAttribute.cs │ │ └── UMPE.bindings.cs │ ├── UnityAnalytics/ │ │ ├── ContinuousEvent/ │ │ │ └── ContinuousEvent.bindings.cs │ │ ├── Public/ │ │ │ ├── AnalyticsSessionInfo.bindings.cs │ │ │ ├── Events/ │ │ │ │ ├── CustomEventData.bindings.cs │ │ │ │ └── CustomEventData.cs │ │ │ ├── UnityAnalytics.bindings.cs │ │ │ └── UnityAnalytics.cs │ │ └── RemoteSettings/ │ │ └── RemoteSettings.bindings.cs │ ├── UnityAnalyticsCommon/ │ │ └── Public/ │ │ ├── Events/ │ │ │ ├── AssetDatabaseAnalytics.cs │ │ │ ├── BuildAssetBundleAnalytics.cs │ │ │ ├── CollabOperationAnalytics.cs │ │ │ ├── LicensingAnalytics.cs │ │ │ ├── MetalUtilAnalytics.cs │ │ │ ├── NavigationAnalytics.cs │ │ │ ├── PackageManagerAnalytics.cs │ │ │ ├── PackageUtilityAnalytics.cs │ │ │ ├── StallAnalytics.cs │ │ │ ├── SubsystemsAnalytics.cs │ │ │ ├── TestAnalytics.cs │ │ │ └── VRDeviceAnalytics.cs │ │ ├── UnityAnalyticsCommon.bindings.cs │ │ └── UnityAnalyticsCommon.cs │ ├── UnityConnect/ │ │ ├── UnityAds/ │ │ │ └── UnityAdsSettings.bindings.cs │ │ └── UnityConnectSettings.bindings.cs │ ├── UnityConnectEditor/ │ │ ├── Analytics/ │ │ │ └── EditorGameServicesAnalytics.cs │ │ ├── Common/ │ │ │ ├── CoppaDrawer.cs │ │ │ ├── CoppaManager.cs │ │ │ ├── INotificationSubscriber.cs │ │ │ ├── IProjectEditorDrawer.cs │ │ │ ├── Notification.cs │ │ │ ├── NotificationManager.cs │ │ │ ├── PackageHelper.cs │ │ │ ├── ProjectBindDrawer.cs │ │ │ ├── ProjectBindManager.cs │ │ │ ├── ProjectNameSlashReplacer.cs │ │ │ ├── ServicesConstants.cs │ │ │ ├── SimpleStateMachine.cs │ │ │ └── UIElementsNotificationSubscriber.cs │ │ ├── ProjectSettings/ │ │ │ ├── AdsProjectSettings.cs │ │ │ ├── AnalyticsProjectSettings.cs │ │ │ ├── CloudBuildProjectSettings.cs │ │ │ ├── CloudDiagProjectSettings.cs │ │ │ ├── Fallback/ │ │ │ │ ├── InstallPackageSection.cs │ │ │ │ ├── PackageInstallationHandler.cs │ │ │ │ ├── VisualElementConstants.cs │ │ │ │ └── VisualElementUtils.cs │ │ │ ├── GeneralProjectSettings.cs │ │ │ └── PurchasingProjectSettings.cs │ │ ├── Services/ │ │ │ ├── AdsService.cs │ │ │ ├── AnalyticsConfiguration.cs │ │ │ ├── AnalyticsService.cs │ │ │ ├── AnalyticsValidationPoller.cs │ │ │ ├── BuildService.cs │ │ │ ├── CloudBuildPoller.cs │ │ │ ├── CrashService.cs │ │ │ ├── PurchasingConfiguration.cs │ │ │ ├── PurchasingService.cs │ │ │ ├── SingleService.cs │ │ │ └── UDPServices.cs │ │ ├── ServicesExploreMenu.cs │ │ ├── ServicesProjectSettings.cs │ │ └── ServicesRepository.cs │ ├── UnityCurl/ │ │ ├── FriendAttributes.cs │ │ └── Public/ │ │ └── UnityCurl.bindings.cs │ ├── UnityEditorAnalyticsEditor/ │ │ ├── BuildEventsHandler.cs │ │ ├── DebuggerEventListHandler.cs │ │ ├── EditorAnalytics.bindings.cs │ │ ├── Events/ │ │ │ └── EditorAnalyticsEvent.bindings.cs │ │ └── UsabilityAnalytics.bindings.cs │ ├── UnityWebRequest/ │ │ ├── FriendAttributes.cs │ │ └── Public/ │ │ ├── CertificateHandler/ │ │ │ └── CertificateHandler.bindings.cs │ │ ├── DownloadHandler/ │ │ │ └── DownloadHandler.bindings.cs │ │ ├── MultipartFormHelper.cs │ │ ├── UnityWebRequest.bindings.cs │ │ ├── UploadHandler/ │ │ │ └── UploadHandler.bindings.cs │ │ ├── WebRequest.deprecated.cs │ │ ├── WebRequestExtensions.cs │ │ └── WebRequestUtils.cs │ ├── UnityWebRequestAssetBundle/ │ │ ├── Public/ │ │ │ └── DownloadHandlerAssetBundle.bindings.cs │ │ └── UnityWebRequestAssetBundle.cs │ ├── UnityWebRequestAudio/ │ │ ├── Public/ │ │ │ └── DownloadHandlerAudio.bindings.cs │ │ └── UnityWebRequestMultimedia.cs │ ├── UnityWebRequestTexture/ │ │ ├── Public/ │ │ │ └── DownloadHandlerTexture.bindings.cs │ │ └── UnityWebRequestTexture.cs │ ├── UnityWebRequestWWW/ │ │ └── Public/ │ │ ├── WWW.bindings.cs │ │ ├── WWW.cs │ │ └── WWWAudio.deprecated.cs │ ├── VFX/ │ │ ├── FriendAttributes.cs │ │ └── Public/ │ │ └── ScriptBindings/ │ │ ├── VFXEnums.bindings.cs │ │ ├── VFXEventAttribute.bindings.cs │ │ ├── VFXExpressionValues.bindings.cs │ │ ├── VFXManager.bindings.cs │ │ ├── VFXSpawnerCallbacks.bindings.cs │ │ ├── VFXSpawnerState.bindings.cs │ │ └── VisualEffect.bindings.cs │ ├── VFXEditor/ │ │ ├── FriendAttributes.cs │ │ └── Public/ │ │ ├── ScriptBindings/ │ │ │ ├── VFXExpressionMesh.bindings.cs │ │ │ ├── VFXExpressionNoise.bindings.cs │ │ │ ├── VFXExpressionTexture.bindings.cs │ │ │ ├── VFXMemorySerializer.bindings.cs │ │ │ ├── VisualEffectImporter.bindings.cs │ │ │ ├── VisualEffectResource.bindings.cs │ │ │ ├── VisualEffectSubgraph.bindings.cs │ │ │ ├── VisualEffectTest.bindings.cs │ │ │ └── VisualEffectUtility.bindings.cs │ │ └── VisualEffectEditorDefault.cs │ ├── VR/ │ │ └── ScriptBindings/ │ │ └── XR.bindings.cs │ ├── VREditor/ │ │ ├── Mono/ │ │ │ └── VREditor.cs │ │ └── ScriptBindings/ │ │ └── VREditor.bindings.cs │ ├── Vehicles/ │ │ └── Vehicles.bindings.cs │ ├── Video/ │ │ └── Public/ │ │ └── ScriptBindings/ │ │ ├── MediaComponent.bindings.cs │ │ ├── VideoClip.bindings.cs │ │ ├── VideoClipPlayable.bindings.cs │ │ ├── VideoMediaPlayback.bindings.cs │ │ ├── VideoPlayer.bindings.cs │ │ └── VideoPlayerExtensions.bindings.cs │ ├── VideoEditor/ │ │ └── Editor/ │ │ ├── VideoClipInspector.cs │ │ ├── VideoPlayerEditor.cs │ │ └── VideoUtil.bindings.cs │ ├── VirtualTexturing/ │ │ ├── Managed/ │ │ │ └── AssemblyInfo.cs │ │ └── ScriptBindings/ │ │ └── VirtualTexturing.bindings.cs │ ├── VirtualTexturingEditor/ │ │ └── ScriptBindings/ │ │ └── VirtualTexturingEditor.bindings.cs │ ├── Wind/ │ │ └── Public/ │ │ └── WindZone.bindings.cs │ ├── XR/ │ │ ├── ScriptBindings/ │ │ │ ├── InputTracking.cs │ │ │ ├── VRNode.cs │ │ │ ├── VRNodeState.cs │ │ │ └── XRInput.bindings.cs │ │ ├── Stats/ │ │ │ └── XRStats.bindings.cs │ │ └── Subsystems/ │ │ ├── Display/ │ │ │ ├── XRDisplaySubsystem.bindings.cs │ │ │ └── XRDisplaySubsystemDescriptor.bindings.cs │ │ ├── Input/ │ │ │ ├── XRInputSubsystem.bindings.cs │ │ │ └── XRInputSubsystemDescriptor.bindings.cs │ │ └── Meshing/ │ │ ├── XRMeshSubsystem.bindings.cs │ │ └── XRMeshSubsystemDescriptor.bindings.cs │ └── XREditor/ │ ├── Boot/ │ │ └── XRBoot.bindings.cs │ ├── Build/ │ │ └── XRBuildUtilities.bindings.cs │ └── XRManagementInstaller.cs ├── Projects/ │ └── CSharp/ │ ├── UnityEditor.csproj │ ├── UnityEngine.csproj │ └── UnityReferenceSource.sln ├── README.md ├── Runtime/ │ ├── 2D/ │ │ ├── Common/ │ │ │ └── ScriptBindings/ │ │ │ ├── Clipper2D.bindings.cs │ │ │ ├── ClipperOffset2D.bindings.cs │ │ │ ├── Light2DBase.cs │ │ │ ├── PixelSnapping.bindings.cs │ │ │ ├── SpriteDataAccess.bindings.cs │ │ │ ├── SpriteDataUtility.cs │ │ │ ├── SpriteRenderer.bindings.cs │ │ │ ├── SpriteRendererGroup.bindings.cs │ │ │ └── Sprites.bindings.cs │ │ ├── Sorting/ │ │ │ └── ScriptBindings/ │ │ │ └── SortingGroup.bindings.cs │ │ └── SpriteAtlas/ │ │ └── ScriptBindings/ │ │ └── SpriteAtlas.bindings.cs │ ├── Export/ │ │ ├── 2D/ │ │ │ └── SortingLayer.bindings.cs │ │ ├── Analytics/ │ │ │ └── AnalyticsCommon.cs │ │ ├── Animation/ │ │ │ └── AnimationCurve.bindings.cs │ │ ├── Annotations/ │ │ │ └── JetBrains.Annotations.cs │ │ ├── Apple/ │ │ │ └── FrameCaptureMetal.bindings.cs │ │ ├── Application/ │ │ │ ├── Application.bindings.cs │ │ │ └── Application.cs │ │ ├── AssemblyInfo.cs │ │ ├── Assertions/ │ │ │ ├── Assert/ │ │ │ │ ├── AssertBase.cs │ │ │ │ ├── AssertBool.cs │ │ │ │ ├── AssertFloat.cs │ │ │ │ ├── AssertGeneric.cs │ │ │ │ ├── AssertNull.cs │ │ │ │ ├── AssertPrimitiveTypes.cs │ │ │ │ ├── AssertionException.cs │ │ │ │ ├── AssertionMessageUtil.cs │ │ │ │ └── Comparers/ │ │ │ │ └── FloatComparer.cs │ │ │ └── Must/ │ │ │ ├── MustBool.cs │ │ │ ├── MustFloat.cs │ │ │ ├── MustGeneric.cs │ │ │ └── MustNull.cs │ │ ├── Audio/ │ │ │ └── AudioType.cs │ │ ├── BaseClass.cs │ │ ├── Bootstrap/ │ │ │ └── BootConfig.bindings.cs │ │ ├── Burst/ │ │ │ ├── BurstAuthorizedExternalMethodAttribute.cs │ │ │ ├── BurstCompilerService.bindings.cs │ │ │ ├── BurstCompilerService.cs │ │ │ └── BurstDiscardAttribute.cs │ │ ├── BurstLike/ │ │ │ └── BurstLike.bindings.cs │ │ ├── Caching/ │ │ │ ├── Cache.bindings.cs │ │ │ ├── Caching.bindings.cs │ │ │ └── Caching.deprecated.cs │ │ ├── Camera/ │ │ │ ├── BatchRenderGroupAnalytics.cs │ │ │ ├── BuiltinRuntimeReflectionSystem.bindings.cs │ │ │ ├── Camera.bindings.cs │ │ │ ├── Camera.deprecated.cs │ │ │ ├── CullingGroup.bindings.cs │ │ │ ├── FlareLayer.bindings.cs │ │ │ ├── IScriptableRuntimeReflectionSystem.cs │ │ │ ├── ReflectionProbe.bindings.cs │ │ │ ├── ScriptableRuntimeReflectionSystem.cs │ │ │ ├── ScriptableRuntimeReflectionSystemSettings.bindings.cs │ │ │ └── ScriptableRuntimeReflectionSystemWrapper.cs │ │ ├── Collections/ │ │ │ ├── CollectionExtensions.cs │ │ │ ├── NativeCollectionAccessPolicies.cs │ │ │ └── NativeCollectionEnums.bindings.cs │ │ ├── ContentNamespace/ │ │ │ └── ContentNamespace.bindings.cs │ │ ├── Coverage.bindings.cs │ │ ├── CrashReport/ │ │ │ └── CrashReport.bindings.cs │ │ ├── Debug/ │ │ │ ├── Debug.bindings.cs │ │ │ └── Debug.deprecated.cs │ │ ├── DedicatedServer/ │ │ │ └── Arguments.bindings.cs │ │ ├── Device/ │ │ │ ├── Application.cs │ │ │ ├── Screen.cs │ │ │ └── SystemInfo.cs │ │ ├── DiagnosticSwitch/ │ │ │ └── DiagnosticSwitch.cs │ │ ├── Diagnostics/ │ │ │ └── DiagnosticsUtils.bindings.cs │ │ ├── Director/ │ │ │ ├── CameraPlayable.bindings.cs │ │ │ ├── ExposedPropertyTable.bindings.cs │ │ │ ├── ExposedReference.cs │ │ │ ├── FrameData.cs │ │ │ ├── FrameRate.bindings.cs │ │ │ ├── IExposedPropertyTable.cs │ │ │ ├── INotification.cs │ │ │ ├── INotificationReceiver.cs │ │ │ ├── IPlayable.cs │ │ │ ├── IPlayableBehaviour.cs │ │ │ ├── IPlayableOutput.cs │ │ │ ├── MaterialEffectPlayable.bindings.cs │ │ │ ├── Notification.cs │ │ │ ├── Playable.cs │ │ │ ├── PlayableAsset.cs │ │ │ ├── PlayableBehaviour.cs │ │ │ ├── PlayableBindings.cs │ │ │ ├── PlayableExtensions.cs │ │ │ ├── PlayableGraph.bindings.cs │ │ │ ├── PlayableHandle.bindings.cs │ │ │ ├── PlayableOutput.cs │ │ │ ├── PlayableOutputExtensions.cs │ │ │ ├── PlayableOutputExtensions.deprecated.cs │ │ │ ├── PlayableOutputHandle.bindings.cs │ │ │ ├── ScriptPlayable.cs │ │ │ ├── ScriptPlayableBinding.cs │ │ │ ├── ScriptPlayableOutput.cs │ │ │ ├── TextureMixerPlayable.bindings.cs │ │ │ ├── TexturePlayableBinding.cs │ │ │ ├── TexturePlayableGraphExtensions.bindings.cs │ │ │ └── TexturePlayableOutput.bindings.cs │ │ ├── ETW/ │ │ │ └── EventProvider.bindings.cs │ │ ├── ExpressionEvaluator.cs │ │ ├── File/ │ │ │ ├── ArchiveFile.bindings.cs │ │ │ ├── AsyncReadManager.bindings.cs │ │ │ ├── BuildCompression.cs │ │ │ ├── File.bindings.cs │ │ │ └── VirtualFileSystem.bindings.cs │ │ ├── FriendAttributes.cs │ │ ├── GI/ │ │ │ ├── DynamicGI.bindings.cs │ │ │ ├── GIDebugVisualisation.bindings.cs │ │ │ ├── LightingSettings.bindings.cs │ │ │ ├── LightingSettings.deprecated.cs │ │ │ └── Lightmapping.cs │ │ ├── Geometry/ │ │ │ ├── Bounds.cs │ │ │ ├── BoundsInt.cs │ │ │ ├── GeometryUtility.cs │ │ │ ├── Plane.cs │ │ │ ├── Ray.cs │ │ │ ├── Ray2D.cs │ │ │ ├── Rect.cs │ │ │ ├── RectInt.cs │ │ │ └── RectOffset.cs │ │ ├── Gizmos/ │ │ │ ├── Gizmos.bindings.cs │ │ │ └── Gizmos.cs │ │ ├── Graphics/ │ │ │ ├── AsyncGPUReadback.bindings.cs │ │ │ ├── BeforeRenderHelper.cs │ │ │ ├── BillboardRenderer.bindings.cs │ │ │ ├── CustomRenderTextureManager.cs │ │ │ ├── Display.bindings.cs │ │ │ ├── GPUFence.deprecated.cs │ │ │ ├── Graphics.bindings.cs │ │ │ ├── Graphics.cs │ │ │ ├── Graphics.deprecated.cs │ │ │ ├── GraphicsBuffer.bindings.cs │ │ │ ├── GraphicsComponents.bindings.cs │ │ │ ├── GraphicsDeviceDebug.bindings.cs │ │ │ ├── GraphicsEnums.cs │ │ │ ├── GraphicsFence.bindings.cs │ │ │ ├── GraphicsFormatUtility.bindings.cs │ │ │ ├── GraphicsJobsFilterModes.bindings.cs │ │ │ ├── GraphicsManagers.bindings.cs │ │ │ ├── GraphicsRenderers.bindings.cs │ │ │ ├── GraphicsSettings.RenderPipelineGlobalSettings.bindings.cs │ │ │ ├── GraphicsSettings.bindings.cs │ │ │ ├── GraphicsStateCollection.bindings.cs │ │ │ ├── GraphicsStateCollection.cs │ │ │ ├── GraphicsTexture.bindings.cs │ │ │ ├── Graphics_BindingsOverloads.cs │ │ │ ├── HDROutputFaking.bindings.cs │ │ │ ├── IRenderPipelineGraphicsSettings.cs │ │ │ ├── IRenderPipelineResources.cs │ │ │ ├── LOD.bindings.cs │ │ │ ├── Light.bindings.cs │ │ │ ├── Light.deprecated.cs │ │ │ ├── LightProbeGroup.bindings.cs │ │ │ ├── LightProbeProxyVolume.bindings.cs │ │ │ ├── LineUtility.cs │ │ │ ├── MachineLearningContext.bindings.cs │ │ │ ├── MachineLearningContext.cs │ │ │ ├── MachineLearningOperator.bindings.cs │ │ │ ├── MachineLearningOperator.cs │ │ │ ├── MachineLearningTensor.bindings.cs │ │ │ ├── MachineLearningTensor.cs │ │ │ ├── Mesh.bindings.cs │ │ │ ├── Mesh.cs │ │ │ ├── MeshData.bindings.cs │ │ │ ├── MeshData.cs │ │ │ ├── OnDemandRendering.bindings.cs │ │ │ ├── RayTracingAccelerationStructure.bindings.cs │ │ │ ├── RenderPipelineGraphicsSettingsCollection.cs │ │ │ ├── RenderPipelineGraphicsSettingsExtensions.cs │ │ │ ├── RenderingCommandBuffer.bindings.cs │ │ │ ├── RenderingCommandBuffer.cs │ │ │ ├── RenderingCommandBuffer.deprecated.cs │ │ │ ├── RenderingCommandBufferExtensions.bindings.cs │ │ │ ├── ShaderWarmup.bindings.cs │ │ │ ├── ShadingRateImage.bindings.cs │ │ │ ├── ShadingRateImage.cs │ │ │ ├── ShadingRateInfo.binding.cs │ │ │ ├── SplashScreen.bindings.cs │ │ │ ├── SupportedOnRenderPipeline.cs │ │ │ ├── Texture.bindings.cs │ │ │ ├── Texture.cs │ │ │ ├── Texture.deprecated.cs │ │ │ ├── VulkanDeviceFilterLists.bindings.cs │ │ │ └── Watermark.bindings.cs │ │ ├── Handheld/ │ │ │ └── Handheld.bindings.cs │ │ ├── Hashing/ │ │ │ ├── Hash128.bindings.cs │ │ │ ├── HashUtilities.cs │ │ │ └── SpookyHash.cs │ │ ├── Hmi/ │ │ │ └── HmiPlatform.bindings.cs │ │ ├── IMGUI/ │ │ │ ├── GUIElement.bindings.cs │ │ │ └── ScrollWaitDefinitions.cs │ │ ├── Input/ │ │ │ ├── Cursor.bindings.cs │ │ │ └── KeyCode.cs │ │ ├── Internal/ │ │ │ ├── DefaultValueAttribute.cs │ │ │ ├── ExcludeFromDocs.cs │ │ │ └── InternalInterfaces.cs │ │ ├── Jobs/ │ │ │ └── AtomicSafetyHandle.bindings.cs │ │ ├── Logging/ │ │ │ ├── DebugLogHandler.cs │ │ │ ├── ILogHandler.cs │ │ │ ├── ILogger.cs │ │ │ ├── Logger.cs │ │ │ └── UnityLogWriter.bindings.cs │ │ ├── Lumin/ │ │ │ ├── UsesLuminPlatformLevel.cs │ │ │ └── UsesLuminPrivilege.cs │ │ ├── Math/ │ │ │ ├── Color.cs │ │ │ ├── Color32.cs │ │ │ ├── ColorUtility.bindings.cs │ │ │ ├── ColorUtility.cs │ │ │ ├── Gradient.bindings.cs │ │ │ ├── Math.bindings.cs │ │ │ ├── Mathf.cs │ │ │ ├── Matrix4x4.cs │ │ │ ├── Quaternion.cs │ │ │ ├── SphericalHarmonicsL2.bindings.cs │ │ │ ├── Vector2.cs │ │ │ ├── Vector2Int.cs │ │ │ ├── Vector3.cs │ │ │ ├── Vector3Int.cs │ │ │ └── Vector4.cs │ │ ├── Misc/ │ │ │ └── ObjectDispatcher.bindings.cs │ │ ├── NativeArray/ │ │ │ ├── DisposeSentinel.cs │ │ │ ├── NativeArray.cs │ │ │ └── NativeSlice.cs │ │ ├── Networking/ │ │ │ ├── Ping.bindings.cs │ │ │ └── PlayerConnection/ │ │ │ ├── ConnectionApi.cs │ │ │ ├── PlayerConnection.cs │ │ │ └── PlayerEditorConnectionEvents.cs │ │ ├── NumericFieldDraggerUtility.cs │ │ ├── ObjectPool/ │ │ │ ├── CollectionPool.cs │ │ │ ├── GenericPool.cs │ │ │ ├── IObjectPool.cs │ │ │ ├── IPool.cs │ │ │ ├── LinkedPool.cs │ │ │ ├── ObjectPools.cs │ │ │ ├── PoolManager.cs │ │ │ ├── PooledObject.cs │ │ │ └── UnsafeGenericPool.cs │ │ ├── PlayerConnection/ │ │ │ ├── PlayerConnection.cs │ │ │ └── PlayerConnectionInternal.bindings.cs │ │ ├── PlayerLoop/ │ │ │ └── PlayerLoop.bindings.cs │ │ ├── PlayerPrefs/ │ │ │ └── PlayerPrefs.bindings.cs │ │ ├── Profiler/ │ │ │ └── IgnoredByDeepProfilerAttribute.cs │ │ ├── Properties/ │ │ │ └── DrivenPropertyManager.bindings.cs │ │ ├── PropertyDrawer/ │ │ │ └── PropertyAttribute.cs │ │ ├── PropertyName/ │ │ │ ├── PropertyName.bindings.cs │ │ │ └── PropertyName.cs │ │ ├── Random/ │ │ │ ├── Random.bindings.cs │ │ │ └── Random.cs │ │ ├── RenderPipeline/ │ │ │ ├── AttachmentDescriptor.cs │ │ │ ├── BitOperationUtils.cs │ │ │ ├── BlendState.cs │ │ │ ├── CullingParameters.cs │ │ │ ├── CullingResults.bindings.cs │ │ │ ├── CullingResults.cs │ │ │ ├── DepthState.cs │ │ │ ├── DrawRendererFlags.cs │ │ │ ├── DrawingSettings.cs │ │ │ ├── FilteringSettings.cs │ │ │ ├── GizmoSubset.cs │ │ │ ├── InvalidImportException.cs │ │ │ ├── LODParameters.cs │ │ │ ├── ObjectIdRequest.cs │ │ │ ├── PerObjectData.cs │ │ │ ├── RasterState.cs │ │ │ ├── ReflectionProbeSortingCriteria.cs │ │ │ ├── RenderPipeline.cs │ │ │ ├── RenderPipelineAsset.cs │ │ │ ├── RenderPipelineAsset.deprecated.cs │ │ │ ├── RenderPipelineGlobalSettings.cs │ │ │ ├── RenderPipelineManager.cs │ │ │ ├── RenderPipelineManager.deprecated.cs │ │ │ ├── RenderQueueRange.cs │ │ │ ├── RenderStateBlock.cs │ │ │ ├── RenderStateMask.cs │ │ │ ├── RenderTargetBlendState.cs │ │ │ ├── RendererList.bindings.cs │ │ │ ├── RenderingLayerMask.bindings.cs │ │ │ ├── ScopedRenderPass.cs │ │ │ ├── ScriptableRenderContext.bindings.cs │ │ │ ├── ScriptableRenderContext.cs │ │ │ ├── ShaderTagId.cs │ │ │ ├── ShadowDrawingSettings.cs │ │ │ ├── ShadowSplitData.cs │ │ │ ├── SortingCriteria.cs │ │ │ ├── SortingLayerRange.cs │ │ │ ├── SortingSettings.cs │ │ │ ├── StencilState.cs │ │ │ ├── SupportedRenderingFeatures.cs │ │ │ ├── UISubset.cs │ │ │ ├── VisibleLight.cs │ │ │ ├── VisibleLightFlags.cs │ │ │ └── VisibleReflectionProbe.cs │ │ ├── Rendering/ │ │ │ ├── BatchRendererGroup.bindings.cs │ │ │ └── GPUDrivenRendering.bindings.cs │ │ ├── Resources/ │ │ │ ├── IconAttribute.cs │ │ │ ├── Resources.bindings.cs │ │ │ └── Resources.deprecated.cs │ │ ├── SceneManager/ │ │ │ ├── LineUtility.bindings.cs │ │ │ ├── Scene.bindings.cs │ │ │ ├── Scene.cs │ │ │ ├── SceneManager.bindings.cs │ │ │ ├── SceneManager.cs │ │ │ └── SceneUtility.bindings.cs │ │ ├── Scripting/ │ │ │ ├── APIUpdating/ │ │ │ │ └── UpdatedFromAttribute.cs │ │ │ ├── AlwaysLinkAssemblyAttribute.cs │ │ │ ├── ApiRestrictions.bindings.cs │ │ │ ├── AsyncInstantiateOperation.bindings.cs │ │ │ ├── AsyncOperation.bindings.cs │ │ │ ├── AsyncOperation.cs │ │ │ ├── AttributeHelperEngine.cs │ │ │ ├── Attributes.cs │ │ │ ├── Awaitable.AsyncMethodBuilder.cs │ │ │ ├── Awaitable.AsyncMethodBuilderT.cs │ │ │ ├── Awaitable.AsyncOperation.cs │ │ │ ├── Awaitable.Awaiter.cs │ │ │ ├── Awaitable.Bindings.cs │ │ │ ├── Awaitable.DelayedCall.cs │ │ │ ├── Awaitable.Threading.cs │ │ │ ├── Awaitable.UnityEvents.cs │ │ │ ├── Awaitable.cs │ │ │ ├── AwaitableCompletionSource.cs │ │ │ ├── AwaitableT.cs │ │ │ ├── Behaviour.bindings.cs │ │ │ ├── BindingsHelpers.cs │ │ │ ├── CastHelper.cs │ │ │ ├── Categorize.cs │ │ │ ├── ClassLibraryInitializer.cs │ │ │ ├── Component.bindings.cs │ │ │ ├── Component.deprecated.cs │ │ │ ├── Coroutine.bindings.cs │ │ │ ├── Coroutines.cs │ │ │ ├── CustomYieldInstruction.cs │ │ │ ├── EnumDataUtility.cs │ │ │ ├── EnumInfo.cs │ │ │ ├── ExcludeFromObjectFactoryAttribute.cs │ │ │ ├── ExtensionOfNativeClassAttribute.cs │ │ │ ├── FailedToLoadScriptObject.cs │ │ │ ├── GameObject.bindings.cs │ │ │ ├── GameObject.deprecated.cs │ │ │ ├── GarbageCollector.bindings.cs │ │ │ ├── Il2CppEagerStaticClassConstructionAttribute.cs │ │ │ ├── InspectorOrderAttribute.cs │ │ │ ├── LayerMask.bindings.cs │ │ │ ├── LazyLoadReference.cs │ │ │ ├── ManagedStreamHelpers.cs │ │ │ ├── MonoBehaviour.bindings.cs │ │ │ ├── NoAllocHelpers.bindings.cs │ │ │ ├── PreserveAttribute.cs │ │ │ ├── RangeInt.cs │ │ │ ├── RequireAttributeUsagesAttribute.cs │ │ │ ├── RequireDerivedAttribute.cs │ │ │ ├── RequireImplementorsAttribute.cs │ │ │ ├── RequiredInterfaceAttribute.cs │ │ │ ├── RequiredMemberAttribute.cs │ │ │ ├── RuntimeInitializeOnLoadAttribute.cs │ │ │ ├── ScriptableObject.bindings.cs │ │ │ ├── ScriptingRuntime.bindings.cs │ │ │ ├── ScriptingUtility.cs │ │ │ ├── SelectionBaseAttribute.cs │ │ │ ├── StackTrace.cs │ │ │ ├── TagHandle.cs │ │ │ ├── TextAsset.bindings.cs │ │ │ ├── TextAsset.cs │ │ │ ├── TrackedReference.cs │ │ │ ├── UnhandledExceptionHandler.bindings.cs │ │ │ ├── UnityAPICompatibilityVersionAttribute.cs │ │ │ ├── UnityEngineObject.bindings.cs │ │ │ ├── UnitySynchronizationContext.cs │ │ │ ├── WaitForEndOfFrame.cs │ │ │ ├── WaitForFixedUpdate.cs │ │ │ ├── WaitForSeconds.cs │ │ │ ├── WaitForSecondsRealtime.cs │ │ │ ├── WaitTimeoutMode.cs │ │ │ ├── WaitUntil.cs │ │ │ ├── WaitWhile.cs │ │ │ └── YieldOperation.cs │ │ ├── SearchService/ │ │ │ ├── ObjectSelectorHandlerAttribute.Deprecated.cs │ │ │ └── SearchContextAttribute.cs │ │ ├── Security/ │ │ │ ├── Security.cs │ │ │ └── SecurityPublic.cs │ │ ├── Serialization/ │ │ │ ├── FormerlySerializedAsAttribute.cs │ │ │ ├── ManagedReferenceUtility.bindings.cs │ │ │ ├── Serialization.cs │ │ │ └── UnitySurrogateSelector.cs │ │ ├── Shaders/ │ │ │ ├── ComputeBuffer.bindings.cs │ │ │ ├── ComputeShader.bindings.cs │ │ │ ├── ComputeShader.cs │ │ │ ├── GlobalKeyword.bindings.cs │ │ │ ├── LocalKeyword.bindings.cs │ │ │ ├── LocalKeywordSpace.bindings.cs │ │ │ ├── Material.bindings.cs │ │ │ ├── Material.cs │ │ │ ├── MaterialPropertyBlock.bindings.cs │ │ │ ├── MaterialPropertyBlock.cs │ │ │ ├── PassIdentifier.bindings.cs │ │ │ ├── PlatformKeywordSet.cs │ │ │ ├── RayTracingShader.bindings.cs │ │ │ ├── RayTracingShader.cs │ │ │ ├── Shader.bindings.cs │ │ │ ├── Shader.cs │ │ │ ├── ShaderKeyword.bindings.cs │ │ │ ├── ShaderKeyword.deprecated.cs │ │ │ ├── ShaderKeywordSet.bindings.cs │ │ │ ├── ShaderProperties.cs │ │ │ ├── ShaderVariantAnalytics/ │ │ │ │ └── ShaderRuntimeInfoAnalytics.cs │ │ │ ├── ShaderVariantCollection.bindings.cs │ │ │ └── ShaderVariantCollection.cs │ │ ├── Snapping/ │ │ │ ├── SnapAxis.cs │ │ │ └── Snapping.cs │ │ ├── StaticBatching/ │ │ │ ├── CombineForStaticBatching.cs │ │ │ └── MeshSubsetCombineUtility.cs │ │ ├── StaticShim/ │ │ │ ├── ApplicationShimBase.cs │ │ │ ├── ScreenShimBase.cs │ │ │ ├── ShimManager.cs │ │ │ └── SystemInfoShimBase.cs │ │ ├── Stripping/ │ │ │ └── ClassStubsForStripping.cs │ │ ├── SystemInfo/ │ │ │ ├── SystemInfo.bindings.cs │ │ │ └── SystemInfo.deprecated.cs │ │ ├── Time/ │ │ │ ├── DiscreteTime.cs │ │ │ ├── RationalTime.bindings.cs │ │ │ ├── SystemClock.cs │ │ │ └── Time.bindings.cs │ │ ├── TouchScreenKeyboard/ │ │ │ ├── TouchScreenKeyboard.bindings.cs │ │ │ └── TouchScreenKeyboardType.cs │ │ ├── UINumericFieldsUtils.cs │ │ ├── Undo/ │ │ │ └── RuntimeUndo.bindings.cs │ │ ├── UnityEngineInternal/ │ │ │ ├── APIUpdaterRuntimeServices.cs │ │ │ ├── TypeInferenceRuleAttribute.cs │ │ │ └── WrappedTypes.cs │ │ ├── UnityEvent/ │ │ │ ├── PropertyChangedEvent.cs │ │ │ ├── UnityEvent.cs │ │ │ └── UnityEventQueueSystem.bindings.cs │ │ ├── Unsafe/ │ │ │ ├── UTF8StringView.bindings.cs │ │ │ ├── UnsafeUtility.bindings.cs │ │ │ ├── UnsafeUtility.cs │ │ │ └── UnsafeUtilityPatched.cs │ │ ├── WSA/ │ │ │ ├── WSAApplication.bindings.cs │ │ │ ├── WSACursor.bindings.cs │ │ │ ├── WSALauncher.bindings.cs │ │ │ └── WSATiles.bindings.cs │ │ ├── Windows/ │ │ │ ├── AppTrial.bindings.cs │ │ │ ├── PhotoCapture.bindings.cs │ │ │ ├── Speech.bindings.cs │ │ │ ├── Speech.cs │ │ │ ├── VideoCapture.bindings.cs │ │ │ ├── WebCam.bindings.cs │ │ │ ├── WindowsCrashReporting.bindings.cs │ │ │ ├── WindowsCrypto.bindings.cs │ │ │ ├── WindowsDirectory.bindings.cs │ │ │ ├── WindowsFile.bindings.cs │ │ │ ├── WindowsInput.bindings.cs │ │ │ └── WindowsInput.cs │ │ ├── XR/ │ │ │ └── Pose.cs │ │ └── iOS/ │ │ ├── AppleDevice.bindings.cs │ │ ├── OnDemandResources.bindings.cs │ │ ├── iOS.deprecated.cs │ │ ├── iOSDevice.bindings.cs │ │ ├── iOSDevice.cs │ │ ├── iOSReplayKit.bindings.cs │ │ ├── iOSSystemGestureDeferMode.cs │ │ ├── tvOS.bindings.cs │ │ ├── tvOS.bindings.deprecated.cs │ │ └── tvOSDevice.cs │ ├── Jobs/ │ │ ├── Managed/ │ │ │ ├── BatchQueries.cs │ │ │ ├── IJob.cs │ │ │ ├── IJobFor.cs │ │ │ ├── IJobParallelFor.cs │ │ │ └── IJobParallelForTransform.cs │ │ └── ScriptBindings/ │ │ ├── JobHandle.bindings.cs │ │ └── Jobs.bindings.cs │ ├── NameFormatter/ │ │ └── NameFormatter.bindings.cs │ ├── Profiler/ │ │ └── ScriptBindings/ │ │ ├── FrameDebugger.bindings.cs │ │ ├── MemoryProfiling.bindings.cs │ │ ├── MemoryProfiling.deprecated.cs │ │ ├── Profiler.bindings.cs │ │ ├── ProfilerCategory.cs │ │ ├── ProfilerMarker.bindings.cs │ │ ├── ProfilerMarker.cs │ │ ├── ProfilerRecorder.bindings.cs │ │ ├── ProfilerScreenCapture.cs │ │ ├── ProfilerUnsafeUtility.bindings.cs │ │ ├── ProfilerUtility.cs │ │ ├── Recorder.bindings.cs │ │ └── Sampler.bindings.cs │ ├── Scripting/ │ │ ├── APIUpdating/ │ │ │ └── APIUpdaterRuntimeHelpers.cs │ │ ├── Internal/ │ │ │ └── LifecycleManagement/ │ │ │ ├── AssemblyLoadedAttributes.cs │ │ │ ├── CodeLoadedScope.cs │ │ │ ├── LifecycleAttributeBase.cs │ │ │ ├── RequiredMemberAttribute.cs │ │ │ ├── ScopedLazy.cs │ │ │ └── StaticsCleanupAttributes.cs │ │ └── Marshalling/ │ │ ├── ArrayHandleOnStack.cs │ │ ├── BindingsHelpers.bindings.cs │ │ ├── BlittableArrayWrapper.cs │ │ ├── BlittableListWrapper.cs │ │ ├── ExceptionMarshaller.cs │ │ ├── ManagedSpanWrapper.cs │ │ ├── StringMarshalling.cs │ │ └── UnmanagedCallersOnlyAttribute.cs │ └── Transform/ │ └── ScriptBindings/ │ ├── RectTransform.bindings.cs │ ├── Transform.bindings.cs │ └── TransformAccessArray.bindings.cs ├── Tools/ │ ├── Unity.CecilTools/ │ │ ├── CecilUtils.cs │ │ ├── ElementType.cs │ │ ├── Extensions/ │ │ │ ├── MethodDefinitionExtensions.cs │ │ │ ├── ResolutionExtensions.cs │ │ │ ├── TypeDefinitionExtensions.cs │ │ │ └── TypeReferenceExtensions.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── Unity.CecilTools.gen.csproj │ └── Unity.SerializationLogic/ │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Unity.SerializationLogic.gen.csproj │ ├── UnityEngineTypePredicates.cs │ └── UnitySerializationLogic.cs ├── artifacts/ │ └── generated/ │ └── UnityEvent/ │ ├── UnityEvent_0.cs │ ├── UnityEvent_1.cs │ ├── UnityEvent_2.cs │ ├── UnityEvent_3.cs │ └── UnityEvent_4.cs └── third-party-notices.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/BeeBuildProgramCommon.Data.gen.csproj ================================================  BeeBuildProgramCommon.Data netstandard2.1 false false false latest 1701 ================================================ FILE: Editor/IncrementalBuildPipeline/BeeBuildProgramCommon.Data/Data.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License namespace BeeBuildProgramCommon.Data { public class PackageInfo { public string Name; public string ResolvedPath; } public struct Version { public int Release, Major, Minor; public Version(int release, int major, int minor) { Release = release; Major = major; Minor = minor; } } public class ConfigurationData { public string Il2CppDir; public string UnityLinkerPath; public string Il2CppPath; public string NetCoreRunPath; public string DotNetExe; public string EditorContentsPath; public PackageInfo[] Packages; public string UnityVersion; public Version UnityVersionNumeric; public string UnitySourceCodePath; public bool Batchmode; public bool EmitDataForBeeWhy; public string NamedPipeOrUnixSocket; } } ================================================ FILE: Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/Data.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License namespace PlayerBuildProgramLibrary.Data { public class Plugin { public string AssetPath; public string DestinationPath; public string Architecture; public string CompileFlags; public bool AddToEmbeddedBinaries; public override string ToString() { return $"'{AssetPath} -> '{DestinationPath}' ({Architecture})"; } } public class PluginsData { public Plugin[] Plugins = new Plugin[0]; } public class GenerateNativePluginsForAssembliesArgs { public string PluginOutputFolder; public string SymbolOutputFolder; public string[] Assemblies; } public class GenerateNativePluginsForAssembliesSettings { public bool HasCallback; public string DisplayName; public string[] AdditionalInputFiles = new string[0]; } public class PlayerBuildConfig { public string DestinationPath; public string StagingArea; public string DataFolder; public string CompanyName; public string ProductName; public string PlayerPackage; public string ApplicationIdentifier; public string Architecture; public ScriptingBackend ScriptingBackend; public bool NoGUID; public bool InstallIntoBuildsFolder; public bool GenerateIdeProject; public bool Development; public bool UseNewInputSystem; public GenerateNativePluginsForAssembliesSettings GenerateNativePluginsForAssembliesSettings; public Services Services; public string[] ManagedAssemblies; public StreamingAssetsFile[] StreamingAssetsFiles; } public class BuiltFilesOutput { public string[] Files = new string[0]; public string BootConfigArtifact; } public class LinkerConfig { public string[] LinkXmlFiles = new string[0]; public string[] AssembliesToProcess = new string[0]; public string EditorToLinkerData; public string Runtime; public string Profile; public string Ruleset; public string ModulesAssetPath; public string[] AdditionalArgs = new string[0]; public bool AllowDebugging; public bool PerformEngineStripping; } public class Il2CppConfig { public bool EnableDeepProfilingSupport; public bool EnableFullGenericSharing; public string Profile; public string[] IDEProjectDefines; public string ConfigurationName; public bool GcWBarrierValidation; public bool GcIncremental; public string[] AdditionalCppFiles = new string[0]; public string[] AdditionalArgs = new string[0]; public string CompilerFlags; public string[] AdditionalLibraries; public string[] AdditionalDefines; public string[] AdditionalIncludeDirectories; public string[] AdditionalLinkDirectories; public string LinkerFlags; public string LinkerFlagsFile; public string ExtraTypes; public bool CreateSymbolFiles; public bool AllowDebugging; public string SysRootPath; public string ToolChainPath; public string RelativeDataPath; public bool GenerateUsymFile; public string UsymtoolPath; } public class Services { public bool EnableUnityConnect; public bool EnablePerformanceReporting; public bool EnableAnalytics; public bool EnableCrashReporting; public bool EnableInsights; } public class StreamingAssetsFile { public string File; public string RelativePath; } public enum ScriptingBackend { Mono, IL2CPP, CoreCLR, } } ================================================ FILE: Editor/IncrementalBuildPipeline/PlayerBuildProgramLibrary.Data/PlayerBuildProgramLibrary.Data.gen.csproj ================================================  PlayerBuildProgramLibrary.Data netstandard2.1 false false false latest 1701 PlayerBuildProgramLibrary.Data ================================================ FILE: Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/Data.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License namespace ScriptCompilationBuildProgram.Data { public static class Constants { public const string ScriptAssembliesTarget = "ScriptAssemblies"; public const string ScriptAssembliesAndTypeDBTarget = "ScriptAssembliesAndTypeDB"; public const string MovedFromExtension = "mvfrm"; } public class ScriptCompilationData { public AssemblyData[] Assemblies; public AssemblyData[] CodegenAssemblies; public string DotnetRuntimePath; public string DotnetRoslynPath; public string MovedFromExtractorPath; public string OutputDirectory; public bool Debug; public string BuildTarget; public string Localization; public string BuildPlayerDataOutput; public bool ExtractRuntimeInitializeOnLoads; public bool EnableDiagnostics; public bool EmitInfoForScriptUpdater; public string[] AssembliesToScanForTypeDB; public string[] SearchPaths; } public class AssemblyData { public string Name; public string[] SourceFiles = new string[0]; public string[] Defines = new string[0]; public string[] PrebuiltReferences = new string[0]; public int[] References = new int[0]; public bool AllowUnsafeCode; public string RuleSet; public string AnalyzerConfigPath; public string LanguageVersion; public bool UseDeterministicCompilation; public bool SuppressCompilerWarnings; public string[] Analyzers = new string[0]; public string[] AdditionalFiles = new string[0]; public string Asmdef; public string[] BclDirectories = new string[0]; public string[] CustomCompilerOptions = new string[0]; public int DebugIndex; public bool SkipCodeGen; public string Path; } public class ScriptCompilationData_Out { public AssemblyData_Out[] Assemblies; public bool LocalizeCompilerMessages; } public class AssemblyData_Out { public string Path; public string ScriptUpdaterRsp; public string MovedFromExtractorFile; } } ================================================ FILE: Editor/IncrementalBuildPipeline/ScriptCompilationBuildProgram.Data/ScriptCompilationBuildProgram.Data.gen.csproj ================================================  ScriptCompilationBuildProgram.Data netstandard2.1 false false false latest 1701 ================================================ FILE: Editor/Mono/2D/Common/ScriptBindings/SpriteEditorExtension.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Bindings; using UnityEngine.U2D; namespace UnityEditor.U2D { [NativeHeader("Editor/Src/2D/SpriteEditorExtension.h")] public static class SpriteEditorExtension { public static GUID GetSpriteID(this Sprite sprite) { return new GUID(GetSpriteIDScripting(sprite)); } public static void SetSpriteID(this Sprite sprite, GUID guid) { SetSpriteIDScripting(sprite, guid.ToString()); } private static extern string GetSpriteIDScripting([NotNull] Sprite sprite); private static extern void SetSpriteIDScripting([NotNull] Sprite sprite, string spriteID); internal static extern SpriteAtlas GetActiveAtlas([NotNull] this Sprite sprite); internal static extern string GetActiveAtlasName([NotNull] this Sprite sprite); internal static extern Texture2D GetActiveAtlasTexture([NotNull] this Sprite sprite); internal static extern Rect GetActiveAtlasTextureRect([NotNull] this Sprite sprite); internal static extern Vector2 GetActiveAtlasTextureRectOffset([NotNull] this Sprite sprite); internal static extern Texture2D GetActiveAtlasAlphaTexture([NotNull] this Sprite sprite); } } ================================================ FILE: Editor/Mono/2D/Common/SpriteEditorUtility.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditor; using UnityEngine; using UnityEngine.Assertions; using Object = UnityEngine.Object; namespace UnityEditorInternal { static internal class SpriteEditorUtility { public static Vector2 GetPivotValue(SpriteAlignment alignment, Vector2 customOffset) { switch (alignment) { case SpriteAlignment.BottomLeft: return new Vector2(0f, 0f); case SpriteAlignment.BottomCenter: return new Vector2(0.5f, 0f); case SpriteAlignment.BottomRight: return new Vector2(1f, 0f); case SpriteAlignment.LeftCenter: return new Vector2(0f, 0.5f); case SpriteAlignment.Center: return new Vector2(0.5f, 0.5f); case SpriteAlignment.RightCenter: return new Vector2(1f, 0.5f); case SpriteAlignment.TopLeft: return new Vector2(0f, 1f); case SpriteAlignment.TopCenter: return new Vector2(0.5f, 1f); case SpriteAlignment.TopRight: return new Vector2(1f, 1f); case SpriteAlignment.Custom: return customOffset; } return Vector2.zero; } public static Rect RoundedRect(Rect rect) { return new Rect( Mathf.RoundToInt(rect.xMin), Mathf.RoundToInt(rect.yMin), Mathf.RoundToInt(rect.width), Mathf.RoundToInt(rect.height) ); } public static Rect RoundToInt(Rect r) { r.xMin = Mathf.RoundToInt(r.xMin); r.yMin = Mathf.RoundToInt(r.yMin); r.xMax = Mathf.RoundToInt(r.xMax); r.yMax = Mathf.RoundToInt(r.yMax); return r; } public static Rect ClampedRect(Rect rect, Rect clamp, bool maintainSize) { Rect r = new Rect(rect); if (maintainSize) { Vector2 center = rect.center; if (center.x + Mathf.Abs(rect.width) * .5f > clamp.xMax) center.x = clamp.xMax - rect.width * .5f; if (center.x - Mathf.Abs(rect.width) * .5f < clamp.xMin) center.x = clamp.xMin + rect.width * .5f; if (center.y + Mathf.Abs(rect.height) * .5f > clamp.yMax) center.y = clamp.yMax - rect.height * .5f; if (center.y - Mathf.Abs(rect.height) * .5f < clamp.yMin) center.y = clamp.yMin + rect.height * .5f; r.center = center; } else { if (r.width > 0f) { r.xMin = Mathf.Max(rect.xMin, clamp.xMin); r.xMax = Mathf.Min(rect.xMax, clamp.xMax); } else { r.xMin = Mathf.Min(rect.xMin, clamp.xMax); r.xMax = Mathf.Max(rect.xMax, clamp.xMin); } if (r.height > 0f) { r.yMin = Mathf.Max(rect.yMin, clamp.yMin); r.yMax = Mathf.Min(rect.yMax, clamp.yMax); } else { r.yMin = Mathf.Min(rect.yMin, clamp.yMax); r.yMax = Mathf.Max(rect.yMax, clamp.yMin); } } r.width = Mathf.Abs(r.width); r.height = Mathf.Abs(r.height); return r; } public static void DrawBox(Rect position) { Vector3[] points = new Vector3[5]; int i = 0; points[i++] = new Vector3(position.xMin, position.yMin, 0f); points[i++] = new Vector3(position.xMax, position.yMin, 0f); points[i++] = new Vector3(position.xMax, position.yMax, 0f); points[i++] = new Vector3(position.xMin, position.yMax, 0f); DrawLine(points[0], points[1]); DrawLine(points[1], points[2]); DrawLine(points[2], points[3]); DrawLine(points[3], points[0]); } public static void DrawLine(Vector3 p1, Vector3 p2) { Assert.IsTrue(Event.current.type == EventType.Repaint); GL.Vertex(p1); GL.Vertex(p2); } public static void BeginLines(Color color) { Assert.IsTrue(Event.current.type == EventType.Repaint); HandleUtility.ApplyWireMaterial(); GL.PushMatrix(); GL.MultMatrix(Handles.matrix); GL.Begin(GL.LINES); GL.Color(color); } public static void EndLines() { Assert.IsTrue(Event.current.type == EventType.Repaint); GL.End(); GL.PopMatrix(); } public static void FourIntFields(Vector2 rectSize, GUIContent label, GUIContent labelX, GUIContent labelY, GUIContent labelZ, GUIContent labelW, ref int x, ref int y, ref int z, ref int w) { Rect rect = GUILayoutUtility.GetRect(rectSize.x, rectSize.y); Rect labelRect = rect; labelRect.width = EditorGUIUtility.labelWidth; labelRect.height = EditorGUI.kSingleLineHeight; GUI.Label(labelRect, label); Rect fieldRect = rect; fieldRect.width -= EditorGUIUtility.labelWidth; fieldRect.height = EditorGUI.kSingleLineHeight; fieldRect.x += EditorGUIUtility.labelWidth; fieldRect.width /= 2; fieldRect.width -= EditorGUI.kSpacingSubLabel; float oldLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = EditorGUI.CalcPrefixLabelWidth(labelX); GUI.SetNextControlName("FourIntFields_x"); x = EditorGUI.IntField(fieldRect, labelX, x); fieldRect.x += fieldRect.width + EditorGUI.kSpacing; GUI.SetNextControlName("FourIntFields_y"); y = EditorGUI.IntField(fieldRect, labelY, y); fieldRect.y += EditorGUI.kSingleLineHeight + EditorGUI.kVerticalSpacingMultiField; fieldRect.x -= fieldRect.width + EditorGUI.kSpacing; GUI.SetNextControlName("FourIntFields_z"); z = EditorGUI.IntField(fieldRect, labelZ, z); fieldRect.x += fieldRect.width + EditorGUI.kSpacing; GUI.SetNextControlName("FourIntFields_w"); w = EditorGUI.IntField(fieldRect, labelW, w); EditorGUIUtility.labelWidth = oldLabelWidth; } } } ================================================ FILE: Editor/Mono/2D/Common/TexturePlatformSettingsController.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; using UnityEditor.U2D.Interface; using UnityEngine; using UnityEngine.Assertions; using TargetAttributes = UnityEditor.BuildTargetDiscovery.TargetAttributes; namespace UnityEditor.U2D.Common { internal class TexturePlatformSettingsViewController : ITexturePlatformSettingsController { public bool HandleDefaultSettings(List platformSettings, ITexturePlatformSettingsView view, ITexturePlatformSettingsFormatHelper formatHelper) { Assert.IsTrue(platformSettings.Count > 0, "At least 1 platform setting is needed to display the texture platform setting UI."); var allSize = platformSettings[0].maxTextureSize; var allFormat = platformSettings[0].format; var allCompression = platformSettings[0].textureCompression; var allUseCrunchedCompression = platformSettings[0].crunchedCompression; var allCompressionQuality = platformSettings[0].compressionQuality; var mixedSize = false; var mixedFormat = false; var mixedCompression = false; var mixedUseCrunchedCompression = false; var mixedCompressionQuality = false; var sizeChanged = false; var formatChanged = false; var compressionChanged = false; var useCrunchedCompressionChanged = false; var compressionQualityChanged = false; for (var i = 1; i < platformSettings.Count; ++i) { var settings = platformSettings[i]; if (settings.maxTextureSize != allSize) mixedSize = true; if (settings.format != allFormat) mixedFormat = true; if (settings.textureCompression != allCompression) mixedCompression = true; if (settings.crunchedCompression != allUseCrunchedCompression) mixedUseCrunchedCompression = true; if (settings.compressionQuality != allCompressionQuality) mixedCompressionQuality = true; } allSize = view.DrawMaxSize(allSize, mixedSize, false, out sizeChanged); int[] formatValues = null; string[] formatStrings = null; formatHelper.AcquireDefaultTextureFormatValuesAndStrings(out formatValues, out formatStrings); allFormat = view.DrawFormat(allFormat, formatValues, formatStrings, mixedFormat, false, out formatChanged); if (allFormat == TextureImporterFormat.Automatic && (!mixedFormat || formatChanged)) { allCompression = view.DrawCompression(allCompression, mixedCompression, false, out compressionChanged); if (allCompression != TextureImporterCompression.Uncompressed && (!mixedCompression || compressionChanged)) { allUseCrunchedCompression = view.DrawUseCrunchedCompression(allUseCrunchedCompression, mixedUseCrunchedCompression, false, out useCrunchedCompressionChanged); if (allUseCrunchedCompression && (!mixedUseCrunchedCompression || useCrunchedCompressionChanged)) { allCompressionQuality = view.DrawCompressionQualitySlider(allCompressionQuality, mixedCompressionQuality, false, out compressionQualityChanged); } } } if (sizeChanged || compressionChanged || formatChanged || useCrunchedCompressionChanged || compressionQualityChanged) { for (var i = 0; i < platformSettings.Count; ++i) { if (sizeChanged) platformSettings[i].maxTextureSize = allSize; if (formatChanged) platformSettings[i].format = allFormat; if (compressionChanged) platformSettings[i].textureCompression = allCompression; if (useCrunchedCompressionChanged) platformSettings[i].crunchedCompression = allUseCrunchedCompression; if (compressionQualityChanged) platformSettings[i].compressionQuality = allCompressionQuality; } return true; } else return false; } public bool HandlePlatformSettings(BuildTarget buildTarget, List platformSettings, ITexturePlatformSettingsView view, ITexturePlatformSettingsFormatHelper formatHelper) { Assert.IsTrue(platformSettings.Count > 0, "At least 1 platform setting is needed to display the texture platform setting UI."); var allOverride = platformSettings[0].overridden; var allSize = platformSettings[0].maxTextureSize; var allFormat = platformSettings[0].format; var allCompressionQuality = platformSettings[0].compressionQuality; var allAlphaSplit = platformSettings[0].allowsAlphaSplitting; var mixedOverride = false; var mixedSize = false; var mixedFormat = false; var mixedCompression = false; var mixedAlphaSplit = false; var overrideChanged = false; var sizeChanged = false; var formatChanged = false; var compressionChanged = false; var alphaSplitChanged = false; for (var i = 1; i < platformSettings.Count; ++i) { var settings = platformSettings[i]; if (settings.overridden != allOverride) mixedOverride = true; if (settings.maxTextureSize != allSize) mixedSize = true; if (settings.format != allFormat) mixedFormat = true; if (settings.compressionQuality != allCompressionQuality) mixedCompression = true; if (settings.allowsAlphaSplitting != allAlphaSplit) mixedAlphaSplit = true; } allOverride = view.DrawOverride(allOverride, mixedOverride, out overrideChanged); allSize = view.DrawMaxSize(allSize, mixedSize, mixedOverride || !allOverride, out sizeChanged); int[] formatValues = null; string[] formatStrings = null; formatHelper.AcquireTextureFormatValuesAndStrings(buildTarget, out formatValues, out formatStrings); allFormat = view.DrawFormat(allFormat, formatValues, formatStrings, mixedFormat, mixedOverride || !allOverride, out formatChanged); if (!mixedFormat && formatHelper.TextureFormatRequireCompressionQualityInput(allFormat)) { bool showAsEnum = BuildTargetDiscovery.PlatformHasFlag(buildTarget, TargetAttributes.HasIntegratedGPU); if (showAsEnum) { var compressionMode = 1; if (allCompressionQuality == (int)TextureCompressionQuality.Fast) compressionMode = 0; else if (allCompressionQuality == (int)TextureCompressionQuality.Best) compressionMode = 2; compressionMode = view.DrawCompressionQualityPopup(compressionMode, mixedCompression, mixedOverride || !allOverride, out compressionChanged); if (compressionChanged) { switch (compressionMode) { case 0: allCompressionQuality = (int)TextureCompressionQuality.Fast; break; case 1: allCompressionQuality = (int)TextureCompressionQuality.Normal; break; case 2: allCompressionQuality = (int)TextureCompressionQuality.Best; break; default: Assert.IsTrue(false, "ITexturePlatformSettingsView.DrawCompressionQualityPopup should never return compression option value that's not 0, 1 or 2."); break; } } } else { allCompressionQuality = view.DrawCompressionQualitySlider(allCompressionQuality, mixedCompression, mixedOverride || !allOverride, out compressionChanged); } // show the ETC1 split option only for sprites on platforms supporting ETC. bool isETCPlatform = TextureImporter.IsETC1SupportedByBuildTarget(buildTarget); bool isETCFormatSelected = TextureImporter.IsTextureFormatETC1Compression((TextureFormat)allFormat); if (isETCPlatform && isETCFormatSelected) { allAlphaSplit = view.DrawAlphaSplit(allAlphaSplit, mixedAlphaSplit, mixedOverride || !allOverride, out alphaSplitChanged); } } if (overrideChanged || sizeChanged || formatChanged || compressionChanged || alphaSplitChanged) { for (var i = 0; i < platformSettings.Count; ++i) { if (overrideChanged) platformSettings[i].overridden = allOverride; if (sizeChanged) platformSettings[i].maxTextureSize = allSize; if (formatChanged) platformSettings[i].format = allFormat; if (compressionChanged) platformSettings[i].compressionQuality = allCompressionQuality; if (alphaSplitChanged) platformSettings[i].allowsAlphaSplitting = allAlphaSplit; } return true; } else return false; } } } ================================================ FILE: Editor/Mono/2D/Common/TexturePlatformSettingsFormatHelper.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor.U2D.Interface; namespace UnityEditor.U2D.Common { internal class TexturePlatformSettingsFormatHelper : ITexturePlatformSettingsFormatHelper { public void AcquireTextureFormatValuesAndStrings(BuildTarget buildTarget, out int[] formatValues, out string[] formatStrings) { TextureImportValidFormats.GetPlatformTextureFormatValuesAndStrings(TextureImporterType.Sprite, buildTarget, out formatValues, out formatStrings); } public void AcquireDefaultTextureFormatValuesAndStrings(out int[] formatValues, out string[] formatStrings) { TextureImportValidFormats.GetDefaultTextureFormatValuesAndStrings(TextureImporterType.Sprite, out formatValues, out formatStrings); } public bool TextureFormatRequireCompressionQualityInput(TextureImporterFormat format) { return TextureImporterInspector.IsFormatRequireCompressionSetting(format); } } } ================================================ FILE: Editor/Mono/2D/Common/TexturePlatformSettingsView.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor.U2D.Interface; namespace UnityEditor.U2D.Common { internal class TexturePlatformSettingsView : ITexturePlatformSettingsView { class Styles { public readonly GUIContent textureFormatLabel = EditorGUIUtility.TrTextContent("Format"); public readonly GUIContent maxTextureSizeLabel = EditorGUIUtility.TrTextContent("Max Texture Size", "Maximum size of the packed texture."); public readonly GUIContent compressionLabel = EditorGUIUtility.TrTextContent("Compression", "How will this texture be compressed?"); public readonly GUIContent useCrunchedCompressionLabel = EditorGUIUtility.TrTextContent("Use Crunch Compression", "Texture is crunch-compressed to save space on disk when applicable."); public readonly GUIContent useAlphaSplitLabel = EditorGUIUtility.TrTextContent("Split Alpha Channel", "Alpha for this texture will be preserved by splitting the alpha channel to another texture, and both resulting textures will be compressed using ETC1."); public readonly GUIContent compressionQualityLabel = EditorGUIUtility.TrTextContent("Compressor Quality"); public readonly GUIContent compressionQualitySliderLabel = EditorGUIUtility.TrTextContent("Compressor Quality", "Use the slider to adjust compression quality from 0 (Fastest) to 100 (Best)"); public readonly int[] kMaxTextureSizeValues = { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 }; public readonly GUIContent[] kMaxTextureSizeStrings; public readonly GUIContent[] kTextureCompressionOptions = { EditorGUIUtility.TrTextContent("None", "Texture is not compressed."), EditorGUIUtility.TrTextContent("Low Quality", "Texture compressed with low quality but high performance, high compression format."), EditorGUIUtility.TrTextContent("Normal Quality", "Texture is compressed with a standard format."), EditorGUIUtility.TrTextContent("High Quality", "Texture compressed with a high quality format."), }; public readonly int[] kTextureCompressionValues = { (int)TextureImporterCompression.Uncompressed, (int)TextureImporterCompression.CompressedLQ, (int)TextureImporterCompression.Compressed, (int)TextureImporterCompression.CompressedHQ }; public readonly GUIContent[] kMobileCompressionQualityOptions = { EditorGUIUtility.TrTextContent("Fast"), EditorGUIUtility.TrTextContent("Normal"), EditorGUIUtility.TrTextContent("Best") }; public Styles() { kMaxTextureSizeStrings = new GUIContent[kMaxTextureSizeValues.Length]; for (var i = 0; i < kMaxTextureSizeValues.Length; ++i) kMaxTextureSizeStrings[i] = EditorGUIUtility.TextContent(string.Format("{0}", kMaxTextureSizeValues[i])); } } private static Styles s_Styles; public string buildPlatformTitle { get; set; } internal TexturePlatformSettingsView() { s_Styles = s_Styles ?? new Styles(); } public virtual TextureImporterCompression DrawCompression(TextureImporterCompression defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = (TextureImporterCompression)EditorGUILayout.IntPopup(s_Styles.compressionLabel, (int)defaultValue, s_Styles.kTextureCompressionOptions, s_Styles.kTextureCompressionValues); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } public virtual bool DrawUseCrunchedCompression(bool defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = EditorGUILayout.Toggle(s_Styles.useCrunchedCompressionLabel, defaultValue); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } public virtual bool DrawOverride(bool defaultValue, bool isMixedValue, out bool changed) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = EditorGUILayout.ToggleLeft(EditorGUIUtility.TempContent("Override for " + buildPlatformTitle), defaultValue); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } public virtual int DrawMaxSize(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = EditorGUILayout.IntPopup(s_Styles.maxTextureSizeLabel, defaultValue, s_Styles.kMaxTextureSizeStrings, s_Styles.kMaxTextureSizeValues); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } public virtual TextureImporterFormat DrawFormat(TextureImporterFormat defaultValue, int[] displayValues, string[] displayStrings, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = (TextureImporterFormat)EditorGUILayout.IntPopup(s_Styles.textureFormatLabel, (int)defaultValue, EditorGUIUtility.TempContent(displayStrings), displayValues); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } public virtual int DrawCompressionQualityPopup(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = EditorGUILayout.Popup(s_Styles.compressionQualityLabel, defaultValue, s_Styles.kMobileCompressionQualityOptions); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } public virtual int DrawCompressionQualitySlider(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = EditorGUILayout.IntSlider(s_Styles.compressionQualitySliderLabel, defaultValue, 0, 100); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } public virtual bool DrawAlphaSplit(bool defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { using (new EditorGUI.DisabledScope(isDisabled)) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = isMixedValue; defaultValue = EditorGUILayout.Toggle(s_Styles.useAlphaSplitLabel, defaultValue); EditorGUI.showMixedValue = false; changed = EditorGUI.EndChangeCheck(); return defaultValue; } } } } ================================================ FILE: Editor/Mono/2D/Interface/IEvent.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEvent = UnityEngine.Event; // We are putting this in the Editor folder for now since on SpriteEditorWindow et al. are using it namespace UnityEngine.U2D.Interface { internal interface IEvent { EventType type { get; } string commandName { get; } bool control { get; } bool alt { get; } bool shift { get; } KeyCode keyCode { get; } Vector2 mousePosition { get; } int button { get; } EventModifiers modifiers { get; } EventType GetTypeForControl(int id); void Use(); } internal class Event : IEvent { UnityEvent m_Event; public Event() { m_Event = UnityEvent.current; } public EventType type { get { return m_Event.type; } } public string commandName { get { return m_Event.commandName; } } public bool control { get { return m_Event.control; } } public bool alt { get { return m_Event.alt; } } public bool shift { get { return m_Event.shift; } } public KeyCode keyCode { get { return m_Event.keyCode; } } public Vector2 mousePosition { get { return m_Event.mousePosition; } } public int button { get { return m_Event.button; } } public void Use() { m_Event.Use(); } public EventModifiers modifiers { get { return m_Event.modifiers; } } public EventType GetTypeForControl(int id) { return m_Event.GetTypeForControl(id); } } internal interface IEventSystem { IEvent current { get; } } internal class EventSystem : IEventSystem { public IEvent current { get { return new Event(); } } } } ================================================ FILE: Editor/Mono/2D/Interface/ITexturePlatformSetting.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; using UnityEngine; namespace UnityEditor.U2D.Interface { internal interface ITexturePlatformSettingsView { string buildPlatformTitle { get; set; } TextureImporterCompression DrawCompression(TextureImporterCompression defaultValue, bool isMixedValue, bool isDisabled, out bool changed); bool DrawUseCrunchedCompression(bool defaultValue, bool isMixedValue, bool isDisabled, out bool changed); bool DrawAlphaSplit(bool defaultValue, bool isMixedValue, bool isDisabled, out bool changed); bool DrawOverride(bool defaultValue, bool isMixedValue, out bool changed); int DrawMaxSize(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed); TextureImporterFormat DrawFormat(TextureImporterFormat defaultValue, int[] displayValues, string[] displayStrings, bool isMixedValue, bool isDisabled, out bool changed); int DrawCompressionQualityPopup(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed); int DrawCompressionQualitySlider(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed); } internal interface ITexturePlatformSettingsFormatHelper { void AcquireTextureFormatValuesAndStrings(BuildTarget buildTarget, out int[] displayValues, out string[] displayStrings); void AcquireDefaultTextureFormatValuesAndStrings(out int[] formatValues, out string[] formatStrings); bool TextureFormatRequireCompressionQualityInput(TextureImporterFormat format); } internal interface ITexturePlatformSettingsController { bool HandleDefaultSettings(List platformSettings, ITexturePlatformSettingsView view, ITexturePlatformSettingsFormatHelper formatHelper); bool HandlePlatformSettings(BuildTarget buildTarget, List platformSettings, ITexturePlatformSettingsView view, ITexturePlatformSettingsFormatHelper formatHelper); } } ================================================ FILE: Editor/Mono/2D/SpriteAtlas/EditorSpriteAtlas.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.U2D; using UnityEngine.Bindings; using System; using UnityEditor.AssetImporters; namespace UnityEditor.U2D { [NativeHeader("Runtime/2D/SpriteAtlas/SpriteAtlas.h")] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlasPackingUtilities.h")] public class SpriteAtlasUtility { [FreeFunction("SpriteAtlasExtensions::EnableV2Import")] extern internal static void EnableV2Import(bool onOff); [FreeFunction("SpriteAtlasExtensions::CleanupAtlasPacking")] extern public static void CleanupAtlasPacking(); [FreeFunction("CollectAllSpriteAtlasesAndPack")] extern public static void PackAllAtlases(BuildTarget target, bool canCancel = true); [FreeFunction("PackSpriteAtlases")] extern internal static void PackAtlasesInternal(SpriteAtlas[] atlases, BuildTarget target, bool canCancel = true, bool invokedFromImporter = false, bool unloadSprites = false); public static void PackAtlases(SpriteAtlas[] atlases, BuildTarget target, bool canCancel = true) { if (atlases == null) throw new ArgumentNullException("atlases", "Value for parameter atlases is null"); foreach (var atlas in atlases) if (atlas == null) throw new ArgumentNullException("atlases", "One of the elements in atlases is null. Please check your Inputs."); PackAtlasesInternal(atlases, target, canCancel, false, true); } } [StructLayout(LayoutKind.Sequential)] public struct SpriteAtlasTextureSettings { [NativeName("anisoLevel")] private int m_AnisoLevel; [NativeName("compressionQuality")] private int m_CompressionQuality; [NativeName("maxTextureSize")] private int m_MaxTextureSize; [NativeName("textureCompression")] private int m_TextureCompression; [NativeName("filterMode")] private int m_FilterMode; [NativeName("generateMipMaps")] private bool m_GenerateMipMaps; [NativeName("readable")] private bool m_Readable; [NativeName("crunchedCompression")] private bool m_CrunchedCompression; [NativeName("sRGB")] private bool m_sRGB; public int maxTextureSize { get { return m_MaxTextureSize; } } public int anisoLevel { get { return m_AnisoLevel; } set { m_AnisoLevel = value; } } public FilterMode filterMode { get { return (FilterMode)m_FilterMode; } set { m_FilterMode = (int)value; } } public bool generateMipMaps { get { return m_GenerateMipMaps; } set { m_GenerateMipMaps = value; } } public bool readable { get { return m_Readable; } set { m_Readable = value; } } public bool sRGB { get { return m_sRGB; } set { m_sRGB = value; } } } [StructLayout(LayoutKind.Sequential)] public struct SpriteAtlasPackingSettings { [NativeName("blockOffset")] private int m_BlockOffset; [NativeName("padding")] private int m_Padding; [NativeName("allowAlphaSplitting")] private bool m_AllowAlphaSplitting; [NativeName("enableRotation")] private bool m_EnableRotation; [NativeName("enableTightPacking")] private bool m_EnableTightPacking; [NativeName("enableAlphaDilation")] private bool m_EnableAlphaDilation; public int blockOffset { get { return m_BlockOffset; } set { m_BlockOffset = value; } } public int padding { get { return m_Padding; } set { m_Padding = value; } } public bool enableRotation { get { return m_EnableRotation; } set { m_EnableRotation = value; } } public bool enableTightPacking { get { return m_EnableTightPacking; } set { m_EnableTightPacking = value; } } public bool enableAlphaDilation { get { return m_EnableAlphaDilation; } set { m_EnableAlphaDilation = value; } } } [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterTypes.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.bindings.h")] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlas_EditorTypes.h")] [NativeHeader("Runtime/2D/SpriteAtlas/SpriteAtlas.h")] public static class SpriteAtlasExtensions { extern public static void Add([NotNull] this SpriteAtlas spriteAtlas, UnityEngine.Object[] objects); extern public static void Remove([NotNull] this SpriteAtlas spriteAtlas, UnityEngine.Object[] objects); extern internal static void RemoveAt([NotNull] this SpriteAtlas spriteAtlas, int index); extern public static UnityEngine.Object[] GetPackables([NotNull] this SpriteAtlas spriteAtlas); extern internal static void SetV2([NotNull] this SpriteAtlas spriteAtlas); internal static void RegisterAndPackAtlas(this SpriteAtlas spriteAtlas, AssetImportContext context, AssetImporter importer, U2D.ScriptablePacker scriptablePacker) { RegisterAndPackAtlasInternal(spriteAtlas, context, importer, scriptablePacker); } extern private static void RegisterAndPackAtlasInternal([NotNull] this SpriteAtlas spriteAtlas, [NotNull] AssetImportContext context, [NotNull] AssetImporter importer, UnityEngine.Object scriptablePacker); extern public static SpriteAtlasTextureSettings GetTextureSettings([NotNull] this SpriteAtlas spriteAtlas); extern public static void SetTextureSettings([NotNull] this SpriteAtlas spriteAtlas, SpriteAtlasTextureSettings src); extern public static SpriteAtlasPackingSettings GetPackingSettings([NotNull] this SpriteAtlas spriteAtlas); extern public static void SetPackingSettings([NotNull] this SpriteAtlas spriteAtlas, SpriteAtlasPackingSettings src); [NativeName("GetPlatformSettings")] extern private static TextureImporterPlatformSettings GetPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, string buildTarget); public static TextureImporterPlatformSettings GetPlatformSettings(this SpriteAtlas spriteAtlas, string buildTarget) { buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". return GetPlatformSettings_Internal(spriteAtlas, buildTarget); } [NativeName("SetPlatformSettings")] extern private static void SetPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src); public static void SetPlatformSettings(this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src) { src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". SetPlatformSettings_Internal(spriteAtlas, src); } extern public static void SetIncludeInBuild([NotNull] this SpriteAtlas spriteAtlas, bool value); extern public static void SetIsVariant([NotNull] this SpriteAtlas spriteAtlas, bool value); extern public static void SetMasterAtlas([NotNull] this SpriteAtlas spriteAtlas, SpriteAtlas value); extern public static void SetVariantScale([NotNull] this SpriteAtlas spriteAtlas, float value); extern public static bool IsIncludeInBuild([NotNull] this SpriteAtlas spriteAtlas); extern public static SpriteAtlas GetMasterAtlas([NotNull] this SpriteAtlas spriteAtlas); extern internal static void CopyMasterAtlasSettings([NotNull] this SpriteAtlas spriteAtlas); extern internal static string GetHash([NotNull] this SpriteAtlas spriteAtlas); extern internal static Texture2D[] GetPreviewTextures([NotNull] this SpriteAtlas spriteAtlas); extern internal static Texture2D[] GetPreviewAlphaTextures([NotNull] this SpriteAtlas spriteAtlas); extern internal static TextureFormat GetTextureFormat([NotNull] this SpriteAtlas spriteAtlas, BuildTarget target); extern internal static Sprite[] GetPackedSprites([NotNull] this SpriteAtlas spriteAtlas); extern internal static Hash128 GetStoredHash([NotNull] this SpriteAtlas spriteAtlas); [NativeName("GetSecondaryPlatformSettings")] extern private static TextureImporterPlatformSettings GetSecondaryPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, string buildTarget, string secondaryTextureName); internal static TextureImporterPlatformSettings GetSecondaryPlatformSettings(this SpriteAtlas spriteAtlas, string buildTarget, string secondaryTextureName) { buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". return GetSecondaryPlatformSettings_Internal(spriteAtlas, buildTarget, secondaryTextureName); } [NativeName("SetSecondaryPlatformSettings")] extern private static void SetSecondaryPlatformSettings_Internal([NotNull] this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src, string secondaryTextureName); internal static void SetSecondaryPlatformSettings(this SpriteAtlas spriteAtlas, TextureImporterPlatformSettings src, string secondaryTextureName) { src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". SetSecondaryPlatformSettings_Internal(spriteAtlas, src, secondaryTextureName); } extern internal static bool GetSecondaryColorSpace([NotNull] this SpriteAtlas spriteAtlas, string secondaryTextureName); extern internal static void SetSecondaryColorSpace([NotNull] this SpriteAtlas spriteAtlas, string secondaryTextureName, bool srGB); extern internal static void DeleteSecondaryPlatformSettings([NotNull] this SpriteAtlas spriteAtlas, string secondaryTextureName); extern internal static string GetSecondaryTextureNameInAtlas(string atlasTextureName); extern internal static string GetPageNumberInAtlas(string atlasTextureName); } } ================================================ FILE: Editor/Mono/2D/SpriteAtlas/EditorSpritePacking.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Unity.Collections; using UnityEngine; using UnityEngine.U2D; using UnityEngine.Bindings; using UnityEngine.Scripting; using Unity.Collections.LowLevel.Unsafe; namespace UnityEditor.U2D.SpritePacking { [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] internal struct SpritePackInfoInternal { public int guid; public int texIndex; public int indexCount; public int vertexCount; public RectInt rect; public IntPtr indices; public IntPtr vertices; }; [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] internal struct SpritePackTextureInfoInternal { public int width; public int height; public IntPtr buffer; }; [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] internal struct SpritePackDatasetInternal { public SpritePackInfoInternal spriteData; public SpritePackTextureInfoInternal textureData; }; [StructLayout(LayoutKind.Sequential)] internal struct SpritePackInfo { public int guid; public int texIndex; public int indexCount; public int vertexCount; public RectInt rect; public NativeArray indices; public NativeArray vertices; }; [StructLayout(LayoutKind.Sequential)] internal struct SpritePackTextureInfo { public int width; public int height; public NativeArray buffer; }; internal struct SpritePackDataset { public List spriteData; public List textureData; }; internal struct SpritePackConfig { public int padding; }; [NativeHeader("Runtime/2D/SpriteAtlas/SpriteAtlas.h")] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlasPackingUtilities.h")] internal class SpritePackUtility { internal unsafe static SpritePackDataset PackCustomSpritesWrapper(SpritePackDataset input, SpritePackConfig packConfig, Allocator alloc) { var output = new SpritePackDataset(); var spriteCount = input.spriteData.Count; if (0 == spriteCount) return output; var data = new NativeArray(spriteCount, Allocator.Temp, NativeArrayOptions.ClearMemory); for (int i = 0; i < spriteCount; ++i) { SpritePackDatasetInternal rawData = data[i]; rawData.spriteData.guid = input.spriteData[i].guid; int texIndex = input.spriteData[i].texIndex; if (texIndex >= input.textureData.Count) { data.Dispose(); throw new ArgumentOutOfRangeException("texIndex", "texIndex must point to a valid index in textureData list."); } rawData.spriteData.texIndex = texIndex; rawData.spriteData.indexCount = input.spriteData[i].indexCount; rawData.spriteData.vertexCount = input.spriteData[i].vertexCount; rawData.spriteData.rect = input.spriteData[i].rect; rawData.spriteData.indices = input.spriteData[i].indices.IsCreated ? (IntPtr)input.spriteData[i].indices.GetUnsafePtr() : (IntPtr)0; rawData.spriteData.vertices = input.spriteData[i].vertices.IsCreated ? (IntPtr)input.spriteData[i].vertices.GetUnsafePtr() : (IntPtr)0; rawData.textureData.width = input.textureData[texIndex].width; rawData.textureData.height = input.textureData[texIndex].height; rawData.textureData.buffer = input.textureData[texIndex].buffer.IsCreated ? (IntPtr)input.textureData[texIndex].buffer.GetUnsafePtr() : (IntPtr)0; data[i] = rawData; } var spriteOutput = (SpritePackDatasetInternal*)PackCustomSpritesInternal(spriteCount, (SpritePackDatasetInternal*)data.GetUnsafePtr(), packConfig); if (null != spriteOutput) { var colorBufferArray = new SpritePackTextureInfo[spriteCount]; for (int i = 0; i < spriteCount; ++i) { SpritePackTextureInfoInternal rawBuffer = spriteOutput[i].textureData; int index = spriteOutput[i].spriteData.texIndex; SpritePackTextureInfo outputBuffer = colorBufferArray[index]; // New Texture. Copy. if (!outputBuffer.buffer.IsCreated) { outputBuffer.width = rawBuffer.width; outputBuffer.height = rawBuffer.height; Color32* rawColor = (Color32*)rawBuffer.buffer; if (null != rawColor) { outputBuffer.buffer = new NativeArray(rawBuffer.width * rawBuffer.height, alloc); UnsafeUtility.MemCpy(outputBuffer.buffer.GetUnsafePtr(), rawColor, rawBuffer.width * rawBuffer.height * sizeof(Color32)); } UnsafeUtility.Free((void*)rawBuffer.buffer, Allocator.Persistent); } colorBufferArray[index] = outputBuffer; } output.textureData = new List(colorBufferArray); var spriteDataArray = new SpritePackInfo[spriteCount]; for (int i = 0; i < spriteCount; ++i) { SpritePackInfo spriteData = spriteDataArray[i]; spriteData.guid = spriteOutput[i].spriteData.guid; spriteData.indexCount = spriteOutput[i].spriteData.indexCount; spriteData.vertexCount = spriteOutput[i].spriteData.vertexCount; spriteData.rect = spriteOutput[i].spriteData.rect; if (0 != spriteData.indexCount && 0 != spriteData.vertexCount) { int* rawIndices = (int*)spriteOutput[i].spriteData.indices; if (null != rawIndices) { spriteData.indices = new NativeArray(spriteOutput[i].spriteData.indexCount, alloc); UnsafeUtility.MemCpy(spriteData.indices.GetUnsafePtr(), rawIndices, spriteOutput[i].spriteData.indexCount * sizeof(int)); } Vector3* rawVertices = (Vector3*)spriteOutput[i].spriteData.vertices; if (null != rawVertices) { spriteData.vertices = new NativeArray(spriteOutput[i].spriteData.vertexCount, alloc); UnsafeUtility.MemCpy(spriteData.vertices.GetUnsafePtr(), rawVertices, spriteOutput[i].spriteData.vertexCount * sizeof(Vector3)); } UnsafeUtility.Free((void*)spriteOutput[i].spriteData.indices, Allocator.Persistent); UnsafeUtility.Free((void*)spriteOutput[i].spriteData.vertices, Allocator.Persistent); } spriteData.texIndex = spriteOutput[i].spriteData.texIndex; spriteDataArray[i] = spriteData; } output.spriteData = new List(spriteDataArray); UnsafeUtility.Free((void*)spriteOutput, Allocator.Persistent); } data.Dispose(); return output; } internal static SpritePackDataset PackCustomSprites(SpritePackDataset spriteDataInput, SpritePackConfig packConfig, Allocator outputAlloc) { return PackCustomSpritesWrapper(spriteDataInput, packConfig, outputAlloc); } [NativeThrows] [FreeFunction("PackCustomSprites")] extern internal unsafe static IntPtr PackCustomSpritesInternal(int spriteCount, SpritePackDatasetInternal* data, SpritePackConfig packConfig); } } ================================================ FILE: Editor/Mono/2D/SpriteAtlas/SpriteAtlasAsset.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.U2D; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections; namespace UnityEditor.U2D { [UsedByNativeCode] public abstract class ScriptablePacker : ScriptableObject { public enum PackTransform { None = 0, FlipHorizontal = 1, FlipVertical = 2, Rotate180 = 3, } [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] public struct SpritePack { public int x; public int y; public int page; public PackTransform rot; }; [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] public struct SpriteData { public int guid; public int texIndex; public int indexCount; public int vertexCount; public int indexOffset; public int vertexOffset; public RectInt rect; public SpritePack output; }; [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] public struct TextureData { public int width; public int height; public int bufferOffset; }; [RequiredByNativeCode] [StructLayout(LayoutKind.Sequential)] internal struct PackerDataInternal { public int colorCount; public IntPtr colorData; public int spriteCount; public IntPtr spriteData; public int textureCount; public IntPtr textureData; public int indexCount; public IntPtr indexData; public int vertexCount; public IntPtr vertexData; }; [StructLayout(LayoutKind.Sequential)] public struct PackerData { public NativeArray colorData; public NativeArray spriteData; public NativeArray textureData; public NativeArray indexData; public NativeArray vertexData; }; // Public function to pack. public abstract bool Pack(SpriteAtlasPackingSettings config, SpriteAtlasTextureSettings setting, PackerData input); // Internal Glue function. [RequiredByNativeCode] internal bool PackInternal(SpriteAtlasPackingSettings config, SpriteAtlasTextureSettings setting, PackerDataInternal packerData) { var input = new PackerData(); unsafe { input.colorData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((void*)packerData.colorData, packerData.colorCount, Allocator.None); input.spriteData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((void*)packerData.spriteData, packerData.spriteCount, Allocator.None); input.textureData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((void*)packerData.textureData, packerData.textureCount, Allocator.None); input.indexData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((void*)packerData.indexData, packerData.indexCount, Allocator.None); input.vertexData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((void*)packerData.vertexData, packerData.vertexCount, Allocator.None); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref input.colorData, AtomicSafetyHandle.GetTempMemoryHandle()); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref input.spriteData, AtomicSafetyHandle.GetTempMemoryHandle()); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref input.textureData, AtomicSafetyHandle.GetTempMemoryHandle()); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref input.indexData, AtomicSafetyHandle.GetTempMemoryHandle()); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref input.vertexData, AtomicSafetyHandle.GetTempMemoryHandle()); } return Pack(config, setting, input); } }; // SpriteAtlas Importer lets you modify [[SpriteAtlas]] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlasAsset.h")] [NativeType(Header = "Editor/Src/2D/SpriteAtlas/SpriteAtlasAsset.h")] public class SpriteAtlasAsset : UnityEngine.Object { public SpriteAtlasAsset() { Internal_Create(this); } extern private static void Internal_Create([Writable] SpriteAtlasAsset self); extern public bool isVariant { [NativeMethod("GetIsVariant")] get; } extern public void SetIsVariant(bool value); extern public void SetMasterAtlas(SpriteAtlas atlas); extern public SpriteAtlas GetMasterAtlas(); extern internal UnityEngine.Object GetPacker(); extern internal void SetPacker(UnityEngine.Object obj); extern public void Add(UnityEngine.Object[] objects); extern public void Remove(UnityEngine.Object[] objects); public void SetScriptablePacker(ScriptablePacker obj) { SetPacker(obj); } extern internal void RemoveAt(int index); [Obsolete("SetVariantScale is no longer supported and will be removed. Use SpriteAtlasImporter.SetVariantScale instead.")] public void SetVariantScale(float value) { } [Obsolete("SetIncludeInBuild is no longer supported and will be removed. Use SpriteAtlasImporter.SetIncludeInBuild instead.")] public void SetIncludeInBuild(bool value) { } [Obsolete("IsIncludeInBuild is no longer supported and will be removed. Use SpriteAtlasImporter.IsIncludeInBuild instead.")] public bool IsIncludeInBuild() { return true; } [Obsolete("SetPlatformSettings is no longer supported and will be removed. Use SpriteAtlasImporter.SetPlatformSettings instead.")] public void SetPlatformSettings(TextureImporterPlatformSettings src) { } [Obsolete("SetTextureSettings is no longer supported and will be removed. Use SpriteAtlasImporter.SetTextureSettings instead.")] public void SetTextureSettings(SpriteAtlasTextureSettings src) { } [Obsolete("SetPackingSettings is no longer supported and will be removed. Use SpriteAtlasImporter.SetPackingSettings instead.")] public void SetPackingSettings(SpriteAtlasPackingSettings src) { } [Obsolete("GetPackingSettings is no longer supported and will be removed. Use SpriteAtlasImporter.GetPackingSettings instead.")] public SpriteAtlasPackingSettings GetPackingSettings() { return new SpriteAtlasPackingSettings(); } [Obsolete("GetTextureSettings is no longer supported and will be removed. Use SpriteAtlasImporter.GetTextureSettings instead.")] public SpriteAtlasTextureSettings GetTextureSettings() { return new SpriteAtlasTextureSettings(); } [Obsolete("GetPlatformSettings is no longer supported and will be removed. Use SpriteAtlasImporter.GetPlatformSettingss instead.")] public TextureImporterPlatformSettings GetPlatformSettings(string buildTarget) { return new TextureImporterPlatformSettings(); } // Load SpriteAtlasAsset public static SpriteAtlasAsset Load(string assetPath) { var objs = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(assetPath); return (objs.Length > 0) ? objs[0] as SpriteAtlasAsset : null; } public static void Save(SpriteAtlasAsset asset, string assetPath) { if (asset == null) throw new ArgumentNullException("Parameter asset is null"); var objs = new UnityEngine.Object[] { asset }; UnityEditorInternal.InternalEditorUtility.SaveToSerializedFileAndForget(objs, assetPath, UnityEditor.EditorSettings.serializationMode != UnityEditor.SerializationMode.ForceBinary); } } }; ================================================ FILE: Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.ComponentModel; using UnityEditor.Build; using UnityEditor.AssetImporters; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.U2D; namespace UnityEditor.U2D { // SpriteAtlas Importer lets you modify [[SpriteAtlas]] [HelpURL("https://docs.unity3d.com/6000.2/Documentation/Manual/sprite/atlas/v2/sprite-atlas-v2.html")] [NativeHeader("Editor/Src/2D/SpriteAtlas/SpriteAtlasImporter.h")] public sealed partial class SpriteAtlasImporter : AssetImporter { extern internal static void MigrateAllSpriteAtlases(); extern public float variantScale { get; set; } extern public bool includeInBuild { get; set; } extern public SpriteAtlasPackingSettings packingSettings { get; set; } extern public SpriteAtlasTextureSettings textureSettings { get; set; } [NativeName("SetPlatformSettings")] extern private void SetPlatformSettings_Internal(TextureImporterPlatformSettings src); public void SetPlatformSettings(TextureImporterPlatformSettings src) { src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". SetPlatformSettings_Internal(src); } [NativeName("GetPlatformSettings")] extern private TextureImporterPlatformSettings GetPlatformSettings_Internal(string buildTarget); public TextureImporterPlatformSettings GetPlatformSettings(string buildTarget) { buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". return GetPlatformSettings_Internal(buildTarget); } extern internal TextureFormat GetTextureFormat(BuildTarget target); [NativeName("GetSecondaryPlatformSettings")] extern private TextureImporterPlatformSettings GetSecondaryPlatformSettings_Internal(string buildTarget, string secondaryTextureName); internal TextureImporterPlatformSettings GetSecondaryPlatformSettings(string buildTarget, string secondaryTextureName) { buildTarget = TextureImporter.GetTexturePlatformSerializationName(buildTarget); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". return GetSecondaryPlatformSettings_Internal(buildTarget, secondaryTextureName); } [NativeName("SetSecondaryPlatformSettings")] extern private void SetSecondaryPlatformSettings_Internal(TextureImporterPlatformSettings src, string secondaryTextureName); internal void SetSecondaryPlatformSettings(TextureImporterPlatformSettings src, string secondaryTextureName) { src.name = TextureImporter.GetTexturePlatformSerializationName(src.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". SetSecondaryPlatformSettings_Internal(src, secondaryTextureName); } extern internal bool GetSecondaryColorSpace(string secondaryTextureName); extern internal void SetSecondaryColorSpace(string secondaryTextureName, bool srGB); extern internal void DeleteSecondaryPlatformSettings(string secondaryTextureName); } }; ================================================ FILE: Editor/Mono/2D/SpriteAtlas/SpriteAtlasImporterInspector.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.IO; using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.U2D; using UnityEditor.Build; using UnityEditor.U2D.Common; using UnityEditor.U2D.Interface; using UnityEditorInternal; using UnityEditor.AssetImporters; namespace UnityEditor.U2D { [CustomEditor(typeof(SpriteAtlasImporter))] internal class SpriteAtlasImporterInspector : AssetImporterEditor { class SpriteAtlasInspectorPlatformSettingView : TexturePlatformSettingsView { private bool m_ShowMaxSizeOption; public SpriteAtlasInspectorPlatformSettingView(bool showMaxSizeOption) { m_ShowMaxSizeOption = showMaxSizeOption; } public override int DrawMaxSize(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { if (m_ShowMaxSizeOption) return base.DrawMaxSize(defaultValue, isMixedValue, isDisabled, out changed); else changed = false; return defaultValue; } } class Styles { public readonly GUIStyle preDropDown = "preDropDown"; public readonly GUIStyle previewButton = "preButton"; public readonly GUIStyle previewSlider = "preSlider"; public readonly GUIStyle previewSliderThumb = "preSliderThumb"; public readonly GUIStyle previewLabel = "preLabel"; public readonly GUIContent textureSettingLabel = EditorGUIUtility.TrTextContent("Texture"); public readonly GUIContent variantSettingLabel = EditorGUIUtility.TrTextContent("Variant"); public readonly GUIContent packingParametersLabel = EditorGUIUtility.TrTextContent("Packing"); public readonly GUIContent atlasTypeLabel = EditorGUIUtility.TrTextContent("Type"); public readonly GUIContent defaultPlatformLabel = EditorGUIUtility.TrTextContent("Default"); public readonly GUIContent masterAtlasLabel = EditorGUIUtility.TrTextContent("Master Atlas", "Assigning another Sprite Atlas asset will make this atlas a variant of it."); public readonly GUIContent packerLabel = EditorGUIUtility.TrTextContent("Scriptable Packer", "Scriptable Object that implements custom packing for Sprite-Atlas."); public readonly GUIContent bindAsDefaultLabel = EditorGUIUtility.TrTextContent("Include in Build", "Packed textures will be included in the build by default."); public readonly GUIContent enableRotationLabel = EditorGUIUtility.TrTextContent("Allow Rotation", "Try rotating the sprite to fit better during packing."); public readonly GUIContent enableTightPackingLabel = EditorGUIUtility.TrTextContent("Tight Packing", "Use the mesh outline to fit instead of the whole texture rect during packing."); public readonly GUIContent enableAlphaDilationLabel = EditorGUIUtility.TrTextContent("Alpha Dilation", "Enable Alpha Dilation for SpriteAtlas padding pixels."); public readonly GUIContent paddingLabel = EditorGUIUtility.TrTextContent("Padding", "The amount of extra padding between packed sprites."); public readonly GUIContent generateMipMapLabel = EditorGUIUtility.TrTextContent("Generate Mip Maps"); public readonly GUIContent packPreviewLabel = EditorGUIUtility.TrTextContent("Pack Preview", "Save and preview packed Sprite Atlas textures."); public readonly GUIContent sRGBLabel = EditorGUIUtility.TrTextContent("sRGB", "Texture content is stored in gamma space."); public readonly GUIContent readWrite = EditorGUIUtility.TrTextContent("Read/Write", "Enable to be able to access the raw pixel data from code."); public readonly GUIContent variantMultiplierLabel = EditorGUIUtility.TrTextContent("Scale", "Down scale ratio."); public readonly GUIContent copyMasterButton = EditorGUIUtility.TrTextContent("Copy Master's Settings", "Copy all master's settings into this variant."); public readonly GUIContent disabledPackLabel = EditorGUIUtility.TrTextContent("Sprite Atlas packing is disabled. Enable it in Edit > Project Settings > Editor.", null, EditorGUIUtility.GetHelpIcon(MessageType.Info)); public readonly GUIContent packableListLabel = EditorGUIUtility.TrTextContent("Objects for Packing", "Only accepts Folders, Sprite Sheet (Texture) and Sprite."); public readonly GUIContent notPowerOfTwoWarning = EditorGUIUtility.TrTextContent("This scale will produce a Variant Sprite Atlas with a packed Texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/Texture formats."); public readonly GUIContent secondaryTextureNameLabel = EditorGUIUtility.TrTextContent("Secondary Texture Name", "The name of the Secondary Texture to apply the following settings to."); public readonly GUIContent platformSettingsDropDownLabel = EditorGUIUtility.TrTextContent("Show Platform Settings For"); public readonly GUIContent smallZoom = EditorGUIUtility.IconContent("PreTextureMipMapLow"); public readonly GUIContent largeZoom = EditorGUIUtility.IconContent("PreTextureMipMapHigh"); public readonly GUIContent alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha"); public readonly GUIContent RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB"); public readonly GUIContent trashIcon = EditorGUIUtility.TrIconContent("TreeEditor.Trash", "Delete currently selected settings."); public readonly int packableElementHash = "PackableElement".GetHashCode(); public readonly int packableSelectorHash = "PackableSelector".GetHashCode(); public readonly string swapObjectRegisterUndo = L10n.Tr("Swap Packable"); public readonly string secondaryTextureNameTextControlName = "secondary_texture_name_text_field"; public readonly string defaultTextForSecondaryTextureName = L10n.Tr("(Matches the names of the Secondary Textures in your Sprites.)"); public readonly string nameUniquenessWarning = L10n.Tr("Secondary Texture names must be unique within a Sprite or Sprite Atlas."); public readonly int[] atlasTypeValues = { 0, 1 }; public readonly GUIContent[] atlasTypeOptions = { EditorGUIUtility.TrTextContent("Master"), EditorGUIUtility.TrTextContent("Variant"), }; public readonly int[] paddingValues = { 2, 4, 8 }; public readonly GUIContent[] paddingOptions; public Styles() { paddingOptions = new GUIContent[paddingValues.Length]; for (var i = 0; i < paddingValues.Length; ++i) paddingOptions[i] = EditorGUIUtility.TextContent(paddingValues[i].ToString()); } } private static Styles s_Styles; private static Styles styles { get { s_Styles = s_Styles ?? new Styles(); return s_Styles; } } private SpriteAtlasAsset spriteAtlasAsset { get { return m_TargetAsset; } } private SpriteAtlasImporter spriteAtlasImporter { get { return target as SpriteAtlasImporter; } } private enum AtlasType { Undefined = -1, Master = 0, Variant = 1 } private SerializedProperty m_FilterMode; private SerializedProperty m_AnisoLevel; private SerializedProperty m_GenerateMipMaps; private SerializedProperty m_Readable; private SerializedProperty m_UseSRGB; private SerializedProperty m_EnableTightPacking; private SerializedProperty m_EnableAlphaDilation; private SerializedProperty m_EnableRotation; private SerializedProperty m_Padding; private SerializedProperty m_BindAsDefault; private SerializedProperty m_Packables; private SerializedProperty m_MasterAtlas; private SerializedProperty m_VariantScale; private SerializedProperty m_ScriptablePacker; private string m_Hash; private int m_PreviewPage = 0; private int m_TotalPages = 0; private int[] m_OptionValues = null; private string[] m_OptionDisplays = null; private Texture2D[] m_PreviewTextures = null; private Texture2D[] m_PreviewAlphaTextures = null; private bool m_PackableListExpanded = true; private ReorderableList m_PackableList; private SpriteAtlasAsset m_TargetAsset; private string m_AssetPath; private float m_MipLevel = 0; private bool m_ShowAlpha; private bool m_Discard = false; private List m_PlatformSettingsOptions; private int m_SelectedPlatformSettings = 0; private int m_ContentHash = 0; private List m_ValidPlatforms; private Dictionary> m_TempPlatformSettings; private ITexturePlatformSettingsView m_TexturePlatformSettingsView; private ITexturePlatformSettingsView m_SecondaryTexturePlatformSettingsView; private ITexturePlatformSettingsFormatHelper m_TexturePlatformSettingTextureHelper; private ITexturePlatformSettingsController m_TexturePlatformSettingsController; private SerializedObject m_SerializedAssetObject = null; // The first two options are the main texture and a separator while the last two options are another separator and the new settings menu. private bool secondaryTextureSelected { get { return m_SelectedPlatformSettings >= 2 && m_SelectedPlatformSettings <= m_PlatformSettingsOptions.Count - 3; } } static bool IsPackable(Object o) { return o != null && (o.GetType() == typeof(Sprite) || o.GetType() == typeof(Texture2D) || (o.GetType() == typeof(DefaultAsset) && ProjectWindowUtil.IsFolder(o.GetInstanceID()))); } static Object ValidateObjectForPackableFieldAssignment(Object[] references, System.Type objType, SerializedProperty property, EditorGUI.ObjectFieldValidatorOptions options) { // We only validate and care about the first one as this is a object field assignment. if (references.Length > 0 && IsPackable(references[0])) return references[0]; return null; } bool IsTargetVariant() { return spriteAtlasAsset ? spriteAtlasAsset.isVariant : false; } bool IsTargetMaster() { return spriteAtlasAsset ? !spriteAtlasAsset.isVariant : true; } protected override bool needsApplyRevert => false; internal override string targetTitle { get { return spriteAtlasAsset ? ( Path.GetFileNameWithoutExtension(m_AssetPath) + " (Sprite Atlas)" ) : "SpriteAtlasImporter Settings"; } } private string LoadSourceAsset() { var assetPath = AssetDatabase.GetAssetPath(target); var loadedObjects = InternalEditorUtility.LoadSerializedFileAndForget(assetPath); if (loadedObjects.Length > 0) m_TargetAsset = loadedObjects[0] as SpriteAtlasAsset; return assetPath; } private SerializedObject serializedAssetObject { get { return GetSerializedAssetObject(); } } internal static int SpriteAtlasAssetHash(SerializedObject obj) { int hashCode = 0; if (obj == null) return 0; unchecked { hashCode = (int)2166136261 ^ (int) obj.FindProperty("m_MasterAtlas").contentHash; hashCode = hashCode * 16777619 ^ (int) obj.FindProperty("m_ImporterData").contentHash; hashCode = hashCode * 16777619 ^ (int) obj.FindProperty("m_IsVariant").contentHash; hashCode = hashCode * 16777619 ^ (int)obj.FindProperty("m_ScriptablePacker").contentHash; } return hashCode; } internal static int SpriteAtlasImporterHash(SerializedObject obj) { int hashCode = 0; if (obj == null) return 0; unchecked { hashCode = (int)2166136261 ^ (int)obj.FindProperty("m_PackingSettings").contentHash; hashCode = hashCode * 16777619 ^ (int)obj.FindProperty("m_TextureSettings").contentHash; hashCode = hashCode * 16777619 ^ (int)obj.FindProperty("m_PlatformSettings").contentHash; hashCode = hashCode * 16777619 ^ (int)obj.FindProperty("m_SecondaryTextureSettings").contentHash; hashCode = hashCode * 16777619 ^ (int)obj.FindProperty("m_BindAsDefault").contentHash; hashCode = hashCode * 16777619 ^ (int)obj.FindProperty("m_VariantMultiplier").contentHash; } return hashCode; } internal int GetInspectorHash() { return SpriteAtlasAssetHash(m_SerializedAssetObject) * 16777619 ^ SpriteAtlasImporterHash(m_SerializedObject); } private SerializedObject GetSerializedAssetObject() { if (m_SerializedAssetObject == null) { try { m_SerializedAssetObject = new SerializedObject(spriteAtlasAsset, m_Context); m_SerializedAssetObject.inspectorMode = inspectorMode; m_ContentHash = GetInspectorHash(); m_EnabledProperty = m_SerializedAssetObject.FindProperty("m_Enabled"); } catch (System.ArgumentException e) { m_SerializedAssetObject = null; m_EnabledProperty = null; throw new SerializedObjectNotCreatableException(e.Message); } } return m_SerializedAssetObject; } public override void OnEnable() { base.OnEnable(); m_FilterMode = serializedObject.FindProperty("m_TextureSettings.filterMode"); m_AnisoLevel = serializedObject.FindProperty("m_TextureSettings.anisoLevel"); m_GenerateMipMaps = serializedObject.FindProperty("m_TextureSettings.generateMipMaps"); m_Readable = serializedObject.FindProperty("m_TextureSettings.readable"); m_UseSRGB = serializedObject.FindProperty("m_TextureSettings.sRGB"); m_EnableTightPacking = serializedObject.FindProperty("m_PackingSettings.enableTightPacking"); m_EnableRotation = serializedObject.FindProperty("m_PackingSettings.enableRotation"); m_EnableAlphaDilation = serializedObject.FindProperty("m_PackingSettings.enableAlphaDilation"); m_Padding = serializedObject.FindProperty("m_PackingSettings.padding"); m_BindAsDefault = serializedObject.FindProperty("m_BindAsDefault"); m_VariantScale = serializedObject.FindProperty("m_VariantMultiplier"); PopulatePlatformSettingsOptions(); SyncPlatformSettings(); m_TexturePlatformSettingsView = new SpriteAtlasInspectorPlatformSettingView(IsTargetMaster()); m_TexturePlatformSettingTextureHelper = new TexturePlatformSettingsFormatHelper(); m_TexturePlatformSettingsController = new TexturePlatformSettingsViewController(); // Don't show max size option for secondary textures as they must have the same size as the main texture. m_SecondaryTexturePlatformSettingsView = new SpriteAtlasInspectorPlatformSettingView(false); m_AssetPath = LoadSourceAsset(); if (spriteAtlasAsset == null) return; m_MasterAtlas = serializedAssetObject.FindProperty("m_MasterAtlas"); m_ScriptablePacker = serializedAssetObject.FindProperty("m_ScriptablePacker"); m_Packables = serializedAssetObject.FindProperty("m_ImporterData.packables"); m_PackableList = new ReorderableList(serializedAssetObject, m_Packables, true, true, true, true); m_PackableList.onAddCallback = AddPackable; m_PackableList.drawElementCallback = DrawPackableElement; m_PackableList.elementHeight = EditorGUIUtility.singleLineHeight; m_PackableList.headerHeight = 0f; } // Populate the platform settings dropdown list with secondary texture names found through serialized properties of the Sprite Atlas assets. private void PopulatePlatformSettingsOptions() { m_PlatformSettingsOptions = new List { L10n.Tr("Main Texture"), "", "", L10n.Tr("New Secondary Texture settings.") }; SerializedProperty secondaryPlatformSettings = serializedObject.FindProperty("m_SecondaryTextureSettings"); if (secondaryPlatformSettings != null && !secondaryPlatformSettings.hasMultipleDifferentValues) { int numSecondaryTextures = secondaryPlatformSettings.arraySize; List secondaryTextureNames = new List(numSecondaryTextures); for (int i = 0; i < numSecondaryTextures; ++i) secondaryTextureNames.Add(secondaryPlatformSettings.GetArrayElementAtIndex(i).displayName); // Insert after main texture and the separator. m_PlatformSettingsOptions.InsertRange(2, secondaryTextureNames); } m_SelectedPlatformSettings = 0; } void SyncPlatformSettings() { m_TempPlatformSettings = new Dictionary>(); string secondaryTextureName = null; if (secondaryTextureSelected) secondaryTextureName = m_PlatformSettingsOptions[m_SelectedPlatformSettings]; // Default platform var defaultSettings = new List(); m_TempPlatformSettings.Add(TextureImporterInspector.s_DefaultPlatformName, defaultSettings); var settings = secondaryTextureSelected ? spriteAtlasImporter.GetSecondaryPlatformSettings(TextureImporterInspector.s_DefaultPlatformName, secondaryTextureName) : spriteAtlasImporter.GetPlatformSettings(TextureImporterInspector.s_DefaultPlatformName); defaultSettings.Add(settings); m_ValidPlatforms = BuildPlatforms.instance.GetValidPlatforms(); foreach (var platform in m_ValidPlatforms) { var platformSettings = new List(); m_TempPlatformSettings.Add(platform.name, platformSettings); var perPlatformSettings = secondaryTextureSelected ? spriteAtlasImporter.GetSecondaryPlatformSettings(platform.name, secondaryTextureName) : spriteAtlasImporter.GetPlatformSettings(platform.name); // setting will be in default state if copy failed platformSettings.Add(perPlatformSettings); } } void RenameSecondaryPlatformSettings(string oldName, string newName) { spriteAtlasImporter.DeleteSecondaryPlatformSettings(oldName); var defaultPlatformSettings = m_TempPlatformSettings[TextureImporterInspector.s_DefaultPlatformName]; spriteAtlasImporter.SetSecondaryPlatformSettings(defaultPlatformSettings[0], newName); foreach (var buildPlatform in m_ValidPlatforms) { var platformSettings = m_TempPlatformSettings[buildPlatform.name]; spriteAtlasImporter.SetSecondaryPlatformSettings(platformSettings[0], newName); } } void AddPackable(ReorderableList list) { ObjectSelector.get.Show(null, typeof(Object), null, false); ObjectSelector.get.searchFilter = "t:sprite t:texture2d t:folder"; ObjectSelector.get.objectSelectorID = styles.packableSelectorHash; } void DrawPackableElement(Rect rect, int index, bool selected, bool focused) { var property = m_Packables.GetArrayElementAtIndex(index); var controlID = EditorGUIUtility.GetControlID(styles.packableElementHash, FocusType.Passive); var previousObject = property.objectReferenceValue; var changedObject = EditorGUI.DoObjectField(rect, rect, controlID, previousObject, target, typeof(Object), ValidateObjectForPackableFieldAssignment, false); if (changedObject != previousObject) { Undo.RegisterCompleteObjectUndo(spriteAtlasAsset, styles.swapObjectRegisterUndo); property.objectReferenceValue = changedObject; } if (GUIUtility.keyboardControl == controlID && !selected) m_PackableList.index = index; } protected override void Apply() { if (HasModified()) { if (spriteAtlasAsset) { SpriteAtlasAsset.Save(spriteAtlasAsset, m_AssetPath); AssetDatabase.ImportAsset(m_AssetPath); } m_ContentHash = GetInspectorHash(); } base.Apply(); } protected override bool useAssetDrawPreview { get { return false; } } protected void PackPreviewGUI() { EditorGUILayout.Space(); using (new GUILayout.HorizontalScope()) { using (new EditorGUI.DisabledScope(!HasModified() || !IsValidAtlas() || Application.isPlaying)) { GUILayout.FlexibleSpace(); if (GUILayout.Button(styles.packPreviewLabel)) { GUI.FocusControl(null); SpriteAtlasUtility.EnableV2Import(true); SaveChanges(); SpriteAtlasUtility.EnableV2Import(false); } } } } private bool IsValidAtlas() { if (IsTargetVariant()) return m_MasterAtlas.objectReferenceValue != null; else return true; } public override bool HasModified() { return !m_Discard && (base.HasModified() || m_ContentHash != GetInspectorHash()); } private void ValidateMasterAtlas() { if (m_MasterAtlas.objectReferenceValue != null) { var assetPath = AssetDatabase.GetAssetPath(m_MasterAtlas.objectReferenceValue); if (assetPath == m_AssetPath) { UnityEngine.Debug.LogWarning("Cannot set itself as MasterAtlas. Please assign a valid MasterAtlas."); m_MasterAtlas.objectReferenceValue = null; } } if (m_MasterAtlas.objectReferenceValue != null) { SpriteAtlas masterAsset = m_MasterAtlas.objectReferenceValue as SpriteAtlas; if (masterAsset != null && masterAsset.isVariant) { UnityEngine.Debug.LogWarning("Cannot set a VariantAtlas as MasterAtlas. Please assign a valid MasterAtlas."); m_MasterAtlas.objectReferenceValue = null; } } } public override void OnInspectorGUI() { // Ensure changes done through script are reflected immediately in Inspector by Syncing m_TempPlatformSettings with Actual Settings. SyncPlatformSettings(); serializedObject.Update(); if (spriteAtlasAsset) { serializedAssetObject.Update(); HandleCommonSettingUI(); } EditorGUILayout.PropertyField(m_BindAsDefault, styles.bindAsDefaultLabel); GUILayout.Space(EditorGUI.kSpacing); bool isTargetMaster = true; if (spriteAtlasAsset) isTargetMaster = IsTargetMaster(); if (isTargetMaster) HandleMasterSettingUI(); if (!spriteAtlasAsset || IsTargetVariant()) HandleVariantSettingUI(); GUILayout.Space(EditorGUI.kSpacing); HandleTextureSettingUI(); GUILayout.Space(EditorGUI.kSpacing); // Only show the packable object list when: // - This is a master atlas. if (targets.Length == 1 && IsTargetMaster() && spriteAtlasAsset) HandlePackableListUI(); serializedObject.ApplyModifiedProperties(); if (spriteAtlasAsset) { serializedAssetObject.ApplyModifiedProperties(); PackPreviewGUI(); } ApplyRevertGUI(); } private void HandleCommonSettingUI() { var atlasType = AtlasType.Undefined; if (IsTargetMaster()) atlasType = AtlasType.Master; else if (IsTargetVariant()) atlasType = AtlasType.Variant; EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = atlasType == AtlasType.Undefined; atlasType = (AtlasType)EditorGUILayout.IntPopup(styles.atlasTypeLabel, (int)atlasType, styles.atlasTypeOptions, styles.atlasTypeValues); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { bool setToVariant = atlasType == AtlasType.Variant; spriteAtlasAsset.SetIsVariant(setToVariant); // Reinit the platform setting view m_TexturePlatformSettingsView = new SpriteAtlasInspectorPlatformSettingView(IsTargetMaster()); } m_ScriptablePacker.objectReferenceValue = EditorGUILayout.ObjectField(styles.packerLabel, m_ScriptablePacker.objectReferenceValue, typeof(UnityEditor.U2D.ScriptablePacker), false); if (atlasType == AtlasType.Variant) { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_MasterAtlas, styles.masterAtlasLabel); if (EditorGUI.EndChangeCheck()) { ValidateMasterAtlas(); // Apply modified properties here to have latest master atlas reflected in native codes. serializedAssetObject.ApplyModifiedPropertiesWithoutUndo(); PopulatePlatformSettingsOptions(); SyncPlatformSettings(); } } } private void HandleVariantSettingUI() { EditorGUILayout.LabelField(styles.variantSettingLabel, EditorStyles.boldLabel); EditorGUILayout.PropertyField(m_VariantScale, styles.variantMultiplierLabel); // Test if the multiplier scale a power of two size (1024) into another power of 2 size. if (!Mathf.IsPowerOfTwo((int)(m_VariantScale.floatValue * 1024))) EditorGUILayout.HelpBox(styles.notPowerOfTwoWarning.text, MessageType.Warning, true); } private void HandleBoolToIntPropertyField(SerializedProperty prop, GUIContent content) { Rect rect = EditorGUILayout.GetControlRect(); EditorGUI.BeginProperty(rect, content, prop); EditorGUI.BeginChangeCheck(); var boolValue = EditorGUI.Toggle(rect, content, prop.boolValue); if (EditorGUI.EndChangeCheck()) prop.boolValue = boolValue; EditorGUI.EndProperty(); } private void HandleMasterSettingUI() { EditorGUILayout.LabelField(styles.packingParametersLabel, EditorStyles.boldLabel); HandleBoolToIntPropertyField(m_EnableRotation, styles.enableRotationLabel); HandleBoolToIntPropertyField(m_EnableTightPacking, styles.enableTightPackingLabel); HandleBoolToIntPropertyField(m_EnableAlphaDilation, styles.enableAlphaDilationLabel); EditorGUILayout.IntPopup(m_Padding, styles.paddingOptions, styles.paddingValues, styles.paddingLabel); GUILayout.Space(EditorGUI.kSpacing); } private void HandleTextureSettingUI() { EditorGUILayout.LabelField(styles.textureSettingLabel, EditorStyles.boldLabel); HandleBoolToIntPropertyField(m_Readable, styles.readWrite); HandleBoolToIntPropertyField(m_GenerateMipMaps, styles.generateMipMapLabel); HandleBoolToIntPropertyField(m_UseSRGB, styles.sRGBLabel); EditorGUILayout.PropertyField(m_FilterMode); var showAniso = !m_FilterMode.hasMultipleDifferentValues && !m_GenerateMipMaps.hasMultipleDifferentValues && (FilterMode)m_FilterMode.intValue != FilterMode.Point && m_GenerateMipMaps.boolValue; if (showAniso) EditorGUILayout.IntSlider(m_AnisoLevel, 0, 16); GUILayout.Space(EditorGUI.kSpacing); // "Show Platform Settings For" dropdown EditorGUILayout.BeginHorizontal(); { EditorGUILayout.PrefixLabel(s_Styles.platformSettingsDropDownLabel); EditorGUI.BeginChangeCheck(); m_SelectedPlatformSettings = EditorGUILayout.Popup(m_SelectedPlatformSettings, m_PlatformSettingsOptions.ToArray(), GUILayout.MaxWidth(150.0f)); if (EditorGUI.EndChangeCheck()) { // New settings option is selected... if (m_SelectedPlatformSettings == m_PlatformSettingsOptions.Count - 1) { m_PlatformSettingsOptions.Insert(m_SelectedPlatformSettings - 1, s_Styles.defaultTextForSecondaryTextureName); m_SelectedPlatformSettings--; EditorGUI.FocusTextInControl(s_Styles.secondaryTextureNameTextControlName); } SyncPlatformSettings(); } if (secondaryTextureSelected) { // trash can button if (GUILayout.Button(s_Styles.trashIcon, EditorStyles.iconButton, GUILayout.ExpandWidth(false))) { EditorGUI.EndEditingActiveTextField(); spriteAtlasImporter.DeleteSecondaryPlatformSettings(m_PlatformSettingsOptions[m_SelectedPlatformSettings]); m_PlatformSettingsOptions.RemoveAt(m_SelectedPlatformSettings); m_SelectedPlatformSettings--; if (m_SelectedPlatformSettings == 1) m_SelectedPlatformSettings = 0; SyncPlatformSettings(); } } } EditorGUILayout.EndHorizontal(); // Texture platform settings UI. EditorGUILayout.BeginHorizontal(); { EditorGUI.indentLevel++; GUILayout.Space(EditorGUI.indent); EditorGUI.indentLevel--; if (m_SelectedPlatformSettings == 0) { GUILayout.Space(EditorGUI.kSpacing); HandlePlatformSettingUI(null); } else { EditorGUILayout.BeginVertical(); { GUILayout.Space(EditorGUI.kSpacing); string oldSecondaryTextureName = m_PlatformSettingsOptions[m_SelectedPlatformSettings]; GUI.SetNextControlName(s_Styles.secondaryTextureNameTextControlName); EditorGUI.BeginChangeCheck(); string textFieldText = EditorGUILayout.DelayedTextField(s_Styles.secondaryTextureNameLabel, oldSecondaryTextureName); if (EditorGUI.EndChangeCheck() && oldSecondaryTextureName != textFieldText) { if (!m_PlatformSettingsOptions.Exists(x => x == textFieldText)) { m_PlatformSettingsOptions[m_SelectedPlatformSettings] = textFieldText; RenameSecondaryPlatformSettings(oldSecondaryTextureName, textFieldText); } else { Debug.LogWarning(s_Styles.nameUniquenessWarning); EditorGUI.FocusTextInControl(s_Styles.secondaryTextureNameTextControlName); } } string secondaryTextureName = m_PlatformSettingsOptions[m_SelectedPlatformSettings]; EditorGUI.BeginChangeCheck(); bool value = EditorGUILayout.Toggle(s_Styles.sRGBLabel, spriteAtlasImporter.GetSecondaryColorSpace(secondaryTextureName)); if (EditorGUI.EndChangeCheck()) spriteAtlasImporter.SetSecondaryColorSpace(secondaryTextureName, value); HandlePlatformSettingUI(textFieldText); } EditorGUILayout.EndVertical(); } } EditorGUILayout.EndHorizontal(); } private void HandlePlatformSettingUI(string secondaryTextureName) { int shownTextureFormatPage = EditorGUILayout.BeginPlatformGrouping(m_ValidPlatforms.ToArray(), styles.defaultPlatformLabel); var defaultPlatformSettings = m_TempPlatformSettings[TextureImporterInspector.s_DefaultPlatformName]; bool isSecondary = secondaryTextureName != null; ITexturePlatformSettingsView view = isSecondary ? m_SecondaryTexturePlatformSettingsView : m_TexturePlatformSettingsView; if (shownTextureFormatPage == -1) { if (m_TexturePlatformSettingsController.HandleDefaultSettings(defaultPlatformSettings, view, m_TexturePlatformSettingTextureHelper)) { for (var i = 0; i < defaultPlatformSettings.Count; ++i) { if (isSecondary) spriteAtlasImporter.SetSecondaryPlatformSettings(defaultPlatformSettings[i], secondaryTextureName); else spriteAtlasImporter.SetPlatformSettings(defaultPlatformSettings[i]); } } } else { var buildPlatform = m_ValidPlatforms[shownTextureFormatPage]; var platformSettings = m_TempPlatformSettings[buildPlatform.name]; for (var i = 0; i < platformSettings.Count; ++i) { var settings = platformSettings[i]; if (!settings.overridden) { if (defaultPlatformSettings[0].format == TextureImporterFormat.Automatic) { settings.format = (TextureImporterFormat)spriteAtlasImporter.GetTextureFormat(buildPlatform.defaultTarget); } else { settings.format = defaultPlatformSettings[0].format; } settings.maxTextureSize = defaultPlatformSettings[0].maxTextureSize; settings.crunchedCompression = defaultPlatformSettings[0].crunchedCompression; settings.compressionQuality = defaultPlatformSettings[0].compressionQuality; } } m_TexturePlatformSettingsView.buildPlatformTitle = buildPlatform.title.text; if (m_TexturePlatformSettingsController.HandlePlatformSettings(buildPlatform.defaultTarget, platformSettings, view, m_TexturePlatformSettingTextureHelper)) { for (var i = 0; i < platformSettings.Count; ++i) { if (isSecondary) spriteAtlasImporter.SetSecondaryPlatformSettings(platformSettings[i], secondaryTextureName); else spriteAtlasImporter.SetPlatformSettings(platformSettings[i]); } } } EditorGUILayout.EndPlatformGrouping(); } private void HandlePackableListUI() { var currentEvent = Event.current; var usedEvent = false; Rect rect = EditorGUILayout.GetControlRect(); var controlID = EditorGUIUtility.s_LastControlID; switch (currentEvent.type) { case EventType.DragExited: if (GUI.enabled) HandleUtility.Repaint(); break; case EventType.DragUpdated: case EventType.DragPerform: if (rect.Contains(currentEvent.mousePosition) && GUI.enabled) { // Check each single object, so we can add multiple objects in a single drag. var didAcceptDrag = false; var references = DragAndDrop.objectReferences; foreach (var obj in references) { if (IsPackable(obj)) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; if (currentEvent.type == EventType.DragPerform) { m_Packables.AppendFoldoutPPtrValue(obj); didAcceptDrag = true; DragAndDrop.activeControlID = 0; } else DragAndDrop.activeControlID = controlID; } } if (didAcceptDrag) { GUI.changed = true; DragAndDrop.AcceptDrag(); usedEvent = true; } } break; case EventType.ValidateCommand: if (currentEvent.commandName == ObjectSelector.ObjectSelectorClosedCommand && ObjectSelector.get.objectSelectorID == styles.packableSelectorHash) usedEvent = true; break; case EventType.ExecuteCommand: if (currentEvent.commandName == ObjectSelector.ObjectSelectorClosedCommand && ObjectSelector.get.objectSelectorID == styles.packableSelectorHash) { var obj = ObjectSelector.GetCurrentObject(); if (IsPackable(obj)) { m_Packables.AppendFoldoutPPtrValue(obj); m_PackableList.index = m_Packables.arraySize - 1; } usedEvent = true; } break; } // Handle Foldout after we handle the current event because Foldout might process the drag and drop event and used it. m_PackableListExpanded = EditorGUI.Foldout(rect, m_PackableListExpanded, styles.packableListLabel, true); if (usedEvent) currentEvent.Use(); if (m_PackableListExpanded) { EditorGUI.indentLevel++; m_PackableList.DoLayoutList(); EditorGUI.indentLevel--; } } public override void SaveChanges() { if (!m_Discard) base.SaveChanges(); m_ContentHash = GetInspectorHash(); } public override void DiscardChanges() { m_Discard = true; base.DiscardChanges(); m_ContentHash = GetInspectorHash(); } void CachePreviewTexture() { var spriteAtlas = AssetDatabase.LoadAssetAtPath(m_AssetPath); if (spriteAtlas != null) { bool hasPreview = m_PreviewTextures != null && m_PreviewTextures.Length > 0; if (hasPreview) { foreach (var previewTexture in m_PreviewTextures) hasPreview = previewTexture != null; } if (!hasPreview || m_Hash != spriteAtlas.GetHash()) { m_PreviewTextures = spriteAtlas.GetPreviewTextures(); m_PreviewAlphaTextures = spriteAtlas.GetPreviewAlphaTextures(); m_Hash = spriteAtlas.GetHash(); if (m_PreviewTextures != null && m_PreviewTextures.Length > 0 && m_TotalPages != m_PreviewTextures.Length) { m_TotalPages = m_PreviewTextures.Length; m_OptionDisplays = new string[m_TotalPages]; m_OptionValues = new int[m_TotalPages]; for (int i = 0; i < m_TotalPages; ++i) { string texName = m_PreviewTextures[i].name; var pageNum = SpriteAtlasExtensions.GetPageNumberInAtlas(texName); var secondaryName = SpriteAtlasExtensions.GetSecondaryTextureNameInAtlas(texName); m_OptionDisplays[i] = secondaryName == "" ? string.Format("MainTex - Page ({0})", pageNum) : string.Format("{0} - Page ({1})", secondaryName, pageNum); m_OptionValues[i] = i; } } } } } public override string GetInfoString() { if (m_PreviewTextures != null && m_PreviewPage < m_PreviewTextures.Length) { Texture2D t = m_PreviewTextures[m_PreviewPage]; GraphicsFormat format = GraphicsFormatUtility.GetFormat(t); return string.Format("{0}x{1} {2}\n{3}", t.width, t.height, GraphicsFormatUtility.GetFormatString(format), EditorUtility.FormatBytes(TextureUtil.GetStorageMemorySizeLong(t))); } return ""; } public override bool HasPreviewGUI() { CachePreviewTexture(); if (m_PreviewTextures != null && m_PreviewTextures.Length > 0) { Texture2D t = m_PreviewTextures[0]; return t != null; } return false; } public override void OnPreviewSettings() { // Do not allow changing of pages when multiple atlases is selected. if (targets.Length == 1 && m_OptionDisplays != null && m_OptionValues != null && m_TotalPages > 1) m_PreviewPage = EditorGUILayout.IntPopup(m_PreviewPage, m_OptionDisplays, m_OptionValues, styles.preDropDown, GUILayout.MaxWidth(50)); else m_PreviewPage = 0; if (m_PreviewTextures != null) { m_PreviewPage = Mathf.Min(m_PreviewPage, m_PreviewTextures.Length - 1); Texture2D t = m_PreviewTextures[m_PreviewPage]; if (t == null) return; if (GraphicsFormatUtility.HasAlphaChannel(t.format) || (m_PreviewAlphaTextures != null && m_PreviewAlphaTextures.Length > 0)) m_ShowAlpha = GUILayout.Toggle(m_ShowAlpha, m_ShowAlpha ? styles.alphaIcon : styles.RGBIcon, styles.previewButton); int mipCount = Mathf.Max(1, TextureUtil.GetMipmapCount(t)); if (mipCount > 1) { GUILayout.Box(styles.smallZoom, styles.previewLabel); m_MipLevel = Mathf.Round(GUILayout.HorizontalSlider(m_MipLevel, mipCount - 1, 0, styles.previewSlider, styles.previewSliderThumb, GUILayout.MaxWidth(64))); GUILayout.Box(styles.largeZoom, styles.previewLabel); } } } public override void OnPreviewGUI(Rect r, GUIStyle background) { CachePreviewTexture(); if (m_ShowAlpha && m_PreviewAlphaTextures != null && m_PreviewPage < m_PreviewAlphaTextures.Length) { var at = m_PreviewAlphaTextures[m_PreviewPage]; var bias = m_MipLevel - (float)(System.Math.Log(at.width / r.width) / System.Math.Log(2)); EditorGUI.DrawTextureTransparent(r, at, ScaleMode.ScaleToFit, 0, bias); } else if (m_PreviewTextures != null && m_PreviewPage < m_PreviewTextures.Length) { Texture2D t = m_PreviewTextures[m_PreviewPage]; if (t == null) return; float bias = m_MipLevel - (float)(System.Math.Log(t.width / r.width) / System.Math.Log(2)); if (m_ShowAlpha) EditorGUI.DrawTextureAlpha(r, t, ScaleMode.ScaleToFit, 0, bias); else EditorGUI.DrawTextureTransparent(r, t, ScaleMode.ScaleToFit, 0, bias); } } } } ================================================ FILE: Editor/Mono/2D/SpriteAtlas/SpriteAtlasInspector.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.U2D; using UnityEditor.Build; using UnityEditor.U2D.Common; using UnityEditor.U2D.Interface; using UnityEditorInternal; namespace UnityEditor.U2D { [CustomEditor(typeof(SpriteAtlas))] [CanEditMultipleObjects] internal class SpriteAtlasInspector : Editor { class SpriteAtlasInspectorPlatformSettingView : TexturePlatformSettingsView { private bool m_ShowMaxSizeOption; public SpriteAtlasInspectorPlatformSettingView(bool showMaxSizeOption) { m_ShowMaxSizeOption = showMaxSizeOption; } public override int DrawMaxSize(int defaultValue, bool isMixedValue, bool isDisabled, out bool changed) { if (m_ShowMaxSizeOption) return base.DrawMaxSize(defaultValue, isMixedValue, isDisabled, out changed); else changed = false; return defaultValue; } } class Styles { public readonly GUIStyle preDropDown = "preDropDown"; public readonly GUIStyle previewButton = "preButton"; public readonly GUIStyle previewSlider = "preSlider"; public readonly GUIStyle previewSliderThumb = "preSliderThumb"; public readonly GUIStyle previewLabel = "preLabel"; public readonly GUIContent textureSettingLabel = EditorGUIUtility.TrTextContent("Texture"); public readonly GUIContent variantSettingLabel = EditorGUIUtility.TrTextContent("Variant"); public readonly GUIContent packingParametersLabel = EditorGUIUtility.TrTextContent("Packing"); public readonly GUIContent atlasTypeLabel = EditorGUIUtility.TrTextContent("Type"); public readonly GUIContent defaultPlatformLabel = EditorGUIUtility.TrTextContent("Default"); public readonly GUIContent masterAtlasLabel = EditorGUIUtility.TrTextContent("Master Atlas", "Assigning another Sprite Atlas asset will make this atlas a variant of it."); public readonly GUIContent bindAsDefaultLabel = EditorGUIUtility.TrTextContent("Include in Build", "Packed textures will be included in the build by default."); public readonly GUIContent enableRotationLabel = EditorGUIUtility.TrTextContent("Allow Rotation", "Try rotating the sprite to fit better during packing."); public readonly GUIContent enableTightPackingLabel = EditorGUIUtility.TrTextContent("Tight Packing", "Use the mesh outline to fit instead of the whole texture rect during packing."); public readonly GUIContent enableAlphaDilationLabel = EditorGUIUtility.TrTextContent("Alpha Dilation", "Enable Alpha Dilation for SpriteAtlas padding pixels."); public readonly GUIContent paddingLabel = EditorGUIUtility.TrTextContent("Padding", "The amount of extra padding between packed sprites."); public readonly GUIContent generateMipMapLabel = EditorGUIUtility.TrTextContent("Generate Mip Maps"); public readonly GUIContent sRGBLabel = EditorGUIUtility.TrTextContent("sRGB", "Texture content is stored in gamma space."); public readonly GUIContent readWrite = EditorGUIUtility.TrTextContent("Read/Write", "Enable to be able to access the raw pixel data from code."); public readonly GUIContent variantMultiplierLabel = EditorGUIUtility.TrTextContent("Scale", "Down scale ratio."); public readonly GUIContent packButton = EditorGUIUtility.TrTextContent("Pack Preview", "Pack this atlas."); public readonly GUIContent disabledPackLabel = EditorGUIUtility.TrTextContent("Sprite Atlas packing is disabled. Enable it in Edit > Project Settings > Editor.", null, EditorGUIUtility.GetHelpIcon(MessageType.Info)); public readonly GUIContent packableListLabel = EditorGUIUtility.TrTextContent("Objects for Packing", "Only accepts Folder, Sprite Sheet (Texture) and Sprite."); public readonly GUIContent notPowerOfTwoWarning = EditorGUIUtility.TrTextContent("This scale will produce a Variant Sprite Atlas with a packed Texture that is NPOT (non - power of two). This may cause visual artifacts in certain compression/Texture formats."); public readonly GUIContent secondaryTextureNameLabel = EditorGUIUtility.TrTextContent("Secondary Texture Name", "The name of the Secondary Texture to apply the following settings to."); public readonly GUIContent platformSettingsDropDownLabel = EditorGUIUtility.TrTextContent("Show Platform Settings For"); public readonly GUIContent smallZoom = EditorGUIUtility.IconContent("PreTextureMipMapLow"); public readonly GUIContent largeZoom = EditorGUIUtility.IconContent("PreTextureMipMapHigh"); public readonly GUIContent alphaIcon = EditorGUIUtility.IconContent("PreTextureAlpha"); public readonly GUIContent RGBIcon = EditorGUIUtility.IconContent("PreTextureRGB"); public readonly GUIContent trashIcon = EditorGUIUtility.TrIconContent("TreeEditor.Trash", "Delete currently selected settings."); public readonly int packableElementHash = "PackableElement".GetHashCode(); public readonly int packableSelectorHash = "PackableSelector".GetHashCode(); public readonly string swapObjectRegisterUndo = L10n.Tr("Swap Packable"); public readonly string secondaryTextureNameTextControlName = "secondary_texture_name_text_field"; public readonly string defaultTextForSecondaryTextureName = L10n.Tr("(Matches the names of the Secondary Textures in your Sprites.)"); public readonly string nameUniquenessWarning = L10n.Tr("Secondary Texture names must be unique within a Sprite or Sprite Atlas."); public readonly int[] atlasTypeValues = { 0, 1 }; public readonly GUIContent[] atlasTypeOptions = { EditorGUIUtility.TrTextContent("Master"), EditorGUIUtility.TrTextContent("Variant"), }; public readonly int[] paddingValues = { 2, 4, 8 }; public readonly GUIContent[] paddingOptions; public Styles() { paddingOptions = new GUIContent[paddingValues.Length]; for (var i = 0; i < paddingValues.Length; ++i) paddingOptions[i] = EditorGUIUtility.TextContent(paddingValues[i].ToString()); } } private static Styles s_Styles; private static Styles styles { get { s_Styles = s_Styles ?? new Styles(); return s_Styles; } } private enum AtlasType { Undefined = -1, Master = 0, Variant = 1 } private SerializedProperty m_FilterMode; private SerializedProperty m_AnisoLevel; private SerializedProperty m_GenerateMipMaps; private SerializedProperty m_Readable; private SerializedProperty m_UseSRGB; private SerializedProperty m_EnableTightPacking; private SerializedProperty m_EnableAlphaDilation; private SerializedProperty m_EnableRotation; private SerializedProperty m_Padding; private SerializedProperty m_BindAsDefault; private SerializedProperty m_Packables; private SerializedProperty m_MasterAtlas; private SerializedProperty m_VariantScale; private string m_Hash; private int m_PreviewPage = 0; private int m_TotalPages = 0; private int[] m_OptionValues = null; private string[] m_OptionDisplays = null; private Texture2D[] m_PreviewTextures = null; private Texture2D[] m_PreviewAlphaTextures = null; private bool m_PackableListExpanded = true; private ReorderableList m_PackableList; private float m_MipLevel = 0; private bool m_ShowAlpha; private List m_PlatformSettingsOptions; private int m_SelectedPlatformSettings = 0; private List m_ValidPlatforms; private Dictionary> m_TempPlatformSettings; private ITexturePlatformSettingsView m_TexturePlatformSettingsView; private ITexturePlatformSettingsView m_SecondaryTexturePlatformSettingsView; private ITexturePlatformSettingsFormatHelper m_TexturePlatformSettingTextureHelper; private ITexturePlatformSettingsController m_TexturePlatformSettingsController; private SpriteAtlas spriteAtlas { get { return target as SpriteAtlas; } } // The first two options are the main texture and a separator while the last two options are another separator and the new settings menu. private bool secondaryTextureSelected { get { return m_SelectedPlatformSettings >= 2 && m_SelectedPlatformSettings <= m_PlatformSettingsOptions.Count - 3; } } static bool IsPackable(Object o) { return o != null && (o.GetType() == typeof(Sprite) || o.GetType() == typeof(Texture2D) || (o.GetType() == typeof(DefaultAsset) && ProjectWindowUtil.IsFolder(o.GetInstanceID()))); } static Object ValidateObjectForPackableFieldAssignment(Object[] references, System.Type objType, SerializedProperty property, EditorGUI.ObjectFieldValidatorOptions options) { // We only validate and care about the first one as this is a object field assignment. if (references.Length > 0 && IsPackable(references[0])) return references[0]; return null; } bool AllTargetsAreVariant() { foreach (SpriteAtlas sa in targets) { if (!sa.isVariant) return false; } return true; } bool AllTargetsAreMaster() { foreach (SpriteAtlas sa in targets) { if (sa.isVariant) return false; } return true; } void OnEnable() { if (targets == null) return; var validCount = 0; foreach (var so in targets) { if (so != null) validCount++; } if (validCount == 0) return; m_FilterMode = serializedObject.FindProperty("m_EditorData.textureSettings.filterMode"); m_AnisoLevel = serializedObject.FindProperty("m_EditorData.textureSettings.anisoLevel"); m_GenerateMipMaps = serializedObject.FindProperty("m_EditorData.textureSettings.generateMipMaps"); m_Readable = serializedObject.FindProperty("m_EditorData.textureSettings.readable"); m_UseSRGB = serializedObject.FindProperty("m_EditorData.textureSettings.sRGB"); m_EnableTightPacking = serializedObject.FindProperty("m_EditorData.packingSettings.enableTightPacking"); m_EnableAlphaDilation = serializedObject.FindProperty("m_EditorData.packingSettings.enableAlphaDilation"); m_EnableRotation = serializedObject.FindProperty("m_EditorData.packingSettings.enableRotation"); m_Padding = serializedObject.FindProperty("m_EditorData.packingSettings.padding"); m_MasterAtlas = serializedObject.FindProperty("m_MasterAtlas"); m_BindAsDefault = serializedObject.FindProperty("m_EditorData.bindAsDefault"); m_VariantScale = serializedObject.FindProperty("m_EditorData.variantMultiplier"); PopulatePlatformSettingsOptions(); m_Packables = serializedObject.FindProperty("m_EditorData.packables"); m_PackableList = new ReorderableList(serializedObject, m_Packables, true, true, true, true); m_PackableList.drawHeaderCallback = DrawPackablesHeader; m_PackableList.onAddCallback = AddPackable; m_PackableList.onRemoveCallback = RemovePackable; m_PackableList.drawElementCallback = DrawPackableElement; m_PackableList.elementHeight = EditorGUIUtility.singleLineHeight; m_PackableList.headerHeight = 3f + EditorGUIUtility.singleLineHeight; SyncPlatformSettings(); m_TexturePlatformSettingsView = new SpriteAtlasInspectorPlatformSettingView(AllTargetsAreMaster()); m_TexturePlatformSettingTextureHelper = new TexturePlatformSettingsFormatHelper(); m_TexturePlatformSettingsController = new TexturePlatformSettingsViewController(); // Don't show max size option for secondary textures as they must have the same size as the main texture. m_SecondaryTexturePlatformSettingsView = new SpriteAtlasInspectorPlatformSettingView(false); } // Populate the platform settings dropdown list with secondary texture names found through serialized properties of the Sprite Atlas assets. private void PopulatePlatformSettingsOptions() { m_PlatformSettingsOptions = new List { L10n.Tr("Main Texture"), "", "", L10n.Tr("New Secondary Texture settings.") }; SerializedProperty secondaryPlatformSettings = serializedObject.FindProperty("m_EditorData.secondaryTextureSettings"); if (secondaryPlatformSettings != null && !secondaryPlatformSettings.hasMultipleDifferentValues) { int numSecondaryTextures = secondaryPlatformSettings.arraySize; List secondaryTextureNames = new List(numSecondaryTextures); for (int i = 0; i < numSecondaryTextures; ++i) secondaryTextureNames.Add(secondaryPlatformSettings.GetArrayElementAtIndex(i).displayName); // Insert after main texture and the separator. m_PlatformSettingsOptions.InsertRange(2, secondaryTextureNames); } m_SelectedPlatformSettings = 0; } void SyncPlatformSettings() { m_TempPlatformSettings = new Dictionary>(); string secondaryTextureName = null; if (secondaryTextureSelected) secondaryTextureName = m_PlatformSettingsOptions[m_SelectedPlatformSettings]; // Default platform var defaultSettings = new List(); m_TempPlatformSettings.Add(TextureImporterInspector.s_DefaultPlatformName, defaultSettings); foreach (SpriteAtlas sa in targets) { var settings = secondaryTextureSelected ? sa.GetSecondaryPlatformSettings(TextureImporterInspector.s_DefaultPlatformName, secondaryTextureName) : sa.GetPlatformSettings(TextureImporterInspector.s_DefaultPlatformName); defaultSettings.Add(settings); } m_ValidPlatforms = BuildPlatforms.instance.GetValidPlatforms(); foreach (var platform in m_ValidPlatforms) { var platformSettings = new List(); m_TempPlatformSettings.Add(platform.name, platformSettings); foreach (SpriteAtlas sa in targets) { var settings = secondaryTextureSelected ? sa.GetSecondaryPlatformSettings(platform.name, secondaryTextureName) : sa.GetPlatformSettings(platform.name); // setting will be in default state if copy failed platformSettings.Add(settings); } } } void RenameSecondaryPlatformSettings(string oldName, string newName) { for (var i = 0; i < targets.Length; ++i) { SpriteAtlas sa = (SpriteAtlas)targets[i]; sa.DeleteSecondaryPlatformSettings(oldName); var defaultPlatformSettings = m_TempPlatformSettings[TextureImporterInspector.s_DefaultPlatformName]; sa.SetSecondaryPlatformSettings(defaultPlatformSettings[i], newName); foreach (var buildPlatform in m_ValidPlatforms) { var platformSettings = m_TempPlatformSettings[buildPlatform.name]; sa.SetSecondaryPlatformSettings(platformSettings[i], newName); } } } void DrawPackablesHeader(Rect headerRect) { EditorGUI.LabelField(headerRect, EditorGUIUtility.TempContent("Packables")); } void AddPackable(ReorderableList list) { ObjectSelector.get.Show(null, typeof(Object), null, false); ObjectSelector.get.searchFilter = "t:sprite t:texture2d t:folder"; ObjectSelector.get.objectSelectorID = styles.packableSelectorHash; } void RemovePackable(ReorderableList list) { var index = list.index; if (index != -1) spriteAtlas.RemoveAt(index); } void DrawPackableElement(Rect rect, int index, bool selected, bool focused) { var property = m_Packables.GetArrayElementAtIndex(index); var controlID = EditorGUIUtility.GetControlID(styles.packableElementHash, FocusType.Passive); var previousObject = property.objectReferenceValue; var changedObject = EditorGUI.DoObjectField(rect, rect, controlID, previousObject, target, typeof(Object), ValidateObjectForPackableFieldAssignment, false); if (changedObject != previousObject) { // Always call Remove() on the previous object if we swapping the object field item. // This ensure the Sprites was pack in this atlas will be refreshed of it unbound. Undo.RegisterCompleteObjectUndo(spriteAtlas, styles.swapObjectRegisterUndo); if (previousObject != null) spriteAtlas.Remove(new Object[] { previousObject }); property.objectReferenceValue = changedObject; } if (GUIUtility.keyboardControl == controlID && !selected) m_PackableList.index = index; } public override void OnInspectorGUI() { // Ensure changes done through script are reflected immediately in Inspector by Syncing m_TempPlatformSettings with Actual Settings. SyncPlatformSettings(); serializedObject.Update(); HandleCommonSettingUI(); GUILayout.Space(EditorGUI.kSpacing); if (AllTargetsAreVariant()) HandleVariantSettingUI(); else if (AllTargetsAreMaster()) HandleMasterSettingUI(); GUILayout.Space(EditorGUI.kSpacing); HandleTextureSettingUI(); GUILayout.Space(EditorGUI.kSpacing); // Only show the packable object list when: // - This is a master atlas. // - It is not currently selecting multiple atlases. if (targets.Length == 1 && AllTargetsAreMaster()) HandlePackableListUI(); bool spriteAtlasPackingEnabled = (EditorSettings.spritePackerMode == SpritePackerMode.BuildTimeOnlyAtlas || EditorSettings.spritePackerMode == SpritePackerMode.AlwaysOnAtlas || EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2); if (spriteAtlasPackingEnabled && !Application.isPlaying) { using (new EditorGUI.DisabledScope(!Editor.IsPersistent(spriteAtlas))) { if (GUILayout.Button(styles.packButton, GUILayout.ExpandWidth(false))) { SpriteAtlas[] spriteAtlases = new SpriteAtlas[targets.Length]; for (int i = 0; i < spriteAtlases.Length; ++i) spriteAtlases[i] = (SpriteAtlas)targets[i]; SpriteAtlasUtility.PackAtlases(spriteAtlases, EditorUserBuildSettings.activeBuildTarget); // Packing an atlas might change platform settings in the process, reinitialize SyncPlatformSettings(); GUIUtility.ExitGUI(); } } } else { if (GUILayout.Button(styles.disabledPackLabel, EditorStyles.helpBox)) { SettingsService.OpenProjectSettings("Project/Editor"); } } serializedObject.ApplyModifiedProperties(); } private void HandleCommonSettingUI() { var atlasType = AtlasType.Undefined; if (AllTargetsAreMaster()) atlasType = AtlasType.Master; else if (AllTargetsAreVariant()) atlasType = AtlasType.Variant; EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = atlasType == AtlasType.Undefined; using (new EditorGUI.DisabledScope(!Editor.IsPersistent(spriteAtlas))) { atlasType = (AtlasType)EditorGUILayout.IntPopup(styles.atlasTypeLabel, (int)atlasType, styles.atlasTypeOptions, styles.atlasTypeValues); } EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { bool setToVariant = atlasType == AtlasType.Variant; foreach (SpriteAtlas sa in targets) sa.SetIsVariant(setToVariant); // Reinit the platform setting view m_TexturePlatformSettingsView = new SpriteAtlasInspectorPlatformSettingView(AllTargetsAreMaster()); } if (atlasType == AtlasType.Variant) { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_MasterAtlas, styles.masterAtlasLabel); if (EditorGUI.EndChangeCheck()) { // Apply modified properties here to have latest master atlas reflected in native codes. serializedObject.ApplyModifiedProperties(); foreach (SpriteAtlas sa in targets) sa.CopyMasterAtlasSettings(); PopulatePlatformSettingsOptions(); SyncPlatformSettings(); } } EditorGUILayout.PropertyField(m_BindAsDefault, styles.bindAsDefaultLabel); } private void HandleVariantSettingUI() { EditorGUILayout.LabelField(styles.variantSettingLabel, EditorStyles.boldLabel); EditorGUILayout.PropertyField(m_VariantScale, styles.variantMultiplierLabel); // Test if the multiplier scale a power of two size (1024) into another power of 2 size. if (!Mathf.IsPowerOfTwo((int)(m_VariantScale.floatValue * 1024))) EditorGUILayout.HelpBox(styles.notPowerOfTwoWarning.text, MessageType.Warning, true); } private void HandleBoolToIntPropertyField(SerializedProperty prop, GUIContent content) { Rect rect = EditorGUILayout.GetControlRect(); EditorGUI.BeginProperty(rect, content, prop); EditorGUI.BeginChangeCheck(); var boolValue = EditorGUI.Toggle(rect, content, prop.boolValue); if (EditorGUI.EndChangeCheck()) prop.boolValue = boolValue; EditorGUI.EndProperty(); } private void HandleMasterSettingUI() { EditorGUILayout.LabelField(styles.packingParametersLabel, EditorStyles.boldLabel); HandleBoolToIntPropertyField(m_EnableRotation, styles.enableRotationLabel); HandleBoolToIntPropertyField(m_EnableTightPacking, styles.enableTightPackingLabel); HandleBoolToIntPropertyField(m_EnableAlphaDilation, styles.enableAlphaDilationLabel); EditorGUILayout.IntPopup(m_Padding, styles.paddingOptions, styles.paddingValues, styles.paddingLabel); GUILayout.Space(EditorGUI.kSpacing); } private void HandleTextureSettingUI() { EditorGUILayout.LabelField(styles.textureSettingLabel, EditorStyles.boldLabel); HandleBoolToIntPropertyField(m_Readable, styles.readWrite); HandleBoolToIntPropertyField(m_GenerateMipMaps, styles.generateMipMapLabel); HandleBoolToIntPropertyField(m_UseSRGB, styles.sRGBLabel); EditorGUILayout.PropertyField(m_FilterMode); var showAniso = !m_FilterMode.hasMultipleDifferentValues && !m_GenerateMipMaps.hasMultipleDifferentValues && (FilterMode)m_FilterMode.intValue != FilterMode.Point && m_GenerateMipMaps.boolValue; if (showAniso) EditorGUILayout.IntSlider(m_AnisoLevel, 0, 16); GUILayout.Space(EditorGUI.kSpacing); // "Show Platform Settings For" dropdown EditorGUILayout.BeginHorizontal(); { EditorGUILayout.PrefixLabel(s_Styles.platformSettingsDropDownLabel); EditorGUI.BeginChangeCheck(); m_SelectedPlatformSettings = EditorGUILayout.Popup(m_SelectedPlatformSettings, m_PlatformSettingsOptions.ToArray(), GUILayout.MaxWidth(150.0f)); if (EditorGUI.EndChangeCheck()) { // New settings option is selected... if (m_SelectedPlatformSettings == m_PlatformSettingsOptions.Count - 1) { m_PlatformSettingsOptions.Insert(m_SelectedPlatformSettings - 1, s_Styles.defaultTextForSecondaryTextureName); m_SelectedPlatformSettings--; EditorGUI.FocusTextInControl(s_Styles.secondaryTextureNameTextControlName); } SyncPlatformSettings(); } if (secondaryTextureSelected) { // trash can button if (GUILayout.Button(s_Styles.trashIcon, EditorStyles.iconButton, GUILayout.ExpandWidth(false))) { EditorGUI.EndEditingActiveTextField(); foreach (SpriteAtlas sa in targets) sa.DeleteSecondaryPlatformSettings(m_PlatformSettingsOptions[m_SelectedPlatformSettings]); m_PlatformSettingsOptions.RemoveAt(m_SelectedPlatformSettings); m_SelectedPlatformSettings--; if (m_SelectedPlatformSettings == 1) m_SelectedPlatformSettings = 0; SyncPlatformSettings(); } } } EditorGUILayout.EndHorizontal(); // Texture platform settings UI. EditorGUILayout.BeginHorizontal(); { EditorGUI.indentLevel++; GUILayout.Space(EditorGUI.indent); EditorGUI.indentLevel--; if (m_SelectedPlatformSettings == 0) { GUILayout.Space(EditorGUI.kSpacing); HandlePlatformSettingUI(null); } else { EditorGUILayout.BeginVertical(); { GUILayout.Space(EditorGUI.kSpacing); string oldSecondaryTextureName = m_PlatformSettingsOptions[m_SelectedPlatformSettings]; GUI.SetNextControlName(s_Styles.secondaryTextureNameTextControlName); EditorGUI.BeginChangeCheck(); string textFieldText = EditorGUILayout.DelayedTextField(s_Styles.secondaryTextureNameLabel, oldSecondaryTextureName); if (EditorGUI.EndChangeCheck() && oldSecondaryTextureName != textFieldText) { if (!m_PlatformSettingsOptions.Exists(x => x == textFieldText)) { m_PlatformSettingsOptions[m_SelectedPlatformSettings] = textFieldText; RenameSecondaryPlatformSettings(oldSecondaryTextureName, textFieldText); } else { Debug.LogWarning(s_Styles.nameUniquenessWarning); EditorGUI.FocusTextInControl(s_Styles.secondaryTextureNameTextControlName); } } string secondaryTextureName = m_PlatformSettingsOptions[m_SelectedPlatformSettings]; SpriteAtlas sa = (SpriteAtlas)target; EditorGUI.BeginChangeCheck(); bool value = EditorGUILayout.Toggle(s_Styles.sRGBLabel, sa.GetSecondaryColorSpace(secondaryTextureName)); if (EditorGUI.EndChangeCheck()) sa.SetSecondaryColorSpace(secondaryTextureName, value); HandlePlatformSettingUI(textFieldText); } EditorGUILayout.EndVertical(); } } EditorGUILayout.EndHorizontal(); } private void HandlePlatformSettingUI(string secondaryTextureName) { bool isSecondary = secondaryTextureName != null; ITexturePlatformSettingsView view = isSecondary ? m_SecondaryTexturePlatformSettingsView : m_TexturePlatformSettingsView; int shownTextureFormatPage = EditorGUILayout.BeginPlatformGrouping(m_ValidPlatforms.ToArray(), s_Styles.defaultPlatformLabel); var defaultPlatformSettings = m_TempPlatformSettings[TextureImporterInspector.s_DefaultPlatformName]; if (shownTextureFormatPage == -1) { if (m_TexturePlatformSettingsController.HandleDefaultSettings(defaultPlatformSettings, view, m_TexturePlatformSettingTextureHelper)) { for (var i = 0; i < defaultPlatformSettings.Count; ++i) { SpriteAtlas sa = (SpriteAtlas)targets[i]; if (isSecondary) sa.SetSecondaryPlatformSettings(defaultPlatformSettings[i], secondaryTextureName); else sa.SetPlatformSettings(defaultPlatformSettings[i]); } } } else { var buildPlatform = m_ValidPlatforms[shownTextureFormatPage]; var platformSettings = m_TempPlatformSettings[buildPlatform.name]; for (var i = 0; i < platformSettings.Count; ++i) { var settings = platformSettings[i]; if (!settings.overridden) { if (defaultPlatformSettings[0].format == TextureImporterFormat.Automatic) { SpriteAtlas sa = (SpriteAtlas)targets[i]; settings.format = (TextureImporterFormat)sa.GetTextureFormat(buildPlatform.defaultTarget); } else { settings.format = defaultPlatformSettings[0].format; } settings.maxTextureSize = defaultPlatformSettings[0].maxTextureSize; settings.crunchedCompression = defaultPlatformSettings[0].crunchedCompression; settings.compressionQuality = defaultPlatformSettings[0].compressionQuality; } } view.buildPlatformTitle = buildPlatform.title.text; if (m_TexturePlatformSettingsController.HandlePlatformSettings(buildPlatform.defaultTarget, platformSettings, view, m_TexturePlatformSettingTextureHelper)) { for (var i = 0; i < platformSettings.Count; ++i) { SpriteAtlas sa = (SpriteAtlas)targets[i]; if (isSecondary) sa.SetSecondaryPlatformSettings(platformSettings[i], secondaryTextureName); else sa.SetPlatformSettings(platformSettings[i]); } } } EditorGUILayout.EndPlatformGrouping(); } private void HandlePackableListUI() { var currentEvent = Event.current; var usedEvent = false; Rect rect = EditorGUILayout.GetControlRect(); var controlID = EditorGUIUtility.s_LastControlID; switch (currentEvent.type) { case EventType.DragExited: if (GUI.enabled) HandleUtility.Repaint(); break; case EventType.DragUpdated: case EventType.DragPerform: if (rect.Contains(currentEvent.mousePosition) && GUI.enabled) { // Check each single object, so we can add multiple objects in a single drag. var didAcceptDrag = false; var references = DragAndDrop.objectReferences; foreach (var obj in references) { if (IsPackable(obj)) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; if (currentEvent.type == EventType.DragPerform) { m_Packables.AppendFoldoutPPtrValue(obj); didAcceptDrag = true; DragAndDrop.activeControlID = 0; } else DragAndDrop.activeControlID = controlID; } } if (didAcceptDrag) { GUI.changed = true; DragAndDrop.AcceptDrag(); usedEvent = true; } } break; case EventType.ValidateCommand: if (currentEvent.commandName == ObjectSelector.ObjectSelectorClosedCommand && ObjectSelector.get.objectSelectorID == styles.packableSelectorHash) usedEvent = true; break; case EventType.ExecuteCommand: if (currentEvent.commandName == ObjectSelector.ObjectSelectorClosedCommand && ObjectSelector.get.objectSelectorID == styles.packableSelectorHash) { var obj = ObjectSelector.GetCurrentObject(); if (IsPackable(obj)) { m_Packables.AppendFoldoutPPtrValue(obj); m_PackableList.index = m_Packables.arraySize - 1; } usedEvent = true; } break; } // Handle Foldout after we handle the current event because Foldout might process the drag and drop event and used it. m_PackableListExpanded = EditorGUI.Foldout(rect, m_PackableListExpanded, styles.packableListLabel, true); if (usedEvent) currentEvent.Use(); if (m_PackableListExpanded) { EditorGUI.indentLevel++; m_PackableList.DoLayoutList(); EditorGUI.indentLevel--; } } void CachePreviewTexture() { if (m_PreviewTextures == null || m_Hash != spriteAtlas.GetHash()) { m_PreviewTextures = spriteAtlas.GetPreviewTextures(); m_PreviewAlphaTextures = spriteAtlas.GetPreviewAlphaTextures(); m_Hash = spriteAtlas.GetHash(); if (m_PreviewTextures != null && m_PreviewTextures.Length > 0 && m_TotalPages != m_PreviewTextures.Length) { m_TotalPages = m_PreviewTextures.Length; m_OptionDisplays = new string[m_TotalPages]; m_OptionValues = new int[m_TotalPages]; for (int i = 0; i < m_TotalPages; ++i) { string texName = m_PreviewTextures[i].name; var pageNum = SpriteAtlasExtensions.GetPageNumberInAtlas(texName); var secondaryName = SpriteAtlasExtensions.GetSecondaryTextureNameInAtlas(texName); m_OptionDisplays[i] = secondaryName == "" ? string.Format("MainTex - Page ({0})", pageNum) : string.Format("{0} - Page ({1})", secondaryName, pageNum); m_OptionValues[i] = i; } } } } public override string GetInfoString() { if (m_PreviewTextures != null && m_PreviewPage < m_PreviewTextures.Length) { Texture2D t = m_PreviewTextures[m_PreviewPage]; GraphicsFormat format = GraphicsFormatUtility.GetFormat(t); return string.Format("{0}x{1} {2}\n{3}", t.width, t.height, GraphicsFormatUtility.GetFormatString(format), EditorUtility.FormatBytes(TextureUtil.GetStorageMemorySizeLong(t))); } return ""; } public override bool HasPreviewGUI() { CachePreviewTexture(); return (m_PreviewTextures != null && m_PreviewTextures.Length > 0); } public override void OnPreviewSettings() { // Do not allow changing of pages when multiple atlases is selected. if (targets.Length == 1 && m_OptionDisplays != null && m_OptionValues != null && m_TotalPages > 1) m_PreviewPage = EditorGUILayout.IntPopup(m_PreviewPage, m_OptionDisplays, m_OptionValues, styles.preDropDown, GUILayout.MaxWidth(50)); else m_PreviewPage = 0; if (m_PreviewTextures != null) { m_PreviewPage = Mathf.Min(m_PreviewPage, m_PreviewTextures.Length - 1); Texture2D t = m_PreviewTextures[m_PreviewPage]; if (GraphicsFormatUtility.HasAlphaChannel(t.format) || (m_PreviewAlphaTextures != null && m_PreviewAlphaTextures.Length > 0)) m_ShowAlpha = GUILayout.Toggle(m_ShowAlpha, m_ShowAlpha ? styles.alphaIcon : styles.RGBIcon, styles.previewButton); int mipCount = Mathf.Max(1, TextureUtil.GetMipmapCount(t)); if (mipCount > 1) { GUILayout.Box(styles.smallZoom, styles.previewLabel); m_MipLevel = Mathf.Round(GUILayout.HorizontalSlider(m_MipLevel, mipCount - 1, 0, styles.previewSlider, styles.previewSliderThumb, GUILayout.MaxWidth(64))); GUILayout.Box(styles.largeZoom, styles.previewLabel); } } } public override void OnPreviewGUI(Rect r, GUIStyle background) { CachePreviewTexture(); if (m_ShowAlpha && m_PreviewAlphaTextures != null && m_PreviewPage < m_PreviewAlphaTextures.Length) { var at = m_PreviewAlphaTextures[m_PreviewPage]; var bias = m_MipLevel - (float)(System.Math.Log(at.width / r.width) / System.Math.Log(2)); EditorGUI.DrawTextureTransparent(r, at, ScaleMode.ScaleToFit, 0, bias); } else if (m_PreviewTextures != null && m_PreviewPage < m_PreviewTextures.Length) { Texture2D t = m_PreviewTextures[m_PreviewPage]; float bias = m_MipLevel - (float)(System.Math.Log(t.width / r.width) / System.Math.Log(2)); if (m_ShowAlpha) EditorGUI.DrawTextureAlpha(r, t, ScaleMode.ScaleToFit, 0, bias); else EditorGUI.DrawTextureTransparent(r, t, ScaleMode.ScaleToFit, 0, bias); } } } } ================================================ FILE: Editor/Mono/Accessibility/UserAccessibilitySettings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; namespace UnityEditor.Accessibility { internal enum ColorBlindCondition { Default, Deuteranopia, Protanopia, Tritanopia, } // NOTE: The preferences in this class are currently only exposed via a context menu in the ProfilerWindow // these toggles need to instead be moved to e.g., the Preferences menu before they are used elsewhere internal static class UserAccessiblitySettings { static UserAccessiblitySettings() { s_ColorBlindCondition = (ColorBlindCondition)EditorPrefs.GetInt(k_ColorBlindConditionPrefKey, (int)ColorBlindCondition.Default); } private const string k_ColorBlindConditionPrefKey = "AccessibilityColorBlindCondition"; public static ColorBlindCondition colorBlindCondition { get { return s_ColorBlindCondition; } set { if (s_ColorBlindCondition != value) { s_ColorBlindCondition = value; EditorPrefs.SetInt(k_ColorBlindConditionPrefKey, (int)value); if (colorBlindConditionChanged != null) colorBlindConditionChanged(); } } } private static ColorBlindCondition s_ColorBlindCondition; public static Action colorBlindConditionChanged; } } ================================================ FILE: Editor/Mono/Animation/AnimationClipSettings.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.Playables; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Internal; namespace UnityEditor { [NativeType(CodegenOptions.Custom, "MonoAnimationClipSettings")] [NativeAsStruct] [StructLayout(LayoutKind.Sequential)] [RequiredByNativeCode] public class AnimationClipSettings { public AnimationClip additiveReferencePoseClip; public float additiveReferencePoseTime; public float startTime; public float stopTime; public float orientationOffsetY; public float level; public float cycleOffset; public bool hasAdditiveReferencePose; public bool loopTime; public bool loopBlend; public bool loopBlendOrientation; public bool loopBlendPositionY; public bool loopBlendPositionXZ; public bool keepOriginalOrientation; public bool keepOriginalPositionY; public bool keepOriginalPositionXZ; public bool heightFromFeet; public bool mirror; } } ================================================ FILE: Editor/Mono/Animation/AnimationClipStats.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.Playables; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Internal; namespace UnityEditor { // Must be kept in sync with AnimationClipStats in AnimationClipStats internal struct AnimationClipStats { public int size; public int clips; public int positionCurves; public int quaternionCurves; public int eulerCurves; public int scaleCurves; public int muscleCurves; public int genericCurves; public int pptrCurves; public int totalCurves; public int constantCurves; public int denseCurves; public int streamCurves; public void Reset() { size = 0; clips = 0; positionCurves = 0; quaternionCurves = 0; eulerCurves = 0; scaleCurves = 0; muscleCurves = 0; genericCurves = 0; pptrCurves = 0; totalCurves = 0; constantCurves = 0; denseCurves = 0; streamCurves = 0; } public void Combine(AnimationClipStats other) { size += other.size; clips += other.clips; positionCurves += other.positionCurves; quaternionCurves += other.quaternionCurves; eulerCurves += other.eulerCurves; scaleCurves += other.scaleCurves; muscleCurves += other.muscleCurves; genericCurves += other.genericCurves; pptrCurves += other.pptrCurves; totalCurves += other.totalCurves; constantCurves += other.constantCurves; denseCurves += other.denseCurves; streamCurves += other.streamCurves; } } } ================================================ FILE: Editor/Mono/Animation/AnimationMode.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Playables; using UnityEngine; using Object = UnityEngine.Object; namespace UnityEditor { public class AnimationModeDriver : ScriptableObject { internal delegate bool IsKeyCallback(Object target, string propertyPath); internal IsKeyCallback isKeyCallback; [UsedByNativeCode] internal bool InvokeIsKeyCallback_Internal(Object target, string propertyPath) { if (isKeyCallback == null) return false; return isKeyCallback(target, propertyPath); } } [NativeHeader("Editor/Src/Animation/AnimationMode.bindings.h")] [NativeHeader("Editor/Src/Animation/EditorCurveBinding.bindings.h")] [NativeHeader("Editor/Src/Prefabs/PropertyModification.h")] public class AnimationMode { static private bool s_InAnimationPlaybackMode = false; static private bool s_InAnimationRecordMode = false; static internal event Action onAnimationRecordingStart; static internal event Action onAnimationRecordingStop; static private PrefColor s_AnimatedPropertyColor = new PrefColor("Animation/Property Animated", 0.82f, 0.97f, 1.00f, 1.00f, 0.54f, 0.85f, 1.00f, 1.00f); static private PrefColor s_RecordedPropertyColor = new PrefColor("Animation/Property Recorded", 1.00f, 0.60f, 0.60f, 1.00f, 1.00f, 0.50f, 0.50f, 1.00f); static private PrefColor s_CandidatePropertyColor = new PrefColor("Animation/Property Candidate", 1.00f, 0.70f, 0.60f, 1.00f, 1.00f, 0.67f, 0.43f, 1.00f); static public Color animatedPropertyColor { get { return s_AnimatedPropertyColor; } } static public Color recordedPropertyColor { get { return s_RecordedPropertyColor; } } static public Color candidatePropertyColor { get { return s_CandidatePropertyColor; } } static private AnimationModeDriver s_DummyDriver; static private AnimationModeDriver DummyDriver() { if (s_DummyDriver == null) { s_DummyDriver = ScriptableObject.CreateInstance(); s_DummyDriver.name = "DummyDriver"; } return s_DummyDriver; } extern public static bool IsPropertyAnimated(Object target, string propertyPath); extern internal static bool IsPropertyCandidate(Object target, string propertyPath); // Stops animation mode, as used by the animation editor. public static void StopAnimationMode() { Internal_StopAnimationMode(DummyDriver()); } // Stops animation mode, as used by the animation editor. public static void StopAnimationMode(AnimationModeDriver driver) { Internal_StopAnimationMode(driver); } // Returns true if the editor is currently in animation mode. public static bool InAnimationMode() { return Internal_InAnimationModeNoDriver(); } // Returns true if the editor is currently in animation mode. public static bool InAnimationMode(AnimationModeDriver driver) { return Internal_InAnimationMode(driver); } // Starts animation mode, as used by the animation editor. public static void StartAnimationMode() { Internal_StartAnimationMode(DummyDriver()); } // Starts animation mode, as used by the animation editor. public static void StartAnimationMode(AnimationModeDriver driver) { Internal_StartAnimationMode(driver); } // Stops animation playback mode, as used by the animation editor. internal static void StopAnimationPlaybackMode() { s_InAnimationPlaybackMode = false; } // Returns true if the editor is currently in animation playback mode. internal static bool InAnimationPlaybackMode() { return s_InAnimationPlaybackMode; } // Starts animation mode, as used by the animation editor playback mode. internal static void StartAnimationPlaybackMode() { s_InAnimationPlaybackMode = true; } internal static void StopAnimationRecording() { s_InAnimationRecordMode = false; onAnimationRecordingStop?.Invoke(); } internal static bool InAnimationRecording() { return s_InAnimationRecordMode; } internal static void StartAnimationRecording() { s_InAnimationRecordMode = true; onAnimationRecordingStart?.Invoke(); } internal static void StartCandidateRecording(AnimationModeDriver driver) { Internal_StartCandidateRecording(driver); } [NativeThrows] extern internal static void AddCandidate(EditorCurveBinding binding, PropertyModification modification, bool keepPrefabOverride); [NativeThrows] extern internal static void AddCandidates([NotNull] GameObject gameObject, [NotNull] AnimationClip clip); extern internal static void StopCandidateRecording(); extern internal static bool IsRecordingCandidates(); [NativeThrows] extern public static void BeginSampling(); [NativeThrows] extern public static void EndSampling(); [NativeThrows] extern public static void SampleAnimationClip([NotNull] GameObject gameObject, [NotNull] AnimationClip clip, float time); [NativeThrows] extern internal static void SampleCandidateClip([NotNull] GameObject gameObject, [NotNull] AnimationClip clip, float time); [NativeThrows] extern public static void SamplePlayableGraph(PlayableGraph graph, int index, float time); [NativeThrows] extern public static void AddPropertyModification(EditorCurveBinding binding, PropertyModification modification, bool keepPrefabOverride); [NativeThrows] extern public static void AddEditorCurveBinding([NotNull] GameObject gameObject, EditorCurveBinding binding); [NativeThrows] extern internal static void AddTransformTR([NotNull] GameObject root, string path); [NativeThrows] extern internal static void AddTransformTRS([NotNull] GameObject root, string path); [NativeThrows] extern internal static void InitializePropertyModificationForGameObject([NotNull] GameObject gameObject, [NotNull] AnimationClip clip); [NativeThrows] extern internal static void InitializePropertyModificationForObject([NotNull] Object target, [NotNull] AnimationClip clip); [NativeThrows] extern internal static void RevertPropertyModificationsForGameObject([NotNull] GameObject gameObject); [NativeThrows] extern internal static void RevertPropertyModificationsForObject([NotNull] Object target); // Returns editor curve bindings for animation clip and animator hierarchy that need to be snapshot for animation mode. extern internal static EditorCurveBinding[] GetAllBindings([NotNull] GameObject root, [NotNull] AnimationClip clip); // Returns editor curve bindings for animation clip that need to be snapshot for animation mode. extern internal static EditorCurveBinding[] GetCurveBindings([NotNull] AnimationClip clip); // Return editor curve bindings for animator hierarhcy that need to be snapshot for animation mode. extern internal static EditorCurveBinding[] GetAnimatorBindings([NotNull] GameObject root); extern private static void Internal_StartAnimationMode(Object driver); extern private static void Internal_StopAnimationMode(Object driver); extern private static bool Internal_InAnimationMode(Object driver); extern private static bool Internal_InAnimationModeNoDriver(); [NativeThrows] extern private static void Internal_StartCandidateRecording(Object driver); } } ================================================ FILE: Editor/Mono/Animation/AnimationUtility.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Linq; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.Scripting.APIUpdating; using UnityEngine; using UnityEngine.Internal; using Object = UnityEngine.Object; using UnityEngine.Animations; namespace UnityEditor { public struct ObjectReferenceKeyframe { public float time; public UnityEngine.Object value; } // An AnimationClipCurveData object contains all the information needed to identify a specific curve in an AnimationClip. The curve animates a specific property of a component / material attached to a game object / animated bone. public class AnimationClipCurveData { // The path of the game object / bone being animated. public string path; // The type of the component / material being animated. public Type type; // The name of the property being animated. public string propertyName; // The actual animation curve. public AnimationCurve curve; // This is only used internally for deleting curves internal int classID; internal int scriptInstanceID; public AnimationClipCurveData() { } public AnimationClipCurveData(EditorCurveBinding binding) { path = binding.path; type = binding.type; propertyName = binding.propertyName; curve = null; classID = binding.m_ClassID; scriptInstanceID = binding.m_ScriptInstanceID; } } [NativeHeader("Editor/Src/Animation/AnimationUtility.bindings.h")] [NativeHeader("Modules/Animation/ScriptBindings/GenericBinding.bindings.h")] public partial class AnimationUtility { public enum CurveModifiedType { CurveDeleted = 0, CurveModified = 1, ClipModified = 2 } public enum TangentMode { Free = 0, Auto = 1, Linear = 2, Constant = 3, ClampedAuto = 4 } internal enum PolynomialValid { Valid = 0, InvalidPreWrapMode = 1, InvalidPostWrapMode = 2, TooManySegments = 3 } internal enum DiscreteBindingResult { Valid = 0, InvalidScript = 1, MissingField = 2, IncompatibleFieldType = 3, MissingDiscreteAttribute = 4 } public delegate void OnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, CurveModifiedType type); public static OnCurveWasModified onCurveWasModified; [RequiredByNativeCode] private static void Internal_CallOnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, CurveModifiedType type) { if (onCurveWasModified != null) onCurveWasModified(clip, binding, type); } [RequiredByNativeCode] private static void Internal_CallAnimationClipAwake(AnimationClip clip) { if (onCurveWasModified != null) onCurveWasModified(clip, new EditorCurveBinding(), CurveModifiedType.ClipModified); } [Obsolete("GetAnimationClips(Animation) is deprecated. Use GetAnimationClips(GameObject) instead.")] public static AnimationClip[] GetAnimationClips(Animation component) { return GetAnimationClipsInAnimationPlayer(component.gameObject); } // Returns the array of AnimationClips that are referenced in the Animation component public static AnimationClip[] GetAnimationClips(GameObject gameObject) { if (gameObject == null) throw new ArgumentNullException("gameObject"); AnimationClip[] clips = GetAnimationClipsInAnimationPlayer(gameObject); IAnimationClipSource[] clipSources = gameObject.GetComponents(); if (clipSources.Length > 0) { var allClips = new List(clips); for (int i = 0; i < clipSources.Length; ++i) { var extraClips = new List(); clipSources[i].GetAnimationClips(extraClips); allClips.Capacity = allClips.Count + extraClips.Count; foreach (var clip in extraClips) { if (clip != null) allClips.Add(clip); } } return allClips.ToArray(); } return clips; } // Returns the array of AnimationClips that are referenced in the Animation component extern internal static AnimationClip[] GetAnimationClipsInAnimationPlayer([NotNull] GameObject gameObject); // Sets the array of AnimationClips to be referenced in the Animation component extern public static void SetAnimationClips([NotNull] Animation animation, [Unmarshalled] AnimationClip[] clips); public static EditorCurveBinding[] GetAnimatableBindings(GameObject targetObject, GameObject root) { return Internal_GetGameObjectAnimatableBindings(targetObject, root); } internal static EditorCurveBinding[] GetAnimatableBindings(ScriptableObject scriptableObject) { return Internal_GetScriptableObjectAnimatableBindings(scriptableObject); } internal static EditorCurveBinding[] GetAnimationStreamBindings(GameObject root) { return Internal_GetAnimationStreamBindings(root); } extern private static EditorCurveBinding[] Internal_GetGameObjectAnimatableBindings([NotNull] GameObject targetObject, [NotNull] GameObject root); extern private static EditorCurveBinding[] Internal_GetScriptableObjectAnimatableBindings([NotNull] ScriptableObject scriptableObject); extern private static EditorCurveBinding[] Internal_GetAnimationStreamBindings([NotNull] GameObject root); // Binds the property and returns the type of the bound value (Can be used to display special UI for it and to enforce correct drag and drop) // null if it can't be bound. public static System.Type GetEditorCurveValueType(GameObject root, EditorCurveBinding binding) { return Internal_GetGameObjectEditorCurveValueType(root, binding); } internal static System.Type GetEditorCurveValueType(ScriptableObject scriptableObject, EditorCurveBinding binding) { return Internal_GetScriptableObjectEditorCurveValueType(scriptableObject, binding); } extern private static System.Type Internal_GetGameObjectEditorCurveValueType([NotNull] GameObject root, EditorCurveBinding binding); extern private static System.Type Internal_GetScriptableObjectEditorCurveValueType([NotNull] ScriptableObject scriptableObject, EditorCurveBinding binding); extern public static bool GetFloatValue([NotNull] GameObject root, EditorCurveBinding binding, out float data); extern public static bool GetDiscreteIntValue([NotNull] GameObject root, EditorCurveBinding binding, out int data); public static bool GetObjectReferenceValue(GameObject root, EditorCurveBinding binding, out Object data) { data = Internal_GetObjectReferenceValue(root, binding, out bool result); return result; } extern private static Object Internal_GetObjectReferenceValue([NotNull] GameObject root, EditorCurveBinding binding, out bool result); extern public static Object GetAnimatedObject([NotNull] GameObject root, EditorCurveBinding binding); public static Type PropertyModificationToEditorCurveBinding(PropertyModification modification, GameObject gameObject, out EditorCurveBinding binding) { binding = new EditorCurveBinding(); binding.type = typeof(Object); // dummy type to avoid errors while marshalling. if (modification == null) return null; return Internal_PropertyModificationToEditorCurveBinding(modification, gameObject, out binding); } extern private static Type Internal_PropertyModificationToEditorCurveBinding(PropertyModification modification, [NotNull] GameObject gameObject, out EditorCurveBinding binding); extern internal static PropertyModification EditorCurveBindingToPropertyModification(EditorCurveBinding binding, [NotNull] GameObject gameObject); extern public static EditorCurveBinding[] GetCurveBindings([NotNull] AnimationClip clip); extern public static EditorCurveBinding[] GetObjectReferenceCurveBindings([NotNull] AnimationClip clip); extern public static ObjectReferenceKeyframe[] GetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding); public static void SetObjectReferenceCurve(AnimationClip clip, EditorCurveBinding binding, [Unmarshalled]ObjectReferenceKeyframe[] keyframes) { Internal_SetObjectReferenceCurve(clip, binding, keyframes, true); Internal_InvokeOnCurveWasModified(clip, binding, keyframes != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted); } public static void SetObjectReferenceCurves(AnimationClip clip, EditorCurveBinding[] bindings, ObjectReferenceKeyframe[][] keyframes) { if (bindings == null) throw new ArgumentNullException(nameof(bindings), $"{nameof(bindings)} must be non-null"); if (keyframes == null) throw new ArgumentNullException(nameof(keyframes), $"{nameof(keyframes)} must be non-null"); if (bindings.Length != keyframes.Length) throw new InvalidOperationException($"{nameof(bindings)} and {nameof(keyframes)} must be of equal length"); int length = bindings.Length; for (int i = 0; i < length; i++) { SetObjectReferenceCurveNoSync(clip, bindings[i], keyframes[i]); } SyncEditorCurves(clip); Internal_InvokeOnCurveWasModified(clip, new EditorCurveBinding(), CurveModifiedType.ClipModified); } internal static void SetObjectReferenceCurveNoSync(AnimationClip clip, EditorCurveBinding binding, ObjectReferenceKeyframe[] keyframes) { Internal_SetObjectReferenceCurve(clip, binding, keyframes, false); Internal_InvokeOnCurveWasModified(clip, binding, keyframes != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted); } [NativeThrows] extern private static void Internal_SetObjectReferenceCurve([NotNull] AnimationClip clip, EditorCurveBinding binding, [Unmarshalled] ObjectReferenceKeyframe[] keyframes, bool updateMuscleClip); extern public static AnimationCurve GetEditorCurve([NotNull] AnimationClip clip, EditorCurveBinding binding); public static void SetEditorCurve(AnimationClip clip, EditorCurveBinding binding, AnimationCurve curve) { Internal_SetEditorCurve(clip, binding, curve, true); Internal_InvokeOnCurveWasModified(clip, binding, curve != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted); } public static void SetEditorCurves(AnimationClip clip, EditorCurveBinding[] bindings, AnimationCurve[] curves) { if (bindings == null) throw new ArgumentNullException(nameof(bindings), $"{nameof(bindings)} must be non-null"); if (curves == null) throw new ArgumentNullException(nameof(curves), $"{nameof(curves)} must be non-null"); if (bindings.Length != curves.Length) throw new InvalidOperationException($"{nameof(bindings)} and {nameof(curves)} must be of equal length"); int length = bindings.Length; for (int i = 0; i < length; i++) { SetEditorCurveNoSync(clip, bindings[i], curves[i]); } SyncEditorCurves(clip); Internal_InvokeOnCurveWasModified(clip, new EditorCurveBinding(), AnimationUtility.CurveModifiedType.ClipModified); } internal static void SetEditorCurveNoSync(AnimationClip clip, EditorCurveBinding binding, AnimationCurve curve) { Internal_SetEditorCurve(clip, binding, curve, false); Internal_InvokeOnCurveWasModified(clip, binding, curve != null ? CurveModifiedType.CurveModified : CurveModifiedType.CurveDeleted); } [NativeThrows] extern private static void Internal_SetEditorCurve([NotNull] AnimationClip clip, EditorCurveBinding binding, AnimationCurve curve, bool syncEditorCurves); extern internal static DiscreteBindingResult IsDiscreteIntBinding(EditorCurveBinding binding); extern internal static void SyncEditorCurves([NotNull] AnimationClip clip); private static void Internal_InvokeOnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, CurveModifiedType type) { if (onCurveWasModified != null) { onCurveWasModified(clip, binding, type); } } [NativeThrows] extern internal static void UpdateTangentsFromModeSurrounding([NotNull] AnimationCurve curve, int index); extern internal static void UpdateTangentsFromMode([NotNull] AnimationCurve curve); [NativeThrows, ThreadSafe] extern public static TangentMode GetKeyLeftTangentMode([NotNull] AnimationCurve curve, int index); [NativeThrows, ThreadSafe] extern public static TangentMode GetKeyRightTangentMode([NotNull] AnimationCurve curve, int index); [NativeThrows] extern public static bool GetKeyBroken([NotNull] AnimationCurve curve, int index); [NativeThrows, ThreadSafe] extern public static void SetKeyLeftTangentMode([NotNull] AnimationCurve curve, int index, TangentMode tangentMode); [NativeThrows, ThreadSafe] extern public static void SetKeyRightTangentMode([NotNull] AnimationCurve curve, int index, TangentMode tangentMode); [NativeThrows] extern public static void SetKeyBroken([NotNull] AnimationCurve curve, int index, bool broken); internal static TangentMode GetKeyLeftTangentMode(Keyframe key) { return Internal_GetKeyLeftTangentMode(key); } internal static TangentMode GetKeyRightTangentMode(Keyframe key) { return Internal_GetKeyRightTangentMode(key); } internal static bool GetKeyBroken(Keyframe key) { return Internal_GetKeyBroken(key); } extern private static TangentMode Internal_GetKeyLeftTangentMode(Keyframe key); extern private static TangentMode Internal_GetKeyRightTangentMode(Keyframe key); extern private static bool Internal_GetKeyBroken(Keyframe key); internal static void SetKeyLeftTangentMode(ref Keyframe key, TangentMode tangentMode) { Internal_SetKeyLeftTangentMode(ref key, tangentMode); } internal static void SetKeyRightTangentMode(ref Keyframe key, TangentMode tangentMode) { Internal_SetKeyRightTangentMode(ref key, tangentMode); } internal static void SetKeyBroken(ref Keyframe key, bool broken) { Internal_SetKeyBroken(ref key, broken); } extern private static void Internal_SetKeyLeftTangentMode(ref Keyframe key, TangentMode tangentMode); extern private static void Internal_SetKeyRightTangentMode(ref Keyframe key, TangentMode tangentMode); extern private static void Internal_SetKeyBroken(ref Keyframe key, bool broken); [NativeThrows] extern internal static int AddInbetweenKey(AnimationCurve curve, float time); [Obsolete("GetAllCurves is deprecated. Use GetCurveBindings and GetObjectReferenceCurveBindings instead.")] public static AnimationClipCurveData[] GetAllCurves(AnimationClip clip) { bool includeCurveData = true; return GetAllCurves(clip, includeCurveData); } [Obsolete("GetAllCurves is deprecated. Use GetCurveBindings and GetObjectReferenceCurveBindings instead.")] public static AnimationClipCurveData[] GetAllCurves(AnimationClip clip, [DefaultValue("true")] bool includeCurveData) { EditorCurveBinding[] bindings = GetCurveBindings(clip); AnimationClipCurveData[] curves = new AnimationClipCurveData[bindings.Length]; for (int i = 0; i < curves.Length; i++) { curves[i] = new AnimationClipCurveData(bindings[i]); if (includeCurveData) curves[i].curve = GetEditorCurve(clip, bindings[i]); } return curves; } [Obsolete("This overload is deprecated. Use the one with EditorCurveBinding instead.")] public static bool GetFloatValue(GameObject root, string relativePath, Type type, string propertyName, out float data) { return GetFloatValue(root, EditorCurveBinding.FloatCurve(relativePath, type, propertyName), out data); } [Obsolete("This overload is deprecated. Use the one with EditorCurveBinding instead.")] public static void SetEditorCurve(AnimationClip clip, string relativePath, Type type, string propertyName, AnimationCurve curve) { SetEditorCurve(clip, EditorCurveBinding.FloatCurve(relativePath, type, propertyName), curve); } [Obsolete("This overload is deprecated. Use the one with EditorCurveBinding instead.")] public static AnimationCurve GetEditorCurve(AnimationClip clip, string relativePath, Type type, string propertyName) { return GetEditorCurve(clip, EditorCurveBinding.FloatCurve(relativePath, type, propertyName)); } public static AnimationEvent[] GetAnimationEvents(AnimationClip clip) { var blittableEvents = GetAnimationEventsInternal(clip); var animationEvents = blittableEvents.Select(AnimationEventBlittable.ToAnimationEvent).ToArray(); foreach (var blittableEvent in blittableEvents) blittableEvent.Dispose(); return animationEvents; } [return:Unmarshalled] extern internal static AnimationEventBlittable[] GetAnimationEventsInternal([NotNull] AnimationClip clip); public static void SetAnimationEvents(AnimationClip clip, AnimationEvent[] events) { var blittableEvents = events.Select(AnimationEventBlittable.FromAnimationEvent).ToArray(); SetAnimationEventsInternal(clip, blittableEvents); foreach (var blittableEvent in blittableEvents) blittableEvent.Dispose(); } extern internal static void SetAnimationEventsInternal([NotNull] AnimationClip clip, [NotNull] AnimationEventBlittable[] events); extern public static string CalculateTransformPath([NotNull] Transform targetTransform, Transform root); extern public static AnimationClipSettings GetAnimationClipSettings([NotNull] AnimationClip clip); extern internal static void RebuildMecanimData([NotNull] AnimationClip clip); extern public static void SetAnimationClipSettings([NotNull] AnimationClip clip, AnimationClipSettings srcClipInfo); extern internal static void SetAnimationClipSettingsNoDirty([NotNull] AnimationClip clip, AnimationClipSettings srcClipInfo); extern public static void SetAdditiveReferencePose(AnimationClip clip, AnimationClip referenceClip, float time); extern internal static bool IsValidOptimizedPolynomialCurve(AnimationCurve curve); extern public static void ConstrainToPolynomialCurve(AnimationCurve curve); extern internal static int GetMaxNumPolynomialSegmentsSupported(); extern internal static PolynomialValid IsValidPolynomialCurve(AnimationCurve curve); extern internal static AnimationClipStats GetAnimationClipStats(AnimationClip clip); [Obsolete("This is not used anymore. Root motion curves are automatically generated if applyRootMotion is enabled on Animator component.")] public static bool GetGenerateMotionCurves(AnimationClip clip) { return true; } [Obsolete("This is not used anymore. Root motion curves are automatically generated if applyRootMotion is enabled on Animator component.")] public static void SetGenerateMotionCurves(AnimationClip clip, bool value) { } [Obsolete("Use AnimationClip.hasGenericRootTransform instead.")] extern internal static bool HasGenericRootTransform(AnimationClip clip); [Obsolete("Use AnimationClip.hasMotionFloatCurves instead.")] extern internal static bool HasMotionFloatCurves(AnimationClip clip); [Obsolete("Use AnimationClip.hasMotionCurves instead.")] extern internal static bool HasMotionCurves(AnimationClip clip); [Obsolete("Use AnimationClip.hasRootCurves instead.")] extern internal static bool HasRootCurves(AnimationClip clip); extern internal static bool AmbiguousBinding(string path, int classID, Transform root); extern internal static Vector3 GetClosestEuler(Quaternion q, Vector3 eulerHint, RotationOrder rotationOrder); [NativeHeader("Modules/Animation/AnimationUtility.h")] [FreeFunction] extern internal static void SampleEulerHint([NotNull] GameObject go, [NotNull] AnimationClip clip, float inTime, WrapMode wrapMode); [Obsolete("Use AnimationMode.InAnimationMode instead.")] static public bool InAnimationMode() { return AnimationMode.InAnimationMode(); } [Obsolete("Use AnimationMode.StartAnimationmode instead.")] public static void StartAnimationMode(Object[] objects) { Debug.LogWarning("AnimationUtility.StartAnimationMode is deprecated. Use AnimationMode.StartAnimationMode with the new APIs. The objects passed to this function will no longer be reverted automatically. See AnimationMode.AddPropertyModification"); AnimationMode.StartAnimationMode(); } [Obsolete("Use AnimationMode.StopAnimationMode instead.")] public static void StopAnimationMode() { AnimationMode.StopAnimationMode(); } [Obsolete("SetAnimationType is no longer supported.")] public static void SetAnimationType(AnimationClip clip, ModelImporterAnimationType type) {} extern public static GenericBinding[] EditorCurveBindingsToGenericBindings(EditorCurveBinding[] editorCurveBindings); } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AddCurvesPopup.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; using UnityEditor; using UnityEngine; using System; using Object = UnityEngine.Object; namespace UnityEditorInternal { internal class AddCurvesPopup : EditorWindow { const float k_WindowPadding = 3; const float k_SpaceForSlider = 16; const float k_WindowMaxWidth = 450; const float k_WindowMinWidth = 240; const float k_WindowFixedHeight = 250; internal static AnimationWindowState s_State; private static AddCurvesPopup s_AddCurvesPopup; private static long s_LastClosedTime; private static AddCurvesPopupHierarchy s_Hierarchy; public delegate void OnNewCurveAdded(AddCurvesPopupPropertyNode node); private static OnNewCurveAdded NewCurveAddedCallback; Vector2 GetWindowSize() { float contentWidth = s_Hierarchy.GetContentWidth(); float width = Mathf.Clamp(contentWidth + k_SpaceForSlider + k_WindowPadding, k_WindowMinWidth, k_WindowMaxWidth); return new Vector2(width, k_WindowFixedHeight); } void Init(Rect buttonRect) { s_Hierarchy = new AddCurvesPopupHierarchy(); s_Hierarchy.InitIfNeeded(this, new Rect(0, 0, k_WindowMinWidth, k_WindowFixedHeight)); buttonRect = GUIUtility.GUIToScreenRect(buttonRect); ShowAsDropDown(buttonRect, GetWindowSize(), new[] { PopupLocation.Right }); } void OnEnable() { AssemblyReloadEvents.beforeAssemblyReload += Close; } void OnDisable() { AssemblyReloadEvents.beforeAssemblyReload -= Close; s_LastClosedTime = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; s_AddCurvesPopup = null; s_Hierarchy = null; } internal static void AddNewCurve(AddCurvesPopupPropertyNode node) { AnimationWindowUtility.CreateDefaultCurves(s_State, node.curveBindings); if (NewCurveAddedCallback != null) NewCurveAddedCallback(node); } internal static bool ShowAtPosition(Rect buttonRect, AnimationWindowState state, OnNewCurveAdded newCurveCallback) { // We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting playmode, we assume an increasing time when comparing time. long nowMilliSeconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; bool justClosed = nowMilliSeconds < s_LastClosedTime + 50; if (!justClosed) { Event.current.Use(); if (s_AddCurvesPopup == null) s_AddCurvesPopup = ScriptableObject.CreateInstance(); NewCurveAddedCallback = newCurveCallback; s_State = state; s_AddCurvesPopup.Init(buttonRect); return true; } return false; } internal void OnGUI() { // We do not use the layout event if (Event.current.type == EventType.Layout) return; Vector2 windowSize = GetWindowSize(); Rect rect = new Rect(1, 1, windowSize.x - k_WindowPadding, windowSize.y - k_WindowPadding); GUI.Box(new Rect(0, 0, windowSize.x, windowSize.y), GUIContent.none, "grey_border"); s_Hierarchy.OnGUI(rect, this); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchy.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewState = UnityEditor.IMGUI.Controls.TreeViewState; namespace UnityEditorInternal { internal class AddCurvesPopupHierarchy { private TreeViewController m_TreeView; private TreeViewState m_TreeViewState; private AddCurvesPopupHierarchyDataSource m_TreeViewDataSource; private float m_ContentWidth = 0f; public float GetContentWidth() { return m_ContentWidth; } public void OnGUI(Rect position, EditorWindow owner) { m_TreeView.SetTotalRect(position); m_TreeView.OnEvent(); m_TreeView.OnGUI(position, GUIUtility.GetControlID(FocusType.Keyboard)); } public void InitIfNeeded(EditorWindow owner, Rect rect) { if (m_TreeViewState == null) m_TreeViewState = new TreeViewState(); else return; m_TreeView = new TreeViewController(owner, m_TreeViewState); m_TreeView.deselectOnUnhandledMouseDown = true; m_TreeViewDataSource = new AddCurvesPopupHierarchyDataSource(m_TreeView); AddCurvesPopupHierarchyGUI gui = new AddCurvesPopupHierarchyGUI(m_TreeView, owner); m_TreeView.Init(rect, m_TreeViewDataSource, gui, null ); m_TreeViewDataSource.UpdateData(); m_ContentWidth = gui.GetContentWidth(); } internal virtual bool IsRenamingNodeAllowed(TreeViewItem node) { return false; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyBuilder.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; using UnityEditor; using UnityEngine; using System; using System.Linq; using UnityEditor.IMGUI.Controls; using Object = UnityEngine.Object; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewUtility = UnityEditor.IMGUI.Controls.TreeViewUtility; namespace UnityEditorInternal { struct AddCurvesPopupHierarchyBuilder { struct KeyComparer : IComparer { static readonly Type s_GameObjectType = typeof(GameObject); static readonly Type s_TransformType = typeof(Transform); public int Compare(Key x, Key y) { var result = String.Compare(x.path, y.path, StringComparison.Ordinal); if (result == 0 && x.type != y.type) { // Make sure GameObject properties appear first, then Transform. if (x.type == s_GameObjectType) return -1; if (y.type == s_GameObjectType) return 1; if (x.type == typeof(Transform)) return -1; if (y.type == typeof(Transform)) return 1; return String.Compare(x.type.Name, y.type.Name, StringComparison.Ordinal); } return result; } } struct Key { public string path; public Type type; } SortedDictionary> m_AccumulatedBindings; AnimationWindowState m_State; public AddCurvesPopupHierarchyBuilder(AnimationWindowState state) { m_AccumulatedBindings = new SortedDictionary>(new KeyComparer()); m_State = state; } public void Add(EditorCurveBinding binding) { var key = new Key { path = binding.path, type = binding.type }; if (m_AccumulatedBindings.TryGetValue(key, out var bindings)) bindings.Add(binding); else m_AccumulatedBindings[key] = new List(new [] {binding}); } void RemoveUnnecessaryBindings(List bindings) { for (int i = bindings.Count - 1; i >= 0; --i) { // Let's not add those that already have a existing curve. if (AnimationWindowUtility.IsCurveCreated(m_State.activeAnimationClip, bindings[i])) bindings.RemoveAt(i); // Remove animator enabled property which shouldn't be animated. else if (bindings[i].type == typeof(Animator) && bindings[i].propertyName == "m_Enabled") bindings.RemoveAt(i); // For RectTransform.position we only want .z else if (AnimationWindowUtility.IsRectTransformPosition(bindings[i]) && !bindings[i].propertyName.EndsWith(".z")) bindings.RemoveAt(i); // Don't show for the root go else if (bindings[i].type == typeof(GameObject) && string.IsNullOrEmpty(bindings[i].path)) bindings.RemoveAt(i); } } public TreeViewItem CreateTreeView() { TreeViewItem rootNode; // Bindings of a single Component/ScriptableObject, skip the group node. if (m_AccumulatedBindings.Count == 1) { var bindings = m_AccumulatedBindings.First().Value; RemoveUnnecessaryBindings(bindings); if (bindings.Count > 0) { rootNode = AddAnimatableObjectToHierarchy(bindings, null, ""); } else { rootNode = new AddCurvesPopupObjectNode(null, string.Empty, string.Empty); } } else { var groupNodes = new Dictionary(); var childNodes = new Dictionary>(); var inheritedNodeWeights = new Dictionary(); rootNode = new AddCurvesPopupObjectNode(null, string.Empty, string.Empty); TreeViewItem groupNode = rootNode; groupNodes.Add(string.Empty, (rootNode)); childNodes.Add(groupNode, new List()); inheritedNodeWeights.Add(groupNode, 0); string currentPath = string.Empty; foreach (var kvp in m_AccumulatedBindings) { if (!currentPath.Equals(kvp.Key.path)) { TreeViewItem parentNode = rootNode; var parentPath = GetParentPath(kvp.Key.path); while (parentPath != null) { if (groupNodes.TryGetValue(parentPath, out var node)) { parentNode = node; break; } parentPath = GetParentPath(parentPath); } groupNode = new AddCurvesPopupObjectNode(parentNode, kvp.Key.path, "", GetObjectName(kvp.Key.path)); groupNodes.Add(kvp.Key.path, groupNode); childNodes.Add(groupNode, new List()); inheritedNodeWeights.Add(groupNode, 0); childNodes[parentNode].Add(groupNode); currentPath = kvp.Key.path; } var bindings = kvp.Value; RemoveUnnecessaryBindings(bindings); if (bindings.Count > 0) { // Builtin GameObject attributes. if (kvp.Key.type == typeof(GameObject)) { TreeViewItem newNode = CreateNode(bindings.ToArray(), groupNode, null); if (newNode != null) childNodes[groupNode].Add(newNode); } else { childNodes[groupNode].Add(AddAnimatableObjectToHierarchy(bindings, groupNode, kvp.Key.path)); var parentGroupNode = groupNode; while (parentGroupNode != null) { inheritedNodeWeights[parentGroupNode] += bindings.Count; parentGroupNode = parentGroupNode.parent; } } } } // Remove empty leaves from tree view. foreach (var kvp in inheritedNodeWeights) { // Remove Leaves nodes without properties. if (inheritedNodeWeights[kvp.Key] == 0 && kvp.Key.parent != null) { childNodes[kvp.Key.parent].Remove(kvp.Key); kvp.Key.parent = null; } } // Set child parent references. foreach (var kvp in childNodes) { TreeViewUtility.SetChildParentReferences(kvp.Value, kvp.Key); } } m_AccumulatedBindings.Clear(); return rootNode; } private string GetParentPath(string path) { if (String.IsNullOrEmpty(path)) return null; int index = path.LastIndexOf('/'); if (index == -1) return string.Empty; return path.Substring(0, index); } private string GetObjectName(string path) { if (String.IsNullOrEmpty(path)) return null; int index = path.LastIndexOf('/'); if (index == -1) return path; return path.Substring(index + 1); } private string GetClassName(EditorCurveBinding binding) { if (m_State.activeRootGameObject != null) { Object target = AnimationUtility.GetAnimatedObject(m_State.activeRootGameObject, binding); if (target != null) return ObjectNames.GetInspectorTitle(target); } return binding.type.Name; } private Texture2D GetIcon(EditorCurveBinding binding) { return AssetPreview.GetMiniTypeThumbnail(binding.type); } private TreeViewItem AddAnimatableObjectToHierarchy(List curveBindings, TreeViewItem parentNode, string path) { TreeViewItem node = new AddCurvesPopupObjectNode(parentNode, path, GetClassName(curveBindings[0])); node.icon = GetIcon(curveBindings[0]); List childNodes = new List(); List singlePropertyBindings = new List(); SerializedObject so = null; for (int i = 0; i < curveBindings.Count; i++) { EditorCurveBinding curveBinding = curveBindings[i]; if (m_State.activeRootGameObject && curveBinding.isSerializeReferenceCurve) { var animatedObject = AnimationUtility.GetAnimatedObject(m_State.activeRootGameObject, curveBinding); if (animatedObject != null && (so == null || so.targetObject != animatedObject)) so = new SerializedObject(animatedObject); } singlePropertyBindings.Add(curveBinding); // We expect curveBindings to come sorted by propertyname if (i == curveBindings.Count - 1 || AnimationWindowUtility.GetPropertyGroupName(curveBindings[i + 1].propertyName) != AnimationWindowUtility.GetPropertyGroupName(curveBinding.propertyName)) { TreeViewItem newNode = CreateNode(singlePropertyBindings.ToArray(), node, so); if (newNode != null) childNodes.Add(newNode); singlePropertyBindings.Clear(); } } childNodes.Sort(); TreeViewUtility.SetChildParentReferences(childNodes, node); return node; } private TreeViewItem CreateNode(EditorCurveBinding[] curveBindings, TreeViewItem parentNode, SerializedObject so) { var node = new AddCurvesPopupPropertyNode(parentNode, curveBindings, AnimationWindowUtility.GetNicePropertyGroupDisplayName(curveBindings[0], so)); node.icon = parentNode.icon; return node; } } class AddCurvesPopupObjectNode : TreeViewItem { public AddCurvesPopupObjectNode(TreeViewItem parent, string path, string className, string displayName = null) : base((path + className).GetHashCode(), parent != null ? parent.depth + 1 : -1, parent, displayName ?? className) { } } class AddCurvesPopupPropertyNode : TreeViewItem { public EditorCurveBinding[] curveBindings; public AddCurvesPopupPropertyNode(TreeViewItem parent, EditorCurveBinding[] curveBindings, string displayName) : base(curveBindings[0].GetHashCode(), parent.depth + 1, parent, displayName) { this.curveBindings = curveBindings; } public override int CompareTo(TreeViewItem other) { AddCurvesPopupPropertyNode otherNode = other as AddCurvesPopupPropertyNode; if (otherNode != null) { if (displayName.Contains("Rotation") && otherNode.displayName.Contains("Position")) return 1; if (displayName.Contains("Position") && otherNode.displayName.Contains("Rotation")) return -1; } return base.CompareTo(other); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyDataSource.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEditor.IMGUI.Controls; using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController; using TreeViewDataSource = UnityEditor.IMGUI.Controls.TreeViewDataSource; namespace UnityEditorInternal { class AddCurvesPopupHierarchyDataSource : TreeViewDataSource { public AddCurvesPopupHierarchyDataSource(TreeViewController treeView) : base(treeView) { showRootItem = false; rootIsCollapsable = false; } private void SetupRootNodeSettings() { showRootItem = false; SetExpanded(root, true); } public override void FetchData() { m_RootItem = null; if (AddCurvesPopup.s_State.selection.canAddCurves) { var state = AddCurvesPopup.s_State; AddBindingsToHierarchy(state.controlInterface.GetAnimatableBindings()); } SetupRootNodeSettings(); m_NeedRefreshRows = true; } private void AddBindingsToHierarchy(EditorCurveBinding[] bindings) { if (bindings == null || bindings.Length == 0) { m_RootItem = new AddCurvesPopupObjectNode(null, "", ""); return; } var builder = new AddCurvesPopupHierarchyBuilder(AddCurvesPopup.s_State); for (int i = 0; i < bindings.Length; i++) { builder.Add(bindings[i]); } m_RootItem = builder.CreateTreeView(); } public void UpdateData() { m_TreeView.ReloadData(); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AddCurvesPopupHierarchyGUI.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using System.Collections.Generic; using UnityEditor.ShortcutManagement; using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewGUI = UnityEditor.IMGUI.Controls.TreeViewGUI; namespace UnityEditorInternal { internal class AddCurvesPopupHierarchyGUI : TreeViewGUI { public EditorWindow owner; public bool showPlusButton { get; set; } private GUIStyle buttonStyle = "IconButton"; private GUIContent plusIcon = EditorGUIUtility.TrIconContent("Toolbar Plus"); private GUIStyle plusButtonBackgroundStyle = "Tag MenuItem"; private GUIContent addPropertiesContent = EditorGUIUtility.TrTextContent("Add Properties"); private const float plusButtonWidth = 17; public AddCurvesPopupHierarchyGUI(TreeViewController treeView, EditorWindow owner) : base(treeView, true) { this.owner = owner; } public override void OnRowGUI(Rect rowRect, TreeViewItem node, int row, bool selected, bool focused) { base.OnRowGUI(rowRect, node, row, selected, focused); DoAddCurveButton(rowRect, node); HandleContextMenu(rowRect, node); } private void DoAddCurveButton(Rect rowRect, TreeViewItem node) { // Is it propertynode. If not, then we don't need plusButton so quit here AddCurvesPopupPropertyNode hierarchyNode = node as AddCurvesPopupPropertyNode; if (hierarchyNode == null || hierarchyNode.curveBindings == null || hierarchyNode.curveBindings.Length == 0) return; Rect buttonRect = new Rect(rowRect.width - plusButtonWidth, rowRect.yMin, plusButtonWidth, buttonStyle.fixedHeight); // TODO Make a style for add curves popup // Draw background behind plus button to prevent text overlapping GUI.Box(buttonRect, GUIContent.none, plusButtonBackgroundStyle); // Check if the curve already exists and remove plus button if (GUI.Button(buttonRect, plusIcon, buttonStyle)) { AddCurvesPopup.AddNewCurve(hierarchyNode); // Hold shift key to add new curves and keep window opened. if (Event.current.shift) m_TreeView.ReloadData(); else owner.Close(); } } private void HandleContextMenu(Rect rowRect, TreeViewItem node) { if (Event.current.type != EventType.ContextClick) return; if (rowRect.Contains(Event.current.mousePosition)) { // Add current node to selection var ids = new List(m_TreeView.GetSelection()); ids.Add(node.id); m_TreeView.SetSelection(ids.ToArray(), false, false); GenerateMenu().ShowAsContext(); Event.current.Use(); } } private GenericMenu GenerateMenu() { GenericMenu menu = new GenericMenu(); menu.AddItem(addPropertiesContent, false, AddPropertiesFromSelectedNodes); return menu; } private void AddPropertiesFromSelectedNodes() { int[] ids = m_TreeView.GetSelection(); for (int i = 0; i < ids.Length; ++i) { var node = m_TreeView.FindItem(ids[i]); var propertyNode = node as AddCurvesPopupPropertyNode; if (propertyNode != null) { AddCurvesPopup.AddNewCurve(propertyNode); } else if (node.hasChildren) { foreach (var childNode in node.children) { var childPropertyNode = childNode as AddCurvesPopupPropertyNode; if (childPropertyNode != null) { AddCurvesPopup.AddNewCurve(childPropertyNode); } } } } m_TreeView.ReloadData(); } public float GetContentWidth() { IList rows = m_TreeView.data.GetRows(); List allRows = new List(); allRows.AddRange(rows); for (int i = 0; i < allRows.Count; ++i) { var row = allRows[i]; if (row.hasChildren) allRows.AddRange(row.children); } float rowWidth = GetMaxWidth(allRows); return rowWidth + plusButtonWidth; } override protected void SyncFakeItem() { //base.SyncFakeItem(); } override protected void RenameEnded() { //base.RenameEnded(); } override protected bool IsRenaming(int id) { return false; } public override bool BeginRename(TreeViewItem item, float delay) { return false; } override protected Texture GetIconForItem(TreeViewItem item) { if (item != null) return item.icon; return null; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using System.Collections.Generic; using UnityEditor.ShortcutManagement; using UnityEditorInternal; namespace UnityEditor { internal enum WrapModeFixed { Default = (int)WrapMode.Default, Once = (int)WrapMode.Once, Loop = (int)WrapMode.Loop, ClampForever = (int)WrapMode.ClampForever, PingPong = (int)WrapMode.PingPong } [Serializable] class AnimEditor : ScriptableObject { // Active Animation windows private static List s_AnimationWindows = new List(); public static List GetAllAnimationWindows() { return s_AnimationWindows; } public bool stateDisabled { get { return m_State.disabled; } } [SerializeField] private SplitterState m_HorizontalSplitter; [SerializeReference] private AnimationWindowState m_State; [SerializeReference] private DopeSheetEditor m_DopeSheet; [SerializeReference] private CurveEditor m_CurveEditor; [SerializeField] private AnimationWindowHierarchy m_Hierarchy; [SerializeField] private AnimationWindowClipPopup m_ClipPopup; [SerializeField] private AnimationEventTimeLine m_Events; [SerializeField] private AnimEditorOverlay m_Overlay; [SerializeField] private EditorWindow m_OwnerWindow; [System.NonSerialized] private Rect m_Position; [System.NonSerialized] private bool m_TriggerFraming; [System.NonSerialized] private bool m_Initialized; private float hierarchyWidth { get { return m_HorizontalSplitter.realSizes[0]; } } private float contentWidth { get { return m_HorizontalSplitter.realSizes[1]; } } internal static PrefColor kEulerXColor = new PrefColor("Animation/EulerX", 1.0f, 0.0f, 1.0f, 1.0f); internal static PrefColor kEulerYColor = new PrefColor("Animation/EulerY", 1.0f, 1.0f, 0.0f, 1.0f); internal static PrefColor kEulerZColor = new PrefColor("Animation/EulerZ", 0.0f, 1.0f, 1.0f, 1.0f); static private Color s_SelectionRangeColorLight = new Color32(255, 255, 255, 90); static private Color s_SelectionRangeColorDark = new Color32(200, 200, 200, 40); static private Color selectionRangeColor { get { return EditorGUIUtility.isProSkin ? s_SelectionRangeColorDark : s_SelectionRangeColorLight; } } static private Color s_OutOfRangeColorLight = new Color32(160, 160, 160, 127); static private Color s_OutOfRangeColorDark = new Color32(40, 40, 40, 127); static private Color outOfRangeColor { get { return EditorGUIUtility.isProSkin ? s_OutOfRangeColorDark : s_OutOfRangeColorLight; } } static private Color s_InRangeColorLight = new Color32(211, 211, 211, 255); static private Color s_InRangeColorDark = new Color32(75, 75, 75, 255); static private Color inRangeColor { get { return EditorGUIUtility.isProSkin ? s_InRangeColorDark : s_InRangeColorLight; } } static private Color s_FilterBySelectionColorLight = new Color(0.82f, 0.97f, 1.00f, 1.00f); static private Color s_FilterBySelectionColorDark = new Color(0.54f, 0.85f, 1.00f, 1.00f); static private Color filterBySelectionColor { get { return EditorGUIUtility.isProSkin ? s_FilterBySelectionColorDark : s_FilterBySelectionColorLight; } } internal const int kSliderThickness = 13; internal const int kIntFieldWidth = 35; internal const int kHierarchyMinWidth = 300; internal const int kToggleButtonWidth = 80; internal const float kDisabledRulerAlpha = 0.12f; private int layoutRowHeight { get { return (int)EditorGUI.kWindowToolbarHeight; } } internal struct FrameRateMenuEntry { public FrameRateMenuEntry(GUIContent content, float value) { this.content = content; this.value = value; } public GUIContent content; public float value; } internal static FrameRateMenuEntry[] kAvailableFrameRates = new FrameRateMenuEntry[] { new FrameRateMenuEntry(EditorGUIUtility.TextContent("Set Sample Rate/24"), 24f), new FrameRateMenuEntry(EditorGUIUtility.TextContent("Set Sample Rate/25"), 25f), new FrameRateMenuEntry(EditorGUIUtility.TextContent("Set Sample Rate/30"), 30f), new FrameRateMenuEntry(EditorGUIUtility.TextContent("Set Sample Rate/50"), 50f), new FrameRateMenuEntry(EditorGUIUtility.TextContent("Set Sample Rate/60"), 60f) }; public AnimationWindowState state { get { return m_State; } } public AnimationWindowSelectionItem selection { get { return m_State.selection; } set { m_State.selection = value; } } public IAnimationWindowController controlInterface { get { return state.controlInterface; } } public IAnimationWindowController overrideControlInterface { get { return state.overrideControlInterface; } set { state.overrideControlInterface = value; } } private bool triggerFraming { set { m_TriggerFraming = value; } get { return m_TriggerFraming; } } internal CurveEditor curveEditor { get { return m_CurveEditor; } } internal DopeSheetEditor dopeSheetEditor { get { return m_DopeSheet; } } public void OnAnimEditorGUI(EditorWindow parent, Rect position) { m_DopeSheet.m_Owner = parent; m_OwnerWindow = parent; m_Position = position; if (!m_Initialized) Initialize(); m_State.OnGUI(); if (m_State.disabled && m_State.recording) m_State.recording = false; SynchronizeLayout(); using (new EditorGUI.DisabledScope(m_State.disabled || m_State.animatorIsOptimized)) { int optionsID = GUIUtility.GetControlID(FocusType.Passive); if (Event.current.type != EventType.Repaint) OptionsOnGUI(optionsID); OverlayEventOnGUI(); GUILayout.BeginHorizontal(); SplitterGUILayout.BeginHorizontalSplit(m_HorizontalSplitter); // Left side GUILayout.BeginVertical(); // First row of controls GUILayout.BeginHorizontal(AnimationWindowStyles.animPlayToolBar); PlayControlsOnGUI(); GUILayout.EndHorizontal(); // Second row of controls GUILayout.BeginHorizontal(AnimationWindowStyles.animClipToolBar); LinkOptionsOnGUI(); ClipSelectionDropDownOnGUI(); GUILayout.FlexibleSpace(); FrameRateInputFieldOnGUI(); FilterBySelectionButtonOnGUI(); AddKeyframeButtonOnGUI(); AddEventButtonOnGUI(); GUILayout.EndHorizontal(); HierarchyOnGUI(); // Bottom row of controls using (new GUILayout.HorizontalScope(AnimationWindowStyles.toolbarBottom)) { TabSelectionOnGUI(); } GUILayout.EndVertical(); // Right side GUILayout.BeginVertical(); // Acquire Rects Rect timerulerRect = GUILayoutUtility.GetRect(contentWidth, layoutRowHeight); Rect eventsRect = GUILayoutUtility.GetRect(contentWidth, layoutRowHeight - 1); Rect contentLayoutRect = GUILayoutUtility.GetRect(contentWidth, contentWidth, 0f, float.MaxValue, GUILayout.ExpandHeight(true)); // MainContent must be done first since it resizes the Zoomable area. MainContentOnGUI(contentLayoutRect); TimeRulerOnGUI(timerulerRect); EventLineOnGUI(eventsRect); GUILayout.EndVertical(); SplitterGUILayout.EndHorizontalSplit(); GUILayout.EndHorizontal(); // Overlay OverlayOnGUI(contentLayoutRect); if (Event.current.type == EventType.Repaint) { OptionsOnGUI(optionsID); AnimationWindowStyles.separator.Draw(new Rect(hierarchyWidth, 0, 1, position.height), false, false, false, false); } RenderEventTooltip(); } } void MainContentOnGUI(Rect contentLayoutRect) { // Bail out if the hierarchy in animator is optimized. if (m_State.animatorIsOptimized) { GUI.Label(contentLayoutRect, GUIContent.none, AnimationWindowStyles.dopeSheetBackground); Vector2 textSize = GUI.skin.label.CalcSize(AnimationWindowStyles.animatorOptimizedText); Rect labelRect = new Rect(contentLayoutRect.x + contentLayoutRect.width * .5f - textSize.x * .5f, contentLayoutRect.y + contentLayoutRect.height * .5f - textSize.y * .5f, textSize.x, textSize.y); GUI.Label(labelRect, AnimationWindowStyles.animatorOptimizedText); return; } var mainAreaControlID = 0; if (m_State.disabled) { SetupWizardOnGUI(contentLayoutRect); } else { Event evt = Event.current; if (evt.type == EventType.MouseDown && contentLayoutRect.Contains(evt.mousePosition)) m_Events.ClearSelection(); if (triggerFraming && evt.type == EventType.Repaint) { m_DopeSheet.FrameClip(); m_CurveEditor.FrameClip(true, true); triggerFraming = false; } if (m_State.showCurveEditor) { CurveEditorOnGUI(contentLayoutRect); mainAreaControlID = m_CurveEditor.areaControlID; } else { DopeSheetOnGUI(contentLayoutRect); mainAreaControlID = m_DopeSheet.areaControlID; } } HandleMainAreaCopyPaste(mainAreaControlID); } private void OverlayEventOnGUI() { if (m_State.animatorIsOptimized) return; if (m_State.disabled) return; Rect overlayRect = new Rect(hierarchyWidth - 1, 0f, contentWidth - kSliderThickness, m_Position.height - kSliderThickness); GUI.BeginGroup(overlayRect); m_Overlay.HandleEvents(); GUI.EndGroup(); } private void OverlayOnGUI(Rect contentRect) { if (m_State.animatorIsOptimized) return; if (m_State.disabled) return; if (Event.current.type != EventType.Repaint) return; Rect contentRectNoSliders = new Rect(contentRect.xMin, contentRect.yMin, contentRect.width - kSliderThickness, contentRect.height - kSliderThickness); Rect overlayRectNoSliders = new Rect(hierarchyWidth - 1, 0f, contentWidth - kSliderThickness, m_Position.height - kSliderThickness); GUI.BeginGroup(overlayRectNoSliders); Rect localRect = new Rect(0, 0, overlayRectNoSliders.width, overlayRectNoSliders.height); Rect localContentRect = contentRectNoSliders; localContentRect.position -= overlayRectNoSliders.min; m_Overlay.OnGUI(localRect, localContentRect); GUI.EndGroup(); } public void Update() { if (m_State == null) return; PlaybackUpdate(); } public void OnEnable() { s_AnimationWindows.Add(this); if (m_State == null) { m_State = new AnimationWindowState(); m_State.animEditor = this; InitializeHorizontalSplitter(); InitializeClipSelection(); InitializeDopeSheet(); InitializeEvents(); InitializeCurveEditor(); InitializeOverlay(); } InitializeNonserializedValues(); m_State.timeArea = m_State.showCurveEditor ? (TimeArea)m_CurveEditor : m_DopeSheet; m_DopeSheet.state = m_State; m_ClipPopup.state = m_State; m_Overlay.state = m_State; m_State.OnEnable(); m_CurveEditor.curvesUpdated += SaveChangedCurvesFromCurveEditor; m_CurveEditor.OnEnable(); } public void OnDisable() { s_AnimationWindows.Remove(this); if (m_CurveEditor != null) { m_CurveEditor.curvesUpdated -= SaveChangedCurvesFromCurveEditor; m_CurveEditor.OnDisable(); } m_DopeSheet?.OnDisable(); m_State.OnDisable(); } public void OnDestroy() { m_CurveEditor?.OnDestroy(); m_State?.OnDestroy(); } public void OnSelectionChanged() { triggerFraming = true; // Framing of clip can only be done after Layout. Here we just order it to happen later on. Repaint(); } public void OnSelectionUpdated() { m_State.OnSelectionUpdated(); Repaint(); } public void OnStartLiveEdit() { SaveCurveEditorKeySelection(); } public void OnEndLiveEdit() { UpdateSelectedKeysToCurveEditor(); m_State.ResampleAnimation(); } public void OnLostFocus() { // RenameOverlay might still be active, close before switching to another window. if (m_Hierarchy != null) m_Hierarchy.EndNameEditing(true); // Stop text editing. FrameRate ui or Frame ui may still be in edition mode. EditorGUI.EndEditingActiveTextField(); } private void PlaybackUpdate() { if (m_State.disabled && m_State.playing) m_State.playing = false; if (m_State.PlaybackUpdate()) Repaint(); } private void SetupWizardOnGUI(Rect position) { GUI.Label(position, GUIContent.none, AnimationWindowStyles.dopeSheetBackground); Rect positionWithoutScrollBar = new Rect(position.x, position.y, position.width - kSliderThickness, position.height - kSliderThickness); GUI.BeginClip(positionWithoutScrollBar); GUI.enabled = true; m_State.showCurveEditor = false; m_State.timeArea = m_DopeSheet; m_State.timeArea.SetShownHRangeInsideMargins(0f, 1f); bool animatableObject = m_State.activeGameObject && !EditorUtility.IsPersistent(m_State.activeGameObject); if (animatableObject) { var missingObjects = (!m_State.activeRootGameObject && !m_State.activeAnimationClip) ? AnimationWindowStyles.animatorAndAnimationClip.text : AnimationWindowStyles.animationClip.text; string txt = String.Format(AnimationWindowStyles.formatIsMissing.text, m_State.activeGameObject.name, missingObjects); const float buttonWidth = 70f; const float buttonHeight = 20f; const float buttonPadding = 3f; GUIContent textContent = GUIContent.Temp(txt); Vector2 textSize = GUI.skin.label.CalcSize(textContent); Rect labelRect = new Rect(positionWithoutScrollBar.width * .5f - textSize.x * .5f, positionWithoutScrollBar.height * .5f - textSize.y * .5f, textSize.x, textSize.y); GUI.Label(labelRect, textContent); Rect buttonRect = new Rect(positionWithoutScrollBar.width * .5f - buttonWidth * .5f, labelRect.yMax + buttonPadding, buttonWidth, buttonHeight); if (GUI.Button(buttonRect, AnimationWindowStyles.create)) { if (AnimationWindowUtility.InitializeGameobjectForAnimation(m_State.activeGameObject)) { Component animationPlayer = AnimationWindowUtility.GetClosestAnimationPlayerComponentInParents(m_State.activeGameObject.transform); m_State.activeAnimationClip = AnimationUtility.GetAnimationClips(animationPlayer.gameObject)[0]; } // Layout has changed, bail out now. EditorGUIUtility.ExitGUI(); } } else { Color oldColor = GUI.color; GUI.color = Color.gray; Vector2 textSize = GUI.skin.label.CalcSize(AnimationWindowStyles.noAnimatableObjectSelectedText); Rect labelRect = new Rect(positionWithoutScrollBar.width * .5f - textSize.x * .5f, positionWithoutScrollBar.height * .5f - textSize.y * .5f, textSize.x, textSize.y); GUI.Label(labelRect, AnimationWindowStyles.noAnimatableObjectSelectedText); GUI.color = oldColor; } GUI.EndClip(); GUI.enabled = false; // Reset state to false. It's always false originally for SetupWizardOnGUI. } private void EventLineOnGUI(Rect eventsRect) { eventsRect.width -= kSliderThickness; GUI.Label(eventsRect, GUIContent.none, AnimationWindowStyles.eventBackground); using (new EditorGUI.DisabledScope(!selection.animationIsEditable)) { m_Events.EventLineGUI(eventsRect, m_State); } } private void RenderEventTooltip() { m_Events.DrawInstantTooltip(m_Position); } private void TabSelectionOnGUI() { GUILayout.FlexibleSpace(); EditorGUI.BeginChangeCheck(); GUILayout.Toggle(!m_State.showCurveEditor, AnimationWindowStyles.dopesheet, AnimationWindowStyles.miniToolbarButton, GUILayout.Width(kToggleButtonWidth)); GUILayout.Toggle(m_State.showCurveEditor, AnimationWindowStyles.curves, EditorStyles.toolbarButtonRight, GUILayout.Width(kToggleButtonWidth)); if (EditorGUI.EndChangeCheck()) { SwitchBetweenCurvesAndDopesheet(); } } private void HierarchyOnGUI() { Rect hierarchyLayoutRect = GUILayoutUtility.GetRect(hierarchyWidth, hierarchyWidth, 0f, float.MaxValue, GUILayout.ExpandHeight(true)); if (!m_State.showReadOnly && !m_State.selection.animationIsEditable) { Vector2 labelSize = GUI.skin.label.CalcSize(AnimationWindowStyles.readOnlyPropertiesLabel); const float buttonWidth = 210f; const float buttonHeight = 20f; const float buttonPadding = 3f; Rect labelRect = new Rect(hierarchyLayoutRect.x + hierarchyLayoutRect.width * .5f - labelSize.x * .5f, hierarchyLayoutRect.y + hierarchyLayoutRect.height * .5f - labelSize.y, labelSize.x, labelSize.y); Rect buttonRect = new Rect(hierarchyLayoutRect.x + hierarchyLayoutRect.width * .5f - buttonWidth * .5f, labelRect.yMax + buttonPadding, buttonWidth, buttonHeight); GUI.Label(labelRect, AnimationWindowStyles.readOnlyPropertiesLabel); if (GUI.Button(buttonRect, AnimationWindowStyles.readOnlyPropertiesButton)) { m_State.showReadOnly = true; // Layout has changed, bail out now. EditorGUIUtility.ExitGUI(); } return; } if (!m_State.disabled) m_Hierarchy.OnGUI(hierarchyLayoutRect); } private void FrameRateInputFieldOnGUI() { if (!m_State.showFrameRate) return; using (new EditorGUI.DisabledScope(!selection.animationIsEditable)) { GUILayout.Label(AnimationWindowStyles.samples, EditorStyles.toolbarLabel); EditorGUI.BeginChangeCheck(); int clipFrameRate = EditorGUILayout.DelayedIntField((int)m_State.clipFrameRate, EditorStyles.toolbarTextField, GUILayout.Width(kIntFieldWidth)); if (EditorGUI.EndChangeCheck()) { m_State.clipFrameRate = clipFrameRate; UpdateSelectedKeysToCurveEditor(); } } } private void ClipSelectionDropDownOnGUI() { m_ClipPopup.OnGUI(); } private void DopeSheetOnGUI(Rect position) { Rect noVerticalSliderRect = new Rect(position.xMin, position.yMin, position.width - kSliderThickness, position.height); if (Event.current.type == EventType.Repaint) { m_DopeSheet.rect = noVerticalSliderRect; m_DopeSheet.SetTickMarkerRanges(); m_DopeSheet.RecalculateBounds(); } if (m_State.showCurveEditor) return; Rect noSlidersRect = new Rect(position.xMin, position.yMin, position.width - kSliderThickness, position.height - kSliderThickness); m_DopeSheet.BeginViewGUI(); GUI.Label(position, GUIContent.none, AnimationWindowStyles.dopeSheetBackground); if (!m_State.disabled) { m_DopeSheet.TimeRuler(noSlidersRect, m_State.frameRate, false, true, kDisabledRulerAlpha, m_State.timeFormat); // grid } m_DopeSheet.OnGUI(noSlidersRect, m_State.hierarchyState.scrollPos * -1); m_DopeSheet.EndViewGUI(); Rect verticalScrollBarPosition = new Rect(noVerticalSliderRect.xMax, noVerticalSliderRect.yMin, kSliderThickness, noSlidersRect.height); float visibleHeight = m_Hierarchy.GetTotalRect().height; float contentHeight = Mathf.Max(visibleHeight, m_Hierarchy.GetContentSize().y); m_State.hierarchyState.scrollPos.y = GUI.VerticalScrollbar(verticalScrollBarPosition, m_State.hierarchyState.scrollPos.y, visibleHeight, 0f, contentHeight); if (m_DopeSheet.spritePreviewLoading == true) Repaint(); } private void CurveEditorOnGUI(Rect position) { if (Event.current.type == EventType.Repaint) { m_CurveEditor.rect = position; m_CurveEditor.SetTickMarkerRanges(); } Rect noSlidersRect = new Rect(position.xMin, position.yMin, position.width - kSliderThickness, position.height - kSliderThickness); m_CurveEditor.vSlider = m_State.showCurveEditor; m_CurveEditor.hSlider = m_State.showCurveEditor; // Sync animation curves in curve editor. Do it only once per frame. if (Event.current.type == EventType.Layout) UpdateCurveEditorData(); m_CurveEditor.BeginViewGUI(); if (!m_State.disabled) { GUI.Box(noSlidersRect, GUIContent.none, AnimationWindowStyles.curveEditorBackground); m_CurveEditor.GridGUI(); } EditorGUI.BeginChangeCheck(); m_CurveEditor.CurveGUI(); if (EditorGUI.EndChangeCheck()) { SaveChangedCurvesFromCurveEditor(); } m_CurveEditor.EndViewGUI(); } private void TimeRulerOnGUI(Rect timeRulerRect) { Rect timeRulerRectNoScrollbar = new Rect(timeRulerRect.xMin, timeRulerRect.yMin, timeRulerRect.width - kSliderThickness, timeRulerRect.height); Rect timeRulerBackgroundRect = timeRulerRectNoScrollbar; GUI.Box(timeRulerBackgroundRect, GUIContent.none, AnimationWindowStyles.timeRulerBackground); if (!m_State.disabled) { RenderInRangeOverlay(timeRulerRectNoScrollbar); RenderSelectionOverlay(timeRulerRectNoScrollbar); } m_State.timeArea.TimeRuler(timeRulerRectNoScrollbar, m_State.frameRate, true, false, 1f, m_State.timeFormat); if (!m_State.disabled) RenderOutOfRangeOverlay(timeRulerRectNoScrollbar); } private GenericMenu GenerateOptionsMenu() { GenericMenu menu = new GenericMenu(); menu.AddItem(EditorGUIUtility.TextContent("Seconds"), m_State.timeFormat == TimeArea.TimeFormat.TimeFrame, () => m_State.timeFormat = TimeArea.TimeFormat.TimeFrame); menu.AddItem(EditorGUIUtility.TextContent("Frames"), m_State.timeFormat == TimeArea.TimeFormat.Frame, () => m_State.timeFormat = TimeArea.TimeFormat.Frame); menu.AddSeparator(""); menu.AddItem(EditorGUIUtility.TextContent("Ripple"), m_State.rippleTime, () => m_State.rippleTime = !m_State.rippleTime); menu.AddSeparator(""); menu.AddItem(EditorGUIUtility.TextContent("Show Sample Rate"), m_State.showFrameRate, () => m_State.showFrameRate = !m_State.showFrameRate); bool isAnimatable = selection != null && selection.animationIsEditable; GenericMenu.MenuFunction2 nullMenuFunction2 = null; for (int i = 0; i < kAvailableFrameRates.Length; ++i) { FrameRateMenuEntry entry = kAvailableFrameRates[i]; bool isActive = m_State.clipFrameRate.Equals(entry.value); menu.AddItem(entry.content, isActive, isAnimatable ? SetFrameRate : nullMenuFunction2, entry.value); } menu.AddSeparator(""); menu.AddItem(EditorGUIUtility.TextContent("Show Read-only Properties"), m_State.showReadOnly, () => m_State.showReadOnly = !m_State.showReadOnly); return menu; } private void OptionsOnGUI(int controlID) { Rect layoutRect = new Rect(hierarchyWidth, 0f, contentWidth, layoutRowHeight); GUI.BeginGroup(layoutRect); Vector2 optionsSize = EditorStyles.toolbarButtonRight.CalcSize(AnimationWindowStyles.optionsContent); Rect optionsRect = new Rect(layoutRect.width - kSliderThickness, 0f, optionsSize.x, optionsSize.y); GUI.Box(optionsRect, GUIContent.none, AnimationWindowStyles.animPlayToolBar); if (EditorGUI.DropdownButton(controlID, optionsRect, AnimationWindowStyles.optionsContent, AnimationWindowStyles.optionsButton)) { var menu = GenerateOptionsMenu(); menu.ShowAsContext(); } GUI.EndGroup(); } internal void SetFrameRate(object frameRate) { m_State.clipFrameRate = (float)frameRate; UpdateSelectedKeysToCurveEditor(); } private void FilterBySelectionButtonOnGUI() { Color backupColor = GUI.color; if (m_State.filterBySelection) { Color selectionColor = filterBySelectionColor; selectionColor.a *= GUI.color.a; GUI.color = selectionColor; } EditorGUI.BeginChangeCheck(); bool filterBySelection = GUILayout.Toggle(m_State.filterBySelection, AnimationWindowStyles.filterBySelectionContent, EditorStyles.toolbarButton); if (EditorGUI.EndChangeCheck()) { m_State.filterBySelection = filterBySelection; } GUI.color = backupColor; } private void AddEventButtonOnGUI() { using (new EditorGUI.DisabledScope(!selection.animationIsEditable)) { if (GUILayout.Button(AnimationWindowStyles.addEventContent, AnimationWindowStyles.animClipToolbarButton)) m_Events.AddEvent(m_State.currentTime, selection.rootGameObject, selection.animationClip); } } private void AddKeyframeButtonOnGUI() { bool canAddKey = selection.animationIsEditable && m_State.filteredCurves.Count != 0; using (new EditorGUI.DisabledScope(!canAddKey)) { if (GUILayout.Button(AnimationWindowStyles.addKeyframeContent, AnimationWindowStyles.animClipToolbarButton)) { SaveCurveEditorKeySelection(); var keyTime = AnimationKeyTime.Time(m_State.currentTime, m_State.frameRate); AnimationWindowUtility.AddSelectedKeyframes(m_State, keyTime); UpdateSelectedKeysToCurveEditor(); // data is scheduled for an update, bail out now to avoid using out of date data. EditorGUIUtility.ExitGUI(); } } } private void PlayControlsOnGUI() { using (new EditorGUI.DisabledScope(!m_State.canPreview)) { PreviewButtonOnGUI(); } using (new EditorGUI.DisabledScope(!m_State.canRecord)) { RecordButtonOnGUI(); } if (GUILayout.Button(AnimationWindowStyles.firstKeyContent, EditorStyles.toolbarButton)) { state.GoToFirstKeyframe(); // Stop text editing. User may be editing frame navigation ui which will not update until we exit text editing. EditorGUI.EndEditingActiveTextField(); } if (GUILayout.Button(AnimationWindowStyles.prevKeyContent, EditorStyles.toolbarButton)) { state.GoToPreviousKeyframe(); // Stop text editing. User may be editing frame navigation ui which will not update until we exit text editing. EditorGUI.EndEditingActiveTextField(); } using (new EditorGUI.DisabledScope(!m_State.canPlay)) { PlayButtonOnGUI(); } if (GUILayout.Button(AnimationWindowStyles.nextKeyContent, EditorStyles.toolbarButton)) { state.GoToNextKeyframe(); // Stop text editing. User may be editing frame navigation ui which will not update until we exit text editing. EditorGUI.EndEditingActiveTextField(); } if (GUILayout.Button(AnimationWindowStyles.lastKeyContent, EditorStyles.toolbarButton)) { state.GoToLastKeyframe(); // Stop text editing. User may be editing frame navigation ui which will not update until we exit text editing. EditorGUI.EndEditingActiveTextField(); } GUILayout.FlexibleSpace(); EditorGUI.BeginChangeCheck(); int newFrame = EditorGUILayout.DelayedIntField(m_State.currentFrame, EditorStyles.toolbarTextField, GUILayout.Width(kIntFieldWidth)); if (EditorGUI.EndChangeCheck()) { state.currentFrame = newFrame; } } private void LinkOptionsOnGUI() { if (m_State.linkedWithSequencer) { if (GUILayout.Toggle(true, AnimationWindowStyles.sequencerLinkContent, EditorStyles.toolbarButton) == false) { m_State.linkedWithSequencer = false; m_State.selection = null; m_State.overrideControlInterface = null; // Layout has changed, bail out now. EditorGUIUtility.ExitGUI(); } } } static void ExecuteShortcut(ShortcutArguments args, Action exp) { var animationWindow = (AnimationWindow)args.context; var animEditor = animationWindow.animEditor; if (EditorWindow.focusedWindow != animationWindow) return; if (animEditor.stateDisabled || animEditor.state.animatorIsOptimized) return; exp(animEditor); animEditor.Repaint(); } static void ExecuteShortcut(ShortcutArguments args, Action exp) { ExecuteShortcut(args, animEditor => exp(animEditor.state)); } [FormerlyPrefKeyAs("Animation/Show Curves", "c")] [Shortcut("Animation/Show Curves", typeof(AnimationWindow), KeyCode.C)] static void ShowCurves(ShortcutArguments args) { ExecuteShortcut(args, animEditor => { animEditor.SwitchBetweenCurvesAndDopesheet(); }); } [FormerlyPrefKeyAs("Animation/Play Animation", " ")] [Shortcut("Animation/Play Animation", typeof(AnimationWindow), KeyCode.Space)] static void TogglePlayAnimation(ShortcutArguments args) { ExecuteShortcut(args, state => { state.playing = !state.playing; }); } [FormerlyPrefKeyAs("Animation/Next Frame", ".")] [Shortcut("Animation/Next Frame", typeof(AnimationWindow), KeyCode.Period)] static void NextFrame(ShortcutArguments args) { ExecuteShortcut(args, state => state.GoToNextFrame()); } [FormerlyPrefKeyAs("Animation/Previous Frame", ",")] [Shortcut("Animation/Previous Frame", typeof(AnimationWindow), KeyCode.Comma)] static void PreviousFrame(ShortcutArguments args) { ExecuteShortcut(args, state => state.GoToPreviousFrame()); } [FormerlyPrefKeyAs("Animation/Previous Keyframe", "&,")] [Shortcut("Animation/Previous Keyframe", typeof(AnimationWindow), KeyCode.Comma, ShortcutModifiers.Alt)] static void PreviousKeyFrame(ShortcutArguments args) { ExecuteShortcut(args, state => state.GoToPreviousKeyframe()); } [FormerlyPrefKeyAs("Animation/Next Keyframe", "&.")] [Shortcut("Animation/Next Keyframe", typeof(AnimationWindow), KeyCode.Period, ShortcutModifiers.Alt)] static void NextKeyFrame(ShortcutArguments args) { ExecuteShortcut(args, state => state.GoToNextKeyframe()); } [FormerlyPrefKeyAs("Animation/First Keyframe", "#,")] [Shortcut("Animation/First Keyframe", typeof(AnimationWindow), KeyCode.Comma, ShortcutModifiers.Shift)] static void FirstKeyFrame(ShortcutArguments args) { ExecuteShortcut(args, state => state.GoToFirstKeyframe()); } [FormerlyPrefKeyAs("Animation/Last Keyframe", "#.")] [Shortcut("Animation/Last Keyframe", typeof(AnimationWindow), KeyCode.Period, ShortcutModifiers.Shift)] static void LastKeyFrame(ShortcutArguments args) { ExecuteShortcut(args, state => state.GoToLastKeyframe()); } [FormerlyPrefKeyAs("Animation/Key Selected", "k")] [Shortcut("Animation/Key Selected", null, KeyCode.K)] static void KeySelected(ShortcutArguments args) { AnimationWindow animationWindow = AnimationWindow.GetAllAnimationWindows().Find(aw => (aw.state.previewing || aw == EditorWindow.focusedWindow)); if (animationWindow == null) return; var animEditor = animationWindow.animEditor; animEditor.SaveCurveEditorKeySelection(); AnimationWindowUtility.AddSelectedKeyframes(animEditor.m_State, AnimationKeyTime.Frame(animEditor.state.currentFrame, animEditor.state.frameRate)); animEditor.state.ClearCandidates(); animEditor.UpdateSelectedKeysToCurveEditor(); animEditor.Repaint(); } [FormerlyPrefKeyAs("Animation/Key Modified", "#k")] [Shortcut("Animation/Key Modified", null, KeyCode.K, ShortcutModifiers.Shift)] static void KeyModified(ShortcutArguments args) { AnimationWindow animationWindow = AnimationWindow.GetAllAnimationWindows().Find(aw => (aw.state.previewing || aw == EditorWindow.focusedWindow)); if (animationWindow == null) return; var animEditor = animationWindow.animEditor; animEditor.SaveCurveEditorKeySelection(); animEditor.state.ProcessCandidates(); animEditor.UpdateSelectedKeysToCurveEditor(); animEditor.Repaint(); } [Shortcut("Animation/Toggle Ripple", typeof(AnimationWindow), KeyCode.Alpha2, ShortcutModifiers.Shift)] static void ToggleRipple(ShortcutArguments args) { ExecuteShortcut(args, animEditor => { animEditor.state.rippleTime = !animEditor.state.rippleTime; }); } [ClutchShortcut("Animation/Ripple (Clutch)", typeof(AnimationWindow), KeyCode.Alpha2)] static void ClutchRipple(ShortcutArguments args) { ExecuteShortcut(args, animEditor => { animEditor.state.rippleTimeClutch = args.stage == ShortcutStage.Begin; }); } [Shortcut("Animation/Frame All", typeof(AnimationWindow), KeyCode.A)] static void FrameAll(ShortcutArguments args) { ExecuteShortcut(args, animEditor => { animEditor.triggerFraming = true; }); } private void PlayButtonOnGUI() { EditorGUI.BeginChangeCheck(); bool playbackEnabled = GUILayout.Toggle(m_State.playing, AnimationWindowStyles.playContent, EditorStyles.toolbarButton); if (EditorGUI.EndChangeCheck()) { m_State.playing = playbackEnabled; // Stop text editing. User may be editing frame navigation ui which will not update until we exit text editing. EditorGUI.EndEditingActiveTextField(); } } private void PreviewButtonOnGUI() { EditorGUI.BeginChangeCheck(); bool recordingEnabled = GUILayout.Toggle(m_State.previewing, AnimationWindowStyles.previewContent, EditorStyles.toolbarButton); if (EditorGUI.EndChangeCheck()) { m_State.previewing = recordingEnabled; } } private void RecordButtonOnGUI() { EditorGUI.BeginChangeCheck(); Color backupColor = GUI.color; if (m_State.recording) { Color recordedColor = AnimationMode.recordedPropertyColor; recordedColor.a *= GUI.color.a; GUI.color = recordedColor; } bool recordingEnabled = GUILayout.Toggle(m_State.recording, AnimationWindowStyles.recordContent, EditorStyles.toolbarButton); if (EditorGUI.EndChangeCheck()) { m_State.recording = recordingEnabled; if (!recordingEnabled) { // Force refresh in inspector as stopping recording does not invalidate any data. InspectorWindow.RepaintAllInspectors(); } } GUI.color = backupColor; } private void SwitchBetweenCurvesAndDopesheet() { if (!m_State.showCurveEditor) { SwitchToCurveEditor(); } else { SwitchToDopeSheetEditor(); } } internal void SwitchToCurveEditor() { m_State.showCurveEditor = true; UpdateSelectedKeysToCurveEditor(); AnimationWindowUtility.SyncTimeArea(m_DopeSheet, m_CurveEditor); m_State.timeArea = m_CurveEditor; } internal void SwitchToDopeSheetEditor() { m_State.showCurveEditor = false; UpdateSelectedKeysFromCurveEditor(); AnimationWindowUtility.SyncTimeArea(m_CurveEditor, m_DopeSheet); m_State.timeArea = m_DopeSheet; } private void RenderSelectionOverlay(Rect rect) { if (m_State.showCurveEditor && !m_CurveEditor.hasSelection) return; if (!m_State.showCurveEditor && m_State.selectedKeys.Count == 0) return; const int kOverlayMinWidth = 14; Bounds bounds = m_State.showCurveEditor ? m_CurveEditor.selectionBounds : m_State.selectionBounds; float startPixel = m_State.TimeToPixel(bounds.min.x) + rect.xMin; float endPixel = m_State.TimeToPixel(bounds.max.x) + rect.xMin; if ((endPixel - startPixel) < kOverlayMinWidth) { float centerPixel = (startPixel + endPixel) * 0.5f; startPixel = centerPixel - kOverlayMinWidth * 0.5f; endPixel = centerPixel + kOverlayMinWidth * 0.5f; } AnimationWindowUtility.DrawSelectionOverlay(rect, selectionRangeColor, startPixel, endPixel); } private void RenderInRangeOverlay(Rect rect) { Color color = inRangeColor; if (m_State.recording) color *= AnimationMode.recordedPropertyColor; else if (m_State.previewing) color *= AnimationMode.animatedPropertyColor; else color = Color.clear; Vector2 timeRange = m_State.timeRange; AnimationWindowUtility.DrawInRangeOverlay(rect, color, m_State.TimeToPixel(timeRange.x) + rect.xMin, m_State.TimeToPixel(timeRange.y) + rect.xMin); } private void RenderOutOfRangeOverlay(Rect rect) { Color color = outOfRangeColor; if (m_State.recording) color *= AnimationMode.recordedPropertyColor; else if (m_State.previewing) color *= AnimationMode.animatedPropertyColor; Vector2 timeRange = m_State.timeRange; AnimationWindowUtility.DrawOutOfRangeOverlay(rect, color, m_State.TimeToPixel(timeRange.x) + rect.xMin, m_State.TimeToPixel(timeRange.y) + rect.xMin); } private void SynchronizeLayout() { m_HorizontalSplitter.realSizes[1] = (int)Mathf.Max(Mathf.Min(m_Position.width - m_HorizontalSplitter.realSizes[0], m_HorizontalSplitter.realSizes[1]), 0); // Synchronize frame rate if (selection.animationClip != null) { m_State.frameRate = selection.animationClip.frameRate; } else { m_State.frameRate = AnimationWindowState.kDefaultFrameRate; } } struct ChangedCurvesPerClip { public List bindings; public List curves; } // Curve editor changes curves, but we are in charge of saving them into the clip private void SaveChangedCurvesFromCurveEditor() { m_State.SaveKeySelection(AnimationWindowState.kEditCurveUndoLabel); var curvesToUpdate = new Dictionary(); var changedCurves = new ChangedCurvesPerClip(); for (int i = 0; i < m_CurveEditor.animationCurves.Length; ++i) { CurveWrapper curveWrapper = m_CurveEditor.animationCurves[i]; if (curveWrapper.changed) { if (!curveWrapper.animationIsEditable) Debug.LogError("Curve is not editable and shouldn't be saved."); if (curveWrapper.animationClip != null) { if (curvesToUpdate.TryGetValue(curveWrapper.animationClip, out changedCurves)) { changedCurves.bindings.Add(curveWrapper.binding); changedCurves.curves.Add(curveWrapper.curve.length > 0 ? curveWrapper.curve : null); } else { changedCurves.bindings = new List(); changedCurves.curves = new List(); changedCurves.bindings.Add(curveWrapper.binding); changedCurves.curves.Add(curveWrapper.curve.length > 0 ? curveWrapper.curve : null); curvesToUpdate.Add(curveWrapper.animationClip, changedCurves); } } curveWrapper.changed = false; } } if (curvesToUpdate.Count > 0) { foreach (var kvp in curvesToUpdate) { Undo.RegisterCompleteObjectUndo(kvp.Key, AnimationWindowState.kEditCurveUndoLabel); AnimationWindowUtility.SaveCurves(kvp.Key, kvp.Value.bindings, kvp.Value.curves); } m_State.ResampleAnimation(); } } // We sync keyframe selection from curve editor to AnimationWindowState private void UpdateSelectedKeysFromCurveEditor() { m_State.ClearKeySelections(); foreach (CurveSelection curveSelection in m_CurveEditor.selectedCurves) { AnimationWindowKeyframe keyFrame = AnimationWindowUtility.CurveSelectionToAnimationWindowKeyframe(curveSelection, m_State.filteredCurves); if (keyFrame != null) m_State.SelectKey(keyFrame); } } // We sync keyframe selection from AnimationWindowState to curve editor private void UpdateSelectedKeysToCurveEditor() { UpdateCurveEditorData(); m_CurveEditor.ClearSelection(); m_CurveEditor.BeginRangeSelection(); foreach (AnimationWindowKeyframe keyframe in m_State.selectedKeys) { CurveSelection curveSelection = AnimationWindowUtility.AnimationWindowKeyframeToCurveSelection(keyframe, m_CurveEditor); if (curveSelection != null) m_CurveEditor.AddSelection(curveSelection); } m_CurveEditor.EndRangeSelection(); } private void SaveCurveEditorKeySelection() { // Synchronize current selection in curve editor and save selection snapshot in undo redo. if (m_State.showCurveEditor) UpdateSelectedKeysFromCurveEditor(); else UpdateSelectedKeysToCurveEditor(); m_CurveEditor.SaveKeySelection(AnimationWindowState.kEditCurveUndoLabel); } public void BeginKeyModification() { SaveCurveEditorKeySelection(); m_State.SaveKeySelection(AnimationWindowState.kEditCurveUndoLabel); m_State.ClearKeySelections(); } public void EndKeyModification() { UpdateSelectedKeysToCurveEditor(); } void HandleMainAreaCopyPaste(int controlID) { var evt = Event.current; var type = evt.GetTypeForControl(controlID); if (type != EventType.ValidateCommand && type != EventType.ExecuteCommand) return; if (evt.commandName == EventCommandNames.Copy) { // If events timeline has selected events right now then bail out; copying of // these will get processed later by AnimationEventTimeLine. if (m_Events.HasSelectedEvents) return; if (type == EventType.ExecuteCommand) { if (m_State.showCurveEditor) UpdateSelectedKeysFromCurveEditor(); m_State.CopyKeys(); } evt.Use(); } else if (evt.commandName == EventCommandNames.Paste) { if (type == EventType.ExecuteCommand) { // If clipboard contains events right now then paste those. if (AnimationWindowEventsClipboard.CanPaste()) { m_Events.PasteEvents(m_State.activeRootGameObject, m_State.activeAnimationClip, m_State.currentTime); } else { SaveCurveEditorKeySelection(); m_State.PasteKeys(); UpdateSelectedKeysToCurveEditor(); } // data is scheduled for an update, bail out now to avoid using out of date data. EditorGUIUtility.ExitGUI(); } evt.Use(); } } internal void UpdateCurveEditorData() { m_CurveEditor.animationCurves = m_State.activeCurveWrappers; } public void Repaint() { if (m_OwnerWindow != null) m_OwnerWindow.Repaint(); } // Called just-in-time by OnGUI private void Initialize() { AnimationWindowStyles.Initialize(); InitializeHierarchy(); m_CurveEditor.state = m_State; // The rect here is only for initialization and will be overriden at layout m_HorizontalSplitter.realSizes[0] = kHierarchyMinWidth; m_HorizontalSplitter.realSizes[1] = (int)Mathf.Max(m_Position.width - kHierarchyMinWidth, kHierarchyMinWidth); m_DopeSheet.rect = new Rect(0, 0, contentWidth, 100); m_Initialized = true; } // Called once during initialization of m_State private void InitializeClipSelection() { m_ClipPopup = new AnimationWindowClipPopup(); } // Called once during initialization of m_State private void InitializeHierarchy() { // The rect here is only for initialization and will be overriden at layout m_Hierarchy = new AnimationWindowHierarchy(m_State, m_OwnerWindow, new Rect(0, 0, hierarchyWidth, 100)); } // Called once during initialization of m_State private void InitializeDopeSheet() { m_DopeSheet = new DopeSheetEditor(m_OwnerWindow); m_DopeSheet.SetTickMarkerRanges(); m_DopeSheet.hSlider = true; m_DopeSheet.shownArea = new Rect(1, 1, 1, 1); // The rect here is only for initialization and will be overriden at layout m_DopeSheet.rect = new Rect(0, 0, contentWidth, 100); m_DopeSheet.hTicks.SetTickModulosForFrameRate(m_State.frameRate); } // Called once during initialization of m_State private void InitializeEvents() { m_Events = new AnimationEventTimeLine(m_OwnerWindow); } // Called once during initialization of m_State private void InitializeCurveEditor() { // The rect here is only for initialization and will be overriden at layout m_CurveEditor = new CurveEditor(new Rect(0, 0, contentWidth, 100), new CurveWrapper[0], false); CurveEditorSettings settings = new CurveEditorSettings(); settings.hTickStyle.distMin = 30; // min distance between vertical lines before they disappear completely settings.hTickStyle.distFull = 80; // distance between vertical lines where they gain full strength settings.hTickStyle.distLabel = 0; // min distance between vertical lines labels if (EditorGUIUtility.isProSkin) { settings.vTickStyle.tickColor.color = new Color(1, 1, 1, settings.vTickStyle.tickColor.color.a); // color and opacity of horizontal lines settings.vTickStyle.labelColor.color = new Color(1, 1, 1, settings.vTickStyle.labelColor.color.a); // color and opacity of horizontal line labels } settings.vTickStyle.distMin = 15; // min distance between horizontal lines before they disappear completely settings.vTickStyle.distFull = 40; // distance between horizontal lines where they gain full strength settings.vTickStyle.distLabel = 30; // min distance between horizontal lines labels settings.vTickStyle.stubs = true; settings.hRangeMin = 0; settings.hRangeLocked = false; settings.vRangeLocked = false; settings.hSlider = true; settings.vSlider = true; settings.allowDeleteLastKeyInCurve = true; settings.rectangleToolFlags = CurveEditorSettings.RectangleToolFlags.FullRectangleTool; settings.undoRedoSelection = true; settings.flushCurveCache = false; // Curve Wrappers are cached in AnimationWindowState. m_CurveEditor.shownArea = new Rect(1, 1, 1, 1); m_CurveEditor.settings = settings; m_CurveEditor.state = m_State; } // Called once during initialization of m_State private void InitializeHorizontalSplitter() { m_HorizontalSplitter = SplitterState.FromRelative(new float[] { kHierarchyMinWidth, kHierarchyMinWidth * 3 }, new float[] { kHierarchyMinWidth, kHierarchyMinWidth }, null); m_HorizontalSplitter.realSizes[0] = kHierarchyMinWidth; m_HorizontalSplitter.realSizes[1] = kHierarchyMinWidth; } // Called once during initialization of m_State private void InitializeOverlay() { m_Overlay = new AnimEditorOverlay(); } // Called during initialization, even when m_State already exists private void InitializeNonserializedValues() { // Since CurveEditor doesn't know about AnimationWindowState, a delegate allows it to reflect frame rate changes m_State.onFrameRateChange += delegate(float newFrameRate) { m_CurveEditor.invSnap = newFrameRate; m_CurveEditor.hTicks.SetTickModulosForFrameRate(newFrameRate); }; m_State.onStartLiveEdit += OnStartLiveEdit; m_State.onEndLiveEdit += OnEndLiveEdit; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimEditorOverlay.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using UnityEditorInternal; using Object = UnityEngine.Object; namespace UnityEditorInternal { [System.Serializable] internal class AnimEditorOverlay { [SerializeReference] public AnimationWindowState state; private TimeCursorManipulator m_PlayHeadCursor; private Rect m_Rect; private Rect m_ContentRect; public Rect rect { get { return m_Rect; } } public Rect contentRect { get { return m_ContentRect; } } public void Initialize() { if (m_PlayHeadCursor == null) { m_PlayHeadCursor = new TimeCursorManipulator(AnimationWindowStyles.playHead); m_PlayHeadCursor.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (evt.mousePosition.y <= (m_Rect.yMin + 20)) return OnStartDragPlayHead(evt); return false; }; m_PlayHeadCursor.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { return OnDragPlayHead(evt); }; m_PlayHeadCursor.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { return OnEndDragPlayHead(evt); }; } } public void OnGUI(Rect rect, Rect contentRect) { if (Event.current.type != EventType.Repaint) return; m_Rect = rect; m_ContentRect = contentRect; Initialize(); m_PlayHeadCursor.OnGUI(m_Rect, m_Rect.xMin + TimeToPixel(state.currentTime)); } public void HandleEvents() { Initialize(); m_PlayHeadCursor.HandleEvents(); } private bool OnStartDragPlayHead(Event evt) { state.playing = false; state.controlInterface.time = MousePositionToTime(evt); return true; } private bool OnDragPlayHead(Event evt) { state.controlInterface.time = MousePositionToTime(evt); return true; } private bool OnEndDragPlayHead(Event evt) { return true; } public float MousePositionToTime(Event evt) { float width = m_ContentRect.width; float time = Mathf.Max(((evt.mousePosition.x / width) * state.visibleTimeSpan + state.minVisibleTime), 0); time = state.SnapToFrame(time, AnimationWindowState.SnapMode.SnapToFrame); return time; } public float MousePositionToValue(Event evt) { float height = m_ContentRect.height; float valuePixel = height - evt.mousePosition.y; TimeArea timeArea = state.timeArea; float pixelPerValue = timeArea.m_Scale.y * -1f; float zeroValuePixel = timeArea.shownArea.yMin * pixelPerValue * -1f; float value = (valuePixel - zeroValuePixel) / pixelPerValue; return value; } public float TimeToPixel(float time) { return state.TimeToPixel(time); } public float ValueToPixel(float value) { TimeArea timeArea = state.timeArea; float pixelPerValue = timeArea.m_Scale.y * -1f; float zeroValuePixel = timeArea.shownArea.yMin * pixelPerValue * -1f; float pixelValue = value * pixelPerValue + zeroValuePixel; return pixelValue; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationClipSelectionItem.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using Object = UnityEngine.Object; namespace UnityEditorInternal { [Serializable] internal class AnimationClipSelectionItem : AnimationWindowSelectionItem { public static AnimationClipSelectionItem Create(AnimationClip animationClip, Object sourceObject = null) { var selectionItem = new AnimationClipSelectionItem { gameObject = sourceObject as GameObject, scriptableObject = sourceObject as ScriptableObject, animationClip = animationClip, id = 0 }; return selectionItem; } public override bool canPreview { get { return false; } } public override bool canRecord { get { return false; } } public override bool canChangeAnimationClip { get { return false; } } public override bool canSyncSceneSelection { get { return false; } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationContextualPropertyMenu.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Collections.Generic; using UnityEngine; using UnityEditor; namespace UnityEditorInternal { class AnimationPropertyContextualMenu { public static AnimationPropertyContextualMenu Instance = new AnimationPropertyContextualMenu(); IAnimationContextualResponder m_Responder; private static GUIContent addKeyContent = EditorGUIUtility.TrTextContent("Add Key"); private static GUIContent updateKeyContent = EditorGUIUtility.TrTextContent("Update Key"); private static GUIContent removeKeyContent = EditorGUIUtility.TrTextContent("Remove Key"); private static GUIContent removeCurveContent = EditorGUIUtility.TrTextContent("Remove All Keys"); private static GUIContent goToPreviousKeyContent = EditorGUIUtility.TrTextContent("Go to Previous Key"); private static GUIContent goToNextKeyContent = EditorGUIUtility.TrTextContent("Go to Next Key"); private static GUIContent addCandidatesContent = EditorGUIUtility.TrTextContent("Key All Modified"); private static GUIContent addAnimatedContent = EditorGUIUtility.TrTextContent("Key All Animated"); public AnimationPropertyContextualMenu() { EditorApplication.contextualPropertyMenu += OnPropertyContextMenu; MaterialEditor.contextualPropertyMenu += OnPropertyContextMenu; } public void SetResponder(IAnimationContextualResponder responder) { m_Responder = responder; } public bool IsResponder(IAnimationContextualResponder responder) { return responder == m_Responder; } void OnPropertyContextMenu(GenericMenu menu, SerializedProperty property) { if (m_Responder == null) return; PropertyModification[] modifications = AnimationWindowUtility.SerializedPropertyToPropertyModifications(property); bool isPropertyAnimatable = m_Responder.IsAnimatable(modifications); if (isPropertyAnimatable) { var targetObject = property.serializedObject.targetObject; if (m_Responder.IsEditable(targetObject)) OnPropertyContextMenu(menu, modifications); else OnDisabledPropertyContextMenu(menu); } } void OnPropertyContextMenu(GenericMenu menu, MaterialProperty property, Renderer[] renderers) { if (m_Responder == null) return; if (property.targets == null || property.targets.Length == 0) return; if (renderers == null || renderers.Length == 0) return; var modifications = new List(); foreach (Renderer renderer in renderers) { modifications.AddRange(MaterialAnimationUtility.MaterialPropertyToPropertyModifications(property, renderer)); } if (m_Responder.IsEditable(renderers[0])) OnPropertyContextMenu(menu, modifications.ToArray()); else OnDisabledPropertyContextMenu(menu); } void OnPropertyContextMenu(GenericMenu menu, PropertyModification[] modifications) { bool hasKey = m_Responder.KeyExists(modifications); bool hasCandidate = m_Responder.CandidateExists(modifications); bool hasCurve = (hasKey || m_Responder.CurveExists(modifications)); bool hasAnyCandidate = m_Responder.HasAnyCandidates(); bool hasAnyCurve = m_Responder.HasAnyCurves(); menu.AddItem(((hasKey && hasCandidate) ? updateKeyContent : addKeyContent), false, () => { m_Responder.AddKey(modifications); }); if (hasKey) { menu.AddItem(removeKeyContent, false, () => { m_Responder.RemoveKey(modifications); }); } else { menu.AddDisabledItem(removeKeyContent); } if (hasCurve) { menu.AddItem(removeCurveContent, false, () => { m_Responder.RemoveCurve(modifications); }); } else { menu.AddDisabledItem(removeCurveContent); } menu.AddSeparator(string.Empty); if (hasAnyCandidate) { menu.AddItem(addCandidatesContent, false, () => { m_Responder.AddCandidateKeys(); }); } else { menu.AddDisabledItem(addCandidatesContent); } if (hasAnyCurve) { menu.AddItem(addAnimatedContent, false, () => { m_Responder.AddAnimatedKeys(); }); } else { menu.AddDisabledItem(addAnimatedContent); } menu.AddSeparator(string.Empty); if (hasCurve) { menu.AddItem(goToPreviousKeyContent, false, () => { m_Responder.GoToPreviousKeyframe(modifications); }); menu.AddItem(goToNextKeyContent, false, () => { m_Responder.GoToNextKeyframe(modifications); }); } else { menu.AddDisabledItem(goToPreviousKeyContent); menu.AddDisabledItem(goToNextKeyContent); } } void OnDisabledPropertyContextMenu(GenericMenu menu) { menu.AddDisabledItem(addKeyContent); menu.AddDisabledItem(removeKeyContent); menu.AddDisabledItem(removeCurveContent); menu.AddSeparator(string.Empty); menu.AddDisabledItem(addCandidatesContent); menu.AddDisabledItem(addAnimatedContent); menu.AddSeparator(string.Empty); menu.AddDisabledItem(goToPreviousKeyContent); menu.AddDisabledItem(goToNextKeyContent); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationKeyTime.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; namespace UnityEditorInternal { [System.Serializable] struct AnimationKeyTime { [SerializeField] private float m_FrameRate; [SerializeField] private int m_Frame; [SerializeField] private float m_Time; public float time { get { return m_Time; } } public int frame { get { return m_Frame; } } public float frameRate { get { return m_FrameRate; } } /// /// A frame has a range of time. This is the beginning of the frame. /// public float frameFloor { get { return ((float)frame - 0.5f) / frameRate; } } /// /// A frame has a range of time. This is the end of the frame. /// public float frameCeiling { get { return ((float)frame + 0.5f) / frameRate; } } public float timeRound { get { return (float)frame / frameRate; } } public static AnimationKeyTime Time(float time, float frameRate) { AnimationKeyTime key = new AnimationKeyTime(); key.m_Time = Mathf.Max(time, 0f); key.m_FrameRate = frameRate; key.m_Frame = UnityEngine.Mathf.RoundToInt(key.m_Time * frameRate); return key; } public static AnimationKeyTime Frame(int frame, float frameRate) { AnimationKeyTime key = new AnimationKeyTime(); key.m_Frame = (frame < 0) ? 0 : frame; key.m_Time = key.m_Frame / frameRate; key.m_FrameRate = frameRate; return key; } // Check if a time in seconds overlaps with the frame public bool ContainsTime(float time) { return time >= frameFloor && time < frameCeiling; } public bool Equals(AnimationKeyTime key) { return m_Frame == key.m_Frame && m_FrameRate == key.m_FrameRate && Mathf.Approximately(m_Time, key.m_Time); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationRecording.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using UnityEngine; using UnityEditor; namespace UnityEditorInternal { internal class AnimationRecording { const string kLocalPosition = "m_LocalPosition"; const string kLocalRotation = "m_LocalRotation"; const string kLocalScale = "m_LocalScale"; const string kLocalEulerAnglesHint = "m_LocalEulerAnglesHint"; static bool HasAnyRecordableModifications(GameObject root, UndoPropertyModification[] modifications) { for (int i = 0; i < modifications.Length; i++) { EditorCurveBinding tempBinding; if (AnimationUtility.PropertyModificationToEditorCurveBinding(modifications[i].previousValue, root, out tempBinding) != null) return true; } return false; } static private UndoPropertyModification[] FilterModifications(IAnimationRecordingState state, ref UndoPropertyModification[] modifications) { GameObject rootGameObject = state.activeRootGameObject; List discardedModifications = new List(); List outModifications = new List(); for (int i = 0; i < modifications.Length; ++i) { UndoPropertyModification modification = modifications[i]; PropertyModification prop = modification.previousValue; if (state.DiscardModification(prop)) { discardedModifications.Add(modification); continue; } var binding = new EditorCurveBinding(); if (AnimationUtility.PropertyModificationToEditorCurveBinding(prop, rootGameObject, out binding) != null) outModifications.Add(modification); else discardedModifications.Add(modification); } if (discardedModifications.Count > 0) modifications = outModifications.ToArray(); return discardedModifications.ToArray(); } internal class RotationModification { public UndoPropertyModification x; public UndoPropertyModification y; public UndoPropertyModification z; public UndoPropertyModification w; public UndoPropertyModification lastQuatModification; public bool useEuler = false; public UndoPropertyModification eulerX; public UndoPropertyModification eulerY; public UndoPropertyModification eulerZ; } static private void CollectRotationModifications(IAnimationRecordingState state, ref UndoPropertyModification[] modifications, ref Dictionary rotationModifications) { List outModifs = new List(); foreach (var modification in modifications) { PropertyModification prop = modification.previousValue; if (!(prop.target is Transform)) { outModifs.Add(modification); continue; } EditorCurveBinding binding = new EditorCurveBinding(); AnimationUtility.PropertyModificationToEditorCurveBinding(prop, state.activeRootGameObject, out binding); if (binding.propertyName.StartsWith(kLocalRotation)) { RotationModification rotationModification; if (!rotationModifications.TryGetValue(prop.target, out rotationModification)) { rotationModification = new RotationModification(); rotationModifications[prop.target] = rotationModification; } if (binding.propertyName.EndsWith("x")) rotationModification.x = modification; else if (binding.propertyName.EndsWith("y")) rotationModification.y = modification; else if (binding.propertyName.EndsWith("z")) rotationModification.z = modification; else if (binding.propertyName.EndsWith("w")) rotationModification.w = modification; rotationModification.lastQuatModification = modification; } else if (prop.propertyPath.StartsWith(kLocalEulerAnglesHint)) { RotationModification rotationModification; if (!rotationModifications.TryGetValue(prop.target, out rotationModification)) { rotationModification = new RotationModification(); rotationModifications[prop.target] = rotationModification; } rotationModification.useEuler = true; if (prop.propertyPath.EndsWith("x")) rotationModification.eulerX = modification; else if (prop.propertyPath.EndsWith("y")) rotationModification.eulerY = modification; else if (prop.propertyPath.EndsWith("z")) rotationModification.eulerZ = modification; } else { outModifs.Add(modification); } } if (rotationModifications.Count > 0) { modifications = outModifs.ToArray(); } } static private void DiscardRotationModification(RotationModification rotationModification, ref List discardedModifications) { if (rotationModification.x.currentValue != null) discardedModifications.Add(rotationModification.x); if (rotationModification.y.currentValue != null) discardedModifications.Add(rotationModification.y); if (rotationModification.z.currentValue != null) discardedModifications.Add(rotationModification.z); if (rotationModification.w.currentValue != null) discardedModifications.Add(rotationModification.w); if (rotationModification.eulerX.currentValue != null) discardedModifications.Add(rotationModification.eulerX); if (rotationModification.eulerY.currentValue != null) discardedModifications.Add(rotationModification.eulerY); if (rotationModification.eulerZ.currentValue != null) discardedModifications.Add(rotationModification.eulerZ); } static private UndoPropertyModification[] FilterRotationModifications(IAnimationRecordingState state, ref Dictionary rotationModifications) { GameObject rootGameObject = state.activeRootGameObject; List itemsToRemove = new List(); List discardedModifications = new List(); foreach (KeyValuePair item in rotationModifications) { RotationModification m = item.Value; if (state.DiscardModification(m.lastQuatModification.currentValue)) { DiscardRotationModification(m, ref discardedModifications); itemsToRemove.Add(item.Key); continue; } EditorCurveBinding binding = new EditorCurveBinding(); Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(m.lastQuatModification.currentValue, rootGameObject, out binding); if (type == null) { DiscardRotationModification(m, ref discardedModifications); itemsToRemove.Add(item.Key); } } foreach (var key in itemsToRemove) { rotationModifications.Remove(key); } return discardedModifications.ToArray(); } static private void AddRotationPropertyModification(IAnimationRecordingState state, EditorCurveBinding baseBinding, UndoPropertyModification modification) { if (modification.previousValue == null) return; // case 817356. Reuse baseBinding as basis for rotation binding. // This is needed to register valid bindings for m_LocalEulerAnglesHint // that cannot be converted to EditorCurveBinding otherwise. EditorCurveBinding binding = baseBinding; binding.propertyName = modification.previousValue.propertyPath; state.AddPropertyModification(binding, modification.previousValue, modification.keepPrefabOverride); } static private void ProcessRotationModifications(IAnimationRecordingState state, ref Dictionary rotationModifications) { foreach (KeyValuePair item in rotationModifications) { RotationModification m = item.Value; Transform target = item.Key as Transform; if (target == null) continue; EditorCurveBinding binding = new EditorCurveBinding(); Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(m.lastQuatModification.currentValue, state.activeRootGameObject, out binding); if (type == null) continue; AddRotationPropertyModification(state, binding, m.x); AddRotationPropertyModification(state, binding, m.y); AddRotationPropertyModification(state, binding, m.z); AddRotationPropertyModification(state, binding, m.w); Quaternion previousValue = target.localRotation; Quaternion currentValue = target.localRotation; object x, y, z, w; if (ValueFromPropertyModification(m.x.previousValue, binding, out x)) previousValue.x = (float)x; if (ValueFromPropertyModification(m.y.previousValue, binding, out y)) previousValue.y = (float)y; if (ValueFromPropertyModification(m.z.previousValue, binding, out z)) previousValue.z = (float)z; if (ValueFromPropertyModification(m.w.previousValue, binding, out w)) previousValue.w = (float)w; if (ValueFromPropertyModification(m.x.currentValue, binding, out x)) currentValue.x = (float)x; if (ValueFromPropertyModification(m.y.currentValue, binding, out y)) currentValue.y = (float)y; if (ValueFromPropertyModification(m.z.currentValue, binding, out z)) currentValue.z = (float)z; if (ValueFromPropertyModification(m.w.currentValue, binding, out w)) currentValue.w = (float)w; // Favour euler hints if one or more exist on any axis. if (m.useEuler) { AddRotationPropertyModification(state, binding, m.eulerX); AddRotationPropertyModification(state, binding, m.eulerY); AddRotationPropertyModification(state, binding, m.eulerZ); Vector3 previousEulerAngles = target.GetLocalEulerAngles(RotationOrder.OrderZXY); Vector3 currentEulerAngles = previousEulerAngles; object eulerX, eulerY, eulerZ; if (ValueFromPropertyModification(m.eulerX.previousValue, binding, out eulerX)) previousEulerAngles.x = (float)eulerX; if (ValueFromPropertyModification(m.eulerY.previousValue, binding, out eulerY)) previousEulerAngles.y = (float)eulerY; if (ValueFromPropertyModification(m.eulerZ.previousValue, binding, out eulerZ)) previousEulerAngles.z = (float)eulerZ; if (ValueFromPropertyModification(m.eulerX.currentValue, binding, out eulerX)) currentEulerAngles.x = (float)eulerX; if (ValueFromPropertyModification(m.eulerY.currentValue, binding, out eulerY)) currentEulerAngles.y = (float)eulerY; if (ValueFromPropertyModification(m.eulerZ.currentValue, binding, out eulerZ)) currentEulerAngles.z = (float)eulerZ; // Fallback to quaternion euler values if euler hint and quaternion are out of sync. previousEulerAngles = AnimationUtility.GetClosestEuler(previousValue, previousEulerAngles, RotationOrder.OrderZXY); currentEulerAngles = AnimationUtility.GetClosestEuler(currentValue, currentEulerAngles, RotationOrder.OrderZXY); AddRotationKey(state, binding, type, previousEulerAngles, currentEulerAngles); } else { Vector3 eulerAngles = target.GetLocalEulerAngles(RotationOrder.OrderZXY); Vector3 previousEulerAngles = AnimationUtility.GetClosestEuler(previousValue, eulerAngles, RotationOrder.OrderZXY); Vector3 currentEulerAngles = AnimationUtility.GetClosestEuler(currentValue, eulerAngles, RotationOrder.OrderZXY); AddRotationKey(state, binding, type, previousEulerAngles, currentEulerAngles); } } } internal class Vector3Modification { public UndoPropertyModification x; public UndoPropertyModification y; public UndoPropertyModification z; public UndoPropertyModification last; } static private void CollectVector3Modifications(IAnimationRecordingState state, ref UndoPropertyModification[] modifications, ref Dictionary vector3Modifications, string propertyName) { List outModifs = new List(); foreach (var modification in modifications) { PropertyModification prop = modification.previousValue; if (!(prop.target is Transform)) { outModifs.Add(modification); continue; } EditorCurveBinding binding = new EditorCurveBinding(); AnimationUtility.PropertyModificationToEditorCurveBinding(prop, state.activeRootGameObject, out binding); if (binding.propertyName.StartsWith(propertyName)) { Vector3Modification vector3Modification; if (!vector3Modifications.TryGetValue(prop.target, out vector3Modification)) { vector3Modification = new Vector3Modification(); vector3Modifications[prop.target] = vector3Modification; } if (binding.propertyName.EndsWith("x")) vector3Modification.x = modification; else if (binding.propertyName.EndsWith("y")) vector3Modification.y = modification; else if (binding.propertyName.EndsWith("z")) vector3Modification.z = modification; vector3Modification.last = modification; } else { outModifs.Add(modification); } } if (vector3Modifications.Count > 0) { modifications = outModifs.ToArray(); } } static private void ProcessVector3Modification(IAnimationRecordingState state, EditorCurveBinding baseBinding, UndoPropertyModification modification, Transform target, string axis, float scale = 1.0f) { var binding = baseBinding; binding.propertyName = binding.propertyName.Remove(binding.propertyName.Length - 1, 1) + axis; object currentValue = CurveBindingUtility.GetCurrentValue(state.activeRootGameObject, binding); var previousModification = modification.previousValue; if (previousModification == null) { // create dummy previousModification = new PropertyModification(); previousModification.target = target; previousModification.propertyPath = binding.propertyName; previousModification.value = ((float)currentValue).ToString(CultureInfo.InvariantCulture.NumberFormat); } object previousValue = currentValue; ValueFromPropertyModification(previousModification, binding, out previousValue); state.AddPropertyModification(binding, previousModification, modification.keepPrefabOverride); if (scale != 1.0f) { previousValue = (object)((float)previousValue / scale); currentValue = (object)((float)currentValue / scale); } AddKey(state, binding, typeof(float), previousValue, currentValue); } static public void ProcessVector3Modifications(IAnimationRecordingState state, ref Dictionary vector3Modifications) { foreach (KeyValuePair item in vector3Modifications) { Vector3Modification m = item.Value; Transform target = item.Key as Transform; if (target == null) continue; EditorCurveBinding binding = new EditorCurveBinding(); Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(m.last.currentValue, state.activeRootGameObject, out binding); if (type == null) continue; ProcessVector3Modification(state, binding, m.x, target, "x"); ProcessVector3Modification(state, binding, m.y, target, "y"); ProcessVector3Modification(state, binding, m.z, target, "z"); } } static public void ProcessModifications(IAnimationRecordingState state, UndoPropertyModification[] modifications) { GameObject root = state.activeRootGameObject; // Record modified properties for (int i = 0; i < modifications.Length; i++) { EditorCurveBinding binding = new EditorCurveBinding(); PropertyModification prop = modifications[i].previousValue; Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(prop, root, out binding); if (type != null) { object currentValue = CurveBindingUtility.GetCurrentValue(root, binding); object previousValue = null; if (!ValueFromPropertyModification(prop, binding, out previousValue)) previousValue = currentValue; state.AddPropertyModification(binding, prop, modifications[i].keepPrefabOverride); AddKey(state, binding, type, previousValue, currentValue); } } } static public UndoPropertyModification[] Process(IAnimationRecordingState state, UndoPropertyModification[] modifications) { GameObject root = state.activeRootGameObject; if (root == null) return modifications; // Fast path detect that nothing recordable has changed. if (!HasAnyRecordableModifications(root, modifications)) return modifications; Dictionary rotationModifications = new Dictionary(); Dictionary scaleModifications = new Dictionary(); Dictionary positionModifications = new Dictionary(); //Strip out the rotation modifications from the rest CollectRotationModifications(state, ref modifications, ref rotationModifications); UndoPropertyModification[] discardedRotationModifications = FilterRotationModifications(state, ref rotationModifications); UndoPropertyModification[] discardedModifications = FilterModifications(state, ref modifications); CollectVector3Modifications(state, ref modifications, ref positionModifications, kLocalPosition); CollectVector3Modifications(state, ref modifications, ref scaleModifications, kLocalScale); // Process Animator Modifications. ProcessAnimatorModifications(state, ref positionModifications, ref rotationModifications, ref scaleModifications); // Process position modifications. ProcessVector3Modifications(state, ref positionModifications); // Process rotation modifications. ProcessRotationModifications(state, ref rotationModifications); // Process scale modifications. ProcessVector3Modifications(state, ref scaleModifications); // Process what's remaining. ProcessModifications(state, modifications); return discardedModifications.Concat(discardedRotationModifications).ToArray(); } static bool ValueFromPropertyModification(PropertyModification modification, EditorCurveBinding binding, out object outObject) { if (modification == null) { outObject = null; return false; } else if (binding.isPPtrCurve) { outObject = modification.objectReference; return true; } else { if(binding.isDiscreteCurve && int.TryParse(modification.value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int tempInt)) { outObject = tempInt; return true; } else if (float.TryParse(modification.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float tempFloat)) { outObject = tempFloat; return true; } else { outObject = null; return false; } } } static void AddKey(IAnimationRecordingState state, EditorCurveBinding binding, Type type, object previousValue, object currentValue) { AnimationClip clip = state.activeAnimationClip; if ((clip.hideFlags & HideFlags.NotEditable) != 0) return; AnimationWindowCurve curve = new AnimationWindowCurve(clip, binding, type); // Add previous value at first frame on empty curves. if (state.addZeroFrame) { // Is it a new curve? if (curve.length == 0) { if (state.currentFrame != 0) { AnimationWindowUtility.AddKeyframeToCurve(curve, previousValue, type, AnimationKeyTime.Frame(0, clip.frameRate)); } } } // Add key at current frame. AnimationWindowUtility.AddKeyframeToCurve(curve, currentValue, type, AnimationKeyTime.Frame(state.currentFrame, clip.frameRate)); state.SaveCurve(curve); } static void AddRotationKey(IAnimationRecordingState state, EditorCurveBinding binding, Type type, Vector3 previousEulerAngles, Vector3 currentEulerAngles) { AnimationClip clip = state.activeAnimationClip; if ((clip.hideFlags & HideFlags.NotEditable) != 0) return; EditorCurveBinding[] additionalBindings = RotationCurveInterpolation.RemapAnimationBindingForRotationAddKey(binding, clip); // Add key at current frame for (int i = 0; i < 3; i++) { AnimationWindowCurve curve = new AnimationWindowCurve(clip, additionalBindings[i], type); if (state.addZeroFrame) { // Is it a new curve? if (curve.length == 0) { if (state.currentFrame != 0) { AnimationWindowUtility.AddKeyframeToCurve(curve, previousEulerAngles[i], type, AnimationKeyTime.Frame(0, clip.frameRate)); } } } AnimationWindowUtility.AddKeyframeToCurve(curve, currentEulerAngles[i], type, AnimationKeyTime.Frame(state.currentFrame, clip.frameRate)); state.SaveCurve(curve); } } internal class RootMotionModification { public UndoPropertyModification px; public UndoPropertyModification py; public UndoPropertyModification pz; public UndoPropertyModification lastP; public UndoPropertyModification rx; public UndoPropertyModification ry; public UndoPropertyModification rz; public UndoPropertyModification rw; public UndoPropertyModification lastR; } static private void ProcessRootMotionModifications(IAnimationRecordingState state, ref Dictionary rootMotionModifications) { GameObject root = state.activeRootGameObject; Animator animator = root.GetComponent(); bool isHuman = animator != null ? animator.isHuman : false; foreach (KeyValuePair item in rootMotionModifications) { RootMotionModification m = item.Value; Transform target = item.Key as Transform; Vector3 scale = target.localScale * (isHuman ? animator.humanScale : 1); Vector3 position = target.localPosition; Quaternion rotation = target.localRotation; if (m.lastP.previousValue != null) { ProcessRootMotionModification(state, animator, m.px, "MotionT.x", position.x, scale.x); ProcessRootMotionModification(state, animator, m.py, "MotionT.y", position.y, scale.y); ProcessRootMotionModification(state, animator, m.pz, "MotionT.z", position.z, scale.z); } if (m.lastR.previousValue != null) { ProcessRootMotionModification(state, animator, m.rx, "MotionQ.x", rotation.x, 1); ProcessRootMotionModification(state, animator, m.ry, "MotionQ.y", rotation.y, 1); ProcessRootMotionModification(state, animator, m.rz, "MotionQ.z", rotation.z, 1); ProcessRootMotionModification(state, animator, m.rw, "MotionQ.w", rotation.w, 1); } } } static private void ProcessAnimatorModifications(IAnimationRecordingState state, ref Dictionary positionModifications, ref Dictionary rotationModifications, ref Dictionary scaleModifications) { Dictionary rootMotionModifications = new Dictionary(); AnimationClip clip = state.activeAnimationClip; GameObject root = state.activeRootGameObject; Animator animator = root.GetComponent(); bool isHuman = animator != null ? animator.isHuman : false; bool hasRootMotion = animator != null ? animator.hasRootMotion : false; bool applyRootMotion = animator != null ? animator.applyRootMotion : false; bool hasRootCurves = clip.hasRootCurves; // process animator positions List discardListPos = new List(); foreach (KeyValuePair item in positionModifications) { Vector3Modification m = item.Value; Transform target = item.Key as Transform; if (target == null) continue; EditorCurveBinding binding = new EditorCurveBinding(); Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(m.last.currentValue, state.activeRootGameObject, out binding); if (type == null) continue; bool isRootTransform = root.transform == target; bool isRootMotion = isRootTransform && applyRootMotion && hasRootCurves && (isHuman || hasRootMotion); bool isHumanBone = isHuman && !isRootTransform && animator.IsBoneTransform(target); if (isHumanBone) { Debug.LogWarning("Keyframing translation on humanoid rig is not supported!", target); discardListPos.Add(item.Key); } else if (isRootMotion) { RootMotionModification rootMotionModification; if (!rootMotionModifications.TryGetValue(target, out rootMotionModification)) { rootMotionModification = new RootMotionModification(); rootMotionModifications[target] = rootMotionModification; } rootMotionModification.lastP = m.last; rootMotionModification.px = m.x; rootMotionModification.py = m.y; rootMotionModification.pz = m.z; discardListPos.Add(item.Key); } else if (applyRootMotion && isRootTransform) { Vector3 scale = root.transform.localScale * (isHuman ? animator.humanScale : 1); ProcessVector3Modification(state, binding, m.x, target, "x", scale.x); ProcessVector3Modification(state, binding, m.y, target, "y", scale.y); ProcessVector3Modification(state, binding, m.z, target, "z", scale.z); discardListPos.Add(item.Key); } } foreach (object key in discardListPos) { positionModifications.Remove(key); } // process animator rotation List discardListRot = new List(); foreach (KeyValuePair item in rotationModifications) { RotationModification m = item.Value; Transform target = item.Key as Transform; if (target == null) continue; EditorCurveBinding binding = new EditorCurveBinding(); Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(m.lastQuatModification.currentValue, state.activeRootGameObject, out binding); if (type == null) continue; bool isRootTransform = root.transform == target; bool isRootMotion = isRootTransform && applyRootMotion && hasRootCurves && (isHuman || hasRootMotion); bool isHumanBone = isHuman && !isRootTransform && animator.IsBoneTransform(target); if (isHumanBone) { Debug.LogWarning("Keyframing rotation on humanoid rig is not supported!", target); discardListRot.Add(item.Key); } else if (isRootMotion) { RootMotionModification rootMotionModification; if (!rootMotionModifications.TryGetValue(target, out rootMotionModification)) { rootMotionModification = new RootMotionModification(); rootMotionModifications[target] = rootMotionModification; } rootMotionModification.lastR = m.lastQuatModification; rootMotionModification.rx = m.x; rootMotionModification.ry = m.y; rootMotionModification.rz = m.z; rootMotionModification.rw = m.w; discardListRot.Add(item.Key); } } foreach (object key in discardListRot) { rotationModifications.Remove(key); } // process animator scales List discardListScale = new List(); foreach (KeyValuePair item in scaleModifications) { Vector3Modification m = item.Value; Transform target = item.Key as Transform; if (target == null) continue; EditorCurveBinding binding = new EditorCurveBinding(); Type type = AnimationUtility.PropertyModificationToEditorCurveBinding(m.last.currentValue, state.activeRootGameObject, out binding); if (type == null) continue; bool isRootTransform = root.transform == target; bool isHumanBone = isHuman && !isRootTransform && animator.IsBoneTransform(target); if (isHumanBone) { Debug.LogWarning("Keyframing scale on humanoid rig is not supported!", target); discardListScale.Add(item.Key); } } foreach (object key in discardListScale) { scaleModifications.Remove(key); } ProcessRootMotionModifications(state, ref rootMotionModifications); } static void ProcessRootMotionModification(IAnimationRecordingState state, Animator animator, UndoPropertyModification modification, string name, float value, float scale) { AnimationClip clip = state.activeAnimationClip; if ((clip.hideFlags & HideFlags.NotEditable) != 0) return; float prevValue = value; object oValue; if (ValueFromPropertyModification(modification.currentValue, new EditorCurveBinding(), out oValue)) value = (float)oValue; if (ValueFromPropertyModification(modification.previousValue, new EditorCurveBinding(), out oValue)) prevValue = (float)oValue; value = Mathf.Abs(scale) > Mathf.Epsilon ? value / scale : value; prevValue = Mathf.Abs(scale) > Mathf.Epsilon ? prevValue / scale : prevValue; var binding = new EditorCurveBinding(); binding.propertyName = name; binding.path = ""; binding.type = typeof(Animator); var prop = new PropertyModification(); prop.target = animator; prop.propertyPath = binding.propertyName; prop.value = value.ToString(CultureInfo.InvariantCulture.NumberFormat); state.AddPropertyModification(binding, prop, modification.keepPrefabOverride); AnimationWindowCurve curve = new AnimationWindowCurve(clip, binding, typeof(float)); if (state.addZeroFrame && state.currentFrame != 0 && curve.length == 0) { AnimationWindowUtility.AddKeyframeToCurve(curve, prevValue, typeof(float), AnimationKeyTime.Frame(0, clip.frameRate)); } AnimationWindowUtility.AddKeyframeToCurve(curve, value, typeof(float), AnimationKeyTime.Frame(state.currentFrame, clip.frameRate)); state.SaveCurve(curve); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditorInternal; using Object = UnityEngine.Object; namespace UnityEditor { [EditorWindowTitle(title = "Animation", useTypeNameAsIconName = true)] public sealed class AnimationWindow : EditorWindow, IHasCustomMenu { // Active Animation windows private static List s_AnimationWindows = new List(); internal static List GetAllAnimationWindows() { return s_AnimationWindows; } private static Type[] s_CustomControllerTypes = null; private AnimEditor m_AnimEditor; [SerializeField] EditorGUIUtility.EditorLockTracker m_LockTracker = new EditorGUIUtility.EditorLockTracker(); [SerializeField] private int m_LastSelectedObjectID; private GUIStyle m_LockButtonStyle; private GUIContent m_DefaultTitleContent; private GUIContent m_RecordTitleContent; internal AnimEditor animEditor => m_AnimEditor; internal AnimationWindowState state { get { if (m_AnimEditor != null) { return m_AnimEditor.state; } return null; } } public AnimationClip animationClip { get { if (m_AnimEditor != null) { return m_AnimEditor.state.activeAnimationClip; } return null; } set { if (m_AnimEditor != null) { m_AnimEditor.state.activeAnimationClip = value; } } } public bool previewing { get { if (m_AnimEditor != null) { return m_AnimEditor.state.previewing; } return false; } set { if (m_AnimEditor != null) { m_AnimEditor.state.previewing = value; } } } public bool canPreview { get { if (m_AnimEditor != null) { return m_AnimEditor.state.canPreview; } return false; } } public bool recording { get { if (m_AnimEditor != null) { return m_AnimEditor.state.recording; } return false; } set { if (m_AnimEditor != null) { m_AnimEditor.state.recording = value; } } } public bool canRecord { get { if (m_AnimEditor != null) { return m_AnimEditor.state.canRecord; } return false; } } public bool playing { get { if (m_AnimEditor != null) { return m_AnimEditor.state.playing; } return false; } set { if (m_AnimEditor != null) { m_AnimEditor.state.playing = value; } } } public float time { get { if (m_AnimEditor != null) { return m_AnimEditor.state.currentTime; } return 0.0f; } set { if (m_AnimEditor != null) { m_AnimEditor.state.currentTime = value; } } } public int frame { get { if (m_AnimEditor != null) { return m_AnimEditor.state.currentFrame; } return 0; } set { if (m_AnimEditor != null) { m_AnimEditor.state.currentFrame = value; } } } private AnimationWindow() {} internal void ForceRefresh() { if (m_AnimEditor != null) { m_AnimEditor.state.ForceRefresh(); } } void OnEnable() { if (m_AnimEditor == null) { m_AnimEditor = CreateInstance(); m_AnimEditor.hideFlags = HideFlags.HideAndDontSave; } s_AnimationWindows.Add(this); titleContent = GetLocalizedTitleContent(); m_DefaultTitleContent = titleContent; m_RecordTitleContent = EditorGUIUtility.TextContentWithIcon(titleContent.text, "Animation.Record"); OnSelectionChange(); Undo.undoRedoEvent += UndoRedoPerformed; } void OnDisable() { s_AnimationWindows.Remove(this); m_AnimEditor.OnDisable(); Undo.undoRedoEvent -= UndoRedoPerformed; } void OnDestroy() { DestroyImmediate(m_AnimEditor); } void Update() { if (m_AnimEditor == null) return; m_AnimEditor.Update(); } void OnGUI() { if (m_AnimEditor == null) return; titleContent = m_AnimEditor.state.recording ? m_RecordTitleContent : m_DefaultTitleContent; m_AnimEditor.OnAnimEditorGUI(this, position); } internal void OnSelectionChange() { if (m_AnimEditor == null) return; Object activeObject = Selection.activeObject; bool restoringLockedSelection = false; if (m_LockTracker.isLocked && m_AnimEditor.stateDisabled) { activeObject = EditorUtility.InstanceIDToObject(m_LastSelectedObjectID); restoringLockedSelection = true; m_LockTracker.isLocked = false; } if (activeObject is GameObject activeGameObject) { EditGameObject(activeGameObject); } else { if (activeObject is Transform activeTransform) { EditGameObject(activeTransform.gameObject); } else { if (activeObject is AnimationClip activeAnimationClip) EditAnimationClip(activeAnimationClip); } } if (restoringLockedSelection && !m_AnimEditor.stateDisabled) { m_LockTracker.isLocked = true; } } void OnFocus() { OnSelectionChange(); } internal void OnControllerChange() { // Refresh selectedItem to update selected clips. OnSelectionChange(); } void OnLostFocus() { if (m_AnimEditor != null) m_AnimEditor.OnLostFocus(); } [Callbacks.OnOpenAsset] static bool OnOpenAsset(int instanceID, int line) { var clip = EditorUtility.InstanceIDToObject(instanceID) as AnimationClip; if (clip) { EditorWindow.GetWindow(); return true; } return false; } internal bool EditGameObject(GameObject gameObject) { if (EditorUtility.IsPersistent(gameObject)) return false; if ((gameObject.hideFlags & HideFlags.NotEditable) != 0) return false; var newSelection = GameObjectSelectionItem.Create(gameObject); if (ShouldUpdateGameObjectSelection(newSelection)) { m_AnimEditor.selection = newSelection; IAnimationWindowController controller = null; var rootGameObject = newSelection.rootGameObject; if (rootGameObject != null) controller = FindCustomController(rootGameObject); m_AnimEditor.overrideControlInterface = controller; m_LastSelectedObjectID = gameObject != null ? gameObject.GetInstanceID() : 0; } else m_AnimEditor.OnSelectionUpdated(); return true; } internal bool EditAnimationClip(AnimationClip animationClip) { if (state.linkedWithSequencer == true) return false; EditAnimationClipInternal(animationClip, (Object)null, (IAnimationWindowController)null); return true; } internal bool EditSequencerClip(AnimationClip animationClip, Object sourceObject, IAnimationWindowControl controlInterface) { EditAnimationClipInternal(animationClip, sourceObject, controlInterface); state.linkedWithSequencer = true; if (controlInterface != null) controlInterface.Init(state); return true; } internal void UnlinkSequencer() { if (state.linkedWithSequencer) { state.linkedWithSequencer = false; // Selected object could have been changed when unlocking the animation window EditAnimationClip(null); OnSelectionChange(); } } private IAnimationWindowController FindCustomController(GameObject gameObject) { IAnimationWindowController controller = null; if (s_CustomControllerTypes == null) { s_CustomControllerTypes = TypeCache .GetTypesWithAttribute() .Where(type => typeof(IAnimationWindowController).IsAssignableFrom(type)) .ToArray(); } foreach (var controllerType in s_CustomControllerTypes) { var attribute = controllerType.GetCustomAttribute(); var component = gameObject.GetComponent(attribute.componentType); if (component != null) { controller = (IAnimationWindowController)Activator.CreateInstance(controllerType); controller.OnCreate(this, component); break; } } return controller; } private void EditAnimationClipInternal(AnimationClip animationClip, Object sourceObject, IAnimationWindowController controlInterface) { var newSelection = AnimationClipSelectionItem.Create(animationClip, sourceObject); if (ShouldUpdateSelection(newSelection)) { m_AnimEditor.selection = newSelection; m_AnimEditor.overrideControlInterface = controlInterface; m_LastSelectedObjectID = animationClip != null ? animationClip.GetInstanceID() : 0; } else m_AnimEditor.OnSelectionUpdated(); } void ShowButton(Rect r) { if (m_LockButtonStyle == null) m_LockButtonStyle = "IN LockButton"; EditorGUI.BeginChangeCheck(); m_LockTracker.ShowButton(r, m_LockButtonStyle, m_AnimEditor.stateDisabled); // Selected object could have been changed when unlocking the animation window if (EditorGUI.EndChangeCheck()) OnSelectionChange(); } private bool ShouldUpdateGameObjectSelection(GameObjectSelectionItem selectedItem) { if (m_LockTracker.isLocked) return false; if (state.linkedWithSequencer) return false; // Selected game object with no animation player. if (selectedItem.rootGameObject == null) return true; AnimationWindowSelectionItem currentSelection = m_AnimEditor.selection; // Game object holding animation player has changed. Update selection. if (selectedItem.rootGameObject != currentSelection.rootGameObject) return true; // No clip in current selection, favour new selection. if (currentSelection.animationClip == null) return true; // Make sure that animation clip is still referenced in animation player. if (currentSelection.rootGameObject != null) { AnimationClip[] allClips = AnimationUtility.GetAnimationClips(currentSelection.rootGameObject); if (!Array.Exists(allClips, x => x == currentSelection.animationClip)) return true; } return false; } private bool ShouldUpdateSelection(AnimationWindowSelectionItem selectedItem) { if (m_LockTracker.isLocked) return false; AnimationWindowSelectionItem currentSelection = m_AnimEditor.selection; return (selectedItem.GetRefreshHash() != currentSelection.GetRefreshHash()); } private void UndoRedoPerformed(in UndoRedoInfo info) { Repaint(); } public void AddItemsToMenu(GenericMenu menu) { m_LockTracker.AddItemsToMenu(menu, m_AnimEditor.stateDisabled); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowClipPopup.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using UnityEngine; using UnityEditorInternal; namespace UnityEditor { [System.Serializable] class AnimationWindowClipPopup { [SerializeReference] public AnimationWindowState state; static int s_ClipPopupHash = "s_ClipPopupHash".GetHashCode(); private const float kMenuOffsetMac = 19; internal sealed class ClipPopupCallbackInfo { // The global shared popup state public static ClipPopupCallbackInfo instance = null; // Name of the command event sent from the popup menu to OnGUI when user has changed selection private const string kPopupMenuChangedMessage = "ClipPopupMenuChanged"; // The control ID of the popup menu that is currently displayed. // Used to pass selection changes back again... private readonly int m_ControlID = 0; // Which item was selected private AnimationClip m_SelectedClip = null; // Which view should we send it to. private readonly GUIView m_SourceView; public ClipPopupCallbackInfo(int controlID) { m_ControlID = controlID; m_SourceView = GUIView.current; } public static AnimationClip GetSelectedClipForControl(int controlID, AnimationClip clip) { Event evt = Event.current; if (evt.type == EventType.ExecuteCommand && evt.commandName == kPopupMenuChangedMessage) { if (instance == null) { Debug.LogError("Popup menu has no instance"); return clip; } if (instance.m_ControlID == controlID) { clip = instance.m_SelectedClip; instance = null; GUI.changed = true; evt.Use(); } } return clip; } public static void SetSelectedClip(AnimationClip clip) { if (instance == null) { Debug.LogError("Popup menu has no instance"); return; } instance.m_SelectedClip = clip; } public static void SendEvent() { if (instance == null) { Debug.LogError("Popup menu has no instance"); return; } instance.m_SourceView.SendEvent(EditorGUIUtility.CommandEvent(kPopupMenuChangedMessage)); } } private void DisplayClipMenu(Rect position, int controlID, AnimationClip clip) { AnimationClip[] clips = GetOrderedClipList(); GUIContent[] menuContent = GetClipMenuContent(clips); int selected = ClipToIndex(clips, clip); // Center popup menu around button widget if (Application.platform == RuntimePlatform.OSXEditor) { position.y = position.y - selected * EditorGUI.kSingleLineHeight - kMenuOffsetMac; } ClipPopupCallbackInfo.instance = new ClipPopupCallbackInfo(controlID); EditorUtility.DisplayCustomMenu(position, menuContent, null, selected, (userData, options, index) => { if (index < clips.Length) { ClipPopupCallbackInfo.SetSelectedClip(clips[index]); } else { AnimationClip newClip = AnimationWindowUtility.CreateNewClip(state.selection.rootGameObject.name); if (newClip) { AnimationWindowUtility.AddClipToAnimationPlayerComponent(state.activeAnimationPlayer, newClip); ClipPopupCallbackInfo.SetSelectedClip(newClip); } } ClipPopupCallbackInfo.SendEvent(); }, null); } // (case 1029160) Modified version of EditorGUI.DoPopup to fit large data list query. private AnimationClip DoClipPopup(AnimationClip clip, GUIStyle style) { Rect position = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight, style); int controlID = GUIUtility.GetControlID(s_ClipPopupHash, FocusType.Keyboard, position); clip = ClipPopupCallbackInfo.GetSelectedClipForControl(controlID, clip); Event evt = Event.current; switch (evt.type) { case EventType.Repaint: Font originalFont = style.font; if (originalFont && EditorGUIUtility.GetBoldDefaultFont() && originalFont == EditorStyles.miniFont) { style.font = EditorStyles.miniBoldFont; } GUIContent buttonContent = EditorGUIUtility.TempContent(CurveUtility.GetClipName(clip)); buttonContent.tooltip = AssetDatabase.GetAssetPath(clip); style.Draw(position, buttonContent, controlID, false); style.font = originalFont; break; case EventType.MouseDown: if (evt.button == 0 && position.Contains(evt.mousePosition)) { DisplayClipMenu(position, controlID, clip); GUIUtility.keyboardControl = controlID; evt.Use(); } break; case EventType.KeyDown: if (evt.MainActionKeyForControl(controlID)) { DisplayClipMenu(position, controlID, clip); evt.Use(); } break; } return clip; } public void OnGUI() { if (state.selection.canChangeAnimationClip) { EditorGUI.BeginChangeCheck(); var newClip = DoClipPopup(state.activeAnimationClip, AnimationWindowStyles.animClipToolbarPopup); if (EditorGUI.EndChangeCheck()) { state.activeAnimationClip = newClip; // Layout has changed, bail out now. EditorGUIUtility.ExitGUI(); } } else if (state.activeAnimationClip != null) { Rect r = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight, AnimationWindowStyles.toolbarLabel); EditorGUI.LabelField(r, CurveUtility.GetClipName(state.activeAnimationClip), AnimationWindowStyles.toolbarLabel); } } private GUIContent[] GetClipMenuContent(AnimationClip[] clips) { int size = clips.Length; if (state.selection.canCreateClips) size += 2; GUIContent[] content = new GUIContent[size]; for (int i = 0; i < clips.Length; i++) { content[i] = new GUIContent(CurveUtility.GetClipName(clips[i])); } if (state.selection.canCreateClips) { content[content.Length - 2] = GUIContent.none; content[content.Length - 1] = AnimationWindowStyles.createNewClip; } return content; } private AnimationClip[] GetOrderedClipList() { AnimationClip[] clips = new AnimationClip[0]; if (state.activeRootGameObject != null) clips = AnimationUtility.GetAnimationClips(state.activeRootGameObject); //Using AlphaNum/Natural Compare to sort clips Array.Sort(clips, (AnimationClip clip1, AnimationClip clip2) => EditorUtility.NaturalCompareObjectNames(clip1, clip2)); return clips; } private int ClipToIndex(AnimationClip[] clips, AnimationClip clip) { for (int index = 0; index < clips.Length; ++index) { if (clips[index] == clip) return index; } return 0; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowClipboard.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace UnityEditorInternal { // Classes for copy/paste support of animation window related things [Serializable] internal sealed class AnimationWindowEventClipboard { public float time = 0; public string functionName = ""; public string stringParam = ""; public int objectParam = 0; public float floatParam = 0; public int intParam = 0; public SendMessageOptions messageOptions = SendMessageOptions.RequireReceiver; public AnimationWindowEventClipboard(AnimationEvent e) { time = e.time; functionName = e.functionName; stringParam = e.stringParameter; objectParam = e.objectReferenceParameter ? e.objectReferenceParameter.GetInstanceID() : 0; floatParam = e.floatParameter; intParam = e.intParameter; messageOptions = e.messageOptions; } public static AnimationEvent FromClipboard(AnimationWindowEventClipboard e) { return new AnimationEvent { time = e.time, functionName = e.functionName, stringParameter = e.stringParam, objectReferenceParameter = InternalEditorUtility.GetObjectFromInstanceID(e.objectParam), floatParameter = e.floatParam, intParameter = e.intParam, messageOptions = e.messageOptions }; } } [Serializable] internal class AnimationWindowEventsClipboard { public AnimationWindowEventClipboard[] events; internal static bool CanPaste() { return Clipboard.HasCustomValue(); } internal static void CopyEvents(IList allEvents, bool[] selected, int explicitIndex = -1) { var copyEvents = new List(); // If a selection already exists, copy selection instead of clicked index if (Array.Exists(selected, s => s)) { for (var i = 0; i < selected.Length; ++i) { if (selected[i]) copyEvents.Add(new AnimationWindowEventClipboard(allEvents[i])); } } // Else, only copy the clicked animation event else if (explicitIndex >= 0) { copyEvents.Add(new AnimationWindowEventClipboard(allEvents[explicitIndex])); } var data = new AnimationWindowEventsClipboard {events = copyEvents.ToArray()}; // Animation keyframes right now do not go through regular clipboard machinery, // so when copying Events, make sure Keyframes are cleared from the clipboard, or things // get confusing. AnimationWindowState.ClearKeyframeClipboard(); Clipboard.SetCustomValue(data); } internal static AnimationEvent[] AddPastedEvents(AnimationEvent[] events, float time, out bool[] selected) { selected = null; var data = Clipboard.GetCustomValue(); if (data?.events == null || data.events.Length == 0) return null; var minTime = data.events.Min(e => e.time); var origEventsCount = events.Length; // Append new events to the end first, var newEvents = new List(); foreach (var e in data.events) { var t = e.time - minTime + time; var newEvent = AnimationWindowEventClipboard.FromClipboard(e); newEvent.time = t; newEvents.Add(newEvent); } events = events.Concat(newEvents).ToArray(); // Re-sort events by time var order = new int[events.Length]; for (var i = 0; i < order.Length; i++) order[i] = i; Array.Sort(events, order, new AnimationEventTimeLine.EventComparer()); // Mark pasted ones as selected selected = new bool[events.Length]; for (var i = 0; i < order.Length; ++i) selected[i] = order[i] >= origEventsCount; return events; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowControl.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using System.Collections.Generic; using Object = UnityEngine.Object; using UnityEngine.Playables; using UnityEngine.Animations; using Unity.Profiling; namespace UnityEditorInternal { [Serializable] class AnimationWindowControl : IAnimationWindowController, IAnimationContextualResponder { class CandidateRecordingState : IAnimationRecordingState { public GameObject activeGameObject { get; private set; } public GameObject activeRootGameObject { get; private set; } public AnimationClip activeAnimationClip { get; private set; } public int currentFrame { get { return 0; } } public bool addZeroFrame { get { return false; } } public CandidateRecordingState(AnimationWindowState state, AnimationClip candidateClip) { activeGameObject = state.activeGameObject; activeRootGameObject = state.activeRootGameObject; activeAnimationClip = candidateClip; } public bool DiscardModification(PropertyModification modification) { return !AnimationMode.IsPropertyAnimated(modification.target, modification.propertyPath); } public void SaveCurve(AnimationWindowCurve curve) { Undo.RegisterCompleteObjectUndo(curve.clip, "Edit Candidate Curve"); AnimationWindowUtility.SaveCurve(curve.clip, curve); } public void AddPropertyModification(EditorCurveBinding binding, PropertyModification propertyModification, bool keepPrefabOverride) { AnimationMode.AddCandidate(binding, propertyModification, keepPrefabOverride); } } enum RecordingStateMode { ManualKey, AutoKey } class RecordingState : IAnimationRecordingState { private AnimationWindowState m_State; private RecordingStateMode m_Mode; public GameObject activeGameObject { get { return m_State.activeGameObject; } } public GameObject activeRootGameObject { get { return m_State.activeRootGameObject; } } public AnimationClip activeAnimationClip { get { return m_State.activeAnimationClip; } } public int currentFrame { get { return m_State.currentFrame; } } public bool addZeroFrame { get { return (m_Mode == RecordingStateMode.AutoKey); } } public bool addPropertyModification { get { return m_State.previewing; } } public RecordingState(AnimationWindowState state, RecordingStateMode mode) { m_State = state; m_Mode = mode; } public bool DiscardModification(PropertyModification modification) { return false; } public void SaveCurve(AnimationWindowCurve curve) { m_State.SaveCurve(curve.clip, curve); } public void AddPropertyModification(EditorCurveBinding binding, PropertyModification propertyModification, bool keepPrefabOverride) { AnimationMode.AddPropertyModification(binding, propertyModification, keepPrefabOverride); } } [Flags] enum ResampleFlags { None = 0, RebuildGraph = 1 << 0, RefreshViews = 1 << 1, FlushUndos = 1 << 2, Default = RefreshViews | FlushUndos } private static bool HasFlag(ResampleFlags flags, ResampleFlags flag) { return (flags & flag) != 0; } [SerializeField] private AnimationKeyTime m_Time; [NonSerialized] private float m_PreviousUpdateTime; [NonSerialized] public AnimationWindowState state; public AnimEditor animEditor { get { return state.animEditor; } } [SerializeField] private AnimationClip m_CandidateClip; [SerializeField] private AnimationClip m_DefaultPose; [SerializeField] private AnimationModeDriver m_Driver; [SerializeField] private AnimationModeDriver m_CandidateDriver; private PlayableGraph m_Graph; private Playable m_GraphRoot; private AnimationClipPlayable m_ClipPlayable; private AnimationClipPlayable m_CandidateClipPlayable; private AnimationClipPlayable m_DefaultPosePlayable; private bool m_UsesPostProcessComponents = false; private static ProfilerMarker s_ResampleAnimationMarker = new ProfilerMarker("AnimationWindowControl.ResampleAnimation"); public void OnEnable() { EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } public void OnDisable() { EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; } public void OnCreate(AnimationWindow animationWindow, Component component) { // nothing to do. } public void OnDestroy() { if (m_Driver != null) ScriptableObject.DestroyImmediate(m_Driver); } public void OnSelectionChanged() { // Set back time at beginning and stop recording. if (state != null) m_Time = AnimationKeyTime.Time(0f, state.frameRate); StopPreview(); } void OnPlayModeStateChanged(PlayModeStateChange state) { if (state == PlayModeStateChange.ExitingPlayMode || state == PlayModeStateChange.ExitingEditMode) { StopPreview(); } } public float time { get => m_Time.time; set => SetCurrentTime(value); } public int frame { get => m_Time.frame; set => SetCurrentFrame(value); } public void GoToTime(float time) { SetCurrentTime(time); } public void GoToFrame(int frame) { SetCurrentFrame(frame); } public void GoToPreviousKeyframe(PropertyModification[] modifications) { EditorCurveBinding[] bindings = AnimationWindowUtility.PropertyModificationsToEditorCurveBindings(modifications, state.activeRootGameObject, state.activeAnimationClip); if (bindings.Length == 0) return; List curves = new List(); for (int i = 0; i < state.filteredCurves.Count; ++i) { AnimationWindowCurve curve = state.filteredCurves[i]; if (Array.Exists(bindings, binding => curve.binding.Equals(binding))) curves.Add(curve); } float newTime = AnimationWindowUtility.GetPreviousKeyframeTime(curves.ToArray(), time, state.clipFrameRate); SetCurrentTime(state.SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToFrame)); state.Repaint(); } public void GoToNextKeyframe(PropertyModification[] modifications) { EditorCurveBinding[] bindings = AnimationWindowUtility.PropertyModificationsToEditorCurveBindings(modifications, state.activeRootGameObject, state.activeAnimationClip); if (bindings.Length == 0) return; List curves = new List(); for (int i = 0; i < state.filteredCurves.Count; ++i) { AnimationWindowCurve curve = state.filteredCurves[i]; if (Array.Exists(bindings, binding => curve.binding.Equals(binding))) curves.Add(curve); } float newTime = AnimationWindowUtility.GetNextKeyframeTime(curves.ToArray(), time, state.clipFrameRate); SetCurrentTime(state.SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToFrame)); state.Repaint(); } private void SnapTimeToFrame() { float newTime = state.FrameToTime(frame); SetCurrentTime(newTime); } private void SetCurrentTime(float value) { if (!Mathf.Approximately(value, time)) { m_Time = AnimationKeyTime.Time(value, state.frameRate); StartPreview(); ClearCandidates(); ResampleAnimation(); } } private void SetCurrentFrame(int value) { if (value != frame) { m_Time = AnimationKeyTime.Frame(value, state.frameRate); StartPreview(); ClearCandidates(); ResampleAnimation(); } } public bool canPlay { get { return canPreview; } } public bool playing { get { return AnimationMode.InAnimationPlaybackMode() && previewing; } set { if (value) StartPlayback(); else StopPlayback(); } } private void StartPlayback() { if (!canPlay || playing) return; AnimationMode.StartAnimationPlaybackMode(); m_PreviousUpdateTime = Time.realtimeSinceStartup; // Auto-Preview when start playing ClearCandidates(); } private void StopPlayback() { if (AnimationMode.InAnimationPlaybackMode()) { AnimationMode.StopAnimationPlaybackMode(); // Snap to frame when playing stops SnapTimeToFrame(); } } public bool PlaybackUpdate() { float deltaTime = Time.realtimeSinceStartup - m_PreviousUpdateTime; m_PreviousUpdateTime = Time.realtimeSinceStartup; float newTime = time + deltaTime; // looping if (newTime > state.maxTime) newTime = state.minTime; m_Time = AnimationKeyTime.Time(Mathf.Clamp(newTime, state.minTime, state.maxTime), state.frameRate); ResampleAnimation(); return true; } public bool canPreview { get { if (!state.selection.canPreview) return false; var driver = GetAnimationModeDriverNoAlloc(); return (driver != null && AnimationMode.InAnimationMode(driver)) || !AnimationMode.InAnimationMode(); } } public bool previewing { get { var driver = GetAnimationModeDriverNoAlloc(); if (driver == null) return false; return AnimationMode.InAnimationMode(driver); } set { if (value) StartPreview(); else StopPreview(); } } private void StartPreview() { if (previewing || !canPreview) return; AnimationMode.StartAnimationMode(GetAnimationModeDriver()); AnimationPropertyContextualMenu.Instance.SetResponder(this); Undo.postprocessModifications += PostprocessAnimationRecordingModifications; PrefabUtility.allowRecordingPrefabPropertyOverridesFor += AllowRecordingPrefabPropertyOverridesFor; DestroyGraph(); CreateCandidateClip(); //If a hierarchy was created and array reorder happen in the inspector prior //to the preview being started we will need to ensure that the display name //reflects the binding path on an array element. state.UpdateCurvesDisplayName(); IAnimationWindowPreview[] previewComponents = FetchPostProcessComponents(); m_UsesPostProcessComponents = previewComponents != null && previewComponents.Length > 0; if (previewComponents != null) { // Animation preview affects inspector values, so make sure we ignore constrain proportions ConstrainProportionsTransformScale.m_IsAnimationPreview = true; foreach (var component in previewComponents) { component.StartPreview(); } } ResampleAnimation(); } private void StopPreview() { if (!previewing) return; OnExitingAnimationMode(); ConstrainProportionsTransformScale.m_IsAnimationPreview = false; ClearCandidates(); DestroyGraph(); DestroyCandidateClip(); AnimationMode.StopAnimationMode(GetAnimationModeDriver()); // reset responder only if we have set it if (AnimationPropertyContextualMenu.Instance.IsResponder(this)) { AnimationPropertyContextualMenu.Instance.SetResponder(null); } if (m_UsesPostProcessComponents) { IAnimationWindowPreview[] previewComponents = FetchPostProcessComponents(); if (previewComponents != null) { foreach (var component in previewComponents) { component.StopPreview(); } if (!Application.isPlaying) { var animator = state.activeAnimationPlayer as Animator; if (animator != null) { animator.UnbindAllHandles(); } } } m_UsesPostProcessComponents = false; } } public bool canRecord { get { if (!state.selection.canRecord) return false; return canPreview; } } public bool recording { get { if (previewing) return AnimationMode.InAnimationRecording(); return false; } set { if (value) StartRecording(); else StopRecording(); } } private void StartRecording() { if (!canRecord || recording) return; AnimationMode.StartAnimationRecording(); ClearCandidates(); } private void StopRecording() { if (!recording) return; AnimationMode.StopAnimationRecording(); } private void StartCandidateRecording() { AnimationMode.StartCandidateRecording(GetCandidateDriver()); } private void StopCandidateRecording() { AnimationMode.StopCandidateRecording(); } private void DestroyGraph() { if (!m_Graph.IsValid()) return; m_Graph.Destroy(); m_GraphRoot = Playable.Null; } private void RebuildGraph(Animator animator) { DestroyGraph(); m_Graph = PlayableGraph.Create("PreviewGraph"); m_Graph.SetTimeUpdateMode(DirectorUpdateMode.Manual); m_ClipPlayable = AnimationClipPlayable.Create(m_Graph, state.activeAnimationClip); m_ClipPlayable.SetOverrideLoopTime(true); m_ClipPlayable.SetLoopTime(false); m_ClipPlayable.SetApplyFootIK(false); m_CandidateClipPlayable = AnimationClipPlayable.Create(m_Graph, m_CandidateClip); m_CandidateClipPlayable.SetApplyFootIK(false); IAnimationWindowPreview[] previewComponents = FetchPostProcessComponents(); bool requiresDefaultPose = previewComponents != null && previewComponents.Length > 0; int nInputs = requiresDefaultPose ? 3 : 2; // Create a layer mixer if necessary, we'll connect playable nodes to it after having populated AnimationStream. AnimationLayerMixerPlayable mixer = AnimationLayerMixerPlayable.Create(m_Graph, nInputs); m_GraphRoot = (Playable)mixer; // Populate custom playable preview graph. if (previewComponents != null) { foreach (var component in previewComponents) { m_GraphRoot = component.BuildPreviewGraph(m_Graph, m_GraphRoot); } } // Finish hooking up mixer. int inputIndex = 0; if (requiresDefaultPose) { AnimationMode.RevertPropertyModificationsForGameObject(state.activeRootGameObject); EditorCurveBinding[] streamBindings = AnimationUtility.GetAnimationStreamBindings(state.activeRootGameObject); m_DefaultPose = new AnimationClip() { name = "DefaultPose" }; AnimationWindowUtility.CreateDefaultCurves(state, m_DefaultPose, streamBindings); m_DefaultPosePlayable = AnimationClipPlayable.Create(m_Graph, m_DefaultPose); m_DefaultPosePlayable.SetApplyFootIK(false); mixer.ConnectInput(inputIndex++, m_DefaultPosePlayable, 0, 1.0f); } mixer.ConnectInput(inputIndex++, m_ClipPlayable, 0, 1.0f); mixer.ConnectInput(inputIndex++, m_CandidateClipPlayable, 0, 1.0f); if (animator.applyRootMotion) { var motionX = AnimationMotionXToDeltaPlayable.Create(m_Graph); motionX.SetAbsoluteMotion(true); motionX.SetInputWeight(0, 1.0f); m_Graph.Connect(m_GraphRoot, 0, motionX, 0); m_GraphRoot = (Playable)motionX; } var output = AnimationPlayableOutput.Create(m_Graph, "output", animator); output.SetSourcePlayable(m_GraphRoot); output.SetWeight(0.0f); } private IAnimationWindowPreview[] FetchPostProcessComponents() { if (state.activeRootGameObject != null) { return state.activeRootGameObject.GetComponents(); } return null; } public void ResampleAnimation() { ResampleAnimation(ResampleFlags.Default); } private void ResampleAnimation(ResampleFlags flags) { if (state.disabled) return; if (!canPreview || !previewing) return; s_ResampleAnimationMarker.Begin(); if (state.activeAnimationClip != null) { var animationPlayer = state.activeAnimationPlayer; bool usePlayableGraph = animationPlayer is Animator; if (usePlayableGraph) { var isValidGraph = m_Graph.IsValid(); if (isValidGraph) { var playableOutput = (AnimationPlayableOutput)m_Graph.GetOutput(0); isValidGraph = playableOutput.GetTarget() == (Animator)animationPlayer; } if (HasFlag(flags, ResampleFlags.RebuildGraph) || !isValidGraph) { RebuildGraph((Animator)animationPlayer); } } AnimationMode.BeginSampling(); if (HasFlag(flags, ResampleFlags.FlushUndos)) Undo.FlushUndoRecordObjects(); if (usePlayableGraph) { if (m_UsesPostProcessComponents) { IAnimationWindowPreview[] previewComponents = FetchPostProcessComponents(); if (previewComponents != null) { foreach (var component in previewComponents) { component.UpdatePreviewGraph(m_Graph); } } } if (!m_CandidateClip.empty) { StartCandidateRecording(); AnimationMode.AddCandidates(state.activeRootGameObject, m_CandidateClip); } m_ClipPlayable.SetSampleRate(playing ? -1 : state.activeAnimationClip.frameRate); AnimationMode.SamplePlayableGraph(m_Graph, 0, time); // This will cover euler/quaternion matching in basic playable graphs only (animation clip + candidate clip). AnimationUtility.SampleEulerHint(state.activeRootGameObject, state.activeAnimationClip, time, WrapMode.Clamp); if (!m_CandidateClip.empty) AnimationUtility.SampleEulerHint(state.activeRootGameObject, m_CandidateClip, time, WrapMode.Clamp); } else { AnimationMode.SampleAnimationClip(state.activeRootGameObject, state.activeAnimationClip, time); if (!m_CandidateClip.empty) AnimationMode.SampleCandidateClip(state.activeRootGameObject, m_CandidateClip, 0f); } AnimationMode.EndSampling(); if (HasFlag(flags, ResampleFlags.RefreshViews)) { SceneView.RepaintAll(); InspectorWindow.RepaintAllInspectors(); // Particle editor needs to be manually repainted to refresh the animated properties var particleSystemWindow = ParticleSystemWindow.GetInstance(); if (particleSystemWindow) particleSystemWindow.Repaint(); } } s_ResampleAnimationMarker.End(); } private AnimationModeDriver GetAnimationModeDriver() { if (m_Driver == null) { m_Driver = ScriptableObject.CreateInstance(); m_Driver.hideFlags = HideFlags.HideAndDontSave; m_Driver.name = "AnimationWindowDriver"; m_Driver.isKeyCallback += (Object target, string propertyPath) => { if (AnimationMode.IsPropertyAnimated(target, propertyPath)) { var modification = new PropertyModification(); modification.target = target; modification.propertyPath = propertyPath; return KeyExists(new PropertyModification[] {modification}); } return false; }; } return m_Driver; } private AnimationModeDriver GetAnimationModeDriverNoAlloc() { return m_Driver; } private AnimationModeDriver GetCandidateDriver() { if (m_CandidateDriver == null) { m_CandidateDriver = ScriptableObject.CreateInstance(); m_CandidateDriver.name = "AnimationWindowCandidateDriver"; } return m_CandidateDriver; } private bool AllowRecordingPrefabPropertyOverridesFor(UnityEngine.Object componentOrGameObject) { if (componentOrGameObject == null) throw new ArgumentNullException(nameof(componentOrGameObject)); if (componentOrGameObject is not Component && componentOrGameObject is not GameObject) return true; var rootOfAnimation = state.activeRootGameObject; if (rootOfAnimation == null) return true; return false; } void OnExitingAnimationMode() { Undo.postprocessModifications -= PostprocessAnimationRecordingModifications; PrefabUtility.allowRecordingPrefabPropertyOverridesFor -= AllowRecordingPrefabPropertyOverridesFor; } void RecordPropertyOverridesForNonAnimatedProperties(UndoPropertyModification[] modifications) { PrefabUtility.allowRecordingPrefabPropertyOverridesFor -= AllowRecordingPrefabPropertyOverridesFor; foreach (var mod in modifications) { PrefabUtility.RecordPrefabInstancePropertyModifications(mod.currentValue.target); } PrefabUtility.allowRecordingPrefabPropertyOverridesFor += AllowRecordingPrefabPropertyOverridesFor; } private UndoPropertyModification[] PostprocessAnimationRecordingModifications(UndoPropertyModification[] modifications) { //Fix for case 751009: The animationMode can be changed outside the AnimationWindow, and callbacks needs to be unregistered. if (!AnimationMode.InAnimationMode(GetAnimationModeDriver())) { OnExitingAnimationMode(); return modifications; } if (recording) modifications = ProcessAutoKey(modifications); else if (previewing) modifications = RegisterCandidates(modifications); // Fix for UUM-61742: Unrecorded Prefab overloads should be recorded immediately RecordPropertyOverridesForNonAnimatedProperties(modifications); RefreshDisplayNamesOnArrayTopologicalChange(modifications); // Only resample when playable graph has been customized with post process nodes. if (m_UsesPostProcessComponents) ResampleAnimation(ResampleFlags.None); return modifications; } private void RefreshDisplayNamesOnArrayTopologicalChange(UndoPropertyModification[] modifications) { if (modifications.Length >= 2) { if (modifications[0].currentValue.propertyPath.EndsWith("]") && modifications[0].currentValue.propertyPath.Contains(".Array.data[") && modifications[1].currentValue.propertyPath.EndsWith("]") && modifications[1].currentValue.propertyPath.Contains(".Array.data[")) { //Array reordering might affect curves display name state.UpdateCurvesDisplayName(); } else if (modifications[0].currentValue.propertyPath.EndsWith(".Array.size") && Convert.ToInt64(modifications[0].currentValue.value) < Convert.ToInt64(modifications[0].previousValue.value)) { //Array shrinking might affect curves display name state.UpdateCurvesDisplayName(); } } } private UndoPropertyModification[] ProcessAutoKey(UndoPropertyModification[] modifications) { BeginKeyModification(); RecordingState recordingState = new RecordingState(state, RecordingStateMode.AutoKey); UndoPropertyModification[] discardedModifications = AnimationRecording.Process(recordingState, modifications); EndKeyModification(); return discardedModifications; } private UndoPropertyModification[] RegisterCandidates(UndoPropertyModification[] modifications) { bool hasCandidates = AnimationMode.IsRecordingCandidates(); if (!hasCandidates) StartCandidateRecording(); CandidateRecordingState recordingState = new CandidateRecordingState(state, m_CandidateClip); UndoPropertyModification[] discardedModifications = AnimationRecording.Process(recordingState, modifications); // No modifications were added to the candidate clip, stop recording candidates. if (!hasCandidates && discardedModifications.Length == modifications.Length) StopCandidateRecording(); // Make sure inspector is repainted after adding new candidates to get appropriate feedback. InspectorWindow.RepaintAllInspectors(); return discardedModifications; } private void RemoveFromCandidates(PropertyModification[] modifications) { EditorCurveBinding[] bindings = AnimationWindowUtility.PropertyModificationsToEditorCurveBindings(modifications, state.activeRootGameObject, m_CandidateClip); if (bindings.Length == 0) return; // Remove entry from candidate clip. Undo.RegisterCompleteObjectUndo(m_CandidateClip, "Edit Candidate Curve"); for (int i = 0; i < bindings.Length; ++i) { EditorCurveBinding binding = bindings[i]; if (binding.isPPtrCurve) AnimationUtility.SetObjectReferenceCurve(m_CandidateClip, binding, null); else AnimationUtility.SetEditorCurve(m_CandidateClip, binding, null); } // Clear out candidate clip if it's empty. if (AnimationUtility.GetCurveBindings(m_CandidateClip).Length == 0 && AnimationUtility.GetObjectReferenceCurveBindings(m_CandidateClip).Length == 0) ClearCandidates(); } private void CreateCandidateClip() { m_CandidateClip = new AnimationClip(); m_CandidateClip.legacy = state.activeAnimationClip.legacy; m_CandidateClip.name = "CandidateClip"; } private void DestroyCandidateClip() { m_CandidateClip = null; } public void ClearCandidates() { StopCandidateRecording(); if (m_CandidateClip != null) m_CandidateClip.ClearCurves(); } public void ProcessCandidates() { BeginKeyModification(); EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(m_CandidateClip); EditorCurveBinding[] objectCurveBindings = AnimationUtility.GetObjectReferenceCurveBindings(m_CandidateClip); List curves = new List(); for (int i = 0; i < state.filteredCurves.Count; ++i) { AnimationWindowCurve curve = state.filteredCurves[i]; EditorCurveBinding remappedBinding = RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(curve.binding, m_CandidateClip); if (Array.Exists(bindings, binding => remappedBinding.Equals(binding)) || Array.Exists(objectCurveBindings, binding => remappedBinding.Equals(binding))) curves.Add(curve); } AnimationWindowUtility.AddKeyframes(state, curves, m_Time); EndKeyModification(); ClearCandidates(); } private List GetKeys(PropertyModification[] modifications) { var keys = new List(); EditorCurveBinding[] bindings = AnimationWindowUtility.PropertyModificationsToEditorCurveBindings(modifications, state.activeRootGameObject, state.activeAnimationClip); if (bindings.Length == 0) return keys; for (int i = 0; i < state.filteredCurves.Count; ++i) { AnimationWindowCurve curve = state.filteredCurves[i]; if (Array.Exists(bindings, binding => curve.binding.Equals(binding))) { int keyIndex = curve.GetKeyframeIndex(state.time); if (keyIndex >= 0) { keys.Add(curve.keyframes[keyIndex]); } } } return keys; } public bool IsAnimatable(PropertyModification[] modifications) { for (int i = 0; i < modifications.Length; ++i) { var modification = modifications[i]; if (AnimationWindowUtility.PropertyIsAnimatable(modification.target, modification.propertyPath, state.activeRootGameObject)) return true; } return false; } public bool IsEditable(Object targetObject) { if (state.selection.disabled) return false; if (previewing == false) return false; GameObject gameObject = null; if (targetObject is Component) gameObject = ((Component)targetObject).gameObject; else if (targetObject is GameObject) gameObject = (GameObject)targetObject; if (gameObject != null) { Component animationPlayer = AnimationWindowUtility.GetClosestAnimationPlayerComponentInParents(gameObject.transform); if (state.selection.animationPlayer == animationPlayer) { return state.selection.animationIsEditable; } } return false; } public bool KeyExists(PropertyModification[] modifications) { return (GetKeys(modifications).Count > 0); } public bool CandidateExists(PropertyModification[] modifications) { if (!HasAnyCandidates()) return false; for (int i = 0; i < modifications.Length; ++i) { var modification = modifications[i]; if (AnimationMode.IsPropertyCandidate(modification.target, modification.propertyPath)) return true; } return false; } public bool CurveExists(PropertyModification[] modifications) { EditorCurveBinding[] bindings = AnimationWindowUtility.PropertyModificationsToEditorCurveBindings(modifications, state.activeRootGameObject, state.activeAnimationClip); if (bindings.Length == 0) return false; EditorCurveBinding[] clipBindings = AnimationUtility.GetCurveBindings(state.activeAnimationClip); if (clipBindings.Length == 0) return false; if (Array.Exists(bindings, binding => Array.Exists(clipBindings, clipBinding => clipBinding.Equals(binding)))) return true; EditorCurveBinding[] objectCurveBindings = AnimationUtility.GetObjectReferenceCurveBindings(state.activeAnimationClip); if (objectCurveBindings.Length == 0) return false; return Array.Exists(objectCurveBindings, binding => Array.Exists(clipBindings, clipBinding => clipBinding.Equals(binding))); } public bool HasAnyCandidates() { return !m_CandidateClip.empty; } public bool HasAnyCurves() { return (state.filteredCurves.Count > 0); } public void AddKey(SerializedProperty property) { AddKey(AnimationWindowUtility.SerializedPropertyToPropertyModifications(property)); } public void AddKey(PropertyModification[] modifications) { var undoModifications = new UndoPropertyModification[modifications.Length]; for (int i = 0; i < modifications.Length; ++i) { var modification = modifications[i]; undoModifications[i].previousValue = modification; undoModifications[i].currentValue = modification; } BeginKeyModification(); var recordingState = new RecordingState(state, RecordingStateMode.ManualKey); AnimationRecording.Process(recordingState, undoModifications); EndKeyModification(); RemoveFromCandidates(modifications); ResampleAnimation(); state.Repaint(); } public void RemoveKey(SerializedProperty property) { RemoveKey(AnimationWindowUtility.SerializedPropertyToPropertyModifications(property)); } public void RemoveKey(PropertyModification[] modifications) { BeginKeyModification(); List keys = GetKeys(modifications); state.DeleteKeys(keys); RemoveFromCandidates(modifications); EndKeyModification(); ResampleAnimation(); state.Repaint(); } public void RemoveCurve(SerializedProperty property) { RemoveCurve(AnimationWindowUtility.SerializedPropertyToPropertyModifications(property)); } public void RemoveCurve(PropertyModification[] modifications) { EditorCurveBinding[] bindings = AnimationWindowUtility.PropertyModificationsToEditorCurveBindings(modifications, state.activeRootGameObject, state.activeAnimationClip); if (bindings.Length == 0) return; BeginKeyModification(); Undo.RegisterCompleteObjectUndo(state.activeAnimationClip, "Remove Curve"); for (int i = 0; i < bindings.Length; ++i) { EditorCurveBinding binding = bindings[i]; if (binding.isPPtrCurve) AnimationUtility.SetObjectReferenceCurve(state.activeAnimationClip, binding, null); else AnimationUtility.SetEditorCurve(state.activeAnimationClip, binding, null); } EndKeyModification(); RemoveFromCandidates(modifications); ResampleAnimation(); state.Repaint(); } public void AddCandidateKeys() { ProcessCandidates(); ResampleAnimation(); state.Repaint(); } public void AddAnimatedKeys() { BeginKeyModification(); AnimationWindowUtility.AddKeyframes(state, state.filteredCurves, m_Time); ClearCandidates(); EndKeyModification(); ResampleAnimation(); state.Repaint(); } private void BeginKeyModification() { if (animEditor != null) animEditor.BeginKeyModification(); } private void EndKeyModification() { if (animEditor != null) animEditor.EndKeyModification(); } public EditorCurveBinding[] GetAnimatableBindings() { var rootGameObject = state.activeRootGameObject; var scriptableObject = state.activeScriptableObject; if (rootGameObject != null) { return AnimationWindowUtility.GetAnimatableBindings(rootGameObject); } if (scriptableObject != null) { return AnimationUtility.GetAnimatableBindings(scriptableObject); } return Array.Empty(); } public EditorCurveBinding[] GetAnimatableBindings(GameObject gameObject) { var rootGameObject = state.activeRootGameObject; return AnimationUtility.GetAnimatableBindings(gameObject, rootGameObject); } public Type GetValueType(EditorCurveBinding binding) { var rootGameObject = state.activeRootGameObject; var scriptableObject = state.activeScriptableObject; if (rootGameObject != null) { return AnimationUtility.GetEditorCurveValueType(rootGameObject, binding); } else if (scriptableObject != null) { return AnimationUtility.GetEditorCurveValueType(scriptableObject, binding); } else { if (binding.isPPtrCurve) { // Cannot extract type of PPtrCurve. return null; } else { // Cannot extract type of AnimationCurve. Default to float. return typeof(float); } } } public float GetFloatValue(EditorCurveBinding binding) { AnimationUtility.GetFloatValue(state.activeRootGameObject, binding, out var value); return value; } public int GetIntValue(EditorCurveBinding binding) { AnimationUtility.GetDiscreteIntValue(state.activeRootGameObject, binding, out var value); return value; } public Object GetObjectReferenceValue(EditorCurveBinding binding) { AnimationUtility.GetObjectReferenceValue(state.activeRootGameObject, binding, out var value); return value; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowControllerAttribute.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; namespace UnityEditor { [AttributeUsage(AttributeTargets.Class)] class AnimationWindowControllerAttribute : Attribute { public Type componentType { get; } public AnimationWindowControllerAttribute(System.Type type) { if (type == null) Debug.LogError("Failed to load AnimationWindowControl component type"); componentType = type; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowCurve.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Text.RegularExpressions; using Object = UnityEngine.Object; namespace UnityEditorInternal { internal class AnimationWindowCurve : IComparable, IEquatable { public const float timeEpsilon = 0.00001f; private List m_Keyframes; private EditorCurveBinding m_Binding; private int m_BindingHashCode; private AnimationClip m_Clip; private AnimationWindowSelectionItem m_SelectionBinding; private System.Type m_ValueType; public EditorCurveBinding binding { get { return m_Binding; } } public bool isPPtrCurve { get { return m_Binding.isPPtrCurve; } } public bool isDiscreteCurve { get { return m_Binding.isDiscreteCurve; } } public bool isSerializeReferenceCurve{ get {return m_Binding.isSerializeReferenceCurve;}} public bool isPhantom { get { return m_Binding.isPhantom; } } public string propertyName { get { return m_Binding.propertyName; } } public string path { get { return m_Binding.path; } } public System.Type type { get { return m_Binding.type; } } public System.Type valueType { get { return m_ValueType; } } public int length { get { return m_Keyframes.Count; } } public int depth { get { return path.Length > 0 ? path.Split('/').Length : 0; } } public AnimationClip clip { get { return m_Clip; } } public GameObject rootGameObject { get { return m_SelectionBinding != null ? m_SelectionBinding.rootGameObject : null; } } public ScriptableObject scriptableObject { get { return m_SelectionBinding != null ? m_SelectionBinding.scriptableObject : null; } } public bool clipIsEditable { get { return m_SelectionBinding != null ? m_SelectionBinding.clipIsEditable : true; } } public bool animationIsEditable { get { return m_SelectionBinding != null ? m_SelectionBinding.animationIsEditable : true; } } public int selectionID { get { return m_SelectionBinding != null ? m_SelectionBinding.id : 0; } } public IReadOnlyList keyframes => m_Keyframes; private object defaultValue { get { if (isPPtrCurve) return null; if (isDiscreteCurve) return 0; return 0f; } } public AnimationWindowSelectionItem selectionBinding { get { return m_SelectionBinding; } set { m_SelectionBinding = value; } } public AnimationWindowCurve(AnimationClip clip, EditorCurveBinding binding, System.Type valueType) { binding = RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(binding, clip); m_Binding = binding; m_BindingHashCode = binding.GetHashCode(); m_ValueType = valueType; m_Clip = clip; LoadKeyframes(clip); } public void LoadKeyframes(AnimationCurve curve) { if (curve == null) return; for (int i = 0; i < curve.length; i++) m_Keyframes.Add(new AnimationWindowKeyframe(this, curve[i])); } public void LoadKeyframes(AnimationClip clip) { m_Keyframes = new List(); if (!m_Binding.isPPtrCurve) { AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding); LoadKeyframes(curve); } else { ObjectReferenceKeyframe[] curve = AnimationUtility.GetObjectReferenceCurve(clip, binding); if (curve != null) { for (int i = 0; i < curve.Length; i++) m_Keyframes.Add(new AnimationWindowKeyframe(this, curve[i])); } } } public override int GetHashCode() { int clipID = (clip == null ? 0 : clip.GetInstanceID()); return unchecked(selectionID * 92821 ^ clipID * 19603 ^ GetBindingHashCode()); } public int GetBindingHashCode() { return m_BindingHashCode; } public int CompareTo(AnimationWindowCurve obj) { if (!path.Equals(obj.path)) { return ComparePaths(obj.path); } bool sameTransformComponent = type == typeof(Transform) && obj.type == typeof(Transform); bool oneIsTransformComponent = (type == typeof(Transform) || obj.type == typeof(Transform)); // We want to sort position before rotation if (sameTransformComponent) { string propertyGroupA = AnimationWindowUtility.GetPropertyGroupName(propertyName); string propertyGroupB = AnimationWindowUtility.GetPropertyGroupName(obj.propertyName); if (propertyGroupA.Equals("m_LocalPosition") && (propertyGroupB.Equals("m_LocalRotation") || propertyGroupB.StartsWith("localEulerAngles"))) return -1; if ((propertyGroupA.Equals("m_LocalRotation") || propertyGroupA.StartsWith("localEulerAngles")) && propertyGroupB.Equals("m_LocalPosition")) return 1; } // Transform component should always come first. else if (oneIsTransformComponent) { if (type == typeof(Transform)) return -1; else return 1; } // Sort (.r, .g, .b, .a) and (.x, .y, .z, .w) if (obj.type == type) { int lhsIndex = AnimationWindowUtility.GetComponentIndex(obj.propertyName); int rhsIndex = AnimationWindowUtility.GetComponentIndex(propertyName); if (lhsIndex != -1 && rhsIndex != -1 && propertyName.Substring(0, propertyName.Length - 2) == obj.propertyName.Substring(0, obj.propertyName.Length - 2)) return rhsIndex - lhsIndex; } return string.Compare((path + type + propertyName), obj.path + obj.type + obj.propertyName, StringComparison.Ordinal); } public bool Equals(AnimationWindowCurve other) { return CompareTo(other) == 0; } int ComparePaths(string otherPath) { var thisPath = path.Split('/'); var objPath = otherPath.Split('/'); int smallerLength = Math.Min(thisPath.Length, objPath.Length); for (int i = 0; i < smallerLength; ++i) { int compare = string.Compare(thisPath[i], objPath[i], StringComparison.Ordinal); if (compare == 0) { continue; } return compare; } if (thisPath.Length < objPath.Length) { return -1; } return 1; } public AnimationCurve ToAnimationCurve() { int length = m_Keyframes.Count; AnimationCurve animationCurve = new AnimationCurve(); List keys = new List(); for (int i = 0; i < length; i++) { Keyframe newKeyframe = m_Keyframes[i].ToKeyframe(); keys.Add(newKeyframe); } animationCurve.keys = keys.ToArray(); return animationCurve; } public ObjectReferenceKeyframe[] ToObjectCurve() { int length = m_Keyframes.Count; List keys = new List(); for (int i = 0; i < length; i++) { ObjectReferenceKeyframe newKeyframe = m_Keyframes[i].ToObjectReferenceKeyframe(); keys.Add(newKeyframe); } keys.Sort((a, b) => a.time.CompareTo(b.time)); return keys.ToArray(); } public AnimationWindowKeyframe FindKeyAtTime(AnimationKeyTime keyTime) { int index = GetKeyframeIndex(keyTime); if (index == -1) return null; return m_Keyframes[index]; } public object Evaluate(float time) { if (m_Keyframes.Count == 0) return defaultValue; AnimationWindowKeyframe firstKey = m_Keyframes[0]; if (time <= firstKey.time) return firstKey.value; AnimationWindowKeyframe lastKey = m_Keyframes[m_Keyframes.Count - 1]; if (time >= lastKey.time) return lastKey.value; AnimationWindowKeyframe key = firstKey; for (int i = 1; i < m_Keyframes.Count; ++i) { AnimationWindowKeyframe nextKey = m_Keyframes[i]; if (key.time <= time && nextKey.time > time) { if (isPPtrCurve || isDiscreteCurve) { return key.value; } else { // Create an animation curve stub and evaluate. Keyframe keyframe = key.ToKeyframe(); Keyframe nextKeyframe = nextKey.ToKeyframe(); AnimationCurve animationCurve = new AnimationCurve(); animationCurve.keys = new Keyframe[2] { keyframe, nextKeyframe }; return animationCurve.Evaluate(time); } } key = nextKey; } // Shouldn't happen... return defaultValue; } public void AddKeyframe(AnimationWindowKeyframe key, AnimationKeyTime keyTime) { // If there is already key in this time, we always want to remove it RemoveKeyframe(keyTime); m_Keyframes.Add(key); m_Keyframes.Sort((a, b) => a.time.CompareTo(b.time)); } public void RemoveKeyframe(AnimationKeyTime time) { // Loop backwards so key removals don't mess up order for (int i = m_Keyframes.Count - 1; i >= 0; i--) { if (time.ContainsTime(m_Keyframes[i].time)) m_Keyframes.RemoveAt(i); } } public void RemoveKeyframe(AnimationWindowKeyframe keyframe) { m_Keyframes.Remove(keyframe); } public bool HasKeyframe(AnimationKeyTime time) { return GetKeyframeIndex(time) != -1; } public int GetKeyframeIndex(AnimationKeyTime time) { for (int i = 0; i < m_Keyframes.Count; i++) { if (time.ContainsTime(m_Keyframes[i].time)) return i; } return -1; } // Remove keys at range. Start time is exclusive and end time inclusive. public void RemoveKeysAtRange(float startTime, float endTime) { for (int i = m_Keyframes.Count - 1; i >= 0; i--) { if (Mathf.Approximately(endTime, m_Keyframes[i].time) || m_Keyframes[i].time > startTime && m_Keyframes[i].time < endTime) m_Keyframes.RemoveAt(i); } } public void Clear() { m_Keyframes.Clear(); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowEvent.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using Object = UnityEngine.Object; namespace UnityEditor { /// /// Holds the context for AnimationEvent editing. /// class AnimationEventEditorState { static bool s_ShowOverloadedFunctionsDetails = true; static bool s_ShowDuplicatedFunctionsDetails = true; bool m_ShowOverloadedFunctionsDetails = s_ShowOverloadedFunctionsDetails; bool m_ShowDuplicatedFunctionsDetails = s_ShowDuplicatedFunctionsDetails; /// /// Used to track whether or not to show extra details about duplicated function names found in among the potential supported functions /// public bool ShowOverloadedFunctionsDetails { get => m_ShowOverloadedFunctionsDetails; set { m_ShowOverloadedFunctionsDetails = s_ShowOverloadedFunctionsDetails = value; } } /// /// Used to track whether or not to show extra details about overloaded function names found in among the potential supported functions /// public bool ShowDuplicatedFunctionsDetails { get => m_ShowDuplicatedFunctionsDetails; set { m_ShowDuplicatedFunctionsDetails = s_ShowDuplicatedFunctionsDetails = value; } } public AnimationEventEditorState() { m_ShowOverloadedFunctionsDetails = s_ShowOverloadedFunctionsDetails; m_ShowDuplicatedFunctionsDetails = s_ShowDuplicatedFunctionsDetails; } } internal class AnimationWindowEvent : ScriptableObject { public GameObject root; public AnimationClip clip; public AnimationClipInfoProperties clipInfo; public int eventIndex; static public AnimationWindowEvent CreateAndEdit(GameObject root, AnimationClip clip, float time) { AnimationEvent animationEvent = new AnimationEvent(); animationEvent.time = time; // Or add a new one AnimationEvent[] events = AnimationUtility.GetAnimationEvents(clip); int eventIndex = InsertAnimationEvent(ref events, clip, animationEvent); AnimationWindowEvent animationWindowEvent = CreateInstance(); animationWindowEvent.hideFlags = HideFlags.HideInHierarchy; animationWindowEvent.name = "Animation Event"; animationWindowEvent.root = root; animationWindowEvent.clip = clip; animationWindowEvent.clipInfo = null; animationWindowEvent.eventIndex = eventIndex; return animationWindowEvent; } static public AnimationWindowEvent Edit(GameObject root, AnimationClip clip, int eventIndex) { AnimationWindowEvent animationWindowEvent = CreateInstance(); animationWindowEvent.hideFlags = HideFlags.HideInHierarchy; animationWindowEvent.name = "Animation Event"; animationWindowEvent.root = root; animationWindowEvent.clip = clip; animationWindowEvent.clipInfo = null; animationWindowEvent.eventIndex = eventIndex; return animationWindowEvent; } static public AnimationWindowEvent Edit(AnimationClipInfoProperties clipInfo, int eventIndex) { AnimationWindowEvent animationWindowEvent = CreateInstance(); animationWindowEvent.hideFlags = HideFlags.HideInHierarchy; animationWindowEvent.name = "Animation Event"; animationWindowEvent.root = null; animationWindowEvent.clip = null; animationWindowEvent.clipInfo = clipInfo; animationWindowEvent.eventIndex = eventIndex; return animationWindowEvent; } static private int InsertAnimationEvent(ref AnimationEvent[] events, AnimationClip clip, AnimationEvent evt) { Undo.RegisterCompleteObjectUndo(clip, "Add Event"); // Or add a new one int insertIndex = events.Length; for (int i = 0; i < events.Length; i++) { if (events[i].time > evt.time) { insertIndex = i; break; } } ArrayUtility.Insert(ref events, insertIndex, evt); AnimationUtility.SetAnimationEvents(clip, events); events = AnimationUtility.GetAnimationEvents(clip); if (events[insertIndex].time != evt.time || events[insertIndex].functionName != evt.functionName) Debug.LogError("Failed insertion"); return insertIndex; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowEventInspector.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Linq; using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Reflection; using System; using Object = UnityEngine.Object; namespace UnityEditor { [CustomEditor(typeof(AnimationWindowEvent))] [CanEditMultipleObjects] internal class AnimationWindowEventInspector : Editor { public static GUIContent s_OverloadWarning = EditorGUIUtility.TrTextContent("Some functions were overloaded in MonoBehaviour components and may not work as intended if used with Animation Events!"); public static GUIContent s_DuplicatesWarning = EditorGUIUtility.TrTextContent("Some functions have the same name across several Monobehaviour components and may not work as intended if used with Animation Events!"); const string kNotSupportedPostFix = " (Function Not Supported)"; const string kNoneSelected = "(No Function Selected)"; AnimationEventEditorState m_State = new(); public override void OnInspectorGUI() { var awes = targets.Select(o => o as AnimationWindowEvent).ToArray(); OnEditAnimationEvents(awes, m_State); } protected override void OnHeaderGUI() { string targetTitle = (targets.Length == 1) ? "Animation Event" : targets.Length + " Animation Events"; DrawHeaderGUI(this, targetTitle); } public static void OnEditAnimationEvent(AnimationWindowEvent awe, AnimationEventEditorState state) { OnEditAnimationEvents(new AnimationWindowEvent[] {awe}, state); } // These are used so we don't alloc new lists on every call static List supportedMethods; static List overloads; static List duplicates; public static void OnEditAnimationEvents(AnimationWindowEvent[] awEvents, AnimationEventEditorState state) { AnimationWindowEventData data = GetData(awEvents); if (data.events == null || data.selectedEvents == null || data.selectedEvents.Length == 0) return; AnimationEvent firstEvent = data.selectedEvents[0]; bool singleFunctionName = Array.TrueForAll(data.selectedEvents, evt => evt.functionName == firstEvent.functionName); EditorGUI.BeginChangeCheck(); if (data.root != null) { supportedMethods ??= new List(); overloads ??= new List(); duplicates ??= new List(); supportedMethods.Clear(); overloads.Clear(); duplicates.Clear(); CollectSupportedMethods(data.root, supportedMethods, overloads, duplicates); int selected = supportedMethods.FindIndex(method => method.Name == firstEvent.functionName); // A non-empty array used for rendering the contents of the popup // It is of size 1 greater than the list of supported methods to account for the "None" option string[] methodsFormatted = new string[supportedMethods.Count + 1]; for (int i = 0; i < supportedMethods.Count; ++i) { AnimationMethodMap methodMap = supportedMethods[i]; string menuPath = methodMap.methodMenuPath; methodsFormatted[i] = menuPath; } // Add a final option to set the function to no selected function int notSupportedIndex = supportedMethods.Count; if (selected == -1) { selected = notSupportedIndex; // Display that the current function is not supported if applicable if (string.IsNullOrEmpty(firstEvent.functionName)) methodsFormatted[notSupportedIndex] = kNoneSelected; else methodsFormatted[notSupportedIndex] = firstEvent.functionName + kNotSupportedPostFix; var emptyMethodMap = new AnimationMethodMap(); supportedMethods.Add(emptyMethodMap); } EditorGUIUtility.labelWidth = 130; EditorGUI.showMixedValue = !singleFunctionName; int wasSelected = singleFunctionName ? selected : -1; selected = EditorGUILayout.Popup("Function: ", selected, methodsFormatted); if (wasSelected != selected && selected != -1 && selected != notSupportedIndex) { foreach (var evt in data.selectedEvents) { evt.functionName = supportedMethods[selected].Name; evt.stringParameter = string.Empty; } } EditorGUI.showMixedValue = false; var selectedParameter = supportedMethods[selected].parameterType; if (singleFunctionName && selectedParameter != null) { EditorGUILayout.Space(); if (selectedParameter == typeof(AnimationEvent)) EditorGUILayout.PrefixLabel("Event Data"); else EditorGUILayout.PrefixLabel("Parameters"); DoEditRegularParameters(data.selectedEvents, selectedParameter); } if (overloads.Count > 0) { EditorGUILayout.Space(); EditorGUILayout.HelpBox(s_OverloadWarning.text, MessageType.Warning, true); state.ShowOverloadedFunctionsDetails = EditorGUILayout.Foldout(state.ShowOverloadedFunctionsDetails, "Show Details"); if (state.ShowOverloadedFunctionsDetails) { string overloadedFunctionDetails = "Overloaded Functions: \n" + GetFormattedMethodsText(overloads); GUILayout.Label(overloadedFunctionDetails, EditorStyles.helpBox); } } if (duplicates.Count > 0) { EditorGUILayout.Space(); EditorGUILayout.HelpBox(s_DuplicatesWarning.text, MessageType.Warning, true); state.ShowDuplicatedFunctionsDetails = EditorGUILayout.Foldout(state.ShowDuplicatedFunctionsDetails, "Show Details"); if (state.ShowDuplicatedFunctionsDetails) { string duplicatedFunctionDetails = "Duplicated Functions: \n" + GetFormattedMethodsText(duplicates); GUILayout.Label(duplicatedFunctionDetails, EditorStyles.helpBox); } } } else { EditorGUI.showMixedValue = !singleFunctionName; string oldFunctionName = singleFunctionName ? firstEvent.functionName : ""; string functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), oldFunctionName).Replace(" ", ""); if (functionName != oldFunctionName) { foreach (var evt in data.selectedEvents) { evt.functionName = functionName; } } EditorGUI.showMixedValue = false; if (singleFunctionName) { DoEditRegularParameters(data.selectedEvents, typeof(AnimationEvent)); } else { using (new EditorGUI.DisabledScope(true)) { AnimationEvent dummyEvent = new AnimationEvent(); DoEditRegularParameters(new AnimationEvent[] { dummyEvent }, typeof(AnimationEvent)); } } } if (EditorGUI.EndChangeCheck()) SetData(awEvents, data); } static string GetFormattedMethodsText(List methods) { string text = ""; foreach (AnimationMethodMap methodMap in methods) { text += string.Format("{0}.{1} ( {2} )\n", methodMap.sourceBehaviour.GetType().Name, methodMap.Name, GetTypeName(methodMap.parameterType)); } text = text.Trim(); return text; } static string GetTypeName(Type t) { if (t == null) return ""; if (t == typeof(int)) return "int"; if (t == typeof(float)) return "float"; if (t == typeof(string)) return "string"; if (t == typeof(bool)) return "bool"; return t.Name; } static string GetFormattedMethodName(AnimationMethodMap methodMap) { string targetName = methodMap.sourceBehaviour.GetType().Name; string methodName = methodMap.Name; string args = GetTypeName(methodMap.parameterType); if (methodName.StartsWith("set_") || methodName.StartsWith("get_")) return string.Format("{0}/Properties/{1} ( {2} )", targetName, methodName, args); else return string.Format("{0}/Methods/{1} ( {2} )", targetName, methodName, args); } public static void OnDisabledAnimationEvent() { AnimationEvent dummyEvent = new AnimationEvent(); using (new EditorGUI.DisabledScope(true)) { dummyEvent.functionName = EditorGUILayout.TextField(EditorGUIUtility.TrTextContent("Function"), dummyEvent.functionName); DoEditRegularParameters(new AnimationEvent[] { dummyEvent }, typeof(AnimationEvent)); } } static Dictionary> s_TypeAnimationMethodMapCache = new Dictionary>(); static void CollectSupportedMethods(GameObject gameObject, List supportedMethods, List overloadedMethods, List duplicatedMethods) { if (gameObject == null) return; MonoBehaviour[] behaviours = gameObject.GetComponents(); foreach (MonoBehaviour behaviour in behaviours) { if (behaviour == null) continue; Type type = behaviour.GetType(); while (type != typeof(MonoBehaviour) && type != null) { if (!s_TypeAnimationMethodMapCache.TryGetValue(type, out IReadOnlyList validMethods)) { var pendingValidMethods = new List(); MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly ); for (int i = 0; i < methods.Length; i++) { MethodInfo method = methods[i]; string name = method.Name; if (!IsSupportedMethodName(name)) continue; ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length > 1) continue; Type parameterType = null; if (parameters.Length == 1) { parameterType = parameters[0].ParameterType; if (!(parameterType == typeof(string) || parameterType == typeof(float) || parameterType == typeof(int) || parameterType == typeof(AnimationEvent) || parameterType == typeof(UnityEngine.Object) || parameterType.IsSubclassOf(typeof(UnityEngine.Object)) || parameterType.IsEnum)) continue; } AnimationMethodMap newMethodMap = new AnimationMethodMap { sourceBehaviour = behaviour, methodInfo = method, parameterType = parameterType }; newMethodMap.methodMenuPath = GetFormattedMethodName(newMethodMap); pendingValidMethods.Add(newMethodMap); } validMethods = pendingValidMethods.AsReadOnly(); s_TypeAnimationMethodMapCache.Add(type, validMethods); } foreach (var method in validMethods) { // Since AnimationEvents only stores method name, it can't handle functions with multiple overloads. // or functions with the same name across multiple monobehaviours // Only retrieve first found method, and discard overloads and duplicate names. int existingMethodIndex = supportedMethods.FindIndex(m => m.Name == method.Name); if (existingMethodIndex != -1) { // The method is only ambiguous if it has a different signature to the one we saw before if (supportedMethods[existingMethodIndex].parameterType != method.parameterType) { overloadedMethods.Add(method); } // Otherwise, there is another monobehaviour with the same method name. else { duplicatedMethods.Add(method); } } else { supportedMethods.Add(method); } } type = type.BaseType; } } } /// /// Maps the methodInfo and paramter type of a considered animation method to a source monobeheaviour. /// Mimics the structure of /// struct AnimationMethodMap { public Object sourceBehaviour; public MethodInfo methodInfo; public Type parameterType; // Used for caching public string methodMenuPath; public string Name => methodInfo?.Name ?? ""; } public static string FormatEvent(GameObject root, AnimationEvent evt) { if (string.IsNullOrEmpty(evt.functionName)) return kNoneSelected; if (!IsSupportedMethodName(evt.functionName)) return evt.functionName + kNotSupportedPostFix; if (root == null) return evt.functionName + kNotSupportedPostFix; foreach (var behaviour in root.GetComponents()) { if (behaviour == null) continue; var type = behaviour.GetType(); if (type == typeof(MonoBehaviour) || (type.BaseType != null && type.BaseType.Name == "GraphBehaviour")) continue; MethodInfo method = null; try { method = type.GetMethod(evt.functionName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); } catch (AmbiguousMatchException) { } if (method == null) continue; var parameterTypes = method.GetParameters(); return evt.functionName + FormatEventArguments(parameterTypes, evt); } return evt.functionName + kNotSupportedPostFix; } private static void DoEditRegularParameters(AnimationEvent[] events, Type selectedParameter) { AnimationEvent firstEvent = events[0]; if (selectedParameter == typeof(AnimationEvent) || selectedParameter == typeof(float)) { bool singleParamValue = Array.TrueForAll(events, evt => evt.floatParameter == firstEvent.floatParameter); EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = !singleParamValue; float newValue = EditorGUILayout.FloatField("Float", firstEvent.floatParameter); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { foreach (var evt in events) evt.floatParameter = newValue; } } if (selectedParameter == typeof(AnimationEvent) || selectedParameter == typeof(int) || selectedParameter.IsEnum) { bool singleParamValue = Array.TrueForAll(events, evt => evt.intParameter == firstEvent.intParameter); EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = !singleParamValue; int newValue = 0; if (selectedParameter.IsEnum) newValue = EnumPopup("Enum", selectedParameter, firstEvent.intParameter); else newValue = EditorGUILayout.IntField("Int", firstEvent.intParameter); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { foreach (var evt in events) evt.intParameter = newValue; } } if (selectedParameter == typeof(AnimationEvent) || selectedParameter == typeof(string)) { bool singleParamValue = Array.TrueForAll(events, evt => evt.stringParameter == firstEvent.stringParameter); EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = !singleParamValue; string newValue = EditorGUILayout.TextField("String", firstEvent.stringParameter); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { foreach (var evt in events) evt.stringParameter = newValue; } } if (selectedParameter == typeof(AnimationEvent) || selectedParameter.IsSubclassOf(typeof(UnityEngine.Object)) || selectedParameter == typeof(UnityEngine.Object)) { bool singleParamValue = Array.TrueForAll(events, evt => evt.objectReferenceParameter == firstEvent.objectReferenceParameter); EditorGUI.BeginChangeCheck(); Type type = typeof(UnityEngine.Object); if (selectedParameter != typeof(AnimationEvent)) type = selectedParameter; EditorGUI.showMixedValue = !singleParamValue; bool allowSceneObjects = false; Object newValue = EditorGUILayout.ObjectField(ObjectNames.NicifyVariableName(type.Name), firstEvent.objectReferenceParameter, type, allowSceneObjects); EditorGUI.showMixedValue = false; if (EditorGUI.EndChangeCheck()) { foreach (var evt in events) evt.objectReferenceParameter = newValue; } } } private static int EnumPopup(string label, Type enumType, int selected) { if (!enumType.IsEnum) throw new Exception("parameter _enum must be of type System.Enum"); string[] enumStrings = System.Enum.GetNames(enumType); int i = System.Array.IndexOf(enumStrings, Enum.GetName(enumType, selected)); i = EditorGUILayout.Popup(label, i, enumStrings, EditorStyles.popup); if (i == -1) return selected; else { System.Enum res = (System.Enum)Enum.Parse(enumType, enumStrings[i]); return Convert.ToInt32(res); } } private static bool IsSupportedMethodName(string name) { return name != "Main" && name != "Start" && name != "Awake" && name != "Update"; } private static string FormatEventArguments(ParameterInfo[] paramTypes, AnimationEvent evt) { if (paramTypes.Length == 0) return " ( )"; if (paramTypes.Length > 1) return kNotSupportedPostFix; var paramType = paramTypes[0].ParameterType; if (paramType == typeof(string)) return " ( \"" + evt.stringParameter + "\" )"; if (paramType == typeof(float)) return " ( " + evt.floatParameter + " )"; if (paramType == typeof(int)) return " ( " + evt.intParameter + " )"; if (paramType.IsEnum) return " ( " + paramType.Name + "." + Enum.GetName(paramType, evt.intParameter) + " )"; if (paramType == typeof(AnimationEvent)) return " ( " + evt.floatParameter + " / " + evt.intParameter + " / \"" + evt.stringParameter + "\" / " + (evt.objectReferenceParameter == null ? "null" : evt.objectReferenceParameter.name) + " )"; if (paramType.IsSubclassOf(typeof(UnityEngine.Object)) || paramType == typeof(UnityEngine.Object)) return " ( " + (evt.objectReferenceParameter == null ? "null" : evt.objectReferenceParameter.name) + " )"; return kNotSupportedPostFix; } private struct AnimationWindowEventData { public GameObject root; public AnimationClip clip; public AnimationClipInfoProperties clipInfo; public AnimationEvent[] events; public AnimationEvent[] selectedEvents; } // this are used so we don't alloc new lists on every call static List getDataSelectedEvents; private static AnimationWindowEventData GetData(AnimationWindowEvent[] awEvents) { var data = new AnimationWindowEventData(); if (awEvents.Length == 0) return data; AnimationWindowEvent firstAwEvent = awEvents[0]; data.root = firstAwEvent.root; data.clip = firstAwEvent.clip; data.clipInfo = firstAwEvent.clipInfo; if (data.clip != null) data.events = AnimationUtility.GetAnimationEvents(data.clip); else if (data.clipInfo != null) data.events = data.clipInfo.GetEvents(); if (data.events != null) { getDataSelectedEvents ??= new List(); getDataSelectedEvents.Clear(); foreach (var awEvent in awEvents) { if (awEvent.eventIndex >= 0 && awEvent.eventIndex < data.events.Length) getDataSelectedEvents.Add(data.events[awEvent.eventIndex]); } data.selectedEvents = getDataSelectedEvents.ToArray(); } return data; } private static void SetData(AnimationWindowEvent[] awEvents, AnimationWindowEventData data) { if (data.events == null) return; if (data.clip != null) { Undo.RegisterCompleteObjectUndo(data.clip, "Animation Event Change"); AnimationUtility.SetAnimationEvents(data.clip, data.events); } else if (data.clipInfo != null) { foreach (var awEvent in awEvents) { if (awEvent.eventIndex >= 0 && awEvent.eventIndex < data.events.Length) data.clipInfo.SetEvent(awEvent.eventIndex, data.events[awEvent.eventIndex]); } } } [MenuItem("CONTEXT/AnimationWindowEvent/Reset", secondaryPriority = 7)] static void ResetValues(MenuCommand command) { AnimationWindowEvent awEvent = command.context as AnimationWindowEvent; AnimationWindowEvent[] awEvents = new AnimationWindowEvent[] { awEvent }; AnimationWindowEventData data = GetData(awEvents); if (data.events == null || data.selectedEvents == null || data.selectedEvents.Length == 0) return; foreach (var evt in data.selectedEvents) { evt.functionName = ""; evt.stringParameter = string.Empty; evt.floatParameter = 0f; evt.intParameter = 0; evt.objectReferenceParameter = null; } SetData(awEvents, data); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchy.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections.Generic; using UnityEditor.IMGUI.Controls; using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewState = UnityEditor.IMGUI.Controls.TreeViewState; namespace UnityEditorInternal { [System.Serializable] internal class AnimationWindowHierarchyState : TreeViewState { private List m_TallInstanceIDs = new List(); public bool GetTallMode(AnimationWindowHierarchyNode node) { return m_TallInstanceIDs.Contains(node.id); } public void SetTallMode(AnimationWindowHierarchyNode node, bool tallMode) { if (tallMode) m_TallInstanceIDs.Add(node.id); else m_TallInstanceIDs.Remove(node.id); } public int GetTallInstancesCount() { return m_TallInstanceIDs.Count; } public void AddTallInstance(int id) { if (!m_TallInstanceIDs.Contains(id)) m_TallInstanceIDs.Add(id); } } internal class AnimationWindowHierarchy { // Animation window shared state private AnimationWindowState m_State; private TreeViewController m_TreeView; private AnimationWindowHierarchyGUI m_HierarchyGUI; public Vector2 GetContentSize() { return m_TreeView.GetContentSize(); } public Rect GetTotalRect() { return m_TreeView.GetTotalRect(); } public AnimationWindowHierarchy(AnimationWindowState state, EditorWindow owner, Rect position) { m_State = state; Init(owner, position); } public void OnGUI(Rect position) { m_TreeView.OnEvent(); m_TreeView.OnGUI(position, GUIUtility.GetControlID(FocusType.Keyboard)); m_HierarchyGUI.ReclaimPendingFieldFocus(); } public void Init(EditorWindow owner, Rect rect) { if (m_State.hierarchyState == null) m_State.hierarchyState = new AnimationWindowHierarchyState(); m_TreeView = new TreeViewController(owner, m_State.hierarchyState); m_State.hierarchyData = new AnimationWindowHierarchyDataSource(m_TreeView, m_State); m_HierarchyGUI = new AnimationWindowHierarchyGUI(m_TreeView, m_State); m_TreeView.Init(rect, m_State.hierarchyData, m_HierarchyGUI, null ); m_TreeView.deselectOnUnhandledMouseDown = true; m_TreeView.selectionChangedCallback += m_State.OnHierarchySelectionChanged; m_TreeView.ReloadData(); } virtual internal bool IsRenamingNodeAllowed(TreeViewItem node) { return true; } public bool IsIDVisible(int id) { if (m_TreeView == null) return false; var rows = m_TreeView.data.GetRows(); return TreeViewController.GetIndexOfID(rows, id) >= 0; } public void EndNameEditing(bool acceptChanges) { m_TreeView.EndNameEditing(acceptChanges); } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyDataSource.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using System.Collections.Generic; using UnityEditor.IMGUI.Controls; using UnityEngine; using Object = UnityEngine.Object; using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewUtility = UnityEditor.IMGUI.Controls.TreeViewUtility; using TreeViewDataSource = UnityEditor.IMGUI.Controls.TreeViewDataSource; namespace UnityEditorInternal { internal class AnimationWindowHierarchyDataSource : TreeViewDataSource { // Animation window shared state private AnimationWindowState state { get; set; } public bool showAll { get; set; } public AnimationWindowHierarchyDataSource(TreeViewController treeView, AnimationWindowState animationWindowState) : base(treeView) { state = animationWindowState; } private void SetupRootNodeSettings() { showRootItem = false; rootIsCollapsable = false; SetExpanded(m_RootItem, true); } private AnimationWindowHierarchyNode GetEmptyRootNode() { return new AnimationWindowHierarchyNode(0, -1, null, null, "", "", "root"); } public override void FetchData() { m_RootItem = GetEmptyRootNode(); SetupRootNodeSettings(); m_NeedRefreshRows = true; if (state.selection.disabled) { root.children = null; return; } List childNodes = new List(); if (state.filteredCurves.Count > 0) { AnimationWindowHierarchyMasterNode masterNode = new AnimationWindowHierarchyMasterNode(); masterNode.curves = state.filteredCurves.ToArray(); childNodes.Add(masterNode); } childNodes.AddRange(CreateTreeFromCurves()); childNodes.Add(new AnimationWindowHierarchyAddButtonNode()); TreeViewUtility.SetChildParentReferences(new List(childNodes.ToArray()), root); } public override bool IsRenamingItemAllowed(TreeViewItem item) { if (item is AnimationWindowHierarchyAddButtonNode || item is AnimationWindowHierarchyMasterNode || item is AnimationWindowHierarchyClipNode) return false; return (item as AnimationWindowHierarchyNode).path.Length != 0; } public List CreateTreeFromCurves() { List nodes = new List(); List singlePropertyCurves = new List(); List curves = state.filteredCurves; AnimationWindowHierarchyNode parentNode = (AnimationWindowHierarchyNode)m_RootItem; SerializedObject so = null; for (int i = 0; i < curves.Count; i++) { AnimationWindowCurve curve = curves[i]; if (!state.ShouldShowCurve(curve)) continue; AnimationWindowCurve nextCurve = i < curves.Count - 1 ? curves[i + 1] : null; if (curve.isSerializeReferenceCurve && state.activeRootGameObject != null) { var animatedObject = AnimationUtility.GetAnimatedObject(state.activeRootGameObject, curve.binding); if (animatedObject != null && (so == null || so.targetObject != animatedObject)) so = new SerializedObject(animatedObject); } singlePropertyCurves.Add(curve); bool areSameGroup = nextCurve != null && AnimationWindowUtility.GetPropertyGroupName(nextCurve.propertyName) == AnimationWindowUtility.GetPropertyGroupName(curve.propertyName); bool areSamePathAndType = nextCurve != null && curve.path.Equals(nextCurve.path) && curve.type == nextCurve.type; // We expect curveBindings to come sorted by propertyname // So we compare curve vs nextCurve. If its different path or different group (think "scale.xyz" as group), then we know this is the last element of such group. if (i == curves.Count - 1 || !areSameGroup || !areSamePathAndType) { if (singlePropertyCurves.Count > 1) nodes.Add(AddPropertyGroupToHierarchy(singlePropertyCurves.ToArray(), parentNode, so)); else nodes.Add(AddPropertyToHierarchy(singlePropertyCurves[0], parentNode, so)); singlePropertyCurves.Clear(); } } return nodes; } private AnimationWindowHierarchyPropertyGroupNode AddPropertyGroupToHierarchy(AnimationWindowCurve[] curves, AnimationWindowHierarchyNode parentNode, SerializedObject so) { List childNodes = new List(); System.Type animatableObjectType = curves[0].type; AnimationWindowHierarchyPropertyGroupNode node = new AnimationWindowHierarchyPropertyGroupNode(animatableObjectType, 0, AnimationWindowUtility.GetPropertyGroupName(curves[0].propertyName), curves[0].path, parentNode, AnimationWindowUtility.GetNicePropertyGroupDisplayName(curves[0].binding, so)); node.icon = GetIcon(curves[0].binding); node.indent = curves[0].depth; node.curves = curves; foreach (AnimationWindowCurve curve in curves) { AnimationWindowHierarchyPropertyNode childNode = AddPropertyToHierarchy(curve, node, so); // For child nodes we do not want to display the type in front (It is already shown by the group node) childNode.displayName = AnimationWindowUtility.GetPropertyDisplayName(childNode.propertyName); childNodes.Add(childNode); } TreeViewUtility.SetChildParentReferences(new List(childNodes.ToArray()), node); return node; } private AnimationWindowHierarchyPropertyNode AddPropertyToHierarchy(AnimationWindowCurve curve, AnimationWindowHierarchyNode parentNode, SerializedObject so) { AnimationWindowHierarchyPropertyNode node = new AnimationWindowHierarchyPropertyNode(curve.type, 0, curve.propertyName, curve.path, parentNode, curve.binding, curve.isPPtrCurve, AnimationWindowUtility.GetNicePropertyDisplayName(curve.binding, so)); if (parentNode.icon != null) node.icon = parentNode.icon; else node.icon = GetIcon(curve.binding); node.indent = curve.depth; node.curves = new[] { curve }; return node; } public Texture2D GetIcon(EditorCurveBinding curveBinding) { return AssetPreview.GetMiniTypeThumbnail(curveBinding.type); } public void UpdateSerializeReferenceCurvesArrayNiceDisplayName() { if (state.activeRootGameObject == null) return; //This is required in the case that there might have been a topological change //leading to a different display name(topological path) SerializedObject so = null; foreach (AnimationWindowHierarchyNode hierarchyNode in GetRows()) { if (hierarchyNode.curves != null) { foreach (var curve in hierarchyNode.curves) { if (curve.isSerializeReferenceCurve && hierarchyNode.displayName.Contains(".Array.data[")) { var animatedObject = AnimationUtility.GetAnimatedObject(state.activeRootGameObject, curve.binding); if (animatedObject != null && (so == null || so.targetObject != animatedObject)) so = new SerializedObject(animatedObject); hierarchyNode.displayName = AnimationWindowUtility.GetNicePropertyDisplayName(curve.binding, so); } } } } } public void UpdateData() { m_TreeView.ReloadData(); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyGUI.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.Linq; using UnityEditor.IMGUI.Controls; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; using TreeViewGUI = UnityEditor.IMGUI.Controls.TreeViewGUI; using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController; namespace UnityEditorInternal { internal class AnimationWindowHierarchyGUI : TreeViewGUI { public AnimationWindowState state { get; set; } readonly GUIContent k_AnimatePropertyLabel = EditorGUIUtility.TrTextContent("Add Property"); private GUIStyle m_AnimationRowEvenStyle; private GUIStyle m_AnimationRowOddStyle; private GUIStyle m_AnimationSelectionTextField; private GUIStyle m_AnimationCurveDropdown; private bool m_StyleInitialized; private AnimationWindowHierarchyNode m_RenamedNode; private Color m_LightSkinPropertyTextColor = new Color(.35f, .35f, .35f); private Color m_PhantomCurveColor = new Color(0f, 153f / 255f, 153f / 255f); private int[] m_HierarchyItemFoldControlIDs; private int[] m_HierarchyItemValueControlIDs; private int[] m_HierarchyItemButtonControlIDs; private bool m_NeedsToReclaimFieldFocus; private int m_FieldToReclaimFocus; private const float k_RowRightOffset = 10; private const float k_ValueFieldDragWidth = 15; private const float k_ValueFieldWidth = 80; private const float k_ValueFieldOffsetFromRightSide = 100; private const float k_ColorIndicatorTopMargin = 3; public static readonly float k_DopeSheetRowHeight = EditorGUI.kSingleLineHeight; public static readonly float k_DopeSheetRowHeightTall = k_DopeSheetRowHeight * 2f; public const float k_AddCurveButtonNodeHeight = 40f; public const float k_RowBackgroundColorBrightness = 0.28f; private const float k_SelectedPhantomCurveColorMultiplier = 1.4f; private const float k_CurveColorIndicatorIconSize = 11; private readonly static Color k_KeyColorInDopesheetMode = new Color(0.7f, 0.7f, 0.7f, 1); private readonly static Color k_KeyColorForNonCurves = new Color(0.7f, 0.7f, 0.7f, 0.5f); private readonly static Color k_LeftoverCurveColor = Color.yellow; private static readonly string k_DefaultValue = L10n.Tr(" (Default Value)"); private static readonly string k_TransformPosition = L10n.Tr("Transform position, rotation and scale can't be partially animated. This value will be animated to the default value"); private static readonly string k_Missing = L10n.Tr(" (Missing!)"); private static readonly string k_GameObjectComponentMissing = L10n.Tr("The GameObject or Component is missing ({0})"); private static readonly string k_DuplicateGameObjectName = L10n.Tr(" (Duplicate GameObject name!)"); private static readonly string k_TargetForCurveIsAmbigous = L10n.Tr("Target for curve is ambiguous since there are multiple GameObjects with same name ({0})"); private static readonly string k_RemoveProperties = L10n.Tr("Remove Properties"); private static readonly string k_RemoveProperty = L10n.Tr("Remove Property"); private static readonly string k_AddKey = L10n.Tr("Add Key"); private static readonly string k_DeleteKey = L10n.Tr("Delete Key"); private static readonly string k_RemoveCurve = L10n.Tr("Remove Curve"); internal static int s_WasInsideValueRectFrame = -1; public AnimationWindowHierarchyGUI(TreeViewController treeView, AnimationWindowState state) : base(treeView) { this.state = state; InitStyles(); } protected void InitStyles() { if (!m_StyleInitialized) { m_AnimationRowEvenStyle = "AnimationRowEven"; m_AnimationRowOddStyle = "AnimationRowOdd"; m_AnimationSelectionTextField = "AnimationSelectionTextField"; lineStyle = Styles.lineStyle; lineStyle.padding.left = 0; m_AnimationCurveDropdown = "AnimPropDropdown"; m_StyleInitialized = true; } } protected void DoNodeGUI(Rect rect, AnimationWindowHierarchyNode node, bool selected, bool focused, int row) { InitStyles(); if (node is AnimationWindowHierarchyMasterNode) return; float indent = k_BaseIndent + (node.depth + node.indent) * k_IndentWidth; if (node is AnimationWindowHierarchyAddButtonNode) { if (Event.current.type == EventType.MouseMove && s_WasInsideValueRectFrame >= 0) { if (s_WasInsideValueRectFrame >= Time.frameCount - 1) Event.current.Use(); else s_WasInsideValueRectFrame = -1; } using (new EditorGUI.DisabledScope(!state.selection.canAddCurves)) { DoAddCurveButton(rect, node, row); } } else { DoRowBackground(rect, row); DoIconAndName(rect, node, selected, focused, indent); DoFoldout(node, rect, indent, row); bool enabled = false; if (node.curves != null) { enabled = !Array.Exists(node.curves, curve => curve.animationIsEditable == false); } using (new EditorGUI.DisabledScope(!enabled)) { DoValueField(rect, node, row); } DoCurveDropdown(rect, node, row, enabled); HandleContextMenu(rect, node, enabled); DoCurveColorIndicator(rect, node); } EditorGUIUtility.SetIconSize(Vector2.zero); } public override void BeginRowGUI() { base.BeginRowGUI(); HandleDelete(); // Reserve unique control ids. // This is required to avoid changing control ids mapping as we scroll in the tree view // and change items visibility. Hopefully, we should be able to remove it entirely if we // isolate the animation window layouts in separate IMGUIContainers... int rowCount = m_TreeView.data.rowCount; m_HierarchyItemFoldControlIDs = new int[rowCount]; m_HierarchyItemValueControlIDs = new int[rowCount]; m_HierarchyItemButtonControlIDs = new int[rowCount]; for (int i = 0; i < rowCount; ++i) { var propertyNode = m_TreeView.data.GetItem(i) as AnimationWindowHierarchyPropertyNode; if (propertyNode != null && !propertyNode.isPptrNode) m_HierarchyItemValueControlIDs[i] = GUIUtility.GetControlID(FocusType.Keyboard); else m_HierarchyItemValueControlIDs[i] = 0; // not needed. m_HierarchyItemFoldControlIDs[i] = GUIUtility.GetControlID(FocusType.Passive); m_HierarchyItemButtonControlIDs[i] = GUIUtility.GetControlID(FocusType.Passive); } } private void DoAddCurveButton(Rect rect, AnimationWindowHierarchyNode node, int row) { const int k_ButtonWidth = 230; float xMargin = (rect.width - k_ButtonWidth) / 2f; float yMargin = 10f; Rect rectWithMargin = new Rect(rect.xMin + xMargin, rect.yMin + yMargin, rect.width - xMargin * 2f, rect.height - yMargin * 2f); // case 767863. // This control id is unique to the hierarchy node it refers to. // The tree view only renders the elements that are visible, and will cause // the control id counter to shift when scrolling through the view. if (DoTreeViewButton(m_HierarchyItemButtonControlIDs[row], rectWithMargin, k_AnimatePropertyLabel, GUI.skin.button)) { if (AddCurvesPopup.ShowAtPosition(rectWithMargin, state, OnNewCurveAdded)) { GUIUtility.ExitGUI(); } } } private void OnNewCurveAdded(AddCurvesPopupPropertyNode node) { } private void DoRowBackground(Rect rect, int row) { if (Event.current.type != EventType.Repaint) return; // Different background for even rows if (row % 2 == 0) m_AnimationRowEvenStyle.Draw(rect, false, false, false, false); else m_AnimationRowOddStyle.Draw(rect, false, false, false, false); } // Draw foldout (after text content above to ensure drop down icon is rendered above selection highlight) private void DoFoldout(AnimationWindowHierarchyNode node, Rect rect, float indent, int row) { if (m_TreeView.data.IsExpandable(node)) { Rect toggleRect = rect; toggleRect.x = indent; toggleRect.width = foldoutStyleWidth; EditorGUI.BeginChangeCheck(); bool newExpandedValue = GUI.Toggle(toggleRect, m_HierarchyItemFoldControlIDs[row], m_TreeView.data.IsExpanded(node), GUIContent.none, foldoutStyle); if (EditorGUI.EndChangeCheck()) { if (Event.current.alt) m_TreeView.data.SetExpandedWithChildren(node, newExpandedValue); else m_TreeView.data.SetExpanded(node, newExpandedValue); } } else { AnimationWindowHierarchyPropertyNode hierarchyPropertyNode = node as AnimationWindowHierarchyPropertyNode; AnimationWindowHierarchyState hierarchyState = m_TreeView.state as AnimationWindowHierarchyState; if (hierarchyPropertyNode != null && hierarchyPropertyNode.isPptrNode) { Rect toggleRect = rect; toggleRect.x = indent; toggleRect.width = foldoutStyleWidth; EditorGUI.BeginChangeCheck(); bool tallMode = hierarchyState.GetTallMode(hierarchyPropertyNode); tallMode = GUI.Toggle(toggleRect, m_HierarchyItemFoldControlIDs[row], tallMode, GUIContent.none, foldoutStyle); if (EditorGUI.EndChangeCheck()) hierarchyState.SetTallMode(hierarchyPropertyNode, tallMode); } } } private void DoIconAndName(Rect rect, AnimationWindowHierarchyNode node, bool selected, bool focused, float indent) { EditorGUIUtility.SetIconSize(new Vector2(13, 13)); // If not set we see icons scaling down if text is being cropped // TODO: All this is horrible. SHAME FIX! if (Event.current.type == EventType.Repaint) { if (selected) selectionStyle.Draw(rect, false, false, true, focused); // Leave some space for the value field that comes after. if (node is AnimationWindowHierarchyPropertyNode) rect.width -= k_ValueFieldOffsetFromRightSide + 2; bool isLeftOverCurve = AnimationWindowUtility.IsNodeLeftOverCurve(state, node); bool isAmbiguous = AnimationWindowUtility.IsNodeAmbiguous(node); bool isPhantom = AnimationWindowUtility.IsNodePhantom(node); string warningText = ""; string tooltipText = ""; if (isPhantom) { warningText = k_DefaultValue; tooltipText = k_TransformPosition; } if (isLeftOverCurve) { warningText = k_Missing; tooltipText = string.Format(k_GameObjectComponentMissing, node.path); } if (isAmbiguous) { warningText = k_DuplicateGameObjectName; tooltipText = string.Format(k_TargetForCurveIsAmbigous, node.path); } Color oldColor = lineStyle.normal.textColor; Color textColor = oldColor; if (node.depth == 0) { string nodePrefix = ""; if (node.curves.Length > 0) { AnimationWindowSelectionItem selectionBinding = node.curves[0].selectionBinding; string gameObjectName = GetGameObjectName(selectionBinding != null ? selectionBinding.rootGameObject : null, node.path); nodePrefix = string.IsNullOrEmpty(gameObjectName) ? "" : gameObjectName + " : "; } Styles.content = new GUIContent(nodePrefix + node.displayName + warningText, GetIconForItem(node), tooltipText); textColor = EditorGUIUtility.isProSkin ? Color.gray * 1.35f : Color.black; } else { Styles.content = new GUIContent(node.displayName + warningText, GetIconForItem(node), tooltipText); textColor = EditorGUIUtility.isProSkin ? Color.gray : m_LightSkinPropertyTextColor; var phantomColor = selected ? m_PhantomCurveColor * k_SelectedPhantomCurveColorMultiplier : m_PhantomCurveColor; textColor = isPhantom ? phantomColor : textColor; } textColor = isLeftOverCurve || isAmbiguous ? k_LeftoverCurveColor : textColor; SetStyleTextColor(lineStyle, textColor); rect.xMin += (int)(indent + foldoutStyleWidth + lineStyle.margin.left); rect.yMin = rect.y + (rect.height - EditorGUIUtility.singleLineHeight) / 2; GUI.Label(rect, Styles.content, lineStyle); SetStyleTextColor(lineStyle, oldColor); } if (IsRenaming(node.id) && Event.current.type != EventType.Layout) GetRenameOverlay().editFieldRect = new Rect(rect.x + k_IndentWidth, rect.y, rect.width - k_IndentWidth - 1, rect.height); } private string GetGameObjectName(GameObject rootGameObject, string path) { if (string.IsNullOrEmpty(path)) return rootGameObject != null ? rootGameObject.name : ""; string[] splits = path.Split('/'); return splits[splits.Length - 1]; } private void DoValueField(Rect rect, AnimationWindowHierarchyNode node, int row) { bool curvesChanged = false; if (node is AnimationWindowHierarchyPropertyNode) { AnimationWindowCurve[] curves = node.curves; if (curves == null || curves.Length == 0) return; // We do valuefields for dopelines that only have single curve AnimationWindowCurve curve = curves[0]; object value = CurveBindingUtility.GetCurrentValue(state, curve); if (!curve.isPPtrCurve) { Rect valueFieldDragRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide - k_ValueFieldDragWidth, rect.y, k_ValueFieldDragWidth, rect.height); Rect valueFieldRect = new Rect(rect.xMax - k_ValueFieldOffsetFromRightSide, rect.y, k_ValueFieldWidth, rect.height); if (Event.current.type == EventType.MouseMove && valueFieldRect.Contains(Event.current.mousePosition)) s_WasInsideValueRectFrame = Time.frameCount; EditorGUI.BeginChangeCheck(); if (curve.valueType == typeof(bool)) { value = GUI.Toggle(valueFieldRect, m_HierarchyItemValueControlIDs[row], Convert.ToSingle(value) != 0f, GUIContent.none, EditorStyles.toggle) ? 1f : 0f; } else { int id = m_HierarchyItemValueControlIDs[row]; bool enterInTextField = (EditorGUIUtility.keyboardControl == id && EditorGUIUtility.editingTextField && Event.current.type == EventType.KeyDown && (Event.current.character == '\n' || (int)Event.current.character == 3)); // Force back keyboard focus to float field editor when editing it since the TreeView forces keyboard focus on itself at mouse down. // The focus will be reclaimed after the TreeViewController.OnGUI call. if (EditorGUI.s_RecycledEditor.controlID == id && Event.current.type == EventType.MouseDown && valueFieldRect.Contains(Event.current.mousePosition)) { m_NeedsToReclaimFieldFocus = true; m_FieldToReclaimFocus = id; } if (curve.isDiscreteCurve) { value = EditorGUI.DoIntField(EditorGUI.s_RecycledEditor, valueFieldRect, valueFieldDragRect, id, Convert.ToInt32(value), EditorGUI.kIntFieldFormatString, m_AnimationSelectionTextField, true, 0); if (enterInTextField) { GUI.changed = true; Event.current.Use(); } } else { value = EditorGUI.DoFloatField(EditorGUI.s_RecycledEditor, valueFieldRect, valueFieldDragRect, id, Convert.ToSingle(value), "g5", m_AnimationSelectionTextField, true); if (enterInTextField) { GUI.changed = true; Event.current.Use(); } var floatValue = Convert.ToSingle(value); if (float.IsInfinity(floatValue) || float.IsNaN(floatValue)) value = 0f; } } if (EditorGUI.EndChangeCheck()) { string undoLabel = "Edit Key"; AnimationKeyTime newAnimationKeyTime = AnimationKeyTime.Time(state.currentTime, curve.clip.frameRate); AnimationWindowUtility.AddKeyframeToCurve(curve, value, curve.valueType, newAnimationKeyTime); state.SaveCurve(curve.clip, curve, undoLabel); curvesChanged = true; } } } if (curvesChanged) { //Fix for case 1382193: Stop recording any candidates if a property value field is modified if (AnimationMode.IsRecordingCandidates()) state.ClearCandidates(); state.ResampleAnimation(); } } internal void ReclaimPendingFieldFocus() { if (m_NeedsToReclaimFieldFocus) { GUIUtility.keyboardControl = m_FieldToReclaimFocus; m_NeedsToReclaimFieldFocus = false; } } private bool DoTreeViewButton(int id, Rect position, GUIContent content, GUIStyle style) { Event evt = Event.current; EventType type = evt.GetTypeForControl(id); switch (type) { case EventType.Repaint: style.Draw(position, content, id, false, position.Contains(evt.mousePosition)); break; case EventType.MouseDown: if (position.Contains(evt.mousePosition) && evt.button == 0) { GUIUtility.hotControl = id; evt.Use(); } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { GUIUtility.hotControl = 0; evt.Use(); if (position.Contains(evt.mousePosition)) { return true; } } break; } return false; } private void DoCurveDropdown(Rect rect, AnimationWindowHierarchyNode node, int row, bool enabled) { rect = new Rect( rect.xMax - k_RowRightOffset - 12, rect.yMin + 2 + (rect.height - EditorGUIUtility.singleLineHeight) / 2, 22, 12); // case 767863. // This control id is unique to the hierarchy node it refers to. // The tree view only renders the elements that are visible, and will cause // the control id counter to shift when scrolling through the view. if (DoTreeViewButton(m_HierarchyItemButtonControlIDs[row], rect, GUIContent.none, m_AnimationCurveDropdown)) { state.SelectHierarchyItem(node.id, false, false); GenericMenu menu = GenerateMenu(new AnimationWindowHierarchyNode[] { node }.ToList(), enabled); menu.DropDown(rect); Event.current.Use(); } } private void DoCurveColorIndicator(Rect rect, AnimationWindowHierarchyNode node) { if (Event.current.type != EventType.Repaint) return; Color originalColor = GUI.color; if (!state.showCurveEditor) GUI.color = k_KeyColorInDopesheetMode; else if (node.curves.Length == 1 && !node.curves[0].isPPtrCurve) GUI.color = CurveUtility.GetPropertyColor(node.curves[0].binding.propertyName); else GUI.color = k_KeyColorForNonCurves; bool hasKey = false; if (state.previewing) { foreach (var curve in node.curves) { if (curve.keyframes.Any(key => state.time.ContainsTime(key.time))) { hasKey = true; } } } Texture icon = hasKey ? CurveUtility.GetIconKey() : CurveUtility.GetIconCurve(); rect = new Rect(rect.xMax - k_RowRightOffset - (k_CurveColorIndicatorIconSize / 2) - 5, rect.yMin + k_ColorIndicatorTopMargin + (rect.height - EditorGUIUtility.singleLineHeight) / 2, k_CurveColorIndicatorIconSize, k_CurveColorIndicatorIconSize); GUI.DrawTexture(rect, icon, ScaleMode.ScaleToFit, true, 1); GUI.color = originalColor; } private void HandleDelete() { if (m_TreeView.HasFocus()) { switch (Event.current.type) { case EventType.ExecuteCommand: if ((Event.current.commandName == EventCommandNames.SoftDelete || Event.current.commandName == EventCommandNames.Delete)) { if (Event.current.type == EventType.ExecuteCommand) RemoveCurvesFromSelectedNodes(); Event.current.Use(); } break; case EventType.KeyDown: if (Event.current.keyCode == KeyCode.Backspace || Event.current.keyCode == KeyCode.Delete) { RemoveCurvesFromSelectedNodes(); Event.current.Use(); } break; } } } private void HandleContextMenu(Rect rect, AnimationWindowHierarchyNode node, bool enabled) { if (Event.current.type != EventType.ContextClick) return; if (rect.Contains(Event.current.mousePosition)) { state.SelectHierarchyItem(node.id, true, true); //state.animationWindow.RefreshShownCurves (true); GenerateMenu(state.selectedHierarchyNodes, enabled).ShowAsContext(); Event.current.Use(); } } private GenericMenu GenerateMenu(List interactedNodes, bool enabled) { List curves = GetCurvesAffectedByNodes(interactedNodes, false); // Linked curves are like regular affected curves but always include transform siblings List linkedCurves = GetCurvesAffectedByNodes(interactedNodes, true); bool forceGroupRemove = curves.Count == 1 ? AnimationWindowUtility.ForceGrouping(curves[0].binding) : false; GenericMenu menu = new GenericMenu(); // Remove curves GUIContent removePropertyContent = new GUIContent(curves.Count > 1 || forceGroupRemove ? k_RemoveProperties : k_RemoveProperty); if (!enabled) menu.AddDisabledItem(removePropertyContent); else menu.AddItem(removePropertyContent, false, RemoveCurvesFromSelectedNodes); // Change rotation interpolation bool showInterpolation = true; EditorCurveBinding[] curveBindings = new EditorCurveBinding[linkedCurves.Count]; for (int i = 0; i < linkedCurves.Count; i++) curveBindings[i] = linkedCurves[i].binding; RotationCurveInterpolation.Mode rotationInterpolation = GetRotationInterpolationMode(curveBindings); if (rotationInterpolation == RotationCurveInterpolation.Mode.Undefined) { showInterpolation = false; } else { foreach (var node in interactedNodes) { if (!(node is AnimationWindowHierarchyPropertyGroupNode)) showInterpolation = false; } } if (showInterpolation) { string legacyWarning = state.activeAnimationClip.legacy ? " (Not fully supported in Legacy)" : ""; GenericMenu.MenuFunction2 nullMenuFunction2 = null; menu.AddItem(EditorGUIUtility.TrTextContent("Interpolation/Euler Angles" + legacyWarning), rotationInterpolation == RotationCurveInterpolation.Mode.RawEuler, enabled ? ChangeRotationInterpolation : nullMenuFunction2, RotationCurveInterpolation.Mode.RawEuler); menu.AddItem(EditorGUIUtility.TrTextContent("Interpolation/Euler Angles (Quaternion)"), rotationInterpolation == RotationCurveInterpolation.Mode.Baked, enabled ? ChangeRotationInterpolation : nullMenuFunction2, RotationCurveInterpolation.Mode.Baked); menu.AddItem(EditorGUIUtility.TrTextContent("Interpolation/Quaternion"), rotationInterpolation == RotationCurveInterpolation.Mode.NonBaked, enabled ? ChangeRotationInterpolation : nullMenuFunction2, RotationCurveInterpolation.Mode.NonBaked); } // Menu items that are only applicaple when in animation mode: if (state.previewing) { menu.AddSeparator(""); bool allHaveKeys = true; bool noneHaveKeys = true; foreach (AnimationWindowCurve curve in curves) { bool curveHasKey = curve.HasKeyframe(state.time); if (!curveHasKey) allHaveKeys = false; else noneHaveKeys = false; } string str; str = k_AddKey; if (allHaveKeys || !enabled) menu.AddDisabledItem(new GUIContent(str)); else menu.AddItem(new GUIContent(str), false, AddKeysAtCurrentTime, curves); str = k_DeleteKey; if (noneHaveKeys || !enabled) menu.AddDisabledItem(new GUIContent(str)); else menu.AddItem(new GUIContent(str), false, DeleteKeysAtCurrentTime, curves); } return menu; } private void AddKeysAtCurrentTime(object obj) { AddKeysAtCurrentTime((List)obj); } private void AddKeysAtCurrentTime(List curves) { AnimationWindowUtility.AddKeyframes(state, curves, state.time); } private void DeleteKeysAtCurrentTime(object obj) { DeleteKeysAtCurrentTime((List)obj); } private void DeleteKeysAtCurrentTime(List curves) { AnimationWindowUtility.RemoveKeyframes(state, curves, state.time); } private void ChangeRotationInterpolation(System.Object interpolationMode) { RotationCurveInterpolation.Mode mode = (RotationCurveInterpolation.Mode)interpolationMode; AnimationWindowCurve[] activeCurves = state.activeCurves.ToArray(); EditorCurveBinding[] curveBindings = new EditorCurveBinding[activeCurves.Length]; for (int i = 0; i < activeCurves.Length; i++) { curveBindings[i] = activeCurves[i].binding; } RotationCurveInterpolation.SetInterpolation(state.activeAnimationClip, curveBindings, mode); MaintainTreeviewStateAfterRotationInterpolation(mode); state.hierarchyData.ReloadData(); } private void RemoveCurvesFromSelectedNodes() { RemoveCurvesFromNodes(state.selectedHierarchyNodes); } private void RemoveCurvesFromNodes(List nodes) { string undoLabel = k_RemoveCurve; state.SaveKeySelection(undoLabel); foreach (var node in nodes) { AnimationWindowHierarchyNode hierarchyNode = (AnimationWindowHierarchyNode)node; if (hierarchyNode.parent is AnimationWindowHierarchyPropertyGroupNode && hierarchyNode.binding != null && AnimationWindowUtility.ForceGrouping((EditorCurveBinding)hierarchyNode.binding)) hierarchyNode = (AnimationWindowHierarchyNode)hierarchyNode.parent; if (hierarchyNode.curves == null) continue; List curves = null; // Property or propertygroup if (hierarchyNode is AnimationWindowHierarchyPropertyGroupNode || hierarchyNode is AnimationWindowHierarchyPropertyNode) curves = AnimationWindowUtility.FilterCurves(hierarchyNode.curves.ToArray(), hierarchyNode.path, hierarchyNode.animatableObjectType, hierarchyNode.propertyName); else curves = AnimationWindowUtility.FilterCurves(hierarchyNode.curves.ToArray(), hierarchyNode.path, hierarchyNode.animatableObjectType); foreach (AnimationWindowCurve animationWindowCurve in curves) state.RemoveCurve(animationWindowCurve, undoLabel); } m_TreeView.ReloadData(); state.controlInterface.ResampleAnimation(); } private List GetCurvesAffectedByNodes(List nodes, bool includeLinkedCurves) { List curves = new List(); foreach (var node in nodes) { AnimationWindowHierarchyNode hierarchyNode = node; if (hierarchyNode.parent is AnimationWindowHierarchyPropertyGroupNode && includeLinkedCurves) hierarchyNode = (AnimationWindowHierarchyNode)hierarchyNode.parent; if (hierarchyNode.curves == null) continue; if (hierarchyNode.curves.Length > 0) { // Property or propertygroup if (hierarchyNode is AnimationWindowHierarchyPropertyGroupNode || hierarchyNode is AnimationWindowHierarchyPropertyNode) curves.AddRange(AnimationWindowUtility.FilterCurves(hierarchyNode.curves, hierarchyNode.path, hierarchyNode.animatableObjectType, hierarchyNode.propertyName)); else curves.AddRange(AnimationWindowUtility.FilterCurves(hierarchyNode.curves, hierarchyNode.path, hierarchyNode.animatableObjectType)); } } return curves.Distinct().ToList(); } // Changing rotation interpolation will change the propertynames of the curves // Propertynames are used in treeview node IDs, so we need to anticipate the new IDs by injecting them into treeview state // This way treeview state (selection and expanding) will be preserved once the curve data is eventually reloaded private void MaintainTreeviewStateAfterRotationInterpolation(RotationCurveInterpolation.Mode newMode) { List selectedInstaceIDs = state.hierarchyState.selectedIDs; List expandedInstaceIDs = state.hierarchyState.expandedIDs; List oldIDs = new List(); List newIds = new List(); for (int i = 0; i < selectedInstaceIDs.Count; i++) { AnimationWindowHierarchyNode node = state.hierarchyData.FindItem(selectedInstaceIDs[i]) as AnimationWindowHierarchyNode; if (node != null && !node.propertyName.Equals(RotationCurveInterpolation.GetPrefixForInterpolation(newMode))) { string oldPrefix = node.propertyName.Split('.')[0]; string newPropertyName = node.propertyName.Replace(oldPrefix, RotationCurveInterpolation.GetPrefixForInterpolation(newMode)); // old treeview node id oldIDs.Add(selectedInstaceIDs[i]); // and its new replacement newIds.Add((node.path + node.animatableObjectType.Name + newPropertyName).GetHashCode()); } } // Replace old IDs with new ones for (int i = 0; i < oldIDs.Count; i++) { if (selectedInstaceIDs.Contains(oldIDs[i])) { int index = selectedInstaceIDs.IndexOf(oldIDs[i]); selectedInstaceIDs[index] = newIds[i]; } if (expandedInstaceIDs.Contains(oldIDs[i])) { int index = expandedInstaceIDs.IndexOf(oldIDs[i]); expandedInstaceIDs[index] = newIds[i]; } if (state.hierarchyState.lastClickedID == oldIDs[i]) state.hierarchyState.lastClickedID = newIds[i]; } state.hierarchyState.selectedIDs = new List(selectedInstaceIDs); state.hierarchyState.expandedIDs = new List(expandedInstaceIDs); } private RotationCurveInterpolation.Mode GetRotationInterpolationMode(EditorCurveBinding[] curves) { if (curves == null || curves.Length == 0) return RotationCurveInterpolation.Mode.Undefined; RotationCurveInterpolation.Mode mode = RotationCurveInterpolation.GetModeFromCurveData(curves[0]); for (int i = 1; i < curves.Length; i++) { RotationCurveInterpolation.Mode nextMode = RotationCurveInterpolation.GetModeFromCurveData(curves[i]); if (mode != nextMode) return RotationCurveInterpolation.Mode.Undefined; } return mode; } // TODO: Make real styles, not this private void SetStyleTextColor(GUIStyle style, Color color) { style.normal.textColor = color; style.focused.textColor = color; style.active.textColor = color; style.hover.textColor = color; } public override void GetFirstAndLastRowVisible(out int firstRowVisible, out int lastRowVisible) { firstRowVisible = 0; lastRowVisible = m_TreeView.data.rowCount - 1; } public float GetNodeHeight(AnimationWindowHierarchyNode node) { if (node is AnimationWindowHierarchyAddButtonNode) return k_AddCurveButtonNodeHeight; AnimationWindowHierarchyState hierarchyState = m_TreeView.state as AnimationWindowHierarchyState; return hierarchyState.GetTallMode(node) ? k_DopeSheetRowHeightTall : k_DopeSheetRowHeight; } public override Vector2 GetTotalSize() { var rows = m_TreeView.data.GetRows(); float height = 0f; for (int i = 0; i < rows.Count; i++) { AnimationWindowHierarchyNode node = rows[i] as AnimationWindowHierarchyNode; height += GetNodeHeight(node); } return new Vector2(1, height); } float GetTopPixelOfRow(int row, IList rows) { float top = 0f; for (int i = 0; i < row && i < rows.Count; i++) { AnimationWindowHierarchyNode node = rows[i] as AnimationWindowHierarchyNode; top += GetNodeHeight(node); } return top; } public override Rect GetRowRect(int row, float rowWidth) { var rows = m_TreeView.data.GetRows(); AnimationWindowHierarchyNode hierarchyNode = rows[row] as AnimationWindowHierarchyNode; if (hierarchyNode.topPixel == null) hierarchyNode.topPixel = GetTopPixelOfRow(row, rows); float rowHeight = GetNodeHeight(hierarchyNode); return new Rect(0, (float)hierarchyNode.topPixel, rowWidth, rowHeight); } public override void OnRowGUI(Rect rowRect, TreeViewItem node, int row, bool selected, bool focused) { AnimationWindowHierarchyNode hierarchyNode = node as AnimationWindowHierarchyNode; DoNodeGUI(rowRect, hierarchyNode, selected, focused, row); } override public bool BeginRename(TreeViewItem item, float delay) { m_RenamedNode = item as AnimationWindowHierarchyNode; return GetRenameOverlay().BeginRename(m_RenamedNode.path, item.id, delay); } override protected void SyncFakeItem() { //base.SyncFakeItem(); } override protected void RenameEnded() { var renameOverlay = GetRenameOverlay(); if (renameOverlay.userAcceptedRename) { string newName = renameOverlay.name; string oldName = renameOverlay.originalName; if (newName != oldName) { Undo.RecordObject(state.activeAnimationClip, "Rename Curve"); foreach (AnimationWindowCurve curve in m_RenamedNode.curves) { EditorCurveBinding newBinding = AnimationWindowUtility.GetRenamedBinding(curve.binding, newName); if (AnimationWindowUtility.CurveExists(newBinding, state.filteredCurves.ToArray())) { Debug.LogWarning("Curve already exists, renaming cancelled."); continue; } AnimationWindowUtility.RenameCurvePath(curve, newBinding, curve.clip); } } } m_RenamedNode = null; } override protected Texture GetIconForItem(TreeViewItem item) { if (item != null) return item.icon; return null; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowHierarchyNode.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEngine; using System.Collections.Generic; using UnityEditor.IMGUI.Controls; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; namespace UnityEditorInternal { internal class AnimationWindowHierarchyNode : TreeViewItem { public string path; public System.Type animatableObjectType; public string propertyName; public EditorCurveBinding? binding; public AnimationWindowCurve[] curves; public float? topPixel = null; public int indent = 0; public AnimationWindowHierarchyNode(int instanceID, int depth, TreeViewItem parent, System.Type animatableObjectType, string propertyName, string path, string displayName) : base(instanceID, depth, parent, displayName) { this.displayName = displayName; this.animatableObjectType = animatableObjectType; this.propertyName = propertyName; this.path = path; } } internal class AnimationWindowHierarchyPropertyGroupNode : AnimationWindowHierarchyNode { public AnimationWindowHierarchyPropertyGroupNode(System.Type animatableObjectType, int setId, string propertyName, string path, TreeViewItem parent, string displayName) : base(AnimationWindowUtility.GetPropertyNodeID(setId, path, animatableObjectType, propertyName), parent != null ? parent.depth + 1 : -1, parent, animatableObjectType, AnimationWindowUtility.GetPropertyGroupName(propertyName), path, displayName) {} } internal class AnimationWindowHierarchyPropertyNode : AnimationWindowHierarchyNode { public bool isPptrNode; public AnimationWindowHierarchyPropertyNode(System.Type animatableObjectType, int setId, string propertyName, string path, TreeViewItem parent, EditorCurveBinding binding, bool isPptrNode, string displayName) : base(AnimationWindowUtility.GetPropertyNodeID(setId, path, animatableObjectType, propertyName), parent != null ? parent.depth + 1 : -1, parent, animatableObjectType, propertyName, path, displayName) { this.binding = binding; this.isPptrNode = isPptrNode; } } internal class AnimationWindowHierarchyClipNode : AnimationWindowHierarchyNode { public AnimationWindowHierarchyClipNode(TreeViewItem parent, int setId, string name) : base(setId, parent != null ? parent.depth + 1 : -1, parent, null, null, null, name) {} } internal class AnimationWindowHierarchyMasterNode : AnimationWindowHierarchyNode { public AnimationWindowHierarchyMasterNode() : base(0, -1, null, null, null, null, "") {} } // A special node to put "Add Curve" button in bottom of the tree internal class AnimationWindowHierarchyAddButtonNode : AnimationWindowHierarchyNode { public AnimationWindowHierarchyAddButtonNode() : base(0, -1, null, null, null, null, "") {} } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowKeySelection.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Collections; using Object = UnityEngine.Object; namespace UnityEditorInternal { [System.Serializable] internal class AnimationWindowKeySelection : ScriptableObject, ISerializationCallbackReceiver { private HashSet m_SelectedKeyHashes; [SerializeField] private List m_SelectedKeyHashesSerialized; public HashSet selectedKeyHashes { get { return m_SelectedKeyHashes ?? (m_SelectedKeyHashes = new HashSet()); } set { m_SelectedKeyHashes = value; } } public void SaveSelection(string undoLabel) { Undo.RegisterCompleteObjectUndo(this, undoLabel); } public void OnBeforeSerialize() { m_SelectedKeyHashesSerialized = m_SelectedKeyHashes.ToList(); } public void OnAfterDeserialize() { m_SelectedKeyHashes = new HashSet(m_SelectedKeyHashesSerialized); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowKeyframe.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; namespace UnityEditorInternal { internal class AnimationWindowKeyframe { public float m_InTangent; public float m_OutTangent; public float m_InWeight; public float m_OutWeight; public WeightedMode m_WeightedMode; public int m_TangentMode; public int m_TimeHash; int m_Hash; float m_time; object m_value; AnimationWindowCurve m_curve; public float time { get { return m_time; } set { m_time = value; m_Hash = 0; m_TimeHash = value.GetHashCode(); } } public object value { get { return m_value; } set { m_value = value; } } public float inTangent { get { return m_InTangent; } set { m_InTangent = value; } } public float outTangent { get { return m_OutTangent; } set { m_OutTangent = value; } } public float inWeight { get { return m_InWeight; } set { m_InWeight = value; } } public float outWeight { get { return m_OutWeight; } set { m_OutWeight = value; } } public WeightedMode weightedMode { get { return m_WeightedMode; } set { m_WeightedMode = value; } } public AnimationWindowCurve curve { get { return m_curve; } set { m_curve = value; m_Hash = 0; } } public bool isPPtrCurve { get { return curve.isPPtrCurve; } } public bool isDiscreteCurve { get { return curve.isDiscreteCurve; } } public AnimationWindowKeyframe() { } public AnimationWindowKeyframe(AnimationWindowKeyframe key) { this.time = key.time; this.value = key.value; this.curve = key.curve; this.m_InTangent = key.m_InTangent; this.m_OutTangent = key.m_OutTangent; this.m_InWeight = key.inWeight; this.m_OutWeight = key.outWeight; this.m_WeightedMode = key.weightedMode; this.m_TangentMode = key.m_TangentMode; this.m_curve = key.m_curve; } public AnimationWindowKeyframe(AnimationWindowCurve curve, Keyframe key) { this.time = key.time; if (curve.isDiscreteCurve) { this.value = UnityEngine.Animations.DiscreteEvaluationAttributeUtilities.ConvertFloatToDiscreteInt(key.value); } else { this.value = key.value; } this.curve = curve; this.m_InTangent = key.inTangent; this.m_OutTangent = key.outTangent; this.m_InWeight = key.inWeight; this.m_OutWeight = key.outWeight; this.m_WeightedMode = key.weightedMode; this.m_TangentMode = key.tangentModeInternal; this.m_curve = curve; } public AnimationWindowKeyframe(AnimationWindowCurve curve, ObjectReferenceKeyframe key) { this.time = key.time; this.value = key.value; this.curve = curve; } public int GetHash() { if (m_Hash == 0) { // Berstein hash unchecked { m_Hash = curve.GetHashCode(); m_Hash = 33 * m_Hash + time.GetHashCode(); } } return m_Hash; } public int GetIndex() { for (int i = 0; i < curve.keyframes.Count; i++) { if (curve.keyframes[i] == this) { return i; } } return -1; } public Keyframe ToKeyframe() { float floatValue; if (curve.isDiscreteCurve) { // case 1395978 // Negative int values converted to float create NaN values. Limiting discrete int values to only positive values // until we rewrite the animation backend with dedicated int curves. floatValue = UnityEngine.Animations.DiscreteEvaluationAttributeUtilities.ConvertDiscreteIntToFloat(Math.Max(Convert.ToInt32(value), 0)); } else { floatValue = Convert.ToSingle(value); } var keyframe = new Keyframe(time, floatValue, inTangent, outTangent); keyframe.tangentModeInternal = m_TangentMode; keyframe.weightedMode = weightedMode; keyframe.inWeight = inWeight; keyframe.outWeight = outWeight; return keyframe; } public ObjectReferenceKeyframe ToObjectReferenceKeyframe() { var keyframe = new ObjectReferenceKeyframe(); keyframe.time = time; keyframe.value = (UnityEngine.Object)value; return keyframe; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowManipulator.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using Object = UnityEngine.Object; namespace UnityEditor { internal class AnimationWindowManipulator { public delegate bool OnStartDragDelegate(AnimationWindowManipulator manipulator, Event evt); public delegate bool OnDragDelegate(AnimationWindowManipulator manipulator, Event evt); public delegate bool OnEndDragDelegate(AnimationWindowManipulator manipulator, Event evt); public OnStartDragDelegate onStartDrag; public OnDragDelegate onDrag; public OnEndDragDelegate onEndDrag; public Rect rect; public int controlID; public AnimationWindowManipulator() { // NoOps... onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { return false; }; onDrag += (AnimationWindowManipulator manipulator, Event evt) => { return false; }; onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { return false; }; } public virtual void HandleEvents() { controlID = GUIUtility.GetControlID(FocusType.Passive); Event evt = Event.current; EventType eventType = evt.GetTypeForControl(controlID); bool handled = false; switch (eventType) { case EventType.MouseDown: if (evt.button == 0) { handled = onStartDrag(this, evt); if (handled) GUIUtility.hotControl = controlID; } break; case EventType.MouseDrag: if (GUIUtility.hotControl == controlID) { handled = onDrag(this, evt); } break; case EventType.MouseUp: if (GUIUtility.hotControl == controlID) { handled = onEndDrag(this, evt); GUIUtility.hotControl = 0; } break; } if (handled) evt.Use(); } public virtual void IgnoreEvents() { GUIUtility.GetControlID(FocusType.Passive); } } internal class AreaManipulator : AnimationWindowManipulator { private GUIStyle m_Style; private MouseCursor m_Cursor; public AreaManipulator(GUIStyle style, MouseCursor cursor) { m_Style = style; m_Cursor = cursor; } public AreaManipulator(GUIStyle style) { m_Style = style; m_Cursor = MouseCursor.Arrow; } public void OnGUI(Rect widgetRect) { if (m_Style == null) return; rect = widgetRect; if (Mathf.Approximately(widgetRect.width * widgetRect.height, 0f)) return; GUI.Label(widgetRect, GUIContent.none, m_Style); if (GUIUtility.hotControl == 0 && m_Cursor != MouseCursor.Arrow) { EditorGUIUtility.AddCursorRect(widgetRect, m_Cursor); } else if (GUIUtility.hotControl == controlID) { Vector2 mousePosition = Event.current.mousePosition; EditorGUIUtility.AddCursorRect(new Rect(mousePosition.x - 10, mousePosition.y - 10, 20, 20), m_Cursor); } } } internal class TimeCursorManipulator : AnimationWindowManipulator { public enum Alignment { Center, Left, Right } public Alignment alignment; public Color headColor; public Color lineColor; public bool dottedLine; public bool drawLine; public bool drawHead; public string tooltip; private GUIStyle m_Style; public TimeCursorManipulator(GUIStyle style) { m_Style = style; dottedLine = false; headColor = Color.white; lineColor = style.normal.textColor; drawLine = true; drawHead = true; tooltip = string.Empty; alignment = Alignment.Center; } public void OnGUI(Rect windowRect, float pixelTime) { float widgetWidth = m_Style.fixedWidth; float widgetHeight = m_Style.fixedHeight; Vector2 windowCoordinate = new Vector2(pixelTime, windowRect.yMin); switch (alignment) { case Alignment.Center: rect = new Rect((windowCoordinate.x - widgetWidth / 2.0f), windowCoordinate.y, widgetWidth, widgetHeight); break; case Alignment.Left: rect = new Rect(windowCoordinate.x - widgetWidth, windowCoordinate.y, widgetWidth, widgetHeight); break; case Alignment.Right: rect = new Rect(windowCoordinate.x, windowCoordinate.y, widgetWidth, widgetHeight); break; } Vector3 p1 = new Vector3(windowCoordinate.x, windowCoordinate.y + widgetHeight, 0.0f); Vector3 p2 = new Vector3(windowCoordinate.x, windowRect.height, 0.0f); if (drawLine) { Handles.color = lineColor; if (dottedLine) Handles.DrawDottedLine(p1, p2, 5.0f); else Handles.DrawLine(p1, p2); } if (drawHead) { Color c = GUI.color; GUI.color = headColor; GUI.Box(rect, GUIContent.none, m_Style); GUI.color = c; } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowOptions.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditor; using UnityEngine; namespace UnityEditorInternal { internal static class AnimationWindowOptions { static string kTimeFormat = "AnimationWindow.TimeFormat"; static string kFilterBySelection = "AnimationWindow.FilterBySelection"; static string kShowReadOnly = "AnimationWindow.ShowReadOnly"; static string kShowFrameRate = "AnimationWindow.ShowFrameRate"; private static TimeArea.TimeFormat m_TimeFormat; private static bool m_FilterBySelection; private static bool m_ShowReadOnly; private static bool m_ShowFrameRate; static AnimationWindowOptions() { m_TimeFormat = (TimeArea.TimeFormat)EditorPrefs.GetInt(kTimeFormat, (int)TimeArea.TimeFormat.TimeFrame); m_FilterBySelection = EditorPrefs.GetBool(kFilterBySelection, false); m_ShowReadOnly = EditorPrefs.GetBool(kShowReadOnly, false); m_ShowFrameRate = EditorPrefs.GetBool(kShowFrameRate, false); } public static TimeArea.TimeFormat timeFormat { get { return m_TimeFormat; } set { m_TimeFormat = value; EditorPrefs.SetInt(kTimeFormat, (int)value); } } public static bool filterBySelection { get { return m_FilterBySelection; } set { m_FilterBySelection = value; EditorPrefs.SetBool(kFilterBySelection, value); } } public static bool showReadOnly { get { return m_ShowReadOnly; } set { m_ShowReadOnly = value; EditorPrefs.SetBool(kShowReadOnly, value); } } public static bool showFrameRate { get { return m_ShowFrameRate; } set { m_ShowFrameRate = value; EditorPrefs.SetBool(kShowFrameRate, value); } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowSelectionItem.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Collections; using Object = UnityEngine.Object; namespace UnityEditorInternal { [Serializable] abstract class AnimationWindowSelectionItem : System.IEquatable, ISelectionBinding { [SerializeField] private int m_Id; [SerializeField] private GameObject m_GameObject; [SerializeField] private ScriptableObject m_ScriptableObject; [SerializeField] private AnimationClip m_AnimationClip; public virtual int id { get { return m_Id; } set { m_Id = value; } } public virtual GameObject gameObject { get { return m_GameObject; } set { m_GameObject = value; } } public virtual ScriptableObject scriptableObject { get { return m_ScriptableObject; } set { m_ScriptableObject = value; } } public virtual Object sourceObject { get { return (gameObject != null) ? (Object)gameObject : (Object)scriptableObject; } } public virtual AnimationClip animationClip { get { return m_AnimationClip; } set { m_AnimationClip = value; } } public virtual GameObject rootGameObject { get { Component animationPlayer = this.animationPlayer; if (animationPlayer != null) { return animationPlayer.gameObject; } return null; } } public virtual Component animationPlayer { get { if (gameObject != null) return AnimationWindowUtility.GetClosestAnimationPlayerComponentInParents(gameObject.transform); return null; } } public bool disabled { get { // To be editable, a selection must at least contain an animation clip. return (animationClip == null); } } public virtual bool animationIsEditable { get { // Clip is imported and shouldn't be edited if (animationClip && (animationClip.hideFlags & HideFlags.NotEditable) != 0) return false; // Object is a prefab - shouldn't be edited if (objectIsPrefab) return false; return true; } } public virtual bool clipIsEditable { get { if (!animationClip) return false; // Clip is imported and shouldn't be edited if ((animationClip.hideFlags & HideFlags.NotEditable) != 0) return false; if (!AssetDatabase.IsOpenForEdit(animationClip, StatusQueryOptions.UseCachedIfPossible)) return false; return true; } } public virtual bool objectIsPrefab { get { // No gameObject selected if (!gameObject) return false; if (EditorUtility.IsPersistent(gameObject)) return true; if ((gameObject.hideFlags & HideFlags.NotEditable) != 0) return true; return false; } } public virtual bool objectIsOptimized { get { Animator animator = animationPlayer as Animator; if (animator == null) return false; return animator.isOptimizable && !animator.hasTransformHierarchy; } } public virtual bool canPreview { get { if (rootGameObject != null) { return !objectIsOptimized; } return false; } } public virtual bool canRecord { get { if (!animationIsEditable) return false; return canPreview; } } public virtual bool canChangeAnimationClip { get { return rootGameObject != null; } } public virtual bool canAddCurves { get { if (gameObject != null) { return !objectIsPrefab && clipIsEditable; } else if (scriptableObject != null) { return true; } return false; } } public virtual bool canCreateClips { get { Component animationPlayer = this.animationPlayer; if (animationPlayer == null) return false; Animator animator = animationPlayer as Animator; if (animator != null) { // Need a valid state machine to create clips in the Animator. return (animator.runtimeAnimatorController != null); } return true; } } public virtual bool canSyncSceneSelection { get { return true; } } public int GetRefreshHash() { return unchecked (id * 19603 ^ (animationClip != null ? 729 * animationClip.GetHashCode() : 0) ^ (rootGameObject != null ? 27 * rootGameObject.GetHashCode() : 0) ^ (scriptableObject != null ? scriptableObject.GetHashCode() : 0)); } virtual public void Synchronize() { // nothing to do. } public bool Equals(AnimationWindowSelectionItem other) { return id == other.id && animationClip == other.animationClip && gameObject == other.gameObject && scriptableObject == other.scriptableObject; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowState.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using UnityEngine; using UnityEditor; using System.Collections.Generic; using UnityEditor.IMGUI.Controls; using Object = UnityEngine.Object; using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem; namespace UnityEditorInternal { [System.Serializable] class AnimationWindowState : ICurveEditorState { public enum RefreshType { None = 0, CurvesOnly = 1, Everything = 2 } public enum SnapMode { Disabled = 0, SnapToFrame = 1, [Obsolete("SnapToClipFrame has been made redundant with SnapToFrame, SnapToFrame will behave the same.")] SnapToClipFrame = 2 } [SerializeField] public AnimEditor animEditor; // Reference to owner of this state. Used to trigger repaints. [SerializeField] public AnimationWindowHierarchyState hierarchyState = new(); // Persistent state of treeview on the left side of window [NonSerialized] public AnimationWindowHierarchyDataSource hierarchyData; [SerializeReference] private TimeArea m_TimeArea; // Either curveeditor or dopesheet depending on which is selected [SerializeReference] private AnimationWindowControl m_ControlInterface; [SerializeReference] private IAnimationWindowController m_OverrideControlInterface; [SerializeReference] private AnimationWindowSelectionItem m_EmptySelection; [SerializeReference] private AnimationWindowSelectionItem m_Selection; // Internal selection [SerializeField] private AnimationWindowKeySelection m_KeySelection; // What is selected. Hashes persist cache reload, because they are from keyframe time+value [SerializeField] public bool showCurveEditor; // Do we show dopesheet or curves [SerializeField] public bool linkedWithSequencer; // Toggle Sequencer selection mode. [SerializeField] private bool m_RippleTime; // Toggle ripple time option for curve editor and dopesheet. private bool m_RippleTimeClutch; // Toggle ripple time option for curve editor and dopesheet. [SerializeField] private int m_ActiveKeyframeHash; // Which keyframe is active (selected key that user previously interacted with) [SerializeField] private float m_FrameRate = kDefaultFrameRate; [SerializeField] private int[] m_SelectionFilter; [NonSerialized] public Action onStartLiveEdit; [NonSerialized] public Action onEndLiveEdit; [NonSerialized] public Action onFrameRateChange; private static List s_KeyframeClipboard; // For copy-pasting keyframes private bool m_AllCurvesCacheDirty = true; private bool m_FilteredCurvesCacheDirty = true; private bool m_ActiveCurvesCacheDirty = true; private List m_AllCurvesCache = new (); private List m_FilteredCurvesCache = new (); private List m_ActiveCurvesCache = new (); private List m_dopelinesCache; private List m_SelectedKeysCache; private Bounds? m_SelectionBoundsCache; private CurveWrapper[] m_ActiveCurveWrappersCache; private AnimationWindowKeyframe m_ActiveKeyframeCache; private HashSet m_ModifiedCurves = new HashSet(); private EditorCurveBinding? m_lastAddedCurveBinding; // Hash of all the things that require animationWindow to refresh if they change private int m_PreviousRefreshHash; // Changing m_Refresh means you are ordering a refresh at the next OnGUI(). // CurvesOnly means that there is no need to refresh the hierarchy, since only the keyframe data changed. private RefreshType m_Refresh = RefreshType.None; private struct LiveEditKeyframe { public AnimationWindowKeyframe keySnapshot; public AnimationWindowKeyframe key; } private class LiveEditCurve { public AnimationWindowCurve curve; public List selectedKeys = new List(); public List unselectedKeys = new List(); } private List m_LiveEditSnapshot; public const float kDefaultFrameRate = 60.0f; public const string kEditCurveUndoLabel = "Edit Curve"; public AnimationWindowSelectionItem selection { get { if (m_Selection != null) return m_Selection; if (m_EmptySelection == null) m_EmptySelection = AnimationClipSelectionItem.Create(null, null); return m_EmptySelection; } set { m_Selection = value; OnSelectionChanged(); } } // AnimationClip we are currently editing public AnimationClip activeAnimationClip { get { return selection.animationClip; } set { if (selection.canChangeAnimationClip) { selection.animationClip = value; OnSelectionChanged(); } } } // Previously or currently selected gameobject is considered as the active gameobject public GameObject activeGameObject { get { return selection.gameObject; } } // Closes parent to activeGameObject that has Animator component public GameObject activeRootGameObject { get { return selection.rootGameObject; } } public Component activeAnimationPlayer { get { return selection.animationPlayer; } } public ScriptableObject activeScriptableObject { get { return selection.scriptableObject; } } // Is the hierarchy in animator optimized public bool animatorIsOptimized { get { return selection.objectIsOptimized; } } public bool disabled { get { return selection.disabled; } } public IAnimationWindowController controlInterface => m_OverrideControlInterface ?? m_ControlInterface; public IAnimationWindowController overrideControlInterface { get => m_OverrideControlInterface; set { if (m_OverrideControlInterface != null) m_OverrideControlInterface.OnDestroy(); m_OverrideControlInterface = value; } } public bool filterBySelection { get { return AnimationWindowOptions.filterBySelection; } set { AnimationWindowOptions.filterBySelection = value; UpdateSelectionFilter(); // Refresh everything. refresh = RefreshType.Everything; } } public bool showReadOnly { get { return AnimationWindowOptions.showReadOnly; } set { AnimationWindowOptions.showReadOnly = value; // Refresh everything. refresh = RefreshType.Everything; } } public bool rippleTime { get { return m_RippleTime || m_RippleTimeClutch; } set { m_RippleTime = value; } } public bool rippleTimeClutch { get { return m_RippleTimeClutch; } set { m_RippleTimeClutch = value; } } public bool showFrameRate { get { return AnimationWindowOptions.showFrameRate; } set { AnimationWindowOptions.showFrameRate = value; } } public void OnGUI() { RefreshHashCheck(); Refresh(); } private void RefreshHashCheck() { int newRefreshHash = GetRefreshHash(); if (m_PreviousRefreshHash != newRefreshHash) { refresh = RefreshType.Everything; m_PreviousRefreshHash = newRefreshHash; } } private void Refresh() { selection.Synchronize(); if (refresh == RefreshType.Everything) { m_AllCurvesCacheDirty = true; m_FilteredCurvesCacheDirty = true; m_ActiveCurvesCacheDirty = true; m_ActiveKeyframeCache = null; m_dopelinesCache = null; m_SelectedKeysCache = null; m_SelectionBoundsCache = null; if (animEditor != null && animEditor.curveEditor != null) animEditor.curveEditor.InvalidateSelectionBounds(); ClearCurveWrapperCache(); if (hierarchyData != null) hierarchyData.UpdateData(); // If there was new curve added, set it as active selection if (m_lastAddedCurveBinding != null) OnNewCurveAdded((EditorCurveBinding)m_lastAddedCurveBinding); // select top dopeline if there is no selection available if (activeCurves.Count == 0 && dopelines.Count > 0) SelectHierarchyItem(dopelines[0], false, false); m_Refresh = RefreshType.None; } else if (refresh == RefreshType.CurvesOnly) { m_ActiveKeyframeCache = null; m_SelectedKeysCache = null; m_SelectionBoundsCache = null; if (animEditor != null && animEditor.curveEditor != null) animEditor.curveEditor.InvalidateSelectionBounds(); ReloadModifiedAnimationCurveCache(); ReloadModifiedDopelineCache(); ReloadModifiedCurveWrapperCache(); m_Refresh = RefreshType.None; m_ModifiedCurves.Clear(); } } // Hash for checking if any of these things is changed private int GetRefreshHash() { return selection.GetRefreshHash() ^ (hierarchyState != null ? hierarchyState.expandedIDs.Count : 0) ^ (hierarchyState != null ? hierarchyState.GetTallInstancesCount() : 0) ^ (showCurveEditor ? 1 : 0); } public void ForceRefresh() { refresh = RefreshType.Everything; } private void PurgeSelection() { linkedWithSequencer = false; m_OverrideControlInterface = null; m_Selection = null; } public void OnEnable() { AnimationUtility.onCurveWasModified += CurveWasModified; Undo.undoRedoEvent += UndoRedoPerformed; AssemblyReloadEvents.beforeAssemblyReload += PurgeSelection; // NoOps... onStartLiveEdit += () => {}; onEndLiveEdit += () => {}; m_ControlInterface = new AnimationWindowControl { state = this }; m_ControlInterface.OnEnable(); m_AllCurvesCacheDirty = true; m_FilteredCurvesCacheDirty = true; m_ActiveCurvesCacheDirty = true; } public void OnDisable() { AnimationUtility.onCurveWasModified -= CurveWasModified; Undo.undoRedoEvent -= UndoRedoPerformed; AssemblyReloadEvents.beforeAssemblyReload -= PurgeSelection; m_ControlInterface.OnDisable(); previewing = false; } public void OnDestroy() { m_ControlInterface.OnDestroy(); m_ControlInterface = null; if (m_OverrideControlInterface != null) { m_OverrideControlInterface.OnDestroy(); m_OverrideControlInterface = null; } Object.DestroyImmediate(m_KeySelection); } public void OnSelectionChanged() { if (onFrameRateChange != null) onFrameRateChange(frameRate); UpdateSelectionFilter(); // reset back time at 0 upon selection change. controlInterface.OnSelectionChanged(); if (animEditor != null) animEditor.OnSelectionChanged(); } public void OnSelectionUpdated() { UpdateSelectionFilter(); if (filterBySelection) { // Refresh everything. refresh = RefreshType.Everything; } } // Set this property to ask for refresh at the next OnGUI. public RefreshType refresh { get { return m_Refresh; } // Make sure that if full refresh is already ordered, nobody gets to f*** with it set { if ((int)m_Refresh < (int)value) m_Refresh = value; } } public void UndoRedoPerformed(in UndoRedoInfo info) { refresh = RefreshType.Everything; ResampleAnimation(); } // When curve is modified, we never trigger refresh right away. We order a refresh at later time by setting refresh to appropriate value. void CurveWasModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType type) { // AnimationWindow doesn't care if some other clip somewhere changed if (activeAnimationClip != clip) return; // Refresh curves that already exist. if (type == AnimationUtility.CurveModifiedType.CurveModified) { bool didFind = false; bool hadPhantom = false; int hashCode = binding.GetHashCode(); var curves = filteredCurves; for (int j = 0; j < curves.Count; ++j) { var curve = curves[j]; int curveHash = curve.GetBindingHashCode(); if (curveHash == hashCode) { m_ModifiedCurves.Add(curve.GetHashCode()); didFind = true; hadPhantom |= curve.binding.isPhantom; } } if (didFind && !hadPhantom) refresh = RefreshType.CurvesOnly; else { // New curve was added, so let's save binding and make it active selection when Refresh is called next time m_lastAddedCurveBinding = binding; refresh = RefreshType.Everything; } } else { // Otherwise do a full reload refresh = RefreshType.Everything; } // Force repaint to display live animation curve changes from other editor window (like timeline). Repaint(); } public void SaveKeySelection(string undoLabel) { if (m_KeySelection != null) Undo.RegisterCompleteObjectUndo(m_KeySelection, undoLabel); } public void SaveCurve(AnimationClip clip, AnimationWindowCurve curve) { SaveCurve(clip, curve, kEditCurveUndoLabel); } public void SaveCurve(AnimationClip clip, AnimationWindowCurve curve, string undoLabel) { if (!curve.animationIsEditable) Debug.LogError("Curve is not editable and shouldn't be saved."); Undo.RegisterCompleteObjectUndo(clip, undoLabel); AnimationWindowUtility.SaveCurve(clip, curve); Repaint(); } public void SaveCurves(AnimationClip clip, ICollection curves, string undoLabel = kEditCurveUndoLabel) { if (curves.Count == 0) return; Undo.RegisterCompleteObjectUndo(clip, undoLabel); AnimationWindowUtility.SaveCurves(clip, curves); Repaint(); } private void SaveSelectedKeys(string undoLabel) { List saveCurves = new List(); // Find all curves that need saving foreach (LiveEditCurve snapshot in m_LiveEditSnapshot) { if (!snapshot.curve.animationIsEditable) continue; if (!saveCurves.Contains(snapshot.curve)) saveCurves.Add(snapshot.curve); List toBeDeleted = new List(); // If selected keys are dragged over non-selected keyframe at exact same time, then delete the unselected ones underneath foreach (AnimationWindowKeyframe other in snapshot.curve.keyframes) { // Keyframe is in selection, skip. if (snapshot.selectedKeys.Exists(liveEditKey => liveEditKey.key == other)) continue; // There is already a selected keyframe at that time, delete non-selected keyframe. if (!snapshot.selectedKeys.Exists(liveEditKey => AnimationKeyTime.Time(liveEditKey.key.time, frameRate).frame == AnimationKeyTime.Time(other.time, frameRate).frame)) continue; toBeDeleted.Add(other); } foreach (AnimationWindowKeyframe deletedKey in toBeDeleted) { snapshot.curve.RemoveKeyframe(deletedKey); } } SaveCurves(activeAnimationClip, saveCurves, undoLabel); } public void RemoveCurve(AnimationWindowCurve curve, string undoLabel) { if (!curve.animationIsEditable) return; Undo.RegisterCompleteObjectUndo(curve.clip, undoLabel); if (curve.isPPtrCurve) AnimationUtility.SetObjectReferenceCurve(curve.clip, curve.binding, null); else AnimationUtility.SetEditorCurve(curve.clip, curve.binding, null); } public bool previewing { get => controlInterface.previewing; set { if (controlInterface.previewing == value) return; if (value) { if (canPreview) controlInterface.previewing = true; } else { // Automatically stop recording and playback when stopping preview. controlInterface.playing = false; controlInterface.recording = false; controlInterface.previewing = false; } } } public bool canPreview => controlInterface.canPreview; public void UpdateCurvesDisplayName() { if (hierarchyData != null) hierarchyData.UpdateSerializeReferenceCurvesArrayNiceDisplayName(); } public bool recording { get => controlInterface.recording; set { if (controlInterface.recording == value) return; if (value) { if (canRecord) { // Auto-Preview when starting recording controlInterface.previewing = true; if (controlInterface.previewing) controlInterface.recording = true; } } else controlInterface.recording = false; } } public bool canRecord => controlInterface.canRecord; public bool playing { get => controlInterface.playing; set { if (controlInterface.playing == value) return; if (value) { if (canPlay) { // Auto-Preview when starting playback. controlInterface.previewing = true; if (controlInterface.previewing) controlInterface.playing = true; } } else controlInterface.playing = false; } } public bool canPlay => controlInterface.canPlay; public void ResampleAnimation() { if (disabled) return; if (controlInterface.previewing == false) return; if (controlInterface.canPreview == false) return; controlInterface.ResampleAnimation(); } public bool PlaybackUpdate() { if (disabled) return false; if (!controlInterface.playing) return false; return controlInterface.PlaybackUpdate(); } public void ClearCandidates() => controlInterface.ClearCandidates(); public void ProcessCandidates() => controlInterface.ProcessCandidates(); public bool ShouldShowCurve(AnimationWindowCurve curve) { if (filterBySelection && activeRootGameObject != null) { if (m_SelectionFilter != null) { Transform t = activeRootGameObject.transform.Find(curve.path); if (t != null) { if (!m_SelectionFilter.Contains(t.gameObject.GetInstanceID())) return false; } else { return false; } } } return true; } private void UpdateSelectionFilter() { m_SelectionFilter = (filterBySelection) ? (int[])Selection.instanceIDs.Clone() : null; } void RebuildAllCurvesCacheIfNecessary() { if (m_AllCurvesCacheDirty == false && m_AllCurvesCache != null) return; if (m_AllCurvesCache == null) m_AllCurvesCache = new List(); else m_AllCurvesCache.Clear(); var animationClip = activeAnimationClip; if (animationClip == null || (!selection.animationIsEditable && !showReadOnly)) return; EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(animationClip); EditorCurveBinding[] objectCurveBindings = AnimationUtility.GetObjectReferenceCurveBindings(animationClip); List transformCurves = new List(); foreach (EditorCurveBinding curveBinding in curveBindings) { if (AnimationWindowUtility.ShouldShowAnimationWindowCurve(curveBinding)) { AnimationWindowCurve curve = new AnimationWindowCurve(animationClip, curveBinding, controlInterface.GetValueType(curveBinding)); curve.selectionBinding = selection; m_AllCurvesCache.Add(curve); if (AnimationWindowUtility.IsActualTransformCurve(curveBinding)) { transformCurves.Add(curve); } } } foreach (EditorCurveBinding curveBinding in objectCurveBindings) { AnimationWindowCurve curve = new AnimationWindowCurve(animationClip, curveBinding, controlInterface.GetValueType(curveBinding)); curve.selectionBinding = selection; m_AllCurvesCache.Add(curve); } transformCurves.Sort(); if (transformCurves.Count > 0) { FillInMissingTransformCurves(animationClip, transformCurves, ref m_AllCurvesCache); } // Curves need to be sorted with path/type/property name so it's possible to construct hierarchy from them // Sorting logic in AnimationWindowCurve.CompareTo() m_AllCurvesCache.Sort(); m_AllCurvesCacheDirty = false; } private void RebuildFilteredCurvesCacheIfNecessary() { if (m_FilteredCurvesCacheDirty == false && m_FilteredCurvesCache != null) return; if (m_FilteredCurvesCache == null) m_FilteredCurvesCache = new List(); else m_FilteredCurvesCache.Clear(); for (int i = 0; i < m_AllCurvesCache.Count; ++i) { if (ShouldShowCurve(m_AllCurvesCache[i])) m_FilteredCurvesCache.Add(m_AllCurvesCache[i]); } m_FilteredCurvesCacheDirty = false; } private void RebuildActiveCurvesCacheIfNecessary() { if (m_ActiveCurvesCacheDirty == false && m_ActiveCurvesCache != null) return; if (m_ActiveCurvesCache == null) m_ActiveCurvesCache = new List(); else m_ActiveCurvesCache.Clear(); if (hierarchyState != null && hierarchyData != null) { foreach (int id in hierarchyState.selectedIDs) { TreeViewItem node = hierarchyData.FindItem(id); AnimationWindowHierarchyNode hierarchyNode = node as AnimationWindowHierarchyNode; if (hierarchyNode == null) continue; AnimationWindowCurve[] curves = hierarchyNode.curves; if (curves == null) continue; foreach (AnimationWindowCurve curve in curves) if (!m_ActiveCurvesCache.Contains(curve)) m_ActiveCurvesCache.Add(curve); } m_ActiveCurvesCache.Sort(); } m_ActiveCurvesCacheDirty = false; } private void FillInMissingTransformCurves(AnimationClip animationClip, List transformCurves, ref List curvesCache) { EditorCurveBinding lastBinding = transformCurves[0].binding; var propertyGroup = new EditorCurveBinding ? [3]; string propertyGroupName; foreach (var transformCurve in transformCurves) { var transformBinding = transformCurve.binding; //if it's a new property group if (transformBinding.path != lastBinding.path || AnimationWindowUtility.GetPropertyGroupName(transformBinding.propertyName) != AnimationWindowUtility.GetPropertyGroupName(lastBinding.propertyName)) { propertyGroupName = AnimationWindowUtility.GetPropertyGroupName(lastBinding.propertyName); FillPropertyGroup(animationClip, ref propertyGroup, lastBinding, propertyGroupName, ref curvesCache); lastBinding = transformBinding; propertyGroup = new EditorCurveBinding ? [3]; } AssignBindingToRightSlot(transformBinding, ref propertyGroup); } FillPropertyGroup(animationClip, ref propertyGroup, lastBinding, AnimationWindowUtility.GetPropertyGroupName(lastBinding.propertyName), ref curvesCache); } private void FillPropertyGroup(AnimationClip animationClip, ref EditorCurveBinding?[] propertyGroup, EditorCurveBinding lastBinding, string propertyGroupName, ref List curvesCache) { var newBinding = lastBinding; newBinding.isPhantom = true; if (!propertyGroup[0].HasValue) { newBinding.propertyName = propertyGroupName + ".x"; AnimationWindowCurve curve = new AnimationWindowCurve(animationClip, newBinding, controlInterface.GetValueType(newBinding)); curve.selectionBinding = selection; curvesCache.Add(curve); } if (!propertyGroup[1].HasValue) { newBinding.propertyName = propertyGroupName + ".y"; AnimationWindowCurve curve = new AnimationWindowCurve(animationClip, newBinding, controlInterface.GetValueType(newBinding)); curve.selectionBinding = selection; curvesCache.Add(curve); } if (!propertyGroup[2].HasValue) { newBinding.propertyName = propertyGroupName + ".z"; AnimationWindowCurve curve = new AnimationWindowCurve(animationClip, newBinding, controlInterface.GetValueType(newBinding)); curve.selectionBinding = selection; curvesCache.Add(curve); } } private void AssignBindingToRightSlot(EditorCurveBinding transformBinding, ref EditorCurveBinding?[] propertyGroup) { if (transformBinding.propertyName.EndsWith(".x")) { propertyGroup[0] = transformBinding; } else if (transformBinding.propertyName.EndsWith(".y")) { propertyGroup[1] = transformBinding; } else if (transformBinding.propertyName.EndsWith(".z")) { propertyGroup[2] = transformBinding; } } public List allCurves { get { RebuildAllCurvesCacheIfNecessary(); return m_AllCurvesCache; } } public List filteredCurves { get { RebuildAllCurvesCacheIfNecessary(); RebuildFilteredCurvesCacheIfNecessary(); return m_FilteredCurvesCache; } } public List activeCurves { get { RebuildActiveCurvesCacheIfNecessary(); return m_ActiveCurvesCache; } } public CurveWrapper[] activeCurveWrappers { get { if (m_ActiveCurveWrappersCache == null || m_ActiveCurvesCache == null) { List activeCurveWrappers = new List(); foreach (AnimationWindowCurve curve in activeCurves) if (AnimationWindowUtility.GetCurveWrapper(curve, curve.clip) is CurveWrapper wrapper) activeCurveWrappers.Add(wrapper); // If there are no active curves, we would end up with empty curve editor so we just give all curves insteads if (!activeCurveWrappers.Any()) foreach (AnimationWindowCurve curve in filteredCurves) if (AnimationWindowUtility.GetCurveWrapper(curve, curve.clip) is CurveWrapper wrapper) activeCurveWrappers.Add(wrapper); m_ActiveCurveWrappersCache = activeCurveWrappers.ToArray(); } return m_ActiveCurveWrappersCache; } } public List dopelines { get { if (m_dopelinesCache == null) { m_dopelinesCache = new List(); if (hierarchyData != null) { foreach (TreeViewItem node in hierarchyData.GetRows()) { AnimationWindowHierarchyNode hierarchyNode = node as AnimationWindowHierarchyNode; if (hierarchyNode == null || hierarchyNode is AnimationWindowHierarchyAddButtonNode) continue; AnimationWindowCurve[] curves = hierarchyNode.curves; if (curves == null) continue; DopeLine dopeLine = new DopeLine(node.id, curves); dopeLine.tallMode = hierarchyState.GetTallMode(hierarchyNode); dopeLine.objectType = hierarchyNode.animatableObjectType; dopeLine.hasChildren = !(hierarchyNode is AnimationWindowHierarchyPropertyNode); dopeLine.isMasterDopeline = node is AnimationWindowHierarchyMasterNode; m_dopelinesCache.Add(dopeLine); } } } return m_dopelinesCache; } } public List selectedHierarchyNodes { get { List selectedHierarchyNodes = new List(); if (activeAnimationClip != null && hierarchyData != null) { foreach (int id in hierarchyState.selectedIDs) { AnimationWindowHierarchyNode hierarchyNode = (AnimationWindowHierarchyNode)hierarchyData.FindItem(id); if (hierarchyNode == null || hierarchyNode is AnimationWindowHierarchyAddButtonNode) continue; selectedHierarchyNodes.Add(hierarchyNode); } } return selectedHierarchyNodes; } } public AnimationWindowKeyframe activeKeyframe { get { if (m_ActiveKeyframeCache == null) { foreach (AnimationWindowCurve curve in filteredCurves) { foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { if (keyframe.GetHash() == m_ActiveKeyframeHash) m_ActiveKeyframeCache = keyframe; } } } return m_ActiveKeyframeCache; } set { m_ActiveKeyframeCache = null; m_ActiveKeyframeHash = value != null ? value.GetHash() : 0; } } public List selectedKeys { get { if (m_SelectedKeysCache == null) { m_SelectedKeysCache = new List(); foreach (AnimationWindowCurve curve in filteredCurves) { foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { if (KeyIsSelected(keyframe)) { m_SelectedKeysCache.Add(keyframe); } } } } return m_SelectedKeysCache; } } public Bounds selectionBounds { get { if (m_SelectionBoundsCache == null) { List keys = selectedKeys; if (keys.Count > 0) { AnimationWindowKeyframe key = keys[0]; float time = key.time; float val = key.isPPtrCurve || key.isDiscreteCurve ? 0.0f : (float)key.value; Bounds bounds = new Bounds(new Vector2(time, val), Vector2.zero); for (int i = 1; i < keys.Count; ++i) { key = keys[i]; time = key.time; val = key.isPPtrCurve || key.isDiscreteCurve ? 0.0f : (float)key.value; bounds.Encapsulate(new Vector2(time, val)); } m_SelectionBoundsCache = bounds; } else { m_SelectionBoundsCache = new Bounds(Vector2.zero, Vector2.zero); } } return m_SelectionBoundsCache.Value; } } private HashSet selectedKeyHashes { get { if (m_KeySelection == null) { m_KeySelection = ScriptableObject.CreateInstance(); m_KeySelection.hideFlags = HideFlags.HideAndDontSave; } return m_KeySelection.selectedKeyHashes; } set { if (m_KeySelection == null) { m_KeySelection = ScriptableObject.CreateInstance(); m_KeySelection.hideFlags = HideFlags.HideAndDontSave; } m_KeySelection.selectedKeyHashes = value; } } public bool AnyKeyIsSelected(DopeLine dopeline) { foreach (AnimationWindowKeyframe keyframe in dopeline.keys) if (KeyIsSelected(keyframe)) return true; return false; } public bool KeyIsSelected(AnimationWindowKeyframe keyframe) { return selectedKeyHashes.Contains(keyframe.GetHash()); } public void SelectKey(AnimationWindowKeyframe keyframe) { int hash = keyframe.GetHash(); if (!selectedKeyHashes.Contains(hash)) selectedKeyHashes.Add(hash); m_SelectedKeysCache = null; m_SelectionBoundsCache = null; } public void UnselectKey(AnimationWindowKeyframe keyframe) { int hash = keyframe.GetHash(); if (selectedKeyHashes.Contains(hash)) selectedKeyHashes.Remove(hash); m_SelectedKeysCache = null; m_SelectionBoundsCache = null; } public void DeleteSelectedKeys() { if (selectedKeys.Count == 0) return; DeleteKeys(selectedKeys); } public void DeleteKeys(List keys) { SaveKeySelection(kEditCurveUndoLabel); HashSet curves = new HashSet(); foreach (AnimationWindowKeyframe keyframe in keys) { if (!keyframe.curve.animationIsEditable) continue; curves.Add(keyframe.curve); UnselectKey(keyframe); keyframe.curve.RemoveKeyframe(keyframe); } SaveCurves(activeAnimationClip, curves, kEditCurveUndoLabel); ResampleAnimation(); } public void StartLiveEdit() { if (onStartLiveEdit != null) onStartLiveEdit(); m_LiveEditSnapshot = new List(); SaveKeySelection(kEditCurveUndoLabel); foreach (AnimationWindowKeyframe selectedKey in selectedKeys) { if (!m_LiveEditSnapshot.Exists(snapshot => snapshot.curve == selectedKey.curve)) { LiveEditCurve snapshot = new LiveEditCurve(); snapshot.curve = selectedKey.curve; foreach (AnimationWindowKeyframe key in selectedKey.curve.keyframes) { LiveEditKeyframe liveEditKey = new LiveEditKeyframe(); liveEditKey.keySnapshot = new AnimationWindowKeyframe(key); liveEditKey.key = key; if (KeyIsSelected(key)) snapshot.selectedKeys.Add(liveEditKey); else snapshot.unselectedKeys.Add(liveEditKey); } m_LiveEditSnapshot.Add(snapshot); } } } public void EndLiveEdit() { SaveSelectedKeys(kEditCurveUndoLabel); m_LiveEditSnapshot = null; if (onEndLiveEdit != null) onEndLiveEdit(); } public bool InLiveEdit() { return m_LiveEditSnapshot != null; } public void MoveSelectedKeys(float deltaTime, bool snapToFrame) { bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); // Clear selections since all hashes are now different ClearKeySelections(); foreach (LiveEditCurve snapshot in m_LiveEditSnapshot) { foreach (LiveEditKeyframe liveEditKey in snapshot.selectedKeys) { if (snapshot.curve.animationIsEditable) { liveEditKey.key.time = Mathf.Max(liveEditKey.keySnapshot.time + deltaTime, 0f); if (snapToFrame) liveEditKey.key.time = SnapToFrame(liveEditKey.key.time, snapshot.curve.clip.frameRate); } SelectKey(liveEditKey.key); } } if (!inLiveEdit) EndLiveEdit(); } public void TransformSelectedKeys(Matrix4x4 matrix, bool flipX, bool flipY, bool snapToFrame) { bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); // Clear selections since all hashes are now different ClearKeySelections(); foreach (LiveEditCurve snapshot in m_LiveEditSnapshot) { foreach (LiveEditKeyframe liveEditKey in snapshot.selectedKeys) { if (snapshot.curve.animationIsEditable) { // Transform time value. Vector3 v = new Vector3(liveEditKey.keySnapshot.time, liveEditKey.keySnapshot.isPPtrCurve || liveEditKey.keySnapshot.isDiscreteCurve ? 0f : (float)liveEditKey.keySnapshot.value, 0f); v = matrix.MultiplyPoint3x4(v); liveEditKey.key.time = Mathf.Max((snapToFrame) ? SnapToFrame(v.x, snapshot.curve.clip.frameRate) : v.x, 0f); if (flipX) { liveEditKey.key.inTangent = (liveEditKey.keySnapshot.outTangent != Mathf.Infinity) ? -liveEditKey.keySnapshot.outTangent : Mathf.Infinity; liveEditKey.key.outTangent = (liveEditKey.keySnapshot.inTangent != Mathf.Infinity) ? -liveEditKey.keySnapshot.inTangent : Mathf.Infinity; if (liveEditKey.keySnapshot.weightedMode == WeightedMode.In) liveEditKey.key.weightedMode = WeightedMode.Out; else if (liveEditKey.keySnapshot.weightedMode == WeightedMode.Out) liveEditKey.key.weightedMode = WeightedMode.In; else liveEditKey.key.weightedMode = liveEditKey.keySnapshot.weightedMode; liveEditKey.key.inWeight = liveEditKey.keySnapshot.outWeight; liveEditKey.key.outWeight = liveEditKey.keySnapshot.inWeight; } if (!liveEditKey.key.isPPtrCurve && !liveEditKey.key.isDiscreteCurve) { liveEditKey.key.value = v.y; if (flipY) { liveEditKey.key.inTangent = (liveEditKey.key.inTangent != Mathf.Infinity) ? -liveEditKey.key.inTangent : Mathf.Infinity; liveEditKey.key.outTangent = (liveEditKey.key.outTangent != Mathf.Infinity) ? -liveEditKey.key.outTangent : Mathf.Infinity; } } } SelectKey(liveEditKey.key); } } if (!inLiveEdit) EndLiveEdit(); } public void TransformRippleKeys(Matrix4x4 matrix, float t1, float t2, bool flipX, bool snapToFrame) { bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); // Clear selections since all hashes are now different ClearKeySelections(); foreach (LiveEditCurve snapshot in m_LiveEditSnapshot) { foreach (LiveEditKeyframe liveEditKey in snapshot.selectedKeys) { if (snapshot.curve.animationIsEditable) { Vector3 v = new Vector3(liveEditKey.keySnapshot.time, 0f, 0f); v = matrix.MultiplyPoint3x4(v); liveEditKey.key.time = Mathf.Max((snapToFrame) ? SnapToFrame(v.x, snapshot.curve.clip.frameRate) : v.x, 0f); if (flipX) { liveEditKey.key.inTangent = (liveEditKey.keySnapshot.outTangent != Mathf.Infinity) ? -liveEditKey.keySnapshot.outTangent : Mathf.Infinity; liveEditKey.key.outTangent = (liveEditKey.keySnapshot.inTangent != Mathf.Infinity) ? -liveEditKey.keySnapshot.inTangent : Mathf.Infinity; } } SelectKey(liveEditKey.key); } if (!snapshot.curve.animationIsEditable) continue; foreach (LiveEditKeyframe liveEditKey in snapshot.unselectedKeys) { if (liveEditKey.keySnapshot.time > t2) { Vector3 v = new Vector3(flipX ? t1 : t2, 0f, 0f); v = matrix.MultiplyPoint3x4(v); float dt = v.x - t2; if (dt > 0f) { float newTime = liveEditKey.keySnapshot.time + dt; liveEditKey.key.time = Mathf.Max((snapToFrame) ? SnapToFrame(newTime, snapshot.curve.clip.frameRate) : newTime, 0f); } else { liveEditKey.key.time = liveEditKey.keySnapshot.time; } } else if (liveEditKey.keySnapshot.time < t1) { Vector3 v = new Vector3(flipX ? t2 : t1, 0f, 0f); v = matrix.MultiplyPoint3x4(v); float dt = v.x - t1; if (dt < 0f) { float newTime = liveEditKey.keySnapshot.time + dt; liveEditKey.key.time = Mathf.Max((snapToFrame) ? SnapToFrame(newTime, snapshot.curve.clip.frameRate) : newTime, 0f); } else { liveEditKey.key.time = liveEditKey.keySnapshot.time; } } } } if (!inLiveEdit) EndLiveEdit(); } internal static bool CanPasteKeys() { return s_KeyframeClipboard != null && s_KeyframeClipboard.Count > 0; } internal static void ClearKeyframeClipboard() { s_KeyframeClipboard?.Clear(); } public void CopyKeys() { if (s_KeyframeClipboard == null) s_KeyframeClipboard = new List(); float smallestTime = float.MaxValue; s_KeyframeClipboard.Clear(); foreach (AnimationWindowKeyframe keyframe in selectedKeys) { s_KeyframeClipboard.Add(new AnimationWindowKeyframe(keyframe)); if (keyframe.time < smallestTime) smallestTime = keyframe.time; } if (s_KeyframeClipboard.Count > 0) // copying selected keys { foreach (AnimationWindowKeyframe keyframe in s_KeyframeClipboard) { keyframe.time -= smallestTime; } } else // No selected keys, lets copy entire curves { CopyAllActiveCurves(); } // Animation keyframes right now do not go through regular clipboard machinery, // so when copying keyframes, make sure regular clipboard is cleared, or things // get confusing. if (s_KeyframeClipboard.Count > 0) Clipboard.stringValue = string.Empty; } public void CopyAllActiveCurves() { foreach (AnimationWindowCurve curve in activeCurves) { foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { s_KeyframeClipboard.Add(new AnimationWindowKeyframe(keyframe)); } } } public void PasteKeys() { if (s_KeyframeClipboard == null) s_KeyframeClipboard = new List(); SaveKeySelection(kEditCurveUndoLabel); HashSet oldSelection = new HashSet(selectedKeyHashes); ClearKeySelections(); AnimationWindowCurve lastTargetCurve = null; AnimationWindowCurve lastSourceCurve = null; float lastTime = 0f; List clipboardCurves = new List(); foreach (AnimationWindowKeyframe keyframe in s_KeyframeClipboard) if (!clipboardCurves.Any() || clipboardCurves.Last() != keyframe.curve) clipboardCurves.Add(keyframe.curve); // If we have equal number of target and source curves, then match by index. If not, then try to match with AnimationWindowUtility.BestMatchForPaste. bool matchCurveByIndex = clipboardCurves.Count == activeCurves.Count; int targetIndex = 0; foreach (AnimationWindowKeyframe keyframe in s_KeyframeClipboard) { if (lastSourceCurve != null && keyframe.curve != lastSourceCurve) targetIndex++; AnimationWindowKeyframe newKeyframe = new AnimationWindowKeyframe(keyframe); if (matchCurveByIndex) newKeyframe.curve = activeCurves[targetIndex]; else newKeyframe.curve = AnimationWindowUtility.BestMatchForPaste(newKeyframe.curve.binding, clipboardCurves, activeCurves); if (newKeyframe.curve == null) // Paste as new curve { // Curves are selected in the animation window hierarchy. Since we couldn't find a proper match, // create a new curve in first selected clip in active curves. if (activeCurves.Count > 0) { AnimationWindowCurve firstCurve = activeCurves[0]; if (firstCurve.animationIsEditable) { newKeyframe.curve = new AnimationWindowCurve(firstCurve.clip, keyframe.curve.binding, keyframe.curve.valueType); newKeyframe.curve.selectionBinding = firstCurve.selectionBinding; newKeyframe.time = keyframe.time; } } // If nothing is selected, create a new curve in first selected clip. else { if (selection.animationIsEditable) { newKeyframe.curve = new AnimationWindowCurve(selection.animationClip, keyframe.curve.binding, keyframe.curve.valueType); newKeyframe.curve.selectionBinding = selection; newKeyframe.time = keyframe.time; } } } if (newKeyframe.curve == null || !newKeyframe.curve.animationIsEditable) continue; newKeyframe.time = AnimationKeyTime.Time(newKeyframe.time + currentTime, newKeyframe.curve.clip.frameRate).timeRound; // Only allow pasting of key frame from numerical curves to numerical curves or from pptr curves to pptr curves. if ((newKeyframe.time >= 0.0f) && (newKeyframe.curve != null) && (newKeyframe.curve.isPPtrCurve == keyframe.curve.isPPtrCurve)) { var keyTime = AnimationKeyTime.Time(newKeyframe.time, newKeyframe.curve.clip.frameRate); if (newKeyframe.curve.HasKeyframe(keyTime)) newKeyframe.curve.RemoveKeyframe(keyTime); // When copy-pasting multiple keyframes (curve), its a continous thing. This is why we delete the existing keyframes in the pasted range. if (lastTargetCurve == newKeyframe.curve) newKeyframe.curve.RemoveKeysAtRange(lastTime, newKeyframe.time); newKeyframe.curve.AddKeyframe(newKeyframe, keyTime); SelectKey(newKeyframe); // TODO: Optimize to only save curve once instead once per keyframe SaveCurve(newKeyframe.curve.clip, newKeyframe.curve, kEditCurveUndoLabel); lastTargetCurve = newKeyframe.curve; lastTime = newKeyframe.time; } lastSourceCurve = keyframe.curve; } // If nothing is pasted, then we revert to old selection if (selectedKeyHashes.Count == 0) selectedKeyHashes = oldSelection; else ResampleAnimation(); } public void ClearSelections() { ClearKeySelections(); ClearHierarchySelection(); } public void ClearKeySelections() { selectedKeyHashes.Clear(); m_SelectedKeysCache = null; m_SelectionBoundsCache = null; } public void ClearHierarchySelection() { hierarchyState.selectedIDs.Clear(); m_ActiveCurvesCache = null; } private void ClearCurveWrapperCache() { if (m_ActiveCurveWrappersCache == null) return; for (int i = 0; i < m_ActiveCurveWrappersCache.Length; ++i) { CurveWrapper curveWrapper = m_ActiveCurveWrappersCache[i]; if (curveWrapper.renderer != null) curveWrapper.renderer.FlushCache(); } m_ActiveCurveWrappersCache = null; } private void ReloadModifiedDopelineCache() { if (m_dopelinesCache == null) return; for (int i = 0; i < m_dopelinesCache.Count; ++i) { DopeLine dopeLine = m_dopelinesCache[i]; AnimationWindowCurve[] curves = dopeLine.curves; for (int j = 0; j < curves.Length; ++j) { if (m_ModifiedCurves.Contains(curves[j].GetHashCode())) { dopeLine.InvalidateKeyframes(); break; } } } } private void ReloadModifiedCurveWrapperCache() { if (m_ActiveCurveWrappersCache == null) return; Dictionary updateList = new Dictionary(); for (int i = 0; i < m_ActiveCurveWrappersCache.Length; ++i) { CurveWrapper curveWrapper = m_ActiveCurveWrappersCache[i]; if (m_ModifiedCurves.Contains(curveWrapper.id)) { AnimationWindowCurve curve = filteredCurves.Find(c => c.GetHashCode() == curveWrapper.id); if (curve != null) { // Boundaries have changed, invalidate all curves if (curve.clip.startTime != curveWrapper.renderer.RangeStart() || curve.clip.stopTime != curveWrapper.renderer.RangeEnd()) { ClearCurveWrapperCache(); return; } else { updateList[i] = curve; } } } } // Only update curve wrappers that were modified. for (int i = 0; i < updateList.Count; ++i) { var entry = updateList.ElementAt(i); CurveWrapper curveWrapper = m_ActiveCurveWrappersCache[entry.Key]; if (curveWrapper.renderer != null) curveWrapper.renderer.FlushCache(); // Recreate curve wrapper only if curve has been modified. m_ActiveCurveWrappersCache[entry.Key] = AnimationWindowUtility.GetCurveWrapper(entry.Value, entry.Value.clip); } } private void ReloadModifiedAnimationCurveCache() { for (int i = 0; i < filteredCurves.Count; ++i) { AnimationWindowCurve curve = filteredCurves[i]; if (m_ModifiedCurves.Contains(curve.GetHashCode())) curve.LoadKeyframes(curve.clip); } } // This is called when there is a new curve, but after the data refresh. // This means that hierarchynodes and dopeline(s) for new curve are already available. private void OnNewCurveAdded(EditorCurveBinding newCurve) { // Retrieve group property name. // For example if we got "position.z" as our newCurve, // the property will be "position" with three child child nodes x,y,z string propertyName = newCurve.propertyName; string groupPropertyName = AnimationWindowUtility.GetPropertyGroupName(newCurve.propertyName); if (hierarchyData == null) return; if (HasHierarchySelection()) { // Update hierarchy selection with newly created curve. foreach (AnimationWindowHierarchyNode node in hierarchyData.GetRows()) { if (node.path != newCurve.path || node.animatableObjectType != newCurve.type || (node.propertyName != propertyName && node.propertyName != groupPropertyName)) continue; SelectHierarchyItem(node.id, true, false); // We want the pptr curves to be in tall mode by default if (newCurve.isPPtrCurve) hierarchyState.AddTallInstance(node.id); } } // Values do not change whenever a new curve is added, so we force an inspector update here. ResampleAnimation(); m_lastAddedCurveBinding = null; } public void Repaint() { if (animEditor != null) animEditor.Repaint(); } public List GetAggregateKeys(AnimationWindowHierarchyNode hierarchyNode) { DopeLine dopeline = dopelines.FirstOrDefault(e => e.hierarchyNodeID == hierarchyNode.id); if (dopeline == null) return null; return dopeline.keys; } public void OnHierarchySelectionChanged(int[] selectedInstanceIDs) { HandleHierarchySelectionChanged(selectedInstanceIDs, true); } public void HandleHierarchySelectionChanged(int[] selectedInstanceIDs, bool triggerSceneSelectionSync) { m_ActiveCurvesCache = null; if (triggerSceneSelectionSync) SyncSceneSelection(selectedInstanceIDs); } public void SelectHierarchyItem(DopeLine dopeline, bool additive) { SelectHierarchyItem(dopeline.hierarchyNodeID, additive, true); } public void SelectHierarchyItem(DopeLine dopeline, bool additive, bool triggerSceneSelectionSync) { SelectHierarchyItem(dopeline.hierarchyNodeID, additive, triggerSceneSelectionSync); } public void SelectHierarchyItem(int hierarchyNodeID, bool additive, bool triggerSceneSelectionSync) { if (!additive) ClearHierarchySelection(); hierarchyState.selectedIDs.Add(hierarchyNodeID); int[] selectedInstanceIDs = hierarchyState.selectedIDs.ToArray(); // We need to manually trigger this event, because injecting data to m_SelectedInstanceIDs directly doesn't trigger one via TreeView HandleHierarchySelectionChanged(selectedInstanceIDs, triggerSceneSelectionSync); } public void SelectHierarchyItems(IEnumerable hierarchyNodeIDs, bool additive, bool triggerSceneSelectionSync) { if (!additive) ClearHierarchySelection(); hierarchyState.selectedIDs.AddRange(hierarchyNodeIDs); int[] selectedInstanceIDs = hierarchyState.selectedIDs.ToArray(); // We need to manually trigger this event, because injecting data to m_SelectedInstanceIDs directly doesn't trigger one via TreeView HandleHierarchySelectionChanged(selectedInstanceIDs, triggerSceneSelectionSync); } public void UnSelectHierarchyItem(DopeLine dopeline) { UnSelectHierarchyItem(dopeline.hierarchyNodeID); } public void UnSelectHierarchyItem(int hierarchyNodeID) { hierarchyState.selectedIDs.Remove(hierarchyNodeID); } public bool HasHierarchySelection() { if (hierarchyState.selectedIDs.Count == 0) return false; if (hierarchyState.selectedIDs.Count == 1) return (hierarchyState.selectedIDs[0] != 0); return true; } public HashSet GetAffectedHierarchyIDs(List keyframes) { HashSet hierarchyIDs = new HashSet(); foreach (AnimationWindowKeyframe keyframe in keyframes) { var curve = keyframe.curve; int hierarchyID = AnimationWindowUtility.GetPropertyNodeID(0, curve.path, curve.type, curve.propertyName); if (hierarchyIDs.Add(hierarchyID)) { string propertyGroupName = AnimationWindowUtility.GetPropertyGroupName(curve.propertyName); hierarchyIDs.Add(AnimationWindowUtility.GetPropertyNodeID(0, curve.path, curve.type, propertyGroupName)); } } return hierarchyIDs; } public List GetAffectedCurves(List keyframes) { List affectedCurves = new List(); foreach (AnimationWindowKeyframe keyframe in keyframes) if (!affectedCurves.Contains(keyframe.curve)) affectedCurves.Add(keyframe.curve); return affectedCurves; } public DopeLine GetDopeline(int selectedInstanceID) { foreach (var dopeline in dopelines) { if (dopeline.hierarchyNodeID == selectedInstanceID) return dopeline; } return null; } // Set scene active go to be the same as the one selected from hierarchy private void SyncSceneSelection(int[] selectedNodeIDs) { if (filterBySelection) return; if (!selection.canSyncSceneSelection) return; GameObject rootGameObject = selection.rootGameObject; if (rootGameObject == null) return; List selectedGameObjectIDs = new List(selectedNodeIDs.Length); foreach (var selectedNodeID in selectedNodeIDs) { // Skip nodes without associated curves. if (selectedNodeID == 0) continue; AnimationWindowHierarchyNode node = hierarchyData.FindItem(selectedNodeID) as AnimationWindowHierarchyNode; if (node == null) continue; if (node is AnimationWindowHierarchyMasterNode) continue; Transform t = rootGameObject.transform.Find(node.path); // In the case of nested animation component, we don't want to sync the scene selection (case 569506) // When selection changes, animation window will always pick nearest animator component in terms of hierarchy depth // Automatically syncinc scene selection in nested scenarios would cause unintuitive clip & animation change for animation window so we check for it and deny sync if necessary if (t != null && rootGameObject != null && activeAnimationPlayer == AnimationWindowUtility.GetClosestAnimationPlayerComponentInParents(t)) selectedGameObjectIDs.Add(t.gameObject.GetInstanceID()); } if (selectedGameObjectIDs.Count > 0) UnityEditor.Selection.instanceIDs = selectedGameObjectIDs.ToArray(); else UnityEditor.Selection.activeGameObject = rootGameObject; } public float clipFrameRate { get { if (activeAnimationClip == null) return 60.0f; return activeAnimationClip.frameRate; } set { // @TODO: Changing the clip in AnimationWindowState.frame rate feels a bit intrusive // Should probably be done explicitly from the UI and not go through AnimationWindowState... if (activeAnimationClip != null && value > 0 && value <= 10000) { // Clear selection and save empty selection snapshot for undo consistency. ClearKeySelections(); SaveKeySelection(kEditCurveUndoLabel); // Reposition all keyframes to match the new sampling rate foreach (var curve in allCurves) { foreach (var key in curve.keyframes) { int frame = AnimationKeyTime.Time(key.time, clipFrameRate).frame; key.time = AnimationKeyTime.Frame(frame, value).time; } } SaveCurves(activeAnimationClip, allCurves, kEditCurveUndoLabel); AnimationEvent[] events = AnimationUtility.GetAnimationEvents(activeAnimationClip); foreach (AnimationEvent ev in events) { int frame = AnimationKeyTime.Time(ev.time, clipFrameRate).frame; ev.time = AnimationKeyTime.Frame(frame, value).time; } AnimationUtility.SetAnimationEvents(activeAnimationClip, events); activeAnimationClip.frameRate = value; } } } public float frameRate { get { return m_FrameRate; } set { if (m_FrameRate != value) { m_FrameRate = value; if (onFrameRateChange != null) onFrameRateChange(m_FrameRate); } } } public AnimationKeyTime time => AnimationKeyTime.Time(controlInterface.time, frameRate); public int currentFrame { get => controlInterface.frame; set => controlInterface.frame = value; } public float currentTime { get => controlInterface.time; set => controlInterface.time = value; } public TimeArea.TimeFormat timeFormat { get { return AnimationWindowOptions.timeFormat; } set { AnimationWindowOptions.timeFormat = value; } } public TimeArea timeArea { get { return m_TimeArea; } set { m_TimeArea = value; } } // Pixel to time ratio (used for time-pixel conversions) public float pixelPerSecond { get { return timeArea.m_Scale.x; } } // The GUI x-coordinate, where time==0 (used for time-pixel conversions) public float zeroTimePixel { get { return timeArea.shownArea.xMin * timeArea.m_Scale.x * -1f; } } public float PixelToTime(float pixel) { return PixelToTime(pixel, SnapMode.Disabled); } public float PixelToTime(float pixel, SnapMode snap) { float time = pixel - zeroTimePixel; return SnapToFrame(time / pixelPerSecond, snap); } public float TimeToPixel(float time) { return TimeToPixel(time, SnapMode.Disabled); } public float TimeToPixel(float time, SnapMode snap) { return SnapToFrame(time, snap) * pixelPerSecond + zeroTimePixel; } //@TODO: Move to animatkeytime?? public float SnapToFrame(float time, SnapMode snap) { if (snap == SnapMode.Disabled) return time; float fps = (snap == SnapMode.SnapToFrame) ? frameRate : clipFrameRate; return SnapToFrame(time, fps); } public float SnapToFrame(float time, float fps) { float snapTime = Mathf.Round(time * fps) / fps; return snapTime; } public float minVisibleTime { get { return m_TimeArea.shownArea.xMin; } } public float maxVisibleTime { get { return m_TimeArea.shownArea.xMax; } } public float visibleTimeSpan { get { return maxVisibleTime - minVisibleTime; } } public float minVisibleFrame { get { return minVisibleTime * frameRate; } } public float maxVisibleFrame { get { return maxVisibleTime * frameRate; } } public float visibleFrameSpan { get { return visibleTimeSpan * frameRate; } } public float minTime { get { return timeRange.x; } } public float maxTime { get { return timeRange.y; } } public Vector2 timeRange { get { if (activeAnimationClip != null) return new Vector2(activeAnimationClip.startTime, activeAnimationClip.stopTime); return Vector2.zero; } } public string FormatFrame(int frame, int frameDigits) { return (frame / (int)frameRate) + ":" + (frame % frameRate).ToString().PadLeft(frameDigits, '0'); } //@TODO: Remove. Replace with animationkeytime public float TimeToFrame(float time) { return time * frameRate; } //@TODO: Remove. Replace with animationkeytime public float FrameToTime(float frame) { return frame / frameRate; } public int TimeToFrameFloor(float time) { return Mathf.FloorToInt(TimeToFrame(time)); } public int TimeToFrameRound(float time) { return Mathf.RoundToInt(TimeToFrame(time)); } public float FrameToPixel(float i, Rect rect) { return (i - minVisibleFrame) * rect.width / visibleFrameSpan; } public float FrameDeltaToPixel(Rect rect) { return rect.width / visibleFrameSpan; } public float TimeToPixel(float time, Rect rect) { return FrameToPixel(time * frameRate, rect); } public float PixelToTime(float pixelX, Rect rect) { return (pixelX * visibleTimeSpan / rect.width + minVisibleTime); } public float PixelDeltaToTime(Rect rect) { return visibleTimeSpan / rect.width; } public void GoToPreviousFrame() { controlInterface.frame -= 1; } public void GoToNextFrame() { controlInterface.frame += 1; } public void GoToPreviousKeyframe() { List curves = (showCurveEditor && activeCurves.Count > 0) ? activeCurves : filteredCurves; float newTime = AnimationWindowUtility.GetPreviousKeyframeTime(curves.ToArray(), controlInterface.time, clipFrameRate); controlInterface.time = SnapToFrame(newTime, SnapMode.SnapToFrame); } public void GoToNextKeyframe() { List curves = (showCurveEditor && activeCurves.Count > 0) ? activeCurves : filteredCurves; float newTime = AnimationWindowUtility.GetNextKeyframeTime(curves.ToArray(), controlInterface.time, clipFrameRate); controlInterface.time = SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToFrame); } public void GoToFirstKeyframe() { if (activeAnimationClip) controlInterface.time = activeAnimationClip.startTime; } public void GoToLastKeyframe() { if (activeAnimationClip) controlInterface.time = activeAnimationClip.stopTime; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowStyles.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using UnityEditorInternal; namespace UnityEditor { internal class AnimationWindowStyles { public static Texture2D pointIcon = EditorGUIUtility.LoadIcon("animationkeyframe"); public static GUIContent playContent = EditorGUIUtility.TrIconContent("Animation.Play", "Play the animation clip."); public static GUIContent recordContent = EditorGUIUtility.TrIconContent("Animation.Record", "Enable/disable keyframe recording mode."); public static GUIContent previewContent = EditorGUIUtility.TrTextContent("Preview", "Enable/disable scene preview mode."); public static GUIContent prevKeyContent = EditorGUIUtility.TrIconContent("Animation.PrevKey", "Go to previous keyframe."); public static GUIContent nextKeyContent = EditorGUIUtility.TrIconContent("Animation.NextKey", "Go to next keyframe."); public static GUIContent firstKeyContent = EditorGUIUtility.TrIconContent("Animation.FirstKey", "Go to the beginning of the animation clip."); public static GUIContent lastKeyContent = EditorGUIUtility.TrIconContent("Animation.LastKey", "Go to the end of the animation clip."); public static GUIContent addKeyframeContent = EditorGUIUtility.TrIconContent("Animation.AddKeyframe", "Add keyframe."); public static GUIContent addEventContent = EditorGUIUtility.TrIconContent("Animation.AddEvent", "Add event."); public static GUIContent filterBySelectionContent = EditorGUIUtility.TrIconContent("Animation.FilterBySelection", "Filter by selection."); public static GUIContent sequencerLinkContent = EditorGUIUtility.TrIconContent("Animation.SequencerLink", "Animation Window is linked to Timeline Editor. Press to Unlink."); public static GUIContent noAnimatableObjectSelectedText = EditorGUIUtility.TrTextContent("No animatable object selected."); public static GUIContent formatIsMissing = EditorGUIUtility.TrTextContent("To begin animating {0}, create {1}."); public static GUIContent animatorAndAnimationClip = EditorGUIUtility.TrTextContent("an Animator and an Animation Clip"); public static GUIContent animationClip = EditorGUIUtility.TrTextContent("an Animation Clip"); public static GUIContent create = EditorGUIUtility.TrTextContent("Create"); public static GUIContent dopesheet = EditorGUIUtility.TrTextContent("Dopesheet"); public static GUIContent curves = EditorGUIUtility.TrTextContent("Curves"); public static GUIContent samples = EditorGUIUtility.TrTextContent("Samples"); public static GUIContent createNewClip = EditorGUIUtility.TrTextContent("Create New Clip..."); public static GUIContent animatorOptimizedText = EditorGUIUtility.TrTextContent("Editing and playback of animations on optimized game object hierarchy is not supported.\nPlease select a game object that does not have 'Optimize Game Objects' applied."); public static GUIContent readOnlyPropertiesLabel = EditorGUIUtility.TrTextContent("Animation Clip is Read-Only"); public static GUIContent readOnlyPropertiesButton = EditorGUIUtility.TrTextContent("Show Read-Only Properties"); public static GUIContent optionsContent = EditorGUIUtility.IconContent("_Menu"); public static GUIStyle playHead = "AnimationPlayHead"; public static GUIStyle animPlayToolBar = "AnimPlayToolbar"; public static GUIStyle animClipToolBar = "AnimClipToolbar"; public static GUIStyle animClipToolbarButton = "AnimClipToolbarButton"; public static GUIStyle animClipToolbarPopup = "AnimClipToolbarPopup"; public static GUIStyle timeRulerBackground = "TimeRulerBackground"; public static GUIStyle curveEditorBackground = "CurveEditorBackground"; public static GUIStyle curveEditorLabelTickmarks = "CurveEditorLabelTickmarks"; public static GUIStyle eventBackground = "AnimationEventBackground"; public static GUIStyle eventTooltip = "AnimationEventTooltip"; public static GUIStyle eventTooltipArrow = "AnimationEventTooltipArrow"; public static GUIStyle keyframeBackground = "AnimationKeyframeBackground"; public static GUIStyle timelineTick = "AnimationTimelineTick"; public static GUIStyle dopeSheetKeyframe = "Dopesheetkeyframe"; public static GUIStyle dopeSheetBackground = "DopesheetBackground"; public static GUIStyle popupCurveDropdown = "PopupCurveDropdown"; public static GUIStyle popupCurveEditorBackground = "PopupCurveEditorBackground"; public static GUIStyle popupCurveEditorSwatch = "PopupCurveEditorSwatch"; public static GUIStyle popupCurveSwatchBackground = "PopupCurveSwatchBackground"; public static GUIStyle separator = new GUIStyle("AnimLeftPaneSeparator"); public static GUIStyle toolbarBottom = "ToolbarBottom"; public static GUIStyle optionsButton = new GUIStyle(EditorStyles.toolbarButtonRight); public static GUIStyle miniToolbarButton = new GUIStyle(EditorStyles.toolbarButton); public static GUIStyle toolbarLabel = new GUIStyle(AnimationWindowStyles.animClipToolbarPopup); public static void Initialize() { toolbarLabel.normal.background = null; optionsButton.padding = new RectOffset(); optionsButton.imagePosition = ImagePosition.ImageOnly; optionsButton.contentOffset = new Vector2(-7, 0); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/AnimationWindowUtility.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Globalization; using System.Linq; using System.IO; using System.Collections.Generic; using UnityEditor; using UnityEditor.Animations; using UnityEngine; using Object = UnityEngine.Object; using TangentMode = UnityEditor.AnimationUtility.TangentMode; namespace UnityEditorInternal { static class AnimationWindowUtility { public static void SaveCurve(AnimationClip clip, AnimationWindowCurve curve) { if (!curve.animationIsEditable) throw new ArgumentException("Curve is not editable and shouldn't be saved."); if (curve.isPPtrCurve) { ObjectReferenceKeyframe[] objectCurve = curve.ToObjectCurve(); if (objectCurve.Length == 0) objectCurve = null; AnimationUtility.SetObjectReferenceCurve(clip, curve.binding, objectCurve); } else { AnimationCurve animationCurve = curve.ToAnimationCurve(); if (animationCurve.keys.Length == 0) animationCurve = null; else AnimationUtility.UpdateTangentsFromMode(animationCurve); AnimationUtility.SetEditorCurve(clip, curve.binding, animationCurve); } } public static void SaveCurves(AnimationClip clip, IEnumerable curves) { foreach (AnimationWindowCurve curve in curves) { if (!curve.animationIsEditable) throw new ArgumentException("Curve is not editable and shouldn't be saved."); if (curve.isPPtrCurve) { ObjectReferenceKeyframe[] objectCurve = curve.ToObjectCurve(); if (objectCurve.Length == 0) objectCurve = null; AnimationUtility.SetObjectReferenceCurveNoSync(clip, curve.binding, objectCurve); } else { AnimationCurve animationCurve = curve.ToAnimationCurve(); if (animationCurve.keys.Length == 0) animationCurve = null; else AnimationUtility.UpdateTangentsFromMode(animationCurve); AnimationUtility.SetEditorCurveNoSync(clip, curve.binding, animationCurve); } } AnimationUtility.SyncEditorCurves(clip); } public static void SaveCurves(AnimationClip clip, IList bindings, IList curves) { if (bindings.Count != curves.Count) throw new ArgumentException("bindings and curves array sizes do not match"); for (int i = 0; i < bindings.Count; ++i) { AnimationUtility.SetEditorCurveNoSync(clip, bindings[i], curves[i]); } AnimationUtility.SyncEditorCurves(clip); } public static void CreateDefaultCurves(AnimationWindowState state, EditorCurveBinding[] properties) { CreateDefaultCurves(state, state.activeAnimationClip, properties); } public static void CreateDefaultCurves(AnimationWindowState state, AnimationClip animationClip, EditorCurveBinding[] properties) { properties = RotationCurveInterpolation.ConvertRotationPropertiesToDefaultInterpolation(animationClip, properties); if (properties.Length == 0) return; var curves = new List(properties.Length); foreach (EditorCurveBinding prop in properties) curves.Add(CreateDefaultCurve(state, animationClip, prop)); state.SaveCurves(animationClip, curves); } public static AnimationWindowCurve CreateDefaultCurve(AnimationWindowState state, AnimationClip animationClip, EditorCurveBinding binding) { Type type = state.controlInterface.GetValueType(binding); AnimationWindowCurve curve = new AnimationWindowCurve(animationClip, binding, type); object currentValue = CurveBindingUtility.GetCurrentValue(state, binding); if (animationClip.length == 0.0F) { AddKeyframeToCurve(curve, currentValue, type, AnimationKeyTime.Time(0.0F, animationClip.frameRate)); } else { AddKeyframeToCurve(curve, currentValue, type, AnimationKeyTime.Time(0.0F, animationClip.frameRate)); AddKeyframeToCurve(curve, currentValue, type, AnimationKeyTime.Time(animationClip.length, animationClip.frameRate)); } return curve; } public static bool ShouldShowAnimationWindowCurve(EditorCurveBinding curveBinding) { // We don't want to convert the w component of rotation curves to be shown in animation window if (IsTransformType(curveBinding.type)) return !curveBinding.propertyName.EndsWith(".w"); return true; } public static bool IsNodeLeftOverCurve(AnimationWindowState state, AnimationWindowHierarchyNode node) { if (node.binding != null) { if (node.curves.Length > 0) { AnimationWindowSelectionItem selectionBinding = node.curves[0].selectionBinding; if (selectionBinding != null) { if (selectionBinding.rootGameObject == null && selectionBinding.scriptableObject == null) return false; return state.controlInterface.GetValueType((EditorCurveBinding)node.binding) == null; } } } // Go through all child nodes recursively if (node.hasChildren) { foreach (var child in node.children) return IsNodeLeftOverCurve(state, child as AnimationWindowHierarchyNode); } return false; } public static bool IsNodeAmbiguous(AnimationWindowHierarchyNode node) { if (node.binding != null) { if (node.curves.Length > 0) { AnimationWindowSelectionItem selectionBinding = node.curves[0].selectionBinding; if (selectionBinding != null) { if (selectionBinding.rootGameObject != null) return AnimationUtility.AmbiguousBinding(node.binding.Value.path, node.binding.Value.m_ClassID, selectionBinding.rootGameObject.transform); } } } // Go through all child nodes recursively if (node.hasChildren) { foreach (var child in node.children) return IsNodeAmbiguous(child as AnimationWindowHierarchyNode); } return false; } public static bool IsNodePhantom(AnimationWindowHierarchyNode node) { if (node.binding != null) return node.binding.Value.isPhantom; return false; } public static void AddSelectedKeyframes(AnimationWindowState state, AnimationKeyTime time) { List curves = state.activeCurves.Count > 0 ? state.activeCurves : state.filteredCurves; AddKeyframes(state, curves, time); } public static void AddKeyframes(AnimationWindowState state, IList curves, AnimationKeyTime time) { string undoLabel = L10n.Tr("Add Key"); state.SaveKeySelection(undoLabel); state.ClearKeySelections(); foreach (AnimationWindowCurve curve in curves) { if (!curve.animationIsEditable) continue; AnimationKeyTime shiftedMouseKeyTime = AnimationKeyTime.Time(time.time, time.frameRate); object value = CurveBindingUtility.GetCurrentValue(state, curve); AnimationWindowKeyframe keyframe = AnimationWindowUtility.AddKeyframeToCurve(curve, value, curve.valueType, shiftedMouseKeyTime); if (keyframe != null) state.SelectKey(keyframe); } state.SaveCurves(state.activeAnimationClip, curves, undoLabel); } public static void RemoveKeyframes(AnimationWindowState state, IList curves, AnimationKeyTime time) { string undoLabel = L10n.Tr("Remove Key"); state.SaveKeySelection(undoLabel); foreach (AnimationWindowCurve curve in curves) { if (!curve.animationIsEditable) continue; curve.RemoveKeyframe(time); } state.SaveCurves(state.activeAnimationClip, curves, undoLabel); } public static AnimationWindowKeyframe AddKeyframeToCurve(AnimationWindowCurve curve, object value, Type type, AnimationKeyTime time) { // When there is already a key a this time // Make sure that only value is updated but tangents are maintained. AnimationWindowKeyframe previousKey = curve.FindKeyAtTime(time); if (previousKey != null) { previousKey.value = value; return previousKey; } AnimationWindowKeyframe keyframe = null; if (curve.isPPtrCurve) { keyframe = new AnimationWindowKeyframe(); keyframe.time = time.time; keyframe.value = value; keyframe.curve = curve; curve.AddKeyframe(keyframe, time); } else if (curve.isDiscreteCurve) { Keyframe tempKey = new Keyframe(time.time, 0f); AnimationUtility.SetKeyLeftTangentMode(ref tempKey, TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(ref tempKey, TangentMode.Constant); AnimationUtility.SetKeyBroken(ref tempKey, true); keyframe = new AnimationWindowKeyframe(curve, tempKey); keyframe.value = Convert.ToInt32(value); curve.AddKeyframe(keyframe, time); } else if (type == typeof(bool) || type == typeof(float) || type == typeof(int)) { Keyframe tempKey = new Keyframe(time.time, Convert.ToSingle(value)); if (type == typeof(bool)) { AnimationUtility.SetKeyLeftTangentMode(ref tempKey, TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(ref tempKey, TangentMode.Constant); AnimationUtility.SetKeyBroken(ref tempKey, true); } else if (type == typeof(int)) { // Create temporary curve to get proper tangents AnimationCurve animationCurve = curve.ToAnimationCurve(); if (animationCurve.length <= 1) { TangentMode tangentMode = TangentMode.Linear; AnimationUtility.SetKeyLeftTangentMode(ref tempKey, tangentMode); AnimationUtility.SetKeyRightTangentMode(ref tempKey, tangentMode); } else { int keyIndex = animationCurve.AddKey(tempKey); if (keyIndex != -1) { CurveUtility.SetKeyModeFromContext(animationCurve, keyIndex); tempKey = animationCurve[keyIndex]; } } AnimationUtility.SetKeyBroken(ref tempKey, true); } else { // Create temporary curve to get proper tangents AnimationCurve animationCurve = curve.ToAnimationCurve(); int keyIndex = animationCurve.AddKey(tempKey); if (keyIndex != -1) { // Make sure tangent slopes default to ClampedAuto. Tangent mode will be modified afterwards. AnimationUtility.SetKeyLeftTangentMode(animationCurve, keyIndex, TangentMode.ClampedAuto); AnimationUtility.SetKeyRightTangentMode(animationCurve, keyIndex, TangentMode.ClampedAuto); AnimationUtility.UpdateTangentsFromModeSurrounding(animationCurve, keyIndex); CurveUtility.SetKeyModeFromContext(animationCurve, keyIndex); tempKey = animationCurve[keyIndex]; } } keyframe = new AnimationWindowKeyframe(curve, tempKey); curve.AddKeyframe(keyframe, time); } return keyframe; } public static List FilterCurves(AnimationWindowCurve[] curves, string path, bool entireHierarchy) { List results = new List(); if (curves != null) { foreach (AnimationWindowCurve curve in curves) if (curve.path.Equals(path) || (entireHierarchy && curve.path.Contains(path))) results.Add(curve); } return results; } public static List FilterCurves(AnimationWindowCurve[] curves, string path, Type animatableObjectType) { List results = new List(); if (curves != null) { foreach (AnimationWindowCurve curve in curves) if (curve.path.Equals(path) && curve.type == animatableObjectType) results.Add(curve); } return results; } public static bool IsCurveCreated(AnimationClip clip, EditorCurveBinding binding) { if (binding.isPPtrCurve) { return AnimationUtility.GetObjectReferenceCurve(clip, binding) != null; } else { // For RectTransform.position we only want .z if (IsRectTransformPosition(binding)) binding.propertyName = binding.propertyName.Replace(".x", ".z").Replace(".y", ".z"); if (IsRotationCurve(binding)) { return AnimationUtility.GetEditorCurve(clip, binding) != null || HasOtherRotationCurve(clip, binding); } return AnimationUtility.GetEditorCurve(clip, binding) != null; } } internal static bool HasOtherRotationCurve(AnimationClip clip, EditorCurveBinding rotationBinding) { if (rotationBinding.propertyName.StartsWith("m_LocalRotation")) { EditorCurveBinding x = rotationBinding; EditorCurveBinding y = rotationBinding; EditorCurveBinding z = rotationBinding; x.propertyName = "localEulerAnglesRaw.x"; y.propertyName = "localEulerAnglesRaw.y"; z.propertyName = "localEulerAnglesRaw.z"; return AnimationUtility.GetEditorCurve(clip, x) != null || AnimationUtility.GetEditorCurve(clip, y) != null || AnimationUtility.GetEditorCurve(clip, z) != null; } else { EditorCurveBinding x = rotationBinding; EditorCurveBinding y = rotationBinding; EditorCurveBinding z = rotationBinding; EditorCurveBinding w = rotationBinding; x.propertyName = "m_LocalRotation.x"; y.propertyName = "m_LocalRotation.y"; z.propertyName = "m_LocalRotation.z"; w.propertyName = "m_LocalRotation.w"; return AnimationUtility.GetEditorCurve(clip, x) != null || AnimationUtility.GetEditorCurve(clip, y) != null || AnimationUtility.GetEditorCurve(clip, z) != null || AnimationUtility.GetEditorCurve(clip, w) != null; } } internal static bool IsRotationCurve(EditorCurveBinding curveBinding) { string propertyName = GetPropertyGroupName(curveBinding.propertyName); return propertyName == "m_LocalRotation" || propertyName == "localEulerAnglesRaw"; } public static bool IsRectTransformPosition(EditorCurveBinding curveBinding) { return curveBinding.type == typeof(RectTransform) && GetPropertyGroupName(curveBinding.propertyName) == "m_LocalPosition"; } public static bool ContainsFloatKeyframes(List keyframes) { if (keyframes == null || keyframes.Count == 0) return false; foreach (var key in keyframes) { if (!key.isPPtrCurve) return true; } return false; } // Get curves for property or propertygroup (example: x,y,z) public static List FilterCurves(AnimationWindowCurve[] curves, string path, Type animatableObjectType, string propertyName) { List results = new List(); if (curves != null) { string propertyGroupName = GetPropertyGroupName(propertyName); bool isPropertyGroup = propertyGroupName == propertyName; foreach (AnimationWindowCurve curve in curves) { bool propertyNameMatches = isPropertyGroup ? GetPropertyGroupName(curve.propertyName).Equals(propertyGroupName) : curve.propertyName.Equals(propertyName); if (curve.path.Equals(path) && curve.type == animatableObjectType && propertyNameMatches) results.Add(curve); } } return results; } // Current value of the property that rootGO + curveBinding is pointing to public static object GetCurrentValue(GameObject rootGameObject, EditorCurveBinding curveBinding) { if (curveBinding.isPPtrCurve) { AnimationUtility.GetObjectReferenceValue(rootGameObject, curveBinding, out var value); return value; } else if (curveBinding.isDiscreteCurve) { AnimationUtility.GetDiscreteIntValue(rootGameObject, curveBinding, out var value); return value; } else { AnimationUtility.GetFloatValue(rootGameObject, curveBinding, out var value); return value; } } public static EditorCurveBinding[] GetAnimatableBindings(GameObject rootGameObject) { if (rootGameObject != null) { var transforms = rootGameObject.GetComponentsInChildren(); // At least 10 bindings for Transform (m_LocalPosition, m_LocalRotation, m_LocalScale) and 1 binding for GameObject (m_IsActive) const int kMinNumberOfBindingsPerGameObject = 11; var bindings = new List(transforms.Length * kMinNumberOfBindingsPerGameObject); for (int i = 0; i < transforms.Length; ++i) { bindings.AddRange(AnimationUtility.GetAnimatableBindings(transforms[i].gameObject, rootGameObject)); } return bindings.ToArray(); } return Array.Empty(); } public static bool PropertyIsAnimatable(Object targetObject, string propertyPath, Object rootObject) { if (targetObject is ScriptableObject) { ScriptableObject scriptableObject = (ScriptableObject)targetObject; EditorCurveBinding[] allCurveBindings = AnimationUtility.GetAnimatableBindings(scriptableObject); return Array.Exists(allCurveBindings, binding => binding.propertyName == propertyPath); } else { GameObject gameObject = targetObject as GameObject; if (targetObject is Component) gameObject = ((Component)targetObject).gameObject; if (gameObject != null) { var dummyModification = new PropertyModification(); dummyModification.propertyPath = propertyPath; dummyModification.target = targetObject; EditorCurveBinding binding = new EditorCurveBinding(); return AnimationUtility.PropertyModificationToEditorCurveBinding(dummyModification, rootObject == null ? gameObject : (GameObject)rootObject, out binding) != null; } } return false; } // Given a serialized property, gathers all animateable properties public static PropertyModification[] SerializedPropertyToPropertyModifications(SerializedProperty property) { List properties = new List(); properties.Add(property); // handles child properties (Vector3 is 3 recordable properties) if (property.hasChildren) { var iter = property.Copy(); var end = property.GetEndProperty(false); // recurse over all children properties while (iter.Next(true) && !SerializedProperty.EqualContents(iter, end) && iter.propertyPath.StartsWith(property.propertyPath)) { properties.Add(iter.Copy()); } } // Special case for m_LocalRotation... if (property.propertyPath.StartsWith("m_LocalRotation")) { var serializedObject = property.serializedObject; if (serializedObject.targetObject is Transform) { SerializedProperty eulerHintProperty = serializedObject.FindProperty("m_LocalEulerAnglesHint"); if (eulerHintProperty != null && eulerHintProperty.hasChildren) { var iter = eulerHintProperty.Copy(); var end = eulerHintProperty.GetEndProperty(false); // recurse over all children properties while (iter.Next(true) && !SerializedProperty.EqualContents(iter, end) && iter.propertyPath.StartsWith(eulerHintProperty.propertyPath)) { properties.Add(iter.Copy()); } } } } List modifications = new List(); for (int i = 0; i < properties.Count; ++i) { var propertyIter = properties[i]; var isObject = propertyIter.propertyType == SerializedPropertyType.ObjectReference; var isFloat = propertyIter.propertyType == SerializedPropertyType.Float; var isBool = propertyIter.propertyType == SerializedPropertyType.Boolean; var isInt = propertyIter.propertyType == SerializedPropertyType.Integer; var isEnum = propertyIter.propertyType == SerializedPropertyType.Enum; if (isObject || isFloat || isBool || isInt || isEnum) { var serializedObject = propertyIter.serializedObject; var targetObjects = serializedObject.targetObjects; if (propertyIter.hasMultipleDifferentValues) { for (int j = 0; j < targetObjects.Length; ++j) { var singleObject = new SerializedObject(targetObjects[j]); SerializedProperty singleProperty = singleObject.FindProperty(propertyIter.propertyPath); string value = string.Empty; Object objectReference = null; if (isObject) objectReference = singleProperty.objectReferenceValue; else if (isFloat) value = singleProperty.floatValue.ToString(CultureInfo.InvariantCulture); else if (isInt) value = singleProperty.intValue.ToString(); else if (isEnum) value = singleProperty.enumValueIndex.ToString(); else // if (isBool) value = singleProperty.boolValue ? "1" : "0"; var modification = new PropertyModification(); modification.target = targetObjects[j]; modification.propertyPath = (singleProperty.isReferencingAManagedReferenceField ? singleProperty.managedReferencePropertyPath : singleProperty.propertyPath); modification.value = value; modification.objectReference = objectReference; modifications.Add(modification); } } // fast path else { string value = string.Empty; Object objectReference = null; if (isObject) objectReference = propertyIter.objectReferenceValue; else if (isFloat) value = propertyIter.floatValue.ToString(CultureInfo.InvariantCulture); else if (isInt) value = propertyIter.intValue.ToString(); else if (isEnum) value = propertyIter.enumValueIndex.ToString(); else // if (isBool) value = propertyIter.boolValue ? "1" : "0"; for (int j = 0; j < targetObjects.Length; ++j) { var modification = new PropertyModification(); modification.target = targetObjects[j]; modification.propertyPath = (propertyIter.isReferencingAManagedReferenceField ? propertyIter.managedReferencePropertyPath : propertyIter.propertyPath); modification.value = value; modification.objectReference = objectReference; modifications.Add(modification); } } } } return modifications.ToArray(); } public static EditorCurveBinding[] PropertyModificationsToEditorCurveBindings(PropertyModification[] modifications, GameObject rootGameObject, AnimationClip animationClip) { if (modifications == null) return new EditorCurveBinding[] {}; var bindings = new HashSet(); for (int i = 0; i < modifications.Length; ++i) { var binding = new EditorCurveBinding(); if (AnimationUtility.PropertyModificationToEditorCurveBinding(modifications[i], rootGameObject, out binding) != null) { EditorCurveBinding[] additionalBindings = RotationCurveInterpolation.RemapAnimationBindingForAddKey(binding, animationClip); if (additionalBindings != null) { for (int j = 0; j < additionalBindings.Length; ++j) { bindings.Add(additionalBindings[j]); } } else { bindings.Add(binding); } } } return bindings.ToArray(); } public static EditorCurveBinding[] SerializedPropertyToEditorCurveBindings(SerializedProperty property, GameObject rootGameObject, AnimationClip animationClip) { PropertyModification[] modifications = AnimationWindowUtility.SerializedPropertyToPropertyModifications(property); return PropertyModificationsToEditorCurveBindings(modifications, rootGameObject, animationClip); } public static bool CurveExists(EditorCurveBinding binding, AnimationWindowCurve[] curves) { foreach (var animationWindowCurve in curves) { if (binding.propertyName == animationWindowCurve.binding.propertyName && binding.type == animationWindowCurve.binding.type && binding.path == animationWindowCurve.binding.path) return true; } return false; } public static EditorCurveBinding GetRenamedBinding(EditorCurveBinding binding, string newPath) { EditorCurveBinding newBinding = new EditorCurveBinding(); newBinding.path = newPath; newBinding.propertyName = binding.propertyName; newBinding.type = binding.type; return newBinding; } public static void RenameCurvePath(AnimationWindowCurve curve, EditorCurveBinding newBinding, AnimationClip clip) { if (curve.isPPtrCurve) { // Delete old curve AnimationUtility.SetObjectReferenceCurve(clip, curve.binding, null); // Add new curve AnimationUtility.SetObjectReferenceCurve(clip, newBinding, curve.ToObjectCurve()); } else { // Delete old curve AnimationUtility.SetEditorCurve(clip, curve.binding, null); // Add new curve AnimationUtility.SetEditorCurve(clip, newBinding, curve.ToAnimationCurve()); } } private static readonly string k_PositionDisplayName = L10n.Tr("Position"); private static readonly string k_ScaleDisplayName = L10n.Tr("Scale"); private static readonly string k_RotationDisplayName = L10n.Tr("Rotation"); private static readonly string k_MaterialReferenceDisplayName = L10n.Tr("Material Reference"); // Takes raw animation curve propertyname and makes it pretty public static string GetPropertyDisplayName(string propertyName) { propertyName = propertyName.Replace("m_LocalPosition", k_PositionDisplayName); propertyName = propertyName.Replace("m_LocalScale", k_ScaleDisplayName); propertyName = propertyName.Replace("m_LocalRotation", k_RotationDisplayName); propertyName = propertyName.Replace("localEulerAnglesBaked", k_RotationDisplayName); propertyName = propertyName.Replace("localEulerAnglesRaw", k_RotationDisplayName); propertyName = propertyName.Replace("localEulerAngles", k_RotationDisplayName); propertyName = propertyName.Replace("m_Materials.Array.data", k_MaterialReferenceDisplayName); if (propertyName.StartsWith("managedReferences[")) propertyName = propertyName.Remove(0, propertyName.IndexOf('.')+1); propertyName = ObjectNames.NicifyVariableName(propertyName); propertyName = propertyName.Replace("m_", ""); return propertyName; } // Transform and Sprite: just show Position / Rotation / Scale / Sprite public static bool ShouldPrefixWithTypeName(Type animatableObjectType, string propertyName) { if (animatableObjectType == typeof(Transform) || animatableObjectType == typeof(RectTransform)) return false; if (animatableObjectType == typeof(SpriteRenderer) && propertyName == "m_Sprite") return false; return true; } public static string GetNicePropertyDisplayName(EditorCurveBinding curveBinding, SerializedObject so) { if (curveBinding.isSerializeReferenceCurve) { if (so != null) { var displayName = curveBinding.propertyName; var sp = so.FindFirstPropertyFromManagedReferencePath(displayName); if (sp != null) displayName = AnimationWindowUtility.GetPropertyDisplayName(AnimationWindowUtility.GetPropertyGroupName(sp.propertyPath)); if (displayName != "") return displayName; } else { return ObjectNames.NicifyVariableName(curveBinding.type.Name) + "." + curveBinding.propertyName; } } return AnimationWindowUtility.GetNicePropertyDisplayName(curveBinding.type, AnimationWindowUtility.GetPropertyGroupName(curveBinding.propertyName)); } public static string GetNicePropertyDisplayName(Type animatableObjectType, string propertyName) { if (ShouldPrefixWithTypeName(animatableObjectType, propertyName)) return ObjectNames.NicifyVariableName(animatableObjectType.Name) + "." + GetPropertyDisplayName(propertyName); else return GetPropertyDisplayName(propertyName); } public static string GetNicePropertyGroupDisplayName(EditorCurveBinding curveBinding, SerializedObject so) { if (curveBinding.isSerializeReferenceCurve ) { if (so != null) { var displayName = curveBinding.propertyName; var sp = so.FindFirstPropertyFromManagedReferencePath(displayName); if (sp != null) displayName = AnimationWindowUtility.GetPropertyDisplayName(AnimationWindowUtility.GetPropertyGroupName(sp.propertyPath)); if (displayName != "") return displayName; } else { return ObjectNames.NicifyVariableName(curveBinding.type.Name) + "." + curveBinding.propertyName; } } return NicifyPropertyGroupName(curveBinding.type, AnimationWindowUtility.GetPropertyGroupName(curveBinding.propertyName)); } public static string GetNicePropertyGroupDisplayName(Type animatableObjectType, string propertyGroupName) { if (ShouldPrefixWithTypeName(animatableObjectType, propertyGroupName)) return ObjectNames.NicifyVariableName(animatableObjectType.Name) + "." + NicifyPropertyGroupName(animatableObjectType, propertyGroupName); else return NicifyPropertyGroupName(animatableObjectType, propertyGroupName); } // Takes raw animation curve propertyname and returns a pretty groupname public static string NicifyPropertyGroupName(Type animatableObjectType, string propertyGroupName) { string result = GetPropertyGroupName(GetPropertyDisplayName(propertyGroupName)); // Workaround for uGUI RectTransform which only animates position.z if (animatableObjectType == typeof(RectTransform) && result.Equals("Position")) result = "Position (Z)"; return result; } // We automatically group Vector4, Vector3 and Color static public int GetComponentIndex(string name) { if (name == null || name.Length < 3 || name[name.Length - 2] != '.') return -1; char lastCharacter = name[name.Length - 1]; switch (lastCharacter) { case 'r': return 0; case 'g': return 1; case 'b': return 2; case 'a': return 3; case 'x': return 0; case 'y': return 1; case 'z': return 2; case 'w': return 3; default: return -1; } } // If Vector4, Vector3 or Color, return group name instead of full name public static string GetPropertyGroupName(string propertyName) { if (GetComponentIndex(propertyName) != -1) return propertyName.Substring(0, propertyName.Length - 2); return propertyName; } public static float GetNextKeyframeTime(AnimationWindowCurve[] curves, float currentTime, float frameRate) { AnimationKeyTime candidateKeyTime = AnimationKeyTime.Frame(int.MaxValue, frameRate); AnimationKeyTime time = AnimationKeyTime.Time(currentTime, frameRate); AnimationKeyTime nextTime = AnimationKeyTime.Frame(time.frame + 1, frameRate); bool found = false; foreach (AnimationWindowCurve curve in curves) { foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { AnimationKeyTime keyTime = AnimationKeyTime.Time(keyframe.time, frameRate); if (keyTime.frame <= candidateKeyTime.frame && keyTime.frame >= nextTime.frame) { if (keyframe.time <= candidateKeyTime.time) { candidateKeyTime = keyTime; found = true; } } } } return found ? candidateKeyTime.time : time.time; } public static float GetPreviousKeyframeTime(AnimationWindowCurve[] curves, float currentTime, float frameRate) { AnimationKeyTime candidateKeyTime = AnimationKeyTime.Time(float.MinValue, frameRate); AnimationKeyTime time = AnimationKeyTime.Time(currentTime, frameRate); AnimationKeyTime previousTime = AnimationKeyTime.Frame(time.frame - 1, frameRate); bool found = false; foreach (AnimationWindowCurve curve in curves) { foreach (AnimationWindowKeyframe keyframe in curve.keyframes) { AnimationKeyTime keyTime = AnimationKeyTime.Time(keyframe.time, frameRate); if (keyTime.frame >= candidateKeyTime.frame && keyTime.frame <= previousTime.frame) { if (keyTime.time >= candidateKeyTime.time) { candidateKeyTime = keyTime; found = true; } } } } return found ? candidateKeyTime.time : time.time; } // Add animator, controller and clip to gameobject if they are missing to make this gameobject animatable public static bool InitializeGameobjectForAnimation(GameObject animatedObject) { Component animationPlayer = GetClosestAnimationPlayerComponentInParents(animatedObject.transform); if (animationPlayer == null) { var newClip = CreateNewClip(animatedObject.name); if (newClip == null) return false; animationPlayer = EnsureActiveAnimationPlayer(animatedObject); Undo.RecordObject(animationPlayer, "Add animation clip"); bool success = AddClipToAnimationPlayerComponent(animationPlayer, newClip); if (!success) Object.DestroyImmediate(animationPlayer); return success; } return EnsureAnimationPlayerHasClip(animationPlayer); } // Ensures that the gameobject or it's parents have an animation player component. If not try to create one. public static Component EnsureActiveAnimationPlayer(GameObject animatedObject) { Component closestAnimator = GetClosestAnimationPlayerComponentInParents(animatedObject.transform); if (closestAnimator == null) { return Undo.AddComponent(animatedObject); } return closestAnimator; } // Ensures that animator has atleast one clip and controller to go with it private static bool EnsureAnimationPlayerHasClip(Component animationPlayer) { if (animationPlayer == null) return false; if (AnimationUtility.GetAnimationClips(animationPlayer.gameObject).Length > 0) return true; // At this point we know that we can create a clip var newClip = CreateNewClip(animationPlayer.gameObject.name); if (newClip == null) return false; // End animation mode before adding or changing animation component to object AnimationMode.StopAnimationMode(); // By default add it the animation to the Animator component. return AddClipToAnimationPlayerComponent(animationPlayer, newClip); } public static bool AddClipToAnimationPlayerComponent(Component animationPlayer, AnimationClip newClip) { if (animationPlayer is Animator) return AddClipToAnimatorComponent(animationPlayer as Animator, newClip); else if (animationPlayer is Animation) return AddClipToAnimationComponent(animationPlayer as Animation, newClip); return false; } public static bool AddClipToAnimatorComponent(Animator animator, AnimationClip newClip) { UnityEditor.Animations.AnimatorController controller = UnityEditor.Animations.AnimatorController.GetEffectiveAnimatorController(animator); if (controller == null) { controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerForClip(newClip, animator.gameObject); Undo.RecordObject(animator, "Set Controller"); UnityEditor.Animations.AnimatorController.SetAnimatorController(animator, controller); if (controller != null) return true; } else { // Do we already have a state with the clips name? ChildAnimatorState childAnimatorState = controller.layers[0].stateMachine.FindState(newClip.name); if (childAnimatorState.Equals(default(ChildAnimatorState))) controller.AddMotion(newClip); // Assign clip if state already present, but without a motion else if (childAnimatorState.state && childAnimatorState.state.motion == null) childAnimatorState.state.motion = newClip; // State present, but with some other clip else if (childAnimatorState.state && childAnimatorState.state.motion != newClip) controller.AddMotion(newClip); return true; } return false; } public static bool AddClipToAnimationComponent(Animation animation, AnimationClip newClip) { SetClipAsLegacy(newClip); animation.AddClip(newClip, newClip.name); return true; } internal static string s_LastPathUsedForNewClip; internal static AnimationClip CreateNewClip(string gameObjectName) { // Go forward with presenting user a save clip dialog string message = string.Format(L10n.Tr("Create a new animation for the game object '{0}':"), gameObjectName); string newClipDirectory = ProjectWindowUtil.GetActiveFolderPath(); if (s_LastPathUsedForNewClip != null) { string directoryPath = Path.GetDirectoryName(s_LastPathUsedForNewClip); if (directoryPath != null && Directory.Exists(directoryPath)) { newClipDirectory = directoryPath; } } string newClipPath = EditorUtility.SaveFilePanelInProject(L10n.Tr("Create New Animation"), "New Animation", "anim", message, newClipDirectory); // If user canceled or save path is invalid, we can't create a clip if (newClipPath == "") return null; return CreateNewClipAtPath(newClipPath); } // Create a new animation clip asset for gameObject at a certain asset path. // The clipPath parameter must be a full asset path ending with '.anim'. e.g. "Assets/Animations/New Clip.anim" // This function will overwrite existing .anim files. internal static AnimationClip CreateNewClipAtPath(string clipPath) { s_LastPathUsedForNewClip = clipPath; var newClip = new AnimationClip(); var info = AnimationUtility.GetAnimationClipSettings(newClip); info.loopTime = true; AnimationUtility.SetAnimationClipSettingsNoDirty(newClip, info); AnimationClip asset = AssetDatabase.LoadMainAssetAtPath(clipPath) as AnimationClip; if (asset) { newClip.name = asset.name; EditorUtility.CopySerialized(newClip, asset); AssetDatabase.SaveAssets(); Object.DestroyImmediate(newClip); return asset; } AssetDatabase.CreateAsset(newClip, clipPath); return newClip; } private static void SetClipAsLegacy(AnimationClip clip) { SerializedObject s = new SerializedObject(clip); s.FindProperty("m_Legacy").boolValue = true; s.ApplyModifiedProperties(); } internal static AnimationClip AllocateAndSetupClip(bool useAnimator) { // At this point we know that we can create a clip AnimationClip newClip = new AnimationClip(); if (useAnimator) { AnimationClipSettings info = AnimationUtility.GetAnimationClipSettings(newClip); info.loopTime = true; AnimationUtility.SetAnimationClipSettingsNoDirty(newClip, info); } return newClip; } public static int GetPropertyNodeID(int setId, string path, System.Type type, string propertyName) { return (setId + path + type.Name + propertyName).GetHashCode(); } // What is the first animation player component (Animator or Animation) when recursing parent tree toward root public static Component GetClosestAnimationPlayerComponentInParents(Transform tr) { while (true) { if (tr.TryGetComponent(out Animator animator)) { return animator; } if (tr.TryGetComponent(out Animation animation)) { return animation; } if (tr.TryGetComponent(out IAnimationClipSource clipPlayer)) { if (clipPlayer is Component clipPlayerComponent) { return clipPlayerComponent; } } if (tr == tr.root) break; tr = tr.parent; } return null; } // What is the first animator component when recursing parent tree toward root public static Animator GetClosestAnimatorInParents(Transform tr) { while (true) { if (tr.TryGetComponent(out Animator animator)) { return animator; } if (tr == tr.root) break; tr = tr.parent; } return null; } // What is the first animation component when recursing parent tree toward root public static Animation GetClosestAnimationInParents(Transform tr) { while (true) { if (tr.TryGetComponent(out Animation animation)) { return animation; } if (tr == tr.root) break; tr = tr.parent; } return null; } public static void SyncTimeArea(TimeArea from, TimeArea to) { to.SetDrawRectHack(from.drawRect); to.m_Scale = new Vector2(from.m_Scale.x, to.m_Scale.y); to.m_Translation = new Vector2(from.m_Translation.x, to.m_Translation.y); to.EnforceScaleAndRange(); } public static void DrawInRangeOverlay(Rect rect, Color color, float startOfClipPixel, float endOfClipPixel) { // Rect shaded shape drawn inside range if (endOfClipPixel >= rect.xMin) { if (color.a > 0f) { Rect inRect = Rect.MinMaxRect(Mathf.Max(startOfClipPixel, rect.xMin), rect.yMin, Mathf.Min(endOfClipPixel, rect.xMax), rect.yMax); DrawRect(inRect, color); } } } public static void DrawOutOfRangeOverlay(Rect rect, Color color, float startOfClipPixel, float endOfClipPixel) { Color lineColor = Color.white.RGBMultiplied(0.4f); // Rect shaded shape drawn before range if (startOfClipPixel > rect.xMin) { Rect startRect = Rect.MinMaxRect(rect.xMin, rect.yMin, Mathf.Min(startOfClipPixel, rect.xMax), rect.yMax); DrawRect(startRect, color); TimeArea.DrawVerticalLine(startRect.xMax, startRect.yMin, startRect.yMax, lineColor); } // Rect shaded shape drawn after range Rect endRect = Rect.MinMaxRect(Mathf.Max(endOfClipPixel, rect.xMin), rect.yMin, rect.xMax, rect.yMax); DrawRect(endRect, color); TimeArea.DrawVerticalLine(endRect.xMin, endRect.yMin, endRect.yMax, lineColor); } public static void DrawSelectionOverlay(Rect rect, Color color, float startPixel, float endPixel) { startPixel = Mathf.Max(startPixel, rect.xMin); endPixel = Mathf.Max(endPixel, rect.xMin); Rect labelRect = Rect.MinMaxRect(startPixel, rect.yMin, endPixel, rect.yMax); DrawRect(labelRect, color); } public static void DrawRect(Rect rect, Color color) { if (Event.current.type != EventType.Repaint) return; HandleUtility.ApplyWireMaterial(); GL.PushMatrix(); GL.MultMatrix(Handles.matrix); GL.Begin(GL.QUADS); GL.Color(color); GL.Vertex(rect.min); GL.Vertex(new Vector2(rect.xMax, rect.yMin)); GL.Vertex(rect.max); GL.Vertex(new Vector2(rect.xMin, rect.yMax)); GL.End(); GL.PopMatrix(); } private static CurveRenderer CreateRendererForCurve(AnimationWindowCurve curve) { CurveRenderer renderer; switch (System.Type.GetTypeCode(curve.valueType)) { case TypeCode.Int32: renderer = new IntCurveRenderer(curve.ToAnimationCurve()); break; case TypeCode.Boolean: renderer = new BoolCurveRenderer(curve.ToAnimationCurve()); break; default: renderer = new NormalCurveRenderer(curve.ToAnimationCurve()); break; } return renderer; } private static CurveWrapper.PreProcessKeyMovement CreateKeyPreprocessorForCurve(AnimationWindowCurve curve) { CurveWrapper.PreProcessKeyMovement method; switch (System.Type.GetTypeCode(curve.valueType)) { case TypeCode.Int32: method = (ref Keyframe key) => { key.value = Mathf.Floor(key.value + 0.5f); }; break; case TypeCode.Boolean: method = (ref Keyframe key) => { key.value = key.value > 0.5f ? 1.0f : 0.0f; }; break; default: method = null; break; } return method; } public static CurveWrapper GetCurveWrapper(AnimationWindowCurve curve, AnimationClip clip) { //Discrete and PPtr curves are not allowed to create curve wrappers. if (curve.isDiscreteCurve || curve.isPPtrCurve) return null; CurveWrapper curveWrapper = new CurveWrapper(); curveWrapper.renderer = CreateRendererForCurve(curve); curveWrapper.preProcessKeyMovementDelegate = CreateKeyPreprocessorForCurve(curve); curveWrapper.renderer.SetWrap(WrapMode.Clamp, clip.isLooping ? WrapMode.Loop : WrapMode.Clamp); curveWrapper.renderer.SetCustomRange(clip.startTime, clip.stopTime); curveWrapper.binding = curve.binding; curveWrapper.id = curve.GetHashCode(); curveWrapper.color = CurveUtility.GetPropertyColor(curve.propertyName); curveWrapper.hidden = false; curveWrapper.selectionBindingInterface = curve.selectionBinding; return curveWrapper; } // Convert keyframe from curve editor representation (CurveSelection) to animation window representation (AnimationWindowKeyframe) public static AnimationWindowKeyframe CurveSelectionToAnimationWindowKeyframe(CurveSelection curveSelection, List allCurves) { foreach (AnimationWindowCurve curve in allCurves) { int curveID = curve.GetHashCode(); if (curveID == curveSelection.curveID) if (curve.keyframes.Count > curveSelection.key) return curve.keyframes[curveSelection.key]; } return null; } // Convert keyframe from animation window representation (AnimationWindowKeyframe) to curve editor representation (CurveSelection) to animation window representation (AnimationWindowKeyframe) public static CurveSelection AnimationWindowKeyframeToCurveSelection(AnimationWindowKeyframe keyframe, CurveEditor curveEditor) { int curveID = keyframe.curve.GetHashCode(); foreach (CurveWrapper curveWrapper in curveEditor.animationCurves) if (curveWrapper.id == curveID && keyframe.GetIndex() >= 0) return new CurveSelection(curveWrapper.id, keyframe.GetIndex()); return null; } public static AnimationWindowCurve BestMatchForPaste(EditorCurveBinding binding, List clipboardCurves, List targetCurves) { // Exact match foreach (AnimationWindowCurve targetCurve in targetCurves) if (targetCurve.binding == binding) return targetCurve; // Matching propertyname foreach (AnimationWindowCurve targetCurve in targetCurves) { if (targetCurve.binding.propertyName == binding.propertyName) { // Only match if key in binding is not already being pasted itself in clipboardCurves. if (!clipboardCurves.Exists(clipboardCurve => clipboardCurve.binding == targetCurve.binding)) { return targetCurve; } } } // No good match found. return null; } // Make a rect from MinMax values and make sure they're positive sizes internal static Rect FromToRect(Vector2 start, Vector2 end) { Rect r = new Rect(start.x, start.y, end.x - start.x, end.y - start.y); if (r.width < 0) { r.x += r.width; r.width = -r.width; } if (r.height < 0) { r.y += r.height; r.height = -r.height; } return r; } public static bool IsTransformType(Type type) { return type == typeof(Transform) || type == typeof(RectTransform); } public static bool IsActualTransformCurve(EditorCurveBinding curveBinding) { return curveBinding.type == typeof(Transform) || curveBinding.type == typeof(RectTransform) && (curveBinding.propertyName.StartsWith("m_LocalScale") || curveBinding.propertyName.StartsWith("m_LocalRotation") || curveBinding.propertyName.StartsWith("localEuler")); } public static bool ForceGrouping(EditorCurveBinding binding) { if (binding.type == typeof(Transform)) return true; if (binding.type == typeof(RectTransform)) { string group = GetPropertyGroupName(binding.propertyName); return group == "m_LocalPosition" || group == "m_LocalScale" || group == "m_LocalRotation" || group == "localEulerAnglesBaked" || group == "localEulerAngles" || group == "localEulerAnglesRaw"; } if (typeof(Renderer).IsAssignableFrom(binding.type)) { string group = GetPropertyGroupName(binding.propertyName); return group == "material._Color" || group == "material._BaseColor"; } return false; } public static void ControllerChanged() { foreach (AnimationWindow animationWindow in AnimationWindow.GetAllAnimationWindows()) animationWindow.OnControllerChange(); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/ControlPointRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using System.Linq; namespace UnityEditor { // Control point renderer internal class ControlPointRenderer { private class RenderChunk { public Mesh mesh; public List vertices; public List colors; public List uvs; public List indices; public bool isDirty = true; } private int m_RenderChunkIndex = -1; private List m_RenderChunks = new List(); private Texture2D m_Icon; // Can hold a maximum of 16250 control points. const int kMaxVertices = 65000; const string kControlPointRendererMeshName = "ControlPointRendererMesh"; private static Material s_Material; public static Material material { get { if (!s_Material) { Shader shader = (Shader)EditorGUIUtility.LoadRequired("Editors/AnimationWindow/ControlPoint.shader"); s_Material = new Material(shader); s_Material.hideFlags = HideFlags.HideAndDontSave; } return s_Material; } } public ControlPointRenderer(Texture2D icon) { m_Icon = icon; } public void FlushCache() { for (int i = 0; i < m_RenderChunks.Count; ++i) { Object.DestroyImmediate(m_RenderChunks[i].mesh); } m_RenderChunks.Clear(); } public void Clear() { for (int i = 0; i < m_RenderChunks.Count; ++i) { RenderChunk renderChunk = m_RenderChunks[i]; renderChunk.mesh.Clear(); renderChunk.vertices.Clear(); renderChunk.colors.Clear(); renderChunk.uvs.Clear(); renderChunk.indices.Clear(); renderChunk.isDirty = true; } m_RenderChunkIndex = 0; } public void Render() { Material mat = material; mat.SetTexture("_MainTex", m_Icon); mat.SetPass(0); for (int i = 0; i < m_RenderChunks.Count; ++i) { RenderChunk renderChunk = m_RenderChunks[i]; if (renderChunk.isDirty) { renderChunk.mesh.SetVertices(renderChunk.vertices); renderChunk.mesh.SetColors(renderChunk.colors); renderChunk.mesh.SetUVs(0, renderChunk.uvs); renderChunk.mesh.SetIndices(renderChunk.indices, MeshTopology.Triangles, 0); renderChunk.isDirty = false; } // Previous camera may still be active when calling DrawMeshNow. Camera.SetupCurrent(null); Graphics.DrawMeshNow(renderChunk.mesh, Handles.matrix); } } public void AddPoint(Rect rect, Color color) { RenderChunk renderChunk = GetRenderChunk(); int baseIndex = renderChunk.vertices.Count; renderChunk.vertices.Add(new Vector3(rect.xMin, rect.yMin, 0.0f)); renderChunk.vertices.Add(new Vector3(rect.xMax, rect.yMin, 0.0f)); renderChunk.vertices.Add(new Vector3(rect.xMax, rect.yMax, 0.0f)); renderChunk.vertices.Add(new Vector3(rect.xMin, rect.yMax, 0.0f)); renderChunk.colors.Add(color); renderChunk.colors.Add(color); renderChunk.colors.Add(color); renderChunk.colors.Add(color); renderChunk.uvs.Add(new Vector2(0.0f, 0.0f)); renderChunk.uvs.Add(new Vector2(1.0f, 0.0f)); renderChunk.uvs.Add(new Vector2(1.0f, 1.0f)); renderChunk.uvs.Add(new Vector2(0.0f, 1.0f)); renderChunk.indices.Add(baseIndex); renderChunk.indices.Add(baseIndex + 1); renderChunk.indices.Add(baseIndex + 2); renderChunk.indices.Add(baseIndex); renderChunk.indices.Add(baseIndex + 2); renderChunk.indices.Add(baseIndex + 3); renderChunk.isDirty = true; } private RenderChunk GetRenderChunk() { RenderChunk renderChunk = null; if (m_RenderChunks.Count > 0) { while (m_RenderChunkIndex < m_RenderChunks.Count) { renderChunk = m_RenderChunks[m_RenderChunkIndex]; // Dynamically create new render chunks when needed. if ((renderChunk.vertices.Count + 4) > kMaxVertices) { m_RenderChunkIndex++; renderChunk = null; continue; } break; } if (renderChunk == null) { renderChunk = CreateRenderChunk(); } } else { renderChunk = CreateRenderChunk(); } return renderChunk; } private RenderChunk CreateRenderChunk() { RenderChunk renderChunk = new RenderChunk(); renderChunk.mesh = new Mesh(); renderChunk.mesh.name = kControlPointRendererMeshName; renderChunk.mesh.hideFlags |= HideFlags.DontSave; renderChunk.vertices = new List(); renderChunk.colors = new List(); renderChunk.uvs = new List(); renderChunk.indices = new List(); m_RenderChunks.Add(renderChunk); m_RenderChunkIndex = m_RenderChunks.Count - 1; return renderChunk; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveBindingUtility.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEngine; namespace UnityEditorInternal { static internal class CurveBindingUtility { // Current value of the property that rootGO + curveBinding is pointing to public static object GetCurrentValue(AnimationWindowState state, EditorCurveBinding binding) { if (binding.isPPtrCurve) { return state.controlInterface.GetObjectReferenceValue(binding); } else if (binding.isDiscreteCurve) { return state.controlInterface.GetIntValue(binding); } return state.controlInterface.GetFloatValue(binding); } // Retrieve current value. If bindings are available and value is animated, use bindings to get value. // Otherwise, evaluate AnimationWindowCurve at current time. public static object GetCurrentValue(AnimationWindowState state, AnimationWindowCurve curve) { // UUM-66112 - state.linkedWithSequencer - Padding for issue in Timeline where muscle // values are not updated in the editor when previewing in the Animation Window. // Fallback to curve values. if (state.previewing && curve.rootGameObject != null && !state.linkedWithSequencer) { return GetCurrentValue(state, curve.binding); } else { return curve.Evaluate(state.currentTime); } } // Retrieve Current Value. Use specified bindings to do so. public static object GetCurrentValue(GameObject rootGameObject, EditorCurveBinding curveBinding) { if (rootGameObject != null) { return AnimationWindowUtility.GetCurrentValue(rootGameObject, curveBinding); } else { if (curveBinding.isPPtrCurve) { // Cannot extract type of PPtrCurve. return null; } else if (curveBinding.isDiscreteCurve) { return 0; } else { // Cannot extract type of AnimationCurve. Default to float. return 0.0f; } } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Object = UnityEngine.Object; using TangentMode = UnityEditor.AnimationUtility.TangentMode; using RectangleToolFlags = UnityEditor.CurveEditorSettings.RectangleToolFlags; namespace UnityEditor { // External selection interface internal interface ISelectionBinding { GameObject rootGameObject { get; } AnimationClip animationClip { get; } bool clipIsEditable { get; } bool animationIsEditable { get; } int id { get; } } // External state interface internal interface ICurveEditorState { TimeArea.TimeFormat timeFormat { get; } Vector2 timeRange { get; } bool rippleTime { get; } } internal class CurveWrapper { public delegate Vector2 GetAxisScalarsCallback(); public delegate void SetAxisScalarsCallback(Vector2 newAxisScalars); public delegate void PreProcessKeyMovement(ref Keyframe key); public CurveWrapper() { id = 0; groupId = -1; regionId = -1; hidden = false; readOnly = false; listIndex = -1; getAxisUiScalarsCallback = null; setAxisUiScalarsCallback = null; } internal enum SelectionMode { None = 0, Selected = 1, SemiSelected = 2 } // Curve management private CurveRenderer m_Renderer; private ISelectionBinding m_SelectionBinding; public CurveRenderer renderer { get { return m_Renderer; } set { m_Renderer = value; } } public AnimationCurve curve { get { return renderer.GetCurve(); } } public GameObject rootGameObjet { get { return m_SelectionBinding != null ? m_SelectionBinding.rootGameObject : null; } } public AnimationClip animationClip { get { return m_SelectionBinding != null ? m_SelectionBinding.animationClip : null; } } public bool clipIsEditable { get { return m_SelectionBinding != null ? m_SelectionBinding.clipIsEditable : true; } } public bool animationIsEditable { get { return m_SelectionBinding != null ? m_SelectionBinding.animationIsEditable : true; } } public int selectionID { get { return m_SelectionBinding != null ? m_SelectionBinding.id : 0; } } public ISelectionBinding selectionBindingInterface { get { return m_SelectionBinding; } set { m_SelectionBinding = value; } } public Bounds bounds { get { return renderer.GetBounds(); } } // Input - should not be changed by curve editor public int id; public EditorCurveBinding binding; public int groupId; public int regionId; // Regions are defined by two curves added after each other with the same regionId. public Color color; public Color wrapColorMultiplier = Color.white; public bool readOnly; public bool hidden; public GetAxisScalarsCallback getAxisUiScalarsCallback; // Delegate used to fetch values that are multiplied on x and y axis ui values public SetAxisScalarsCallback setAxisUiScalarsCallback; // Delegate used to set values back that has been changed by this curve editor public PreProcessKeyMovement preProcessKeyMovementDelegate; // Delegate used limit key manipulation to fit curve constraints // Should be updated by curve editor as appropriate public SelectionMode selected; public int listIndex; // Index into m_AnimationCurves list private bool m_Changed; public bool changed { get { return m_Changed; } set { m_Changed = value; if (value && renderer != null) renderer.FlushCache(); } } public int AddKey(Keyframe key) { PreProcessKey(ref key); return curve.AddKey(key); } public void PreProcessKey(ref Keyframe key) { if (preProcessKeyMovementDelegate != null) preProcessKeyMovementDelegate(ref key); } public int MoveKey(int index, ref Keyframe key) { PreProcessKey(ref key); return curve.MoveKey(index, key); } internal Bounds ComputeBoundsBetweenTime(float start, float end) => renderer.GetBounds(start, end); // An additional vertical min / max range clamp when editing multiple curves with different ranges public float vRangeMin = -Mathf.Infinity; public float vRangeMax = Mathf.Infinity; public bool useScalingInKeyEditor = false; public string xAxisLabel = null; public string yAxisLabel = null; } // Control point collection renderer class CurveControlPointRenderer { // Control point mesh renderers. private ControlPointRenderer m_UnselectedPointRenderer; private ControlPointRenderer m_SelectedPointRenderer; private ControlPointRenderer m_SelectedPointOverlayRenderer; private ControlPointRenderer m_SemiSelectedPointOverlayRenderer; private ControlPointRenderer m_WeightedPointRenderer; public CurveControlPointRenderer() { m_UnselectedPointRenderer = new ControlPointRenderer(CurveEditor.Styles.pointIcon); m_SelectedPointRenderer = new ControlPointRenderer(CurveEditor.Styles.pointIconSelected); m_SelectedPointOverlayRenderer = new ControlPointRenderer(CurveEditor.Styles.pointIconSelectedOverlay); m_SemiSelectedPointOverlayRenderer = new ControlPointRenderer(CurveEditor.Styles.pointIconSemiSelectedOverlay); m_WeightedPointRenderer = new ControlPointRenderer(CurveEditor.Styles.pointIconWeighted); } public void FlushCache() { m_UnselectedPointRenderer.FlushCache(); m_SelectedPointRenderer.FlushCache(); m_SelectedPointOverlayRenderer.FlushCache(); m_SemiSelectedPointOverlayRenderer.FlushCache(); m_WeightedPointRenderer.FlushCache(); } public void Clear() { m_UnselectedPointRenderer.Clear(); m_SelectedPointRenderer.Clear(); m_SelectedPointOverlayRenderer.Clear(); m_SemiSelectedPointOverlayRenderer.Clear(); m_WeightedPointRenderer.Clear(); } public void Render() { m_UnselectedPointRenderer.Render(); m_SelectedPointRenderer.Render(); m_SelectedPointOverlayRenderer.Render(); m_SemiSelectedPointOverlayRenderer.Render(); m_WeightedPointRenderer.Render(); } public void AddPoint(Rect rect, Color color) { m_UnselectedPointRenderer.AddPoint(rect, color); } public void AddSelectedPoint(Rect rect, Color color) { m_SelectedPointRenderer.AddPoint(rect, color); m_SelectedPointOverlayRenderer.AddPoint(rect, Color.white); } public void AddSemiSelectedPoint(Rect rect, Color color) { m_SelectedPointRenderer.AddPoint(rect, color); m_SemiSelectedPointOverlayRenderer.AddPoint(rect, Color.white); } public void AddWeightedPoint(Rect rect, Color color) { m_WeightedPointRenderer.AddPoint(rect, color); } } [System.Serializable] internal class CurveEditor : TimeArea, CurveUpdater { CurveWrapper[] m_AnimationCurves; static int s_SelectKeyHash = "SelectKeys".GetHashCode(); private static readonly string k_DeleteKeys = L10n.Tr("Delete Keys"); private static readonly string k_DeleteKey = L10n.Tr("Delete Key"); private static readonly string k_EditKeys = L10n.Tr("Edit Keys..."); private static readonly string k_EditKey = L10n.Tr("Edit Key..."); private static readonly string k_EditCurve = L10n.Tr("Edit Curve"); private static readonly string k_AddKey = L10n.Tr("Add Key"); public delegate void CallbackFunction(); public CallbackFunction curvesUpdated; public CurveWrapper[] animationCurves { get { if (m_AnimationCurves == null) m_AnimationCurves = new CurveWrapper[0]; return m_AnimationCurves; } set { FlushCurvesCache(); m_AnimationCurves = value; curveIDToIndexMap.Clear(); m_EnableCurveGroups = false; for (int i = 0; i < m_AnimationCurves.Length; ++i) { m_AnimationCurves[i].listIndex = i; if (!curveIDToIndexMap.ContainsKey(m_AnimationCurves[i].id)) { curveIDToIndexMap.Add(m_AnimationCurves[i].id, i); } else { var binding = m_AnimationCurves[i].binding; Debug.LogWarning("Mismatching curve: '" + (string.IsNullOrEmpty(binding.path) ? "" : binding.path + " : ") + binding.propertyName + "'"); } m_EnableCurveGroups = m_EnableCurveGroups || (m_AnimationCurves[i].groupId != -1); } SyncDrawOrder(); SyncSelection(); ValidateCurveList(); } } public bool GetTopMostCurveID(out int curveID) { if (m_DrawOrder.Count > 0) { curveID = m_DrawOrder[m_DrawOrder.Count - 1]; return true; } curveID = -1; return false; } void FlushCurvesCache() { if (!settings.flushCurveCache) return; if (m_AnimationCurves == null) return; for (int i = 0; i < m_AnimationCurves.Length; ++i) { CurveWrapper curveWrapper = m_AnimationCurves[i]; if (curveWrapper.renderer != null) curveWrapper.renderer.FlushCache(); } } private List m_DrawOrder = new List(); // contains curveIds (last element topmost) void SyncDrawOrder() { // Init if (m_DrawOrder.Count == 0) { m_DrawOrder = m_AnimationCurves.Select(cw => cw.id).ToList(); return; } List newDrawOrder = new List(m_AnimationCurves.Length); // First add known curveIds (same order as before) for (int i = 0; i < m_DrawOrder.Count; ++i) { int curveID = m_DrawOrder[i]; for (int j = 0; j < m_AnimationCurves.Length; ++j) { if (m_AnimationCurves[j].id == curveID) { newDrawOrder.Add(curveID); break; } } } m_DrawOrder = newDrawOrder; // Found them all if (m_DrawOrder.Count == m_AnimationCurves.Length) return; // Add nonexisting curveIds (new curves are top most) for (int i = 0; i < m_AnimationCurves.Length; ++i) { int curveID = m_AnimationCurves[i].id; bool found = false; for (int j = 0; j < m_DrawOrder.Count; ++j) { if (m_DrawOrder[j] == curveID) { found = true; break; } } if (!found) m_DrawOrder.Add(curveID); } // Fallback if invalid setup with multiple curves with same curveID (see case 482048) if (m_DrawOrder.Count != m_AnimationCurves.Length) { // Ordering can fail if we have a hierarchy like: // // Piston // Cylinder // InnerCyl // Cylinder // InnerCyl // Since we cannot generate unique curve ids for identical paths like Cylinder and InnerCyl. m_DrawOrder = m_AnimationCurves.Select(cw => cw.id).ToList(); } } public ICurveEditorState state; public TimeArea.TimeFormat timeFormat { get { if (state != null) return state.timeFormat; return TimeArea.TimeFormat.None; } } public bool rippleTime { get { if (state != null) return state.rippleTime; return false; } } internal Bounds m_DefaultBounds = new Bounds(new Vector3(0.5f, 0.5f, 0), new Vector3(1, 1, 0)); private CurveEditorSettings m_Settings = new CurveEditorSettings(); public CurveEditorSettings settings { get { return m_Settings; } set { if (value != null) { m_Settings = value; ApplySettings(); } } } protected void ApplySettings() { hRangeLocked = settings.hRangeLocked; vRangeLocked = settings.vRangeLocked; hRangeMin = settings.hRangeMin; hRangeMax = settings.hRangeMax; vRangeMin = settings.vRangeMin; vRangeMax = settings.vRangeMax; scaleWithWindow = settings.scaleWithWindow; hSlider = settings.hSlider; vSlider = settings.vSlider; RecalculateBounds(); } // Other style settings private Color m_TangentColor = new Color(1, 1, 1, 0.5f); private Color m_WeightedTangentColor = new Color(1, 1, 1, 1f); /// 1/time to snap all keyframes to while dragging. Set to 0 for no snap (default) public float invSnap = 0; private CurveMenuManager m_MenuManager; static int s_TangentControlIDHash = "s_TangentControlIDHash".GetHashCode(); [SerializeField] CurveEditorSelection m_Selection; internal CurveEditorSelection selection { get { if (m_Selection == null) { m_Selection = ScriptableObject.CreateInstance(); m_Selection.hideFlags = HideFlags.HideAndDontSave; } return m_Selection; } } internal List selectedCurves { get { return selection.selectedCurves; } set { selection.selectedCurves = value; InvalidateSelectionBounds(); } } List preCurveDragSelection = null; public bool hasSelection { get { return selectedCurves.Count != 0; } } bool m_InRangeSelection = false; internal void BeginRangeSelection() { m_InRangeSelection = true; } internal void EndRangeSelection() { m_InRangeSelection = false; selectedCurves.Sort(); } internal void AddSelection(CurveSelection curveSelection) { selectedCurves.Add(curveSelection); if (!m_InRangeSelection) selectedCurves.Sort(); InvalidateSelectionBounds(); } internal void RemoveSelection(CurveSelection curveSelection) { selectedCurves.Remove(curveSelection); InvalidateSelectionBounds(); } internal void ClearSelection() { selectedCurves.Clear(); InvalidateSelectionBounds(); } internal CurveWrapper GetCurveWrapperFromID(int curveID) { if (m_AnimationCurves == null) return null; int index; if (curveIDToIndexMap.TryGetValue(curveID, out index)) return m_AnimationCurves[index]; return null; } internal CurveWrapper GetCurveWrapperFromSelection(CurveSelection curveSelection) { return GetCurveWrapperFromID(curveSelection.curveID); } internal AnimationCurve GetCurveFromSelection(CurveSelection curveSelection) { CurveWrapper curveWrapper = GetCurveWrapperFromSelection(curveSelection); return (curveWrapper != null) ? curveWrapper.curve : null; } internal Keyframe GetKeyframeFromSelection(CurveSelection curveSelection) { AnimationCurve curve = GetCurveFromSelection(curveSelection); if (curve != null) { if (curveSelection.key >= 0 && curveSelection.key < curve.length) { return curve[curveSelection.key]; } } return new Keyframe(); } // Array of tangent points that have been revealed CurveSelection m_SelectedTangentPoint; // Selection tracking: // What the selection was at the start of a drag List s_SelectionBackup; // Time range selection, is it active and what was the mousedown time (start) and the current end time. float s_TimeRangeSelectionStart, s_TimeRangeSelectionEnd; bool s_TimeRangeSelectionActive = false; private bool m_BoundsAreDirty = true; private bool m_SelectionBoundsAreDirty = true; private bool m_SelectionWithCurvesBoundsAreDirty = true; private bool m_EnableCurveGroups = false; Bounds m_SelectionBounds = new Bounds(Vector3.zero, Vector3.zero); public Bounds selectionBounds { get { RecalculateSelectionBounds(); return m_SelectionBounds; } } Bounds m_SelectionWithCurvesBounds = new Bounds(Vector3.zero, Vector3.zero); public Bounds selectionWithCurvesBounds { get { RecalculateSelectionWithCurvesBounds(); return m_SelectionWithCurvesBounds; } } Bounds m_CurveBounds = new Bounds(Vector3.zero, Vector3.zero); public Bounds curveBounds { get { RecalculateBounds(); return m_CurveBounds; } } Bounds m_DrawingBounds = new Bounds(Vector3.zero, Vector3.zero); public override Bounds drawingBounds { get { RecalculateBounds(); return m_DrawingBounds; } } // Helpers for temporarily saving a bunch of keys. class SavedCurve { public class SavedKeyFrame { public Keyframe key; public CurveWrapper.SelectionMode selected; public SavedKeyFrame(Keyframe key, CurveWrapper.SelectionMode selected) { this.key = key; this.selected = selected; } public SavedKeyFrame Clone() { SavedKeyFrame duplicate = new SavedKeyFrame(key, selected); return duplicate; } } public class SavedKeyFrameComparer : IComparer { public static SavedKeyFrameComparer Instance = new SavedKeyFrameComparer(); public int Compare(float time1, float time2) { float cmp = time1 - time2; return cmp < -kCurveTimeEpsilon ? -1 : (cmp >= kCurveTimeEpsilon ? 1 : 0); } } public int curveId; public List keys; public delegate SavedKeyFrame KeyFrameOperation(SavedKeyFrame keyframe, SavedCurve curve); } List m_CurveBackups; CurveWrapper m_DraggingKey = null; Vector2 m_DraggedCoord; Vector2 m_MoveCoord; private enum AxisLock { None, X, Y } private AxisLock m_AxisLock; CurveControlPointRenderer m_PointRenderer; CurveEditorRectangleTool m_RectangleTool; // The square of the maximum pick distance in pixels. // The mouse will select a key if it's within this distance from the key point. const float kMaxPickDistSqr = 10 * 10; const float kExactPickDistSqr = 4 * 4; const float kCurveTimeEpsilon = 0.00001f; public CurveEditor(Rect rect, CurveWrapper[] curves, bool minimalGUI) : base(minimalGUI) { this.rect = rect; animationCurves = curves; float[] modulos = new float[] { 0.0000001f, 0.0000005f, 0.000001f, 0.000005f, 0.00001f, 0.00005f, 0.0001f, 0.0005f, 0.001f, 0.005f, 0.01f, 0.05f, 0.1f, 0.5f, 1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 5000000, 10000000 }; hTicks = new TickHandler(); hTicks.SetTickModulos(modulos); vTicks = new TickHandler(); vTicks.SetTickModulos(modulos); margin = 40.0f; OnEnable(); } public void OnEnable() { // Only add callback once. Undo.undoRedoEvent -= UndoRedoPerformed; Undo.undoRedoEvent += UndoRedoPerformed; } public void OnDisable() { Undo.undoRedoEvent -= UndoRedoPerformed; if (m_PointRenderer != null) m_PointRenderer.FlushCache(); FlushCurvesCache(); } public void OnDestroy() { if (m_Selection != null) ScriptableObject.DestroyImmediate(m_Selection); } void UndoRedoPerformed(in UndoRedoInfo info) { if (settings.undoRedoSelection) InvalidateSelectionBounds(); else SelectNone(); } private void ValidateCurveList() { // Validate that regions are valid (they should consist of two curves after each other with same regionId) for (int i = 0; i < m_AnimationCurves.Length; ++i) { CurveWrapper cw = m_AnimationCurves[i]; int regId1 = cw.regionId; if (regId1 >= 0) { if (i == m_AnimationCurves.Length - 1) { Debug.LogError("Region has only one curve last! Regions should be added as two curves after each other with same regionId"); return; } CurveWrapper cw2 = m_AnimationCurves[++i]; int regId2 = cw2.regionId; if (regId1 != regId2) { Debug.LogError("Regions should be added as two curves after each other with same regionId: " + regId1 + " != " + regId2); return; } } } if (m_DrawOrder.Count != m_AnimationCurves.Length) { Debug.LogError("DrawOrder and AnimationCurves mismatch: DrawOrder " + m_DrawOrder.Count + ", AnimationCurves: " + m_AnimationCurves.Length); return; } // Validate draw order regions int numCurves = m_DrawOrder.Count; for (int i = 0; i < numCurves; ++i) { int curveID = m_DrawOrder[i]; // If curve is part of a region then find other curve int regionId = GetCurveWrapperFromID(curveID).regionId; if (regionId >= 0) { if (i == numCurves - 1) { Debug.LogError("Region has only one curve last! Regions should be added as two curves after each other with same regionId"); return; } // Ensure next curve has a matching regionId int curveId2 = m_DrawOrder[++i]; int regionId2 = GetCurveWrapperFromID(curveId2).regionId; if (regionId != regionId2) { Debug.LogError("DrawOrder: Regions not added correctly after each other. RegionIds: " + regionId + " , " + regionId2); return; } } } // Debug.Log all curves and their state (outcomment if needed) /* string info = "Count: " + m_AnimationCurves.Length + " (Click me for more info)\n"; foreach (CurveWrapper cw in m_AnimationCurves) info += ("Curve: id " + cw.id + ", regionId " + cw.regionId + ", hidden " + cw.hidden + "\n"); Debug.Log(info); */ } Dictionary m_CurveIDToIndexMap; private Dictionary curveIDToIndexMap { get { if (m_CurveIDToIndexMap == null) m_CurveIDToIndexMap = new Dictionary(); return m_CurveIDToIndexMap; } } private void SyncSelection() { Init(); List newSelection = new List(selectedCurves.Count); foreach (CurveSelection cs in selectedCurves) { CurveWrapper cw = GetCurveWrapperFromSelection(cs); if (cw != null && (!cw.hidden || cw.groupId != -1)) { cw.selected = CurveWrapper.SelectionMode.Selected; newSelection.Add(cs); } } if (newSelection.Count != selectedCurves.Count) { selectedCurves = newSelection; } InvalidateBounds(); } public void InvalidateBounds() { m_BoundsAreDirty = true; } private void RecalculateBounds() { if (InLiveEdit()) return; if (!m_BoundsAreDirty) return; const float kMinRange = 0.1F; if (state != null) { m_CurveBounds.SetMinMax(new Vector3(state.timeRange.x, 0f, 0f), new Vector3(state.timeRange.y, 1f, 0f)); m_DrawingBounds = m_CurveBounds; } else { m_DrawingBounds = m_CurveBounds = m_DefaultBounds; } if (animationCurves != null) { bool assigned = false; for (int i = 0; i < animationCurves.Length; ++i) { CurveWrapper wrapper = animationCurves[i]; if (wrapper.hidden) continue; if (wrapper.curve.length == 0) continue; if (!assigned) { m_CurveBounds = wrapper.bounds; assigned = true; } else { m_CurveBounds.Encapsulate(wrapper.bounds); } } } // Calculate bounds based on curve bounds if bound is not set by hRangeMin/hRangeMax vRangeMin/vRangeMax. float minx = hRangeMin != Mathf.NegativeInfinity ? hRangeMin : m_CurveBounds.min.x; float miny = vRangeMin != Mathf.NegativeInfinity ? vRangeMin : m_CurveBounds.min.y; float maxx = hRangeMax != Mathf.Infinity ? hRangeMax : m_CurveBounds.max.x; float maxy = vRangeMax != Mathf.Infinity ? vRangeMax : m_CurveBounds.max.y; m_DrawingBounds.SetMinMax(new Vector3(minx, miny, m_CurveBounds.min.z), new Vector3(maxx, maxy, m_CurveBounds.max.z)); // Enforce minimum size of bounds m_DrawingBounds.size = new Vector3(Mathf.Max(m_DrawingBounds.size.x, kMinRange), Mathf.Max(m_DrawingBounds.size.y, kMinRange), 0); m_CurveBounds.size = new Vector3(Mathf.Max(m_CurveBounds.size.x, kMinRange), Mathf.Max(m_CurveBounds.size.y, kMinRange), 0); m_BoundsAreDirty = false; } public void InvalidateSelectionBounds() { m_SelectionBoundsAreDirty = true; m_SelectionWithCurvesBoundsAreDirty = true; } private void RecalculateSelectionBounds() { if (!m_SelectionBoundsAreDirty) return; if (hasSelection) { List selected = selectedCurves; Keyframe keyframe = GetKeyframeFromSelection(selected[0]); m_SelectionBounds = new Bounds(new Vector2(keyframe.time, keyframe.value), Vector2.zero); for (int i = 1; i < selected.Count; ++i) { keyframe = GetKeyframeFromSelection(selected[i]); m_SelectionBounds.Encapsulate(new Vector2(keyframe.time, keyframe.value)); } } else { m_SelectionBounds = new Bounds(Vector3.zero, Vector3.zero); } m_SelectionBoundsAreDirty = false; } private void RecalculateSelectionWithCurvesBounds() { if (!m_SelectionWithCurvesBoundsAreDirty) return; m_SelectionWithCurvesBounds = m_SelectionBounds; //Aggregate selected key in between curves. if (hasSelection) { CurveSelection[] selected = selectedCurves.OrderBy(p => p.curveID).ThenBy(p => p.key).ToArray(); int currentCurveId = selected.First().curveID; CurveWrapper currentCurveWrapper = GetCurveWrapperFromID(currentCurveId); for (int i = 0; i < selected.Length - 1; i++) { if (currentCurveId != selected[i + 1].curveID) { currentCurveId = selected[i + 1].curveID; currentCurveWrapper = GetCurveWrapperFromID(currentCurveId); continue; } Keyframe keyframeStart = GetKeyframeFromSelection(selected[i]); Keyframe keyframeEnd = GetKeyframeFromSelection(selected[i + 1]); Bounds inBetweenBounds = currentCurveWrapper.ComputeBoundsBetweenTime(keyframeStart.time, keyframeEnd.time); m_SelectionWithCurvesBounds.Encapsulate(inBetweenBounds); } } m_SelectionWithCurvesBoundsAreDirty = false; } public Bounds GetClipBounds() { return curveBounds; } public Bounds GetSelectionBounds() { if (!hasSelection) return GetClipBounds(); Bounds frameBounds; // Add neighboring keys in bounds if only a single key is selected. if (selectedCurves.Count == 1) { CurveSelection cs = selectedCurves[0]; CurveWrapper cw = GetCurveWrapperFromSelection(cs); // Encapsulate key in bounds frameBounds = new Bounds(new Vector2(cw.curve[cs.key].time, cw.curve[cs.key].value), Vector2.zero); // Include neighboring keys in bounds if (cs.key - 1 >= 0) frameBounds.Encapsulate(new Vector2(cw.curve[cs.key - 1].time, cw.curve[cs.key - 1].value)); if (cs.key + 1 < cw.curve.length) frameBounds.Encapsulate(new Vector2(cw.curve[cs.key + 1].time, cw.curve[cs.key + 1].value)); //Include neighboring curves in bounds if (cs.key - 1 >= 0) frameBounds.Encapsulate(cw.ComputeBoundsBetweenTime(cw.curve[cs.key - 1].time, cw.curve[cs.key].time)); if (cs.key + 1 < cw.curve.length) frameBounds.Encapsulate(cw.ComputeBoundsBetweenTime(cw.curve[cs.key].time, cw.curve[cs.key + 1].time)); } else { frameBounds = selectionWithCurvesBounds; } // Enforce minimum size of bounds frameBounds.size = new Vector3(Mathf.Max(frameBounds.size.x, 0.1F), Mathf.Max(frameBounds.size.y, 0.1F), 0); return frameBounds; } // Frame all curves to be visible. public void FrameClip(bool horizontally, bool vertically) { Frame(GetClipBounds(), horizontally, vertically); } // Frame selected keys to be visible. public void FrameSelected(bool horizontally, bool vertically) { Frame(GetSelectionBounds(), horizontally, vertically); } public void Frame(Bounds frameBounds, bool horizontally, bool vertically) { if (frameBounds.size == Vector3.zero) return; if (horizontally) SetShownHRangeInsideMargins(frameBounds.min.x, frameBounds.max.x); if (vertically) SetShownVRangeInsideMargins(frameBounds.min.y, frameBounds.max.y); } public void UpdateCurves(List curveIds, string undoText) { foreach (int id in curveIds) { CurveWrapper cw = GetCurveWrapperFromID(id); cw.changed = true; } if (curvesUpdated != null) curvesUpdated(); } public void UpdateCurves(List changedCurves, string undoText) { UpdateCurves(new List(changedCurves.Select(curve => curve.curveId)), undoText); } public void StartLiveEdit() { MakeCurveBackups(); } public void EndLiveEdit() { m_CurveBackups = null; } public bool InLiveEdit() { return m_CurveBackups != null; } void Init() { } public void OnGUI() { BeginViewGUI(); GridGUI(); DrawWrapperPopups(); CurveGUI(); EndViewGUI(); } public void CurveGUI() { if (m_PointRenderer == null) m_PointRenderer = new CurveControlPointRenderer(); if (m_RectangleTool == null) { m_RectangleTool = new CurveEditorRectangleTool(); m_RectangleTool.Initialize(this); } GUI.BeginGroup(drawRect); Init(); GUIUtility.GetControlID(s_SelectKeyHash, FocusType.Passive); GUI.contentColor = GUI.backgroundColor = Color.white; Color oldColor = GUI.color; Event evt = Event.current; //Because this uses a keyboard input field, it must be allowed to handle events first if (evt.type != EventType.Repaint) { EditSelectedPoints(); } switch (evt.type) { case EventType.ValidateCommand: case EventType.ExecuteCommand: bool execute = evt.type == EventType.ExecuteCommand; switch (evt.commandName) { case EventCommandNames.Delete: if (hasSelection) { if (execute) { DeleteSelectedKeys(); } evt.Use(); } break; case EventCommandNames.FrameSelected: if (execute) FrameSelected(true, true); evt.Use(); break; case EventCommandNames.SelectAll: if (execute) SelectAll(); evt.Use(); break; } break; case EventType.KeyDown: if ((evt.keyCode == KeyCode.Backspace || evt.keyCode == KeyCode.Delete) && hasSelection) { DeleteSelectedKeys(); evt.Use(); } break; case EventType.ContextClick: CurveSelection mouseKey = FindNearest(); if (mouseKey != null) { List keyList = new List(); // Find out if key under mouse is part of selected keys bool inSelected = false; foreach (CurveSelection sel in selectedCurves) { keyList.Add(new KeyIdentifier(GetCurveFromSelection(sel), sel.curveID, sel.key)); if (sel.curveID == mouseKey.curveID && sel.key == mouseKey.key) inSelected = true; } if (!inSelected) { keyList.Clear(); keyList.Add(new KeyIdentifier(GetCurveFromSelection(mouseKey), mouseKey.curveID, mouseKey.key)); ClearSelection(); AddSelection(mouseKey); } bool isEditable = !selectedCurves.Exists(sel => !GetCurveWrapperFromSelection(sel).animationIsEditable); m_MenuManager = new CurveMenuManager(this); GenericMenu menu = new GenericMenu(); string deleteKeyLabel = keyList.Count > 1 ? k_DeleteKeys : k_DeleteKey; string editKeyLabel = keyList.Count > 1 ? k_EditKeys : k_EditKey; if (isEditable) { menu.AddItem(new GUIContent(deleteKeyLabel), false, DeleteKeys, keyList); menu.AddItem(new GUIContent(editKeyLabel), false, StartEditingSelectedPointsContext, mousePositionInDrawing); } else { menu.AddDisabledItem(new GUIContent(deleteKeyLabel)); menu.AddDisabledItem(new GUIContent(editKeyLabel)); } if (isEditable) { menu.AddSeparator(""); m_MenuManager.AddTangentMenuItems(menu, keyList); } menu.ShowAsContext(); Event.current.Use(); } break; } GUI.color = oldColor; m_RectangleTool.HandleOverlayEvents(); DragTangents(); m_RectangleTool.HandleEvents(); EditAxisLabels(); SelectPoints(); EditorGUI.BeginChangeCheck(); Vector2 move = MovePoints(); if (EditorGUI.EndChangeCheck() && m_DraggingKey != null) { m_MoveCoord = move; } if (evt.type == EventType.Repaint) { DrawCurves(); m_RectangleTool.OnGUI(); DrawCurvesTangents(); DrawCurvesOverlay(); m_RectangleTool.OverlayOnGUI(rect); EditSelectedPoints(); } GUI.color = oldColor; GUI.EndGroup(); } // Recalculate curve.selected from selected curves void RecalcCurveSelection() { // Reset selection state of all curves foreach (CurveWrapper cw in m_AnimationCurves) cw.selected = CurveWrapper.SelectionMode.None; // Now sync with selected curves foreach (CurveSelection cs in selectedCurves) { CurveWrapper cw = GetCurveWrapperFromSelection(cs); if (cw != null) cw.selected = cs.semiSelected ? CurveWrapper.SelectionMode.SemiSelected : CurveWrapper.SelectionMode.Selected; } } void RecalcSecondarySelection() { // No need to recalculate secondary selection if there are no curves with a valid groupId. if (!m_EnableCurveGroups) return; // The new list of secondary selections List newSelection = new List(selectedCurves.Count); // Go through selection, find curveselections that need syncing, add those for the sync points. foreach (CurveSelection cs in selectedCurves) { CurveWrapper cw = GetCurveWrapperFromSelection(cs); if (cw == null) continue; int groupId = cw.groupId; if (groupId != -1 && !cs.semiSelected) { newSelection.Add(cs); foreach (CurveWrapper cw2 in m_AnimationCurves) { if (cw2.groupId == groupId && cw2 != cw) { CurveSelection newCS = new CurveSelection(cw2.id, cs.key); newCS.semiSelected = true; newSelection.Add(newCS); } } } else { newSelection.Add(cs); } } newSelection.Sort(); // the selection can contain duplicate keys. We go through the selection and remove any duplicates we find. // Since the selection is already sorted, the duplicates are next to each other. for (int i = 0; i < newSelection.Count - 1;) { CurveSelection cs1 = newSelection[i]; CurveSelection cs2 = newSelection[i + 1]; if (cs1.curveID == cs2.curveID && cs1.key == cs2.key) { // If we have a collision, one can be fully selected, while the other can be semiselected. Make sure we always get the most selected one. if (!cs1.semiSelected || !cs2.semiSelected) cs1.semiSelected = false; newSelection.RemoveAt(i + 1); } else { i++; } } // Assign back selectedCurves = newSelection; } void DragTangents() { Event evt = Event.current; int tangentId = GUIUtility.GetControlID(s_TangentControlIDHash, FocusType.Passive); switch (evt.GetTypeForControl(tangentId)) { case EventType.MouseDown: if (evt.button == 0 && !evt.alt) { m_SelectedTangentPoint = null; float nearestDist = kMaxPickDistSqr; Vector2 mousePos = Event.current.mousePosition; foreach (CurveSelection cs in selectedCurves) { AnimationCurve curve = GetCurveFromSelection(cs); if (curve == null) continue; if (cs.key < 0 || cs.key >= curve.length) continue; if (IsLeftTangentEditable(cs)) { CurveSelection tangent = new CurveSelection(cs.curveID, cs.key, CurveSelection.SelectionType.InTangent); float d = (DrawingToViewTransformPoint(GetPosition(tangent)) - mousePos).sqrMagnitude; if (d <= nearestDist) { m_SelectedTangentPoint = tangent; nearestDist = d; } } if (IsRightTangentEditable(cs)) { CurveSelection tangent = new CurveSelection(cs.curveID, cs.key, CurveSelection.SelectionType.OutTangent); float d = (DrawingToViewTransformPoint(GetPosition(tangent)) - mousePos).sqrMagnitude; if (d <= nearestDist) { m_SelectedTangentPoint = tangent; nearestDist = d; } } } if (m_SelectedTangentPoint != null) { SaveKeySelection(k_EditCurve); GUIUtility.hotControl = tangentId; evt.Use(); } } break; case EventType.MouseDrag: if (GUIUtility.hotControl == tangentId) { CurveSelection dragged = m_SelectedTangentPoint; CurveWrapper curveWrapper = GetCurveWrapperFromSelection(dragged); if ((curveWrapper != null) && curveWrapper.animationIsEditable) { Vector2 newPosition = mousePositionInDrawing; Keyframe key = curveWrapper.curve[dragged.key]; if (dragged.type == CurveSelection.SelectionType.InTangent) { Keyframe prevKey = curveWrapper.curve[dragged.key - 1]; float dx = key.time - prevKey.time; Vector2 tangentDirection = newPosition - new Vector2(key.time, key.value); if (tangentDirection.x < -0.0001F) { key.inTangent = tangentDirection.y / tangentDirection.x; key.inWeight = Mathf.Clamp(Mathf.Abs(tangentDirection.x / dx), 0f, 1f); } else if (tangentDirection.y > 0) { key.inTangent = Mathf.Infinity; key.inWeight = 0f; } else { key.inTangent = -Mathf.Infinity; key.inWeight = 0f; } AnimationUtility.SetKeyLeftTangentMode(ref key, TangentMode.Free); if (!AnimationUtility.GetKeyBroken(key)) { key.outTangent = key.inTangent; AnimationUtility.SetKeyRightTangentMode(ref key, TangentMode.Free); } } else if (dragged.type == CurveSelection.SelectionType.OutTangent) { Keyframe nextKey = curveWrapper.curve[dragged.key + 1]; float dx = nextKey.time - key.time; Vector2 tangentDirection = newPosition - new Vector2(key.time, key.value); if (tangentDirection.x > 0.0001F) { key.outTangent = tangentDirection.y / tangentDirection.x; key.outWeight = Mathf.Clamp(Mathf.Abs(tangentDirection.x / dx), 0f, 1f); } else if (tangentDirection.y > 0) { key.outTangent = Mathf.Infinity; key.outWeight = 0f; } else { key.outTangent = -Mathf.Infinity; key.outWeight = 0f; } AnimationUtility.SetKeyRightTangentMode(ref key, TangentMode.Free); if (!AnimationUtility.GetKeyBroken(key)) { key.inTangent = key.outTangent; AnimationUtility.SetKeyLeftTangentMode(ref key, TangentMode.Free); } } dragged.key = curveWrapper.MoveKey(dragged.key, ref key); AnimationUtility.UpdateTangentsFromModeSurrounding(curveWrapper.curve, dragged.key); curveWrapper.changed = true; GUI.changed = true; } Event.current.Use(); } break; case EventType.MouseUp: if (GUIUtility.hotControl == tangentId) { GUIUtility.hotControl = 0; evt.Use(); } break; case EventType.Repaint: if (GUIUtility.hotControl == tangentId) { Rect mouseRect = new Rect(evt.mousePosition.x - 10, evt.mousePosition.y - 10, 20, 20); EditorGUIUtility.AddCursorRect(mouseRect, MouseCursor.MoveArrow); } break; } } internal void DeleteSelectedKeys() { string undoLabel; if (selectedCurves.Count > 1) undoLabel = k_DeleteKeys; else undoLabel = k_DeleteKey; SaveKeySelection(undoLabel); // Go over selection backwards and delete (avoids wrecking indices) for (int i = selectedCurves.Count - 1; i >= 0; i--) { CurveSelection k = selectedCurves[i]; CurveWrapper cw = GetCurveWrapperFromSelection(k); if (cw == null) continue; if (!cw.animationIsEditable) continue; if (!settings.allowDeleteLastKeyInCurve) if (cw.curve.length == 1) continue; cw.curve.RemoveKey(k.key); AnimationUtility.UpdateTangentsFromMode(cw.curve); cw.changed = true; GUI.changed = true; } SelectNone(); GUIUtility.hotControl = 0; } private void DeleteKeys(object obj) { List keyList = (List)obj; string undoLabel; if (keyList.Count > 1) undoLabel = k_DeleteKeys; else undoLabel = k_DeleteKey; SaveKeySelection(undoLabel); // Go over selection backwards and delete (avoids wrecking indices) List curveIds = new List(); for (int i = keyList.Count - 1; i >= 0; i--) { if (!settings.allowDeleteLastKeyInCurve) if (keyList[i].curve.length == 1) continue; if (!GetCurveWrapperFromID(keyList[i].curveId).animationIsEditable) continue; keyList[i].curve.RemoveKey(keyList[i].key); AnimationUtility.UpdateTangentsFromMode(keyList[i].curve); curveIds.Add(keyList[i].curveId); } UpdateCurves(curveIds, undoLabel); SelectNone(); } float ClampVerticalValue(float value, int curveID) { // Clamp by global value value = Mathf.Clamp(value, vRangeMin, vRangeMax); // Clamp with per curve settings. CurveWrapper cw = GetCurveWrapperFromID(curveID); if (cw != null) value = Mathf.Clamp(value, cw.vRangeMin, cw.vRangeMax); return value; } internal void TranslateSelectedKeys(Vector2 movement) { bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); UpdateCurvesFromPoints( delegate(SavedCurve.SavedKeyFrame keyframe, SavedCurve curve) { if (keyframe.selected != CurveWrapper.SelectionMode.None) { // Duplicate it - so we don't modify the backup copy var duplicateKeyframe = keyframe.Clone(); // Slide in time. Clamp key. duplicateKeyframe.key.time = Mathf.Clamp(duplicateKeyframe.key.time + movement.x, hRangeMin, hRangeMax); // if it's fully selected, also move on Y if (duplicateKeyframe.selected == CurveWrapper.SelectionMode.Selected) duplicateKeyframe.key.value = ClampVerticalValue(duplicateKeyframe.key.value + movement.y, curve.curveId); return duplicateKeyframe; } return keyframe; } ); if (!inLiveEdit) EndLiveEdit(); } internal void SetSelectedKeyPositions(float newTime, float newValue, bool updateTime, bool updateValue) { if (!updateTime && !updateValue) return; bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); UpdateCurvesFromPoints( delegate(SavedCurve.SavedKeyFrame keyframe, SavedCurve curve) { if (keyframe.selected != CurveWrapper.SelectionMode.None) { // Duplicate it - so we don't modify the backup copy var duplicateKeyframe = keyframe.Clone(); if (updateTime) { duplicateKeyframe.key.time = Mathf.Clamp(newTime, hRangeMin, hRangeMax); } if (updateValue) { duplicateKeyframe.key.value = ClampVerticalValue(newValue, curve.curveId); } return duplicateKeyframe; } return keyframe; } ); if (!inLiveEdit) EndLiveEdit(); } internal void TransformSelectedKeys(Matrix4x4 matrix, bool flipX, bool flipY) { bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); UpdateCurvesFromPoints( delegate(SavedCurve.SavedKeyFrame keyframe, SavedCurve curve) { if (keyframe.selected != CurveWrapper.SelectionMode.None) { // Duplicate it - so we don't modify the backup copy var duplicateKeyframe = keyframe.Clone(); Vector3 v = new Vector3(duplicateKeyframe.key.time, duplicateKeyframe.key.value, 0f); v = matrix.MultiplyPoint3x4(v); v.x = SnapTime(v.x); duplicateKeyframe.key.time = Mathf.Clamp(v.x, hRangeMin, hRangeMax); if (flipX) { duplicateKeyframe.key.inTangent = (keyframe.key.outTangent != Mathf.Infinity) ? -keyframe.key.outTangent : Mathf.Infinity; duplicateKeyframe.key.outTangent = (keyframe.key.inTangent != Mathf.Infinity) ? -keyframe.key.inTangent : Mathf.Infinity; if (keyframe.key.weightedMode == WeightedMode.In) duplicateKeyframe.key.weightedMode = WeightedMode.Out; else if (keyframe.key.weightedMode == WeightedMode.Out) duplicateKeyframe.key.weightedMode = WeightedMode.In; else duplicateKeyframe.key.weightedMode = keyframe.key.weightedMode; duplicateKeyframe.key.inWeight = keyframe.key.outWeight; duplicateKeyframe.key.outWeight = keyframe.key.inWeight; } // if it's fully selected, also move on Y if (duplicateKeyframe.selected == CurveWrapper.SelectionMode.Selected) { duplicateKeyframe.key.value = ClampVerticalValue(v.y, curve.curveId); if (flipY) { duplicateKeyframe.key.inTangent = (duplicateKeyframe.key.inTangent != Mathf.Infinity) ? -duplicateKeyframe.key.inTangent : Mathf.Infinity; duplicateKeyframe.key.outTangent = (duplicateKeyframe.key.outTangent != Mathf.Infinity) ? -duplicateKeyframe.key.outTangent : Mathf.Infinity; } } return duplicateKeyframe; } return keyframe; } ); if (!inLiveEdit) EndLiveEdit(); } internal void TransformRippleKeys(Matrix4x4 matrix, float t1, float t2, bool flipX, bool flipY) { bool inLiveEdit = InLiveEdit(); if (!inLiveEdit) StartLiveEdit(); UpdateCurvesFromPoints( delegate(SavedCurve.SavedKeyFrame keyframe, SavedCurve curve) { float newTime = keyframe.key.time; if (keyframe.selected != CurveWrapper.SelectionMode.None) { Vector3 v = new Vector3(keyframe.key.time, keyframe.key.value, 0f); v = matrix.MultiplyPoint3x4(v); newTime = v.x; // Duplicate it - so we don't modify the backup copy var duplicateKeyframe = keyframe.Clone(); duplicateKeyframe.key.time = SnapTime(Mathf.Clamp(newTime, hRangeMin, hRangeMax)); if (flipX) { duplicateKeyframe.key.inTangent = (keyframe.key.outTangent != Mathf.Infinity) ? -keyframe.key.outTangent : Mathf.Infinity; duplicateKeyframe.key.outTangent = (keyframe.key.inTangent != Mathf.Infinity) ? -keyframe.key.inTangent : Mathf.Infinity; } // if it's fully selected, also move on Y if (duplicateKeyframe.selected == CurveWrapper.SelectionMode.Selected) { duplicateKeyframe.key.value = ClampVerticalValue(v.y, curve.curveId); if (flipY) { duplicateKeyframe.key.inTangent = (duplicateKeyframe.key.inTangent != Mathf.Infinity) ? -duplicateKeyframe.key.inTangent : Mathf.Infinity; duplicateKeyframe.key.outTangent = (duplicateKeyframe.key.outTangent != Mathf.Infinity) ? -duplicateKeyframe.key.outTangent : Mathf.Infinity; } } return duplicateKeyframe; } else { if (keyframe.key.time > t2) { Vector3 v = new Vector3(flipX ? t1 : t2, 0f, 0f); v = matrix.MultiplyPoint3x4(v); float dt = v.x - t2; if (dt > 0f) newTime = keyframe.key.time + dt; } else if (keyframe.key.time < t1) { Vector3 v = new Vector3(flipX ? t2 : t1, 0f, 0f); v = matrix.MultiplyPoint3x4(v); float dt = v.x - t1; if (dt < 0f) newTime = keyframe.key.time + dt; } if (!Mathf.Approximately(newTime, keyframe.key.time)) { // Duplicate it - so we don't modify the backup copy var duplicateKeyframe = keyframe.Clone(); duplicateKeyframe.key.time = SnapTime(Mathf.Clamp(newTime, hRangeMin, hRangeMax)); return duplicateKeyframe; } } return keyframe; } ); if (!inLiveEdit) EndLiveEdit(); } void UpdateCurvesFromPoints(SavedCurve.KeyFrameOperation action) { if (m_CurveBackups == null) return; // Starting up: var dragSelection = new List(); // Go over all saved curves - each of these has at least one selected point. foreach (SavedCurve sc in m_CurveBackups) { CurveWrapper cw = GetCurveWrapperFromID(sc.curveId); if (!cw.animationIsEditable) continue; // Go through each curve and build a new working set of points. var working = new SortedList(SavedCurve.SavedKeyFrameComparer.Instance); // Add all unselected key frames to the working collection first. foreach (SavedCurve.SavedKeyFrame keyframe in sc.keys) { if (keyframe.selected == CurveWrapper.SelectionMode.None) { var newKeyframe = action(keyframe, sc); cw.PreProcessKey(ref newKeyframe.key); // We might have moved keys around, let's add new key or replace existing key. working[newKeyframe.key.time] = newKeyframe; } } // Add all modified key frames to the working collection and remove duplicates. foreach (SavedCurve.SavedKeyFrame keyframe in sc.keys) { if (keyframe.selected != CurveWrapper.SelectionMode.None) { var newKeyframe = action(keyframe, sc); cw.PreProcessKey(ref newKeyframe.key); // We might have moved keys around, let's add new key or replace existing key. working[newKeyframe.key.time] = newKeyframe; } } // Working now contains a set of points with everything set up correctly. // Each point has it's selection set, but m_DisplayCurves has a more traditional key array. // Go through the working points and sort those for display. int idx = 0; Keyframe[] keysToAssign = new Keyframe[working.Count]; foreach (KeyValuePair kvp in working) { SavedCurve.SavedKeyFrame sk = kvp.Value; keysToAssign[idx] = sk.key; if (sk.selected != CurveWrapper.SelectionMode.None) { CurveSelection cs = new CurveSelection(sc.curveId, idx); if (sk.selected == CurveWrapper.SelectionMode.SemiSelected) cs.semiSelected = true; dragSelection.Add(cs); } ++idx; } // We now have the list of keys to assign - let's get them back into the animation clip cw.curve.keys = keysToAssign; AnimationUtility.UpdateTangentsFromMode(cw.curve); cw.changed = true; } selectedCurves = dragSelection; } float SnapTime(float t) { if (EditorGUI.actionKey) { int snapLevel = hTicks.GetLevelWithMinSeparation(5); float snap = hTicks.GetPeriodOfLevel(snapLevel); t = Mathf.Round(t / snap) * snap; } else { if (invSnap != 0.0f) t = Mathf.Round(t * invSnap) / invSnap; } return t; } float SnapValue(float v) { if (EditorGUI.actionKey) { int snapLevel = vTicks.GetLevelWithMinSeparation(5); float snap = vTicks.GetPeriodOfLevel(snapLevel); v = Mathf.Round(v / snap) * snap; } return v; } /*string DebugSelection () { string s = ""; foreach (int i in m_PointSelection) s += i + ", "; s += "\n"; foreach (CurveSelection k in selectedCurves) s += "[" + k.curveID + ", " + k.key+ "], "; return s; }*/ new internal static class Styles { public const float pointIconCenterOffsetX = 7; public const float pointIconCenterOffsetY = 8; public const float pointIconSize = 16; public static Texture2D pointIcon = EditorGUIUtility.LoadIcon("curvekeyframe"); public static Texture2D pointIconWeighted = EditorGUIUtility.LoadIcon("curvekeyframeweighted"); public static Texture2D pointIconSelected = EditorGUIUtility.LoadIcon("curvekeyframeselected"); public static Texture2D pointIconSelectedOverlay = EditorGUIUtility.LoadIcon("curvekeyframeselectedoverlay"); public static Texture2D pointIconSemiSelectedOverlay = EditorGUIUtility.LoadIcon("curvekeyframesemiselectedoverlay"); public static GUIContent wrapModeMenuIcon = EditorGUIUtility.IconContent("AnimationWrapModeMenu"); public static GUIStyle none = new GUIStyle(); public static GUIStyle labelTickMarksY = "CurveEditorLabelTickMarks"; public static GUIStyle labelTickMarksX = "CurveEditorLabelTickmarksOverflow"; public static GUIStyle selectionRect = "SelectionRect"; public static GUIStyle dragLabel = "ProfilerBadge"; public static GUIStyle axisLabelNumberField = "AxisLabelNumberField"; public static GUIStyle rightAlignedLabel = "CurveEditorRightAlignedLabel"; } Vector2 GetGUIPoint(CurveWrapper cw, Vector3 point) { return HandleUtility.WorldToGUIPoint(DrawingToViewTransformPoint(point)); } Rect GetWorldRect(CurveWrapper cw, Rect rect) { Vector2 min = GetWorldPoint(cw, rect.min); Vector2 max = GetWorldPoint(cw, rect.max); // Invert y world coordinates. return Rect.MinMaxRect(min.x, max.y, max.x, min.y); } Vector2 GetWorldPoint(CurveWrapper cw, Vector2 point) { return ViewToDrawingTransformPoint(point); } Rect GetCurveRect(CurveWrapper cw) { Bounds bounds = cw.bounds; return Rect.MinMaxRect(bounds.min.x, bounds.min.y, bounds.max.x, bounds.max.y); } Vector2 s_StartMouseDragPosition, s_EndMouseDragPosition, s_StartKeyDragPosition; PickMode s_PickMode; int OnlyOneEditableCurve() { int index = -1; int curves = 0; for (int i = 0; i < m_AnimationCurves.Length; i++) { CurveWrapper wrapper = m_AnimationCurves[i]; if (wrapper.hidden || wrapper.readOnly) continue; curves++; index = i; } if (curves == 1) return index; else return -1; } // Returns an index into m_AnimationCurves int GetCurveAtPosition(Vector2 viewPos, out Vector2 closestPointOnCurve) { Vector2 localPos = ViewToDrawingTransformPoint(viewPos); // Find the closest curve at the time corresponding to the position int maxPixelDist = (int)Mathf.Sqrt(kMaxPickDistSqr); float smallestDist = kMaxPickDistSqr; int closest = -1; closestPointOnCurve = Vector3.zero; // Use drawOrder to ensure we pick the topmost curve for (int i = m_DrawOrder.Count - 1; i >= 0; --i) { CurveWrapper wrapper = GetCurveWrapperFromID(m_DrawOrder[i]); if (wrapper.hidden || wrapper.readOnly || wrapper.curve.length == 0) continue; // Sample the curves at pixel intervals in the area around the desired time, // corresponding to the max cursor distance allowed. Vector2 valL; valL.x = localPos.x - maxPixelDist / scale.x; valL.y = wrapper.renderer.EvaluateCurveSlow(valL.x); valL = DrawingToViewTransformPoint(valL); for (int x = -maxPixelDist; x < maxPixelDist; x++) { Vector2 valR; valR.x = localPos.x + (x + 1) / scale.x; valR.y = wrapper.renderer.EvaluateCurveSlow(valR.x); valR = DrawingToViewTransformPoint(valR); float dist = HandleUtility.DistancePointLine(viewPos, valL, valR); dist = dist * dist; if (dist < smallestDist) { smallestDist = dist; closest = wrapper.listIndex; closestPointOnCurve = HandleUtility.ProjectPointLine(viewPos, valL, valR); } valL = valR; } } if (closest >= 0) closestPointOnCurve = ViewToDrawingTransformPoint(closestPointOnCurve); return closest; } void CreateKeyFromClick(object obj) { string undoLabel = k_AddKey; SaveKeySelection(undoLabel); List ids = CreateKeyFromClick((Vector2)obj); if (ids.Count > 0) UpdateCurves(ids, undoLabel); } List CreateKeyFromClick(Vector2 viewPos) { List curveIds = new List(); // Check if there is only one curve to edit int singleCurveIndex = OnlyOneEditableCurve(); if (singleCurveIndex >= 0) { // If there is only one curve, allow creating keys on it by double/right-clicking anywhere // if the click is to the left or right of the existing keys, or if there are no existing keys. CurveWrapper cw = m_AnimationCurves[singleCurveIndex]; Vector2 localPos = ViewToDrawingTransformPoint(viewPos); float time = localPos.x; if (cw.curve.length == 0 || time < cw.curve[0].time || time > cw.curve[cw.curve.length - 1].time) { if (CreateKeyFromClick(singleCurveIndex, localPos)) curveIds.Add(cw.id); return curveIds; } } // If we didn't create a key above, only allow creating keys // when double/right-clicking on an existing curve Vector2 closestPointOnCurve; int curveIndex = GetCurveAtPosition(viewPos, out closestPointOnCurve); if (CreateKeyFromClick(curveIndex, closestPointOnCurve.x)) { if (curveIndex >= 0) curveIds.Add(m_AnimationCurves[curveIndex].id); } return curveIds; } bool CreateKeyFromClick(int curveIndex, float time) { time = Mathf.Clamp(time, settings.hRangeMin, settings.hRangeMax); // Add a key on a curve at a specified time if (curveIndex >= 0) { CurveSelection selectedPoint = null; CurveWrapper cw = m_AnimationCurves[curveIndex]; if (cw.animationIsEditable) { if (cw.groupId == -1) { selectedPoint = AddKeyAtTime(cw, time); } else { foreach (CurveWrapper cw2 in m_AnimationCurves) { if (cw2.groupId == cw.groupId) { CurveSelection cs = AddKeyAtTime(cw2, time); if (cw2.id == cw.id) selectedPoint = cs; } } } if (selectedPoint != null) { ClearSelection(); AddSelection(selectedPoint); RecalcSecondarySelection(); } else { SelectNone(); } return true; } } return false; } bool CreateKeyFromClick(int curveIndex, Vector2 localPos) { localPos.x = Mathf.Clamp(localPos.x, settings.hRangeMin, settings.hRangeMax); // Add a key on a curve at a specified time if (curveIndex >= 0) { CurveSelection selectedPoint = null; CurveWrapper cw = m_AnimationCurves[curveIndex]; if (cw.animationIsEditable) { if (cw.groupId == -1) { selectedPoint = AddKeyAtPosition(cw, localPos); } else { foreach (CurveWrapper cw2 in m_AnimationCurves) { if (cw2.groupId == cw.groupId) { if (cw2.id == cw.id) selectedPoint = AddKeyAtPosition(cw2, localPos); else AddKeyAtTime(cw2, localPos.x); } } } if (selectedPoint != null) { ClearSelection(); AddSelection(selectedPoint); RecalcSecondarySelection(); } else { SelectNone(); } return true; } } return false; } public void AddKey(CurveWrapper cw, Keyframe key) { CurveSelection selectedPoint = AddKeyframeAndSelect(key, cw); if (selectedPoint != null) { ClearSelection(); AddSelection(selectedPoint); RecalcSecondarySelection(); } else { SelectNone(); } } // Add a key to cw at time. // Returns the inserted key as a curveSelection CurveSelection AddKeyAtTime(CurveWrapper cw, float time) { // Find out if there's already a key there time = SnapTime(time); float halfFrame; if (invSnap != 0.0f) halfFrame = 0.5f / invSnap; else halfFrame = 0.0001f; if (CurveUtility.HaveKeysInRange(cw.curve, time - halfFrame, time + halfFrame)) return null; // Add the key int keyIndex = AnimationUtility.AddInbetweenKey(cw.curve, time); if (keyIndex >= 0) { CurveUtility.SetKeyModeFromContext(cw.curve, keyIndex); AnimationUtility.UpdateTangentsFromModeSurrounding(cw.curve, keyIndex); // Select the key CurveSelection selectedPoint = new CurveSelection(cw.id, keyIndex); cw.selected = CurveWrapper.SelectionMode.Selected; cw.changed = true; return selectedPoint; } return null; } // Add a key to cw at time. // Returns the inserted key as a curveSelection CurveSelection AddKeyAtPosition(CurveWrapper cw, Vector2 position) { // Find out if there's already a key there position.x = SnapTime(position.x); float halfFrame; if (invSnap != 0.0f) halfFrame = 0.5f / invSnap; else halfFrame = 0.0001f; if (CurveUtility.HaveKeysInRange(cw.curve, position.x - halfFrame, position.x + halfFrame)) return null; // Add the key float slope = 0; Keyframe key = new Keyframe(position.x, SnapValue(position.y), slope, slope); return AddKeyframeAndSelect(key, cw); } CurveSelection AddKeyframeAndSelect(Keyframe key, CurveWrapper cw) { if (!cw.animationIsEditable) return null; int keyIndex = cw.AddKey(key); CurveUtility.SetKeyModeFromContext(cw.curve, keyIndex); AnimationUtility.UpdateTangentsFromModeSurrounding(cw.curve, keyIndex); // Select the key CurveSelection selectedPoint = new CurveSelection(cw.id, keyIndex); cw.selected = CurveWrapper.SelectionMode.Selected; cw.changed = true; return selectedPoint; } // Find keyframe nearest to the mouse. We use the draw order to ensure to return the // key that is topmost rendered if several keys are overlapping. The user can // click on another curve to bring it to front and hereby be able to better select its keys. // Returns null if nothing is within Sqrt(kMaxPickDistSqr) pixels. CurveSelection FindNearest() { Vector2 mousePos = Event.current.mousePosition; bool foundCurve = false; int bestCurveID = -1; int bestKey = -1; float nearestDist = kMaxPickDistSqr; // Last element in draw order list is topmost so reverse traverse list for (int index = m_DrawOrder.Count - 1; index >= 0; --index) { CurveWrapper cw = GetCurveWrapperFromID(m_DrawOrder[index]); if (cw.readOnly || cw.hidden) continue; Keyframe[] keys = cw.curve.keys; for (int i = 0; i < cw.curve.length; ++i) { Keyframe k = keys[i]; float d = (GetGUIPoint(cw, new Vector2(k.time, k.value)) - mousePos).sqrMagnitude; // If we have an exact hit we just return that key if (d <= kExactPickDistSqr) return new CurveSelection(cw.id, i); // Otherwise find closest if (d < nearestDist) { foundCurve = true; bestCurveID = cw.id; bestKey = i; nearestDist = d; } } // If top curve has key within range make it harder for keys below to get selected if (index == m_DrawOrder.Count - 1 && bestCurveID >= 0) nearestDist = kExactPickDistSqr; } if (foundCurve) return new CurveSelection(bestCurveID, bestKey); return null; } public void SelectNone() { ClearSelection(); foreach (CurveWrapper cw in m_AnimationCurves) cw.selected = CurveWrapper.SelectionMode.None; } public void SelectAll() { int totalLength = 0; foreach (CurveWrapper cw in m_AnimationCurves) { if (cw.hidden) continue; totalLength += cw.curve.length; } var newSelection = new List(totalLength); foreach (CurveWrapper cw in m_AnimationCurves) { cw.selected = CurveWrapper.SelectionMode.Selected; for (int j = 0; j < cw.curve.length; j++) newSelection.Add(new CurveSelection(cw.id, j)); } selectedCurves = newSelection; } public bool IsDraggingKey() { return m_DraggingKey != null; } public bool IsDraggingCurveOrRegion() { return m_DraggingCurveOrRegion != null; } public bool IsDraggingCurve(CurveWrapper cw) { return (m_DraggingCurveOrRegion != null && m_DraggingCurveOrRegion.Length == 1 && m_DraggingCurveOrRegion[0] == cw); } public bool IsDraggingRegion(CurveWrapper cw1, CurveWrapper cw2) { return (m_DraggingCurveOrRegion != null && m_DraggingCurveOrRegion.Length == 2 && (m_DraggingCurveOrRegion[0] == cw1 || m_DraggingCurveOrRegion[0] == cw2)); } bool HandleCurveAndRegionMoveToFrontOnMouseDown(ref Vector2 timeValue, ref CurveWrapper[] curves) { // Did we click on a curve Vector2 closestPointOnCurve; int clickedCurveIndex = GetCurveAtPosition(Event.current.mousePosition, out closestPointOnCurve); if (clickedCurveIndex >= 0) { MoveCurveToFront(m_AnimationCurves[clickedCurveIndex].id); timeValue = mousePositionInDrawing; curves = new[] { m_AnimationCurves[clickedCurveIndex] }; return true; } // Did we click in a region for (int i = m_DrawOrder.Count - 1; i >= 0; --i) { CurveWrapper cw = GetCurveWrapperFromID(m_DrawOrder[i]); if (cw == null) continue; if (cw.hidden) continue; if (cw.curve.length == 0) continue; CurveWrapper cw2 = null; if (i > 0) cw2 = GetCurveWrapperFromID(m_DrawOrder[i - 1]); if (IsRegion(cw, cw2)) { float mouseY = mousePositionInDrawing.y; float time = mousePositionInDrawing.x; float v1 = cw.renderer.EvaluateCurveSlow(time); float v2 = cw2.renderer.EvaluateCurveSlow(time); if (v1 > v2) { float tmp = v1; v1 = v2; v2 = tmp; } if (mouseY >= v1 && mouseY <= v2) { timeValue = mousePositionInDrawing; curves = new[] { cw, cw2 }; MoveCurveToFront(cw.id); return true; } i--; // we handled two curves } } return false; // No curves or regions hit } void SelectPoints() { int id = GUIUtility.GetControlID(897560, FocusType.Passive); Event evt = Event.current; bool addToSelection = evt.shift; bool toggleSelection = EditorGUI.actionKey; switch (evt.GetTypeForControl(id)) { case EventType.Layout: float distance = HandleUtility.DistanceToRectangleInternal(drawRect.position, Quaternion.identity, drawRect.size); HandleUtility.AddControl(id, distance); break; case EventType.ContextClick: Rect drawRectAtOrigin = drawRect; drawRectAtOrigin.x = drawRectAtOrigin.y = 0f; if (drawRectAtOrigin.Contains(Event.current.mousePosition)) { Vector2 closestPositionOnCurve; int curveIndex = GetCurveAtPosition(Event.current.mousePosition, out closestPositionOnCurve); if (curveIndex >= 0) { GenericMenu menu = new GenericMenu(); if (m_AnimationCurves[curveIndex].animationIsEditable) menu.AddItem(EditorGUIUtility.TrTextContent("Add Key"), false, CreateKeyFromClick, Event.current.mousePosition); else menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Add Key")); menu.ShowAsContext(); Event.current.Use(); } } break; case EventType.MouseDown: if (evt.clickCount == 2 && evt.button == 0) { CurveSelection selectedPoint = FindNearest(); if (selectedPoint != null) { if (!addToSelection) { ClearSelection(); } AnimationCurve curve = GetCurveFromSelection(selectedPoint); if (curve != null) { BeginRangeSelection(); for (int keyIndex = 0; keyIndex < curve.length; ++keyIndex) { if (!selectedCurves.Any(x => x.curveID == selectedPoint.curveID && x.key == keyIndex)) { var keySelection = new CurveSelection(selectedPoint.curveID, keyIndex); AddSelection(keySelection); } } EndRangeSelection(); } } else { SaveKeySelection(k_AddKey); List curveIds = CreateKeyFromClick(Event.current.mousePosition); if (curveIds.Count > 0) { foreach (int curveId in curveIds) { CurveWrapper cw = GetCurveWrapperFromID(curveId); cw.changed = true; } GUI.changed = true; } } evt.Use(); } else if (evt.button == 0) { CurveSelection selectedPoint = FindNearest(); if (selectedPoint == null || selectedPoint.semiSelected) { // If we did not hit a key then check if a curve or region was clicked Vector2 timeValue = Vector2.zero; CurveWrapper[] curves = null; var curveOrRegionClicked = HandleCurveAndRegionMoveToFrontOnMouseDown(ref timeValue, ref curves); if (!addToSelection && !toggleSelection && !curveOrRegionClicked) { SelectNone(); } GUIUtility.hotControl = id; s_EndMouseDragPosition = s_StartMouseDragPosition = evt.mousePosition; s_PickMode = PickMode.Click; // case 845553 event will be processed afterwards when dragging curve. if (!curveOrRegionClicked) evt.Use(); } else { MoveCurveToFront(selectedPoint.curveID); Keyframe selectedKeyframe = GetKeyframeFromSelection(selectedPoint); s_StartKeyDragPosition = new Vector2(selectedKeyframe.time, selectedKeyframe.value); if (addToSelection) { // Isolate range key indices in current selection. bool addRangeToSelection = false; int keyMin = selectedPoint.key; int keyMax = selectedPoint.key; for (int i = 0; i < selectedCurves.Count; ++i) { CurveSelection cs = selectedCurves[i]; if (cs.curveID == selectedPoint.curveID) { addRangeToSelection = true; keyMin = Mathf.Min(keyMin, cs.key); keyMax = Mathf.Max(keyMax, cs.key); } } if (!addRangeToSelection) { if (!selectedCurves.Contains(selectedPoint)) { AddSelection(selectedPoint); } } else { // Try and add all keys on the same curve in the range [keyMin, keyMax] BeginRangeSelection(); for (var keyIndex = keyMin; keyIndex <= keyMax; ++keyIndex) { if (!selectedCurves.Any(x => x.curveID == selectedPoint.curveID && x.key == keyIndex)) { var rangeSelection = new CurveSelection(selectedPoint.curveID, keyIndex); AddSelection(rangeSelection); } } EndRangeSelection(); } Event.current.Use(); } else if (toggleSelection) { if (!selectedCurves.Contains(selectedPoint)) { AddSelection(selectedPoint); } else { RemoveSelection(selectedPoint); } Event.current.Use(); } else if (!selectedCurves.Contains(selectedPoint)) { ClearSelection(); AddSelection(selectedPoint); } RecalcSecondarySelection(); RecalcCurveSelection(); } HandleUtility.Repaint(); } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { s_EndMouseDragPosition = evt.mousePosition; if (s_PickMode == PickMode.Click) { s_PickMode = PickMode.Marquee; if (addToSelection || toggleSelection) s_SelectionBackup = new List(selectedCurves); else s_SelectionBackup = new List(); } else { Rect r = EditorGUIExt.FromToRect(s_StartMouseDragPosition, evt.mousePosition); List newSelection = new List(s_SelectionBackup); for (int i = 0; i < m_AnimationCurves.Length; ++i) { CurveWrapper cw = m_AnimationCurves[i]; if (cw.readOnly || cw.hidden) continue; Rect worldRect = GetWorldRect(cw, r); if (!GetCurveRect(cw).Overlaps(worldRect)) continue; int keyIndex = 0; foreach (Keyframe k in cw.curve.keys) { if (worldRect.Contains(new Vector2(k.time, k.value))) newSelection.Add(new CurveSelection(cw.id, keyIndex)); ++keyIndex; } } selectedCurves = newSelection; // We need to sort selection since we're mixing existing selection with new selection. if (s_SelectionBackup.Count > 0) selectedCurves.Sort(); RecalcSecondarySelection(); RecalcCurveSelection(); } evt.Use(); } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { if (s_PickMode != PickMode.Click) { // Move selected curves to front. var processedCurves = new HashSet(); for (int i = 0; i < selectedCurves.Count; ++i) { CurveWrapper cw = GetCurveWrapperFromSelection(selectedCurves[i]); if (!processedCurves.Contains(cw.id)) { MoveCurveToFront(cw.id); processedCurves.Add(cw.id); } } } GUIUtility.hotControl = 0; s_PickMode = PickMode.None; Event.current.Use(); } break; } if (s_PickMode == PickMode.Marquee) { GUI.Label(EditorGUIExt.FromToRect(s_StartMouseDragPosition, s_EndMouseDragPosition), GUIContent.none, Styles.selectionRect); } } string m_AxisLabelFormat = "n1"; private void EditAxisLabels() { int id = GUIUtility.GetControlID(18975602, FocusType.Keyboard); List curvesInSameSpace = new List(); Vector2 axisUiScalars = GetAxisUiScalars(curvesInSameSpace); bool isEditable = axisUiScalars.y >= 0 && curvesInSameSpace.Count > 0 && curvesInSameSpace[0].setAxisUiScalarsCallback != null; if (!isEditable) return; Rect editRect = new Rect(0, topmargin - 8, leftmargin - 4, 16); Rect dragRect = editRect; dragRect.y -= editRect.height; Event evt = Event.current; switch (evt.GetTypeForControl(id)) { case EventType.Repaint: if (GUIUtility.hotControl == 0) EditorGUIUtility.AddCursorRect(dragRect, MouseCursor.SlideArrow); break; case EventType.MouseDown: if (evt.button == 0) { if (dragRect.Contains(Event.current.mousePosition)) { if (GUIUtility.hotControl == 0) { GUIUtility.keyboardControl = 0; GUIUtility.hotControl = id; GUI.changed = true; evt.Use(); } } if (!editRect.Contains(Event.current.mousePosition)) GUIUtility.keyboardControl = 0; // If not hitting our FloatField ensure it loses focus } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { float dragSensitity = Mathf.Clamp01(Mathf.Max(axisUiScalars.y, Mathf.Pow(Mathf.Abs(axisUiScalars.y), 0.5f)) * .01f); axisUiScalars.y += HandleUtility.niceMouseDelta * dragSensitity; if (axisUiScalars.y < 0.001f) axisUiScalars.y = 0.001f; // Since the scalar is a magnitude we do not want to drag to 0 and below.. find nicer solution SetAxisUiScalars(axisUiScalars, curvesInSameSpace); evt.Use(); } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { // Reset dragging GUIUtility.hotControl = 0; } break; } // Show input text field string orgFormat = EditorGUI.kFloatFieldFormatString; EditorGUI.kFloatFieldFormatString = m_AxisLabelFormat; float newValue = EditorGUI.FloatField(editRect, axisUiScalars.y, Styles.axisLabelNumberField); if (axisUiScalars.y != newValue) SetAxisUiScalars(new Vector2(axisUiScalars.x, newValue), curvesInSameSpace); EditorGUI.kFloatFieldFormatString = orgFormat; } public void BeginTimeRangeSelection(float time, bool addToSelection) { if (s_TimeRangeSelectionActive) { Debug.LogError("BeginTimeRangeSelection can only be called once"); return; } s_TimeRangeSelectionActive = true; s_TimeRangeSelectionStart = s_TimeRangeSelectionEnd = time; if (addToSelection) s_SelectionBackup = new List(selectedCurves); else s_SelectionBackup = new List(); } public void TimeRangeSelectTo(float time) { if (!s_TimeRangeSelectionActive) { Debug.LogError("TimeRangeSelectTo can only be called after BeginTimeRangeSelection"); return; } s_TimeRangeSelectionEnd = time; var newSelection = new List(s_SelectionBackup); float minTime = Mathf.Min(s_TimeRangeSelectionStart, s_TimeRangeSelectionEnd); float maxTime = Mathf.Max(s_TimeRangeSelectionStart, s_TimeRangeSelectionEnd); foreach (CurveWrapper cw in m_AnimationCurves) { if (cw.readOnly || cw.hidden) continue; int i = 0; foreach (Keyframe k in cw.curve.keys) { if (k.time >= minTime && k.time < maxTime) { newSelection.Add(new CurveSelection(cw.id, i)); } i++; } } selectedCurves = newSelection; RecalcSecondarySelection(); RecalcCurveSelection(); } public void EndTimeRangeSelection() { if (!s_TimeRangeSelectionActive) { Debug.LogError("EndTimeRangeSelection can only be called after BeginTimeRangeSelection"); return; } s_TimeRangeSelectionStart = s_TimeRangeSelectionEnd = 0; s_TimeRangeSelectionActive = false; } public void CancelTimeRangeSelection() { if (!s_TimeRangeSelectionActive) { Debug.LogError("CancelTimeRangeSelection can only be called after BeginTimeRangeSelection"); return; } selectedCurves = s_SelectionBackup; s_TimeRangeSelectionActive = false; } bool m_EditingPoints; bool m_TimeWasEdited; bool m_ValueWasEdited; float m_NewTime; float m_NewValue; const string kPointValueFieldName = "pointValueField"; const string kPointTimeFieldName = "pointTimeField"; string m_FocusedPointField = null; Vector2 m_PointEditingFieldPosition; Vector2 GetPointEditionFieldPosition() { var minTime = selectedCurves.Min(x => GetKeyframeFromSelection(x).time); var maxTime = selectedCurves.Max(x => GetKeyframeFromSelection(x).time); var minValue = selectedCurves.Min(x => GetKeyframeFromSelection(x).value); var maxValue = selectedCurves.Max(x => GetKeyframeFromSelection(x).value); return new Vector2(minTime + maxTime, minValue + maxValue) * 0.5f; } void StartEditingSelectedPointsContext(object fieldPosition) { StartEditingSelectedPoints((Vector2)fieldPosition); } void StartEditingSelectedPoints() { Vector2 centre = GetPointEditionFieldPosition(); StartEditingSelectedPoints(centre); } void StartEditingSelectedPoints(Vector2 fieldPosition) { m_PointEditingFieldPosition = fieldPosition; m_FocusedPointField = kPointValueFieldName; m_TimeWasEdited = false; m_ValueWasEdited = false; // Initialize new values to current selection. m_NewTime = 0.0f; m_NewValue = 0.0f; Keyframe keyframe = GetKeyframeFromSelection(selectedCurves[0]); if (selectedCurves.All(x => GetKeyframeFromSelection(x).time == keyframe.time)) m_NewTime = keyframe.time; if (selectedCurves.All(x => GetKeyframeFromSelection(x).value == keyframe.value)) m_NewValue = keyframe.value; m_EditingPoints = true; } void FinishEditingPoints() { m_EditingPoints = false; } void EditSelectedPoints() { var evt = Event.current; if (m_EditingPoints && !hasSelection) { m_EditingPoints = false; } bool gotEscape = false; if (evt.type == EventType.KeyDown) { if (evt.keyCode == KeyCode.KeypadEnter || evt.keyCode == KeyCode.Return) { if (hasSelection && !m_EditingPoints) { StartEditingSelectedPoints(); evt.Use(); } else if (m_EditingPoints) { SetSelectedKeyPositions(m_NewTime, m_NewValue, m_TimeWasEdited, m_ValueWasEdited); FinishEditingPoints(); GUI.changed = true; evt.Use(); } } else if (evt.keyCode == KeyCode.Escape) { gotEscape = true; } } if (!m_EditingPoints) { return; } var fieldPosition = DrawingToViewTransformPoint(m_PointEditingFieldPosition); const float kFieldHeight = 18f; const float kFieldWidth = 95f; // Keep fields in the drawing margins var drawAreaInMargins = Rect.MinMaxRect(leftmargin, topmargin, rect.width - rightmargin, rect.height - bottommargin); fieldPosition.x = Mathf.Clamp(fieldPosition.x, drawAreaInMargins.xMin, drawAreaInMargins.xMax - kFieldWidth); fieldPosition.y = Mathf.Clamp(fieldPosition.y, drawAreaInMargins.yMin, drawAreaInMargins.yMax - kFieldHeight * 2); EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(kPointTimeFieldName); CurveWrapper curveWrapper = GetCurveWrapperFromSelection(selectedCurves[0]); Vector2 scale = Vector2.one; if (curveWrapper.useScalingInKeyEditor && curveWrapper.getAxisUiScalarsCallback != null) { scale = curveWrapper.getAxisUiScalarsCallback(); // don't scale if the values are too small or negative if (scale.x < 0.0001) scale.x = 1; if (scale.y < 0.0001) scale.y = 1; } m_NewTime = PointFieldForSelection( new Rect(fieldPosition.x, fieldPosition.y, kFieldWidth, kFieldHeight), 1, m_NewTime * scale.x, x => GetKeyframeFromSelection(x).time, (r, id, time) => TimeField(r, id, time, invSnap, timeFormat), curveWrapper.xAxisLabel == null ? settings.xAxisLabel : curveWrapper.xAxisLabel) / scale.x; if (EditorGUI.EndChangeCheck()) { m_TimeWasEdited = true; } EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(kPointValueFieldName); m_NewValue = PointFieldForSelection( new Rect(fieldPosition.x, fieldPosition.y + kFieldHeight, kFieldWidth, kFieldHeight), 2, m_NewValue * scale.y, x => GetKeyframeFromSelection(x).value, (r, id, value) => ValueField(r, id, value), curveWrapper.yAxisLabel == null ? settings.yAxisLabel : curveWrapper.yAxisLabel) / scale.y; if (EditorGUI.EndChangeCheck()) { m_ValueWasEdited = true; } if (gotEscape) { FinishEditingPoints(); } // Delay focusing these controls until they've been named if (m_FocusedPointField != null) { EditorGUI.FocusTextInControl(m_FocusedPointField); if (evt.type == EventType.Repaint) { m_FocusedPointField = null; } } if (evt.type == EventType.KeyDown) { const char tabCharacter = '\t'; const char endOfMediumCharacter = (char)25; // ASCII 25: "End Of Medium" on pressing shift tab // Override Unity's Tab and Shift+Tab handling. if (evt.character == tabCharacter || evt.character == endOfMediumCharacter) { if (m_TimeWasEdited || m_ValueWasEdited) { SetSelectedKeyPositions(m_NewTime, m_NewValue, m_TimeWasEdited, m_ValueWasEdited); m_PointEditingFieldPosition = GetPointEditionFieldPosition(); } m_FocusedPointField = GUI.GetNameOfFocusedControl() == kPointValueFieldName ? kPointTimeFieldName : kPointValueFieldName; evt.Use(); } } // Stop editing if there's an unused click if (evt.type == EventType.MouseDown) { FinishEditingPoints(); } } // Float editing field for members of selected points float PointFieldForSelection( Rect rect, int customID, float value, System.Func memberGetter, System.Func fieldCreator, string label) { float firstSelectedValue = memberGetter(selectedCurves[0]); bool sameValues = selectedCurves.All(x => memberGetter(x) == firstSelectedValue); if (!sameValues) EditorGUI.showMixedValue = true; var labelRect = rect; labelRect.x -= labelRect.width; // Use custom IDs to separate event handling from drawing int id = GUIUtility.GetControlID(customID, FocusType.Keyboard, rect); var oldColor = GUI.color; GUI.color = Color.white; GUI.Label(labelRect, label, Styles.rightAlignedLabel); value = fieldCreator(rect, id, value); GUI.color = oldColor; EditorGUI.showMixedValue = false; return value; } // m_DraggingCurveOrRegion is null if nothing is being dragged, has one entry if single curve being dragged or has two entries if region is being dragged CurveWrapper[] m_DraggingCurveOrRegion = null; void SetupKeyOrCurveDragging(Vector2 timeValue, CurveWrapper cw, int id, Vector2 mousePos) { m_DraggedCoord = timeValue; m_DraggingKey = cw; GUIUtility.hotControl = id; s_StartMouseDragPosition = mousePos; } public Vector2 MovePoints() { int id = GUIUtility.GetControlID(FocusType.Passive); if (!hasSelection && !settings.allowDraggingCurvesAndRegions) return Vector2.zero; Event evt = Event.current; switch (evt.GetTypeForControl(id)) { case EventType.MouseDown: if (evt.button == 0) { // Key dragging foreach (CurveSelection cs in selectedCurves) { CurveWrapper curveWrapper = GetCurveWrapperFromSelection(cs); if ((curveWrapper == null) || curveWrapper.hidden) continue; if ((DrawingToViewTransformPoint(GetPosition(cs)) - evt.mousePosition).sqrMagnitude <= kMaxPickDistSqr) { Keyframe keyframe = GetKeyframeFromSelection(cs); SetupKeyOrCurveDragging(new Vector2(keyframe.time, keyframe.value), curveWrapper, id, evt.mousePosition); m_RectangleTool.OnStartMove(s_StartMouseDragPosition, rippleTime); evt.Use(); break; } } // Curve dragging. Moving keys has highest priority, therefore we check curve/region dragging AFTER key dragging above if (evt.shift && settings.allowDraggingCurvesAndRegions && m_DraggingKey == null) { // We use the logic as for moving keys when we drag entire curves or regions: We just // select all keyFrames in a curve or region before dragging and ensure to hide tangents when drawing. Vector2 timeValue = Vector2.zero; CurveWrapper[] curves = null; if (HandleCurveAndRegionMoveToFrontOnMouseDown(ref timeValue, ref curves)) { var newSelection = new List(); // Add all keys of curves to selection to reuse code of key dragging foreach (CurveWrapper cw in curves) { for (int i = 0; i < cw.curve.keys.Length; ++i) newSelection.Add(new CurveSelection(cw.id, i)); MoveCurveToFront(cw.id); } preCurveDragSelection = selectedCurves; selectedCurves = newSelection; // Call after selection above SetupKeyOrCurveDragging(timeValue, curves[0], id, evt.mousePosition); m_DraggingCurveOrRegion = curves; m_RectangleTool.OnStartMove(s_StartMouseDragPosition, false); evt.Use(); } } } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { Vector2 delta = evt.mousePosition - s_StartMouseDragPosition; Vector2 motion = Vector2.zero; // Only drag along x OR y when shift is held down if (evt.shift && m_AxisLock == AxisLock.None) m_AxisLock = Mathf.Abs(delta.x) > Mathf.Abs(delta.y) ? AxisLock.X : AxisLock.Y; if (m_DraggingCurveOrRegion != null) { // Curve/Region dragging only in y axis direction (for now) delta.x = 0; motion = ViewToDrawingTransformVector(delta); motion.y = SnapValue(motion.y + s_StartKeyDragPosition.y) - s_StartKeyDragPosition.y; } else { switch (m_AxisLock) { case AxisLock.None: motion = ViewToDrawingTransformVector(delta); motion.x = SnapTime(motion.x + s_StartKeyDragPosition.x) - s_StartKeyDragPosition.x; motion.y = SnapValue(motion.y + s_StartKeyDragPosition.y) - s_StartKeyDragPosition.y; break; case AxisLock.X: delta.y = 0; motion = ViewToDrawingTransformVector(delta); motion.x = SnapTime(motion.x + s_StartKeyDragPosition.x) - s_StartKeyDragPosition.x; break; case AxisLock.Y: delta.x = 0; motion = ViewToDrawingTransformVector(delta); motion.y = SnapValue(motion.y + s_StartKeyDragPosition.y) - s_StartKeyDragPosition.y; break; } } m_RectangleTool.OnMove(s_StartMouseDragPosition + motion); GUI.changed = true; evt.Use(); return motion; } break; case EventType.KeyDown: if (GUIUtility.hotControl == id && evt.keyCode == KeyCode.Escape) { TranslateSelectedKeys(Vector2.zero); ResetDragging(); GUI.changed = true; evt.Use(); } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { m_RectangleTool.OnEndMove(); ResetDragging(); GUI.changed = true; evt.Use(); } break; case EventType.Repaint: Rect mouseRect = new Rect(evt.mousePosition.x - 10, evt.mousePosition.y - 10, 20, 20); if (m_DraggingCurveOrRegion != null) EditorGUIUtility.AddCursorRect(mouseRect, MouseCursor.ResizeVertical); else if (m_DraggingKey != null) EditorGUIUtility.AddCursorRect(mouseRect, MouseCursor.MoveArrow); break; } return Vector2.zero; } void ResetDragging() { // If we are dragging entire curve we have selected all keys we therefore ensure to deselect them again... if (m_DraggingCurveOrRegion != null) { selectedCurves = preCurveDragSelection; preCurveDragSelection = null; } // Cleanup GUIUtility.hotControl = 0; m_DraggingKey = null; m_DraggingCurveOrRegion = null; m_MoveCoord = Vector2.zero; m_AxisLock = AxisLock.None; } void MakeCurveBackups() { SaveKeySelection(k_EditCurve); m_CurveBackups = new List(); int lastCurveID = -1; SavedCurve sc = null; for (int i = 0; i < selectedCurves.Count; i++) { CurveSelection cs = selectedCurves[i]; // if it's a different curve than last point, we need to back up this curve. if (cs.curveID != lastCurveID) { AnimationCurve curve = GetCurveFromSelection(cs); if (curve != null) { // Make a new saved curve with copy of all keyframes. No need to mark them as selected sc = new SavedCurve(); lastCurveID = sc.curveId = cs.curveID; Keyframe[] keys = curve.keys; sc.keys = new List(keys.Length); foreach (Keyframe k in keys) sc.keys.Add(new SavedCurve.SavedKeyFrame(k, CurveWrapper.SelectionMode.None)); m_CurveBackups.Add(sc); } } // Mark them as selected sc.keys[cs.key].selected = cs.semiSelected ? CurveWrapper.SelectionMode.SemiSelected : CurveWrapper.SelectionMode.Selected; } } public void SaveKeySelection(string undoLabel) { if (settings.undoRedoSelection) Undo.RegisterCompleteObjectUndo(selection, undoLabel); } // Get the position of a CurveSelection. This will correctly offset tangent handles Vector2 GetPosition(CurveSelection selection) { AnimationCurve curve = GetCurveFromSelection(selection); Keyframe key = curve[selection.key]; Vector2 position = new Vector2(key.time, key.value); float tangentLength = 50F; if (selection.type == CurveSelection.SelectionType.InTangent) { Vector2 dir = new Vector2(1.0F, key.inTangent); if (key.inTangent == Mathf.Infinity) dir = new Vector2(0, -1); else if (key.inTangent == -Mathf.Infinity) dir = new Vector2(0, 1); Vector2 viewDir = NormalizeInViewSpace(dir); if ((key.weightedMode & WeightedMode.In) == WeightedMode.In) { Keyframe prevKey = (selection.key >= 1 && selection.key < curve.length) ? curve[selection.key - 1] : key; float dx = key.time - prevKey.time; Vector2 bezierDir = dir * dx * key.inWeight; if (viewDir.magnitude * 10F < bezierDir.magnitude) return position - bezierDir; else return position - viewDir * 10F; } else { return position - viewDir * tangentLength; } } else if (selection.type == CurveSelection.SelectionType.OutTangent) { Vector2 dir = new Vector2(1.0F, key.outTangent); if (key.outTangent == Mathf.Infinity) dir = new Vector2(0, -1); else if (key.outTangent == -Mathf.Infinity) dir = new Vector2(0, 1); Vector2 viewDir = NormalizeInViewSpace(dir); if ((key.weightedMode & WeightedMode.Out) == WeightedMode.Out) { Keyframe nextKey = (selection.key >= 0 && selection.key < (curve.length - 1)) ? curve[selection.key + 1] : key; float dx = nextKey.time - key.time; Vector2 bezierDir = dir * dx * key.outWeight; if (viewDir.magnitude * 10F < bezierDir.magnitude) return position + bezierDir; else return position + viewDir * 10F; } else { return position + viewDir * tangentLength; } } else return position; } void MoveCurveToFront(int curveID) { int numCurves = m_DrawOrder.Count; for (int i = 0; i < numCurves; ++i) { // Find curveID in draw order list if (m_DrawOrder[i] == curveID) { // If curve is part of a region then find other curve int regionId = GetCurveWrapperFromID(curveID).regionId; if (regionId >= 0) { // The other region curve can be on either side of current int indexOffset = 0; int curveID2 = -1; if (i - 1 >= 0) { int id = m_DrawOrder[i - 1]; if (regionId == GetCurveWrapperFromID(id).regionId) { curveID2 = id; indexOffset = -1; } } if (i + 1 < numCurves && curveID2 < 0) { int id = m_DrawOrder[i + 1]; if (regionId == GetCurveWrapperFromID(id).regionId) { curveID2 = id; indexOffset = 0; } } if (curveID2 >= 0) { m_DrawOrder.RemoveRange(i + indexOffset, 2); m_DrawOrder.Add(curveID2); m_DrawOrder.Add(curveID); // ensure curveID is topMost (last) ValidateCurveList(); return; } Debug.LogError("Unhandled region"); } else // Single curve { if (i == numCurves - 1) return; // curve already last (topmost) m_DrawOrder.RemoveAt(i); m_DrawOrder.Add(curveID); ValidateCurveList(); return; } } } } bool IsCurveSelected(CurveWrapper cw) { if (cw != null) return cw.selected != CurveWrapper.SelectionMode.None; return false; } bool IsRegionCurveSelected(CurveWrapper cw1, CurveWrapper cw2) { return IsCurveSelected(cw1) || IsCurveSelected(cw2); } bool IsRegion(CurveWrapper cw1, CurveWrapper cw2) { if (cw1 != null && cw2 != null) if (cw1.regionId >= 0) return cw1.regionId == cw2.regionId; return false; } bool IsLeftTangentEditable(CurveSelection selection) { AnimationCurve curve = GetCurveFromSelection(selection); if (curve == null) return false; if (selection.key < 1 || selection.key >= curve.length) return false; Keyframe keyframe = curve[selection.key]; TangentMode mode = AnimationUtility.GetKeyLeftTangentMode(keyframe); // Tangent is already set to Free. if (mode == TangentMode.Free) return true; // If tangent is modified, it will be converted to Free. if (mode == TangentMode.ClampedAuto || mode == TangentMode.Auto) return true; return false; } bool IsRightTangentEditable(CurveSelection selection) { AnimationCurve curve = GetCurveFromSelection(selection); if (curve == null) return false; if (selection.key < 0 || selection.key >= (curve.length - 1)) return false; Keyframe keyframe = curve[selection.key]; TangentMode mode = AnimationUtility.GetKeyRightTangentMode(keyframe); // Tangent is already set to Free. if (mode == TangentMode.Free) return true; // If tangent is modified, it will be converted to Free. if (mode == TangentMode.ClampedAuto || mode == TangentMode.Auto) return true; return false; } void DrawCurvesAndRegion(CurveWrapper cw1, CurveWrapper cw2, List selection, bool hasFocus) { DrawRegion(cw1, cw2, hasFocus); DrawCurveAndPoints(cw1, IsCurveSelected(cw1) ? selection : null, hasFocus); DrawCurveAndPoints(cw2, IsCurveSelected(cw2) ? selection : null, hasFocus); } void DrawCurveAndPoints(CurveWrapper cw, List selection, bool hasFocus) { DrawCurve(cw, hasFocus); DrawPointsOnCurve(cw, selection, hasFocus); } bool ShouldCurveHaveFocus(int indexIntoDrawOrder, CurveWrapper cw1, CurveWrapper cw2) { bool focus = false; if (indexIntoDrawOrder == m_DrawOrder.Count - 1) focus = true; else if (hasSelection) focus = IsCurveSelected(cw1) || IsCurveSelected(cw2); return focus; } void DrawCurves() { if (Event.current.type != EventType.Repaint) return; m_PointRenderer.Clear(); // Draw all curves for (int i = 0; i < m_DrawOrder.Count; ++i) { CurveWrapper cw = GetCurveWrapperFromID(m_DrawOrder[i]); if (cw == null) continue; if (cw.hidden) continue; if (cw.curve.length == 0) continue; CurveWrapper cw2 = null; if (i < m_DrawOrder.Count - 1) cw2 = GetCurveWrapperFromID(m_DrawOrder[i + 1]); if (IsRegion(cw, cw2)) { i++; // we handle two curves bool focus = ShouldCurveHaveFocus(i, cw, cw2); DrawCurvesAndRegion(cw, cw2, IsRegionCurveSelected(cw, cw2) ? selectedCurves : null, focus); } else { bool focus = ShouldCurveHaveFocus(i, cw, null); DrawCurveAndPoints(cw, IsCurveSelected(cw) ? selectedCurves : null, focus); } } m_PointRenderer.Render(); } void DrawCurvesTangents() { if (m_DraggingCurveOrRegion != null) return; // Draw left and right tangents lines HandleUtility.ApplyWireMaterial(); GL.Begin(GL.LINES); GL.Color(m_TangentColor * new Color(1, 1, 1, 0.75f)); for (int i = 0; i < selectedCurves.Count; ++i) { CurveSelection sel = selectedCurves[i]; if (sel.semiSelected) continue; CurveWrapper curveWrapper = GetCurveWrapperFromSelection(sel); if (curveWrapper == null) continue; AnimationCurve curve = curveWrapper.curve; if (curve == null) continue; if (curve.length == 0) continue; if (sel.key < 0 || sel.key >= curve.length) continue; Vector2 keyPoint = GetPosition(sel); if (IsLeftTangentEditable(sel) && GetKeyframeFromSelection(sel).time != curve.keys[0].time) { Vector2 leftTangent = GetPosition(new CurveSelection(sel.curveID, sel.key, CurveSelection.SelectionType.InTangent)); DrawLine(leftTangent, keyPoint); } if (IsRightTangentEditable(sel) && GetKeyframeFromSelection(sel).time != curve.keys[curve.keys.Length - 1].time) { Vector2 rightTangent = GetPosition(new CurveSelection(sel.curveID, sel.key, CurveSelection.SelectionType.OutTangent)); DrawLine(keyPoint, rightTangent); } } GL.End(); m_PointRenderer.Clear(); // Draw left and right tangents handles for (int i = 0; i < selectedCurves.Count; ++i) { CurveSelection sel = selectedCurves[i]; if (sel.semiSelected) continue; CurveWrapper curveWrapper = GetCurveWrapperFromSelection(sel); if (curveWrapper == null) continue; AnimationCurve curve = curveWrapper.curve; if (curve == null) continue; if (curve.length == 0) continue; if (IsLeftTangentEditable(sel) && GetKeyframeFromSelection(sel).time != curve.keys[0].time) { Vector2 leftTangent = DrawingToViewTransformPoint(GetPosition(new CurveSelection(sel.curveID, sel.key, CurveSelection.SelectionType.InTangent))); DrawTangentPoint(leftTangent, (GetKeyframeFromSelection(sel).weightedMode & WeightedMode.In) == WeightedMode.In); } if (IsRightTangentEditable(sel) && GetKeyframeFromSelection(sel).time != curve.keys[curve.keys.Length - 1].time) { Vector2 rightTangent = DrawingToViewTransformPoint(GetPosition(new CurveSelection(sel.curveID, sel.key, CurveSelection.SelectionType.OutTangent))); DrawTangentPoint(rightTangent, (GetKeyframeFromSelection(sel).weightedMode & WeightedMode.Out) == WeightedMode.Out); } } m_PointRenderer.Render(); } void DrawCurvesOverlay() { if (m_DraggingCurveOrRegion != null) return; // Draw label with values while dragging if (m_DraggingKey != null && settings.rectangleToolFlags == RectangleToolFlags.NoRectangleTool) { GUI.color = Color.white; // Clamp only using the currently dragged curve (we could have more selected but we only show the coord info for this one). float smallestVRangeMin = vRangeMin; float smallestVRangeMax = vRangeMax; smallestVRangeMin = Mathf.Max(smallestVRangeMin, m_DraggingKey.vRangeMin); smallestVRangeMax = Mathf.Min(smallestVRangeMax, m_DraggingKey.vRangeMax); Vector2 newPoint = m_DraggedCoord + m_MoveCoord; newPoint.x = Mathf.Clamp(newPoint.x, hRangeMin, hRangeMax); newPoint.y = Mathf.Clamp(newPoint.y, smallestVRangeMin, smallestVRangeMax); Vector2 p = DrawingToViewTransformPoint(newPoint); Vector2 axisUiScalars = m_DraggingKey.getAxisUiScalarsCallback != null ? m_DraggingKey.getAxisUiScalarsCallback() : Vector2.one; if (axisUiScalars.x >= 0f) newPoint.x *= axisUiScalars.x; if (axisUiScalars.y >= 0f) newPoint.y *= axisUiScalars.y; GUIContent content = new GUIContent(string.Format("{0}, {1}", FormatTime(newPoint.x, invSnap, timeFormat), FormatValue(newPoint.y))); Vector2 size = Styles.dragLabel.CalcSize(content); EditorGUI.DoDropShadowLabel( new Rect(p.x, p.y - size.y, size.x, size.y), content, Styles.dragLabel, 0.3f ); } } List CreateRegion(CurveWrapper minCurve, CurveWrapper maxCurve, float deltaTime) { var range = settings.curveRegionDomain; // Create list of triangle points List region = new List(); List sampleTimes = new List(); float sampleTime = range.x + deltaTime; for (; sampleTime <= range.y; sampleTime += deltaTime) sampleTimes.Add(sampleTime); if (sampleTime != range.y) sampleTimes.Add(range.y); // To handle constant curves (high gradient) we add key time samples on both side of the keys as well // the key time itself. Keyframe[] maxKeys = maxCurve.curve.keys; for (int i = 0; i < maxKeys.Length; ++i) { Keyframe key = maxKeys[i]; if (key.time > range.x && key.time < range.y) { sampleTimes.Add(key.time - 0.0001f); sampleTimes.Add(key.time); sampleTimes.Add(key.time + 0.0001f); } } Keyframe[] minKeys = minCurve.curve.keys; for (int i = 0; i < minKeys.Length; ++i) { Keyframe key = minKeys[i]; if (key.time > range.x && key.time < range.y) { sampleTimes.Add(key.time - 0.0001f); sampleTimes.Add(key.time); sampleTimes.Add(key.time + 0.0001f); } } sampleTimes.Sort(); Vector3 prevA = new Vector3(range.x, maxCurve.renderer.EvaluateCurveSlow(0.0f), 0.0f); Vector3 prevB = new Vector3(range.x, minCurve.renderer.EvaluateCurveSlow(0.0f), 0.0f); Vector3 screenPrevA = drawingToViewMatrix.MultiplyPoint(prevA); Vector3 screenPrevB = drawingToViewMatrix.MultiplyPoint(prevB); for (int i = 0; i < sampleTimes.Count; ++i) { float time = sampleTimes[i]; Vector3 valueA = new Vector3(time, maxCurve.renderer.EvaluateCurveSlow(time), 0.0f); Vector3 valueB = new Vector3(time, minCurve.renderer.EvaluateCurveSlow(time), 0.0f); Vector3 screenValueA = drawingToViewMatrix.MultiplyPoint(valueA); Vector3 screenValueB = drawingToViewMatrix.MultiplyPoint(valueB); // Add triangles if (prevA.y >= prevB.y && valueA.y >= valueB.y) { // max is top region.Add(screenPrevA); region.Add(screenValueB); region.Add(screenPrevB); region.Add(screenPrevA); region.Add(screenValueA); region.Add(screenValueB); } else if (prevA.y <= prevB.y && valueA.y <= valueB.y) { // min is top region.Add(screenPrevB); region.Add(screenValueA); region.Add(screenPrevA); region.Add(screenPrevB); region.Add(screenValueB); region.Add(screenValueA); } else { // Find intersection Vector2 intersection = Vector2.zero; if (Mathf.LineIntersection(screenPrevA, screenValueA, screenPrevB, screenValueB, ref intersection)) { region.Add(screenPrevA); region.Add(intersection); region.Add(screenPrevB); region.Add(screenValueA); region.Add(intersection); region.Add(screenValueB); } else { Debug.Log("Error: No intersection found! There should be one..."); } } prevA = valueA; prevB = valueB; screenPrevA = screenValueA; screenPrevB = screenValueB; } return region; } public void DrawRegion(CurveWrapper curve1, CurveWrapper curve2, bool hasFocus) { if (Event.current.type != EventType.Repaint) return; float deltaTime = 1.0f / (rect.width / 10.0f); List points = CreateRegion(curve1, curve2, deltaTime); Color color = curve1.color; if (IsDraggingRegion(curve1, curve2)) { color = Color.Lerp(color, Color.black, 0.1f); color.a = 0.4f; } else if (settings.useFocusColors && !hasFocus) { color *= 0.4f; color.a = 0.1f; } else { color *= 1.0f; color.a = 0.4f; } Shader.SetGlobalColor("_HandleColor", color); HandleUtility.ApplyWireMaterial(); GL.Begin(GL.TRIANGLES); int numTriangles = points.Count / 3; for (int i = 0; i < numTriangles; i++) { GL.Color(color); GL.Vertex(points[i * 3]); GL.Vertex(points[i * 3 + 1]); GL.Vertex(points[i * 3 + 2]); } GL.End(); } void DrawCurve(CurveWrapper cw, bool hasFocus) { CurveRenderer renderer = cw.renderer; Color color = cw.color; if (IsDraggingCurve(cw) || cw.selected == CurveWrapper.SelectionMode.Selected) { color = Color.Lerp(color, Color.white, 0.3f); } else if (settings.useFocusColors && !hasFocus) { color *= 0.5f; color.a = 0.8f; } Rect framed = shownArea; renderer.DrawCurve(framed.xMin, framed.xMax, color, drawingToViewMatrix, settings.wrapColor * cw.wrapColorMultiplier); } void DrawPointsOnCurve(CurveWrapper cw, List selected, bool hasFocus) { if (selected == null) { Color color = cw.color; if (settings.useFocusColors && !hasFocus) color *= 0.5f; GUI.color = color; Keyframe[] keys = cw.curve.keys; for (int i = 0; i < keys.Length; ++i) { Keyframe k = keys[i]; DrawPoint(DrawingToViewTransformPoint(new Vector2(k.time, k.value)), CurveWrapper.SelectionMode.None); } } else { Color keyColor = Color.Lerp(cw.color, Color.white, .2f); GUI.color = keyColor; int selectionIdx = 0; // Find the point in selected curves that matches the curve we're about to draw. while (selectionIdx < selected.Count && selected[selectionIdx].curveID != cw.id) selectionIdx++; // we're now at the right point in the selection. int pointIdx = 0; Keyframe[] keys = cw.curve.keys; for (int i = 0; i < keys.Length; ++i) { Keyframe k = keys[i]; if (selectionIdx < selected.Count && selected[selectionIdx].key == pointIdx && selected[selectionIdx].curveID == cw.id) { if (selected[selectionIdx].semiSelected) DrawPoint(DrawingToViewTransformPoint(new Vector2(k.time, k.value)), CurveWrapper.SelectionMode.SemiSelected); else DrawPoint(DrawingToViewTransformPoint(new Vector2(k.time, k.value)), CurveWrapper.SelectionMode.Selected, settings.rectangleToolFlags == RectangleToolFlags.NoRectangleTool ? MouseCursor.MoveArrow : MouseCursor.Arrow); selectionIdx++; } else DrawPoint(DrawingToViewTransformPoint(new Vector2(k.time, k.value)), CurveWrapper.SelectionMode.None); pointIdx++; } GUI.color = Color.white; } } void DrawPoint(Vector2 viewPos, CurveWrapper.SelectionMode selected) { DrawPoint(viewPos, selected, MouseCursor.Arrow); } void DrawPoint(Vector2 viewPos, CurveWrapper.SelectionMode selected, MouseCursor mouseCursor) { // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. var rect = new Rect(Mathf.Floor(viewPos.x) - Styles.pointIconCenterOffsetX, Mathf.Floor(viewPos.y) - Styles.pointIconCenterOffsetY, Styles.pointIconSize, Styles.pointIconSize); if (selected == CurveWrapper.SelectionMode.None) { m_PointRenderer.AddPoint(rect, GUI.color); } else { if (selected == CurveWrapper.SelectionMode.Selected) m_PointRenderer.AddSelectedPoint(rect, GUI.color); else m_PointRenderer.AddSemiSelectedPoint(rect, GUI.color); } // Changing the cursor for every point in the selection can be awfully costly. if (mouseCursor != MouseCursor.Arrow) { if (GUIUtility.hotControl == 0) EditorGUIUtility.AddCursorRect(rect, mouseCursor); } } void DrawTangentPoint(Vector2 viewPos, bool weighted) { // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. var rect = new Rect(Mathf.Floor(viewPos.x) - Styles.pointIconCenterOffsetX, Mathf.Floor(viewPos.y) - Styles.pointIconCenterOffsetY, Styles.pointIconSize, Styles.pointIconSize); if (weighted) { m_PointRenderer.AddWeightedPoint(rect, m_WeightedTangentColor); } else { m_PointRenderer.AddPoint(rect, m_TangentColor); } } // FIXME remove when grid drawing function has been properly rewritten void DrawLine(Vector2 lhs, Vector2 rhs) { GL.Vertex(DrawingToViewTransformPoint(new Vector3(lhs.x, lhs.y, 0))); GL.Vertex(DrawingToViewTransformPoint(new Vector3(rhs.x, rhs.y, 0))); } void DrawWrapperPopups() { if (!settings.showWrapperPopups) return; int curveId; GetTopMostCurveID(out curveId); if (curveId == -1) return; CurveWrapper wrapper = GetCurveWrapperFromID(curveId); AnimationCurve curve = wrapper.curve; if (curve != null && curve.length >= 2 && curve.preWrapMode != WrapMode.Default) { GUI.BeginGroup(drawRect); Color oldText = GUI.contentColor; var preKey = curve.keys[0]; var preWrap = curve.preWrapMode; preWrap = WrapModeIconPopup(preKey, preWrap, -1.5f); if (preWrap != curve.preWrapMode) { curve.preWrapMode = preWrap; wrapper.changed = true; } var postKey = curve.keys[curve.length - 1]; var postWrap = curve.postWrapMode; postWrap = WrapModeIconPopup(postKey, postWrap, 0.5f); if (postWrap != curve.postWrapMode) { curve.postWrapMode = postWrap; wrapper.changed = true; } if (wrapper.changed) { wrapper.renderer.SetWrap(curve.preWrapMode, curve.postWrapMode); if (curvesUpdated != null) curvesUpdated(); } GUI.contentColor = oldText; GUI.EndGroup(); } } WrapMode WrapModeIconPopup(Keyframe key, WrapMode oldWrap, float hOffset) { float buttonSize = Styles.wrapModeMenuIcon.image.width; var keyPosition = new Vector3(key.time, key.value); keyPosition = DrawingToViewTransformPoint(keyPosition); var r = new Rect(keyPosition.x + buttonSize * hOffset, keyPosition.y, buttonSize, buttonSize); var selectedValue = (WrapModeFixedCurve)oldWrap; // EnumPopupInternal begins // sa and values are sorted in the same order Enum[] enumValues = Enum.GetValues(typeof(WrapModeFixedCurve)).Cast().ToArray(); var stringNames = Enum.GetNames(typeof(WrapModeFixedCurve)).Select(x => ObjectNames.NicifyVariableName(x)).ToArray(); int i = Array.IndexOf(enumValues, selectedValue); // PopupInternal begins int controlID = GUIUtility.GetControlID("WrapModeIconPopup".GetHashCode(), FocusType.Keyboard, r); // DoPopup begins var selectedPopupIndex = EditorGUI.PopupCallbackInfo.GetSelectedValueForControl(controlID, i); var popupStrings = EditorGUIUtility.TempContent(stringNames); Event evt = Event.current; switch (evt.type) { case EventType.Repaint: GUIStyle.none.Draw(r, Styles.wrapModeMenuIcon, controlID, false); break; case EventType.MouseDown: if (evt.button == 0 && r.Contains(evt.mousePosition)) { if (Application.platform == RuntimePlatform.OSXEditor) { r.y = r.y - selectedPopupIndex * 16 - 19; } EditorGUI.PopupCallbackInfo.instance = new EditorGUI.PopupCallbackInfo(controlID); EditorUtility.DisplayCustomMenu(r, popupStrings, selectedPopupIndex, EditorGUI.PopupCallbackInfo.instance.SetEnumValueDelegate, null); GUIUtility.keyboardControl = controlID; evt.Use(); } break; case EventType.KeyDown: if (evt.MainActionKeyForControl(controlID)) { if (Application.platform == RuntimePlatform.OSXEditor) { r.y = r.y - selectedPopupIndex * 16 - 19; } EditorGUI.PopupCallbackInfo.instance = new EditorGUI.PopupCallbackInfo(controlID); EditorUtility.DisplayCustomMenu(r, popupStrings, selectedPopupIndex, EditorGUI.PopupCallbackInfo.instance.SetEnumValueDelegate, null); evt.Use(); } break; } return (WrapMode)enumValues[selectedPopupIndex]; } // The return value for each axis can be -1, if so then we do not have any proper value // to use. private Vector2 GetAxisUiScalars(List curvesWithSameParameterSpace) { // If none or just one selected curve then use top most rendered curve value if (selectedCurves.Count <= 1) { if (m_DrawOrder.Count > 0) { CurveWrapper cw = GetCurveWrapperFromID(m_DrawOrder[m_DrawOrder.Count - 1]); if (cw != null && cw.getAxisUiScalarsCallback != null) { // Save list if (curvesWithSameParameterSpace != null) curvesWithSameParameterSpace.Add(cw); return cw.getAxisUiScalarsCallback(); } } return Vector2.one; } // If multiple curves selected we have to check if they are in the same value space Vector2 axisUiScalars = new Vector2(-1, -1); if (selectedCurves.Count > 1) { // Find common axis scalars if more than one key selected bool xAllSame = true; bool yAllSame = true; Vector2 scalars = Vector2.one; for (int i = 0; i < selectedCurves.Count; ++i) { CurveWrapper cw = GetCurveWrapperFromSelection(selectedCurves[i]); if (cw == null) continue; if (cw.getAxisUiScalarsCallback != null) { Vector2 temp = cw.getAxisUiScalarsCallback(); if (i == 0) { scalars = temp; // init scalars } else { if (Mathf.Abs(temp.x - scalars.x) > 0.00001f) xAllSame = false; if (Mathf.Abs(temp.y - scalars.y) > 0.00001f) yAllSame = false; scalars = temp; } // Save list if (curvesWithSameParameterSpace != null) curvesWithSameParameterSpace.Add(cw); } } if (xAllSame) axisUiScalars.x = scalars.x; if (yAllSame) axisUiScalars.y = scalars.y; } return axisUiScalars; } private void SetAxisUiScalars(Vector2 newScalars, List curvesInSameSpace) { foreach (CurveWrapper cw in curvesInSameSpace) { // Only set valid values (-1 indicate invalid value, if so use original value) Vector2 scalar = cw.getAxisUiScalarsCallback(); if (newScalars.x >= 0) scalar.x = newScalars.x; if (newScalars.y >= 0) scalar.y = newScalars.y; if (cw.setAxisUiScalarsCallback != null) cw.setAxisUiScalarsCallback(scalar); } } internal enum PickMode { None, Click, Marquee } public void GridGUI() { if (Event.current.type != EventType.Repaint) return; GUI.BeginClip(drawRect); Color tempCol = GUI.color; // Get axis scalars Vector2 axisUiScalars = GetAxisUiScalars(null); // Cache framed area rect as fetching the property takes some calculations Rect shownRect = shownArea; hTicks.SetRanges(shownRect.xMin * axisUiScalars.x, shownRect.xMax * axisUiScalars.x, drawRect.xMin, drawRect.xMax); vTicks.SetRanges(shownRect.yMin * axisUiScalars.y, shownRect.yMax * axisUiScalars.y, drawRect.yMin, drawRect.yMax); // Draw time markers of various strengths HandleUtility.ApplyWireMaterial(); GL.Begin(GL.LINES); float lineStart, lineEnd; hTicks.SetTickStrengths(settings.hTickStyle.distMin, settings.hTickStyle.distFull, false); if (settings.hTickStyle.stubs) { lineStart = shownRect.yMin; lineEnd = shownRect.yMin - 40 / scale.y; } else { lineStart = Mathf.Max(shownRect.yMin, vRangeMin); lineEnd = Mathf.Min(shownRect.yMax, vRangeMax); } for (int l = 0; l < hTicks.tickLevels; l++) { float strength = hTicks.GetStrengthOfLevel(l); if (strength > 0f) { GL.Color(settings.hTickStyle.tickColor * new Color(1, 1, 1, strength) * new Color(1, 1, 1, 0.75f)); float[] ticks = hTicks.GetTicksAtLevel(l, true); for (int j = 0; j < ticks.Length; j++) { ticks[j] /= axisUiScalars.x; if (ticks[j] > hRangeMin && ticks[j] < hRangeMax) DrawLine(new Vector2(ticks[j], lineStart), new Vector2(ticks[j], lineEnd)); } } } // Draw bounds of allowed range GL.Color(settings.hTickStyle.tickColor * new Color(1, 1, 1, 1) * new Color(1, 1, 1, 0.75f)); if (hRangeMin != Mathf.NegativeInfinity) DrawLine(new Vector2(hRangeMin, lineStart), new Vector2(hRangeMin, lineEnd)); if (hRangeMax != Mathf.Infinity) DrawLine(new Vector2(hRangeMax, lineStart), new Vector2(hRangeMax, lineEnd)); vTicks.SetTickStrengths(settings.vTickStyle.distMin, settings.vTickStyle.distFull, false); if (settings.vTickStyle.stubs) { lineStart = shownRect.xMin; lineEnd = shownRect.xMin + 40 / scale.x; } else { lineStart = Mathf.Max(shownRect.xMin, hRangeMin); lineEnd = Mathf.Min(shownRect.xMax, hRangeMax); } // Draw value markers of various strengths for (int l = 0; l < vTicks.tickLevels; l++) { float strength = vTicks.GetStrengthOfLevel(l); if (strength > 0f) { GL.Color(settings.vTickStyle.tickColor * new Color(1, 1, 1, strength) * new Color(1, 1, 1, 0.75f)); float[] ticks = vTicks.GetTicksAtLevel(l, true); for (int j = 0; j < ticks.Length; j++) { ticks[j] /= axisUiScalars.y; if (ticks[j] > vRangeMin && ticks[j] < vRangeMax) DrawLine(new Vector2(lineStart, ticks[j]), new Vector2(lineEnd, ticks[j])); } } } // Draw bounds of allowed range GL.Color(settings.vTickStyle.tickColor * new Color(1, 1, 1, 1) * new Color(1, 1, 1, 0.75f)); if (vRangeMin != Mathf.NegativeInfinity) DrawLine(new Vector2(lineStart, vRangeMin), new Vector2(lineEnd, vRangeMin)); if (vRangeMax != Mathf.Infinity) DrawLine(new Vector2(lineStart, vRangeMax), new Vector2(lineEnd, vRangeMax)); GL.End(); if (settings.showAxisLabels) { // X Axis labels if (settings.hTickStyle.distLabel > 0 && axisUiScalars.x > 0f) { GUI.color = settings.hTickStyle.labelColor; int labelLevel = hTicks.GetLevelWithMinSeparation(settings.hTickStyle.distLabel); // Calculate how many decimals are needed to show the differences between the labeled ticks int decimals = MathUtils.GetNumberOfDecimalsForMinimumDifference(hTicks.GetPeriodOfLevel(labelLevel)); // now draw float[] ticks = hTicks.GetTicksAtLevel(labelLevel, false); float[] ticksPos = (float[])ticks.Clone(); float vpos = Mathf.Floor(drawRect.height); for (int i = 0; i < ticks.Length; i++) { ticksPos[i] /= axisUiScalars.x; if (ticksPos[i] < hRangeMin || ticksPos[i] > hRangeMax) continue; Vector2 pos = DrawingToViewTransformPoint(new Vector2(ticksPos[i], 0)); // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. pos = new Vector2(Mathf.Floor(pos.x), vpos); float uiValue = ticks[i]; Rect labelRect; TextAnchor wantedAlignment; if (settings.hTickStyle.centerLabel) { wantedAlignment = TextAnchor.UpperCenter; labelRect = new Rect(pos.x, pos.y - 16 - settings.hTickLabelOffset, 1, 16); } else { wantedAlignment = TextAnchor.UpperLeft; labelRect = new Rect(pos.x, pos.y - 16 - settings.hTickLabelOffset, 50, 16); } if (Styles.labelTickMarksX.alignment != wantedAlignment) Styles.labelTickMarksX.alignment = wantedAlignment; GUI.Label(labelRect, uiValue.ToString("n" + decimals) + settings.hTickStyle.unit, Styles.labelTickMarksX); } } // Y Axis labels if (settings.vTickStyle.distLabel > 0 && axisUiScalars.y > 0f) { // Draw value labels GUI.color = settings.vTickStyle.labelColor; int labelLevel = vTicks.GetLevelWithMinSeparation(settings.vTickStyle.distLabel); float[] ticks = vTicks.GetTicksAtLevel(labelLevel, false); float[] ticksPos = (float[])ticks.Clone(); // Calculate how many decimals are needed to show the differences between the labeled ticks int decimals = MathUtils.GetNumberOfDecimalsForMinimumDifference(vTicks.GetPeriodOfLevel(labelLevel)); string format = "n" + decimals; m_AxisLabelFormat = format; // Calculate the size of the biggest shown label float labelSize = 35; if (!settings.vTickStyle.stubs && ticks.Length > 1) { float min = ticks[1]; float max = ticks[ticks.Length - 1]; string minNumber = min.ToString(format) + settings.vTickStyle.unit; string maxNumber = max.ToString(format) + settings.vTickStyle.unit; labelSize = Mathf.Max( Styles.labelTickMarksY.CalcSize(new GUIContent(minNumber)).x, Styles.labelTickMarksY.CalcSize(new GUIContent(maxNumber)).x ) + 6; } // Now draw for (int i = 0; i < ticks.Length; i++) { ticksPos[i] /= axisUiScalars.y; if (ticksPos[i] < vRangeMin || ticksPos[i] > vRangeMax) continue; Vector2 pos = DrawingToViewTransformPoint(new Vector2(0, ticksPos[i])); // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. pos = new Vector2(pos.x, Mathf.Floor(pos.y)); float uiValue = ticks[i]; Rect labelRect; if (settings.vTickStyle.centerLabel) labelRect = new Rect(0, pos.y - 8, leftmargin - 4, 16); // text expands to the left starting from where grid starts (leftmargin size must ensure text is visible) else labelRect = new Rect(0, pos.y - 13, labelSize, 16); // text expands to the right starting from left side of window GUI.Label(labelRect, uiValue.ToString(format) + settings.vTickStyle.unit, Styles.labelTickMarksY); } } } // Cleanup GUI.color = tempCol; GUI.EndClip(); } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveEditorRectangleTool.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using System.Linq; using RectangleToolFlags = UnityEditor.CurveEditorSettings.RectangleToolFlags; namespace UnityEditor { internal class CurveEditorRectangleTool : RectangleTool { const int kHBarMinWidth = 14; const int kHBarHeight = 13; const int kHBarLeftWidth = 15; const int kHBarLeftHeight = 13; const int kHBarRightWidth = 15; const int kHBarRightHeight = 13; const int kHLabelMarginHorizontal = 3; const int kHLabelMarginVertical = 1; const int kVBarMinHeight = 15; const int kVBarWidth = 13; const int kVBarBottomWidth = 13; const int kVBarBottomHeight = 15; const int kVBarTopWidth = 13; const int kVBarTopHeight = 15; const int kVLabelMarginHorizontal = 1; const int kVLabelMarginVertical = 2; const int kScaleLeftWidth = 17; const int kScaleLeftMarginHorizontal = 0; const float kScaleLeftRatio = 0.80f; const int kScaleRightWidth = 17; const int kScaleRightMarginHorizontal = 0; const float kScaleRightRatio = 0.80f; const int kScaleBottomHeight = 17; const int kScaleBottomMarginVertical = 0; const float kScaleBottomRatio = 0.80f; const int kScaleTopHeight = 17; const int kScaleTopMarginVertical = 0; const float kScaleTopRatio = 0.80f; static Rect g_EmptyRect = new Rect(0f, 0f, 0f, 0f); struct ToolLayout { public Rect selectionRect; public Rect hBarRect; public Rect hBarLeftRect; public Rect hBarRightRect; public bool displayHScale; public Rect vBarRect; public Rect vBarBottomRect; public Rect vBarTopRect; public bool displayVScale; public Rect selectionLeftRect; public Rect selectionTopRect; public Rect underlayTopRect; public Rect underlayLeftRect; public Rect scaleLeftRect; public Rect scaleRightRect; public Rect scaleTopRect; public Rect scaleBottomRect; public Vector2 leftLabelAnchor; public Vector2 rightLabelAnchor; public Vector2 bottomLabelAnchor; public Vector2 topLabelAnchor; } private CurveEditor m_CurveEditor; private ToolLayout m_Layout; private Vector2 m_Pivot; private Vector2 m_Previous; private Vector2 m_MouseOffset; enum DragMode { None = 0, MoveHorizontal = 1 << 0, MoveVertical = 1 << 1, MoveBothAxis = MoveHorizontal | MoveVertical, ScaleHorizontal = 1 << 2, ScaleVertical = 1 << 3, ScaleBothAxis = ScaleHorizontal | ScaleVertical, MoveScaleHorizontal = MoveHorizontal | ScaleHorizontal, MoveScaleVertical = MoveVertical | ScaleVertical } private DragMode m_DragMode; private bool m_RippleTime; private float m_RippleTimeStart; private float m_RippleTimeEnd; private AreaManipulator m_HBarLeft; private AreaManipulator m_HBarRight; private AreaManipulator m_HBar; private AreaManipulator m_VBarBottom; private AreaManipulator m_VBarTop; private AreaManipulator m_VBar; private AreaManipulator m_SelectionBox; private AreaManipulator m_SelectionScaleLeft; private AreaManipulator m_SelectionScaleRight; private AreaManipulator m_SelectionRippleLeft; private AreaManipulator m_SelectionRippleRight; private AreaManipulator m_SelectionScaleBottom; private AreaManipulator m_SelectionScaleTop; private bool hasSelection { get { return m_CurveEditor.hasSelection && !m_CurveEditor.IsDraggingCurveOrRegion(); } } private Bounds selectionBounds { get { return m_CurveEditor.selectionBounds; } } private float frameRate { get { return m_CurveEditor.invSnap; } } private bool rippleTime { get { return m_CurveEditor.rippleTime; } } private DragMode dragMode { get { if (m_DragMode != DragMode.None) return m_DragMode; if (m_CurveEditor.IsDraggingKey()) return DragMode.MoveBothAxis; return DragMode.None; } } public override void Initialize(TimeArea timeArea) { base.Initialize(timeArea); m_CurveEditor = timeArea as CurveEditor; if (m_HBarLeft == null) { m_HBarLeft = new AreaManipulator(styles.rectangleToolHBarLeft, MouseCursor.ResizeHorizontal); m_HBarLeft.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Right, ToolCoord.Left, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.ScaleHorizontal, rippleTime); return true; } return false; }; m_HBarLeft.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_HBarLeft.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_HBarRight == null) { m_HBarRight = new AreaManipulator(styles.rectangleToolHBarRight, MouseCursor.ResizeHorizontal); m_HBarRight.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Left, ToolCoord.Right, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.ScaleHorizontal, rippleTime); return true; } return false; }; m_HBarRight.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_HBarRight.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_HBar == null) { m_HBar = new AreaManipulator(styles.rectangleToolHBar, MouseCursor.MoveArrow); m_HBar.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartMove(new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.MoveHorizontal, rippleTime); return true; } return false; }; m_HBar.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnMove(new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f)); return true; }; m_HBar.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndMove(); return true; }; } if (m_VBarBottom == null) { m_VBarBottom = new AreaManipulator(styles.rectangleToolVBarBottom, MouseCursor.ResizeVertical); m_VBarBottom.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Top, ToolCoord.Bottom, new Vector2(0f, PixelToValue(evt.mousePosition.y)), DragMode.ScaleVertical, false); return true; } return false; }; m_VBarBottom.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleValue(PixelToValue(evt.mousePosition.y)); return true; }; m_VBarBottom.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_VBarTop == null) { m_VBarTop = new AreaManipulator(styles.rectangleToolVBarTop, MouseCursor.ResizeVertical); m_VBarTop.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Bottom, ToolCoord.Top, new Vector2(0f, PixelToValue(evt.mousePosition.y)), DragMode.ScaleVertical, false); return true; } return false; }; m_VBarTop.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleValue(PixelToValue(evt.mousePosition.y)); return true; }; m_VBarTop.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_VBar == null) { m_VBar = new AreaManipulator(styles.rectangleToolVBar, MouseCursor.MoveArrow); m_VBar.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartMove(new Vector2(0f, PixelToValue(evt.mousePosition.y)), DragMode.MoveVertical, false); return true; } return false; }; m_VBar.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnMove(new Vector2(0f, PixelToValue(evt.mousePosition.y))); return true; }; m_VBar.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndMove(); return true; }; } if (m_SelectionBox == null) { m_SelectionBox = new AreaManipulator(styles.rectangleToolSelection, MouseCursor.MoveArrow); m_SelectionBox.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { bool curveEditorOverride = evt.shift || EditorGUI.actionKey; if (!curveEditorOverride && hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartMove(new Vector2(PixelToTime(evt.mousePosition.x, frameRate), PixelToValue(evt.mousePosition.y)), DragMode.MoveBothAxis, rippleTime); return true; } return false; }; m_SelectionBox.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { // Only drag along x OR y when shift is held down if (evt.shift && m_DragMode == DragMode.MoveBothAxis) { float deltaX = evt.mousePosition.x - TimeToPixel(m_Previous.x); float deltaY = evt.mousePosition.y - ValueToPixel(m_Previous.y); m_DragMode = Mathf.Abs(deltaX) > Mathf.Abs(deltaY) ? DragMode.MoveHorizontal : DragMode.MoveVertical; } float posX = ((m_DragMode & DragMode.MoveHorizontal) != 0) ? PixelToTime(evt.mousePosition.x, frameRate) : m_Previous.x; float posY = ((m_DragMode & DragMode.MoveVertical) != 0) ? PixelToValue(evt.mousePosition.y) : m_Previous.y; OnMove(new Vector2(posX, posY)); return true; }; m_SelectionBox.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndMove(); return true; }; } if (m_SelectionScaleLeft == null) { m_SelectionScaleLeft = new AreaManipulator(styles.rectangleToolScaleLeft, MouseCursor.ResizeHorizontal); m_SelectionScaleLeft.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Right, ToolCoord.Left, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.ScaleHorizontal, false); return true; } return false; }; m_SelectionScaleLeft.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionScaleLeft.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionScaleRight == null) { m_SelectionScaleRight = new AreaManipulator(styles.rectangleToolScaleRight, MouseCursor.ResizeHorizontal); m_SelectionScaleRight.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Left, ToolCoord.Right, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.ScaleHorizontal, false); return true; } return false; }; m_SelectionScaleRight.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionScaleRight.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionRippleLeft == null) { m_SelectionRippleLeft = new AreaManipulator(styles.rectangleToolRippleLeft, MouseCursor.ResizeHorizontal); m_SelectionRippleLeft.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Right, ToolCoord.Left, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.ScaleHorizontal, true); return true; } return false; }; m_SelectionRippleLeft.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionRippleLeft.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionRippleRight == null) { m_SelectionRippleRight = new AreaManipulator(styles.rectangleToolRippleRight, MouseCursor.ResizeHorizontal); m_SelectionRippleRight.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Left, ToolCoord.Right, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), DragMode.ScaleHorizontal, true); return true; } return false; }; m_SelectionRippleRight.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionRippleRight.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionScaleBottom == null) { m_SelectionScaleBottom = new AreaManipulator(styles.rectangleToolScaleBottom, MouseCursor.ResizeVertical); m_SelectionScaleBottom.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Top, ToolCoord.Bottom, new Vector2(0f, PixelToValue(evt.mousePosition.y)), DragMode.ScaleVertical, false); return true; } return false; }; m_SelectionScaleBottom.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleValue(PixelToValue(evt.mousePosition.y)); return true; }; m_SelectionScaleBottom.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionScaleTop == null) { m_SelectionScaleTop = new AreaManipulator(styles.rectangleToolScaleTop, MouseCursor.ResizeVertical); m_SelectionScaleTop.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Bottom, ToolCoord.Top, new Vector2(0f, PixelToValue(evt.mousePosition.y)), DragMode.ScaleVertical, false); return true; } return false; }; m_SelectionScaleTop.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleValue(PixelToValue(evt.mousePosition.y)); return true; }; m_SelectionScaleTop.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } } public void OnGUI() { if (!hasSelection) return; if (Event.current.type != EventType.Repaint) return; RectangleToolFlags flags = m_CurveEditor.settings.rectangleToolFlags; if (flags == RectangleToolFlags.NoRectangleTool) return; Color oldColor = GUI.color; GUI.color = Color.white; m_Layout = CalculateLayout(); if (flags == RectangleToolFlags.FullRectangleTool) { GUI.Label(m_Layout.selectionLeftRect, GUIContent.none, styles.rectangleToolHighlight); GUI.Label(m_Layout.selectionTopRect, GUIContent.none, styles.rectangleToolHighlight); GUI.Label(m_Layout.underlayLeftRect, GUIContent.none, styles.rectangleToolHighlight); GUI.Label(m_Layout.underlayTopRect, GUIContent.none, styles.rectangleToolHighlight); } m_SelectionBox.OnGUI(m_Layout.selectionRect); m_SelectionScaleTop.OnGUI(m_Layout.scaleTopRect); m_SelectionScaleBottom.OnGUI(m_Layout.scaleBottomRect); bool showRippleHandles = (rippleTime && dragMode == DragMode.None) || (m_RippleTime && dragMode != DragMode.None); if (showRippleHandles) { m_SelectionRippleLeft.OnGUI(m_Layout.scaleLeftRect); m_SelectionRippleRight.OnGUI(m_Layout.scaleRightRect); } else { m_SelectionScaleLeft.OnGUI(m_Layout.scaleLeftRect); m_SelectionScaleRight.OnGUI(m_Layout.scaleRightRect); } GUI.color = oldColor; } public void OverlayOnGUI(Rect bounds) { if (!hasSelection) return; if (Event.current.type != EventType.Repaint) return; Color oldColor = GUI.color; RectangleToolFlags flags = m_CurveEditor.settings.rectangleToolFlags; if (flags == RectangleToolFlags.FullRectangleTool) { GUI.color = Color.white; m_HBar.OnGUI(m_Layout.hBarRect); m_HBarLeft.OnGUI(m_Layout.hBarLeftRect); m_HBarRight.OnGUI(m_Layout.hBarRightRect); m_VBar.OnGUI(m_Layout.vBarRect); m_VBarBottom.OnGUI(m_Layout.vBarBottomRect); m_VBarTop.OnGUI(m_Layout.vBarTopRect); } DrawLabels(bounds); GUI.color = oldColor; } public void HandleEvents() { RectangleToolFlags flags = m_CurveEditor.settings.rectangleToolFlags; if (flags == RectangleToolFlags.NoRectangleTool) return; m_SelectionScaleTop.HandleEvents(); m_SelectionScaleBottom.HandleEvents(); if (rippleTime) { m_SelectionRippleLeft.HandleEvents(); m_SelectionRippleRight.HandleEvents(); } else { m_SelectionScaleLeft.HandleEvents(); m_SelectionScaleRight.HandleEvents(); } m_SelectionBox.HandleEvents(); } public void HandleOverlayEvents() { RectangleToolFlags flags = m_CurveEditor.settings.rectangleToolFlags; if (flags == RectangleToolFlags.NoRectangleTool) return; if (flags == RectangleToolFlags.FullRectangleTool) { m_VBarBottom.HandleEvents(); m_VBarTop.HandleEvents(); m_VBar.HandleEvents(); m_HBarLeft.HandleEvents(); m_HBarRight.HandleEvents(); m_HBar.HandleEvents(); } } private ToolLayout CalculateLayout() { ToolLayout layout = new ToolLayout(); bool canScaleX = !Mathf.Approximately(selectionBounds.size.x, 0f); bool canScaleY = !Mathf.Approximately(selectionBounds.size.y, 0f); float xMin = TimeToPixel(selectionBounds.min.x); float xMax = TimeToPixel(selectionBounds.max.x); float yMin = ValueToPixel(selectionBounds.max.y); float yMax = ValueToPixel(selectionBounds.min.y); layout.selectionRect = new Rect(xMin, yMin, xMax - xMin, yMax - yMin); // Horizontal layout layout.displayHScale = true; float dragHorizWidth = layout.selectionRect.width - kHBarLeftWidth - kHBarRightWidth; if (dragHorizWidth < kHBarMinWidth) { layout.displayHScale = false; dragHorizWidth = layout.selectionRect.width; if (dragHorizWidth < kHBarMinWidth) { layout.selectionRect.x = layout.selectionRect.center.x - kHBarMinWidth * 0.5f; layout.selectionRect.width = kHBarMinWidth; dragHorizWidth = kHBarMinWidth; } } if (layout.displayHScale) { layout.hBarLeftRect = new Rect(layout.selectionRect.xMin, contentRect.yMin, kHBarLeftWidth, kHBarLeftHeight); layout.hBarRect = new Rect(layout.hBarLeftRect.xMax, contentRect.yMin, dragHorizWidth, kHBarHeight); layout.hBarRightRect = new Rect(layout.hBarRect.xMax, contentRect.yMin, kHBarLeftWidth, kHBarRightHeight); } else { layout.hBarRect = new Rect(layout.selectionRect.xMin, contentRect.yMin, dragHorizWidth, kHBarHeight); layout.hBarLeftRect = new Rect(0f, 0f, 0f, 0f); layout.hBarRightRect = new Rect(0f, 0f, 0f, 0f); } // Vertical layout layout.displayVScale = true; float dragVertHeight = layout.selectionRect.height - kVBarBottomHeight - kVBarTopHeight; if (dragVertHeight < kVBarMinHeight) { layout.displayVScale = false; dragVertHeight = layout.selectionRect.height; if (dragVertHeight < kVBarMinHeight) { layout.selectionRect.y = layout.selectionRect.center.y - kVBarMinHeight * 0.5f; layout.selectionRect.height = kVBarMinHeight; dragVertHeight = kVBarMinHeight; } } if (layout.displayVScale) { layout.vBarTopRect = new Rect(contentRect.xMin, layout.selectionRect.yMin, kVBarTopWidth, kVBarTopHeight); layout.vBarRect = new Rect(contentRect.xMin, layout.vBarTopRect.yMax, kVBarWidth, dragVertHeight); layout.vBarBottomRect = new Rect(contentRect.xMin, layout.vBarRect.yMax, kVBarBottomWidth, kVBarBottomHeight); } else { layout.vBarRect = new Rect(contentRect.xMin, layout.selectionRect.yMin, kVBarWidth, dragVertHeight); layout.vBarTopRect = g_EmptyRect; layout.vBarBottomRect = g_EmptyRect; } // Scale handles. if (canScaleX) { float leftRatio = (1.0f - kScaleLeftRatio) * 0.5f; float rightRatio = (1.0f - kScaleRightRatio) * 0.5f; layout.scaleLeftRect = new Rect(layout.selectionRect.xMin - kScaleLeftMarginHorizontal - kScaleLeftWidth, layout.selectionRect.yMin + layout.selectionRect.height * leftRatio, kScaleLeftWidth, layout.selectionRect.height * kScaleLeftRatio); layout.scaleRightRect = new Rect(layout.selectionRect.xMax + kScaleRightMarginHorizontal, layout.selectionRect.yMin + layout.selectionRect.height * rightRatio, kScaleRightWidth, layout.selectionRect.height * kScaleRightRatio); } else { layout.scaleLeftRect = g_EmptyRect; layout.scaleRightRect = g_EmptyRect; } if (canScaleY) { float bottomRatio = (1.0f - kScaleBottomRatio) * 0.5f; float topRatio = (1.0f - kScaleTopRatio) * 0.5f; layout.scaleTopRect = new Rect(layout.selectionRect.xMin + layout.selectionRect.width * topRatio, layout.selectionRect.yMin - kScaleTopMarginVertical - kScaleTopHeight, layout.selectionRect.width * kScaleTopRatio, kScaleTopHeight); layout.scaleBottomRect = new Rect(layout.selectionRect.xMin + layout.selectionRect.width * bottomRatio, layout.selectionRect.yMax + kScaleBottomMarginVertical, layout.selectionRect.width * kScaleBottomRatio, kScaleBottomHeight); } else { layout.scaleTopRect = g_EmptyRect; layout.scaleBottomRect = g_EmptyRect; } // Labels. if (canScaleX) { layout.leftLabelAnchor = new Vector2(layout.selectionRect.xMin - kHLabelMarginHorizontal, contentRect.yMin + kHLabelMarginVertical); layout.rightLabelAnchor = new Vector2(layout.selectionRect.xMax + kHLabelMarginHorizontal, contentRect.yMin + kHLabelMarginVertical); } else { layout.leftLabelAnchor = layout.rightLabelAnchor = new Vector2(layout.selectionRect.xMax + kHLabelMarginHorizontal, contentRect.yMin + kHLabelMarginVertical); } if (canScaleY) { layout.bottomLabelAnchor = new Vector2(contentRect.xMin + kVLabelMarginHorizontal, layout.selectionRect.yMax + kVLabelMarginVertical); layout.topLabelAnchor = new Vector2(contentRect.xMin + kVLabelMarginHorizontal, layout.selectionRect.yMin - kVLabelMarginVertical); } else { layout.bottomLabelAnchor = layout.topLabelAnchor = new Vector2(contentRect.xMin + kVLabelMarginHorizontal, layout.selectionRect.yMin - kVLabelMarginVertical); } // Extra ui. layout.selectionLeftRect = new Rect(contentRect.xMin + kVBarWidth, layout.selectionRect.yMin, layout.selectionRect.xMin - kVBarWidth, layout.selectionRect.height); layout.selectionTopRect = new Rect(layout.selectionRect.xMin, contentRect.yMin + kHBarHeight, layout.selectionRect.width, layout.selectionRect.yMin - kHBarHeight); layout.underlayTopRect = new Rect(contentRect.xMin, contentRect.yMin, contentRect.width, kHBarHeight); layout.underlayLeftRect = new Rect(contentRect.xMin, contentRect.yMin + kHBarHeight, kVBarWidth, contentRect.height - kHBarHeight); return layout; } private void DrawLabels(Rect bounds) { if (dragMode == DragMode.None) return; RectangleToolFlags flags = m_CurveEditor.settings.rectangleToolFlags; bool canScaleX = !Mathf.Approximately(selectionBounds.size.x, 0f); bool canScaleY = !Mathf.Approximately(selectionBounds.size.y, 0f); if (flags == RectangleToolFlags.FullRectangleTool) { // Horizontal labels if ((dragMode & DragMode.MoveScaleHorizontal) != 0) { if (canScaleX) { GUIContent leftLabelContent = new GUIContent(string.Format("{0}", m_CurveEditor.FormatTime(selectionBounds.min.x, m_CurveEditor.invSnap, m_CurveEditor.timeFormat))); GUIContent rightLabelContent = new GUIContent(string.Format("{0}", m_CurveEditor.FormatTime(selectionBounds.max.x, m_CurveEditor.invSnap, m_CurveEditor.timeFormat))); Vector2 leftLabelSize = styles.dragLabel.CalcSize(leftLabelContent); Vector2 rightLabelSize = styles.dragLabel.CalcSize(rightLabelContent); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.leftLabelAnchor.x - leftLabelSize.x, m_Layout.leftLabelAnchor.y, leftLabelSize.x, leftLabelSize.y), leftLabelContent, styles.dragLabel, 0.3f); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.rightLabelAnchor.x, m_Layout.rightLabelAnchor.y, rightLabelSize.x, rightLabelSize.y), rightLabelContent, styles.dragLabel, 0.3f); } else { GUIContent labelContent = new GUIContent(string.Format("{0}", m_CurveEditor.FormatTime(selectionBounds.center.x, m_CurveEditor.invSnap, m_CurveEditor.timeFormat))); Vector2 labelSize = styles.dragLabel.CalcSize(labelContent); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.leftLabelAnchor.x, m_Layout.leftLabelAnchor.y, labelSize.x, labelSize.y), labelContent, styles.dragLabel, 0.3f); } } // Vertical labels if ((dragMode & DragMode.MoveScaleVertical) != 0) { if (canScaleY) { GUIContent bottomLabelContent = new GUIContent(string.Format("{0}", m_CurveEditor.FormatValue(selectionBounds.min.y))); GUIContent topLabelContent = new GUIContent(string.Format("{0}", m_CurveEditor.FormatValue(selectionBounds.max.y))); Vector2 bottomLabelSize = styles.dragLabel.CalcSize(bottomLabelContent); Vector2 topLabelSize = styles.dragLabel.CalcSize(topLabelContent); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.bottomLabelAnchor.x, m_Layout.bottomLabelAnchor.y, bottomLabelSize.x, bottomLabelSize.y), bottomLabelContent, styles.dragLabel, 0.3f); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.topLabelAnchor.x, m_Layout.topLabelAnchor.y - topLabelSize.y, topLabelSize.x, topLabelSize.y), topLabelContent, styles.dragLabel, 0.3f); } else { GUIContent labelContent = new GUIContent(string.Format("{0}", m_CurveEditor.FormatValue(selectionBounds.center.y))); Vector2 labelSize = styles.dragLabel.CalcSize(labelContent); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.topLabelAnchor.x, m_Layout.topLabelAnchor.y - labelSize.y, labelSize.x, labelSize.y), labelContent, styles.dragLabel, 0.3f); } } } else if (flags == RectangleToolFlags.MiniRectangleTool) { if ((dragMode & DragMode.MoveBothAxis) != 0) { Vector2 localPosition = (canScaleX || canScaleY) ? new Vector2(PixelToTime(Event.current.mousePosition.x, frameRate), PixelToValue(Event.current.mousePosition.y)) : (Vector2)selectionBounds.center; Vector2 labelPosition = new Vector2(TimeToPixel(localPosition.x), ValueToPixel(localPosition.y)); GUIContent labelContent = new GUIContent(string.Format("{0}, {1}", m_CurveEditor.FormatTime(localPosition.x, m_CurveEditor.invSnap, m_CurveEditor.timeFormat), m_CurveEditor.FormatValue(localPosition.y))); Vector2 labelSize = styles.dragLabel.CalcSize(labelContent); // Clamp popup to remain inside the curve editor window var labelRect = new Rect(labelPosition.x, labelPosition.y - labelSize.y, labelSize.x, labelSize.y); labelRect.x += Mathf.Max(0.0f, bounds.xMin - labelRect.xMin); labelRect.x -= Mathf.Max(0.0f, labelRect.xMax - bounds.xMax); labelRect.y += Mathf.Max(0.0f, bounds.yMin - labelRect.yMin); labelRect.y -= Mathf.Max(0.0f, labelRect.yMax - bounds.yMax); EditorGUI.DoDropShadowLabel(labelRect, labelContent, styles.dragLabel, 0.3f); } } } private void OnStartScale(ToolCoord pivotCoord, ToolCoord pickedCoord, Vector2 mousePos, DragMode dragMode, bool rippleTime) { Bounds bounds = selectionBounds; m_DragMode = dragMode; m_Pivot = ToolCoordToPosition(pivotCoord, bounds); m_Previous = ToolCoordToPosition(pickedCoord, bounds); m_MouseOffset = mousePos - m_Previous; m_RippleTime = rippleTime; m_RippleTimeStart = bounds.min.x; m_RippleTimeEnd = bounds.max.x; m_CurveEditor.StartLiveEdit(); } private void OnScaleTime(float time) { Matrix4x4 transform; bool flipX; if (CalculateScaleTimeMatrix(m_Previous.x, time, m_MouseOffset.x, m_Pivot.x, frameRate, out transform, out flipX)) TransformKeys(transform, flipX, false); } private void OnScaleValue(float val) { Matrix4x4 transform; bool flipY; if (CalculateScaleValueMatrix(m_Previous.y, val, m_MouseOffset.y, m_Pivot.y, out transform, out flipY)) TransformKeys(transform, false, flipY); } private void OnEndScale() { m_CurveEditor.EndLiveEdit(); m_DragMode = DragMode.None; GUI.changed = true; } internal void OnStartMove(Vector2 position, bool rippleTime) { OnStartMove(position, DragMode.None, rippleTime); } private void OnStartMove(Vector2 position, DragMode dragMode, bool rippleTime) { Bounds bounds = selectionBounds; m_DragMode = dragMode; m_Previous = position; m_RippleTime = rippleTime; m_RippleTimeStart = bounds.min.x; m_RippleTimeEnd = bounds.max.x; m_CurveEditor.StartLiveEdit(); } internal void OnMove(Vector2 position) { Vector2 dv = position - m_Previous; Matrix4x4 transform = Matrix4x4.identity; transform.SetTRS(new Vector3(dv.x, dv.y, 0f), Quaternion.identity, Vector3.one); TransformKeys(transform, false, false); } internal void OnEndMove() { m_CurveEditor.EndLiveEdit(); m_DragMode = DragMode.None; GUI.changed = true; } private void TransformKeys(Matrix4x4 matrix, bool flipX, bool flipY) { if (m_RippleTime) { m_CurveEditor.TransformRippleKeys(matrix, m_RippleTimeStart, m_RippleTimeEnd, flipX, flipY); GUI.changed = true; } else { m_CurveEditor.TransformSelectedKeys(matrix, flipX, flipY); GUI.changed = true; } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveEditorSelection.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Collections; using Object = UnityEngine.Object; namespace UnityEditor { [System.Serializable] internal class CurveEditorSelection : ScriptableObject { [SerializeField] private List m_SelectedCurves; public List selectedCurves { get { return m_SelectedCurves ?? (m_SelectedCurves = new List()); } set { m_SelectedCurves = value; } } } [System.Serializable] internal class CurveSelection : System.IComparable { internal enum SelectionType { Key = 0, InTangent = 1, OutTangent = 2, Count = 3, } [SerializeField] public int curveID = 0; [SerializeField] public int key = -1; [SerializeField] public bool semiSelected = false; [SerializeField] public SelectionType type; internal CurveSelection(int curveID, int key) { this.curveID = curveID; this.key = key; this.type = SelectionType.Key; } internal CurveSelection(int curveID, int key, SelectionType type) { this.curveID = curveID; this.key = key; this.type = type; } public int CompareTo(object _other) { CurveSelection other = (CurveSelection)_other; int cmp = curveID - other.curveID; if (cmp != 0) return cmp; cmp = key - other.key; if (cmp != 0) return cmp; return (int)type - (int)other.type; } public override bool Equals(object _other) { CurveSelection other = _other as CurveSelection; if (ReferenceEquals(other, null)) return false; if (ReferenceEquals(other, this)) return true; return other.curveID == curveID && other.key == key && other.type == type; } public override int GetHashCode() { return curveID * 729 + key * 27 + (int)type; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveEditorSettings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; namespace UnityEditor { [System.Serializable] internal class CurveEditorSettings { // Label settings private TickStyle m_HTickStyle = new TickStyle(); private TickStyle m_VTickStyle = new TickStyle(); internal TickStyle hTickStyle { get { return m_HTickStyle; } set { m_HTickStyle = value; } } internal TickStyle vTickStyle { get { return m_VTickStyle; } set { m_VTickStyle = value; } } // Range lock settings private bool m_HRangeLocked; private bool m_VRangeLocked; internal bool hRangeLocked { get { return m_HRangeLocked; } set { m_HRangeLocked = value; } } internal bool vRangeLocked { get { return m_VRangeLocked; } set { m_VRangeLocked = value; } } // Range settings private float m_HRangeMin = Mathf.NegativeInfinity; private float m_HRangeMax = Mathf.Infinity; private float m_VRangeMin = Mathf.NegativeInfinity; private float m_VRangeMax = Mathf.Infinity; public float hRangeMin { get { return m_HRangeMin; } set { m_HRangeMin = value; } } public float hRangeMax { get { return m_HRangeMax; } set { m_HRangeMax = value; } } public float vRangeMin { get { return m_VRangeMin; } set { m_VRangeMin = value; } } public float vRangeMax { get { return m_VRangeMax; } set { m_VRangeMax = value; } } public bool hasUnboundedRanges { get { return m_HRangeMin == Mathf.NegativeInfinity || m_HRangeMax == Mathf.Infinity || m_VRangeMin == Mathf.NegativeInfinity || m_VRangeMax == Mathf.Infinity; } } // Offset to move the labels along the horizontal axis to make room for the overlaid scrollbar in the // curve editor popup. public float hTickLabelOffset = 0; public EditorGUIUtility.SkinnedColor wrapColor = new EditorGUIUtility.SkinnedColor(new Color(1.0f, 1.0f, 1.0f, 0.5f), new Color(.65f, .65f, .65f, 0.5f)); public bool useFocusColors = false; public bool showAxisLabels = true; public bool showWrapperPopups = false; public bool allowDraggingCurvesAndRegions = true; public bool allowDeleteLastKeyInCurve = false; public bool undoRedoSelection = false; public bool flushCurveCache = true; public string xAxisLabel = L10n.Tr("time"); public string yAxisLabel = L10n.Tr("value"); public Vector2 curveRegionDomain = new Vector2(0, 1); // X axis range in which to draw the shaded curve region between 2 curves. public bool rippleTime = false; // Display options internal enum RectangleToolFlags { NoRectangleTool, MiniRectangleTool, FullRectangleTool } internal RectangleToolFlags rectangleToolFlags = RectangleToolFlags.NoRectangleTool; // Window resize settings private bool m_ScaleWithWindow = true; internal bool scaleWithWindow { get { return m_ScaleWithWindow; } set { m_ScaleWithWindow = value; } } // Slider settings private bool m_HSlider = true; private bool m_VSlider = true; public bool hSlider { get { return m_HSlider; } set { m_HSlider = value; } } public bool vSlider { get { return m_VSlider; } set { m_VSlider = value; } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveEditorWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using UnityEditor.ShortcutManagement; using System.Linq; using TangentMode = UnityEditor.AnimationUtility.TangentMode; namespace UnityEditor { internal enum WrapModeFixedCurve { Clamp = (int)WrapMode.ClampForever, Loop = (int)WrapMode.Loop, PingPong = (int)WrapMode.PingPong } [Serializable] internal class CurveEditorWindow : EditorWindow { public enum NormalizationMode { None = 0, Normalize = 1, Denormalize = 2, } //const int kToolbarHeight = 17; const int kPresetsHeight = 50; static CurveEditorWindow s_SharedCurveEditor; internal CurveEditor m_CurveEditor; Vector2 m_PresetScrollPosition; AnimationCurve m_Curve; Color m_Color; CurvePresetsContentsForPopupWindow m_CurvePresets; GUIContent m_GUIContent = new GUIContent(); [SerializeField] GUIView m_DelegateView; public const string CurveChangedCommand = "CurveChanged"; public const string CurveChangeCompletedCommand = "CurveChangeCompleted"; // Watch out when accessing this, it will create a new curve editor window if none exists yet. // If you don't display the window (call Show()), it will cause an error when validating the layout // (e.g. when the user maximizes a window). public static CurveEditorWindow instance { get { if (!s_SharedCurveEditor) s_SharedCurveEditor = ScriptableObject.CreateInstance(); return s_SharedCurveEditor; } } public string currentPresetLibrary { get { InitCurvePresets(); return m_CurvePresets.currentPresetLibrary; } set { InitCurvePresets(); m_CurvePresets.currentPresetLibrary = value; } } public static AnimationCurve curve { get { return visible ? CurveEditorWindow.instance.m_Curve : null; } set { if (value == null) { CurveEditorWindow.instance.m_Curve = null; } else { CurveEditorWindow.instance.m_Curve = value; CurveEditorWindow.instance.RefreshShownCurves(); } } } public static Color color { get { return CurveEditorWindow.instance.m_Color; } set { CurveEditorWindow.instance.m_Color = value; CurveEditorWindow.instance.RefreshShownCurves(); } } internal static GUIView delegateView { get { return visible ? CurveEditorWindow.instance.m_DelegateView : null; } } public static bool visible { get { return s_SharedCurveEditor != null; } } void OnEnable() { s_SharedCurveEditor = this; Init(null); } // Called by OnEnable to make sure the CurveEditor is not null, // and by Show so we get a fresh CurveEditor when the user clicks a new curve. void Init(CurveEditorSettings settings) { m_CurveEditor = new CurveEditor(GetCurveEditorRect(), GetCurveWrapperArray(), true); m_CurveEditor.curvesUpdated = UpdateCurve; m_CurveEditor.scaleWithWindow = true; m_CurveEditor.margin = 40; if (settings != null) m_CurveEditor.settings = settings; m_CurveEditor.settings.hTickLabelOffset = 10; m_CurveEditor.settings.rectangleToolFlags = CurveEditorSettings.RectangleToolFlags.MiniRectangleTool; // As there is no guarantee animation curve changes are recorded in undo redo, we can't really // handle curve selection in undo redo either. m_CurveEditor.settings.undoRedoSelection = false; m_CurveEditor.settings.showWrapperPopups = true; // For each of horizontal and vertical axis, if we have a finite range for that axis, use that range, // otherwise use framing logic to determine shown range for that axis. bool frameH = true; bool frameV = true; if (m_CurveEditor.settings.hRangeMin != Mathf.NegativeInfinity && m_CurveEditor.settings.hRangeMax != Mathf.Infinity) { m_CurveEditor.SetShownHRangeInsideMargins(m_CurveEditor.settings.hRangeMin, m_CurveEditor.settings.hRangeMax); frameH = false; } if (m_CurveEditor.settings.vRangeMin != Mathf.NegativeInfinity && m_CurveEditor.settings.vRangeMax != Mathf.Infinity) { m_CurveEditor.SetShownVRangeInsideMargins(m_CurveEditor.settings.vRangeMin, m_CurveEditor.settings.vRangeMax); frameV = false; } m_CurveEditor.FrameSelected(frameH, frameV); titleContent = EditorGUIUtility.TrTextContent("Curve"); // deal with window size minSize = new Vector2(240, 240 + kPresetsHeight); maxSize = new Vector2(10000, 10000); } CurveLibraryType curveLibraryType { get { if (m_CurveEditor.settings.hasUnboundedRanges) return CurveLibraryType.Unbounded; return CurveLibraryType.NormalizedZeroToOne; } } // Returns true if a valid normalizationRect is returned (ranges are bounded) static bool GetNormalizationRect(out Rect normalizationRect, CurveEditor curveEditor) { normalizationRect = new Rect(); if (curveEditor.settings.hasUnboundedRanges) return false; normalizationRect = new Rect( curveEditor.settings.hRangeMin, curveEditor.settings.vRangeMin, curveEditor.settings.hRangeMax - curveEditor.settings.hRangeMin, curveEditor.settings.vRangeMax - curveEditor.settings.vRangeMin); return true; } static Keyframe[] CopyAndScaleCurveKeys(Keyframe[] orgKeys, Rect rect, NormalizationMode normalization) { Keyframe[] scaledKeys = new Keyframe[orgKeys.Length]; orgKeys.CopyTo(scaledKeys, 0); if (normalization == NormalizationMode.None) return scaledKeys; if (rect.width == 0f || rect.height == 0f || Single.IsInfinity(rect.width) || Single.IsInfinity(rect.height)) { Debug.LogError("CopyAndScaleCurve: Invalid scale: " + rect); return scaledKeys; } float tangentMultiplier = rect.height / rect.width; switch (normalization) { case NormalizationMode.Normalize: for (int i = 0; i < scaledKeys.Length; ++i) { scaledKeys[i].time = (orgKeys[i].time - rect.xMin) / rect.width; scaledKeys[i].value = (orgKeys[i].value - rect.yMin) / rect.height; if (!Single.IsInfinity(orgKeys[i].inTangent)) scaledKeys[i].inTangent = orgKeys[i].inTangent / tangentMultiplier; if (!Single.IsInfinity(orgKeys[i].outTangent)) scaledKeys[i].outTangent = orgKeys[i].outTangent / tangentMultiplier; } break; case NormalizationMode.Denormalize: // From normalized to real for (int i = 0; i < scaledKeys.Length; ++i) { scaledKeys[i].time = orgKeys[i].time * rect.width + rect.xMin; scaledKeys[i].value = orgKeys[i].value * rect.height + rect.yMin; if (!Single.IsInfinity(orgKeys[i].inTangent)) scaledKeys[i].inTangent = orgKeys[i].inTangent * tangentMultiplier; if (!Single.IsInfinity(orgKeys[i].outTangent)) scaledKeys[i].outTangent = orgKeys[i].outTangent * tangentMultiplier; } break; } return scaledKeys; } void InitCurvePresets() { if (m_CurvePresets == null) { // Selection callback for library window Action presetSelectedCallback = delegate(AnimationCurve presetCurve) { ValidateCurveLibraryTypeAndScale(); // Scale curve up using ranges m_Curve.keys = GetDenormalizedKeys(presetCurve.keys); m_Curve.postWrapMode = presetCurve.postWrapMode; m_Curve.preWrapMode = presetCurve.preWrapMode; m_CurveEditor.SelectNone(); RefreshShownCurves(); SendEvent(CurveChangedCommand, true); }; // We set the curve to save when showing the popup to ensure to scale the current state of the curve AnimationCurve curveToSaveAsPreset = null; m_CurvePresets = new CurvePresetsContentsForPopupWindow(curveToSaveAsPreset, curveLibraryType, presetSelectedCallback); m_CurvePresets.InitIfNeeded(); } } void OnDestroy() { if (m_CurvePresets != null) m_CurvePresets.GetPresetLibraryEditor().UnloadUsedLibraries(); } void OnDisable() { m_CurveEditor.OnDisable(); if (s_SharedCurveEditor == this) s_SharedCurveEditor = null; else if (!this.Equals(s_SharedCurveEditor)) throw new ApplicationException("s_SharedCurveEditor does not equal this"); } private void RefreshShownCurves() { m_CurveEditor.animationCurves = GetCurveWrapperArray(); } public void Show(GUIView viewToUpdate, CurveEditorSettings settings) { m_DelegateView = viewToUpdate; m_OnCurveChanged = null; Init(settings); ShowAuxWindow(); } Action m_OnCurveChanged; public void Show(Action onCurveChanged, CurveEditorSettings settings) { m_OnCurveChanged = onCurveChanged; m_DelegateView = null; Init(settings); ShowAuxWindow(); } public void FrameSelected() { m_CurveEditor.FrameSelected(true, true); } public void FrameClip() { m_CurveEditor.FrameClip(true, true); } internal class Styles { public GUIStyle curveEditorBackground = "PopupCurveEditorBackground"; public GUIStyle miniToolbarButton = "MiniToolbarButtonLeft"; public GUIStyle curveSwatch = "PopupCurveEditorSwatch"; public GUIStyle curveSwatchArea = "PopupCurveSwatchBackground"; } internal static Styles ms_Styles; CurveWrapper[] GetCurveWrapperArray() { if (m_Curve == null) return new CurveWrapper[] {}; CurveWrapper cw = new CurveWrapper(); cw.id = "Curve".GetHashCode(); cw.groupId = -1; cw.color = m_Color; cw.hidden = false; cw.readOnly = false; cw.renderer = new NormalCurveRenderer(m_Curve); cw.renderer.SetWrap(m_Curve.preWrapMode, m_Curve.postWrapMode); return new CurveWrapper[] { cw }; } Rect GetCurveEditorRect() { //return new Rect(0, kToolbarHeight, position.width, position.height-kToolbarHeight); return new Rect(0, 0, position.width, position.height - kPresetsHeight); } static internal Keyframe[] GetLinearKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 0, 1, 1); keys[1] = new Keyframe(1, 1, 1, 1); SetSmoothEditable(ref keys, TangentMode.Auto); return keys; } static internal Keyframe[] GetLinearMirrorKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 1, -1, -1); keys[1] = new Keyframe(1, 0, -1, -1); SetSmoothEditable(ref keys, TangentMode.Auto); return keys; } static internal Keyframe[] GetEaseInKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 0, 0, 0); keys[1] = new Keyframe(1, 1, 2, 2); SetSmoothEditable(ref keys, TangentMode.Free); return keys; } static internal Keyframe[] GetEaseInMirrorKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 1, -2, -2); keys[1] = new Keyframe(1, 0, 0, 0); SetSmoothEditable(ref keys, TangentMode.Free); return keys; } static internal Keyframe[] GetEaseOutKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 0, 2, 2); keys[1] = new Keyframe(1, 1, 0, 0); SetSmoothEditable(ref keys, TangentMode.Free); return keys; } static internal Keyframe[] GetEaseOutMirrorKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 1, 0, 0); keys[1] = new Keyframe(1, 0, -2, -2); SetSmoothEditable(ref keys, TangentMode.Free); return keys; } static internal Keyframe[] GetEaseInOutKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 0, 0, 0); keys[1] = new Keyframe(1, 1, 0, 0); SetSmoothEditable(ref keys, TangentMode.Free); return keys; } static internal Keyframe[] GetEaseInOutMirrorKeys() { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, 1, 0, 0); keys[1] = new Keyframe(1, 0, 0, 0); SetSmoothEditable(ref keys, TangentMode.Free); return keys; } static internal Keyframe[] GetConstantKeys(float value) { Keyframe[] keys = new Keyframe[2]; keys[0] = new Keyframe(0, value, 0, 0); keys[1] = new Keyframe(1, value, 0, 0); SetSmoothEditable(ref keys, TangentMode.Auto); return keys; } static internal void SetSmoothEditable(ref Keyframe[] keys, TangentMode tangentMode) { for (int i = 0; i < keys.Length; i++) { AnimationUtility.SetKeyBroken(ref keys[i], false); AnimationUtility.SetKeyLeftTangentMode(ref keys[i], tangentMode); AnimationUtility.SetKeyRightTangentMode(ref keys[i], tangentMode); } } public static Keyframe[] NormalizeKeys(Keyframe[] sourceKeys, NormalizationMode normalization, CurveEditor curveEditor) { Rect normalizationRect; if (!GetNormalizationRect(out normalizationRect, curveEditor)) // No normalization rect, just return a copy of the source keyframes normalization = NormalizationMode.None; return CopyAndScaleCurveKeys(sourceKeys, normalizationRect, normalization); } Keyframe[] GetDenormalizedKeys(Keyframe[] sourceKeys) { return GetDenormalizedKeys(sourceKeys, m_CurveEditor); } public static Keyframe[] GetDenormalizedKeys(Keyframe[] sourceKeys, CurveEditor curveEditor) { return NormalizeKeys(sourceKeys, NormalizationMode.Denormalize, curveEditor); } Keyframe[] GetNormalizedKeys(Keyframe[] sourceKeys) { return GetNormalizedKeys(sourceKeys, m_CurveEditor); } public static Keyframe[] GetNormalizedKeys(Keyframe[] sourceKeys, CurveEditor curveEditor) { return NormalizeKeys(sourceKeys, NormalizationMode.Normalize, curveEditor); } void OnGUI() { bool gotMouseUp = (Event.current.type == EventType.MouseUp); if (m_DelegateView == null && m_OnCurveChanged == null) m_Curve = null; if (ms_Styles == null) ms_Styles = new Styles(); // Curve Editor m_CurveEditor.rect = GetCurveEditorRect(); m_CurveEditor.hRangeLocked = Event.current.shift; m_CurveEditor.vRangeLocked = EditorGUI.actionKey; GUI.changed = false; GUI.Label(m_CurveEditor.drawRect, GUIContent.none, ms_Styles.curveEditorBackground); m_CurveEditor.OnGUI(); // Preset swatch area var presetRect = new Rect(0, position.height - kPresetsHeight, position.width, kPresetsHeight); GUI.Box(presetRect, "", ms_Styles.curveSwatchArea); Color curveColor = m_Color; curveColor.a *= 0.6f; const float width = 40f; const float height = 25f; const float spaceBetweenSwatches = 5f; const float presetDropdownSize = 16f; const float horizontalScrollbarHeight = 15f; const float presetDropdownCenteringOffset = 2f; float yPos = (kPresetsHeight - height) * 0.5f; InitCurvePresets(); CurvePresetLibrary curveLibrary = m_CurvePresets.GetPresetLibraryEditor().GetCurrentLib(); if (curveLibrary != null) { var numPresets = curveLibrary.Count(); var presetDropDownRect = new Rect(spaceBetweenSwatches, yPos + presetDropdownCenteringOffset, presetDropdownSize, presetDropdownSize); Rect contentRect = new Rect(0, 0, numPresets * (width + spaceBetweenSwatches) + presetDropDownRect.xMax, presetRect.height - horizontalScrollbarHeight); m_PresetScrollPosition = GUI.BeginScrollView( presetRect, // Rectangle of the visible area m_PresetScrollPosition, // Current scroll position contentRect, // Rectangle containing all content false, // Always show horizontal scrollbar false // Always show vertical scrollbar ); PresetDropDown(presetDropDownRect); Rect swatchRect = new Rect(presetDropDownRect.xMax + spaceBetweenSwatches, yPos, width, height); for (int i = 0; i < numPresets; i++) { m_GUIContent.tooltip = curveLibrary.GetName(i); if (GUI.Button(swatchRect, m_GUIContent, ms_Styles.curveSwatch)) { AnimationCurve animCurve = curveLibrary.GetPreset(i) as AnimationCurve; m_Curve.keys = GetDenormalizedKeys(animCurve.keys); m_Curve.postWrapMode = animCurve.postWrapMode; m_Curve.preWrapMode = animCurve.preWrapMode; m_CurveEditor.SelectNone(); SendEvent(CurveChangedCommand, true); } if (Event.current.type == EventType.Repaint) curveLibrary.Draw(swatchRect, i); swatchRect.x += width + spaceBetweenSwatches; } GUI.EndScrollView(); } // For adding default preset curves //if (EditorGUI.DropdownButton(new Rect (position.width -26, yPos, 20, 20), GUIContent.none, FocusType.Passive, "OL Plus")) // AddDefaultPresetsToCurrentLib (); if (Event.current.type == EventType.Used && gotMouseUp) { DoUpdateCurve(false); SendEvent(CurveChangeCompletedCommand, true); } else if (Event.current.type != EventType.Layout && Event.current.type != EventType.Repaint) { DoUpdateCurve(true); } } void PresetDropDown(Rect rect) { if (EditorGUI.DropdownButton(rect, EditorGUI.GUIContents.titleSettingsIcon, FocusType.Passive, EditorStyles.inspectorTitlebarText)) { if (m_Curve != null) { if (m_CurvePresets == null) { Debug.LogError("Curve presets error"); return; } ValidateCurveLibraryTypeAndScale(); AnimationCurve copy = new AnimationCurve(GetNormalizedKeys(m_Curve.keys)); copy.postWrapMode = m_Curve.postWrapMode; copy.preWrapMode = m_Curve.preWrapMode; m_CurvePresets.curveToSaveAsPreset = copy; PopupWindow.Show(rect, m_CurvePresets); } } } void ValidateCurveLibraryTypeAndScale() { Rect normalizationRect; if (GetNormalizationRect(out normalizationRect, m_CurveEditor)) { if (curveLibraryType != CurveLibraryType.NormalizedZeroToOne) Debug.LogError("When having a normalize rect we should be using curve library type: NormalizedZeroToOne (normalizationRect: " + normalizationRect + ")"); } else { if (curveLibraryType != CurveLibraryType.Unbounded) Debug.LogError("When NOT having a normalize rect we should be using library type: Unbounded"); } } public void UpdateCurve() { DoUpdateCurve(false); } private void DoUpdateCurve(bool exitGUI) { if (m_CurveEditor.animationCurves.Length > 0 && m_CurveEditor.animationCurves[0] != null && m_CurveEditor.animationCurves[0].changed) { m_CurveEditor.animationCurves[0].changed = false; RefreshShownCurves(); SendEvent(CurveChangedCommand, exitGUI); } } void SendEvent(string eventName, bool exitGUI) { if (m_DelegateView) { Event e = EditorGUIUtility.CommandEvent(eventName); Repaint(); m_DelegateView.SendEvent(e); if (exitGUI) GUIUtility.ExitGUI(); } if (m_OnCurveChanged != null) { m_OnCurveChanged(curve); } GUI.changed = true; } [Shortcut("Curve Editor/Frame All", typeof(CurveEditorWindow), KeyCode.A)] static void FrameClip(ShortcutArguments args) { var curveEditorWindow = (CurveEditorWindow)args.context; if (EditorWindow.focusedWindow != curveEditorWindow) return; curveEditorWindow.FrameClip(); curveEditorWindow.Repaint(); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveMenuManager.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditorInternal; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; using TangentMode = UnityEditor.AnimationUtility.TangentMode; namespace UnityEditor { internal class ChangedCurve { public AnimationCurve curve; public int curveId; public EditorCurveBinding binding; public ChangedCurve(AnimationCurve curve, int curveId, EditorCurveBinding binding) { this.curve = curve; this.curveId = curveId; this.binding = binding; } public override int GetHashCode() { int hash = 0; unchecked { hash = curve.GetHashCode(); hash = 33 * hash + binding.GetHashCode(); } return hash; } } internal class KeyIdentifier { public AnimationCurve curve; public int curveId; public int key; // Used by dopesheet public EditorCurveBinding binding; public KeyIdentifier(AnimationCurve _curve, int _curveId, int _keyIndex) { curve = _curve; curveId = _curveId; key = _keyIndex; } public KeyIdentifier(AnimationCurve _curve, int _curveId, int _keyIndex, EditorCurveBinding _binding) { curve = _curve; curveId = _curveId; key = _keyIndex; binding = _binding; } public Keyframe keyframe { get { return curve[key]; } } } internal interface CurveUpdater { void UpdateCurves(List curve, string undoText); } internal class CurveMenuManager { CurveUpdater updater; public CurveMenuManager(CurveUpdater updater) { this.updater = updater; } public void AddTangentMenuItems(GenericMenu menu, List keyList) { bool anyKeys = (keyList.Count > 0); // Find out which qualities apply to all the keys bool allClampedAuto = anyKeys; bool allAuto = anyKeys; bool allFreeSmooth = anyKeys; bool allFlat = anyKeys; bool allBroken = anyKeys; bool allLeftWeighted = anyKeys; bool allLeftFree = anyKeys; bool allLeftLinear = anyKeys; bool allLeftConstant = anyKeys; bool allRightWeighted = anyKeys; bool allRightFree = anyKeys; bool allRightLinear = anyKeys; bool allRightConstant = anyKeys; foreach (KeyIdentifier sel in keyList) { Keyframe key = sel.keyframe; TangentMode leftMode = AnimationUtility.GetKeyLeftTangentMode(key); TangentMode rightMode = AnimationUtility.GetKeyRightTangentMode(key); bool broken = AnimationUtility.GetKeyBroken(key); if (leftMode != TangentMode.ClampedAuto || rightMode != TangentMode.ClampedAuto) allClampedAuto = false; if (leftMode != TangentMode.Auto || rightMode != TangentMode.Auto) allAuto = false; if (broken || leftMode != TangentMode.Free || rightMode != TangentMode.Free) allFreeSmooth = false; if (broken || leftMode != TangentMode.Free || key.inTangent != 0 || rightMode != TangentMode.Free || key.outTangent != 0) allFlat = false; if (!broken) allBroken = false; if (!broken || leftMode != TangentMode.Free) allLeftFree = false; if (!broken || leftMode != TangentMode.Linear) allLeftLinear = false; if (!broken || leftMode != TangentMode.Constant) allLeftConstant = false; if (!broken || rightMode != TangentMode.Free) allRightFree = false; if (!broken || rightMode != TangentMode.Linear) allRightLinear = false; if (!broken || rightMode != TangentMode.Constant) allRightConstant = false; if ((key.weightedMode & WeightedMode.In) == WeightedMode.None) allLeftWeighted = false; if ((key.weightedMode & WeightedMode.Out) == WeightedMode.None) allRightWeighted = false; } if (anyKeys) { menu.AddItem(EditorGUIUtility.TrTextContent("Clamped Auto"), allClampedAuto, SetClampedAuto, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Auto"), allAuto, SetAuto, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Free Smooth"), allFreeSmooth, SetEditable, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Flat"), allFlat, SetFlat, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Broken"), allBroken, SetBroken, keyList); menu.AddSeparator(""); menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Free"), allLeftFree, SetLeftEditable, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Linear"), allLeftLinear, SetLeftLinear, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Constant"), allLeftConstant, SetLeftConstant, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Left Tangent/Weighted"), allLeftWeighted, ToggleLeftWeighted, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Free"), allRightFree, SetRightEditable, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Linear"), allRightLinear, SetRightLinear, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Constant"), allRightConstant, SetRightConstant, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Right Tangent/Weighted"), allRightWeighted, ToggleRightWeighted, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Free"), allRightFree && allLeftFree, SetBothEditable, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Linear"), allRightLinear && allLeftLinear, SetBothLinear, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Constant"), allRightConstant && allLeftConstant, SetBothConstant, keyList); menu.AddItem(EditorGUIUtility.TrTextContent("Both Tangents/Weighted"), allRightWeighted && allLeftWeighted, ToggleBothWeighted, keyList); } else { menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Weighted")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Auto")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Free Smooth")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Flat")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Broken")); menu.AddSeparator(""); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Free")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Linear")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Constant")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Left Tangent/Weighted")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Free")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Linear")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Constant")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Right Tangent/Weighted")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Free")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Linear")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Constant")); menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Both Tangents/Weighted")); } } public void ToggleLeftWeighted(object keysToSet) { ToggleWeighted(WeightedMode.In, (List)keysToSet); } public void ToggleRightWeighted(object keysToSet) { ToggleWeighted(WeightedMode.Out, (List)keysToSet); } public void ToggleBothWeighted(object keysToSet) { ToggleWeighted(WeightedMode.Both, (List)keysToSet); } public void ToggleWeighted(WeightedMode weightedMode, List keysToSet) { bool allWeighted = keysToSet.TrueForAll(key => (key.keyframe.weightedMode & weightedMode) == weightedMode); List changedCurves = new List(); foreach (KeyIdentifier keyToSet in keysToSet) { AnimationCurve animationCurve = keyToSet.curve; Keyframe key = keyToSet.keyframe; bool weighted = (key.weightedMode & weightedMode) == weightedMode; if (weighted == allWeighted) { WeightedMode lastWeightedMode = key.weightedMode; key.weightedMode = weighted ? key.weightedMode & ~weightedMode : key.weightedMode | weightedMode; if (key.weightedMode != WeightedMode.None) { TangentMode rightTangentMode = AnimationUtility.GetKeyRightTangentMode(key); TangentMode leftTangentMode = AnimationUtility.GetKeyLeftTangentMode(key); if ((lastWeightedMode & WeightedMode.Out) == WeightedMode.None && (key.weightedMode & WeightedMode.Out) == WeightedMode.Out) { if (rightTangentMode == TangentMode.Linear || rightTangentMode == TangentMode.Constant) AnimationUtility.SetKeyRightTangentMode(ref key, TangentMode.Free); if (keyToSet.key < (animationCurve.length - 1)) key.outWeight = 1 / 3.0f; } if ((lastWeightedMode & WeightedMode.In) == WeightedMode.None && (key.weightedMode & WeightedMode.In) == WeightedMode.In) { if (leftTangentMode == TangentMode.Linear || leftTangentMode == TangentMode.Constant) AnimationUtility.SetKeyLeftTangentMode(ref key, TangentMode.Free); if (keyToSet.key > 0) key.inWeight = 1 / 3.0f; } } animationCurve.MoveKey(keyToSet.key, key); AnimationUtility.UpdateTangentsFromModeSurrounding(animationCurve, keyToSet.key); ChangedCurve changedCurve = new ChangedCurve(animationCurve, keyToSet.curveId, keyToSet.binding); if (!changedCurves.Contains(changedCurve)) changedCurves.Add(changedCurve); } } updater.UpdateCurves(changedCurves, "Toggle Weighted"); } public void SetClampedAuto(object keysToSet) { SetBoth(TangentMode.ClampedAuto, (List)keysToSet); } public void SetAuto(object keysToSet) { SetBoth(TangentMode.Auto, (List)keysToSet); } public void SetEditable(object keysToSet) { SetBoth(TangentMode.Free, (List)keysToSet); } public void SetFlat(object keysToSet) { SetBoth(TangentMode.Free, (List)keysToSet); Flatten((List)keysToSet); } public void SetBoth(TangentMode mode, List keysToSet) { List changedCurves = new List(); foreach (KeyIdentifier keyToSet in keysToSet) { AnimationCurve animationCurve = keyToSet.curve; Keyframe key = keyToSet.keyframe; AnimationUtility.SetKeyBroken(ref key, false); AnimationUtility.SetKeyRightTangentMode(ref key, mode); AnimationUtility.SetKeyLeftTangentMode(ref key, mode); // Smooth Tangents based on neighboring nodes // Note: not needed since the UpdateTangentsFromModeSurrounding call below will handle it //if (mode == TangentMode.ClampedAuto) animationCurve.SmoothTangents(keyToSet.key, 0.0F); // Smooth tangents based on existing tangents if (mode == TangentMode.Free) { float slope = CurveUtility.CalculateSmoothTangent(key); key.inTangent = slope; key.outTangent = slope; } animationCurve.MoveKey(keyToSet.key, key); AnimationUtility.UpdateTangentsFromModeSurrounding(animationCurve, keyToSet.key); ChangedCurve changedCurve = new ChangedCurve(animationCurve, keyToSet.curveId, keyToSet.binding); if (!changedCurves.Contains(changedCurve)) changedCurves.Add(changedCurve); } updater.UpdateCurves(changedCurves, "Set Tangents"); } public void Flatten(List keysToSet) { List changedCurves = new List(); foreach (KeyIdentifier keyToSet in keysToSet) { AnimationCurve animationCurve = keyToSet.curve; Keyframe key = keyToSet.keyframe; key.inTangent = 0; key.outTangent = 0; animationCurve.MoveKey(keyToSet.key, key); AnimationUtility.UpdateTangentsFromModeSurrounding(animationCurve, keyToSet.key); ChangedCurve changedCurve = new ChangedCurve(animationCurve, keyToSet.curveId, keyToSet.binding); if (!changedCurves.Contains(changedCurve)) changedCurves.Add(changedCurve); } updater.UpdateCurves(changedCurves, "Set Tangents"); } public void SetBroken(object _keysToSet) { List changedCurves = new List(); List keysToSet = (List)_keysToSet; foreach (KeyIdentifier keyToSet in keysToSet) { AnimationCurve animationCurve = keyToSet.curve; Keyframe key = keyToSet.keyframe; AnimationUtility.SetKeyBroken(ref key, true); if (AnimationUtility.GetKeyRightTangentMode(key) == TangentMode.ClampedAuto || AnimationUtility.GetKeyRightTangentMode(key) == TangentMode.Auto) AnimationUtility.SetKeyRightTangentMode(ref key, TangentMode.Free); if (AnimationUtility.GetKeyLeftTangentMode(key) == TangentMode.ClampedAuto || AnimationUtility.GetKeyLeftTangentMode(key) == TangentMode.Auto) AnimationUtility.SetKeyLeftTangentMode(ref key, TangentMode.Free); animationCurve.MoveKey(keyToSet.key, key); AnimationUtility.UpdateTangentsFromModeSurrounding(animationCurve, keyToSet.key); ChangedCurve changedCurve = new ChangedCurve(animationCurve, keyToSet.curveId, keyToSet.binding); if (!changedCurves.Contains(changedCurve)) changedCurves.Add(changedCurve); } updater.UpdateCurves(changedCurves, "Set Tangents"); } public void SetLeftEditable(object keysToSet) { SetTangent(0, TangentMode.Free, (List)keysToSet); } public void SetLeftLinear(object keysToSet) { SetTangent(0, TangentMode.Linear, (List)keysToSet); } public void SetLeftConstant(object keysToSet) { SetTangent(0, TangentMode.Constant, (List)keysToSet); } public void SetRightEditable(object keysToSet) { SetTangent(1, TangentMode.Free, (List)keysToSet); } public void SetRightLinear(object keysToSet) { SetTangent(1, TangentMode.Linear, (List)keysToSet); } public void SetRightConstant(object keysToSet) { SetTangent(1, TangentMode.Constant, (List)keysToSet); } public void SetBothEditable(object keysToSet) { SetTangent(2, TangentMode.Free, (List)keysToSet); } public void SetBothLinear(object keysToSet) { SetTangent(2, TangentMode.Linear, (List)keysToSet); } public void SetBothConstant(object keysToSet) { SetTangent(2, TangentMode.Constant, (List)keysToSet); } public void SetTangent(int leftRight, TangentMode mode, List keysToSet) { List changedCurves = new List(); foreach (KeyIdentifier keyToSet in keysToSet) { AnimationCurve animationCurve = keyToSet.curve; Keyframe key = keyToSet.keyframe; AnimationUtility.SetKeyBroken(ref key, true); if (leftRight == 2) { AnimationUtility.SetKeyLeftTangentMode(ref key, mode); AnimationUtility.SetKeyRightTangentMode(ref key, mode); } else { if (leftRight == 0) { AnimationUtility.SetKeyLeftTangentMode(ref key, mode); // Make sure other tangent is handled correctly if (AnimationUtility.GetKeyRightTangentMode(key) == TangentMode.ClampedAuto || AnimationUtility.GetKeyRightTangentMode(key) == TangentMode.Auto) AnimationUtility.SetKeyRightTangentMode(ref key, TangentMode.Free); } else //if (leftRight == 1) { AnimationUtility.SetKeyRightTangentMode(ref key, mode); // Make sure other tangent is handled correctly if (AnimationUtility.GetKeyLeftTangentMode(key) == TangentMode.ClampedAuto || AnimationUtility.GetKeyLeftTangentMode(key) == TangentMode.Auto) AnimationUtility.SetKeyLeftTangentMode(ref key, TangentMode.Free); } } if (mode == TangentMode.Constant && (leftRight == 0 || leftRight == 2)) key.inTangent = Mathf.Infinity; if (mode == TangentMode.Constant && (leftRight == 1 || leftRight == 2)) key.outTangent = Mathf.Infinity; animationCurve.MoveKey(keyToSet.key, key); AnimationUtility.UpdateTangentsFromModeSurrounding(animationCurve, keyToSet.key); // Debug.Log ("Before " + DebKey (key) + " after: " + DebKey (animationCurve[keyToSet.key])); ChangedCurve changedCurve = new ChangedCurve(animationCurve, keyToSet.curveId, keyToSet.binding); if (!changedCurves.Contains(changedCurve)) changedCurves.Add(changedCurve); } updater.UpdateCurves(changedCurves, "Set Tangents"); } /* string DebKey (Keyframe key) { return System.String.Format ("time:{0} value:{1} inTangent:{2} outTangent{3}", key.time, key.value, key.inTangent, key.outTangent); } */ } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveRenderer/BoolCurveRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Linq; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal class BoolCurveRenderer : NormalCurveRenderer { public BoolCurveRenderer(AnimationCurve curve) : base(curve) { } public override float ClampedValue(float value) { return value != 0.0f ? 1.0f : 0.0f; } public override float EvaluateCurveSlow(float time) { return ClampedValue(GetCurve().Evaluate(time)); } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveRenderer/CurveRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal interface CurveRenderer { void DrawCurve(float minTime, float maxTime, Color color, Matrix4x4 transform, Color wrapColor); AnimationCurve GetCurve(); float RangeStart(); float RangeEnd(); void SetWrap(WrapMode wrap); void SetWrap(WrapMode preWrap, WrapMode postWrap); void SetCustomRange(float start, float end); float EvaluateCurveSlow(float time); float EvaluateCurveDeltaSlow(float time); Bounds GetBounds(); Bounds GetBounds(float minTime, float maxTime); float ClampedValue(float value); void FlushCache(); } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveRenderer/EulerCurveCombinedRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal class EulerCurveCombinedRenderer { const int kSegmentResolution = 40; const float epsilon = 0.001f; private AnimationCurve quaternionX; private AnimationCurve quaternionY; private AnimationCurve quaternionZ; private AnimationCurve quaternionW; private AnimationCurve eulerX; private AnimationCurve eulerY; private AnimationCurve eulerZ; private SortedDictionary points; private float cachedEvaluationTime = Mathf.Infinity; private Vector3 cachedEvaluationValue; private float cachedRangeStart = Mathf.Infinity; private float cachedRangeEnd = Mathf.NegativeInfinity; private Vector3 refEuler; private float m_CustomRangeStart = 0; private float m_CustomRangeEnd = 0; private float rangeStart { get { return (m_CustomRangeStart == 0 && m_CustomRangeEnd == 0 && eulerX.length > 0 ? eulerX.keys[0].time : m_CustomRangeStart); } } private float rangeEnd { get { return (m_CustomRangeStart == 0 && m_CustomRangeEnd == 0 && eulerX.length > 0 ? eulerX.keys[eulerX.length - 1].time : m_CustomRangeEnd); } } private WrapMode preWrapMode = WrapMode.Once; private WrapMode postWrapMode = WrapMode.Once; public EulerCurveCombinedRenderer( AnimationCurve quaternionX, AnimationCurve quaternionY, AnimationCurve quaternionZ, AnimationCurve quaternionW, AnimationCurve eulerX, AnimationCurve eulerY, AnimationCurve eulerZ ) { this.quaternionX = (quaternionX == null ? new AnimationCurve() : quaternionX); this.quaternionY = (quaternionY == null ? new AnimationCurve() : quaternionY); this.quaternionZ = (quaternionZ == null ? new AnimationCurve() : quaternionZ); this.quaternionW = (quaternionW == null ? new AnimationCurve() : quaternionW); this.eulerX = (eulerX == null ? new AnimationCurve() : eulerX); this.eulerY = (eulerY == null ? new AnimationCurve() : eulerY); this.eulerZ = (eulerZ == null ? new AnimationCurve() : eulerZ); } public AnimationCurve GetCurveOfComponent(int component) { switch (component) { case 0: return eulerX; case 1: return eulerY; case 2: return eulerZ; default: return null; } } public float RangeStart() { return rangeStart; } public float RangeEnd() { return rangeEnd; } public WrapMode PreWrapMode() { return preWrapMode; } public WrapMode PostWrapMode() { return postWrapMode; } public void SetWrap(WrapMode wrap) { preWrapMode = wrap; postWrapMode = wrap; } public void SetWrap(WrapMode preWrap, WrapMode postWrap) { preWrapMode = preWrap; postWrapMode = postWrap; } public void SetCustomRange(float start, float end) { m_CustomRangeStart = start; m_CustomRangeEnd = end; } private Vector3 GetValues(float time, bool keyReference) { if (quaternionX == null) Debug.LogError("X curve is null!"); if (quaternionY == null) Debug.LogError("Y curve is null!"); if (quaternionZ == null) Debug.LogError("Z curve is null!"); if (quaternionW == null) Debug.LogError("W curve is null!"); Quaternion q; if (quaternionX.length != 0 && quaternionY.length != 0 && quaternionZ.length != 0 && quaternionW.length != 0) { q = EvaluateQuaternionCurvesDirectly(time); if (keyReference) refEuler = EvaluateEulerCurvesDirectly(time); refEuler = QuaternionCurveTangentCalculation.GetEulerFromQuaternion(q, refEuler); } else //euler curves only { refEuler = EvaluateEulerCurvesDirectly(time); } return refEuler; } private Quaternion EvaluateQuaternionCurvesDirectly(float time) { return new Quaternion( quaternionX.Evaluate(time), quaternionY.Evaluate(time), quaternionZ.Evaluate(time), quaternionW.Evaluate(time)); } private Vector3 EvaluateEulerCurvesDirectly(float time) { return new Vector3( eulerX.Evaluate(time), eulerY.Evaluate(time), eulerZ.Evaluate(time)); } private void CalculateCurves(float minTime, float maxTime) { points = new SortedDictionary(); float[,] ranges = NormalCurveRenderer.CalculateRanges(minTime, maxTime, rangeStart, rangeEnd, preWrapMode, postWrapMode); for (int i = 0; i < ranges.GetLength(0); i++) AddPoints(ranges[i, 0], ranges[i, 1]); } private void AddPoints(float minTime, float maxTime) { AnimationCurve refCurve = quaternionX; if (refCurve.length == 0) { refCurve = eulerX; } if (refCurve.length == 0) return; if (refCurve[0].time >= minTime) { Vector3 val = GetValues(refCurve[0].time, true); points[rangeStart] = val; points[refCurve[0].time] = val; } if (refCurve[refCurve.length - 1].time <= maxTime) { Vector3 val = GetValues(refCurve[refCurve.length - 1].time, true); points[refCurve[refCurve.length - 1].time] = val; points[rangeEnd] = val; } for (int i = 0; i < refCurve.length - 1; i++) { // Ignore segments that are outside of the range from minTime to maxTime if (refCurve[i + 1].time < minTime || refCurve[i].time > maxTime) continue; // Get first value from euler curve and move forwards float newTime = refCurve[i].time; points[newTime] = GetValues(newTime, true); // Iterate forwards through curve segments for (float j = 1; j <= kSegmentResolution / 2; j++) { newTime = Mathf.Lerp(refCurve[i].time, refCurve[i + 1].time, (j - 0.001f) / kSegmentResolution); points[newTime] = GetValues(newTime, false); } // Get last value from euler curve and move backwards newTime = refCurve[i + 1].time; points[newTime] = GetValues(newTime, true); // Iterate backwards through curve segment for (float j = 1; j <= kSegmentResolution / 2; j++) { newTime = Mathf.Lerp(refCurve[i].time, refCurve[i + 1].time, 1 - (j - 0.001f) / kSegmentResolution); points[newTime] = GetValues(newTime, false); } } } public float EvaluateCurveDeltaSlow(float time, int component) { if (quaternionX == null) return 0; return (EvaluateCurveSlow(time + epsilon, component) - EvaluateCurveSlow(time - epsilon, component)) / (epsilon * 2); } public float EvaluateCurveSlow(float time, int component) { if (GetCurveOfComponent(component).length == 1) { return GetCurveOfComponent(component)[0].value; } if (time == cachedEvaluationTime) return cachedEvaluationValue[component]; if (time < cachedRangeStart || time > cachedRangeEnd) { // if an evaluate call is outside of cached range we might as well calculate whole range CalculateCurves(rangeStart, rangeEnd); cachedRangeStart = Mathf.NegativeInfinity; cachedRangeEnd = Mathf.Infinity; } float[] times = new float[points.Count]; Vector3[] values = new Vector3[points.Count]; int c = 0; foreach (KeyValuePair kvp in points) { times[c] = kvp.Key; values[c] = kvp.Value; c++; } for (int i = 0; i < times.Length - 1; i++) { if (time < times[i + 1]) { float lerp = Mathf.InverseLerp(times[i], times[i + 1], time); cachedEvaluationValue = Vector3.Lerp(values[i], values[i + 1], lerp); cachedEvaluationTime = time; return cachedEvaluationValue[component]; } } if (values.Length > 0) return values[values.Length - 1][component]; Debug.LogError("List of euler curve points is empty, probably caused by lack of euler curve key synching"); return 0; } public void DrawCurve(float minTime, float maxTime, Color color, Matrix4x4 transform, int component, Color wrapColor) { if (minTime < cachedRangeStart || maxTime > cachedRangeEnd) { CalculateCurves(minTime, maxTime); if (minTime <= rangeStart && maxTime >= rangeEnd) { // if we are covering whole range cachedRangeStart = Mathf.NegativeInfinity; cachedRangeEnd = Mathf.Infinity; } else { cachedRangeStart = minTime; cachedRangeEnd = maxTime; } } List polyLine = new List(); foreach (KeyValuePair kvp in points) { polyLine.Add(new Vector3(kvp.Key, kvp.Value[component])); } NormalCurveRenderer.DrawCurveWrapped(minTime, maxTime, rangeStart, rangeEnd, preWrapMode, postWrapMode, color, transform, polyLine.ToArray(), wrapColor); } public Bounds GetBounds(float minTime, float maxTime, int component) { //if (alreadyDrawn[component]) //{ CalculateCurves(minTime, maxTime); // for (int i=0; i kvp in points) { if (kvp.Value[component] > max) max = kvp.Value[component]; if (kvp.Value[component] < min) min = kvp.Value[component]; } if (min == Mathf.Infinity) { min = 0; max = 0; } return new Bounds(new Vector3((maxTime + minTime) * 0.5f, (max + min) * 0.5f, 0), new Vector3(maxTime - minTime, max - min, 0)); } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveRenderer/EulerCurveRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal class EulerCurveRenderer : CurveRenderer { private int component; private EulerCurveCombinedRenderer renderer; public EulerCurveRenderer(int component, EulerCurveCombinedRenderer renderer) { this.component = component; this.renderer = renderer; } public AnimationCurve GetCurve() { return renderer.GetCurveOfComponent(component); } public float ClampedValue(float value) { return value; } public float RangeStart() { return renderer.RangeStart(); } public float RangeEnd() { return renderer.RangeEnd(); } public void SetWrap(WrapMode wrap) { renderer.SetWrap(wrap); } public void SetWrap(WrapMode preWrapMode, WrapMode postWrapMode) { renderer.SetWrap(preWrapMode, postWrapMode); } public void SetCustomRange(float start, float end) { renderer.SetCustomRange(start, end); } public float EvaluateCurveSlow(float time) { return renderer.EvaluateCurveSlow(time, component); } public float EvaluateCurveDeltaSlow(float time) { return renderer.EvaluateCurveDeltaSlow(time, component); } public void DrawCurve(float minTime, float maxTime, Color color, Matrix4x4 transform, Color wrapColor) { renderer.DrawCurve(minTime, maxTime, color, transform, component, wrapColor); } public Bounds GetBounds() { return GetBounds(renderer.RangeStart(), renderer.RangeEnd()); } public Bounds GetBounds(float minTime, float maxTime) { return renderer.GetBounds(minTime, maxTime, component); } public void FlushCache() { } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveRenderer/IntCurveRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Linq; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal class IntCurveRenderer : NormalCurveRenderer { const float kSegmentWindowResolution = 1000; const int kMaximumSampleCount = 1000; const float kStepHelperOffset = 0.000001f; public IntCurveRenderer(AnimationCurve curve) : base(curve) { } public override float ClampedValue(float value) { return Mathf.Floor(value + 0.5f); } public override float EvaluateCurveSlow(float time) { return ClampedValue(GetCurve().Evaluate(time)); } protected override int GetSegmentResolution(float minTime, float maxTime, float keyTime, float nextKeyTime) { float fullTimeRange = maxTime - minTime; float keyTimeRange = nextKeyTime - keyTime; int count = Mathf.RoundToInt(kSegmentWindowResolution * (keyTimeRange / fullTimeRange)); return Mathf.Clamp(count, 1, kMaximumSampleCount); } protected override void AddPoint(ref List points, ref float lastTime, float sampleTime, ref float lastValue, float sampleValue) { if (lastValue != sampleValue) { points.Add(new Vector3(lastTime + kStepHelperOffset, sampleValue)); } points.Add(new Vector3(sampleTime, sampleValue)); lastTime = sampleTime; lastValue = sampleValue; } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/CurveRenderer/NormalCurveRenderer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.Linq; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal class NormalCurveRenderer : CurveRenderer { const float kSegmentWindowResolution = 1000; const int kMaximumSampleCount = 50; const int kMaximumLoops = 100; const string kCurveRendererMeshName = "NormalCurveRendererMesh"; private AnimationCurve m_Curve; private float m_CustomRangeStart = 0; private float m_CustomRangeEnd = 0; private float rangeStart { get { return (m_CustomRangeStart == 0 && m_CustomRangeEnd == 0 && m_Curve.length > 0 ? m_Curve[0].time : m_CustomRangeStart); } } private float rangeEnd { get { return (m_CustomRangeStart == 0 && m_CustomRangeEnd == 0 && m_Curve.length > 0 ? m_Curve[m_Curve.length - 1].time : m_CustomRangeEnd); } } private WrapMode preWrapMode = WrapMode.Once; private WrapMode postWrapMode = WrapMode.Once; private Bounds? m_Bounds; private Mesh m_CurveMesh; private static Material s_CurveMaterial; public static Material curveMaterial { get { if (!s_CurveMaterial) { Shader shader = (Shader)EditorGUIUtility.LoadRequired("Editors/AnimationWindow/Curve.shader"); s_CurveMaterial = new Material(shader); s_CurveMaterial.hideFlags = HideFlags.HideAndDontSave; } return s_CurveMaterial; } } public NormalCurveRenderer(AnimationCurve curve) { m_Curve = curve; if (m_Curve == null) m_Curve = new AnimationCurve(); } public AnimationCurve GetCurve() { return m_Curve; } public float RangeStart() { return rangeStart; } public float RangeEnd() { return rangeEnd; } public void SetWrap(WrapMode wrap) { this.preWrapMode = wrap; this.postWrapMode = wrap; } public void SetWrap(WrapMode preWrap, WrapMode postWrap) { this.preWrapMode = preWrap; this.postWrapMode = postWrap; } public void SetCustomRange(float start, float end) { m_CustomRangeStart = start; m_CustomRangeEnd = end; } public virtual float ClampedValue(float value) { return value; } public virtual float EvaluateCurveSlow(float time) { return m_Curve.Evaluate(time); } // TODO: Implement proper analytic evaluation of curve delta instead of this numeric hack public float EvaluateCurveDeltaSlow(float time) { float epsilon = 0.0001f; return (EvaluateCurveSlow(time + epsilon) - EvaluateCurveSlow(time - epsilon)) / (epsilon * 2); } private Vector3[] GetPoints() { return GetPoints(rangeStart, rangeEnd); } private Vector3[] GetPoints(float minTime, float maxTime) { List points = new List(); if (m_Curve.length == 0) return points.ToArray(); points.Capacity = 1000 + m_Curve.length; float[,] ranges = CalculateRanges(minTime, maxTime, rangeStart, rangeEnd, preWrapMode, postWrapMode); for (int i = 0; i < ranges.GetLength(0); i++) AddPoints(ref points, ranges[i, 0], ranges[i, 1], minTime, maxTime); // Remove points that don't go in ascending time order if (points.Count > 0) { for (int i = 1; i < points.Count; i++) { if (points[i].x < points[i - 1].x) { points.RemoveAt(i); i--; } } } return points.ToArray(); } public static float[,] CalculateRanges(float minTime, float maxTime, float rangeStart, float rangeEnd, WrapMode preWrapMode, WrapMode postWrapMode) { // Don't want to deal with optimizing for different pre and post wrap mode for now WrapMode wrap = preWrapMode; if (postWrapMode != wrap) return new float[1, 2] {{rangeStart, rangeEnd}}; if (wrap == WrapMode.Loop) { // If we are covering a range longer than a full loop, just add all points: if (maxTime - minTime > rangeEnd - rangeStart) { return new float[1, 2] {{rangeStart, rangeEnd}}; } // Else, only add the needed range(s) else { // Find the start and end of needed shown range repeated into the range of the curve minTime = Mathf.Repeat(minTime - rangeStart, rangeEnd - rangeStart) + rangeStart; maxTime = Mathf.Repeat(maxTime - rangeStart, rangeEnd - rangeStart) + rangeStart; if (minTime < maxTime) return new float[1, 2] {{minTime, maxTime}}; else return new float[2, 2] {{rangeStart, maxTime}, {minTime, rangeEnd}}; } } else if (wrap == WrapMode.PingPong) { // TODO: Maybe optimize so not whole range is calculated if not needed return new float[1, 2] {{rangeStart, rangeEnd}}; } else return new float[1, 2] {{minTime, maxTime}}; } protected virtual int GetSegmentResolution(float minTime, float maxTime, float keyTime, float nextKeyTime) { float fullTimeRange = maxTime - minTime; float keyTimeRange = nextKeyTime - keyTime; int count = Mathf.RoundToInt(kSegmentWindowResolution * (keyTimeRange / fullTimeRange)); return Mathf.Clamp(count, 1, kMaximumSampleCount); } protected virtual void AddPoint(ref List points, ref float lastTime, float sampleTime, ref float lastValue, float sampleValue) { points.Add(new Vector3(sampleTime, sampleValue)); lastTime = sampleTime; lastValue = sampleValue; } private void AddPoints(ref List points, float minTime, float maxTime, float visibleMinTime, float visibleMaxTime) { if (m_Curve[0].time >= minTime) { points.Add(new Vector3(rangeStart, ClampedValue(m_Curve[0].value))); points.Add(new Vector3(m_Curve[0].time, ClampedValue(m_Curve[0].value))); } for (int i = 0; i < m_Curve.length - 1; i++) { Keyframe key = m_Curve[i]; Keyframe nextKey = m_Curve[i + 1]; // Ignore segments that are outside of the range from minTime to maxTime if (nextKey.time < minTime || key.time > maxTime) continue; // Get first value from actual key rather than evaluating curve (to correctly handle stepped interpolation) points.Add(new Vector3(key.time, key.value)); // Place second sample very close to first one (to correctly handle stepped interpolation) int segmentResolution = GetSegmentResolution(visibleMinTime, visibleMaxTime, key.time, nextKey.time); float newTime = Mathf.Lerp(key.time, nextKey.time, 0.001f / segmentResolution); float lastTime = key.time; float lastValue = ClampedValue(key.value); float value = EvaluateCurveSlow(newTime); AddPoint(ref points, ref lastTime, newTime, ref lastValue, value); // Iterate through curve segment for (float j = 1; j < segmentResolution; j++) { newTime = Mathf.Lerp(key.time, nextKey.time, j / segmentResolution); value = EvaluateCurveSlow(newTime); AddPoint(ref points, ref lastTime, newTime, ref lastValue, value); } // Place second last sample very close to last one (to correctly handle stepped interpolation) newTime = Mathf.Lerp(key.time, nextKey.time, 1 - 0.001f / segmentResolution); value = EvaluateCurveSlow(newTime); AddPoint(ref points, ref lastTime, newTime, ref lastValue, value); // Get last value from actual key rather than evaluating curve (to correctly handle stepped interpolation) newTime = nextKey.time; AddPoint(ref points, ref lastTime, newTime, ref lastValue, value); } if (m_Curve[m_Curve.length - 1].time <= maxTime) { float clampedValue = ClampedValue(m_Curve[m_Curve.length - 1].value); points.Add(new Vector3(m_Curve[m_Curve.length - 1].time, clampedValue)); points.Add(new Vector3(rangeEnd, clampedValue)); } } private void BuildCurveMesh() { if (m_CurveMesh != null) return; Vector3[] vertices = GetPoints(); m_CurveMesh = new Mesh(); m_CurveMesh.name = kCurveRendererMeshName; m_CurveMesh.hideFlags |= HideFlags.DontSave; m_CurveMesh.vertices = vertices; if (vertices.Length > 0) { int nIndices = vertices.Length - 1; int index = 0; List indices = new List(nIndices * 2); while (index < nIndices) { indices.Add(index); indices.Add(++index); } m_CurveMesh.SetIndices(indices.ToArray(), MeshTopology.Lines, 0); } } public void DrawCurve(float minTime, float maxTime, Color color, Matrix4x4 transform, Color wrapColor) { BuildCurveMesh(); int numKeys = m_Curve.length; int first = 0; int last = Mathf.Max(first, numKeys - 1); if (numKeys > 0) { Vector3 firstPoint = new Vector3(rangeStart, m_Curve[first].value); Vector3 lastPoint = new Vector3(rangeEnd, m_Curve[last].value); DrawCurveWrapped(minTime, maxTime, rangeStart, rangeEnd, preWrapMode, postWrapMode, m_CurveMesh, firstPoint, lastPoint, transform, color, wrapColor); } } public static void DrawPolyLine(Matrix4x4 transform, float minDistance, params Vector3[] points) { if (Event.current.type != EventType.Repaint) return; Color col = Handles.color * new Color(1, 1, 1, 0.75f); HandleUtility.ApplyWireMaterial(); GL.PushMatrix(); GL.MultMatrix(Handles.matrix); GL.Begin(GL.LINES); GL.Color(col); Vector3 previous = transform.MultiplyPoint(points[0]); for (int i = 1; i < points.Length; i++) { Vector3 current = transform.MultiplyPoint(points[i]); if ((previous - current).magnitude > minDistance) { GL.Vertex(previous); GL.Vertex(current); previous = current; } } GL.End(); GL.PopMatrix(); } public static void DrawCurveWrapped(float minTime, float maxTime, float rangeStart, float rangeEnd, WrapMode preWrap, WrapMode postWrap, Mesh mesh, Vector3 firstPoint, Vector3 lastPoint, Matrix4x4 transform, Color color, Color wrapColor) { if (mesh.vertexCount == 0) return; if (Event.current.type != EventType.Repaint) return; int minRep; int maxRep; if (rangeEnd - rangeStart != 0) { minRep = Mathf.FloorToInt((minTime - rangeStart) / (rangeEnd - rangeStart)); maxRep = Mathf.CeilToInt((maxTime - rangeEnd) / (rangeEnd - rangeStart)); // Prevent too many loops from being rendered at one time. if (minRep < -kMaximumLoops) preWrap = WrapMode.Clamp; if (maxRep > kMaximumLoops) postWrap = WrapMode.Clamp; } else { preWrap = WrapMode.Clamp; postWrap = WrapMode.Clamp; minRep = (minTime < rangeStart ? -1 : 0); maxRep = (maxTime > rangeEnd ? 1 : 0); } // Draw curve itself Material mat = curveMaterial; mat.SetColor("_Color", color); Handles.color = color; // Previous camera may still be active when calling DrawMeshNow. Camera.SetupCurrent(null); mat.SetPass(0); Graphics.DrawMeshNow(mesh, Handles.matrix * transform); // Draw wrapping mat.SetColor("_Color", new Color(wrapColor.r, wrapColor.g, wrapColor.b, wrapColor.a * color.a)); Handles.color = new Color(wrapColor.r, wrapColor.g, wrapColor.b, wrapColor.a * color.a); // Draw pre wrapping if (preWrap == WrapMode.Loop) { Matrix4x4 firstTransform = Handles.matrix * transform * Matrix4x4.TRS(new Vector3(minRep * (rangeEnd - rangeStart), 0.0f, 0.0f), Quaternion.identity, Vector3.one); Matrix4x4 iterTransform = Matrix4x4.TRS(new Vector3(rangeEnd - rangeStart, 0.0f, 0.0f), Quaternion.identity, Vector3.one); // Render loop curve itself. mat.SetPass(0); Matrix4x4 curTransform = firstTransform; for (int r = minRep; r < 0; ++r) { Graphics.DrawMeshNow(mesh, curTransform); curTransform = curTransform * iterTransform; } // Render straight lines connecting one loop to another. curTransform = firstTransform; for (int r = minRep; r < 0; ++r) { Matrix4x4 nextTransform = curTransform * iterTransform; Handles.DrawLine( curTransform.MultiplyPoint(lastPoint), nextTransform.MultiplyPoint(firstPoint) ); curTransform = nextTransform; } } else if (preWrap == WrapMode.PingPong) { mat.SetPass(0); for (int r = minRep; r < 0; ++r) { // Even loop if (r % 2 == 0) { Matrix4x4 shiftTransform = Matrix4x4.TRS(new Vector3(r * (rangeEnd - rangeStart), 0.0f, 0.0f), Quaternion.identity, Vector3.one); Graphics.DrawMeshNow(mesh, Handles.matrix * transform * shiftTransform); } // Odd loop else { Matrix4x4 shiftTransform = Matrix4x4.TRS(new Vector3((r + 1) * (rangeEnd - rangeStart) + rangeStart * 2, 0.0f, 0.0f), Quaternion.identity, new Vector3(-1.0f, 1.0f, 1.0f)); Graphics.DrawMeshNow(mesh, Handles.matrix * transform * shiftTransform); } } } else { if (minRep < 0) { Handles.DrawLine( transform.MultiplyPoint(new Vector3(minTime, firstPoint.y, 0)), transform.MultiplyPoint(new Vector3(Mathf.Min(maxTime, firstPoint.x), firstPoint.y, 0)) ); } } // Draw post wrapping if (postWrap == WrapMode.Loop) { Matrix4x4 firstTransform = Handles.matrix * transform; Matrix4x4 iterTransform = Matrix4x4.TRS(new Vector3(rangeEnd - rangeStart, 0.0f, 0.0f), Quaternion.identity, Vector3.one); // Render straight lines connecting one loop to another. Matrix4x4 prevTransform = firstTransform; for (int r = 1; r <= maxRep; ++r) { Matrix4x4 curTransform = prevTransform * iterTransform; Handles.DrawLine( prevTransform.MultiplyPoint(lastPoint), curTransform.MultiplyPoint(firstPoint) ); prevTransform = curTransform; } // Render loop curve itself. mat.SetPass(0); prevTransform = firstTransform; for (int r = 1; r <= maxRep; ++r) { Matrix4x4 curTransform = prevTransform * iterTransform; Graphics.DrawMeshNow(mesh, curTransform); prevTransform = curTransform; } } else if (postWrap == WrapMode.PingPong) { mat.SetPass(0); for (int r = 1; r <= maxRep; ++r) { // Even loop if (r % 2 == 0) { Matrix4x4 shiftTransform = Matrix4x4.TRS(new Vector3(r * (rangeEnd - rangeStart), 0.0f, 0.0f), Quaternion.identity, Vector3.one); Graphics.DrawMeshNow(mesh, Handles.matrix * transform * shiftTransform); } // Odd loop else { Matrix4x4 shiftTransform = Matrix4x4.TRS(new Vector3((r + 1) * (rangeEnd - rangeStart) + rangeStart * 2, 0.0f, 0.0f), Quaternion.identity, new Vector3(-1.0f, 1.0f, 1.0f)); Graphics.DrawMeshNow(mesh, Handles.matrix * transform * shiftTransform); } } } else { if (maxRep > 0) { Handles.DrawLine( transform.MultiplyPoint(new Vector3(Mathf.Max(minTime, lastPoint.x), lastPoint.y, 0)), transform.MultiplyPoint(new Vector3(maxTime, lastPoint.y, 0)) ); } } } public static void DrawCurveWrapped(float minTime, float maxTime, float rangeStart, float rangeEnd, WrapMode preWrap, WrapMode postWrap, Color color, Matrix4x4 transform, Vector3[] points, Color wrapColor) { if (points.Length == 0) return; int minRep; int maxRep; if (rangeEnd - rangeStart != 0) { minRep = Mathf.FloorToInt((minTime - rangeStart) / (rangeEnd - rangeStart)); maxRep = Mathf.CeilToInt((maxTime - rangeEnd) / (rangeEnd - rangeStart)); // Prevent too many loops from being rendered at one time. if (minRep < -kMaximumLoops) preWrap = WrapMode.Clamp; if (maxRep > kMaximumLoops) postWrap = WrapMode.Clamp; } else { preWrap = WrapMode.Clamp; postWrap = WrapMode.Clamp; minRep = (minTime < rangeStart ? -1 : 0); maxRep = (maxTime > rangeEnd ? 1 : 0); } int last = points.Length - 1; // Draw curve itself Handles.color = color; List line = new List(); if (minRep <= 0 && maxRep >= 0) { // Use line drawing with minimum segment length. Faster for large data sets DrawPolyLine(transform, 2, points); } else Handles.DrawPolyLine(points); // Draw wrapping Handles.color = new Color(wrapColor.r, wrapColor.g, wrapColor.b, wrapColor.a * color.a); // Draw pre wrapping if (preWrap == WrapMode.Loop) { line = new List(); for (int r = minRep; r < 0; r++) { for (int i = 0; i < points.Length; i++) { Vector3 point = points[i]; point.x += r * (rangeEnd - rangeStart); point = transform.MultiplyPoint(point); line.Add(point); } } line.Add(transform.MultiplyPoint(points[0])); Handles.DrawPolyLine(line.ToArray()); } else if (preWrap == WrapMode.PingPong) { line = new List(); for (int r = minRep; r < 0; r++) { for (int i = 0; i < points.Length; i++) { if (r / 2 == r / 2f) { Vector3 point = points[i]; point.x += r * (rangeEnd - rangeStart); point = transform.MultiplyPoint(point); line.Add(point); } else { Vector3 point = points[last - i]; point.x = -point.x + (r + 1) * (rangeEnd - rangeStart) + rangeStart * 2; point = transform.MultiplyPoint(point); line.Add(point); } } } Handles.DrawPolyLine(line.ToArray()); } else { if (minRep < 0) { Handles.DrawPolyLine(new Vector3[] { transform.MultiplyPoint(new Vector3(minTime, points[0].y, 0)), transform.MultiplyPoint(new Vector3(Mathf.Min(maxTime, points[0].x), points[0].y, 0)) }); } } // Draw post wrapping if (postWrap == WrapMode.Loop) { line = new List(); line.Add(transform.MultiplyPoint(points[last])); for (int r = 1; r <= maxRep; r++) { for (int i = 0; i < points.Length; i++) { Vector3 point = points[i]; point.x += r * (rangeEnd - rangeStart); point = transform.MultiplyPoint(point); line.Add(point); } } Handles.DrawPolyLine(line.ToArray()); } else if (postWrap == WrapMode.PingPong) { line = new List(); for (int r = 1; r <= maxRep; r++) { for (int i = 0; i < points.Length; i++) { if (r / 2 == r / 2f) { Vector3 point = points[i]; point.x += r * (rangeEnd - rangeStart); point = transform.MultiplyPoint(point); line.Add(point); } else { Vector3 point = points[last - i]; point.x = -point.x + (r + 1) * (rangeEnd - rangeStart) + rangeStart * 2; point = transform.MultiplyPoint(point); line.Add(point); } } } Handles.DrawPolyLine(line.ToArray()); } else { if (maxRep > 0) { Handles.DrawPolyLine(new Vector3[] { transform.MultiplyPoint(new Vector3(Mathf.Max(minTime, points[last].x), points[last].y, 0)), transform.MultiplyPoint(new Vector3(maxTime, points[last].y, 0)) }); } } } public Bounds GetBounds() { BuildCurveMesh(); if (m_Bounds == null) m_Bounds = m_CurveMesh.bounds; return m_Bounds.Value; } public Bounds GetBounds(float minTime, float maxTime) { Vector3[] points = GetPoints(minTime, maxTime); float min = Mathf.Infinity; float max = Mathf.NegativeInfinity; for (int i = 0; i < points.Length; ++i) { Vector3 point = points[i]; if (point.y > max) max = point.y; if (point.y < min) min = point.y; } if (min == Mathf.Infinity) { min = 0; max = 0; } return new Bounds(new Vector3((maxTime + minTime) * 0.5f, (max + min) * 0.5f, 0), new Vector3(maxTime - minTime, max - min, 0)); } public void FlushCache() { Object.DestroyImmediate(m_CurveMesh); } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/Deprecated/AnimationEventTimeline.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using System.Collections.Generic; using UnityEditorInternal; using System.Linq; using Object = UnityEngine.Object; namespace UnityEditor { [System.Serializable] internal class AnimationEventTimeLine { internal static class Styles { public static GUIContent textAddEvent = EditorGUIUtility.TrTextContent("Add Animation Event"); public static GUIContent textDeleteEvents = EditorGUIUtility.TrTextContent("Delete Animation Events"); public static GUIContent textDeleteEvent = EditorGUIUtility.TrTextContent("Delete Animation Event"); public static GUIContent textCopyEvents = EditorGUIUtility.TrTextContent("Copy Animation Events"); public static GUIContent textPasteEvents = EditorGUIUtility.TrTextContent("Paste Animation Events"); } [System.NonSerialized] private AnimationEvent[] m_EventsAtMouseDown; [System.NonSerialized] private float[] m_EventTimes; private static readonly Vector2 k_EventMarkerSize = new Vector2(16, 16); private bool m_DirtyTooltip = false; private int m_HoverEvent = -1; private string m_InstantTooltipText = null; private Vector2 m_InstantTooltipPoint = Vector2.zero; private bool m_HasSelectedEvents; public AnimationEventTimeLine(EditorWindow owner) { } public class EventComparer : IComparer { public int Compare(AnimationEvent x, AnimationEvent y) { float timeX = x.time; float timeY = y.time; if (timeX != timeY) return ((int)Mathf.Sign(timeX - timeY)); int valueX = x.GetHashCode(); int valueY = y.GetHashCode(); return valueX - valueY; } } private struct EventLineContextMenuObject { public GameObject m_Animated; public AnimationClip m_Clip; public float m_Time; public int m_Index; public bool[] m_Selected; public EventLineContextMenuObject(GameObject animated, AnimationClip clip, float time, int index, bool[] selected) { m_Animated = animated; m_Clip = clip; m_Time = time; m_Index = index; m_Selected = selected; } } internal bool HasSelectedEvents => m_HasSelectedEvents; public void AddEvent(float time, GameObject gameObject, AnimationClip animationClip) { AnimationWindowEvent awEvent = AnimationWindowEvent.CreateAndEdit(gameObject, animationClip, time); Selection.activeObject = awEvent; } public void EditEvents(GameObject gameObject, AnimationClip clip, bool[] selectedIndices) { List awEvents = new List(); for (int index = 0; index < selectedIndices.Length; ++index) { if (selectedIndices[index]) awEvents.Add(AnimationWindowEvent.Edit(gameObject, clip, index)); } if (awEvents.Count > 0) { Selection.objects = awEvents.ToArray(); } else { ClearSelection(); } } public void EditEvent(GameObject gameObject, AnimationClip clip, int index) { AnimationWindowEvent awEvent = AnimationWindowEvent.Edit(gameObject, clip, index); Selection.activeObject = awEvent; } public void ClearSelection() { // Do not unecessarily clear selection. Only clear if selection already is animation window event. if (Selection.activeObject is AnimationWindowEvent) Selection.activeObject = null; } public void DeleteEvents(AnimationClip clip, bool[] deleteIndices) { bool deletedAny = false; List eventList = new List(AnimationUtility.GetAnimationEvents(clip)); for (int i = eventList.Count - 1; i >= 0; i--) { if (deleteIndices[i]) { eventList.RemoveAt(i); deletedAny = true; } } if (deletedAny) { Undo.RegisterCompleteObjectUndo(clip, "Delete Event"); AnimationUtility.SetAnimationEvents(clip, eventList.ToArray()); Selection.objects = new AnimationWindowEvent[] {}; m_DirtyTooltip = true; } } void CopyEvents(AnimationClip clip, bool[] selected, int explicitIndex = -1) { var allEvents = new List(AnimationUtility.GetAnimationEvents(clip)); AnimationWindowEventsClipboard.CopyEvents(allEvents, selected, explicitIndex); } internal void PasteEvents(GameObject animated, AnimationClip clip, float time) { var oldEvents = AnimationUtility.GetAnimationEvents(clip); var newEvents = AnimationWindowEventsClipboard.AddPastedEvents(oldEvents, time, out var selected); if (newEvents == null) return; Undo.RegisterCompleteObjectUndo(clip, "Paste Events"); EditEvents(animated, clip, selected); AnimationUtility.SetAnimationEvents(clip, newEvents); m_DirtyTooltip = true; } public void EventLineGUI(Rect rect, AnimationWindowState state) { // We only display and manipulate animation events from the main // game object in selection. If we ever want to update to handle // a multiple selection, a single timeline might not be sufficient... AnimationClip clip = state.activeAnimationClip; GameObject animated = state.activeRootGameObject; GUI.BeginGroup(rect); Color backupCol = GUI.color; Rect eventLineRect = new Rect(0, 0, rect.width, rect.height); float mousePosTime = Mathf.Max(Mathf.RoundToInt(state.PixelToTime(Event.current.mousePosition.x, rect) * state.frameRate) / state.frameRate, 0.0f); // Draw events if (clip != null) { AnimationEvent[] events = AnimationUtility.GetAnimationEvents(clip); Texture eventMarker = EditorGUIUtility.IconContent("Animation.EventMarker").image; // Calculate rects Rect[] hitRects = new Rect[events.Length]; Rect[] drawRects = new Rect[events.Length]; int shared = 1; int sharedLeft = 0; for (int i = 0; i < events.Length; i++) { AnimationEvent evt = events[i]; if (sharedLeft == 0) { shared = 1; while (i + shared < events.Length && events[i + shared].time == evt.time) shared++; sharedLeft = shared; } sharedLeft--; // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. float keypos = Mathf.Floor(state.FrameToPixel(evt.time * clip.frameRate, rect)); int sharedOffset = 0; if (shared > 1) { float spread = Mathf.Min((shared - 1) * (k_EventMarkerSize.x - 1), (int)(state.FrameDeltaToPixel(rect) - k_EventMarkerSize.x * 2)); sharedOffset = Mathf.FloorToInt(Mathf.Max(0, spread - (k_EventMarkerSize.x - 1) * (sharedLeft))); } Rect r = new Rect( keypos + sharedOffset - k_EventMarkerSize.x / 2, (rect.height - 10) * (float)(sharedLeft - shared + 1) / Mathf.Max(1, shared - 1), k_EventMarkerSize.x, k_EventMarkerSize.y); hitRects[i] = r; drawRects[i] = r; } // Store tooptip info if (m_DirtyTooltip) { if (m_HoverEvent >= 0 && m_HoverEvent < hitRects.Length) { m_InstantTooltipText = AnimationWindowEventInspector.FormatEvent(animated, events[m_HoverEvent]); m_InstantTooltipPoint = new Vector2(hitRects[m_HoverEvent].xMin + (int)(hitRects[m_HoverEvent].width / 2) + rect.x - 30, rect.yMax); } m_DirtyTooltip = false; } bool[] selectedEvents = new bool[events.Length]; m_HasSelectedEvents = false; Object[] selectedObjects = Selection.objects; foreach (Object selectedObject in selectedObjects) { AnimationWindowEvent awe = selectedObject as AnimationWindowEvent; if (awe != null) { if (awe.eventIndex >= 0 && awe.eventIndex < selectedEvents.Length) { selectedEvents[awe.eventIndex] = true; m_HasSelectedEvents = true; } } } Vector2 offset = Vector2.zero; int clickedIndex; float startSelection, endSelection; // TODO: GUIStyle.none has hopping margins that need to be fixed HighLevelEvent hEvent = EditorGUIExt.MultiSelection( rect, drawRects, new GUIContent(eventMarker), hitRects, ref selectedEvents, null, out clickedIndex, out offset, out startSelection, out endSelection, GUIStyle.none ); if (hEvent != HighLevelEvent.None) { switch (hEvent) { case HighLevelEvent.BeginDrag: m_EventsAtMouseDown = events; m_EventTimes = new float[events.Length]; for (int i = 0; i < events.Length; i++) m_EventTimes[i] = events[i].time; break; case HighLevelEvent.SelectionChanged: state.ClearKeySelections(); EditEvents(animated, clip, selectedEvents); break; case HighLevelEvent.Delete: DeleteEvents(clip, selectedEvents); break; case HighLevelEvent.Copy: CopyEvents(clip, selectedEvents); break; case HighLevelEvent.Paste: PasteEvents(animated, clip, state.currentTime); break; case HighLevelEvent.DoubleClick: if (clickedIndex != -1) EditEvents(animated, clip, selectedEvents); else EventLineContextMenuAdd(new EventLineContextMenuObject(animated, clip, mousePosTime, -1, selectedEvents)); break; case HighLevelEvent.Drag: for (int i = events.Length - 1; i >= 0; i--) { if (selectedEvents[i]) { AnimationEvent evt = m_EventsAtMouseDown[i]; evt.time = m_EventTimes[i] + offset.x * state.PixelDeltaToTime(rect); evt.time = Mathf.Max(0.0F, evt.time); evt.time = Mathf.RoundToInt(evt.time * clip.frameRate) / clip.frameRate; } } int[] order = new int[selectedEvents.Length]; for (int i = 0; i < order.Length; i++) { order[i] = i; } System.Array.Sort(m_EventsAtMouseDown, order, new EventComparer()); bool[] selectedOld = (bool[])selectedEvents.Clone(); float[] timesOld = (float[])m_EventTimes.Clone(); for (int i = 0; i < order.Length; i++) { selectedEvents[i] = selectedOld[order[i]]; m_EventTimes[i] = timesOld[order[i]]; } // Update selection to reflect new order. EditEvents(animated, clip, selectedEvents); Undo.RegisterCompleteObjectUndo(clip, "Move Event"); AnimationUtility.SetAnimationEvents(clip, m_EventsAtMouseDown); m_DirtyTooltip = true; break; case HighLevelEvent.ContextClick: CreateContextMenu(animated, clip, events[clickedIndex].time, clickedIndex, selectedEvents); // Mouse may move while context menu is open - make sure instant tooltip is handled m_InstantTooltipText = null; m_DirtyTooltip = true; state.Repaint(); break; } } CheckRectsOnMouseMove(rect, events, hitRects); // Bring up menu when context-clicking on an empty timeline area (context-clicking on events is handled above) if (Event.current.type == EventType.ContextClick && eventLineRect.Contains(Event.current.mousePosition)) { Event.current.Use(); CreateContextMenu(animated, clip, mousePosTime, -1, selectedEvents); } } GUI.color = backupCol; GUI.EndGroup(); } void CreateContextMenu(GameObject animatedGo, AnimationClip clip, float time, int eventIndex, bool[] selectedEvents) { GenericMenu menu = new GenericMenu(); var contextData = new EventLineContextMenuObject(animatedGo, clip, time, eventIndex, selectedEvents); var selectedCount = selectedEvents.Count(selected => selected); menu.AddItem(Styles.textAddEvent, false, EventLineContextMenuAdd, contextData); if (selectedCount > 0 || eventIndex != -1) { menu.AddItem(selectedCount > 1 ? Styles.textDeleteEvents : Styles.textDeleteEvent, false, EventLineContextMenuDelete, contextData); menu.AddItem(Styles.textCopyEvents, false, EventLineContextMenuCopy, contextData); } else { menu.AddDisabledItem(Styles.textDeleteEvents); menu.AddDisabledItem(Styles.textCopyEvents); } if (AnimationWindowEventsClipboard.CanPaste()) menu.AddItem(Styles.textPasteEvents, false, EventLineContextMenuPaste, contextData); else menu.AddDisabledItem(Styles.textPasteEvents); menu.ShowAsContext(); } public void DrawInstantTooltip(Rect position) { if (!string.IsNullOrEmpty(m_InstantTooltipText)) { // Draw body of tooltip GUIStyle style = (GUIStyle)"AnimationEventTooltip"; // TODO: Move to editor_resources style.contentOffset = new Vector2(0f, 0f); style.overflow = new RectOffset(10, 10, 0, 0); Vector2 size = style.CalcSize(new GUIContent(m_InstantTooltipText)); Rect rect = new Rect(m_InstantTooltipPoint.x - size.x * .5f, m_InstantTooltipPoint.y + 24, size.x, size.y); // Right align tooltip rect if it would otherwise exceed the bounds of the window if (rect.xMax > position.width) rect.x = position.width - rect.width; GUI.Label(rect, m_InstantTooltipText, style); // Draw arrow of tooltip rect = new Rect(m_InstantTooltipPoint.x - 33, m_InstantTooltipPoint.y, 7, 25); GUI.Label(rect, "", "AnimationEventTooltipArrow"); } } public void EventLineContextMenuAdd(object obj) { EventLineContextMenuObject eventObj = (EventLineContextMenuObject)obj; AddEvent(eventObj.m_Time, eventObj.m_Animated, eventObj.m_Clip); } public void EventLineContextMenuEdit(object obj) { EventLineContextMenuObject eventObj = (EventLineContextMenuObject)obj; if (Array.Exists(eventObj.m_Selected, selected => selected)) { EditEvents(eventObj.m_Animated, eventObj.m_Clip, eventObj.m_Selected); } else if (eventObj.m_Index >= 0) { EditEvent(eventObj.m_Animated, eventObj.m_Clip, eventObj.m_Index); } } public void EventLineContextMenuDelete(object obj) { EventLineContextMenuObject eventObj = (EventLineContextMenuObject)obj; AnimationClip clip = eventObj.m_Clip; if (clip == null) return; int clickedIndex = eventObj.m_Index; // If a selection already exists, delete selection instead of clicked index if (Array.Exists(eventObj.m_Selected, selected => selected)) { DeleteEvents(clip, eventObj.m_Selected); } // Else, only delete the clicked animation event else if (clickedIndex >= 0) { bool[] deleteIndices = new bool[eventObj.m_Selected.Length]; deleteIndices[clickedIndex] = true; DeleteEvents(clip, deleteIndices); } } void EventLineContextMenuCopy(object obj) { var ctx = (EventLineContextMenuObject)obj; var clip = ctx.m_Clip; if (clip != null) CopyEvents(clip, ctx.m_Selected, ctx.m_Index); } void EventLineContextMenuPaste(object obj) { var ctx = (EventLineContextMenuObject)obj; AnimationClip clip = ctx.m_Clip; if (clip != null) PasteEvents(ctx.m_Animated, clip, ctx.m_Time); } private void CheckRectsOnMouseMove(Rect eventLineRect, AnimationEvent[] events, Rect[] hitRects) { Vector2 mouse = Event.current.mousePosition; bool hasFound = false; if (events.Length == hitRects.Length) { for (int i = hitRects.Length - 1; i >= 0; i--) { if (hitRects[i].Contains(mouse)) { hasFound = true; if (m_HoverEvent != i) { m_HoverEvent = i; m_InstantTooltipText = events[m_HoverEvent].functionName; m_InstantTooltipPoint = new Vector2(hitRects[m_HoverEvent].xMin + (int)(hitRects[m_HoverEvent].width / 2) + eventLineRect.x, eventLineRect.yMax); m_DirtyTooltip = true; } } } } if (!hasFound) { m_HoverEvent = -1; m_InstantTooltipText = ""; } } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/Deprecated/EditorGUIExt.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { internal class EditorGUIExt { class Styles { public GUIStyle selectionRect = "SelectionRect"; } static Styles ms_Styles = new Styles(); // Copied from GUI class and modified slightly to not require // calls to methods that are internal to the GUI class static bool DoRepeatButton(Rect position, GUIContent content, GUIStyle style, FocusType focusType) { //GUIUtility.CheckOnGUI (); int id = GUIUtility.GetControlID(repeatButtonHash, focusType, position); switch (Event.current.GetTypeForControl(id)) { case EventType.MouseDown: // If the mouse is inside the button, we say that we're the hot control if (position.Contains(Event.current.mousePosition)) { GUIUtility.hotControl = id; Event.current.Use(); } return false; case EventType.MouseUp: if (GUIUtility.hotControl == id) { GUIUtility.hotControl = 0; // If we got the mousedown, the mouseup is ours as well // (no matter if the click was in the button or not) Event.current.Use(); // But we only return true if the button was actually clicked return position.Contains(Event.current.mousePosition); } return false; case EventType.Repaint: style.Draw(position, content, id, false, position.Contains(Event.current.mousePosition)); return id == GUIUtility.hotControl && position.Contains(Event.current.mousePosition); } return false; } static int repeatButtonHash = "repeatButton".GetHashCode(); static float nextScrollStepTime = 0; static int firstScrollWait = 250; // ms static int scrollWait = 30; // ms /// *undocumented* // Copied from GUI class and modified slightly to not require // calls to methods that are internal to the GUI class static bool ScrollerRepeatButton(int scrollerID, Rect rect, GUIStyle style) { bool changed = false; if (DoRepeatButton(rect, GUIContent.none, style, FocusType.Passive)) { bool firstClick = scrollControlID != scrollerID; scrollControlID = scrollerID; if (firstClick) { changed = true; nextScrollStepTime = Time.realtimeSinceStartup + 0.001f * firstScrollWait; } else { if (Time.realtimeSinceStartup >= nextScrollStepTime) { changed = true; nextScrollStepTime = Time.realtimeSinceStartup + 0.001f * scrollWait; } } if (Event.current.type == EventType.Repaint) // GUI.InternalRepaintEditorWindow(); HandleUtility.Repaint(); } return changed; } static int scrollControlID; public static void MinMaxScroller(Rect position, int id, ref float value, ref float size, float visualStart, float visualEnd, float startLimit, float endLimit, GUIStyle slider, GUIStyle thumb, GUIStyle leftButton, GUIStyle rightButton, bool horiz) { //GUIUtility.CheckOnGUI (); float scrollStepSize; if (horiz) scrollStepSize = size * 10 / position.width; else scrollStepSize = size * 10 / position.height; // Rect sliderRect, minRect, maxRect; if (horiz) { sliderRect = new Rect( position.x + leftButton.fixedWidth, position.y, position.width - leftButton.fixedWidth - rightButton.fixedWidth, position.height ); minRect = new Rect(position.x, position.y, leftButton.fixedWidth, position.height); maxRect = new Rect(position.xMax - rightButton.fixedWidth, position.y, rightButton.fixedWidth, position.height); } else { sliderRect = new Rect( position.x, position.y + leftButton.fixedHeight, position.width, position.height - leftButton.fixedHeight - rightButton.fixedHeight ); minRect = new Rect(position.x, position.y, position.width, leftButton.fixedHeight); maxRect = new Rect(position.x, position.yMax - rightButton.fixedHeight, position.width, rightButton.fixedHeight); } float newVisualStart = Mathf.Min(visualStart, value); float newVisualEnd = Mathf.Max(visualEnd , value + size); MinMaxSlider(sliderRect, ref value, ref size, newVisualStart, newVisualEnd, newVisualStart, newVisualEnd, slider, thumb, horiz); if (ScrollerRepeatButton(id, minRect, leftButton)) value -= scrollStepSize * (visualStart < visualEnd ? 1f : -1f); if (ScrollerRepeatButton(id, maxRect, rightButton)) value += scrollStepSize * (visualStart < visualEnd ? 1f : -1f); if (Event.current.type == EventType.MouseUp && Event.current.type == EventType.Used) // repeat buttons ate mouse up event - release scrolling scrollControlID = 0; if (startLimit < endLimit) value = Mathf.Clamp(value, startLimit, endLimit - size); else value = Mathf.Clamp(value, endLimit, startLimit - size); } // State for when we're dragging a MinMax slider. class MinMaxSliderState { public float dragStartPos = 0; // Start of the drag (mousePosition) public float dragStartValue = 0; // Value at start of drag. public float dragStartSize = 0; // Size at start of drag. public float dragStartValuesPerPixel = 0; public float dragStartLimit = 0; // start limit at start of drag public float dragEndLimit = 0; // end limit at start of drag public int whereWeDrag = -1; // which part are we dragging? 0 = middle, 1 = min, 2 = max, 3 = min trough, 4 = max trough } static MinMaxSliderState s_MinMaxSliderState; static int kFirstScrollWait = 250; // ms static int kScrollWait = 30; // ms static System.DateTime s_NextScrollStepTime = System.DateTime.Now; // whatever but null // Mouse down position for private static Vector2 s_MouseDownPos = Vector2.zero; // Are we doing a drag selection (as opposed to when the mousedown was over a selection rect) enum DragSelectionState { None, DragSelecting, Dragging } static DragSelectionState s_MultiSelectDragSelection = DragSelectionState.None; static Vector2 s_StartSelectPos = Vector2.zero; static List s_SelectionBackup = null; static List s_LastFrameSelections = null; internal static int s_MinMaxSliderHash = "MinMaxSlider".GetHashCode(); /// Make a double-draggable slider that will let you specify a range of values. /// @param position where to draw it /// @param value the current start position /// @param size the size of the covered range /// @param visualStart what is displayed as the start of the range. The user can drag beyond this, but the displays shows this as the limit. Set this to be the start of the relevant data. /// @param visualEnd what is displayed as the end of the range. The user can drag beyond this, but the displays shows this as the limit. Set this to be the end of the relevant data. /// @param startLimit what is the lowest possible value? The user can never slide beyond this in the minimum direction. If you don't want a limit, set it to -Mathf.Infinity /// @param endLimit what is the highes possible value? The user can never slide beyond this in the maximum direction. If you don't want a limit, set it to Mathf.Infinity public static void MinMaxSlider(Rect position, ref float value, ref float size, float visualStart, float visualEnd, float startLimit, float endLimit, GUIStyle slider, GUIStyle thumb, bool horiz) { DoMinMaxSlider(position, GUIUtility.GetControlID(s_MinMaxSliderHash, FocusType.Passive), ref value, ref size, visualStart, visualEnd, startLimit, endLimit, slider, thumb, horiz); } private static float ThumbSize(bool horiz, GUIStyle thumb) { if (horiz) return thumb.fixedWidth != 0 ? thumb.fixedWidth : thumb.padding.horizontal; return thumb.fixedHeight != 0 ? thumb.fixedHeight : thumb.padding.vertical; } internal static void DoMinMaxSlider(Rect position, int id, ref float value, ref float size, float visualStart, float visualEnd, float startLimit, float endLimit, GUIStyle slider, GUIStyle thumb, bool horiz) { Event evt = Event.current; bool usePageScrollbars = size == 0; float minVisual = Mathf.Min(visualStart, visualEnd); float maxVisual = Mathf.Max(visualStart, visualEnd); float minLimit = Mathf.Min(startLimit, endLimit); float maxLimit = Mathf.Max(startLimit, endLimit); MinMaxSliderState state = s_MinMaxSliderState; if (GUIUtility.hotControl == id && state != null) { minVisual = state.dragStartLimit; minLimit = state.dragStartLimit; maxVisual = state.dragEndLimit; maxLimit = state.dragEndLimit; } float minSize = 0; float displayValue = Mathf.Clamp(value, minVisual, maxVisual); float displaySize = Mathf.Clamp(value + size, minVisual, maxVisual) - displayValue; float sign = visualStart > visualEnd ? -1 : 1; if (slider == null || thumb == null) return; // Figure out the rects float pixelsPerValue; float mousePosition; Rect thumbRect; Rect thumbMinRect, thumbMaxRect; Rect contentRect = thumb.margin.Remove(slider.padding.Remove(position)); float thumbSize = ThumbSize(horiz, thumb); if (horiz) { float thumbHeight = (thumb.fixedHeight != 0 ? thumb.fixedHeight : contentRect.height); pixelsPerValue = (position.width - slider.padding.horizontal - thumbSize) / (maxVisual - minVisual); thumbRect = new Rect( (displayValue - minVisual) * pixelsPerValue + contentRect.x, contentRect.y, displaySize * pixelsPerValue + thumbSize, thumbHeight); thumbMinRect = new Rect(thumbRect.x, thumbRect.y, thumb.padding.left, thumbRect.height); thumbMaxRect = new Rect(thumbRect.xMax - thumb.padding.right, thumbRect.y, thumb.padding.right, thumbRect.height); mousePosition = evt.mousePosition.x - position.x; } else { float thumbWidth = (thumb.fixedWidth != 0 ? thumb.fixedWidth : contentRect.width); pixelsPerValue = (position.height - slider.padding.vertical - thumbSize) / (maxVisual - minVisual); thumbRect = new Rect( contentRect.x, (displayValue - minVisual) * pixelsPerValue + contentRect.y, thumbWidth, displaySize * pixelsPerValue + thumbSize); thumbMinRect = new Rect(thumbRect.x, thumbRect.y, thumbRect.width, thumb.padding.top); thumbMaxRect = new Rect(thumbRect.x, thumbRect.yMax - thumb.padding.bottom, thumbRect.width, thumb.padding.bottom); mousePosition = evt.mousePosition.y - position.y; } float mousePos; float thumbPos; switch (evt.GetTypeForControl(id)) { case EventType.MouseDown: // if the click is outside this control, just bail out... if (evt.button != 0 || !position.Contains(evt.mousePosition) || minVisual - maxVisual == 0) return; if (state == null) state = s_MinMaxSliderState = new MinMaxSliderState(); // These are required to be set whenever we grab hotcontrol, regardless of if we actually drag or not. (case 585577) state.dragStartLimit = startLimit; state.dragEndLimit = endLimit; if (thumbRect.Contains(evt.mousePosition)) { // We have a mousedown on the thumb // Record where we're draging from, so the user can get back. state.dragStartPos = mousePosition; state.dragStartValue = value; state.dragStartSize = size; state.dragStartValuesPerPixel = pixelsPerValue; if (thumbMinRect.Contains(evt.mousePosition)) state.whereWeDrag = 1; else if (thumbMaxRect.Contains(evt.mousePosition)) state.whereWeDrag = 2; else state.whereWeDrag = 0; GUIUtility.hotControl = id; evt.Use(); return; } else { // We're outside the thumb, but inside the trough. // If we have no background, we just bail out. if (slider == GUIStyle.none) return; // If we have a scrollSize, we do pgup/pgdn style movements // if not, we just snap to the current position and begin tracking if (size != 0 && usePageScrollbars) { if (horiz) { if (mousePosition > thumbRect.xMax - position.x) value += size * sign * .9f; else value -= size * sign * .9f; } else { if (mousePosition > thumbRect.yMax - position.y) value += size * sign * .9f; else value -= size * sign * .9f; } state.whereWeDrag = 0; GUI.changed = true; s_NextScrollStepTime = System.DateTime.Now.AddMilliseconds(kFirstScrollWait); mousePos = horiz ? evt.mousePosition.x : evt.mousePosition.y; thumbPos = horiz ? thumbRect.x : thumbRect.y; state.whereWeDrag = mousePos > thumbPos ? 4 : 3; } else { if (horiz) value = ((float)mousePosition - thumbRect.width * .5f) / pixelsPerValue + minVisual - size * .5f; else value = ((float)mousePosition - thumbRect.height * .5f) / pixelsPerValue + minVisual - size * .5f; state.dragStartPos = mousePosition; state.dragStartValue = value; state.dragStartSize = size; state.dragStartValuesPerPixel = pixelsPerValue; state.whereWeDrag = 0; GUI.changed = true; } GUIUtility.hotControl = id; value = Mathf.Clamp(value, minLimit, maxLimit - size); evt.Use(); return; } case EventType.MouseDrag: if (GUIUtility.hotControl != id) return; // Recalculate the value from the mouse position. this has the side effect that values are relative to the // click point - no matter where inside the trough the original value was. Also means user can get back original value // if he drags back to start position. float deltaVal = (mousePosition - state.dragStartPos) / state.dragStartValuesPerPixel; switch (state.whereWeDrag) { case 0: // normal drag value = Mathf.Clamp(state.dragStartValue + deltaVal, minLimit, maxLimit - size); break; case 1:// min size drag value = state.dragStartValue + deltaVal; size = state.dragStartSize - deltaVal; if (value < minLimit) { size -= minLimit - value; value = minLimit; } if (size < minSize) { value -= minSize - size; size = minSize; } break; case 2:// max size drag size = state.dragStartSize + deltaVal; if (value + size > maxLimit) size = maxLimit - value; if (size < minSize) size = minSize; break; } GUI.changed = true; evt.Use(); break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { evt.Use(); GUIUtility.hotControl = 0; } break; case EventType.Repaint: slider.Draw(position, GUIContent.none, id); thumb.Draw(thumbRect, GUIContent.none, id); EditorGUIUtility.AddCursorRect(thumbMinRect, horiz ? MouseCursor.ResizeHorizontal : MouseCursor.ResizeVertical, state != null && state.whereWeDrag == 1 ? id : -1); EditorGUIUtility.AddCursorRect(thumbMaxRect, horiz ? MouseCursor.ResizeHorizontal : MouseCursor.ResizeVertical, state != null && state.whereWeDrag == 2 ? id : -1); // if the mouse is outside this control, just bail out... if (GUIUtility.hotControl != id || !position.Contains(evt.mousePosition) || minVisual - maxVisual == 0) { return; } if (thumbRect.Contains(evt.mousePosition)) { if (state != null && (state.whereWeDrag == 3 || state.whereWeDrag == 4)) // if was scrolling with "through" and the thumb reached mouse - sliding action over GUIUtility.hotControl = 0; return; } if (System.DateTime.Now < s_NextScrollStepTime) return; mousePos = horiz ? evt.mousePosition.x : evt.mousePosition.y; thumbPos = horiz ? thumbRect.x : thumbRect.y; int currentSide = mousePos > thumbPos ? 4 : 3; if (state != null && currentSide != state.whereWeDrag) return; // If we have a scrollSize, we do pgup/pgdn style movements if (size != 0 && usePageScrollbars) { if (horiz) { if (mousePosition > thumbRect.xMax - position.x) value += size * sign * .9f; else value -= size * sign * .9f; } else { if (mousePosition > thumbRect.yMax - position.y) value += size * sign * .9f; else value -= size * sign * .9f; } if (state != null) state.whereWeDrag = -1; GUI.changed = true; } value = Mathf.Clamp(value, minLimit, maxLimit - size); s_NextScrollStepTime = System.DateTime.Now.AddMilliseconds(kScrollWait); break; } } private static bool adding = false; private static bool[] initSelections; private static int initIndex = 0; // Used for selecting multiple rows on the left in the animation window. public static bool DragSelection(Rect[] positions, ref bool[] selections, GUIStyle style) { int id = GUIUtility.GetControlID(34553287, FocusType.Keyboard); Event evt = Event.current; int selectedIndex = -1; for (int i = positions.Length - 1; i >= 0; i--) if (positions[i].Contains(evt.mousePosition)) { selectedIndex = i; break; } EventType type = evt.GetTypeForControl(id); switch (type) { case EventType.Repaint: for (int i = 0; i < positions.Length; i++) style.Draw(positions[i], GUIContent.none, id, selections[i]); break; case EventType.MouseDown: if (evt.button == 0 && selectedIndex >= 0) { GUIUtility.keyboardControl = 0; bool deselecting = false; // If clicking on an already selected item if (selections[selectedIndex]) { int counter = 0; foreach (bool sel in selections) { if (sel) { counter++; if (counter > 1) break; } } // ...and it's the only one selected, then deselect it if (counter == 1) deselecting = true; } // Shift click to add to current selection if (!evt.shift && !EditorGUI.actionKey) for (int i = 0; i < positions.Length; i++) selections[i] = false; initIndex = selectedIndex; initSelections = (bool[])selections.Clone(); // Command click to toggle adding = true; if ((evt.shift || EditorGUI.actionKey) && selections[selectedIndex] == true) adding = false; selections[selectedIndex] = (deselecting ? false : adding); GUIUtility.hotControl = id; evt.Use(); return true; } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { if (evt.button == 0) { if (selectedIndex < 0) { // Clamp index to nearest positions if outside of range // (so that less precision is required for hitting the last rect) Rect dummyRect = new Rect(positions[0].x, positions[0].y - 200, positions[0].width, 200); if (dummyRect.Contains(evt.mousePosition)) selectedIndex = 0; dummyRect.y = positions[positions.Length - 1].yMax; if (dummyRect.Contains(evt.mousePosition)) selectedIndex = selections.Length - 1; } if (selectedIndex < 0) return false; int min = Mathf.Min(initIndex, selectedIndex); int max = Mathf.Max(initIndex, selectedIndex); for (int i = 0; i < selections.Length; i++) { if (i >= min && i <= max) selections[i] = adding; else selections[i] = initSelections[i]; } evt.Use(); return true; } } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) GUIUtility.hotControl = 0; break; } return false; } static bool Any(bool[] selections) { for (int i = 0; i < selections.Length; i++) { if (selections[i]) return true; } return false; } public static HighLevelEvent MultiSelection( Rect rect, Rect[] positions, GUIContent content, Rect[] hitPositions, ref bool[] selections, bool[] readOnly, out int clickedIndex, out Vector2 offset, out float startSelect, out float endSelect, GUIStyle style ) { int id = GUIUtility.GetControlID(41623453, FocusType.Keyboard); Event evt = Event.current; offset = Vector2.zero; clickedIndex = -1; startSelect = endSelect = 0f; if (evt.type == EventType.Used) return HighLevelEvent.None; bool selected = false; if (Event.current.type != EventType.Layout) { if (GUIUtility.keyboardControl == id) selected = true; } int selectedIndex; EventType type = evt.GetTypeForControl(id); switch (type) { case EventType.Repaint: // Draw selection rect if (GUIUtility.hotControl == id && s_MultiSelectDragSelection == DragSelectionState.DragSelecting) { float min = Mathf.Min(s_StartSelectPos.x, evt.mousePosition.x); float max = Mathf.Max(s_StartSelectPos.x, evt.mousePosition.x); Rect selRect = new Rect(0, 0, rect.width, rect.height); selRect.x = min; selRect.width = max - min; // Display if bigger than 1 pixel. if (selRect.width > 1) GUI.Box(selRect, "", ms_Styles.selectionRect); } // Draw controls Color tempCol = GUI.color; for (int i = 0; i < positions.Length; i++) { if (readOnly != null && readOnly[i]) GUI.color = tempCol * new Color(0.90f, 0.90f, 0.90f, 0.5f); else if (selections[i]) GUI.color = tempCol * new Color(0.30f, 0.55f, 0.95f, 1); else GUI.color = tempCol * new Color(0.90f, 0.90f, 0.90f, 1); style.Draw(positions[i], content, id, selections[i]); } GUI.color = tempCol; break; case EventType.MouseDown: if (evt.button == 0) { GUIUtility.hotControl = id; GUIUtility.keyboardControl = id; s_StartSelectPos = evt.mousePosition; selectedIndex = GetIndexUnderMouse(hitPositions, readOnly); if (Event.current.clickCount == 2) { if (selectedIndex >= 0) { for (int i = 0; i < selections.Length; i++) selections[i] = false; selections[selectedIndex] = true; evt.Use(); clickedIndex = selectedIndex; return HighLevelEvent.DoubleClick; } } if (selectedIndex >= 0) { // Shift click to add to current selection if (!evt.shift && !EditorGUI.actionKey && !selections[selectedIndex]) for (int i = 0; i < hitPositions.Length; i++) selections[i] = false; if (evt.shift || EditorGUI.actionKey) selections[selectedIndex] = !selections[selectedIndex]; else selections[selectedIndex] = true; s_MouseDownPos = evt.mousePosition; s_MultiSelectDragSelection = DragSelectionState.None; evt.Use(); clickedIndex = selectedIndex; return HighLevelEvent.SelectionChanged; } else { // Shift click to add to current selection bool changed = false; if (!evt.shift && !EditorGUI.actionKey) { for (int i = 0; i < hitPositions.Length; i++) selections[i] = false; changed = true; } else changed = false; s_SelectionBackup = new List(selections); s_LastFrameSelections = new List(selections); s_MultiSelectDragSelection = DragSelectionState.DragSelecting; evt.Use(); return changed ? HighLevelEvent.SelectionChanged : HighLevelEvent.None; } } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { if (s_MultiSelectDragSelection == DragSelectionState.DragSelecting) { float min = Mathf.Min(s_StartSelectPos.x, evt.mousePosition.x); float max = Mathf.Max(s_StartSelectPos.x, evt.mousePosition.x); s_SelectionBackup.CopyTo(selections); for (int i = 0; i < hitPositions.Length; i++) { if (selections[i]) continue; float center = hitPositions[i].x + hitPositions[i].width * .5f; if (center >= min && center <= max) selections[i] = true; } evt.Use(); startSelect = min; endSelect = max; // Check if the selections _actually_ changed from last call bool changed = false; for (int i = 0; i < selections.Length; i++) { if (selections[i] != s_LastFrameSelections[i]) { changed = true; s_LastFrameSelections[i] = selections[i]; } } return changed ? HighLevelEvent.SelectionChanged : HighLevelEvent.None; } else { offset = evt.mousePosition - s_MouseDownPos; evt.Use(); if (s_MultiSelectDragSelection == DragSelectionState.None) { s_MultiSelectDragSelection = DragSelectionState.Dragging; return HighLevelEvent.BeginDrag; } else { return HighLevelEvent.Drag; } } } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { GUIUtility.hotControl = 0; if (s_StartSelectPos != evt.mousePosition) evt.Use(); // TODO fix magic number for max dragging distance to be ignored if (s_MultiSelectDragSelection == DragSelectionState.None) { clickedIndex = GetIndexUnderMouse(hitPositions, readOnly); if (evt.clickCount == 1) return HighLevelEvent.Click; } else { s_MultiSelectDragSelection = DragSelectionState.None; s_SelectionBackup = null; s_LastFrameSelections = null; return HighLevelEvent.EndDrag; } } break; case EventType.ValidateCommand: case EventType.ExecuteCommand: if (selected) { bool execute = evt.type == EventType.ExecuteCommand; switch (evt.commandName) { case EventCommandNames.Delete: evt.Use(); if (execute) return HighLevelEvent.Delete; break; case EventCommandNames.Copy: evt.Use(); if (execute) return HighLevelEvent.Copy; break; case EventCommandNames.Paste: evt.Use(); if (execute) return HighLevelEvent.Paste; break; } } break; case EventType.KeyDown: if (selected) { if (evt.keyCode == KeyCode.Backspace || evt.keyCode == KeyCode.Delete) { evt.Use(); return HighLevelEvent.Delete; } } break; case EventType.ContextClick: selectedIndex = GetIndexUnderMouse(hitPositions, readOnly); if (selectedIndex >= 0) { clickedIndex = selectedIndex; GUIUtility.keyboardControl = id; evt.Use(); return HighLevelEvent.ContextClick; } break; } return HighLevelEvent.None; } // Helper for MultiSelection above. static int GetIndexUnderMouse(Rect[] hitPositions, bool[] readOnly) { Vector2 mousePos = Event.current.mousePosition; for (int i = hitPositions.Length - 1; i >= 0; i--) if ((readOnly == null || !readOnly[i]) && hitPositions[i].Contains(mousePos)) return i; return -1; } // Small helper: Make a rect from MinMax values and make sure they're positive sizes internal static Rect FromToRect(Vector2 start, Vector2 end) { Rect r = new Rect(start.x, start.y, end.x - start.x, end.y - start.y); if (r.width < 0) { r.x += r.width; r.width = -r.width; } if (r.height < 0) { r.y += r.height; r.height = -r.height; } return r; } } internal enum HighLevelEvent { None, Click, DoubleClick, ContextClick, BeginDrag, Drag, EndDrag, Delete, SelectionChanged, Copy, Paste } } //namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/Deprecated/UtilityClasses.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using TangentMode = UnityEditor.AnimationUtility.TangentMode; namespace UnityEditor { internal static class CurveUtility { private static Texture2D iconKey; private static Texture2D iconCurve; public static Texture2D GetIconCurve() { if (iconCurve == null) iconCurve = EditorGUIUtility.LoadIcon("animationanimated"); return iconCurve; } public static Texture2D GetIconKey() { if (iconKey == null) iconKey = EditorGUIUtility.LoadIcon("animationkeyframe"); return iconKey; } public static bool HaveKeysInRange(AnimationCurve curve, float beginTime, float endTime) { // Loop backwards for (int i = curve.length - 1; i >= 0; i--) { if (curve[i].time >= beginTime && curve[i].time < endTime) { return true; } } return false; } public static void RemoveKeysInRange(AnimationCurve curve, float beginTime, float endTime) { // Loop backwards so key removals don't mess up order for (int i = curve.length - 1; i >= 0; i--) { if (curve[i].time >= beginTime && curve[i].time < endTime) { curve.RemoveKey(i); } } } public static float CalculateSmoothTangent(Keyframe key) { if (key.inTangent == Mathf.Infinity) key.inTangent = 0; if (key.outTangent == Mathf.Infinity) key.outTangent = 0; return (key.outTangent + key.inTangent) * 0.5f; } // Move me to CurveEditor.cs public static void SetKeyModeFromContext(AnimationCurve curve, int keyIndex) { Keyframe key = curve[keyIndex]; bool broken = false; bool smoothTangent = false; if (keyIndex > 0) { if (AnimationUtility.GetKeyBroken(curve[keyIndex - 1])) broken = true; TangentMode prevTangentMode = AnimationUtility.GetKeyRightTangentMode(curve[keyIndex - 1]); if (prevTangentMode == TangentMode.ClampedAuto || prevTangentMode == TangentMode.Auto) smoothTangent = true; } if (keyIndex < curve.length - 1) { if (AnimationUtility.GetKeyBroken(curve[keyIndex + 1])) broken = true; TangentMode nextTangentMode = AnimationUtility.GetKeyLeftTangentMode(curve[keyIndex + 1]); if (nextTangentMode == TangentMode.ClampedAuto || nextTangentMode == TangentMode.Auto) smoothTangent = true; } AnimationUtility.SetKeyBroken(ref key, broken); if (broken && !smoothTangent) { if (keyIndex > 0) AnimationUtility.SetKeyLeftTangentMode(ref key, AnimationUtility.GetKeyRightTangentMode(curve[keyIndex - 1])); if (keyIndex < curve.length - 1) AnimationUtility.SetKeyRightTangentMode(ref key, AnimationUtility.GetKeyLeftTangentMode(curve[keyIndex + 1])); // Keys at boundaries. Make sure left and right tangent modes are the same. if (keyIndex == 0) AnimationUtility.SetKeyLeftTangentMode(ref key, AnimationUtility.GetKeyRightTangentMode(key)); if (keyIndex == curve.length - 1) AnimationUtility.SetKeyRightTangentMode(ref key, AnimationUtility.GetKeyLeftTangentMode(key)); } else { // If both neighbors or only neighbor are set to TangentMode.Auto or TangentMode.ClampedAuto, set new key to this mode as well. // If there are no neighbors, set new key to TangentMode.ClampedAuto. // Otherwise, fall back to TangentMode.Free. TangentMode mode = TangentMode.Free; if ((keyIndex == 0 || AnimationUtility.GetKeyRightTangentMode(curve[keyIndex - 1]) == TangentMode.ClampedAuto) && (keyIndex == curve.length - 1 || AnimationUtility.GetKeyLeftTangentMode(curve[keyIndex + 1]) == TangentMode.ClampedAuto)) { mode = TangentMode.ClampedAuto; } else if ((keyIndex == 0 || AnimationUtility.GetKeyRightTangentMode(curve[keyIndex - 1]) == TangentMode.Auto) && (keyIndex == curve.length - 1 || AnimationUtility.GetKeyLeftTangentMode(curve[keyIndex + 1]) == TangentMode.Auto)) { mode = TangentMode.Auto; } AnimationUtility.SetKeyLeftTangentMode(ref key, mode); AnimationUtility.SetKeyRightTangentMode(ref key, mode); } curve.MoveKey(keyIndex, key); } static public string GetClipName(AnimationClip clip) { if (clip == null) return "[No Clip]"; string name = clip.name; if ((clip.hideFlags & HideFlags.NotEditable) != 0) name += " (Read-Only)"; return name; } public static Color GetBalancedColor(Color c) { return new Color( 0.15f + 0.75f * c.r, 0.20f + 0.60f * c.g, 0.10f + 0.90f * c.b ); } public static Color GetPropertyColor(string name) { Color col = Color.white; int type = 0; if (name.StartsWith("m_LocalPosition")) type = 1; if (name.StartsWith("localEulerAngles")) type = 2; if (name.StartsWith("m_LocalScale")) type = 3; if (type == 1) { if (name.EndsWith(".x")) col = Handles.xAxisColor; else if (name.EndsWith(".y")) col = Handles.yAxisColor; else if (name.EndsWith(".z")) col = Handles.zAxisColor; } else if (type == 2) { if (name.EndsWith(".x")) col = AnimEditor.kEulerXColor; else if (name.EndsWith(".y")) col = AnimEditor.kEulerYColor; else if (name.EndsWith(".z")) col = AnimEditor.kEulerZColor; } else if (type == 3) { if (name.EndsWith(".x")) col = GetBalancedColor(new Color(0.7f, 0.4f, 0.4f)); else if (name.EndsWith(".y")) col = GetBalancedColor(new Color(0.4f, 0.7f, 0.4f)); else if (name.EndsWith(".z")) col = GetBalancedColor(new Color(0.4f, 0.4f, 0.7f)); } else if (name.EndsWith(".x")) col = Handles.xAxisColor; else if (name.EndsWith(".y")) col = Handles.yAxisColor; else if (name.EndsWith(".z")) col = Handles.zAxisColor; else if (name.EndsWith(".w")) col = new Color(1.0f, 0.5f, 0.0f); else if (name.EndsWith(".r")) col = GetBalancedColor(Color.red); else if (name.EndsWith(".g")) col = GetBalancedColor(Color.green); else if (name.EndsWith(".b")) col = GetBalancedColor(Color.blue); else if (name.EndsWith(".a")) col = GetBalancedColor(Color.yellow); else if (name.EndsWith(".width")) col = GetBalancedColor(Color.blue); else if (name.EndsWith(".height")) col = GetBalancedColor(Color.yellow); else { float rand = Mathf.PI * 2 * (name.GetHashCode() % 1000); rand = rand - Mathf.Floor(rand); col = GetBalancedColor(Color.HSVToRGB(rand, 1, 1)); } col.a = 1; // Some preference colors do not have full alpha return col; } } struct QuaternionCurveTangentCalculation { public static Vector3[] GetEquivalentEulerAngles(Quaternion quat) { Vector3 euler = quat.eulerAngles; Vector3[] eulers = new Vector3[2]; eulers[0] = euler; eulers[1] = new Vector3(180 - euler.x, euler.y + 180, euler.z + 180); return eulers; } public static Vector3 GetEulerFromQuaternion(Quaternion q, Vector3 refEuler) { Vector3[] eulers = GetEquivalentEulerAngles(q); for (int i = 0; i < eulers.Length; i++) { eulers[i] = new Vector3( Mathf.Repeat(eulers[i].x - refEuler.x + 180, 360) + refEuler.x - 180, Mathf.Repeat(eulers[i].y - refEuler.y + 180, 360) + refEuler.y - 180, Mathf.Repeat(eulers[i].z - refEuler.z + 180, 360) + refEuler.z - 180 ); float xRot = Mathf.Repeat(eulers[i].x, 360); if (Mathf.Abs(xRot - 90) < 1.0f) { float newCombiAngle = eulers[i].z - eulers[i].y; float refCombiAngle = refEuler.z - refEuler.y; float angleDiff = newCombiAngle - refCombiAngle; eulers[i].z = refEuler.z + angleDiff * 0.5f; eulers[i].y = refEuler.y - angleDiff * 0.5f; } if (Mathf.Abs(xRot - 270) < 1.0f) { float newCombiAngle = eulers[i].z + eulers[i].y; float refCombiAngle = refEuler.z + refEuler.y; float angleDiff = newCombiAngle - refCombiAngle; eulers[i].z = refEuler.z + angleDiff * 0.5f; eulers[i].y = refEuler.y + angleDiff * 0.5f; } } // Find out which euler is closest to reference Vector3 euler = eulers[0]; float dist = (eulers[0] - refEuler).sqrMagnitude; for (int i = 1; i < eulers.Length; i++) { float newDist = (eulers[i] - refEuler).sqrMagnitude; if (newDist < dist) { dist = newDist; euler = eulers[i]; } } return euler; } } } // namespace ================================================ FILE: Editor/Mono/Animation/AnimationWindow/DopeLine.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Linq; using System; namespace UnityEditorInternal { internal class DopeLine { private int m_HierarchyNodeID; private AnimationWindowCurve[] m_Curves; private List m_Keys; public static GUIStyle dopekeyStyle = "Dopesheetkeyframe"; public Rect position; public System.Type objectType; public bool tallMode; public bool hasChildren; public bool isMasterDopeline; public System.Type valueType { get { if (m_Curves.Length > 0) { System.Type type = m_Curves[0].valueType; for (int i = 1; i < m_Curves.Length; i++) { if (m_Curves[i].valueType != type) return null; } return type; } return null; } } public bool isPptrDopeline { get { if (m_Curves.Length > 0) { for (int i = 0; i < m_Curves.Length; i++) { if (!m_Curves[i].isPPtrCurve) return false; } return true; } return false; } } public bool isEditable { get { if (m_Curves.Length > 0) { bool isReadOnly = Array.Exists(m_Curves, curve => !curve.animationIsEditable); return !isReadOnly; } return false; } } public int hierarchyNodeID { get { return m_HierarchyNodeID; } } public AnimationWindowCurve[] curves { get { return m_Curves; } } public List keys { get { if (m_Keys == null) { m_Keys = new List(); foreach (AnimationWindowCurve curve in m_Curves) foreach (AnimationWindowKeyframe key in curve.keyframes) m_Keys.Add(key); m_Keys.Sort((a, b) => a.time.CompareTo(b.time)); } return m_Keys; } } public void InvalidateKeyframes() { m_Keys = null; } public DopeLine(int hierarchyNodeID, AnimationWindowCurve[] curves) { m_HierarchyNodeID = hierarchyNodeID; m_Curves = curves; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/DopeSheetEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using System.Collections.Generic; using Event = UnityEngine.Event; using Object = UnityEngine.Object; using System.Collections; using System.Linq; namespace UnityEditorInternal { [System.Serializable] class DopeSheetEditor : TimeArea, CurveUpdater { [SerializeReference] public AnimationWindowState state; // How much rendered keyframe left edge is visually offset when compared to the time it represents. // A diamond shape left edge isn't representing the time, the middle part is. private const float k_KeyframeOffset = -6.5f; // Pptr keyframe preview also needs 1px offset so it sits more tightly in the grid private const float k_PptrKeyframeOffset = -1; private static readonly Vector2 k_ControlPointSize = new Vector2(16, 16); const int kLabelMarginHorizontal = 8; const int kLabelMarginVertical = 2; static private Color s_SelectedKeyColor = new Color32(87, 133, 217, 255); struct DrawElement { public Rect position; public Color color; public Texture2D texture; public DrawElement(Rect position, Color color, Texture2D texture) { this.position = position; this.color = color; this.texture = texture; } } // Control point collection renderer class DopeSheetControlPointRenderer { // Unoptimized control point list. Rendered through GUI.Label calls. private List m_UnselectedKeysDrawBuffer = new List(); private List m_SelectedKeysDrawBuffer = new List(); private List m_DragDropKeysDrawBuffer = new List(); // Control point mesh renderers. private ControlPointRenderer m_UnselectedKeysRenderer; private ControlPointRenderer m_SelectedKeysRenderer; private ControlPointRenderer m_DragDropKeysRenderer; private Texture2D m_DefaultDopeKeyIcon; public void FlushCache() { m_UnselectedKeysRenderer.FlushCache(); m_SelectedKeysRenderer.FlushCache(); m_DragDropKeysRenderer.FlushCache(); } private void DrawElements(List elements) { if (elements.Count == 0) return; Color oldColor = GUI.color; Color color = Color.white; GUI.color = color; Texture icon = m_DefaultDopeKeyIcon; for (int i = 0; i < elements.Count; ++i) { DrawElement element = elements[i]; // Change color if (element.color != color) { color = GUI.enabled ? element.color : element.color * 0.8f; GUI.color = color; } // Element with specific texture (sprite). if (element.texture != null) { GUI.Label(element.position, element.texture, GUIStyle.none); } // Ordinary control point. else { Rect rect = new Rect((element.position.center.x - icon.width / 2), (element.position.center.y - icon.height / 2), icon.width, icon.height); GUI.Label(rect, icon, GUIStyle.none); } } GUI.color = oldColor; } public DopeSheetControlPointRenderer() { m_DefaultDopeKeyIcon = EditorGUIUtility.LoadIcon("blendKey"); m_UnselectedKeysRenderer = new ControlPointRenderer(m_DefaultDopeKeyIcon); m_SelectedKeysRenderer = new ControlPointRenderer(m_DefaultDopeKeyIcon); m_DragDropKeysRenderer = new ControlPointRenderer(m_DefaultDopeKeyIcon); } public void Clear() { m_UnselectedKeysDrawBuffer.Clear(); m_SelectedKeysDrawBuffer.Clear(); m_DragDropKeysDrawBuffer.Clear(); m_UnselectedKeysRenderer.Clear(); m_SelectedKeysRenderer.Clear(); m_DragDropKeysRenderer.Clear(); } public void Render() { DrawElements(m_UnselectedKeysDrawBuffer); m_UnselectedKeysRenderer.Render(); DrawElements(m_SelectedKeysDrawBuffer); m_SelectedKeysRenderer.Render(); DrawElements(m_DragDropKeysDrawBuffer); m_DragDropKeysRenderer.Render(); } public void AddUnselectedKey(DrawElement element) { // Control point has a specific texture (sprite image). // This will not be batched rendered and must be handled separately. if (element.texture != null) { m_UnselectedKeysDrawBuffer.Add(element); } else { Rect rect = element.position; rect.size = k_ControlPointSize; m_UnselectedKeysRenderer.AddPoint(rect, element.color); } } public void AddSelectedKey(DrawElement element) { // Control point has a specific texture (sprite image). // This will not be batched rendered and must be handled separately. if (element.texture != null) { m_SelectedKeysDrawBuffer.Add(element); } else { Rect rect = element.position; rect.size = k_ControlPointSize; m_SelectedKeysRenderer.AddPoint(rect, element.color); } } public void AddDragDropKey(DrawElement element) { // Control point has a specific texture (sprite image). // This will not be batched rendered and must be handled separately. if (element.texture != null) { m_DragDropKeysDrawBuffer.Add(element); } else { Rect rect = element.position; rect.size = k_ControlPointSize; m_DragDropKeysRenderer.AddPoint(rect, element.color); } } } public float contentHeight { get { float height = 0f; foreach (DopeLine dopeline in state.dopelines) height += dopeline.tallMode ? AnimationWindowHierarchyGUI.k_DopeSheetRowHeightTall : AnimationWindowHierarchyGUI.k_DopeSheetRowHeight; height += AnimationWindowHierarchyGUI.k_AddCurveButtonNodeHeight; return height; } } [SerializeField] public EditorWindow m_Owner; DopeSheetSelectionRect m_SelectionRect; float m_DragStartTime; bool m_MousedownOnKeyframe; bool m_IsDragging; bool m_IsDraggingPlayheadStarted; bool m_IsDraggingPlayhead; bool m_Initialized; bool m_SpritePreviewLoading; int m_SpritePreviewCacheSize; public Bounds m_Bounds = new Bounds(Vector3.zero, Vector3.zero); public override Bounds drawingBounds { get { return m_Bounds; } } public bool isDragging { get { return m_IsDragging; } } DopeSheetControlPointRenderer m_PointRenderer; DopeSheetEditorRectangleTool m_RectangleTool; internal int assetPreviewManagerID { get { return m_Owner != null ? m_Owner.GetInstanceID() : 0; } } public bool spritePreviewLoading { get { return m_SpritePreviewLoading; } } public DopeSheetEditor(EditorWindow owner) : base(false) { m_Owner = owner; } public void OnDisable() { if (m_PointRenderer != null) m_PointRenderer.FlushCache(); } internal void OnDestroy() { AssetPreview.DeletePreviewTextureManagerByID(assetPreviewManagerID); } public void OnGUI(Rect position, Vector2 scrollPosition) { Init(); // drag'n'drops outside any dopelines HandleDragAndDropToEmptyArea(); GUIClip.Push(position, scrollPosition, Vector2.zero, false); HandleRectangleToolEvents(); Rect localRect = new Rect(0, 0, position.width, position.height); Rect dopesheetRect = DopelinesGUI(localRect, scrollPosition); HandleKeyboard(); HandleDragging(); HandleSelectionRect(dopesheetRect); HandleDelete(); RectangleToolGUI(); GUIClip.Pop(); } public void Init() { if (!m_Initialized) { // Set TimeArea constrains hSlider = true; vSlider = false; hRangeLocked = false; vRangeLocked = true; hRangeMin = 0; margin = 40; scaleWithWindow = true; ignoreScrollWheelUntilClicked = false; } m_Initialized = true; if (m_PointRenderer == null) m_PointRenderer = new DopeSheetControlPointRenderer(); if (m_RectangleTool == null) { m_RectangleTool = new DopeSheetEditorRectangleTool(); m_RectangleTool.Initialize(this); } } public void RecalculateBounds() { if (!state.disabled) { Vector2 timeRange = state.timeRange; m_Bounds.SetMinMax(new Vector3(timeRange.x, 0, 0), new Vector3(timeRange.y, 0, 0)); } } private Rect DopelinesGUI(Rect position, Vector2 scrollPosition) { Color oldColor = GUI.color; Rect linePosition = position; m_PointRenderer.Clear(); if (Event.current.type == EventType.Repaint) m_SpritePreviewLoading = false; // Workaround for cases when mouseup happens outside the window. Apparently the mouseup event is lost (not true on OSX, though). if (Event.current.type == EventType.MouseDown) m_IsDragging = false; // Find out how large preview pool is needed for sprite previews UpdateSpritePreviewCacheSize(); List dopelines = state.dopelines; for (int i = 0; i < dopelines.Count; ++i) { DopeLine dopeLine = dopelines[i]; dopeLine.position = linePosition; dopeLine.position.height = (dopeLine.tallMode ? AnimationWindowHierarchyGUI.k_DopeSheetRowHeightTall : AnimationWindowHierarchyGUI.k_DopeSheetRowHeight); // Cull out dopelines that are not visible if (dopeLine.position.yMin + scrollPosition.y >= position.yMin && dopeLine.position.yMin + scrollPosition.y <= position.yMax || dopeLine.position.yMax + scrollPosition.y >= position.yMin && dopeLine.position.yMax + scrollPosition.y <= position.yMax) { Event evt = Event.current; switch (evt.type) { case EventType.DragUpdated: case EventType.DragPerform: { HandleDragAndDrop(dopeLine); break; } case EventType.ContextClick: { if (!m_IsDraggingPlayhead) { HandleContextMenu(dopeLine); } break; } case EventType.MouseDown: { if (evt.button == 0) { HandleMouseDown(dopeLine); } break; } case EventType.Repaint: { DopeLineRepaint(dopeLine); break; } } } linePosition.y += dopeLine.position.height; } if (Event.current.type == EventType.MouseUp) { m_IsDraggingPlayheadStarted = false; m_IsDraggingPlayhead = false; } Rect dopelinesRect = new Rect(position.xMin, position.yMin, position.width, linePosition.yMax - position.yMin); if (Event.current.type == EventType.Repaint) m_PointRenderer.Render(); GUI.color = oldColor; return dopelinesRect; } private void RectangleToolGUI() { m_RectangleTool.OnGUI(); } public void DrawMasterDopelineBackground(Rect position) { if (Event.current.type != EventType.Repaint) return; AnimationWindowStyles.eventBackground.Draw(position, false, false, false, false); } void UpdateSpritePreviewCacheSize() { int newPreviewCacheSize = 1; // Add all expanded sprite dopelines foreach (DopeLine dopeLine in state.dopelines) { if (dopeLine.tallMode && dopeLine.isPptrDopeline) { newPreviewCacheSize += dopeLine.keys.Count; } } // Add all drag'n'drop objects newPreviewCacheSize += DragAndDrop.objectReferences.Length; if (newPreviewCacheSize > m_SpritePreviewCacheSize) { AssetPreview.SetPreviewTextureCacheSize(newPreviewCacheSize, assetPreviewManagerID); m_SpritePreviewCacheSize = newPreviewCacheSize; } } private void DopeLineRepaint(DopeLine dopeline) { Color oldColor = GUI.color; AnimationWindowHierarchyNode node = (AnimationWindowHierarchyNode)state.hierarchyData.FindItem(dopeline.hierarchyNodeID); bool isChild = node != null && node.depth > 0; Color color = isChild ? Color.gray.AlphaMultiplied(0.05f) : Color.gray.AlphaMultiplied(0.16f); // Draw background if (dopeline.isMasterDopeline) DrawMasterDopelineBackground(dopeline.position); else DrawBox(dopeline.position, color); // Draw keys int? previousTimeHash = null; int length = dopeline.keys.Count; for (int i = 0; i < length; i++) { AnimationWindowKeyframe keyframe = dopeline.keys[i]; // Hash optimizations if (previousTimeHash == keyframe.m_TimeHash) continue; previousTimeHash = keyframe.m_TimeHash; // Default values Rect rect = GetKeyframeRect(dopeline, keyframe); color = dopeline.isMasterDopeline ? Color.gray.RGBMultiplied(0.85f) : Color.gray.RGBMultiplied(1.2f); Texture2D texture = null; if (keyframe.isPPtrCurve && dopeline.tallMode) texture = keyframe.value == null ? null : AssetPreview.GetAssetPreview(((Object)keyframe.value).GetInstanceID(), assetPreviewManagerID); if (texture != null) { rect = GetPreviewRectFromKeyFrameRect(rect); color = Color.white.AlphaMultiplied(0.5f); } else if (keyframe.value != null && keyframe.isPPtrCurve && dopeline.tallMode) { m_SpritePreviewLoading = true; } // TODO: Find out why zero time, and only zero time, is offset from grid if (Mathf.Approximately(keyframe.time, 0f)) rect.xMin -= 0.01f; if (AnyKeyIsSelectedAtTime(dopeline, i)) { color = s_SelectedKeyColor; if (dopeline.tallMode && dopeline.isPptrDopeline) color = Color.white; if (dopeline.isMasterDopeline) color = color.RGBMultiplied(0.85f); m_PointRenderer.AddSelectedKey(new DrawElement(rect, color, texture)); } else { m_PointRenderer.AddUnselectedKey(new DrawElement(rect, color, texture)); } } if (DoDragAndDrop(dopeline, dopeline.position, false)) { float time = Mathf.Max(state.PixelToTime(Event.current.mousePosition.x, AnimationWindowState.SnapMode.SnapToFrame), 0f); Color keyColor = Color.gray.RGBMultiplied(1.2f); Texture2D texture = null; foreach (Object obj in GetSortedDragAndDropObjectReferences()) { Rect rect = GetDragAndDropRect(dopeline, time); if (dopeline.isPptrDopeline && dopeline.tallMode) texture = AssetPreview.GetAssetPreview(obj.GetInstanceID(), assetPreviewManagerID); if (texture != null) { rect = GetPreviewRectFromKeyFrameRect(rect); keyColor = Color.white.AlphaMultiplied(0.5f); } m_PointRenderer.AddDragDropKey(new DrawElement(rect, keyColor, texture)); time += 1f / state.frameRate; } } GUI.color = oldColor; } private Rect GetPreviewRectFromKeyFrameRect(Rect keyframeRect) { keyframeRect.width -= 2; keyframeRect.height -= 2; keyframeRect.xMin += 2; keyframeRect.yMin += 2; return keyframeRect; } private Rect GetDragAndDropRect(DopeLine dopeline, float time) { Rect rect = GetKeyframeRect(dopeline, null); float offsetX = GetKeyframeOffset(dopeline, null); rect.center = new Vector2(state.TimeToPixel(time) + rect.width * .5f + offsetX, rect.center.y); return rect; } // TODO: This is just temporary until real styles private static void DrawBox(Rect position, Color color) { Color oldColor = GUI.color; GUI.color = color; DopeLine.dopekeyStyle.Draw(position, GUIContent.none, 0, false); GUI.color = oldColor; } private GenericMenu GenerateMenu(DopeLine dopeline) { GenericMenu menu = new GenericMenu(); // Collect hovering keys. List hoveringKeys = new List(); foreach (var key in dopeline.keys) { Rect rect = GetKeyframeRect(dopeline, key); if (rect.Contains(Event.current.mousePosition)) hoveringKeys.Add(key); } AnimationKeyTime mouseKeyTime = AnimationKeyTime.Time(state.PixelToTime(Event.current.mousePosition.x, AnimationWindowState.SnapMode.SnapToFrame), state.frameRate); string str = L10n.Tr("Add Key"); if (dopeline.isEditable && hoveringKeys.Count == 0) menu.AddItem(new GUIContent(str), false, AddKeyToDopeline, new AddKeyToDopelineContext {dopeline = dopeline, time = mouseKeyTime}); else menu.AddDisabledItem(new GUIContent(str)); str = state.selectedKeys.Count > 1 ? L10n.Tr("Delete Keys") : L10n.Tr("Delete Key"); if (dopeline.isEditable && (state.selectedKeys.Count > 0 || hoveringKeys.Count > 0)) menu.AddItem(new GUIContent(str), false, DeleteKeys, state.selectedKeys.Count > 0 ? state.selectedKeys : hoveringKeys); else menu.AddDisabledItem(new GUIContent(str)); // Float curve tangents if (dopeline.isEditable && AnimationWindowUtility.ContainsFloatKeyframes(state.selectedKeys)) { menu.AddSeparator(string.Empty); List keyList = new List(); Hashtable editorCurves = new Hashtable(); foreach (AnimationWindowKeyframe key in state.selectedKeys) { if (key.isDiscreteCurve) continue; int index = key.curve.GetKeyframeIndex(AnimationKeyTime.Time(key.time, state.frameRate)); if (index == -1) continue; int id = key.curve.GetHashCode(); AnimationCurve curve = (AnimationCurve)editorCurves[id]; if (curve == null) { curve = AnimationUtility.GetEditorCurve(key.curve.clip, key.curve.binding); if (curve == null) curve = new AnimationCurve(); editorCurves.Add(id, curve); } keyList.Add(new KeyIdentifier(curve, id, index, key.curve.binding)); } CurveMenuManager menuManager = new CurveMenuManager(this); menuManager.AddTangentMenuItems(menu, keyList); } return menu; } private void HandleDragging() { int id = EditorGUIUtility.GetControlID("dopesheetdrag".GetHashCode(), FocusType.Passive, new Rect()); EventType eventType = Event.current.GetTypeForControl(id); if ((eventType == EventType.MouseDrag || eventType == EventType.MouseUp) && m_MousedownOnKeyframe) { if (eventType == EventType.MouseDrag && !EditorGUI.actionKey && !Event.current.shift) { if (!m_IsDragging && state.selectedKeys.Count > 0) { m_IsDragging = true; m_IsDraggingPlayheadStarted = true; GUIUtility.hotControl = id; m_DragStartTime = state.PixelToTime(Event.current.mousePosition.x); m_RectangleTool.OnStartMove(new Vector2(m_DragStartTime, 0f), state.rippleTime); Event.current.Use(); } } // What is the distance from first selected key to zero time. We need this in order to make sure no key goes to negative time while dragging. float firstSelectedKeyTime = float.MaxValue; foreach (AnimationWindowKeyframe selectedKey in state.selectedKeys) firstSelectedKeyTime = Mathf.Min(selectedKey.time, firstSelectedKeyTime); float currentTime = state.SnapToFrame(state.PixelToTime(Event.current.mousePosition.x), AnimationWindowState.SnapMode.SnapToFrame); if (m_IsDragging) { if (!Mathf.Approximately(currentTime, m_DragStartTime)) { m_RectangleTool.OnMove(new Vector2(currentTime, 0f)); Event.current.Use(); } } if (eventType == EventType.MouseUp) { if (m_IsDragging && GUIUtility.hotControl == id) { m_RectangleTool.OnEndMove(); Event.current.Use(); m_IsDragging = false; } m_MousedownOnKeyframe = false; GUIUtility.hotControl = 0; } } if (m_IsDraggingPlayheadStarted && eventType == EventType.MouseDrag && Event.current.button == 1) { m_IsDraggingPlayhead = true; //int frame = state.m_Frame; //if (!m_IsDragging) // frame = state.TimeToFrameFloor(state.SnapToFrame (state.PixelToTime (Event.current.mousePosition.x))); //state.animationWindow.PreviewFrame (frame); Event.current.Use(); } if (m_IsDragging) { Vector2 mousePosition = Event.current.mousePosition; Rect mouseRect = new Rect(mousePosition.x - 10, mousePosition.y - 10, 20, 20); EditorGUIUtility.AddCursorRect(mouseRect, MouseCursor.MoveArrow); } } private void HandleKeyboard() { if (Event.current.type == EventType.ValidateCommand || Event.current.type == EventType.ExecuteCommand) { switch (Event.current.commandName) { case EventCommandNames.SelectAll: if (Event.current.type == EventType.ExecuteCommand) HandleSelectAll(); Event.current.Use(); break; case EventCommandNames.FrameSelected: if (Event.current.type == EventType.ExecuteCommand) FrameSelected(); Event.current.Use(); break; } } } private void HandleSelectAll() { foreach (DopeLine dopeline in state.dopelines) { foreach (AnimationWindowKeyframe keyframe in dopeline.keys) { state.SelectKey(keyframe); } state.SelectHierarchyItem(dopeline, true, false); } } private void HandleDelete() { if (state.selectedKeys.Count == 0) return; switch (Event.current.type) { case EventType.ValidateCommand: case EventType.ExecuteCommand: if ((Event.current.commandName == EventCommandNames.SoftDelete || Event.current.commandName == EventCommandNames.Delete)) { if (Event.current.type == EventType.ExecuteCommand) state.DeleteSelectedKeys(); Event.current.Use(); } break; case EventType.KeyDown: if (Event.current.keyCode == KeyCode.Backspace || Event.current.keyCode == KeyCode.Delete) { state.DeleteSelectedKeys(); Event.current.Use(); } break; } } private void HandleSelectionRect(Rect rect) { if (m_SelectionRect == null) m_SelectionRect = new DopeSheetSelectionRect(this); if (!m_MousedownOnKeyframe) m_SelectionRect.OnGUI(rect); } // Handles drag and drop into empty area outside dopelines private void HandleDragAndDropToEmptyArea() { Event evt = Event.current; if (evt.type != EventType.DragPerform && evt.type != EventType.DragUpdated) return; if (!ValidateDragAndDropObjects()) return; // TODO: handle multidropping of other types than sprites/textures if (DragAndDrop.objectReferences[0].GetType() == typeof(Sprite) || DragAndDrop.objectReferences[0].GetType() == typeof(Texture2D)) { if (state.selection.clipIsEditable && state.selection.canAddCurves) { if (!DopelineForValueTypeExists(typeof(Sprite))) { if (evt.type == EventType.DragPerform) { EditorCurveBinding? spriteBinding = CreateNewPptrDopeline(state.selection, typeof(Sprite)); if (spriteBinding != null) DoSpriteDropAfterGeneratingNewDopeline(state.activeAnimationClip, spriteBinding); } DragAndDrop.visualMode = DragAndDropVisualMode.Copy; evt.Use(); return; } } } DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; } private void DoSpriteDropAfterGeneratingNewDopeline(AnimationClip animationClip, EditorCurveBinding? spriteBinding) { // Create the new curve for our sprites AnimationWindowCurve newCurve = new AnimationWindowCurve(animationClip, (EditorCurveBinding)spriteBinding, typeof(Sprite)); // Perform the drop onto the curve PerformDragAndDrop(newCurve, 0f); // Assign the Sprite in the first keyframe to the SpriteRenderer's Sprite property AssignSpriteToSpriteRenderer(newCurve); } private void AssignSpriteToSpriteRenderer(AnimationWindowCurve curve) { var rootGameObject = state.selection.rootGameObject; if (rootGameObject == null) return; var hasValidCurve = curve.keyframes.Count > 0 && curve.binding.type == typeof(SpriteRenderer); if (!hasValidCurve) return; var spriteRenderer = AnimationUtility.GetAnimatedObject(rootGameObject, curve.binding) as SpriteRenderer; var hasValidSpriteRenderer = spriteRenderer != null && spriteRenderer.sprite == null; if (!hasValidSpriteRenderer) return; var keyframe = curve.keyframes[0]; var sprite = keyframe.value as Sprite; if (sprite != null) { Undo.RecordObject(spriteRenderer, "Add Sprite"); spriteRenderer.sprite = sprite; } } private void HandleRectangleToolEvents() { m_RectangleTool.HandleEvents(); } private bool DopelineForValueTypeExists(Type valueType) { return state.filteredCurves.Exists(curve => curve.valueType == valueType); } public EditorCurveBinding[] GetAnimatableProperties(AnimationWindowSelectionItem selection, Type valueType) { EditorCurveBinding[] allBindings = null; if (selection.gameObject != null) { allBindings = state.controlInterface.GetAnimatableBindings(selection.gameObject); } else if (selection.scriptableObject != null) { allBindings = state.controlInterface.GetAnimatableBindings(); } return allBindings .Where(binding => state.controlInterface.GetValueType(binding) == valueType) .ToArray(); } private EditorCurveBinding? CreateNewPptrDopeline(AnimationWindowSelectionItem selectedItem, Type valueType) { EditorCurveBinding[] potentialBindings = null; if (selectedItem.rootGameObject != null) { potentialBindings = GetAnimatableProperties(selectedItem, valueType); if (potentialBindings.Length == 0 && valueType == typeof(Sprite)) // No animatable properties for Sprite available. Default as SpriteRenderer. { return CreateNewSpriteRendererDopeline(selectedItem.rootGameObject, selectedItem.rootGameObject); } } else if (selectedItem.scriptableObject != null) { potentialBindings = GetAnimatableProperties(selectedItem, valueType); } if (potentialBindings == null || potentialBindings.Length == 0) return null; if (potentialBindings.Length == 1) // Single property for this valuetype, return it { return potentialBindings[0]; } else // Multiple properties, dropdown selection { List menuItems = new List(); foreach (EditorCurveBinding binding in potentialBindings) menuItems.Add(binding.type.Name); List userDataList = new List(); userDataList.Add(selectedItem.animationClip); userDataList.Add(potentialBindings); Rect r = new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 1, 1); EditorUtility.DisplayCustomMenu(r, EditorGUIUtility.TempContent(menuItems.ToArray()), -1, SelectTypeForCreatingNewPptrDopeline, userDataList); return null; // We return null, but creation is handled via dropdown callback code } } private void SelectTypeForCreatingNewPptrDopeline(object userData, string[] options, int selected) { List userDataList = userData as List; AnimationClip animationClip = userDataList[0] as AnimationClip; List bindings = userDataList[1] as List; if (bindings.Count > selected) DoSpriteDropAfterGeneratingNewDopeline(animationClip, bindings[selected]); } private EditorCurveBinding CreateNewSpriteRendererDopeline(GameObject targetGameObject, GameObject rootGameObject) { // Let's make sure there is spriterenderer to animate if (!targetGameObject.GetComponent()) targetGameObject.AddComponent(); return EditorCurveBinding.PPtrCurve( AnimationUtility.CalculateTransformPath(targetGameObject.transform, rootGameObject.transform), typeof(SpriteRenderer), "m_Sprite"); } private void HandleDragAndDrop(DopeLine dopeline) { Event evt = Event.current; if (evt.type != EventType.DragPerform && evt.type != EventType.DragUpdated) return; if (DoDragAndDrop(dopeline, dopeline.position, evt.type == EventType.DragPerform)) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; evt.Use(); } else { DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; } } private void HandleMouseDown(DopeLine dopeline) { Event evt = Event.current; if (!dopeline.position.Contains(evt.mousePosition)) return; bool keysAreSelected = false; foreach (AnimationWindowKeyframe keyframe in dopeline.keys) { Rect r = GetKeyframeRect(dopeline, keyframe); if (r.Contains(evt.mousePosition) && state.KeyIsSelected(keyframe)) { keysAreSelected = true; break; } } // For ctrl selecting, we unselect keys if we clicked a selected key frame. bool canUnselectKeys = (keysAreSelected && EditorGUI.actionKey); // Only allow new selected keys if current clicked key frame is unselected. bool canSelectKeys = !keysAreSelected; // If there are no selected keyframe in click without shift or EditorGUI.actionKey, then clear all other selections if (!keysAreSelected && !EditorGUI.actionKey && !evt.shift) state.ClearSelections(); float startTime = state.PixelToTime(Event.current.mousePosition.x); float endTime = startTime; // For shift selecting we need to have time range we choose between if (Event.current.shift) { foreach (AnimationWindowKeyframe key in dopeline.keys) { if (state.KeyIsSelected(key)) { if (key.time < startTime) startTime = key.time; if (key.time > endTime) endTime = key.time; } } } bool clickedOnKeyframe = false; foreach (AnimationWindowKeyframe keyframe in dopeline.keys) { Rect r = GetKeyframeRect(dopeline, keyframe); if (r.Contains(evt.mousePosition)) { clickedOnKeyframe = true; if (canUnselectKeys) { if (state.KeyIsSelected(keyframe)) { state.UnselectKey(keyframe); if (!state.AnyKeyIsSelected(dopeline)) state.UnSelectHierarchyItem(dopeline); } } else if (canSelectKeys) { if (!state.KeyIsSelected(keyframe)) { if (Event.current.shift) { foreach (AnimationWindowKeyframe key in dopeline.keys) if (key == keyframe || key.time > startTime && key.time < endTime) state.SelectKey(key); } else { state.SelectKey(keyframe); } if (!dopeline.isMasterDopeline) state.SelectHierarchyItem(dopeline, EditorGUI.actionKey || evt.shift); } } state.activeKeyframe = keyframe; m_MousedownOnKeyframe = true; evt.Use(); } } if (dopeline.isMasterDopeline) { HashSet hierarchyIDs = state.GetAffectedHierarchyIDs(state.selectedKeys); state.SelectHierarchyItems(hierarchyIDs, true, true); } if (evt.clickCount == 2 && evt.button == 0 && !Event.current.shift && !EditorGUI.actionKey) HandleDopelineDoubleclick(dopeline); // Move playhead when clicked with right mouse button if (evt.button == 1 && !state.controlInterface.playing) { // Clear keyframe selection if right clicked empty space if (!clickedOnKeyframe) { state.ClearSelections(); m_IsDraggingPlayheadStarted = true; HandleUtility.Repaint(); evt.Use(); } } } private void HandleDopelineDoubleclick(DopeLine dopeline) { float timeAtMousePosition = state.PixelToTime(Event.current.mousePosition.x, AnimationWindowState.SnapMode.SnapToFrame); AnimationKeyTime mouseKeyTime = AnimationKeyTime.Time(timeAtMousePosition, state.frameRate); AnimationWindowUtility.AddKeyframes(state, dopeline.curves, mouseKeyTime); Event.current.Use(); } private void HandleContextMenu(DopeLine dopeline) { if (!dopeline.position.Contains(Event.current.mousePosition)) return; // Actual context menu GenerateMenu(dopeline).ShowAsContext(); } private Rect GetKeyframeRect(DopeLine dopeline, AnimationWindowKeyframe keyframe) { float time = keyframe != null ? keyframe.time : 0f; float width = 10f; if (dopeline.isPptrDopeline && dopeline.tallMode && (keyframe == null || keyframe.value != null)) width = dopeline.position.height; return new Rect(state.TimeToPixel(state.SnapToFrame(time, AnimationWindowState.SnapMode.SnapToFrame)) + GetKeyframeOffset(dopeline, keyframe), dopeline.position.yMin, width, dopeline.position.height); } // This means "how much is the rendered keyframe offset in pixels for x-axis". // Say you are rendering keyframe to some time t. The time t relates to some pixel x, but you then need to offset because keyframe diamond center represents the time, not the left edge // However for pptr keyframes, the time is represented by left edge private float GetKeyframeOffset(DopeLine dopeline, AnimationWindowKeyframe keyframe) { if (dopeline.isPptrDopeline && dopeline.tallMode && (keyframe == null || keyframe.value != null)) return k_PptrKeyframeOffset; else return k_KeyframeOffset; } // Frame the selected keyframes or selected dopelines public void FrameClip() { if (state.disabled) return; Vector2 timeRange = state.timeRange; timeRange.y = Mathf.Max(timeRange.x + 0.1f, timeRange.y); SetShownHRangeInsideMargins(timeRange.x, timeRange.y); } public void FrameSelected() { Bounds frameBounds = new Bounds(); bool firstKey = true; bool keyframesSelected = state.selectedKeys.Count > 0; if (keyframesSelected) { foreach (AnimationWindowKeyframe key in state.selectedKeys) { Vector2 pt = new Vector2(key.time, 0.0f); if (firstKey) { frameBounds.SetMinMax(pt, pt); firstKey = false; } else { frameBounds.Encapsulate(pt); } } } // No keyframes selected. Frame to selected dopelines bool frameToClip = !keyframesSelected; if (!keyframesSelected) { bool dopelinesSelected = state.hierarchyState.selectedIDs.Count > 0; if (dopelinesSelected) { foreach (AnimationWindowCurve curve in state.activeCurves) { int keyCount = curve.keyframes.Count; if (keyCount > 1) { Vector2 pt1 = new Vector2(curve.keyframes[0].time, 0.0f); Vector2 pt2 = new Vector2(curve.keyframes[keyCount - 1].time, 0.0f); if (firstKey) { frameBounds.SetMinMax(pt1, pt2); firstKey = false; } else { frameBounds.Encapsulate(pt1); frameBounds.Encapsulate(pt2); } frameToClip = false; } } } } if (frameToClip) FrameClip(); else { // Let's make sure we don't zoom too close. frameBounds.size = new Vector3(Mathf.Max(frameBounds.size.x, 0.1f), Mathf.Max(frameBounds.size.y, 0.1f), 0); SetShownHRangeInsideMargins(frameBounds.min.x, frameBounds.max.x); } } private bool DoDragAndDrop(DopeLine dopeLine, Rect position, bool perform) { if (position.Contains(Event.current.mousePosition) == false) return false; if (!ValidateDragAndDropObjects()) return false; System.Type targetType = DragAndDrop.objectReferences[0].GetType(); AnimationWindowCurve curve = null; if (dopeLine.valueType == targetType) { curve = dopeLine.curves[0]; } else { // dopeline ValueType wasn't exact match. We can still look for a curve that accepts our drop object type foreach (AnimationWindowCurve dopelineCurve in dopeLine.curves) { if (dopelineCurve.isPPtrCurve) { if (dopelineCurve.valueType == targetType) curve = dopelineCurve; List sprites = SpriteUtility.GetSpriteFromPathsOrObjects(DragAndDrop.objectReferences, DragAndDrop.paths, Event.current.type); if (dopelineCurve.valueType == typeof(Sprite) && sprites.Count > 0) { curve = dopelineCurve; targetType = typeof(Sprite); } } } } if (curve == null) return false; if (!curve.clipIsEditable) return false; if (perform) { float time = Mathf.Max(state.PixelToTime(Event.current.mousePosition.x, AnimationWindowState.SnapMode.SnapToFrame), 0f); AnimationWindowCurve targetCurve = GetCurveOfType(dopeLine, targetType); PerformDragAndDrop(targetCurve, time); } return true; } private void PerformDragAndDrop(AnimationWindowCurve targetCurve, float time) { if (DragAndDrop.objectReferences.Length == 0 || targetCurve == null) return; string undoLabel = L10n.Tr("Drop Key"); state.SaveKeySelection(undoLabel); state.ClearSelections(); Object[] objectReferences = GetSortedDragAndDropObjectReferences(); int startFrame = AnimationKeyTime.Time(time, targetCurve.clip.frameRate).frame; for (int i = 0; i < objectReferences.Length; ++i) { Object value = objectReferences[i]; if (value is Texture2D) value = SpriteUtility.TextureToSprite(value as Texture2D); CreateNewPPtrKeyframe(AnimationKeyTime.Frame(startFrame + i, targetCurve.clip.frameRate).time, value, targetCurve); } state.SaveCurve(targetCurve.clip, targetCurve, undoLabel); DragAndDrop.AcceptDrag(); } private Object[] GetSortedDragAndDropObjectReferences() { Object[] objectReferences = DragAndDrop.objectReferences; // Use same name compare as when we sort in the backend: See AssetDatabase.cpp: SortChildren System.Array.Sort(objectReferences, (a, b) => EditorUtility.NaturalCompare(a.name, b.name)); return objectReferences; } private void CreateNewPPtrKeyframe(float time, Object value, AnimationWindowCurve targetCurve) { ObjectReferenceKeyframe referenceKeyframe = new ObjectReferenceKeyframe(); referenceKeyframe.time = time; referenceKeyframe.value = value; AnimationWindowKeyframe keyframe = new AnimationWindowKeyframe(targetCurve, referenceKeyframe); AnimationKeyTime newTime = AnimationKeyTime.Time(keyframe.time, state.frameRate); targetCurve.AddKeyframe(keyframe, newTime); state.SelectKey(keyframe); } // if targetType == null, it means that all types are fine (as long as they are all of the same type) private static bool ValidateDragAndDropObjects() { if (DragAndDrop.objectReferences.Length == 0) return false; // Let's be safe and early out if any of the objects are null or if they aren't all of the same type (exception beign sprite vs. texture2D, which are considered equal here) for (int i = 0; i < DragAndDrop.objectReferences.Length; i++) { Object obj = DragAndDrop.objectReferences[i]; if (obj == null) { return false; } if (i < DragAndDrop.objectReferences.Length - 1) { Object nextObj = DragAndDrop.objectReferences[i + 1]; bool bothAreSpritesOrTextures = (obj is Texture2D || obj is Sprite) && (nextObj is Texture2D || nextObj is Sprite); if (obj.GetType() != nextObj.GetType() && !bothAreSpritesOrTextures) { return false; } } } return true; } private AnimationWindowCurve GetCurveOfType(DopeLine dopeLine, System.Type type) { foreach (AnimationWindowCurve curve in dopeLine.curves) { if (curve.valueType == type) return curve; } return null; } // For optimizing. Starting from keyIndex, we check through any key with same time and see if any are selected private bool AnyKeyIsSelectedAtTime(DopeLine dopeLine, int keyIndex) { AnimationWindowKeyframe keyframe = dopeLine.keys[keyIndex]; int firstTimeHash = keyframe.m_TimeHash; int length = dopeLine.keys.Count; for (int i = keyIndex; i < length; i++) { keyframe = dopeLine.keys[i]; int timeHash = keyframe.m_TimeHash; if (timeHash != firstTimeHash) return false; if (state.KeyIsSelected(keyframe)) return true; } return false; } private struct AddKeyToDopelineContext { public DopeLine dopeline; public AnimationKeyTime time; } private void AddKeyToDopeline(object obj) { AddKeyToDopeline((AddKeyToDopelineContext)obj); } private void AddKeyToDopeline(AddKeyToDopelineContext context) { AnimationWindowUtility.AddKeyframes(state, context.dopeline.curves, context.time); } private void DeleteKeys(object obj) { DeleteKeys((List)obj); } private void DeleteKeys(List keys) { state.DeleteKeys(keys); } internal class DopeSheetSelectionRect { Vector2 m_SelectStartPoint; Vector2 m_SelectMousePoint; bool m_ValidRect; private DopeSheetEditor owner; enum SelectionType { Normal, Additive, Subtractive } public readonly GUIStyle createRect = "U2D.createRect"; static int s_RectSelectionID = GUIUtility.GetPermanentControlID(); public DopeSheetSelectionRect(DopeSheetEditor owner) { this.owner = owner; } public void OnGUI(Rect position) { Event evt = Event.current; Vector2 mousePos = evt.mousePosition; int id = s_RectSelectionID; switch (evt.GetTypeForControl(id)) { case EventType.MouseDown: if (evt.button == 0 && position.Contains(mousePos)) { GUIUtility.hotControl = id; m_SelectStartPoint = mousePos; m_ValidRect = false; evt.Use(); } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { m_ValidRect = Mathf.Abs((mousePos - m_SelectStartPoint).x) > 1f; if (m_ValidRect) m_SelectMousePoint = new Vector2(mousePos.x, mousePos.y); evt.Use(); } break; case EventType.Repaint: if (GUIUtility.hotControl == id && m_ValidRect) EditorStyles.selectionRect.Draw(GetCurrentPixelRect(), GUIContent.none, false, false, false, false); break; case EventType.MouseUp: if (GUIUtility.hotControl == id && evt.button == 0) { if (m_ValidRect) { if (!evt.shift && !EditorGUI.actionKey) owner.state.ClearSelections(); float frameRate = owner.state.frameRate; Rect timeRect = GetCurrentTimeRect(); GUI.changed = true; owner.state.ClearHierarchySelection(); HashSet toBeUnselected = new HashSet(); HashSet toBeSelected = new HashSet(); foreach (DopeLine dopeline in owner.state.dopelines) { if (dopeline.position.yMin >= timeRect.yMin && dopeline.position.yMax <= timeRect.yMax) { foreach (AnimationWindowKeyframe keyframe in dopeline.keys) { AnimationKeyTime startTime = AnimationKeyTime.Time(timeRect.xMin, frameRate); AnimationKeyTime endTime = AnimationKeyTime.Time(timeRect.xMax, frameRate); AnimationKeyTime keyTime = AnimationKeyTime.Time(keyframe.time, frameRate); // for dopeline tallmode, we don't want to select the sprite at the end. It just feels wrong. if (!dopeline.tallMode && keyTime.frame >= startTime.frame && keyTime.frame <= endTime.frame || dopeline.tallMode && keyTime.frame >= startTime.frame && keyTime.frame < endTime.frame) { if (!toBeSelected.Contains(keyframe) && !toBeUnselected.Contains(keyframe)) { if (!owner.state.KeyIsSelected(keyframe)) toBeSelected.Add(keyframe); else if (owner.state.KeyIsSelected(keyframe)) toBeUnselected.Add(keyframe); } } } } } // Only if all the keys inside rect are selected, we want to unselect them. if (toBeSelected.Count == 0) foreach (AnimationWindowKeyframe keyframe in toBeUnselected) owner.state.UnselectKey(keyframe); foreach (AnimationWindowKeyframe keyframe in toBeSelected) owner.state.SelectKey(keyframe); // Update hierarchy selection based on newly selected keys foreach (DopeLine dopeline in owner.state.dopelines) if (owner.state.AnyKeyIsSelected(dopeline)) owner.state.SelectHierarchyItem(dopeline, true, false); } else { owner.state.ClearSelections(); } evt.Use(); GUIUtility.hotControl = 0; } break; } } public Rect GetCurrentPixelRect() { float height = AnimationWindowHierarchyGUI.k_DopeSheetRowHeight; Rect r = AnimationWindowUtility.FromToRect(m_SelectStartPoint, m_SelectMousePoint); r.xMin = owner.state.TimeToPixel(owner.state.PixelToTime(r.xMin, AnimationWindowState.SnapMode.SnapToFrame), AnimationWindowState.SnapMode.SnapToFrame); r.xMax = owner.state.TimeToPixel(owner.state.PixelToTime(r.xMax, AnimationWindowState.SnapMode.SnapToFrame), AnimationWindowState.SnapMode.SnapToFrame); r.yMin = Mathf.Floor(r.yMin / height) * height; r.yMax = (Mathf.Floor(r.yMax / height) + 1) * height; return r; } public Rect GetCurrentTimeRect() { float height = AnimationWindowHierarchyGUI.k_DopeSheetRowHeight; Rect r = AnimationWindowUtility.FromToRect(m_SelectStartPoint, m_SelectMousePoint); r.xMin = owner.state.PixelToTime(r.xMin, AnimationWindowState.SnapMode.SnapToFrame); r.xMax = owner.state.PixelToTime(r.xMax, AnimationWindowState.SnapMode.SnapToFrame); r.yMin = Mathf.Floor(r.yMin / height) * height; r.yMax = (Mathf.Floor(r.yMax / height) + 1) * height; return r; } } public void UpdateCurves(List changedCurves, string undoText) { Undo.RegisterCompleteObjectUndo(state.activeAnimationClip, undoText); foreach (ChangedCurve changedCurve in changedCurves) { AnimationWindowCurve curve = state.filteredCurves.Find(c => changedCurve.curveId == c.GetHashCode()); if (curve != null) { AnimationUtility.SetEditorCurve(curve.clip, changedCurve.binding, changedCurve.curve); } else { Debug.LogError("Could not match ChangedCurve data to destination curves."); } } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/DopeSheetEditorRectangleTool.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditorInternal; using System.Collections.Generic; namespace UnityEditor { internal class DopeSheetEditorRectangleTool : RectangleTool { const int kScaleLeftWidth = 17; const int kScaleLeftMarginHorizontal = 0; const float kScaleLeftMarginVertical = 4; const int kScaleRightWidth = 17; const int kScaleRightMarginHorizontal = 0; const float kScaleRightMarginVertical = 4; const int kHLabelMarginHorizontal = 8; const int kHLabelMarginVertical = 1; static Rect g_EmptyRect = new Rect(0f, 0f, 0f, 0f); struct ToolLayout { public Rect summaryRect; public Rect selectionRect; public Rect scaleLeftRect; public Rect scaleRightRect; public Vector2 leftLabelAnchor; public Vector2 rightLabelAnchor; } private DopeSheetEditor m_DopeSheetEditor; private AnimationWindowState m_State; private ToolLayout m_Layout; private Vector2 m_Pivot; private Vector2 m_Previous; private Vector2 m_MouseOffset; private bool m_IsDragging; private bool m_RippleTime; private float m_RippleTimeStart; private float m_RippleTimeEnd; private AreaManipulator[] m_SelectionBoxes; private AreaManipulator m_SelectionScaleLeft; private AreaManipulator m_SelectionScaleRight; private AreaManipulator m_SelectionRippleLeft; private AreaManipulator m_SelectionRippleRight; private bool hasSelection { get { return (m_State.selectedKeys.Count > 0); } } private Bounds selectionBounds { get { return m_State.selectionBounds; } } private float frameRate { get { return m_State.frameRate; } } private bool rippleTime { get { return m_State.rippleTime; } } private bool isDragging { get { return m_IsDragging || m_DopeSheetEditor.isDragging; } } public override void Initialize(TimeArea timeArea) { base.Initialize(timeArea); m_DopeSheetEditor = timeArea as DopeSheetEditor; m_State = m_DopeSheetEditor.state; if (m_SelectionBoxes == null) { m_SelectionBoxes = new AreaManipulator[2]; for (int i = 0; i < 2; ++i) { m_SelectionBoxes[i] = new AreaManipulator(styles.rectangleToolSelection, MouseCursor.MoveArrow); m_SelectionBoxes[i].onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { bool curveEditorOverride = evt.shift || EditorGUI.actionKey; if (!curveEditorOverride && hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartMove(new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0.0f), rippleTime); return true; } return false; }; m_SelectionBoxes[i].onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnMove(new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0.0f)); return true; }; m_SelectionBoxes[i].onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndMove(); return true; }; } } if (m_SelectionScaleLeft == null) { m_SelectionScaleLeft = new AreaManipulator(styles.dopesheetScaleLeft, MouseCursor.ResizeHorizontal); m_SelectionScaleLeft.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Right, ToolCoord.Left, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), false); return true; } return false; }; m_SelectionScaleLeft.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionScaleLeft.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionScaleRight == null) { m_SelectionScaleRight = new AreaManipulator(styles.dopesheetScaleRight, MouseCursor.ResizeHorizontal); m_SelectionScaleRight.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Left, ToolCoord.Right, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), false); return true; } return false; }; m_SelectionScaleRight.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionScaleRight.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionRippleLeft == null) { m_SelectionRippleLeft = new AreaManipulator(styles.dopesheetRippleLeft, MouseCursor.ResizeHorizontal); m_SelectionRippleLeft.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Right, ToolCoord.Left, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), true); return true; } return false; }; m_SelectionRippleLeft.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionRippleLeft.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } if (m_SelectionRippleRight == null) { m_SelectionRippleRight = new AreaManipulator(styles.dopesheetRippleRight, MouseCursor.ResizeHorizontal); m_SelectionRippleRight.onStartDrag += (AnimationWindowManipulator manipulator, Event evt) => { if (hasSelection && manipulator.rect.Contains(evt.mousePosition)) { OnStartScale(ToolCoord.Left, ToolCoord.Right, new Vector2(PixelToTime(evt.mousePosition.x, frameRate), 0f), true); return true; } return false; }; m_SelectionRippleRight.onDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnScaleTime(PixelToTime(evt.mousePosition.x, frameRate)); return true; }; m_SelectionRippleRight.onEndDrag += (AnimationWindowManipulator manipulator, Event evt) => { OnEndScale(); return true; }; } } public void OnGUI() { if (!hasSelection) return; if (Event.current.type != EventType.Repaint) return; m_Layout = CalculateLayout(); m_SelectionBoxes[0].OnGUI(m_Layout.summaryRect); m_SelectionBoxes[1].OnGUI(m_Layout.selectionRect); bool showRippleHandles = (rippleTime && !isDragging) || (m_RippleTime && isDragging); if (showRippleHandles) { m_SelectionRippleLeft.OnGUI(m_Layout.scaleLeftRect); m_SelectionRippleRight.OnGUI(m_Layout.scaleRightRect); } else { m_SelectionScaleLeft.OnGUI(m_Layout.scaleLeftRect); m_SelectionScaleRight.OnGUI(m_Layout.scaleRightRect); } DrawLabels(); } public void HandleEvents() { if (rippleTime) { m_SelectionRippleLeft.HandleEvents(); m_SelectionRippleRight.HandleEvents(); } else { m_SelectionScaleLeft.HandleEvents(); m_SelectionScaleRight.HandleEvents(); } m_SelectionBoxes[0].HandleEvents(); m_SelectionBoxes[1].HandleEvents(); } private ToolLayout CalculateLayout() { ToolLayout layout = new ToolLayout(); Bounds bounds = selectionBounds; bool canScaleX = !Mathf.Approximately(bounds.size.x, 0f); float xMin = TimeToPixel(bounds.min.x); float xMax = TimeToPixel(bounds.max.x); float yMin = 0f, yMax = 0f; bool firstKey = true; float heightCumul = 0f; List dopelines = m_State.dopelines; for (int i = 0; i < dopelines.Count; ++i) { DopeLine dopeline = dopelines[i]; float dopelineHeight = (dopeline.tallMode ? AnimationWindowHierarchyGUI.k_DopeSheetRowHeightTall : AnimationWindowHierarchyGUI.k_DopeSheetRowHeight); if (!dopeline.isMasterDopeline) { int length = dopeline.keys.Count; for (int j = 0; j < length; j++) { AnimationWindowKeyframe keyframe = dopeline.keys[j]; if (m_State.KeyIsSelected(keyframe)) { if (firstKey) { yMin = heightCumul; firstKey = false; } yMax = heightCumul + dopelineHeight; break; } } } heightCumul += dopelineHeight; } layout.summaryRect = new Rect(xMin, 0f, xMax - xMin, AnimationWindowHierarchyGUI.k_DopeSheetRowHeight); layout.selectionRect = new Rect(xMin, yMin, xMax - xMin, yMax - yMin); // Scale handles. if (canScaleX) { layout.scaleLeftRect = new Rect(layout.selectionRect.xMin - kScaleLeftMarginHorizontal - kScaleLeftWidth, layout.selectionRect.yMin + kScaleLeftMarginVertical, kScaleLeftWidth, layout.selectionRect.height - kScaleLeftMarginVertical * 2); layout.scaleRightRect = new Rect(layout.selectionRect.xMax + kScaleRightMarginHorizontal, layout.selectionRect.yMin + kScaleRightMarginVertical, kScaleRightWidth, layout.selectionRect.height - kScaleRightMarginVertical * 2); } else { layout.scaleLeftRect = g_EmptyRect; layout.scaleRightRect = g_EmptyRect; } if (canScaleX) { layout.leftLabelAnchor = new Vector2(layout.summaryRect.xMin - kHLabelMarginHorizontal, contentRect.yMin + kHLabelMarginVertical); layout.rightLabelAnchor = new Vector2(layout.summaryRect.xMax + kHLabelMarginHorizontal, contentRect.yMin + kHLabelMarginVertical); } else { layout.leftLabelAnchor = layout.rightLabelAnchor = new Vector2(layout.summaryRect.center.x + kHLabelMarginHorizontal, contentRect.yMin + kHLabelMarginVertical); } return layout; } private void DrawLabels() { if (isDragging == false) return; bool canScaleX = !Mathf.Approximately(selectionBounds.size.x, 0f); if (canScaleX) { GUIContent leftLabelContent = new GUIContent(string.Format("{0}", m_DopeSheetEditor.FormatTime(selectionBounds.min.x, m_State.frameRate, m_State.timeFormat))); GUIContent rightLabelContent = new GUIContent(string.Format("{0}", m_DopeSheetEditor.FormatTime(selectionBounds.max.x, m_State.frameRate, m_State.timeFormat))); Vector2 leftLabelSize = styles.dragLabel.CalcSize(leftLabelContent); Vector2 rightLabelSize = styles.dragLabel.CalcSize(rightLabelContent); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.leftLabelAnchor.x - leftLabelSize.x, m_Layout.leftLabelAnchor.y, leftLabelSize.x, leftLabelSize.y), leftLabelContent, styles.dragLabel, 0.3f); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.rightLabelAnchor.x, m_Layout.rightLabelAnchor.y, rightLabelSize.x, rightLabelSize.y), rightLabelContent, styles.dragLabel, 0.3f); } else { GUIContent labelContent = new GUIContent(string.Format("{0}", m_DopeSheetEditor.FormatTime(selectionBounds.center.x, m_State.frameRate, m_State.timeFormat))); Vector2 labelSize = styles.dragLabel.CalcSize(labelContent); EditorGUI.DoDropShadowLabel(new Rect(m_Layout.leftLabelAnchor.x, m_Layout.leftLabelAnchor.y, labelSize.x, labelSize.y), labelContent, styles.dragLabel, 0.3f); } } private void OnStartScale(ToolCoord pivotCoord, ToolCoord pickedCoord, Vector2 mousePos, bool rippleTime) { Bounds bounds = selectionBounds; m_IsDragging = true; m_Pivot = ToolCoordToPosition(pivotCoord, bounds); m_Previous = ToolCoordToPosition(pickedCoord, bounds); m_MouseOffset = mousePos - m_Previous; m_RippleTime = rippleTime; m_RippleTimeStart = bounds.min.x; m_RippleTimeEnd = bounds.max.x; m_State.StartLiveEdit(); } private void OnScaleTime(float time) { Matrix4x4 transform; bool flipX; if (CalculateScaleTimeMatrix(m_Previous.x, time, m_MouseOffset.x, m_Pivot.x, frameRate, out transform, out flipX)) TransformKeys(transform, flipX, false); } private void OnEndScale() { m_State.EndLiveEdit(); m_IsDragging = false; } internal void OnStartMove(Vector2 position, bool rippleTime) { Bounds bounds = selectionBounds; m_IsDragging = true; m_Previous = position; m_RippleTime = rippleTime; m_RippleTimeStart = bounds.min.x; m_RippleTimeEnd = bounds.max.x; m_State.StartLiveEdit(); } internal void OnMove(Vector2 position) { Vector2 dv = position - m_Previous; Matrix4x4 transform = Matrix4x4.identity; transform.SetTRS(new Vector3(dv.x, dv.y, 0f), Quaternion.identity, Vector3.one); TransformKeys(transform, false, false); } internal void OnEndMove() { m_State.EndLiveEdit(); m_IsDragging = false; } private void TransformKeys(Matrix4x4 matrix, bool flipX, bool flipY) { if (m_RippleTime) m_State.TransformRippleKeys(matrix, m_RippleTimeStart, m_RippleTimeEnd, flipX, true); else m_State.TransformSelectedKeys(matrix, flipX, flipY, true); } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/GameObjectSelectionItem.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; namespace UnityEditorInternal { [Serializable] internal class GameObjectSelectionItem : AnimationWindowSelectionItem { public static GameObjectSelectionItem Create(GameObject gameObject) { var selectionItem = new GameObjectSelectionItem(); selectionItem.gameObject = gameObject; selectionItem.animationClip = null; selectionItem.id = 0; // no need for id since there's only one item in selection. if (selectionItem.rootGameObject != null) { AnimationClip[] allClips = AnimationUtility.GetAnimationClips(selectionItem.rootGameObject); if (selectionItem.animationClip == null && selectionItem.gameObject != null) // there is activeGO but clip is still null selectionItem.animationClip = allClips.Length > 0 ? allClips[0] : null; else if (!Array.Exists(allClips, x => x == selectionItem.animationClip)) // clip doesn't belong to the currently active GO selectionItem.animationClip = allClips.Length > 0 ? allClips[0] : null; } return selectionItem; } public override AnimationClip animationClip { set { base.animationClip = value; } get { if (animationPlayer == null) return null; return base.animationClip; } } public override void Synchronize() { if (rootGameObject != null) { AnimationClip[] allClips = AnimationUtility.GetAnimationClips(rootGameObject); if (allClips.Length > 0) { if (!Array.Exists(allClips, x => x == animationClip)) { animationClip = allClips[0]; } } else { animationClip = null; } } } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/IAnimationContextualResponder.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using Object = UnityEngine.Object; namespace UnityEditorInternal { // Required information for animation recording. internal interface IAnimationContextualResponder { bool IsAnimatable(PropertyModification[] modifications); bool IsEditable(Object targetObject); bool KeyExists(PropertyModification[] modifications); bool CandidateExists(PropertyModification[] modifications); bool CurveExists(PropertyModification[] modifications); bool HasAnyCandidates(); bool HasAnyCurves(); void AddKey(PropertyModification[] modifications); void RemoveKey(PropertyModification[] modifications); void RemoveCurve(PropertyModification[] modifications); void AddCandidateKeys(); void AddAnimatedKeys(); void GoToNextKeyframe(PropertyModification[] modifications); void GoToPreviousKeyframe(PropertyModification[] modifications); } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/IAnimationRecordingState.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using UnityEditor; namespace UnityEditorInternal { // Required information for animation recording. internal interface IAnimationRecordingState { GameObject activeGameObject { get; } GameObject activeRootGameObject { get; } AnimationClip activeAnimationClip { get; } int currentFrame { get; } bool addZeroFrame { get; } bool DiscardModification(PropertyModification modification); void SaveCurve(AnimationWindowCurve curve); void AddPropertyModification(EditorCurveBinding binding, PropertyModification propertyModification, bool keepPrefabOverride); } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/IAnimationWindowControl.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace UnityEditorInternal { // Compatibility implementation of IAnimationWindowController. abstract class IAnimationWindowControl : ScriptableObject, IAnimationWindowController { [SerializeReference] AnimationWindowState m_State; public virtual void OnEnable() { hideFlags = HideFlags.HideAndDontSave; } public abstract void OnSelectionChanged(); public void Init(AnimationWindowState state) { m_State = state; } void IAnimationWindowController.OnCreate(AnimationWindow animationWindow, UnityEngine.Component component) { } void IAnimationWindowController.OnDestroy() { // Animation Window has ownership of `IAnimationWindowControl` in legacy workflow. Object.DestroyImmediate(this); } float IAnimationWindowController.time { get => time.time; set => GoToTime(value); } int IAnimationWindowController.frame { get => time.frame; set => GoToFrame(value); } public abstract AnimationKeyTime time { get; } public abstract void GoToTime(float time); public abstract void GoToFrame(int frame); // Not used anymore. This was not used by either the AnimationWindow nor Timeline. // Replaced by setting IAnimationWindowController.time. public abstract void StartScrubTime(); public abstract void ScrubTime(float time); public abstract void EndScrubTime(); // Not used anymore. Required internal knowledge of the Animation Window and // default implementation should suffice. public abstract void GoToPreviousFrame(); public abstract void GoToNextFrame(); public abstract void GoToPreviousKeyframe(); public abstract void GoToNextKeyframe(); public abstract void GoToFirstKeyframe(); public abstract void GoToLastKeyframe(); public abstract bool canPlay { get; } bool IAnimationWindowController.playing { get => playing; set { if (value) StartPlayback(); else StopPlayback(); } } public abstract bool playing { get; } public abstract bool StartPlayback(); public abstract void StopPlayback(); public abstract bool PlaybackUpdate(); public abstract bool canPreview { get; } bool IAnimationWindowController.previewing { get => previewing; set { if (value) StartPreview(); else StopPreview(); } } public abstract bool previewing { get; } public abstract bool StartPreview(); public abstract void StopPreview(); public abstract bool canRecord { get; } bool IAnimationWindowController.recording { get => recording; set { if (value) StartRecording(null); else StopRecording(); } } public abstract bool recording { get; } // targetObject parameter is not used. public abstract bool StartRecording(Object targetObject); public abstract void StopRecording(); public abstract void ResampleAnimation(); public abstract void ProcessCandidates(); public abstract void ClearCandidates(); EditorCurveBinding[] IAnimationWindowController.GetAnimatableBindings() { if (m_State == null) return Array.Empty(); var rootGameObject = m_State.activeRootGameObject; var scriptableObject = m_State.activeScriptableObject; if (rootGameObject != null) { return AnimationWindowUtility.GetAnimatableBindings(rootGameObject); } if (scriptableObject != null) { return AnimationUtility.GetAnimatableBindings(scriptableObject); } return Array.Empty(); } EditorCurveBinding[] IAnimationWindowController.GetAnimatableBindings(GameObject gameObject) { if (m_State == null) return Array.Empty(); var rootGameObject = m_State.activeRootGameObject; return AnimationUtility.GetAnimatableBindings(gameObject, rootGameObject); } System.Type IAnimationWindowController.GetValueType(EditorCurveBinding binding) { if (m_State == null) return default; var rootGameObject = m_State.activeRootGameObject; var scriptableObject = m_State.activeScriptableObject; if (rootGameObject != null) { return AnimationUtility.GetEditorCurveValueType(rootGameObject, binding); } else if (scriptableObject != null) { return AnimationUtility.GetEditorCurveValueType(scriptableObject, binding); } else { if (binding.isPPtrCurve) { // Cannot extract type of PPtrCurve. return null; } else { // Cannot extract type of AnimationCurve. Default to float. return typeof(float); } } } float IAnimationWindowController.GetFloatValue(EditorCurveBinding binding) { if (m_State == null) return default; AnimationUtility.GetFloatValue(m_State.activeRootGameObject, binding, out var value); return value; } int IAnimationWindowController.GetIntValue(EditorCurveBinding binding) { if (m_State == null) return default; AnimationUtility.GetDiscreteIntValue(m_State.activeRootGameObject, binding, out var value); return value; } UnityEngine.Object IAnimationWindowController.GetObjectReferenceValue(EditorCurveBinding binding) { if (m_State == null) return default; AnimationUtility.GetObjectReferenceValue(m_State.activeRootGameObject, binding, out var value); return value; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/IAnimationWindowController.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; namespace UnityEditor { interface IAnimationWindowController { void OnCreate(AnimationWindow animationWindow, UnityEngine.Component component); void OnDestroy(); void OnSelectionChanged(); float time { get; set; } int frame { get; set; } bool canPlay { get; } bool playing { get; set; } bool PlaybackUpdate(); bool canPreview { get; } bool previewing { get; set; } bool canRecord { get; } bool recording { get; set; } void ResampleAnimation(); void ProcessCandidates(); void ClearCandidates(); EditorCurveBinding[] GetAnimatableBindings(GameObject gameObject); EditorCurveBinding[] GetAnimatableBindings(); System.Type GetValueType(EditorCurveBinding binding); float GetFloatValue(EditorCurveBinding binding); int GetIntValue(EditorCurveBinding binding); UnityEngine.Object GetObjectReferenceValue(EditorCurveBinding binding); } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/MinMaxCurveEditorWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; namespace UnityEditor { internal class MinMaxCurveEditorWindow : EditorWindow { const int k_PresetsHeight = 46; const float k_WindowMinSize = 240; const float k_WindowMaxSize = 10000; const float k_PresetSwatchMargin = 45f; const float k_PresetSwatchWidth = 40f; const float k_PresetSwatchHeight = 25f; const float k_PresetSwatchSeperation = 5; const float k_PresetsDropdownButtonSize = 20; static MinMaxCurveEditorWindow s_SharedMinMaxCurveEditor; static CurveEditorWindow.Styles s_Styles; CurveEditor m_CurveEditor; AnimationCurve m_MinCurve; AnimationCurve m_MaxCurve; SerializedProperty m_MultiplierProperty; Color m_Color; DoubleCurvePresetsContentsForPopupWindow m_CurvePresets; [SerializeField] GUIView delegateView; public static MinMaxCurveEditorWindow instance { get { if (!s_SharedMinMaxCurveEditor) s_SharedMinMaxCurveEditor = ScriptableObject.CreateInstance(); return s_SharedMinMaxCurveEditor; } } public AnimationCurve minCurve { get { return m_MinCurve; } } public AnimationCurve maxCurve { get { return m_MaxCurve; } } public static string xAxisLabel { get; set; } = "time"; public static bool visible { get { return s_SharedMinMaxCurveEditor != null; } } // Called by OnEnable to make sure the CurveEditor is not null, // and by Show so we get a fresh CurveEditor when the user clicks a new curve. void Init(CurveEditorSettings settings) { m_CurveEditor = new CurveEditor(GetCurveEditorRect(), GetCurveWrapperArray(), true); m_CurveEditor.curvesUpdated = UpdateCurve; m_CurveEditor.scaleWithWindow = true; m_CurveEditor.margin = 40; if (settings != null) m_CurveEditor.settings = settings; m_CurveEditor.settings.hTickLabelOffset = 10; m_CurveEditor.settings.rectangleToolFlags = CurveEditorSettings.RectangleToolFlags.MiniRectangleTool; m_CurveEditor.settings.undoRedoSelection = true; m_CurveEditor.settings.showWrapperPopups = true; m_CurveEditor.settings.xAxisLabel = xAxisLabel; UpdateRegionDomain(); // For each of horizontal and vertical axis, if we have a finite range for that axis, use that range, // otherwise use framing logic to determine shown range for that axis. bool frameH = true; bool frameV = true; if (!float.IsNegativeInfinity(m_CurveEditor.settings.hRangeMin) && !float.IsInfinity(m_CurveEditor.settings.hRangeMax)) { m_CurveEditor.SetShownHRangeInsideMargins(m_CurveEditor.settings.hRangeMin, m_CurveEditor.settings.hRangeMax); frameH = false; } if (!float.IsNegativeInfinity(m_CurveEditor.settings.vRangeMin) && !float.IsInfinity(m_CurveEditor.settings.vRangeMax)) { m_CurveEditor.SetShownVRangeInsideMargins(m_CurveEditor.settings.vRangeMin, m_CurveEditor.settings.vRangeMax); frameV = false; } m_CurveEditor.FrameSelected(frameH, frameV); } void InitCurvePresets() { if (m_CurvePresets == null) { AnimationCurve max = m_CurveEditor.animationCurves[0].curve; AnimationCurve min = m_CurveEditor.animationCurves.Length > 1 ? m_CurveEditor.animationCurves[1].curve : new AnimationCurve(); // Selection callback for library window System.Action presetSelectedCallback = delegate(DoubleCurve presetCurve) { var doubleCurve = new DoubleCurve(min, max, true); doubleCurve.minCurve.keys = CurveEditorWindow.GetNormalizedKeys(presetCurve.minCurve.keys, m_CurveEditor); doubleCurve.minCurve.postWrapMode = presetCurve.minCurve.postWrapMode; doubleCurve.minCurve.preWrapMode = presetCurve.minCurve.preWrapMode; doubleCurve.maxCurve.keys = CurveEditorWindow.GetNormalizedKeys(presetCurve.maxCurve.keys, m_CurveEditor); doubleCurve.maxCurve.postWrapMode = presetCurve.maxCurve.postWrapMode; doubleCurve.maxCurve.preWrapMode = presetCurve.maxCurve.preWrapMode; m_MinCurve = doubleCurve.minCurve; m_MaxCurve = doubleCurve.maxCurve; m_CurveEditor.SelectNone(); RefreshShownCurves(); SendEvent("CurveChanged", true); }; // We set the curve to save when showing the popup to ensure to scale the current state of the curve m_CurvePresets = new DoubleCurvePresetsContentsForPopupWindow(new DoubleCurve(min, max, true), presetSelectedCallback); m_CurvePresets.InitIfNeeded(); m_CurvePresets.GetPresetLibraryEditor().GetCurrentLib().useRanges = false; } } public static void SetCurves(SerializedProperty max, SerializedProperty min, SerializedProperty multiplier, Color color) { instance.m_Color = color; if (max == null) instance.m_MaxCurve = null; else instance.m_MaxCurve = max.hasMultipleDifferentValues ? new AnimationCurve() : max.animationCurveValue; if (min == null) instance.m_MinCurve = null; else instance.m_MinCurve = min.hasMultipleDifferentValues ? new AnimationCurve() : min.animationCurveValue; instance.m_MultiplierProperty = multiplier; instance.RefreshShownCurves(); } public static void ShowPopup(GUIView viewToUpdate) { instance.Show(viewToUpdate, null); } void SetAxisUiScalarsCallback(Vector2 newAxisScalars) { if (m_MultiplierProperty == null) return; m_MultiplierProperty.floatValue = newAxisScalars.y; // We must apply the changes as this is called outside of the OnGUI code and changes made will not be applied. m_MultiplierProperty.serializedObject.ApplyModifiedProperties(); } Vector2 GetAxisUiScalarsCallback() { if (m_MultiplierProperty == null) return Vector2.one; if (m_MultiplierProperty.floatValue < 0) { m_MultiplierProperty.floatValue = Mathf.Abs(m_MultiplierProperty.floatValue); m_MultiplierProperty.serializedObject.ApplyModifiedProperties(); } return new Vector2(1, m_MultiplierProperty.floatValue); } CurveWrapper GetCurveWrapper(AnimationCurve curve, int id) { CurveWrapper cw = new CurveWrapper(); cw.id = id; cw.groupId = -1; cw.color = m_Color; cw.hidden = false; cw.readOnly = false; cw.getAxisUiScalarsCallback = GetAxisUiScalarsCallback; cw.setAxisUiScalarsCallback = SetAxisUiScalarsCallback; cw.renderer = new NormalCurveRenderer(curve); cw.renderer.SetWrap(curve.preWrapMode, curve.postWrapMode); return cw; } CurveWrapper[] GetCurveWrapperArray() { int id = "Curve".GetHashCode(); if (m_MaxCurve != null) { var maxWrapper = GetCurveWrapper(m_MaxCurve, id); if (m_MinCurve != null) { var minWrapper = GetCurveWrapper(m_MinCurve, id + 1); minWrapper.regionId = maxWrapper.regionId = 1; return new[] { maxWrapper, minWrapper }; } else { return new[] { maxWrapper }; } } return new CurveWrapper[] {}; } Rect GetCurveEditorRect() { return new Rect(0, 0, position.width, position.height - k_PresetsHeight); } void OnEnable() { if (s_SharedMinMaxCurveEditor && s_SharedMinMaxCurveEditor != this) s_SharedMinMaxCurveEditor.Close(); s_SharedMinMaxCurveEditor = this; Init(null); } void OnDestroy() { m_CurvePresets.GetPresetLibraryEditor().UnloadUsedLibraries(); } void OnDisable() { m_CurveEditor.OnDisable(); if (s_SharedMinMaxCurveEditor == this) s_SharedMinMaxCurveEditor = null; } void RefreshShownCurves() { m_CurveEditor.animationCurves = GetCurveWrapperArray(); UpdateRegionDomain(); } void UpdateRegionDomain() { // Calculate region domain for drawing the shaded region between 2 curves. var domain = new Vector2(float.MaxValue, float.MinValue); if (m_MaxCurve != null && m_MinCurve != null) { foreach (var animationCurve in new[] { m_MaxCurve, m_MinCurve }) { var keyCount = animationCurve.length; if (keyCount > 0) { var keys = animationCurve.keys; domain.x = Mathf.Min(domain.x, animationCurve[0].time); domain.y = Math.Max(domain.y, animationCurve[keyCount - 1].time); } } } m_CurveEditor.settings.curveRegionDomain = domain; } public void Show(GUIView viewToUpdate, CurveEditorSettings settings) { delegateView = viewToUpdate; Init(settings); ShowAuxWindow(); titleContent = EditorGUIUtility.TrTextContent("Curve Editor"); // deal with window size minSize = new Vector2(k_WindowMinSize, k_WindowMinSize + k_PresetsHeight); maxSize = new Vector2(k_WindowMaxSize, k_WindowMaxSize); } void DrawPresetSwatchArea() { GUI.Box(new Rect(0, position.height - k_PresetsHeight, position.width, k_PresetsHeight), "", s_Styles.curveSwatchArea); Color curveColor = m_Color; curveColor.a *= 0.6f; float yPos = position.height - k_PresetsHeight + (k_PresetsHeight - k_PresetSwatchHeight) * 0.5f; InitCurvePresets(); var curveLibrary = m_CurvePresets.GetPresetLibraryEditor().GetCurrentLib(); if (curveLibrary != null) { GUIContent guiContent = EditorGUIUtility.TempContent(string.Empty); for (int i = 0; i < curveLibrary.Count(); i++) { Rect swatchRect = new Rect(k_PresetSwatchMargin + (k_PresetSwatchWidth + k_PresetSwatchSeperation) * i, yPos, k_PresetSwatchWidth, k_PresetSwatchHeight); guiContent.tooltip = curveLibrary.GetName(i); if (GUI.Button(swatchRect, guiContent, s_Styles.curveSwatch)) { AnimationCurve max = m_CurveEditor.animationCurves[0].curve; AnimationCurve min = m_CurveEditor.animationCurves.Length > 1 ? m_CurveEditor.animationCurves[1].curve : null; var animCurve = curveLibrary.GetPreset(i) as DoubleCurve; max.keys = CurveEditorWindow.GetDenormalizedKeys(animCurve.maxCurve.keys, m_CurveEditor); max.postWrapMode = animCurve.maxCurve.postWrapMode; max.preWrapMode = animCurve.maxCurve.preWrapMode; if (min != null) { min.keys = CurveEditorWindow.GetDenormalizedKeys(animCurve.minCurve.keys, m_CurveEditor); min.postWrapMode = animCurve.minCurve.postWrapMode; min.preWrapMode = animCurve.minCurve.preWrapMode; } m_CurveEditor.SelectNone(); RefreshShownCurves(); SendEvent("CurveChanged", true); } if (Event.current.type == EventType.Repaint) curveLibrary.Draw(swatchRect, i); if (swatchRect.xMax > position.width - 2 * k_PresetSwatchMargin) break; } } // Dropdown Rect presetDropDownButtonRect = new Rect(k_PresetSwatchMargin - k_PresetsDropdownButtonSize, yPos + k_PresetSwatchSeperation, k_PresetsDropdownButtonSize, k_PresetsDropdownButtonSize); if (EditorGUI.DropdownButton(presetDropDownButtonRect, EditorGUI.GUIContents.titleSettingsIcon, FocusType.Passive, EditorStyles.inspectorTitlebarText)) { if (m_MaxCurve != null) { AnimationCurve max = m_CurveEditor.animationCurves[0].curve; AnimationCurve maxCopy = new AnimationCurve(CurveEditorWindow.GetNormalizedKeys(max.keys, m_CurveEditor)); maxCopy.postWrapMode = max.postWrapMode; maxCopy.preWrapMode = max.preWrapMode; AnimationCurve minCopy = null; if (m_MinCurve != null) { AnimationCurve min = m_CurveEditor.animationCurves[1].curve; minCopy = new AnimationCurve(CurveEditorWindow.GetNormalizedKeys(min.keys, m_CurveEditor)); minCopy.postWrapMode = min.postWrapMode; minCopy.preWrapMode = min.preWrapMode; } m_CurvePresets.doubleCurveToSave = new DoubleCurve(minCopy, maxCopy, true); PopupWindow.Show(presetDropDownButtonRect, m_CurvePresets); } } } void OnGUI() { bool gotMouseUp = (Event.current.type == EventType.MouseUp); if (delegateView == null) { m_MinCurve = null; m_MaxCurve = null; } if (s_Styles == null) s_Styles = new CurveEditorWindow.Styles(); // Curve Editor m_CurveEditor.rect = GetCurveEditorRect(); m_CurveEditor.hRangeLocked = Event.current.shift; m_CurveEditor.vRangeLocked = EditorGUI.actionKey; GUI.changed = false; GUI.Label(m_CurveEditor.drawRect, GUIContent.none, s_Styles.curveEditorBackground); m_CurveEditor.OnGUI(); DrawPresetSwatchArea(); if (Event.current.type == EventType.Used && gotMouseUp) { DoUpdateCurve(false); SendEvent("CurveChangeCompleted", true); } else if (Event.current.type != EventType.Layout && Event.current.type != EventType.Repaint) { DoUpdateCurve(true); } } public void UpdateCurve() { DoUpdateCurve(false); } void DoUpdateCurve(bool exitGUI) { bool minChanged = m_CurveEditor.animationCurves.Length > 0 && m_CurveEditor.animationCurves[0] != null && m_CurveEditor.animationCurves[0].changed; bool maxChanged = m_CurveEditor.animationCurves.Length > 1 && m_CurveEditor.animationCurves[1] != null && m_CurveEditor.animationCurves[1].changed; if (minChanged || maxChanged) { if (minChanged) m_CurveEditor.animationCurves[0].changed = false; if (maxChanged) m_CurveEditor.animationCurves[1].changed = false; RefreshShownCurves(); SendEvent("CurveChanged", exitGUI); } } void SendEvent(string eventName, bool exitGUI) { if (delegateView) { Event e = EditorGUIUtility.CommandEvent(eventName); Repaint(); delegateView.SendEvent(e); if (exitGUI) GUIUtility.ExitGUI(); } GUI.changed = true; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/RectangleTool.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; namespace UnityEditor { internal class RectangleTool { private TimeArea m_TimeArea; private Styles m_Styles; internal enum ToolCoord { BottomLeft, Bottom, BottomRight, Left, Center, Right, TopLeft, Top, TopRight } internal class Styles { public GUIStyle rectangleToolHBarLeft = "RectangleToolHBarLeft"; public GUIStyle rectangleToolHBarRight = "RectangleToolHBarRight"; public GUIStyle rectangleToolHBar = "RectangleToolHBar"; public GUIStyle rectangleToolVBarBottom = "RectangleToolVBarBottom"; public GUIStyle rectangleToolVBarTop = "RectangleToolVBarTop"; public GUIStyle rectangleToolVBar = "RectangleToolVBar"; public GUIStyle rectangleToolSelection = "RectangleToolSelection"; public GUIStyle rectangleToolHighlight = "RectangleToolHighlight"; public GUIStyle rectangleToolScaleLeft = "RectangleToolScaleLeft"; public GUIStyle rectangleToolScaleRight = "RectangleToolScaleRight"; public GUIStyle rectangleToolScaleBottom = "RectangleToolScaleBottom"; public GUIStyle rectangleToolScaleTop = "RectangleToolScaleTop"; public GUIStyle rectangleToolRippleLeft = "RectangleToolRippleLeft"; public GUIStyle rectangleToolRippleRight = "RectangleToolRippleRight"; public GUIStyle dopesheetScaleLeft = "DopesheetScaleLeft"; public GUIStyle dopesheetScaleRight = "DopesheetScaleRight"; public GUIStyle dopesheetRippleLeft = "DopesheetRippleLeft"; public GUIStyle dopesheetRippleRight = "DopesheetRippleRight"; public GUIStyle dragLabel = "ProfilerBadge"; } public TimeArea timeArea { get { return m_TimeArea; } } public Styles styles { get { return m_Styles; } } public Rect contentRect { get { return new Rect(0, 0, m_TimeArea.drawRect.width, m_TimeArea.drawRect.height); } } public virtual void Initialize(TimeArea timeArea) { m_TimeArea = timeArea; if (m_Styles == null) m_Styles = new Styles(); } public Vector2 ToolCoordToPosition(ToolCoord coord, Bounds bounds) { switch (coord) { case ToolCoord.BottomLeft: return bounds.min; case ToolCoord.Bottom: return new Vector2(bounds.center.x, bounds.min.y); case ToolCoord.BottomRight: return new Vector2(bounds.max.x, bounds.min.y); case ToolCoord.Left: return new Vector2(bounds.min.x, bounds.center.y); case ToolCoord.Center: return bounds.center; case ToolCoord.Right: return new Vector2(bounds.max.x, bounds.center.y); case ToolCoord.TopLeft: return new Vector2(bounds.min.x, bounds.max.y); case ToolCoord.Top: return new Vector2(bounds.center.x, bounds.max.y); case ToolCoord.TopRight: return bounds.max; } return Vector2.zero; } public bool CalculateScaleTimeMatrix(float fromTime, float toTime, float offsetTime, float pivotTime, float frameRate, out Matrix4x4 transform, out bool flipKeys) { transform = Matrix4x4.identity; flipKeys = false; float thresholdTime = (Mathf.Approximately(frameRate, 0f)) ? 0.001f : 1f / frameRate; float dtNum = toTime - pivotTime; float dtDenum = fromTime - pivotTime; // Scale handle overlaps pivot, discard operation. if ((Mathf.Abs(dtNum) - Mathf.Abs(offsetTime)) < 0f) return false; dtNum = (Mathf.Sign(dtNum) == Mathf.Sign(dtDenum)) ? dtNum - offsetTime : dtNum + offsetTime; if (Mathf.Approximately(dtDenum, 0f)) { transform.SetTRS(new Vector3(dtNum, 0f, 0f), Quaternion.identity, Vector3.one); flipKeys = false; return true; } if (Mathf.Abs(dtNum) < thresholdTime) dtNum = (dtNum < 0f) ? -thresholdTime : thresholdTime; float scaleTime = dtNum / dtDenum; transform.SetTRS(new Vector3(pivotTime, 0f, 0f), Quaternion.identity, Vector3.one); transform = transform * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(scaleTime, 1f, 1f)); transform = transform * Matrix4x4.TRS(new Vector3(-pivotTime, 0f), Quaternion.identity, Vector3.one); flipKeys = scaleTime < 0f; return true; } public bool CalculateScaleValueMatrix(float fromValue, float toValue, float offsetValue, float pivotValue, out Matrix4x4 transform, out bool flipKeys) { transform = Matrix4x4.identity; flipKeys = false; float thresholdValue = 0.001f; float dvNum = toValue - pivotValue; float dvDenum = fromValue - pivotValue; // Scale handle overlaps pivot, discard operation. if ((Mathf.Abs(dvNum) - Mathf.Abs(offsetValue)) < 0f) return false; dvNum = (Mathf.Sign(dvNum) == Mathf.Sign(dvDenum)) ? dvNum - offsetValue : dvNum + offsetValue; if (Mathf.Approximately(dvDenum, 0f)) { transform.SetTRS(new Vector3(0f, dvNum, 0f), Quaternion.identity, Vector3.one); flipKeys = false; return true; } if (Mathf.Abs(dvNum) < thresholdValue) dvNum = (dvNum < 0f) ? -thresholdValue : thresholdValue; float scaleValue = dvNum / dvDenum; transform.SetTRS(new Vector3(0f, pivotValue, 0f), Quaternion.identity, Vector3.one); transform = transform * Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, scaleValue, 1f)); transform = transform * Matrix4x4.TRS(new Vector3(0f, -pivotValue, 0f), Quaternion.identity, Vector3.one); flipKeys = scaleValue < 0f; return true; } public float PixelToTime(float pixelTime, float frameRate) { float width = contentRect.width; float visibleTimeSpan = m_TimeArea.shownArea.xMax - m_TimeArea.shownArea.xMin; float minVisibleTime = m_TimeArea.shownArea.xMin; float time = ((pixelTime / width) * visibleTimeSpan + minVisibleTime); if (frameRate != 0f) time = Mathf.Round(time * frameRate) / frameRate; return time; } public float PixelToValue(float pixelValue) { float height = contentRect.height; float pixelPerValue = m_TimeArea.m_Scale.y * -1f; float zeroValuePixel = m_TimeArea.shownArea.yMin * pixelPerValue * -1f; float value = (height - pixelValue - zeroValuePixel) / pixelPerValue; return value; } public float TimeToPixel(float time) { float width = contentRect.width; float visibleTimeSpan = m_TimeArea.shownArea.xMax - m_TimeArea.shownArea.xMin; float minVisibleTime = m_TimeArea.shownArea.xMin; float pixelTime = (time - minVisibleTime) * width / visibleTimeSpan; return pixelTime; } public float ValueToPixel(float value) { float height = contentRect.height; float pixelPerValue = m_TimeArea.m_Scale.y * -1f; float zeroValuePixel = m_TimeArea.shownArea.yMin * pixelPerValue * -1f; float pixelValue = height - (value * pixelPerValue + zeroValuePixel); return pixelValue; } } } ================================================ FILE: Editor/Mono/Animation/AnimationWindow/RotationCurveInterpolation.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditorInternal; using UnityEngine; using System.Collections.Generic; namespace UnityEditor { internal class RotationCurveInterpolation { public struct State { public bool allAreNonBaked; public bool allAreBaked; public bool allAreRaw; public bool allAreRotations; } public static char[] kPostFix = { 'x', 'y', 'z', 'w' }; public enum Mode { Baked, NonBaked, RawQuaternions, RawEuler, Undefined } public static Mode GetModeFromCurveData(EditorCurveBinding data) { if (AnimationWindowUtility.IsTransformType(data.type) && data.propertyName.StartsWith("localEulerAngles")) { if (data.propertyName.StartsWith("localEulerAnglesBaked")) return Mode.Baked; else if (data.propertyName.StartsWith("localEulerAnglesRaw")) return Mode.RawEuler; else return Mode.NonBaked; } else if (AnimationWindowUtility.IsTransformType(data.type) && data.propertyName.StartsWith("m_LocalRotation")) return Mode.RawQuaternions; return Mode.Undefined; } // Extracts the interpolation state for the selection from the clip public static State GetCurveState(AnimationClip clip, EditorCurveBinding[] selection) { State state; state.allAreRaw = true; state.allAreNonBaked = true; state.allAreBaked = true; state.allAreRotations = true; foreach (EditorCurveBinding data in selection) { Mode mode = GetModeFromCurveData(data); state.allAreBaked &= (mode == Mode.Baked); state.allAreNonBaked &= (mode == Mode.NonBaked); state.allAreRaw &= mode == (Mode.RawEuler); state.allAreRotations &= mode != (Mode.Undefined); } return state; } public static char ExtractComponentCharacter(string name) { return name[name.Length - 1]; } public static string GetPrefixForInterpolation(Mode newInterpolationMode) { if (newInterpolationMode == Mode.Baked) return "localEulerAnglesBaked"; else if (newInterpolationMode == Mode.NonBaked) return "localEulerAngles"; else if (newInterpolationMode == Mode.RawEuler) return "localEulerAnglesRaw"; else if (newInterpolationMode == Mode.RawQuaternions) return "m_LocalRotation"; else return null; } internal static EditorCurveBinding[] ConvertRotationPropertiesToDefaultInterpolation(AnimationClip clip, EditorCurveBinding[] selection) { var mode = clip.legacy ? Mode.Baked : Mode.RawEuler; return ConvertRotationPropertiesToInterpolationType(selection, mode); } internal static EditorCurveBinding[] ConvertRotationPropertiesToInterpolationType(EditorCurveBinding[] selection, Mode newInterpolationMode) { if (selection.Length != 4) return selection; if (GetModeFromCurveData(selection[0]) == Mode.RawQuaternions) { EditorCurveBinding[] newCurves = new EditorCurveBinding[3]; newCurves[0] = selection[0]; newCurves[1] = selection[1]; newCurves[2] = selection[2]; string prefix = GetPrefixForInterpolation(newInterpolationMode); newCurves[0].propertyName = prefix + ".x"; newCurves[1].propertyName = prefix + ".y"; newCurves[2].propertyName = prefix + ".z"; return newCurves; } else return selection; } static EditorCurveBinding[] GenerateTransformCurveBindingArray(string path, string property, Type type, int count) { EditorCurveBinding[] bindings = new EditorCurveBinding[count]; for (int i = 0; i < count; i++) bindings[i] = EditorCurveBinding.FloatCurve(path, type, property + kPostFix[i]); return bindings; } static public EditorCurveBinding[] RemapAnimationBindingForAddKey(EditorCurveBinding binding, AnimationClip clip) { if (!AnimationWindowUtility.IsTransformType(binding.type)) { return null; } else if (binding.propertyName.StartsWith("m_LocalPosition.")) { if (binding.type == typeof(Transform)) return GenerateTransformCurveBindingArray(binding.path, "m_LocalPosition.", binding.type, 3); else return null; } else if (binding.propertyName.StartsWith("m_LocalScale.")) return GenerateTransformCurveBindingArray(binding.path, "m_LocalScale.", binding.type, 3); else if (binding.propertyName.StartsWith("m_LocalRotation")) { return SelectRotationBindingForAddKey(binding, clip); } else return null; } static public EditorCurveBinding[] RemapAnimationBindingForRotationAddKey(EditorCurveBinding binding, AnimationClip clip) { if (!AnimationWindowUtility.IsTransformType(binding.type)) { return null; } else if (binding.propertyName.StartsWith("m_LocalRotation")) { return SelectRotationBindingForAddKey(binding, clip); } else return null; } static private EditorCurveBinding[] SelectRotationBindingForAddKey(EditorCurveBinding binding, AnimationClip clip) { EditorCurveBinding testBinding = binding; testBinding.propertyName = "localEulerAnglesBaked.x"; if (AnimationUtility.GetEditorCurve(clip, testBinding) != null) return GenerateTransformCurveBindingArray(binding.path, "localEulerAnglesBaked.", binding.type, 3); else { testBinding.propertyName = "localEulerAngles.x"; if (AnimationUtility.GetEditorCurve(clip, testBinding) != null) { return GenerateTransformCurveBindingArray(binding.path, "localEulerAngles.", binding.type, 3); } else { testBinding.propertyName = "localEulerAnglesRaw.x"; if (clip.legacy && AnimationUtility.GetEditorCurve(clip, testBinding) == null) return GenerateTransformCurveBindingArray(binding.path, "localEulerAnglesBaked.", binding.type, 3); return GenerateTransformCurveBindingArray(binding.path, "localEulerAnglesRaw.", binding.type, 3); } } } static public EditorCurveBinding RemapAnimationBindingForRotationCurves(EditorCurveBinding curveBinding, AnimationClip clip) { if (!AnimationWindowUtility.IsTransformType(curveBinding.type)) return curveBinding; // Convert rotation binding to valid editor curve binding. // Only a single rotation binding (RawEuler, Baked or NonBaked) should be allowed in one clip. // RawQuaternions should be converted to appropriate euler bindings if available. Mode mode = GetModeFromCurveData(curveBinding); if (mode != Mode.Undefined) { string suffix = curveBinding.propertyName.Split('.')[1]; EditorCurveBinding newBinding = curveBinding; if (mode != Mode.NonBaked) { newBinding.propertyName = GetPrefixForInterpolation(Mode.NonBaked) + "." + suffix; AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, newBinding); if (curve != null) return newBinding; } if (mode != Mode.Baked) { newBinding.propertyName = GetPrefixForInterpolation(Mode.Baked) + "." + suffix; AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, newBinding); if (curve != null) return newBinding; } if (mode != Mode.RawEuler) { newBinding.propertyName = GetPrefixForInterpolation(Mode.RawEuler) + "." + suffix; AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, newBinding); if (curve != null) return newBinding; } return curveBinding; } else return curveBinding; } internal static void SetInterpolation(AnimationClip clip, EditorCurveBinding[] curveBindings, Mode newInterpolationMode) { Undo.RegisterCompleteObjectUndo(clip, L10n.Tr("Rotation Interpolation")); if (clip.legacy && newInterpolationMode == Mode.RawEuler) { Debug.LogWarning("Warning, Euler Angles interpolation mode is not fully supported for Legacy animation clips. If you mix clips using Euler Angles interpolation with clips using other interpolation modes (using Animation.CrossFade, Animation.Blend or other methods), you will get erroneous results. Use with caution.", clip); } List newCurvesBindings = new List(); List newCurveDatas = new List(); List oldCurvesBindings = new List(); foreach (EditorCurveBinding curveBinding in curveBindings) { Mode currentMode = GetModeFromCurveData(curveBinding); if (currentMode == Mode.Undefined) continue; if (currentMode == Mode.RawQuaternions) { Debug.LogWarning("Can't convert quaternion curve: " + curveBinding.propertyName); continue; } AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, curveBinding); if (curve == null) continue; string newPropertyPath = GetPrefixForInterpolation(newInterpolationMode) + '.' + ExtractComponentCharacter(curveBinding.propertyName); EditorCurveBinding newBinding = new EditorCurveBinding(); newBinding.propertyName = newPropertyPath; newBinding.type = curveBinding.type; newBinding.path = curveBinding.path; newCurvesBindings.Add(newBinding); newCurveDatas.Add(curve); EditorCurveBinding removeCurve = new EditorCurveBinding(); removeCurve.propertyName = curveBinding.propertyName; removeCurve.type = curveBinding.type; removeCurve.path = curveBinding.path; oldCurvesBindings.Add(removeCurve); } Undo.RegisterCompleteObjectUndo(clip, L10n.Tr("Rotation Interpolation")); foreach (EditorCurveBinding binding in oldCurvesBindings) AnimationUtility.SetEditorCurve(clip, binding, null); foreach (EditorCurveBinding binding in newCurvesBindings) AnimationUtility.SetEditorCurve(clip, binding, newCurveDatas[newCurvesBindings.IndexOf(binding)]); } } } ================================================ FILE: Editor/Mono/Animation/AnimatorController.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using System.IO; using System.Collections.Generic; using RequiredByNativeCodeAttribute = UnityEngine.Scripting.RequiredByNativeCodeAttribute; namespace UnityEditor.Animations { [NativeClass(null)] public sealed partial class AnimatorController : RuntimeAnimatorController { internal System.Action OnAnimatorControllerDirty; internal static AnimatorController lastActiveController = null; internal static int lastActiveLayerIndex = 0; private const string kControllerExtension = "controller"; internal PushUndoIfNeeded undoHandler = new PushUndoIfNeeded(true); internal bool pushUndo { set { undoHandler.pushUndo = value; } } internal string GetDefaultBlendTreeParameter() { for (int i = 0; i < parameters.Length; i++) { if (parameters[i].type == AnimatorControllerParameterType.Float) return parameters[i].name; } AddParameter("Blend", AnimatorControllerParameterType.Float); return "Blend"; } [RequiredByNativeCode] internal static void OnInvalidateAnimatorController(AnimatorController controller) { if (controller.OnAnimatorControllerDirty != null) controller.OnAnimatorControllerDirty(); } internal AnimatorStateMachine FindEffectiveRootStateMachine(int layerIndex) { AnimatorControllerLayer currentLayer = layers[layerIndex]; while (currentLayer.syncedLayerIndex != -1) { currentLayer = layers[currentLayer.syncedLayerIndex]; } return currentLayer.stateMachine; } public void AddLayer(string name) { AnimatorControllerLayer newLayer = new AnimatorControllerLayer(); newLayer.name = MakeUniqueLayerName(name); newLayer.stateMachine = new AnimatorStateMachine(); newLayer.stateMachine.name = newLayer.name; newLayer.stateMachine.hideFlags = HideFlags.HideInHierarchy; if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(newLayer.stateMachine, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(newLayer.stateMachine, "Layer added"); AddLayer(newLayer); } public void AddLayer(AnimatorControllerLayer layer) { undoHandler.DoUndo(this, "Layer added"); AnimatorControllerLayer[] layerVector = layers; ArrayUtility.Add(ref layerVector, layer); layers = layerVector; } internal void RemoveLayers(List layerIndexes) { undoHandler.DoUndo(this, "Layers removed"); AnimatorControllerLayer[] layerVector = this.layers; foreach (var layerIndex in layerIndexes) { for (var i = 0; i < layerVector.Length; ++i) { var syncedLayerIndex = layerVector[i].syncedLayerIndex; if (syncedLayerIndex > layerIndex) { // synced layer is after the layer being removed, so it's going to be shifted upon removal layerVector[i].syncedLayerIndex = syncedLayerIndex - 1; } } RemoveLayerInternal(layerIndex, ref layerVector); } this.layers = layerVector; } private void RemoveLayerInternal(int index, ref AnimatorControllerLayer[] layerVector) { if (layerVector[index].syncedLayerIndex == -1 && layerVector[index].stateMachine != null) { undoHandler.DoUndo(layerVector[index].stateMachine, "Layer removed"); layerVector[index].stateMachine.Clear(); if (MecanimUtilities.AreSameAsset(this, layerVector[index].stateMachine)) Undo.DestroyObjectImmediate(layerVector[index].stateMachine); } ArrayUtility.Remove(ref layerVector, layerVector[index]); } public void RemoveLayer(int index) { undoHandler.DoUndo(this, "Layer removed"); AnimatorControllerLayer[] layerVector = layers; RemoveLayerInternal(index, ref layerVector); layers = layerVector; } internal bool IsStateInLayer(AnimatorState state, int layerIndex) { if (layerIndex >= layers.Length) { return false; } return layers[layerIndex].stateMachine.HasState(state); } internal int GetStateLayer(AnimatorState state) { for (int i = 0; i < layers.Length; ++i) { if (layers[i].stateMachine.HasState(state)) { return i; } } return -1; } public void AddParameter(string name, AnimatorControllerParameterType type) { AnimatorControllerParameter newParameter = new AnimatorControllerParameter(); newParameter.name = MakeUniqueParameterName(name); newParameter.type = type; AddParameter(newParameter); } public void AddParameter(AnimatorControllerParameter paramater) { undoHandler.DoUndo(this, "Parameter added"); AnimatorControllerParameter[] parameterVector = parameters; ArrayUtility.Add(ref parameterVector, paramater); parameters = parameterVector; } public void RemoveParameter(int index) { undoHandler.DoUndo(this, "Parameter removed"); AnimatorControllerParameter[] parameterVector = parameters; ArrayUtility.Remove(ref parameterVector, parameterVector[index]); parameters = parameterVector; } public void RemoveParameter(AnimatorControllerParameter parameter) { undoHandler.DoUndo(this, "Parameter removed"); AnimatorControllerParameter[] parameterVector = parameters; ArrayUtility.Remove(ref parameterVector, parameter); parameters = parameterVector; } // We cannot call AddMotion from native code, because there are multiple signatures of that method, creating ambiguity. [RequiredByNativeCode] private AnimatorState AddMotionInternal(Motion motion) { return AddMotion(motion); } public AnimatorState AddMotion(Motion motion) { return AddMotion(motion, 0); } public AnimatorState AddMotion(Motion motion, int layerIndex) { AnimatorState state = layers[layerIndex].stateMachine.AddState(motion.name); state.motion = motion; return state; } public AnimatorState CreateBlendTreeInController(string name, out BlendTree tree) { return CreateBlendTreeInController(name, out tree, 0); } public AnimatorState CreateBlendTreeInController(string name, out BlendTree tree, int layerIndex) { tree = new BlendTree(); tree.name = name; tree.blendParameter = tree.blendParameterY = GetDefaultBlendTreeParameter(); if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(tree, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(tree, "Blend Tree Created"); AnimatorState state = layers[layerIndex].stateMachine.AddState(tree.name); state.motion = tree; return state; } public static AnimatorController CreateAnimatorControllerAtPath(string path) { AnimatorController controller = new AnimatorController(); controller.name = Path.GetFileName(path); AssetDatabase.CreateAsset(controller, path); controller.pushUndo = false; controller.AddLayer("Base Layer"); controller.pushUndo = true; return controller; } public static AnimationClip AllocateAnimatorClip(string name) { var clip = UnityEditorInternal.AnimationWindowUtility.AllocateAndSetupClip(true); clip.name = name; return clip; } [RequiredByNativeCode] internal static AnimatorController CreateAnimatorControllerForClip(AnimationClip clip, GameObject animatedObject) { string path = AssetDatabase.GetAssetPath(clip); if (string.IsNullOrEmpty(path)) return null; string name = System.Text.RegularExpressions.Regex.Replace(animatedObject.name, @"[\\\./]", "_"); path = Path.Combine(FileUtil.DeleteLastPathNameComponent(path), name + "." + kControllerExtension); path = AssetDatabase.GenerateUniqueAssetPath(path); if (string.IsNullOrEmpty(path)) return null; return CreateAnimatorControllerAtPathWithClip(path, clip); } public static AnimatorController CreateAnimatorControllerAtPathWithClip(string path, AnimationClip clip) { AnimatorController controller = CreateAnimatorControllerAtPath(path); AnimatorStateMachine stateMachine = controller.layers[0].stateMachine; stateMachine.pushUndo = false; var state = stateMachine.AddState(clip.name); state.motion = clip; stateMachine.pushUndo = true; return controller; } public void SetStateEffectiveMotion(AnimatorState state, Motion motion) { SetStateEffectiveMotion(state, motion, 0); } public void SetStateEffectiveMotion(AnimatorState state, Motion motion, int layerIndex) { //delete existing nested blend tree asset Motion selectedMotion = GetStateEffectiveMotion(state, layerIndex); BlendTree blendTree = selectedMotion as BlendTree; if (blendTree != null && !AssetDatabase.IsMainAsset(blendTree)) { MecanimUtilities.DestroyBlendTreeRecursive(blendTree); } if (layers[layerIndex].syncedLayerIndex == -1) { undoHandler.DoUndo(state, "Set Motion"); state.motion = motion; } else { undoHandler.DoUndo(this, "Set Motion"); AnimatorControllerLayer[] layerArray = layers; layerArray[layerIndex].SetOverrideMotion(state, motion); layers = layerArray; } } public Motion GetStateEffectiveMotion(AnimatorState state) { return GetStateEffectiveMotion(state, 0); } public Motion GetStateEffectiveMotion(AnimatorState state, int layerIndex) { if (layerIndex < 0 || layerIndex > layers.Length) return null; return layers[layerIndex].syncedLayerIndex == -1 ? state.motion : layers[layerIndex].GetOverrideMotion(state); } public void SetStateEffectiveBehaviours(AnimatorState state, int layerIndex, StateMachineBehaviour[] behaviours) { if (layers[layerIndex].syncedLayerIndex == -1) { undoHandler.DoUndo(state, "Set Behaviours"); state.behaviours = behaviours; } else { undoHandler.DoUndo(this, "Set Behaviours"); Internal_SetEffectiveBehaviours(state, layerIndex, behaviours); } } public StateMachineBehaviour[] GetStateEffectiveBehaviours(AnimatorState state, int layerIndex) { if (layerIndex < 0 || layerIndex > layers.Length) return Array.Empty(); return Internal_GetEffectiveBehaviours(state, layerIndex) as StateMachineBehaviour[]; } [System.Obsolete("parameterCount is obsolete. Use parameters.Length instead.", true)] int parameterCount { get { return 0; } } [System.Obsolete("layerCount is obsolete. Use layers.Length instead.", true)] int layerCount { get { return 0; } } } } ================================================ FILE: Editor/Mono/Animation/BlendTree.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; using UnityEditor; using UnityEditor.Animations; using System.Linq; namespace UnityEditor.Animations { [ExcludeFromPreset] public partial class BlendTree : Motion { public void AddChild(Motion motion) { AddChild(motion, Vector2.zero, 0); } public void AddChild(Motion motion, Vector2 position) { AddChild(motion, position, 0); } public void AddChild(Motion motion, float threshold) { AddChild(motion, Vector2.zero, threshold); } public void RemoveChild(int index) { Undo.RecordObject(this, "Remove Child"); ChildMotion[] childMotions = children; ArrayUtility.RemoveAt(ref childMotions, index); children = childMotions; } internal void AddChild(Motion motion, Vector2 position, float threshold) { Undo.RecordObject(this, "Added BlendTree Child"); ChildMotion[] childMotions = children; ChildMotion newMotion = new ChildMotion(); newMotion.timeScale = 1; newMotion.motion = motion; newMotion.position = position; newMotion.threshold = threshold; newMotion.directBlendParameter = "Blend"; ArrayUtility.Add(ref childMotions, newMotion); children = childMotions; } public BlendTree CreateBlendTreeChild(float threshold) { return CreateBlendTreeChild(Vector2.zero, threshold); } public BlendTree CreateBlendTreeChild(Vector2 position) { return CreateBlendTreeChild(position, 0); } internal bool HasChild(BlendTree childTree, bool recursive) { foreach (ChildMotion child in children) { if (child.motion == childTree) { return true; } if (recursive && child.motion is BlendTree && (child.motion as BlendTree).HasChild(childTree, true)) { return true; } } return false; } internal BlendTree CreateBlendTreeChild(Vector2 position, float threshold) { BlendTree tree = new BlendTree(); tree.name = "BlendTree"; tree.hideFlags = HideFlags.HideInHierarchy; if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(tree, AssetDatabase.GetAssetPath(this)); Undo.RegisterCreatedObjectUndo(tree, "Blend Tree Created"); Undo.RecordObject(this, "Created BlendTree Child"); AddChild(tree, position, threshold); return tree; } } } ================================================ FILE: Editor/Mono/Animation/EditorCurveBinding.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.Playables; using UnityEngine.Scripting.APIUpdating; using UnityEngine.Internal; using UnityEngine; using static UnityEditor.AnimationUtility; namespace UnityEditor { [NativeType(CodegenOptions.Custom, "MonoEditorCurveBinding")] public struct EditorCurveBinding : IEquatable { // The path of the game object / bone being animated. public string path; // The type of the component / material being animated. private Type m_type; // The name of the property being animated. public string propertyName; // is it a PPtr curve private int m_isPPtrCurve; //is it a discrete curve private int m_isDiscreteCurve; //is it a serialize reference curve private int m_isSerializeReferenceCurve; //is it placeholder curve private int m_isPhantom; //is it a unknown curve: mean it needs to be processed further more to find out what is this curve // This is necessary to support old user code using FloatCurve for non float type private int m_isUnknownCurve; // This is only used internally for deleting curves internal int m_ClassID; internal int m_ScriptInstanceID; public bool isPPtrCurve { get { return m_isPPtrCurve != 0; } } public bool isDiscreteCurve { get { return m_isDiscreteCurve != 0; } } public bool isSerializeReferenceCurve { get { return m_isSerializeReferenceCurve != 0; } } internal bool isPhantom { get { return m_isPhantom != 0; } set { m_isPhantom = value == true ? 1 : 0; } } public static bool operator==(EditorCurveBinding lhs, EditorCurveBinding rhs) { // Only if classID actually has been setup do we compare it (It might only be type) if (lhs.m_ClassID != 0 && rhs.m_ClassID != 0) { if (lhs.m_ClassID != rhs.m_ClassID || lhs.m_ScriptInstanceID != rhs.m_ScriptInstanceID) return false; } return lhs.m_isPPtrCurve == rhs.m_isPPtrCurve && lhs.m_isDiscreteCurve == rhs.m_isDiscreteCurve && lhs.m_isSerializeReferenceCurve == rhs.m_isSerializeReferenceCurve && lhs.path == rhs.path && lhs.type == rhs.type && lhs.propertyName == rhs.propertyName; } public static bool operator!=(EditorCurveBinding lhs, EditorCurveBinding rhs) { return !(lhs == rhs); } public override int GetHashCode() { return String.Format("{0}:{1}:{2}", path, type.Name, propertyName).GetHashCode(); } public override bool Equals(object other) { return other is EditorCurveBinding && Equals((EditorCurveBinding)other); } public bool Equals(EditorCurveBinding other) { return this == other; } public Type type { get { return m_type; } set { m_type = value; m_ClassID = 0; m_ScriptInstanceID = 0; } } static private void BaseCurve(string inPath, System.Type inType, string inPropertyName, out EditorCurveBinding binding) { binding = new EditorCurveBinding(); binding.path = inPath; binding.type = inType; binding.propertyName = inPropertyName; binding.m_isPhantom = 0; } static public EditorCurveBinding FloatCurve(string inPath, System.Type inType, string inPropertyName) { EditorCurveBinding binding; BaseCurve(inPath, inType, inPropertyName, out binding); binding.m_isPPtrCurve = 0; binding.m_isDiscreteCurve = 0; binding.m_isSerializeReferenceCurve = 0; binding.m_isUnknownCurve = 1; return binding; } static public EditorCurveBinding PPtrCurve(string inPath, System.Type inType, string inPropertyName) { EditorCurveBinding binding; BaseCurve(inPath, inType, inPropertyName, out binding); binding.m_isPPtrCurve = 1; binding.m_isDiscreteCurve = 1; binding.m_isSerializeReferenceCurve = 0; binding.m_isUnknownCurve = 0; return binding; } static public EditorCurveBinding DiscreteCurve(string inPath, System.Type inType, string inPropertyName) { EditorCurveBinding binding; BaseCurve(inPath, inType, inPropertyName, out binding); binding.m_isPPtrCurve = 0; binding.m_isDiscreteCurve = 1; binding.m_isSerializeReferenceCurve = 0; binding.m_isUnknownCurve = 0; DiscreteBindingResult result = AnimationUtility.IsDiscreteIntBinding(binding); if (result == DiscreteBindingResult.IncompatibleFieldType || result == DiscreteBindingResult.MissingDiscreteAttribute) { Debug.LogWarning( $"Property [" + inPropertyName + "] is not a supported discrete curve binding. " + "Discrete curves only support [" + typeof(Enum) + "] and [" + typeof(int) + " with the `DiscreteEvaluation` attribute]."); binding.m_isDiscreteCurve = 0; binding.m_isUnknownCurve = 1; } return binding; } static public EditorCurveBinding SerializeReferenceCurve(string inPath, System.Type inType, long refID, string inPropertyName, bool isPPtr, bool isDiscrete) { EditorCurveBinding binding; BaseCurve(inPath, inType, $"managedReferences[{refID}].{inPropertyName}", out binding); binding.m_isPPtrCurve = isPPtr ? 1 : 0; binding.m_isDiscreteCurve = isDiscrete || isPPtr ? 1 : 0; binding.m_isSerializeReferenceCurve = 1; binding.m_isUnknownCurve = 0; return binding; } } } ================================================ FILE: Editor/Mono/Animation/GameObjectRecorder.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using UnityEditor; using Object = UnityEngine.Object; using uei = UnityEngine.Internal; namespace UnityEditor.Animations { public struct CurveFilterOptions { public float positionError; public float rotationError; public float scaleError; public float floatError; public bool keyframeReduction; public bool unrollRotation; } [NativeHeader("Editor/Src/Animation/EditorCurveBinding.bindings.h")] [NativeHeader("Editor/Src/Animation/GameObjectRecorder.h")] [NativeHeader("Modules/Animation/AnimationClip.h")] [NativeType] public class GameObjectRecorder : Object { readonly static CurveFilterOptions k_DefaultCurveFilterOptions = new CurveFilterOptions() { unrollRotation = true, keyframeReduction = true, positionError = 0.5f, rotationError = 0.5f, scaleError = 0.5f, floatError = 0.5f }; public GameObjectRecorder(GameObject root) { Internal_Create(this, root); } public void BindComponentsOfType(GameObject target, bool recursive) where T : Component { BindComponentsOfType(target, typeof(T), recursive); } public void BindComponentsOfType(GameObject target, Type componentType, bool recursive) { Component[] components; if (recursive) components = target.GetComponentsInChildren(componentType, true); else components = target.GetComponents(componentType); for (int i = 0; i < components.Length; ++i) BindComponent(components[i]); } extern private static void Internal_Create([Writable] GameObjectRecorder self, [NotNull] GameObject root); // Root. extern public GameObject root { get; } // Bindings. public void Bind(EditorCurveBinding binding) { if (!binding.type.IsSubclassOf(typeof(UnityEngine.Object))) throw new InvalidCastException("Binding type should derive from Unity type."); BindInternal(binding); } [NativeMethod("Bind")] extern private void BindInternal(EditorCurveBinding binding); extern public void BindAll(GameObject target, bool recursive); extern public void BindComponent([NotNull] Component component); extern public EditorCurveBinding[] GetBindings(); // Recording. extern public void TakeSnapshot(float dt); extern public float currentTime { get; } extern public bool isRecording { get; } public void SaveToClip(AnimationClip clip) { SaveToClip(clip, 60.0f); } public void SaveToClip(AnimationClip clip, float fps) { if (fps <= Mathf.Epsilon) throw new ArgumentException("FPS can't be 0.0 or less"); if (!isRecording) throw new InvalidOperationException("Cannot save to clip as there is nothing to save. The method TakeSnapshot() has not been called."); SaveToClipInternal(clip, fps, k_DefaultCurveFilterOptions); AnimationUtility.onCurveWasModified?.Invoke(clip, new EditorCurveBinding(), AnimationUtility.CurveModifiedType.ClipModified); } public void SaveToClip(AnimationClip clip, float fps, CurveFilterOptions filterOptions) { if (fps <= Mathf.Epsilon) throw new ArgumentException("FPS can't be 0.0 or less"); if (filterOptions.keyframeReduction) { if (filterOptions.positionError < 0 || filterOptions.rotationError < 0 || filterOptions.scaleError < 0 || filterOptions.floatError < 0) throw new ArgumentException("Allowed errors for keyframe reduction cannot be negative."); } if (!isRecording) throw new InvalidOperationException("Cannot save to clip as there is nothing to save. The method TakeSnapshot() has not been called."); SaveToClipInternal(clip, fps, filterOptions); AnimationUtility.onCurveWasModified?.Invoke(clip, new EditorCurveBinding(), AnimationUtility.CurveModifiedType.ClipModified); } [NativeMethod("SaveToClip")] extern void SaveToClipInternal(AnimationClip clip, float fps, CurveFilterOptions filterOptions); extern public void ResetRecording(); // Obsolete [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("The GameObjectRecorder constructor now takes a root GameObject", true)] public GameObjectRecorder() {} [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("BindComponent() using a System::Type is obsolete, use BindComponentsOfType() instead (UnityUpgradable) -> BindComponentsOfType(*)", true)] public void BindComponent(GameObject target, Type componentType, bool recursive) {} [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("\"BindComponent() where T : Component\" is obsolete, use BindComponentsOfType() instead (UnityUpgradable) -> BindComponentsOfType(*)", true)] public void BindComponent(GameObject target, bool recursive) where T : Component {} } } ================================================ FILE: Editor/Mono/Animation/MaterialAnimationUtility.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using ShaderPropertyType = UnityEngine.Rendering.ShaderPropertyType; using Object = UnityEngine.Object; namespace UnityEditorInternal { static internal class MaterialAnimationUtility { const string kMaterialPrefix = "material."; static PropertyModification[] CreatePropertyModifications(int count, Object target) { PropertyModification[] modifications = new PropertyModification[count]; for (int i = 0; i < modifications.Length; i++) { modifications[i] = new PropertyModification(); modifications[i].target = target; } return modifications; } static void SetupPropertyModification(string name, float value, PropertyModification prop) { prop.propertyPath = kMaterialPrefix + name; prop.value = value.ToString(); } static PropertyModification[] MaterialPropertyToPropertyModifications(MaterialProperty materialProp, Object target, float value) { PropertyModification[] modifications = CreatePropertyModifications(1, target); SetupPropertyModification(materialProp.name, value, modifications[0]); return modifications; } static PropertyModification[] MaterialPropertyToPropertyModifications(MaterialProperty materialProp, Object target, Color color) { PropertyModification[] modifications = CreatePropertyModifications(4, target); SetupPropertyModification(materialProp.name + ".r", color.r, modifications[0]); SetupPropertyModification(materialProp.name + ".g", color.g, modifications[1]); SetupPropertyModification(materialProp.name + ".b", color.b, modifications[2]); SetupPropertyModification(materialProp.name + ".a", color.a, modifications[3]); return modifications; } static PropertyModification[] MaterialPropertyToPropertyModifications(MaterialProperty materialProp, Object target, Vector4 vec) { return MaterialPropertyToPropertyModifications(materialProp.name, target, vec); } static PropertyModification[] MaterialPropertyToPropertyModifications(string name, Object target, Vector4 vec) { PropertyModification[] modifications = CreatePropertyModifications(4, target); SetupPropertyModification(name + ".x", vec.x, modifications[0]); SetupPropertyModification(name + ".y", vec.y, modifications[1]); SetupPropertyModification(name + ".z", vec.z, modifications[2]); SetupPropertyModification(name + ".w", vec.w, modifications[3]); return modifications; } static bool ApplyMaterialModificationToAnimationRecording(PropertyModification[] previousModifications, PropertyModification[] currentModifications) { UndoPropertyModification[] undoModifications = new UndoPropertyModification[previousModifications.Length]; for (int i = 0; i < undoModifications.Length; ++i) { undoModifications[i].previousValue = previousModifications[i]; undoModifications[i].currentValue = currentModifications[i]; } UndoPropertyModification[] ret = Undo.InvokePostprocessModifications(undoModifications); return ret.Length != undoModifications.Length; } static public bool OverridePropertyColor(MaterialProperty materialProp, Renderer target, out Color color) { var propertyPaths = new List(); string basePropertyPath = kMaterialPrefix + materialProp.name; if (materialProp.propertyType == ShaderPropertyType.Texture) { propertyPaths.Add(basePropertyPath + "_ST.x"); propertyPaths.Add(basePropertyPath + "_ST.y"); propertyPaths.Add(basePropertyPath + "_ST.z"); propertyPaths.Add(basePropertyPath + "_ST.w"); } else if (materialProp.propertyType == ShaderPropertyType.Color) { propertyPaths.Add(basePropertyPath + ".r"); propertyPaths.Add(basePropertyPath + ".g"); propertyPaths.Add(basePropertyPath + ".b"); propertyPaths.Add(basePropertyPath + ".a"); } else if (materialProp.propertyType == ShaderPropertyType.Vector) { propertyPaths.Add(basePropertyPath + ".x"); propertyPaths.Add(basePropertyPath + ".y"); propertyPaths.Add(basePropertyPath + ".z"); propertyPaths.Add(basePropertyPath + ".w"); } else { propertyPaths.Add(basePropertyPath); } if (propertyPaths.Exists(path => AnimationMode.IsPropertyAnimated(target, path))) { color = AnimationMode.animatedPropertyColor; if (AnimationMode.InAnimationRecording()) color = AnimationMode.recordedPropertyColor; else if (propertyPaths.Exists(path => AnimationMode.IsPropertyCandidate(target, path))) color = AnimationMode.candidatePropertyColor; return true; } color = Color.white; return false; } static public void SetupMaterialPropertyBlock(MaterialProperty materialProp, int changedMask, Renderer target) { MaterialPropertyBlock block = new MaterialPropertyBlock(); target.GetPropertyBlock(block); materialProp.WriteToMaterialPropertyBlock(block, changedMask); target.SetPropertyBlock(block); } static public void TearDownMaterialPropertyBlock(Renderer target) { target.SetPropertyBlock(null); } static public bool ApplyMaterialModificationToAnimationRecording(MaterialProperty materialProp, int changedMask, Renderer target, object oldValue) { bool applied = false; switch (materialProp.propertyType) { case ShaderPropertyType.Color: SetupMaterialPropertyBlock(materialProp, changedMask, target); applied = ApplyMaterialModificationToAnimationRecording(MaterialPropertyToPropertyModifications(materialProp, target, (Color)oldValue), MaterialPropertyToPropertyModifications(materialProp, target, materialProp.colorValue)); if (!applied) TearDownMaterialPropertyBlock(target); return applied; case ShaderPropertyType.Vector: SetupMaterialPropertyBlock(materialProp, changedMask, target); applied = ApplyMaterialModificationToAnimationRecording(MaterialPropertyToPropertyModifications(materialProp, target, (Vector4)oldValue), MaterialPropertyToPropertyModifications(materialProp, target, materialProp.vectorValue)); if (!applied) TearDownMaterialPropertyBlock(target); return applied; case ShaderPropertyType.Float: case ShaderPropertyType.Range: SetupMaterialPropertyBlock(materialProp, changedMask, target); applied = ApplyMaterialModificationToAnimationRecording(MaterialPropertyToPropertyModifications(materialProp, target, (float)oldValue), MaterialPropertyToPropertyModifications(materialProp, target, materialProp.floatValue)); if (!applied) TearDownMaterialPropertyBlock(target); return applied; case ShaderPropertyType.Texture: { if (MaterialProperty.IsTextureOffsetAndScaleChangedMask(changedMask)) { string name = materialProp.name + "_ST"; SetupMaterialPropertyBlock(materialProp, changedMask, target); applied = ApplyMaterialModificationToAnimationRecording(MaterialPropertyToPropertyModifications(name, target, (Vector4)oldValue), MaterialPropertyToPropertyModifications(name, target, materialProp.textureScaleAndOffset)); if (!applied) TearDownMaterialPropertyBlock(target); return applied; } else return false; } } return false; } static public PropertyModification[] MaterialPropertyToPropertyModifications(MaterialProperty materialProp, Renderer target) { switch (materialProp.propertyType) { case ShaderPropertyType.Color: return MaterialPropertyToPropertyModifications(materialProp, target, materialProp.colorValue); case ShaderPropertyType.Vector: return MaterialPropertyToPropertyModifications(materialProp, target, materialProp.vectorValue); case ShaderPropertyType.Float: case ShaderPropertyType.Range: return MaterialPropertyToPropertyModifications(materialProp, target, materialProp.floatValue); case ShaderPropertyType.Texture: { string name = materialProp.name + "_ST"; return MaterialPropertyToPropertyModifications(name, target, materialProp.vectorValue); } } return null; } } } ================================================ FILE: Editor/Mono/Animation/MecanimUtilities.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections; using UnityEditor; using System.Collections.Generic; using System.Linq; namespace UnityEditor.Animations { internal class MecanimUtilities { public static bool StateMachineRelativePath(AnimatorStateMachine parent, AnimatorStateMachine toFind, ref List hierarchy) { hierarchy.Add(parent); if (parent == toFind) return true; var childStateMachines = AnimatorStateMachine.StateMachineCache.GetChildStateMachines(parent); for (int i = 0; i < childStateMachines.Length; i++) { if (StateMachineRelativePath(childStateMachines[i].stateMachine, toFind, ref hierarchy)) return true; } hierarchy.Remove(parent); return false; } internal static bool AreSameAsset(Object obj1, Object obj2) { return AssetDatabase.GetAssetPath(obj1) == AssetDatabase.GetAssetPath(obj2); } internal static void DestroyBlendTreeRecursive(BlendTree blendTree) { for (int i = 0; i < blendTree.children.Length; i++) { BlendTree childBlendTree = blendTree.children[i].motion as BlendTree; if (childBlendTree != null && AreSameAsset(blendTree, childBlendTree)) DestroyBlendTreeRecursive(childBlendTree); } Undo.DestroyObjectImmediate(blendTree); } } } ================================================ FILE: Editor/Mono/Animation/SerializedStringTable.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections; [System.Serializable] internal class SerializedStringTable { [SerializeField] private string[] keys; [SerializeField] private int[] values; private Hashtable table; public Hashtable hashtable { get { SanityCheck(); return table; } } public int Length { get { SanityCheck(); return keys.Length; } } private void SanityCheck() { if (keys == null) { keys = new string[0]; values = new int[0]; } if (table == null) { table = new Hashtable(); for (int i = 0; i < keys.Length; i++) table[keys[i]] = values[i]; } } private void SynchArrays() { keys = new string[table.Count]; values = new int[table.Count]; table.Keys.CopyTo(keys, 0); table.Values.CopyTo(values, 0); } public void Set(string key, int value) { SanityCheck(); table[key] = value; SynchArrays(); } public void Set(string key) { Set(key, 0); } public bool Contains(string key) { SanityCheck(); return table.Contains(key); } public int Get(string key) { SanityCheck(); if (!table.Contains(key)) return -1; return (int)table[key]; } public void Remove(string key) { SanityCheck(); if (table.Contains(key)) table.Remove(key); SynchArrays(); } } ================================================ FILE: Editor/Mono/Animation/StateMachine.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; using UnityEditor; using System.Linq; namespace UnityEditor.Animations { internal struct PushUndoIfNeeded { public bool pushUndo { get { return impl.m_PushUndo; } set { impl.m_PushUndo = value; } } public PushUndoIfNeeded(bool pushUndo) { m_Impl = new PushUndoIfNeededImpl(pushUndo); } public void DoUndo(Object target, string undoOperation) { impl.DoUndo(target, undoOperation); } public void DoUndoCreated(Object target, string undoOperation) { impl.DoUndoCreated(target, undoOperation); } PushUndoIfNeededImpl impl { get { if (m_Impl == null) m_Impl = new PushUndoIfNeededImpl(true); return m_Impl; } } PushUndoIfNeededImpl m_Impl; private class PushUndoIfNeededImpl { public PushUndoIfNeededImpl(bool pushUndo) { m_PushUndo = pushUndo; } public void DoUndo(Object target, string undoOperation) { if (m_PushUndo) { Undo.RegisterCompleteObjectUndo(target, undoOperation); } } public void DoUndoCreated(Object target, string undoOperation) { if (m_PushUndo) { Undo.RegisterCreatedObjectUndo(target, undoOperation); } } public bool m_PushUndo; } } public partial class AnimatorTransitionBase : Object { private PushUndoIfNeeded undoHandler = new PushUndoIfNeeded(true); internal bool pushUndo { set { undoHandler.pushUndo = value; } } public void AddCondition(AnimatorConditionMode mode, float threshold, string parameter) { undoHandler.DoUndo(this, "Condition added"); AnimatorCondition[] conditionVector = conditions; AnimatorCondition newCondition = new AnimatorCondition(); newCondition.mode = mode; newCondition.parameter = parameter; newCondition.threshold = threshold; ArrayUtility.Add(ref conditionVector, newCondition); conditions = conditionVector; } public void RemoveCondition(AnimatorCondition condition) { undoHandler.DoUndo(this, "Condition removed"); AnimatorCondition[] conditionVector = conditions; ArrayUtility.Remove(ref conditionVector, condition); conditions = conditionVector; } } [HelpURL("StateMachineTransitions")] internal class AnimatorDefaultTransition : ScriptableObject { } [HelpURL("class-State")] public partial class AnimatorState : Object { private PushUndoIfNeeded undoHandler = new PushUndoIfNeeded(true); internal bool pushUndo { set { undoHandler.pushUndo = value; } } public void AddTransition(AnimatorStateTransition transition) { undoHandler.DoUndo(this, "Transition added"); AnimatorStateTransition[] transitionsVector = transitions; ArrayUtility.Add(ref transitionsVector, transition); transitions = transitionsVector; } public void RemoveTransition(AnimatorStateTransition transition) { undoHandler.DoUndo(this, "Transition removed"); AnimatorStateTransition[] transitionsVector = transitions; ArrayUtility.Remove(ref transitionsVector, transition); transitions = transitionsVector; if (MecanimUtilities.AreSameAsset(this, transition)) Undo.DestroyObjectImmediate(transition); } private AnimatorStateTransition CreateTransition(bool setDefaultExitTime) { AnimatorStateTransition newTransition = new AnimatorStateTransition(); newTransition.hasExitTime = false; newTransition.hasFixedDuration = true; if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(newTransition, AssetDatabase.GetAssetPath(this)); newTransition.hideFlags = HideFlags.HideInHierarchy; undoHandler.DoUndoCreated(newTransition, "Transition Created"); if (setDefaultExitTime) SetDefaultTransitionExitTime(ref newTransition); return newTransition; } private void SetDefaultTransitionExitTime(ref AnimatorStateTransition newTransition) { newTransition.hasExitTime = true; if (motion != null && motion.averageDuration > 0.0f) { const float transitionDefaultDuration = 0.25f; float transitionDurationNormalized = transitionDefaultDuration / motion.averageDuration; newTransition.duration = transitionDefaultDuration; newTransition.exitTime = 1.0f - transitionDurationNormalized; } else { newTransition.duration = 0.25f; newTransition.exitTime = 0.75f; } } public AnimatorStateTransition AddTransition(AnimatorState destinationState) { AnimatorStateTransition newTransition = CreateTransition(false); newTransition.destinationState = destinationState; AddTransition(newTransition); return newTransition; } public AnimatorStateTransition AddTransition(AnimatorStateMachine destinationStateMachine) { AnimatorStateTransition newTransition = CreateTransition(false); newTransition.destinationStateMachine = destinationStateMachine; AddTransition(newTransition); return newTransition; } public AnimatorStateTransition AddTransition(AnimatorState destinationState, bool defaultExitTime) { AnimatorStateTransition newTransition = CreateTransition(defaultExitTime); newTransition.destinationState = destinationState; AddTransition(newTransition); return newTransition; } public AnimatorStateTransition AddTransition(AnimatorStateMachine destinationStateMachine, bool defaultExitTime) { AnimatorStateTransition newTransition = CreateTransition(defaultExitTime); newTransition.destinationStateMachine = destinationStateMachine; AddTransition(newTransition); return newTransition; } public AnimatorStateTransition AddExitTransition() { return AddExitTransition(false); } public AnimatorStateTransition AddExitTransition(bool defaultExitTime) { AnimatorStateTransition newTransition = CreateTransition(defaultExitTime); newTransition.isExit = true; AddTransition(newTransition); return newTransition; } internal AnimatorStateMachine FindParent(AnimatorStateMachine root) { if (root.HasState(this, false)) return root; else return root.stateMachinesRecursive.Find(sm => sm.stateMachine.HasState(this, false)).stateMachine; } internal AnimatorStateTransition FindTransition(AnimatorState destinationState) // pp todo return a list? { return (new List(transitions)).Find(t => t.destinationState == destinationState); } [System.Obsolete("uniqueName does not exist anymore. Consider using .name instead.", true)] public string uniqueName { get { return ""; } } [System.Obsolete("GetMotion() is obsolete. Use motion", true)] public Motion GetMotion() { return null; } [System.Obsolete("uniqueNameHash does not exist anymore.", true)] public int uniqueNameHash { get { return -1; } } } [HelpURL("NestedStateMachines")] public partial class AnimatorStateMachine : Object { private PushUndoIfNeeded undoHandler = new PushUndoIfNeeded(true); internal bool pushUndo { set { undoHandler.pushUndo = value; } } internal class StateMachineCache { static Dictionary m_ChildStateMachines; static bool m_Initialized; static void Init() { if (!m_Initialized) { m_ChildStateMachines = new Dictionary(); m_Initialized = true; } } static public void Clear() { Init(); m_ChildStateMachines.Clear(); } static public ChildAnimatorStateMachine[] GetChildStateMachines(AnimatorStateMachine parent) { Init(); ChildAnimatorStateMachine[] children; if (m_ChildStateMachines.TryGetValue(parent, out children) == false) { children = parent.stateMachines; m_ChildStateMachines.Add(parent, children); } return children; } } internal List statesRecursive { get { List ret = new List(); ret.AddRange(states); for (int j = 0; j < stateMachines.Length; j++) { ret.AddRange(stateMachines[j].stateMachine.statesRecursive); } return ret; } } internal List stateMachinesRecursive { get { List ret = new List(); var childStateMachines = AnimatorStateMachine.StateMachineCache.GetChildStateMachines(this); ret.AddRange(childStateMachines); for (int j = 0; j < childStateMachines.Length; j++) { ret.AddRange(childStateMachines[j].stateMachine.stateMachinesRecursive); } return ret; } } internal List anyStateTransitionsRecursive { get { List childTransitions = new List(); childTransitions.AddRange(anyStateTransitions); foreach (ChildAnimatorStateMachine stateMachine in stateMachines) { childTransitions.AddRange(stateMachine.stateMachine.anyStateTransitionsRecursive); } return childTransitions; } } internal Vector3 GetStatePosition(AnimatorState state) { ChildAnimatorState[] animatorStates = states; for (int i = 0; i < animatorStates.Length; i++) if (state == animatorStates[i].state) return animatorStates[i].position; System.Diagnostics.Debug.Fail("Can't find state (" + state.name + ") in parent state machine (" + name + ")."); return Vector3.zero; } internal void SetStatePosition(AnimatorState state, Vector3 position) { ChildAnimatorState[] childStates = states; for (int i = 0; i < childStates.Length; i++) if (state == childStates[i].state) { childStates[i].position = position; states = childStates; return; } System.Diagnostics.Debug.Fail("Can't find state (" + state.name + ") in parent state machine (" + name + ")."); } internal Vector3 GetStateMachinePosition(AnimatorStateMachine stateMachine) { ChildAnimatorStateMachine[] childSM = stateMachines; for (int i = 0; i < childSM.Length; i++) if (stateMachine == childSM[i].stateMachine) return childSM[i].position; System.Diagnostics.Debug.Fail("Can't find state machine (" + stateMachine.name + ") in parent state machine (" + name + ")."); return Vector3.zero; } internal void SetStateMachinePosition(AnimatorStateMachine stateMachine, Vector3 position) { ChildAnimatorStateMachine[] childSM = stateMachines; for (int i = 0; i < childSM.Length; i++) if (stateMachine == childSM[i].stateMachine) { childSM[i].position = position; stateMachines = childSM; return; } System.Diagnostics.Debug.Fail("Can't find state machine (" + stateMachine.name + ") in parent state machine (" + name + ")."); } public AnimatorState AddState(string name) { return AddState(name, states.Length > 0 ? states[states.Length - 1].position + new Vector3(35, 65) : new Vector3(200, 0, 0)); } public AnimatorState AddState(string name, Vector3 position) { AnimatorState state = new AnimatorState(); state.hideFlags = HideFlags.HideInHierarchy; state.name = MakeUniqueStateName(name); if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(state, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(state, "State Created"); AddState(state, position); return state; } public void AddState(AnimatorState state, Vector3 position) { ChildAnimatorState[] childStates = states; if (System.Array.Exists(childStates, childState => childState.state == state)) { Debug.LogWarning(System.String.Format("State '{0}' already exists in state machine '{1}', discarding new state.", state.name, name)); return; } undoHandler.DoUndo(this, "State added"); ChildAnimatorState newState = new ChildAnimatorState(); newState.state = state; newState.position = position; ArrayUtility.Add(ref childStates, newState); states = childStates; } public void RemoveState(AnimatorState state) { undoHandler.DoUndo(this, "State removed"); undoHandler.DoUndo(state, "State removed"); RemoveStateInternal(state); } public AnimatorStateMachine AddStateMachine(string name) { return AddStateMachine(name, Vector3.zero); } public AnimatorStateMachine AddStateMachine(string name, Vector3 position) { AnimatorStateMachine stateMachine = new AnimatorStateMachine(); stateMachine.hideFlags = HideFlags.HideInHierarchy; stateMachine.name = MakeUniqueStateMachineName(name); AddStateMachine(stateMachine, position); if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(stateMachine, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(stateMachine, "StateMachine Created"); return stateMachine; } public void AddStateMachine(AnimatorStateMachine stateMachine, Vector3 position) { ChildAnimatorStateMachine[] childStateMachines = stateMachines; if (System.Array.Exists(childStateMachines, childStateMachine => childStateMachine.stateMachine == stateMachine)) { Debug.LogWarning(System.String.Format("Sub state machine '{0}' already exists in state machine '{1}', discarding new state machine.", stateMachine.name, name)); return; } undoHandler.DoUndo(this, "StateMachine " + stateMachine.name + " added"); ChildAnimatorStateMachine newStateMachine = new ChildAnimatorStateMachine(); newStateMachine.stateMachine = stateMachine; newStateMachine.position = position; ArrayUtility.Add(ref childStateMachines, newStateMachine); stateMachines = childStateMachines; } public void RemoveStateMachine(AnimatorStateMachine stateMachine) { undoHandler.DoUndo(this, "StateMachine removed"); undoHandler.DoUndo(stateMachine, "StateMachine removed"); RemoveStateMachineInternal(stateMachine); } private AnimatorStateTransition AddAnyStateTransition() { AnimatorStateTransition newTransition = new AnimatorStateTransition(); newTransition.hasExitTime = false; newTransition.hasFixedDuration = true; newTransition.duration = 0.25f; newTransition.exitTime = 0.75f; newTransition.hideFlags = HideFlags.HideInHierarchy; if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(newTransition, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(newTransition, "AnyState Transition Created"); undoHandler.DoUndo(this, "AnyState Transition Added"); AnimatorStateTransition[] transitionsVector = anyStateTransitions; ArrayUtility.Add(ref transitionsVector, newTransition); anyStateTransitions = transitionsVector; return newTransition; } public AnimatorStateTransition AddAnyStateTransition(AnimatorState destinationState) { AnimatorStateTransition newTransition = AddAnyStateTransition(); newTransition.destinationState = destinationState; return newTransition; } public AnimatorStateTransition AddAnyStateTransition(AnimatorStateMachine destinationStateMachine) { AnimatorStateTransition newTransition = AddAnyStateTransition(); newTransition.destinationStateMachine = destinationStateMachine; return newTransition; } public bool RemoveAnyStateTransition(AnimatorStateTransition transition) { if ((new List(anyStateTransitions)).Any(t => t == transition)) { undoHandler.DoUndo(this, "AnyState Transition Removed"); AnimatorStateTransition[] transitionsVector = anyStateTransitions; ArrayUtility.Remove(ref transitionsVector, transition); anyStateTransitions = transitionsVector; if (MecanimUtilities.AreSameAsset(this, transition)) Undo.DestroyObjectImmediate(transition); return true; } return false; } internal void RemoveAnyStateTransitionRecursive(AnimatorStateTransition transition) { if (RemoveAnyStateTransition(transition)) return; List childStateMachines = stateMachinesRecursive; foreach (ChildAnimatorStateMachine sm in childStateMachines) { if (sm.stateMachine.RemoveAnyStateTransition(transition)) return; } } public AnimatorTransition AddStateMachineTransition(AnimatorStateMachine sourceStateMachine) { AnimatorTransition newTransition = new AnimatorTransition(); AddStateMachineTransition(sourceStateMachine, newTransition); return newTransition; } public AnimatorTransition AddStateMachineTransition(AnimatorStateMachine sourceStateMachine, AnimatorStateMachine destinationStateMachine) { var newTransition = new AnimatorTransition(); newTransition.destinationStateMachine = destinationStateMachine; AddStateMachineTransition(sourceStateMachine, newTransition); return newTransition; } public AnimatorTransition AddStateMachineTransition(AnimatorStateMachine sourceStateMachine, AnimatorState destinationState) { var newTransition = new AnimatorTransition(); newTransition.destinationState = destinationState; AddStateMachineTransition(sourceStateMachine, newTransition); return newTransition; } internal void AddStateMachineTransition(AnimatorStateMachine sourceStateMachine, AnimatorTransition transition) { transition.hideFlags = HideFlags.HideInHierarchy; if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(transition, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(transition, "StateMachine Transition Created"); undoHandler.DoUndo(this, "StateMachine Transition Added"); AnimatorTransition[] transitionsVector = GetStateMachineTransitions(sourceStateMachine); ArrayUtility.Add(ref transitionsVector, transition); SetStateMachineTransitions(sourceStateMachine, transitionsVector); } public AnimatorTransition AddStateMachineExitTransition(AnimatorStateMachine sourceStateMachine) { var newTransition = new AnimatorTransition(); newTransition.isExit = true; AddStateMachineTransition(sourceStateMachine, newTransition); return newTransition; } public bool RemoveStateMachineTransition(AnimatorStateMachine sourceStateMachine, AnimatorTransition transition) { undoHandler.DoUndo(this, "StateMachine Transition Removed"); AnimatorTransition[] transitionsVector = GetStateMachineTransitions(sourceStateMachine); int baseSize = transitionsVector.Length; ArrayUtility.Remove(ref transitionsVector, transition); SetStateMachineTransitions(sourceStateMachine, transitionsVector); if (MecanimUtilities.AreSameAsset(this, transition)) Undo.DestroyObjectImmediate(transition); return baseSize != transitionsVector.Length; } private AnimatorTransition AddEntryTransition() { AnimatorTransition newTransition = new AnimatorTransition(); newTransition.hideFlags = HideFlags.HideInHierarchy; if (AssetDatabase.GetAssetPath(this) != "") AssetDatabase.AddObjectToAsset(newTransition, AssetDatabase.GetAssetPath(this)); undoHandler.DoUndoCreated(newTransition, "Entry Transition Created"); undoHandler.DoUndo(this, "Entry Transition Added"); AnimatorTransition[] transitionsVector = entryTransitions; ArrayUtility.Add(ref transitionsVector, newTransition); entryTransitions = transitionsVector; return newTransition; } public AnimatorTransition AddEntryTransition(AnimatorState destinationState) { AnimatorTransition newTransition = AddEntryTransition(); newTransition.destinationState = destinationState; return newTransition; } public AnimatorTransition AddEntryTransition(AnimatorStateMachine destinationStateMachine) { AnimatorTransition newTransition = AddEntryTransition(); newTransition.destinationStateMachine = destinationStateMachine; return newTransition; } public bool RemoveEntryTransition(AnimatorTransition transition) { if ((new List(entryTransitions)).Any(t => t == transition)) { undoHandler.DoUndo(this, "Entry Transition Removed"); AnimatorTransition[] transitionsVector = entryTransitions; ArrayUtility.Remove(ref transitionsVector, transition); entryTransitions = transitionsVector; if (MecanimUtilities.AreSameAsset(this, transition)) Undo.DestroyObjectImmediate(transition); return true; } return false; } internal ChildAnimatorState FindState(int nameHash) { return (new List(states)).Find(s => s.state.nameHash == nameHash); } internal ChildAnimatorState FindState(string name) { return (new List(states)).Find(s => s.state.name == name); } internal bool HasState(AnimatorState state) { return statesRecursive.Any(s => s.state == state); } internal bool IsDirectParent(AnimatorStateMachine stateMachine) { return stateMachines.Any(sm => sm.stateMachine == stateMachine); } internal bool HasStateMachine(AnimatorStateMachine child) { return stateMachinesRecursive.Any(sm => sm.stateMachine == child); } internal bool HasTransition(AnimatorState stateA, AnimatorState stateB) { return stateA.transitions.Any(t => t.destinationState == stateB) || stateB.transitions.Any(t => t.destinationState == stateA); } internal AnimatorStateMachine FindParent(AnimatorStateMachine stateMachine) { if (stateMachines.Any(childSM => childSM.stateMachine == stateMachine)) return this; else return stateMachinesRecursive.Find(sm => sm.stateMachine.stateMachines.Any(childSM => childSM.stateMachine == stateMachine)).stateMachine; } internal AnimatorStateMachine FindStateMachine(string path) { string[] smNames = path.Split('.'); // first element is always Root statemachine 'this' AnimatorStateMachine currentSM = this; // last element is state name, we don't care for (int i = 1; i < smNames.Length - 1 && currentSM != null; ++i) { var childStateMachines = AnimatorStateMachine.StateMachineCache.GetChildStateMachines(currentSM); int index = System.Array.FindIndex(childStateMachines, t => t.stateMachine.name == smNames[i]); currentSM = index >= 0 ? childStateMachines[index].stateMachine : null; } return (currentSM == null) ? this : currentSM; } internal AnimatorStateMachine FindStateMachine(AnimatorState state) { if (HasState(state, false)) return this; List childStateMachines = stateMachinesRecursive; int index = childStateMachines.FindIndex(sm => sm.stateMachine.HasState(state, false)); return index >= 0 ? childStateMachines[index].stateMachine : null; } internal AnimatorStateTransition FindTransition(AnimatorState destinationState) { return (new List(anyStateTransitions)).Find(t => t.destinationState == destinationState); } [System.Obsolete("stateCount is obsolete. Use .states.Length instead.", true)] int stateCount { get { return 0; } } [System.Obsolete("stateMachineCount is obsolete. Use .stateMachines.Length instead.", true)] int stateMachineCount { get { return 0; } } [System.Obsolete("GetTransitionsFromState is obsolete. Use AnimatorState.transitions instead.", true)] AnimatorState GetTransitionsFromState(AnimatorState state) { return null; } [System.Obsolete("uniqueNameHash does not exist anymore.", true)] int uniqueNameHash { get { return -1; } } } } ================================================ FILE: Editor/Mono/Animation/TickHandler.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEditor; using System.Collections; using System.Collections.Generic; namespace UnityEditor { [System.Serializable] internal class TickHandler { // Variables related to drawing tick markers [SerializeField] private float[] m_TickModulos = new float[] {}; // array with possible modulo numbers to choose from [SerializeField] private float[] m_TickStrengths = new float[] {}; // array with current strength of each modulo number [SerializeField] private int m_SmallestTick = 0; // index of the currently smallest modulo number used to draw ticks [SerializeField] private int m_BiggestTick = -1; // index of the currently biggest modulo number used to draw ticks [SerializeField] private float m_MinValue = 0; // shownArea min (in curve space) [SerializeField] private float m_MaxValue = 1; // shownArea max (in curve space) [SerializeField] private float m_PixelRange = 1; // total width/height of curveeditor public int tickLevels { get { return m_BiggestTick - m_SmallestTick + 1; } } private List m_TickList = new List(1000); public void SetTickModulos(float[] tickModulos) { m_TickModulos = tickModulos; } public List GetTickModulosForFrameRate(float frameRate) { List modulos; // Make frames multiples of 5 and 10, if frameRate is too high (avoid overflow) or not an even number if (frameRate > int.MaxValue / 2.0f || frameRate != Mathf.Round(frameRate)) { modulos = new List { 1f / frameRate, 5f / frameRate, 10f / frameRate, 50f / frameRate, 100f / frameRate, 500f / frameRate, 1000f / frameRate, 5000f / frameRate, 10000f / frameRate, 50000f / frameRate, 100000f / frameRate, 500000f / frameRate }; return modulos; } List dividers = new List(); int divisor = 1; while (divisor < frameRate) { if (Math.Abs(divisor - frameRate) < 1e-5) break; int multiple = Mathf.RoundToInt(frameRate / divisor); if (multiple % 60 == 0) { divisor *= 2; dividers.Add(divisor); } else if (multiple % 30 == 0) { divisor *= 3; dividers.Add(divisor); } else if (multiple % 20 == 0) { divisor *= 2; dividers.Add(divisor); } else if (multiple % 10 == 0) { divisor *= 2; dividers.Add(divisor); } else if (multiple % 5 == 0) { divisor *= 5; dividers.Add(divisor); } else if (multiple % 2 == 0) { divisor *= 2; dividers.Add(divisor); } else if (multiple % 3 == 0) { divisor *= 3; dividers.Add(divisor); } else divisor = Mathf.RoundToInt(frameRate); } modulos = new List(13 + dividers.Count); for (int i = 0; i < dividers.Count; i++) modulos.Add(1f / dividers[dividers.Count - i - 1]); // Ticks based on seconds modulos.Add(1); modulos.Add(5); modulos.Add(10); modulos.Add(30); modulos.Add(60); modulos.Add(60 * 5); modulos.Add(60 * 10); modulos.Add(60 * 30); modulos.Add(3600); modulos.Add(3600 * 6); modulos.Add(3600 * 24); modulos.Add(3600 * 24 * 7); modulos.Add(3600 * 24 * 14); return modulos; } public void SetTickModulosForFrameRate(float frameRate) { var modulos = GetTickModulosForFrameRate(frameRate); SetTickModulos(modulos.ToArray()); } public void SetRanges(float minValue, float maxValue, float minPixel, float maxPixel) { m_MinValue = minValue; m_MaxValue = maxValue; m_PixelRange = maxPixel - minPixel; } public float[] GetTicksAtLevel(int level, bool excludeTicksFromHigherlevels) { if (level < 0) return new float[0] {}; m_TickList.Clear(); GetTicksAtLevel(level, excludeTicksFromHigherlevels, m_TickList); return m_TickList.ToArray(); } public void GetTicksAtLevel(int level, bool excludeTicksFromHigherlevels, List list) { if (list == null) throw new System.ArgumentNullException("list"); int l = Mathf.Clamp(m_SmallestTick + level, 0, m_TickModulos.Length - 1); int startTick = Mathf.FloorToInt(m_MinValue / m_TickModulos[l]); int endTick = Mathf.CeilToInt(m_MaxValue / m_TickModulos[l]); for (int i = startTick; i <= endTick; i++) { // Return if tick mark is at same time as larger tick mark if (excludeTicksFromHigherlevels && l < m_BiggestTick && (i % Mathf.RoundToInt(m_TickModulos[l + 1] / m_TickModulos[l]) == 0)) continue; list.Add(i * m_TickModulos[l]); } } public float GetStrengthOfLevel(int level) { return m_TickStrengths[m_SmallestTick + level]; } public float GetPeriodOfLevel(int level) { return m_TickModulos[Mathf.Clamp(m_SmallestTick + level, 0, m_TickModulos.Length - 1)]; } public int GetLevelWithMinSeparation(float pixelSeparation) { for (int i = 0; i < m_TickModulos.Length; i++) { // How far apart (in pixels) these modulo ticks are spaced: float tickSpacing = m_TickModulos[i] * m_PixelRange / (m_MaxValue - m_MinValue); if (tickSpacing >= pixelSeparation) return i - m_SmallestTick; } return -1; } public void SetTickStrengths(float tickMinSpacing, float tickMaxSpacing, bool sqrt) { if (m_TickStrengths == null || m_TickStrengths.Length != m_TickModulos.Length) { m_TickStrengths = new float[m_TickModulos.Length]; } m_SmallestTick = 0; m_BiggestTick = m_TickModulos.Length - 1; // Find the strength for each modulo number tick marker for (int i = m_TickModulos.Length - 1; i >= 0; i--) { // How far apart (in pixels) these modulo ticks are spaced: float tickSpacing = m_TickModulos[i] * m_PixelRange / (m_MaxValue - m_MinValue); // Calculate the strength of the tick markers based on the spacing: m_TickStrengths[i] = (tickSpacing - tickMinSpacing) / (tickMaxSpacing - tickMinSpacing); // Beyond kTickHeightFatThreshold the ticks don't get any bigger or fatter, // so ignore them, since they are already covered by smalle modulo ticks anyway: if (m_TickStrengths[i] >= 1) m_BiggestTick = i; // Do not show tick markers less than 3 pixels apart: if (tickSpacing <= tickMinSpacing) { m_SmallestTick = i; break; } } // Use sqrt on actively used modulo number tick markers for (int i = m_SmallestTick; i <= m_BiggestTick; i++) { m_TickStrengths[i] = Mathf.Clamp01(m_TickStrengths[i]); if (sqrt) m_TickStrengths[i] = Mathf.Sqrt(m_TickStrengths[i]); } } } } // namespace ================================================ FILE: Editor/Mono/Animation/TickStyle.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; namespace UnityEditor { [System.Serializable] internal class TickStyle { public EditorGUIUtility.SkinnedColor tickColor = new EditorGUIUtility.SkinnedColor(new Color(0.0f, 0.0f, 0.0f, 0.2f), new Color(.45f, .45f, .45f, 0.2f)); // color and opacity of ticks public EditorGUIUtility.SkinnedColor labelColor = new EditorGUIUtility.SkinnedColor(new Color(0.0f, 0.0f, 0.0f, 0.32f), new Color(0.8f, 0.8f, 0.8f, 0.32f)); // color and opacity of tick labels public int distMin = 10; // min distance between ticks before they disappear completely public int distFull = 80; // distance between ticks where they gain full strength public int distLabel = 50; // min distance between tick labels public bool stubs = false; // draw ticks as stubs or as full lines? public bool centerLabel = false; // center label on tick lines public string unit = ""; // unit to write after the number } } ================================================ FILE: Editor/Mono/Animation/TimeArea.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; using System.Globalization; namespace UnityEditor { [System.Serializable] class TimeArea : ZoomableArea { [SerializeField] private TickHandler m_HTicks; public TickHandler hTicks { get { return m_HTicks; } set { m_HTicks = value; } } [SerializeField] private TickHandler m_VTicks; static readonly List s_TickCache = new List(1000); public TickHandler vTicks { get { return m_VTicks; } set { m_VTicks = value; } } internal const int kTickRulerDistMin = 3 ; // min distance between ruler tick marks before they disappear completely internal const int kTickRulerDistFull = 80; // distance between ruler tick marks where they gain full strength internal const int kTickRulerDistLabel = 40; // min distance between ruler tick mark labels internal const float kTickRulerHeightMax = 0.7f; // height of the ruler tick marks when they are highest internal const float kTickRulerFatThreshold = 0.5f ; // size of ruler tick marks at which they begin getting fatter public enum TimeFormat { None, // Unformatted time TimeFrame, // Time:Frame Frame // Integer frame } class Styles2 { public GUIStyle timelineTick = "AnimationTimelineTick"; public GUIStyle playhead = "AnimationPlayHead"; } static Styles2 timeAreaStyles; static void InitStyles() { if (timeAreaStyles == null) timeAreaStyles = new Styles2(); } public TimeArea(bool minimalGUI) : this(minimalGUI, true, true) {} public TimeArea(bool minimalGUI, bool enableSliderZoom) : this(minimalGUI, enableSliderZoom, enableSliderZoom) {} public TimeArea(bool minimalGUI, bool enableSliderZoomHorizontal, bool enableSliderZoomVertical) : base(minimalGUI, enableSliderZoomHorizontal, enableSliderZoomVertical) { float[] modulos = new float[] { 0.0000001f, 0.0000005f, 0.000001f, 0.000005f, 0.00001f, 0.00005f, 0.0001f, 0.0005f, 0.001f, 0.005f, 0.01f, 0.05f, 0.1f, 0.5f, 1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 5000000, 10000000 }; hTicks = new TickHandler(); hTicks.SetTickModulos(modulos); vTicks = new TickHandler(); vTicks.SetTickModulos(modulos); } public void SetTickMarkerRanges() { hTicks.SetRanges(shownArea.xMin, shownArea.xMax, drawRect.xMin, drawRect.xMax); vTicks.SetRanges(shownArea.yMin, shownArea.yMax, drawRect.yMin, drawRect.yMax); } public void DrawMajorTicks(Rect position, float frameRate) { GUI.BeginGroup(position); if (Event.current.type != EventType.Repaint) { GUI.EndGroup(); return; } InitStyles(); HandleUtility.ApplyWireMaterial(); SetTickMarkerRanges(); hTicks.SetTickStrengths(kTickRulerDistMin, kTickRulerDistFull, true); Color tickColor = timeAreaStyles.timelineTick.normal.textColor; tickColor.a = 0.1f; if (Application.platform == RuntimePlatform.WindowsEditor) GL.Begin(GL.QUADS); else GL.Begin(GL.LINES); // Draw tick markers of various sizes Rect theShowArea = shownArea; for (int l = 0; l < hTicks.tickLevels; l++) { float strength = hTicks.GetStrengthOfLevel(l) * .9f; if (strength > kTickRulerFatThreshold) { s_TickCache.Clear(); hTicks.GetTicksAtLevel(l, true, s_TickCache); for (int i = 0; i < s_TickCache.Count; i++) { if (s_TickCache[i] < 0) continue; int frame = Mathf.RoundToInt(s_TickCache[i] * frameRate); float x = FrameToPixel(frame, frameRate, position, theShowArea); // Draw line DrawVerticalLineFast(x, 0.0f, position.height, tickColor); } } } GL.End(); GUI.EndGroup(); } public void TimeRuler(Rect position, float frameRate) { TimeRuler(position, frameRate, true, false, 1f, TimeFormat.TimeFrame); } public void TimeRuler(Rect position, float frameRate, bool labels, bool useEntireHeight, float alpha, TimeFormat timeFormat) { Color backupCol = GUI.color; GUI.BeginGroup(position); InitStyles(); HandleUtility.ApplyWireMaterial(); Color tempBackgroundColor = GUI.backgroundColor; SetTickMarkerRanges(); hTicks.SetTickStrengths(kTickRulerDistMin, kTickRulerDistFull, true); Color baseColor = timeAreaStyles.timelineTick.normal.textColor; baseColor.a = 0.75f * alpha; if (Event.current.type == EventType.Repaint) { if (Application.platform == RuntimePlatform.WindowsEditor) GL.Begin(GL.QUADS); else GL.Begin(GL.LINES); // Draw tick markers of various sizes Rect cachedShowArea = shownArea; for (int l = 0; l < hTicks.tickLevels; l++) { float strength = hTicks.GetStrengthOfLevel(l) * .9f; s_TickCache.Clear(); hTicks.GetTicksAtLevel(l, true, s_TickCache); for (int i = 0; i < s_TickCache.Count; i++) { if (s_TickCache[i] < hRangeMin || s_TickCache[i] > hRangeMax) continue; int frame = Mathf.RoundToInt(s_TickCache[i] * frameRate); float height = useEntireHeight ? position.height : position.height * Mathf.Min(1, strength) * kTickRulerHeightMax; float x = FrameToPixel(frame, frameRate, position, cachedShowArea); // Draw line DrawVerticalLineFast(x, position.height - height + 0.5f, position.height - 0.5f, new Color(1, 1, 1, strength / kTickRulerFatThreshold) * baseColor); } } GL.End(); } if (labels) { // Draw tick labels int labelLevel = hTicks.GetLevelWithMinSeparation(kTickRulerDistLabel); s_TickCache.Clear(); hTicks.GetTicksAtLevel(labelLevel, false, s_TickCache); for (int i = 0; i < s_TickCache.Count; i++) { if (s_TickCache[i] < hRangeMin || s_TickCache[i] > hRangeMax) continue; int frame = Mathf.RoundToInt(s_TickCache[i] * frameRate); // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. float labelpos = Mathf.Floor(FrameToPixel(frame, frameRate, position)); string label = FormatTickTime(s_TickCache[i], frameRate, timeFormat); GUI.Label(new Rect(labelpos + 3, -1, 40, 20), label, timeAreaStyles.timelineTick); } } GUI.EndGroup(); GUI.backgroundColor = tempBackgroundColor; GUI.color = backupCol; } public static void DrawPlayhead(float x, float yMin, float yMax, float thickness, float alpha) { if (Event.current.type != EventType.Repaint) return; InitStyles(); float halfThickness = thickness * 0.5f; Color lineColor = timeAreaStyles.playhead.normal.textColor.AlphaMultiplied(alpha); if (thickness > 1f) { Rect labelRect = Rect.MinMaxRect(x - halfThickness, yMin, x + halfThickness, yMax); EditorGUI.DrawRect(labelRect, lineColor); } else { DrawVerticalLine(x, yMin, yMax, lineColor); } } public static void DrawVerticalLine(float x, float minY, float maxY, Color color) { if (Event.current.type != EventType.Repaint) return; Color backupCol = Handles.color; HandleUtility.ApplyWireMaterial(); if (Application.platform == RuntimePlatform.WindowsEditor) GL.Begin(GL.QUADS); else GL.Begin(GL.LINES); DrawVerticalLineFast(x, minY, maxY, color); GL.End(); Handles.color = backupCol; } public static void DrawVerticalLineFast(float x, float minY, float maxY, Color color) { if (Application.platform == RuntimePlatform.WindowsEditor) { GL.Color(color); GL.Vertex(new Vector3(x - 0.5f, minY, 0)); GL.Vertex(new Vector3(x + 0.5f, minY, 0)); GL.Vertex(new Vector3(x + 0.5f, maxY, 0)); GL.Vertex(new Vector3(x - 0.5f, maxY, 0)); } else { GL.Color(color); GL.Vertex(new Vector3(x, minY, 0)); GL.Vertex(new Vector3(x, maxY, 0)); } } public enum TimeRulerDragMode { None, Start, End, Dragging, Cancel } static float s_OriginalTime; static float s_PickOffset; public TimeRulerDragMode BrowseRuler(Rect position, ref float time, float frameRate, bool pickAnywhere, GUIStyle thumbStyle) { int id = GUIUtility.GetControlID(3126789, FocusType.Passive); return BrowseRuler(position, id, ref time, frameRate, pickAnywhere, thumbStyle); } public TimeRulerDragMode BrowseRuler(Rect position, int id, ref float time, float frameRate, bool pickAnywhere, GUIStyle thumbStyle) { Event evt = Event.current; Rect pickRect = position; if (time != -1) { pickRect.x = Mathf.Round(TimeToPixel(time, position)) - thumbStyle.overflow.left; pickRect.width = thumbStyle.fixedWidth + thumbStyle.overflow.horizontal; } switch (evt.GetTypeForControl(id)) { case EventType.Repaint: if (time != -1) { bool hover = position.Contains(evt.mousePosition); pickRect.x += thumbStyle.overflow.left; thumbStyle.Draw(pickRect, id == GUIUtility.hotControl, hover || id == GUIUtility.hotControl, false, false); } break; case EventType.MouseDown: if (pickRect.Contains(evt.mousePosition)) { GUIUtility.hotControl = id; s_PickOffset = evt.mousePosition.x - TimeToPixel(time, position); evt.Use(); return TimeRulerDragMode.Start; } else if (pickAnywhere && position.Contains(evt.mousePosition)) { GUIUtility.hotControl = id; float newT = SnapTimeToWholeFPS(PixelToTime(evt.mousePosition.x, position), frameRate); s_OriginalTime = time; if (newT != time) GUI.changed = true; time = newT; s_PickOffset = 0; evt.Use(); return TimeRulerDragMode.Start; } break; case EventType.MouseDrag: if (GUIUtility.hotControl == id) { float newT = SnapTimeToWholeFPS(PixelToTime(evt.mousePosition.x - s_PickOffset, position), frameRate); if (newT != time) GUI.changed = true; time = newT; evt.Use(); return TimeRulerDragMode.Dragging; } break; case EventType.MouseUp: if (GUIUtility.hotControl == id) { GUIUtility.hotControl = 0; evt.Use(); return TimeRulerDragMode.End; } break; case EventType.KeyDown: if (GUIUtility.hotControl == id && evt.keyCode == KeyCode.Escape) { if (time != s_OriginalTime) GUI.changed = true; time = s_OriginalTime; GUIUtility.hotControl = 0; evt.Use(); return TimeRulerDragMode.Cancel; } break; } return TimeRulerDragMode.None; } private float FrameToPixel(float i, float frameRate, Rect rect, Rect theShownArea) { return (i - theShownArea.xMin * frameRate) * rect.width / (theShownArea.width * frameRate); } public float FrameToPixel(float i, float frameRate, Rect rect) { return FrameToPixel(i, frameRate, rect, shownArea); } public float TimeField(Rect rect, int id, float time, float frameRate, TimeFormat timeFormat) { if (timeFormat == TimeFormat.None) { float newTime = EditorGUI.DoFloatField( EditorGUI.s_RecycledEditor, rect, new Rect(0, 0, 0, 0), id, time, EditorGUI.kFloatFieldFormatString, EditorStyles.numberField, false); return SnapTimeToWholeFPS(newTime, frameRate); } if (timeFormat == TimeFormat.Frame) { int frame = Mathf.RoundToInt(time * frameRate); int newFrame = EditorGUI.DoIntField( EditorGUI.s_RecycledEditor, rect, new Rect(0, 0, 0, 0), id, frame, EditorGUI.kIntFieldFormatString, EditorStyles.numberField, false, 0f); return (float)newFrame / frameRate; } else // if (timeFormat == TimeFormat.TimeFrame) { string str = FormatTime(time, frameRate, TimeFormat.TimeFrame); string allowedCharacters = "0123456789.,:"; bool changed; str = EditorGUI.DoTextField(EditorGUI.s_RecycledEditor, id, rect, str, EditorStyles.numberField, allowedCharacters, out changed, false, false, false); if (changed) { if (GUIUtility.keyboardControl == id) { GUI.changed = true; // Make sure that comma & period are interchangable. str = str.Replace(',', '.'); // format is time:frame int index = str.IndexOf(':'); if (index >= 0) { string timeStr = str.Substring(0, index); string frameStr = str.Substring(index + 1); int timeValue, frameValue; if (int.TryParse(timeStr, out timeValue) && int.TryParse(frameStr, out frameValue)) { float newTime = (float)timeValue + (float)frameValue / frameRate; return newTime; } } // format is floating time value. else { float newTime; if (float.TryParse(str, out newTime)) { return SnapTimeToWholeFPS(newTime, frameRate); } } } } } return time; } public float ValueField(Rect rect, int id, float value) { float newValue = EditorGUI.DoFloatField( EditorGUI.s_RecycledEditor, rect, new Rect(0, 0, 0, 0), id, value, EditorGUI.kFloatFieldFormatString, EditorStyles.numberField, false); return newValue; } public string FormatTime(float time, float frameRate, TimeFormat timeFormat) { if (timeFormat == TimeFormat.None) { int hDecimals; if (frameRate != 0) hDecimals = MathUtils.GetNumberOfDecimalsForMinimumDifference(1 / frameRate); else hDecimals = MathUtils.GetNumberOfDecimalsForMinimumDifference(shownArea.width / drawRect.width); return time.ToString("N" + hDecimals, CultureInfo.InvariantCulture.NumberFormat); } int frame = Mathf.RoundToInt(time * frameRate); if (timeFormat == TimeFormat.TimeFrame) { int frameDigits = frameRate != 0 ? ((int)frameRate - 1).ToString().Length : 1; string sign = string.Empty; if (frame < 0) { sign = "-"; frame = -frame; } return sign + (frame / (int)frameRate) + ":" + (frame % frameRate).ToString().PadLeft(frameDigits, '0'); } else { return frame.ToString(); } } public virtual string FormatTickTime(float time, float frameRate, TimeFormat timeFormat) { return FormatTime(time, frameRate, timeFormat); } public string FormatValue(float value) { int vDecimals = MathUtils.GetNumberOfDecimalsForMinimumDifference(shownArea.height / drawRect.height); return value.ToString("N" + vDecimals, CultureInfo.InvariantCulture.NumberFormat); } public float SnapTimeToWholeFPS(float time, float frameRate) { if (frameRate == 0) return time; return Mathf.Round(time * frameRate) / frameRate; } public void DrawTimeOnSlider(float time, Color c, float maxTime, float leftSidePadding = 0, float rightSidePadding = 0) { const float maxTimeFudgeFactor = 3; if (!hSlider) return; if (styles.horizontalScrollbar == null) styles.InitGUIStyles(false, allowSliderZoomHorizontal, allowSliderZoomVertical); var inMin = TimeToPixel(0, rect); // Assume 0 minTime var inMax = TimeToPixel(maxTime, rect); var outMin = TimeToPixel(shownAreaInsideMargins.xMin, rect) + styles.horizontalScrollbarLeftButton.fixedWidth + leftSidePadding; var outMax = TimeToPixel(shownAreaInsideMargins.xMax, rect) - (styles.horizontalScrollbarRightButton.fixedWidth + rightSidePadding); var x = (TimeToPixel(time, rect) - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; if (x > rect.xMax - (styles.horizontalScrollbarLeftButton.fixedWidth + leftSidePadding + maxTimeFudgeFactor)) return; var inset = styles.sliderWidth - styles.visualSliderWidth; var otherInset = (vSlider && hSlider) ? inset : 0; var hRangeSliderRect = new Rect(drawRect.x + 1, drawRect.yMax - inset, drawRect.width - otherInset, styles.sliderWidth); var p1 = new Vector2(x, hRangeSliderRect.yMin); var p2 = new Vector2(x, hRangeSliderRect.yMax); var lineRect = Rect.MinMaxRect(p1.x - 0.5f, p1.y, p2.x + 0.5f, p2.y); EditorGUI.DrawRect(lineRect, c); } } } ================================================ FILE: Editor/Mono/Animation/TransitionPreview.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections; using UnityEditor; using System.Collections.Generic; using UnityEditor.Animations; namespace UnityEditor { internal class TransitionPreview { private AvatarPreview m_AvatarPreview; private TimelineControl m_Timeline; private AnimatorController m_Controller; private AnimatorStateMachine m_StateMachine; private List m_ParameterMinMax = new List(); private List m_ParameterInfoList; private AnimatorStateTransition m_RefTransition; private TransitionInfo m_RefTransitionInfo = new TransitionInfo(); private AnimatorStateTransition m_Transition; private AnimatorState m_SrcState; private AnimatorState m_DstState; private AnimatorState m_RefSrcState; private AnimatorState m_RefDstState; private Motion m_SrcMotion; private Motion m_DstMotion; private bool m_ShowBlendValue = false; private bool m_MustResample = true; private bool m_MustSampleMotions = false; private bool m_MustResetParameterInfoList = false; public bool mustResample { set { m_MustResample = value; } get { return m_MustResample; } } private float m_LastEvalTime = -1.0f; private bool m_IsResampling = false; private AvatarMask m_LayerMask; private int m_LayerIndex; private bool m_ValidTransition = true; class ParameterInfo { public string m_Name; public float m_Value; } int FindParameterInfo(List parameterInfoList, string name) { int ret = -1; for (int i = 0; i < parameterInfoList.Count && ret == -1; i++) { if (parameterInfoList[i].m_Name == name) { ret = i; } } return ret; } class TransitionInfo { AnimatorState m_SrcState; AnimatorState m_DstState; float m_TransitionDuration; float m_TransitionOffset; float m_ExitTime; public bool IsEqual(TransitionInfo info) { return m_SrcState == info.m_SrcState && m_DstState == info.m_DstState && Mathf.Approximately(m_TransitionDuration, info.m_TransitionDuration) && Mathf.Approximately(m_TransitionOffset, info.m_TransitionOffset) && Mathf.Approximately(m_ExitTime, info.m_ExitTime); } public TransitionInfo() { Init(); } void Init() { m_SrcState = null; m_DstState = null; m_TransitionDuration = 0.0f; m_TransitionOffset = 0.0f; m_ExitTime = 0.5f; } public void Set(AnimatorStateTransition transition, AnimatorState srcState, AnimatorState dstState) { if (transition != null) { m_SrcState = srcState; m_DstState = dstState; m_TransitionDuration = transition.duration; m_TransitionOffset = transition.offset; m_ExitTime = transition.exitTime; } else { Init(); } } } private void CopyStateForPreview(AnimatorState src, ref AnimatorState dst) { dst.iKOnFeet = src.iKOnFeet; dst.speed = src.speed; dst.mirror = src.mirror; dst.motion = src.motion; } private void CopyTransitionForPreview(AnimatorStateTransition src, ref AnimatorStateTransition dst) { if (src != null) { dst.duration = src.duration; dst.offset = src.offset; dst.exitTime = src.exitTime; dst.hasFixedDuration = src.hasFixedDuration; } } float m_LeftStateWeightA = 0; float m_LeftStateWeightB = 1; float m_LeftStateTimeA = 0; float m_LeftStateTimeB = 1; float m_RightStateWeightA = 0; float m_RightStateWeightB = 1; float m_RightStateTimeA = 0; float m_RightStateTimeB = 1; List m_SrcPivotList = new List(); List m_DstPivotList = new List(); private bool MustResample(TransitionInfo info) { bool isInPlayback = m_AvatarPreview != null && m_AvatarPreview.Animator != null && m_AvatarPreview.Animator.recorderMode == AnimatorRecorderMode.Playback; return !isInPlayback || mustResample || !info.IsEqual(m_RefTransitionInfo); } private void WriteParametersInController() { if (m_Controller) { int parameterCount = m_Controller.parameters.Length; for (int i = 0; i < parameterCount; i++) { string parameterName = m_Controller.parameters[i].name; int parameterInfoIndex = FindParameterInfo(m_ParameterInfoList, parameterName); if (parameterInfoIndex != -1) { m_AvatarPreview.Animator.SetFloat(parameterName, m_ParameterInfoList[parameterInfoIndex].m_Value); } } } } private void ResampleTransition(AnimatorStateTransition transition, AvatarMask layerMask, TransitionInfo info, Animator previewObject) { m_IsResampling = true; m_MustResample = false; m_ValidTransition = true; bool resetTimeSettings = m_RefTransition != transition; m_RefTransition = transition; m_RefTransitionInfo = info; m_LayerMask = layerMask; if (m_AvatarPreview != null) { m_AvatarPreview.OnDisable(); m_AvatarPreview = null; } ClearController(); Motion sourceStateMotion = m_RefSrcState.motion; Init(previewObject, sourceStateMotion != null ? sourceStateMotion : m_RefDstState.motion); if (m_Controller == null) // did not create controller { m_IsResampling = false; return; } // since transform might change during sampling, and could alter the default valuesarray, and break recording m_AvatarPreview.Animator.allowConstantClipSamplingOptimization = false; // sample all frames m_StateMachine.defaultState = m_DstState; m_Transition.mute = true; AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, m_Controller); m_AvatarPreview.Animator.Update(0.00001f); WriteParametersInController(); m_AvatarPreview.Animator.SetLayerWeight(m_LayerIndex, 1); float nextStateDuration = m_AvatarPreview.Animator.GetCurrentAnimatorStateInfo(m_LayerIndex).length; m_StateMachine.defaultState = m_SrcState; m_Transition.mute = false; AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, m_Controller); m_AvatarPreview.Animator.Update(0.00001f); WriteParametersInController(); m_AvatarPreview.Animator.SetLayerWeight(m_LayerIndex, 1); float currentStateDuration = m_AvatarPreview.Animator.GetCurrentAnimatorStateInfo(m_LayerIndex).length; if (m_LayerIndex > 0) m_AvatarPreview.Animator.stabilizeFeet = false; float maxDuration = (currentStateDuration * m_RefTransition.exitTime) + (m_Transition.duration * (m_RefTransition.hasFixedDuration ? 1.0f : currentStateDuration)) + nextStateDuration; // case 546812 disable previewer if the duration is too big, otherwise it hang Unity. 2000.0f is an arbitrary choice, it can be increase if needed. // in some case we got a m_Transition.duration == Infinity, bail out before unity hang. if (maxDuration > 2000.0f) { Debug.LogWarning("Transition duration is longer than 2000 second, Disabling previewer."); m_ValidTransition = false; m_IsResampling = false; return; } float effectiveCurrentStatetime = m_RefTransition.exitTime > 0 ? currentStateDuration * m_RefTransition.exitTime : currentStateDuration; // We want 30 samples/sec, maxed at 300 sample for very long state, and very short animation like 1 frame should at least get 5 sample float currentStateStepTime = effectiveCurrentStatetime > 0 ? Mathf.Min(Mathf.Max(effectiveCurrentStatetime / 300.0f, 1.0f / 30.0f), effectiveCurrentStatetime / 5.0f) : 1.0f / 30.0f; float nextStateStepTime = nextStateDuration > 0 ? Mathf.Min(Mathf.Max(nextStateDuration / 300.0f, 1.0f / 30.0f), nextStateDuration / 5.0f) : 1.0f / 30.0f; currentStateStepTime = Mathf.Max(currentStateStepTime, maxDuration / 600.0f); nextStateStepTime = Mathf.Max(nextStateStepTime, maxDuration / 600.0f); float stepTime = currentStateStepTime; float currentTime = 0.0f; bool hasStarted = false; bool hasTransitioned = false; bool hasFinished = false; //For transitions with exit time == 0, skip to end of clip so transition happens on first frame if (m_RefTransition.exitTime == 0) { m_AvatarPreview.Animator.CrossFade(0, 0f, 0, 0.9f); } m_AvatarPreview.Animator.StartRecording(-1); m_AvatarPreview.Animator.Update(0.0f); AnimatorStateInfo currentState = m_AvatarPreview.Animator.GetCurrentAnimatorStateInfo(m_LayerIndex); m_LeftStateWeightA = currentState.normalizedTime; m_LeftStateTimeA = currentTime; while (!hasFinished) { m_AvatarPreview.Animator.Update(stepTime); currentState = m_AvatarPreview.Animator.GetCurrentAnimatorStateInfo(m_LayerIndex); currentTime += stepTime; if (!hasStarted) { m_LeftStateWeightB = currentState.normalizedTime; m_LeftStateTimeB = currentTime; hasStarted = true; } if (hasTransitioned && currentTime >= maxDuration) { hasFinished = true; } if (!hasTransitioned && currentState.IsName(m_DstState.name)) { m_RightStateWeightA = currentState.normalizedTime; m_RightStateTimeA = currentTime; hasTransitioned = true; } if (!hasTransitioned) { m_LeftStateWeightB = currentState.normalizedTime; m_LeftStateTimeB = currentTime; } if (hasTransitioned || hasFinished) { m_RightStateWeightB = currentState.normalizedTime; m_RightStateTimeB = currentTime; } if (m_AvatarPreview.Animator.IsInTransition(m_LayerIndex)) { stepTime = nextStateStepTime; } } float endTime = currentTime; m_AvatarPreview.Animator.StopRecording(); if (Mathf.Approximately(m_LeftStateWeightB, m_LeftStateWeightA) || Mathf.Approximately(m_RightStateWeightB, m_RightStateWeightA)) { Debug.LogWarning("Difference in effective length between states is too big. Transition preview will be disabled."); m_ValidTransition = false; m_IsResampling = false; return; } float leftDuration = (m_LeftStateTimeB - m_LeftStateTimeA) / (m_LeftStateWeightB - m_LeftStateWeightA); float rightDuration = (m_RightStateTimeB - m_RightStateTimeA) / (m_RightStateWeightB - m_RightStateWeightA); // Ensure step times make sense based on these timings // If step time is too small, the samping will take too long currentStateStepTime = Mathf.Max(currentStateStepTime, leftDuration / 600.0f); nextStateStepTime = Mathf.Max(nextStateStepTime, rightDuration / 600.0f); if (m_MustSampleMotions) { // Do this as infrequently as possible m_MustSampleMotions = false; m_SrcPivotList.Clear(); m_DstPivotList.Clear(); stepTime = nextStateStepTime; m_StateMachine.defaultState = m_DstState; m_Transition.mute = true; AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, m_Controller); m_AvatarPreview.Animator.Update(0.0f); m_AvatarPreview.Animator.SetLayerWeight(m_LayerIndex, 1); m_AvatarPreview.Animator.Update(0.0000001f); WriteParametersInController(); currentTime = 0.0f; while (currentTime <= rightDuration) { TimelineControl.PivotSample sample = new TimelineControl.PivotSample(); sample.m_Time = currentTime; sample.m_Weight = m_AvatarPreview.Animator.pivotWeight; m_DstPivotList.Add(sample); m_AvatarPreview.Animator.Update(stepTime * 2); currentTime += stepTime * 2; } stepTime = currentStateStepTime; m_StateMachine.defaultState = m_SrcState; m_Transition.mute = true; AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, m_Controller); m_AvatarPreview.Animator.Update(0.0000001f); WriteParametersInController(); m_AvatarPreview.Animator.SetLayerWeight(m_LayerIndex, 1); currentTime = 0.0f; while (currentTime <= leftDuration) { TimelineControl.PivotSample sample = new TimelineControl.PivotSample(); sample.m_Time = currentTime; sample.m_Weight = m_AvatarPreview.Animator.pivotWeight; m_SrcPivotList.Add(sample); m_AvatarPreview.Animator.Update(stepTime * 2); currentTime += stepTime * 2; } m_Transition.mute = false; AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, m_Controller); m_AvatarPreview.Animator.Update(0.0000001f); WriteParametersInController(); } m_Timeline.StopTime = m_AvatarPreview.timeControl.stopTime = endTime; m_AvatarPreview.timeControl.currentTime = m_Timeline.Time; if (resetTimeSettings) { m_Timeline.Time = m_Timeline.StartTime = m_AvatarPreview.timeControl.currentTime = m_AvatarPreview.timeControl.startTime = 0; m_Timeline.ResetRange(); } m_AvatarPreview.Animator.StartPlayback(); m_AvatarPreview.Animator.playbackTime = 0f; m_AvatarPreview.Animator.Update(0f); m_AvatarPreview.ResetPreviewFocus(); m_IsResampling = false; } public void SetTransition(AnimatorStateTransition transition, AnimatorState sourceState, AnimatorState destinationState, AnimatorControllerLayer srcLayer, Animator previewObject) { m_RefSrcState = sourceState; m_MustResetParameterInfoList = m_RefDstState != destinationState; m_RefDstState = destinationState; TransitionInfo info = new TransitionInfo(); info.Set(transition, sourceState, destinationState); if (MustResample(info)) { ResampleTransition(transition, srcLayer.avatarMask, info, previewObject); } } private void OnPreviewAvatarChanged() { m_RefTransitionInfo = new TransitionInfo(); ClearController(); CreateController(); CreateParameterInfoList(); } void ClearController() { if (m_AvatarPreview != null && m_AvatarPreview.Animator != null) AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, null); Object.DestroyImmediate(m_Controller); Object.DestroyImmediate(m_SrcState); Object.DestroyImmediate(m_DstState); Object.DestroyImmediate(m_Transition); m_StateMachine = null; m_Controller = null; m_SrcState = null; m_DstState = null; m_Transition = null; } void CreateParameterInfoList() { m_ParameterInfoList = new List(); if (m_Controller && m_Controller.parameters != null) { int parameterCount = m_Controller.parameters.Length; for (int i = 0; i < parameterCount; i++) { ParameterInfo parameterInfo = new ParameterInfo(); parameterInfo.m_Name = m_Controller.parameters[i].name; m_ParameterInfoList.Add(parameterInfo); } } } void CreateController() { if (m_Controller == null && m_AvatarPreview != null && m_AvatarPreview.Animator != null && m_RefTransition != null) { // controller m_LayerIndex = 0; m_Controller = new AnimatorController(); m_Controller.pushUndo = false; m_Controller.hideFlags = HideFlags.HideAndDontSave; m_Controller.AddLayer("preview"); bool isDefaultMask = true; if (m_LayerMask != null) { for (AvatarMaskBodyPart i = 0; i < AvatarMaskBodyPart.LastBodyPart && isDefaultMask; i++) if (!m_LayerMask.GetHumanoidBodyPartActive(i)) isDefaultMask = false; if (!isDefaultMask) { m_Controller.AddLayer("Additionnal"); m_LayerIndex++; AnimatorControllerLayer[] layers = m_Controller.layers; layers[m_LayerIndex].avatarMask = m_LayerMask; m_Controller.layers = layers; } } m_StateMachine = m_Controller.layers[m_LayerIndex].stateMachine; m_StateMachine.pushUndo = false; m_StateMachine.hideFlags = HideFlags.HideAndDontSave; m_SrcMotion = m_RefSrcState.motion; m_DstMotion = m_RefDstState.motion; /// Add parameters m_ParameterMinMax.Clear(); if (m_SrcMotion && m_SrcMotion is BlendTree) { BlendTree leftBlendTree = m_SrcMotion as BlendTree; for (int i = 0; i < leftBlendTree.recursiveBlendParameterCount; i++) { string blendValueName = leftBlendTree.GetRecursiveBlendParameter(i); if (m_Controller.IndexOfParameter(blendValueName) == -1) { m_Controller.AddParameter(blendValueName, AnimatorControllerParameterType.Float); m_ParameterMinMax.Add(new Vector2(leftBlendTree.GetRecursiveBlendParameterMin(i), leftBlendTree.GetRecursiveBlendParameterMax(i))); } } } if (m_DstMotion && m_DstMotion is BlendTree) { BlendTree rightBlendTree = m_DstMotion as BlendTree; for (int i = 0; i < rightBlendTree.recursiveBlendParameterCount; i++) { string blendValueName = rightBlendTree.GetRecursiveBlendParameter(i); int parameterIndex = m_Controller.IndexOfParameter(blendValueName); if (parameterIndex == -1) { m_Controller.AddParameter(blendValueName, AnimatorControllerParameterType.Float); m_ParameterMinMax.Add(new Vector2(rightBlendTree.GetRecursiveBlendParameterMin(i), rightBlendTree.GetRecursiveBlendParameterMax(i))); } else { m_ParameterMinMax[parameterIndex] = new Vector2(Mathf.Min(rightBlendTree.GetRecursiveBlendParameterMin(i), m_ParameterMinMax[parameterIndex][0]), Mathf.Max(rightBlendTree.GetRecursiveBlendParameterMax(i), m_ParameterMinMax[parameterIndex][1])); } } } // states m_SrcState = m_StateMachine.AddState(m_RefSrcState.name); m_SrcState.pushUndo = false; m_SrcState.hideFlags = HideFlags.HideAndDontSave; m_DstState = m_StateMachine.AddState(m_RefDstState.name); m_DstState.pushUndo = false; m_DstState.hideFlags = HideFlags.HideAndDontSave; CopyStateForPreview(m_RefSrcState, ref m_SrcState); CopyStateForPreview(m_RefDstState, ref m_DstState); // transition m_Transition = m_SrcState.AddTransition(m_DstState, true); m_Transition.pushUndo = false; m_Transition.hideFlags = HideFlags.DontSave; CopyTransitionForPreview(m_RefTransition, ref m_Transition); DisableIKOnFeetIfNeeded(); AnimatorController.SetAnimatorController(m_AvatarPreview.Animator, m_Controller); m_Controller.OnAnimatorControllerDirty += ControllerDirty; } } private void ControllerDirty() { if (!m_IsResampling) m_MustResample = true; } private void DisableIKOnFeetIfNeeded() { bool disable = m_SrcMotion == null || m_DstMotion == null; if (m_LayerIndex > 0) { disable = !m_LayerMask.hasFeetIK; } if (disable) { m_SrcState.iKOnFeet = false; m_DstState.iKOnFeet = false; } } private void Init(Animator scenePreviewObject, Motion motion) { if (m_AvatarPreview == null) { m_AvatarPreview = new AvatarPreview(scenePreviewObject, motion); m_AvatarPreview.OnAvatarChangeFunc = OnPreviewAvatarChanged; m_AvatarPreview.ShowIKOnFeetButton = false; m_AvatarPreview.ResetPreviewFocus(); } if (m_Timeline == null) { m_Timeline = new TimelineControl(); m_MustSampleMotions = true; } CreateController(); if(m_ParameterInfoList == null || m_MustResetParameterInfoList) { m_MustResetParameterInfoList = false; CreateParameterInfoList(); } } public void DoTransitionPreview() { if (m_Controller == null) return; DoTimeline(); // Draw the blend values AnimatorControllerParameter[] parameters = m_Controller.parameters; if (parameters.Length > 0) { m_ShowBlendValue = EditorGUILayout.Foldout(m_ShowBlendValue, "BlendTree Parameters", true); if (m_ShowBlendValue) { for (int i = 0; i < parameters.Length; i++) { AnimatorControllerParameter parameter = m_Controller.parameters[i]; float value = m_ParameterInfoList[i].m_Value; float newValue = EditorGUILayout.Slider(parameter.name, value, m_ParameterMinMax[i][0], m_ParameterMinMax[i][1]); if (newValue != value) { m_ParameterInfoList[i].m_Value = newValue; mustResample = true; m_MustSampleMotions = true; } } } } } private void DoTimeline() { if (!m_ValidTransition) { return; } // get local durations float srcStateDuration = (m_LeftStateTimeB - m_LeftStateTimeA) / (m_LeftStateWeightB - m_LeftStateWeightA); float dstStateDuration = (m_RightStateTimeB - m_RightStateTimeA) / (m_RightStateWeightB - m_RightStateWeightA); float transitionDuration = m_Transition.duration * (m_RefTransition.hasFixedDuration ? 1.0f : srcStateDuration); // Set the timeline values m_Timeline.SrcStartTime = 0f; m_Timeline.SrcStopTime = srcStateDuration; m_Timeline.SrcName = m_RefSrcState.name; m_Timeline.HasExitTime = m_RefTransition.hasExitTime; m_Timeline.srcLoop = m_SrcMotion ? m_SrcMotion.isLooping : false; m_Timeline.dstLoop = m_DstMotion ? m_DstMotion.isLooping : false; m_Timeline.TransitionStartTime = m_RefTransition.exitTime * srcStateDuration; m_Timeline.TransitionStopTime = m_Timeline.TransitionStartTime + transitionDuration; m_Timeline.Time = m_AvatarPreview.timeControl.currentTime; m_Timeline.DstStartTime = m_Timeline.TransitionStartTime - m_RefTransition.offset * dstStateDuration; m_Timeline.DstStopTime = m_Timeline.DstStartTime + dstStateDuration; m_Timeline.SampleStopTime = m_AvatarPreview.timeControl.stopTime; if (m_Timeline.TransitionStopTime == Mathf.Infinity) m_Timeline.TransitionStopTime = Mathf.Min(m_Timeline.DstStopTime, m_Timeline.SrcStopTime); m_Timeline.DstName = m_RefDstState.name; m_Timeline.SrcPivotList = m_SrcPivotList; m_Timeline.DstPivotList = m_DstPivotList; // Do the timeline Rect previewRect = EditorGUILayout.GetControlRect(false, 150, EditorStyles.label); EditorGUI.BeginChangeCheck(); bool changedData = m_Timeline.DoTimeline(previewRect); if (EditorGUI.EndChangeCheck()) { if (changedData) { Undo.RegisterCompleteObjectUndo(m_RefTransition, "Edit Transition"); m_RefTransition.exitTime = m_Timeline.TransitionStartTime / m_Timeline.SrcDuration; m_RefTransition.duration = m_Timeline.TransitionDuration / (m_RefTransition.hasFixedDuration ? 1.0f : m_Timeline.SrcDuration); m_RefTransition.offset = (m_Timeline.TransitionStartTime - m_Timeline.DstStartTime) / m_Timeline.DstDuration; } m_AvatarPreview.timeControl.nextCurrentTime = Mathf.Clamp(m_Timeline.Time, 0, m_AvatarPreview.timeControl.stopTime); } } public void OnDisable() { ClearController(); if (m_Timeline != null) { m_Timeline = null; } if (m_AvatarPreview != null) { m_AvatarPreview.OnDisable(); m_AvatarPreview = null; } } public bool HasPreviewGUI() { return true; } public void OnPreviewSettings() { if (m_AvatarPreview != null) m_AvatarPreview.DoPreviewSettings(); } public void OnInteractivePreviewGUI(Rect r, GUIStyle background) { if (m_AvatarPreview != null && m_Controller != null) { bool isRepaint = (Event.current.type == EventType.Repaint); if (isRepaint) m_AvatarPreview.timeControl.Update(); if (m_LastEvalTime != m_AvatarPreview.timeControl.currentTime && isRepaint) { m_AvatarPreview.Animator.playbackTime = m_AvatarPreview.timeControl.currentTime; m_AvatarPreview.Animator.Update(0); m_LastEvalTime = m_AvatarPreview.timeControl.currentTime; } m_AvatarPreview.DoAvatarPreview(r, background); } } } }//namespace UnityEditor ================================================ FILE: Editor/Mono/Animation/ZoomableArea.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; namespace UnityEditor { // NOTE: do _not_ use GUILayout to get the rectangle for zoomable area, // will not work and will start failing miserably (mouse events not hitting it, etc.). // That's because ZoomableArea is using GUILayout itself, and needs an actual // physical rectangle. [System.Serializable] internal class ZoomableArea { // Global state private static Vector2 m_MouseDownPosition = new Vector2(-1000000, -1000000); // in transformed space private static int zoomableAreaHash = "ZoomableArea".GetHashCode(); // Range lock settings [SerializeField] private bool m_HRangeLocked; [SerializeField] private bool m_VRangeLocked; public bool hRangeLocked { get { return m_HRangeLocked; } set { m_HRangeLocked = value; } } public bool vRangeLocked { get { return m_VRangeLocked; } set { m_VRangeLocked = value; } } // Zoom lock settings public bool hZoomLockedByDefault = false; public bool vZoomLockedByDefault = false; [SerializeField] private float m_HBaseRangeMin = 0; [SerializeField] private float m_HBaseRangeMax = 1; [SerializeField] private float m_VBaseRangeMin = 0; [SerializeField] private float m_VBaseRangeMax = 1; public float hBaseRangeMin { get { return m_HBaseRangeMin; } set { m_HBaseRangeMin = value; } } public float hBaseRangeMax { get { return m_HBaseRangeMax; } set { m_HBaseRangeMax = value; } } public float vBaseRangeMin { get { return m_VBaseRangeMin; } set { m_VBaseRangeMin = value; } } public float vBaseRangeMax { get { return m_VBaseRangeMax; } set { m_VBaseRangeMax = value; } } [SerializeField] private bool m_HAllowExceedBaseRangeMin = true; [SerializeField] private bool m_HAllowExceedBaseRangeMax = true; [SerializeField] private bool m_VAllowExceedBaseRangeMin = true; [SerializeField] private bool m_VAllowExceedBaseRangeMax = true; public bool hAllowExceedBaseRangeMin { get { return m_HAllowExceedBaseRangeMin; } set { m_HAllowExceedBaseRangeMin = value; } } public bool hAllowExceedBaseRangeMax { get { return m_HAllowExceedBaseRangeMax; } set { m_HAllowExceedBaseRangeMax = value; } } public bool vAllowExceedBaseRangeMin { get { return m_VAllowExceedBaseRangeMin; } set { m_VAllowExceedBaseRangeMin = value; } } public bool vAllowExceedBaseRangeMax { get { return m_VAllowExceedBaseRangeMax; } set { m_VAllowExceedBaseRangeMax = value; } } public float hRangeMin { get { return (hAllowExceedBaseRangeMin ? Mathf.NegativeInfinity : hBaseRangeMin); } set { SetAllowExceed(ref m_HBaseRangeMin, ref m_HAllowExceedBaseRangeMin, value); } } public float hRangeMax { get { return (hAllowExceedBaseRangeMax ? Mathf.Infinity : hBaseRangeMax); } set { SetAllowExceed(ref m_HBaseRangeMax, ref m_HAllowExceedBaseRangeMax, value); } } public float vRangeMin { get { return (vAllowExceedBaseRangeMin ? Mathf.NegativeInfinity : vBaseRangeMin); } set { SetAllowExceed(ref m_VBaseRangeMin, ref m_VAllowExceedBaseRangeMin, value); } } public float vRangeMax { get { return (vAllowExceedBaseRangeMax ? Mathf.Infinity : vBaseRangeMax); } set { SetAllowExceed(ref m_VBaseRangeMax, ref m_VAllowExceedBaseRangeMax, value); } } private void SetAllowExceed(ref float rangeEnd, ref bool allowExceed, float value) { if (value == Mathf.NegativeInfinity || value == Mathf.Infinity) { rangeEnd = (value == Mathf.NegativeInfinity ? 0 : 1); allowExceed = true; } else { rangeEnd = value; allowExceed = false; } } private const float kMinScale = 0.00001f; private const float kMaxScale = 100000.0f; private float m_HScaleMin = kMinScale; private float m_HScaleMax = kMaxScale; private float m_VScaleMin = kMinScale; private float m_VScaleMax = kMaxScale; private float m_MinWidth = 0.05f; private const float kMinHeight = 0.05f; private const float k_ScrollStepSize = 10f; // mirrors GUI scrollstepsize as there is no global const for this. public float minWidth { get { return m_MinWidth; } set { if (value > 0) m_MinWidth = value; else { Debug.LogWarning("Zoomable area width cannot have a value of " + "or below 0. Reverting back to a default value of 0.05f"); m_MinWidth = 0.05f; } } } public float hScaleMin { get { return m_HScaleMin; } set { m_HScaleMin = Mathf.Clamp(value, kMinScale, kMaxScale); styles.enableSliderZoomHorizontal = allowSliderZoomHorizontal; } } public float hScaleMax { get { return m_HScaleMax; } set { m_HScaleMax = Mathf.Clamp(value, kMinScale, kMaxScale); styles.enableSliderZoomHorizontal = allowSliderZoomHorizontal; } } public float vScaleMin { get { return m_VScaleMin; } set { m_VScaleMin = Mathf.Clamp(value, kMinScale, kMaxScale); styles.enableSliderZoomVertical = allowSliderZoomVertical; } } public float vScaleMax { get { return m_VScaleMax; } set { m_VScaleMax = Mathf.Clamp(value, kMinScale, kMaxScale); styles.enableSliderZoomVertical = allowSliderZoomVertical; } } // Window resize settings [SerializeField] private bool m_ScaleWithWindow = false; public bool scaleWithWindow { get { return m_ScaleWithWindow; } set { m_ScaleWithWindow = value; } } // Slider settings [SerializeField] private bool m_HSlider = true; [SerializeField] private bool m_VSlider = true; public bool hSlider { get { return m_HSlider; } set { Rect r = rect; m_HSlider = value; rect = r; } } public bool vSlider { get { return m_VSlider; } set { Rect r = rect; m_VSlider = value; rect = r; } } [SerializeField] private bool m_IgnoreScrollWheelUntilClicked = false; public bool ignoreScrollWheelUntilClicked { get { return m_IgnoreScrollWheelUntilClicked; } set { m_IgnoreScrollWheelUntilClicked = value; } } [SerializeField] private bool m_EnableMouseInput = true; public bool enableMouseInput { get { return m_EnableMouseInput; } set { m_EnableMouseInput = value; } } [SerializeField] private bool m_EnableSliderZoomHorizontal = true; [SerializeField] private bool m_EnableSliderZoomVertical = true; // if the min and max scaling does not allow for actual zooming, there is no point in allowing it protected bool allowSliderZoomHorizontal { get { return m_EnableSliderZoomHorizontal && m_HScaleMin < m_HScaleMax; } } protected bool allowSliderZoomVertical { get { return m_EnableSliderZoomVertical && m_VScaleMin < m_VScaleMax; } } public bool m_UniformScale; public bool uniformScale { get { return m_UniformScale; } set { m_UniformScale = value; } } // This is optional now, but used to be default behaviour because ZoomableAreas are mostly graphs with +Y being up public enum YDirection { Positive, Negative } [SerializeField] private YDirection m_UpDirection = YDirection.Positive; public YDirection upDirection { get { return m_UpDirection; } set { if (m_UpDirection != value) { m_UpDirection = value; m_Scale.y = -m_Scale.y; } } } // View and drawing settings [SerializeField] private Rect m_DrawArea = new Rect(0, 0, 100, 100); internal void SetDrawRectHack(Rect r) { m_DrawArea = r; } [SerializeField] internal Vector2 m_Scale = new Vector2(1, -1); [SerializeField] internal Vector2 m_Translation = new Vector2(0, 0); [SerializeField] private float m_MarginLeft, m_MarginRight, m_MarginTop, m_MarginBottom; [SerializeField] private Rect m_LastShownAreaInsideMargins = new Rect(0, 0, 100, 100); public Vector2 scale { get { return m_Scale; } } public Vector2 translation { get { return m_Translation; } } public float margin { set { m_MarginLeft = m_MarginRight = m_MarginTop = m_MarginBottom = value; } } public float leftmargin { get { return m_MarginLeft; } set { m_MarginLeft = value; } } public float rightmargin { get { return m_MarginRight; } set { m_MarginRight = value; } } public float topmargin { get { return m_MarginTop; } set { m_MarginTop = value; } } public float bottommargin { get { return m_MarginBottom; } set { m_MarginBottom = value; } } public float vSliderWidth { get { return vSlider ? styles.sliderWidth : 0f; } } public float hSliderHeight { get { return hSlider ? styles.sliderWidth : 0f; } } // IDs for controls internal int areaControlID; int verticalScrollbarID, horizontalScrollbarID; [SerializeField] bool m_MinimalGUI; public class Styles { public GUIStyle horizontalScrollbar; public GUIStyle horizontalMinMaxScrollbarThumb { get { return GetSliderAxisStyle(enableSliderZoomHorizontal).horizontal; } } public GUIStyle horizontalScrollbarLeftButton; public GUIStyle horizontalScrollbarRightButton; public GUIStyle verticalScrollbar; public GUIStyle verticalMinMaxScrollbarThumb { get { return GetSliderAxisStyle(enableSliderZoomVertical).vertical; } } public GUIStyle verticalScrollbarUpButton; public GUIStyle verticalScrollbarDownButton; public bool enableSliderZoomHorizontal; public bool enableSliderZoomVertical; public float sliderWidth; public float visualSliderWidth; private bool minimalGUI; private SliderTypeStyles.SliderAxisStyles GetSliderAxisStyle(bool enableSliderZoom) { if (minimalGUI) return enableSliderZoom ? minimalSliderStyles.minMaxSliders : minimalSliderStyles.scrollbar; else return enableSliderZoom ? normalSliderStyles.minMaxSliders : normalSliderStyles.scrollbar; } private static SliderTypeStyles minimalSliderStyles; private static SliderTypeStyles normalSliderStyles; private class SliderTypeStyles { public SliderAxisStyles scrollbar; public SliderAxisStyles minMaxSliders; public class SliderAxisStyles { public GUIStyle horizontal; public GUIStyle vertical; } } public Styles(bool minimalGUI) { if (minimalGUI) { visualSliderWidth = 0; sliderWidth = 13; } else { visualSliderWidth = 13; sliderWidth = 13; } } public void InitGUIStyles(bool minimalGUI, bool enableSliderZoom) { InitGUIStyles(minimalGUI, enableSliderZoom, enableSliderZoom); } public void InitGUIStyles(bool minimalGUI, bool enableSliderZoomHorizontal, bool enableSliderZoomVertical) { this.minimalGUI = minimalGUI; this.enableSliderZoomHorizontal = enableSliderZoomHorizontal; this.enableSliderZoomVertical = enableSliderZoomVertical; if (minimalGUI) { if (minimalSliderStyles == null) { minimalSliderStyles = new SliderTypeStyles() { scrollbar = new SliderTypeStyles.SliderAxisStyles() { horizontal = "MiniSliderhorizontal", vertical = "MiniSliderVertical" }, minMaxSliders = new SliderTypeStyles.SliderAxisStyles() { horizontal = "MiniMinMaxSliderHorizontal", vertical = "MiniMinMaxSlidervertical" }, }; } horizontalScrollbarLeftButton = GUIStyle.none; horizontalScrollbarRightButton = GUIStyle.none; horizontalScrollbar = GUIStyle.none; verticalScrollbarUpButton = GUIStyle.none; verticalScrollbarDownButton = GUIStyle.none; verticalScrollbar = GUIStyle.none; } else { if (normalSliderStyles == null) { normalSliderStyles = new SliderTypeStyles() { scrollbar = new SliderTypeStyles.SliderAxisStyles() { horizontal = "horizontalscrollbarthumb", vertical = "verticalscrollbarthumb" }, minMaxSliders = new SliderTypeStyles.SliderAxisStyles() { horizontal = "horizontalMinMaxScrollbarThumb", vertical = "verticalMinMaxScrollbarThumb" }, }; } horizontalScrollbarLeftButton = "horizontalScrollbarLeftbutton"; horizontalScrollbarRightButton = "horizontalScrollbarRightbutton"; horizontalScrollbar = GUI.skin.horizontalScrollbar; verticalScrollbarUpButton = "verticalScrollbarUpbutton"; verticalScrollbarDownButton = "verticalScrollbarDownbutton"; verticalScrollbar = GUI.skin.verticalScrollbar; } } } private Styles m_Styles; protected Styles styles { get { if (m_Styles == null) m_Styles = new Styles(m_MinimalGUI); return m_Styles; } } public Rect rect { get { return new Rect(drawRect.x, drawRect.y, drawRect.width + (m_VSlider ? styles.visualSliderWidth : 0), drawRect.height + (m_HSlider ? styles.visualSliderWidth : 0)); } set { Rect newDrawArea = new Rect(value.x, value.y, value.width - (m_VSlider ? styles.visualSliderWidth : 0), value.height - (m_HSlider ? styles.visualSliderWidth : 0)); if (newDrawArea != m_DrawArea) { if (m_ScaleWithWindow) { m_DrawArea = newDrawArea; shownAreaInsideMargins = m_LastShownAreaInsideMargins; } else { m_Translation += new Vector2((newDrawArea.width - m_DrawArea.width) / 2, (newDrawArea.height - m_DrawArea.height) / 2); m_DrawArea = newDrawArea; } } EnforceScaleAndRange(); } } public Rect drawRect { get { return m_DrawArea; } } public void SetShownHRangeInsideMargins(float min, float max) { float widthInsideMargins = drawRect.width - leftmargin - rightmargin; if (widthInsideMargins < m_MinWidth) widthInsideMargins = m_MinWidth; float denum = max - min; if (denum < m_MinWidth) denum = m_MinWidth; m_Scale.x = widthInsideMargins / denum; m_Translation.x = -min * m_Scale.x + leftmargin; EnforceScaleAndRange(); } public void SetShownHRange(float min, float max) { float denum = max - min; if (denum < m_MinWidth) denum = m_MinWidth; m_Scale.x = drawRect.width / denum; m_Translation.x = -min * m_Scale.x; EnforceScaleAndRange(); } public void SetShownVRangeInsideMargins(float min, float max) { float heightInsideMargins = drawRect.height - topmargin - bottommargin; if (heightInsideMargins < kMinHeight) heightInsideMargins = kMinHeight; float denum = max - min; if (denum < kMinHeight) denum = kMinHeight; if (m_UpDirection == YDirection.Positive) { m_Scale.y = -heightInsideMargins / denum; m_Translation.y = drawRect.height - min * m_Scale.y - topmargin; } else { m_Scale.y = heightInsideMargins / denum; m_Translation.y = -min * m_Scale.y - bottommargin; } EnforceScaleAndRange(); } public void SetShownVRange(float min, float max) { float denum = max - min; if (denum < kMinHeight) denum = kMinHeight; if (m_UpDirection == YDirection.Positive) { m_Scale.y = -drawRect.height / denum; m_Translation.y = drawRect.height - min * m_Scale.y; } else { m_Scale.y = drawRect.height / denum; m_Translation.y = -min * m_Scale.y; } EnforceScaleAndRange(); } // ShownArea is in curve space public Rect shownArea { set { float width = (value.width < m_MinWidth) ? m_MinWidth : value.width; float height = (value.height < kMinHeight) ? kMinHeight : value.height; if (m_UpDirection == YDirection.Positive) { m_Scale.x = drawRect.width / width; m_Scale.y = -drawRect.height / height; m_Translation.x = -value.x * m_Scale.x; m_Translation.y = drawRect.height - value.y * m_Scale.y; } else { m_Scale.x = drawRect.width / width; m_Scale.y = drawRect.height / height; m_Translation.x = -value.x * m_Scale.x; m_Translation.y = -value.y * m_Scale.y; } EnforceScaleAndRange(); } get { if (m_UpDirection == YDirection.Positive) { return new Rect( -m_Translation.x / m_Scale.x, -(m_Translation.y - drawRect.height) / m_Scale.y, drawRect.width / m_Scale.x, drawRect.height / -m_Scale.y ); } else { return new Rect( -m_Translation.x / m_Scale.x, -m_Translation.y / m_Scale.y, drawRect.width / m_Scale.x, drawRect.height / m_Scale.y ); } } } public Rect shownAreaInsideMargins { set { shownAreaInsideMarginsInternal = value; EnforceScaleAndRange(); } get { return shownAreaInsideMarginsInternal; } } private Rect shownAreaInsideMarginsInternal { set { float width = (value.width < m_MinWidth) ? m_MinWidth : value.width; float height = (value.height < kMinHeight) ? kMinHeight : value.height; float widthInsideMargins = drawRect.width - leftmargin - rightmargin; if (widthInsideMargins < m_MinWidth) widthInsideMargins = m_MinWidth; float heightInsideMargins = drawRect.height - topmargin - bottommargin; if (heightInsideMargins < kMinHeight) heightInsideMargins = kMinHeight; if (m_UpDirection == YDirection.Positive) { m_Scale.x = widthInsideMargins / width; m_Scale.y = -heightInsideMargins / height; m_Translation.x = -value.x * m_Scale.x + leftmargin; m_Translation.y = drawRect.height - value.y * m_Scale.y - topmargin; } else { m_Scale.x = widthInsideMargins / width; m_Scale.y = heightInsideMargins / height; m_Translation.x = -value.x * m_Scale.x + leftmargin; m_Translation.y = -value.y * m_Scale.y + topmargin; } } get { float leftmarginRel = leftmargin / m_Scale.x; float rightmarginRel = rightmargin / m_Scale.x; float topmarginRel = topmargin / m_Scale.y; float bottommarginRel = bottommargin / m_Scale.y; Rect area = shownArea; area.x += leftmarginRel; area.y -= topmarginRel; area.width -= leftmarginRel + rightmarginRel; area.height += topmarginRel + bottommarginRel; return area; } } float GetWidthInsideMargins(float widthWithMargins, bool substractSliderWidth = false) { float width = (widthWithMargins < m_MinWidth) ? m_MinWidth : widthWithMargins; float widthInsideMargins = width - leftmargin - rightmargin - (substractSliderWidth ? (m_VSlider ? styles.visualSliderWidth : 0) : 0); return Mathf.Max(widthInsideMargins, m_MinWidth); } float GetHeightInsideMargins(float heightWithMargins, bool substractSliderHeight = false) { float height = (heightWithMargins < kMinHeight) ? kMinHeight : heightWithMargins; float heightInsideMargins = height - topmargin - bottommargin - (substractSliderHeight ? (m_HSlider ? styles.visualSliderWidth : 0) : 0); return Mathf.Max(heightInsideMargins, kMinHeight); } public virtual Bounds drawingBounds { get { return new Bounds( new Vector3((hBaseRangeMin + hBaseRangeMax) * 0.5f, (vBaseRangeMin + vBaseRangeMax) * 0.5f, 0), new Vector3(hBaseRangeMax - hBaseRangeMin, vBaseRangeMax - vBaseRangeMin, 1) ); } } // Utility transform functions public Matrix4x4 drawingToViewMatrix { get { return Matrix4x4.TRS(m_Translation, Quaternion.identity, new Vector3(m_Scale.x, m_Scale.y, 1)); } } public Vector2 DrawingToViewTransformPoint(Vector2 lhs) { return new Vector2(lhs.x * m_Scale.x + m_Translation.x, lhs.y * m_Scale.y + m_Translation.y); } public Vector3 DrawingToViewTransformPoint(Vector3 lhs) { return new Vector3(lhs.x * m_Scale.x + m_Translation.x, lhs.y * m_Scale.y + m_Translation.y, 0); } public Vector2 ViewToDrawingTransformPoint(Vector2 lhs) { return new Vector2((lhs.x - m_Translation.x) / m_Scale.x , (lhs.y - m_Translation.y) / m_Scale.y); } public Vector3 ViewToDrawingTransformPoint(Vector3 lhs) { return new Vector3((lhs.x - m_Translation.x) / m_Scale.x , (lhs.y - m_Translation.y) / m_Scale.y, 0); } public Vector2 DrawingToViewTransformVector(Vector2 lhs) { return new Vector2(lhs.x * m_Scale.x, lhs.y * m_Scale.y); } public Vector3 DrawingToViewTransformVector(Vector3 lhs) { return new Vector3(lhs.x * m_Scale.x, lhs.y * m_Scale.y, 0); } public Vector2 ViewToDrawingTransformVector(Vector2 lhs) { return new Vector2(lhs.x / m_Scale.x, lhs.y / m_Scale.y); } public Vector3 ViewToDrawingTransformVector(Vector3 lhs) { return new Vector3(lhs.x / m_Scale.x, lhs.y / m_Scale.y, 0); } public Vector2 mousePositionInDrawing { get { return ViewToDrawingTransformPoint(Event.current.mousePosition); } } public Vector2 NormalizeInViewSpace(Vector2 vec) { vec = Vector2.Scale(vec, m_Scale); vec /= vec.magnitude; return Vector2.Scale(vec, new Vector2(1 / m_Scale.x, 1 / m_Scale.y)); } // Utility mouse event functions private bool IsZoomEvent() { return ( (Event.current.button == 1 && Event.current.alt) // right+alt drag //|| (Event.current.button == 0 && Event.current.command) // left+commend drag //|| (Event.current.button == 2 && Event.current.command) // middle+command drag ); } private bool IsPanEvent() { return ( (Event.current.button == 0 && Event.current.alt) // left+alt drag || (Event.current.button == 2 && !Event.current.command) // middle drag ); } public ZoomableArea() { m_MinimalGUI = false; } public ZoomableArea(bool minimalGUI) { m_MinimalGUI = minimalGUI; } public ZoomableArea(bool minimalGUI, bool enableSliderZoom) : this(minimalGUI, enableSliderZoom, enableSliderZoom) {} public ZoomableArea(bool minimalGUI, bool enableSliderZoomHorizontal, bool enableSliderZoomVertical) { m_MinimalGUI = minimalGUI; m_EnableSliderZoomHorizontal = enableSliderZoomHorizontal; m_EnableSliderZoomVertical = enableSliderZoomVertical; } public void BeginViewGUI() { if (styles.horizontalScrollbar == null) styles.InitGUIStyles(m_MinimalGUI, allowSliderZoomHorizontal, allowSliderZoomVertical); if (enableMouseInput) HandleZoomAndPanEvents(m_DrawArea); horizontalScrollbarID = GUIUtility.GetControlID(EditorGUIExt.s_MinMaxSliderHash, FocusType.Passive); verticalScrollbarID = GUIUtility.GetControlID(EditorGUIExt.s_MinMaxSliderHash, FocusType.Passive); if (!m_MinimalGUI || Event.current.type != EventType.Repaint) SliderGUI(); } public void HandleZoomAndPanEvents(Rect area) { GUILayout.BeginArea(area); area.x = 0; area.y = 0; int id = GUIUtility.GetControlID(zoomableAreaHash, FocusType.Passive, area); areaControlID = id; switch (Event.current.GetTypeForControl(id)) { case EventType.MouseDown: if (area.Contains(Event.current.mousePosition)) { // Catch keyboard control when clicked inside zoomable area // (used to restrict scrollwheel) GUIUtility.keyboardControl = id; if (IsZoomEvent() || IsPanEvent()) { GUIUtility.hotControl = id; m_MouseDownPosition = mousePositionInDrawing; Event.current.Use(); } } break; case EventType.MouseUp: //Debug.Log("mouse-up!"); if (GUIUtility.hotControl == id) { GUIUtility.hotControl = 0; // If we got the mousedown, the mouseup is ours as well // (no matter if the click was in the area or not) m_MouseDownPosition = new Vector2(-1000000, -1000000); //Event.current.Use(); } break; case EventType.MouseDrag: if (GUIUtility.hotControl != id) break; if (IsZoomEvent()) { // Zoom in around mouse down position HandleZoomEvent(m_MouseDownPosition, false); Event.current.Use(); } else if (IsPanEvent()) { // Pan view Pan(); Event.current.Use(); } break; case EventType.ScrollWheel: if (!area.Contains(Event.current.mousePosition)) { HandleScrolling(area); break; } if (m_IgnoreScrollWheelUntilClicked && GUIUtility.keyboardControl != id) break; // Zoom in around cursor position HandleZoomEvent(mousePositionInDrawing, true); Event.current.Use(); break; } GUILayout.EndArea(); } void HandleScrolling(Rect area) { if (m_MinimalGUI) return; if (m_VSlider && new Rect(area.x + area.width, area.y + GUI.skin.verticalScrollbarUpButton.fixedHeight, vSliderWidth, area.height - (GUI.skin.verticalScrollbarDownButton.fixedHeight + hSliderHeight)).Contains(Event.current.mousePosition)) { SetTransform(new Vector2(m_Translation.x, m_Translation.y - (Event.current.delta.y * k_ScrollStepSize)), m_Scale); Event.current.Use(); return; } if (m_HSlider && new Rect(area.x + GUI.skin.horizontalScrollbarLeftButton.fixedWidth, area.y + area.height, area.width - (GUI.skin.horizontalScrollbarRightButton.fixedWidth + vSliderWidth), hSliderHeight).Contains(Event.current.mousePosition)) { SetTransform(new Vector2(m_Translation.x + (Event.current.delta.y * k_ScrollStepSize), m_Translation.y), m_Scale); Event.current.Use(); } } public void EndViewGUI() { if (m_MinimalGUI && Event.current.type == EventType.Repaint) SliderGUI(); } void SliderGUI() { if (!m_HSlider && !m_VSlider) return; using (new EditorGUI.DisabledScope(!enableMouseInput)) { Bounds editorBounds = drawingBounds; Rect area = shownAreaInsideMargins; float min, max; float inset = styles.sliderWidth - styles.visualSliderWidth; float otherInset = (vSlider && hSlider) ? inset : 0; Vector2 scaleDelta = m_Scale; // Horizontal range slider if (m_HSlider) { Rect hRangeSliderRect = new Rect(drawRect.x + 1, drawRect.yMax - inset, drawRect.width - otherInset, styles.sliderWidth); float shownXRange = area.width; float shownXMin = area.xMin; if (allowSliderZoomHorizontal) { EditorGUIExt.MinMaxScroller(hRangeSliderRect, horizontalScrollbarID, ref shownXMin, ref shownXRange, editorBounds.min.x, editorBounds.max.x, Mathf.NegativeInfinity, Mathf.Infinity, styles.horizontalScrollbar, styles.horizontalMinMaxScrollbarThumb, styles.horizontalScrollbarLeftButton, styles.horizontalScrollbarRightButton, true); } else { shownXMin = GUI.Scroller(hRangeSliderRect, shownXMin, shownXRange, editorBounds.min.x, editorBounds.max.x, styles.horizontalScrollbar, styles.horizontalMinMaxScrollbarThumb, styles.horizontalScrollbarLeftButton, styles.horizontalScrollbarRightButton, true); } min = shownXMin; max = shownXMin + shownXRange; float rectWidthWithinMargins = GetWidthInsideMargins(rect.width, true); if (min > area.xMin) min = Mathf.Min(min, max - rectWidthWithinMargins / m_HScaleMax); if (max < area.xMax) max = Mathf.Max(max, min + rectWidthWithinMargins / m_HScaleMax); SetShownHRangeInsideMargins(min, max); } // Vertical range slider // Reverse y values since y increses upwards for the draw area but downwards for the slider if (m_VSlider) { if (m_UpDirection == YDirection.Positive) { Rect vRangeSliderRect = new Rect(drawRect.xMax - inset, drawRect.y, styles.sliderWidth, drawRect.height - otherInset); float shownYRange = area.height; float shownYMin = -area.yMax; if (allowSliderZoomVertical) { EditorGUIExt.MinMaxScroller(vRangeSliderRect, verticalScrollbarID, ref shownYMin, ref shownYRange, -editorBounds.max.y, -editorBounds.min.y, Mathf.NegativeInfinity, Mathf.Infinity, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } else { shownYMin = GUI.Scroller(vRangeSliderRect, shownYMin, shownYRange, -editorBounds.max.y, -editorBounds.min.y, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } min = -(shownYMin + shownYRange); max = -shownYMin; float rectHeightWithinMargins = GetHeightInsideMargins(rect.height, true); if (min > area.yMin) min = Mathf.Min(min, max - rectHeightWithinMargins / m_VScaleMax); if (max < area.yMax) max = Mathf.Max(max, min + rectHeightWithinMargins / m_VScaleMax); SetShownVRangeInsideMargins(min, max); } else { Rect vRangeSliderRect = new Rect(drawRect.xMax - inset, drawRect.y, styles.sliderWidth, drawRect.height - otherInset); float shownYRange = area.height; float shownYMin = area.yMin; if (allowSliderZoomVertical) { EditorGUIExt.MinMaxScroller(vRangeSliderRect, verticalScrollbarID, ref shownYMin, ref shownYRange, editorBounds.min.y, editorBounds.max.y, Mathf.NegativeInfinity, Mathf.Infinity, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } else { shownYMin = GUI.Scroller(vRangeSliderRect, shownYMin, shownYRange, editorBounds.min.y, editorBounds.max.y, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } min = shownYMin; max = shownYMin + shownYRange; float rectHeightWithinMargins = GetHeightInsideMargins(rect.height, true); if (min > area.yMin) min = Mathf.Min(min, max - rectHeightWithinMargins / m_VScaleMax); if (max < area.yMax) max = Mathf.Max(max, min + rectHeightWithinMargins / m_VScaleMax); SetShownVRangeInsideMargins(min, max); } } if (uniformScale) { float aspect = drawRect.width / drawRect.height; scaleDelta -= m_Scale; var delta = new Vector2(-scaleDelta.y * aspect, -scaleDelta.x / aspect); m_Scale -= delta; m_Translation.x -= scaleDelta.y / 2; m_Translation.y -= scaleDelta.x / 2; EnforceScaleAndRange(); } } } private void Pan() { if (!m_HRangeLocked) m_Translation.x += Event.current.delta.x; if (!m_VRangeLocked) m_Translation.y += Event.current.delta.y; EnforceScaleAndRange(); } private void HandleZoomEvent(Vector2 zoomAround, bool scrollwhell) { // Get delta (from scroll wheel or mouse pad) // Add x and y delta to cover all cases // (scrool view has only y or only x when shift is pressed, // while mouse pad has both x and y at all times) float delta = Event.current.delta.x + Event.current.delta.y; if (scrollwhell) delta = -delta; // Scale multiplier. Don't allow scale of zero or below! float scale = Mathf.Max(0.01F, 1 + delta * 0.01F); // Cap scale when at min width to not "glide" away when zooming closer float width = shownAreaInsideMargins.width; if (width / scale <= m_MinWidth) return; SetScaleFocused(zoomAround, scale * m_Scale, Event.current.shift, EditorGUI.actionKey); } // Sets a new scale, keeping focalPoint in the same relative position public void SetScaleFocused(Vector2 focalPoint, Vector2 newScale) { SetScaleFocused(focalPoint, newScale, false, false); } public void SetScaleFocused(Vector2 focalPoint, Vector2 newScale, bool lockHorizontal, bool lockVertical) { if (uniformScale) lockHorizontal = lockVertical = false; else { // if an axis is locked by default, it is as if that modifier key is permanently held down // actually pressing the key then lifts the lock. In other words, LockedByDefault acts like an inversion. if (hZoomLockedByDefault) lockHorizontal = !lockHorizontal; if (hZoomLockedByDefault) lockVertical = !lockVertical; } if (!m_HRangeLocked && !lockHorizontal) { // Offset to make zoom centered around cursor position m_Translation.x -= focalPoint.x * (newScale.x - m_Scale.x); // Apply zooming m_Scale.x = newScale.x; } if (!m_VRangeLocked && !lockVertical) { // Offset to make zoom centered around cursor position m_Translation.y -= focalPoint.y * (newScale.y - m_Scale.y); // Apply zooming m_Scale.y = newScale.y; } EnforceScaleAndRange(); } public void SetTransform(Vector2 newTranslation, Vector2 newScale) { m_Scale = newScale; m_Translation = newTranslation; EnforceScaleAndRange(); } public void EnforceScaleAndRange() { Rect oldArea = m_LastShownAreaInsideMargins; Rect newArea = shownAreaInsideMargins; if (newArea == oldArea) return; float minChange = 0.01f; if (!Mathf.Approximately(newArea.width, oldArea.width)) { float constrainedWidth = newArea.width; if (newArea.width < oldArea.width) { // The shown area decreasing in size means the scale is increasing. This happens e.g. while zooming in. // Only the max scale restricts the shown area size here, range has no influence. constrainedWidth = GetWidthInsideMargins(drawRect.width / m_HScaleMax, false); } else { constrainedWidth = GetWidthInsideMargins(drawRect.width / m_HScaleMin, false); if (hRangeMax != Mathf.Infinity && hRangeMin != Mathf.NegativeInfinity) { // range only has an influence if it is enforced, i.e. not infinity float denum = hRangeMax - hRangeMin; if (denum < m_MinWidth) denum = m_MinWidth; constrainedWidth = Mathf.Min(constrainedWidth, denum); } } float xLerp = Mathf.InverseLerp(oldArea.width, newArea.width, constrainedWidth); float newWidth = Mathf.Lerp(oldArea.width, newArea.width, xLerp); float widthChange = Mathf.Abs(newWidth - newArea.width); newArea = new Rect( // only affect the position if there was any significant change in width // this fixes an issue where if width was only different due to rounding issues, position changes are ignored as xLerp comes back 0 (or very nearly 0) widthChange > minChange ? Mathf.Lerp(oldArea.x, newArea.x, xLerp) : newArea.x, newArea.y, newWidth, newArea.height ); } if (!Mathf.Approximately(newArea.height, oldArea.height)) { float constrainedHeight = newArea.height; if (newArea.height < oldArea.height) { // The shown area decreasing in size means the scale is increasing. This happens e.g. while zooming in. // Only the max scale restricts the shown area size here, range has no influence. constrainedHeight = GetHeightInsideMargins(drawRect.height / m_VScaleMax, false); } else { constrainedHeight = GetHeightInsideMargins(drawRect.height / m_VScaleMin, false); if (vRangeMax != Mathf.Infinity && vRangeMin != Mathf.NegativeInfinity) { // range only has an influence if it is enforced, i.e. not infinity float denum = vRangeMax - vRangeMin; if (denum < kMinHeight) denum = kMinHeight; constrainedHeight = Mathf.Min(constrainedHeight, denum); } } float yLerp = Mathf.InverseLerp(oldArea.height, newArea.height, constrainedHeight); float newHeight = Mathf.Lerp(oldArea.height, newArea.height, yLerp); float heightChange = Mathf.Abs(newHeight - newArea.height); newArea = new Rect( newArea.x, // only affect the position if there was any significant change in height // this fixes an issue where if height was only different due to rounding issues, position changes are ignored as yLerp comes back 0 (or very nearly 0) heightChange > minChange ? Mathf.Lerp(oldArea.y, newArea.y, yLerp) : newArea.y, newArea.width, newHeight ); } // Enforce ranges if (newArea.xMin < hRangeMin) newArea.x = hRangeMin; if (newArea.xMax > hRangeMax) newArea.x = hRangeMax - newArea.width; if (newArea.yMin < vRangeMin) newArea.y = vRangeMin; if (newArea.yMax > vRangeMax) newArea.y = vRangeMax - newArea.height; shownAreaInsideMarginsInternal = newArea; m_LastShownAreaInsideMargins = shownAreaInsideMargins; } public float PixelToTime(float pixelX, Rect rect) { Rect area = shownArea; return ((pixelX - rect.x) * area.width / rect.width + area.x); } public float TimeToPixel(float time, Rect rect) { Rect area = shownArea; return (time - area.x) / area.width * rect.width + rect.x; } public float PixelDeltaToTime(Rect rect) { return shownArea.width / rect.width; } public void UpdateZoomScale(float fMaxScaleValue, float fMinScaleValue) { // Update/reset the values of the scale to new zoom range, if the current values do not fall in the range of the new resolution if (m_Scale.y > fMaxScaleValue || m_Scale.y < fMinScaleValue) { m_Scale.y = m_Scale.y > fMaxScaleValue ? fMaxScaleValue : fMinScaleValue; } if (m_Scale.x > fMaxScaleValue || m_Scale.x < fMinScaleValue) { m_Scale.x = m_Scale.x > fMaxScaleValue ? fMaxScaleValue : fMinScaleValue; } } } } // namespace ================================================ FILE: Editor/Mono/AnimationCurvePreviewCache.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using UnityEditor; namespace UnityEditorInternal { [NativeHeader("Editor/Src/AnimationCurvePreviewCache.bindings.h")] [NativeHeader("Editor/Src/Utility/SerializedProperty.h")] [NativeHeader("Runtime/Graphics/Texture2D.h")] [StaticAccessor("AnimationCurvePreviewCacheBindings", StaticAccessorType.DoubleColon)] internal class AnimationCurvePreviewCache { // Regions as SerializedProperty public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, SerializedProperty property2, Color color, Rect curveRanges) { return GetPreview(previewWidth, previewHeight, property, property2, color, Color.clear, Color.clear); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, SerializedProperty property2, Color color, Color topFillColor, Color bottomFillColor, Rect curveRanges) { if (property2 == null) return GetPropertyPreviewFilled(previewWidth, previewHeight, true, curveRanges, property, color, topFillColor, bottomFillColor); else return GetPropertyPreviewRegionFilled(previewWidth, previewHeight, true, curveRanges, property, property2, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, SerializedProperty property2, Color color) { return GetPreview(previewWidth, previewHeight, property, property2, color, Color.clear, Color.clear); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, SerializedProperty property2, Color color, Color topFillColor, Color bottomFillColor) { if (property2 == null) return GetPropertyPreviewFilled(previewWidth, previewHeight, false, new Rect(), property, color, topFillColor, bottomFillColor); else return GetPropertyPreviewRegionFilled(previewWidth, previewHeight, false, new Rect(), property, property2, color, topFillColor, bottomFillColor); } // Regions as AnimationCurves public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, AnimationCurve curve2, Color color, Color topFillColor, Color bottomFillColor, Rect curveRanges) { return GetCurvePreviewRegionFilled(previewWidth, previewHeight, true, curveRanges, curve, curve2, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, AnimationCurve curve2, Color color, Rect curveRanges) { return GetPreview(previewWidth, previewHeight, curve, curve2, color, Color.clear, Color.clear, curveRanges); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, AnimationCurve curve2, Color color, Color topFillColor, Color bottomFillColor) { return GetCurvePreviewRegionFilled(previewWidth, previewHeight, false, new Rect(), curve, curve2, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, AnimationCurve curve2, Color color) { return GetPreview(previewWidth, previewHeight, curve, curve2, color, Color.clear, Color.clear, new Rect()); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, Color color, Color topFillColor, Color bottomFillColor, Rect curveRanges) { return GetPropertyPreviewFilled(previewWidth, previewHeight, true, curveRanges, property, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, Color color, Rect curveRanges) { return GetPreview(previewWidth, previewHeight, property, color, Color.clear, Color.clear, curveRanges); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, Color color, Color topFillColor, Color bottomFillColor) { return GetPropertyPreviewFilled(previewWidth, previewHeight, false, new Rect(), property, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, SerializedProperty property, Color color) { return GetPreview(previewWidth, previewHeight, property, color, Color.clear, Color.clear, new Rect()); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, Color color, Color topFillColor, Color bottomFillColor, Rect curveRanges) { return GetCurvePreviewFilled(previewWidth, previewHeight, true, curveRanges, curve, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, Color color, Rect curveRanges) { return GetPreview(previewWidth, previewHeight, curve, color, Color.clear, Color.clear, curveRanges); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, Color color, Color topFillColor, Color bottomFillColor) { return GetCurvePreviewFilled(previewWidth, previewHeight, false, new Rect(), curve, color, topFillColor, bottomFillColor); } public static Texture2D GetPreview(int previewWidth, int previewHeight, AnimationCurve curve, Color color) { return GetPreview(previewWidth, previewHeight, curve, color, Color.clear, Color.clear, new Rect()); } public static extern Texture2D GenerateCurvePreview(int previewWidth, int previewHeight, Rect curveRanges, AnimationCurve curve, Color color, Texture2D existingTexture); internal extern static void ClearCache(); private extern static Texture2D GetPropertyPreview(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, SerializedProperty property, Color color); private extern static Texture2D GetPropertyPreviewFilled(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, SerializedProperty property, Color color, Color topFillColor, Color bottomFillColor); private extern static Texture2D GetPropertyPreviewRegion(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, SerializedProperty property, SerializedProperty property2, Color color); private extern static Texture2D GetPropertyPreviewRegionFilled(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, SerializedProperty property, SerializedProperty property2, Color color, Color topFillColor, Color bottomFillColor); private extern static Texture2D GetCurvePreview(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, AnimationCurve curve, Color color); private extern static Texture2D GetCurvePreviewFilled(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, AnimationCurve curve, Color color, Color topFillColor, Color bottomFillColor); private extern static Texture2D GetCurvePreviewRegion(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, AnimationCurve curve, AnimationCurve curve2, Color color); private extern static Texture2D GetCurvePreviewRegionFilled(int previewWidth, int previewHeight, bool useCurveRanges, Rect curveRanges, AnimationCurve curve, AnimationCurve curve2, Color color, Color topFillColor, Color bottomFillColor); } } ================================================ FILE: Editor/Mono/AnimatorController.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Animations; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngineInternal; using UnityEditor; using System.Runtime.InteropServices; namespace UnityEditor.Animations { [NativeHeader("Modules/Animation/Animator.h")] [NativeHeader("Editor/Src/Animation/StateMachineBehaviourScripting.h")] [NativeHeader("Editor/Src/Animation/AnimatorController.bindings.h")] [NativeHeader("Modules/Animation/AnimatorController.h")] public partial class AnimatorController : RuntimeAnimatorController { public AnimatorController() { Internal_Create(this); } [FreeFunction("AnimatorControllerBindings::Internal_Create")] extern private static void Internal_Create([Writable] AnimatorController self); extern public AnimatorControllerLayer[] layers { [FreeFunction(Name = "AnimatorControllerBindings::GetLayers", HasExplicitThis = true)] get; [FreeFunction(Name = "AnimatorControllerBindings::SetLayers", HasExplicitThis = true, ThrowsException = true)] [param: Unmarshalled] set; } extern public AnimatorControllerParameter[] parameters { [FreeFunction(Name = "AnimatorControllerBindings::GetParameters", HasExplicitThis = true)] get; [FreeFunction(Name = "AnimatorControllerBindings::SetParameters", HasExplicitThis = true, ThrowsException = true)] [param: Unmarshalled] set; } [FreeFunction(Name = "AnimatorControllerBindings::GetEffectiveAnimatorController")] extern internal static AnimatorController GetEffectiveAnimatorController(Animator animator); internal static AnimatorControllerPlayable FindAnimatorControllerPlayable(Animator animator, AnimatorController controller) { PlayableHandle handle = new PlayableHandle(); Internal_FindAnimatorControllerPlayable(ref handle, animator, controller); if (!handle.IsValid()) return AnimatorControllerPlayable.Null; return new AnimatorControllerPlayable(handle); } [FreeFunction(Name = "AnimatorControllerBindings::Internal_FindAnimatorControllerPlayable")] extern internal static void Internal_FindAnimatorControllerPlayable(ref PlayableHandle ret, Animator animator, AnimatorController controller); public static void SetAnimatorController(Animator animator, AnimatorController controller) { animator.runtimeAnimatorController = controller; } extern internal int IndexOfParameter(string name); extern internal void RenameParameter(string prevName, string newName); extern public string MakeUniqueParameterName(string name); extern public string MakeUniqueLayerName(string name); static public StateMachineBehaviourContext[] FindStateMachineBehaviourContext(StateMachineBehaviour behaviour) { return Internal_FindStateMachineBehaviourContext(behaviour); } [FreeFunction("FindStateMachineBehaviourContext")] extern internal static StateMachineBehaviourContext[] Internal_FindStateMachineBehaviourContext(ScriptableObject behaviour); [FreeFunction("AnimatorControllerBindings::Internal_CreateStateMachineBehaviour")] extern public static int CreateStateMachineBehaviour(MonoScript script); [FreeFunction("AnimatorControllerBindings::CanAddStateMachineBehaviours")] extern internal static bool CanAddStateMachineBehaviours(); extern internal MonoScript GetBehaviourMonoScript(AnimatorState state, int layerIndex, int behaviourIndex); [FreeFunction] extern private static ScriptableObject ScriptingAddStateMachineBehaviourWithType(Type stateMachineBehaviourType, [NotNull] AnimatorController controller, [NotNull] AnimatorState state, int layerIndex); [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)] public StateMachineBehaviour AddEffectiveStateMachineBehaviour(Type stateMachineBehaviourType, AnimatorState state, int layerIndex) { return (StateMachineBehaviour)ScriptingAddStateMachineBehaviourWithType(stateMachineBehaviourType, this, state, layerIndex); } public T AddEffectiveStateMachineBehaviour(AnimatorState state, int layerIndex) where T : StateMachineBehaviour { return AddEffectiveStateMachineBehaviour(typeof(T), state, layerIndex) as T; } public T[] GetBehaviours() where T : StateMachineBehaviour { return ConvertStateMachineBehaviour(InternalGetBehaviours(typeof(T))); } [FreeFunction(Name = "AnimatorControllerBindings::Internal_GetBehaviours", HasExplicitThis = true)] extern internal ScriptableObject[] InternalGetBehaviours([NotNull] Type type); internal static T[] ConvertStateMachineBehaviour(ScriptableObject[] rawObjects) where T : StateMachineBehaviour { if (rawObjects == null) return null; T[] typedObjects = new T[rawObjects.Length]; for (int i = 0; i < typedObjects.Length; i++) typedObjects[i] = (T)rawObjects[i]; return typedObjects; } extern internal UnityEngine.Object[] CollectObjectsUsingParameter(string parameterName); internal extern bool isAssetBundled { [NativeName("IsAssetBundled")] get; } extern internal void AddStateEffectiveBehaviour([NotNull] AnimatorState state, int layerIndex, int instanceID); extern internal void RemoveStateEffectiveBehaviour([NotNull] AnimatorState state, int layerIndex, int behaviourIndex); [FreeFunction(Name = "AnimatorControllerBindings::Internal_GetEffectiveBehaviours", HasExplicitThis = true)] extern internal ScriptableObject[] Internal_GetEffectiveBehaviours([NotNull] AnimatorState state, int layerIndex); [FreeFunction(Name = "AnimatorControllerBindings::Internal_SetEffectiveBehaviours", HasExplicitThis = true)] extern internal void Internal_SetEffectiveBehaviours([NotNull] AnimatorState state, int layerIndex, [Unmarshalled] ScriptableObject[] behaviours); } } ================================================ FILE: Editor/Mono/AnimatorControllerLayer.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEditor; using System.Runtime.InteropServices; namespace UnityEditor.Animations { public enum AnimatorLayerBlendingMode { Override = 0, Additive = 1, } [NativeHeader("Editor/Src/Animation/AnimatorControllerLayer.h")] [NativeHeader("Editor/Src/Animation/AnimatorControllerLayer.bindings.h")] [StructLayout(LayoutKind.Sequential)] [NativeType(CodegenOptions.Custom, "MonoStateMotionPair")] internal struct StateMotionPair { public AnimatorState m_State; public Motion m_Motion; } [NativeHeader("Editor/Src/Animation/AnimatorControllerLayer.h")] [NativeHeader("Editor/Src/Animation/AnimatorControllerLayer.bindings.h")] [StructLayout(LayoutKind.Sequential)] [NativeType(CodegenOptions.Custom, "MonoStateBehavioursPair")] internal struct StateBehavioursPair { public AnimatorState m_State; public ScriptableObject[] m_Behaviours; } [NativeHeader("Editor/Src/Animation/AnimatorControllerLayer.h")] [NativeHeader("Editor/Src/Animation/AnimatorControllerLayer.bindings.h")] [StructLayout(LayoutKind.Sequential)] [NativeAsStruct] [NativeType(CodegenOptions.Custom, "MonoAnimatorControllerLayer")] public partial class AnimatorControllerLayer { public string name { get { return m_Name; } set { m_Name = value; } } public AnimatorStateMachine stateMachine { get { return m_StateMachine; } set { m_StateMachine = value; } } public AvatarMask avatarMask { get { return m_AvatarMask; } set { m_AvatarMask = value; } } public AnimatorLayerBlendingMode blendingMode { get { return m_BlendingMode; } set { m_BlendingMode = value; } } public int syncedLayerIndex { get { return m_SyncedLayerIndex; } set { m_SyncedLayerIndex = value; } } public bool iKPass { get { return m_IKPass; } set { m_IKPass = value; } } public float defaultWeight { get { return m_DefaultWeight; } set { m_DefaultWeight = value; } } public bool syncedLayerAffectsTiming { get { return m_SyncedLayerAffectsTiming; } set { m_SyncedLayerAffectsTiming = value; }} public Motion GetOverrideMotion(AnimatorState state) { if (m_Motions != null) foreach (StateMotionPair pair in m_Motions) if (pair.m_State == state) return pair.m_Motion; return null; } public void SetOverrideMotion(AnimatorState state, Motion motion) { if (m_Motions == null) m_Motions = new StateMotionPair[] {}; for (int i = 0; i < m_Motions.Length; ++i) { if (m_Motions[i].m_State == state) { m_Motions[i].m_Motion = motion; return; } } StateMotionPair newPair; newPair.m_State = state; newPair.m_Motion = motion; ArrayUtility.Add(ref m_Motions, newPair); } public StateMachineBehaviour[] GetOverrideBehaviours(AnimatorState state) { if (m_Behaviours != null) { foreach (StateBehavioursPair pair in m_Behaviours) { if (pair.m_State == state) return pair.m_Behaviours as StateMachineBehaviour[]; } } return new StateMachineBehaviour[0]; } public void SetOverrideBehaviours(AnimatorState state, StateMachineBehaviour[] behaviours) { if (m_Behaviours == null) m_Behaviours = new StateBehavioursPair[] {}; for (int i = 0; i < m_Behaviours.Length; ++i) { if (m_Behaviours[i].m_State == state) { m_Behaviours[i].m_Behaviours = behaviours; return; } } StateBehavioursPair newPair; newPair.m_State = state; newPair.m_Behaviours = behaviours; ArrayUtility.Add(ref m_Behaviours, newPair); } string m_Name; AnimatorStateMachine m_StateMachine; AvatarMask m_AvatarMask; StateMotionPair[] m_Motions; StateBehavioursPair[] m_Behaviours; AnimatorLayerBlendingMode m_BlendingMode; int m_SyncedLayerIndex = -1; bool m_IKPass; float m_DefaultWeight; bool m_SyncedLayerAffectsTiming; } } ================================================ FILE: Editor/Mono/Annotation/AnnotationUtility.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine.Bindings; namespace UnityEditor { [NativeType(CodegenOptions.Custom, "AnnotationBindings")] struct Annotation { public int iconEnabled; public int gizmoEnabled; public int flags; public int classID; public string scriptClass; } [NativeHeader("Editor/Mono/Annotation/AnnotationUtility.bindings.h")] [NativeHeader("Editor/Src/Gizmos/AnnotationManager.h")] static class AnnotationUtility { // Similar values as in Annotation (in AnnotationManager.h) public enum Flags { kHasIcon = 1 << 0, kHasGizmo = 1 << 1, kIsDisabled = 1 << 2 }; extern internal static Annotation[] GetAnnotations(); extern internal static Annotation[] GetRecentlyChangedAnnotations(); extern internal static Annotation GetAnnotation(int classID, string scriptClass); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] extern internal static string GetNameOfCurrentSetup(); extern internal static void SetGizmoEnabled(int classID, string scriptClass, int gizmoEnabled, bool addToMostRecentChanged); extern internal static void SetIconEnabled(int classID, string scriptClass, int iconEnabled); [StaticAccessor("GizmoManager::Get()", StaticAccessorType.Dot)] extern internal static int SetGizmosDirty(); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] extern internal static string[] GetPresetList(); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] extern internal static void LoadPreset(string presetName); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] extern internal static void SavePreset(string presetName); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] extern internal static void DeletePreset(string presetName); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] [NativeMethod("ResetPresetsToFactorySettings")] extern internal static void ResetToFactorySettings(); [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] [NativeMethod("3dGizmosEnabled")] internal extern static bool use3dGizmos { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] // Thomas Tu: 2019-06-20. Will be marked as Obsolete. // We need to deal with code dependency in packages first. internal extern static bool showGrid { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] internal extern static bool showSelectionOutline { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] internal extern static bool showSelectionWire { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] internal extern static float iconSize { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] internal extern static float fadeGizmoSize { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] internal extern static bool fadeGizmos { get; set; } [StaticAccessor("GetAnnotationManager()", StaticAccessorType.Dot)] internal extern static bool useInspectorExpandedState { get; set; } } } ================================================ FILE: Editor/Mono/Annotation/AnnotationWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; using UnityEditor.ShortcutManagement; namespace UnityEditor { internal class AnnotationWindow : EditorWindow { private static SavedBool s_ShowTerrainDebugWarnings = new SavedBool("Terrain.ShowDebugWarnings", true); public static bool ShowTerrainDebugWarnings { get => s_ShowTerrainDebugWarnings.value; set => s_ShowTerrainDebugWarnings.value = value; } class Styles { public GUIStyle toggle = "Toggle"; public GUIStyle toggleMixed = EditorStyles.toggleMixed; public GUIStyle listEvenBg = "ObjectPickerResultsOdd";//"ObjectPickerResultsEven";// public GUIStyle listOddBg = "ObjectPickerResultsEven";//"ObjectPickerResultsEven";// public GUIStyle background = "grey_border"; public GUIStyle seperator = "sv_iconselector_sep"; public GUIStyle iconDropDown = "IN dropdown"; public GUIStyle listTextStyle; public GUIStyle listHeaderStyle; public GUIStyle columnHeaderStyle; public const float k_ToggleSize = 17f; public Styles() { listTextStyle = new GUIStyle(EditorStyles.label); listTextStyle.alignment = TextAnchor.MiddleLeft; listTextStyle.padding.left = 10; listHeaderStyle = new GUIStyle(EditorStyles.boldLabel); listHeaderStyle.padding.left = 5; columnHeaderStyle = EditorStyles.miniLabel; } } private enum EnabledState { NotSet = -1, None = 0, All = 1, Mixed = 2 } const float k_WindowWidth = 270; const float k_ScrollBarWidth = 14; const float k_ListElementHeight = 18; const float k_FrameWidth = 1f; float iconSize = 16; float gizmoRightAlign; float gizmoTextRightAlign; float iconRightAlign; float iconTextRightAlign; static AnnotationWindow s_AnnotationWindow = null; static long s_LastClosedTime; const long k_JustClosedPeriod = 400; static Styles m_Styles; List m_RecentAnnotations; List m_BuiltinAnnotations; List m_ScriptAnnotations; Vector2 m_ScrollPosition; bool m_SyncWithState; string m_LastScriptThatHasShownTheIconSelector; List m_MonoScriptIconsChanged; const int maxShowRecent = 5; readonly string textGizmoVisible = L10n.Tr("Show/Hide Gizmo"); GUIContent generalContent = EditorGUIUtility.TrTextContent("General"); GUIContent iconToggleContent = EditorGUIUtility.TrTextContent("", "Show/Hide Icon"); GUIContent iconSelectContent = EditorGUIUtility.TrTextContent("", "Select Icon"); GUIContent icon3dGizmoContent = EditorGUIUtility.TrTextContent("3D Icons"); GUIContent terrainDebugWarnings = EditorGUIUtility.TrTextContent("Terrain Debug Warnings"); GUIContent showOutlineContent = EditorGUIUtility.TrTextContent("Selection Outline"); GUIContent showWireframeContent = EditorGUIUtility.TrTextContent("Selection Wire"); GUIContent fadeGizmosContent = EditorGUIUtility.TrTextContent("Fade Gizmos", "Fade out and stop rendering gizmos that are small on screen"); GUIContent lightProbeVisualizationContent = EditorGUIUtility.TrTextContent("Light Probe Visualization"); GUIContent displayWeightsContent = EditorGUIUtility.TrTextContent("Display Weights"); GUIContent displayOcclusionContent = EditorGUIUtility.TrTextContent("Display Occlusion"); GUIContent highlightInvalidCellsContent = EditorGUIUtility.TrTextContent("Highlight Invalid Cells", "Highlight the invalid cells that cannot be used for probe interpolation."); private bool m_IsGameView; string m_SearchFilter = string.Empty; const float exponentStart = -3.0f; const float exponentRange = 3.0f; static float ConvertTexelWorldSizeTo01(float texelWorldSize) { if (texelWorldSize == -1.0f) return 1.0f; if (texelWorldSize == 0.0f) return 0.0f; return (Mathf.Log10(texelWorldSize) - exponentStart) / exponentRange; } static float Convert01ToTexelWorldSize(float value01) { if (value01 <= 0.0f) return 0.0f; // always hidden return Mathf.Pow(10.0f, exponentStart + exponentRange * value01); // texel size is between 10e-2 (0.01) and 10e2 (100) worldunits. (texel in the icon texture) } public void MonoScriptIconChanged(MonoScript monoScript) { if (monoScript == null) return; bool add = true; foreach (MonoScript m in m_MonoScriptIconsChanged) if (m.GetInstanceID() == monoScript.GetInstanceID()) add = false; if (add) m_MonoScriptIconsChanged.Add(monoScript); } static public void IconChanged() { if (s_AnnotationWindow != null) s_AnnotationWindow.IconHasChanged(); } float GetTopSectionHeight() { const int numberOfGeneralControls = 6; int numberOfLightProbeVisualizationControls = 0; if (!UnityEngine.Rendering.SupportedRenderingFeatures.active.overridesLightProbeSystem) { if (LightProbeVisualization.lightProbeVisualizationMode == LightProbeVisualization.LightProbeVisualizationMode.None) numberOfLightProbeVisualizationControls = 3; else numberOfLightProbeVisualizationControls = 5; } int numberOfControls = numberOfGeneralControls + numberOfLightProbeVisualizationControls; return EditorGUI.kSingleLineHeight * numberOfControls + EditorGUI.kControlVerticalSpacing * (numberOfControls - 1) + EditorStyles.inspectorBig.padding.bottom; } void OnEnable() { AssemblyReloadEvents.beforeAssemblyReload += Close; hideFlags = HideFlags.DontSave; } void OnDisable() { AssemblyReloadEvents.beforeAssemblyReload -= Close; // When window closes we copy all changes to monoimporters (reimport monoScripts) foreach (MonoScript monoScript in m_MonoScriptIconsChanged) IconSelector.CopyIconToImporter(monoScript); s_LastClosedTime = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; s_AnnotationWindow = null; } internal static bool ShowAtPosition(Rect buttonRect, bool isGameView) { // We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting playmode, we assume an increasing time when comparing time. long nowMilliSeconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; bool justClosed = nowMilliSeconds < s_LastClosedTime + k_JustClosedPeriod; if (!justClosed) { Event.current.Use(); if (s_AnnotationWindow == null) s_AnnotationWindow = ScriptableObject.CreateInstance(); else { // We are treating AnnotationWindow like a PopupWindow which has logic to reclose it when opened, // AuxWindows derived from EditorWindow reset/reopen when repeatedly clicking the open button by design. // Call Cancel() here if it is already open. s_AnnotationWindow.Cancel(); return false; } s_AnnotationWindow.Init(buttonRect, isGameView); return true; } return false; } void Init(Rect buttonRect, bool isGameView) { // Has to be done before calling Show / ShowWithMode buttonRect = GUIUtility.GUIToScreenRect(buttonRect); m_MonoScriptIconsChanged = new List(); m_SyncWithState = true; m_IsGameView = isGameView; SyncToState(); float windowHeight = 2 * k_FrameWidth + GetTopSectionHeight() + DrawNormalList(false, 100, 0, 10000); windowHeight = Mathf.Min(windowHeight, 900); Vector2 windowSize = new Vector2(k_WindowWidth, windowHeight); ShowAsDropDown(buttonRect, windowSize); } private void IconHasChanged() { if (string.IsNullOrEmpty(m_LastScriptThatHasShownTheIconSelector)) return; foreach (GizmoInfo t in m_ScriptAnnotations) { if (t.scriptClass == m_LastScriptThatHasShownTheIconSelector) { if (t.iconEnabled == false) { t.iconEnabled = true; SetIconState(t); break; } } } SyncToState(); Repaint(); } void Cancel() { // Undo changes we have done. // PerformTemporaryUndoStack must be called before Close() below // to ensure that we properly undo changes before closing window. //Undo.PerformTemporaryUndoStack(); Close(); GUI.changed = true; GUIUtility.ExitGUI(); } GizmoInfo GetAInfo(int classID, string scriptClass) { if (scriptClass != "") return m_ScriptAnnotations.Find(delegate(GizmoInfo o) { return o.scriptClass == scriptClass; }); return m_BuiltinAnnotations.Find(delegate(GizmoInfo o) { return o.classID == classID; }); } private void SyncToState() { // Sync annotations Annotation[] a = AnnotationUtility.GetAnnotations(); m_BuiltinAnnotations = new List(); m_ScriptAnnotations = new List(); for (int i = 0; i < a.Length; ++i) { GizmoInfo anno = new GizmoInfo(a[i]); if(string.IsNullOrEmpty(anno.scriptClass)) m_BuiltinAnnotations.Add(anno); else m_ScriptAnnotations.Add(anno); } m_BuiltinAnnotations.Sort(); m_ScriptAnnotations.Sort(); // Sync recently changed annotations m_RecentAnnotations = new List(); Annotation[] recent = AnnotationUtility.GetRecentlyChangedAnnotations(); for (int i = 0; i < recent.Length && i < maxShowRecent; ++i) { // Note: ainfo can be null if script has been renamed. GizmoInfo ainfo = GetAInfo(recent[i].classID, recent[i].scriptClass); if (ainfo != null) m_RecentAnnotations.Add(ainfo); } m_SyncWithState = false; } internal void OnGUI() { if (m_Styles == null) m_Styles = new Styles(); if (m_SyncWithState) SyncToState(); // Content float topSectionHeight = GetTopSectionHeight(); DrawTopSection(topSectionHeight); DrawAnnotationList(topSectionHeight, position.height - topSectionHeight); // Background with 1 pixel border if (Event.current.type == EventType.Repaint) m_Styles.background.Draw(new Rect(0, 0, position.width, position.height), GUIContent.none, false, false, false, false); if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) Cancel(); } // ------------------------------------ // TOP SECTION // ------------------------------------ void DrawTopSection(float topSectionHeight) { // Bg GUI.Label(new Rect(k_FrameWidth, 0, position.width - 2 * k_FrameWidth, topSectionHeight), "", EditorStyles.inspectorBig); float topmargin = 7; float margin = 11; float curY = topmargin; float labelWidth = m_Styles.listHeaderStyle.CalcSize(terrainDebugWarnings).x + GUI.skin.toggle.CalcSize(GUIContent.none).x + 1; float rowHeight = 18; // General section Rect toggleRect = new Rect(margin, curY, labelWidth, rowHeight); GUI.Label(toggleRect, generalContent, EditorStyles.boldLabel); curY += rowHeight; // 3D icons toggle & slider toggleRect = new Rect(margin, curY, labelWidth, rowHeight); AnnotationUtility.use3dGizmos = GUI.Toggle(toggleRect, AnnotationUtility.use3dGizmos, icon3dGizmoContent); using (new EditorGUI.DisabledScope(!AnnotationUtility.use3dGizmos)) { float texelWorldSize = AnnotationUtility.iconSize; float sliderWidth = position.width - margin - labelWidth; float iconSize01 = ConvertTexelWorldSizeTo01(texelWorldSize); Rect sliderRect = new Rect(labelWidth + margin, curY, sliderWidth - margin, rowHeight); iconSize01 = GUI.HorizontalSlider(sliderRect, iconSize01, 0.0f, 1.0f); if (GUI.changed) { AnnotationUtility.iconSize = Convert01ToTexelWorldSize(iconSize01); SceneView.RepaintAll(); } } curY += rowHeight; // Gizmo fadeout toggle & slider toggleRect = new Rect(margin, curY, labelWidth, rowHeight); AnnotationUtility.fadeGizmos = GUI.Toggle(toggleRect, AnnotationUtility.fadeGizmos, fadeGizmosContent); using (new EditorGUI.DisabledScope(!AnnotationUtility.fadeGizmos)) { float fadeSize = AnnotationUtility.fadeGizmoSize; float sliderWidth = position.width - margin - labelWidth; Rect sliderRect = new Rect(labelWidth + margin, curY, sliderWidth - margin, rowHeight); float newFadeSize = GUI.HorizontalSlider(sliderRect, fadeSize, 2.0f, 10.0f); if (fadeSize != newFadeSize) { AnnotationUtility.fadeGizmoSize = newFadeSize; SceneView.RepaintAll(); } } curY += rowHeight; using (new EditorGUI.DisabledScope(m_IsGameView)) { // Selection outline/wire toggleRect = new Rect(margin, curY, labelWidth, rowHeight); AnnotationUtility.showSelectionOutline = GUI.Toggle(toggleRect, AnnotationUtility.showSelectionOutline, showOutlineContent); curY += rowHeight; toggleRect = new Rect(margin, curY, labelWidth, rowHeight); AnnotationUtility.showSelectionWire = GUI.Toggle(toggleRect, AnnotationUtility.showSelectionWire, showWireframeContent); curY += rowHeight; // TODO: Change to Debug Errors & Debug Warnings toggleRect = new Rect(margin, curY, labelWidth, rowHeight); EditorGUI.BeginChangeCheck(); s_ShowTerrainDebugWarnings.value = GUI.Toggle(toggleRect, s_ShowTerrainDebugWarnings.value, terrainDebugWarnings); if (EditorGUI.EndChangeCheck()) SceneView.RepaintAll(); } curY += rowHeight; // Light probe section if (!UnityEngine.Rendering.SupportedRenderingFeatures.active.overridesLightProbeSystem) { curY += rowHeight; toggleRect = new Rect(margin, curY, labelWidth, rowHeight); GUI.Label(toggleRect, lightProbeVisualizationContent, EditorStyles.boldLabel); curY += rowHeight; toggleRect = new Rect(margin, curY, position.width - margin * 2, rowHeight); LightProbeVisualization.lightProbeVisualizationMode = (LightProbeVisualization.LightProbeVisualizationMode)EditorGUI.EnumPopup(toggleRect, LightProbeVisualization.lightProbeVisualizationMode); curY += rowHeight; if (LightProbeVisualization.lightProbeVisualizationMode != LightProbeVisualization.LightProbeVisualizationMode.None) { toggleRect = new Rect(margin, curY, labelWidth, rowHeight); LightProbeVisualization.showInterpolationWeights = GUI.Toggle(toggleRect, LightProbeVisualization.showInterpolationWeights, displayWeightsContent); curY += rowHeight; toggleRect = new Rect(margin, curY, labelWidth, rowHeight); LightProbeVisualization.showOcclusions = GUI.Toggle(toggleRect, LightProbeVisualization.showOcclusions, displayOcclusionContent); curY += rowHeight; toggleRect = new Rect(margin, curY, labelWidth, rowHeight); LightProbeVisualization.highlightInvalidCells = GUI.Toggle(toggleRect, LightProbeVisualization.highlightInvalidCells, highlightInvalidCellsContent); } } } // ------------------------------------ // ANNOTATION SECTION // ------------------------------------ // Returns height used void DrawAnnotationList(float startY, float height) { // Calc sizes const float barHeight = 1; Rect scrollViewRect = new Rect(0, startY + barHeight, position.width - 4, height - barHeight - k_FrameWidth); float totalContentHeight = DrawNormalList(false, 0, 0, 100000); Rect contentRect = new Rect(0, 0, 1, totalContentHeight); bool isScrollbarVisible = totalContentHeight > scrollViewRect.height; float listElementWidth = scrollViewRect.width; if (isScrollbarVisible) listElementWidth -= k_ScrollBarWidth; // Scrollview m_ScrollPosition = GUI.BeginScrollView(scrollViewRect, m_ScrollPosition, contentRect, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, EditorStyles.scrollViewAlt); { DrawNormalList(true, listElementWidth, m_ScrollPosition.y - k_ListElementHeight, m_ScrollPosition.y + totalContentHeight); } GUI.EndScrollView(); } void Flip(ref bool even) { even = !even; } float DrawNormalList(bool doDraw, float listElementWidth, float startY, float endY) { bool even = true; float curY = 0; bool headerDrawn = false; bool searchDrawn = false; curY = DrawListSection(curY, L10n.Tr("Recently Changed"), m_RecentAnnotations, doDraw, listElementWidth, startY, endY, ref even, true, ref headerDrawn, ref searchDrawn); curY = DrawListSection(curY, L10n.Tr("Scripts"), m_ScriptAnnotations, doDraw, listElementWidth, startY, endY, ref even, false, ref headerDrawn, ref searchDrawn); curY = DrawListSection(curY, L10n.Tr("Built-in Components"), m_BuiltinAnnotations, doDraw, listElementWidth, startY, endY, ref even, false, ref headerDrawn, ref searchDrawn); return curY; } bool DoesMatchFilter(GizmoInfo el) { if (string.IsNullOrEmpty(m_SearchFilter)) return true; if (el.name.IndexOf(m_SearchFilter, System.StringComparison.OrdinalIgnoreCase) < 0) return false; return true; } float DrawListSection(float y, string sectionHeader, List listElements, bool doDraw, float listElementWidth, float startY, float endY, ref bool even, bool useSeperator, ref bool headerDrawn, ref bool searchDrawn) { float curY = y; const float kSearchPaddingV = 3; const float kSearchHeight = EditorGUI.kSingleSmallLineHeight + kSearchPaddingV; const float extraHeader = 15; const float headerHeight = 20; if (listElements.Count > 0) { if (doDraw) { // Header background Rect rect = new Rect(1, curY, listElementWidth - 2, extraHeader + (headerDrawn ? 0 : kSearchHeight) + headerHeight); Flip(ref even); GUIStyle backgroundStyle = even ? m_Styles.listEvenBg : m_Styles.listOddBg; // m_Styles.listSectionHeaderBg;// GUI.Label(rect, GUIContent.Temp(""), backgroundStyle); } // Search field if (!searchDrawn) { searchDrawn = true; if (doDraw) { Rect searchRect = new Rect(11, curY + kSearchPaddingV, listElementWidth - 16, EditorGUI.kSingleSmallLineHeight); m_SearchFilter = EditorGUI.ToolbarSearchField(searchRect, m_SearchFilter, false); } curY += kSearchHeight; } curY += extraHeader; if (doDraw) { // Header text DrawListHeader(sectionHeader, listElements, new Rect(3, curY, listElementWidth, headerHeight), ref headerDrawn); } curY += headerHeight; // List elements for (int i = 0; i < listElements.Count; ++i) { if (!DoesMatchFilter(listElements[i])) continue; Flip(ref even); if (curY > startY && curY < endY) { Rect rect = new Rect(1, curY, listElementWidth - 2, k_ListElementHeight); if (doDraw) DrawListElement(rect, even, listElements[i]); } curY += k_ListElementHeight; } if (useSeperator) { float height = 6; if (doDraw) { GUIStyle backgroundStyle = even ? m_Styles.listEvenBg : m_Styles.listOddBg; GUI.Label(new Rect(1, curY, listElementWidth - 2, height), GUIContent.Temp(""), backgroundStyle); GUI.Label(new Rect(10, curY + 3, listElementWidth - 15, 3), GUIContent.Temp(""), m_Styles.seperator); } curY += height; } } return curY; } void DrawListHeader(string header, List elements, Rect rect, ref bool headerDrawn) { GUI.Label(rect, GUIContent.Temp(header), m_Styles.listHeaderStyle); float toggleSize = Styles.k_ToggleSize; EnabledState enabledState = EnabledState.NotSet; for (int i = 0; i < elements.Count; i++) { var element = elements[i]; if (!DoesMatchFilter(element)) continue; if (element.hasGizmo) { if (enabledState == EnabledState.NotSet) { enabledState = element.gizmoEnabled ? EnabledState.All : EnabledState.None; } else if ((enabledState == EnabledState.All) != element.gizmoEnabled) { enabledState = EnabledState.Mixed; break; } } } if (enabledState == EnabledState.NotSet) return; var gizmoText = "gizmo"; var gizmoTextSize = m_Styles.columnHeaderStyle.CalcSize(new GUIContent(gizmoText)); gizmoTextRightAlign = gizmoTextSize.x; gizmoRightAlign = gizmoTextRightAlign - (gizmoTextSize.x * 0.5f - m_Styles.toggle.CalcSize(GUIContent.none).x*0.5f); var iconText = "icon"; var iconTextSize = m_Styles.columnHeaderStyle.CalcSize(new GUIContent(iconText)); iconTextRightAlign = iconTextSize.x + gizmoTextRightAlign + 10; iconRightAlign = iconTextRightAlign - (iconTextSize.x * 0.5f - iconSize * 0.5f); GUIStyle style = m_Styles.toggle; bool enabled = enabledState == EnabledState.All; bool setMixed = enabledState == EnabledState.Mixed; if (setMixed) style = m_Styles.toggleMixed; Rect toggleRect = new Rect(rect.width - gizmoRightAlign, rect.y + (rect.height - toggleSize) * 0.5f, toggleSize, toggleSize); EditorGUI.BeginChangeCheck(); bool newEnabled = GUI.Toggle(toggleRect, enabled, GUIContent.none, style); if (EditorGUI.EndChangeCheck()) { for (int i = 0; i < elements.Count; i++) { var element = elements[i]; if (!DoesMatchFilter(element)) continue; if (element.gizmoEnabled != newEnabled) { element.gizmoEnabled = newEnabled; SetGizmoState(element, false); } } } if (headerDrawn == false) { headerDrawn = true; GUI.color = new Color(1, 1, 1, 0.65f); // Column headers Rect columnRect = rect; columnRect.y -= gizmoTextSize.y + 3; columnRect.x = rect.width - gizmoTextRightAlign; GUI.Label(columnRect, gizmoText, m_Styles.columnHeaderStyle); columnRect.x = rect.width - iconTextRightAlign; GUI.Label(columnRect, iconText, m_Styles.columnHeaderStyle); GUI.color = Color.white; } } void DrawListElement(Rect rect, bool even, GizmoInfo ainfo) { if (ainfo == null) { Debug.LogError("DrawListElement: AInfo not valid!"); return; } string tooltip; float togglerSize = Styles.k_ToggleSize; float disabledAlpha = 0.3f; // We maintain our own gui.changed bool orgGUIChanged = GUI.changed; bool orgGUIEnabled = GUI.enabled; Color orgColor = GUI.color; GUI.changed = false; GUI.enabled = true; // Bg GUIStyle backgroundStyle = even ? m_Styles.listEvenBg : m_Styles.listOddBg; GUI.Label(rect, GUIContent.Temp(""), backgroundStyle); // Text Rect textRect = rect; //textRect.x += 22; textRect.width = rect.width - iconRightAlign - 22; // ensure text doesnt flow behind toggles GUI.Label(textRect, ainfo.name, m_Styles.listTextStyle); // Icon toggle Rect iconRect = new Rect(rect.width - iconRightAlign + 2, rect.y + (rect.height - iconSize) * 0.5f, iconSize, iconSize); // +2 because the given rect is shortened by 2px before this method call if (ainfo.scriptClass != "") { Rect div = iconRect; div.x += 18; div.y += 1; div.width = 1; div.height = 12; if (!EditorGUIUtility.isProSkin) GUI.color = new Color(0, 0, 0, 0.33f); else GUI.color = new Color(1, 1, 1, 0.13f); GUI.DrawTexture(div, EditorGUIUtility.whiteTexture, ScaleMode.StretchToFill); GUI.color = Color.white; if (!ainfo.disabled) { Rect arrowRect = iconRect; arrowRect.x += 18; arrowRect.y += 0; arrowRect.width = 9; if (GUI.Button(arrowRect, iconSelectContent, m_Styles.iconDropDown)) { Object script = EditorGUIUtility.GetScript(ainfo.scriptClass); if (script != null) { m_LastScriptThatHasShownTheIconSelector = ainfo.scriptClass; if (IconSelector.ShowAtPosition(script, arrowRect, true)) { IconSelector.SetMonoScriptIconChangedCallback(MonoScriptIconChanged); GUIUtility.ExitGUI(); } } } } } if (ainfo.thumb != null) { if (!ainfo.iconEnabled) { GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, disabledAlpha); tooltip = ""; } iconToggleContent.image = ainfo.thumb; if (GUI.Button(iconRect, iconToggleContent, GUIStyle.none)) { ainfo.iconEnabled = !ainfo.iconEnabled; SetIconState(ainfo); } GUI.color = orgColor; } if (GUI.changed) { SetIconState(ainfo); GUI.changed = false; } GUI.enabled = true; GUI.color = orgColor; // Gizmo toggle if (ainfo.hasGizmo) { tooltip = textGizmoVisible; Rect togglerRect = new Rect(rect.width - gizmoRightAlign + 2, rect.y + (rect.height - togglerSize) * 0.5f, togglerSize, togglerSize); // +2 because the given rect is shortened by 2px before this method call ainfo.gizmoEnabled = GUI.Toggle(togglerRect, ainfo.gizmoEnabled, new GUIContent("", tooltip), m_Styles.toggle); if (GUI.changed) { SetGizmoState(ainfo); } } GUI.enabled = orgGUIEnabled; GUI.changed = orgGUIChanged; GUI.color = orgColor; } void SetIconState(GizmoInfo ainfo) { AnnotationUtility.SetIconEnabled(ainfo.classID, ainfo.scriptClass, ainfo.iconEnabled ? 1 : 0); SceneView.RepaintAll(); } void SetGizmoState(GizmoInfo ainfo, bool addToMostRecentChanged = true) { AnnotationUtility.SetGizmoEnabled(ainfo.classID, ainfo.scriptClass, ainfo.gizmoEnabled ? 1 : 0, addToMostRecentChanged); SceneView.RepaintAll(); } [Shortcut("Scene View/Toggle Selection Outline", typeof(SceneView))] static void ToggleSelectionOutline() { AnnotationUtility.showSelectionOutline = !AnnotationUtility.showSelectionOutline; } [Shortcut("Scene View/Toggle Selection Wireframe", typeof(SceneView))] static void ToggleSelectionWireframe() { AnnotationUtility.showSelectionWire = !AnnotationUtility.showSelectionWire; } } } ================================================ FILE: Editor/Mono/Annotation/GizmoInfo.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityObject = UnityEngine.Object; namespace UnityEditor { [Serializable] public class GizmoInfo : IComparable { [SerializeField] bool m_IconEnabled; [SerializeField] bool m_GizmoEnabled; [SerializeField] int m_ClassID; [SerializeField] string m_ScriptClass; [SerializeField] string m_Name; [SerializeField] int m_Flags; [NonSerialized] UnityObject m_Script; [NonSerialized] Texture2D m_Thumb; internal GizmoInfo() { m_IconEnabled = false; m_GizmoEnabled = false; m_ClassID = -1; m_ScriptClass = null; m_Name = ""; m_Flags = 0; } internal GizmoInfo(Annotation annotation) { m_GizmoEnabled = annotation.gizmoEnabled > 0; m_IconEnabled = annotation.iconEnabled > 0; m_ClassID = annotation.classID; m_ScriptClass = annotation.scriptClass; m_Flags = annotation.flags; m_Name = string.IsNullOrEmpty(m_ScriptClass) ? UnityType.FindTypeByPersistentTypeID(m_ClassID).name : m_Name = m_ScriptClass; } internal int classID => m_ClassID; internal string scriptClass => m_ScriptClass; public string name => m_Name; public bool hasGizmo => (m_Flags & (int)AnnotationUtility.Flags.kHasGizmo) > 0; public bool gizmoEnabled { get => m_GizmoEnabled; set => m_GizmoEnabled = value; } public bool hasIcon => (m_Flags & (int)AnnotationUtility.Flags.kHasIcon) > 0; public bool iconEnabled { get => m_IconEnabled; set => m_IconEnabled = value; } internal bool disabled => (m_Flags & (int)AnnotationUtility.Flags.kIsDisabled) > 0; public UnityObject script { get { if (m_Script == null && m_ScriptClass != "") m_Script = EditorGUIUtility.GetScript(m_ScriptClass); return m_Script; } } public Texture2D thumb { get { if (m_Thumb == null) { // Icon for scripts if (script != null) m_Thumb = EditorGUIUtility.GetIconForObject(m_Script); // Icon for builtin components else if (hasIcon) m_Thumb = AssetPreview.GetMiniTypeThumbnailFromClassID(m_ClassID); } return m_Thumb; } } public int CompareTo(object obj) { if(obj is GizmoInfo other) return m_Name.CompareTo(other.m_Name); return 1; } } } ================================================ FILE: Editor/Mono/Annotation/GizmoUtility.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using UnityEngine; namespace UnityEditor { public static class GizmoUtility { const float k_MinFadeGizmoSize = 1f; const float k_MaxFadeGizmoSize = 10f; static void GetAnnotationIdAndClass(Type type, out int id, out string klass) { var unityType = UnityType.FindTypeByName(type.Name); id = unityType?.persistentTypeID ?? 0; // In AnnotationManager, if script name is null or empty the persistent ID is used. If not, the type is // assumed to be a built-in type. klass = unityType == null ? type.Name : null; } public static bool TryGetGizmoInfo(Type type, out GizmoInfo info) { GetAnnotationIdAndClass(type, out var id, out var name); var annotation = AnnotationUtility.GetAnnotation(id, name); if (annotation.gizmoEnabled == -1 && annotation.iconEnabled == -1 && annotation.flags == -1) { info = new GizmoInfo(); return false; } info = new GizmoInfo(annotation); return true; } public static GizmoInfo[] GetGizmoInfo() { var annotations = AnnotationUtility.GetAnnotations(); return annotations.Select(x => new GizmoInfo(x)).ToArray(); } public static void ApplyGizmoInfo(GizmoInfo info, bool addToRecentlyChanged = true) { int gizmoEnabled = info.gizmoEnabled ? 1 : 0; AnnotationUtility.SetGizmoEnabled(info.classID, info.scriptClass, gizmoEnabled, addToRecentlyChanged); AnnotationUtility.SetIconEnabled(info.classID, info.scriptClass, info.iconEnabled ? 1 : 0); } public static void SetGizmoEnabled(Type type, bool enabled, bool addToRecentlyChanged = true) { GetAnnotationIdAndClass(type, out var id, out var name); AnnotationUtility.SetGizmoEnabled(id, name, enabled ? 1 : 0, addToRecentlyChanged); } public static void SetIconEnabled(Type type, bool enabled) { GetAnnotationIdAndClass(type, out var id, out var name); AnnotationUtility.SetIconEnabled(id, name, enabled ? 1 : 0); } public static float iconSize { get => AnnotationUtility.iconSize; set => AnnotationUtility.iconSize = Mathf.Clamp01(value); } public static bool use3dIcons { get => AnnotationUtility.use3dGizmos; set => AnnotationUtility.use3dGizmos = value; } } } ================================================ FILE: Editor/Mono/Annotation/LayerVisibilityWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditorInternal; using System.Collections.Generic; namespace UnityEditor { internal class LayerVisibilityWindow : EditorWindow { private class Styles { public readonly GUIStyle background = "grey_border"; public readonly GUIStyle menuItem = "MenuItem"; public readonly GUIStyle listEvenBg = "ObjectPickerResultsOdd"; public readonly GUIStyle listOddBg = "ObjectPickerResultsEven"; public readonly GUIStyle separator = "sv_iconselector_sep"; public readonly GUIStyle listTextStyle; public readonly GUIStyle listHeaderStyle; public readonly Texture2D visibleOn = EditorGUIUtility.LoadIcon("animationvisibilitytoggleon"); public readonly Texture2D visibleOff = EditorGUIUtility.LoadIcon("animationvisibilitytoggleoff"); public readonly Texture2D pickable = EditorGUIUtility.LoadIcon("scenepicking_pickable"); public readonly Texture2D notpickable = EditorGUIUtility.LoadIcon("scenepicking_notpickable"); public readonly GUIContent editLayers = EditorGUIUtility.TrTextContent("Edit Layers..."); public Styles() { listTextStyle = new GUIStyle(EditorStyles.label); listTextStyle.alignment = TextAnchor.MiddleLeft; listTextStyle.padding.left = 10; listHeaderStyle = new GUIStyle(EditorStyles.boldLabel); listHeaderStyle.padding.left = 5; } } const float k_ScrollBarWidth = 14; const float k_FrameWidth = 1f; const float k_ToggleSize = 17; const float k_SeparatorHeight = 6; const string k_LayerVisible = "Show/Hide Layer"; const string k_LayerPickable = "Toggle Pickable status this Layer. Non-Pickable items cannot be selected in the Scene View."; private static LayerVisibilityWindow s_LayerVisibilityWindow; private static long s_LastClosedTime; const long k_JustClosedPeriod = 400; private static Styles s_Styles; private List s_LayerNames; private List s_LayerMasks; private int m_AllLayersMask; private float m_ContentHeight; private Vector2 m_ScrollPosition; private void CalcValidLayers() { s_LayerNames = new List(); s_LayerMasks = new List(); m_AllLayersMask = 0; for (var i = 0; i < 32; i++) { var s = InternalEditorUtility.GetLayerName(i); if (s == string.Empty) continue; s_LayerNames.Add(string.Format("{0}: {1}", i, s)); s_LayerMasks.Add(i); m_AllLayersMask |= (1 << i); } } internal void OnEnable() { hideFlags = HideFlags.DontSave; wantsMouseMove = true; } internal void OnDisable() { s_LastClosedTime = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; s_LayerVisibilityWindow = null; } internal static bool ShowAtPosition(Rect buttonRect) { // We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting playmode, we assume an increasing time when comparing time. long nowMilliSeconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond; bool justClosed = nowMilliSeconds < s_LastClosedTime + k_JustClosedPeriod; if (!justClosed) { Event.current.Use(); if (s_LayerVisibilityWindow == null) s_LayerVisibilityWindow = CreateInstance(); s_LayerVisibilityWindow.Init(buttonRect); return true; } return false; } private void Init(Rect buttonRect) { // Has to be done before calling Show / ShowWithMode buttonRect = GUIUtility.GUIToScreenRect(buttonRect); CalcValidLayers(); var rowCount = (s_LayerNames.Count + 2 + 1 + 1); var windowHeight = rowCount * EditorGUI.kSingleLineHeight + k_SeparatorHeight; int sortingLayerCount = InternalEditorUtility.GetSortingLayerCount(); if (sortingLayerCount > 1) { windowHeight += k_SeparatorHeight + EditorGUI.kSingleLineHeight; windowHeight += sortingLayerCount * EditorGUI.kSingleLineHeight; } m_ContentHeight = windowHeight; windowHeight += 2 * k_FrameWidth; windowHeight = Mathf.Min(windowHeight, 600); var windowSize = new Vector2(180, windowHeight); ShowAsDropDown(buttonRect, windowSize); } internal void OnGUI() { // We do not use the layout event if (Event.current.type == EventType.Layout) return; if (s_Styles == null) s_Styles = new Styles(); var scrollViewRect = new Rect(k_FrameWidth, k_FrameWidth, position.width - 2 * k_FrameWidth, position.height - 2 * k_FrameWidth); var contentRect = new Rect(0, 0, 1, m_ContentHeight); bool isScrollbarVisible = m_ContentHeight > scrollViewRect.height; float listElementWidth = scrollViewRect.width; if (isScrollbarVisible) listElementWidth -= k_ScrollBarWidth; m_ScrollPosition = GUI.BeginScrollView(scrollViewRect, m_ScrollPosition, contentRect, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, EditorStyles.scrollViewAlt); Draw(listElementWidth); GUI.EndScrollView(); // Background with 1 pixel border GUI.Label(new Rect(0, 0, position.width, position.height), GUIContent.none, s_Styles.background); // Use mouse move so we get hover state correctly in the menu item rows if (Event.current.type == EventType.MouseMove) Event.current.Use(); // Escape closes the window if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) { Close(); GUIUtility.ExitGUI(); } } private void DrawListBackground(Rect rect, bool even) { GUIStyle backgroundStyle = even ? s_Styles.listEvenBg : s_Styles.listOddBg; GUI.Label(rect, GUIContent.none, backgroundStyle); } private void DrawHeader(ref Rect rect, string text, ref bool even) { DrawListBackground(rect, even); GUI.Label(rect, GUIContent.Temp(text), s_Styles.listHeaderStyle); rect.y += EditorGUI.kSingleLineHeight; even = !even; } private void DrawSeparator(ref Rect rect, bool even) { DrawListBackground(new Rect(rect.x + 1, rect.y, rect.width - 2, k_SeparatorHeight), even); GUI.Label(new Rect(rect.x + 5, rect.y + 3, rect.width - 10, 3), GUIContent.none, s_Styles.separator); rect.y += k_SeparatorHeight; } private void Draw(float listElementWidth) { var drawPos = new Rect(0, 0, listElementWidth, EditorGUI.kSingleLineHeight); bool even = false; DrawHeader(ref drawPos, "Layers", ref even); // Everything & nothing entries DoSpecialLayer(drawPos, true, ref even); drawPos.y += EditorGUI.kSingleLineHeight; DoSpecialLayer(drawPos, false, ref even); drawPos.y += EditorGUI.kSingleLineHeight; // Layers for (var i = 0; i < s_LayerNames.Count; ++i) { DoOneLayer(drawPos, i, ref even); drawPos.y += EditorGUI.kSingleLineHeight; } // Sorting layers, if anything else than the single default one is present int sortingLayerCount = InternalEditorUtility.GetSortingLayerCount(); if (sortingLayerCount > 1) { DrawSeparator(ref drawPos, even); DrawHeader(ref drawPos, "Sorting Layers", ref even); for (var i = 0; i < sortingLayerCount; ++i) { DoOneSortingLayer(drawPos, i, ref even); drawPos.y += EditorGUI.kSingleLineHeight; } } // Edit Layers entry DrawSeparator(ref drawPos, even); DrawListBackground(drawPos, even); if (GUI.Button(drawPos, s_Styles.editLayers, s_Styles.menuItem)) { Close(); Selection.activeObject = EditorApplication.tagManager; GUIUtility.ExitGUI(); } } void DoSpecialLayer(Rect rect, bool all, ref bool even) { int visibleMask = Tools.visibleLayers; int expectedMask = all ? m_AllLayersMask : 0; bool visible = (visibleMask & m_AllLayersMask) == expectedMask; bool visibleChanged, lockedChanged; DoLayerEntry(rect, all ? "Everything" : "Nothing", even, true, false, visible, false, out visibleChanged, out lockedChanged); if (visibleChanged) { Tools.visibleLayers = all ? ~0 : 0; RepaintAllSceneViews(); } even = !even; } void DoOneLayer(Rect rect, int index, ref bool even) { int visibleMask = Tools.visibleLayers; int lockedMask = Tools.lockedLayers; int layerMask = 1 << (s_LayerMasks[index]); bool visible = (visibleMask & layerMask) != 0; bool locked = (lockedMask & layerMask) != 0; bool visibleChanged, lockedChanged; DoLayerEntry(rect, s_LayerNames[index], even, true, true, visible, locked, out visibleChanged, out lockedChanged); if (visibleChanged) { Tools.visibleLayers ^= layerMask; RepaintAllSceneViews(); } if (lockedChanged) { Tools.lockedLayers ^= layerMask; } even = !even; } void DoOneSortingLayer(Rect rect, int index, ref bool even) { bool locked = InternalEditorUtility.GetSortingLayerLocked(index); bool visibleChanged, lockedChanged; DoLayerEntry(rect, InternalEditorUtility.GetSortingLayerName(index), even, false, true, true, locked, out visibleChanged, out lockedChanged); if (lockedChanged) { InternalEditorUtility.SetSortingLayerLocked(index, !locked); } even = !even; } private void DoLayerEntry(Rect rect, string layerName, bool even, bool showVisible, bool showLock, bool visible, bool picked, out bool visibleChanged, out bool lockedChanged) { DrawListBackground(rect, even); EditorGUI.BeginChangeCheck(); // text (works as visibility toggle as well) Rect textRect = rect; textRect.width -= k_ToggleSize * 2; visible = GUI.Toggle(textRect, visible, EditorGUIUtility.TempContent(layerName), s_Styles.listTextStyle); // visible checkbox var toggleRect = new Rect(rect.width - k_ToggleSize * 2, rect.y + (rect.height - k_ToggleSize) * 0.5f, k_ToggleSize, k_ToggleSize); visibleChanged = false; if (showVisible) { var iconRect = toggleRect; var gc = new GUIContent(string.Empty, visible ? s_Styles.visibleOn : s_Styles.visibleOff, k_LayerVisible); GUI.Toggle(iconRect, visible, gc, GUIStyle.none); visibleChanged = EditorGUI.EndChangeCheck(); } // locked checkbox lockedChanged = false; if (showLock) { toggleRect.x += k_ToggleSize; EditorGUI.BeginChangeCheck(); var gc = new GUIContent(string.Empty, picked ? s_Styles.notpickable : s_Styles.pickable, k_LayerPickable); GUI.Toggle(toggleRect, picked, gc, GUIStyle.none); lockedChanged = EditorGUI.EndChangeCheck(); } } static void RepaintAllSceneViews() { foreach (SceneView sv in Resources.FindObjectsOfTypeAll(typeof(SceneView))) sv.Repaint(); } } } ================================================ FILE: Editor/Mono/Annotation/SceneFXWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Rendering; namespace UnityEditor { internal class SceneFXWindow : PopupWindowContent { private class Styles { public readonly GUIStyle menuItem = "MenuItem"; } private static Styles s_Styles; private readonly SceneView m_SceneView; const float kFrameWidth = 1f; public override Vector2 GetWindowSize() { var windowHeight = 2f * kFrameWidth + EditorGUI.kSingleLineHeight * 7; if (UnityEngine.VFX.VFXManager.activateVFX) { windowHeight += EditorGUI.kSingleLineHeight; } var windowSize = new Vector2(160, windowHeight); return windowSize; } public SceneFXWindow(SceneView sceneView) { m_SceneView = sceneView; } public override void OnGUI(Rect rect) { if (m_SceneView == null || m_SceneView.sceneViewState == null) return; // We do not use the layout event if (Event.current.type == EventType.Layout) return; if (s_Styles == null) s_Styles = new Styles(); // Content Draw(rect); // Use mouse move so we get hover state correctly in the menu item rows if (Event.current.type == EventType.MouseMove) Event.current.Use(); // Escape closes the window if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) { editorWindow.Close(); GUIUtility.ExitGUI(); } } private void Draw(Rect rect) { if (m_SceneView == null || m_SceneView.sceneViewState == null) return; var drawPos = new Rect(kFrameWidth, kFrameWidth, rect.width - 2 * kFrameWidth, EditorGUI.kSingleLineHeight); var state = m_SceneView.sceneViewState; DrawListElement(drawPos, "Skybox", state.showSkybox, value => state.showSkybox = value); drawPos.y += EditorGUI.kSingleLineHeight; if(SupportedRenderingFeatures.active.supportsClouds) { DrawListElement(drawPos, "Clouds", state.showClouds, value => state.showClouds = value); drawPos.y += EditorGUI.kSingleLineHeight; } DrawListElement(drawPos, "Fog", state.showFog, value => state.showFog = value); drawPos.y += EditorGUI.kSingleLineHeight; DrawListElement(drawPos, "Flares", state.showFlares, value => state.showFlares = value); drawPos.y += EditorGUI.kSingleLineHeight; DrawListElement(drawPos, "Always Refresh", state.alwaysRefresh, value => state.alwaysRefresh = value); drawPos.y += EditorGUI.kSingleLineHeight; DrawListElement(drawPos, "Post Processing", state.showImageEffects, value => state.showImageEffects = value); drawPos.y += EditorGUI.kSingleLineHeight; DrawListElement(drawPos, "Particle Systems", state.showParticleSystems, value => state.showParticleSystems = value); drawPos.y += EditorGUI.kSingleLineHeight; if (UnityEngine.VFX.VFXManager.activateVFX) { DrawListElement(drawPos, "Visual Effect Graphs", state.showVisualEffectGraphs, value => state.showVisualEffectGraphs = value); drawPos.y += EditorGUI.kSingleLineHeight; } } void DrawListElement(Rect rect, string toggleName, bool value, Action setValue) { EditorGUI.BeginChangeCheck(); bool result = GUI.Toggle(rect, value, EditorGUIUtility.TempContent(toggleName), s_Styles.menuItem); if (EditorGUI.EndChangeCheck()) { setValue(result); m_SceneView.Repaint(); } } } } ================================================ FILE: Editor/Mono/Annotation/SceneRenderModeWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Rendering; namespace UnityEditor { // Match EditorDrawingMode order in C++ code! public enum DrawCameraMode { UserDefined = int.MinValue, Normal = -1, Textured = 0, Wireframe = 1, TexturedWire = 2, ShadowCascades = 3, RenderPaths = 4, AlphaChannel = 5, Overdraw = 6, Mipmaps = 7, DeferredDiffuse = 8, DeferredSpecular = 9, DeferredSmoothness = 10, DeferredNormal = 11, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("Renamed to better distinguish this mode from new Progressive baked modes. Please use RealtimeCharting instead. (UnityUpgradable) -> RealtimeCharting", true)] Charting = -12, RealtimeCharting = 12, Systems = 13, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("Renamed to better distinguish this mode from new Progressive baked modes. Please use RealtimeAlbedo instead. (UnityUpgradable) -> RealtimeAlbedo", true)] Albedo = -14, RealtimeAlbedo = 14, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("Renamed to better distinguish this mode from new Progressive baked modes. Please use RealtimeEmissive instead. (UnityUpgradable) -> RealtimeEmissive", true)] Emissive = -15, RealtimeEmissive = 15, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("Renamed to better distinguish this mode from new Progressive baked modes. Please use RealtimeIndirect instead. (UnityUpgradable) -> RealtimeIndirect", true)] Irradiance = -16, RealtimeIndirect = 16, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("Renamed to better distinguish this mode from new Progressive baked modes. Please use RealtimeDirectionality instead. (UnityUpgradable) -> RealtimeDirectionality", true)] Directionality = -17, RealtimeDirectionality = 17, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("Renamed to better distinguish this mode from new Progressive baked modes. Please use BakedLightmap instead. (UnityUpgradable) -> BakedLightmap", true)] Baked = -18, BakedLightmap = 18, Clustering = 19, LitClustering = 20, ValidateAlbedo = 21, ValidateMetalSpecular = 22, ShadowMasks = 23, LightOverlap = 24, BakedAlbedo = 25, BakedEmissive = 26, BakedDirectionality = 27, BakedTexelValidity = 28, BakedIndices = 29, BakedCharting = 30, SpriteMask = 31, BakedUVOverlap = 32, TextureStreaming = 33, BakedLightmapCulling = 34, GIContributorsReceivers = 35, } internal class SceneRenderModeWindow : PopupWindowContent { static class Styles { private static GUIStyle menuItem; private static GUIStyle separator; private static GUIContent debuggerLabel; public static GUIStyle s_MenuItem => menuItem ?? (menuItem = "MenuItem"); public static GUIStyle s_Separator => separator ?? (separator = "sv_iconselector_sep"); public static GUIContent s_DebuggerLabel => debuggerLabel ??= EditorGUIUtility.TrTextContent("Rendering Debugger..."); private static readonly string kShadingMode = "Shading Mode"; private static readonly string kMiscellaneous = "Miscellaneous"; private static readonly string kDeferred = "Deferred"; private static readonly string kLighting = "Lighting"; private static readonly string kRealtimeGI = "Realtime Global Illumination"; private static readonly string kBakedGI = "Baked Global Illumination"; // Map all builtin DrawCameraMode entries // This defines the order in which the entries appear in the dropdown menu! public static readonly SceneView.CameraMode[] sBuiltinCameraModes = { new SceneView.CameraMode(DrawCameraMode.Textured, "Shaded", kShadingMode, false), new SceneView.CameraMode(DrawCameraMode.Wireframe, "Wireframe", kShadingMode, false), new SceneView.CameraMode(DrawCameraMode.TexturedWire, "Shaded Wireframe", kShadingMode, false), new SceneView.CameraMode(DrawCameraMode.GIContributorsReceivers, "Contributors / Receivers", kLighting), new SceneView.CameraMode(DrawCameraMode.ShadowCascades, "Shadow Cascades", kLighting), new SceneView.CameraMode(DrawCameraMode.RealtimeIndirect, "Indirect", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.RealtimeDirectionality, "Directionality", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.RealtimeAlbedo, "Albedo", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.RealtimeEmissive, "Emissive", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.RealtimeCharting, "UV Charts", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.Systems, "Systems", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.Clustering, "Clustering", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.LitClustering, "Lit Clustering", kRealtimeGI), new SceneView.CameraMode(DrawCameraMode.BakedLightmap, "Baked Lightmap", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedDirectionality, "Directionality", kBakedGI), new SceneView.CameraMode(DrawCameraMode.ShadowMasks, "Shadowmask", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedAlbedo, "Albedo", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedEmissive, "Emissive", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedCharting, "UV Charts", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedTexelValidity, "Texel Validity", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedUVOverlap, "UV Overlap", kBakedGI), new SceneView.CameraMode(DrawCameraMode.BakedIndices, "Lightmap Indices", kBakedGI), new SceneView.CameraMode(DrawCameraMode.LightOverlap, "Light Overlap", kBakedGI), new SceneView.CameraMode(DrawCameraMode.DeferredDiffuse, "Albedo", kDeferred), new SceneView.CameraMode(DrawCameraMode.DeferredSpecular, "Specular", kDeferred), new SceneView.CameraMode(DrawCameraMode.DeferredSmoothness, "Smoothness", kDeferred), new SceneView.CameraMode(DrawCameraMode.DeferredNormal, "Normal", kDeferred), new SceneView.CameraMode(DrawCameraMode.RenderPaths, "Render Paths", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.AlphaChannel, "Alpha Channel", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.Overdraw, "Overdraw", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.Mipmaps, "Mipmaps", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.TextureStreaming, "Texture Mipmap Streaming", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.SpriteMask, "Sprite Mask", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.ValidateAlbedo, "Validate Albedo", kMiscellaneous), new SceneView.CameraMode(DrawCameraMode.ValidateMetalSpecular, "Validate Metal Specular", kMiscellaneous), }; } // UUM-96180: Default CameraMode should be DrawCameraMode.GIContributorsReceivers internal static SceneView.CameraMode defaultCameraMode { get { var mode = Styles.sBuiltinCameraModes [3]; if(mode.drawMode != DrawCameraMode.GIContributorsReceivers) Debug.LogError("Default Draw Camera mode should be set to DrawCameraMode.GIContributorsReceivers."); return mode; } } private Dictionary foldoutStates = new Dictionary(); private float windowHeight { get { int headers; int modes; // Hide unsupported items and headers headers = Styles.sBuiltinCameraModes.Where(mode => m_SceneView.IsCameraDrawModeSupported(mode) && mode.show) .Select(mode => mode.section).Distinct().Count() + SceneView.userDefinedModes.Where(mode => m_SceneView.IsCameraDrawModeSupported(mode) && mode.show) .Select(mode => mode.section).Distinct().Count(); modes = Styles.sBuiltinCameraModes.Count(mode => m_SceneView.IsCameraDrawModeSupported(mode) && mode.show) + SceneView.userDefinedModes.Count(mode => m_SceneView.IsCameraDrawModeSupported(mode) && mode.show); return UpdatedHeight(headers, modes, GraphicsSettings.isScriptableRenderPipelineEnabled); } } const float windowWidth = 205; const float kSeparatorHeight = 3; const float kHeaderHorizontalPadding = 5f; const float kHeaderVerticalPadding = 3f; readonly SceneView m_SceneView; public SceneRenderModeWindow(SceneView sceneView) { m_SceneView = sceneView; } public override Vector2 GetWindowSize() { return new Vector2(windowWidth, windowHeight); } public override void OnGUI(Rect rect) { if (m_SceneView == null || m_SceneView.sceneViewState == null) return; // We do not use the layout event if (Event.current.type == EventType.Layout) return; Draw(rect.width); if (GUI.changed) { GUIUtility.ExitGUI(); editorWindow.RepaintImmediately(); GUI.changed = false; // Reset the changed flag } // Use mouse move so we get hover state correctly in the menu item rows if (Event.current.type == EventType.MouseMove) Event.current.Use(); // Escape closes the window if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) { editorWindow.Close(); GUIUtility.ExitGUI(); } } private void DrawSeparator(ref Rect rect) { var labelRect = rect; labelRect.x += kHeaderHorizontalPadding; labelRect.y += kSeparatorHeight; labelRect.width -= kHeaderHorizontalPadding * 2; labelRect.height = kSeparatorHeight; GUI.Label(labelRect, GUIContent.none, Styles.s_Separator); rect.y += kSeparatorHeight; } //Opens render debugger window located at \newSRP\unity\Packages\com.unity.render-pipelines.core\Editor\Debugging\DebugWindow.cs private void DrawRenderingDebuggerShortCut(Rect rect) { var labelRect = rect; labelRect.y += kHeaderVerticalPadding; EditorGUI.LabelField(labelRect,string.Empty, Styles.s_MenuItem); labelRect.x -= kHeaderHorizontalPadding; var debuggerLabelSize = EditorStyles.foldout.CalcSize(Styles.s_DebuggerLabel); labelRect.height = debuggerLabelSize.y; if (GUI.Button(labelRect, Styles.s_DebuggerLabel, Styles.s_MenuItem)) { EditorApplication.ExecuteMenuItem("Window/Analysis/Rendering Debugger"); editorWindow.Close(); } rect.y += labelRect.height; } private void Draw(float listElementWidth) { var drawPos = new Rect(0, 0, listElementWidth, EditorGUI.kSingleLineHeight); string lastSection = null; foreach (SceneView.CameraMode mode in SceneView.userDefinedModes.OrderBy(mode => mode.section) .Concat(Styles.sBuiltinCameraModes)) { if (!mode.show) continue; // Draw separators and headers if (mode.drawMode != DrawCameraMode.UserDefined && !m_SceneView.IsCameraDrawModeSupported(mode)) // Hide unsupported items and headers continue; if (lastSection != mode.section) { lastSection = mode.section; if (!foldoutStates.ContainsKey(lastSection)) { foldoutStates.Add(lastSection, true); } bool previousState = foldoutStates[lastSection]; Rect foldoutRect = new Rect(drawPos.x, drawPos.y, drawPos.width, EditorGUI.kSingleLineHeight); EditorGUI.LabelField(foldoutRect,string.Empty, Styles.s_MenuItem); foldoutStates[lastSection] = EditorGUI.Foldout(foldoutRect, foldoutStates[lastSection], EditorGUIUtility.TextContent(lastSection), true); drawPos.y += EditorGUI.kSingleLineHeight; if (previousState != foldoutStates[lastSection]) { UpdateWindowSize(); } } if (foldoutStates[lastSection]) { using (new EditorGUI.DisabledScope(!m_SceneView.IsCameraDrawModeEnabled(mode))) { using (new EditorGUI.IndentLevelScope()) { DoBuiltinMode(ref drawPos, mode); } } } } if (GraphicsSettings.isScriptableRenderPipelineEnabled) { DrawSeparator(ref drawPos); DrawRenderingDebuggerShortCut(drawPos); } } private void UpdateWindowSize() { if (editorWindow != null && Event.current.type != EventType.Layout) { float newWindowHeight = RecalculateWindowHeight(); editorWindow.minSize = new Vector2(windowWidth, newWindowHeight); editorWindow.maxSize = new Vector2(windowWidth, newWindowHeight); } } private float RecalculateWindowHeight() { int headers = 0; int modes = 0; string lastSection = null; foreach (SceneView.CameraMode mode in SceneView.userDefinedModes.OrderBy(mode => mode.section) .Concat(Styles.sBuiltinCameraModes)) { if (!mode.show) continue; if (mode.drawMode != DrawCameraMode.UserDefined && !m_SceneView.IsCameraDrawModeSupported(mode)) continue; if (lastSection != mode.section) { headers++; lastSection = mode.section; } if (foldoutStates.ContainsKey(lastSection) && foldoutStates[lastSection]) { modes++; } } return UpdatedHeight(headers, modes, GraphicsSettings.isScriptableRenderPipelineEnabled); } private float UpdatedHeight(int headers, int modes, bool isSRP) { int separators = headers - 1; return ((headers + modes + (isSRP ? 1 : 0)) * EditorGUI.kSingleLineHeight) + (kSeparatorHeight * separators); } bool IsModeEnabled(DrawCameraMode mode) { return m_SceneView.IsCameraDrawModeEnabled(GetBuiltinCameraMode(mode)); } void DoBuiltinMode(ref Rect rect, SceneView.CameraMode mode) { using (new EditorGUI.DisabledScope(!m_SceneView.CheckDrawModeForRenderingPath(mode.drawMode))) { EditorGUI.BeginChangeCheck(); GUI.Toggle(rect, m_SceneView.cameraMode == mode, EditorGUIUtility.TextContent(mode.name), Styles.s_MenuItem); if (EditorGUI.EndChangeCheck()) { m_SceneView.cameraMode = mode; m_SceneView.Repaint(); GUIUtility.ExitGUI(); } rect.y += EditorGUI.kSingleLineHeight; } } public static GUIContent GetGUIContent(DrawCameraMode drawCameraMode) { if (drawCameraMode == DrawCameraMode.UserDefined) return GUIContent.none; return EditorGUIUtility.TextContent(Styles.sBuiltinCameraModes .Single(mode => mode.drawMode == drawCameraMode).name); } internal static SceneView.CameraMode GetBuiltinCameraMode(DrawCameraMode drawMode) { if (drawMode == DrawCameraMode.Normal) drawMode = DrawCameraMode.Textured; return Styles.sBuiltinCameraModes.Single(mode => mode.drawMode == drawMode); } internal static bool DrawCameraModeExists(DrawCameraMode drawMode) { foreach (var mode in Styles.sBuiltinCameraModes) { if (mode.drawMode == drawMode) { return true; } } return false; } } } ================================================ FILE: Editor/Mono/Annotation/SceneViewCameraWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditor.ShortcutManagement; using UnityEngine; namespace UnityEditor { public class SceneViewCameraWindow : PopupWindowContent { static class Styles { public static readonly GUIContent copyPlacementLabel = EditorGUIUtility.TrTextContent("Copy Placement"); public static readonly GUIContent pastePlacementLabel = EditorGUIUtility.TrTextContent("Paste Placement"); public static readonly GUIContent copySettingsLabel = EditorGUIUtility.TrTextContent("Copy Settings"); public static readonly GUIContent pasteSettingsLabel = EditorGUIUtility.TrTextContent("Paste Settings"); public static readonly GUIContent resetSettingsLabel = EditorGUIUtility.TrTextContent("Reset Settings"); public static readonly GUIContent cameraSpeedRange = EditorGUIUtility.TrTextContent(" "); public static readonly GUIStyle settingsArea; static Styles() { settingsArea = new GUIStyle { border = new RectOffset(4, 4, 4, 4), }; } } readonly SceneView m_SceneView; GUIContent m_CameraSpeedSliderContent; GUIContent m_AccelerationEnabled; GUIContent[] m_MinMaxContent; GUIContent m_FieldOfView; GUIContent m_DynamicClip; GUIContent m_OcclusionCulling; GUIContent m_EasingEnabled; GUIContent m_SceneCameraLabel = EditorGUIUtility.TrTextContent("Scene Camera"); GUIContent m_NavigationLabel = EditorGUIUtility.TrTextContent("Navigation"); readonly string k_ClippingPlaneWarning = L10n.Tr("Using extreme values between the near and far planes may cause rendering issues. In general, to get better precision move the Near plane as far as possible."); const int kFieldCount = 13; const int kWindowWidth = 290; const int kContentPadding = 4; const int k_HeaderSpacing = 2; const int kWindowHeight = ((int)EditorGUI.kSingleLineHeight) * kFieldCount + kContentPadding * 2 + k_HeaderSpacing * 2; const float kPrefixLabelWidth = 120f; // FOV values chosen to be the smallest and largest before extreme visual corruption const float k_MinFieldOfView = 4f; const float k_MaxFieldOfView = 120f; Vector2 m_WindowSize; Vector2 m_Scroll; public static event Action additionalSettingsGui; public override Vector2 GetWindowSize() { return m_WindowSize; } public SceneViewCameraWindow(SceneView sceneView) { m_SceneView = sceneView; m_CameraSpeedSliderContent = EditorGUIUtility.TrTextContent("Camera Speed", "The current speed of the camera in the Scene view."); m_AccelerationEnabled = EditorGUIUtility.TrTextContent("Camera Acceleration", "Check this to enable acceleration when moving the camera. When enabled, camera speed is evaluated as a modifier. With acceleration disabled, the camera is accelerated to the Camera Speed."); m_FieldOfView = EditorGUIUtility.TrTextContent("Field of View", "The height of the camera's view angle. Measured in degrees vertically, or along the local Y axis."); m_DynamicClip = EditorGUIUtility.TrTextContent("Dynamic Clipping", "Check this to enable camera's near and far clipping planes to be calculated relative to the viewport size of the Scene."); m_OcclusionCulling = EditorGUIUtility.TrTextContent("Occlusion Culling", "Check this to enable occlusion culling in the Scene view. Occlusion culling disables rendering of objects when they\'re not currently seen by the camera because they\'re hidden (occluded) by other objects."); m_EasingEnabled = EditorGUIUtility.TrTextContent("Camera Easing", "Check this to enable camera movement easing. This makes the camera ease in when it starts moving and ease out when it stops."); m_WindowSize = new Vector2(kWindowWidth, kWindowHeight); m_MinMaxContent = new[] { EditorGUIUtility.TrTextContent("Min", $"The minimum speed of the camera in the Scene view. Valid values are between [{SceneView.CameraSettings.kAbsoluteSpeedMin}, {SceneView.CameraSettings.kAbsoluteSpeedMax - 1}]."), EditorGUIUtility.TrTextContent("Max", $"The maximum speed of the camera in the Scene view. Valid values are between [{SceneView.CameraSettings.kAbsoluteSpeedMin + .0001f}, {SceneView.CameraSettings.kAbsoluteSpeedMax}].") }; } public override void OnGUI(Rect rect) { if (m_SceneView == null || m_SceneView.sceneViewState == null) return; Draw(); // Escape closes the window if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) { editorWindow.Close(); GUIUtility.ExitGUI(); } } void Draw() { var settings = m_SceneView.cameraSettings; m_Scroll = GUILayout.BeginScrollView(m_Scroll); GUILayout.BeginVertical(Styles.settingsArea); EditorGUI.BeginChangeCheck(); GUILayout.Space(k_HeaderSpacing); GUILayout.BeginHorizontal(); GUILayout.Label(m_SceneCameraLabel, EditorStyles.boldLabel); GUILayout.FlexibleSpace(); if (GUILayout.Button(EditorGUI.GUIContents.titleSettingsIcon, EditorStyles.iconButton)) ShowContextMenu(m_SceneView); GUILayout.EndHorizontal(); EditorGUIUtility.labelWidth = kPrefixLabelWidth; // fov isn't applicable in orthographic mode, and orthographic size is controlled by the user zoom using (new EditorGUI.DisabledScope(m_SceneView.orthographic)) { settings.fieldOfView = EditorGUILayout.Slider(m_FieldOfView, settings.fieldOfView, k_MinFieldOfView, k_MaxFieldOfView); } settings.dynamicClip = EditorGUILayout.Toggle(m_DynamicClip, settings.dynamicClip); using (new EditorGUI.DisabledScope(settings.dynamicClip)) { float near = settings.nearClip, far = settings.farClip; DrawStackedFloatField(EditorGUI.s_ClipingPlanesLabel, EditorGUI.s_NearAndFarLabels[0], EditorGUI.s_NearAndFarLabels[1], ref near, ref far, EditorGUI.kNearFarLabelsWidth); settings.SetClipPlanes(near, far); if(far/near > 10000000 || near < 0.0001f) EditorGUILayout.HelpBox(k_ClippingPlaneWarning,MessageType.Warning); } settings.occlusionCulling = EditorGUILayout.Toggle(m_OcclusionCulling, settings.occlusionCulling); if (EditorGUI.EndChangeCheck()) m_SceneView.Repaint(); EditorGUILayout.Space(k_HeaderSpacing); GUILayout.Label(m_NavigationLabel, EditorStyles.boldLabel); settings.easingEnabled = EditorGUILayout.Toggle(m_EasingEnabled, settings.easingEnabled); settings.accelerationEnabled = EditorGUILayout.Toggle(m_AccelerationEnabled, settings.accelerationEnabled); EditorGUI.BeginChangeCheck(); float min = settings.speedMin, max = settings.speedMax, speed = settings.RoundSpeedToNearestSignificantDecimal(settings.speed); speed = EditorGUILayout.Slider(m_CameraSpeedSliderContent, speed, min, max); if (EditorGUI.EndChangeCheck()) settings.speed = settings.RoundSpeedToNearestSignificantDecimal(speed); EditorGUI.BeginChangeCheck(); DrawStackedFloatField(Styles.cameraSpeedRange, m_MinMaxContent[0], m_MinMaxContent[1], ref min, ref max, EditorGUI.kNearFarLabelsWidth); if (EditorGUI.EndChangeCheck()) settings.SetSpeedMinMax(min, max); EditorGUIUtility.labelWidth = 0f; if (additionalSettingsGui != null) { EditorGUILayout.Space(k_HeaderSpacing); additionalSettingsGui(m_SceneView); if (Event.current.type == EventType.Repaint) m_WindowSize.y = Math.Min(GUILayoutUtility.GetLastRect().yMax + kContentPadding * 2, kWindowHeight * 3); } GUILayout.EndVertical(); GUILayout.EndScrollView(); } static void DrawStackedFloatField(GUIContent label, GUIContent firstFieldLabel, GUIContent secondFieldLabel, ref float near, ref float far, float propertyLabelsWidth, params GUILayoutOption[] options) { bool hasLabel = EditorGUI.LabelHasContent(label); float height = EditorGUI.kSingleLineHeight * 2 + EditorGUI.kVerticalSpacingMultiField; Rect r = EditorGUILayout.GetControlRect(hasLabel, height, EditorStyles.numberField, options); Rect fieldPosition = EditorGUI.PrefixLabel(r, label); fieldPosition.height = EditorGUI.kSingleLineHeight; float oldLabelWidth = EditorGUIUtility.labelWidth; int oldIndentLevel = EditorGUI.indentLevel; EditorGUIUtility.labelWidth = propertyLabelsWidth; EditorGUI.indentLevel = 0; near = EditorGUI.FloatField(fieldPosition, firstFieldLabel, near); fieldPosition.y += EditorGUI.kSingleLineHeight + EditorGUI.kVerticalSpacingMultiField; far = EditorGUI.FloatField(fieldPosition, secondFieldLabel, far); EditorGUI.indentLevel = oldIndentLevel; EditorGUIUtility.labelWidth = oldLabelWidth; } internal static void ShowContextMenu(SceneView view) { var menu = new GenericMenu(); menu.AddItem(Styles.copyPlacementLabel, false, () => CopyPlacement(view)); if (CanPastePlacement()) menu.AddItem(Styles.pastePlacementLabel, false, () => PastePlacement(view)); else menu.AddDisabledItem(Styles.pastePlacementLabel); menu.AddItem(Styles.copySettingsLabel, false, () => CopySettings(view)); if (Clipboard.HasCustomValue()) menu.AddItem(Styles.pasteSettingsLabel, false, () => PasteSettings(view)); else menu.AddDisabledItem(Styles.pasteSettingsLabel); menu.AddItem(Styles.resetSettingsLabel, false, () => ResetSettings(view)); menu.ShowAsContext(); } // ReSharper disable once UnusedMember.Local - called by a shortcut [Shortcut("Camera/Copy Placement")] static void CopyPlacementShortcut() { // if we are interacting with a game view, copy the main camera placement var playView = PlayModeView.GetLastFocusedPlayModeView(); if (playView != null && (EditorWindow.focusedWindow == playView || EditorWindow.mouseOverWindow == playView)) { var cam = Camera.main; if (cam != null) Clipboard.SetCustomValue(new TransformWorldPlacement(cam.transform)); } // otherwise copy the last active scene view placement else { var sceneView = SceneView.lastActiveSceneView; if (sceneView != null) CopyPlacement(sceneView); } } static void CopyPlacement(SceneView view) { Clipboard.SetCustomValue(new TransformWorldPlacement(view.camera.transform)); } // ReSharper disable once UnusedMember.Local - called by a shortcut [Shortcut("Camera/Paste Placement")] static void PastePlacementShortcut() { var sceneView = SceneView.lastActiveSceneView; if (sceneView == null) return; if (CanPastePlacement()) PastePlacement(sceneView); } static bool CanPastePlacement() { return Clipboard.HasCustomValue(); } static void PastePlacement(SceneView view) { var tr = view.camera.transform; var placement = Clipboard.GetCustomValue(); tr.position = placement.position; tr.rotation = placement.rotation; tr.localScale = placement.scale; // Similar to what AlignViewToObject does, except we need to do that instantly // in case the shortcut key was pressed while FPS camera controls (right click drag) // were active. view.size = 10; view.LookAt(tr.position + tr.forward * view.cameraDistance, tr.rotation, view.size, view.orthographic, true); view.Repaint(); } static void CopySettings(SceneView view) { Clipboard.SetCustomValue(view.cameraSettings); } static void PasteSettings(SceneView view) { view.cameraSettings = Clipboard.GetCustomValue(); view.Repaint(); } static void ResetSettings(SceneView view) { view.ResetCameraSettings(); view.Repaint(); } } } ================================================ FILE: Editor/Mono/ArrayUtility.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections; using System.Collections.Generic; namespace UnityEditor { // Helpers for builtin arrays ... // These are O(n) operations (where List() is used) - the arrays are actually copied (http://msdn.microsoft.com/en-us/library/fkbw11z0.aspx) // but its pretty helpful for now public static class ArrayUtility { //appends ''item'' to the end of ''array'' public static void Add(ref T[] array, T item) { System.Array.Resize(ref array, array.Length + 1); array[array.Length - 1] = item; } //compares two arrays public static bool ArrayEquals(T[] lhs, T[] rhs) { if (lhs == null || rhs == null) return lhs == rhs; if (lhs.Length != rhs.Length) return false; for (int i = 0; i < lhs.Length; i++) { if (!lhs[i].Equals(rhs[i])) return false; } return true; } //compares two arrays public static bool ArrayReferenceEquals(T[] lhs, T[] rhs) { if (lhs == null || rhs == null) return lhs == rhs; if (lhs.Length != rhs.Length) return false; for (int i = 0; i < lhs.Length; i++) { if (!object.ReferenceEquals(lhs[i], rhs[i])) return false; } return true; } //appends items to the end of array public static void AddRange(ref T[] array, T[] items) { int size = array.Length; System.Array.Resize(ref array, array.Length + items.Length); for (int i = 0; i < items.Length; i++) array[size + i] = items[i]; } //inserts item ''item'' at position ''index'' public static void Insert(ref T[] array, int index, T item) { ArrayList a = new ArrayList(); a.AddRange(array); a.Insert(index, item); array = a.ToArray(typeof(T)) as T[]; } //removes ''item'' from ''array'' public static void Remove(ref T[] array, T item) { List newList = new List(array); newList.Remove(item); array = newList.ToArray(); } public static List FindAll(T[] array, Predicate match) { List list = new List(array); return list.FindAll(match); } public static T Find(T[] array, Predicate match) { List list = new List(array); return list.Find(match); } //Find the index of the first element that satisfies the predicate public static int FindIndex(T[] array, Predicate match) { List list = new List(array); return list.FindIndex(match); } //index of first element with value ''value'' public static int IndexOf(T[] array, T value) { List list = new List(array); return list.IndexOf(value); } //index of the last element with value ''value'' public static int LastIndexOf(T[] array, T value) { List list = new List(array); return list.LastIndexOf(value); } //remove element at position ''index'' public static void RemoveAt(ref T[] array, int index) { List list = new List(array); list.RemoveAt(index); array = list.ToArray(); } //determines if the array contains the item public static bool Contains(T[] array, T item) { List list = new List(array); return list.Contains(item); } //Clears the array public static void Clear(ref T[] array) { System.Array.Clear(array, 0, array.Length); System.Array.Resize(ref array, 0); } } } ================================================ FILE: Editor/Mono/AssemblyHelper.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.IO; using System.Linq; using System.Reflection; using System.Collections.Generic; using Mono.Cecil; using UnityEditor.Modules; using UnityEditorInternal; using UnityEngine; using System.Runtime.InteropServices; using UnityEngine.Scripting; using Debug = UnityEngine.Debug; using Unity.Profiling; using UnityEditor.AssetImporters; using UnityEditor.Scripting.ScriptCompilation; using UnityEngine.Scripting.APIUpdating; namespace UnityEditor { internal class AssemblyHelper { static Dictionary managedToDllType = new Dictionary(); static BuildPlayerDataExtractor m_BuildPlayerDataExtractor = new BuildPlayerDataExtractor(); static public bool IsUnityEngineModule(AssemblyDefinition assembly) { return assembly.CustomAttributes.Any(a => a.AttributeType.FullName == typeof(UnityEngineModuleAssembly).FullName); } static public bool IsUnityEngineModule(Assembly assembly) { return assembly.GetCustomAttributes(typeof(UnityEngineModuleAssembly), false).Length > 0; } public static string[] GetDefaultAssemblySearchPaths() { // Add the path to all available precompiled assemblies var target = EditorUserBuildSettings.activeBuildTarget; var precompiledAssemblyPaths = InternalEditorUtility.GetPrecompiledAssemblyPaths(); HashSet searchPaths = new HashSet(precompiledAssemblyPaths); var precompiledAssemblies = InternalEditorUtility.GetUnityAssemblies(true, target); foreach (var asm in precompiledAssemblies) searchPaths.Add(Path.GetDirectoryName(asm.Path)); // Add Unity compiled assembly output directory. // Required for MonoBehaviour derived types like UIBehaviour that // were previous in a precompiled UnityEngine.UI.dll, but are now // compiled in a package. searchPaths.Add(InternalEditorUtility.GetEditorScriptAssembliesPath()); return searchPaths.ToArray(); } [RequiredByNativeCode] public static bool ExtractAllClassesThatAreUserExtendedScripts(string path, out string[] classNamesArray, out string[] classNameSpacesArray, out string[] movedFromNamespacesArray) { var typesDerivedFromMonoBehaviour = TypeCache.GetTypesDerivedFrom(); var typesDerivedFromScriptableObject = TypeCache.GetTypesDerivedFrom(); var typesDerivedFromScriptedImporter = TypeCache.GetTypesDerivedFrom(); var fileName = Path.GetFileName(path); IEnumerable userTypes = typesDerivedFromMonoBehaviour.Where(x => Path.GetFileName(x.Assembly.Location) == fileName); userTypes = userTypes .Concat(typesDerivedFromScriptableObject.Where(x => Path.GetFileName(x.Assembly.Location) == fileName)) .Concat(typesDerivedFromScriptedImporter.Where(x => Path.GetFileName(x.Assembly.Location) == fileName)).ToList(); List classNames = new List(userTypes.Count()); List nameSpaces = new List(userTypes.Count()); List originalNamespaces = new List(userTypes.Count()); string pathToAssembly = null; foreach (var userType in userTypes) { if (string.IsNullOrEmpty(pathToAssembly)) pathToAssembly = Path.GetFullPath(userType.Assembly.Location); classNames.Add(userType.Name); nameSpaces.Add(userType.Namespace); var movedFromAttribute = userType.GetCustomAttribute(); originalNamespaces.Add(movedFromAttribute?.data.nameSpace); } classNamesArray = classNames.ToArray(); classNameSpacesArray = nameSpaces.ToArray(); movedFromNamespacesArray = originalNamespaces.ToArray(); return !Utility.IsPathsEqual(pathToAssembly, path); } /// Extract information about all types in the specified assembly, searchDirs might be used to resolve dependencies. [RequiredByNativeCode] static public AssemblyInfoManaged[] ExtractAssemblyTypeInfo(bool isEditor) { var extractAssemblyTypeInfo = m_BuildPlayerDataExtractor.ExtractAssemblyTypeInfo(isEditor); return extractAssemblyTypeInfo; } static public AssemblyInfoManaged[] ExtractAssemblyTypeInfoFromFiles(string[] typeDbJsonPaths) { return m_BuildPlayerDataExtractor.ExtractAssemblyTypeInfoFromFiles(typeDbJsonPaths); } [StructLayout(LayoutKind.Sequential)] public struct RuntimeInitializeOnLoadMethodsData { public RuntimeInitializeClassInfo[] classInfos; public int methodsCount; } [RequiredByNativeCode] public static void ExtractPlayerRuntimeInitializeOnLoadMethods(string jsonPath) { try { m_BuildPlayerDataExtractor.ExtractPlayerRuntimeInitializeOnLoadMethods(jsonPath); } catch (Exception exception) { Debug.LogError($"Failed extracting RuntimeInitializeOnLoadMethods. Player will not be able to execute RuntimeInitializeOnLoadMethods: {exception.Message}{Environment.NewLine}{exception.StackTrace}"); } } internal static Type[] GetTypesFromAssembly(Assembly assembly) { if (assembly == null) return new Type[] {}; try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException) { return new Type[] {}; } } public static bool IsManagedAssembly(string file) { bool isManagedDll; if (managedToDllType.TryGetValue(file, out isManagedDll)) { return isManagedDll; } var res = InternalEditorUtility.IsDotNetDll(file); managedToDllType[file] = res; return res; } public static bool IsInternalAssembly(string file) { return ModuleUtils.GetAdditionalReferencesForUserScripts().Any(p => p.Equals(file)); } /// /// Performs a depth-first-search topological sort on the input assemblies, /// based on the outgoing assembly references from each assembly. The /// returned list is sorted such that assembly A appears before any /// assemblies that depend (directly or indirectly) on assembly A. /// internal static Assembly[] TopologicalSort(Assembly[] assemblies) { using var _ = new ProfilerMarker("SortAssembliesTopologically").Auto(); var assembliesByName = new Dictionary(assemblies.Length); for (var i = 0; i < assemblies.Length; i++) { assembliesByName[assemblies[i].GetName().Name] = i; } var result = new Assembly[assemblies.Length]; var resultIndex = 0; var visited = new TopologicalSortVisitStatus[assemblies.Length]; void VisitAssembly(int index) { var visitStatus = visited[index]; switch (visitStatus) { case TopologicalSortVisitStatus.Visiting: // We have a cyclic dependency between assemblies. This should really be an error, but... // We need to allow cyclic dependencies between assemblies, because the rest of Unity allows them. // For example if you make an assembly override for UnityEngine.AccessibilityModule.dll, // it will reference UnityEditor.CoreModule.dll, which in turn references // UnityEngine.AccessibilityModule.dll... and that doesn't trigger an error. // The topological sort won't be correct in this case, but it's better than erroring-out. break; case TopologicalSortVisitStatus.NotVisited: visited[index] = TopologicalSortVisitStatus.Visiting; var assembly = assemblies[index]; var assemblyReferences = assembly.GetReferencedAssemblies(); foreach (var assemblyReference in assemblyReferences) { // It's okay if we can't resolve the assembly. It just means that the referenced assembly // is not in the input set of assemblies, so we wouldn't be able to sort it anyway. if (assembliesByName.TryGetValue(assemblyReference.Name, out var referencedAssembly)) { VisitAssembly(referencedAssembly); } } visited[index] = TopologicalSortVisitStatus.Visited; result[resultIndex++] = assembly; break; } } for (var i = 0; i < assemblies.Length; i++) { VisitAssembly(i); } return result; } private enum TopologicalSortVisitStatus : byte { NotVisited, Visiting, Visited } } } ================================================ FILE: Editor/Mono/AssemblyInfo/AssemblyInfo.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License // NOTE: the build system includes this source file in ALL Editor modules using System.Runtime.CompilerServices; using UnityEngine; [assembly: AssemblyIsEditorAssembly] // TODO: over time, remove the InternalsVisibleTo attributes from this section // You can start by moving them to EditorCoreModuleAssemblyInfo.cs to reduce internal visibility // To remove a line in there, the target assembly must not depend on _any_ internal API from built-in Editor modules // ADD_NEW_PLATFORM_HERE [assembly: InternalsVisibleTo("Unity.LiveNotes")] [assembly: InternalsVisibleTo("Unity.Audio.Tests")] [assembly: InternalsVisibleTo("Unity.Burst")] [assembly: InternalsVisibleTo("Unity.Burst.Editor")] [assembly: InternalsVisibleTo("Unity.Cloud.Collaborate.Editor")] [assembly: InternalsVisibleTo("Unity.CollabProxy.Editor")] [assembly: InternalsVisibleTo("Unity.CollabProxy.EditorTests")] [assembly: InternalsVisibleTo("Unity.CollabProxy.UI")] [assembly: InternalsVisibleTo("Unity.CollabProxy.UI.Tests")] [assembly: InternalsVisibleTo("Unity.CollabProxy.Client")] [assembly: InternalsVisibleTo("Unity.CollabProxy.Client.Tests")] [assembly: InternalsVisibleTo("UnityEditor.Advertisements")] [assembly: InternalsVisibleTo("Unity.PackageManager")] [assembly: InternalsVisibleTo("Unity.PackageManagerStandalone")] [assembly: InternalsVisibleTo("Unity.AndroidBuildPipeline")] [assembly: InternalsVisibleTo("Unity.Automation")] [assembly: InternalsVisibleTo("UnityEngine.Common")] [assembly: InternalsVisibleTo("Unity.PureCSharpTests")] [assembly: InternalsVisibleTo("Unity.IntegrationTests")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Android")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Android.CommonUtils")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Animation")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.AssetImporting")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.BuildPipeline")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Builds")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.CrashReporting")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.DeploymentTargets")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.EditorApplication")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.EditorUI")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.GameCore")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.GameView")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Lightmapping")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Metro")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Misc")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.PackageManager")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Profiler")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.PS4")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.PS5")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Switch")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Rendering")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.SceneVisibility")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.ScriptCompilation")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.ShortcutManager")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.UIToolkit")] [assembly: InternalsVisibleTo("Unity.IntegrationTests.Framework.PackageManager")] [assembly: InternalsVisibleTo("Unity.DeploymentTests.Services")] [assembly: InternalsVisibleTo("Unity.PerformanceIntegrationTests")] [assembly: InternalsVisibleTo("Unity.Timeline.Editor")] [assembly: InternalsVisibleTo("Unity.PackageManagerUI.Develop.Editor")] [assembly: InternalsVisibleTo("Unity.DeviceSimulator.Editor")] [assembly: InternalsVisibleTo("Unity.Timeline.EditorTests")] [assembly: InternalsVisibleTo("UnityEditor.Graphs")] [assembly: InternalsVisibleTo("UnityEditor.UWP.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.iOS.Extensions.Common")] [assembly: InternalsVisibleTo("UnityEditor.Apple.Extensions.Common")] [assembly: InternalsVisibleTo("UnityEditor.iOS.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.VisionOS.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.AppleTV.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.Android.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.XboxOne.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.PS4.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.PS5.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.Switch.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.WebGL.Extensions")] [assembly: InternalsVisibleTo("Unity.Automation.Players.WebGL")] [assembly: InternalsVisibleTo("Unity.WebGL.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.LinuxStandalone.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.CloudRendering.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.EmbeddedLinux.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.QNX.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.Kepler.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.WindowsStandalone.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.OSXStandalone.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.Lumin.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.GameCoreScarlett.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.GameCoreXboxOne.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.GameCoreCommon.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.Networking")] [assembly: InternalsVisibleTo("UnityEngine.Networking")] [assembly: InternalsVisibleTo("Unity.Analytics.Editor")] [assembly: InternalsVisibleTo("UnityEditor.Analytics")] [assembly: InternalsVisibleTo("UnityEditor.Purchasing")] [assembly: InternalsVisibleTo("UnityEditor.Lumin")] [assembly: InternalsVisibleTo("UnityEditor.Switch.Extensions")] [assembly: InternalsVisibleTo("UnityEditor.EditorTestsRunner")] [assembly: InternalsVisibleTo("UnityEditor.TestRunner")] [assembly: InternalsVisibleTo("UnityEditor.TestRunner.Tests")] [assembly: InternalsVisibleTo("Unity.Compiler.Client")] [assembly: InternalsVisibleTo("ExternalCSharpCompiler")] [assembly: InternalsVisibleTo("UnityEngine.TestRunner")] [assembly: InternalsVisibleTo("UnityEditor.VR")] [assembly: InternalsVisibleTo("Unity.RuntimeTests")] [assembly: InternalsVisibleTo("Unity.RuntimeTests.Framework")] [assembly: InternalsVisibleTo("Assembly-CSharp-Editor-firstpass-testable")] [assembly: InternalsVisibleTo("Assembly-CSharp-Editor-testable")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: InternalsVisibleTo("UnityEditor.InteractiveTutorialsFramework")] [assembly: InternalsVisibleTo("UnityEditor.Networking")] [assembly: InternalsVisibleTo("UnityEditor.UI")] [assembly: InternalsVisibleTo("UnityEditor.AR")] [assembly: InternalsVisibleTo("UnityEditor.SpatialTracking")] [assembly: InternalsVisibleTo("Unity.WindowsMRAutomation")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.001")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.002")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.003")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.004")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.005")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.006")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.007")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.008")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.009")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.010")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.011")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.012")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.013")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.014")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.015")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.016")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.017")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.018")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.019")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.020")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.021")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.022")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.023")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridge.024")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridgeDev.001")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridgeDev.002")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridgeDev.003")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridgeDev.004")] [assembly: InternalsVisibleTo("Unity.InternalAPIEditorBridgeDev.005")] [assembly: InternalsVisibleTo("Unity.XR.Remoting.Editor")] [assembly: InternalsVisibleTo("UnityEngine.Common")] [assembly: InternalsVisibleTo("Unity.UI.Builder.Editor")] [assembly: InternalsVisibleTo("Unity.UI.TestFramework.Editor.Tests")] // for UI Test Framework [assembly: InternalsVisibleTo("UnityEditor.UIBuilderModule")] [assembly: InternalsVisibleTo("Unity.UI.Builder.EditorTests")] [assembly: InternalsVisibleTo("Unity.GraphViewTestUtilities.Editor")] [assembly: InternalsVisibleTo("Unity.ProBuilder.Editor")] [assembly: InternalsVisibleTo("Unity.2D.Sprite.Editor")] [assembly: InternalsVisibleTo("Unity.2D.Sprite.EditorTests")] [assembly: InternalsVisibleTo("Unity.2D.Tilemap.Editor")] [assembly: InternalsVisibleTo("Unity.2D.Tilemap.EditorTests")] [assembly: InternalsVisibleTo("Unity.PackageCleanConsoleTest.Editor")] [assembly: InternalsVisibleTo("Unity.TextCore.Editor.Tests")] [assembly: InternalsVisibleTo("UnityEditor.TextCoreTextEngineModule")] [assembly: InternalsVisibleTo("Unity.TextMeshPro.Editor")] [assembly: InternalsVisibleTo("Unity.Animation.Editor.AnimationWindow")] [assembly: InternalsVisibleTo("Unity.VisualEffectGraph.Editor")] [assembly: InternalsVisibleTo("Unity.Testing.VisualEffectGraph.EditorTests")] [assembly: InternalsVisibleTo("Unity.VisualEffectGraph.EditorTests")] [assembly: InternalsVisibleTo("Unity.RenderPipelines.Multiple_SRP.EditorTests")] [assembly: InternalsVisibleTo("Unity.ShaderGraph.Editor")] [assembly: InternalsVisibleTo("Unity.SceneTemplate.Editor")] [assembly: InternalsVisibleTo("com.unity.purchasing.udp.Editor")] [assembly: InternalsVisibleTo("com.unity.search.extensions.editor")] [assembly: InternalsVisibleTo("UnityEditor.Android.Extensions")] [assembly: InternalsVisibleTo("Unity.Entities.Build")] [assembly: InternalsVisibleTo("Unity.Muse.Chat.Bridge")] [assembly: InternalsVisibleTo("Unity.AI.Assistant.Bridge.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Playmode.Editor.Bridge")] [assembly: InternalsVisibleTo("Unity.DedicatedServer.Editor.Bridge")] [assembly: InternalsVisibleTo("Unity.Scenes")] [assembly: InternalsVisibleTo("UnityEditor.Switch.Tests")] [assembly: InternalsVisibleTo("UnityEditor.BuildProfileModule.Tests")] [assembly: InternalsVisibleTo("UnityEditor.PS4.Tests")] [assembly: InternalsVisibleTo("UnityEditor.PS5.Tests")] [assembly: InternalsVisibleTo("Unity.Automation.Players.EmbeddedLinux")] [assembly: InternalsVisibleTo("Unity.Automation.Players.QNX")] [assembly: InternalsVisibleTo("Unity.GraphToolsAuthoringFramework.InternalEditorBridge")] // Module test assemblies [assembly: InternalsVisibleTo("Unity.Modules.iOSExtensions.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Modules.Licensing.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Modules.PlatformIcons.Tests.Editor")] // This should move with the AnimationWindow to a module at some point [assembly: InternalsVisibleTo("Unity.Modules.Animation.AnimationWindow.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Modules.Physics.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Tests.Shared")] // Test Assemblies [assembly: InternalsVisibleTo("Unity.Core.UnityEvent.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Core.Scripting.AssemblyVersion.Tests.Editor")] ================================================ FILE: Editor/Mono/AssemblyReloadEvents.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine.Scripting; namespace UnityEditor { public static class AssemblyReloadEvents { public delegate void AssemblyReloadCallback(); public static event AssemblyReloadCallback beforeAssemblyReload { add => m_BeforeAssemblyReloadEvent.Add(value); remove => m_BeforeAssemblyReloadEvent.Remove(value); } private static EventWithPerformanceTracker m_BeforeAssemblyReloadEvent = new EventWithPerformanceTracker($"{nameof(AssemblyReloadEvents)}.{nameof(beforeAssemblyReload)}"); public static event AssemblyReloadCallback afterAssemblyReload { add => m_AfterAssemblyReloadEvent.Add(value); remove => m_AfterAssemblyReloadEvent.Remove(value); } private static EventWithPerformanceTracker m_AfterAssemblyReloadEvent = new EventWithPerformanceTracker($"{nameof(AssemblyReloadEvents)}.{nameof(afterAssemblyReload)}"); [RequiredByNativeCode] static void OnBeforeAssemblyReload() { if (!m_BeforeAssemblyReloadEvent.hasSubscribers) return; using var scope = new ProgressScope("OnBeforeAssemblyReload Callback", "", forceUpdate: true); foreach (var evt in m_BeforeAssemblyReloadEvent) { scope.SetText($"{evt.Method?.DeclaringType?.FullName}.{evt.Method?.Name}", true); evt(); } } [RequiredByNativeCode] static void OnAfterAssemblyReload() { if (!m_AfterAssemblyReloadEvent.hasSubscribers) return; using var scope = new ProgressScope("OnAfterAssemblyReload Callback", "", forceUpdate: true); foreach (var evt in m_AfterAssemblyReloadEvent) { scope.SetText($"{evt.Method?.DeclaringType?.FullName}.{evt.Method?.Name}", true); evt(); } } } } ================================================ FILE: Editor/Mono/AssemblyValidation.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; using Mono.Cecil; using Unity.IO.LowLevel.Unsafe; using UnityEditor.Scripting.ScriptCompilation; using UnityEngine.Scripting; namespace UnityEditor { static class AssemblyValidation { // Keep in sync with AssemblyValidationFlags in MonoManager.cpp [Flags] public enum ErrorFlags { None = 0, ReferenceHasErrors = (1 << 0), UnresolvableReference = (1 << 1), IncompatibleWithEditor = (1 << 2), AsmdefPluginConflict = (1 << 3) } [StructLayout(LayoutKind.Sequential)] public struct Error { public ErrorFlags flags; public string message; public string assemblyPath; public void Add(ErrorFlags newFlags, string newMessage) { flags |= newFlags; if (message == null) { message = newMessage; } else { message += string.Format("\n{0}", newMessage); } } public bool HasFlag(ErrorFlags testFlags) { return (flags & testFlags) == testFlags; } public void ClearFlags(ErrorFlags clearFlags) { flags &= ~clearFlags; } } public struct AssemblyAndReferences { public int assemblyIndex; public int[] referenceIndicies; } class AssemblyResolver : BaseAssemblyResolver { readonly IDictionary cache; public AssemblyResolver() { cache = new Dictionary(StringComparer.Ordinal); } public override AssemblyDefinition Resolve(AssemblyNameReference name) { if (name == null) throw new ArgumentNullException("name"); AssemblyDefinition assembly; if (cache.TryGetValue(name.Name, out assembly)) return assembly; assembly = base.Resolve(name); cache[name.Name] = assembly; return assembly; } public void RegisterAssembly(AssemblyDefinition assembly) { if (assembly == null) throw new ArgumentNullException("assembly"); var name = assembly.Name.Name; if (cache.ContainsKey(name)) return; cache[name] = assembly; } protected override void Dispose(bool disposing) { foreach (var assembly in cache.Values) assembly.Dispose(); cache.Clear(); base.Dispose(disposing); } } [RequiredByNativeCode] public static Error[] ValidateAssemblies(string[] assemblyPaths, bool enableLogging) { var searchPaths = AssemblyHelper.GetDefaultAssemblySearchPaths(); var assemblyDefinitions = LoadAssemblyDefinitions(assemblyPaths, searchPaths); if (enableLogging) { // Prints assemblies and their references to the Editor.log PrintAssemblyDefinitions(assemblyDefinitions); foreach (var searchPath in searchPaths) { Console.WriteLine("[AssemblyValidation] Search Path: '" + searchPath + "'"); } } var errors = ValidateAssemblyDefinitions(assemblyPaths, assemblyDefinitions); return errors; } [RequiredByNativeCode] internal static Error[] ValidateRoslynAnalyzers(string[] analyzerPaths) { var readerParameters = new ReaderParameters { ReadingMode = ReadingMode.Deferred }; List errors = new List(); foreach(var analyzer in analyzerPaths) { using (var analyzerDefinition = AssemblyDefinition.ReadAssembly(analyzer, readerParameters)) { var netstandardVersion = analyzerDefinition.MainModule.AssemblyReferences.Where(r => r.Name == "netstandard").FirstOrDefault(); if (netstandardVersion != null && netstandardVersion.Version >= new Version(2, 1)) { errors.Add(new Error { assemblyPath = analyzer, flags = ErrorFlags.ReferenceHasErrors, message = $"{analyzerDefinition.Name.Name} references {netstandardVersion}. A roslyn analyzer should reference netstandard version 2.0" }); } } } return errors.ToArray(); } [RequiredByNativeCode] public static Error[] ValidateAssemblyDefinitionFiles() { var customScriptAssemblies = EditorCompilationInterface.Instance.GetCustomScriptAssemblies(); if (customScriptAssemblies.Length == 0) return null; var pluginImporters = PluginImporter.GetAllImporters(); if (pluginImporters == null || pluginImporters.Length == 0) return null; var pluginFilenameToAssetPath = new Dictionary(); foreach (var pluginImporter in pluginImporters) { var pluginAssetPath = pluginImporter.assetPath; var lowerPluginFilename = AssetPath.GetFileName(pluginAssetPath).ToLower(CultureInfo.InvariantCulture); pluginFilenameToAssetPath[lowerPluginFilename] = pluginAssetPath; } var errors = new List(); foreach (var customScriptAssembly in customScriptAssemblies) { var lowerAsmdefFilename = $"{customScriptAssembly.Name.ToLower(CultureInfo.InvariantCulture)}.dll"; string pluginPath; if (pluginFilenameToAssetPath.TryGetValue(lowerAsmdefFilename, out pluginPath)) { var error = new Error() { message = $"Plugin '{pluginPath}' has the same filename as Assembly Definition File '{customScriptAssembly.FilePath}'. Rename the assemblies to avoid hard to diagnose issues and crashes.", flags = ErrorFlags.AsmdefPluginConflict, assemblyPath = pluginPath }; errors.Add(error); } } return errors.ToArray(); } public static bool ShouldValidateReferences(string path, Dictionary allPrecompiledAssemblies) { if (!allPrecompiledAssemblies.TryGetValue(path, out var precompiledAssembly)) return true; return precompiledAssembly.Flags.HasFlag(AssemblyFlags.ValidateAssembly); } public static void PrintAssemblyDefinitions(AssemblyDefinition[] assemblyDefinitions) { foreach (var assemblyDefinition in assemblyDefinitions) { Console.WriteLine("[AssemblyValidation] Assembly: " + assemblyDefinition.Name); var assemblyReferences = GetAssemblyNameReferences(assemblyDefinition); foreach (var reference in assemblyReferences) { Console.WriteLine("[AssemblyValidation] Reference: " + reference); } } } public static Error[] ValidateAssembliesInternal(string[] assemblyPaths, string[] searchPaths) { var assemblyDefinitions = LoadAssemblyDefinitions(assemblyPaths, searchPaths); return ValidateAssemblyDefinitions(assemblyPaths, assemblyDefinitions); } public static Error[] ValidateAssemblyDefinitions(string[] assemblyPaths, AssemblyDefinition[] assemblyDefinitions) { var errors = new Error[assemblyPaths.Length]; CheckAssemblyReferences(assemblyPaths, errors, assemblyDefinitions); return errors; } public static AssemblyDefinition[] LoadAssemblyDefinitions(string[] assemblyPaths, string[] searchPaths) { var assemblyResolver = new AssemblyResolver(); foreach (var asmpath in searchPaths) assemblyResolver.AddSearchDirectory(asmpath); var readerParameters = new ReaderParameters { AssemblyResolver = assemblyResolver }; var assemblyDefinitions = new AssemblyDefinition[assemblyPaths.Length]; for (int i = 0; i < assemblyPaths.Length; ++i) { assemblyDefinitions[i] = AssemblyDefinition.ReadAssembly(assemblyPaths[i], readerParameters); // Cecil tries to resolve references by filename, since Unity force loads // assemblies, then assembly reference will resolve even if the assembly name // does not match the assembly filename. So we register all assemblies in // in the resolver. assemblyResolver.RegisterAssembly(assemblyDefinitions[i]); } return assemblyDefinitions; } public static void CheckAssemblyReferences(string[] assemblyPaths, Error[] errors, AssemblyDefinition[] assemblyDefinitions) { var assemblyDefinitionNameToIndex = new Dictionary(); var assembliesAndReferencesArray = new AssemblyAndReferences[assemblyPaths.Length]; for (int i = 0; i < assemblyDefinitions.Length; ++i) { assemblyDefinitionNameToIndex[assemblyDefinitions[i].Name.Name] = i; assembliesAndReferencesArray[i] = new AssemblyAndReferences { assemblyIndex = i, referenceIndicies = new int[0] }; } var precompiledAssemblies = EditorCompilationInterface.Instance .PrecompiledAssemblyProvider.GetAllPrecompiledAssemblies() .Where(x => x.Flags.HasFlag(AssemblyFlags.UserAssembly)); var allPrecompiledAssemblies = precompiledAssemblies.ToDictionary(x => AssetPath.ReplaceSeparators(VirtualFileSystem.ToLogicalPath(x.Path))); for (int i = 0; i < assemblyPaths.Length; ++i) { if (errors[i].HasFlag(ErrorFlags.IncompatibleWithEditor)) continue; var assemblyPath = assemblyPaths[i]; // Check if "Validate References" option is enabled // in the PluginImporter if (!ShouldValidateReferences(AssetPath.ReplaceSeparators(assemblyPath), allPrecompiledAssemblies)) continue; ResolveAndSetupReferences(i, errors, assemblyDefinitions, assemblyDefinitionNameToIndex, assembliesAndReferencesArray, assemblyPath); } // Check assemblies for references to assemblies with errors int referenceErrorCount; do { referenceErrorCount = 0; foreach (var assemblyAndReferences in assembliesAndReferencesArray) { var assemblyIndex = assemblyAndReferences.assemblyIndex; foreach (var referenceIndex in assemblyAndReferences.referenceIndicies) { var referenceError = errors[referenceIndex]; if (errors[assemblyIndex].flags == ErrorFlags.None && referenceError.flags != ErrorFlags.None) { if (referenceError.HasFlag(ErrorFlags.IncompatibleWithEditor)) { errors[assemblyIndex].Add(ErrorFlags.ReferenceHasErrors | ErrorFlags.IncompatibleWithEditor, string.Format("Reference '{0}' is incompatible with the editor.", assemblyDefinitions[referenceIndex].Name.Name)); } else { errors[assemblyIndex].Add(ErrorFlags.ReferenceHasErrors | referenceError.flags, string.Format("Reference has errors '{0}'.", assemblyDefinitions[referenceIndex].Name.Name)); } referenceErrorCount++; } } } } while (referenceErrorCount > 0); } public static void ResolveAndSetupReferences(int index, Error[] errors, AssemblyDefinition[] assemblyDefinitions, Dictionary assemblyDefinitionNameToIndex, AssemblyAndReferences[] assemblyAndReferences, string assemblyPath) { var assemblyDefinition = assemblyDefinitions[index]; var assemblyResolver = assemblyDefinition.MainModule.AssemblyResolver; var assemblyReferences = GetAssemblyNameReferences(assemblyDefinition); var referenceIndieces = new List { Capacity = assemblyReferences.Length }; bool isReferencingUnityAssemblies = false; foreach (var reference in assemblyReferences) { if (!isReferencingUnityAssemblies && (Utility.FastStartsWith(reference.Name, "UnityEngine", "unityengine") || Utility.FastStartsWith(reference.Name, "UnityEditor", "unityeditor"))) { isReferencingUnityAssemblies = true; } try { var referenceAssemblyDefinition = assemblyResolver.Resolve(reference); if (reference.Name == assemblyDefinition.Name.Name) { errors[index].Add(ErrorFlags.ReferenceHasErrors, $"{reference.Name} references itself."); } if (assemblyDefinitionNameToIndex.TryGetValue(referenceAssemblyDefinition.Name.Name, out int referenceAssemblyDefinitionIndex)) { referenceIndieces.Add(referenceAssemblyDefinitionIndex); } } catch (AssemblyResolutionException) { errors[index].Add(ErrorFlags.UnresolvableReference, string.Format("Unable to resolve reference '{0}'. Is the assembly missing or incompatible with the current platform?\nReference validation can be disabled in the Plugin Inspector.", reference.Name)); } } if (isReferencingUnityAssemblies) { var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(assemblyPath); if (assemblyDefinitions[index].Name.Name != fileNameWithoutExtension) { errors[index].Add(ErrorFlags.ReferenceHasErrors, $"Assembly name '{assemblyDefinitions[index].Name.Name}' does not match file name '{fileNameWithoutExtension}'"); } } assemblyAndReferences[index].referenceIndicies = referenceIndieces.ToArray(); } private static bool IsInSameFolder(AssemblyDefinition first, AssemblyDefinition second) { var firstAssemblyPath = Path.GetDirectoryName(first.MainModule.FileName); var secondAssemblyPath = Path.GetDirectoryName(second.MainModule.FileName); return firstAssemblyPath.Equals(secondAssemblyPath, StringComparison.Ordinal); } private static bool IsSigned(AssemblyNameReference reference) { //Bug in Cecil where HasPublicKey is always false foreach (var publicTokenByte in reference.PublicKeyToken) { if (publicTokenByte != 0) { return true; } } return false; } public static AssemblyNameReference[] GetAssemblyNameReferences(AssemblyDefinition assemblyDefinition) { List result = new List { Capacity = 16 }; foreach (ModuleDefinition module in assemblyDefinition.Modules) { var references = module.AssemblyReferences; foreach (var reference in references) { result.Add(reference); } } return result.ToArray(); } } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetDatabase.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using UnityEditor.VersionControl; using UnityEngine.Scripting; using uei = UnityEngine.Internal; namespace UnityEditor { public sealed partial class AssetDatabase { // Delegate to be called from [[AssetDatabase.ImportPackage]] callbacks public delegate void ImportPackageCallback(string packageName); // Delegate to be called from [[AssetDatabase.ImportPackage]] callbacks in the event of failure public delegate void ImportPackageFailedCallback(string packageName, string errorMessage); // Delegate to be called when package import begins public static event ImportPackageCallback importPackageStarted { add => m_importPackageStartedEvent.Add(value); remove => m_importPackageStartedEvent.Remove(value); } private static EventWithPerformanceTracker m_importPackageStartedEvent = new EventWithPerformanceTracker($"{nameof(AssetDatabase)}.{nameof(importPackageStarted)}"); // Delegate to be called when package import completes public static event ImportPackageCallback importPackageCompleted { add => m_importPackageCompletedEvent.Add(value); remove => m_importPackageCompletedEvent.Remove(value); } private static EventWithPerformanceTracker m_importPackageCompletedEvent = new EventWithPerformanceTracker($"{nameof(AssetDatabase)}.{nameof(importPackageCompleted)}"); // Called when package import completes, listing the selected items public static Action onImportPackageItemsCompleted; private static DelegateWithPerformanceTracker> m_onImportPackageItemsCompleted = new DelegateWithPerformanceTracker>($"{nameof(AssetDatabase)}.{nameof(onImportPackageItemsCompleted)}"); // Delegate to be called when package import is cancelled public static event ImportPackageCallback importPackageCancelled { add => m_importPackageCancelledEvent.Add(value); remove => m_importPackageCancelledEvent.Remove(value); } private static EventWithPerformanceTracker m_importPackageCancelledEvent = new EventWithPerformanceTracker($"{nameof(AssetDatabase)}.{nameof(importPackageCancelled)}"); // Delegate to be called when package import fails public static event ImportPackageFailedCallback importPackageFailed { add => m_importPackageFailedEvent.Add(value); remove => m_importPackageFailedEvent.Remove(value); } private static EventWithPerformanceTracker m_importPackageFailedEvent = new EventWithPerformanceTracker($"{nameof(AssetDatabase)}.{nameof(importPackageFailed)}"); [RequiredByNativeCode] private static void Internal_CallImportPackageStarted(string packageName) { foreach (var evt in m_importPackageStartedEvent) evt(packageName); } [RequiredByNativeCode] private static void Internal_CallImportPackageCompleted(string packageName) { foreach (var evt in m_importPackageCompletedEvent) evt(packageName); } [RequiredByNativeCode] private static void Internal_CallOnImportPackageItemsCompleted(string[] items) { foreach (var evt in m_onImportPackageItemsCompleted.UpdateAndInvoke(onImportPackageItemsCompleted)) evt(items); } [RequiredByNativeCode] private static void Internal_CallImportPackageCancelled(string packageName) { foreach (var evt in m_importPackageCancelledEvent) evt(packageName); } [RequiredByNativeCode] private static void Internal_CallImportPackageFailed(string packageName, string errorMessage) { foreach (var evt in m_importPackageFailedEvent) evt(packageName, errorMessage); } [RequiredByNativeCode] private static bool Internal_IsOpenForEdit(string assetOrMetaFilePath) { return IsOpenForEdit(assetOrMetaFilePath); } public static void CanOpenForEdit(string[] assetOrMetaFilePaths, List outNotEditablePaths, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusQueryOptions = StatusQueryOptions.UseCachedIfPossible) { if (assetOrMetaFilePaths == null) throw new ArgumentNullException(nameof(assetOrMetaFilePaths)); if (outNotEditablePaths == null) throw new ArgumentNullException(nameof(outNotEditablePaths)); UnityEngine.Profiling.Profiler.BeginSample("AssetDatabase.CanOpenForEdit"); AssetModificationProcessorInternal.CanOpenForEdit(assetOrMetaFilePaths, outNotEditablePaths, statusQueryOptions); UnityEngine.Profiling.Profiler.EndSample(); } public static void IsOpenForEdit(string[] assetOrMetaFilePaths, List outNotEditablePaths, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusQueryOptions = StatusQueryOptions.UseCachedIfPossible) { if (assetOrMetaFilePaths == null) throw new ArgumentNullException(nameof(assetOrMetaFilePaths)); if (outNotEditablePaths == null) throw new ArgumentNullException(nameof(outNotEditablePaths)); UnityEngine.Profiling.Profiler.BeginSample("AssetDatabase.IsOpenForEdit"); AssetModificationProcessorInternal.IsOpenForEdit(assetOrMetaFilePaths, outNotEditablePaths, statusQueryOptions); UnityEngine.Profiling.Profiler.EndSample(); } [RequiredByNativeCode] private static bool Internal_MakeEditable(string path) { return MakeEditable(path); } public static bool MakeEditable(string path) { if (path == null) throw new ArgumentNullException(nameof(path)); return MakeEditable(new[] {path}); } [RequiredByNativeCode] private static bool Internal_MakeEditable2(string[] paths, string prompt = null, List outNotEditablePaths = null) { return MakeEditable(paths, prompt, outNotEditablePaths); } public static bool MakeEditable(string[] paths, string prompt = null, List outNotEditablePaths = null) { if (paths == null) throw new ArgumentNullException(nameof(paths)); UnityEngine.Profiling.Profiler.BeginSample("AssetDatabase.MakeEditable"); ChangeSet changeSet = null; var result = Provider.HandlePreCheckoutCallback(ref paths, ref changeSet); if (result && !AssetModificationProcessorInternal.MakeEditable(paths, prompt, outNotEditablePaths)) result = false; if (result && !Provider.MakeEditableImpl(paths, prompt, changeSet, outNotEditablePaths)) result = false; UnityEngine.Profiling.Profiler.EndSample(); return result; } } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetDatabase.deprecated.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEditor.AssetImporters; using UnityEngine.Bindings; namespace UnityEditor { partial class AssetDatabase { // Gets the path to the text .meta file associated with an asset [Obsolete("GetTextMetaDataPathFromAssetPath has been renamed to GetTextMetaFilePathFromAssetPath (UnityUpgradable) -> GetTextMetaFilePathFromAssetPath(*)",true)] public static string GetTextMetaDataPathFromAssetPath(string path) { return null; } } // Used to be part of Asset Server, and public API for some reason. [Obsolete("AssetStatus enum is not used anymore (Asset Server has been removed)",true)] public enum AssetStatus { Calculating = -1, ClientOnly = 0, ServerOnly = 1, Unchanged = 2, Conflict = 3, Same = 4, NewVersionAvailable = 5, NewLocalVersion = 6, RestoredFromTrash = 7, Ignored = 8, BadState = 9 } // Used to be part of Asset Server, and public API for some reason. [Obsolete("AssetsItem class is not used anymore (Asset Server has been removed)",true)] [StructLayout(LayoutKind.Sequential)] [System.Serializable] public sealed class AssetsItem { public string guid; public string pathName; public string message; public string exportedAssetPath; public string guidFolder; public int enabled; public int assetIsDir; public int changeFlags; public string previewPath; public int exists; } } namespace UnityEditor.Experimental { public partial class AssetDatabaseExperimental { [FreeFunction("AssetDatabase::ClearImporterOverride")] [Obsolete("AssetDatabaseExperimental.ClearImporterOverride() has been deprecated. Use AssetDatabase.ClearImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.ClearImporterOverride(*)", true)] extern public static void ClearImporterOverride(string path); [FreeFunction("AssetDatabase::IsCacheServerEnabled")] [Obsolete("AssetDatabaseExperimental.IsCacheServerEnabled() has been deprecated. Use AssetDatabase.IsCacheServerEnabled() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.IsCacheServerEnabled(*)", true)] public extern static bool IsCacheServerEnabled(); [Obsolete("AssetDatabaseExperimental.SetImporterOverride() has been deprecated. Use AssetDatabase.SetImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.SetImporterOverride(*)", true)] public static void SetImporterOverride(string path) where T : ScriptedImporter { AssetDatabase.SetImporterOverrideInternal(path, typeof(T)); } [FreeFunction("AssetDatabase::GetImporterOverride")] [Obsolete("AssetDatabaseExperimental.GetImporterOverride() has been deprecated. Use AssetDatabase.GetImporterOverride() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetImporterOverride(*)", true)] extern public static System.Type GetImporterOverride(string path); [FreeFunction("AssetDatabase::GetAvailableImporters")] [Obsolete("AssetDatabaseExperimental.GetAvailableImporterTypes() has been deprecated. Use AssetDatabase.GetAvailableImporters() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetAvailableImporters(*)", true)] extern public static Type[] GetAvailableImporterTypes(string path); [FreeFunction("AcceleratorClientCanConnectTo")] [Obsolete("AssetDatabaseExperimental.CanConnectToCacheServer() has been deprecated. Use AssetDatabase.CanConnectToCacheServer() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.CanConnectToCacheServer(*)", true)] public extern static bool CanConnectToCacheServer(string ip, UInt16 port); [FreeFunction()] [Obsolete("AssetDatabaseExperimental.RefreshSettings() has been deprecated. Use AssetDatabase.RefreshSettings() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.RefreshSettings(*)", true)] public extern static void RefreshSettings(); [Obsolete("AssetDatabaseExperimental.CacheServerConnectionChangedParameters has been deprecated. Use UnityEditor.CacheServerConnectionChangedParameters instead (UnityUpgradable) -> UnityEditor.CacheServerConnectionChangedParameters", true)] public struct CacheServerConnectionChangedParameters { } #pragma warning disable 67 [Obsolete("AssetDatabaseExperimental.cacheServerConnectionChanged has been deprecated. Use AssetDatabase.cacheServerConnectionChanged instead (UnityUpgradable) -> UnityEditor.AssetDatabase.cacheServerConnectionChanged", true)] public static event Action cacheServerConnectionChanged; #pragma warning restore 67 [FreeFunction("AcceleratorClientIsConnected")] [Obsolete("AssetDatabaseExperimental.IsConnectedToCacheServer() has been deprecated. Use AssetDatabase.IsConnectedToCacheServer() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.IsConnectedToCacheServer(*)", true)] public extern static bool IsConnectedToCacheServer(); [FreeFunction()] [Obsolete("AssetDatabaseExperimental.GetCacheServerAddress() has been deprecated. Use AssetDatabase.GetCacheServerAddress() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetCacheServerAddress(*)", true)] public extern static string GetCacheServerAddress(); [FreeFunction()] [Obsolete("AssetDatabaseExperimental.GetCacheServerPort() has been deprecated. Use AssetDatabase.GetCacheServerPort() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetCacheServerPort(*)", true)] public extern static UInt16 GetCacheServerPort(); [FreeFunction("AssetDatabase::GetCacheServerNamespacePrefix")] [Obsolete("AssetDatabaseExperimental.GetCacheServerNamespacePrefix() has been deprecated. Use AssetDatabase.GetCacheServerNamespacePrefix() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetCacheServerNamespacePrefix(*)", true)] public extern static string GetCacheServerNamespacePrefix(); [FreeFunction("AssetDatabase::GetCacheServerEnableDownload")] [Obsolete("AssetDatabaseExperimental.GetCacheServerEnableDownload() has been deprecated. Use AssetDatabase.GetCacheServerEnableDownload() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetCacheServerEnableDownload(*)", true)] public extern static bool GetCacheServerEnableDownload(); [FreeFunction("AssetDatabase::GetCacheServerEnableUpload")] [Obsolete("AssetDatabaseExperimental.GetCacheServerEnableUpload() has been deprecated. Use AssetDatabase.GetCacheServerEnableUpload() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.GetCacheServerEnableUpload(*)", true)] public extern static bool GetCacheServerEnableUpload(); [FreeFunction("AssetDatabase::IsDirectoryMonitoringEnabled")] [Obsolete("AssetDatabaseExperimental.IsDirectoryMonitoringEnabled() has been deprecated. Use AssetDatabase.IsDirectoryMonitoringEnabled() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.IsDirectoryMonitoringEnabled(*)", true)] public extern static bool IsDirectoryMonitoringEnabled(); [FreeFunction("AssetDatabaseExperimental::RegisterCustomDependency")] [PreventExecutionInState(AssetDatabasePreventExecution.kPreventCustomDependencyChanges, PreventExecutionSeverity.PreventExecution_ManagedException, "Custom dependencies can only be removed when the assetdatabase is not importing.")] [Obsolete("AssetDatabaseExperimental.RegisterCustomDependency() has been deprecated. Use AssetDatabase.RegisterCustomDependency() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.RegisterCustomDependency(*)", true)] public extern static void RegisterCustomDependency(string dependency, Hash128 hashOfValue); [FreeFunction("AssetDatabaseExperimental::UnregisterCustomDependencyPrefixFilter")] [PreventExecutionInState(AssetDatabasePreventExecution.kPreventCustomDependencyChanges, PreventExecutionSeverity.PreventExecution_ManagedException, "Custom dependencies can only be removed when the assetdatabase is not importing.")] [Obsolete("AssetDatabaseExperimental.UnregisterCustomDependencyPrefixFilter() has been deprecated. Use AssetDatabase.UnregisterCustomDependencyPrefixFilter() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.UnregisterCustomDependencyPrefixFilter(*)", true)] public extern static UInt32 UnregisterCustomDependencyPrefixFilter(string prefixFilter); [FreeFunction("AssetDatabase::IsAssetImportProcess")] [Obsolete("AssetDatabaseExperimental.IsAssetImportWorkerProcess() has been deprecated. Use AssetDatabase.IsAssetImportWorkerProcess() instead (UnityUpgradable) -> UnityEditor.AssetDatabase.IsAssetImportWorkerProcess(*)", true)] public extern static bool IsAssetImportWorkerProcess(); } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetDatabaseSearching.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using System.Collections.Generic; using UnityEditor.Utils; using UnityEngine; using UnityEngine.Bindings; using Object = UnityEngine.Object; namespace UnityEditor { public sealed partial class AssetDatabase { public static string[] FindAssets(string filter) { return FindAssets(filter, null); } public static string[] FindAssets(string filter, string[] searchInFolders) { var searchFilter = new SearchFilter { searchArea = SearchFilter.SearchArea.AllAssets }; SearchUtility.ParseSearchString(filter, searchFilter); if (searchInFolders != null && searchInFolders.Length > 0) { searchFilter.folders = searchInFolders; searchFilter.searchArea = SearchFilter.SearchArea.SelectedFolders; } return FindAssets(searchFilter); } internal static string[] FindAssets(SearchFilter searchFilter) { return FindAllAssets(searchFilter).Select(property => property.guid).Distinct().ToArray(); } [VisibleToOtherModules("UnityEditor.UIBuilderModule")] internal static IEnumerable FindAllAssets(SearchFilter searchFilter) { var enumerator = EnumerateAllAssets(searchFilter); while (enumerator.MoveNext()) yield return enumerator.Current; } internal static IEnumerator EnumerateAllAssets(SearchFilter searchFilter) { if (searchFilter.folders != null && searchFilter.folders.Length > 0 && searchFilter.searchArea == SearchFilter.SearchArea.SelectedFolders) return FindInFolders(searchFilter, p => p); return FindEverywhere(searchFilter, p => p); } private static IEnumerator FindInFolders(SearchFilter searchFilter, Func selector) { var folders = new List(); folders.AddRange(searchFilter.folders); if (folders.Remove(PackageManager.Folders.GetPackagesPath())) { var packages = PackageManagerUtilityInternal.GetAllVisiblePackages(searchFilter.skipHidden); foreach (var package in packages) { if (!folders.Contains(package.assetPath)) folders.Add(package.assetPath); } } HierarchyProperty propertyWithFilter = null; foreach (var folderPath in folders) { var sanitizedFolderPath = folderPath.ConvertSeparatorsToUnity().TrimTrailingSlashes(); var folderInstanceID = AssetDatabase.GetMainAssetOrInProgressProxyInstanceID(sanitizedFolderPath); var rootPath = "Assets"; // Find the right rootPath if folderPath is part of a package var packageInfo = PackageManager.PackageInfo.FindForAssetPath(sanitizedFolderPath); if (packageInfo != null) { rootPath = packageInfo.assetPath; if (searchFilter.skipHidden && !PackageManagerUtilityInternal.IsPathInVisiblePackage(rootPath)) continue; } // Set empty filter to ensure we search all assets to find folder var property = new HierarchyProperty(rootPath); property.SetSearchFilter(new SearchFilter()); if (property.Find(folderInstanceID, null)) { // Set filter after we found the folder if (propertyWithFilter != null) property.CopySearchFilterFrom(propertyWithFilter); else { property.SetSearchFilter(searchFilter); propertyWithFilter = property; } int folderDepth = property.depth; int[] expanded = null; // enter all children of folder while (property.NextWithDepthCheck(expanded, folderDepth + 1)) { yield return selector(property); } } else { Debug.LogWarning("AssetDatabase.FindAssets: Folder not found: '" + sanitizedFolderPath + "'"); } } } private static IEnumerator FindEverywhere(SearchFilter searchFilter, Func selector) { var rootPaths = new List(); if (searchFilter.searchArea == SearchFilter.SearchArea.AllAssets || searchFilter.searchArea == SearchFilter.SearchArea.InAssetsOnly) { rootPaths.Add("Assets"); } if (searchFilter.searchArea == SearchFilter.SearchArea.AllAssets || searchFilter.searchArea == SearchFilter.SearchArea.InPackagesOnly) { var packages = PackageManagerUtilityInternal.GetAllVisiblePackages(searchFilter.skipHidden); foreach (var package in packages) { rootPaths.Add(package.assetPath); } } HierarchyProperty lastProperty = null; foreach (var rootPath in rootPaths) { var property = new HierarchyProperty(rootPath); if (lastProperty != null) property.CopySearchFilterFrom(lastProperty); else property.SetSearchFilter(searchFilter); lastProperty = property; while (property.Next(null)) { yield return selector(property); } } } } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetImportInProgressProxy.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine.Bindings; using UnityEngine; namespace UnityEditor { [NativeType(Header = "Modules/AssetDatabase/Editor/Public/AssetImportInProgressProxy.h")] class AssetImportInProgressProxy : UnityEngine.Object { public extern GUID asset { [NativeMethod("GetAsset")] get; [NativeMethod("SetAsset")] set; } [NativeMethod] public extern static bool IsProxyAsset(int instanceID); } [CustomEditor(typeof(AssetImportInProgressProxy))] class AssetImportInProgressProxyEditor : Editor { public override void OnInspectorGUI() { var proxy = (AssetImportInProgressProxy)target; if (GUILayout.Button("Import")) { var mainAsset = AssetDatabase.LoadMainAssetAtGUID(proxy.asset); Selection.activeObject = mainAsset; //@TODO: Properly call this from C++ when asset import completes... //EditorApplication.projectWindowChanged(); } } } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetMoveInfo.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; namespace UnityEditor.Experimental { public struct AssetMoveInfo : IEquatable { public AssetMoveInfo(string sourceAssetPath, string destinationAssetPath) { this.sourceAssetPath = sourceAssetPath; this.destinationAssetPath = destinationAssetPath; } public string sourceAssetPath { get; } public string destinationAssetPath { get; } public bool Equals(AssetMoveInfo other) { return string.Equals(sourceAssetPath, other.sourceAssetPath) && string.Equals(destinationAssetPath, other.destinationAssetPath); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; return obj is AssetMoveInfo && Equals((AssetMoveInfo)obj); } public override int GetHashCode() { unchecked { return ((sourceAssetPath != null ? sourceAssetPath.GetHashCode() : 0) * 397) ^ (destinationAssetPath != null ? destinationAssetPath.GetHashCode() : 0); } } public static bool operator==(AssetMoveInfo left, AssetMoveInfo right) { return left.Equals(right); } public static bool operator!=(AssetMoveInfo left, AssetMoveInfo right) { return !left.Equals(right); } } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetPreview.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using Object = UnityEngine.Object; namespace UnityEditor { [NativeHeader("Editor/Mono/AssetDatabase/AssetPreview.bindings.h")] [NativeHeader("Editor/Src/Utility/ObjectImages.h")] public sealed class AssetPreview { private const int kSharedClientID = 0; public static Texture2D GetAssetPreview(Object asset) { if (asset != null) return GetAssetPreview(asset.GetInstanceID()); return null; } internal static Texture2D GetAssetPreview(EntityId entityId) { return GetAssetPreview(entityId, kSharedClientID); } [FreeFunction("AssetPreviewBindings::GetAssetPreview")] internal static extern Texture2D GetAssetPreview(EntityId entityId, int clientID); [FreeFunction("AssetPreviewBindings::HasAssetPreview")] internal static extern bool HasAssetPreview(EntityId entityId, int clientID); internal static Texture2D GetAssetPreviewFromGUID(string guid) { return GetAssetPreviewFromGUID(guid, kSharedClientID); } [FreeFunction("AssetPreviewBindings::GetAssetPreviewFromGUID")] internal static extern Texture2D GetAssetPreviewFromGUID(string guid, int clientID); public static bool IsLoadingAssetPreview(int instanceID) { return IsLoadingAssetPreview(instanceID, kSharedClientID); } public static bool IsLoadingAssetPreview(EntityId entityId) { return IsLoadingAssetPreview(entityId, kSharedClientID); } [FreeFunction("AssetPreviewBindings::IsLoadingAssetPreview")] internal static extern bool IsLoadingAssetPreview(EntityId entityId, int clientID); public static bool IsLoadingAssetPreviews() { return IsLoadingAssetPreviews(kSharedClientID); } [FreeFunction("AssetPreviewBindings::IsLoadingAssetPreviews")] internal static extern bool IsLoadingAssetPreviews(int clientID); internal static bool HasAnyNewPreviewTexturesAvailable() { return HasAnyNewPreviewTexturesAvailable(kSharedClientID); } [FreeFunction("AssetPreviewBindings::HasAnyNewPreviewTexturesAvailable")] internal static extern bool HasAnyNewPreviewTexturesAvailable(int clientID); public static void SetPreviewTextureCacheSize(int size) { SetPreviewTextureCacheSize(size, kSharedClientID); } [FreeFunction("AssetPreviewBindings::SetPreviewTextureCacheSize")] internal static extern void SetPreviewTextureCacheSize(int size, int clientID); [FreeFunction("AssetPreviewBindings::ClearTemporaryAssetPreviews")] internal static extern void ClearTemporaryAssetPreviews(); [FreeFunction("AssetPreviewBindings::DeletePreviewTextureManagerByID")] internal static extern void DeletePreviewTextureManagerByID(int clientID); public static Texture2D GetMiniThumbnail(Object obj) { return (Texture2D)GetMiniThumbnailInternal(obj); } [FreeFunction("TextureForObject")] private static extern Texture GetMiniThumbnailInternal(Object obj); public static Texture2D GetMiniTypeThumbnail(Type type) { return GetMiniTypeThumbnailFromType(type); } [FreeFunction("AssetPreviewBindings::GetMiniTypeThumbnailFromObject")] internal static extern Texture2D GetMiniTypeThumbnail(Object obj); [FreeFunction("AssetPreviewBindings::GetMiniTypeThumbnailFromClassID")] internal static extern Texture2D GetMiniTypeThumbnailFromClassID(int classID); [FreeFunction("AssetPreviewBindings::GetMiniTypeThumbnailFromType")] internal static extern Texture2D GetMiniTypeThumbnailFromType(Type managedType); } } ================================================ FILE: Editor/Mono/AssetDatabase/AssetsModifiedProcessor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using UnityEditor.Profiling; namespace UnityEditor.Experimental { public abstract class AssetsModifiedProcessor { public HashSet assetsReportedChanged { get; set; } protected void ReportAssetChanged(string assetChanged) { if (assetsReportedChanged == null) throw new InvalidOperationException("Cannot call ReportAssetChanged outside of the OnAssetsModified callback"); assetsReportedChanged.Add(assetChanged); } //Note: changedAssets including added and moved assets may be a usability issue. Review before making public. ///Fired when the [[AssetDatabase]] detects Asset changes before any Assets are imported. ///Paths to the Assets whose file contents have changed. Includes all added and moved Assets. ///Paths to added Assets. ///Paths to deleted Assets. ///Array of AssetMoveInfo that contains the previous and current location of any moved Assets. /// An Asset will only be reported moved if its .meta file is moved as well. protected abstract void OnAssetsModified(string[] changedAssets, string[] addedAssets, string[] deletedAssets, AssetMoveInfo[] movedAssets); internal void Internal_OnAssetsModified(string[] changedAssets, string[] addedAssets, string[] deletedAssets, AssetMoveInfo[] movedAssets) { var type = GetType(); using (new EditorPerformanceMarker($"{type.Name}.{nameof(OnAssetsModified)}", type).Auto()) { OnAssetsModified(changedAssets, addedAssets, deletedAssets, movedAssets); } } } } ================================================ FILE: Editor/Mono/AssetDeleteResult.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; namespace UnityEditor { [Flags] public enum AssetDeleteResult { // Tells the internal implementation that the callback did not delete the asset. The asset will be delete by the internal implementation. DidNotDelete = 0, // Tells Unity that the file cannot be deleted and Unity should leave it alone. FailedDelete = 1, // Tells Unity that the asset was deleted by the callback. Unity will not try to delete the asset, but will delete the cached version and preview file. DidDelete = 2 } } ================================================ FILE: Editor/Mono/AssetModificationProcessor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Scripting; using UnityEditor.VersionControl; using UnityEditorInternal; using UnityEditorInternal.VersionControl; using System.Linq; using System.Reflection; using UnityEditor.Profiling; using UnityEngine.Scripting.APIUpdating; namespace UnityEditor { [MovedFrom("")] public class AssetModificationProcessor { } internal class AssetModificationProcessorInternal { static bool CheckArgumentTypes(Type[] types, MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); if (types.Length != parameters.Length) { Debug.LogWarning("Parameter count did not match. Expected: " + types.Length + " Got: " + parameters.Length + " in " + method.DeclaringType + "." + method.Name); return false; } int i = 0; foreach (Type type in types) { ParameterInfo pInfo = parameters[i]; if (type != pInfo.ParameterType) { Debug.LogWarning("Parameter type mismatch at parameter " + i + ". Expected: " + type + " Got: " + pInfo.ParameterType + " in " + method.DeclaringType + "." + method.Name); return false; } ++i; } return true; } static bool CheckArgumentTypesAndReturnType(Type[] types, MethodInfo method, System.Type returnType) { if (returnType != method.ReturnType) { Debug.LogWarning("Return type mismatch. Expected: " + returnType + " Got: " + method.ReturnType + " in " + method.DeclaringType + "." + method.Name); return false; } return CheckArgumentTypes(types, method); } static bool CheckArguments(object[] args, MethodInfo method) { Type[] types = new Type[args.Length]; for (int i = 0; i < args.Length; i++) types[i] = args[i].GetType(); return CheckArgumentTypes(types, method); } static bool CheckArgumentsAndReturnType(object[] args, MethodInfo method, System.Type returnType) { Type[] types = new Type[args.Length]; for (int i = 0; i < args.Length; i++) types[i] = args[i].GetType(); return CheckArgumentTypesAndReturnType(types, method, returnType); } #pragma warning disable 0618 static System.Collections.Generic.IEnumerable assetModificationProcessors = null; static System.Collections.Generic.IEnumerable AssetModificationProcessors { get { if (assetModificationProcessors == null) { List processors = new List(); processors.AddRange(TypeCache.GetTypesDerivedFrom()); assetModificationProcessors = processors.ToArray(); } return assetModificationProcessors; } } #pragma warning restore 0618 [RequiredByNativeCode] internal static void OnWillCreateAsset(string path) { foreach (var assetModificationProcessorClass in AssetModificationProcessors) { const string methodName = "OnWillCreateAsset"; MethodInfo method = assetModificationProcessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { object[] args = { path }; if (!CheckArguments(args, method)) continue; using (new EditorPerformanceMarker($"{assetModificationProcessorClass.Name}.{methodName}", assetModificationProcessorClass).Auto()) method.Invoke(null, args); } } } // ReSharper disable once UnusedMember.Local - invoked from native code [RequiredByNativeCode] static void FileModeChanged(string[] assets, FileMode mode) { AssetModificationHook.FileModeChanged(assets, mode); object[] args = { assets, mode }; foreach (var type in AssetModificationProcessors) { const string methodName = "FileModeChanged"; var method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method == null) continue; if (!CheckArgumentsAndReturnType(args, method, typeof(void))) continue; using (new EditorPerformanceMarker($"{type.Name}.{methodName}", type).Auto()) method.Invoke(null, args); } } // Postprocess on all assets once an automatic import has completed // ReSharper disable once UnusedMember.Local - invoked from native code [RequiredByNativeCode] static void OnWillSaveAssets(string[] assets, out string[] assetsThatShouldBeSaved, out string[] assetsThatShouldBeReverted, bool explicitlySaveAsset) { assetsThatShouldBeReverted = new string[0]; assetsThatShouldBeSaved = assets; bool showSaveDialog = assets.Length > 0 && EditorPrefs.GetBool("VerifySavingAssets", false) && InternalEditorUtility.isHumanControllingUs; // If we are only saving a single scene or prefab and the user explicitly said we should, skip the dialog. We don't need // to verify this twice. if (explicitlySaveAsset && assets.Length == 1 && (assets[0].EndsWith(".unity") || assets[0].EndsWith(".prefab"))) showSaveDialog = false; if (showSaveDialog) AssetSaveDialog.ShowWindow(assets, out assetsThatShouldBeSaved); else assetsThatShouldBeSaved = assets; foreach (var assetModificationProcessorClass in AssetModificationProcessors) { const string methodName = "OnWillSaveAssets"; MethodInfo method = assetModificationProcessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { object[] args = { assetsThatShouldBeSaved }; if (!CheckArguments(args, method)) continue; string[] result; using (new EditorPerformanceMarker($"{assetModificationProcessorClass.Name}.{methodName}", assetModificationProcessorClass).Auto()) result = (string[])method.Invoke(null, args); if (result != null) assetsThatShouldBeSaved = result; } } if (assetsThatShouldBeSaved == null) { return; } var assetsNotOpened = new List(); AssetDatabase.IsOpenForEdit(assetsThatShouldBeSaved, assetsNotOpened, StatusQueryOptions.ForceUpdate); assets = assetsNotOpened.ToArray(); // Try to checkout if needed var notEditableAssets = new List(); if (assets.Length != 0 && !AssetDatabase.MakeEditable(assets, null, notEditableAssets)) { // only save assets that can be made editable (not locked by someone else, etc.), // unless we are in the behavior mode that just overwrites everything anyway if (!EditorUserSettings.overwriteFailedCheckoutAssets) { assetsThatShouldBeReverted = notEditableAssets.ToArray(); assetsThatShouldBeSaved = assetsThatShouldBeSaved.Except(assetsThatShouldBeReverted).ToArray(); } } } [RequiredByNativeCode] static AssetMoveResult OnWillMoveAsset(string fromPath, string toPath, string[] newPaths, string[] NewMetaPaths) { AssetMoveResult finalResult = AssetMoveResult.DidNotMove; finalResult = AssetModificationHook.OnWillMoveAsset(fromPath, toPath); foreach (var assetModificationProcessorClass in AssetModificationProcessors) { const string methodName = "OnWillMoveAsset"; MethodInfo method = assetModificationProcessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { object[] args = { fromPath, toPath }; if (!CheckArgumentsAndReturnType(args, method, finalResult.GetType())) continue; using (new EditorPerformanceMarker($"{assetModificationProcessorClass.Name}.{methodName}", assetModificationProcessorClass).Auto()) finalResult |= (AssetMoveResult)method.Invoke(null, args); } } return finalResult; } [RequiredByNativeCode] static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options) { AssetDeleteResult finalResult = AssetDeleteResult.DidNotDelete; foreach (var assetModificationProcessorClass in AssetModificationProcessors) { const string methodName = "OnWillDeleteAsset"; MethodInfo method = assetModificationProcessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { object[] args = { assetPath, options }; if (!CheckArgumentsAndReturnType(args, method, finalResult.GetType())) continue; using (new EditorPerformanceMarker($"{assetModificationProcessorClass.Name}.{methodName}", assetModificationProcessorClass).Auto()) finalResult |= (AssetDeleteResult)method.Invoke(null, args); } } if (finalResult != AssetDeleteResult.DidNotDelete) return finalResult; finalResult = AssetModificationHook.OnWillDeleteAsset(assetPath, options); return finalResult; } [RequiredByNativeCode] static void OnWillDeleteAssets(string[] assetPaths, AssetDeleteResult[] outPathDeletionResults, RemoveAssetOptions options) { for (int i = 0; i < outPathDeletionResults.Length; i++) outPathDeletionResults[i] = (int)AssetDeleteResult.DidNotDelete; List nonDeletedPaths = new List(); List nonDeletedPathIndices = new List(); foreach (var assetModificationProcessorClass in AssetModificationProcessors) { const string methodName = "OnWillDeleteAsset"; MethodInfo method = assetModificationProcessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method == null) continue; for (int i = 0; i < assetPaths.Length; i++) { object[] args = { assetPaths[i], options }; if (!CheckArgumentsAndReturnType(args, method, typeof(AssetDeleteResult))) continue; using (new EditorPerformanceMarker($"{assetModificationProcessorClass.Name}.{methodName}", assetModificationProcessorClass).Auto()) { AssetDeleteResult callbackResult = (AssetDeleteResult)method.Invoke(null, args); outPathDeletionResults[i] |= callbackResult; } } } for (int i = 0; i < assetPaths.Length; i++) { if (outPathDeletionResults[i] == (int)AssetDeleteResult.DidNotDelete) { nonDeletedPaths.Add(assetPaths[i]); nonDeletedPathIndices.Add(i); } } if (nonDeletedPaths.Count > 0) { if (!Provider.enabled || EditorUserSettings.WorkOffline) return; for (int i = 0; i < nonDeletedPaths.Count; i++) { if (!Provider.PathIsVersioned(nonDeletedPaths[i])) { nonDeletedPaths.RemoveAt(i); nonDeletedPathIndices.RemoveAt(i); i--; } } var nonDeletedPathDeletionResults = new AssetDeleteResult[nonDeletedPaths.Count]; AssetModificationHook.OnWillDeleteAssets(nonDeletedPaths.ToArray(), nonDeletedPathDeletionResults, options); for (int i = 0; i < nonDeletedPathIndices.Count; i++) { outPathDeletionResults[nonDeletedPathIndices[i]] = nonDeletedPathDeletionResults[i]; } } } static MethodInfo[] s_CanOpenForEditMethods; static MethodInfo[] s_LegacyCanOpenForEditMethods; static MethodInfo[] s_IsOpenForEditMethods; static MethodInfo[] s_LegacyIsOpenForEditMethods; static void GetOpenForEditMethods(bool canOpenForEditVariant, out MethodInfo[] methods, out MethodInfo[] legacyMethods) { if (canOpenForEditVariant) { if (s_CanOpenForEditMethods == null) GetOpenForEditMethods("CanOpenForEdit", out s_CanOpenForEditMethods, out s_LegacyCanOpenForEditMethods); methods = s_CanOpenForEditMethods; legacyMethods = s_LegacyCanOpenForEditMethods; } else { if (s_IsOpenForEditMethods == null) GetOpenForEditMethods("IsOpenForEdit", out s_IsOpenForEditMethods, out s_LegacyIsOpenForEditMethods); methods = s_IsOpenForEditMethods; legacyMethods = s_LegacyIsOpenForEditMethods; } } static void GetOpenForEditMethods(string name, out MethodInfo[] methods, out MethodInfo[] legacyMethods) { var methodList = new List(); var legacyMethodList = new List(); Type[] types = { typeof(string[]), typeof(List), typeof(StatusQueryOptions) }; Type[] legacyTypes = { typeof(string), typeof(string).MakeByRefType() }; foreach (var type in AssetModificationProcessors) { // First look for a method with a signature that accepts multiple paths "bool (string[] assetOrMetaFilePaths, List outNotEditablePaths, StatusQueryOptions statusQueryOptions)". MethodInfo method; try { method = type.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); } catch (AmbiguousMatchException) { Debug.LogWarning($"Ambiguous {name} methods in {type.Name}."); continue; } if (method?.ReturnType == typeof(bool)) { methodList.Add(method); continue; } // Then look for a legacy method that accepts single path only "bool (string assetOrMetaFilePath, out string message)". MethodInfo legacyMethod; try { legacyMethod = type.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch (AmbiguousMatchException) { Debug.LogWarning($"Ambiguous {name} methods in {type.Name}."); continue; } if (legacyMethod != null && CheckArgumentTypesAndReturnType(legacyTypes, legacyMethod, typeof(bool))) legacyMethodList.Add(legacyMethod); } methods = methodList.ToArray(); legacyMethods = legacyMethodList.ToArray(); } enum Editability { Never, Always, Maybe } static Editability GetPathEditability(string assetPath) { // read-only asset locations (e.g. shared packages) are considered not editable bool rootFolder, readOnly; bool validPath = AssetDatabase.TryGetAssetFolderInfo(assetPath, out rootFolder, out readOnly); if (validPath && readOnly) return Editability.Never; // other paths that are not know to asset database, and not versioned, are considered always editable if (!VersionControlUtils.IsPathVersioned(assetPath)) return Editability.Always; return Editability.Maybe; } static bool GetOpenForEditViaScriptCallbacks(bool canOpenForEditVariant, string[] paths, List notEditablePaths, out string message, StatusQueryOptions options) { message = string.Empty; GetOpenForEditMethods(canOpenForEditVariant, out var methods, out var legacyMethods); if (methods.Length != 0) { object[] args = { paths, notEditablePaths, options }; foreach (var method in methods) { if (!(bool)method.Invoke(null, args)) return false; } } if (legacyMethods.Length != 0) { foreach (var legacyMethod in legacyMethods) { var result = true; foreach (var path in paths) { object[] legacyArgs = { path, null }; if (!(bool)legacyMethod.Invoke(null, legacyArgs)) { result = false; notEditablePaths.Add(path); message = legacyArgs[1] as string; } } if (!result) return false; } } return true; } internal static bool CanOpenForEdit(string assetPath, out string message, StatusQueryOptions statusOptions) { if (IsOpenForEdit(assetPath, out message, statusOptions)) return true; // Status has just been updated so there's no need to force update again. if (statusOptions == StatusQueryOptions.ForceUpdate) statusOptions = StatusQueryOptions.UseCachedIfPossible; return GetOpenForEdit(true, assetPath, out message, statusOptions); } internal static bool IsOpenForEdit(string assetPath, out string message, StatusQueryOptions statusOptions) { return GetOpenForEdit(false, assetPath, out message, statusOptions); } static bool GetOpenForEdit(bool canOpenForEditVariant, string assetPath, out string message, StatusQueryOptions statusOptions) { message = string.Empty; if (string.IsNullOrEmpty(assetPath)) return true; // treat empty/null paths as editable (might be under Library folders etc.) var editability = GetPathEditability(assetPath); if (editability == Editability.Always) return true; if (editability == Editability.Never) return false; if (!AssetModificationHook.GetOpenForEdit(canOpenForEditVariant, assetPath, out message, statusOptions)) return false; return GetOpenForEditViaScriptCallbacks(canOpenForEditVariant, new[] { assetPath }, new List(), out message, statusOptions); } internal static bool CanOpenForEdit(string[] assetOrMetaFilePaths, List outNotEditablePaths, StatusQueryOptions statusQueryOptions) { outNotEditablePaths.Clear(); if (assetOrMetaFilePaths == null || assetOrMetaFilePaths.Length == 0) return true; var queryList = GetQueryList(assetOrMetaFilePaths, outNotEditablePaths); if (queryList.Count == 0) return outNotEditablePaths.Count == 0; // Get a list of paths that are not open for edit. var notOpenForEditPaths = new List(); AssetModificationHook.GetOpenForEdit(false, queryList, notOpenForEditPaths, statusQueryOptions); GetOpenForEditViaScriptCallbacks(false, queryList.ToArray(), notOpenForEditPaths, out var message, statusQueryOptions); if (notOpenForEditPaths.Count == 0) return outNotEditablePaths.Count == 0; // Status has just been updated so there's no need to force update again. if (statusQueryOptions == StatusQueryOptions.ForceUpdate) statusQueryOptions = StatusQueryOptions.UseCachedIfPossible; // Check paths that are not open for edit. if (!AssetModificationHook.GetOpenForEdit(true, notOpenForEditPaths, outNotEditablePaths, statusQueryOptions)) return false; return GetOpenForEditViaScriptCallbacks(true, notOpenForEditPaths.ToArray(), outNotEditablePaths, out message, statusQueryOptions); } internal static bool IsOpenForEdit(string[] assetOrMetaFilePaths, List outNotEditablePaths, StatusQueryOptions statusQueryOptions) { outNotEditablePaths.Clear(); if (assetOrMetaFilePaths == null || assetOrMetaFilePaths.Length == 0) return true; var queryList = GetQueryList(assetOrMetaFilePaths, outNotEditablePaths); if (queryList.Count == 0) return outNotEditablePaths.Count == 0; // check with VCS if (!AssetModificationHook.GetOpenForEdit(false, queryList, outNotEditablePaths, statusQueryOptions)) return false; // check with possible script callbacks return GetOpenForEditViaScriptCallbacks(false, queryList.ToArray(), outNotEditablePaths, out var message, statusQueryOptions); } static List GetQueryList(string[] paths, List outNotEditablePaths) { var result = new List(paths.Length); foreach (var path in paths) { if (string.IsNullOrEmpty(path)) continue; // treat empty/null paths as editable (might be under Library folders etc.) var editability = GetPathEditability(path); if (editability == Editability.Always) continue; if (editability == Editability.Never) { outNotEditablePaths.Add(path); continue; } result.Add(path); } return result; } [RequiredByNativeCode] internal static void OnStatusUpdated() { WindowPending.OnStatusUpdated(); foreach (var assetModificationProcessorClass in AssetModificationProcessors) { const string methodName = "OnStatusUpdated"; MethodInfo method = assetModificationProcessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { object[] args = {}; if (!CheckArgumentsAndReturnType(args, method, typeof(void))) continue; using (new EditorPerformanceMarker($"{assetModificationProcessorClass.Name}.{methodName}", assetModificationProcessorClass).Auto()) method.Invoke(null, args); } } } static MethodInfo[] s_MakeEditableMethods; static MethodInfo[] GetMakeEditableMethods() { if (s_MakeEditableMethods == null) { var methods = new List(); Type[] types = { typeof(string[]), typeof(string), typeof(List) }; foreach (var type in AssetModificationProcessors) { MethodInfo method; try { method = type.GetMethod("MakeEditable", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch (AmbiguousMatchException) { Debug.LogWarning($"Ambiguous MakeEditable methods in {type.Name}."); continue; } if (method != null && CheckArgumentTypesAndReturnType(types, method, typeof(bool))) methods.Add(method); } s_MakeEditableMethods = methods.ToArray(); } return s_MakeEditableMethods; } internal static bool MakeEditable(string[] paths, string prompt, List outNotEditablePaths) { var methods = GetMakeEditableMethods(); if (methods == null || methods.Length == 0) return true; object[] args = { paths, prompt ?? string.Empty, outNotEditablePaths ?? new List() }; foreach (var method in methods) { if (!(bool)method.Invoke(null, args)) return false; } return true; } } } ================================================ FILE: Editor/Mono/AssetMoveResult.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; namespace UnityEditor { [Flags] public enum AssetMoveResult { // Tells the internal implementation that the asset was not moved physically on disk by the script DidNotMove = 0, // Tells the internal implementation that the script could not move the assets, and Unity should not attempt to move the asset FailedMove = 1, // Tells the internal implementation that the script moved the asset physically on disk. The internal implementation will DidMove = 2 } } ================================================ FILE: Editor/Mono/AssetPipeline/AssemblyDefinitionImporter.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEngine; namespace UnityEditorInternal { [ExcludeFromPreset] public sealed partial class AssemblyDefinitionImporter : AssetImporter { } public sealed partial class AssemblyDefinitionAsset : TextAsset { private AssemblyDefinitionAsset() {} private AssemblyDefinitionAsset(string text) {} } } ================================================ FILE: Editor/Mono/AssetPipeline/AssemblyDefinitionReferenceImporter.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor; using UnityEngine; namespace UnityEditorInternal { [ExcludeFromPreset] public sealed partial class AssemblyDefinitionReferenceImporter : AssetImporter { } public sealed partial class AssemblyDefinitionReferenceAsset : TextAsset { private AssemblyDefinitionReferenceAsset() {} private AssemblyDefinitionReferenceAsset(string text) {} } } ================================================ FILE: Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Diagnostics; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEngine.Scripting.APIUpdating; using Debug = UnityEngine.Debug; using Object = UnityEngine.Object; using UnityEditor.Experimental; namespace UnityEditor.AssetImporters { /// Universal structure that holds all the data relevant to importing an asset, including temporary data that needs to be shared across stages that make on any given importer's pipeline. /// /// Breaking up legacy importers into peaces and re-arranging them as pipelines that use those pieces so that the pieces can become building blocks that other importers can re-use implies that the pieces /// is not coupled with any given importer. For this decoupling and maximizing reuse, we need something that can hold the information describing what is being imported but also the data generated by the /// various parts that make up an importers pipeline. This container simply transports information from one "stage" to the other. Each stage is free to add/delete/alter the content of the container [RequiredByNativeCode] [NativeHeader("Editor/Src/AssetPipeline/AssetImportContext.h")] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public class AssetImportContext { // The bindings generator is setting the instance pointer in this field internal IntPtr m_Self; // the context can only be instantiated in native code AssetImportContext() {} public extern string assetPath { get; internal set; } [Obsolete("GetResultPath has been deprecated. Use GetOutputArtifactFilePath(string) instead (UnityUpgradable) -> GetOutputArtifactFilePath(*)")] [NativeName("GetOutputArtifactFilePath")] public extern string GetResultPath(string extension); public extern BuildTarget selectedBuildTarget { get; } [NativeThrows] public extern void SetMainObject(Object obj); public extern Object mainObject { get; } public void AddObjectToAsset(string identifier, Object obj) { AddObjectToAsset(identifier, obj, null); } [FreeFunction("AssetImportContextBindings::GetObjects", HasExplicitThis = true)] public extern void GetObjects([NotNull] List objects); [NativeThrows] public extern void AddObjectToAsset(string identifier, Object obj, Texture2D thumbnail); // Create a dependency against the contents of the source asset at the provided path // * if the asset at the path changes, it will trigger an import // * if the asset at the path moves, it will trigger an import public void DependsOnSourceAsset(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path", "Cannot add dependency on invalid path."); } DependsOnSourceAssetInternal(path); } [NativeName("DependsOnSourceAsset")] private extern void DependsOnSourceAssetInternal(string path); public void DependsOnSourceAsset(GUID guid) { if (guid.Empty()) { throw new ArgumentNullException("guid", "Cannot add dependency on empty GUID."); } DependsOnSourceAssetInternalGUID(guid); } [NativeName("DependsOnSourceAsset")] private extern void DependsOnSourceAssetInternalGUID(GUID guid); [NativeName("GetFolderEntries")] internal extern GUID[] GetFolderEntries(GUID folder); internal void DependsOnImportedAsset(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path", "Cannot add dependency on invalid path."); } DependsOnImportedAssetInternal(path); } [NativeName("GetArtifactFilePath")] private extern string GetArtifactFilePath_Internal(string path, string fileName); public string GetArtifactFilePath(string path, string fileName) { return GetArtifactFilePath_Internal(path, fileName); } public string GetArtifactFilePath(GUID guid, string fileName) { return GetArtifactFilePath(new ArtifactKey(guid), fileName); } public extern string GetArtifactFilePath(ArtifactKey key, string fileName); public extern string GetOutputArtifactFilePath(string fileName); [NativeName("DependsOnImportedAsset")] private extern void DependsOnImportedAssetInternal(string path); public extern Object GetReferenceToAssetMainObject(string path); public void DependsOnArtifact(ArtifactKey key) { if (!key.isValid) { throw new ArgumentNullException("key", "Cannot add dependency on invalid ArtifactKey."); } DependsOnArtifactInternal(key); } [NativeName("DependsOnArtifact")] private extern void DependsOnArtifactInternal(ArtifactKey key); public void DependsOnArtifact(GUID guid) { if (guid.Empty()) { throw new ArgumentNullException("guid", "Cannot add dependency on empty GUID."); } DependsOnArtifactInternalGUID(guid); } [NativeName("DependsOnArtifact")] private extern void DependsOnArtifactInternalGUID(GUID guid); public void DependsOnArtifact(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path", "Cannot add dependency on invalid path."); } DependsOnArtifactInternalPath(path); } [NativeName("DependsOnArtifact")] private extern void DependsOnArtifactInternalPath(string path); public void DependsOnCustomDependency(string dependency) { if (string.IsNullOrEmpty(dependency)) { throw new ArgumentNullException("dependency", "Cannot add custom dependency on an empty custom dependency."); } if (string.CompareOrdinal(dependency,"srp/default-shader") == 0 && assetPath.EndsWith(".shader", StringComparison.OrdinalIgnoreCase)) { throw new Exception($"A shader '{assetPath}' cannot depend on the 'srp/default-shader' custom dependency because this operation is unsupported."); } DependsOnCustomDependencyInternal(dependency); } [NativeName("DependsOnCustomDependency")] private extern void DependsOnCustomDependencyInternal(string path); extern void AddImportLog(string msg, string file, int line, ImportLogFlags flags, UnityEngine.Object obj); void AddImportLog(string msg, ImportLogFlags flags, UnityEngine.Object obj) { var st = new StackTrace(2, true); var sf = st.GetFrame(0); AddImportLog(msg, sf.GetFileName(), sf.GetFileLineNumber(), flags, obj); } public void LogImportError(string msg, UnityEngine.Object obj = null) { AddImportLog(msg, ImportLogFlags.Error, obj); } internal void LogImportError(string msg, string file, int line, UnityEngine.Object obj = null) { AddImportLog(msg, file, line, ImportLogFlags.Error, obj); } public void LogImportWarning(string msg, UnityEngine.Object obj = null) { AddImportLog(msg, ImportLogFlags.Warning, obj); } internal void LogImportWarning(string msg, string file, int line, UnityEngine.Object obj = null) { AddImportLog(msg, file, line, ImportLogFlags.Warning, obj); } internal static class BindingsMarshaller { public static IntPtr ConvertToNative(AssetImportContext ctx) => ctx.m_Self; } } } ================================================ FILE: Editor/Mono/AssetPipeline/AssetImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Linq; using System.Collections.Generic; using UnityEngine; using Object = UnityEngine.Object; using UnityEngine.Bindings; using UnityEngine.Scripting; using UnityEditor.AssetImporters; namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/AssetImporter.h")] [NativeHeader("Editor/Src/AssetPipeline/AssetImporter.bindings.h")] [ExcludeFromObjectFactory] [Preserve] [UsedByNativeCode] public partial class AssetImporter : Object { [NativeType(CodegenOptions.Custom, "MonoSourceAssetIdentifier")] public struct SourceAssetIdentifier { public SourceAssetIdentifier(Object asset) { if (asset == null) { throw new ArgumentNullException("asset"); } this.type = asset.GetType(); this.name = asset.name; } public SourceAssetIdentifier(Type type, string name) { if (type == null) { throw new ArgumentNullException("type"); } if (name == null) { throw new ArgumentNullException("name"); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException("The name is empty", "name"); } this.type = type; this.name = name; } public Type type; public string name; } [NativeName("AssetPathName")] public extern string assetPath { get; } public extern bool importSettingsMissing { get; } public extern ulong assetTimeStamp { get; } public extern string userData { get; set; } public extern string assetBundleName { get; set; } public extern string assetBundleVariant { get; set; } public static ImportLog GetImportLog(string path) { return GetImportLog(AssetDatabase.GUIDFromAssetPath(path)); } [FreeFunction("AssetImporter::GetImportLog")] internal static extern ImportLog GetImportLog(GUID guid); [FreeFunction("AssetImporter::GetImportLogEntriesCount")] internal static extern bool GetImportLogEntriesCount(GUID guid, out int nbErrors, out int nbWarnings); [NativeName("SetAssetBundleName")] extern public void SetAssetBundleNameAndVariant(string assetBundleName, string assetBundleVariant); [NativeMethod("SetThumbnailFromTexture2D")] extern internal void SetThumbnailFromTexture2D(Texture2D image, int instanceID); [FreeFunction("FindAssetImporterAtAssetPath")] extern public static AssetImporter GetAtPath(string path); public void SaveAndReimport() { AssetDatabase.ImportAsset(assetPath); } [FreeFunction("AssetImporterBindings::LocalFileIDToClassID")] extern internal static int LocalFileIDToClassID(long fileId); [FreeFunction("AssetImporterBindings::MakeLocalFileIDWithHash")] extern internal static long MakeLocalFileIDWithHash(int persistentTypeId, string name, long offset); extern internal long MakeInternalID(int persistentTypeId, string name); extern public void AddRemap(SourceAssetIdentifier identifier, Object externalObject); extern public bool RemoveRemap(SourceAssetIdentifier identifier); extern internal void RenameSubAssets(int peristentTypeId, string[] oldNames, string[] newNames); [FreeFunction("AssetImporterBindings::GetIdentifiers")] extern private static SourceAssetIdentifier[] GetIdentifiers(AssetImporter self); [FreeFunction("AssetImporterBindings::GetExternalObjects")] extern private static Object[] GetExternalObjects(AssetImporter self); public Dictionary GetExternalObjectMap() { // bogdanc: this is not optimal - we should have only one call to get both the identifiers and the external objects. // However, the new bindings do not support well output array parameters. // FIXME: change this to a single call when the bindings are fixed SourceAssetIdentifier[] identifiers = GetIdentifiers(this); Object[] externalObjects = GetExternalObjects(this); Dictionary map = new Dictionary(); for (int i = 0; i < identifiers.Length; ++i) { map.Add(identifiers[i], externalObjects[i]); } return map; } [FreeFunction("AssetImporterBindings::RegisterImporter", ThrowsException = true)] extern internal static void RegisterImporter(Type importer, int importerVersion, int queuePos, string fileExt, bool supportsImportDependencyHinting, bool autoSelect, bool allowCaching); [FreeFunction("AssetImporterBindings::SupportsRemappedAssetType", HasExplicitThis = true, IsThreadSafe = true)] public extern bool SupportsRemappedAssetType(Type type); internal extern double GetImportStartTime(); internal AssetPostprocessor.PostprocessorInfo[] GetDynamicPostprocessors() { return AssetPostprocessingInternal.GetSortedDynamicPostprocessorsForAsset(assetPath).ToArray(); } internal static AssetPostprocessor.PostprocessorInfo[] GetStaticPostprocessors(Type importerType) { return AssetPostprocessingInternal.GetSortedStaticPostprocessorTypes(importerType).ToArray(); } } } ================================================ FILE: Editor/Mono/AssetPipeline/BumpMapSettings.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Bindings; namespace UnityEditor { [StaticAccessor("BumpMapSettings::Get()", StaticAccessorType.Dot)] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/BumpMapSettings.h")] internal class BumpMapSettings { public static extern bool silentMode { get; set; } public static extern void PerformBumpMapCheck([NotNull] Material material); } public static class MaterialEditorExtensions { public static void PerformBumpMapCheck(this Material material) { BumpMapSettings.PerformBumpMapCheck(material); } } } ================================================ FILE: Editor/Mono/AssetPipeline/CameraDescription.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; namespace UnityEditor.AssetImporters { [RequiredByNativeCode] [NativeHeader("Editor/Src/AssetPipeline/ModelImporting/CameraDescription.h")] public class CameraDescription : IDisposable { internal IntPtr m_Ptr; public CameraDescription() { m_Ptr = Internal_Create(); } ~CameraDescription() { Destroy(); } public void Dispose() { Destroy(); GC.SuppressFinalize(this); } void Destroy() { if (m_Ptr != IntPtr.Zero) { Internal_Destroy(m_Ptr); m_Ptr = IntPtr.Zero; } } public bool TryGetProperty(string propertyName, out float value) => TryGetFloatProperty(propertyName, out value); public bool TryGetProperty(string propertyName, out Vector4 value) => TryGetVector4Property(propertyName, out value); public bool TryGetProperty(string propertyName, out string value) => TryGetStringProperty(propertyName, out value); public bool TryGetProperty(string propertyName, out int value) => TryGetIntProperty(propertyName, out value); public extern void GetVector4PropertyNames(List names); public extern void GetFloatPropertyNames(List names); public extern void GetStringPropertyNames(List names); public extern void GetIntPropertyNames(List names); extern bool TryGetVector4Property(string propertyName, out Vector4 value); extern bool TryGetFloatProperty(string propertyName, out float value); extern bool TryGetStringProperty(string propertyName, out string value); extern bool TryGetIntProperty(string propertyName, out int value); public bool TryGetAnimationCurve(string clipName, string propertyName, out AnimationCurve value) { value = TryGetAnimationCurve(clipName, propertyName); return value != null; } public extern bool HasAnimationCurveInClip(string clipName, string propertyName); public extern bool HasAnimationCurve(string propertyName); extern AnimationCurve TryGetAnimationCurve(string clipName, string propertyName); static extern IntPtr Internal_Create(); static extern void Internal_Destroy(IntPtr ptr); internal static class BindingsMarshaller { public static IntPtr ConvertToNative(CameraDescription desc) => desc.m_Ptr; } } } ================================================ FILE: Editor/Mono/AssetPipeline/ComputeShaderImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Bindings; namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/ComputeShaderImporter.h")] public sealed partial class ComputeShaderImporter : AssetImporter { public PreprocessorOverride preprocessorOverride { get { return PreprocessorOverride.UseProjectSettings; } set {} } } } ================================================ FILE: Editor/Mono/AssetPipeline/IHVImageFormatImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; namespace UnityEditor { [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("DDSImporter is obsolete. Use IHVImageFormatImporter instead (UnityUpgradable) -> IHVImageFormatImporter", true)] [NativeClass(null)] public sealed class DDSImporter : AssetImporter { public bool isReadable { get {return false; } set {} } } [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/IHVImageFormatImporter.h")] public sealed class IHVImageFormatImporter : AssetImporter { public extern bool isReadable { get; set; } public extern FilterMode filterMode { get; set; } // note: wrapMode getter returns U wrapping axis public extern TextureWrapMode wrapMode { [NativeName("GetWrapU")] get; [NativeName("SetWrapUVW")] set; } [NativeName("WrapU")] public extern TextureWrapMode wrapModeU { get; set; } [NativeName("WrapV")] public extern TextureWrapMode wrapModeV { get; set; } [NativeName("WrapW")] public extern TextureWrapMode wrapModeW { get; set; } [NativeConditional("ENABLE_TEXTURE_STREAMING")] public extern bool streamingMipmaps { get; set; } [NativeConditional("ENABLE_TEXTURE_STREAMING")] public extern int streamingMipmapsPriority { get; set; } public extern bool ignoreMipmapLimit { get; set; } public extern string mipmapLimitGroupName { get; set; } } } ================================================ FILE: Editor/Mono/AssetPipeline/ImportLog.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using Object = UnityEngine.Object; namespace UnityEditor.AssetImporters { // Subset of C++ LogMessageFlags in LogAssert.h [Flags] public enum ImportLogFlags { None = 0, // kNoLogMessageFlags Error = 1 << 6, // kAssetImportError Warning = 1 << 7 // kAssetImportWarning }; [NativeHeader("Editor/Src/AssetPipeline/ImportLog.h")] [NativeHeader("Editor/Src/AssetPipeline/ImportLog.bindings.h")] [ExcludeFromObjectFactory] public sealed class ImportLog : Object { internal struct Filters { public const string SearchToken = "i"; public const string AllIssuesStr = "all"; public const string ErrorsStr = "errors"; public const string WarningsStr = "warnings"; } [NativeType(CodegenOptions.Custom, "MonoImportLogEntry")] public struct ImportLogEntry { public string message; public ImportLogFlags flags; public int line; public string file; public UnityEngine.Object context { get => Object.FindObjectFromInstanceID(instanceID); set => instanceID = value.GetInstanceID(); } internal int instanceID; }; public extern ImportLogEntry[] logEntries { [NativeMethod("GetLogEntries")] get; } [NativeMethod("PrintToConsole")] internal extern void PrintToConsole(); } } ================================================ FILE: Editor/Mono/AssetPipeline/LightDescription.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; namespace UnityEditor.AssetImporters { [RequiredByNativeCode] [NativeHeader("Editor/Src/AssetPipeline/ModelImporting/LightDescription.h")] public class LightDescription : IDisposable { internal IntPtr m_Ptr; public LightDescription() { m_Ptr = Internal_Create(); } ~LightDescription() { Destroy(); } public void Dispose() { Destroy(); GC.SuppressFinalize(this); } void Destroy() { if (m_Ptr != IntPtr.Zero) { Internal_Destroy(m_Ptr); m_Ptr = IntPtr.Zero; } } public bool TryGetProperty(string propertyName, out float value) => TryGetFloatProperty(propertyName, out value); public bool TryGetProperty(string propertyName, out Vector4 value) => TryGetVector4Property(propertyName, out value); public bool TryGetProperty(string propertyName, out string value) => TryGetStringProperty(propertyName, out value); public bool TryGetProperty(string propertyName, out int value) => TryGetIntProperty(propertyName, out value); public extern void GetVector4PropertyNames(List names); public extern void GetFloatPropertyNames(List names); public extern void GetStringPropertyNames(List names); public extern void GetIntPropertyNames(List names); extern bool TryGetVector4Property(string propertyName, out Vector4 value); extern bool TryGetFloatProperty(string propertyName, out float value); extern bool TryGetStringProperty(string propertyName, out string value); extern bool TryGetIntProperty(string propertyName, out int value); public bool TryGetAnimationCurve(string clipName, string propertyName, out AnimationCurve value) { value = TryGetAnimationCurve(clipName, propertyName); return value != null; } public extern bool HasAnimationCurveInClip(string clipName, string propertyName); public extern bool HasAnimationCurve(string propertyName); extern AnimationCurve TryGetAnimationCurve(string clipName, string propertyName); static extern IntPtr Internal_Create(); static extern void Internal_Destroy(IntPtr ptr); internal static class BindingsMarshaller { public static IntPtr ConvertToNative(LightDescription desc) => desc.m_Ptr; } } } ================================================ FILE: Editor/Mono/AssetPipeline/LocalCacheServer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System.IO; using UnityEditor.Utils; namespace UnityEditor { internal class LocalCacheServer { public const string PathKey = "LocalCacheServerPath"; public const string CustomPathKey = "LocalCacheServerCustomPath"; public static string GetCacheLocation() { var cachePath = EditorPrefs.GetString(PathKey); var enableCustomPath = EditorPrefs.GetBool(CustomPathKey); var result = cachePath; if (!enableCustomPath || string.IsNullOrEmpty(cachePath)) result = Paths.Combine(OSUtil.GetDefaultCachePath(), "CacheServer"); return result; } public static void Setup() { var mode = (AssetPipelinePreferences.CacheServerMode)EditorPrefs.GetInt("CacheServerMode"); if (mode == AssetPipelinePreferences.CacheServerMode.Local) { EditorGUILayout.HelpBox("Local CacheServer is no longer supported", MessageType.Info, true); } } public static void Clear() { string cacheDirectoryPath = GetCacheLocation(); if (Directory.Exists(cacheDirectoryPath)) Directory.Delete(cacheDirectoryPath, true); } } } ================================================ FILE: Editor/Mono/AssetPipeline/MaterialDescription.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; namespace UnityEditor.AssetImporters { [NativeType(Header = "Editor/Src/AssetPipeline/ModelImporting/MaterialDescription.h")] public struct TexturePropertyDescription { public Vector2 offset; public Vector2 scale; public Texture texture; public string relativePath; public string path; } [RequiredByNativeCode] [NativeHeader("Editor/Src/AssetPipeline/ModelImporting/MaterialDescription.h")] public class MaterialDescription : IDisposable { internal IntPtr m_Ptr; public MaterialDescription() { m_Ptr = Internal_Create(); } ~MaterialDescription() { Destroy(); } public void Dispose() { Destroy(); GC.SuppressFinalize(this); } void Destroy() { if (m_Ptr != IntPtr.Zero) { Internal_Destroy(m_Ptr); m_Ptr = IntPtr.Zero; } } [NativeProperty("materialName", TargetType.Field)] public extern string materialName { get; } public bool TryGetProperty(string propertyName, out float value) => TryGetFloatProperty(propertyName, out value); public bool TryGetProperty(string propertyName, out Vector4 value) => TryGetVector4Property(propertyName, out value); public bool TryGetProperty(string propertyName, out string value) => TryGetStringProperty(propertyName, out value); public bool TryGetProperty(string propertyName, out TexturePropertyDescription value) => TryGetTextureProperty(propertyName, out value); public extern void GetVector4PropertyNames(List names); public extern void GetFloatPropertyNames(List names); public extern void GetTexturePropertyNames(List names); public extern void GetStringPropertyNames(List names); extern bool TryGetVector4Property(string propertyName, out Vector4 value); extern bool TryGetFloatProperty(string propertyName, out float value); extern bool TryGetTextureProperty(string propertyName, out TexturePropertyDescription value); extern bool TryGetStringProperty(string propertyName, out string value); public bool TryGetAnimationCurve(string clipName, string propertyName, out AnimationCurve value) { value = TryGetAnimationCurve(clipName, propertyName); return value != null; } public extern bool HasAnimationCurveInClip(string clipName, string propertyName); public extern bool HasAnimationCurve(string propertyName); extern AnimationCurve TryGetAnimationCurve(string clipName, string propertyName); static extern IntPtr Internal_Create(); static extern void Internal_Destroy(IntPtr ptr); internal static class BindingsMarshaller { public static IntPtr ConvertToNative(MaterialDescription desc) => desc.m_Ptr; } } } ================================================ FILE: Editor/Mono/AssetPipeline/ShaderImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Bindings; namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/ShaderImporter.h")] public sealed partial class ShaderImporter : AssetImporter { public extern Shader GetShader(); public extern void SetDefaultTextures(string[] name, Texture[] textures); public extern Texture GetDefaultTexture(string name); public extern void SetNonModifiableTextures(string[] name, Texture[] textures); public extern Texture GetNonModifiableTexture(string name); public PreprocessorOverride preprocessorOverride { get { return PreprocessorOverride.UseProjectSettings; } set {} } } } ================================================ FILE: Editor/Mono/AssetPipeline/ShaderIncludeImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Bindings; namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/ShaderIncludeImporter.h")] internal sealed partial class ShaderIncludeImporter : AssetImporter { } [NativeHeader("Editor/Src/Shaders/ShaderInclude.h")] public sealed partial class ShaderInclude : TextAsset { } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.IO; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEditor.AssetImporters; using STVertex = UnityEditor.SpeedTree.Importer.Vertex; using Material = UnityEngine.Material; using Color = UnityEngine.Color; using static UnityEditor.SpeedTree.Importer.SpeedTreeImporterCommon; using static UnityEditor.SpeedTree.Importer.WindConfigSDK; using static UnityEditor.SpeedTree.Importer.SpeedTree9Importer; using static UnityEditor.SpeedTree.Importer.SpeedTree9Reader; namespace UnityEditor.SpeedTree.Importer { // [2024-09-27] version: 3 // Fixed code that would lead to m_LODCount vs m_PerLODSettings.arraySize mismatching in GUI [ScriptedImporter(version: 3, ext: "st9", AllowCaching = true)] public class SpeedTree9Importer : ScriptedImporter { const int SPEEDTREE_9_WIND_VERSION = 1; const int SPEEDTREE_9_MATERIAL_VERSION = 1; internal static class ImporterSettings { internal const string kGameObjectName = "SpeedTree"; internal const string kLegacyShaderName = "Nature/SpeedTree9"; internal const string kWindAssetName = "SpeedTreeWind"; internal const string kSRPDependencyName = "SpeedTree9Importer_DefaultShader"; internal const string kMaterialSettingsDependencyname = "SpeedTree9Importer_MaterialSettings"; internal const string kIconName = "UnityEditor/SpeedTree9Importer Icon"; } private static class Styles { internal static readonly Texture2D kIcon = EditorGUIUtility.FindTexture(ImporterSettings.kIconName); } private struct STMeshGeometry { public Vector3[] vertices; public Vector3[] normals; public Vector4[] tangents; public Color32[] colors32; public Vector4[][] uvs; public int lodIndex; public STMeshGeometry(int vertexCount, int UVCount, int indexLod) { vertices = new Vector3[vertexCount]; normals = new Vector3[vertexCount]; tangents = new Vector4[vertexCount]; colors32 = new Color32[vertexCount]; uvs = new Vector4[UVCount][]; lodIndex = indexLod; for (int i = 0; i < UVCount; ++i) { uvs[i] = new Vector4[vertexCount]; } } } [SerializeField] internal MeshSettings m_MeshSettings = new MeshSettings(); [SerializeField] internal MaterialSettings m_MaterialSettings = new MaterialSettings(); [SerializeField] internal LightingSettings m_LightingSettings = new LightingSettings(); [SerializeField] internal AdditionalSettings m_AdditionalSettings = new AdditionalSettings(); [SerializeField] internal LODSettings m_LODSettings = new LODSettings(); [SerializeField] internal List m_PerLODSettings = new List(); [SerializeField] internal WindSettings m_WindSettings = new WindSettings(); [SerializeField] internal int m_MaterialVersion = SPEEDTREE_9_MATERIAL_VERSION; /// /// Necessary to set default HDRP properties and materials upgrade. /// /// The main object used by the importer, containing the data. public delegate void OnAssetPostProcess(GameObject mainObject); /// /// Exposes the Diffuse Profile property in the importer inspector with compatible render pipelines. /// /// The serialized property of the diffusion profile asset. /// The serialized property of the diffusion profile hash. /// /// Necessary to expose the Diffuse Profile property in the inspector, since the importer is not /// aware of HDRP as it's a package. This property is highly used by artists, so exposing it is a big win. /// public delegate void OnCustomEditorSettings(ref SerializedProperty diffusionProfileAsset, ref SerializedProperty diffusionProfileHash); [SerializeField] internal SpeedTreeImporterOutputData m_OutputImporterData; private static ulong s_DefaultShaderHash; private static readonly TimeSpan k_CheckDependencyFrequency = TimeSpan.FromSeconds(5); private static DateTime s_LastCheck; // Cache main objects, created during import process. private AssetImportContext m_Context; private SpeedTree9Reader m_Tree; private Shader m_Shader; private SpeedTreeWindAsset m_WindAsset; // Values cached at the begining of the import process. private bool m_HasFacingData; private bool m_HasBranch2Data; private bool m_LastLodIsBillboard; private bool m_WindEnabled; private uint m_LODCount; private uint m_CollisionObjectsCount; private string m_PathFromDirectory; internal bool MaterialsShouldBeRegenerated => m_MaterialVersion != SPEEDTREE_9_MATERIAL_VERSION; public override bool SupportsRemappedAssetType(Type type) { return true; } public override void OnImportAsset(AssetImportContext ctx) { m_Context = ctx; m_Tree = new SpeedTree9Reader(); FileStatus status = m_Tree.Initialize(ctx.assetPath); if (status != FileStatus.Valid) { ctx.LogImportError($"Error while initializing the SpeedTree9 reader: {status}."); return; } m_Tree.ReadContent(); CacheTreeImporterValues(ctx.assetPath); ctx.DependsOnCustomDependency(ImporterSettings.kSRPDependencyName); if (!TryGetShaderForCurrentRenderPipeline(out m_Shader)) { ctx.LogImportError("SpeedTree9 shader is invalid, cannot create Materials for this SpeedTree asset."); return; } m_OutputImporterData = SpeedTreeImporterOutputData.Create(); m_OutputImporterData.hasBillboard = m_LastLodIsBillboard; CalculateScaleFactorFromUnit(); if (m_WindEnabled) { m_OutputImporterData.m_WindConfig = CopySpeedTree9WindConfig(m_Tree.Wind, m_MeshSettings.scaleFactor, m_Tree.Bounds); SetWindParameters(ref m_OutputImporterData.m_WindConfig); m_WindAsset = new SpeedTreeWindAsset(SPEEDTREE_9_WIND_VERSION, m_OutputImporterData.m_WindConfig) { name = ImporterSettings.kWindAssetName, }; } GameObject mainObject = new GameObject(ImporterSettings.kGameObjectName); if (m_AdditionalSettings.generateRigidbody) { CreateAndAddRigidBodyToAsset(mainObject); } ctx.AddObjectToAsset(ImporterSettings.kGameObjectName, mainObject); ctx.SetMainObject(mainObject); SetThumbnailFromTexture2D(Styles.kIcon, mainObject.GetInstanceID()); m_OutputImporterData.mainObject = mainObject; CalculateBillboardAndPerLODSettings(); CreateMeshAndMaterials(); CreateAssetIdentifiersAndAddMaterialsToContext(); if (m_AdditionalSettings.generateColliders && m_CollisionObjectsCount > 0) { CreateAndAddCollidersToAsset(); } if (m_WindEnabled) { ctx.AddObjectToAsset(ImporterSettings.kWindAssetName, m_WindAsset); } ctx.AddObjectToAsset(m_OutputImporterData.name, m_OutputImporterData); ctx.DependsOnCustomDependency(ImporterSettings.kMaterialSettingsDependencyname); AddDependencyOnExtractedMaterials(); TriggerAllCallbacks(); } private void TriggerAllCallbacks() { var allMethods = AttributeHelper.GetMethodsWithAttribute().methodsWithAttributes; foreach (var method in allMethods) { var callback = Delegate.CreateDelegate(typeof(OnAssetPostProcess), method.info) as OnAssetPostProcess; callback?.Invoke(m_OutputImporterData.mainObject); } } private void CacheTreeImporterValues(string assetPath) { // Variables used a lot are cached, since accessing any Reader array has a non-negligeable cost. m_LODCount = (uint)m_Tree.Lod.Length; if(m_LODCount > LODGroupGUI.kLODColors.Length) { Debug.LogWarningFormat("Number of LOD meshes in asset ({0}) is larger than the maximum number supported by Unity GUI ({1})." + "\nImporting only the first {1} LOD meshes." , m_LODCount, LODGroupGUI.kLODColors.Length); // LODGroup GUI won't draw if we're above this limit, so we prevent future assertions here. m_LODCount = (uint)LODGroupGUI.kLODColors.Length; } m_HasFacingData = TreeHasFacingData(); m_HasBranch2Data = m_Tree.Wind.DoBranch2; m_LastLodIsBillboard = m_Tree.BillboardInfo.LastLodIsBillboard; m_CollisionObjectsCount = (uint)m_Tree.CollisionObjects.Length; WindConfigSDK windCfg = m_Tree.Wind; m_WindEnabled = (windCfg.DoShared || windCfg.DoBranch1 || windCfg.DoBranch2 || windCfg.DoRipple) && m_WindSettings.enableWind; m_PathFromDirectory = Path.GetDirectoryName(assetPath) + "/"; } internal void RegenerateMaterials() { m_OutputImporterData = AssetDatabase.LoadAssetAtPath(assetPath); if (m_OutputImporterData.hasEmbeddedMaterials) { // TODO: Verify if we could only generate the embedded materials // instead of reimporting entirely the asset. SaveAndReimport(); return; } try { AssetDatabase.StartAssetEditing(); RegenerateAndPopulateExternalMaterials(this.assetPath); TriggerAllCallbacks(); } finally { AssetDatabase.StopAssetEditing(); } } [InitializeOnLoadMethod] static void InitializeEditorCallback() { EditorApplication.update += DirtyCustomDependencies; s_DefaultShaderHash = ComputeDefaultShaderHash(); AssetDatabase.RegisterCustomDependency(ImporterSettings.kSRPDependencyName, new Hash128(s_DefaultShaderHash, 0)); } static ulong CombineHash(ulong h1, ulong h2) { unchecked { return h1 ^ h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2); // Similar to c++ boost::hash_combine } } static ulong ComputeDefaultShaderHash() { ulong newDefaultShaderHash = 0UL; if (GraphicsSettings.GetDefaultShader(DefaultShaderType.SpeedTree9) == null) { newDefaultShaderHash = 0; } else { if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(GraphicsSettings.GetDefaultShader(DefaultShaderType.SpeedTree9), out var guid, out long fileId)) { newDefaultShaderHash = CombineHash((ulong)guid.GetHashCode(), (ulong)fileId); } } return newDefaultShaderHash; } static void DirtyCustomDependencies() { DateTime now = DateTime.UtcNow; if (Application.isPlaying || ((now - s_LastCheck) < k_CheckDependencyFrequency)) { return; } s_LastCheck = now; ulong newDefaultShaderHash = ComputeDefaultShaderHash(); if (s_DefaultShaderHash != newDefaultShaderHash) { s_DefaultShaderHash = newDefaultShaderHash; AssetDatabase.RegisterCustomDependency(ImporterSettings.kSRPDependencyName, new Hash128(s_DefaultShaderHash, 0)); AssetDatabase.Refresh(); } } #region Mesh Geometry & Renderers private Mesh CreateMeshAndGeometry(Lod lod, int lodIndex) { bool isBillboard = m_LastLodIsBillboard && (lodIndex == (m_LODCount - 1)); int vertexCount = (int)lod.Vertices.Length; int numUVs = CalculateNumUVs(isBillboard); STMeshGeometry sTMeshGeometry = new STMeshGeometry(vertexCount, numUVs, lodIndex); CalculateMeshGeometry(sTMeshGeometry, lod, isBillboard); Mesh mesh = new Mesh() { name = "LOD" + sTMeshGeometry.lodIndex + "_Mesh", indexFormat = (sTMeshGeometry.vertices.Length > 65535) ? IndexFormat.UInt32 : IndexFormat.UInt16, subMeshCount = (int)lod.DrawCalls.Length, vertices = sTMeshGeometry.vertices, normals = sTMeshGeometry.normals, tangents = sTMeshGeometry.tangents, colors32 = sTMeshGeometry.colors32 }; mesh.SetUVs(0, sTMeshGeometry.uvs[0]); mesh.SetUVs(1, sTMeshGeometry.uvs[1]); if (!isBillboard) { // SpeedTree shader expects certain UV2 & UV3 values for leaf facing & wind. // if we don't claim them here now, tree rendering may break when Unity // uses UV2 & UV3 and the shader finds unexpected values. mesh.SetUVs(2, sTMeshGeometry.uvs[2]); // Branch2Pos, Branch2Dir, Branch2Weight, mesh.SetUVs(3, sTMeshGeometry.uvs[3]); // 2/3 Anchor XYZ, FacingFlag } return mesh; } private void SetMeshIndices(Mesh mesh, Lod lod, DrawCall draw, int drawIndex) { int[] indices = new int[draw.IndexCount]; uint[] lodIndices = lod.Indices; for (int index = 0; index < draw.IndexCount; ++index) { indices[index] = unchecked((int)lodIndices[unchecked((int)(draw.IndexStart + index))]); } mesh.SetIndices(indices, MeshTopology.Triangles, drawIndex, true); } private void CreateMeshAndLODObjects(Mesh mesh, int lodIndex, ref LOD[] lods) { mesh.RecalculateUVDistributionMetrics(); GameObject lodObject = new GameObject("LOD" + lodIndex); lodObject.transform.parent = m_OutputImporterData.mainObject.transform; MeshFilter meshFilter = lodObject.AddComponent(); meshFilter.mesh = mesh; MeshRenderer renderer = lodObject.AddComponent(); { lods[lodIndex] = new LOD(m_PerLODSettings[lodIndex].height, new Renderer[] { renderer }); SetMeshRendererSettings(renderer, lodIndex); } AddTreeComponentToLODObject(lodObject); m_Context.AddObjectToAsset(mesh.name, mesh); } private void CalculateMeshGeometry(STMeshGeometry sTMeshGeometry, Lod lod, bool isBillboard) { STVertex[] vertices = lod.Vertices; for (int i = 0; i < sTMeshGeometry.vertices.Length; ++i) { STVertex vertex = vertices[i]; sTMeshGeometry.vertices[i].Set( vertex.Anchor.X + (vertex.CameraFacing ? -vertex.Offset.X : vertex.Offset.X), vertex.Anchor.Y + vertex.Offset.Y, vertex.Anchor.Z + vertex.Offset.Z); sTMeshGeometry.vertices[i] *= m_MeshSettings.scaleFactor; sTMeshGeometry.normals[i].Set(vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z); Vector3 vertexTangent = new Vector3(vertex.Tangent.X, vertex.Tangent.Y, vertex.Tangent.Z); Vector3 binormal = Vector3.Cross(sTMeshGeometry.normals[i], vertexTangent); Vector2 vertexBinormal = new Vector3(vertex.Binormal.X, vertex.Binormal.Y, vertex.Binormal.Z); float dot = Vector3.Dot(binormal, vertexBinormal); float flip = (dot < 0.0f) ? -1.0f : 1.0f; sTMeshGeometry.tangents[i].Set(vertex.Tangent.X, vertex.Tangent.Y, vertex.Tangent.Z, flip); sTMeshGeometry.colors32[i] = new Color( vertex.Color.X * vertex.AmbientOcclusion, vertex.Color.Y * vertex.AmbientOcclusion, vertex.Color.Z * vertex.AmbientOcclusion, vertex.BlendWeight); // Texcoord setup: // 0 Diffuse UV, Branch1Pos, Branch1Dir // 1 Lightmap UV, Branch1Weight, RippleWeight // If Branch2 is available: // 2 Branch2Pos, Branch2Dir, Branch2Weight, // If camera-facing geom is available: // 2/3 Anchor XYZ, FacingFlag int currentUV = 0; sTMeshGeometry.uvs[currentUV++][i].Set( vertex.TexCoord.X, vertex.TexCoord.Y, vertex.BranchWind1.X, vertex.BranchWind1.Y); sTMeshGeometry.uvs[currentUV++][i].Set( vertex.LightmapTexCoord.X, vertex.LightmapTexCoord.Y, vertex.BranchWind1.Z, vertex.RippleWeight); if (!isBillboard) { float anchorX = m_HasFacingData ? vertex.Anchor.X * m_MeshSettings.scaleFactor : 0.0f; float anchorY = m_HasFacingData ? vertex.Anchor.Y * m_MeshSettings.scaleFactor : 0.0f; float anchorZ = m_HasFacingData ? vertex.Anchor.Z * m_MeshSettings.scaleFactor : 0.0f; float leafFacingFlag = vertex.CameraFacing ? 1.0f : 0.0f; sTMeshGeometry.uvs[currentUV++][i].Set( m_HasBranch2Data ? vertex.BranchWind2.X : anchorX, m_HasBranch2Data ? vertex.BranchWind2.Y : anchorY, m_HasBranch2Data ? vertex.BranchWind2.Z : anchorZ, m_HasBranch2Data ? 0.0f /*UNUSED*/ : leafFacingFlag); bool useUV3 = m_HasBranch2Data && m_HasFacingData; sTMeshGeometry.uvs[currentUV++][i].Set( useUV3 ? anchorX : 0.0f, useUV3 ? anchorY : 0.0f, useUV3 ? anchorZ : 0.0f, useUV3 ? leafFacingFlag : 0.0f); } } } private void SetMeshRendererSettings(MeshRenderer renderer, int lodIndex) { ShadowCastingMode castMode = (m_LightingSettings.enableShadowCasting) ? ShadowCastingMode.On : ShadowCastingMode.Off; LightProbeUsage probeUsage = (m_LightingSettings.enableLightProbes) ? LightProbeUsage.BlendProbes : LightProbeUsage.Off; ReflectionProbeUsage reflectionProbe = m_LightingSettings.reflectionProbeEnumValue; bool receiveShadows = m_LightingSettings.enableShadowReceiving; if (m_PerLODSettings[lodIndex].enableSettingOverride) { castMode = m_PerLODSettings[lodIndex].castShadows ? ShadowCastingMode.On : ShadowCastingMode.Off; probeUsage = m_PerLODSettings[lodIndex].useLightProbes ? LightProbeUsage.BlendProbes : LightProbeUsage.Off; receiveShadows = m_PerLODSettings[lodIndex].receiveShadows; reflectionProbe = m_PerLODSettings[lodIndex].reflectionProbeUsage; } renderer.sharedMaterials = RetrieveMaterialsForCurrentLod(lodIndex); renderer.motionVectorGenerationMode = m_AdditionalSettings.motionVectorModeEnumValue; renderer.receiveShadows = receiveShadows; renderer.shadowCastingMode = castMode; renderer.lightProbeUsage = probeUsage; renderer.reflectionProbeUsage = reflectionProbe; } private int CalculateNumUVs(bool isBillboard) { int numUVs = 2; if (!isBillboard) { numUVs += 2; // reserve UV2 & UV3 for 3D-geometry to detect leaf facing (VS effect) correctly } return numUVs; } #endregion #region LODs private PerLODSettings InstantiateAndInitializeLODSettingsObject(bool isBillboardLOD) { return new PerLODSettings() { enableSettingOverride = isBillboardLOD, enableBump = m_MaterialSettings.enableBumpMapping, enableHue = m_MaterialSettings.enableHueVariation, enableSubsurface = m_MaterialSettings.enableSubsurfaceScattering, castShadows = true, receiveShadows = true, useLightProbes = !isBillboardLOD, reflectionProbeUsage = isBillboardLOD ? ReflectionProbeUsage.Off : ReflectionProbeUsage.BlendProbes, }; } private void CalculateBillboardAndPerLODSettings() { if (m_PerLODSettings.Count > m_LODCount) { m_PerLODSettings.RemoveRange((int)m_LODCount, m_PerLODSettings.Count); } else if (m_PerLODSettings.Count < m_LODCount) { m_PerLODSettings.Clear(); for (int i = 0; i < m_LODCount; ++i) { bool isBillboardLOD = m_LastLodIsBillboard && i == m_LODCount - 1; PerLODSettings lodSettings = InstantiateAndInitializeLODSettingsObject(isBillboardLOD); m_PerLODSettings.Add(lodSettings); } // Always reset LOD heights if size doesn't match. for (int i = 0; i < m_LODCount; ++i) { m_PerLODSettings[i].height = (i == 0) ? 0.5f : m_PerLODSettings[i - 1].height * 0.5f; } if (m_LODCount > 0) { // Using this pattern to avoid using 'Last' from Linq. m_PerLODSettings[^1].height = 0.01f; } } Debug.Assert(m_PerLODSettings.Count == m_LODCount); } private Material[] RetrieveMaterialsForCurrentLod(int lodIndex) { List materials = m_OutputImporterData.lodMaterials.materials; if (materials == null || materials.Count == 0) return null; List matIDs = m_OutputImporterData.lodMaterials.lodToMaterials[lodIndex]; // Using this pattern to avoid using 'Select' from Linq. Material[] lodMaterials = new Material[matIDs.Count]; for (int i = 0; i < matIDs.Count; ++i) { lodMaterials[i] = materials[matIDs[i]].material; } return lodMaterials; } private void AddLODGroupToMainObjectAndSetTransition(LOD[] lods) { LODGroup lodGroup = m_OutputImporterData.mainObject.AddComponent(); lodGroup.SetLODs(lods); int numLODs = lods.Length; if (m_LODSettings.enableSmoothLODTransition && numLODs > 0) { lodGroup.fadeMode = LODFadeMode.CrossFade; lodGroup.animateCrossFading = m_LODSettings.animateCrossFading; lodGroup.lastLODBillboard = m_LastLodIsBillboard; if (!m_LODSettings.animateCrossFading) { int lastLod = numLODs - 1; lods[lastLod].fadeTransitionWidth = m_LODSettings.fadeOutWidth; if (m_LastLodIsBillboard && numLODs > 2) { int lastMeshLOD = numLODs - 2; lods[lastMeshLOD].fadeTransitionWidth = m_LODSettings.billboardTransitionCrossFadeWidth; } } } lodGroup.RecalculateBounds(); } private void AddTreeComponentToLODObject(GameObject mainObject) { // Register the wind asset ptr through Tree component. Tree treeComponent = mainObject.AddComponent(); if (m_WindEnabled) { treeComponent.windAsset = m_WindAsset; } } #endregion #region Materials private void CreateMeshAndMaterials(bool regenerateMaterials = false) { LOD[] lods = new LOD[m_LODCount]; // Loop each LOD (mesh) of the asset. for (int lodIndex = 0; lodIndex < m_LODCount; ++lodIndex) { Lod lod = m_Tree.Lod[lodIndex]; Mesh mesh = CreateMeshAndGeometry(lod, lodIndex); // Loop each DrawCall (material) of the current mesh LOD. for (int drawIndex = 0; drawIndex < lod.DrawCalls.Length; ++drawIndex) { DrawCall draw = lod.DrawCalls[drawIndex]; STMaterial stMaterial = m_Tree.Materials[(int)draw.MaterialIndex]; CreateMaterialsForCurrentLOD(stMaterial, lodIndex, regenerateMaterials); SetMeshIndices(mesh, lod, draw, drawIndex); } CreateMeshAndLODObjects(mesh, lodIndex, ref lods); } AddLODGroupToMainObjectAndSetTransition(lods); } private void CreateMaterialsForCurrentLOD(STMaterial stMaterial, int lodIndex, bool regenerateMaterials) { bool matOverrided = m_PerLODSettings[lodIndex].enableSettingOverride; bool isBillboard = m_LastLodIsBillboard && (lodIndex == (m_LODCount - 1)); // Overrided LODs have they own unique material. string stMatName = stMaterial.Name; if (matOverrided && !isBillboard) stMatName += String.Format("LOD{0}", lodIndex); // Retrieve previously extracted material and update serialized index. if (TryGetExternalMaterial(stMatName, out var extractedMat)) { // Explicity regenerate materials, should happen when bumping the material version for example. if (regenerateMaterials) { extractedMat = CreateMaterial(stMaterial, lodIndex, extractedMat.name, m_PathFromDirectory); SetMaterialTextureAndColorProperties(stMaterial, extractedMat, lodIndex, m_PathFromDirectory); } else { RetrieveMaterialSpecialProperties(extractedMat); } var existedMatIndex = m_OutputImporterData.lodMaterials.materials.FindIndex(m => m.defaultName == stMatName); if (existedMatIndex == -1) { m_OutputImporterData.lodMaterials.materials.Add(new MaterialInfo { material = extractedMat, defaultName = stMatName, exported = true }); m_OutputImporterData.lodMaterials.matNameToIndex[stMatName] = m_OutputImporterData.lodMaterials.materials.Count - 1; } else { m_OutputImporterData.lodMaterials.matNameToIndex[stMatName] = existedMatIndex; } } // Create material if it doesn't exist yet. else if (!m_OutputImporterData.lodMaterials.matNameToIndex.ContainsKey(stMatName)) { Material newMat = CreateMaterial(stMaterial, lodIndex, stMatName, m_PathFromDirectory); m_OutputImporterData.lodMaterials.materials.Add(new MaterialInfo { material = newMat, defaultName = stMatName, exported = false }); m_OutputImporterData.lodMaterials.matNameToIndex.Add(stMatName, m_OutputImporterData.lodMaterials.materials.Count - 1); } // Map the material id to the current LOD. int indexMat = m_OutputImporterData.lodMaterials.matNameToIndex[stMatName]; m_OutputImporterData.lodMaterials.AddLodMaterialIndex(lodIndex, indexMat); } private void CreateAssetIdentifiersAndAddMaterialsToContext() { m_OutputImporterData.materialsIdentifiers.Clear(); foreach (MaterialInfo matInfo in m_OutputImporterData.lodMaterials.materials) { m_OutputImporterData.hasEmbeddedMaterials |= !matInfo.exported; if (!matInfo.exported) { m_Context.AddObjectToAsset(matInfo.material.name, matInfo.material); // It looks like a limitation from the default AssetImporter system. When deleting extracted materials manually, // the external object map still contains a null reference, even after a reimport of the asset. if (TryGetSourceAssetIdentifierFromName(matInfo.material.name, out var assetIdentifier)) { RemoveRemap(assetIdentifier); } } m_OutputImporterData.materialsIdentifiers.Add(new AssetIdentifier(matInfo.material.GetType(), matInfo.defaultName)); } } private void RegenerateMaterialsFromTree() { for (int lodIndex = 0; lodIndex < m_LODCount; lodIndex++) { Lod stLOD = m_Tree.Lod[lodIndex]; // Loop necessary materials for current LOD. for (int drawIndex = 0; drawIndex < stLOD.DrawCalls.Length; ++drawIndex) { int matIndex = (int)stLOD.DrawCalls[drawIndex].MaterialIndex; STMaterial stMaterial = m_Tree.Materials[matIndex]; CreateMaterialsForCurrentLOD(stMaterial, lodIndex, regenerateMaterials: true); } } } private void RegenerateAndPopulateExternalMaterials(string assetPath) { // This object could potentially be cached, but this function is rarely triggered (only when bumping the material version) // so the cost of caching it is not really interesting. m_Tree = new SpeedTree9Reader(); FileStatus status = m_Tree.Initialize(assetPath); if (status != FileStatus.Valid) { Debug.LogError($"Error while initializing the SpeedTree9 reader: {status}."); return; } m_Tree.ReadContent(); CacheTreeImporterValues(assetPath); if (!TryGetShaderForCurrentRenderPipeline(out m_Shader)) { Debug.LogError("SpeedTree9 shader is invalid, cannot create Materials for this SpeedTree asset."); return; } m_OutputImporterData = AssetDatabase.LoadAssetAtPath(assetPath); m_OutputImporterData.lodMaterials.materials.Clear(); if (m_WindEnabled) { m_OutputImporterData.m_WindConfig = CopySpeedTree9WindConfig(m_Tree.Wind, m_MeshSettings.scaleFactor, m_Tree.Bounds); } RegenerateMaterialsFromTree(); foreach (MaterialInfo matInfo in m_OutputImporterData.lodMaterials.materials) { m_OutputImporterData.hasEmbeddedMaterials |= !matInfo.exported; m_OutputImporterData.materialsIdentifiers.Add(new AssetIdentifier(matInfo.material.GetType(), matInfo.material.name)); // Remap the new material to the importer 'ExternalObjectMap'. if (TryGetExternalMaterial(matInfo.defaultName, out var extractedMat)) { string newMatPath = AssetDatabase.GetAssetPath(extractedMat); // Not ideal, but it's safer to regenerate entirely the material (in case the pipeline has changed) // and to avoid any potential issue with the 'ExternalObject' system (material null during next import) if (File.Exists(newMatPath)) { File.Delete(newMatPath); AssetDatabase.CreateAsset(matInfo.material, newMatPath); } if (TryGetSourceAssetIdentifierFromName(matInfo.defaultName, out var assetIdentifier)) { AddRemap(assetIdentifier, matInfo.material); } } } } private bool TryGetExternalMaterial(string name, out Material material) { var externalObjMap = GetExternalObjectMap(); foreach (var obj in externalObjMap) { if (obj.Key.name == name) { material = obj.Value as Material; return material != null; } } material = null; return false; } private bool TryGetSourceAssetIdentifierFromName(string name, out SourceAssetIdentifier assetIdentifier) { var externalObjMap = GetExternalObjectMap(); foreach (var obj in externalObjMap) { if (obj.Key.name == name) { assetIdentifier = obj.Key; return true; } } assetIdentifier = new SourceAssetIdentifier(); return false; } private Material CreateMaterial(STMaterial stMaterial, int lod, string matName, string path) { Material mat = new Material(m_Shader) { name = matName }; RetrieveMaterialSpecialProperties(mat); SetMaterialTextureAndColorProperties(stMaterial, mat, lod, path); SetMaterialOtherProperties(stMaterial, mat); SetWindKeywords(mat, stMaterial.Billboard); return mat; } private bool SetMaterialTexture(Material mat, STMaterial stMaterial, int indexMap, string path, int property) { if (stMaterial.Maps.Length > indexMap) { MaterialMap stMatMap = stMaterial.Maps[indexMap]; string mapPath = stMatMap.Path; if (!stMatMap.Used) return false; if (!string.IsNullOrEmpty(mapPath)) { Texture2D texture = LoadTexture(mapPath, path); if (texture != null) { mat.SetTexture(property, texture); return true; } } } return false; } private Texture2D LoadTexture(string mapPath, string path) { string texturePath = path + mapPath; Texture2D texture = (m_Context != null) ? m_Context.GetReferenceToAssetMainObject(texturePath) as Texture2D : AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)) as Texture2D; if (texture != null) return texture; // Textures are not located near the asset, let's check if they were moved somewhere else. string mapPathWithoutExtension = Path.GetFileNameWithoutExtension(mapPath); string[] textureAssets = AssetDatabase.FindAssets(mapPathWithoutExtension); if (textureAssets != null && textureAssets.Length > 0) { string assetPathFromGUID = AssetDatabase.GUIDToAssetPath(textureAssets[0]); texture = (m_Context != null) ? m_Context.GetReferenceToAssetMainObject(assetPathFromGUID) as Texture2D : AssetDatabase.LoadAssetAtPath(assetPathFromGUID, typeof(Texture2D)) as Texture2D; return texture; } return null; } private bool TryGetInstanceIDFromMaterialProperty(Material material, int propertyName, out int id) { if (!material.HasProperty(propertyName)) { id = 0; return false; } var property = material.GetTexture(propertyName); id = property.GetInstanceID(); return true; } // Not all pipelines support the following properties, so we don't draw them in the inspector if that's the case. private void RetrieveMaterialSpecialProperties(Material mat) { m_OutputImporterData.hasAlphaClipThreshold |= mat.HasProperty(MaterialProperties.AlphaClipThresholdID); m_OutputImporterData.hasTransmissionScale |= mat.HasProperty(MaterialProperties.TransmissionScaleID); } private void SetMaterialTextureAndColorProperties(STMaterial stMaterial, Material mat, int lodIndex, string path) { bool enableHueVariation = m_MaterialSettings.enableHueVariation; bool enableBumpMapping = m_MaterialSettings.enableBumpMapping; bool enableSubsurfaceScattering = m_MaterialSettings.enableSubsurfaceScattering; if (m_PerLODSettings[lodIndex].enableSettingOverride) { enableHueVariation = m_PerLODSettings[lodIndex].enableHue; enableBumpMapping = m_PerLODSettings[lodIndex].enableBump; enableSubsurfaceScattering = m_PerLODSettings[lodIndex].enableSubsurface; } // MainTex and Color { bool colorTex = SetMaterialTexture(mat, stMaterial, 0, path, MaterialProperties.MainTexID); if (colorTex && TryGetInstanceIDFromMaterialProperty(mat, MaterialProperties.MainTexID, out int id) && id != 0) { mat.SetColor(MaterialProperties.ColorTintID, m_MaterialSettings.mainColor); } else if (colorTex) { Vec4 stColorVec = stMaterial.Maps[3].Color; Color rpColor = new Color(stColorVec.X, stColorVec.Y, stColorVec.Z, stColorVec.W); mat.SetColor(MaterialProperties.ColorTintID, m_MaterialSettings.mainColor * rpColor); } } // Bump map { bool hasNormalMap = SetMaterialTexture(mat, stMaterial, 1, path, MaterialProperties.NormalMapID); bool enableFeature = hasNormalMap && enableBumpMapping; mat.SetFloat(MaterialProperties.NormalMapKwToggleID, (enableFeature) ? 1.0f : 0.0f); } // Glossiness, metallic, AO { bool foundExtra = SetMaterialTexture(mat, stMaterial, 2, path, MaterialProperties.ExtraTexID); int id = 0; if (foundExtra && TryGetInstanceIDFromMaterialProperty(mat, MaterialProperties.ExtraTexID, out id) && id != 0) { // _Glossiness (== _Smoothness) is multipled in the shader with the texture values if ExtraTex is present. // Set default value 1.0f to override the default value 0.5, otherwise, the original texture values will // be scaled down to half as much. Same goes for _Metallic mat.SetFloat(MaterialProperties.GlossinessID, 1.0f); mat.SetFloat(MaterialProperties.MetallicID, 1.0f); } else if (foundExtra) { Vec4 stColor = stMaterial.Maps[2].Color; mat.SetFloat(MaterialProperties.GlossinessID, stColor.X); mat.SetFloat(MaterialProperties.MetallicID, stColor.Y); } mat.SetFloat(MaterialProperties.ExtraMapKwToggleID, (foundExtra && id != 0) ? 1.0f : 0.0f); } // Extra and SSS if (stMaterial.TwoSided || stMaterial.Billboard) { bool hasSSSTex = SetMaterialTexture(mat, stMaterial, 3, path, MaterialProperties.SubsurfaceTexID); bool setToggle = hasSSSTex && enableSubsurfaceScattering; // TODO: To implement in ST9 Shader. mat.SetFloat(MaterialProperties.SubsurfaceKwToggleID, (setToggle) ? 1.0f : 0.0f); if (hasSSSTex && TryGetInstanceIDFromMaterialProperty(mat, MaterialProperties.SubsurfaceTexID, out int id) && id != 0) { mat.SetColor(MaterialProperties.SubsurfaceColorID, new Color(1.0f, 1.0f, 1.0f, 1.0f)); } else if (hasSSSTex) { Vec4 stColor = stMaterial.Maps[3].Color; mat.SetColor(MaterialProperties.SubsurfaceColorID, new Color(stColor.X, stColor.Y, stColor.Z, stColor.W)); } if (m_OutputImporterData.hasAlphaClipThreshold && mat.HasFloat(MaterialProperties.AlphaClipThresholdID)) { mat.SetFloat(MaterialProperties.AlphaClipThresholdID, m_MaterialSettings.alphaClipThreshold); } if (m_OutputImporterData.hasTransmissionScale && mat.HasFloat(MaterialProperties.TransmissionScaleID)) { mat.SetFloat(MaterialProperties.TransmissionScaleID, m_MaterialSettings.transmissionScale); } } // Hue effect { mat.SetFloat(MaterialProperties.HueVariationKwToggleID, enableHueVariation ? 1.0f : 0.0f); mat.SetColor(MaterialProperties.HueVariationColorID, m_MaterialSettings.hueVariation); } } private void SetMaterialOtherProperties(STMaterial stMaterial, Material mat) { bool isBillboard = stMaterial.Billboard; // Other properties mat.SetFloat(MaterialProperties.BillboardKwToggleID, isBillboard ? 1.0f : 0.0f); if (isBillboard) { mat.EnableKeyword(MaterialKeywords.BillboardID); } mat.SetFloat(MaterialProperties.LeafFacingKwToggleID, m_HasFacingData ? 1.0f : 0.0f); if (mat.HasFloat(MaterialProperties.DoubleSidedToggleID)) mat.SetFloat(MaterialProperties.DoubleSidedToggleID, stMaterial.TwoSided ? 1.0f : 0.0f); if (mat.HasFloat(MaterialProperties.DoubleSidedNormalModeID)) mat.SetFloat(MaterialProperties.DoubleSidedNormalModeID, stMaterial.FlipNormalsOnBackside ? 0.0f : 2.0f); if (mat.HasVector(MaterialProperties.DiffusionProfileAssetID)) mat.SetVector(MaterialProperties.DiffusionProfileAssetID, m_MaterialSettings.diffusionProfileAssetID); if (mat.HasFloat(MaterialProperties.DiffusionProfileID)) mat.SetFloat(MaterialProperties.DiffusionProfileID, m_MaterialSettings.diffusionProfileID); if (mat.HasFloat(MaterialProperties.BackfaceNormalModeID)) mat.SetFloat(MaterialProperties.BackfaceNormalModeID, stMaterial.FlipNormalsOnBackside ? 0.0f : 2.0f); if (mat.HasFloat(MaterialProperties.TwoSidedID)) mat.SetFloat(MaterialProperties.TwoSidedID, stMaterial.TwoSided ? 0.0f : 2.0f); // matches cull mode. 0: no cull mat.enableInstancing = true; mat.doubleSidedGI = stMaterial.TwoSided; } private void SetWindKeywords(Material material, bool isBillboardMat) { if (material == null) return; //------------------------------------------------------------------------ // Note: // mat.SetFloat(...) : Legacy rendering pipeline keyword toggle // mat.EnableKeyword(...) : SRP keyword toggle //------------------------------------------------------------------------ SpeedTreeWindConfig9 windCfg = m_OutputImporterData.m_WindConfig; if (windCfg.doShared != 0) { material.SetFloat(MaterialProperties.WindSharedKwToggle, 1.0f); } if (!isBillboardMat) { if (windCfg.doBranch2 != 0) { material.SetFloat(MaterialProperties.WindBranch2KwToggle, 1.0f); } if (windCfg.doBranch1 != 0) { material.SetFloat(MaterialProperties.WindBranch1KwToggle, 1.0f); } if (windCfg.doRipple != 0) { material.SetFloat(MaterialProperties.WindRippleKwToggle, 1.0f); if (windCfg.doShimmer != 0) { material.SetFloat(MaterialProperties.WindShimmerKwToggle, 1.0f); } } } } internal bool SearchAndRemapMaterials(string materialFolderPath) { bool changedMappings = false; if (materialFolderPath == null) throw new ArgumentNullException("materialFolderPath"); if (string.IsNullOrEmpty(materialFolderPath)) throw new ArgumentException(string.Format("Invalid material folder path: {0}.", materialFolderPath), "materialFolderPath"); string[] guids = AssetDatabase.FindAssets("t:Material", new string[] { materialFolderPath }); List> materials = new List>(); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); // ensure that we only load material assets, not embedded materials Material material = AssetDatabase.LoadMainAssetAtPath(path) as Material; if (material) materials.Add(new Tuple(path, material)); } m_OutputImporterData = AssetDatabase.LoadAssetAtPath(assetPath); AssetIdentifier[] importedMaterials = m_OutputImporterData.materialsIdentifiers.ToArray(); foreach (Tuple material in materials) { string materialName = material.Item2.name; string materialFile = material.Item1; // the legacy materials have the LOD in the path, while the new materials have the LOD as part of the name bool isLegacyMaterial = !materialName.Contains("LOD") && !materialName.Contains("Billboard"); bool hasLOD = isLegacyMaterial && materialFile.Contains("LOD"); string lod = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(materialFile)); AssetIdentifier importedMaterial = Array.Find(importedMaterials, x => x.name.Contains(materialName) && (!hasLOD || x.name.Contains(lod))); if (!string.IsNullOrEmpty(importedMaterial.name)) { SourceAssetIdentifier importedIdentifier = new SourceAssetIdentifier(material.Item2); AddRemap(importedIdentifier, material.Item2); changedMappings = true; } } return changedMappings; } internal string GetMaterialFolderPath() { return FileUtil.DeleteLastPathNameComponent(assetPath) + "/"; } internal void SetMaterialsVersionToCurrent() { m_MaterialVersion = SPEEDTREE_9_MATERIAL_VERSION; MarkDirty(); } private void AddDependencyOnExtractedMaterials() { Dictionary extMap = GetExternalObjectMap(); foreach (var entry in extMap) { if (entry.Value != null) { string matPath = AssetDatabase.GetAssetPath(entry.Value); m_Context.DependsOnImportedAsset(matPath); // Necessary to avoid the warning "Import of asset setup artifact dependency to but dependency isn't used // and therefore not registered in the asset database". AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)); } } } #endregion #region Wind private unsafe SpeedTreeWindConfig9 CopySpeedTree9WindConfig(WindConfigSDK wind, float scaleFactor, in Bounds3 treeBounds) { const bool CHECK_ZERO = true; const bool DONT_CHECK_ZERO = false; void CopyCurve(in float[] src, float* dst) { const int NUM_CURVE_ELEMENTS = 20; Debug.Assert(src.Length == NUM_CURVE_ELEMENTS); for (global::System.Int32 i = 0; i < NUM_CURVE_ELEMENTS; i++) { dst[i] = src[i]; } } void CopyCurveScale(in float[] src, float* dst, float scaleFactor) { const int NUM_CURVE_ELEMENTS = 20; Debug.Assert(src.Length == NUM_CURVE_ELEMENTS); for (global::System.Int32 i = 0; i < NUM_CURVE_ELEMENTS; i++) { dst[i] = src[i] * scaleFactor; } } bool ValidCurve(float[] curve, bool bCheckZero = CHECK_ZERO) { bool bNonZero = false; for (int i = 0; i < curve.Length; ++i) { bNonZero |= curve[i] != 0.0f; if (float.IsNaN(curve[i])) { return false; } } if (bCheckZero) { return bNonZero; } return true; } bool BranchHasAllCurvesValid(in WindBranch b) { return ValidCurve(b.Bend) && ValidCurve(b.Oscillation) && ValidCurve(b.Speed, CHECK_ZERO) && ValidCurve(b.Turbulence) && ValidCurve(b.Flexibility, DONT_CHECK_ZERO ); } bool RippleHasAllCurvesValid(in WindRipple r) { return ValidCurve(r.Planar) && ValidCurve(r.Directional) && ValidCurve(r.Speed) && ValidCurve(r.Flexibility, DONT_CHECK_ZERO ); } SpeedTreeWindConfig9 cfg = new SpeedTreeWindConfig9(); // common WindConfigCommon common = wind.Common; cfg.strengthResponse = common.StrengthResponse; cfg.directionResponse = common.DirectionResponse; cfg.gustFrequency = common.GustFrequency; cfg.gustStrengthMin = common.GustStrengthMin; cfg.gustStrengthMax = common.GustStrengthMax; cfg.gustDurationMin = common.GustDurationMin; cfg.gustDurationMax = common.GustDurationMax; cfg.gustRiseScalar = common.GustRiseScalar; cfg.gustFallScalar = common.GustFallScalar; // st9 cfg.branch1StretchLimit = wind.Branch1StretchLimit * scaleFactor; cfg.branch2StretchLimit = wind.Branch2StretchLimit * scaleFactor; cfg.importScale = scaleFactor; cfg.treeExtentX = (treeBounds.Max.X - treeBounds.Min.X) * scaleFactor; cfg.treeExtentY = (treeBounds.Max.Y - treeBounds.Min.Y) * scaleFactor; cfg.treeExtentZ = (treeBounds.Max.Z - treeBounds.Min.Z) * scaleFactor; if (wind.DoShared) { WindConfigSDK.WindBranch shared = wind.Shared; CopyCurveScale(shared.Bend, cfg.bendShared, scaleFactor); CopyCurveScale(shared.Oscillation, cfg.oscillationShared, scaleFactor); CopyCurve(shared.Speed, cfg.speedShared); CopyCurve(shared.Turbulence, cfg.turbulenceShared); CopyCurve(shared.Flexibility, cfg.flexibilityShared); cfg.independenceShared = shared.Independence; cfg.sharedHeightStart = wind.SharedStartHeight; // this is a % value if (BranchHasAllCurvesValid(in shared)) { cfg.doShared = 1; } } if (wind.DoBranch1) { WindConfigSDK.WindBranch branch1 = wind.Branch1; CopyCurveScale(branch1.Bend, cfg.bendBranch1, scaleFactor); CopyCurveScale(branch1.Oscillation, cfg.oscillationBranch1, scaleFactor); CopyCurve(branch1.Speed, cfg.speedBranch1); CopyCurve(branch1.Turbulence, cfg.turbulenceBranch1); CopyCurve(branch1.Flexibility, cfg.flexibilityBranch1); cfg.independenceBranch1 = branch1.Independence; if (BranchHasAllCurvesValid(in branch1)) { cfg.doBranch1 = 1; } } if (wind.DoBranch2) { WindConfigSDK.WindBranch branch2 = wind.Branch2; CopyCurveScale(branch2.Bend, cfg.bendBranch2, scaleFactor); CopyCurveScale(branch2.Oscillation, cfg.oscillationBranch2, scaleFactor); CopyCurve(branch2.Speed, cfg.speedBranch2); CopyCurve(branch2.Turbulence, cfg.turbulenceBranch2); CopyCurve(branch2.Flexibility, cfg.flexibilityBranch2); cfg.independenceBranch2 = branch2.Independence; if (BranchHasAllCurvesValid(in branch2)) { cfg.doBranch2 = 1; } } if (wind.DoRipple) { WindConfigSDK.WindRipple ripple = wind.Ripple; CopyCurveScale(ripple.Planar, cfg.planarRipple, scaleFactor); CopyCurveScale(ripple.Directional, cfg.directionalRipple, scaleFactor); CopyCurve(ripple.Speed, cfg.speedRipple); CopyCurve(ripple.Flexibility, cfg.flexibilityRipple); cfg.independenceRipple = ripple.Independence; if (RippleHasAllCurvesValid(in ripple)) { cfg.doRipple = 1; if (wind.DoShimmer) { cfg.doShimmer = 1; cfg.shimmerRipple = ripple.Shimmer; } } } return cfg; } private void SetWindParameters(ref SpeedTreeWindConfig9 cfg) { cfg.strengthResponse = m_WindSettings.strenghResponse; cfg.directionResponse = m_WindSettings.directionResponse; cfg.windIndependence = m_WindSettings.randomness; } #endregion #region Others private void CalculateScaleFactorFromUnit() { switch (m_MeshSettings.unitConversion) { // Use units in the imported file without any conversion. case STUnitConversion.kLeaveAsIs: m_MeshSettings.scaleFactor = 1.0f; break; case STUnitConversion.kFeetToMeters: m_MeshSettings.scaleFactor = SpeedTreeConstants.kFeetToMetersRatio; break; case STUnitConversion.kCentimetersToMeters: m_MeshSettings.scaleFactor = SpeedTreeConstants.kCentimetersToMetersRatio; break; case STUnitConversion.kInchesToMeters: m_MeshSettings.scaleFactor = SpeedTreeConstants.kInchesToMetersRatio; break; case STUnitConversion.kCustomConversion: /* no-op */ break; } } private bool TreeHasFacingData() { for (int lodIndex = 0; lodIndex < m_LODCount; ++lodIndex) { Lod lod = m_Tree.Lod[lodIndex]; for (int drawIndex = 0; drawIndex < lod.DrawCalls.Length; ++drawIndex) { DrawCall draw = lod.DrawCalls[drawIndex]; if(draw.ContainsFacingGeometry) return true; } } return false; } internal static bool TryGetShaderForCurrentRenderPipeline(out Shader shader) { shader = GraphicsSettings.GetDefaultShader(DefaultShaderType.SpeedTree9); if (shader == null) { shader = Shader.Find(ImporterSettings.kLegacyShaderName); } return shader != null; } private void CreateAndAddRigidBodyToAsset(GameObject mainObject) { Rigidbody rb = mainObject.AddComponent(); if (rb != null) { rb.useGravity = false; rb.isKinematic = true; } } private void CreateAndAddCollidersToAsset() { for (int iCollider = 0; iCollider < m_CollisionObjectsCount; ++iCollider) { CollisionObject stCollider = m_Tree.CollisionObjects[iCollider]; GameObject collisionObject = new GameObject("Collider" + (iCollider + 1)); collisionObject.transform.parent = m_OutputImporterData.mainObject.transform; Vector3 vOne = new Vector3(stCollider.Position.X, stCollider.Position.Y, stCollider.Position.Z); Vector3 vTwo = new Vector3(stCollider.Position2.X, stCollider.Position2.Y, stCollider.Position2.Z); vOne *= m_MeshSettings.scaleFactor; vTwo *= m_MeshSettings.scaleFactor; collisionObject.transform.position = (vOne + vTwo) * 0.5f; if ((vOne - vTwo).sqrMagnitude < 0.001f) { SphereCollider collider = collisionObject.AddComponent(); collider.radius = stCollider.Radius * m_MeshSettings.scaleFactor; } else { CapsuleCollider collider = collisionObject.AddComponent(); collider.direction = 2; collider.radius = stCollider.Radius * m_MeshSettings.scaleFactor; collider.height = (vOne - vTwo).magnitude; collisionObject.transform.LookAt(vTwo); } m_Context.AddObjectToAsset(collisionObject.name, collisionObject); } } internal float[] GetPerLODSettingsHeights() { float[] heightsArray = new float[m_PerLODSettings.Count]; for (int i = 0; i < m_PerLODSettings.Count; ++i) { heightsArray[i] = m_PerLODSettings[i].height; } return heightsArray; } #endregion } // Use a postprocessor to set various settings on the textures since these dont stick during first import. class SpeedTree9Postprocessor : AssetPostprocessor { private static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) { foreach (string assetFilename in importedAssets) { if (Path.GetExtension(assetFilename) == ".st9") { try { AssetDatabase.StartAssetEditing(); ChangeTextureImporterSettingsForSt9Files(assetFilename); } finally { AssetDatabase.StopAssetEditing(); } } } if (didDomainReload) { if (TryGetHashSpeedTreeAttributeMaterialSettings(out List strToHash)) { Hash128 hash = new Hash128(); foreach (string str in strToHash) { hash.Append(str); } AssetDatabase.RegisterCustomDependency(ImporterSettings.kMaterialSettingsDependencyname, hash); } else { AssetDatabase.UnregisterCustomDependencyPrefixFilter(ImporterSettings.kMaterialSettingsDependencyname); } } } private static bool TryGetHashSpeedTreeAttributeMaterialSettings(out List strToHash) { var allMethods = AttributeHelper.GetMethodsWithAttribute().methodsWithAttributes; strToHash = new List(); foreach (var method in allMethods) { MaterialSettingsCallbackAttribute attribute = method.attribute as MaterialSettingsCallbackAttribute; strToHash.Add($"{method.info.Name}-{method.info.DeclaringType.AssemblyQualifiedName}-{attribute.MethodVersion.ToString()}"); } strToHash.Sort(); return strToHash.Count > 0; } private static void ChangeTextureImporterSettingsForSt9Files(string assetPath) { SpeedTree9Reader tree = new SpeedTree9Reader(); FileStatus status = tree.Initialize(assetPath); if (status != FileStatus.Valid) { Debug.LogError($"Error while initializing the SpeedTree9 reader: {status}."); return; } tree.ReadContent(); string path = Path.GetDirectoryName(assetPath) + "/"; for (int matIndex = 0; matIndex < tree.Materials.Length; ++matIndex) { STMaterial stMaterial = tree.Materials[matIndex]; if (TryGetTextureImporterFromIndex(stMaterial, 0, path, out TextureImporter texImporterColor)) ApplyColorTextureSettings(texImporterColor); if (TryGetTextureImporterFromIndex(stMaterial, 1, path, out TextureImporter texImporterNormal)) ApplyNormalTextureSettings(texImporterNormal); if (TryGetTextureImporterFromIndex(stMaterial, 2, path, out TextureImporter texImporterExtra)) ApplyExtraTextureSettings(texImporterExtra); } } private static bool TryGetTextureImporterFromIndex( STMaterial stMaterial, int index, string directoryPath, out TextureImporter textureImporter) { textureImporter = null; if (stMaterial.Maps.Length <= index) return false; MaterialMap mat = stMaterial.Maps[index]; if (!mat.Used || string.IsNullOrEmpty(mat.Path)) return false; TextureImporter texImporter = TextureImporter.GetAtPath(directoryPath + mat.Path) as TextureImporter; if (texImporter == null) return false; textureImporter = texImporter; return true; } private static void ApplyColorTextureSettings(TextureImporter texImporter) { if (texImporter.alphaIsTransparency && texImporter.mipMapsPreserveCoverage && texImporter.alphaTestReferenceValue == 0.1f) return; texImporter.alphaIsTransparency = true; texImporter.mipMapsPreserveCoverage = true; texImporter.alphaTestReferenceValue = 0.1f; EditorUtility.SetDirty(texImporter); texImporter.SaveAndReimport(); } private static void ApplyNormalTextureSettings(TextureImporter texImporter) { if (texImporter.textureType == TextureImporterType.NormalMap) return; texImporter.textureType = TextureImporterType.NormalMap; EditorUtility.SetDirty(texImporter); texImporter.SaveAndReimport(); } private static void ApplyExtraTextureSettings(TextureImporter texImporter) { if (texImporter.sRGBTexture == false) return; texImporter.sRGBTexture = false; EditorUtility.SetDirty(texImporter); texImporter.SaveAndReimport(); } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor.AssetImporters; using System.Collections.Generic; using UnityEngine; using System; namespace UnityEditor.SpeedTree.Importer { [CustomEditor(typeof(SpeedTree9Importer))] [CanEditMultipleObjects] internal class SpeedTree9ImporterEditor : AssetImporterTabbedEditor { private static class Styles { public static GUIContent ApplyAndGenerate = EditorGUIUtility.TrTextContent("Apply & Generate Materials", "Apply current importer settings and generate materials with new settings."); public static GUIContent Regenerate = EditorGUIUtility.TrTextContent("Regenerate Materials", "Regenerate materials from the current importer settings."); public static GUIContent RegenerateRemapped = EditorGUIUtility.TrTextContent("Regenerate Materials", "Regenerate the remapped materials from the current import settings."); public static GUIContent ApplyAndGenerateRemapped = EditorGUIUtility.TrTextContent("Apply & Generate Materials", "Apply current importer settings and regenerate the remapped materials with new settings."); public static readonly string ModelTabName = "Model"; public static readonly string MaterialsTabName = "Materials"; public static readonly string WindTabName = "Wind"; } private bool m_HasRemappedMaterials = false; private SpeedTreeImporterOutputData m_OutputImporterData = null; private SpeedTree9Importer m_STImporter = null; internal IEnumerable importers { get { SpeedTree9Importer[] st9Importers = new SpeedTree9Importer[targets.Length]; for (int i = 0; i < targets.Length; ++i) { st9Importers[i] = targets[i] as SpeedTree9Importer; } return st9Importers; } } public override void OnEnable() { m_STImporter = target as SpeedTree9Importer; if (tabs == null) { tabs = new BaseAssetImporterTabUI[] { new SpeedTree9ImporterModelEditor(this), new SpeedTree9ImporterMaterialEditor(this), new SpeedTree9ImporterWindEditor(this) }; m_TabNames = new string[] { Styles.ModelTabName, Styles.MaterialsTabName, Styles.WindTabName }; } m_OutputImporterData = AssetDatabase.LoadAssetAtPath(m_STImporter.assetPath); Debug.Assert(m_OutputImporterData != null); base.OnEnable(); } public override void OnDisable() { foreach (var tab in tabs) { tab.OnDisable(); } base.OnDisable(); } // None of the ModelImporter sub editors support multi preview. public override bool HasPreviewGUI() { return base.HasPreviewGUI() && targets.Length < 2; } // Only show the imported GameObject when the Model tab is active. public override bool showImportedObject { get { return activeTab is SpeedTree9ImporterModelEditor; } } public override GUIContent GetPreviewTitle() { var tab = activeTab as ModelImporterClipEditor; if (tab != null) return new GUIContent(tab.selectedClipName); return base.GetPreviewTitle(); } internal bool upgradeMaterials { get { foreach (var importer in importers) { if (importer != null && importer.MaterialsShouldBeRegenerated) return true; } return false; } } protected override bool OnApplyRevertGUI() { bool applied = base.OnApplyRevertGUI(); bool hasModified = HasModified(); if (tabs == null) // Hitting apply, we lose the tabs object within base.OnApplyRevertGUI() { if (hasModified) Apply(); return applied; } bool doMatsHaveDifferentShaders = (tabs[0] as SpeedTree9ImporterModelEditor).DoMaterialsHaveDifferentShader(); // We show the "Generate" button when we have extracted materials since the user should have 2 choices: // - Apply the importer settings changes on top of the extracted materials (by regenerating them) // - Only apply the importer settings changes to the embedded materials and not the extracted ones, // since the users might want to be able to keep their own changes without the importer to erase them. m_HasRemappedMaterials = HasRemappedMaterials(); if (upgradeMaterials || doMatsHaveDifferentShaders || m_HasRemappedMaterials) { // Force material upgrade when a custom render pipeline is active so that render pipeline-specific material // modifications may be applied. bool upgrade = upgradeMaterials || (UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline != null); if (GUILayout.Button(GetGenerateButtonText(hasModified, upgrade))) { // Apply the changes and generate the materials before importing so that asset previews are up-to-date. if (hasModified) Apply(); if (upgrade) { foreach (var importer in importers) { importer.SetMaterialsVersionToCurrent(); } } GenerateMaterials(); if (hasModified || upgrade) { // Necessary since we remap the newly generated materials to the mesh LOD(s). SaveChanges(); applied = true; } } } return applied; } internal GUIContent GetGenerateButtonText(bool modified, bool upgrade) { if (modified || upgrade) { if (m_HasRemappedMaterials) return Styles.ApplyAndGenerate; else return Styles.ApplyAndGenerateRemapped; } else { if (m_HasRemappedMaterials) return Styles.Regenerate; else return Styles.RegenerateRemapped; } } private bool HasEmbeddedMaterials { get { bool materialsValid = m_OutputImporterData.lodMaterials.materials.Count > 0 && m_OutputImporterData.lodMaterials.materials.TrueForAll(p => p.material != null); return m_OutputImporterData.hasEmbeddedMaterials && materialsValid; } } private void GenerateMaterials() { List paths = new List(); List matFolders = new List(); List importersWithEmbeddedMaterials = new List(); // TODO: Add support for multi-edit. if (HasEmbeddedMaterials) { importersWithEmbeddedMaterials.Add(m_STImporter); } foreach (var importer in importersWithEmbeddedMaterials) { var remappedAssets = importer.GetExternalObjectMap(); List materials = new List(); foreach (var asset in remappedAssets) { if (asset.Value is Material && asset.Value != null) { materials.Add(asset.Value); } } foreach (var material in materials) { string path = AssetDatabase.GetAssetPath(material); paths.Add(path); matFolders.Add(FileUtil.DeleteLastPathNameComponent(path)); } } bool doGenerate = true; if (paths.Count > 0) { doGenerate = AssetDatabase.MakeEditable(paths.ToArray(), $"Materials will be checked out in:\n{string.Join("\n", matFolders.ToArray())}"); } if (doGenerate) { foreach (var importer in importers) { importer.RegenerateMaterials(); } } } private bool HasRemappedMaterials() { m_HasRemappedMaterials = true; if (m_OutputImporterData.materialsIdentifiers.Count == 0) return true; // if the m_ExternalObjecs map has any unapplied changes, keep the state of the button as is if (serializedObject.hasModifiedProperties) return m_HasRemappedMaterials; m_HasRemappedMaterials = true; foreach (var importer in importers) { var externalObjectMap = importer.GetExternalObjectMap(); var materialArray = m_OutputImporterData.materialsIdentifiers.ToArray();// importer.SourceMaterials.ToArray(); int remappedMaterialCount = 0; foreach (var entry in externalObjectMap) { if (entry.Key.type == typeof(Material) && Array.Exists(materialArray, x => x.name == entry.Key.name && entry.Value != null)) ++remappedMaterialCount; } m_HasRemappedMaterials = m_HasRemappedMaterials && remappedMaterialCount != 0; if (!m_HasRemappedMaterials) break; } return m_HasRemappedMaterials; } } internal abstract class BaseSpeedTree9ImporterTabUI : BaseAssetImporterTabUI { protected SpeedTreeImporterOutputData m_OutputImporterData; internal BaseSpeedTree9ImporterTabUI(AssetImporterEditor panelContainer) : base(panelContainer) { } internal override void OnEnable() { TryLoadOutputImporterData(); } protected IEnumerable importers { get { return (panelContainer as SpeedTree9ImporterEditor).importers; } } protected bool upgradeMaterials { get { return (panelContainer as SpeedTree9ImporterEditor).upgradeMaterials; } } protected bool TryLoadOutputImporterData() { m_OutputImporterData = null; // Doesn't support multi-edit for now. foreach (SpeedTree9Importer importer in importers) { m_OutputImporterData = AssetDatabase.LoadAssetAtPath(importer.assetPath); break; } return m_OutputImporterData != null; } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterMaterialEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEditor.AssetImporters; using UnityEngine; namespace UnityEditor.SpeedTree.Importer { class SpeedTree9ImporterMaterialEditor : BaseSpeedTree9ImporterTabUI { private static class Styles { public static GUIContent RemapOptions = EditorGUIUtility.TrTextContent("On Demand Remap"); public static GUIContent RemapMaterialsInProject = EditorGUIUtility.TrTextContent("Search and Remap...", "Click on this button to search and remap the materials from the project."); public static GUIContent ExternalMaterialMappings = EditorGUIUtility.TrTextContent("Remapped Materials", "External materials to use for each embedded material."); public static GUIContent Materials = EditorGUIUtility.TrTextContent("Materials"); public static GUIContent ExtractEmbeddedMaterials = EditorGUIUtility.TrTextContent("Extract Materials...", "Click on this button to extract the embedded materials."); public static GUIContent InternalMaterialHelp = EditorGUIUtility.TrTextContent("Materials are embedded inside the imported asset."); public static GUIContent MaterialAssignmentsHelp = EditorGUIUtility.TrTextContent("Material assignments can be remapped below."); public static GUIContent ExternalMaterialSearchHelp = EditorGUIUtility.TrTextContent("Searches the user provided directory and matches the materials that share the same name and LOD with the originally imported material."); public static GUIContent SelectMaterialFolder = EditorGUIUtility.TrTextContent("Select Materials Folder"); } private SpeedTree9Importer m_STImporter; private SerializedProperty m_ExternalObjects; private bool m_ShowMaterialRemapOptions; private bool m_HasEmbeddedMaterials; private bool ImporterHasEmbeddedMaterials { get { bool materialsValid = m_OutputImporterData.lodMaterials.materials.Count > 0 && m_OutputImporterData.lodMaterials.materials.TrueForAll(p => p.material != null); return m_OutputImporterData.hasEmbeddedMaterials && materialsValid; } } public SpeedTree9ImporterMaterialEditor(AssetImporterEditor panelContainer) : base(panelContainer) { } internal override void OnEnable() { base.OnEnable(); m_STImporter = target as SpeedTree9Importer; m_ExternalObjects = serializedObject.FindProperty("m_ExternalObjects"); } public override void OnInspectorGUI() { serializedObject.Update(); ShowMaterialGUI(); serializedObject.ApplyModifiedProperties(); } private bool HasEmbeddedMaterials() { if (m_OutputImporterData.materialsIdentifiers.Count == 0 || !ImporterHasEmbeddedMaterials) return false; // if the m_ExternalObjecs map has any unapplied changes, keep the state of the button as is if (m_ExternalObjects.serializedObject.hasModifiedProperties) return m_HasEmbeddedMaterials; m_HasEmbeddedMaterials = true; foreach (var t in m_ExternalObjects.serializedObject.targetObjects) { var externalObjectMap = m_STImporter.GetExternalObjectMap(); var materialsList = m_OutputImporterData.materialsIdentifiers.ToArray();// m_STImporter.m_Materials.ToArray(); int remappedMaterialCount = 0; foreach (var entry in externalObjectMap) { bool isMatValid = Array.Exists(materialsList, x => x.name == entry.Key.name && entry.Value != null); if (entry.Key.type == typeof(Material) && isMatValid) ++remappedMaterialCount; } m_HasEmbeddedMaterials = m_HasEmbeddedMaterials && remappedMaterialCount != materialsList.Length; } return m_HasEmbeddedMaterials; } private void ShowMaterialGUI() { serializedObject.UpdateIfRequiredOrScript(); string materialHelp = string.Empty; int arraySize = m_OutputImporterData.materialsIdentifiers.Count; if (arraySize > 0 && HasEmbeddedMaterials()) { // we're generating materials inside the prefab materialHelp = Styles.InternalMaterialHelp.text; } if (targets.Length == 1 && arraySize > 0) { materialHelp += " " + Styles.MaterialAssignmentsHelp.text; } if (ExtractMaterialsGUI()) { // Necessary to avoid the error "BeginLayoutGroup must be called first". GUIUtility.ExitGUI(); return; } if (!string.IsNullOrEmpty(materialHelp)) { EditorGUILayout.HelpBox(materialHelp, MessageType.Info); } // The material remap list if (targets.Length == 1 && arraySize > 0) { GUILayout.Label(Styles.ExternalMaterialMappings, EditorStyles.boldLabel); if (MaterialRemapOptions()) return; // The list of material names is immutable, whereas the map of external objects can change based on user actions. // For each material name, map the external object associated with it. // The complexity comes from the fact that we may not have an external object in the map, so we can't make a property out of it for (int materialIdx = 0; materialIdx < arraySize; ++materialIdx) { string name = m_OutputImporterData.materialsIdentifiers[materialIdx].name; string type = m_OutputImporterData.materialsIdentifiers[materialIdx].type; SerializedProperty materialProp = null; Material material = null; int propertyIdx = 0; for (int externalObjectIdx = 0, count = m_ExternalObjects.arraySize; externalObjectIdx < count; ++externalObjectIdx) { SerializedProperty pair = m_ExternalObjects.GetArrayElementAtIndex(externalObjectIdx); string externalName = pair.FindPropertyRelative("first.name").stringValue; string externalType = pair.FindPropertyRelative("first.type").stringValue; // Cannot do a strict comparison, since externalType is set to "UnityEngine:Material" (C++) // and type "UnityEngine.Material" (C#). bool typeMatching = externalType.Contains("Material") && type.Contains("Material"); if (externalName == name && typeMatching) { materialProp = pair.FindPropertyRelative("second"); material = materialProp != null ? materialProp.objectReferenceValue as Material : null; // If 'material' is null, it's likely because it was deleted. So we assign null to 'materialProp' // to avoid the 'missing material' reference error in the UI. materialProp = (material != null) ? pair.FindPropertyRelative("second") : null; propertyIdx = externalObjectIdx; break; } } GUIContent nameLabel = EditorGUIUtility.TextContent(name); nameLabel.tooltip = name; if (materialProp != null) { EditorGUI.BeginChangeCheck(); EditorGUILayout.ObjectField(materialProp, typeof(Material), nameLabel); if (EditorGUI.EndChangeCheck()) { if (materialProp.objectReferenceValue == null) { m_ExternalObjects.DeleteArrayElementAtIndex(propertyIdx); } } } else { EditorGUI.BeginChangeCheck(); material = EditorGUILayout.ObjectField(nameLabel, material, typeof(Material), false) as Material; if (EditorGUI.EndChangeCheck()) { if (material != null) { int newIndex = m_ExternalObjects.arraySize++; SerializedProperty pair = m_ExternalObjects.GetArrayElementAtIndex(newIndex); pair.FindPropertyRelative("first.name").stringValue = name; pair.FindPropertyRelative("first.type").stringValue = type; pair.FindPropertyRelative("second").objectReferenceValue = material; } } } } } } private bool ExtractMaterialsGUI() { bool buttonPressed = false; EditorGUILayout.BeginHorizontal(); { EditorGUILayout.PrefixLabel(Styles.Materials); using (new EditorGUI.DisabledScope(!HasEmbeddedMaterials())) { buttonPressed = GUILayout.Button(Styles.ExtractEmbeddedMaterials); if (buttonPressed) { // use the first target for selecting the destination folder, but apply that path for all targets string destinationPath = m_STImporter.assetPath; destinationPath = EditorUtility.SaveFolderPanel(Styles.SelectMaterialFolder.text, FileUtil.DeleteLastPathNameComponent(destinationPath), ""); if (string.IsNullOrEmpty(destinationPath)) { // Cancel the extraction if the user did not select a folder. EditorGUILayout.EndHorizontal(); return buttonPressed; } destinationPath = FileUtil.GetProjectRelativePath(destinationPath); try { // batch the extraction of the materials AssetDatabase.StartAssetEditing(); PrefabUtility.ExtractMaterialsFromAsset(targets, destinationPath); } finally { AssetDatabase.StopAssetEditing(); } } } } EditorGUILayout.EndHorizontal(); // AssetDatabase.StopAssetEditing() invokes OnEnable(), which invalidates all the serialized properties, so we must return. return buttonPressed; } private bool MaterialRemapOptions() { bool buttonPressed = false; m_ShowMaterialRemapOptions = EditorGUILayout.Foldout(m_ShowMaterialRemapOptions, Styles.RemapOptions); if (m_ShowMaterialRemapOptions) { EditorGUI.indentLevel++; EditorGUILayout.HelpBox(Styles.ExternalMaterialSearchHelp.text, MessageType.Info); EditorGUI.indentLevel--; using (new EditorGUILayout.HorizontalScope()) { GUILayout.FlexibleSpace(); using (new EditorGUI.DisabledScope(assetTarget == null)) { buttonPressed = GUILayout.Button(Styles.RemapMaterialsInProject); if (buttonPressed) { bool bStartedAssetEditing = false; try { foreach (var t in targets) { string folderToSearch = m_STImporter.GetMaterialFolderPath(); folderToSearch = EditorUtility.OpenFolderPanel(Styles.SelectMaterialFolder.text, folderToSearch, ""); bool bUserSelectedAFolder = folderToSearch != ""; // folderToSearch is empty if the user cancels the window if (bUserSelectedAFolder) { string projectRelativePath = FileUtil.GetProjectRelativePath(folderToSearch); bool bRelativePathIsNotEmpty = projectRelativePath != ""; if (bRelativePathIsNotEmpty) { AssetDatabase.StartAssetEditing(); bStartedAssetEditing = true; m_STImporter.SearchAndRemapMaterials(projectRelativePath); AssetDatabase.WriteImportSettingsIfDirty(m_STImporter.assetPath); AssetDatabase.ImportAsset(m_STImporter.assetPath, ImportAssetOptions.ForceUpdate); } else { Debug.LogWarning("Selected folder is outside of the project's folder hierarchy, please provide a folder from the project.\n"); } } } } finally { if (bStartedAssetEditing) { AssetDatabase.StopAssetEditing(); } } } } } EditorGUILayout.Space(); } return buttonPressed; } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor.AssetImporters; using System; using UnityEditor.AnimatedValues; using UnityEngine; using System.IO; using System.Collections.Generic; using static UnityEditor.SpeedTree.Importer.SpeedTree9Importer; using Styles = UnityEditor.SpeedTree.Importer.SpeedTreeImporterCommonEditor.Styles; namespace UnityEditor.SpeedTree.Importer { class SpeedTree9ImporterModelEditor : BaseSpeedTree9ImporterTabUI { // Mesh properties private SerializedProperty m_UnitConversion; private SerializedProperty m_ScaleFactor; // Material properties private SerializedProperty m_MainColor; private SerializedProperty m_EnableHueVariation; private SerializedProperty m_HueVariation; private SerializedProperty m_AlphaClipThreshold; private SerializedProperty m_TransmissionScale; private SerializedProperty m_EnableBumpMapping; private SerializedProperty m_EnableSubsurfaceScattering; private SerializedProperty m_DiffusionProfileAssetID; private SerializedProperty m_DiffusionProfileID; // Lighting properties private SerializedProperty m_EnableShadowCasting; private SerializedProperty m_EnableShadowReceiving; private SerializedProperty m_EnableLightProbes; private SerializedProperty m_ReflectionProbeEnumValue; // Additional Settings properties private SerializedProperty m_MotionVectorModeEnumValue; private SerializedProperty m_GenerateColliders; private SerializedProperty m_GenerateRigidbody; // LOD properties private SerializedProperty m_EnableSmoothLOD; private SerializedProperty m_AnimateCrossFading; private SerializedProperty m_BillboardTransitionCrossFadeWidth; private SerializedProperty m_FadeOutWidth; private SerializedProperty m_PerLODSettings; // LODGroup GUI private int m_SelectedLODSlider = -1; private int m_SelectedLODRange = 0; private SavedBool[] m_LODGroupFoldoutHeaderValues = null; private Texture2D[] m_LODColorTextures; private AnimBool m_ShowSmoothLODOptions = new AnimBool(); private AnimBool m_ShowCrossFadeWidthOptions = new AnimBool(); private SpeedTree9Importer m_StEditor; public SpeedTree9ImporterModelEditor(AssetImporterEditor panelContainer) : base(panelContainer) { } internal override void OnEnable() { base.OnEnable(); m_StEditor = target as SpeedTree9Importer; // Mesh properties { MeshSettings meshSettings = m_StEditor.m_MeshSettings; string meshSettingsStr = nameof(m_StEditor.m_MeshSettings); m_UnitConversion = FindPropertyFromName(meshSettingsStr, nameof(meshSettings.unitConversion)); m_ScaleFactor = FindPropertyFromName(meshSettingsStr, nameof(meshSettings.scaleFactor)); } // Material properties { MaterialSettings matSettings = m_StEditor.m_MaterialSettings; string matSettingsStr = nameof(m_StEditor.m_MaterialSettings); m_MainColor = FindPropertyFromName(matSettingsStr, nameof(matSettings.mainColor)); m_EnableHueVariation = FindPropertyFromName(matSettingsStr, nameof(matSettings.enableHueVariation)); m_HueVariation = FindPropertyFromName(matSettingsStr, nameof(matSettings.hueVariation)); m_AlphaClipThreshold = FindPropertyFromName(matSettingsStr, nameof(matSettings.alphaClipThreshold)); m_TransmissionScale = FindPropertyFromName(matSettingsStr, nameof(matSettings.transmissionScale)); m_EnableBumpMapping = FindPropertyFromName(matSettingsStr, nameof(matSettings.enableBumpMapping)); m_EnableSubsurfaceScattering = FindPropertyFromName(matSettingsStr, nameof(matSettings.enableSubsurfaceScattering)); m_DiffusionProfileAssetID = FindPropertyFromName(matSettingsStr, nameof(matSettings.diffusionProfileAssetID)); m_DiffusionProfileID = FindPropertyFromName(matSettingsStr, nameof(matSettings.diffusionProfileID)); } // Lighting properties { LightingSettings lightSettings = m_StEditor.m_LightingSettings; string lightSettingsStr = nameof(m_StEditor.m_LightingSettings); m_EnableShadowCasting = FindPropertyFromName(lightSettingsStr, nameof(lightSettings.enableShadowCasting)); m_EnableShadowReceiving = FindPropertyFromName(lightSettingsStr, nameof(lightSettings.enableShadowReceiving)); m_EnableLightProbes = FindPropertyFromName(lightSettingsStr, nameof(lightSettings.enableLightProbes)); m_ReflectionProbeEnumValue = FindPropertyFromName(lightSettingsStr, nameof(lightSettings.reflectionProbeEnumValue)); } // Additional Settings properties { AdditionalSettings addSettings = m_StEditor.m_AdditionalSettings; string addSettingsStr = nameof(m_StEditor.m_AdditionalSettings); m_MotionVectorModeEnumValue = FindPropertyFromName(addSettingsStr, nameof(addSettings.motionVectorModeEnumValue)); m_GenerateColliders = FindPropertyFromName(addSettingsStr, nameof(addSettings.generateColliders)); m_GenerateRigidbody = FindPropertyFromName(addSettingsStr, nameof(addSettings.generateRigidbody)); } // LOD properties { LODSettings lodSettings = m_StEditor.m_LODSettings; string lodSettingsStr = nameof(m_StEditor.m_LODSettings); m_EnableSmoothLOD = FindPropertyFromName(lodSettingsStr, nameof(lodSettings.enableSmoothLODTransition)); m_AnimateCrossFading = FindPropertyFromName(lodSettingsStr, nameof(lodSettings.animateCrossFading)); m_BillboardTransitionCrossFadeWidth = FindPropertyFromName(lodSettingsStr, nameof(lodSettings.billboardTransitionCrossFadeWidth)); m_FadeOutWidth = FindPropertyFromName(lodSettingsStr, nameof(lodSettings.fadeOutWidth)); m_PerLODSettings = serializedObject.FindProperty(nameof(m_StEditor.m_PerLODSettings)); } // Other { m_ShowSmoothLODOptions.value = m_EnableSmoothLOD.hasMultipleDifferentValues || m_EnableSmoothLOD.boolValue; m_ShowSmoothLODOptions.valueChanged.AddListener(Repaint); m_ShowCrossFadeWidthOptions.value = m_AnimateCrossFading.hasMultipleDifferentValues || !m_AnimateCrossFading.boolValue; m_ShowCrossFadeWidthOptions.valueChanged.AddListener(Repaint); } ResetFoldoutLists(); } internal override void OnDisable() { base.OnDisable(); m_ShowSmoothLODOptions.valueChanged.RemoveListener(Repaint); m_ShowCrossFadeWidthOptions.valueChanged.RemoveListener(Repaint); } void TriggerCallbacks() { var allMethods = AttributeHelper.GetMethodsWithAttribute().methodsWithAttributes; foreach (var method in allMethods) { var callback = Delegate.CreateDelegate(typeof(OnCustomEditorSettings), method.info) as OnCustomEditorSettings; callback?.Invoke(ref m_DiffusionProfileAssetID, ref m_DiffusionProfileID); } } public override void OnInspectorGUI() { serializedObject.Update(); // Mesh properties SpeedTreeImporterCommonEditor.ShowMeshGUI(ref m_UnitConversion, ref m_ScaleFactor); EditorGUILayout.Space(); // Material Properties SpeedTreeImporterCommonEditor.ShowMaterialGUI( ref m_MainColor, ref m_EnableHueVariation, ref m_HueVariation, ref m_AlphaClipThreshold, ref m_EnableBumpMapping, ref m_EnableSubsurfaceScattering, renderHueVariationDropdown: m_EnableHueVariation.boolValue, renderAlphaTestRef: m_OutputImporterData.hasAlphaClipThreshold ); if (m_OutputImporterData.hasTransmissionScale) { EditorGUILayout.PropertyField(m_TransmissionScale, Styles.TransmissionScale); } // Update the Diffuse Profile is necessary TriggerCallbacks(); EditorGUILayout.Space(); // Lighting Properties SpeedTreeImporterCommonEditor.ShowLightingGUI( ref m_EnableShadowCasting, ref m_EnableShadowReceiving, ref m_EnableLightProbes, ref m_ReflectionProbeEnumValue); EditorGUILayout.Space(); // Additional Settings SpeedTreeImporterCommonEditor.ShowAdditionalSettingsGUI( ref m_MotionVectorModeEnumValue, ref m_GenerateColliders, ref m_GenerateRigidbody); EditorGUILayout.Space(); // LOD properties SpeedTreeImporterCommonEditor.ShowLODGUI( ref m_EnableSmoothLOD, ref m_AnimateCrossFading, ref m_BillboardTransitionCrossFadeWidth, ref m_FadeOutWidth, ref m_ShowSmoothLODOptions, ref m_ShowCrossFadeWidthOptions); EditorGUILayout.Space(); ShowLODGUI(); ShowMaterialWarnings(); serializedObject.ApplyModifiedProperties(); } private void ShowMaterialWarnings() { EditorGUILayout.Space(); bool materialsNeedToBeUpgraded = upgradeMaterials; if (materialsNeedToBeUpgraded) { EditorGUILayout.HelpBox( String.Format("SpeedTree materials need to be upgraded. " + "Please back them up (if modified manually) then hit the \"{0}\" button below.", Styles.ApplyAndGenerate.text) , MessageType.Warning ); } else if (!materialsNeedToBeUpgraded && DoMaterialsHaveDifferentShader()) { EditorGUILayout.HelpBox( String.Format("There is a different SpeedTree shader provided by the current render pipeline " + "which probably is more suitable for rendering. Hit the \"{0}\" button to regenerate the materials." , (panelContainer as SpeedTree9ImporterEditor).GetGenerateButtonText(HasModified() , materialsNeedToBeUpgraded).text ) , MessageType.Warning ); } } private SerializedProperty FindPropertyFromName(string parentProperty, string childProperty) { const string dotStr = "."; string finalName = String.Concat(parentProperty, dotStr, childProperty); return serializedObject.FindProperty(finalName); } internal bool DoMaterialsHaveDifferentShader() { if (assetTargets is null || assetTargets.Length == 0) { return false; } GameObject[] prefabs = new GameObject[assetTargets.Length]; for (int i = 0; i < assetTargets.Length; ++i) { prefabs[i] = assetTargets[i] as GameObject; } List importerArray = new List(); foreach (SpeedTree9Importer importer in importers) { importerArray.Add(importer); } string defaultShaderName = String.Empty; if (TryGetShaderForCurrentRenderPipeline(out var shader)) { defaultShaderName = shader.name; } else { Debug.LogWarning("SpeedTree9 shader is invalid, cannot create Materials for this SpeedTree asset."); } // In tests assetTargets can become null for (int i = 0; i < Math.Min(importerArray.Count, prefabs?.Length ?? 0); ++i) { foreach (var mr in prefabs[i].transform.GetComponentsInChildren()) { foreach (var mat in mr.sharedMaterials) { if (mat?.shader.name != defaultShaderName) return true; } } } return false; } // TODO: Abstract the following code, so it can be shared between ST8 and ST9. private void ShowLODGUI() { // LOD slider + Customizations if (HasSameLODConfig()) { var area = GUILayoutUtility.GetRect(0, LODGroupGUI.kSliderBarHeight, GUILayout.ExpandWidth(true)); var lods = GetLODInfoArray(area); bool bDrawLODCustomizationGUI = m_SelectedLODRange != -1 && lods.Count > 0; EditorGUILayout.Space(); DrawLODLevelSlider(area, lods); if (bDrawLODCustomizationGUI) { GUILayout.Space(5); DrawLODGroupFoldouts(lods); } } // Mixed Value LOD Slider else { if (CanUnifyLODConfig()) { EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); Rect buttonRect = GUILayoutUtility.GetRect(Styles.ResetLOD, EditorStyles.miniButton); if (GUI.Button(buttonRect, Styles.ResetLOD, EditorStyles.miniButton)) { var dropDownMenu = new GenericMenu(); foreach (SpeedTree9Importer importer in importers) { float[] heights = importer.GetPerLODSettingsHeights(); string[] heightsFormated = new string[heights.Length]; for (int i = 0; i < heights.Length; ++i) { heightsFormated[i] = string.Format("{0:0}%", heights[i] * 100); } var menuText = String.Format("{0}: {1}", Path.GetFileNameWithoutExtension(importer.assetPath), String.Join(" | ", heightsFormated)); dropDownMenu.AddItem(new GUIContent(menuText), false, OnResetLODMenuClick, importer); } dropDownMenu.DropDown(buttonRect); } EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.HelpBox(Styles.MultiSelectionLODNotSupported.text, MessageType.Info); } } EditorGUILayout.Space(); } private readonly int m_LODSliderId = "LODSliderIDHash".GetHashCode(); private void DrawLODLevelSlider(Rect sliderPosition, List lods) { int sliderId = GUIUtility.GetControlID(m_LODSliderId, FocusType.Passive); Event evt = Event.current; switch (evt.GetTypeForControl(sliderId)) { case EventType.Repaint: { LODGroupGUI.DrawLODSlider(sliderPosition, lods, m_SelectedLODRange); break; } case EventType.MouseDown: { // Slightly grow position on the x because edge buttons overflow by 5 pixels var barPosition = sliderPosition; barPosition.x -= 5; barPosition.width += 10; if (barPosition.Contains(evt.mousePosition)) { evt.Use(); GUIUtility.hotControl = sliderId; // Check for button click var clickedButton = false; // case:464019 have to re-sort the LOD array for these buttons to get the overlaps in the right order... List lodsLeft = new List(); List lodsRight = new List(); foreach (LODGroupGUI.LODInfo lodInfo in lods) { if (lodInfo.ScreenPercent > 0.5f) { lodsLeft.Add(lodInfo); } else { lodsRight.Add(lodInfo); } } // Descending order. lodsLeft.Sort(new Comparison((i1, i2) => i2.LODLevel.CompareTo(i1.LODLevel))); // Ascending order. lodsRight.Sort(new Comparison((i1, i2) => i1.LODLevel.CompareTo(i2.LODLevel))); var lodButtonOrder = new List(); lodButtonOrder.AddRange(lodsLeft); lodButtonOrder.AddRange(lodsRight); foreach (var lod in lodButtonOrder) { if (lod.m_ButtonPosition.Contains(evt.mousePosition)) { m_SelectedLODSlider = lod.LODLevel; m_SelectedLODRange = lod.LODLevel; clickedButton = true; break; } } if (!clickedButton) { // Check for range click foreach (var lod in lodButtonOrder) { if (lod.m_RangePosition.Contains(evt.mousePosition)) { m_SelectedLODSlider = -1; m_SelectedLODRange = lod.LODLevel; ExpandSelectedHeaderAndCloseRemaining(m_SelectedLODRange); break; } } } } break; } case EventType.MouseUp: { if (GUIUtility.hotControl == sliderId) { GUIUtility.hotControl = 0; evt.Use(); } break; } case EventType.MouseDrag: { if (GUIUtility.hotControl == sliderId && m_SelectedLODSlider >= 0 && lods[m_SelectedLODSlider] != null) { evt.Use(); var cameraPercent = LODGroupGUI.GetCameraPercent(evt.mousePosition, sliderPosition); // Bias by 0.1% so that there is no skipping when sliding LODGroupGUI.SetSelectedLODLevelPercentage(cameraPercent - 0.001f, m_SelectedLODSlider, lods); m_PerLODSettings.GetArrayElementAtIndex(m_SelectedLODSlider).FindPropertyRelative("height").floatValue = lods[m_SelectedLODSlider].RawScreenPercent; } break; } } } private void DrawLODGroupFoldouts(List lods) { // check camera and bail if null Camera camera = null; if (SceneView.lastActiveSceneView && SceneView.lastActiveSceneView.camera) camera = SceneView.lastActiveSceneView.camera; if (camera == null) return; // draw lod foldouts for (int i = 0; i < m_PerLODSettings.arraySize; i++) { DrawLODGroupFoldout(camera, i, ref m_LODGroupFoldoutHeaderValues[i], lods); } } private string GetLODSubmeshAndTriCountLabel(int numLODs, int lodGroupIndex, SpeedTree9Importer im, LODGroup lodGroup) { LOD[] lods = lodGroup.GetLODs(); if(lods.Length != numLODs) { Debug.LogWarningFormat("Number of LODs mismatch between serialized object & LODGroup: {0}\nPlease re-import the asset and kindly report a bug if this warning keeps coming back.", im.assetPath); numLODs = lods.Length; } int[][] primitiveCounts = new int[numLODs][]; int[] submeshCounts = new int[numLODs]; for (int i = 0; i < lods.Length; i++) { Renderer[] renderers = lods[i].renderers; primitiveCounts[i] = new int[renderers.Length]; for (int j = 0; j < renderers.Length; j++) { bool hasMismatchingSubMeshTopologyTypes = LODGroupEditor.CheckIfMeshesHaveMatchingTopologyTypes(renderers); Mesh rendererMesh = LODGroupEditor.GetMeshFromRendererIfAvailable(renderers[j]); if (rendererMesh == null) continue; submeshCounts[i] += rendererMesh.subMeshCount; if (hasMismatchingSubMeshTopologyTypes) { primitiveCounts[i][j] = rendererMesh.vertexCount; } else { for (int subMeshIndex = 0; subMeshIndex < rendererMesh.subMeshCount; subMeshIndex++) { primitiveCounts[i][j] += (int)rendererMesh.GetIndexCount(subMeshIndex) / 3; } } } } int totalTriCount = 0; if (primitiveCounts.Length > 0 && primitiveCounts[lodGroupIndex] != null) { Array.ForEach(primitiveCounts[lodGroupIndex], delegate (int i) { totalTriCount += i; }); } int sumPrimitiveCounts = 0; Array.ForEach(primitiveCounts[0], delegate (int i) { sumPrimitiveCounts += i; }); int lod0TriCount = sumPrimitiveCounts; var triCountChange = lod0TriCount != 0 ? (float)totalTriCount / lod0TriCount * 100 : 0; string triangleChangeLabel = lodGroupIndex > 0 && lod0TriCount != 0 ? $"({triCountChange.ToString("f2")}% LOD0)" : ""; bool wideInspector = Screen.width >= 480; triangleChangeLabel = wideInspector ? triangleChangeLabel : ""; string submeshCountLabel = wideInspector ? $"- {submeshCounts[lodGroupIndex]} Sub Mesh(es)" : ""; return $"{totalTriCount} {LODGroupGUI.GUIStyles.m_TriangleCountLabel.text} {triangleChangeLabel} {submeshCountLabel}"; } private Color GetLODGroupColor(int lodIndex) { return LODGroupGUI.kLODColors[lodIndex % LODGroupGUI.kLODColors.Length]; } private void DrawLODGroupFoldout(Camera camera, int lodGroupIndex, ref SavedBool foldoutState, List lodInfoList) { GameObject[] ObjectArrayToGameObjectArray(UnityEngine.Object[] objects) { if (objects == null) return null; GameObject[] gameObjects = new GameObject[objects.Length]; for (int i = 0; i < objects.Length; ++i) { gameObjects[i] = objects[i] as GameObject; } return gameObjects; } List importersList = new List(importers); GameObject[] prefabs = ObjectArrayToGameObjectArray(assetTargets); // In tests assetTargets can become null SpeedTree9Importer[] importerArray = importersList.ToArray(); int numSelectedAssets = Math.Min(importerArray.Length, prefabs?.Length ?? 0); bool isDrawingSelectedLODGroup = m_SelectedLODRange == lodGroupIndex; // even though multiple assets may be selected, this code path // ensures the numLODs match for all the selected assets (see HasSameLODConfig() calls) int numLODs = m_PerLODSettings.arraySize; bool hasBillboard = m_OutputImporterData.hasBillboard; string LODFoldoutHeaderLabel = (hasBillboard && lodGroupIndex == m_PerLODSettings.arraySize - 1) ? "Billboard" : $"LOD {lodGroupIndex}"; // primitive and submesh counts are displayed only when a single asset is selected string LODFoldoutHeaderGroupAdditionalText = numSelectedAssets == 1 ? GetLODSubmeshAndTriCountLabel(numLODs, lodGroupIndex, importerArray[0], prefabs[0].GetComponentInChildren()) : ""; // ------------------------------------------------------------------------------------------------------------------------------ if (isDrawingSelectedLODGroup) LODGroupGUI.DrawRoundedBoxAroundLODDFoldout(lodGroupIndex, m_SelectedLODRange); else EditorGUILayout.BeginVertical(EditorStyles.helpBox); foldoutState.value = LODGroupGUI.FoldoutHeaderGroupInternal( GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.inspectorTitlebarFlat) , foldoutState.value , LODFoldoutHeaderLabel , m_LODColorTextures[lodGroupIndex] , GetLODGroupColor(lodGroupIndex) * 0.6f // 0.5f magic number is copied from LODGroupsGUI.cs , LODFoldoutHeaderGroupAdditionalText ); if (foldoutState.value) // expanded LOD-specific options panel { DrawLODSettingCustomizationGUI(lodInfoList, lodGroupIndex); } if (isDrawingSelectedLODGroup) GUILayoutUtility.EndLayoutGroup(); else EditorGUILayout.EndVertical(); } private void DrawLODSettingCustomizationGUI(List lods, int lodIndex) { bool isBillboard = (lodIndex == lods.Count - 1) && m_OutputImporterData.hasBillboard; SerializedProperty selectedLODProp = m_PerLODSettings.GetArrayElementAtIndex(lodIndex); SerializedProperty lodSettingOverride = selectedLODProp.FindPropertyRelative("enableSettingOverride"); // We don't want to clutter the GUI with same options but for billboards, instead // we treat the Billboard LOD level as always 'overrideSettings' and display the // billboard options below without the 'enableSettingOverride' warning text. if (isBillboard) { EditorGUILayout.LabelField("Billboard Options", EditorStyles.boldLabel); EditorGUILayout.HelpBox(Styles.BillboardSettingsHelp.text, MessageType.Info); } else { // Toggle GUIContent customizationLabel = EditorGUIUtility.TrTextContent(String.Format("Customize {0} options", lods[lodIndex].LODName), "To override options for a certain LOD, check this box and select the LOD from the LOD slider above"); EditorGUILayout.PropertyField(lodSettingOverride, customizationLabel); // Warning if (lodSettingOverride.boolValue) { EditorGUILayout.HelpBox(Styles.EnableLodCustomizationsWarn.text, MessageType.Warning); } } EditorGUILayout.Space(); // settings using (new EditorGUI.DisabledScope(!lodSettingOverride.boolValue)) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Lighting", EditorStyles.boldLabel); EditorGUILayout.PropertyField(selectedLODProp.FindPropertyRelative("castShadows"), Styles.CastShadows); using (new EditorGUI.DisabledScope(!UnityEngine.Rendering.SupportedRenderingFeatures.active.receiveShadows)) { EditorGUILayout.PropertyField(selectedLODProp.FindPropertyRelative("receiveShadows"), Styles.ReceiveShadows); } var useLightProbes = selectedLODProp.FindPropertyRelative("useLightProbes"); EditorGUILayout.PropertyField(useLightProbes, Styles.UseLightProbes); if (!useLightProbes.hasMultipleDifferentValues && useLightProbes.boolValue && isBillboard) EditorGUILayout.HelpBox("Enabling Light Probe for billboards breaks batched rendering and may cause performance problem.", MessageType.Warning); // TODO: reflection probe support when PBS is implemented //EditorGUILayout.PropertyField(SelectedLODProp.FindPropertyRelative("useReflectionProbes"), Styles.UseReflectionProbes); EditorGUILayout.Space(); EditorGUILayout.LabelField("Material", EditorStyles.boldLabel); EditorGUILayout.PropertyField(selectedLODProp.FindPropertyRelative("enableHue"), Styles.EnableColorVariation); EditorGUILayout.PropertyField(selectedLODProp.FindPropertyRelative("enableBump"), Styles.EnableBump); EditorGUILayout.PropertyField(selectedLODProp.FindPropertyRelative("enableSubsurface"), Styles.EnableSubsurface); } } private void OnResetLODMenuClick(object userData) { //var toHeights = (userData as SpeedTreeImporter).LODHeights; for (int i = 0; i < m_StEditor.m_PerLODSettings.Count; ++i) { var toHeight = m_StEditor.m_PerLODSettings[i].height; m_PerLODSettings.GetArrayElementAtIndex(i).FindPropertyRelative("height").floatValue = toHeight; } } private bool HasSameLODConfig() { if (m_PerLODSettings.FindPropertyRelative("Array.size").hasMultipleDifferentValues) return false; for (int i = 0; i < m_PerLODSettings.arraySize; ++i) { if (m_PerLODSettings.GetArrayElementAtIndex(i).FindPropertyRelative("height").hasMultipleDifferentValues) return false; } return true; } private void ExpandSelectedHeaderAndCloseRemaining(int index) { // need this to safeguard against drag & drop on Culled section // as that sets the LOD index to 8 which is outside of the total // allowed LOD range if (index >= m_PerLODSettings.arraySize) return; Array.ForEach(m_LODGroupFoldoutHeaderValues, el => el.value = false); m_LODGroupFoldoutHeaderValues[index].value = true; } private List GetLODInfoArray(Rect area) { int lodCount = m_PerLODSettings.arraySize; return LODGroupGUI.CreateLODInfos( lodCount, area, i => i == lodCount - 1 && m_OutputImporterData.hasBillboard ? "Billboard" : String.Format("LOD {0}", i), i => m_PerLODSettings.GetArrayElementAtIndex(i).FindPropertyRelative("height").floatValue); } private bool CanUnifyLODConfig() { // TODO: Add multi-selection support for LOD UI. return false; } void InitAndSetFoldoutLabelTextures() { m_LODColorTextures = new Texture2D[m_PerLODSettings.arraySize]; for (int i = 0; i < m_LODColorTextures.Length; i++) { m_LODColorTextures[i] = new Texture2D(1, 1); m_LODColorTextures[i].SetPixel(0, 0, GetLODGroupColor(i)); } } void ResetFoldoutLists() { int lodArraySize = Mathf.Min(m_PerLODSettings.arraySize, LODGroupGUI.kLODColors.Length); m_LODGroupFoldoutHeaderValues = new SavedBool[lodArraySize]; for (int i = 0; i < lodArraySize; i++) { m_LODGroupFoldoutHeaderValues[i] = new SavedBool($"{target.GetType()}.lodFoldout{i}", false); } InitAndSetFoldoutLabelTextures(); } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterWindEditor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditor.AssetImporters; using System; using UnityEngine; namespace UnityEditor.SpeedTree.Importer { class SpeedTree9ImporterWindEditor : BaseSpeedTree9ImporterTabUI { private class Styles { public static GUIContent EnableWind = EditorGUIUtility.TrTextContent("Enable Wind"); public static GUIContent StrengthResponse = EditorGUIUtility.TrTextContent("Strength Response", "The strength response of the wind."); public static GUIContent DirectionResponse = EditorGUIUtility.TrTextContent("Direction Response", "The direction response of the wind."); public static GUIContent WindRandomness = EditorGUIUtility.TrTextContent("Randomness", "Amount of world position based noise applied to each tree."); } private SerializedProperty m_EnableWind; private SerializedProperty m_StrengthResponse; private SerializedProperty m_DirectionResponse; private SerializedProperty m_WindRandomness; private SpeedTree9Importer m_StEditor; public SpeedTree9ImporterWindEditor(AssetImporterEditor panelContainer) : base(panelContainer) { } internal override void OnEnable() { m_StEditor = target as SpeedTree9Importer; WindSettings windSettings = m_StEditor.m_WindSettings; string windSettingsStr = nameof(m_StEditor.m_WindSettings); m_EnableWind = FindPropertyFromName(windSettingsStr, nameof(windSettings.enableWind)); m_StrengthResponse = FindPropertyFromName(windSettingsStr, nameof(windSettings.strenghResponse)); m_DirectionResponse = FindPropertyFromName(windSettingsStr, nameof(windSettings.directionResponse)); m_WindRandomness = FindPropertyFromName(windSettingsStr, nameof(windSettings.randomness)); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(m_EnableWind, Styles.EnableWind); using (new EditorGUI.DisabledScope(!m_EnableWind.boolValue)) { EditorGUILayout.PropertyField(m_StrengthResponse, Styles.StrengthResponse); EditorGUILayout.PropertyField(m_DirectionResponse, Styles.DirectionResponse); EditorGUILayout.PropertyField(m_WindRandomness, Styles.WindRandomness); } serializedObject.ApplyModifiedProperties(); } private SerializedProperty FindPropertyFromName(string parentProperty, string childProperty) { const string dotStr = "."; string finalName = String.Concat(parentProperty, dotStr, childProperty); return serializedObject.FindProperty(finalName); } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Reader.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.IO; using System.Runtime.InteropServices; using UnityEngine; namespace UnityEditor.SpeedTree.Importer { #region Data Classes [StructLayout(LayoutKind.Sequential, Pack = 4)] struct Vec2 { private float x, y; public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct Vec3 { private float x, y, z; public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } public float Z { get { return z; } set { z = value; } } } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct Vec4 { private float x, y, z, w; public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } public float Z { get { return z; } set { z = value; } } public float W { get { return w; } set { w = value; } } } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct Bounds3 { public Vec3 Min { get; private set; } public Vec3 Max { get; private set; } } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct Vertex { public Vec3 Anchor { get; private set; } public Vec3 Offset { get; private set; } public Vec3 LodOffset { get; private set; } public Vec3 Normal { get; private set; } public Vec3 Tangent { get; private set; } public Vec3 Binormal { get; private set; } public Vec2 TexCoord { get; private set; } public Vec2 LightmapTexCoord { get; private set; } public Vec3 Color { get; private set; } public float AmbientOcclusion { get; private set; } public float BlendWeight { get; private set; } public Vec3 BranchWind1 { get; private set; } // pos, dir, weight public Vec3 BranchWind2 { get; private set; } public float RippleWeight { get; private set; } public bool CameraFacing { get; private set; } public uint BoneID { get; private set; } } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct DrawCall { public uint MaterialIndex { get; private set; } public bool ContainsFacingGeometry { get; private set; } public uint IndexStart { get; private set; } public uint IndexCount { get; private set; } } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct Bone { public uint ID { get; private set; } public bool ParentID { get; private set; } public Vec3 Start { get; private set; } public Vec3 End { get; private set; } public float Radius { get; private set; } } #endregion #region Data Tables interface ReaderData { public void Initialize(Byte[] data, int offset); } class Lod : ReaderData { private enum OffsetData { Vertices = 0, Indices = 1, DrawCalls = 2 } public Vertex[] Vertices { get; private set; } public uint[] Indices { get; private set; } public DrawCall[] DrawCalls { get; private set; } public void Initialize(Byte[] data, int offset) { Vertices = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Vertices); Indices = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Indices); DrawCalls = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.DrawCalls); } } class BillboardInfo : ReaderData { private enum OffsetData { LastLodIsBillboard = 0, IncludesTopDown = 1, SideViewCount = 2 } public bool LastLodIsBillboard { get; private set; } public bool IncludesTopDown { get; private set; } public uint SideViewCount { get; private set; } public void Initialize(Byte[] data, int offset) { LastLodIsBillboard = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.LastLodIsBillboard); IncludesTopDown = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.IncludesTopDown); SideViewCount = SpeedTree9ReaderUtility.GetUInt(data, offset, (int)OffsetData.SideViewCount); } } class CollisionObject : ReaderData { private enum OffsetData { Position = 0, Position2 = 1, Radius = 2, UserData = 3 } public Vec3 Position { get; private set; } public Vec3 Position2 { get; private set; } public float Radius { get; private set; } public string UserData { get; private set; } public void Initialize(Byte[] data, int offset) { Position = SpeedTree9ReaderUtility.GetStruct(data, offset, (int)OffsetData.Position); Position2 = SpeedTree9ReaderUtility.GetStruct(data, offset, (int)OffsetData.Position2); Radius = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.Radius); UserData = SpeedTree9ReaderUtility.GetString(data, offset, (int)OffsetData.UserData); } } class MaterialMap : ReaderData { private enum OffsetData { Used = 0, Path = 1, Color = 2 } public bool Used { get; private set; } public string Path { get; private set; } public Vec4 Color { get; private set; } public void Initialize(Byte[] data, int offset) { Used = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.Used); Path = SpeedTree9ReaderUtility.GetString(data, offset, (int)OffsetData.Path); Color = SpeedTree9ReaderUtility.GetStruct(data, offset, (int)OffsetData.Color); } } class STMaterial : ReaderData { private enum OffsetData { Name = 0, TwoSided = 1, FlipNormalsOnBackside = 2, Billboard = 3, Maps = 4 } public string Name { get; private set; } public bool TwoSided { get; private set; } public bool FlipNormalsOnBackside { get; private set; } public bool Billboard { get; private set; } public MaterialMap[] Maps { get; private set; } public void Initialize(Byte[] data, int offset) { Name = SpeedTree9ReaderUtility.GetString(data, offset, (int)OffsetData.Name); TwoSided = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.TwoSided); FlipNormalsOnBackside = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.FlipNormalsOnBackside); Billboard = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.Billboard); Maps = SpeedTree9ReaderUtility.GetDataObjectArray(data, offset, (int)OffsetData.Maps); } } #endregion #region Wind Data Tables class WindConfigCommon : ReaderData { private enum OffsetData { StrengthResponse = 0, DirectionResponse = 1, GustFrequency = 5, GustStrengthMin = 6, GustStrengthMax = 7, GustDurationMin = 8, GustDurationMax = 9, GustRiseScalar = 10, GustFallScalar = 11, CurrentStrength = 15 } public float StrengthResponse { get; private set; } public float DirectionResponse { get; private set; } public float GustFrequency { get; private set; } public float GustStrengthMin { get; private set; } public float GustStrengthMax { get; private set; } public float GustDurationMin { get; private set; } public float GustDurationMax { get; private set; } public float GustRiseScalar { get; private set; } public float GustFallScalar { get; private set; } public float CurrentStrength { get; private set; } public void Initialize(Byte[] data, int offset) { StrengthResponse = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.StrengthResponse); DirectionResponse = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.DirectionResponse); GustFrequency = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustFrequency); GustStrengthMin = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustStrengthMin); GustStrengthMax = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustStrengthMax); GustDurationMin = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustDurationMin); GustDurationMax = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustDurationMax); GustRiseScalar = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustRiseScalar); GustFallScalar = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.GustFallScalar); CurrentStrength = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.CurrentStrength); } }; class WindConfigSDK : ReaderData { internal class WindBranch : ReaderData { private enum OffsetData { Bend = 0, Oscillation = 1, Speed = 2, Turbulence = 3, Flexibility = 4, Independence = 5 } public float[] Bend { get; private set; } public float[] Oscillation { get; private set; } public float[] Speed { get; private set; } public float[] Turbulence { get; private set; } public float[] Flexibility { get; private set; } public float Independence { get; private set; } public void Initialize(Byte[] data, int offset) { Bend = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Bend); Oscillation = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Oscillation); Speed = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Speed); Turbulence = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Turbulence); Flexibility = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Flexibility); Independence = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.Independence); } }; internal class WindRipple : ReaderData { private enum OffsetData { Planar = 0, Directional = 1, Speed = 2, Flexibility = 3, Shimmer = 4, Independence = 5 } public float[] Planar { get; private set; } public float[] Directional { get; private set; } public float[] Speed { get; private set; } public float[] Flexibility { get; private set; } public float Shimmer { get; private set; } public float Independence { get; private set; } public void Initialize(Byte[] data, int offset) { Planar = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Planar); Directional = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Directional); Speed = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Speed); Flexibility = SpeedTree9ReaderUtility.GetArray(data, offset, (int)OffsetData.Flexibility); Shimmer = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.Shimmer); Independence = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.Independence); } }; private enum OffsetData { Common = 0, Shared = 1, Branch1 = 2, Branch2 = 3, Ripple = 4, SharedStartHeight = 10, Branch1StretchLimit = 11, Branch2StretchLimit = 12, DoShared = 15, DoBranch1 = 16, DoBranch2 = 17, DoRipple = 18, DoShimmer = 19 } public WindConfigCommon Common { get; private set; } public WindBranch Shared { get; private set; } public WindBranch Branch1 { get; private set; } public WindBranch Branch2 { get; private set; } public WindRipple Ripple { get; private set; } public float SharedStartHeight { get; private set; } public float Branch1StretchLimit { get; private set; } public float Branch2StretchLimit { get; private set; } public bool DoShared { get; private set; } public bool DoBranch1 { get; private set; } public bool DoBranch2 { get; private set; } public bool DoRipple { get; private set; } public bool DoShimmer { get; private set; } public void Initialize(Byte[] data, int offset) { Common = SpeedTree9ReaderUtility.GetDataObject(data, offset, (int)OffsetData.Common); Shared = SpeedTree9ReaderUtility.GetDataObject(data, offset, (int)OffsetData.Shared); Branch1 = SpeedTree9ReaderUtility.GetDataObject(data, offset, (int)OffsetData.Branch1); Branch2 = SpeedTree9ReaderUtility.GetDataObject(data, offset, (int)OffsetData.Branch2); Ripple = SpeedTree9ReaderUtility.GetDataObject(data, offset, (int)OffsetData.Ripple); SharedStartHeight = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.SharedStartHeight); Branch1StretchLimit = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.Branch1StretchLimit); Branch2StretchLimit = SpeedTree9ReaderUtility.GetFloat(data, offset, (int)OffsetData.Branch2StretchLimit); DoShared = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.DoShared); DoBranch1 = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.DoBranch1); DoBranch2 = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.DoBranch2); DoRipple = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.DoRipple); DoShimmer = SpeedTree9ReaderUtility.GetBool(data, offset, (int)OffsetData.DoShimmer); } }; #endregion #region Reader Utilities static class SpeedTree9ReaderUtility { public static int GetOffset(in Byte[] data, int offsetIn, int index) { int offset = offsetIn + (index + 1) * 4; return offsetIn + (int)BitConverter.ToUInt32(data, offset); } public static float GetFloat(in Byte[] data, int offsetIn, int index) { return BitConverter.ToSingle(data, GetOffset(data, offsetIn, index)); } public static string GetString(in Byte[] data, int offsetIn, int index) { int offset = GetOffset(data, offsetIn, index); int length = (int)BitConverter.ToUInt32(data, offset); return System.Text.Encoding.UTF8.GetString(data, offset + 4, length - 1); } public static T GetStruct(in Byte[] data, int offsetIn, int index) where T : struct { int offset = GetOffset(data, offsetIn, index); return MemoryMarshal.Cast(data.AsSpan().Slice(offset))[0]; } public static bool GetBool(in Byte[] data, int offsetIn, int index) { return GetInt(data, offsetIn, index) != 0; } public static int GetInt(in Byte[] data, int offsetIn, int index) { return BitConverter.ToInt32(data, GetOffset(data, offsetIn, index)); } public static uint GetUInt(in Byte[] data, int offsetIn, int index) { return BitConverter.ToUInt32(data, GetOffset(data, offsetIn, index)); } public static T[] GetArray(Byte[] data, int offset, int type) where T : struct { int offsetData = SpeedTree9ReaderUtility.GetOffset(data, offset, (int)type); uint countData = BitConverter.ToUInt32(data, offsetData); T[] array = new T[countData]; for (int i = 0; i < countData; i++) { array[i] = MemoryMarshal.Cast(data.AsSpan().Slice(offsetData + 4))[i]; } return array; } public static T GetDataObject(in Byte[] data, int offset, int index) where T : ReaderData, new() { int dataOffset = SpeedTree9ReaderUtility.GetOffset(data, offset, index); T readData = new T(); readData.Initialize(data, dataOffset); return readData; } public static T[] GetDataObjectArray(in Byte[] data, int offset, int index) where T : ReaderData, new() { int dataOffset = SpeedTree9ReaderUtility.GetOffset(data, offset, index); uint dataCount = BitConverter.ToUInt32(data, dataOffset); T[] readData = new T[dataCount]; for (int i = 0; i < dataCount; i++) { int offsetNew = SpeedTree9ReaderUtility.GetOffset(data, dataOffset, i); T newData = new T(); newData.Initialize(data, offsetNew); readData[i] = newData; } return readData; } } #endregion internal class SpeedTree9Reader : IDisposable { internal enum FileStatus { Valid = 0, InvalidPath = 1, InvalidSignature = 2 } enum OffsetData { VersionMajor = 0, VersionMinor = 1, Bounds = 2, Lod = 5, BillboardInfo = 6, CollisionObjects = 7, Materials = 10, Wind = 15 } const string k_TokenKey = "SpeedTree9______"; public uint VersionMajor { get; private set; } public uint VersionMinor { get; private set; } public Bounds3 Bounds { get; private set; } public Lod[] Lod { get; private set; } public BillboardInfo BillboardInfo { get; private set; } public CollisionObject[] CollisionObjects { get; private set; } public STMaterial[] Materials { get; private set; } public WindConfigSDK Wind { get; private set; } private string m_AssetPath; private FileStream m_FileStream; private int m_Offset; private bool m_Disposed; public FileStatus Initialize(string assetPath) { m_AssetPath = assetPath; m_FileStream = new FileStream(m_AssetPath, FileMode.Open, FileAccess.Read); if (!File.Exists(assetPath)) { return FileStatus.InvalidPath; } if (!ValidateFileSignature()) { return FileStatus.InvalidSignature; } return FileStatus.Valid; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { if (!m_Disposed) { // Manual release of managed resources. if (disposing) { if (m_FileStream != null) { m_FileStream.Close(); } } // Release unmanaged resources. m_Disposed = true; } } public void ReadContent() { byte[] data = File.ReadAllBytes(m_AssetPath); VersionMajor = SpeedTree9ReaderUtility.GetUInt(data, m_Offset, (int)OffsetData.VersionMajor); VersionMinor = SpeedTree9ReaderUtility.GetUInt(data, m_Offset, (int)OffsetData.VersionMinor); Bounds = SpeedTree9ReaderUtility.GetStruct(data, m_Offset, (int)OffsetData.Bounds); Lod = SpeedTree9ReaderUtility.GetDataObjectArray(data, m_Offset, (int)OffsetData.Lod); BillboardInfo = SpeedTree9ReaderUtility.GetDataObject(data, m_Offset, (int)OffsetData.BillboardInfo); CollisionObjects = SpeedTree9ReaderUtility.GetDataObjectArray(data, m_Offset, (int)OffsetData.CollisionObjects); Materials = SpeedTree9ReaderUtility.GetDataObjectArray(data, m_Offset, (int)OffsetData.Materials); Wind = SpeedTree9ReaderUtility.GetDataObject(data, m_Offset, (int)OffsetData.Wind); if (m_FileStream != null) { m_FileStream.Close(); } } private unsafe bool ValidateFileSignature() { byte[] buffer = new byte[k_TokenKey.Length]; try { int bytesRead = m_FileStream.Read(buffer, m_Offset, k_TokenKey.Length); if (bytesRead != buffer.Length) { return false; } } catch (UnauthorizedAccessException ex) { Debug.LogError(ex.Message); } bool valid = true; for (int i = 0; i < k_TokenKey.Length && valid; ++i) { valid &= (k_TokenKey[i] == buffer[i]); } if (valid) { m_Offset = k_TokenKey.Length; } return valid; } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterCommon.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System; using UnityEngine.Rendering; using UnityEditor.AnimatedValues; namespace UnityEditor.SpeedTree.Importer { class SpeedTreeConstants { internal static readonly float kFeetToMetersRatio = 0.3048f; internal static readonly float kCentimetersToMetersRatio = 0.01f; internal static readonly float kInchesToMetersRatio = 0.0254f; internal static readonly float kAlphaTestRef = 0.33f; internal static readonly string kBillboardMaterialName = "Billboard"; } static class SpeedTreeImporterCommon { internal enum STUnitConversion { kLeaveAsIs = 0, kFeetToMeters, kCentimetersToMeters, kInchesToMeters, kCustomConversion }; internal static class MaterialProperties { internal static readonly int MainTexID = Shader.PropertyToID("_MainTex"); internal static readonly int ColorTintID = Shader.PropertyToID("_ColorTint"); internal static readonly int NormalMapKwToggleID = Shader.PropertyToID("_NormalMapKwToggle"); internal static readonly int NormalMapID = Shader.PropertyToID("_NormalMap"); internal static readonly int ExtraMapKwToggleID = Shader.PropertyToID("_ExtraMapKwToggle"); internal static readonly int ExtraTexID = Shader.PropertyToID("_ExtraTex"); internal static readonly int GlossinessID = Shader.PropertyToID("_Glossiness"); internal static readonly int MetallicID = Shader.PropertyToID("_Metallic"); internal static readonly int SubsurfaceKwToggleID = Shader.PropertyToID("_SubsurfaceKwToggle"); internal static readonly int SubsurfaceTexID = Shader.PropertyToID("_SubsurfaceTex"); internal static readonly int SubsurfaceColorID = Shader.PropertyToID("_SubsurfaceColor"); internal static readonly int AlphaClipThresholdID = Shader.PropertyToID("_AlphaClipThreshold"); internal static readonly int TransmissionScaleID = Shader.PropertyToID("_TransmissionScale"); internal static readonly int DiffusionProfileAssetID = Shader.PropertyToID("_Diffusion_Profile_Asset"); internal static readonly int DiffusionProfileID = Shader.PropertyToID("_DiffusionProfile"); internal static readonly int HueVariationKwToggleID = Shader.PropertyToID("_HueVariationKwToggle"); internal static readonly int HueVariationColorID = Shader.PropertyToID("_HueVariationColor"); internal static readonly int LeafFacingKwToggleID = Shader.PropertyToID("_LeafFacingKwToggle"); internal static readonly int BillboardKwToggleID = Shader.PropertyToID("_BillboardKwToggle"); internal static readonly int DoubleSidedToggleID = Shader.PropertyToID("_DoubleSidedKwToggle"); internal static readonly int DoubleSidedNormalModeID = Shader.PropertyToID("_DoubleSidedNormalMode"); internal static readonly int BackfaceNormalModeID = Shader.PropertyToID("_BackfaceNormalMode"); internal static readonly int TwoSidedID = Shader.PropertyToID("_TwoSided"); internal static readonly int WindSharedKwToggle = Shader.PropertyToID("_WIND_SHARED"); internal static readonly int WindBranch2KwToggle = Shader.PropertyToID("_WIND_BRANCH2"); internal static readonly int WindBranch1KwToggle = Shader.PropertyToID("_WIND_BRANCH1"); internal static readonly int WindRippleKwToggle = Shader.PropertyToID("_WIND_RIPPLE"); internal static readonly int WindShimmerKwToggle = Shader.PropertyToID("_WIND_SHIMMER"); } internal static class MaterialKeywords { internal static readonly string VBSetupStandardID = "VB_SETUP_STANDARD"; internal static readonly string VBSetupBranch2ID = "VB_SETUP_BRANCH2"; internal static readonly string VBSetupCameraFacingID = "VB_SETUP_CAMERA_FACING"; internal static readonly string VBSetupBranch2AndCameraFacingID = "VB_SETUP_BRANCH2_AND_CAMERA_FACING"; internal static readonly string VBSetupBillboardID = "VB_SETUP_BILLBOARD"; internal static readonly string VBSetupID = "VB_SETUP"; internal static readonly string BillboardID = "_BILLBOARD"; } } static class SpeedTreeImporterCommonEditor { internal class Styles { // Meshes labels public static GUIContent MeshesHeader = EditorGUIUtility.TrTextContent("Meshes"); public static GUIContent UnitConversion = EditorGUIUtility.TrTextContent("Unit Conversion", "Select the unit conversion to apply to the imported SpeedTree asset."); public static GUIContent ScaleFactor = EditorGUIUtility.TrTextContent("Scale Factor", "How much to scale the tree model, interpreting the exported units as meters. Must be positive."); public static GUIContent[] UnitConversionNames = { new GUIContent("Leave As Is") , new GUIContent("ft to m") , new GUIContent("cm to m") , new GUIContent("inch to m") , new GUIContent("Custom") }; // Materials labels public static GUIContent MaterialHeader = EditorGUIUtility.TrTextContent("Material"); public static GUIContent MainColor = EditorGUIUtility.TrTextContent("Main Color", "The color modulating the diffuse lighting component."); public static GUIContent EnableColorVariation = EditorGUIUtility.TrTextContent("Color Variation", "Color is determined by linearly interpolating between the Main Color & Color Variation values based on the world position X, Y and Z values"); public static GUIContent EnableBump = EditorGUIUtility.TrTextContent("Normal Map", "Enable normal (Bump) mapping."); public static GUIContent EnableSubsurface = EditorGUIUtility.TrTextContent("Subsurface Scattering", "Enable subsurface scattering effects."); public static GUIContent HueVariation = EditorGUIUtility.TrTextContent("Variation Color (RGB), Intensity (A)", "Tint the tree with the Variation Color"); public static GUIContent AlphaTestRef = EditorGUIUtility.TrTextContent("Alpha Cutoff", "The alpha-test reference value."); public static GUIContent TransmissionScale = EditorGUIUtility.TrTextContent("Transmission Scale", "The transmission scale value."); // Lighting labels public static GUIContent LightingHeader = EditorGUIUtility.TrTextContent("Lighting"); public static GUIContent CastShadows = EditorGUIUtility.TrTextContent("Cast Shadows", "The tree casts shadow"); public static GUIContent ReceiveShadows = EditorGUIUtility.TrTextContent("Receive Shadows", "The tree receives shadow"); public static GUIContent UseLightProbes = EditorGUIUtility.TrTextContent("Light Probes", "The tree uses light probe for lighting"); // TODO: update help text public static GUIContent UseReflectionProbes = EditorGUIUtility.TrTextContent("Reflection Probes", "The tree uses reflection probe for rendering"); // TODO: update help text public static GUIContent[] ReflectionProbeUsageNames = GetReflectionProbeUsageNames(); public static GUIContent[] GetReflectionProbeUsageNames() { string[] names = Enum.GetNames(typeof(ReflectionProbeUsage)); GUIContent[] probUsageNames = new GUIContent[names.Length]; for (int i = 0; i < names.Length; ++i) { string varName = ObjectNames.NicifyVariableName(names[i]); probUsageNames[i] = (new GUIContent(varName)); } return probUsageNames; } // Additional Settings labels public static GUIContent AdditionalSettingsHeader = EditorGUIUtility.TrTextContent("Additional Settings"); public static GUIContent MotionVectorMode = EditorGUIUtility.TrTextContent("Motion Vectors", "Motion vector mode to set for the mesh renderer of each LOD object"); // Wind labels public static GUIContent WindHeader = EditorGUIUtility.TrTextContent("Wind"); public static GUIContent WindQuality = EditorGUIUtility.TrTextContent("Wind Quality", "Controls the wind effect's quality."); public static GUIContent[] MotionVectorModeNames = // Match SharedRendererDataTypes.h / enum MotionVectorGenerationMode { new GUIContent("Camera Motion Only") // kMotionVectorCamera = 0, // Use camera motion for motion vectors , new GUIContent("Per Object Motion") // kMotionVectorObject, // Use a per object motion vector pass for this object , new GUIContent("Force No Motion") // kMotionVectorForceNoMotion, // Force no motion for this object (0 into motion buffer) }; public static GUIContent[] GetWindQualityNames() { GUIContent[] windQualityNames = new GUIContent[SpeedTreeImporter.windQualityNames.Length]; for (int i = 0; i < SpeedTreeImporter.windQualityNames.Length; ++i) { windQualityNames[i] = new GUIContent(SpeedTreeImporter.windQualityNames[i]); } return windQualityNames; } // LOD labels public static GUIContent LODHeader = EditorGUIUtility.TrTextContent("LOD"); public static GUIContent ResetLOD = EditorGUIUtility.TrTextContent("Reset LOD to...", "Unify the LOD settings for all selected assets"); public static GUIContent SmoothLOD = EditorGUIUtility.TrTextContent("Smooth Transitions", "Toggles smooth LOD transitions"); public static GUIContent AnimateCrossFading = EditorGUIUtility.TrTextContent("Animate Cross-fading", "Cross-fading is animated instead of being calculated by distance"); public static GUIContent CrossFadeWidth = EditorGUIUtility.TrTextContent("Crossfade Width", "Proportion of the last 3D mesh LOD region width which is used for cross-fading to billboard tree"); public static GUIContent FadeOutWidth = EditorGUIUtility.TrTextContent("Fade Out Width", "The proportion of the billboard LOD region width that is used to fade out the billboard."); public static GUIContent EnableLodCustomizationsWarn = EditorGUIUtility.TrTextContent("Customizing LOD options may help with tuning the GPU performance but will likely negatively impact the instanced draw batching, i.e. CPU performance.\nPlease use the per-LOD customizations with careful memory and performance profiling for both CPU and GPU and remember that these options are a trade-off rather than a free win."); public static GUIContent BillboardSettingsHelp = EditorGUIUtility.TrTextContent("Billboard options are separate from the 3D model options shown above.\nChange the options below for influencing billboard rendering."); public static GUIContent MultiSelectionLODNotSupported = EditorGUIUtility.TrTextContent("Multi-selection is not supported for LOD settings."); public static GUIContent ApplyAndGenerate = EditorGUIUtility.TrTextContent("Apply & Generate Materials", "Apply current importer settings and generate asset materials with the new settings."); } static internal void ShowMeshGUI( ref SerializedProperty unitConversionEnumValue, ref SerializedProperty scaleFactor) { GUILayout.Label(Styles.MeshesHeader, EditorStyles.boldLabel); EditorGUILayout.Popup(unitConversionEnumValue, Styles.UnitConversionNames, Styles.UnitConversion); bool bShowCustomScaleFactor = unitConversionEnumValue.intValue == Styles.UnitConversionNames.Length - 1; if (bShowCustomScaleFactor) { EditorGUILayout.PropertyField(scaleFactor, Styles.ScaleFactor); if (scaleFactor.floatValue < 0f) { scaleFactor.floatValue = 0f; } if (scaleFactor.floatValue == 0f) { EditorGUILayout.HelpBox("Scale factor must be positive.", MessageType.Warning); } } } static internal void ShowMaterialGUI( ref SerializedProperty mainColor, ref SerializedProperty enableHueVariation, ref SerializedProperty hueVariation, ref SerializedProperty alphaTestRef, ref SerializedProperty enableBumpMapping, ref SerializedProperty enableSubsurfaceScattering, bool renderHueVariationDropdown = false, bool renderAlphaTestRef = false) { EditorGUILayout.LabelField(Styles.MaterialHeader, EditorStyles.boldLabel); EditorGUILayout.PropertyField(mainColor, Styles.MainColor); EditorGUILayout.PropertyField(enableHueVariation, Styles.EnableColorVariation); if (renderHueVariationDropdown) { EditorGUILayout.PropertyField(hueVariation, Styles.HueVariation); } if (renderAlphaTestRef) EditorGUILayout.Slider(alphaTestRef, 0f, 1f, Styles.AlphaTestRef); EditorGUILayout.PropertyField(enableBumpMapping, Styles.EnableBump); EditorGUILayout.PropertyField(enableSubsurfaceScattering, Styles.EnableSubsurface); } static internal void ShowLightingGUI( ref SerializedProperty enableShadowCasting, ref SerializedProperty enableShadowReceiving, ref SerializedProperty enableLightProbeUsage, ref SerializedProperty reflectionProbeUsage) { GUILayout.Label(Styles.LightingHeader, EditorStyles.boldLabel); EditorGUILayout.PropertyField(enableShadowCasting, Styles.CastShadows); // from the docs page: https://docs.unity3d.com/Manual/SpeedTree.html // Known issues: As with any other renderer, the Receive Shadows option has no effect while using deferred rendering. // TODO: test and conditionally expose this field using (new EditorGUI.DisabledScope(!UnityEngine.Rendering.SupportedRenderingFeatures.active.receiveShadows)) { EditorGUILayout.PropertyField(enableShadowReceiving, Styles.ReceiveShadows); } EditorGUILayout.PropertyField(enableLightProbeUsage, Styles.UseLightProbes); } static internal void ShowAdditionalSettingsGUI( ref SerializedProperty motionVectorModeEnumValue, ref SerializedProperty generateColliders, ref SerializedProperty generateRigidbody) { GUILayout.Label(Styles.AdditionalSettingsHeader, EditorStyles.boldLabel); EditorGUILayout.Popup(motionVectorModeEnumValue, Styles.MotionVectorModeNames, Styles.MotionVectorMode); EditorGUILayout.PropertyField(generateColliders); EditorGUILayout.PropertyField(generateRigidbody); } static internal void ShowWindGUI( ref SerializedProperty bestWindQuality, ref SerializedProperty selectedWindQuality) { GUILayout.Label(Styles.WindHeader, EditorStyles.boldLabel); int NumAvailableWindQualityOptions = 1 + bestWindQuality.intValue; // 0 is None, we want at least 1 value ArraySegment availableWindQualityOptions = new ArraySegment(Styles.GetWindQualityNames(), 0, NumAvailableWindQualityOptions); EditorGUILayout.Popup(selectedWindQuality, availableWindQualityOptions.ToArray(), Styles.WindQuality); } static internal void ShowLODGUI( ref SerializedProperty enableSmoothLOD, ref SerializedProperty animateCrossFading, ref SerializedProperty billboardTransitionCrossFadeWidth, ref SerializedProperty fadeOutWidth, ref AnimBool showSmoothLODOptions, ref AnimBool showCrossFadeWidthOptions) { showSmoothLODOptions.target = enableSmoothLOD.hasMultipleDifferentValues || enableSmoothLOD.boolValue; showCrossFadeWidthOptions.target = animateCrossFading.hasMultipleDifferentValues || !animateCrossFading.boolValue; GUILayout.Label(Styles.LODHeader, EditorStyles.boldLabel); EditorGUILayout.PropertyField(enableSmoothLOD, Styles.SmoothLOD); using (new EditorGUI.IndentLevelScope()) { // Note: FadeGroupScope doesn't work here, as if the 'faded' is not updated correctly. if (EditorGUILayout.BeginFadeGroup(showSmoothLODOptions.faded)) { EditorGUILayout.PropertyField(animateCrossFading, Styles.AnimateCrossFading); if (EditorGUILayout.BeginFadeGroup(showCrossFadeWidthOptions.faded)) { EditorGUILayout.Slider(billboardTransitionCrossFadeWidth, 0.0f, 1.0f, Styles.CrossFadeWidth); EditorGUILayout.Slider(fadeOutWidth, 0.0f, 1.0f, Styles.FadeOutWidth); } EditorGUILayout.EndFadeGroup(); } EditorGUILayout.EndFadeGroup(); } } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterOutputData.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; namespace UnityEditor.SpeedTree.Importer { internal class SpeedTreeImporterOutputData : ScriptableObject { /// /// The main object of the asset. /// public GameObject mainObject; /// /// Contains the materials data and materials are assigned per LOD with an index. /// public LODMaterials lodMaterials = new LODMaterials(); /// /// The materials metadata used for the materials extraction code. /// public List materialsIdentifiers = new List(); /// /// Represents the wind configuration parameters. /// public SpeedTreeWindConfig9 m_WindConfig = new SpeedTreeWindConfig9(); /// /// Determines if the current asset has embedded materials or not. /// public bool hasEmbeddedMaterials = false; /// /// Determines if the current Shader supports alpha clip threshold. /// public bool hasAlphaClipThreshold = false; /// /// Determines if the current Shader supports transmision scale. /// public bool hasTransmissionScale = false; /// /// Determines if the current asset has a biilboard or not. /// public bool hasBillboard = false; /// /// Create an instance of SpeedTreeImporterOutputData with default settings. /// /// The newly created SpeedTreeImporterOutputData instance. static public SpeedTreeImporterOutputData Create() { SpeedTreeImporterOutputData outputImporterData = CreateInstance(); outputImporterData.name = "ImporterOutputData"; // Ideally, we should use this flag so the file is not visible in the asset prefab. // Though, it breaks the 'AssetDatabase.LoadAssetAtPath' function (the file is not loaded anymore). // outputImporterData.hideFlags = HideFlags.HideInHierarchy; return outputImporterData; } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTree/SpeedTreeImporterSettings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System; using UnityEngine.Rendering; using System.Collections.Generic; using static UnityEditor.SpeedTree.Importer.SpeedTreeImporterCommon; namespace UnityEditor.SpeedTree.Importer { [Serializable] internal class MeshSettings { public STUnitConversion unitConversion = STUnitConversion.kFeetToMeters; public float scaleFactor = SpeedTreeConstants.kFeetToMetersRatio; } [Serializable] internal class MaterialSettings { public Color mainColor = Color.white; public Color hueVariation = new Color(1.0f, 0.5f, 0.0f, 0.1f); public float alphaClipThreshold = 0.10f; public float transmissionScale = 0.0f; public bool enableHueVariation = false; public bool enableBumpMapping = true; public bool enableSubsurfaceScattering = true; public Vector4 diffusionProfileAssetID = Vector4.zero; public uint diffusionProfileID = 0; } [Serializable] internal class LightingSettings { public bool enableShadowCasting = true; public bool enableShadowReceiving = true; public bool enableLightProbes = true; public ReflectionProbeUsage reflectionProbeEnumValue = ReflectionProbeUsage.BlendProbes; } [Serializable] internal class AdditionalSettings { public MotionVectorGenerationMode motionVectorModeEnumValue = MotionVectorGenerationMode.Object; public bool generateColliders = true; public bool generateRigidbody = true; } [Serializable] internal class LODSettings { public bool enableSmoothLODTransition = true; public bool animateCrossFading = true; public float billboardTransitionCrossFadeWidth = 0.25f; public float fadeOutWidth = 0.25f; public bool hasBillboard = false; } [Serializable] internal class PerLODSettings { public bool enableSettingOverride = false; // LOD public float height = 0.5f; // Lighting public bool castShadows = false; public bool receiveShadows = false; public bool useLightProbes = false; public ReflectionProbeUsage reflectionProbeUsage = ReflectionProbeUsage.Off; // Material public bool enableBump = false; public bool enableHue = false; public bool enableSubsurface = false; }; [Serializable] internal class MaterialInfo { public Material material = null; public string defaultName = null; public bool exported = false; } [Serializable] internal class LODMaterials { public List materials = new List(); public Dictionary> lodToMaterials = new Dictionary>(); public Dictionary matNameToIndex = new Dictionary(); public void AddLodMaterialIndex(int lod, int matIndex) { if (lodToMaterials.ContainsKey(lod)) { lodToMaterials[lod].Add(matIndex); } else { lodToMaterials.Add(lod, new List() { matIndex}); } } } [Serializable] internal class WindSettings { public bool enableWind = true; [Range(1.0f, 20.0f)] public float strenghResponse = 5.0f; [Range(1.0f, 20.0f)] public float directionResponse = 2.5f; [Range(0.0f, 1.0f)] public float randomness = 0.5f; } // Ideally, we should use 'AssetImporter.SourceAssetIdentifier', but this struct has many problems: // 1 - 'Type' is not a serializable type, so it's always equal to null in our context. // 2 - The C# struct is not matching the C++ class. // 3 - Missing the 'Serializable' attribute on top of the struct. [Serializable] internal class AssetIdentifier { public string type; public string name; public AssetIdentifier(UnityEngine.Object asset) { if (asset == null) { throw new ArgumentNullException("asset"); } this.type = asset.GetType().ToString(); this.name = asset.name; } public AssetIdentifier(Type type, string name) { if (type == null) { throw new ArgumentNullException("type"); } if (name == null) { throw new ArgumentNullException("name"); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException("The name is empty", "name"); } this.type = type.ToString(); this.name = name; } } /// /// This attribute is used as a callback to set SRP specific properties from the importer. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class MaterialSettingsCallbackAttribute : Attribute { /// /// The version of the method. /// public int MethodVersion; /// /// Initializes a new instance of the MaterialSettingsCallbackAttribute with the given method version. /// /// The given method version. public MaterialSettingsCallbackAttribute(int methodVersion) { MethodVersion = methodVersion; } [RequiredSignature] static void SignatureExample(GameObject mainObject) { throw new InvalidOperationException(); } } /// /// This attribute is used as a callback to extend the inspector by adding /// the Diffuse Profile property when the HDRP package is in use. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class DiffuseProfileCallbackAttribute : Attribute { /// /// The version of the method. /// public int MethodVersion; /// /// Initializes a new instance of the DiffuseProfileCallbackAttribute with the given method version. /// /// The given method version. public DiffuseProfileCallbackAttribute(int methodVersion) { MethodVersion = methodVersion; } [RequiredSignature] static void SignatureExample(ref SerializedProperty diffusionProfileAsset, ref SerializedProperty diffusionProfileHash) { throw new InvalidOperationException(); } } } ================================================ FILE: Editor/Mono/AssetPipeline/SpeedTreeImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Rendering; namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/SpeedTreeImporter.h")] [NativeHeader("Editor/Src/AssetPipeline/SpeedTreeImporter.bindings.h")] [NativeHeader("Runtime/Camera/ReflectionProbeTypes.h")] public partial class SpeedTreeImporter : AssetImporter { public enum MaterialLocation { External = 0, InPrefab = 1 } public extern bool hasImported { [FreeFunction(Name = "SpeedTreeImporterBindings::HasImported", HasExplicitThis = true)] get; } public extern string materialFolderPath { get; } public extern MaterialLocation materialLocation { get; set; } public extern bool isV8 { get; } public extern Shader defaultShader { get; } public extern Shader defaultBillboardShader { get; } ///////////////////////////////////////////////////////////////////////////// // Mesh properties public extern float scaleFactor { get; set; } ///////////////////////////////////////////////////////////////////////////// // Material properties public extern Color mainColor { get; set; } // The below properties (specColor and shininess) were first made obsolete in 5.4, they didn't work anyway, AND SpeedTreeImporter should rarely be scripted by anyone // because of that I would say they can be safely removed for 5.6 [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("specColor is no longer used and has been deprecated.", true)] public Color specColor { get; set; } [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("shininess is no longer used and has been deprecated.", true)] public float shininess { get; set; } public extern Color hueVariation { get; set; } public extern float alphaTestRef { get; set; } public extern bool enableBumpByDefault { get; set; } public extern bool enableHueByDefault { get; set; } public extern bool enableSubsurfaceByDefault { get; set; } ///////////////////////////////////////////////////////////////////////////// // Lighting properties public extern bool castShadowsByDefault { get; set; } public extern bool receiveShadowsByDefault { get; set; } public extern bool useLightProbesByDefault { get; set; } public extern int reflectionProbeUsagesByDefault { get; set; } ///////////////////////////////////////////////////////////////////////////// // Wind properties public static readonly string[] windQualityNames = new[] { "None", "Fastest", "Fast", "Better", "Best", "Palm" }; public extern int bestWindQuality { get; } public extern int selectedWindQuality { get; set; } ///////////////////////////////////////////////////////////////////////////// // Physics settings public extern bool generateRigidbody { get; set; } public extern bool generateColliders { get; set; } ///////////////////////////////////////////////////////////////////////////// // LOD settings public extern bool hasBillboard { [NativeName("HasBillboard")] get; } public extern bool enableSmoothLODTransition { get; set; } public extern bool animateCrossFading { get; set; } public extern float billboardTransitionCrossFadeWidth { get; set; } public extern float fadeOutWidth { get; set; } public extern bool[] enableSettingOverride { [FreeFunction(Name = "SpeedTreeImporterBindings::GetEnableSettingOverride", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetEnableSettingOverride", HasExplicitThis = true)] set; } public extern float[] LODHeights { [FreeFunction(Name = "SpeedTreeImporterBindings::GetLODHeights", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetLODHeights", HasExplicitThis = true)] set; } public extern bool[] castShadows { [FreeFunction(Name = "SpeedTreeImporterBindings::GetCastShadows", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetCastShadows", HasExplicitThis = true)] set; } public extern bool[] receiveShadows { [FreeFunction(Name = "SpeedTreeImporterBindings::GetReceiveShadows", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetReceiveShadows", HasExplicitThis = true)] set; } public extern bool[] useLightProbes { [FreeFunction(Name = "SpeedTreeImporterBindings::GetUseLightProbes", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetUseLightProbes", HasExplicitThis = true)] set; } public extern ReflectionProbeUsage[] reflectionProbeUsages { [FreeFunction(Name = "SpeedTreeImporterBindings::GetReflectionProbeUsages", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetReflectionProbeUsages", HasExplicitThis = true)] set; } public extern bool[] enableBump { [FreeFunction(Name = "SpeedTreeImporterBindings::GetEnableBump", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetEnableBump", HasExplicitThis = true)] set; } public extern bool[] enableHue { [FreeFunction(Name = "SpeedTreeImporterBindings::GetEnableHue", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetEnableHue", HasExplicitThis = true)] set; } public extern bool[] enableSubsurface { [FreeFunction(Name = "SpeedTreeImporterBindings::GetEnableSubsurface", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetEnableSubsurface", HasExplicitThis = true)] set; } public extern int[] windQualities { [FreeFunction(Name = "SpeedTreeImporterBindings::GetWindQuality", HasExplicitThis = true)] get; [NativeThrows] [FreeFunction(Name = "SpeedTreeImporterBindings::SetWindQuality", HasExplicitThis = true)] set; } ///////////////////////////////////////////////////////////////////////////// public extern void GenerateMaterials(); internal extern bool materialsShouldBeRegenerated { [NativeName("MaterialsShouldBeRegenerated")] get; } internal extern void SetMaterialVersionToCurrent(); internal extern SourceAssetIdentifier[] sourceMaterials { [FreeFunction(Name = "SpeedTreeImporterBindings::GetSourceMaterials", HasExplicitThis = true)] get; } public bool SearchAndRemapMaterials(string materialFolderPath) { bool changedMappings = false; if (materialFolderPath == null) throw new ArgumentNullException("materialFolderPath"); if (string.IsNullOrEmpty(materialFolderPath)) throw new ArgumentException(string.Format("Invalid material folder path: {0}.", materialFolderPath), "materialFolderPath"); var guids = AssetDatabase.FindAssets("t:Material", new string[] { materialFolderPath }); List> materials = new List>(); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); // ensure that we only load material assets, not embedded materials var material = AssetDatabase.LoadMainAssetAtPath(path) as Material; if (material) materials.Add(new Tuple(path, material)); } var importedMaterials = sourceMaterials; foreach (var material in materials) { var materialName = material.Item2.name; var materialFile = material.Item1; // the legacy materials have the LOD in the path, while the new materials have the LOD as part of the name var isLegacyMaterial = !materialName.Contains("LOD") && !materialName.Contains("Billboard"); var hasLOD = isLegacyMaterial && materialFile.Contains("LOD"); var lod = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(materialFile)); var importedMaterial = Array.Find(importedMaterials, x => x.name.Contains(materialName) && (!hasLOD || x.name.Contains(lod))); if (!string.IsNullOrEmpty(importedMaterial.name)) { AddRemap(importedMaterial, material.Item2); changedMappings = true; } } return changedMappings; } } class SpeedTreePostProcessor : AssetPostprocessor { private static void FixExtraTexture_sRGB(IEnumerable subAssets) { AssetDatabase.StartAssetEditing(); foreach (var subAsset in subAssets) { if (subAsset is Material) { Material m = subAsset as Material; Texture tex = m.GetTexture("_ExtraTex"); if (tex) { string texturePath = AssetDatabase.GetAssetOrScenePath(tex); TextureImporter texImporter = AssetImporter.GetAtPath(texturePath) as TextureImporter; if(texImporter) { // Multiple materials may be referencing the same ExtraTexture, therefore // we'll need to check the importer's setting and only queue a single reimport // for a given texture. if (texImporter.sRGBTexture) { texImporter.sRGBTexture = false; // extra texture does not contain color data, hence shouldn't be sRGB. texImporter.SaveAndReimport(); } } } } } AssetDatabase.StopAssetEditing(); } static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { foreach (var asset in importedAssets) { bool st8 = asset.EndsWith(".st", StringComparison.OrdinalIgnoreCase); if(st8) { SpeedTreeImporter importer = AssetImporter.GetAtPath(asset) as SpeedTreeImporter; if (importer == null) continue; // Check the external materials in case the user has extracted Dictionary externalAssets = importer.GetExternalObjectMap(); if(externalAssets != null) FixExtraTexture_sRGB(externalAssets.Values); // Check the object subassets -- updates the materials if they're embedded in the SpeedTree asset UnityEngine.Object[] subAssets = AssetDatabase.LoadAllAssetsAtPath(asset); FixExtraTexture_sRGB(subAssets); } } } } } ================================================ FILE: Editor/Mono/AssetPipeline/TextureGenerator.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using System.Runtime.InteropServices; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine.Scripting.APIUpdating; namespace UnityEditor.AssetImporters { [StructLayout(LayoutKind.Sequential)] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public struct SpriteImportData { private string m_Name; private Rect m_Rect; private SpriteAlignment m_Alignment; private Vector2 m_Pivot; private Vector4 m_Border; private string m_CustomData; private float m_TessellationDetail; private string m_SpriteID; private List m_Outline; public string name { get { return m_Name; } set { m_Name = value; } } public Rect rect { get { return m_Rect; } set { m_Rect = value; } } public SpriteAlignment alignment { get { return m_Alignment; } set { m_Alignment = value; } } public Vector2 pivot { get { return m_Pivot; } set { m_Pivot = value; } } public Vector4 border { get { return m_Border; } set { m_Border = value; } } internal string customData { get { return m_CustomData; } set { m_CustomData = value; } } public List outline { get { return m_Outline; } set { m_Outline = value; } } public float tessellationDetail {get { return m_TessellationDetail; } set { m_TessellationDetail = value; } } public string spriteID {get { return m_SpriteID; } set { m_SpriteID = value; } } } [StructLayout(LayoutKind.Sequential)] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public struct TextureGenerationOutput { [NativeName("texture")] private Texture m_Texture; [NativeName("importInspectorWarnings")] private string m_ImportInspectorWarnings; [NativeName("importWarnings")] private string[] m_ImportWarnings; [NativeName("thumbNail")] private Texture2D m_ThumbNail; [NativeName("sprites")] private Sprite[] m_Sprites; public Texture2D texture { get { return m_Texture as Texture2D; } } public Texture output { get { return m_Texture; } } public string importInspectorWarnings { get { return m_ImportInspectorWarnings; } } public string[] importWarnings { get { return m_ImportWarnings; } } public Texture2D thumbNail { get { return m_ThumbNail; } } public Sprite[] sprites { get { return m_Sprites; } } } [StructLayout(LayoutKind.Sequential)] [NativeAsStruct] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public class SourceTextureInformation { [NativeName("width")] private int m_Width; [NativeName("height")] private int m_Height; [NativeName("sourceContainsAlpha")] private bool m_SourceContainsAlpha; [NativeName("sourceWasHDR")] private bool m_SourceWasHDR; public int width { get { return m_Width; } set {m_Width = value; } } public int height { get { return m_Height; } set {m_Height = value; } } public bool containsAlpha { get { return m_SourceContainsAlpha; } set {m_SourceContainsAlpha = value; } } public bool hdr { get { return m_SourceWasHDR; } set {m_SourceWasHDR = value; } } } [StructLayout(LayoutKind.Sequential)] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public struct TextureGenerationSettings { [NativeName("assetPath")] private string m_AssetPath; [NativeName("qualifyForSpritePacking")] private bool m_QualifyForSpritePacking; [NativeName("enablePostProcessor")] private bool m_EnablePostProcessor; [NativeName("tiSettings")] private TextureImporterSettings m_Settings; [NativeName("platformSettings")] private TextureImporterPlatformSettings m_PlatformSettings; [NativeName("sourceTextureInformation")] private SourceTextureInformation m_SourceTextureInformation; [NativeName("spriteSheetData")] private SpriteImportData[] m_SpriteImportData; [NativeName("spritePackingTag")] private string m_SpritePackingTag; [NativeName("secondarySpriteTextures")] private SecondarySpriteTexture[] m_SecondarySpriteTextures; public TextureGenerationSettings(TextureImporterType type) { m_EnablePostProcessor = true; m_AssetPath = ""; m_QualifyForSpritePacking = false; m_SpritePackingTag = ""; m_SpriteImportData = null; m_SecondarySpriteTextures = null; m_SourceTextureInformation = new SourceTextureInformation(); m_SourceTextureInformation.width = m_SourceTextureInformation.height = 0; m_SourceTextureInformation.containsAlpha = false; m_SourceTextureInformation.hdr = false; m_PlatformSettings = new TextureImporterPlatformSettings(); m_PlatformSettings.overridden = false; m_PlatformSettings.format = TextureImporterFormat.Automatic; m_PlatformSettings.maxTextureSize = 2048; m_PlatformSettings.allowsAlphaSplitting = false; m_PlatformSettings.resizeAlgorithm = TextureResizeAlgorithm.Mitchell; m_PlatformSettings.compressionQuality = (int)TextureCompressionQuality.Normal; m_PlatformSettings.crunchedCompression = false; m_PlatformSettings.name = TextureImporter.defaultPlatformName; // Values from TextureImporterSettings native constructor m_Settings = new TextureImporterSettings(); m_Settings.textureType = type; m_Settings.textureShape = TextureImporterShape.Texture2D; m_Settings.convertToNormalMap = false; m_Settings.mipmapEnabled = true; m_Settings.mipmapFilter = TextureImporterMipFilter.BoxFilter; m_Settings.sRGBTexture = true; m_Settings.borderMipmap = false; m_Settings.mipMapsPreserveCoverage = false; m_Settings.alphaTestReferenceValue = 0.5f; m_Settings.readable = false; m_Settings.fadeOut = false; m_Settings.mipmapFadeDistanceStart = 1; m_Settings.mipmapFadeDistanceEnd = 3; m_Settings.heightmapScale = 0.25f; m_Settings.normalMapFilter = TextureImporterNormalFilter.Standard; m_Settings.flipGreenChannel = false; m_Settings.swizzleRaw = 0x03020100; // R,G,B,A m_Settings.cubemapConvolution = 0; m_Settings.generateCubemap = TextureImporterGenerateCubemap.AutoCubemap; m_Settings.seamlessCubemap = false; m_Settings.npotScale = TextureImporterNPOTScale.ToNearest; m_Settings.spriteMode = (int)SpriteImportMode.Single; m_Settings.spriteExtrude = 1; m_Settings.spriteMeshType = SpriteMeshType.Tight; m_Settings.spriteAlignment = (int)SpriteAlignment.Center; m_Settings.spritePivot = Vector2.one * 0.5f; m_Settings.spritePixelsPerUnit = 100; m_Settings.spriteBorder = Vector4.zero; m_Settings.alphaSource = TextureImporterAlphaSource.FromInput; m_Settings.alphaIsTransparency = false; m_Settings.spriteTessellationDetail = -1; m_Settings.wrapMode = m_Settings.wrapModeU = m_Settings.wrapModeV = m_Settings.wrapModeW = TextureWrapMode.Repeat; // From TextureImporterSettings::ApplyTextureType switch (type) { case TextureImporterType.Default: m_Settings.sRGBTexture = true; m_Settings.mipmapEnabled = true; break; case TextureImporterType.NormalMap: m_Settings.sRGBTexture = false; break; case TextureImporterType.GUI: m_Settings.sRGBTexture = false; m_Settings.mipmapEnabled = false; m_Settings.alphaIsTransparency = true; m_Settings.npotScale = TextureImporterNPOTScale.None; m_Settings.aniso = 1; m_Settings.wrapMode = m_Settings.wrapModeU = m_Settings.wrapModeV = m_Settings.wrapModeW = TextureWrapMode.Clamp; break; case TextureImporterType.Sprite: m_Settings.npotScale = TextureImporterNPOTScale.None; m_Settings.alphaIsTransparency = true; m_Settings.mipmapEnabled = false; m_Settings.sRGBTexture = true; m_Settings.wrapMode = m_Settings.wrapModeU = m_Settings.wrapModeV = m_Settings.wrapModeW = TextureWrapMode.Clamp; m_Settings.alphaSource = TextureImporterAlphaSource.FromInput; break; case TextureImporterType.Cursor: m_Settings.readable = true; m_Settings.alphaIsTransparency = true; m_Settings.mipmapEnabled = false; m_Settings.npotScale = TextureImporterNPOTScale.None; m_Settings.aniso = 1; m_Settings.wrapMode = m_Settings.wrapModeU = m_Settings.wrapModeV = m_Settings.wrapModeW = TextureWrapMode.Clamp; break; case TextureImporterType.Cookie: m_Settings.borderMipmap = true; m_Settings.wrapMode = m_Settings.wrapModeU = m_Settings.wrapModeV = m_Settings.wrapModeW = TextureWrapMode.Clamp; m_Settings.aniso = 0; break; case TextureImporterType.Lightmap: m_Settings.sRGBTexture = true; m_Settings.npotScale = TextureImporterNPOTScale.ToNearest; m_Settings.alphaIsTransparency = false; m_Settings.alphaSource = TextureImporterAlphaSource.None; break; case TextureImporterType.DirectionalLightmap: m_Settings.sRGBTexture = false; m_Settings.npotScale = TextureImporterNPOTScale.ToNearest; m_Settings.alphaIsTransparency = false; m_Settings.alphaSource = TextureImporterAlphaSource.None; break; case TextureImporterType.Shadowmask: m_Settings.sRGBTexture = false; m_Settings.npotScale = TextureImporterNPOTScale.ToNearest; m_Settings.alphaIsTransparency = false; m_Settings.alphaSource = TextureImporterAlphaSource.None; break; case TextureImporterType.SingleChannel: m_Settings.sRGBTexture = false; break; } } public string assetPath { get { return m_AssetPath; } set { m_AssetPath = value; } } public bool qualifyForSpritePacking { get { return m_QualifyForSpritePacking; } set { m_QualifyForSpritePacking = value; } } public bool enablePostProcessor { get { return m_EnablePostProcessor; } set { m_EnablePostProcessor = value; } } public TextureImporterSettings textureImporterSettings { get { return m_Settings; } set { m_Settings = value; } } public TextureImporterPlatformSettings platformSettings { get { return m_PlatformSettings; } set { m_PlatformSettings = value; } } public SourceTextureInformation sourceTextureInformation { get { return m_SourceTextureInformation; } set { m_SourceTextureInformation = value; } } public SpriteImportData[] spriteImportData { get { return m_SpriteImportData; } set { m_SpriteImportData = value; } } public string spritePackingTag { get { return m_SpritePackingTag; } set { m_SpritePackingTag = value; } } public SecondarySpriteTexture[] secondarySpriteTextures { get { return m_SecondarySpriteTextures; } set { m_SecondarySpriteTextures = value; } } } [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureGenerator.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterTypes.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.bindings.h")] [NativeHeader("Runtime/Serialize/BuildTarget.h")] [MovedFrom("UnityEditor.Experimental.AssetImporters")] public static unsafe class TextureGenerator { public static TextureGenerationOutput GenerateTexture(TextureGenerationSettings settings, NativeArray colorBuffer) { return GenerateTextureImpl(settings, colorBuffer.GetUnsafeReadOnlyPtr(), colorBuffer.Length * UnsafeUtility.SizeOf(), 4); } public static TextureGenerationOutput GenerateTexture(TextureGenerationSettings settings, NativeArray colorBuffer) { return GenerateTextureImpl(settings, colorBuffer.GetUnsafeReadOnlyPtr(), colorBuffer.Length * UnsafeUtility.SizeOf(), 16); } [NativeThrows] [NativeMethod("GenerateTextureScripting")] extern static unsafe TextureGenerationOutput GenerateTextureImpl(TextureGenerationSettings settings, void* colorBuffer, int colorBufferLength, int bytesPerPixel); } } ================================================ FILE: Editor/Mono/AssetPipeline/TextureImporter.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections.Generic; using System.ComponentModel; using UnityEditor.Build; using UnityEditor.AssetImporters; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Scripting; namespace UnityEditor { // Texture importer lets you modify [[Texture2D]] import settings from editor scripts. [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.deprecated.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterUtils.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterPlatformSettingsUtils.h")] [NativeHeader("Editor/Src/EditorUserBuildSettings.h")] public sealed partial class TextureImporter : AssetImporter { [FreeFunction] internal static extern string GetTexturePlatformSerializationName(string platformName); [Obsolete("textureFormat is no longer accessible at the TextureImporter level. For old 'simple' formats use the textureCompression property for the equivalent automatic choice (Uncompressed for TrueColor, Compressed and HQCommpressed for 16 bits). For platform specific formats use the [[PlatformTextureSettings]] API. Using this setter will setup various parameters to match the new automatic system as well as possible. Getter will return the last value set.")] public extern TextureImporterFormat textureFormat { [FreeFunction("GetTextureFormat", HasExplicitThis = true)] get; [FreeFunction("SetTextureFormat", HasExplicitThis = true)] set; } [NativeProperty("TextureImporter::s_DefaultPlatformName", true, TargetType.Field)] internal static extern string defaultPlatformName { get; } public extern int maxTextureSize { get; set; } [NativeProperty("TextureCompressionQuality", false, TargetType.Function)] public extern int compressionQuality { get; set; } public extern bool crunchedCompression { get; set; } public extern bool allowAlphaSplitting { get; set; } public extern AndroidETC2FallbackOverride androidETC2FallbackOverride { get; set; } public extern TextureImporterCompression textureCompression { get; set; } public extern TextureImporterAlphaSource alphaSource { get; set; } internal extern bool forceMaximumCompressionQuality_BC6H_BC7 { get; set; } // Generate alpha channel from intensity? [Obsolete("Use UnityEditor.TextureImporter.alphaSource instead.")] public bool grayscaleToAlpha { get { return alphaSource == TextureImporterAlphaSource.FromGrayScale; } set { if (value) alphaSource = TextureImporterAlphaSource.FromGrayScale; else alphaSource = TextureImporterAlphaSource.FromInput; } } // TODO: make this use struct for possible future expansion // Whether the texture allows alpha splitting for compressions like ETC1 [Obsolete("Use UnityEditor.TextureImporter.GetPlatformTextureSettings() instead.")] [NativeMethod(HasExplicitThis = true)] public extern bool GetAllowsAlphaSplitting(); [Obsolete("Use UnityEditor.TextureImporter.SetPlatformTextureSettings() instead.")] [NativeMethod(HasExplicitThis = true)] public extern void SetAllowsAlphaSplitting(bool flag); public bool GetPlatformTextureSettings(string platform, out int maxTextureSize, out TextureImporterFormat textureFormat, out int compressionQuality, out bool etc1AlphaSplitEnabled) { TextureImporterPlatformSettings settings = GetPlatformTextureSettings(platform); maxTextureSize = settings.maxTextureSize; textureFormat = settings.format; compressionQuality = settings.compressionQuality; etc1AlphaSplitEnabled = settings.allowsAlphaSplitting; return settings.overridden; } public bool GetPlatformTextureSettings(string platform, out int maxTextureSize, out TextureImporterFormat textureFormat, out int compressionQuality) { bool etc1AlphaSplitEnabled = false; return GetPlatformTextureSettings(platform, out maxTextureSize, out textureFormat, out compressionQuality, out etc1AlphaSplitEnabled); } public bool GetPlatformTextureSettings(string platform, out int maxTextureSize, out TextureImporterFormat textureFormat) { int compressionQuality = 0; bool etc1AlphaSplitEnabled = false; return GetPlatformTextureSettings(platform, out maxTextureSize, out textureFormat, out compressionQuality, out etc1AlphaSplitEnabled); } // C++ implementation will return default platform if the requested platform is not valid. // See "Editor/Mono/BuildPipeline/BuildPlatform.cs" -> "GetValidPlatformNames" for more // information regarding what is considered to be a valid platform. [NativeName("GetPlatformTextureSettings")] private extern TextureImporterPlatformSettings GetPlatformTextureSetting_Internal(string platform); // Read texture settings for specified platform into [[TextureImporterPlatformSettings]] class. // public API will always return a valid TextureImporterPlatformSettings, creating it based on the default one if it did not exist. public TextureImporterPlatformSettings GetPlatformTextureSettings(string platform) { platform = GetTexturePlatformSerializationName(platform); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". TextureImporterPlatformSettings dest = GetPlatformTextureSetting_Internal(platform); if (platform != dest.name) { dest.name = platform; dest.overridden = false; } return dest; } public TextureImporterPlatformSettings GetDefaultPlatformTextureSettings() { return GetPlatformTextureSettings(TextureImporterInspector.s_DefaultPlatformName); } public TextureImporterFormat GetAutomaticFormat(string platform) { platform = GetTexturePlatformSerializationName(platform); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". TextureImporterSettings settings = new TextureImporterSettings(); ReadTextureSettings(settings); TextureImporterPlatformSettings platformSettings = GetPlatformTextureSettings(platform); BuildTarget buildTarget = BuildPipeline.GetBuildTargetByName(platform); if (buildTarget != BuildTarget.NoTarget) { return DefaultFormatFromTextureParameters(settings, !platformSettings.overridden ? GetDefaultPlatformTextureSettings() : platformSettings, DoesSourceTextureHaveAlpha(), IsSourceTextureHDR(), buildTarget); // Regarding the "GetDefaultPlatformTextureSettings" call: in case 1281084, we made it so that platform settings stop automatically // resetting to the default platform's settings when the platform override is disabled. This introduced a regression where // "GetAutomaticFormat" would not return the actual format used by platforms with a disabled override, (as in, the one indicated in // the default platform's settings) which is why we pass in the default platform's settings instead. } return TextureImporterFormat.Automatic; } [Obsolete("Use UnityEditor.TextureImporter.SetPlatformTextureSettings(TextureImporterPlatformSettings) instead.")] public void SetPlatformTextureSettings(string platform, int maxTextureSize, TextureImporterFormat textureFormat, int compressionQuality, bool allowsAlphaSplit) { TextureImporterPlatformSettings dest = GetPlatformTextureSettings(platform); dest.overridden = true; dest.maxTextureSize = maxTextureSize; dest.format = textureFormat; dest.compressionQuality = compressionQuality; dest.allowsAlphaSplitting = allowsAlphaSplit; SetPlatformTextureSettings(dest); } [Obsolete("Use UnityEditor.TextureImporter.SetPlatformTextureSettings(TextureImporterPlatformSettings) instead.")] public void SetPlatformTextureSettings(string platform, int maxTextureSize, TextureImporterFormat textureFormat) { SetPlatformTextureSettings(platform, maxTextureSize, textureFormat, false); } [Obsolete("Use UnityEditor.TextureImporter.SetPlatformTextureSettings(TextureImporterPlatformSettings) instead.")] public void SetPlatformTextureSettings(string platform, int maxTextureSize, TextureImporterFormat textureFormat, [DefaultValue(false)] bool allowsAlphaSplit) { TextureImporterPlatformSettings dest = GetPlatformTextureSettings(platform); dest.overridden = true; dest.maxTextureSize = maxTextureSize; dest.format = textureFormat; dest.allowsAlphaSplitting = allowsAlphaSplit; SetPlatformTextureSettings(dest); } [NativeName("SetPlatformTextureSettings")] private extern void SetPlatformTextureSettings_Internal(TextureImporterPlatformSettings platformSettings); // Set specific target platform settings public void SetPlatformTextureSettings(TextureImporterPlatformSettings platformSettings) { platformSettings.name = GetTexturePlatformSerializationName(platformSettings.name); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". SetPlatformTextureSettings_Internal(platformSettings); } // Clear specific target platform settings [NativeName("ClearPlatformTextureSettings")] private extern void ClearPlatformTextureSettings_Internal(string platform); public void ClearPlatformTextureSettings(string platform) { platform = GetTexturePlatformSerializationName(platform); // String may refer to a platform group: if != "Standalone", ensure it refers to a platform instead. E.g.: "iOS", not "iPhone". ClearPlatformTextureSettings_Internal(platform); } [FreeFunction] internal static extern TextureImporterFormat DefaultFormatFromTextureParameters([NotNull] TextureImporterSettings settings, TextureImporterPlatformSettings platformSettings, bool doesTextureContainAlpha, bool sourceWasHDR, BuildTarget destinationPlatform); [FreeFunction] internal static extern TextureImporterFormat[] RecommendedFormatsFromTextureTypeAndPlatform(TextureImporterType textureType, BuildTarget destinationPlatform); [RequiredByNativeCode] public static bool IsPlatformTextureFormatValid(TextureImporterType textureType, BuildTarget target, TextureImporterFormat currentFormat) { if (currentFormat != TextureImporterFormat.Automatic) { int[] formatValues; string[] formatStrings; TextureImportValidFormats.GetPlatformTextureFormatValuesAndStrings(textureType, target, out formatValues, out formatStrings); return Array.Exists(formatValues, i => i == (int)currentFormat); } return true; } [RequiredByNativeCode] public static bool IsDefaultPlatformTextureFormatValid(TextureImporterType textureType, TextureImporterFormat currentFormat) { if (currentFormat != TextureImporterFormat.Automatic) { int[] formatValues; string[] formatStrings; TextureImportValidFormats.GetDefaultTextureFormatValuesAndStrings(textureType, out formatValues, out formatStrings); return Array.Exists(formatValues, i => i == (int)currentFormat); } return true; } // Cubemap generation mode. public extern TextureImporterGenerateCubemap generateCubemap { get; set; } // Scaling mode for non power of two textures. [NativeProperty("NPOTScale")] public extern TextureImporterNPOTScale npotScale { get; set; } // Is texture data readable from scripts. public extern bool isReadable { get; set; } // Is texture data able to be streamed by mip level. [NativeConditional("ENABLE_TEXTURE_STREAMING")] public extern bool streamingMipmaps { get; set; } // This texture's mipmap streaming priority. [NativeConditional("ENABLE_TEXTURE_STREAMING")] public extern int streamingMipmapsPriority { get; set; } // Is texture VT only [NativeConditional("ENABLE_VIRTUALTEXTURING")] [NativeProperty("VTOnly")] public extern bool vtOnly { get; set; } public extern bool ignoreMipmapLimit { get; set; } public extern string mipmapLimitGroupName { get; set; } // Generate mip maps for the texture? public extern bool mipmapEnabled { get; set; } // Keep texture borders the same when generating mipmaps? public extern bool borderMipmap { get; set; } // When in linear rendering should this texture be sampled with hardware gamma correction (sRGB) or without (linear)? [NativeProperty("sRGBTexture")] public extern bool sRGBTexture { get; set; } // Should alpha MIP maps preserve coverage during the alpha test? public extern bool mipMapsPreserveCoverage { get; set; } // Alpha test reference value which determines the coverage. public extern float alphaTestReferenceValue { get; set; } // Mipmap filtering mode. [NativeProperty("MipmapMode")] public extern TextureImporterMipFilter mipmapFilter { get; set; } // Fade out mip levels to gray color? public extern bool fadeout { get; set; } // Mip level where texture begins to fade out. public extern int mipmapFadeDistanceStart { get; set; } // Mip level where texture is faded out completely. public extern int mipmapFadeDistanceEnd { get; set; } // Should mip maps be generated with gamma correction? [Obsolete("generateMipsInLinearSpace Property deprecated. Mipmaps are always generated in linear space.")] public bool generateMipsInLinearSpace { get { return true; } set {} } [Obsolete("correctGamma Property deprecated. Mipmaps are always generated in linear space.")] public bool correctGamma { get { return true; } set {} } [Obsolete("linearTexture Property deprecated. Use sRGBTexture instead.")] public bool linearTexture { get { return !sRGBTexture; } set { sRGBTexture = !value; } } [Obsolete("normalmap Property deprecated. Check [[TextureImporterSettings.textureType]] instead. Getter will work as expected. Setter will set textureType to NormalMap if true, nothing otherwise.")] public bool normalmap { get { return textureType == TextureImporterType.NormalMap; } set { if (value) textureType = TextureImporterType.NormalMap; else textureType = TextureImporterType.Default; } } [Obsolete("lightmap Property deprecated. Check [[TextureImporterSettings.textureType]] instead. Getter will work as expected. Setter will set textureType to Lightmap if true, nothing otherwise.")] public bool lightmap { get { return textureType == TextureImporterType.Lightmap; } set { if (value) textureType = TextureImporterType.Lightmap; else textureType = TextureImporterType.Default; } } public extern bool convertToNormalmap { get; set; } public extern TextureImporterNormalFilter normalmapFilter { get; set; } public extern bool flipGreenChannel { get; set; } extern uint swizzle { get; set; } public TextureImporterSwizzle swizzleR { get => (TextureImporterSwizzle)(swizzle & 0xFF); set => swizzle = (swizzle & 0xFFFFFF00) | (uint)value; } public TextureImporterSwizzle swizzleG { get => (TextureImporterSwizzle)((swizzle >> 8) & 0xFF); set => swizzle = (swizzle & 0xFFFF00FF) | ((uint)value<<8); } public TextureImporterSwizzle swizzleB { get => (TextureImporterSwizzle)((swizzle >> 16) & 0xFF); set => swizzle = (swizzle & 0xFF00FFFF) | ((uint)value<<16); } public TextureImporterSwizzle swizzleA { get => (TextureImporterSwizzle)((swizzle >> 24) & 0xFF); set => swizzle = (swizzle & 0x00FFFFFF) | ((uint)value<<24); } [NativeProperty("NormalmapHeightScale")] public extern float heightmapScale { get; set; } // Anisotropic filtering level of the texture. public extern int anisoLevel { get; set; } // Filtering mode of the texture. public extern FilterMode filterMode { get; set; } // note: wrapMode getter returns U wrapping axis public extern TextureWrapMode wrapMode { [NativeName("GetWrapU")] get; [NativeName("SetWrapUVW")] set; } [NativeProperty("WrapU")] public extern TextureWrapMode wrapModeU { get; set; } [NativeProperty("WrapV")] public extern TextureWrapMode wrapModeV { get; set; } [NativeProperty("WrapW")] public extern TextureWrapMode wrapModeW { get; set; } // Mip map bias of the texture. public extern float mipMapBias { get; set; } // Use alpha channel as transparency. Removes white borders from transparent textures public extern bool alphaIsTransparency { get; set; } public extern bool qualifiesForSpritePacking { get; } [NativeProperty("SpriteMode")] public extern SpriteImportMode spriteImportMode { get; set; } [NativeProperty("SpriteMetaDatas")] [Obsolete("Support for accessing sprite meta data through spritesheet has been removed. Please use the UnityEditor.U2D.Sprites.ISpriteEditorDataProvider interface instead.")] public extern SpriteMetaData[] spritesheet { get; set; } public extern SecondarySpriteTexture[] secondarySpriteTextures { get; set; } [Obsolete("Support for packing sprites through spritePackingTag has been removed. Please use SpriteAtlas instead.")] public string spritePackingTag { get { return ""; } set { } } // The number of pixels in one unit. Note: The C++ side still uses the name pixelsToUnits which is misleading, // but has not been changed yet to minimize merge conflicts. [NativeProperty("SpritePixelsToUnits")] public extern float spritePixelsPerUnit { get; set; } [System.Obsolete("Use spritePixelsPerUnit property instead.")] public extern float spritePixelsToUnits { get; set; } public extern Vector2 spritePivot { get; set; } public extern Vector4 spriteBorder { get; set; } internal void GetWidthAndHeight(ref int width, ref int height) { var info = GetSourceTextureInformation(); width = info.width; height = info.height; } public void GetSourceTextureWidthAndHeight(out int width, out int height) { var info = GetSourceTextureInformation(); if (info.width == -1) throw new InvalidOperationException("The texture has not yet finished importing. This most likely means this method was called in an AssetPostprocessor.OnPreprocessAsset callback."); width = info.width; height = info.height; } internal bool IsSourceTextureHDR() { return GetSourceTextureInformation().hdr; } private extern SourceTextureInformation GetSourceTextureInformation(); [FreeFunction("IsCompressedETCTextureFormat")] internal static extern bool IsTextureFormatETC1Compression(TextureFormat fmt); [FreeFunction("IsBuildTargetETC")] internal static extern bool IsETC1SupportedByBuildTarget(BuildTarget target); // Does textures source image have alpha channel. public bool DoesSourceTextureHaveAlpha() { var info = GetSourceTextureInformation(); if (info.width == -1) throw new ArgumentException("May only be called in OnPostProcessTexture"); return info.containsAlpha; } // Does textures source image have RGB channels. [System.Obsolete("DoesSourceTextureHaveColor always returns true in Unity.")] public bool DoesSourceTextureHaveColor() { return true; } // Which type of texture are we dealing with here public extern TextureImporterType textureType { get; set; } public extern TextureImporterShape textureShape { get; set; } // Read texture settings into [[TextureImporterSettings]] class. public void ReadTextureSettings(TextureImporterSettings dest) { settings.CopyTo(dest); } // Set texture importers settings from [[TextureImporterSettings]] class. public void SetTextureSettings(TextureImporterSettings src) { ValidateAndCorrectTextureImporterSettings(src); settings = src; } private void ValidateAndCorrectTextureImporterSettings(TextureImporterSettings m_Settings) { switch (m_Settings.textureType) { case TextureImporterType.Sprite: m_Settings.npotScale = ValidateAndCorrectSetting(m_Settings.npotScale, TextureImporterNPOTScale.None, nameof(m_Settings.npotScale)); break; } } private T ValidateAndCorrectSetting(T actual, T expected, string settingName) { if (!actual.Equals(expected)) { Debug.LogWarning($"You cannot set {settingName} to {actual} for this texture type. It has been reset to {expected}."); return expected; } return actual; } private extern TextureImporterSettings settings { get; set; } [NativeName("GetImportInspectorWarning")] internal extern string GetImportWarnings(); public extern void ReadTextureImportInstructions(BuildTarget target, out TextureFormat desiredFormat, out ColorSpace colorSpace, out int compressionQuality); internal extern bool textureStillNeedsToBeCompressed { [NativeName("DoesTextureStillNeedToBeCompressed")] get; } // This is pure backward compatibility codepath. It can be removed when we decide that the time has come internal extern bool removeMatte { get; set; } public extern bool ignorePngGamma { get; set; } // This is for remapping Sprite that are renamed. extern internal bool GetNameFromInternalIDMap(long id, ref string name); [NativeName("GetSpriteMetaDatas")] internal extern SpriteMetaData[] GetSpriteMetaDatas(); } } ================================================ FILE: Editor/Mono/AssetPipeline/TextureImporterEnums.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.ComponentModel; using UnityEngine.Scripting; namespace UnityEditor { // Lightmap format of a [[Texture2D|texture]]. internal enum TextureUsageMode { // Not a lightmap. Default = 0, // Range [0;2] packed to [0;1] with loss of precision. BakedLightmapDoubleLDR = 1, // Range [0;kLightmapRGBMMax] packed to [0;1] with multiplier stored in the alpha channel. BakedLightmapRGBM = 2, // Compressed DXT5 normal map NormalmapDXT5nm = 3, // Plain RGB normal map NormalmapPlain = 4, RGBMEncoded = 5, // Texture is always padded if NPOT and on low-end hardware AlwaysPadded = 6, DoubleLDR = 7, // Baked lightmap without any encoding BakedLightmapFullHDR = 8, RealtimeLightmapRGBM = 9, NormalmapASTCnm = 10, SingleChannelRed = 11, SingleChannelAlpha = 12, } // Imported texture format for [[TextureImporter]]. public enum TextureImporterFormat { Automatic = -1, // Choose a compressed format automatically. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member TextureImporterFormat.AutomaticCompressed is obsolete. Use the TextureImporter.textureCompression property instead.", true)] AutomaticCompressed = -1, // Choose a 16 bit format automatically. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member TextureImporterFormat.Automatic16bit is obsolete. Use TextureImporter.textureCompression property instead.", true)] Automatic16bit = -2, // Choose a Truecolor format automatically. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member TextureImporterFormat.AutomaticTruecolor is obsolete. Use the TextureImporter.textureCompression property instead.", true)] AutomaticTruecolor = -3, // Choose a Crunched format automatically. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member TextureImporterFormat.AutomaticCrunched is obsolete. Use the TextureImporter.crunchedCompression property instead.", true)] AutomaticCrunched = -5, // Choose an HDR format automatically. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member TextureImporterFormat.AutomaticHDR is obsolete. HDR is handled automatically now.", true)] AutomaticHDR = -6, // Choose a compresssed HDR format automatically. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member TextureImporterFormat.AutomaticCompressedHDR is obsolete. HDR is handled automatically now.", true)] AutomaticCompressedHDR = -7, // DXT1 compressed texture format. DXT1 = 10, // DXT5 compressed texture format. DXT5 = 12, // RGB 16 bit texture format. RGB16 = 7, // RGB 24 bit texture format. RGB24 = 3, // Alpha 8 bit texture format. // RGBA 32 bit texture format. Alpha8 = 1, // Red 16 bit texture format. R16 = 9, // Red 8 bit texture format. R8 = 63, // RG 16 bit texture format. RG16 = 62, // RGBA 16 bit texture format. ARGB16 = 2, // RGBA 32 bit texture format. RGBA32 = 4, // ARGB 32 bit texture format. ARGB32 = 5, // RGBA 16 bit (4444) texture format. RGBA16 = 13, // R 16 bit texture format. RHalf = 15, // RG 32 bit texture format. RGHalf = 16, // RGBA 64 bit texture format. RGBAHalf = 17, // R 32 bit texture format. RFloat = 18, // RG 64 bit texture format. RGFloat = 19, // RGBA 128 bit texture format. RGBAFloat = 20, // RGB 32 bit packed float format. RGB9E5 = 22, // R BC4 compressed texture format. BC4 = 26, // RG BC5 compressed texture format. BC5 = 27, // HDR RGB BC6 compressed texture format. BC6H = 24, // RGBA BC7 compressed texture format. BC7 = 25, // DXT1 crunched texture format. DXT1Crunched = 28, // DXT5 crunched texture format. DXT5Crunched = 29, // PowerVR (iPhone) 2 bits/pixel compressed color texture format. [System.Obsolete("Texture compression format PVRTC has been deprecated and will be removed in a future release")] [EditorBrowsable(EditorBrowsableState.Never)] PVRTC_RGB2 = 30, // PowerVR (iPhone) 2 bits/pixel compressed with alpha channel texture format. [System.Obsolete("Texture compression format PVRTC has been deprecated and will be removed in a future release")] [EditorBrowsable(EditorBrowsableState.Never)] PVRTC_RGBA2 = 31, // PowerVR (iPhone) 4 bits/pixel compressed color texture format. [System.Obsolete("Texture compression format PVRTC has been deprecated and will be removed in a future release")] [EditorBrowsable(EditorBrowsableState.Never)] PVRTC_RGB4 = 32, // PowerVR (iPhone) 4 bits/pixel compressed with alpha channel texture format. [System.Obsolete("Texture compression format PVRTC has been deprecated and will be removed in a future release")] [EditorBrowsable(EditorBrowsableState.Never)] PVRTC_RGBA4 = 33, // ETC (GLES2.0) 4 bits/pixel compressed RGB texture format. ETC_RGB4 = 34, // ATC (Android) 4 bits/pixel compressed RGB texture format. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ATC_RGB4 is obsolete. Use ETC_RGB4 (UnityUpgradable) -> ETC_RGB4", true)] ATC_RGB4 = 35, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ATC_RGBA8 is obsolete. Use ETC2_RGBA8 (UnityUpgradable) -> ETC2_RGBA8", true)] ATC_RGBA8 = 36, // EAC 4 bits/pixel compressed 16-bit R texture format EAC_R = 41, // EAC 4 bits/pixel compressed 16-bit signed R texture format EAC_R_SIGNED = 42, // EAC 8 bits/pixel compressed 16-bit RG texture format EAC_RG = 43, // EAC 8 bits/pixel compressed 16-bit signed RG texture format EAC_RG_SIGNED = 44, // ETC2 (GLES3.0) 4 bits/pixel compressed RGB texture format. ETC2_RGB4 = 45, // ETC2 (GLES3.0) 4 bits/pixel compressed RGB + 1-bit alpha texture format. ETC2_RGB4_PUNCHTHROUGH_ALPHA = 46, // ETC2 (GLES3.0) 8 bits/pixel compressed RGBA texture format. ETC2_RGBA8 = 47, // ASTC uses 128bit block of varying sizes (we use only square blocks). It does not distinguish RGB/RGBA ASTC_4x4 = 48, ASTC_5x5 = 49, ASTC_6x6 = 50, ASTC_8x8 = 51, ASTC_10x10 = 52, ASTC_12x12 = 53, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGB_4x4 is obsolete. Use ASTC_4x4 (UnityUpgradable) -> ASTC_4x4", true)] ASTC_RGB_4x4 = -48, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGB_5x5 is obsolete. Use ASTC_5x5 (UnityUpgradable) -> ASTC_5x5", true)] ASTC_RGB_5x5 = -49, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGB_6x6 is obsolete. Use ASTC_6x6 (UnityUpgradable) -> ASTC_6x6", true)] ASTC_RGB_6x6 = -50, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGB_8x8 is obsolete. Use ASTC_8x8 (UnityUpgradable) -> ASTC_8x8", true)] ASTC_RGB_8x8 = -51, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGB_10x10 is obsolete. Use ASTC_10x10 (UnityUpgradable) -> ASTC_10x10", true)] ASTC_RGB_10x10 = -52, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGB_12x12 is obsolete. Use ASTC_12x12 (UnityUpgradable) -> ASTC_12x12", true)] ASTC_RGB_12x12 = -53, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGBA_4x4 is obsolete. Use ASTC_4x4 (UnityUpgradable) -> ASTC_4x4", true)] ASTC_RGBA_4x4 = -54, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGBA_5x5 is obsolete. Use ASTC_5x5 (UnityUpgradable) -> ASTC_5x5", true)] ASTC_RGBA_5x5 = -55, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGBA_6x6 is obsolete. Use ASTC_6x6 (UnityUpgradable) -> ASTC_6x6", true)] ASTC_RGBA_6x6 = -56, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGBA_8x8 is obsolete. Use ASTC_8x8 (UnityUpgradable) -> ASTC_8x8", true)] ASTC_RGBA_8x8 = -57, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGBA_10x10 is obsolete. Use ASTC_10x10 (UnityUpgradable) -> ASTC_10x10", true)] ASTC_RGBA_10x10 = -58, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ASTC_RGBA_12x12 is obsolete. Use ASTC_12x12 (UnityUpgradable) -> ASTC_12x12", true)] ASTC_RGBA_12x12 = -59, // Nintendo 3DS-flavoured ETC [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ETC_RGB4_3DS is obsolete. Nintendo 3DS is no longer supported.", true)] ETC_RGB4_3DS = -60, [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [System.Obsolete("Enum member ETC_RGBA8_3DS is obsolete. Nintendo 3DS is no longer supported.", true)] ETC_RGBA8_3DS = -61, // ETC1 crunched texture format. ETC_RGB4Crunched = 64, // ETC2_RGBA8 crunched texture format. ETC2_RGBA8Crunched = 65, // ASTC (block size 4x4) compressed HDR RGB(A) texture format. ASTC_HDR_4x4 = 66, // ASTC (block size 5x5) compressed HDR RGB(A) texture format. ASTC_HDR_5x5 = 67, // ASTC (block size 4x6x6) compressed HDR RGB(A) texture format. ASTC_HDR_6x6 = 68, // ASTC (block size 8x8) compressed HDR RGB(A) texture format. ASTC_HDR_8x8 = 69, // ASTC (block size 10x10) compressed HDR RGB(A) texture format. ASTC_HDR_10x10 = 70, // ASTC (block size 12x12) compressed HDR RGB(A) texture format. ASTC_HDR_12x12 = 71, RG32 = 72, RGB48 = 73, RGBA64 = 74, R8_SIGNED = 75, RG16_SIGNED = 76, RGB24_SIGNED = 77, RGBA32_SIGNED = 78, R16_SIGNED = 79, RG32_SIGNED = 80, RGB48_SIGNED = 81, RGBA64_SIGNED = 82, } // Mip map filter for [[TextureImporter]]. public enum TextureImporterMipFilter { // Box mipmap filter. BoxFilter = 0, // Kaiser mipmap filter. KaiserFilter = 1, } // Cubemap generation mode for [[TextureImporter]]. public enum TextureImporterGenerateCubemap { // Do not generate cubemap (default). [System.Obsolete("This value is deprecated (use TextureImporter.textureShape instead).")] None = 0, // Generate cubemap from spheremap texture. Spheremap = 1, // Generate cubemap from cylindrical texture. Cylindrical = 2, [System.Obsolete("Obscure shperemap modes are not supported any longer (use TextureImporterGenerateCubemap.Spheremap instead).")] SimpleSpheremap = 3, [System.Obsolete("Obscure shperemap modes are not supported any longer (use TextureImporterGenerateCubemap.Spheremap instead).")] NiceSpheremap = 4, // Generate cubemap from vertical or horizontal cross texture. FullCubemap = 5, // Automatically determine type of cubemap generation from the source image. AutoCubemap = 6 } // Scaling mode for non power of two textures in [[TextureImporter]]. public enum TextureImporterNPOTScale { // Keep non power of two textures as is. None = 0, // Scale to nearest power of two. ToNearest = 1, // Scale to larger power of two. ToLarger = 2, // Scale to smaller power of two. ToSmaller = 3, } // Normal map filtering mode for [[TextureImporter]]. public enum TextureImporterNormalFilter { // Standard normal map filter. Standard = 0, // Sobel normal map filter. Sobel = 1, } // Texture Alpha Usage [[TextureImporter]]. public enum TextureImporterAlphaSource { // Alpha won't be used. None = 0, // Alpha comes from input texture if one is provided. FromInput = 1, // Alpha is generated from image gray scale FromGrayScale = 2, } // Single Channel Texture Component [[TextureImporter]]. public enum TextureImporterSingleChannelComponent { // Use the Alpha channel. Alpha = 0, // Use the Red color channel. Red = 1, } [RequiredByNativeCode] public enum TextureImporterType { Default = 0, NormalMap = 1, GUI = 2, Sprite = 8, Cursor = 7, Cookie = 4, Lightmap = 6, SingleChannel = 10, Shadowmask = 11, DirectionalLightmap = 12, [System.Obsolete("Use Default (UnityUpgradable) -> Default", true)] Image = Int32.MinValue, [System.Obsolete("Use NormalMap (UnityUpgradable) -> NormalMap", true)] Bump = -1, [System.Obsolete("Use importer.textureShape = TextureImporterShape.TextureCube", true)] Cubemap = -3, [System.Obsolete("Use a texture setup as a cubemap with glossy reflection instead", true)] Reflection = -3, [System.Obsolete("Use Default instead. All texture types now have an Advanced foldout (UnityUpgradable) -> Default", true)] Advanced = -5, [System.Obsolete("HDRI is not supported anymore", true)] HDRI = -9, } public enum TextureImporterCompression { Uncompressed = 0, Compressed = 1, // High quality compression formats CompressedHQ = 2, // Low quality compression formats but high Performance - low bandwidth - max compression CompressedLQ = 3 } public enum TextureResizeAlgorithm { // Default high quality one size fits ALMOST all cases Mitchell = 0, // Might provide better result for some noise textures, when sharp details wanted Bilinear = 1 } [Flags] public enum TextureImporterShape { Texture2D = 1 << 0, TextureCube = 1 << 1, Texture2DArray = 1 << 2, Texture3D = 1 << 3, } public enum SpriteImportMode { None = 0, Single = 1, Multiple = 2, Polygon = 3 } public enum AndroidETC2FallbackOverride { // Use build settings UseBuildSettings = 0, // 32-bit uncompressed Quality32Bit = 1, // 16-bit uncompressed Quality16Bit = 2, // 32-bit uncompressed, downscaled 2x Quality32BitDownscaled = 3 } public enum TextureImporterSwizzle { R = 0, G = 1, B = 2, A = 3, OneMinusR = 4, OneMinusG = 5, OneMinusB = 6, OneMinusA = 7, Zero = 8, One = 9 } // Cookie light type mode for [[TextureImporter]]. internal enum TextureImporterCookieLightType { Spot = 0, Directional = 1, Point = 2 } } ================================================ FILE: Editor/Mono/AssetPipeline/TextureImporterTypes.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Serialization; namespace UnityEditor { [StructLayout(LayoutKind.Sequential)] public struct SpriteMetaData { public string name; public Rect rect; public int alignment; public Vector2 pivot; public Vector4 border; internal string customData; } // Note: MUST match memory layout of TextureImporterSettings in TextureImporter.h! // This means you need to be careful about field sizes (e.g. don't use "bool" since they are different size in C# and C++). [System.Serializable] [StructLayout(LayoutKind.Sequential)] [NativeAsStruct] [NativeType(CodegenOptions.Custom, "TextureImporterSettings")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.bindings.h")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterTypes.h")] public sealed class TextureImporterSettings { [SerializeField] int m_AlphaSource = (int)TextureImporterAlphaSource.FromInput; [SerializeField] int m_MipMapMode; [SerializeField] int m_EnableMipMap = 1; [SerializeField] int m_FadeOut; [SerializeField] int m_BorderMipMap; [SerializeField] int m_MipMapsPreserveCoverage; [SerializeField] float m_AlphaTestReferenceValue; [SerializeField] int m_MipMapFadeDistanceStart; [SerializeField] int m_MipMapFadeDistanceEnd; #pragma warning disable 169 [SerializeField] int m_ConvertToNormalMap; [SerializeField] float m_HeightScale; [SerializeField] int m_NormalMapFilter; [SerializeField] int m_FlipGreenChannel; [SerializeField] uint m_Swizzle = 0x03020100; [SerializeField] int m_IsReadable; [SerializeField] int m_StreamingMipmaps; [SerializeField] int m_StreamingMipmapsPriority; [SerializeField] int m_VTOnly; [SerializeField, FormerlySerializedAs("m_IgnoreMasterTextureLimit")] int m_IgnoreMipmapLimit; [SerializeField] int m_NPOTScale; [SerializeField] int m_sRGBTexture = 1; [SerializeField] int m_SpriteMode; [SerializeField] uint m_SpriteExtrude; [SerializeField] int m_SpriteMeshType; [SerializeField] int m_Alignment; [SerializeField] Vector2 m_SpritePivot; [SerializeField] float m_SpritePixelsToUnits; [SerializeField] Vector4 m_SpriteBorder; [SerializeField] int m_SpriteGenerateFallbackPhysicsShape; [SerializeField] int m_GenerateCubemap; [SerializeField] int m_CubemapConvolution; [SerializeField] int m_SeamlessCubemap; [SerializeField] int m_AlphaIsTransparency; [SerializeField] float m_SpriteTessellationDetail; [SerializeField] int m_TextureType; [SerializeField] int m_TextureShape; [SerializeField] int m_SingleChannelComponent; [SerializeField] int m_FlipbookRows; [SerializeField] int m_FlipbookColumns; [SerializeField] int m_IgnorePngGamma; [SerializeField] int m_CookieMode; // memory layout of these is in TextureSettings.h [SerializeField] [NativeName("m_TextureSettings.m_FilterMode")] int m_FilterMode; [SerializeField] [NativeName("m_TextureSettings.m_Aniso")] int m_Aniso; [SerializeField] [NativeName("m_TextureSettings.m_MipBias")] float m_MipBias; [SerializeField] [NativeName("m_TextureSettings.m_WrapU")] int m_WrapU; [SerializeField] [NativeName("m_TextureSettings.m_WrapV")] int m_WrapV; [SerializeField] [NativeName("m_TextureSettings.m_WrapW")] int m_WrapW; // Deprecated since texture importer overhaul. Kept for backward compatibility purpose. [SerializeField] [NativeName("m_NormalMap_Deprecated")] int m_NormalMap; [SerializeField] [NativeName("m_TextureFormat_Deprecated")] int m_TextureFormat; [SerializeField] [NativeName("m_MaxTextureSize_Deprecated")] int m_MaxTextureSize; [SerializeField] [NativeName("m_Lightmap_Deprecated")] int m_Lightmap; [SerializeField] [NativeName("m_CompressionQuality_Deprecated")] int m_CompressionQuality; [SerializeField] [NativeName("m_LinearTexture_Deprecated")] int m_LinearTexture; [SerializeField] [NativeName("m_GrayScaleToAlpha_Deprecated")] int m_GrayScaleToAlpha; [SerializeField] [NativeName("m_RGBM_Deprecated")] int m_RGBM; [SerializeField] [NativeName("m_CubemapConvolutionSteps_Deprecated")] int m_CubemapConvolutionSteps; [SerializeField] [NativeName("m_CubemapConvolutionExponent_Deprecated")] float m_CubemapConvolutionExponent; // These are just part of a hack to support backward compatibility for maxTextureSize, textureFormat and compressionQualityProperties [SerializeField] private int m_MaxTextureSizeSet; [SerializeField] private int m_CompressionQualitySet; [SerializeField] private int m_TextureFormatSet; //For backward compatibility for an incorrectly applied gamma decoding step (bug) [SerializeField] int m_ApplyGammaDecoding; public TextureImporterType textureType { get {return (TextureImporterType)m_TextureType; } set { m_TextureType = (int)value; } } public TextureImporterShape textureShape { get {return (TextureImporterShape)m_TextureShape; } set { m_TextureShape = (int)value; } } public TextureImporterMipFilter mipmapFilter { get {return (TextureImporterMipFilter)m_MipMapMode; } set { m_MipMapMode = (int)value; } } public bool mipmapEnabled { get {return m_EnableMipMap != 0; } set { m_EnableMipMap = value ? 1 : 0; } } [Obsolete("Texture mips are now always generated in linear space")] public bool generateMipsInLinearSpace { get { return true; } set {} } public bool sRGBTexture { get {return m_sRGBTexture != 0; } set { m_sRGBTexture = value ? 1 : 0; } } public bool fadeOut { get {return m_FadeOut != 0; } set { m_FadeOut = value ? 1 : 0; } } public bool borderMipmap { get {return m_BorderMipMap != 0; } set { m_BorderMipMap = value ? 1 : 0; } } public bool mipMapsPreserveCoverage { get { return m_MipMapsPreserveCoverage != 0; } set { m_MipMapsPreserveCoverage = value ? 1 : 0; } } public float alphaTestReferenceValue { get { return m_AlphaTestReferenceValue; } set { m_AlphaTestReferenceValue = value; } } public int mipmapFadeDistanceStart { get {return m_MipMapFadeDistanceStart; } set { m_MipMapFadeDistanceStart = value; } } public int mipmapFadeDistanceEnd { get {return m_MipMapFadeDistanceEnd; } set { m_MipMapFadeDistanceEnd = value; } } public bool convertToNormalMap { get {return m_ConvertToNormalMap != 0; } set { m_ConvertToNormalMap = value ? 1 : 0; } } public float heightmapScale { get {return m_HeightScale; } set { m_HeightScale = value; } } public TextureImporterNormalFilter normalMapFilter { get {return (TextureImporterNormalFilter)m_NormalMapFilter; } set { m_NormalMapFilter = (int)value; } } public bool flipGreenChannel { get => m_FlipGreenChannel != 0; set => m_FlipGreenChannel = value ? 1 : 0; } public TextureImporterSwizzle swizzleR { get => (TextureImporterSwizzle)(m_Swizzle & 0xFF); set => m_Swizzle = (m_Swizzle & 0xFFFFFF00) | (uint)value; } public TextureImporterSwizzle swizzleG { get => (TextureImporterSwizzle)((m_Swizzle >> 8) & 0xFF); set => m_Swizzle = (m_Swizzle & 0xFFFF00FF) | ((uint)value<<8); } public TextureImporterSwizzle swizzleB { get => (TextureImporterSwizzle)((m_Swizzle >> 16) & 0xFF); set => m_Swizzle = (m_Swizzle & 0xFF00FFFF) | ((uint)value<<16); } public TextureImporterSwizzle swizzleA { get => (TextureImporterSwizzle)((m_Swizzle >> 24) & 0xFF); set => m_Swizzle = (m_Swizzle & 0x00FFFFFF) | ((uint)value<<24); } internal uint swizzleRaw { get => m_Swizzle; set => m_Swizzle = value; } public TextureImporterAlphaSource alphaSource { get {return (TextureImporterAlphaSource)m_AlphaSource; } set { m_AlphaSource = (int)value; } } public TextureImporterSingleChannelComponent singleChannelComponent { get {return (TextureImporterSingleChannelComponent)m_SingleChannelComponent; } set { m_SingleChannelComponent = (int)value; } } public int flipbookRows { get => m_FlipbookRows; set => m_FlipbookRows = value; } public int flipbookColumns { get => m_FlipbookColumns; set => m_FlipbookColumns = value; } public bool readable { get {return m_IsReadable != 0; } set { m_IsReadable = value ? 1 : 0; } } public bool streamingMipmaps { get {return m_StreamingMipmaps != 0; } set { m_StreamingMipmaps = value ? 1 : 0; } } public int streamingMipmapsPriority { get {return m_StreamingMipmapsPriority; } set { m_StreamingMipmapsPriority = value; } } public bool vtOnly { get { return m_VTOnly != 0; } set { m_VTOnly = value ? 1 : 0; } } public bool ignoreMipmapLimit { get { return m_IgnoreMipmapLimit != 0; } set { m_IgnoreMipmapLimit = value ? 1 : 0; } } public TextureImporterNPOTScale npotScale { get {return (TextureImporterNPOTScale)m_NPOTScale; } set { m_NPOTScale = (int)value; } } public TextureImporterGenerateCubemap generateCubemap { get {return (TextureImporterGenerateCubemap)m_GenerateCubemap; } set { m_GenerateCubemap = (int)value; } } public TextureImporterCubemapConvolution cubemapConvolution { get {return (TextureImporterCubemapConvolution)m_CubemapConvolution; } set { m_CubemapConvolution = (int)value; } } public bool seamlessCubemap { get {return m_SeamlessCubemap != 0; } set { m_SeamlessCubemap = value ? 1 : 0; } } public FilterMode filterMode { get {return (FilterMode)m_FilterMode; } set { m_FilterMode = (int)value; } } public int aniso { get {return m_Aniso; } set { m_Aniso = value; } } public float mipmapBias { get {return m_MipBias; } set { m_MipBias = value; } } public TextureWrapMode wrapMode { get { return (TextureWrapMode)m_WrapU; } set { m_WrapU = (int)value; m_WrapV = (int)value; m_WrapW = (int)value; } } public TextureWrapMode wrapModeU { get { return (TextureWrapMode)m_WrapU; } set { m_WrapU = (int)value; } } public TextureWrapMode wrapModeV { get { return (TextureWrapMode)m_WrapV; } set { m_WrapV = (int)value; } } public TextureWrapMode wrapModeW { get { return (TextureWrapMode)m_WrapW; } set { m_WrapW = (int)value; } } public bool alphaIsTransparency { get {return m_AlphaIsTransparency != 0; } set { m_AlphaIsTransparency = value ? 1 : 0; } } public bool ignorePngGamma { get { return m_IgnorePngGamma != 0; } set { m_IgnorePngGamma = value ? 1 : 0; } } public int spriteMode { get {return m_SpriteMode; } set { m_SpriteMode = value; } } // The number of pixels in one unit. Note: Internally, the name m_SpritePixelsToUnits has not been changed yet to minimize merge conflicts. public float spritePixelsPerUnit { get {return m_SpritePixelsToUnits; } set { m_SpritePixelsToUnits = value; } } [System.Obsolete("Use spritePixelsPerUnit property instead.")] public float spritePixelsToUnits { get {return m_SpritePixelsToUnits; } set { m_SpritePixelsToUnits = value; } } public float spriteTessellationDetail { get {return m_SpriteTessellationDetail; } set { m_SpriteTessellationDetail = value; } } public uint spriteExtrude { get { return m_SpriteExtrude; } set { m_SpriteExtrude = value; } } public SpriteMeshType spriteMeshType { get { return (SpriteMeshType)m_SpriteMeshType; } set { m_SpriteMeshType = (int)value; } } public int spriteAlignment { get {return m_Alignment; } set { m_Alignment = value; } } public Vector2 spritePivot { get { return m_SpritePivot; } set { m_SpritePivot = value; } } public Vector4 spriteBorder { get { return m_SpriteBorder; } set { m_SpriteBorder = value; } } public bool spriteGenerateFallbackPhysicsShape { get {return m_SpriteGenerateFallbackPhysicsShape != 0; } set { m_SpriteGenerateFallbackPhysicsShape = value ? 1 : 0; } } [FreeFunction("TextureImporterBindings::Equal")] public static extern bool Equal(TextureImporterSettings a, TextureImporterSettings b); public void CopyTo(TextureImporterSettings target) { Copy(this, target); } // Test texture importer settings for equality. [FreeFunction("TextureImporterBindings::CopyTo")] private static extern void Copy([NotNull] TextureImporterSettings self, [Out][NotNull] TextureImporterSettings target); // Configure parameters to import a texture for a purpose of ''type'', as described [[TextureImporterType|here]]. [Obsolete("ApplyTextureType(TextureImporterType, bool) is deprecated, use ApplyTextureType(TextureImporterType)")] public void ApplyTextureType(TextureImporterType type, bool applyAll) { Internal_ApplyTextureType(this, type); } public void ApplyTextureType(TextureImporterType type) { Internal_ApplyTextureType(this, type); } [FreeFunction("TextureImporterBindings::ApplyTextureType")] private static extern void Internal_ApplyTextureType([Out][NotNull] TextureImporterSettings self, TextureImporterType type); // Deprecated APIs [Obsolete("Use sRGBTexture instead")] public bool linearTexture { get { return !sRGBTexture; } set { sRGBTexture = !value; } } [Obsolete("Check importer.textureType against TextureImporterType.NormalMap instead. Getter will work as expected. Setter will set textureType to NormalMap if true, nothing otherwise")] public bool normalMap { get { return textureType == TextureImporterType.NormalMap; } set { if (value) textureType = TextureImporterType.NormalMap; else textureType = TextureImporterType.Default; } } [Obsolete("Texture format can only be overridden on a per platform basis. See [[TextureImporterPlatformSettings]]")] public TextureImporterFormat textureFormat { get {return (TextureImporterFormat)m_TextureFormat; } set {m_TextureFormat = (int)textureFormat; textureFormatSet = 1; } } [Obsolete("Texture max size can only be overridden on a per platform basis. See [[TextureImporter.maxTextureSize]] for Default platform or [[TextureImporterPlatformSettings]]")] public int maxTextureSize { get { return m_MaxTextureSize; } set { m_MaxTextureSize = value; maxTextureSizeSet = 1; } } [Obsolete("Check importer.textureType against TextureImporterType.Lightmap instead. Getter will work as expected. Setter will set textureType to Lightmap if true, nothing otherwise.")] public bool lightmap { get { return textureType == TextureImporterType.Lightmap; } set { if (value) textureType = TextureImporterType.Lightmap; else textureType = TextureImporterType.Default; } } [Obsolete("RGBM is no longer a user's choice but has become an implementation detail hidden to the user.")] public TextureImporterRGBMMode rgbm { get { return (TextureImporterRGBMMode)m_RGBM; } set { m_RGBM = (int)value; } } [Obsolete("Use UnityEditor.TextureImporter.alphaSource instead")] public bool grayscaleToAlpha { get { return alphaSource == TextureImporterAlphaSource.FromGrayScale; } set { if (value) alphaSource = TextureImporterAlphaSource.FromGrayScale; else alphaSource = TextureImporterAlphaSource.FromInput; } } [Obsolete("Not used anymore. The right values are automatically picked by the importer.")] public int cubemapConvolutionSteps { get {return m_CubemapConvolutionSteps; } set {m_CubemapConvolutionSteps = value; } } [Obsolete("Not used anymore. The right values are automatically picked by the importer.")] public float cubemapConvolutionExponent { get {return m_CubemapConvolutionExponent; } set {m_CubemapConvolutionExponent = value; } } [Obsolete("Texture compression can only be overridden on a per platform basis. See [[TextureImporter.compressionQuality]] for Default platform or [[TextureImporterPlatformSettings]]")] public int compressionQuality { get { return m_CompressionQuality; } set { m_CompressionQuality = value; compressionQualitySet = 1; } } private int maxTextureSizeSet { get { return m_MaxTextureSizeSet; } set { m_MaxTextureSizeSet = value; } } private int textureFormatSet { get { return m_TextureFormatSet; } set { m_TextureFormatSet = value; } } private int compressionQualitySet { get { return m_CompressionQualitySet; } set { m_CompressionQualitySet = value; } } } // Note: MUST match memory layout of TextureImporterPlatformSettings in TextureImporterTypes.h! // This means you need to be careful about field sizes (e.g. don't use "bool" since they are different size in C# and C++). [System.Serializable] [StructLayout(LayoutKind.Sequential)] [NativeAsStruct] [NativeType(CodegenOptions.Custom, "TextureImporterPlatformSettings_Marshalling")] [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporter.bindings.h")] public sealed partial class TextureImporterPlatformSettings { [SerializeField] [NativeName("m_BuildTarget")] string m_Name = TextureImporterInspector.s_DefaultPlatformName; [SerializeField] int m_Overridden = 0; [SerializeField] int m_IgnorePlatformSupport = 0; [SerializeField] int m_MaxTextureSize = 2048; [SerializeField] int m_ResizeAlgorithm = (int)TextureResizeAlgorithm.Mitchell; [SerializeField] int m_TextureFormat = (int)TextureImporterFormat.Automatic; [SerializeField] int m_TextureCompression = (int)TextureImporterCompression.Compressed; [SerializeField] int m_CompressionQuality = (int)TextureCompressionQuality.Normal; [SerializeField] internal int m_ForceMaximumCompressionQuality_BC6H_BC7 = 0; [SerializeField] int m_CrunchedCompression = 0; [SerializeField] int m_AllowsAlphaSplitting = 0; [SerializeField] int m_AndroidETC2FallbackOverride = (int)AndroidETC2FallbackOverride.UseBuildSettings; public string name { get { return m_Name; } set { m_Name = value; } } public bool overridden { get { return m_Overridden != 0; } set { m_Overridden = value ? 1 : 0; } } public bool ignorePlatformSupport { get { return m_IgnorePlatformSupport != 0; } set { m_IgnorePlatformSupport = value ? 1 : 0; } } public int maxTextureSize { get { return m_MaxTextureSize; } set { m_MaxTextureSize = value; } } public TextureResizeAlgorithm resizeAlgorithm { get { return (TextureResizeAlgorithm)m_ResizeAlgorithm; } set { m_ResizeAlgorithm = (int)value; } } public TextureImporterFormat format { get { return (TextureImporterFormat)m_TextureFormat; } set { m_TextureFormat = (int)value; } } public TextureImporterCompression textureCompression { get { return (TextureImporterCompression)m_TextureCompression; } set { m_TextureCompression = (int)value; } } public int compressionQuality { get { return m_CompressionQuality; } set { m_CompressionQuality = value; } } internal int forceMaximumCompressionQuality_BC6H_BC7 { get { return m_ForceMaximumCompressionQuality_BC6H_BC7; } set { m_ForceMaximumCompressionQuality_BC6H_BC7 = value; } } public bool crunchedCompression { get { return m_CrunchedCompression != 0; } set { m_CrunchedCompression = value ? 1 : 0; } } public bool allowsAlphaSplitting { get { return m_AllowsAlphaSplitting != 0; } set { m_AllowsAlphaSplitting = value ? 1 : 0; } } public AndroidETC2FallbackOverride androidETC2FallbackOverride { get { return (AndroidETC2FallbackOverride)m_AndroidETC2FallbackOverride; } set { m_AndroidETC2FallbackOverride = (int)value; } } public void CopyTo(TextureImporterPlatformSettings target) { Copy(this, target); } [FreeFunction("TextureImporterBindings::CopyTo")] private static extern void Copy([NotNull] TextureImporterPlatformSettings self, [Out][NotNull] TextureImporterPlatformSettings target); } } ================================================ FILE: Editor/Mono/AssetPipeline/TextureUtil.bindings.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Experimental.Rendering; //for GraphicsFormat namespace UnityEditor { [NativeHeader("Editor/Src/AssetPipeline/TextureImporting/TextureImporterUtils.h")] [NativeHeader("Runtime/Graphics/TextureFormat.h")] [NativeHeader("Runtime/Graphics/Format.h")] internal static class TextureUtil { [FreeFunction] public static extern long GetStorageMemorySizeLong([NotNull] Texture t); [FreeFunction] public static extern long GetRuntimeMemorySizeLong([NotNull] Texture t); [FreeFunction] public static extern bool IsNonPowerOfTwo([NotNull] Texture2D t); [FreeFunction] public static extern TextureUsageMode GetUsageMode([NotNull] Texture t); [FreeFunction] public static extern bool IsNormalMapUsageMode(TextureUsageMode usageMode); [FreeFunction] public static extern bool IsRGBMUsageMode(TextureUsageMode usageMode); [FreeFunction] public static extern bool IsDoubleLDRUsageMode(TextureUsageMode usageMode); [FreeFunction] public static extern int GetBytesFromTextureFormat(TextureFormat inFormat); [FreeFunction] public static extern int GetRowBytesFromWidthAndFormat(int width, TextureFormat format); [FreeFunction] public static extern bool IsValidTextureFormat(TextureFormat format); [Obsolete("IsCompressedTextureFormat has been moved to GraphicsFormatUtility.IsCompressedFormat(TextureFormat)")] [FreeFunction("IsAnyCompressedTextureFormat")] public static extern bool IsCompressedTextureFormat(TextureFormat format); [Obsolete("IsCompressedCrunchTextureFormat has been moved to GraphicsFormatUtility.IsCrunchFormat(TextureFormat)")] [FreeFunction("IsCompressedCrunchTextureFormat")] public static extern bool IsCompressedCrunchTextureFormat(TextureFormat format); [FreeFunction] public static extern TextureFormat GetTextureFormat([NotNull] Texture texture); [Obsolete("IsAlphaOnlyTextureFormat has been moved to GraphicsFormatUtility.IsAlphaOnlyFormat(TextureFormat)")] [FreeFunction] public static extern bool IsAlphaOnlyTextureFormat(TextureFormat format); [Obsolete("IsHDRFormat has been moved to GraphicsFormatUtility.IsHDRFormat(TextureFormat)")] [FreeFunction] public static extern bool IsHDRFormat(TextureFormat format); [Obsolete("IsHDRGraphicsFormat has been moved to GraphicsFormatUtility.IsHDRFormat(GraphicsFormat)")] [FreeFunction("IsHDRFormat")] public static extern bool IsHDRGraphicsFormat(GraphicsFormat format); [Obsolete("HasAlphaTextureFormat has been moved to GraphicsFormatUtility.HasAlphaChannel(TextureFormat)")] [FreeFunction] public static extern bool HasAlphaTextureFormat(TextureFormat format); [Obsolete("GetTextureFormatString has been moved to GraphicsFormatUtility.GetFormatString(TextureFormat)")] [FreeFunction] public static extern string GetTextureFormatString(TextureFormat format); [FreeFunction] public static extern string GetTextureColorSpaceString([NotNull] Texture texture); [Obsolete("ConvertToAlphaTextureFormat has been moved to GraphicsFormatUtility.ConvertToAlphaFormat(TextureFormat)")] [FreeFunction] public static extern TextureFormat ConvertToAlphaTextureFormat(TextureFormat format); public static bool IsDepthRTFormat(RenderTextureFormat format) { return format == RenderTextureFormat.Depth || format == RenderTextureFormat.Shadowmap; } [FreeFunction] public static extern bool HasMipMap([NotNull] Texture t); [FreeFunction] public static extern bool NeedsExposureControl([NotNull] Texture t); [FreeFunction] public static extern int GetGPUWidth([NotNull] Texture t); [FreeFunction] public static extern int GetGPUHeight([NotNull] Texture t); [FreeFunction] public static extern int GetMipmapCount([NotNull] Texture t); [FreeFunction] public static extern bool GetLinearSampled([NotNull] Texture t); public static int GetDefaultCompressionQuality() { return (int)TextureCompressionQuality.Normal; } [FreeFunction] public static extern Vector4 GetTexelSizeVector([NotNull] Texture t); [FreeFunction] public static extern Texture2D GetSourceTexture([NotNull] Cubemap cubemapRef, CubemapFace face); [FreeFunction] public static extern void SetSourceTexture([NotNull] Cubemap cubemapRef, CubemapFace face, Texture2D tex); [FreeFunction] public static extern void CopyTextureIntoCubemapFace([NotNull] Texture2D textureRef, [NotNull] Cubemap cubemapRef, CubemapFace face); [FreeFunction] public static extern void CopyCubemapFaceIntoTexture([NotNull] Cubemap cubemapRef, CubemapFace face, [NotNull] Texture2D textureRef); [FreeFunction] public static extern bool ReformatCubemap([NotNull] Cubemap cubemap, int width, int height, TextureFormat textureFormat, bool useMipmap, bool linear); [FreeFunction] public static extern bool ReformatTexture([NotNull] ref Texture2D texture, int width, int height, TextureFormat textureFormat, bool useMipmap, bool linear); [FreeFunction] public static extern void SetAnisoLevelNoDirty(Texture tex, int level); [FreeFunction] public static extern void SetWrapModeNoDirty(Texture tex, TextureWrapMode u, TextureWrapMode v, TextureWrapMode w); [FreeFunction] public static extern void SetMipMapBiasNoDirty(Texture tex, float bias); [FreeFunction] public static extern void SetFilterModeNoDirty(Texture tex, FilterMode mode); [FreeFunction] public static extern bool IsCubemapReadable([NotNull] Cubemap cubemapRef); [FreeFunction] public static extern void MarkCubemapReadable(Cubemap cubemapRef, bool readable); [FreeFunction] public static extern bool GetTexture2DStreamingMipmaps(Texture2D texture); [FreeFunction] public static extern int GetTexture2DStreamingMipmapsPriority(Texture2D texture); [FreeFunction] public static extern bool GetCubemapStreamingMipmaps(Cubemap cubemap); [FreeFunction] public static extern int GetCubemapStreamingMipmapsPriority(Cubemap cubemap); [FreeFunction] public static extern void SetTexture2DStreamingMipmaps(Texture2D textureRef, bool streaming); [FreeFunction] public static extern void SetTexture2DStreamingMipmapsPriority(Texture2D textureRef, int priority); [FreeFunction] public static extern void SetCubemapStreamingMipmaps(Cubemap cubemapRef, bool streaming); [FreeFunction] public static extern void SetCubemapStreamingMipmapsPriority(Cubemap cubemapRef, int priority); } } ================================================ FILE: Editor/Mono/AssetPostprocessor.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Internal; using UnityEngine.Scripting; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor.AssetImporters; using Object = UnityEngine.Object; using UnityEditor.Profiling; using UnityEditor.Callbacks; namespace UnityEditor { // AssetPostprocessor lets you hook into the import pipeline and run scripts prior or after importing assets. public partial class AssetPostprocessor { internal struct PostprocessorInfo { public Type Type { get; } public string[] Methods { get; } public uint Version { get; } public int Priority { get; } /// /// StaticDependency is true if any method in the postprocessor is not part of the NonAutomaticDependencyMethods list. /// This is used to know which PostprocessorInfo should be used for the static importer dependency hash. /// public bool StaticDependency { get; } public PostprocessorInfo(Type assetPostprocessorType, int importerPriority) { Type = assetPostprocessorType; Methods = null; Version = 0; StaticDependency = false; Priority = importerPriority; } public PostprocessorInfo(Type assetPostprocessorType, string[] implementedMethods) { Type = assetPostprocessorType; Methods = implementedMethods; StaticDependency = Methods.Intersect(AssetPostprocessingInternal.k_NonAutomaticDependencyMethods).Count() != Methods.Length; var inst = (AssetPostprocessor)Activator.CreateInstance(assetPostprocessorType); Version = inst.GetVersion(); Priority = inst.GetPostprocessOrder(); } } private string m_PathName; private AssetImportContext m_Context; // The path name of the asset being imported. public string assetPath { get { return m_PathName; } set { m_PathName = value; } } // The context of the import, used to specify dependencies public AssetImportContext context { get { return m_Context; } internal set { m_Context = value; } } // Logs an import warning to the console. [ExcludeFromDocs] [Obsolete("Use context.LogImportWarning(string) instead.")] public void LogWarning(string warning) { Object context = null; LogWarning(warning, context); } [Obsolete("Use context.LogImportWarning(string, Object) instead.")] public void LogWarning(string warning, [DefaultValue("null")] Object context) { if (m_Context != null) m_Context.LogImportWarning(warning, context); else Debug.LogWarning(warning, context); } // Logs an import error message to the console. [ExcludeFromDocs] [Obsolete("Use context.LogImportError(string) instead.")] public void LogError(string warning) { Object context = null; LogError(warning, context); } [Obsolete("Use context.LogImportError(string, Object) instead.")] public void LogError(string warning, [DefaultValue("null")] Object context) { if (m_Context != null) m_Context.LogImportError(warning, context); else Debug.LogError(warning, context); } // Returns the version of the asset postprocessor. public virtual uint GetVersion() { return 0; } // Reference to the asset importer public AssetImporter assetImporter { get { return AssetImporter.GetAtPath(assetPath); } } [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [Obsolete("To set or get the preview, call EditorUtility.SetAssetPreview or AssetPreview.GetAssetPreview instead", true)] public Texture2D preview { get { return null; } set {} } // Override the order in which importers are processed. public virtual int GetPostprocessOrder() { return 0; } } class OnPostprocessAllAssetsCallbackCollection : OrderedCallbackCollection { public class MethodInfoCallback : Callback { public MethodInfo Method { get; } public override Type classType => Method.DeclaringType; public bool MethodDomainReload { get; } public override string name => classType.FullName; public MethodInfoCallback(MethodInfo method, bool methodDomainReload) { Method = method; MethodDomainReload = methodDomainReload; } public override IEnumerable GetCustomAttributes() => Method.GetCustomAttributes(); } public override string name => "OnPostprocessAllAssets"; public override List GetCallbacks() { var methodArgTypes = new Type[] { typeof(string).MakeArrayType(), typeof(string).MakeArrayType(), typeof(string).MakeArrayType(), typeof(string).MakeArrayType() }; var methodDomainReloadParamArgTypes = new Type[] { methodArgTypes[0], methodArgTypes[1], methodArgTypes[2], methodArgTypes[3], typeof(bool) }; const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; var callbacks = new List(); foreach (var assetPostprocessorClass in TypeCache.GetTypesDerivedFrom()) { var method = assetPostprocessorClass.GetMethod("OnPostprocessAllAssets", flags, null, methodArgTypes, null); if (method != null) { callbacks.Add(new MethodInfoCallback(method, false)); } else { // OnPostprocessAllAssets with didDomainReload parameter method = assetPostprocessorClass.GetMethod("OnPostprocessAllAssets", flags, null, methodDomainReloadParamArgTypes, null); if (method != null) { callbacks.Add(new MethodInfoCallback(method, true)); } } } return callbacks; } } internal class AssetPostprocessingInternal { // What is it: // Static postprocessor methods always called for each importer that are part of importer static dependency. // No new postprocessors should be added to these lists. Please reach out to #devs-import-workflow to talk about new additions. internal static readonly string[] k_NonAutomaticDependencyMethods = { "OnPreprocessAsset", }; static readonly string[] k_ModelImporterPostprocessors = { "OnPreprocessModel", "OnPostprocessMeshHierarchy", "OnPostprocessModel", "OnPreprocessAnimation", "OnPostprocessAnimation", "OnPostprocessGameObjectWithAnimatedUserProperties", "OnPostprocessGameObjectWithUserProperties", "OnPostprocessMaterial", "OnAssignMaterialModel", "OnPreprocessMaterialDescription", }; static readonly string[] k_DynamicModelImporterPostprocessors = { "OnPreprocessCameraDescription", "OnPreprocessLightDescription" }; static readonly string[] k_TextureImporterPostprocessors = { "OnPreprocessTexture", "OnPostprocessTexture", "OnPostprocessCubemap", "OnPostprocessSprites", "OnPostprocessTexture3D", "OnPostprocessTexture2DArray" }; static readonly string[] k_IHVImporterPostprocessors = { "OnPostprocessTexture", }; static readonly string[] k_AudioImporterPostprocessors = { "OnPreprocessAudio", "OnPostprocessAudio", }; static readonly string[] k_SpeedTreeImporterPostprocessors = { "OnPreprocessSpeedTree", "OnPostprocessSpeedTree", }; static readonly string[] k_PrefabImporterPostprocessors = { "OnPostprocessPrefab", }; static readonly string[] k_CameraPostprocessors = { "OnPreprocessCameraDescription", }; static readonly string[] k_LightPostprocessors = { "OnPreprocessLightDescription", }; static readonly string[] k_TexturePreprocessors = { "OnPreprocessTexture", }; static readonly string[] k_Texture2DPostprocessors = { "OnPostprocessTexture", }; static readonly string[] k_Texture2DArrayPostprocessors = { "OnPostprocessTexture2DArray" }; static readonly string[] k_Texture3DPostprocessors = { "OnPostprocessTexture3D" }; static readonly string[] k_TextureCubePostprocessors = { "OnPostprocessCubemap" }; static readonly string[] k_SpritePostprocessors = { "OnPostprocessSprites", }; static Dictionary s_PostprocessorMethodsByDependencyKey; static Dictionary s_StaticPostprocessorMethodsByImporterType; static Dictionary s_DynamicPostprocessorMethodsByImporterType; // Internal for debugging purposes. We can generate dependency graphs to help understand issues. internal static OnPostprocessAllAssetsCallbackCollection s_OnPostprocessAllAssetsCallbacks = new OnPostprocessAllAssetsCallbackCollection(); static AssetPostprocessingInternal() { s_StaticPostprocessorMethodsByImporterType = new Dictionary(); s_StaticPostprocessorMethodsByImporterType.Add(typeof(ModelImporter), k_ModelImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(IHVImageFormatImporter), k_IHVImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(SpeedTreeImporter), k_SpeedTreeImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(AudioImporter), k_AudioImporterPostprocessors); s_StaticPostprocessorMethodsByImporterType.Add(typeof(PrefabImporter), k_PrefabImporterPostprocessors); s_DynamicPostprocessorMethodsByImporterType = new Dictionary(); s_DynamicPostprocessorMethodsByImporterType.Add(typeof(ModelImporter), k_DynamicModelImporterPostprocessors); s_DynamicPostprocessorMethodsByImporterType.Add(typeof(TextureImporter), k_TextureImporterPostprocessors); s_PostprocessorMethodsByDependencyKey = new Dictionary(); s_PostprocessorMethodsByDependencyKey.Add(kCameraPostprocessorDependencyName, k_CameraPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kLightPostprocessorDependencyName, k_LightPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexturePreprocessorDependencyName, k_TexturePreprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexture2DPostprocessorDependencyName, k_Texture2DPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexture2DArrayPostprocessorDependencyName, k_Texture2DArrayPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTexture3DPostprocessorDependencyName, k_Texture3DPostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTextureCubePostprocessorDependencyName, k_TextureCubePostprocessors); s_PostprocessorMethodsByDependencyKey.Add(kTextureSpritePostprocessorDependencyName, k_SpritePostprocessors); } [Serializable] class AssetPostProcessorAnalyticsData { public string importActionId; public List postProcessorCalls = new List(); } [Serializable] struct AssetPostProcessorMethodCallAnalyticsData { public string methodName; public float duration_sec; public int invocationCount; } static void LogPostProcessorMissingDefaultConstructor(Type type) { Debug.LogErrorFormat("{0} requires a default constructor to be used as an asset post processor", type); } [RequiredByNativeCode] // Postprocess on all assets once an automatic import has completed static void PostprocessAllAssets(string[] importedAssets, string[] addedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPathAssets, bool didDomainReload) { object[] args = { importedAssets, deletedAssets, movedAssets, movedFromPathAssets }; object[] argsWithDidDomainReload = { importedAssets, deletedAssets, movedAssets, movedFromPathAssets, didDomainReload}; var containsNoAssets = importedAssets.Length == 0 && addedAssets.Length == 0 && deletedAssets.Length == 0 && movedAssets.Length == 0 && movedFromPathAssets.Length == 0; foreach (OnPostprocessAllAssetsCallbackCollection.MethodInfoCallback assetPostProcessor in s_OnPostprocessAllAssetsCallbacks.sortedCallbacks) { try { if (assetPostProcessor.MethodDomainReload) { using (new EditorPerformanceMarker($"{assetPostProcessor.classType.Name}.OnPostprocessAllAssets", assetPostProcessor.classType).Auto()) InvokeMethod(assetPostProcessor.Method, argsWithDidDomainReload); } else { if (containsNoAssets) continue; using (new EditorPerformanceMarker($"{assetPostProcessor.classType.Name}.OnPostprocessAllAssets", assetPostProcessor.classType).Auto()) InvokeMethod(assetPostProcessor.Method, args); } } catch (Exception e) { Debug.LogException(e); } } using (new EditorPerformanceMarker("SyncVS.PostprocessSyncProject").Auto()) CodeEditorProjectSync.PostprocessSyncProject(importedAssets, addedAssets, deletedAssets, movedAssets, movedFromPathAssets); } internal class CompareAssetImportPriority : IComparer, IComparer { public int Compare(AssetPostprocessor.PostprocessorInfo x, AssetPostprocessor.PostprocessorInfo y) { int xo = x.Priority; int yo = y.Priority; var compare = xo.CompareTo(yo); if (compare == 0) { compare = x.Type.FullName.CompareTo(y.Type.FullName); if (compare == 0) compare = x.Type.AssemblyQualifiedName.CompareTo(y.Type.AssemblyQualifiedName); } return compare; } public int Compare(AssetPostprocessor x, AssetPostprocessor y) { var xi = new AssetPostprocessor.PostprocessorInfo(x.GetType(), x.GetPostprocessOrder()); var yi = new AssetPostprocessor.PostprocessorInfo(y.GetType(), y.GetPostprocessOrder()); return Compare(xi, yi); } } private static string BuildStaticDependencyHashString(SortedSet list) { var hashStr = ""; foreach (var info in list) { if (info.StaticDependency) { hashStr += info.Type.AssemblyQualifiedName; hashStr += '.'; hashStr += info.Version; hashStr += '|'; } } return hashStr; } private static string BuildHashString(SortedList list) { var hashStr = ""; foreach (var pair in list) { hashStr += pair.Key; hashStr += '.'; hashStr += pair.Value; hashStr += '|'; } return hashStr; } internal const string kCameraPostprocessorDependencyName = "postprocessor/camera"; internal const string kLightPostprocessorDependencyName = "postprocessor/light"; internal const string kTexture2DPostprocessorDependencyName = "postprocessor/texture2D"; internal const string kTextureCubePostprocessorDependencyName = "postprocessor/textureCube"; internal const string kTexture3DPostprocessorDependencyName = "postprocessor/texture3D"; internal const string kTexture2DArrayPostprocessorDependencyName = "postprocessor/texture2DArray"; internal const string kTextureSpritePostprocessorDependencyName = "postprocessor/textureSprite"; internal const string kTexturePreprocessorDependencyName = "postprocessor/texturePreprocessor"; static Stack> m_PostprocessStack = null; static SortedSet m_ImportProcessors = null; static Type[] m_PostprocessorClasses = null; static string m_MeshProcessorsHashString = null; static string m_AudioProcessorsHashString = null; static string m_SpeedTreeProcessorsHashString = null; static string m_PrefabProcessorsHashString = null; static string m_CameraProcessorsHashString = null; static string m_LightProcessorsHashString = null; static string m_Texture2DProcessorsHashString = null; static string m_TextureCubeProcessorsHashString = null; static string m_Texture3DPostprocessorDependencyName = null; static string m_Texture2DArrayDependencyName = null; static string m_TextureSpriteDependencyName = null; static string m_TexturePreprocessorDependencyName = null; static Dictionary> s_StaticPostprocessorsPerImporterType = new Dictionary>(); static Dictionary> s_DynamicPostprocessorsPerImporterType = new Dictionary>(); static Stack s_AnalyticsEventsStack = new Stack(); static Type[] GetCachedAssetPostprocessorClasses() { if (m_PostprocessorClasses == null) m_PostprocessorClasses = TypeCache.GetTypesDerivedFrom().ToArray(); return m_PostprocessorClasses; } [RequiredByNativeCode] static void InitPostprocessorsForTextureGenerator(string pathName) { var analyticsEvent = new AssetPostProcessorAnalyticsData(); analyticsEvent.importActionId = "None"; s_AnalyticsEventsStack.Push(analyticsEvent); m_ImportProcessors = new SortedSet(new CompareAssetImportPriority()); foreach (var postprocessorInfo in GetSortedStaticPostprocessorTypes(typeof(TextureImporter))) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = null; m_ImportProcessors.Add(assetPostprocessor); } foreach (var postprocessorInfo in GetSortedDynamicPostprocessorTypes(typeof(TextureImporter))) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = null; m_ImportProcessors.Add(assetPostprocessor); } // Setup postprocessing stack to support reentrancy (Import asset immediate) if (m_PostprocessStack == null) m_PostprocessStack = new Stack>(); m_PostprocessStack.Push(m_ImportProcessors); } [RequiredByNativeCode] static void InitPostprocessors(AssetImportContext context, string pathName, Type importerType, double importStartTime) { var analyticsEvent = new AssetPostProcessorAnalyticsData(); analyticsEvent.importActionId = ((int)Math.Floor(importStartTime * 1000)).ToString(); s_AnalyticsEventsStack.Push(analyticsEvent); m_ImportProcessors = new SortedSet(new CompareAssetImportPriority()); foreach (var postprocessorInfo in GetSortedStaticPostprocessorTypes(importerType)) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = context; m_ImportProcessors.Add(assetPostprocessor); } foreach (var postprocessorInfo in GetSortedDynamicPostprocessorTypes(importerType)) { var assetPostprocessor = (AssetPostprocessor)Activator.CreateInstance(postprocessorInfo.Type); assetPostprocessor.assetPath = pathName; assetPostprocessor.context = context; m_ImportProcessors.Add(assetPostprocessor); } // Setup postprocessing stack to support reentrancy (Import asset immediate) if (m_PostprocessStack == null) m_PostprocessStack = new Stack>(); m_PostprocessStack.Push(m_ImportProcessors); } [RequiredByNativeCode] static void CleanupPostprocessors() { if (m_PostprocessStack != null) { m_PostprocessStack.Pop(); m_ImportProcessors = m_PostprocessStack.Count > 0 ? m_PostprocessStack.Peek() : null; } if (s_AnalyticsEventsStack.Count > 0) { var lastEvent = s_AnalyticsEventsStack.Pop(); if (lastEvent.postProcessorCalls.Count > 0) EditorAnalytics.SendAssetPostprocessorsUsage(lastEvent); } } static bool ImplementsAnyOfTheses(Type type, IEnumerable methods, out List usedMethods) { usedMethods = new List(methods.Where(method => type.GetMethod(method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null)); return usedMethods.Count > 0; } /* * Returns the list of actual dynamic postprocessor methods for a particular asset. * Note: That where the asset is not yet imported, this list will be empty. */ internal static SortedSet GetSortedDynamicPostprocessorsForAsset(string path) { var list = new SortedSet(new CompareAssetImportPriority()); var guid = AssetDatabase.GUIDFromAssetPath(path); if (guid.Empty()) return list; //Artifact Infos may contains multiple artifacts, associated with different version of the object (E.g. Main, Preview etc.) var artifactInfos = AssetDatabase.GetArtifactInfos(guid); var allMethodsNames = new List(); foreach (var info in artifactInfos) { if (!info.isCurrentArtifact) continue; foreach (var kvp in info.dependencies) { if (kvp.Value.type == ArtifactInfoDependencyType.Dynamic) { //Try to retrieve Postprocessor Methods associated with the supplied Dependency keys string dependencyName = kvp.Key.Replace(ArtifactDifferenceReporter.kEnvironment_CustomDependency + "/", ""); if (s_PostprocessorMethodsByDependencyKey.TryGetValue(dependencyName, out var methodNames)) allMethodsNames.AddRange(methodNames); } } } if (allMethodsNames.Count == 0) return list; /* * The asset has dynamic dependencies to an Asset Postprocessor, so let's find any Postprocessors which * implements those methods. */ var distinctMethodNames = allMethodsNames.Distinct(); foreach (Type assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { if (ImplementsAnyOfTheses(assetPostprocessorClass, distinctMethodNames, out var methods)) { if (assetPostprocessorClass.GetConstructors().Any(t => t.GetParameters().Count() == 0)) list.Add(new AssetPostprocessor.PostprocessorInfo(assetPostprocessorClass, methods.ToArray())); else LogPostProcessorMissingDefaultConstructor(assetPostprocessorClass); } } return list; } internal static SortedSet GetSortedStaticPostprocessorTypes(Type importer) { var defaultMethods = new string[] { "OnPreprocessAsset" }; return GetSortedPostprocessorTypes(importer, s_StaticPostprocessorMethodsByImporterType, defaultMethods, s_StaticPostprocessorsPerImporterType); } /* * Returns the list of *possible* dynamic postprocessor methods associated with a particular importer type. * See also: GetSortedDynamicPostprocessorsForAsset, to get the actual postprocessor methods for a given asset. */ internal static SortedSet GetSortedDynamicPostprocessorTypes(Type importer) { return GetSortedPostprocessorTypes(importer, s_DynamicPostprocessorMethodsByImporterType, new string[0], s_DynamicPostprocessorsPerImporterType); } static SortedSet GetSortedPostprocessorTypes(Type importer, Dictionary postprocessorMethodsByImporterType, string[] defaultMethods, Dictionary> cache) { if (cache.TryGetValue(importer, out var cachedPostprocessors)) return cachedPostprocessors; var list = new SortedSet(new CompareAssetImportPriority()); var allMethodsNames = defaultMethods.ToList(); var methodsType = importer; while (methodsType != null && methodsType != typeof(AssetImporter)) { if (postprocessorMethodsByImporterType.TryGetValue(methodsType, out var methodNames)) allMethodsNames.AddRange(methodNames); methodsType = methodsType.BaseType; } foreach (Type assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { if (ImplementsAnyOfTheses(assetPostprocessorClass, allMethodsNames, out var methods)) { if (assetPostprocessorClass.GetConstructors().Any(t => t.GetParameters().Count() == 0)) list.Add(new AssetPostprocessor.PostprocessorInfo(assetPostprocessorClass, methods.ToArray())); else LogPostProcessorMissingDefaultConstructor(assetPostprocessorClass); } } cache.Add(importer, list); return list; } [RequiredByNativeCode] static string GetMeshProcessorsHashString() { if (m_MeshProcessorsHashString != null) return m_MeshProcessorsHashString; m_MeshProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(ModelImporter))); return m_MeshProcessorsHashString; } [RequiredByNativeCode] static void PreprocessAsset() { foreach (AssetPostprocessor inst in m_ImportProcessors) { InvokeMethodIfAvailable(inst, "OnPreprocessAsset", null); } } [RequiredByNativeCode] static void PreprocessModel(string pathName) { CallPostProcessMethods("OnPreprocessModel", null); } [RequiredByNativeCode] static void PreprocessSpeedTree(string pathName) { CallPostProcessMethods("OnPreprocessSpeedTree", null); } [RequiredByNativeCode] static void PreprocessAnimation(string pathName) { CallPostProcessMethods("OnPreprocessAnimation", null); } [RequiredByNativeCode] static void PostprocessAnimation(GameObject root, AnimationClip clip) { object[] args = { root, clip }; CallPostProcessMethods("OnPostprocessAnimation", args); } [RequiredByNativeCode] static Material ProcessMeshAssignMaterial(Renderer renderer, Material material) { object[] args = { material, renderer }; Material assignedMaterial; CallPostProcessMethodsUntilReturnedObjectIsValid("OnAssignMaterialModel", args, out assignedMaterial); return assignedMaterial; } [RequiredByNativeCode] static bool ProcessMeshHasAssignMaterial() { foreach (AssetPostprocessor inst in m_ImportProcessors) { if (inst.GetType().GetMethod("OnAssignMaterialModel", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null) return true; } return false; } [RequiredByNativeCode] static void PostprocessMeshHierarchy(GameObject root) { object[] args = { root }; CallPostProcessMethods("OnPostprocessMeshHierarchy", args); } [RequiredByNativeCode] static void PostprocessMesh(GameObject gameObject) { object[] args = { gameObject }; CallPostProcessMethods("OnPostprocessModel", args); } [RequiredByNativeCode] static void PostprocessSpeedTree(GameObject gameObject) { object[] args = { gameObject }; CallPostProcessMethods("OnPostprocessSpeedTree", args); } [RequiredByNativeCode] static void PostprocessMaterial(Material material) { object[] args = { material }; CallPostProcessMethods("OnPostprocessMaterial", args); } [RequiredByNativeCode] static void PreprocessCameraDescription(AssetImportContext assetImportContext, CameraDescription description, Camera camera, AnimationClip[] animations) { assetImportContext.DependsOnCustomDependency(kCameraPostprocessorDependencyName); object[] args = { description, camera, animations }; CallPostProcessMethods("OnPreprocessCameraDescription", args); } [RequiredByNativeCode] static void PreprocessLightDescription(AssetImportContext assetImportContext, LightDescription description, Light light, AnimationClip[] animations) { assetImportContext.DependsOnCustomDependency(kLightPostprocessorDependencyName); object[] args = { description, light, animations }; CallPostProcessMethods("OnPreprocessLightDescription", args); } [RequiredByNativeCode] static void PreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] animations) { object[] args = { description, material, animations }; CallPostProcessMethods("OnPreprocessMaterialDescription", args); } [RequiredByNativeCode] static void PostprocessGameObjectWithUserProperties(GameObject go, string[] prop_names, object[] prop_values) { object[] args = { go, prop_names, prop_values }; CallPostProcessMethods("OnPostprocessGameObjectWithUserProperties", args); } [RequiredByNativeCode] static EditorCurveBinding[] PostprocessGameObjectWithAnimatedUserProperties(GameObject go, EditorCurveBinding[] bindings) { object[] args = { go, bindings }; CallPostProcessMethods("OnPostprocessGameObjectWithAnimatedUserProperties", args); return bindings; } [RequiredByNativeCode] static void PreprocessTexture(string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexturePreprocessorDependencyName); } CallPostProcessMethods("OnPreprocessTexture", null); } [RequiredByNativeCode] static void PreprocessTextureFromScript(string pathName) { CallPostProcessMethods("OnPreprocessTexture", null); } [RequiredByNativeCode] static void PostprocessTexture(Texture2D tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexture2DPostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture", args); } [RequiredByNativeCode] static void PostprocessTextureFromScript(Texture2D tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture", args); } [RequiredByNativeCode] static void PostprocessCubemap(Cubemap tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTextureCubePostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessCubemap", args); } [RequiredByNativeCode] static void PostprocessCubemapFromScript(Cubemap tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessCubemap", args); } [RequiredByNativeCode] static void PostprocessTexture3D(Texture3D tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexture3DPostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture3D", args); } [RequiredByNativeCode] static void PostprocessTexture3DFromScript(Texture3D tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture3D", args); } [RequiredByNativeCode] static void PostprocessTexture2DArray(Texture2DArray tex, string pathName, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTexture2DArrayPostprocessorDependencyName); } object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture2DArray", args); } [RequiredByNativeCode] static void PostprocessTexture2DArrayFromScript(Texture2DArray tex, string pathName) { object[] args = { tex }; CallPostProcessMethods("OnPostprocessTexture2DArray", args); } [RequiredByNativeCode] static void PostprocessSprites(Texture2D tex, string pathName, Sprite[] sprites, AssetImportContext context) { if (context != null) { context.DependsOnCustomDependency(kTextureSpritePostprocessorDependencyName); } object[] args = { tex, sprites }; CallPostProcessMethods("OnPostprocessSprites", args); } [RequiredByNativeCode] static void PostprocessSpritesFromScript(Texture2D tex, string pathName, Sprite[] sprites) { object[] args = { tex, sprites }; CallPostProcessMethods("OnPostprocessSprites", args); } [RequiredByNativeCode] static string GetAudioProcessorsHashString() { if (m_AudioProcessorsHashString != null) return m_AudioProcessorsHashString; m_AudioProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(AudioImporter))); return m_AudioProcessorsHashString; } [RequiredByNativeCode] static void PreprocessAudio(string pathName) { CallPostProcessMethods("OnPreprocessAudio", null); } [RequiredByNativeCode] static void PostprocessAudio(AudioClip clip, string pathName) { object[] args = { clip }; CallPostProcessMethods("OnPostprocessAudio", args); } [RequiredByNativeCode] static string GetPrefabProcessorsHashString() { if (m_PrefabProcessorsHashString != null) return m_PrefabProcessorsHashString; m_PrefabProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(PrefabImporter))); return m_PrefabProcessorsHashString; } [RequiredByNativeCode] static void PostprocessPrefab(GameObject prefabAssetRoot) { object[] args = { prefabAssetRoot }; CallPostProcessMethods("OnPostprocessPrefab", args); } [RequiredByNativeCode] static void PostprocessAssetbundleNameChanged(string assetPath, string previousAssetBundleName, string newAssetBundleName) { object[] args = { assetPath, previousAssetBundleName, newAssetBundleName }; foreach (var assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { var assetPostprocessor = Activator.CreateInstance(assetPostprocessorClass) as AssetPostprocessor; InvokeMethodIfAvailable(assetPostprocessor, "OnPostprocessAssetbundleNameChanged", args); } } [RequiredByNativeCode] static string GetSpeedTreeProcessorsHashString() { if (m_SpeedTreeProcessorsHashString != null) return m_SpeedTreeProcessorsHashString; m_SpeedTreeProcessorsHashString = BuildStaticDependencyHashString(GetSortedStaticPostprocessorTypes(typeof(SpeedTreeImporter))); return m_SpeedTreeProcessorsHashString; } [InitializeOnLoadMethod] static void RefreshCustomDependencies() { AssetDatabase.RegisterCustomDependency(kCameraPostprocessorDependencyName, Hash128.Compute(GetCameraProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kLightPostprocessorDependencyName, Hash128.Compute(GetLightProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexture2DPostprocessorDependencyName, Hash128.Compute(GetTexture2DProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTextureCubePostprocessorDependencyName, Hash128.Compute(GetTextureCubeProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexture3DPostprocessorDependencyName, Hash128.Compute(GetTexture3DProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexture2DArrayPostprocessorDependencyName, Hash128.Compute(GetTexture2DArrayProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTextureSpritePostprocessorDependencyName, Hash128.Compute(GetTextureSpriteProcessorsHashString())); AssetDatabase.RegisterCustomDependency(kTexturePreprocessorDependencyName, Hash128.Compute(GetTexturePreProcessorsHashString())); } static void GetProcessorHashString(string methodName, ref string hashString) { if (hashString != null) return; var versionsByType = new SortedList(); foreach (var assetPostprocessorClass in GetCachedAssetPostprocessorClasses()) { try { if (assetPostprocessorClass.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) != null) { var inst = Activator.CreateInstance(assetPostprocessorClass) as AssetPostprocessor; uint version = inst.GetVersion(); versionsByType.Add(assetPostprocessorClass.FullName, version); } } catch (MissingMethodException) { LogPostProcessorMissingDefaultConstructor(assetPostprocessorClass); } catch (Exception e) { Debug.LogException(e); } } hashString = BuildHashString(versionsByType); } [RequiredByNativeCode] static string GetCameraProcessorsHashString() { GetProcessorHashString("OnPreprocessCameraDescription", ref m_CameraProcessorsHashString); return m_CameraProcessorsHashString; } [RequiredByNativeCode] static string GetLightProcessorsHashString() { GetProcessorHashString("OnPreprocessLightDescription", ref m_LightProcessorsHashString); return m_LightProcessorsHashString; } [RequiredByNativeCode] static string GetTexturePreProcessorsHashString() { GetProcessorHashString("OnPreprocessTexture", ref m_TexturePreprocessorDependencyName); return m_TexturePreprocessorDependencyName; } [RequiredByNativeCode] static string GetTexture2DProcessorsHashString() { GetProcessorHashString("OnPostprocessTexture", ref m_Texture2DProcessorsHashString); return m_Texture2DProcessorsHashString; } [RequiredByNativeCode] static string GetTextureCubeProcessorsHashString() { GetProcessorHashString("OnPostprocessCubemap", ref m_TextureCubeProcessorsHashString); return m_TextureCubeProcessorsHashString; } [RequiredByNativeCode] static string GetTexture3DProcessorsHashString() { GetProcessorHashString("OnPostprocessTexture3D", ref m_Texture3DPostprocessorDependencyName); return m_Texture3DPostprocessorDependencyName; } [RequiredByNativeCode] static string GetTexture2DArrayProcessorsHashString() { GetProcessorHashString("OnPostprocessTexture2DArray", ref m_Texture2DArrayDependencyName); return m_Texture2DArrayDependencyName; } [RequiredByNativeCode] static string GetTextureSpriteProcessorsHashString() { GetProcessorHashString("OnPostprocessSprites", ref m_TextureSpriteDependencyName); return m_TextureSpriteDependencyName; } static bool IsAssetPostprocessorAnalyticsEnabled() { return EditorAnalytics.enabled; } static void CallPostProcessMethodsUntilReturnedObjectIsValid(string methodName, object[] args, out T returnedObject) where T : class { returnedObject = default(T); int invocationCount = 0; float startTime = Time.realtimeSinceStartup; foreach (AssetPostprocessor inst in m_ImportProcessors) { if (InvokeMethodIfAvailable(inst, methodName, args, ref returnedObject)) { invocationCount++; break; } } if (IsAssetPostprocessorAnalyticsEnabled() && invocationCount > 0) { var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData(); methodCallAnalytics.invocationCount = invocationCount; methodCallAnalytics.methodName = methodName; methodCallAnalytics.duration_sec = Time.realtimeSinceStartup - startTime; s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics); } } static void CallPostProcessMethods(string methodName, object[] args) { if (m_ImportProcessors == null) { throw new Exception("m_ImportProcessors is null, InitPostProcessors should be called before any of the post process methods are called."); } if (IsAssetPostprocessorAnalyticsEnabled()) { int invocationCount = 0; float startTime = Time.realtimeSinceStartup; foreach (AssetPostprocessor inst in m_ImportProcessors) { if (InvokeMethodIfAvailable(inst, methodName, args)) invocationCount++; } if (invocationCount > 0) { var methodCallAnalytics = new AssetPostProcessorMethodCallAnalyticsData(); methodCallAnalytics.invocationCount = invocationCount; methodCallAnalytics.methodName = methodName; methodCallAnalytics.duration_sec = Time.realtimeSinceStartup - startTime; s_AnalyticsEventsStack.Peek().postProcessorCalls.Add(methodCallAnalytics); } } else { foreach (AssetPostprocessor inst in m_ImportProcessors) { InvokeMethodIfAvailable(inst, methodName, args); } } } static object InvokeMethod(MethodInfo method, object[] args) { object res = null; using (new EditorPerformanceMarker(method.DeclaringType.Name + "." + method.Name, method.DeclaringType).Auto()) { res = method.Invoke(null, args); } return res; } static bool InvokeMethodIfAvailable(object target, string methodName, object[] args) { var type = target.GetType(); MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (method != null) { using (new EditorPerformanceMarker(type.Name + "." + methodName, type).Auto()) { method.Invoke(target, args); } return true; } return false; } static bool InvokeMethodIfAvailable(object target, string methodName, object[] args, ref T returnedObject) where T : class { var type = target.GetType(); MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (method != null) { using (new EditorPerformanceMarker(type.Name + "." + methodName, type).Auto()) { returnedObject = method.Invoke(target, args) as T; } return returnedObject != null; } return false; } } } ================================================ FILE: Editor/Mono/AssetPreviewUpdater.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Scripting; namespace UnityEditor { internal static class AssetPreviewUpdater { [RequiredByNativeCode] public static Texture2D CreatePreviewForAsset(Object obj, Object[] subAssets, string assetPath) { return CreatePreview(obj, subAssets, assetPath, 128, 128); } // Generate a preview texture for an asset public static Texture2D CreatePreview(Object obj, Object[] subAssets, string assetPath, int width, int height) { var type = CustomEditorAttributes.FindCustomEditorType(obj, false); if (type == null) return null; var info = type.GetMethod("RenderStaticPreview"); if (info == null) { Debug.LogError("Fail to find RenderStaticPreview base method"); return null; } if (info.DeclaringType == typeof(Editor)) return null; var editor = Editor.CreateEditor(obj); if (editor == null) return null; var previewTexture = editor.RenderStaticPreview(assetPath, subAssets, width, height); // For debugging we write the preview to a file (keep) //{ // var bytes = tex.EncodeToPNG(); // string previewFilePath = string.Format ("{0}/../SavedPreview{1}.png", Application.dataPath, (int)(EditorApplication.timeSinceStartup*1000)); // System.IO.File.WriteAllBytes(previewFilePath, bytes); // Debug.Log ("Wrote preview file to: " +previewFilePath); //} Object.DestroyImmediate(editor); return previewTexture; } } } ================================================ FILE: Editor/Mono/AssetStore/AssetStoreAsset.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEditorInternal; using UnityEngine; using UnityEditor; using System.Collections.Generic; namespace UnityEditor { /** * An asset store asset. */ public sealed class AssetStoreAsset { /* Search result data * * This is returned when searching for assets */ public int id; public string name; public string displayName; // name without extension, we cache it to prevent string operations when rendering public string staticPreviewURL; public string dynamicPreviewURL; public string className; public string price; public int packageID; /* Preview data * * This is returned when clicking on an assets for preview */ internal class PreviewInfo { public string packageName; public string packageShortUrl; public int packageSize; public string packageVersion; public int packageRating; public int packageAssetCount; public bool isPurchased; public bool isDownloadable; public string publisherName; public string encryptionKey; public string packageUrl; public float buildProgress; // -1 when not building public float downloadProgress; // -1 when not downloading public string categoryName; } internal PreviewInfo previewInfo; // public Texture2D previewImage; internal AssetBundleCreateRequest previewBundleRequest; internal AssetBundle previewBundle; internal Object previewAsset; internal bool disposed; // public AssetStoreAssetsInfo assetsInfo; public AssetStoreAsset() { disposed = false; } public void Dispose() { if (previewImage != null) { Object.DestroyImmediate(previewImage); previewImage = null; } if (previewBundle != null) { previewBundle.Unload(true); previewBundle = null; previewAsset = null; } disposed = true; } public Object Preview { get { if (previewAsset != null) return previewAsset; return previewImage; } } public bool HasLivePreview { get { return previewAsset != null; } } internal string DebugString { get { string r = string.Format("id: {0}\nname: {1}\nstaticPreviewURL: {2}\ndynamicPreviewURL: {3}\n" + "className: {4}\nprice: {5}\npackageID: {6}", id, name ?? "N/A", staticPreviewURL ?? "N/A", dynamicPreviewURL ?? "N/A", className ?? "N/A", price, packageID); if (previewInfo != null) { r += string.Format("previewInfo {{\n" + " packageName: {0}\n" + " packageShortUrl: {1}\n" + " packageSize: {2}\n" + " packageVersion: {3}\n" + " packageRating: {4}\n" + " packageAssetCount: {5}\n" + " isPurchased: {6}\n" + " isDownloadable: {7}\n" + " publisherName: {8}\n" + " encryptionKey: {9}\n" + " packageUrl: {10}\n" + " buildProgress: {11}\n" + " downloadProgress: {12}\n" + " categoryName: {13}\n" + "}}", previewInfo.packageName ?? "N/A", previewInfo.packageShortUrl ?? "N/A", previewInfo.packageSize, previewInfo.packageVersion ?? "N/A", previewInfo.packageRating, previewInfo.packageAssetCount, previewInfo.isPurchased, previewInfo.isDownloadable, previewInfo.publisherName ?? "N/A", previewInfo.encryptionKey ?? "N/A", previewInfo.packageUrl ?? "N/A", previewInfo.buildProgress, previewInfo.downloadProgress, previewInfo.categoryName ?? "N/A"); } return r; } } } /** * An asset store asset selection. * * This class works as a singleton for keeping the list of selected * asset store assets. This is not handled by the normal select framework * because an asset store asset does not have an instanceID since it is not * actually an asset in the local project. * * Currently there is only support for handling a single selected asset store * asset at a time. This class is somewhat prepared for multiple asset store asset * selections though. */ internal static class AssetStoreAssetSelection { public delegate void AssetsRefreshed(); static internal Dictionary s_SelectedAssets; public static void AddAsset(AssetStoreAsset searchResult, Texture2D placeholderPreviewImage) { if (placeholderPreviewImage != null) searchResult.previewImage = ScaleImage(placeholderPreviewImage, 256, 256); searchResult.previewInfo = null; searchResult.previewBundleRequest = null; // Dynamic previews is asset bundles to be displayed in // the inspector. Static previews are images. if (!string.IsNullOrEmpty(searchResult.dynamicPreviewURL) && searchResult.previewBundle == null) { // Debug.Log("dyn url " + searchResult.disposed.ToString() + " " + searchResult.dynamicPreviewURL); searchResult.disposed = false; // searchResult.previewBundle = AssetBundle.CreateFromFile("/users/jonasd/test.unity3d"); // searchResult.previewAsset = searchResult.previewBundle.mainAsset; // Request the asset bundle data from the url and register a callback AsyncHTTPClient client = new AsyncHTTPClient(searchResult.dynamicPreviewURL); client.doneCallback = delegate(IAsyncHTTPClient c) { if (!client.IsSuccess()) { System.Console.WriteLine("Error downloading dynamic preview: " + client.text); // Try the static preview instead searchResult.dynamicPreviewURL = null; DownloadStaticPreview(searchResult); return; } // We only suppport one asset so grab the first one AssetStoreAsset sel = GetFirstAsset(); // Make sure that the selection hasn't changed meanwhile if (searchResult.disposed || sel == null || searchResult.id != sel.id) { //Debug.Log("dyn disposed " + searchResult.disposed.ToString() + " " + (sel == null ? "null" : sel.id.ToString()) + " " + searchResult.id.ToString()); return; } // Go create the asset bundle in memory from the binary blob asynchronously try { AssetBundleCreateRequest cr = AssetBundle.LoadFromMemoryAsync(c.bytes); // Workaround: Don't subject the bundle to the usual compatibility checks. We want // to stay compatible with previews created in prior versions of Unity and with the // stuff we put into previews, we should generally be able to still load the content // in the editor. cr.DisableCompatibilityChecks(); searchResult.previewBundleRequest = cr; EditorApplication.CallbackFunction callback = null; // The callback will be called each tick and check if the asset bundle is ready double startTime = EditorApplication.timeSinceStartup; callback = () => { AssetStoreUtils.UpdatePreloading(); if (!cr.isDone) { double nowTime = EditorApplication.timeSinceStartup; if (nowTime - startTime > 10.0) { // Timeout. Stop polling EditorApplication.update -= callback; System.Console.WriteLine("Timed out fetch live preview bundle " + (searchResult.dynamicPreviewURL ?? "")); // Debug.Log("Not done Timed out" + cr.progress.ToString() ); } else { // Debug.Log("Not done " + cr.progress.ToString() ); } return; } // Done cooking. Stop polling. EditorApplication.update -= callback; // Make sure that the selection hasn't changed meanwhile AssetStoreAsset sel2 = GetFirstAsset(); if (searchResult.disposed || sel2 == null || searchResult.id != sel2.id) { // No problem. Just ignore. // Debug.Log("dyn late disposed " + searchResult.disposed.ToString() + " " + (sel2 == null ? "null" : sel2.id.ToString()) + " " + searchResult.id.ToString()); } else { searchResult.previewBundle = cr.assetBundle; #pragma warning disable 618 if (cr.assetBundle == null || cr.assetBundle.mainAsset == null) { // Failed downloading live preview. Fallback to static searchResult.dynamicPreviewURL = null; DownloadStaticPreview(searchResult); } else searchResult.previewAsset = searchResult.previewBundle.mainAsset; #pragma warning restore 618 } }; EditorApplication.update += callback; } catch (System.Exception e) { System.Console.Write(e.Message); Debug.Log(e.Message); } }; client.Begin(); } else if (!string.IsNullOrEmpty(searchResult.staticPreviewURL)) { DownloadStaticPreview(searchResult); } // searchResult.previewBundle = null; AddAssetInternal(searchResult); RefreshFromServer(null); } // Also used by AssetStoreToolUtils internal static void AddAssetInternal(AssetStoreAsset searchResult) { if (s_SelectedAssets == null) s_SelectedAssets = new Dictionary(); s_SelectedAssets[searchResult.id] = searchResult; } static void DownloadStaticPreview(AssetStoreAsset searchResult) { AsyncHTTPClient client = new AsyncHTTPClient(searchResult.staticPreviewURL); client.doneCallback = delegate(IAsyncHTTPClient c) { if (!client.IsSuccess()) { System.Console.WriteLine("Error downloading static preview: " + client.text); // Debug.LogError("Error downloading static preview: " + client.text); return; } // Need to put the texture through some scaling magic in order for the // TextureInspector to be able to show it. // TODO: This is a workaround and should be fixed. Texture2D srcTex = c.texture; Texture2D tex = new Texture2D(srcTex.width, srcTex.height, TextureFormat.RGB24, false, true); AssetStorePreviewManager.ScaleImage(tex.width, tex.height, srcTex, tex, null); // tex.Compress(true); searchResult.previewImage = tex; Object.DestroyImmediate(srcTex); AssetStoreAssetInspector.Instance.Repaint(); }; client.Begin(); } // Refresh information about displayed asset by quering the // asset store server. This is typically after the user has // logged in because we need to know if he already owns the // displayed asset. public static void RefreshFromServer(AssetsRefreshed callback) { if (s_SelectedAssets.Count == 0) return; // Refetch assetInfo // Query the asset store for more info List queryAssets = new List(); foreach (KeyValuePair qasset in s_SelectedAssets) queryAssets.Add(qasset.Value); // This will fill the queryAssets with extra preview data AssetStoreClient.AssetsInfo(queryAssets, delegate(AssetStoreAssetsInfo results) { AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.ServiceDisabled; if (!string.IsNullOrEmpty(results.error)) { System.Console.WriteLine("Error performing Asset Store Info search: " + results.error); AssetStoreAssetInspector.OfflineNoticeEnabled = true; //Debug.LogError("Error performing Asset Store Info search: " + results.error); if (callback != null) callback(); return; } AssetStoreAssetInspector.OfflineNoticeEnabled = false; if (results.status == AssetStoreAssetsInfo.Status.Ok) AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.Ok; else if (results.status == AssetStoreAssetsInfo.Status.BasketNotEmpty) AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.BasketNotEmpty; else if (results.status == AssetStoreAssetsInfo.Status.AnonymousUser) AssetStoreAssetInspector.paymentAvailability = AssetStoreAssetInspector.PaymentAvailability.AnonymousUser; AssetStoreAssetInspector.s_PurchaseMessage = results.message; AssetStoreAssetInspector.s_PaymentMethodCard = results.paymentMethodCard; AssetStoreAssetInspector.s_PaymentMethodExpire = results.paymentMethodExpire; AssetStoreAssetInspector.s_PriceText = results.priceText; AssetStoreAssetInspector.Instance.Repaint(); if (callback != null) callback(); }); } private static Texture2D ScaleImage(Texture2D source, int w, int h) { // Bug: When scaling down things look weird unless the source size is // == 0 when mod 4. Therefore we just return null if that's the case. if (source.width % 4 != 0) return null; Texture2D result = new Texture2D(w, h, TextureFormat.RGB24, false, true); Color[] rpixels = result.GetPixels(0); double dx = 1.0 / (double)w; double dy = 1.0 / (double)h; double x = 0; double y = 0; int idx = 0; for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++, idx++) { rpixels[idx] = source.GetPixelBilinear((float)x, (float)y); x += dx; } x = 0; y += dy; } result.SetPixels(rpixels, 0); result.Apply(); return result; } public static bool ContainsAsset(int id) { return s_SelectedAssets != null && s_SelectedAssets.ContainsKey(id); } public static void Clear() { if (s_SelectedAssets == null) return; foreach (var kv in s_SelectedAssets) kv.Value.Dispose(); s_SelectedAssets.Clear(); } public static int Count { get { return s_SelectedAssets == null ? 0 : s_SelectedAssets.Count; } } public static bool Empty { get { return s_SelectedAssets == null ? true : s_SelectedAssets.Count == 0; } } public static AssetStoreAsset GetFirstAsset() { if (s_SelectedAssets == null) return null; var i = s_SelectedAssets.GetEnumerator(); if (!i.MoveNext()) return null; return i.Current.Value; } } /** * In addition to being an inspector for AssetStoreAssets this * inspector works as the object that is selected when an * asset store asset is selected ie. * Selection.object == AssetStoreAssetInspector.Instance * when asset store assets are selected. */ [CustomEditor(typeof(AssetStoreAssetInspector))] internal class AssetStoreAssetInspector : Editor { static AssetStoreAssetInspector s_SharedAssetStoreAssetInspector; public static AssetStoreAssetInspector Instance { get { if (s_SharedAssetStoreAssetInspector == null) { s_SharedAssetStoreAssetInspector = ScriptableObject.CreateInstance(); s_SharedAssetStoreAssetInspector.hideFlags = HideFlags.HideAndDontSave; } return s_SharedAssetStoreAssetInspector; } } class Styles { public GUIStyle link = new GUIStyle(EditorStyles.label); public Styles() { link.normal.textColor = new Color(.26f, .51f, .75f, 1f); } } static Styles styles; bool packageInfoShown = true; // Payment info for all selected assets internal static string s_PurchaseMessage = ""; internal static string s_PaymentMethodCard = ""; internal static string s_PaymentMethodExpire = ""; internal static string s_PriceText = ""; static GUIContent[] sStatusWheel; public static bool OfflineNoticeEnabled { get; set; } // Asset store payment availability internal enum PaymentAvailability { BasketNotEmpty, ServiceDisabled, AnonymousUser, Ok } internal static PaymentAvailability m_PaymentAvailability; internal static PaymentAvailability paymentAvailability { get { if (AssetStoreClient.LoggedOut()) m_PaymentAvailability = PaymentAvailability.AnonymousUser; return m_PaymentAvailability; } set { if (AssetStoreClient.LoggedOut()) m_PaymentAvailability = PaymentAvailability.AnonymousUser; else m_PaymentAvailability = value; } } int lastAssetID; // Callback for curl to call public void OnDownloadProgress(string id, string message, int bytes, int total) { AssetStoreAsset activeAsset = AssetStoreAssetSelection.GetFirstAsset(); if (activeAsset == null) return; AssetStoreAsset.PreviewInfo info = activeAsset.previewInfo; if (info == null) return; if (activeAsset.packageID.ToString() != id) return; if ((message == "downloading" || message == "connecting") && !OfflineNoticeEnabled) { info.downloadProgress = (float)bytes / (float)total; } else { info.downloadProgress = -1f; } Repaint(); } public void Update() { // Repaint if asset has changed. // This has to be done here because the .target is always set to // this inspector when inspecting asset store assets. AssetStoreAsset a = AssetStoreAssetSelection.GetFirstAsset(); bool hasProgress = a != null && a.previewInfo != null && (a.previewInfo.buildProgress >= 0f || a.previewInfo.downloadProgress >= 0f); if ((a == null && lastAssetID != 0) || (a != null && lastAssetID != a.id) || hasProgress) { lastAssetID = a == null ? 0 : a.id; Repaint(); } // Repaint when the main asset of a possibly downloaded bundle is ready for preview if (a != null && a.previewBundle != null) { a.previewBundle.Unload(false); a.previewBundle = null; Repaint(); } } public override void OnInspectorGUI() { if (styles == null) { // Set the singleton in case the DrawEditors() has created this window s_SharedAssetStoreAssetInspector = this; styles = new Styles(); } AssetStoreAsset activeAsset = AssetStoreAssetSelection.GetFirstAsset(); AssetStoreAsset.PreviewInfo info = null; if (activeAsset != null) info = activeAsset.previewInfo; if (activeAsset != null) target.name = string.Format("Asset Store: {0}", activeAsset.name); else target.name = "Asset Store"; EditorGUILayout.BeginVertical(); bool guiEnabled = GUI.enabled; GUI.enabled = activeAsset != null && activeAsset.packageID != 0; if (OfflineNoticeEnabled) { Color col = GUI.color; GUI.color = Color.yellow; GUILayout.Label("Network is offline"); GUI.color = col; } if (activeAsset != null) { string typeName = activeAsset.className == null ? "" : activeAsset.className.Split(new char[] {' '}, 2)[0]; bool isPackage = activeAsset.id == -activeAsset.packageID; if (isPackage) typeName = "Package"; if (activeAsset.HasLivePreview) typeName = activeAsset.Preview.GetType().Name; EditorGUILayout.LabelField("Type", typeName); if (isPackage) { packageInfoShown = true; } else { EditorGUILayout.Separator(); packageInfoShown = EditorGUILayout.Foldout(packageInfoShown , "Part of package", true); } if (packageInfoShown) { EditorGUILayout.LabelField("Name", info == null ? "-" : info.packageName); EditorGUILayout.LabelField("Version", info == null ? "-" : info.packageVersion); string price = info == null ? "-" : (!string.IsNullOrEmpty(activeAsset.price) ? activeAsset.price : "free"); EditorGUILayout.LabelField("Price", price); string rating = info != null && info.packageRating >= 0 ? info.packageRating + " of 5" : "-"; EditorGUILayout.LabelField("Rating", rating); EditorGUILayout.LabelField("Size", info == null ? "-" : intToSizeString(info.packageSize)); string assetCount = info != null && info.packageAssetCount >= 0 ? info.packageAssetCount.ToString() : "-"; EditorGUILayout.LabelField("Asset count", assetCount); GUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Web page"); bool hasPageUrl = info != null && info.packageShortUrl != null && info.packageShortUrl != ""; bool guiBefore = GUI.enabled; GUI.enabled = hasPageUrl; if (GUILayout.Button(hasPageUrl ? new GUIContent(info.packageShortUrl, "View in browser") : EditorGUIUtility.TempContent("-"), styles.link)) { Application.OpenURL(info.packageShortUrl); } if (GUI.enabled) EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link); GUI.enabled = guiBefore; GUILayout.EndHorizontal(); EditorGUILayout.LabelField("Publisher", info == null ? "-" : info.publisherName); } if (activeAsset.id != 0) { GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Open Asset Store", GUILayout.Height(40), GUILayout.Width(120))) { OpenItemInAssetStore(activeAsset); GUIUtility.ExitGUI(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.FlexibleSpace(); } EditorWrapper editor = previewEditor; if (editor != null && activeAsset != null && activeAsset.HasLivePreview) editor.OnAssetStoreInspectorGUI(); GUI.enabled = guiEnabled; EditorGUILayout.EndVertical(); } public static void OpenItemInAssetStore(AssetStoreAsset activeAsset) { if (activeAsset.id != 0) { AssetStore.Open(string.Format("content/{0}?assetID={1}", activeAsset.packageID, activeAsset.id)); } } private static string intToSizeString(int inValue) { if (inValue < 0) return "unknown"; float val = (float)inValue; string[] scale = new string[] { "TB", "GB", "MB", "KB", "Bytes" }; int idx = scale.Length - 1; while (val > 1000.0f && idx >= 0) { val /= 1000f; idx--; } if (idx < 0) return ""; return string.Format("{0:#.##} {1}", val, scale[idx]); } public override bool HasPreviewGUI() { return (target != null && AssetStoreAssetSelection.Count != 0); } EditorWrapper m_PreviewEditor; Object m_PreviewObject; public void OnEnable() { EditorApplication.update += Update; AssetStoreUtils.RegisterDownloadDelegate(this); } public void OnDisable() { EditorApplication.update -= Update; if (m_PreviewEditor != null) { m_PreviewEditor.Dispose(); m_PreviewEditor = null; } if (m_PreviewObject != null) m_PreviewObject = null; AssetStoreUtils.UnRegisterDownloadDelegate(this); } private EditorWrapper previewEditor { get { AssetStoreAsset asset = AssetStoreAssetSelection.GetFirstAsset(); if (asset == null) return null; Object preview = asset.Preview; if (preview == null) return null; if (preview != m_PreviewObject) { m_PreviewObject = preview; if (m_PreviewEditor != null) m_PreviewEditor.Dispose(); m_PreviewEditor = EditorWrapper.Make(m_PreviewObject, EditorFeatures.PreviewGUI); } return m_PreviewEditor; } } public override void OnPreviewSettings() { AssetStoreAsset asset = AssetStoreAssetSelection.GetFirstAsset(); if (asset == null) return; EditorWrapper editor = previewEditor; if (editor != null && asset.HasLivePreview) editor.OnPreviewSettings(); } public override string GetInfoString() { EditorWrapper editor = previewEditor; AssetStoreAsset a = AssetStoreAssetSelection.GetFirstAsset(); if (a == null) return "No item selected"; if (editor != null && a.HasLivePreview) return editor.GetInfoString(); return ""; } public override void OnPreviewGUI(Rect r, GUIStyle background) { if (m_PreviewObject == null) return; EditorWrapper editor = previewEditor; // Special handling for animation clips because they only have // an interactive preview available which shows play button etc. // The OnPreviewGUI is also used for the small icons in the top // of the inspectors where buttons should not be rendered. if (editor != null && m_PreviewObject is AnimationClip) editor.OnPreviewGUI(r, background); // currently renders nothing for animation clips else OnInteractivePreviewGUI(r, background); } public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) { EditorWrapper editor = previewEditor; if (editor != null) { editor.OnInteractivePreviewGUI(r, background); } // If the live preview is not available yes the show a spinner AssetStoreAsset a = AssetStoreAssetSelection.GetFirstAsset(); if (a != null && !a.HasLivePreview && !string.IsNullOrEmpty(a.dynamicPreviewURL)) { GUIContent c = StatusWheel; r.y += (r.height - c.image.height) / 2f; r.x += (r.width - c.image.width) / 2f; GUI.Label(r, StatusWheel); Repaint(); } } static GUIContent StatusWheel { get { if (sStatusWheel == null) { sStatusWheel = new GUIContent[12]; for (int i = 0; i < 12; i++) { GUIContent gc = new GUIContent(); gc.image = EditorGUIUtility.LoadIcon("WaitSpin" + i.ToString("00")) as Texture2D; sStatusWheel[i] = gc; } } int frame = (int)Mathf.Repeat(Time.realtimeSinceStartup * 10, 11.99f); return sStatusWheel[frame]; } } public override GUIContent GetPreviewTitle() { return GUIContent.Temp("Asset Store Preview"); } } // Inspector class } ================================================ FILE: Editor/Mono/AssetStore/AssetStoreClient.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License /* * Server class handling communication with the server backend * * Jonas Drewsen - (C) Unity3d.com - 2011 * */ using UnityEngine; using UnityEditor; using UnityEditorInternal; using System.Text.RegularExpressions; using System.IO; using System.Collections.Generic; using System; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; namespace UnityEditor { /** * The raw response from the asset store before being parsed into * specific result class (see below). */ class AssetStoreResponse { internal IAsyncHTTPClient job; public Dictionary dict; public bool ok; public bool failed { get { return !ok; } } public string message { get { if (dict == null || !dict.ContainsKey("message")) return null; return dict["message"].AsString(true); } } // Encode a string into a json string private static string EncodeString(string str) { str = str.Replace("\"", "\\\""); str = str.Replace("\\", "\\\\"); str = str.Replace("\b", "\\b"); str = str.Replace("\f", "\\f"); str = str.Replace("\n", "\\n"); str = str.Replace("\r", "\\r"); str = str.Replace("\t", "\\t"); // We do not use \uXXXX specifier but direct unicode in the string. return str; } public override string ToString() { string res = "{"; string delim = ""; foreach (KeyValuePair kv in dict) { res += delim + '"' + EncodeString(kv.Key) + "\" : " + kv.Value; delim = ", "; } return res + "}"; } } /** * All results derive from this class. */ abstract class AssetStoreResultBase where Derived : class { public delegate void Callback(Derived res); private Callback callback; public string error; public string warnings; public AssetStoreResultBase(Callback cb) { callback = cb; warnings = ""; } public void Parse(AssetStoreResponse response) { if (response.job != null && response.job.IsSuccess()) { if (response.job.responseCode >= 300) error = string.Format("HTTP status code {0}", response.job.responseCode); else if (response.dict.ContainsKey("error")) error = response.dict["error"].AsString(true); else Parse(response.dict); } else { string url = response.job == null ? "nulljob" : (response.job.url == null ? "null" : response.job.url); error = "Error receiving response from server on url '" + url + "': " + (response.job.text ?? "n/a"); } callback(this as Derived); } abstract protected void Parse(Dictionary dict); } /** * Result that contains the list of asset matching a search criteria. */ class AssetStoreSearchResults : AssetStoreResultBase { internal struct Group { public static Group Create() { Group g = new Group(); g.assets = new List(); g.label = ""; g.name = ""; g.offset = 0; g.limit = -1; // no limit return g; } public List assets; public int totalFound; public string label; public string name; public int offset; public int limit; } internal List groups = new List(); public AssetStoreSearchResults(Callback c) : base(c) {} protected override void Parse(Dictionary dict) { foreach (JSONValue v in dict["groups"].AsList(true)) { Group group = Group.Create(); ParseList(v, ref group); groups.Add(group); } JSONValue offsets = dict["query"]["offsets"]; List limits = dict["query"]["limits"].AsList(true); int idx = 0; foreach (JSONValue v in offsets.AsList(true)) { Group g = groups[idx]; g.offset = (int)v.AsFloat(true); g.limit = (int)limits[idx].AsFloat(true); groups[idx] = g; idx++; } } static string StripExtension(string path) { if (path == null) return null; int i = path.LastIndexOf("."); return i < 0 ? path : path.Substring(0, i); } private void ParseList(JSONValue matches, ref Group group) { List assets = group.assets; if (matches.ContainsKey("error")) error = matches["error"].AsString(true); if (matches.ContainsKey("warnings")) warnings = matches["warnings"].AsString(true); if (matches.ContainsKey("name")) group.name = matches["name"].AsString(true); if (matches.ContainsKey("label")) group.label = matches["label"].AsString(true); group.label = group.label ?? group.name; // fallback if (matches.ContainsKey("total_found")) group.totalFound = (int)matches["total_found"].AsFloat(true); if (matches.ContainsKey("matches")) { foreach (JSONValue asset in matches["matches"].AsList(true)) { AssetStoreAsset res = new AssetStoreAsset(); if (!asset.ContainsKey("id") || !asset.ContainsKey("name") || !asset.ContainsKey("package_id")) continue; res.id = (int)asset["id"].AsFloat(); res.name = asset["name"].AsString(); res.displayName = StripExtension(res.name); res.packageID = (int)asset["package_id"].AsFloat(); if (asset.ContainsKey("static_preview_url")) res.staticPreviewURL = asset["static_preview_url"].AsString(); if (asset.ContainsKey("dynamic_preview_url")) res.dynamicPreviewURL = asset["dynamic_preview_url"].AsString(); res.className = asset.ContainsKey("class_name") ? asset["class_name"].AsString() : ""; if (asset.ContainsKey("price")) res.price = asset["price"].AsString(); assets.Add(res); } } } } /** * Result that will flesh out the specified AssetStoreAssets with * info to be displayed in an inspector. */ class AssetStoreAssetsInfo : AssetStoreResultBase { internal enum Status { BasketNotEmpty, ServiceDisabled, AnonymousUser, Ok } internal Status status; internal Dictionary assets = new Dictionary(); internal bool paymentTokenAvailable; internal string paymentMethodCard; internal string paymentMethodExpire; internal float price; internal float vat; internal string currency; internal string priceText; internal string vatText; internal string message; internal AssetStoreAssetsInfo(Callback c, List assets) : base(c) { foreach (AssetStoreAsset a in assets) this.assets[a.id] = a; } protected override void Parse(Dictionary dict) { Dictionary purchaseInfo = dict["purchase_info"].AsDict(true); string statusStr = purchaseInfo["status"].AsString(true); if (statusStr == "basket-not-empty") status = Status.BasketNotEmpty; else if (statusStr == "service-disabled") status = Status.ServiceDisabled; else if (statusStr == "user-anonymous") status = Status.AnonymousUser; else if (statusStr == "ok") status = Status.Ok; paymentTokenAvailable = purchaseInfo["payment_token_available"].AsBool(); if (purchaseInfo.ContainsKey("payment_method_card")) paymentMethodCard = purchaseInfo["payment_method_card"].AsString(true); if (purchaseInfo.ContainsKey("payment_method_expire")) paymentMethodExpire = purchaseInfo["payment_method_expire"].AsString(true); price = purchaseInfo["price"].AsFloat(true); vat = purchaseInfo["vat"].AsFloat(true); priceText = purchaseInfo["price_text"].AsString(true); vatText = purchaseInfo["vat_text"].AsString(true); currency = purchaseInfo["currency"].AsString(true); message = purchaseInfo.ContainsKey("message") ? purchaseInfo["message"].AsString(true) : null; List assetsIn = dict["results"].AsList(true); foreach (JSONValue val in assetsIn) { AssetStoreAsset asset; int aid = 0; if (val["id"].IsString()) aid = int.Parse(val["id"].AsString()); else aid = (int)val["id"].AsFloat(); if (!assets.TryGetValue(aid, out asset)) continue; if (asset.previewInfo == null) asset.previewInfo = new AssetStoreAsset.PreviewInfo(); AssetStoreAsset.PreviewInfo a = asset.previewInfo; asset.className = val["class_names"].AsString(true).Trim(); a.packageName = val["package_name"].AsString(true).Trim(); a.packageShortUrl = val["short_url"].AsString(true).Trim(); // a.packagePrice = val.ContainsKey("price_text") ? val["price_text"].AsString(true).Trim() : null; asset.price = val.ContainsKey("price_text") ? val["price_text"].AsString(true).Trim() : null; a.packageSize = int.Parse(val.Get("package_size").IsNull() ? "-1" : val["package_size"].AsString(true)); asset.packageID = int.Parse(val["package_id"].AsString()); a.packageVersion = val["package_version"].AsString(); a.packageRating = int.Parse(val.Get("rating").IsNull() || val["rating"].AsString(true).Length == 0 ? "-1" : val["rating"].AsString(true)); a.packageAssetCount = int.Parse(val["package_asset_count"].IsNull() ? "-1" : val["package_asset_count"].AsString(true)); a.isPurchased = val.ContainsKey("purchased") ? val["purchased"].AsBool(true) : false; a.isDownloadable = a.isPurchased || asset.price == null; a.publisherName = val["publisher_name"].AsString(true).Trim(); // a.previewBundle = (!val.ContainsKey("preview_bundle") || val["preview_bundle"].IsNull()) ? "" : val["preview_bundle"].AsString(true); a.packageUrl = val.Get("package_url").IsNull() ? "" : val["package_url"].AsString(true); a.encryptionKey = val.Get("encryption_key").IsNull() ? "" : val["encryption_key"].AsString(true); a.categoryName = val.Get("category_name").IsNull() ? "" : val["category_name"].AsString(true); a.buildProgress = -1f; a.downloadProgress = -1f; } } } /** * Success of purchasing a package */ class PurchaseResult : AssetStoreResultBase { public enum Status { BasketNotEmpty, ServiceDisabled, AnonymousUser, PasswordMissing, PasswordWrong, PurchaseDeclined, Ok } public Status status; public int packageID; public string message; public PurchaseResult(Callback c) : base(c) { } protected override void Parse(Dictionary dict) { packageID = int.Parse(dict["package_id"].AsString()); message = dict.ContainsKey("message") ? dict["message"].AsString(true) : null; string statusStr = dict["status"].AsString(true); if (statusStr == "basket-not-empty") status = Status.BasketNotEmpty; else if (statusStr == "service-disabled") status = Status.ServiceDisabled; else if (statusStr == "user-anonymous") status = Status.AnonymousUser; else if (statusStr == "password-missing") status = Status.PasswordMissing; else if (statusStr == "password-wrong") status = Status.PasswordWrong; else if (statusStr == "purchase-declined") status = Status.PurchaseDeclined; else if (statusStr == "ok") status = Status.Ok; } } /** * Status of building a package on the asset store server. */ class BuildPackageResult : AssetStoreResultBase { internal AssetStoreAsset asset; internal int packageID; internal BuildPackageResult(AssetStoreAsset asset, Callback c) : base(c) { this.asset = asset; packageID = -1; } protected override void Parse(Dictionary dict) { dict = dict["download"].AsDict(); packageID = int.Parse(dict["id"].AsString()); if (packageID != asset.packageID) { Debug.LogError("Got asset store server build result from mismatching package"); return; } asset.previewInfo.packageUrl = dict.ContainsKey("url") ? dict["url"].AsString(true) : ""; asset.previewInfo.encryptionKey = dict.ContainsKey("key") ? dict["key"].AsString(true) : ""; // The asset store is weird. Of pct is 0 it returns a 0.0 float, if it is > 0 it returns a string with the pct ie "23". asset.previewInfo.buildProgress = (dict["progress"].IsFloat() ? dict["progress"].AsFloat(true) : float.Parse(dict["progress"].AsString(true), System.Globalization.CultureInfo.InvariantCulture)) / 100.0f; } } /** * The main API class for communication with the asset store server. * Most interesting methods are the static ones specified in the * bottom of this class. */ class AssetStoreClient { const string kUnauthSessionID = "26c4202eb475d02864b40827dfff11a14657aa41"; internal enum LoginState { LOGGED_OUT, IN_PROGRESS, LOGGED_IN, LOGIN_ERROR } static string s_AssetStoreUrl = null; static string s_AssetStoreSearchUrl = null; static LoginState sLoginState = AssetStoreClient.LoginState.LOGGED_OUT; static string sLoginErrorMessage = null; public static string LoginErrorMessage { get { return sLoginErrorMessage; } } public delegate void DoneCallback(AssetStoreResponse response); public delegate void DoneLoginCallback(string errorMessage); // Version parameters expected by the server static string VersionParams { get { return "unityversion=" + System.Uri.EscapeDataString(Application.unityVersion) + "&skip_terms=1"; } } // The base asset store url as parsed from the loader.html file static string AssetStoreUrl { get { if (s_AssetStoreUrl == null) s_AssetStoreUrl = AssetStoreUtils.GetAssetStoreUrl(); // s_AssetStoreUrl = "https://kharma-test.unity3d.com"; return s_AssetStoreUrl; } } // The base asset store url as parsed from the loader.html file static string AssetStoreSearchUrl { get { if (s_AssetStoreSearchUrl == null) s_AssetStoreSearchUrl = AssetStoreUtils.GetAssetStoreSearchUrl(); // s_AssetStoreSearchUrl = "http://shawarma-test.unity3d.com"; return s_AssetStoreSearchUrl; } } static AssetStoreClient() { ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; } // The base server API url. This is used for all the normal request towards the server API. static string APIUrl(string path) { return String.Format("{0}/api{2}.json?{1}" , AssetStoreUrl, VersionParams, path); } static string APISearchUrl(string path) { return String.Format("{0}/public-api{2}.json?{1}" , AssetStoreSearchUrl, VersionParams, path); } // The saved session ID if the user has allowed remembering it. static string SavedSessionID { get { if (RememberSession) return EditorPrefs.GetString("kharma.sessionid", ""); return ""; } set { EditorPrefs.SetString("kharma.sessionid", value); } } public static bool HasSavedSessionID { get { return !string.IsNullOrEmpty(SavedSessionID); } } internal static string ActiveSessionID { get { if (AssetStoreContext.SessionHasString("kharma.active_sessionid")) return AssetStoreContext.SessionGetString("kharma.active_sessionid"); return ""; } set { AssetStoreContext.SessionSetString("kharma.active_sessionid", value); } } public static bool HasActiveSessionID { get { return !string.IsNullOrEmpty(ActiveSessionID); } } static string ActiveOrUnauthSessionID { get { string s = ActiveSessionID; if (s == "") return kUnauthSessionID; return s; } } static public bool RememberSession { get { return EditorPrefs.GetString("kharma.remember_session") == "1"; } set { EditorPrefs.SetString("kharma.remember_session", value ? "1" : "0"); } } // The authentication token static string GetToken() { return InternalEditorUtility.GetAuthToken(); } // Login status public static bool LoggedIn() { return !string.IsNullOrEmpty(ActiveSessionID); } public static bool LoggedOut() { return string.IsNullOrEmpty(ActiveSessionID); } public static bool LoginError() { return sLoginState == LoginState.LOGIN_ERROR; } public static bool LoginInProgress() { return sLoginState == LoginState.IN_PROGRESS; } internal static void LoginWithCredentials(string username, string password, bool rememberMe, DoneLoginCallback callback) { if (sLoginState == LoginState.IN_PROGRESS) { Debug.LogError("Tried to login with credentials while already in progress of logging in"); return; } sLoginState = LoginState.IN_PROGRESS; RememberSession = rememberMe; string url = AssetStoreUrl + "/login?skip_terms=1"; AssetStoreClient.sLoginErrorMessage = null; AsyncHTTPClient client = new AsyncHTTPClient(url.Replace("http://", "https://")); client.postData = "user=" + username + "&pass=" + password; client.header["X-Unity-Session"] = kUnauthSessionID + GetToken(); client.doneCallback = WrapLoginCallback(callback); client.Begin(); } /* * Tries to login using a remembered session */ internal static void LoginWithRememberedSession(DoneLoginCallback callback) { if (sLoginState == LoginState.IN_PROGRESS) { Debug.LogError("Tried to login with remembered session while already in progress of logging in"); return; } sLoginState = LoginState.IN_PROGRESS; // Make sure the session is not present if we're not allowed to use it if (!RememberSession) SavedSessionID = ""; string url = AssetStoreUrl + "/login?skip_terms=1&reuse_session=" + SavedSessionID; AssetStoreClient.sLoginErrorMessage = null; AsyncHTTPClient client = new AsyncHTTPClient(url); client.header["X-Unity-Session"] = kUnauthSessionID + GetToken(); client.doneCallback = WrapLoginCallback(callback); client.Begin(); } // Helper function for login callbacks static AsyncHTTPClient.DoneCallback WrapLoginCallback(DoneLoginCallback callback) { return delegate(IAsyncHTTPClient job) { // We're logging in string msg = job.text; if (!job.IsSuccess()) { AssetStoreClient.sLoginState = LoginState.LOGIN_ERROR; AssetStoreClient.sLoginErrorMessage = job.responseCode >= 200 && job.responseCode < 300 ? msg : "Failed to login - please retry"; } else if (msg.StartsWith(" param, DoneCallback callback) { AsyncHTTPClient client = new AsyncHTTPClient(url); client.header["X-Unity-Session"] = ActiveOrUnauthSessionID + GetToken(); client.postDictionary = param; client.doneCallback = WrapJsonCallback(callback); client.Begin(); return client; } /* TODO: Implement PUT from a filepath in back end curl code // Create a pending HTTP PUT request to the server static void CreateJSONRequestPut(string url, string filepath, DoneCallback callback) { AsyncHTTPClient job = new AsyncHTTPClient(url, "PUT"); client.SetUploadFilePath(filepath); client.header["X-Unity-Session"] = ActiveOrUnauthSessionID + GetToken(); client.doneCallback = WrapJsonCallback(callback); client.Begin(); } */ /* Handle HTTP results and forward them to the original callback. * * This will callback any handler registered for requests that has finished. */ private static AsyncHTTPClient.DoneCallback WrapJsonCallback(DoneCallback callback) { return delegate(IAsyncHTTPClient job) { if (job.IsDone()) { try { AssetStoreResponse c = ParseContent(job); callback(c); } catch (Exception ex) { Debug.Log("Uncaught exception in async net callback: " + ex.Message); Debug.Log(ex.StackTrace); } } }; } /* * Parse the HTTP response as a JSON string and into a AssetStoreResponse object. */ static AssetStoreResponse ParseContent(IAsyncHTTPClient job) { AssetStoreResponse resp = new AssetStoreResponse(); resp.job = job; resp.dict = null; resp.ok = false; AsyncHTTPClient.State state = job.state; string content = job.text; if (!AsyncHTTPClient.IsSuccess(state)) { Console.WriteLine(content); // Debug.Log("Request error: " + content); return resp; // abort } string status; string message; resp.dict = ParseJSON(content, out status, out message); if (status == "error") { Debug.LogError("Request error (" + status + "): " + message); return resp; } resp.ok = true; return resp; } /* * Parse the HTTP response as a JSON string into a string/JSONValue dictionary */ static Dictionary ParseJSON(string content, out string status, out string message) { // extract ids etc. from json message message = null; status = null; JSONValue jval; try { JSONParser parser = new JSONParser(content); jval = parser.Parse(); } catch (JSONParseException ex) { Debug.Log("Error parsing server reply: " + content); Debug.Log(ex.Message); return null; } Dictionary dict; try { dict = jval.AsDict(true); if (dict == null) { Debug.Log("Error parsing server message: " + content); return null; } // Old backend encapsulated all replies in a "result" dict. Just unwrap that here. if (dict.ContainsKey("result") && dict["result"].IsDict()) { dict = dict["result"].AsDict(true); } if (dict.ContainsKey("message")) message = dict["message"].AsString(true); if (dict.ContainsKey("status")) status = dict["status"].AsString(true); else if (dict.ContainsKey("error")) { status = dict["error"].AsString(true); if (status == "") status = "ok"; } else status = "ok"; } catch (JSONTypeException ex) { Debug.Log("Error parsing server reply. " + content); Debug.Log(ex.Message); return null; } return dict; } internal struct SearchCount { public string name; public int offset; public int limit; } /* * Searches the asset store for assets and passes the results to DoneCallback */ internal static AsyncHTTPClient SearchAssets(string searchString, string[] requiredClassNames, string[] assetLabels, List counts, AssetStoreSearchResults.Callback callback) { string offsets = ""; string limits = ""; string groupNames = ""; string delim = ""; foreach (SearchCount v in counts) { offsets += delim + v.offset; limits += delim + v.limit; groupNames += delim + v.name; delim = ","; } // If one af the class names is "MonoScript" then also include "Script" since // that is what asset store expects if (Array.Exists(requiredClassNames, (string a) => { return a.Equals("MonoScript", StringComparison.OrdinalIgnoreCase); })) { Array.Resize(ref requiredClassNames, requiredClassNames.Length + 1); requiredClassNames[requiredClassNames.Length - 1] = "Script"; } string url = string.Format("{0}&q={1}&c={2}&l={3}&O={4}&N={5}&G={6}", APISearchUrl("/search/assets"), System.Uri.EscapeDataString(searchString), System.Uri.EscapeDataString(string.Join(",", requiredClassNames)), System.Uri.EscapeDataString(string.Join(",", assetLabels)), offsets, limits, groupNames); //Debug.Log(url); //Debug.Log("session key " + ActiveOrUnauthSessionID + GetToken()); AssetStoreSearchResults r = new AssetStoreSearchResults(callback); return CreateJSONRequest(url, delegate(AssetStoreResponse ar) { r.Parse(ar); }); } /* * Looks up assets by ID in the asset store and passes the results to DoneCallback */ internal static AsyncHTTPClient AssetsInfo(List assets, AssetStoreAssetsInfo.Callback callback) { string url = APIUrl("/assets/list"); foreach (AssetStoreAsset asset in assets) url += "&id=" + asset.id; // Debug.Log(url); AssetStoreAssetsInfo r = new AssetStoreAssetsInfo(callback, assets); return CreateJSONRequest(url, delegate(AssetStoreResponse ar) { r.Parse(ar); }); } /* * Returns purchase info for the package that has assetID as part of it. */ internal static AsyncHTTPClient DirectPurchase(int packageID, string password, PurchaseResult.Callback callback) { string url = APIUrl(string.Format("/purchase/direct/{0}", packageID.ToString())); // Debug.Log(url); PurchaseResult r = new PurchaseResult(callback); Dictionary d = new Dictionary(); d["password"] = password; return CreateJSONRequestPost(url, d, delegate(AssetStoreResponse ar) { r.Parse(ar); }); } internal static AsyncHTTPClient BuildPackage(AssetStoreAsset asset, BuildPackageResult.Callback callback) { string url = APIUrl("/content/download/" + asset.packageID); // Debug.Log(url); BuildPackageResult r = new BuildPackageResult(asset, callback); return CreateJSONRequest(url, delegate(AssetStoreResponse ar) { r.Parse(ar); }); } } } ================================================ FILE: Editor/Mono/AssetStore/AssetStoreContext.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEditor; using UnityEditorInternal; using System; using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Linq; using System.IO; using UnityEditor.Web; namespace UnityEditor { [InitializeOnLoad] internal partial class AssetStoreContext { static AssetStoreContext() { AssetStoreContext.GetInstance(); } public static AssetStoreContext GetInstance() { if (s_Instance == null) { s_Instance = new AssetStoreContext(); } return s_Instance; } public string GetInitialOpenURL() { if (initialOpenURL != null) { string tmp = initialOpenURL; initialOpenURL = null; return tmp; } else { return ""; } } public string GetAuthToken() { return UnityEditorInternal.InternalEditorUtility.GetAuthToken(); } [Obsolete("GetLicenseFlags is no longer supported", error: true)] public int[] GetLicenseFlags() { return new int[] {}; } public string GetString(string key) { return EditorPrefs.GetString(key); } public int GetInt(string key) { return EditorPrefs.GetInt(key); } public float GetFloat(string key) { return EditorPrefs.GetFloat(key); } public void SetString(string key, string value) { EditorPrefs.SetString(key, value); } public void SetInt(string key, int value) { EditorPrefs.SetInt(key, value); } public void SetFloat(string key, float value) { EditorPrefs.SetFloat(key, value); } public bool HasKey(string key) { return EditorPrefs.HasKey(key); } public void DeleteKey(string key) { EditorPrefs.DeleteKey(key); } public int GetSkinIndex() { return EditorGUIUtility.skinIndex; } public bool OpenPackage(string id) { return OpenPackage(id, "default"); } public bool OpenPackage(string id, string action) { return OpenPackageInternal(id); } public static bool OpenPackageInternal(string id) { Match match = s_GeneratedIDRegExp.Match(id); if (match.Success && File.Exists(match.Groups[1].Value)) // If id looks like a path name, just try to open that { AssetDatabase.ImportPackage(match.Groups[1].Value, true); return true; } else { foreach (PackageInfo package in PackageInfo.GetPackageList()) { if (package.jsonInfo != "") { JSONValue item = JSONParser.SimpleParse(package.jsonInfo); string itemID = item.Get("id").IsNull() ? null : item["id"].AsString(true); if (itemID != null && itemID == id && File.Exists(package.packagePath)) { AssetDatabase.ImportPackage(package.packagePath, true); return true; } } } } Debug.LogError("Unknown package ID " + id); return false; } public void OpenBrowser(string url) { Application.OpenURL(url); } [Serializable] public struct DownloadAssetInfo { public string package_id; public string package_name; public string publisher_name; public string category_name; } public void Download(Package package, DownloadInfo downloadInfo) { Download( downloadInfo.id, downloadInfo.url, downloadInfo.key, package.title, package.publisher.label, package.category.label, null ); } public static void Download(string package_id, string url, string key, string package_name, string publisher_name, string category_name, AssetStoreUtils.DownloadDoneCallback doneCallback) { string[] dest = PackageStorePath(publisher_name, category_name, package_name, package_id, url); JSONValue existing = JSONParser.SimpleParse(AssetStoreUtils.CheckDownload(package_id, url, dest, key)); // If the package is actively being downloaded right now just return if (existing.Get("in_progress").AsBool(true)) { Debug.Log("Will not download " + package_name + ". Download is already in progress."); return; } // The package is not being downloaded. // If the package has previously been partially downloaded then // resume that download. string existingUrl = existing.Get("download.url").AsString(true); string existingKey = existing.Get("download.key").AsString(true); bool resumeOK = (existingUrl == url && existingKey == key); JSONValue download = new JSONValue(); download["url"] = url; download["key"] = key; JSONValue parameters = new JSONValue(); parameters["download"] = download; AssetStoreUtils.Download(package_id, url, dest, key, parameters.ToString(), resumeOK, doneCallback); EditorAnalytics.SendAssetDownloadEvent(new DownloadAssetInfo() { package_id = package_id, package_name = package_name, publisher_name = publisher_name, category_name = category_name }); } /// /// Create an array consisting of publisherName, categoryName and packageName /// This is to be used by AssetStoreUtils.*Download functions /// public static string[] PackageStorePath(string publisher_name, string category_name, string package_name, string package_id, string url) { string[] dest = { publisher_name, category_name, package_name }; for (int i = 0; i < 3; i++) dest[i] = s_InvalidPathCharsRegExp.Replace(dest[i], ""); // If package name cannot be stored as a valid file name, use the package id if (dest[2] == "") dest[2] = s_InvalidPathCharsRegExp.Replace(package_id, ""); // If still no valid chars use a mangled url as the file name if (dest[2] == "") dest[2] = s_InvalidPathCharsRegExp.Replace(url, ""); return dest; } public PackageList GetPackageList() { var packages = new Dictionary(); var packageInfos = PackageInfo.GetPackageList(); foreach (PackageInfo info in packageInfos) { Package package = new Package(); if (info.jsonInfo == "") { package.title = System.IO.Path.GetFileNameWithoutExtension(info.packagePath); package.id = info.packagePath; if (IsBuiltinStandardAsset(info.packagePath)) { package.publisher = new LabelAndId { label = "Unity Technologies", id = "1" }; package.category = new LabelAndId { label = "Prefab Packages", id = "4" }; package.version = "3.5.0.0"; } } else { var jsonData = JSONParser.SimpleParse(info.jsonInfo); if (jsonData.IsNull()) continue; package.Initialize(jsonData); if (package.id == null) { var linkId = jsonData.Get("link.id"); if (!linkId.IsNull()) package.id = linkId.AsString(); else package.id = info.packagePath; } } package.local_icon = info.iconURL; package.local_path = info.packagePath; // If no package with the same ID is in the dictionary yet or if the current package // is newer than what we currently have in the dictionary, add the package to the // dictionary. if (!packages.ContainsKey(package.id) || packages[package.id].version_id == null || packages[package.id].version_id == "-1" || (package.version_id != null && package.version_id != "-1" && Int32.Parse(packages[package.id].version_id) <= Int32.Parse(package.version_id))) { packages[package.id] = package; } } var results = packages.Values.ToArray(); return new PackageList { results = results }; } private bool IsBuiltinStandardAsset(string path) { return s_StandardPackageRegExp.IsMatch(path); } private static Regex s_StandardPackageRegExp = new Regex(@"/Standard Packages/(Character\ Controller|Glass\ Refraction\ \(Pro\ Only\)|Image\ Effects\ \(Pro\ Only\)|Light\ Cookies|Light\ Flares|Particles|Physic\ Materials|Projectors|Scripts|Standard\ Assets\ \(Mobile\)|Skyboxes|Terrain\ Assets|Toon\ Shading|Tree\ Creator|Water\ \(Basic\)|Water\ \(Pro\ Only\))\.unitypackage$", RegexOptions.IgnoreCase); private static Regex s_GeneratedIDRegExp = new Regex(@"^\{(.*)\}$"); private static Regex s_InvalidPathCharsRegExp = new Regex(@"[^a-zA-Z0-9() _-]"); internal string initialOpenURL; private static AssetStoreContext s_Instance; // Some data is created through reflection in C++ and then // passed on to us. Shut up warning about fields that are // never assigned to. #pragma warning disable 0649 // The following classes are used for data interchange with JavaScript. // Public fields in them directly translate to respective properties on // JavaScript objects. public class DownloadInfo { public string url; public string key; public string id; } public class LabelAndId { public string label; public string id; public void Initialize(JSONValue json) { if (json.ContainsKey("label")) label = json["label"].AsString(); if (json.ContainsKey("id")) id = json["id"].AsString(); } public override string ToString() { return string.Format("{{label={0}, id={1}}}", label, id); } } public class Link { public string type; public string id; public void Initialize(JSONValue json) { if (json.ContainsKey("type")) type = json["type"].AsString(); if (json.ContainsKey("id")) id = json["id"].AsString(); } public override string ToString() { return string.Format("{{type={0}, id={1}}}", type, id); } } public class Package { public string title; public string id; public string version; public string version_id; public string local_icon; public string local_path; public string pubdate; public string description; public LabelAndId publisher; public LabelAndId category; public Link link; public void Initialize(JSONValue json) { if (json.ContainsKey("title")) title = json["title"].AsString(); if (json.ContainsKey("id")) id = json["id"].AsString(); if (json.ContainsKey("version")) version = json["version"].AsString(); if (json.ContainsKey("version_id")) version_id = json["version_id"].AsString(); if (json.ContainsKey("local_icon")) local_icon = json["local_icon"].AsString(); if (json.ContainsKey("local_path")) local_path = json["local_path"].AsString(); if (json.ContainsKey("pubdate")) pubdate = json["pubdate"].AsString(); if (json.ContainsKey("description")) description = json["description"].AsString(); if (json.ContainsKey("publisher")) { publisher = new LabelAndId(); publisher.Initialize(json["publisher"]); } if (json.ContainsKey("category")) { category = new LabelAndId(); category.Initialize(json["category"]); } if (json.ContainsKey("link")) { link = new Link(); link.Initialize(json["link"]); } } public override string ToString() { return string.Format ("{{title={0}, id={1}, publisher={2}, category={3}, pubdate={8}, version={4}, version_id={5}, description={9}, link={10}, local_icon={6}, local_path={7}}}", title, id, publisher, category, version, version_id, local_icon, local_path, pubdate, description, link); } } public class PackageList { public Package[] results; } } } ================================================ FILE: Editor/Mono/AssetStore/AssetStorePreviewManager.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; namespace UnityEditor { /** * Fetching and caches preview images of asset store assets from the * asset store server */ internal sealed class AssetStorePreviewManager { private AssetStorePreviewManager() {} // disallow instantiation static AssetStorePreviewManager s_SharedAssetStorePreviewManager = null; static RenderTexture s_RenderTexture = null; static internal AssetStorePreviewManager Instance { get { if (s_SharedAssetStorePreviewManager == null) { s_SharedAssetStorePreviewManager = new AssetStorePreviewManager(); s_SharedAssetStorePreviewManager.m_DummyItem.lastUsed = -1; } return s_SharedAssetStorePreviewManager; } } public class CachedAssetStoreImage { private const double kFadeTime = 0.5; public Texture2D image; public double lastUsed; // time since app start public double lastFetched; // time since app start public int requestedWidth; // public string label; // internal AsyncHTTPClient client; // null if not in progress of fetching preview public Color color { get { return Color.Lerp(new Color(1, 1, 1, 0), new Color(1, 1, 1, 1), Mathf.Min(1f, (float)((EditorApplication.timeSinceStartup - lastFetched) / kFadeTime))); } } } // @TODO: MAKE handling of incoming images span several repaints in order to // not have the jumpy behaviour. Just put the in a queue an handle them from there. // Dictionary m_CachedAssetStoreImages; const int kMaxConcurrentDownloads = 15; const int kMaxConvertionsPerTick = 1; int m_MaxCachedAssetStoreImages = 10; int m_Aborted = 0; int m_Success = 0; internal int Requested = 0; internal int CacheHit = 0; int m_CacheRemove = 0; int m_ConvertedThisTick = 0; CachedAssetStoreImage m_DummyItem = new CachedAssetStoreImage(); static bool s_NeedsRepaint = false; static Dictionary CachedAssetStoreImages { get { if (Instance.m_CachedAssetStoreImages == null) { Instance.m_CachedAssetStoreImages = new Dictionary(); } return Instance.m_CachedAssetStoreImages; } } public static int MaxCachedImages { get { return Instance.m_MaxCachedAssetStoreImages; } set { Instance.m_MaxCachedAssetStoreImages = value; } } public static bool CacheFull { get { return CachedAssetStoreImages.Count >= MaxCachedImages; } } public static int Downloading { get { int c = 0; foreach (KeyValuePair kv in CachedAssetStoreImages) if (kv.Value.client != null) c++; return c; } } public static string StatsString() { return string.Format("Reqs: {0}, OK: {1}, Abort: {2}, CacheDel: {3}, Cache: {4}/{5}, CacheHit: {6}", Instance.Requested, Instance.m_Success, Instance.m_Aborted, Instance.m_CacheRemove, AssetStorePreviewManager.CachedAssetStoreImages.Count, Instance.m_MaxCachedAssetStoreImages, Instance.CacheHit); } /** * Return a texture from a url that points to an image resource * * This method does not block but queues a request to fetch the image and return null. * When the image has been fetched this method will return the image texture downloaded. */ public static CachedAssetStoreImage TextureFromUrl(string url, string label, int textureSize, GUIStyle labelStyle, GUIStyle iconStyle, bool onlyCached) { if (string.IsNullOrEmpty(url)) return Instance.m_DummyItem; CachedAssetStoreImage cached; bool newentry = true; if (CachedAssetStoreImages.TryGetValue(url, out cached)) { cached.lastUsed = EditorApplication.timeSinceStartup; // Refetch the image if the size has changed and is not in the progress of being fetched bool refetchInitiated = cached.requestedWidth == textureSize; bool correctSize = cached.image != null && cached.image.width == textureSize; bool cacheRequestAborted = cached.requestedWidth == -1; if ((correctSize || refetchInitiated || onlyCached) && !cacheRequestAborted) { Instance.CacheHit++; // Use cached image (that may be null) if we're in progress of fetching the image // or if we have rendered the images correctly //return cached; bool fetchingImage = cached.client != null; bool labelDrawn = cached.label == null; bool valid = fetchingImage || labelDrawn; bool convPerTickExceeded = Instance.m_ConvertedThisTick > kMaxConvertionsPerTick; s_NeedsRepaint = s_NeedsRepaint || convPerTickExceeded; return (valid || convPerTickExceeded) ? cached : RenderEntry(cached, labelStyle, iconStyle); } //Debug.Log(string.Format("Found {0} {1} {2} {3}", correctSize, refetchInitiated, onlyCached, cacheRequestAborted)); newentry = false; if (Downloading >= kMaxConcurrentDownloads) return cached.image == null ? Instance.m_DummyItem : cached; } else { if (onlyCached || Downloading >= kMaxConcurrentDownloads) return Instance.m_DummyItem; cached = new CachedAssetStoreImage(); cached.image = null; cached.lastUsed = EditorApplication.timeSinceStartup; //Debug.Log("url is " + textureSize.ToString() + " " + url); } // Only set fetch time when there is not image in order to use it for // fading in the image when it becomes available if (cached.image == null) cached.lastFetched = EditorApplication.timeSinceStartup; cached.requestedWidth = textureSize; cached.label = label; AsyncHTTPClient client = null; client = SetupTextureDownload(cached, url, "previewSize-" + textureSize); ExpireCacheEntries(); if (newentry) CachedAssetStoreImages.Add(url, cached); client.Begin(); Instance.Requested++; return cached; } private static AsyncHTTPClient SetupTextureDownload(CachedAssetStoreImage cached, string url, string tag) { AsyncHTTPClient client = new AsyncHTTPClient(url); cached.client = client; client.tag = tag; client.doneCallback = delegate(IAsyncHTTPClient c) { // Debug.Log("Got image " + EditorApplication.timeSinceStartup.ToString()); cached.client = null; if (!client.IsSuccess()) { if (client.state != AsyncHTTPClient.State.ABORTED) { string err = "error " + client.text + " " + client.state + " '" + url + "'"; if (ObjectListArea.s_Debug) Debug.LogError(err); else System.Console.Write(err); } else { Instance.m_Aborted++; } return; } // In the case of refetch because of resize first destroy the current image if (cached.image != null) Object.DestroyImmediate(cached.image); cached.image = c.texture; s_NeedsRepaint = true; Instance.m_Success++; }; return client; } // Make room for image if needed (because of cache limits) private static void ExpireCacheEntries() { while (CacheFull) { string oldestUrl = null; CachedAssetStoreImage oldestEntry = null; foreach (KeyValuePair kv in CachedAssetStoreImages) { if (oldestEntry == null || oldestEntry.lastUsed > kv.Value.lastUsed) { oldestEntry = kv.Value; oldestUrl = kv.Key; } } CachedAssetStoreImages.Remove(oldestUrl); Instance.m_CacheRemove++; if (oldestEntry == null) { Debug.LogError("Null entry found while removing cache entry"); break; } if (oldestEntry.client != null) { oldestEntry.client.Abort(); oldestEntry.client = null; } if (oldestEntry.image != null) Object.DestroyImmediate(oldestEntry.image); } } /* * When the new GUI system is is place this should render the label to the cached icon * to speed up rendering. For now we just scale the incoming image and render the label * separately */ private static CachedAssetStoreImage RenderEntry(CachedAssetStoreImage cached, GUIStyle labelStyle, GUIStyle iconStyle) { if (cached.label == null || cached.image == null) return cached; Texture2D tmp = cached.image; cached.image = new Texture2D(cached.requestedWidth, cached.requestedWidth, TextureFormat.RGB24, false, true); ScaleImage(cached.requestedWidth, cached.requestedWidth, tmp, cached.image, iconStyle); // Compressing creates artifacts on the images // cached.image.Compress(true); Object.DestroyImmediate(tmp); cached.label = null; Instance.m_ConvertedThisTick++; return cached; } /** Slow version of scaling an image but i doesn't * matter since it is not performance critical */ internal static void ScaleImage(int w, int h, Texture2D inimage, Texture2D outimage, GUIStyle bgStyle) { SavedRenderTargetState saved = new SavedRenderTargetState(); if (s_RenderTexture != null && (s_RenderTexture.width != w || s_RenderTexture.height != h)) { Object.DestroyImmediate(s_RenderTexture); s_RenderTexture = null; } if (s_RenderTexture == null) { s_RenderTexture = RenderTexture.GetTemporary(w, h, 16, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); s_RenderTexture.hideFlags = HideFlags.HideAndDontSave; } RenderTexture r = s_RenderTexture; RenderTexture.active = r; Rect rect = new Rect(0, 0, w, h); EditorGUIUtility.SetRenderTextureNoViewport(r); GL.LoadOrtho(); GL.LoadPixelMatrix(0, w, h, 0); ShaderUtil.rawViewportRect = new Rect(0, 0, w, h); ShaderUtil.rawScissorRect = new Rect(0 , 0, w, h); GL.Clear(true, true, new Color(0, 0, 0, 0)); Rect blitRect = rect; if (inimage.width > inimage.height) { float newHeight = (float)blitRect.height * ((float)inimage.height / (float)inimage.width); blitRect.height = (int)newHeight; blitRect.y += (int)(newHeight * 0.5f); } else if (inimage.width < inimage.height) { float newWidth = (float)blitRect.width * ((float)inimage.width / (float)inimage.height); blitRect.width = (int)newWidth; blitRect.x += (int)(newWidth * 0.5f); } if (bgStyle != null && bgStyle.normal != null && bgStyle.normal.background != null) Graphics.DrawTexture(rect, bgStyle.normal.background); Graphics.DrawTexture(blitRect, inimage); outimage.ReadPixels(rect, 0, 0, false); outimage.Apply(); outimage.hideFlags = HideFlags.HideAndDontSave; saved.Restore(); } /* * Check if textures queued for download has finished downloading. * This call will reset the flag for finished downloads. */ public static bool CheckRepaint() { bool needsRepaint = s_NeedsRepaint; s_NeedsRepaint = false; return needsRepaint; } // Abort fetching all previews with the specified size public static void AbortSize(int size) { AsyncHTTPClient.AbortByTag("previewSize-" + size); // Mark any pending requests for that width in the cases as invalid // now that requests has been aborted. foreach (KeyValuePair kv in AssetStorePreviewManager.CachedAssetStoreImages) { if (kv.Value.requestedWidth != size) continue; kv.Value.requestedWidth = -1; kv.Value.client = null; } } // Abort fetching all reviews that haven't been used since timestamp public static void AbortOlderThan(double timestamp) { foreach (KeyValuePair kv in AssetStorePreviewManager.CachedAssetStoreImages) { CachedAssetStoreImage entry = kv.Value; if (entry.lastUsed >= timestamp || entry.client == null) continue; entry.requestedWidth = -1; entry.client.Abort(); entry.client = null; } // @TODO: Currently we know that AbortOlderThan is called exactly once each repaint. // Therefore this counter can be reset here. Should probably be moved to a // more intuitive location. Instance.m_ConvertedThisTick = 0; } } } // UnityEditor namespace ================================================ FILE: Editor/Mono/AssetStore/AssetStoreWindow.cs ================================================ // Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using UnityEngine.UIElements; using UnityEditor.Connect; using UnityEditor.PackageManager.UI; using UnityEditor.UIElements; namespace UnityEditor { [EditorWindowTitle(title = "Asset Store", icon = "Asset Store")] internal class AssetStoreWindow : EditorWindow { public static AssetStoreWindow Init() { if (EditorPrefs.GetBool("AlwaysOpenAssetStoreInBrowser", false)) { OpenAssetStoreInBrowser(); return null; } else { AssetStoreWindow window = GetWindow(typeof(SceneView)); window.SetMinMaxSizes(); window.Show(); return window; } } [MenuItem("Window/Package Management/Asset Store", false, 2000)] public static void OpenAssetStoreInBrowser() { string assetStoreUrl = UnityConnect.instance.GetConfigurationURL(CloudConfigUrl.CloudAssetStoreUrl); assetStoreUrl += "?utm_source=unity-editor-window-menu&utm_medium=desktop-app"; if (UnityConnect.instance.loggedIn) UnityConnect.instance.OpenAuthorizedURLInWebBrowser(assetStoreUrl); else Application.OpenURL(assetStoreUrl); } [MenuItem("Window/Package Management/My Assets", false, 1501)] public static void OpenMyAssetsInPackageManager() { PackageManagerWindow.OpenAndSelectPage(PackageManager.UI.Internal.MyAssetsPage.k_Id); } public void OnEnable() { this.antiAliasing = 4; titleContent = GetLocalizedTitleContent(); var windowResource = EditorGUIUtility.Load("UXML/AssetStore/AssetStoreWindow.uxml") as VisualTreeAsset; if (windowResource != null) { var root = windowResource.CloneTree(); var lightStyleSheet = EditorGUIUtility.Load(UIElementsEditorUtility.s_DefaultCommonLightStyleSheetPath) as StyleSheet; var assetStoreStyleSheet = EditorGUIUtility.Load("StyleSheets/AssetStore/AssetStoreWindow.uss") as StyleSheet; var styleSheet = CreateInstance(); styleSheet.isDefaultStyleSheet = true; var resolver = new StyleSheets.StyleSheetResolver(); resolver.AddStyleSheets(lightStyleSheet, assetStoreStyleSheet); resolver.ResolveTo(styleSheet); root.styleSheets.Add(styleSheet); rootVisualElement.Add(root); root.StretchToParentSize(); visitWebsiteButton.clickable.clicked += OnVisitWebsiteButtonClicked; launchPackageManagerButton.clickable.clicked += OnLaunchPackageManagerButtonClicked; alwaysOpenInBrowserToggle.SetValueWithoutNotify(EditorPrefs.GetBool("AlwaysOpenAssetStoreInBrowser", false)); alwaysOpenInBrowserToggle.RegisterValueChangedCallback(changeEvent => { EditorPrefs.SetBool("AlwaysOpenAssetStoreInBrowser", changeEvent.newValue); }); } } public void OnDisable() { visitWebsiteButton.clickable.clicked -= OnVisitWebsiteButtonClicked; launchPackageManagerButton.clickable.clicked -= OnLaunchPackageManagerButtonClicked; } public static void OpenURL(string url) { string assetStoreUrl = $"{UnityConnect.instance.GetConfigurationURL(CloudConfigUrl.CloudAssetStoreUrl)}/packages/{url}"; if (UnityEditor.Connect.UnityConnect.instance.loggedIn) UnityEditor.Connect.UnityConnect.instance.OpenAuthorizedURLInWebBrowser(assetStoreUrl); else Application.OpenURL(assetStoreUrl); } private void OnVisitWebsiteButtonClicked() { OpenAssetStoreInBrowser(); } private void OnLaunchPackageManagerButtonClicked() { PackageManagerWindow.OpenAndSelectPackage(null); } private void SetMinMaxSizes() { this.minSize = new Vector2(455, 354); this.maxSize = new Vector2(4000, 4000); } private Button visitWebsiteButton { get { return rootVisualElement.Q