release(camera-ai): publish 0.4.14 importer hotfix
This commit is contained in:
@@ -440,7 +440,7 @@ namespace Streamingle.Editor
|
||||
originalDirector.gameObject.activeSelf;
|
||||
try
|
||||
{
|
||||
previewDirectorObject = UnityEngine.Object.Instantiate(
|
||||
previewDirectorObject = InstantiateInactivePreviewCloneForEditor(
|
||||
originalDirector.gameObject,
|
||||
originalDirector.transform.parent);
|
||||
Undo.RegisterCreatedObjectUndo(
|
||||
@@ -456,6 +456,7 @@ namespace Streamingle.Editor
|
||||
originalTimeline,
|
||||
previewDirector,
|
||||
copiedTimeline);
|
||||
SuppressAuthoredCinemachineOutputsForEditor(previewDirector);
|
||||
MuteCopiedCameraTracks(previewDirector, copiedTimeline);
|
||||
MuteCopiedRecorderTracks(copiedTimeline);
|
||||
|
||||
@@ -595,6 +596,7 @@ namespace Streamingle.Editor
|
||||
$"Position Dense C2 Fallbacks: {positionDenseFallbackCount:N0}",
|
||||
$"Preview Director: {GetHierarchyPath(previewDirector.transform)}",
|
||||
$"Preview Camera Root: {GetHierarchyPath(previewCameraObject.transform)}",
|
||||
GetPreviewOutputStatusForEditor(brain),
|
||||
$"Timeline Asset: {copiedTimelinePath}",
|
||||
$"Animation Assets: {generatedClipPaths.Count:N0} " +
|
||||
$"under {shotAnimationFolder}",
|
||||
@@ -1449,6 +1451,8 @@ namespace Streamingle.Editor
|
||||
TimelineAsset finalTimeline = null;
|
||||
GameObject cameraPrefab = null;
|
||||
GameObject finalCameraRoot = null;
|
||||
AuthoredCinemachineStateSnapshot destinationCinemachineStateBefore =
|
||||
null;
|
||||
try
|
||||
{
|
||||
var sourceTimelinePath = AssetDatabase.GetAssetPath(sourceTimeline);
|
||||
@@ -1511,6 +1515,11 @@ namespace Streamingle.Editor
|
||||
destinationTimelineBefore,
|
||||
finalTimeline,
|
||||
finalCameraRoot);
|
||||
destinationCinemachineStateBefore =
|
||||
CaptureAuthoredCinemachineState(destinationDirector);
|
||||
SuppressAuthoredCinemachineOutputsForEditor(
|
||||
destinationDirector,
|
||||
false);
|
||||
destinationDirector.time = destinationTimeBefore;
|
||||
destinationDirector.RebuildGraph();
|
||||
destinationDirector.time = destinationTimeBefore;
|
||||
@@ -1545,6 +1554,8 @@ namespace Streamingle.Editor
|
||||
}
|
||||
catch
|
||||
{
|
||||
RestoreAuthoredCinemachineState(
|
||||
destinationCinemachineStateBefore);
|
||||
RestoreDestinationDirectorAfterFinalizationFailure(
|
||||
destinationStateBefore,
|
||||
finalTimeline);
|
||||
@@ -1943,6 +1954,7 @@ namespace Streamingle.Editor
|
||||
destinationDirector.SetReferenceValue(exposedName, camera);
|
||||
EditorUtility.SetDirty(shot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static void ClearTimelineBindingsForFinalization(
|
||||
@@ -5575,6 +5587,46 @@ namespace Streamingle.Editor
|
||||
return clonedComponents[index];
|
||||
}
|
||||
|
||||
internal static GameObject InstantiateInactivePreviewCloneForEditor(
|
||||
GameObject sourceObject,
|
||||
Transform destinationParent)
|
||||
{
|
||||
if (sourceObject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceObject));
|
||||
}
|
||||
|
||||
var sourceScene = sourceObject.scene;
|
||||
if (!sourceScene.IsValid() || !sourceScene.isLoaded)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The source Timeline object must belong to a loaded scene.");
|
||||
}
|
||||
|
||||
var stagingObject = new GameObject(
|
||||
"__CWAI_InactivePreviewCloneStaging");
|
||||
stagingObject.SetActive(false);
|
||||
try
|
||||
{
|
||||
if (stagingObject.scene != sourceScene)
|
||||
{
|
||||
SceneManager.MoveGameObjectToScene(stagingObject, sourceScene);
|
||||
}
|
||||
|
||||
var clone = UnityEngine.Object.Instantiate(
|
||||
sourceObject,
|
||||
stagingObject.transform,
|
||||
true);
|
||||
clone.SetActive(false);
|
||||
clone.transform.SetParent(destinationParent, true);
|
||||
return clone;
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(stagingObject);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureNoExistingPreview(
|
||||
Scene scene,
|
||||
string previewDirectorName,
|
||||
@@ -5645,30 +5697,49 @@ namespace Streamingle.Editor
|
||||
}
|
||||
|
||||
var scene = director.gameObject.scene;
|
||||
var boundBrains = timeline.GetOutputTracks()
|
||||
.OfType<CinemachineTrack>()
|
||||
.Select(track => director.GetGenericBinding(track) as CinemachineBrain)
|
||||
.Where(brain => brain != null)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
if (boundBrains.Length > 1)
|
||||
var directlyBoundBrains = GetBoundCinemachineBrains(
|
||||
director,
|
||||
timeline);
|
||||
if (directlyBoundBrains.Length > 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The Timeline has Cinemachine Tracks bound to multiple " +
|
||||
"CinemachineBrain components. Bind every camera track to one Brain.");
|
||||
}
|
||||
|
||||
if (boundBrains.Length == 1)
|
||||
if (directlyBoundBrains.Length == 1)
|
||||
{
|
||||
var boundBrain = boundBrains[0];
|
||||
if (!IsUsableCinemachineBrain(boundBrain, scene))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The CinemachineBrain bound to the Timeline must be enabled, " +
|
||||
"attached to an enabled Camera, and belong to the Timeline scene.");
|
||||
}
|
||||
return ValidateExplicitCinemachineBrain(
|
||||
directlyBoundBrains[0],
|
||||
scene);
|
||||
}
|
||||
|
||||
return boundBrain;
|
||||
var descendantBoundBrains = director.gameObject
|
||||
.GetComponentsInChildren<PlayableDirector>(true)
|
||||
.Where(descendant =>
|
||||
descendant != null &&
|
||||
descendant != director &&
|
||||
descendant.gameObject.scene == scene &&
|
||||
descendant.playableAsset is TimelineAsset)
|
||||
.SelectMany(descendant => GetBoundCinemachineBrains(
|
||||
descendant,
|
||||
(TimelineAsset)descendant.playableAsset))
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
if (descendantBoundBrains.Length > 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Descendant PlayableDirectors under the selected Timeline object " +
|
||||
"have Cinemachine Tracks bound to multiple CinemachineBrain " +
|
||||
"components. Bind them to one Brain or bind the selected Timeline " +
|
||||
"directly.");
|
||||
}
|
||||
|
||||
if (descendantBoundBrains.Length == 1)
|
||||
{
|
||||
return ValidateExplicitCinemachineBrain(
|
||||
descendantBoundBrains[0],
|
||||
scene);
|
||||
}
|
||||
|
||||
var usableBrains = sceneBrains
|
||||
@@ -5678,7 +5749,7 @@ namespace Streamingle.Editor
|
||||
var mainCameraBrains = usableBrains
|
||||
.Where(brain =>
|
||||
{
|
||||
var camera = brain.GetComponent<Camera>();
|
||||
var camera = brain.OutputCamera;
|
||||
return camera != null && camera.CompareTag("MainCamera");
|
||||
})
|
||||
.ToArray();
|
||||
@@ -5714,6 +5785,81 @@ namespace Streamingle.Editor
|
||||
"one Camera as MainCamera or bind a Cinemachine Track explicitly.");
|
||||
}
|
||||
|
||||
private static CinemachineBrain[] GetBoundCinemachineBrains(
|
||||
PlayableDirector director,
|
||||
TimelineAsset timeline)
|
||||
{
|
||||
return timeline.GetOutputTracks()
|
||||
.OfType<CinemachineTrack>()
|
||||
.Select(track =>
|
||||
director.GetGenericBinding(track) as CinemachineBrain)
|
||||
.Where(brain => brain != null)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static CinemachineBrain ValidateExplicitCinemachineBrain(
|
||||
CinemachineBrain brain,
|
||||
Scene scene)
|
||||
{
|
||||
if (brain.gameObject.scene != scene)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The CinemachineBrain explicitly bound to a Timeline must belong " +
|
||||
"to the same scene as the selected Timeline director.");
|
||||
}
|
||||
|
||||
var camera = brain.OutputCamera;
|
||||
if (camera == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The CinemachineBrain explicitly bound to a Timeline must have an " +
|
||||
"output Camera component on its controlled GameObject.");
|
||||
}
|
||||
|
||||
if (camera.gameObject.scene != scene)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The CinemachineBrain output Camera explicitly bound to a Timeline " +
|
||||
"must belong to the same scene as the selected Timeline director.");
|
||||
}
|
||||
|
||||
if (!brain.isActiveAndEnabled || !camera.isActiveAndEnabled)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"The CinemachineBrain explicitly bound to a Timeline or its " +
|
||||
"Camera is disabled. Camera AI will use the configured Brain " +
|
||||
"without enabling either component automatically.",
|
||||
brain);
|
||||
}
|
||||
|
||||
return brain;
|
||||
}
|
||||
|
||||
internal static string GetPreviewOutputStatusForEditor(
|
||||
CinemachineBrain brain)
|
||||
{
|
||||
if (brain == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(brain));
|
||||
}
|
||||
|
||||
var camera = brain.OutputCamera;
|
||||
var cameraName = camera != null
|
||||
? camera.gameObject.name
|
||||
: brain.gameObject.name;
|
||||
if (camera != null &&
|
||||
brain.isActiveAndEnabled &&
|
||||
camera.isActiveAndEnabled)
|
||||
{
|
||||
return $"Preview Output: configured '{cameraName}' CinemachineBrain " +
|
||||
"and Camera are active.";
|
||||
}
|
||||
|
||||
return $"Preview Output: configured '{cameraName}' CinemachineBrain or " +
|
||||
"Camera is disabled; enable it to see AI camera playback.";
|
||||
}
|
||||
|
||||
private static bool IsUsableCinemachineBrain(
|
||||
CinemachineBrain brain,
|
||||
Scene scene)
|
||||
@@ -5725,8 +5871,193 @@ namespace Streamingle.Editor
|
||||
return false;
|
||||
}
|
||||
|
||||
var camera = brain.GetComponent<Camera>();
|
||||
return camera != null && camera.isActiveAndEnabled;
|
||||
var camera = brain.OutputCamera;
|
||||
return camera != null &&
|
||||
camera.gameObject.scene == scene &&
|
||||
camera.isActiveAndEnabled;
|
||||
}
|
||||
|
||||
internal static void SuppressAuthoredCinemachineOutputsForEditor(
|
||||
PlayableDirector selectedDirector,
|
||||
bool recordUndo = true)
|
||||
{
|
||||
if (selectedDirector == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(selectedDirector));
|
||||
}
|
||||
|
||||
var directorObject = selectedDirector.gameObject;
|
||||
var descendantDirectors = directorObject
|
||||
.GetComponentsInChildren<PlayableDirector>(true)
|
||||
.Where(descendant =>
|
||||
descendant != null &&
|
||||
descendant != selectedDirector &&
|
||||
descendant.playableAsset is TimelineAsset)
|
||||
.ToArray();
|
||||
foreach (var descendantDirector in descendantDirectors)
|
||||
{
|
||||
var descendantTimeline =
|
||||
(TimelineAsset)descendantDirector.playableAsset;
|
||||
var cinemachineTracks = descendantTimeline.GetOutputTracks()
|
||||
.OfType<CinemachineTrack>()
|
||||
.ToArray();
|
||||
if (cinemachineTracks.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (recordUndo)
|
||||
{
|
||||
Undo.RecordObject(
|
||||
descendantDirector,
|
||||
"Disconnect Authored Cameras From AI Preview");
|
||||
}
|
||||
|
||||
foreach (var cinemachineTrack in cinemachineTracks)
|
||||
{
|
||||
descendantDirector.ClearGenericBinding(cinemachineTrack);
|
||||
}
|
||||
|
||||
RebuildDirectorGraphPreservingPlayback(descendantDirector);
|
||||
|
||||
EditorUtility.SetDirty(descendantDirector);
|
||||
}
|
||||
|
||||
foreach (var authoredCamera in directorObject
|
||||
.GetComponentsInChildren<CinemachineVirtualCameraBase>(true))
|
||||
{
|
||||
if (authoredCamera == null || !authoredCamera.enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (recordUndo)
|
||||
{
|
||||
Undo.RecordObject(
|
||||
authoredCamera,
|
||||
"Disable Authored Cameras In AI Output");
|
||||
}
|
||||
|
||||
authoredCamera.enabled = false;
|
||||
EditorUtility.SetDirty(authoredCamera);
|
||||
}
|
||||
}
|
||||
|
||||
internal static AuthoredCinemachineStateSnapshot
|
||||
CaptureAuthoredCinemachineState(PlayableDirector selectedDirector)
|
||||
{
|
||||
if (selectedDirector == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(selectedDirector));
|
||||
}
|
||||
|
||||
var directorObject = selectedDirector.gameObject;
|
||||
var directorStates = directorObject
|
||||
.GetComponentsInChildren<PlayableDirector>(true)
|
||||
.Where(descendant =>
|
||||
descendant != null &&
|
||||
descendant != selectedDirector &&
|
||||
descendant.playableAsset is TimelineAsset)
|
||||
.Select(descendant =>
|
||||
{
|
||||
var timeline = (TimelineAsset)descendant.playableAsset;
|
||||
var tracks = timeline.GetOutputTracks()
|
||||
.OfType<CinemachineTrack>()
|
||||
.ToArray();
|
||||
return new AuthoredCinemachineDirectorState(
|
||||
descendant,
|
||||
tracks,
|
||||
tracks.Select(descendant.GetGenericBinding).ToArray());
|
||||
})
|
||||
.Where(state => state.Tracks.Length > 0)
|
||||
.ToArray();
|
||||
var cameraStates = directorObject
|
||||
.GetComponentsInChildren<CinemachineVirtualCameraBase>(true)
|
||||
.Where(camera => camera != null)
|
||||
.Select(camera => new AuthoredVirtualCameraState(
|
||||
camera,
|
||||
camera.enabled))
|
||||
.ToArray();
|
||||
return new AuthoredCinemachineStateSnapshot(
|
||||
directorStates,
|
||||
cameraStates);
|
||||
}
|
||||
|
||||
internal static void RestoreAuthoredCinemachineState(
|
||||
AuthoredCinemachineStateSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var cameraState in snapshot.CameraStates)
|
||||
{
|
||||
if (cameraState.Camera == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cameraState.Camera.enabled = cameraState.Enabled;
|
||||
EditorUtility.SetDirty(cameraState.Camera);
|
||||
}
|
||||
|
||||
foreach (var directorState in snapshot.DirectorStates)
|
||||
{
|
||||
var director = directorState.Director;
|
||||
if (director == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var index = 0; index < directorState.Tracks.Length; index++)
|
||||
{
|
||||
var track = directorState.Tracks[index];
|
||||
if (track == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var binding = directorState.Bindings[index];
|
||||
if (binding != null)
|
||||
{
|
||||
director.SetGenericBinding(track, binding);
|
||||
}
|
||||
else
|
||||
{
|
||||
director.ClearGenericBinding(track);
|
||||
}
|
||||
}
|
||||
|
||||
RebuildDirectorGraphPreservingPlayback(director);
|
||||
|
||||
EditorUtility.SetDirty(director);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RebuildDirectorGraphPreservingPlayback(
|
||||
PlayableDirector director)
|
||||
{
|
||||
if (!director.playableGraph.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var time = director.time;
|
||||
var wasPlaying = director.state == PlayState.Playing;
|
||||
director.Stop();
|
||||
director.RebuildGraph();
|
||||
director.time = time;
|
||||
if (wasPlaying)
|
||||
{
|
||||
director.Play();
|
||||
director.time = time;
|
||||
}
|
||||
else
|
||||
{
|
||||
director.Evaluate();
|
||||
director.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyTrackBindings(
|
||||
@@ -6944,6 +7275,51 @@ namespace Streamingle.Editor
|
||||
public GameObject CameraRoot { get; }
|
||||
}
|
||||
|
||||
internal sealed class AuthoredCinemachineStateSnapshot
|
||||
{
|
||||
public AuthoredCinemachineStateSnapshot(
|
||||
AuthoredCinemachineDirectorState[] directorStates,
|
||||
AuthoredVirtualCameraState[] cameraStates)
|
||||
{
|
||||
DirectorStates = directorStates;
|
||||
CameraStates = cameraStates;
|
||||
}
|
||||
|
||||
public AuthoredCinemachineDirectorState[] DirectorStates { get; }
|
||||
public AuthoredVirtualCameraState[] CameraStates { get; }
|
||||
}
|
||||
|
||||
internal sealed class AuthoredCinemachineDirectorState
|
||||
{
|
||||
public AuthoredCinemachineDirectorState(
|
||||
PlayableDirector director,
|
||||
CinemachineTrack[] tracks,
|
||||
UnityEngine.Object[] bindings)
|
||||
{
|
||||
Director = director;
|
||||
Tracks = tracks;
|
||||
Bindings = bindings;
|
||||
}
|
||||
|
||||
public PlayableDirector Director { get; }
|
||||
public CinemachineTrack[] Tracks { get; }
|
||||
public UnityEngine.Object[] Bindings { get; }
|
||||
}
|
||||
|
||||
internal sealed class AuthoredVirtualCameraState
|
||||
{
|
||||
public AuthoredVirtualCameraState(
|
||||
CinemachineVirtualCameraBase camera,
|
||||
bool enabled)
|
||||
{
|
||||
Camera = camera;
|
||||
Enabled = enabled;
|
||||
}
|
||||
|
||||
public CinemachineVirtualCameraBase Camera { get; }
|
||||
public bool Enabled { get; }
|
||||
}
|
||||
|
||||
private sealed class DirectorStateSnapshot
|
||||
{
|
||||
public DirectorStateSnapshot(
|
||||
|
||||
Reference in New Issue
Block a user