Skip to content

Commit 0a24bde

Browse files
authored
feat: add components to make it easier to update PencilLineCache (#462)
* add components to make it easier to update PencilLineCache * wrap the new components with AT_USE_PENCILLINE which is defined only if the project is using PencilLine package
1 parent 90dfb1a commit 0a24bde

10 files changed

Lines changed: 357 additions & 3 deletions
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#if AT_USE_PENCILLINE
2+
3+
using System.Collections;
4+
using System.Collections.Generic;
5+
using Pencil_4;
6+
using Unity.EditorCoroutines.Editor;
7+
using Unity.FilmInternalUtilities; //Required when using Timeline 1.4.x or below
8+
using Unity.FilmInternalUtilities.Editor;
9+
using Unity.StreamingImageSequence;
10+
using Unity.StreamingImageSequence.Editor;
11+
using UnityEditor;
12+
using UnityEditor.Timeline;
13+
using UnityEngine;
14+
using UnityEngine.Assertions;
15+
using UnityEngine.Playables;
16+
using UnityEngine.Timeline;
17+
18+
[CustomEditor(typeof(PencilLineCacheUpdater))]
19+
[CanEditMultipleObjects]
20+
internal class PencilLineCacheUpdaterInspector : Editor {
21+
void OnEnable() {
22+
m_lineCacheUpdater = this.target as PencilLineCacheUpdater;
23+
Assert.IsNotNull(m_lineCacheUpdater);
24+
}
25+
26+
public override void OnInspectorGUI() {
27+
EditorGUIDrawerUtility.DrawUndoableGUI(m_lineCacheUpdater, "PencilLineCacheUpdater",
28+
guiFunc: () => {
29+
return EditorGUILayout.ObjectField("Director", m_lineCacheUpdater.GetPlayableDirector(),
30+
typeof(PlayableDirector), allowSceneObjects: true) as PlayableDirector;
31+
},
32+
updateFunc: (PlayableDirector director) => { m_lineCacheUpdater.SetPlayableDirector(director); }
33+
);
34+
35+
PlayableDirector director = m_lineCacheUpdater.GetPlayableDirector();
36+
if (null == director) {
37+
EditorGUILayout.HelpBox("Please assign a PlayableDirector to browse RenderCache assets.", MessageType.Warning);
38+
}
39+
40+
DrawRenderCachePlayableAssetGUI(director);
41+
42+
RenderCachePlayableAsset renderCachePlayableAsset = m_lineCacheUpdater.GetRenderCachePlayableAsset();
43+
if (null == renderCachePlayableAsset) {
44+
EditorGUILayout.HelpBox("Click the browse button and assign a RenderCachePlayableAsset.", MessageType.Warning);
45+
}
46+
47+
48+
DrawDefaultInspector();
49+
GUILayout.Space(30);
50+
51+
bool canUpdate = null != director && null != renderCachePlayableAsset;
52+
EditorGUI.BeginDisabledGroup(!canUpdate);
53+
if (GUILayout.Button("Update Pencil Line Cache")) {
54+
UpdatePencilLineCache(director, renderCachePlayableAsset);
55+
}
56+
57+
EditorGUI.EndDisabledGroup();
58+
}
59+
60+
void DrawRenderCachePlayableAssetGUI(PlayableDirector director) {
61+
const int BUTTON_WIDTH = 30;
62+
63+
Rect rect = EditorGUILayout.GetControlRect();
64+
rect.width -= BUTTON_WIDTH;
65+
EditorGUI.ObjectField(rect, "Render Cache Playable Asset", m_lineCacheUpdater.GetRenderCachePlayableAsset(),
66+
typeof(RenderCachePlayableAsset), allowSceneObjects: false);
67+
68+
69+
Rect buttonRect = rect;
70+
buttonRect.x = rect.x + rect.width;
71+
buttonRect.width = BUTTON_WIDTH;
72+
73+
if (null == director) {
74+
return;
75+
}
76+
77+
if (!GUI.Button(buttonRect, ".."))
78+
return;
79+
80+
// Show the dropdown under the field only, but not the label
81+
Rect fieldRect = rect;
82+
fieldRect.xMin += EditorGUIUtility.labelWidth;
83+
84+
if (null == director.playableAsset) {
85+
EditorUtility.DisplayDialog(SISEditorConstants.DIALOG_TITLE, ASSIGN_TIMELINE_ASSET_MSG, "OK");
86+
return;
87+
}
88+
89+
TimelineAsset timelineAsset = director.playableAsset as TimelineAsset;
90+
RenderCachePlayableAssetPopup.Show(fieldRect, new Vector2(fieldRect.width, 80), timelineAsset, OnClipSelected);
91+
}
92+
93+
void OnClipSelected(TimelineClip clip) {
94+
m_lineCacheUpdater.SetRenderCachePlayableAsset(null == clip ? null : clip.asset as RenderCachePlayableAsset);
95+
}
96+
97+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
98+
99+
void UpdatePencilLineCache(PlayableDirector director, RenderCachePlayableAsset renderCachePlayableAsset) {
100+
Assert.IsNotNull(director);
101+
Assert.IsNotNull(renderCachePlayableAsset);
102+
103+
TimelineAsset timelineAsset = renderCachePlayableAsset.GetBoundClipData()?.GetOwner().GetParentTrack().timelineAsset;
104+
if (null == timelineAsset || timelineAsset != director.playableAsset) {
105+
EditorUtility.DisplayDialog(SISEditorConstants.DIALOG_TITLE, "The RenderCachePlayableAsset does not exist in the specified Director object", "OK");
106+
return;
107+
}
108+
109+
EditorCoroutineUtility.StartCoroutineOwnerless(UpdatePencilLineCacheCoroutine(director, renderCachePlayableAsset));
110+
}
111+
112+
private IEnumerator UpdatePencilLineCacheCoroutine(PlayableDirector director, RenderCachePlayableAsset renderCachePlayableAsset) {
113+
TimelineEditorUtility.SelectDirectorInTimelineWindow(director); //Need to show in TimelineWindow
114+
yield return null;
115+
116+
if (TimelineEditor.inspectedAsset != director.playableAsset) {
117+
EditorUtility.DisplayDialog(SISEditorConstants.DIALOG_TITLE, "Can't show the specified Director in the Timeline Window. Please unlock it if it's locked.", "OK");
118+
yield break;
119+
}
120+
121+
if (m_lineCacheUpdater.IsUpdateSeparately()) {
122+
yield return UpdateSeparatePencilLineCacheCoroutine(director, renderCachePlayableAsset);
123+
}
124+
else {
125+
yield return RenderCachePlayableAssetInspector.UpdateRenderCacheCoroutine(director, renderCachePlayableAsset);
126+
}
127+
}
128+
129+
130+
private IEnumerator UpdateSeparatePencilLineCacheCoroutine(PlayableDirector director,
131+
RenderCachePlayableAsset playableAsset) {
132+
Dictionary<LineNode, bool> origLineNodeEnabledDic = new Dictionary<LineNode, bool>();
133+
string origFolder = playableAsset.GetFolder();
134+
135+
136+
foreach (PencilLineCacheInfo info in m_lineCacheUpdater.EnumerateSeparateCacheInfo()) {
137+
origLineNodeEnabledDic[info.lineNode] = info.lineNode.enabled;
138+
info.lineNode.enabled = false;
139+
}
140+
141+
//disable all lineNodes
142+
foreach (PencilLineCacheInfo info in m_lineCacheUpdater.EnumerateSeparateCacheInfo()) {
143+
info.lineNode.enabled = false;
144+
}
145+
146+
147+
foreach (PencilLineCacheInfo info in m_lineCacheUpdater.EnumerateSeparateCacheInfo()) {
148+
info.lineNode.enabled = true;
149+
playableAsset.SetFolder(info.cacheFolder);
150+
IEnumerator it = RenderCachePlayableAssetInspector.UpdateRenderCacheCoroutine(director, playableAsset);
151+
while (it.MoveNext()) {
152+
yield return null;
153+
}
154+
155+
info.lineNode.enabled = false;
156+
yield return null;
157+
}
158+
159+
//return original values
160+
foreach (PencilLineCacheInfo info in m_lineCacheUpdater.EnumerateSeparateCacheInfo()) {
161+
info.lineNode.enabled = origLineNodeEnabledDic[info.lineNode];
162+
}
163+
164+
playableAsset.SetFolder(origFolder);
165+
}
166+
167+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
168+
169+
private PencilLineCacheUpdater m_lineCacheUpdater = null;
170+
171+
private const string ASSIGN_TIMELINE_ASSET_MSG = "Please assign a TimelineAsset to the PlayableDirector.";
172+
}
173+
174+
175+
#endif //AT_USE_PENCILLINE

Editor/Scripts/Features/RenderCache/PencilLineCacheUpdaterInspector.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Unity.FilmInternalUtilities; //Required when using Timeline 1.4.x or below
4+
using Unity.StreamingImageSequence;
5+
using UnityEngine;
6+
using UnityEditor;
7+
using UnityEngine.Assertions;
8+
using UnityEngine.Timeline;
9+
using UnityEngine.UIElements;
10+
11+
internal class RenderCachePlayableAssetPopup : EditorWindow {
12+
public static void Show(Rect popupRect, Vector2 size, TimelineAsset timelineAsset, Action<TimelineClip> onClipSelected) {
13+
RenderCachePlayableAssetPopup popup = CreateInstance<RenderCachePlayableAssetPopup>();
14+
popup.Init(timelineAsset, onClipSelected);
15+
popup.ShowAsDropDown(GUIUtility.GUIToScreenRect(popupRect), size);
16+
}
17+
18+
private void Init(TimelineAsset timelineAsset, Action<TimelineClip> onClipSelected) {
19+
Assert.IsNotNull(timelineAsset);
20+
m_onClipSelected = onClipSelected;
21+
22+
m_trackClips.Clear();
23+
m_trackClips.Add(null); //for "none option"
24+
25+
foreach (TrackAsset t in timelineAsset.GetOutputTracks()) {
26+
RenderCacheTrack rcTrack = t as RenderCacheTrack;
27+
if (null == rcTrack)
28+
continue;
29+
30+
foreach (TimelineClip clip in rcTrack.GetClips()) {
31+
m_trackClips.Add(clip);
32+
}
33+
}
34+
}
35+
36+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
37+
private void CreateGUI() {
38+
ListView list = new ListView(m_trackClips, itemHeight: 21, MakeListItem, BindListItem);
39+
IStyle style = list.style;
40+
style.flexGrow = 1;
41+
style.borderLeftColor = style.borderRightColor = style.borderTopColor = style.borderBottomColor = Color.black;
42+
style.borderLeftWidth = style.borderRightWidth = style.borderTopWidth = style.borderBottomWidth = 1;
43+
rootVisualElement.Add(list);
44+
}
45+
46+
private VisualElement MakeListItem() {
47+
return new Label();
48+
}
49+
50+
private void BindListItem(VisualElement element, int index) {
51+
TimelineClip clip = m_trackClips[index];
52+
53+
Label label = element as Label;
54+
Assert.IsNotNull(label);
55+
if (null == clip) {
56+
label.text = "None";
57+
} else {
58+
TrackAsset track = clip.GetParentTrack();
59+
label.text = $"{track.name}-{clip.displayName}";
60+
}
61+
62+
63+
element.userData = clip;
64+
element.RegisterCallback<MouseDownEvent>(OnMouseDown);
65+
}
66+
67+
private void OnMouseDown(MouseDownEvent evt) {
68+
TimelineClip clip = ((VisualElement)evt.currentTarget).userData as TimelineClip;
69+
m_onClipSelected?.Invoke(clip);
70+
Close();
71+
}
72+
73+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
74+
75+
private readonly List<TimelineClip> m_trackClips = new List<TimelineClip>();
76+
private Action<TimelineClip> m_onClipSelected = null;
77+
}

Editor/Scripts/Features/RenderCache/RenderCachePlayableAssetPopup.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/Scripts/SISEditorConstants.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ internal static class SISEditorConstants {
1616
internal const string SHORTCUT_TOGGLE_FRAME_MARKER = "StreamingImageSequence/Toggle Frame Marker";
1717
internal const string SHORTCUT_LOCK_AND_EDIT_FRAME = "StreamingImageSequence/Lock and Edit Frame";
1818
internal const string SHORTCUT_UPDATE_RENDER_CACHE = "StreamingImageSequence/Update Render Cache";
19-
20-
21-
19+
20+
internal const string DIALOG_TITLE = "Streaming Image Sequence";
2221

2322
}
2423

Editor/Unity.StreamingImageSequence.Editor.asmdef

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "Unity.StreamingImageSequence.Editor",
3+
"rootNamespace": "",
34
"references": [
45
"GUID:83bf442171810a04381b4d745b2fdb72",
56
"GUID:f06555f75b070af458a003d92f9efb00",
@@ -37,6 +38,11 @@
3738
"name": "com.unity.render-pipelines.universal",
3839
"expression": "",
3940
"define": "AT_USE_URP"
41+
},
42+
{
43+
"name": "jp.co.psoft.pencil_4.line.unity",
44+
"expression": "",
45+
"define": "AT_USE_PENCILLINE"
4046
}
4147
],
4248
"noEngineReferences": false
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#if AT_USE_PENCILLINE
2+
3+
using System;
4+
using Pencil_4;
5+
6+
[Serializable]
7+
internal struct PencilLineCacheInfo {
8+
public LineNode lineNode;
9+
public string cacheFolder;
10+
}
11+
12+
#endif //AT_USE_PENCILLINE

Runtime/Scripts/Features/RenderCache/Components/PencilLineCacheInfo.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#if AT_USE_PENCILLINE
2+
3+
using System.Collections.Generic;
4+
using Unity.StreamingImageSequence;
5+
using UnityEngine;
6+
using UnityEngine.Playables;
7+
8+
public class PencilLineCacheUpdater : MonoBehaviour {
9+
internal bool IsUpdateSeparately() => m_updateSeparately;
10+
11+
internal PlayableDirector GetPlayableDirector() => m_playableDirector;
12+
13+
internal void SetPlayableDirector(PlayableDirector director) {
14+
m_playableDirector = director;
15+
}
16+
17+
internal RenderCachePlayableAsset GetRenderCachePlayableAsset() => m_renderCachePlayableAsset;
18+
19+
internal void SetRenderCachePlayableAsset(RenderCachePlayableAsset playableAsset) {
20+
m_renderCachePlayableAsset = playableAsset;
21+
}
22+
23+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
24+
25+
26+
internal IEnumerable<PencilLineCacheInfo> EnumerateSeparateCacheInfo() {
27+
foreach (PencilLineCacheInfo cacheInfo in m_separateCacheInfoList)
28+
yield return cacheInfo;
29+
}
30+
31+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
32+
33+
[SerializeField] [HideInInspector] private PlayableDirector m_playableDirector;
34+
35+
[SerializeField] [HideInInspector] private RenderCachePlayableAsset m_renderCachePlayableAsset;
36+
37+
[SerializeField] private bool m_updateSeparately;
38+
[SerializeField] private List<PencilLineCacheInfo> m_separateCacheInfoList;
39+
}
40+
41+
#endif //AT_USE_PENCILLINE

Runtime/Scripts/Features/RenderCache/Components/PencilLineCacheUpdater.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)