protected void finishRecording(ProgressBar progress) { string targetFolderPath = targetFolder.Path; if (targetFolderPath == null) { if (gameObject.scene.IsValid() && !string.IsNullOrEmpty(gameObject.scene.path)) { string sceneFolder = Path.GetDirectoryName(gameObject.scene.path); targetFolderPath = Path.Combine(sceneFolder, "Recordings"); } else { targetFolderPath = Path.Combine("Assets", "Recordings"); } } int folderSuffix = 1; string finalSubFolder; do { finalSubFolder = Path.Combine(targetFolderPath, recordingName + " " + folderSuffix.ToString().PadLeft(2, '0')); folderSuffix++; } while (Directory.Exists(finalSubFolder)); string dataDirectory = Path.Combine(finalSubFolder, "_Data"); Directory.CreateDirectory(dataDirectory); Directory.CreateDirectory(finalSubFolder); AssetDatabase.Refresh(); progress.Begin(6, "Saving Recording", "", () => { if (!_isRecording) { return; } _isRecording = false; //Turn on auto-pushing for all auto-proxy components foreach (var autoProxy in GetComponentsInChildren <AutoValueProxy>()) { autoProxy.autoPushingEnabled = true; } progress.Begin(3, "", "Reverting Scene State", () => { //For all of our transform data, revert to the first piece recorded progress.Begin(_transformData.Count, "", "", () => { foreach (var pair in _transformData) { progress.Step(); var transform = pair.Key; var data = pair.Value; if (transform == null || data.Count == 0) { continue; } data[0].ApplyTo(transform); } }); //For all recorded curves, revert to start of curve progress.Begin(_curves.Count, "", "", () => { AnimationClip tempClip = new AnimationClip(); foreach (var data in _curves) { progress.Step(); AnimationUtility.SetEditorCurve(tempClip, data.binding, data.curve); } tempClip.SampleAnimation(gameObject, 0); }); //For all non-transform components, revert to original serialized values progress.Begin(_initialComponentData.Count, "", "", () => { foreach (var pair in _initialComponentData) { progress.Step(); var component = pair.Key; var sobj = pair.Value; if (component == null || component is Transform) { continue; } //We don't want to revert method recordings! if (component is MethodRecording || component is RecordedAudio) { continue; } var flags = sobj.FindProperty("m_ObjectHideFlags"); if (flags == null) { Debug.LogError("Could not find hide flags for " + component); continue; } //We have to dirty the serialized object somehow //apparently there is no api to do this //all objects have hide flags so we just touch them and revert them int originalFlags = flags.intValue; flags.intValue = ~originalFlags; flags.intValue = originalFlags; try { //Applies previous state of entire component sobj.ApplyModifiedProperties(); } catch (Exception e) { Debug.LogError("Exception when trying to apply properties to " + component); Debug.LogException(e); } } }); }); progress.Begin(1, "", "Patching Materials: ", () => { GetComponentsInChildren(true, _recorders); foreach (var recorder in _recorders) { DestroyImmediate(recorder); } //Patch up renderer references to materials var allMaterials = Resources.FindObjectsOfTypeAll <Material>(). Query(). Where(AssetDatabase.IsMainAsset). ToList(); var renderers = GetComponentsInChildren <Renderer>(includeInactive: true); progress.Begin(renderers.Length, "", "", () => { foreach (var renderer in renderers) { progress.Step(renderer.name); var materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { var material = materials[i]; if (material == null) { continue; } if (!AssetDatabase.IsMainAsset(material)) { var matchingMaterial = allMaterials.Query().FirstOrDefault(m => material.name.Contains(m.name) && material.shader == m.shader); if (matchingMaterial != null) { materials[i] = matchingMaterial; } } } renderer.sharedMaterials = materials; } }); }); progress.Begin(_behaviourActivity.Count, "", "Converting Activity Data: ", () => { foreach (var pair in _behaviourActivity) { var targetBehaviour = pair.Key; var activityData = pair.Value; if (targetBehaviour == null) { continue; } progress.Step(targetBehaviour.name); string path = AnimationUtility.CalculateTransformPath(targetBehaviour.transform, transform); Type type = targetBehaviour.GetType(); string propertyName = "m_Enabled"; AnimationCurve curve = new AnimationCurve(); foreach (var dataPoint in activityData) { int index = curve.AddKey(dataPoint.time, dataPoint.enabled ? 1 : 0); AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } if (curve.IsConstant()) { continue; } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.Query().Any(c => c.binding == binding)) { Debug.LogError("Binding already existed?"); Debug.LogError(binding.path + " : " + binding.propertyName); continue; } _curves.Add(new CurveData() { binding = binding, curve = curve }); } }); progress.Begin(_transformData.Count, "", "Converting Transform Data: ", () => { foreach (var pair in _transformData) { var targetTransform = pair.Key; var targetData = pair.Value; progress.Step(targetTransform.name); string path = AnimationUtility.CalculateTransformPath(targetTransform, transform); bool isActivityConstant = true; bool isPositionConstant = true; bool isRotationConstant = true; bool isScaleConstant = true; { bool startEnabled = targetData[0].enabled; Vector3 startPosition = targetData[0].localPosition; Quaternion startRotation = targetData[0].localRotation; Vector3 startScale = targetData[0].localScale; for (int i = 1; i < targetData.Count; i++) { isActivityConstant &= targetData[i].enabled == startEnabled; isPositionConstant &= targetData[i].localPosition == startPosition; isRotationConstant &= targetData[i].localRotation == startRotation; isScaleConstant &= targetData[i].localScale == startScale; } } for (int i = 0; i < TransformData.CURVE_COUNT; i++) { string propertyName = TransformData.GetName(i); Type type = typeof(Transform); AnimationCurve curve = new AnimationCurve(); var dataType = TransformData.GetDataType(i); switch (dataType) { case TransformDataType.Position: if (isPositionConstant) { continue; } break; case TransformDataType.Rotation: if (isRotationConstant) { continue; } break; case TransformDataType.Scale: if (isScaleConstant) { continue; } break; case TransformDataType.Activity: if (isActivityConstant) { continue; } type = typeof(GameObject); break; } for (int j = 0; j < targetData.Count; j++) { int index = curve.AddKey(targetData[j].time, targetData[j].GetFloat(i)); if (dataType == TransformDataType.Activity) { AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.Query().Any(c => c.binding == binding)) { Debug.LogError("Duplicate object was created??"); Debug.LogError("Named " + targetTransform.name + " : " + binding.path + " : " + binding.propertyName); } else { _curves.Add(new CurveData() { binding = binding, curve = curve }); } } } }); progress.Begin(_curves.Count, "", "Compressing Data: ", () => { _curves.Sort((a, b) => a.binding.propertyName.CompareTo(b.binding.propertyName)); foreach (var data in _curves) { using (new ProfilerSample("A")) { EditorCurveBinding binding = data.binding; AnimationCurve curve = data.curve; progress.Step(binding.propertyName); GameObject animationGameObject; { var animatedObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (animatedObj is GameObject) { animationGameObject = animatedObj as GameObject; } else { animationGameObject = (animatedObj as Component).gameObject; } } bool isMatBinding = binding.propertyName.StartsWith("material.") && binding.type.IsSubclassOf(typeof(Renderer)); //But if the curve is constant, just get rid of it! //Except for material curves, which we always need to keep if (curve.IsConstant() && !isMatBinding) { //Check to make sure there are no other matching curves that are //non constant. If X and Y are constant but Z is not, we need to //keep them all :( if (_curves.Query().Where(p => p.binding.path == binding.path && p.binding.type == binding.type && p.binding.propertyName.TrimEnd(2) == binding.propertyName.TrimEnd(2)). All(k => k.curve.IsConstant())) { continue; } } //First do a lossless compression using (new ProfilerSample("B")) { curve = AnimationCurveUtil.Compress(curve, Mathf.Epsilon, checkSteps: 3); } Transform targetTransform = null; var targetObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (targetObj is GameObject) { targetTransform = (targetObj as GameObject).transform; } else if (targetObj is Component) { targetTransform = (targetObj as Component).transform; } else { Debug.LogError("Target obj was of type " + targetObj.GetType().Name); } } } }); }); progress.Begin(4, "Finalizing Assets", "", () => { var postProcessComponent = gameObject.AddComponent <HierarchyPostProcess>(); GameObject myGameObject = gameObject; DestroyImmediate(this); //Create all the files for the method recording progress.Step("Creating Method Recording Files..."); var methodRecordings = myGameObject.GetComponentsInChildren <MethodRecording>(); for (int i = 0; i < methodRecordings.Length; i++) { var methodRecording = methodRecordings[i]; string fullPath = Path.Combine(finalSubFolder, "MethodRecording_" + i + ".asset"); methodRecording.ExitRecordingMode(fullPath); } postProcessComponent.dataFolder = new AssetFolder(dataDirectory); //Create the asset that holds all of the curve data progress.Begin(_curves.Count, "", "", () => { string curveFile = Path.Combine(dataDirectory, "Curves.data"); using (var writer = File.CreateText(curveFile)) { foreach (var data in _curves) { progress.Step(data.binding.propertyName); var bindingData = new EditorCurveBindingData() { path = data.binding.path, propertyName = data.binding.propertyName, typeName = data.binding.type.Name, curve = data.curve }; writer.WriteLine(JsonUtility.ToJson(bindingData)); } } }); //Create the asset that holds all of the leap data if (_leapData.Count > 0) { progress.Begin(_leapData.Count, "", "", () => { string leapFile = Path.Combine(dataDirectory, "Frames.data"); using (var writer = File.CreateText(leapFile)) { for (int i = 0; i < _leapData.Count; i++) { progress.Step("Frame " + i); writer.WriteLine(JsonUtility.ToJson(_leapData[i])); } } }); } progress.Step("Creating Final Prefab..."); //Init the post process component postProcessComponent.recordingName = recordingName; postProcessComponent.assetFolder = new AssetFolder(finalSubFolder); string prefabPath = Path.Combine(finalSubFolder, recordingName + " Raw.prefab"); PrefabUtility.CreatePrefab(prefabPath.Replace('\\', '/'), myGameObject); AssetDatabase.Refresh(); EditorApplication.isPlaying = false; }); }
protected void finishRecording(ProgressBar progress) { progress.Begin(5, "Saving Recording", "", () => { if (!_isRecording) { return; } _isRecording = false; //Turn on auto-pushing for all auto-proxy components foreach (var autoProxy in GetComponentsInChildren <AutoValueProxy>()) { autoProxy.autoPushingEnabled = true; } progress.Begin(1, "", "Reverting Scene State", () => { foreach (var pair in _initialTransformData) { pair.Key.localPosition = pair.Value.localPosition; pair.Key.localRotation = pair.Value.localRotation; pair.Key.localScale = pair.Value.localScale; pair.Key.gameObject.SetActive(pair.Value.enabled); } foreach (var pair in _initialActivityData) { EditorUtility.SetObjectEnabled(pair.Key, pair.Value); } }); progress.Begin(1, "", "Patching Materials: ", () => { GetComponentsInChildren(true, _recorders); foreach (var recorder in _recorders) { DestroyImmediate(recorder); } //Patch up renderer references to materials var allMaterials = Resources.FindObjectsOfTypeAll <Material>(). Query(). Where(AssetDatabase.IsMainAsset). ToList(); var renderers = GetComponentsInChildren <Renderer>(includeInactive: true); progress.Begin(renderers.Length, "", "", () => { foreach (var renderer in renderers) { progress.Step(renderer.name); var materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { var material = materials[i]; if (!AssetDatabase.IsMainAsset(material)) { var matchingMaterial = allMaterials.Query().FirstOrDefault(m => material.name.Contains(m.name) && material.shader == m.shader); if (matchingMaterial != null) { materials[i] = matchingMaterial; } } } renderer.sharedMaterials = materials; } }); }); progress.Begin(_behaviourActivity.Count, "", "Converting Activity Data: ", () => { foreach (var pair in _behaviourActivity) { var targetBehaviour = pair.Key; var activityData = pair.Value; progress.Step(targetBehaviour.name); string path = AnimationUtility.CalculateTransformPath(targetBehaviour.transform, transform); Type type = targetBehaviour.GetType(); string propertyName = "m_Enabled"; AnimationCurve curve = new AnimationCurve(); foreach (var dataPoint in activityData) { int index = curve.AddKey(dataPoint.time, dataPoint.enabled ? 1 : 0); AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } if (curve.IsConstant()) { continue; } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.ContainsKey(binding)) { Debug.LogError("Binding already existed?"); Debug.LogError(binding.path + " : " + binding.propertyName); continue; } _curves.Add(binding, curve); } }); progress.Begin(_transformData.Count, "", "Converting Transform Data: ", () => { foreach (var pair in _transformData) { var targetTransform = pair.Key; var targetData = pair.Value; progress.Step(targetTransform.name); string path = AnimationUtility.CalculateTransformPath(targetTransform, transform); bool isActivityConstant = true; bool isPositionConstant = true; bool isRotationConstant = true; bool isScaleConstant = true; { bool startEnabled = targetData[0].enabled; Vector3 startPosition = targetData[0].localPosition; Quaternion startRotation = targetData[0].localRotation; Vector3 startScale = targetData[0].localScale; for (int i = 1; i < targetData.Count; i++) { isActivityConstant &= targetData[i].enabled == startEnabled; isPositionConstant &= targetData[i].localPosition == startPosition; isRotationConstant &= targetData[i].localRotation == startRotation; isScaleConstant &= targetData[i].localScale == startScale; } } for (int i = 0; i < TransformData.CURVE_COUNT; i++) { string propertyName = TransformData.GetName(i); Type type = typeof(Transform); AnimationCurve curve = new AnimationCurve(); var dataType = TransformData.GetDataType(i); switch (dataType) { case TransformDataType.Position: if (isPositionConstant) { continue; } break; case TransformDataType.Rotation: if (isRotationConstant) { continue; } break; case TransformDataType.Scale: if (isScaleConstant) { continue; } break; case TransformDataType.Activity: if (isActivityConstant) { continue; } type = typeof(GameObject); break; } for (int j = 0; j < targetData.Count; j++) { int index = curve.AddKey(targetData[j].time, targetData[j].GetFloat(i)); if (dataType == TransformDataType.Activity) { AnimationUtility.SetKeyLeftTangentMode(curve, index, AnimationUtility.TangentMode.Constant); AnimationUtility.SetKeyRightTangentMode(curve, index, AnimationUtility.TangentMode.Constant); } } var binding = EditorCurveBinding.FloatCurve(path, type, propertyName); if (_curves.ContainsKey(binding)) { Debug.LogError("Duplicate object was created??"); Debug.LogError("Named " + targetTransform.name + " : " + binding.path + " : " + binding.propertyName); } else { _curves.Add(binding, curve); } } } }); progress.Begin(_curves.Count, "", "Compressing Data: ", () => { foreach (var pair in _curves) { EditorCurveBinding binding = pair.Key; AnimationCurve curve = pair.Value; progress.Step(binding.propertyName); GameObject animationGameObject; { var animatedObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (animatedObj is GameObject) { animationGameObject = animatedObj as GameObject; } else { animationGameObject = (animatedObj as Component).gameObject; } } //But if the curve is constant, just get rid of it! if (curve.IsConstant()) { //Check to make sure there are no other matching curves that are //non constant. If X and Y are constant but Z is not, we need to //keep them all :( if (_curves.Query().Where(p => p.Key.path == binding.path && p.Key.type == binding.type && p.Key.propertyName.TrimEnd(2) == binding.propertyName.TrimEnd(2)). All(k => k.Value.IsConstant())) { continue; } } //First do a lossless compression curve = AnimationCurveUtil.Compress(curve, Mathf.Epsilon); Transform targetTransform = null; var targetObj = AnimationUtility.GetAnimatedObject(gameObject, binding); if (targetObj is GameObject) { targetTransform = (targetObj as GameObject).transform; } else if (targetObj is Component) { targetTransform = (targetObj as Component).transform; } else { Debug.LogError("Target obj was of type " + targetObj.GetType().Name); } var dataRecorder = targetTransform.GetComponent <RecordedData>(); if (dataRecorder == null) { dataRecorder = targetTransform.gameObject.AddComponent <RecordedData>(); } dataRecorder.data.Add(new RecordedData.EditorCurveBindingData() { path = binding.path, propertyName = binding.propertyName, typeName = binding.type.Name, curve = curve }); } }); progress.Step("Finalizing Prefab..."); var postProcessComponent = gameObject.AddComponent <HierarchyPostProcess>(); GameObject myGameObject = gameObject; DestroyImmediate(this); string targetFolderPath = targetFolder.Path; if (targetFolderPath == null) { if (myGameObject.scene.IsValid() && !string.IsNullOrEmpty(myGameObject.scene.path)) { string sceneFolder = Path.GetDirectoryName(myGameObject.scene.path); targetFolderPath = Path.Combine(sceneFolder, "Recordings"); } else { targetFolderPath = Path.Combine("Assets", "Recordings"); } } int folderSuffix = 1; string finalSubFolder; do { finalSubFolder = Path.Combine(targetFolderPath, recordingName + " " + folderSuffix.ToString().PadLeft(2, '0')); folderSuffix++; } while (Directory.Exists(finalSubFolder)); Directory.CreateDirectory(finalSubFolder); AssetDatabase.Refresh(); postProcessComponent.recordingName = recordingName; postProcessComponent.assetFolder.Path = finalSubFolder; postProcessComponent.leapData = _leapData; string prefabPath = Path.Combine(finalSubFolder, recordingName + " Raw.prefab"); PrefabUtility.CreatePrefab(prefabPath.Replace('\\', '/'), myGameObject); AssetDatabase.Refresh(); EditorApplication.isPlaying = false; }); }