Exemplo n.º 1
0
        /// <summary>
        /// Retrieves import options for the audio clip we're currently inspecting.
        /// </summary>
        /// <returns>Audio clip import options object.</returns>
        private AudioClipImportOptions GetImportOptions()
        {
            AudioClip audioClip           = InspectedObject as AudioClip;
            AudioClipImportOptions output = null;

            if (audioClip != null)
            {
                LibraryEntry meshEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(audioClip));
                if (meshEntry != null && meshEntry.Type == LibraryEntryType.File)
                {
                    FileEntry meshFileEntry = (FileEntry)meshEntry;
                    output = meshFileEntry.Options as AudioClipImportOptions;
                }
            }

            if (output == null)
            {
                if (importOptions == null)
                {
                    output = new AudioClipImportOptions();
                }
                else
                {
                    output = importOptions;
                }
            }

            return(output);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Reimports the resource according to the currently set import options.
        /// </summary>
        private void TriggerReimport()
        {
            AudioClip audioClip    = (AudioClip)InspectedObject;
            string    resourcePath = ProjectLibrary.GetPath(audioClip);

            ProjectLibrary.Reimport(resourcePath, importOptions, true);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Retrieves import options for the texture we're currently inspecting.
        /// </summary>
        /// <returns>Font import options object.</returns>
        private FontImportOptions GetImportOptions()
        {
            Font font = InspectedObject as Font;
            FontImportOptions output = null;

            if (font != null)
            {
                LibraryEntry texEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(font));
                if (texEntry != null && texEntry.Type == LibraryEntryType.File)
                {
                    FileEntry texFileEntry = (FileEntry)texEntry;
                    output = texFileEntry.Options as FontImportOptions;
                }
            }

            if (output == null)
            {
                if (importOptions == null)
                {
                    output = new FontImportOptions();
                }
                else
                {
                    output = importOptions;
                }
            }

            return(output);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Reimports the resource according to the currently set import options.
        /// </summary>
        private void TriggerReimport()
        {
            Mesh   mesh         = (Mesh)InspectedObject;
            string resourcePath = ProjectLibrary.GetPath(mesh);

            ProjectLibrary.Reimport(resourcePath, importOptions, true);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Retrieves import options for the mesh we're currently inspecting.
        /// </summary>
        /// <returns>Mesh import options object.</returns>
        private MeshImportOptions GetImportOptions()
        {
            Mesh mesh = InspectedObject as Mesh;
            MeshImportOptions output = null;

            if (mesh != null)
            {
                LibraryEntry meshEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(mesh));
                if (meshEntry != null && meshEntry.Type == LibraryEntryType.File)
                {
                    FileEntry meshFileEntry = (FileEntry)meshEntry;
                    output = meshFileEntry.Options as MeshImportOptions;
                }
            }

            if (output == null)
            {
                if (importOptions == null)
                {
                    output = new MeshImportOptions();
                }
                else
                {
                    output = importOptions;
                }
            }

            return(output);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Retrieves import options for the resource we're currently inspecting.
        /// </summary>
        /// <returns>Script code import options object.</returns>
        private ScriptCodeImportOptions GetImportOptions()
        {
            ScriptCode scriptCode          = InspectedObject as ScriptCode;
            ScriptCodeImportOptions output = null;

            if (scriptCode != null)
            {
                LibraryEntry libEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(scriptCode));
                if (libEntry != null && libEntry.Type == LibraryEntryType.File)
                {
                    FileEntry fileEntry = (FileEntry)libEntry;
                    output = fileEntry.Options as ScriptCodeImportOptions;
                }
            }

            if (output == null)
            {
                if (importOptions == null)
                {
                    output = new ScriptCodeImportOptions();
                }
                else
                {
                    output = importOptions;
                }
            }

            return(output);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Reimports the texture resource according to the currently set import options.
        /// </summary>
        private void TriggerReimport()
        {
            Texture2D texture      = (Texture2D)InspectedObject;
            string    resourcePath = ProjectLibrary.GetPath(texture);

            ProjectLibrary.Reimport(resourcePath, importOptions, true);
        }
Exemplo n.º 8
0
        public static void SaveProject()
        {
            // Apply changes to any animation clips edited using the animation editor
            foreach (var KVP in persistentData.dirtyAnimClips)
            {
                KVP.Value.SaveToClip();
            }

            // Save all dirty resources to disk
            foreach (var KVP in persistentData.dirtyResources)
            {
                UUID   resourceUUID = KVP.Key;
                string path         = ProjectLibrary.GetPath(resourceUUID);
                if (!IsNative(path))
                {
                    continue; // Imported resources can't be changed
                }
                Resource resource = ProjectLibrary.Load <Resource>(path);

                if (resource != null)
                {
                    ProjectLibrary.Save(resource);
                }
            }

            persistentData.dirtyAnimClips.Clear();
            persistentData.dirtyResources.Clear();
            SetStatusProject(false);

            Internal_SaveProject();
        }
Exemplo n.º 9
0
        /// <summary>
        /// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously
        /// saved it is instead automatically saved at the last location.
        /// </summary>
        public static void SaveScene(Action onSuccess = null, Action onFailure = null)
        {
            if (!Scene.ActiveSceneUUID.IsEmpty())
            {
                string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID);
                if (!string.IsNullOrEmpty(scenePath))
                {
                    if (Scene.IsGenericPrefab)
                    {
                        SaveGenericPrefab(onSuccess, onFailure);
                    }
                    else
                    {
                        SaveScene(scenePath);

                        if (onSuccess != null)
                        {
                            onSuccess();
                        }
                    }
                }
                else
                {
                    SaveSceneAs(onSuccess, onFailure);
                }
            }
            else
            {
                SaveSceneAs(onSuccess, onFailure);
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Retrieves import options for the texture we're currently inspecting.
        /// </summary>
        /// <returns>Texture import options object.</returns>
        private TextureImportOptions GetImportOptions()
        {
            Texture2D            texture = InspectedObject as Texture2D;
            TextureImportOptions output  = null;

            if (texture != null)
            {
                LibraryEntry texEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(texture));
                if (texEntry != null && texEntry.Type == LibraryEntryType.File)
                {
                    FileEntry texFileEntry = (FileEntry)texEntry;
                    output = texFileEntry.Options as TextureImportOptions;
                }
            }

            if (output == null)
            {
                if (importOptions == null)
                {
                    output = new TextureImportOptions();
                }
                else
                {
                    output = importOptions;
                }
            }

            return(output);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Reimports the script code resource according to the currently set import options.
        /// </summary>
        private void TriggerReimport()
        {
            ScriptCode scriptCode   = (ScriptCode)InspectedObject;
            string     resourcePath = ProjectLibrary.GetPath(scriptCode);

            ProjectLibrary.Reimport(resourcePath, importOptions, true);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Reimports the resource according to the currently set import options.
        /// </summary>
        private void TriggerReimport()
        {
            Mesh   mesh         = (Mesh)InspectedObject;
            string resourcePath = ProjectLibrary.GetPath(mesh);

            importOptions.AnimationClipSplits = splitInfos;

            ProjectLibrary.Reimport(resourcePath, importOptions, true);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Saves the animation curves and events stored in this object, into the associated animation clip resource.
        /// Relevant animation clip resource must already be created and exist in the project library.
        /// </summary>
        public void SaveToClip()
        {
            if (!isImported)
            {
                EditorAnimClipTangents tangents;
                Apply(out tangents);

                string resourcePath = ProjectLibrary.GetPath(clip);
                ProjectLibrary.Save(clip);

                ProjectLibrary.SetEditorData(resourcePath, tangents);
            }
            else
            {
                string       resourcePath = ProjectLibrary.GetPath(clip);
                LibraryEntry entry        = ProjectLibrary.GetEntry(resourcePath);

                if (entry != null && entry.Type == LibraryEntryType.File)
                {
                    FileEntry         fileEntry         = (FileEntry)entry;
                    MeshImportOptions meshImportOptions = (MeshImportOptions)fileEntry.Options;

                    string clipName = PathEx.GetTail(resourcePath);

                    List <ImportedAnimationEvents> newEvents = new List <ImportedAnimationEvents>();
                    newEvents.AddRange(meshImportOptions.AnimationEvents);

                    bool isExisting = false;
                    for (int i = 0; i < newEvents.Count; i++)
                    {
                        if (newEvents[i].name == clipName)
                        {
                            newEvents[i].events = events;
                            isExisting          = true;
                            break;
                        }
                    }

                    if (!isExisting)
                    {
                        ImportedAnimationEvents newEntry = new ImportedAnimationEvents();
                        newEntry.name   = clipName;
                        newEntry.events = events;

                        newEvents.Add(newEntry);
                    }

                    meshImportOptions.AnimationEvents = newEvents.ToArray();

                    ProjectLibrary.Reimport(resourcePath, meshImportOptions, true);
                }
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Updates the contents of the prefab with the contents of the provided prefab instance. If the provided object
        /// is not a prefab instance nothing happens.
        /// </summary>
        /// <param name="obj">Prefab instance whose prefab to update.</param>
        /// <param name="refreshScene">If true, all prefab instances in the current scene will be updated so they consistent
        ///                            with the newly saved data.</param>
        public static void ApplyPrefab(SceneObject obj, bool refreshScene = true)
        {
            if (obj == null)
            {
                return;
            }

            SceneObject prefabInstanceRoot = GetPrefabParent(obj);

            if (prefabInstanceRoot == null)
            {
                return;
            }

            if (refreshScene)
            {
                SceneObject root = Scene.Root;
                if (root != null)
                {
                    Internal_RecordPrefabDiff(root.GetCachedPtr());
                }
            }

            UUID   prefabUUID = GetPrefabUUID(prefabInstanceRoot);
            string prefabPath = ProjectLibrary.GetPath(prefabUUID);
            Prefab prefab     = ProjectLibrary.Load <Prefab>(prefabPath);

            if (prefab != null)
            {
                IntPtr soPtr     = prefabInstanceRoot.GetCachedPtr();
                IntPtr prefabPtr = prefab.GetCachedPtr();

                Internal_ApplyPrefab(soPtr, prefabPtr);
                ProjectLibrary.Save(prefab);
            }

            if (refreshScene)
            {
                SceneObject root = Scene.Root;
                if (root != null)
                {
                    Internal_UpdateFromPrefab(root.GetCachedPtr());
                }
            }
        }
Exemplo n.º 15
0
        public static void SaveProject()
        {
            foreach (var KVP in persistentData.dirtyResources)
            {
                string resourceUUID = KVP.Key;
                string path         = ProjectLibrary.GetPath(resourceUUID);
                if (!IsNative(path))
                {
                    continue; // Native resources can't be changed
                }
                Resource resource = ProjectLibrary.Load <Resource>(path);

                if (resource != null)
                {
                    ProjectLibrary.Save(resource);
                }
            }

            persistentData.dirtyResources.Clear();
            SetStatusProject(false);

            Internal_SaveProject();
        }
Exemplo n.º 16
0
        /// <summary>
        /// Loads curve and event information from the provided clip, and creates a new instance of this object containing
        /// the required data for editing the source clip in the animation editor.
        /// </summary>
        /// <param name="clip">Clip to load.</param>
        /// <returns>Editor specific editable information about an animation clip.</returns>
        public static EditorAnimClipInfo Create(AnimationClip clip)
        {
            EditorAnimClipInfo clipInfo = new EditorAnimClipInfo();

            clipInfo.clip       = clip;
            clipInfo.isImported = IsClipImported(clip);
            clipInfo.sampleRate = (int)clip.SampleRate;

            AnimationCurves        clipCurves      = clip.Curves;
            EditorAnimClipTangents editorCurveData = null;

            string resourcePath = ProjectLibrary.GetPath(clip);

            if (!string.IsNullOrEmpty(resourcePath))
            {
                LibraryEntry entry    = ProjectLibrary.GetEntry(resourcePath);
                string       clipName = PathEx.GetTail(resourcePath);

                if (entry != null && entry.Type == LibraryEntryType.File)
                {
                    FileEntry      fileEntry = (FileEntry)entry;
                    ResourceMeta[] metas     = fileEntry.ResourceMetas;

                    if (clipInfo.isImported)
                    {
                        for (int i = 0; i < metas.Length; i++)
                        {
                            if (clipName == metas[i].SubresourceName)
                            {
                                editorCurveData = metas[i].EditorData as EditorAnimClipTangents;
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (metas.Length > 0)
                        {
                            editorCurveData = metas[0].EditorData as EditorAnimClipTangents;
                        }
                    }
                }
            }

            if (editorCurveData == null)
            {
                editorCurveData = new EditorAnimClipTangents();
            }

            int globalCurveIdx = 0;
            Action <NamedVector3Curve[], EditorVector3CurveTangents[], string> loadVector3Curve =
                (curves, tangents, subPath) =>
            {
                foreach (var curveEntry in curves)
                {
                    TangentMode[] tangentsX = null;
                    TangentMode[] tangentsY = null;
                    TangentMode[] tangentsZ = null;

                    if (tangents != null)
                    {
                        foreach (var tangentEntry in tangents)
                        {
                            if (tangentEntry.name == curveEntry.name)
                            {
                                tangentsX = tangentEntry.tangentsX;
                                tangentsY = tangentEntry.tangentsY;
                                tangentsZ = tangentEntry.tangentsZ;
                                break;
                            }
                        }
                    }

                    // Convert compound curve to three per-component curves
                    AnimationCurve[] componentCurves = AnimationUtility.SplitCurve(curveEntry.curve);

                    FieldAnimCurves fieldCurves = new FieldAnimCurves();
                    fieldCurves.type            = SerializableProperty.FieldType.Vector3;
                    fieldCurves.curveInfos      = new CurveDrawInfo[3];
                    fieldCurves.isPropertyCurve = !clipInfo.isImported;

                    fieldCurves.curveInfos[0]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[0].curve = new EdAnimationCurve(componentCurves[0], tangentsX);
                    fieldCurves.curveInfos[0].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    fieldCurves.curveInfos[1]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[1].curve = new EdAnimationCurve(componentCurves[1], tangentsY);
                    fieldCurves.curveInfos[1].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    fieldCurves.curveInfos[2]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[2].curve = new EdAnimationCurve(componentCurves[2], tangentsZ);
                    fieldCurves.curveInfos[2].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    string curvePath = curveEntry.name.TrimEnd('/') + subPath;
                    clipInfo.curves[curvePath] = fieldCurves;
                }
            };

            // Convert rotation from quaternion to euler
            NamedQuaternionCurve[] rotationCurves      = clipCurves.Rotation;
            NamedVector3Curve[]    eulerRotationCurves = new NamedVector3Curve[rotationCurves.Length];
            for (int i = 0; i < rotationCurves.Length; i++)
            {
                eulerRotationCurves[i]       = new NamedVector3Curve();
                eulerRotationCurves[i].name  = rotationCurves[i].name;
                eulerRotationCurves[i].flags = rotationCurves[i].flags;
                eulerRotationCurves[i].curve = AnimationUtility.QuaternionToEulerCurve(rotationCurves[i].curve);
            }

            loadVector3Curve(clipCurves.Position, editorCurveData.positionCurves, "/Position");
            loadVector3Curve(eulerRotationCurves, editorCurveData.rotationCurves, "/Rotation");
            loadVector3Curve(clipCurves.Scale, editorCurveData.scaleCurves, "/Scale");

            // Find which individual float curves belong to the same field
            Dictionary <string, Tuple <int, int, bool>[]> floatCurveMapping = new Dictionary <string, Tuple <int, int, bool>[]>();

            {
                int curveIdx = 0;
                foreach (var curveEntry in clipCurves.Generic)
                {
                    string path         = curveEntry.name;
                    string pathNoSuffix = null;

                    string pathSuffix;
                    if (path.Length >= 2)
                    {
                        pathSuffix   = path.Substring(path.Length - 2, 2);
                        pathNoSuffix = path.Substring(0, path.Length - 2);
                    }
                    else
                    {
                        pathSuffix = "";
                    }

                    int tangentIdx        = -1;
                    int currentTangentIdx = 0;
                    foreach (var tangentEntry in editorCurveData.floatCurves)
                    {
                        if (tangentEntry.name == curveEntry.name)
                        {
                            tangentIdx = currentTangentIdx;
                            break;
                        }

                        currentTangentIdx++;
                    }

                    Animation.PropertySuffixInfo suffixInfo;
                    if (Animation.PropertySuffixInfos.TryGetValue(pathSuffix, out suffixInfo))
                    {
                        Tuple <int, int, bool>[] curveInfo;
                        if (!floatCurveMapping.TryGetValue(pathNoSuffix, out curveInfo))
                        {
                            curveInfo = new Tuple <int, int, bool> [4];
                        }

                        curveInfo[suffixInfo.elementIdx] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.isVector);
                        floatCurveMapping[pathNoSuffix]  = curveInfo;
                    }
                    else
                    {
                        Tuple <int, int, bool>[] curveInfo = new Tuple <int, int, bool> [4];
                        curveInfo[0] = Tuple.Create(curveIdx, tangentIdx, suffixInfo.isVector);

                        floatCurveMapping[path] = curveInfo;
                    }

                    curveIdx++;
                }
            }

            foreach (var KVP in floatCurveMapping)
            {
                int numCurves = 0;
                for (int i = 0; i < 4; i++)
                {
                    if (KVP.Value[i] == null)
                    {
                        continue;
                    }

                    numCurves++;
                }

                if (numCurves == 0)
                {
                    continue; // Invalid curve
                }
                FieldAnimCurves fieldCurves = new FieldAnimCurves();

                // Deduce type (note that all single value types are assumed to be float even if their source type is int or bool)
                if (numCurves == 1)
                {
                    fieldCurves.type = SerializableProperty.FieldType.Float;
                }
                else if (numCurves == 2)
                {
                    fieldCurves.type = SerializableProperty.FieldType.Vector2;
                }
                else if (numCurves == 3)
                {
                    fieldCurves.type = SerializableProperty.FieldType.Vector3;
                }
                else // 4 curves
                {
                    bool isVector = KVP.Value[0].Item3;
                    if (isVector)
                    {
                        fieldCurves.type = SerializableProperty.FieldType.Vector4;
                    }
                    else
                    {
                        fieldCurves.type = SerializableProperty.FieldType.Color;
                    }
                }

                bool   isMorphCurve = false;
                string curvePath    = KVP.Key;

                fieldCurves.curveInfos = new CurveDrawInfo[numCurves];
                for (int i = 0; i < numCurves; i++)
                {
                    int curveIdx   = KVP.Value[i].Item1;
                    int tangentIdx = KVP.Value[i].Item2;

                    TangentMode[] tangents = null;
                    if (tangentIdx != -1)
                    {
                        tangents = editorCurveData.floatCurves[tangentIdx].tangents;
                    }

                    fieldCurves.curveInfos[i]       = new CurveDrawInfo();
                    fieldCurves.curveInfos[i].curve = new EdAnimationCurve(clipCurves.Generic[curveIdx].curve, tangents);
                    fieldCurves.curveInfos[i].color = GUICurveDrawing.GetUniqueColor(globalCurveIdx++);

                    if (clipCurves.Generic[curveIdx].flags.HasFlag(AnimationCurveFlags.MorphFrame))
                    {
                        curvePath    = "MorphShapes/Frames/" + KVP.Key;
                        isMorphCurve = true;
                    }
                    else if (clipCurves.Generic[curveIdx].flags.HasFlag(AnimationCurveFlags.MorphWeight))
                    {
                        curvePath    = "MorphShapes/Weight/" + KVP.Key;
                        isMorphCurve = true;
                    }
                }

                fieldCurves.isPropertyCurve = !clipInfo.isImported && !isMorphCurve;

                clipInfo.curves[curvePath] = fieldCurves;
            }

            // Add events
            clipInfo.events = clip.Events;
            return(clipInfo);
        }
Exemplo n.º 17
0
        /// <summary>
        /// Checks is the specified animation clip is imported from an external file, or created within the editor.
        /// </summary>
        /// <param name="clip">Clip to check.</param>
        /// <returns>True if the clip is imported from an external file (e.g. FBX file), or false if the clip is a native
        ///          resource created within the editor.</returns>
        public static bool IsClipImported(AnimationClip clip)
        {
            string resourcePath = ProjectLibrary.GetPath(clip);

            return(ProjectLibrary.IsSubresource(resourcePath));
        }
Exemplo n.º 18
0
        /// <summary>
        /// Pings the resource, highlighting it in its respective editors.
        /// </summary>
        /// <param name="resource">Resource to highlight.</param>
        public static void Ping(Resource resource)
        {
            string path = ProjectLibrary.GetPath(resource);

            Internal_PingResource(path);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Saves the animation curves and events stored in this object, into the associated animation clip resource.
        /// Relevant animation clip resource must already be created and exist in the project library.
        /// </summary>
        public void SaveToClip()
        {
            if (!isImported)
            {
                List <NamedVector3Curve> positionCurves = new List <NamedVector3Curve>();
                List <NamedVector3Curve> rotationCurves = new List <NamedVector3Curve>();
                List <NamedVector3Curve> scaleCurves    = new List <NamedVector3Curve>();
                List <NamedFloatCurve>   floatCurves    = new List <NamedFloatCurve>();

                List <EditorVector3CurveTangents> positionTangents = new List <EditorVector3CurveTangents>();
                List <EditorVector3CurveTangents> rotationTangents = new List <EditorVector3CurveTangents>();
                List <EditorVector3CurveTangents> scaleTangents    = new List <EditorVector3CurveTangents>();
                List <EditorFloatCurveTangents>   floatTangents    = new List <EditorFloatCurveTangents>();

                foreach (var kvp in curves)
                {
                    string[] pathEntries = kvp.Key.Split('/');
                    if (pathEntries.Length == 0)
                    {
                        continue;
                    }

                    string lastEntry = pathEntries[pathEntries.Length - 1];

                    if (lastEntry == "Position" || lastEntry == "Rotation" || lastEntry == "Scale")
                    {
                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < pathEntries.Length - 2; i++)
                        {
                            sb.Append(pathEntries[i] + "/");
                        }

                        if (pathEntries.Length > 1)
                        {
                            sb.Append(pathEntries[pathEntries.Length - 2]);
                        }

                        string curvePath = sb.ToString();

                        NamedVector3Curve curve = new NamedVector3Curve(curvePath,
                                                                        new AnimationCurve(kvp.Value.curveInfos[0].curve.KeyFrames),
                                                                        new AnimationCurve(kvp.Value.curveInfos[1].curve.KeyFrames),
                                                                        new AnimationCurve(kvp.Value.curveInfos[2].curve.KeyFrames));

                        EditorVector3CurveTangents tangents = new EditorVector3CurveTangents();
                        tangents.name      = curvePath;
                        tangents.tangentsX = kvp.Value.curveInfos[0].curve.TangentModes;
                        tangents.tangentsY = kvp.Value.curveInfos[1].curve.TangentModes;
                        tangents.tangentsZ = kvp.Value.curveInfos[2].curve.TangentModes;

                        if (lastEntry == "Position")
                        {
                            positionCurves.Add(curve);
                            positionTangents.Add(tangents);
                        }
                        else if (lastEntry == "Rotation")
                        {
                            rotationCurves.Add(curve);
                            rotationTangents.Add(tangents);
                        }
                        else if (lastEntry == "Scale")
                        {
                            scaleCurves.Add(curve);
                            scaleTangents.Add(tangents);
                        }
                    }
                    else
                    {
                        Action <int, string> addCurve = (idx, subPath) =>
                        {
                            string path = kvp.Key + subPath;

                            NamedFloatCurve curve = new NamedFloatCurve(path,
                                                                        new AnimationCurve(kvp.Value.curveInfos[idx].curve.KeyFrames));

                            EditorFloatCurveTangents tangents = new EditorFloatCurveTangents();
                            tangents.name     = path;
                            tangents.tangents = kvp.Value.curveInfos[idx].curve.TangentModes;

                            floatCurves.Add(curve);
                            floatTangents.Add(tangents);
                        };

                        switch (kvp.Value.type)
                        {
                        case SerializableProperty.FieldType.Vector2:
                            addCurve(0, ".x");
                            addCurve(1, ".y");
                            break;

                        case SerializableProperty.FieldType.Vector3:
                            addCurve(0, ".x");
                            addCurve(1, ".y");
                            addCurve(2, ".z");
                            break;

                        case SerializableProperty.FieldType.Vector4:
                            addCurve(0, ".x");
                            addCurve(1, ".y");
                            addCurve(2, ".z");
                            addCurve(3, ".w");
                            break;

                        case SerializableProperty.FieldType.Color:
                            addCurve(0, ".r");
                            addCurve(1, ".g");
                            addCurve(2, ".b");
                            addCurve(3, ".a");
                            break;

                        case SerializableProperty.FieldType.Bool:
                        case SerializableProperty.FieldType.Int:
                        case SerializableProperty.FieldType.Float:
                            addCurve(0, "");
                            break;
                        }
                    }
                }

                AnimationCurves newClipCurves = new AnimationCurves();
                newClipCurves.PositionCurves = positionCurves.ToArray();
                newClipCurves.RotationCurves = rotationCurves.ToArray();
                newClipCurves.ScaleCurves    = scaleCurves.ToArray();
                newClipCurves.FloatCurves    = floatCurves.ToArray();

                clip.Curves     = newClipCurves;
                clip.Events     = events;
                clip.SampleRate = sampleRate;

                string resourcePath = ProjectLibrary.GetPath(clip);
                ProjectLibrary.Save(clip);

                // Save tangents for editor only use
                EditorAnimClipTangents newCurveData = new EditorAnimClipTangents();
                newCurveData.positionCurves = positionTangents.ToArray();
                newCurveData.rotationCurves = rotationTangents.ToArray();
                newCurveData.scaleCurves    = scaleTangents.ToArray();
                newCurveData.floatCurves    = floatTangents.ToArray();

                ProjectLibrary.SetEditorData(resourcePath, newCurveData);
            }
            else
            {
                string       resourcePath = ProjectLibrary.GetPath(clip);
                LibraryEntry entry        = ProjectLibrary.GetEntry(resourcePath);

                if (entry != null && entry.Type == LibraryEntryType.File)
                {
                    FileEntry         fileEntry         = (FileEntry)entry;
                    MeshImportOptions meshImportOptions = (MeshImportOptions)fileEntry.Options;

                    string clipName = PathEx.GetTail(resourcePath);

                    List <ImportedAnimationEvents> newEvents = new List <ImportedAnimationEvents>();
                    newEvents.AddRange(meshImportOptions.AnimationEvents);

                    bool isExisting = false;
                    for (int i = 0; i < newEvents.Count; i++)
                    {
                        if (newEvents[i].name == clipName)
                        {
                            newEvents[i].events = events;
                            isExisting          = true;
                            break;
                        }
                    }

                    if (!isExisting)
                    {
                        ImportedAnimationEvents newEntry = new ImportedAnimationEvents();
                        newEntry.name   = clipName;
                        newEntry.events = events;

                        newEvents.Add(newEntry);
                    }

                    meshImportOptions.AnimationEvents = newEvents.ToArray();

                    ProjectLibrary.Reimport(resourcePath, meshImportOptions, true);
                }
            }
        }
Exemplo n.º 20
0
        /// <summary>
        /// Tests saving, loading and updating of prefabs.
        /// </summary>
        private static void UnitTest4_Prefabs()
        {
            if (!EditorApplication.IsProjectLoaded)
            {
                Debug.LogWarning("Skipping unit test as no project is loaded.");
                return;
            }

            if (EditorApplication.IsSceneModified())
            {
                Debug.LogWarning("Cannot perform unit test as the current scene is modified.");
                return;
            }

            Action PrintSceneState = () =>
            {
                SceneObject root = Scene.Root;

                Stack <SceneObject> todo = new Stack <SceneObject>();
                todo.Push(root);

                StringBuilder output = new StringBuilder();
                while (todo.Count > 0)
                {
                    SceneObject so = todo.Pop();

                    int numChildren = so.GetNumChildren();
                    for (int i = numChildren - 1; i >= 0; i--)
                    {
                        SceneObject child = so.GetChild(i);

                        output.AppendLine(child.Name);
                        todo.Push(child);
                    }
                }

                Debug.Log(output);
            };

            // Disabled because it's a slow test, enable only when relevant (or when a build machine is set up)
            return;

            string oldScene = Scene.ActiveSceneUUID;

            Scene.Clear();

            try
            {
                // Simple scene save & load
                {
                    {
                        // unitTest4Scene_0.prefab:
                        // so0 (Comp1)
                        //  - so0_0
                        //  - so0_1 (Comp1)
                        //    - so0_1_0 (Comp1)
                        // so1 (Comp2)
                        //  - so1_0

                        SceneObject so0     = new SceneObject("so0");
                        SceneObject so1     = new SceneObject("so1");
                        SceneObject so0_0   = new SceneObject("so0_0");
                        SceneObject so0_1   = new SceneObject("so0_1");
                        SceneObject so1_0   = new SceneObject("so1_0");
                        SceneObject so0_1_0 = new SceneObject("so0_1_0");

                        so0_0.Parent   = so0;
                        so0_1.Parent   = so0;
                        so1_0.Parent   = so1;
                        so0_1_0.Parent = so0_1;

                        so0_1_0.LocalPosition = new Vector3(10.0f, 15.0f, 20.0f);
                        so0_1.LocalPosition   = new Vector3(1.0f, 2.0f, 3.0f);
                        so1_0.LocalPosition   = new Vector3(0, 123.0f, 0.0f);

                        UT1_Component1 comp0     = so0.AddComponent <UT1_Component1>();
                        UT1_Component2 comp1     = so1.AddComponent <UT1_Component2>();
                        UT1_Component1 comp1_1   = so0_1.AddComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0 = so0_1_0.AddComponent <UT1_Component1>();

                        comp0.otherSO        = so0_1_0;
                        comp0.otherComponent = comp1;

                        comp1_1.b = "originalValue2";

                        comp0_1_0.b               = "testValue";
                        comp0_1_0.otherSO         = so0;
                        comp0_1_0.otherComponent2 = comp0;

                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
                    }
                    {
                        EditorApplication.LoadScene("unitTest4Scene_0.prefab");

                        SceneObject sceneRoot = Scene.Root;
                        SceneObject so0       = sceneRoot.FindChild("so0", false);
                        SceneObject so1       = sceneRoot.FindChild("so1", false);
                        SceneObject so0_0     = so0.FindChild("so0_0", false);
                        SceneObject so0_1     = so0.FindChild("so0_1", false);
                        SceneObject so0_1_0   = so0_1.FindChild("so0_1_0", false);

                        Assert(so0_0 != null);
                        Assert(so0_1 != null);
                        Assert(so0_1_0 != null);

                        UT1_Component1 comp0     = so0.GetComponent <UT1_Component1>();
                        UT1_Component2 comp1     = so1.GetComponent <UT1_Component2>();
                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();

                        Assert(comp0 != null);
                        Assert(comp1 != null);
                        Assert(comp0_1_0 != null);
                        Assert(comp0_1_0.b == "testValue");
                        Assert(comp0.otherSO == so0_1_0);
                        Assert(comp0.otherComponent == comp1);
                        Assert(comp0_1_0.otherSO == so0);
                        Assert(comp0_1_0.otherComponent2 == comp0);
                    }
                }

                Debug.Log("Passed stage 1");

                // Load & save a scene that contains a prefab and references its objects
                {
                    {
                        // unitTest4Scene_1.prefab:
                        // parentSO0
                        //  - [unitTest4Scene_0.prefab]
                        // parentSO1
                        //  - parentSO1_0 (Comp1)

                        Scene.Clear();

                        SceneObject parentSO0   = new SceneObject("parentSO0", false);
                        SceneObject parentSO1   = new SceneObject("parentSO1", false);
                        SceneObject parentSO1_0 = new SceneObject("parentSO1_0", false);

                        parentSO1_0.Parent      = parentSO1;
                        parentSO0.LocalPosition = new Vector3(50.0f, 50.0f, 50.0f);

                        UT1_Component1 parentComp1_0 = parentSO1_0.AddComponent <UT1_Component1>();

                        Prefab      scene0Prefab   = ProjectLibrary.Load <Prefab>("unitTest4Scene_0.prefab");
                        SceneObject prefabInstance = scene0Prefab.Instantiate();
                        prefabInstance.Parent        = parentSO0;
                        prefabInstance.LocalPosition = Vector3.Zero;

                        SceneObject so0     = prefabInstance.FindChild("so0", false);
                        SceneObject so1     = prefabInstance.FindChild("so1", false);
                        SceneObject so0_1   = so0.FindChild("so0_1", false);
                        SceneObject so1_0   = so1.FindChild("so1_0", false);
                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);

                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();

                        parentComp1_0.otherSO         = so1_0;
                        parentComp1_0.otherComponent2 = comp0_1_0;

                        EditorApplication.SaveScene("unitTest4Scene_1.prefab");
                    }
                    {
                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");

                        SceneObject parentSO0   = Scene.Root.FindChild("parentSO0", false);
                        SceneObject parentSO1   = Scene.Root.FindChild("parentSO1", false);
                        SceneObject parentSO1_0 = parentSO1.FindChild("parentSO1_0", false);

                        UT1_Component1 parentComp1_0 = parentSO1_0.GetComponent <UT1_Component1>();

                        SceneObject prefabInstance = parentSO0.GetChild(0);
                        SceneObject so0            = prefabInstance.FindChild("so0", false);
                        SceneObject so1            = prefabInstance.FindChild("so1", false);
                        SceneObject so0_1          = so0.FindChild("so0_1", false);
                        SceneObject so1_0          = so1.FindChild("so1_0", false);
                        SceneObject so0_1_0        = so0_1.FindChild("so0_1_0", false);

                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();

                        Assert(parentComp1_0.otherSO == so1_0);
                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
                    }
                }

                Debug.Log("Passed stage 2");

                // Modify prefab, reload the scene and ensure it is updated with modified prefab
                {
                    {
                        // unitTest4Scene_0.prefab:
                        // so0
                        //  - so0_1 (Comp1)
                        //    - so0_1_0 (Comp1)
                        // so1 (Comp1, Comp2)
                        //  - so1_0
                        //  - so1_1

                        Scene.Load("unitTest4Scene_0.prefab");

                        SceneObject sceneRoot = Scene.Root;
                        SceneObject so0       = sceneRoot.FindChild("so0", false);
                        SceneObject so0_0     = so0.FindChild("so0_0", false);
                        SceneObject so0_1     = so0.FindChild("so0_1", false);
                        SceneObject so1       = sceneRoot.FindChild("so1", false);
                        SceneObject so1_0     = so1.FindChild("so1_0", false);
                        SceneObject so0_1_0   = so0_1.FindChild("so0_1_0", false);

                        SceneObject so1_1 = new SceneObject("so1_1");
                        so1_1.Parent = so1;

                        so0.RemoveComponent <UT1_Component1>();
                        UT1_Component1 comp1     = so1.AddComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();

                        so0_0.Destroy();

                        comp1.otherSO         = so1_0;
                        comp1.otherComponent2 = comp0_1_0;

                        comp0_1_0.otherSO         = so1_1;
                        comp0_1_0.otherComponent2 = comp1;
                        comp0_1_0.a = 123;
                        comp0_1_0.b = "modifiedValue";

                        so1.Name          = "so1_modified";
                        so1.LocalPosition = new Vector3(0, 999.0f, 0.0f);

                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
                    }

                    {
                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");

                        SceneObject parentSO0   = Scene.Root.FindChild("parentSO0", false);
                        SceneObject parentSO1   = Scene.Root.FindChild("parentSO1", false);
                        SceneObject parentSO1_0 = parentSO1.FindChild("parentSO1_0", false);

                        UT1_Component1 parentComp1_0 = parentSO1_0.GetComponent <UT1_Component1>();

                        SceneObject prefabInstance = parentSO0.GetChild(0);
                        SceneObject so0            = prefabInstance.FindChild("so0", false);
                        SceneObject so1            = prefabInstance.FindChild("so1_modified", false);
                        SceneObject so0_0          = so0.FindChild("so0_0", false);
                        SceneObject so0_1          = so0.FindChild("so0_1", false);
                        SceneObject so1_0          = so1.FindChild("so1_0", false);
                        SceneObject so0_1_0        = so0_1.FindChild("so0_1_0", false);
                        SceneObject so1_1          = so1.FindChild("so1_1", false);

                        UT1_Component1 comp0     = so0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp1     = so1.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();

                        Assert(parentComp1_0.otherSO == so1_0);
                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
                        Assert(so1_1 != null);
                        Assert(so0_0 == null);
                        Assert(comp0 == null);
                        Assert(comp0_1_0.otherSO == so1_1);
                        Assert(comp0_1_0.otherComponent2 == comp1);
                        Assert(comp0_1_0.a == 123);
                        Assert(comp0_1_0.b == "modifiedValue");
                        Assert(comp1.otherSO == so1_0);
                        Assert(comp1.otherComponent2 == comp0_1_0);
                        Assert(MathEx.ApproxEquals(so1.LocalPosition.y, 999.0f));
                    }
                }

                Debug.Log("Passed stage 3");

                // Make instance specific changes to the prefab, modify the prefab itself and ensure
                // both changes persist
                {
                    // Create new scene referencing the prefab and make instance modifications
                    {
                        // unitTest4Scene_2.prefab:
                        // parent2SO0
                        //  - [unitTest4Scene_0.prefab]
                        // parent2SO1
                        //  - parent2SO1_0 (Comp1)

                        // unitTest4Scene_0.prefab (unitTest4Scene_2.prefab instance):
                        // so0 (Comp1(INSTANCE))
                        //  - so0_0 (INSTANCE)
                        //  - so0_1 (Comp1)
                        //    - so0_1_0 (Comp1)
                        // so1 (Comp2)
                        //  - so1_0

                        Scene.Clear();

                        SceneObject parent2SO0   = new SceneObject("parent2SO0");
                        SceneObject parent2SO1   = new SceneObject("parent2SO1");
                        SceneObject parent2SO1_0 = new SceneObject("parent2SO1_0");

                        parent2SO1_0.Parent = parent2SO1;

                        UT1_Component1 parentComp1_0 = parent2SO1_0.AddComponent <UT1_Component1>();

                        Prefab      scene0Prefab   = ProjectLibrary.Load <Prefab>("unitTest4Scene_0.prefab");
                        SceneObject prefabInstance = scene0Prefab.Instantiate();
                        prefabInstance.Parent = parent2SO0;

                        SceneObject so0 = prefabInstance.FindChild("so0", false);
                        SceneObject so1 = prefabInstance.FindChild("so1_modified", false);

                        SceneObject so0_1   = so0.FindChild("so0_1", false);
                        SceneObject so1_0   = so1.FindChild("so1_0", false);
                        SceneObject so1_1   = so1.FindChild("so1_1", false);
                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);

                        UT1_Component2 comp1     = so1.GetComponent <UT1_Component2>();
                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1   = so0_1.GetComponent <UT1_Component1>();

                        SceneObject so0_0 = new SceneObject("so0_0");
                        so0_0.Parent = so0;
                        UT1_Component1 comp0 = so0.AddComponent <UT1_Component1>();

                        so1.RemoveComponent <UT1_Component1>();
                        so1_1.Destroy();

                        comp0.otherSO        = so0_1_0;
                        comp0.otherComponent = comp1;

                        parentComp1_0.otherSO         = so1_0;
                        parentComp1_0.otherComponent2 = comp0_1_0;

                        comp0_1_0.otherSO         = parent2SO1_0;
                        comp0_1_0.otherComponent2 = parentComp1_0;
                        comp0_1_0.b = "instanceValue";

                        comp0_1.b = "instanceValue2";

                        EditorApplication.SaveScene("unitTest4Scene_2.prefab");
                    }

                    Debug.Log("Passed stage 4.1");

                    // Reload the scene and ensure instance modifications remain
                    {
                        EditorApplication.LoadScene("unitTest4Scene_2.prefab");

                        SceneObject root         = Scene.Root;
                        SceneObject parent2SO0   = root.FindChild("parent2SO0", false);
                        SceneObject parent2SO1   = root.FindChild("parent2SO1", false);
                        SceneObject parent2SO1_0 = parent2SO1.FindChild("parent2SO1_0", false);

                        SceneObject prefabInstance = parent2SO0.GetChild(0);

                        SceneObject so0     = prefabInstance.FindChild("so0", false);
                        SceneObject so1     = prefabInstance.FindChild("so1_modified", false);
                        SceneObject so0_0   = so0.FindChild("so0_0", false);
                        SceneObject so0_1   = so0.FindChild("so0_1", false);
                        SceneObject so1_0   = so1.FindChild("so1_0", false);
                        SceneObject so1_1   = so1.FindChild("so1_1", false);
                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);

                        UT1_Component1 parentComp1_0 = parent2SO1_0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0         = so0.GetComponent <UT1_Component1>();
                        UT1_Component2 comp1         = so1.GetComponent <UT1_Component2>();
                        UT1_Component1 comp11        = so1.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0     = so0_1_0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1       = so0_1.GetComponent <UT1_Component1>();

                        Assert(so0_0 != null);
                        Assert(comp0 != null);
                        Assert(so1_1 == null);
                        Assert(comp11 == null);

                        Assert(comp0.otherSO == so0_1_0);
                        Assert(comp0.otherComponent == comp1);

                        Assert(parentComp1_0.otherSO == so1_0);
                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);

                        Debug.Log(comp0_1_0.otherSO == null);
                        if (comp0_1_0.otherSO != null)
                        {
                            Debug.Log(comp0_1_0.otherSO.InstanceId + " - " + parent2SO1_0.InstanceId);
                        }

                        Assert(comp0_1_0.otherSO == parent2SO1_0);
                        Assert(comp0_1_0.otherComponent2 == parentComp1_0);
                        Assert(comp0_1_0.b == "instanceValue");

                        Assert(comp0_1.b == "instanceValue2");
                    }

                    Debug.Log("Passed stage 4.2");

                    // Load original scene and ensure instance modifications didn't influence it
                    {
                        EditorApplication.LoadScene("unitTest4Scene_1.prefab");

                        SceneObject parentSO0   = Scene.Root.FindChild("parentSO0", false);
                        SceneObject parentSO1   = Scene.Root.FindChild("parentSO1", false);
                        SceneObject parentSO1_0 = parentSO1.FindChild("parentSO1_0", false);

                        UT1_Component1 parentComp1_0 = parentSO1_0.GetComponent <UT1_Component1>();

                        SceneObject prefabInstance = parentSO0.GetChild(0);
                        SceneObject so0            = prefabInstance.FindChild("so0", false);
                        SceneObject so1            = prefabInstance.FindChild("so1_modified", false);
                        SceneObject so0_0          = so0.FindChild("so0_0", false);
                        SceneObject so0_1          = so0.FindChild("so0_1", false);
                        SceneObject so1_0          = so1.FindChild("so1_0", false);
                        SceneObject so0_1_0        = so0_1.FindChild("so0_1_0", false);
                        SceneObject so1_1          = so1.FindChild("so1_1", false);

                        UT1_Component1 comp0     = so0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp1     = so1.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1   = so0_1.GetComponent <UT1_Component1>();

                        Assert(parentComp1_0.otherSO == so1_0);
                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);
                        Assert(so1_1 != null);
                        Assert(so0_0 == null);
                        Assert(comp0 == null);
                        Assert(comp0_1_0.otherSO == so1_1);
                        Assert(comp0_1_0.otherComponent2 == comp1);
                        Assert(comp0_1_0.a == 123);
                        Assert(comp0_1_0.b == "modifiedValue");
                        Assert(comp1.otherSO == so1_0);
                        Assert(comp1.otherComponent2 == comp0_1_0);
                        Assert(comp0_1.b == "originalValue2");
                        Assert(MathEx.ApproxEquals(so1.LocalPosition.y, 999.0f));
                    }

                    Debug.Log("Passed stage 4.3");

                    // Modify prefab and ensure both prefab and instance modifications remain
                    {
                        // unitTest4Scene_0.prefab:
                        // so0 (Comp1)
                        //  - so0_1
                        //    - so0_1_0 (Comp1)
                        // so1 (Comp1, Comp2)
                        //  - so1_1
                        //  - so1_2 (Comp1)

                        // unitTest4Scene_0.prefab (unitTest4Scene_2.prefab instance):
                        // so0 (Comp1)
                        //  - so0_0
                        //  - so0_1 (Comp1)
                        //    - so0_1_0 (Comp1)
                        // so1 (Comp2)
                        //  - so1_2 (Comp1)

                        Scene.Load("unitTest4Scene_0.prefab");

                        SceneObject sceneRoot = Scene.Root;
                        SceneObject so0       = sceneRoot.FindChild("so0", false);
                        SceneObject so0_1     = so0.FindChild("so0_1", false);
                        SceneObject so1       = sceneRoot.FindChild("so1_modified", false);
                        SceneObject so1_0     = so1.FindChild("so1_0", false);
                        SceneObject so0_1_0   = so0_1.FindChild("so0_1_0", false);

                        SceneObject so1_2 = new SceneObject("so1_2");
                        so1_2.Parent = so1;

                        so0.AddComponent <UT1_Component1>();
                        so0_1.RemoveComponent <UT1_Component1>();
                        so1_0.Destroy();

                        UT1_Component1 comp3     = so1_2.AddComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0 = so0_1_0.GetComponent <UT1_Component1>();
                        comp0_1_0.b = "modifiedValueAgain";
                        so1.Name    = "so1_modifiedAgain";

                        comp3.otherSO         = so0_1;
                        comp3.otherComponent2 = comp0_1_0;

                        EditorApplication.SaveScene("unitTest4Scene_0.prefab");
                    }

                    Debug.Log("Passed stage 4.4");

                    // Reload the scene and ensure both instance and prefab modifications remain
                    {
                        EditorApplication.LoadScene("unitTest4Scene_2.prefab");

                        SceneObject root         = Scene.Root;
                        SceneObject parent2SO0   = root.FindChild("parent2SO0", false);
                        SceneObject parent2SO1   = root.FindChild("parent2SO1", false);
                        SceneObject parent2SO1_0 = parent2SO1.FindChild("parent2SO1_0", false);

                        SceneObject prefabInstance = parent2SO0.GetChild(0);

                        SceneObject so0     = prefabInstance.FindChild("so0", false);
                        SceneObject so1     = prefabInstance.FindChild("so1_modifiedAgain", false);
                        SceneObject so0_0   = so0.FindChild("so0_0", false);
                        SceneObject so0_1   = so0.FindChild("so0_1", false);
                        SceneObject so1_0   = so1.FindChild("so1_0", false);
                        SceneObject so1_1   = so1.FindChild("so1_1", false);
                        SceneObject so1_2   = so1.FindChild("so1_2", false);
                        SceneObject so0_1_0 = so0_1.FindChild("so0_1_0", false);

                        UT1_Component1 parentComp1_0 = parent2SO1_0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0         = so0.GetComponent <UT1_Component1>();
                        UT1_Component2 comp1         = so1.GetComponent <UT1_Component2>();
                        UT1_Component1 comp11        = so1.GetComponent <UT1_Component1>();
                        UT1_Component1 comp0_1_0     = so0_1_0.GetComponent <UT1_Component1>();
                        UT1_Component1 comp3         = so1_2.AddComponent <UT1_Component1>();

                        // Check instance modifications (they should override any prefab modifications)
                        Assert(so0_0 != null);
                        Assert(comp0 != null);
                        Assert(so1_1 == null);
                        Assert(comp11 == null);

                        Assert(comp0.otherSO == so0_1_0);
                        Assert(comp0.otherComponent == comp1);

                        Assert(parentComp1_0.otherSO == so1_0);
                        Assert(parentComp1_0.otherComponent2 == comp0_1_0);

                        Assert(comp0_1_0.otherSO == parent2SO1_0);
                        Assert(comp0_1_0.otherComponent2 == parentComp1_0);
                        Assert(comp0_1_0.b == "instanceValue");

                        // Check prefab modifications
                        Assert(so1_0 == null);
                        Assert(so1.Name == "so1_modifiedAgain");
                        Assert(comp3.otherSO == so0_1);
                        Assert(comp3.otherComponent2 == comp0_1_0);
                    }

                    Debug.Log("Passed stage 4.5");
                }
            }
            catch
            {
                PrintSceneState();

                throw;
            }
            finally
            {
                if (!string.IsNullOrEmpty(oldScene))
                {
                    Scene.Load(ProjectLibrary.GetPath(oldScene));
                }
                else
                {
                    Scene.Clear();
                }

                ProjectLibrary.Delete("unitTest4Scene_0.prefab");
                ProjectLibrary.Delete("unitTest4Scene_1.prefab");
                ProjectLibrary.Delete("unitTest4Scene_2.prefab");
            }
        }