JD2022-TU1/main/tools/modules/JD.SceneExplorer/ViewModels/SceneTree.cs

699 lines
24 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using JD.SceneExplorer.Utils;
using JD.WPF.Models;
namespace JD.SceneExplorer.ViewModels
{
public class SceneTree : ObjectWithRef
{
public const string SceneFileExtension = "isc";
private readonly ObservableCollection<SceneTree> m_childScenesInternal = new ObservableCollection<SceneTree>();
private readonly ObservableCollection<Actor> m_actorsInternal = new ObservableCollection<Actor>();
private readonly ObservableCollection<Frise> m_frisesInternal = new ObservableCollection<Frise>();
private readonly ReadOnlyObservableCollection<SceneTree> m_childScenes;
private readonly ReadOnlyObservableCollection<Actor> m_actors;
private readonly ReadOnlyObservableCollection<Frise> m_frises;
private String m_uniqueName;
private String m_path;
private uint m_relatedActorRef;
public SceneTree(uint objectRef, SceneTree parent, System.ComponentModel.PropertyChangedEventHandler propertyChangedHandler)
: base(objectRef, parent, propertyChangedHandler)
{
m_childScenes = new ReadOnlyObservableCollection<SceneTree>(m_childScenesInternal);
m_actors = new ReadOnlyObservableCollection<Actor>(m_actorsInternal);
m_frises = new ReadOnlyObservableCollection<Frise>(m_frisesInternal);
m_relatedActorRef = ObjectWithRef.InvalidObject.ObjectRef;
}
public SceneTree(uint objectRef, SceneTree parent)
: this(objectRef, parent, parent != null ? parent.PropertyChangedHandler : null)
{
}
public override ObjectWithRefType Type { get { return ObjectWithRefType.Scene; } }
protected override void LoadChildren()
{
List<ObjectWithRef> itemsToAdd = new List<ObjectWithRef>();
itemsToAdd.Capacity = m_childScenes.Count + m_actors.Count + m_frises.Count;
foreach (ObjectWithRef itemToAdd in m_childScenes.Union<ObjectWithRef>(m_actors).Union<ObjectWithRef>(m_frises))
{
//if (itemToAdd.IsFiltered)
// continue;
if (Children.FirstOrDefault(child => child == itemToAdd) != null)
continue;
// TODO : Triggers a lot of events. Override ObservableCollection to add an AddRange function?
if (Children.Count == 0)
Children.Add(itemToAdd);
else
Children.Insert(FindInsertPosition(itemToAdd), itemToAdd);
itemToAdd.UpdateChildren();
}
// Use optimized filling if available.
//if (Children is MultiThreadedObservableRangeCollection<ITreeViewItemData>)
//{
// (Children as MultiThreadedObservableRangeCollection<ITreeViewItemData>).AddRange(itemsToAdd);
//}
//else
//{
// foreach (ObjectWithRef itemToAdd in itemsToAdd)
// Children.Add(itemToAdd);
//}
UpdateFlags(false);
}
protected override int GetChildrenCount()
{
return m_childScenes.Count + m_actors.Count + m_frises.Count;
}
public override uint PickableRef
{
get { return ObjectWithRef.InvalidObject.ObjectRef; }
}
public override bool CanHide { get { return false; } }
public override bool Visible
{
get
{
if (!ChildrenLoaded)
return false;
if (Children.Count == 0)
return true;
else
return Children.Cast<ObjectWithRef>().Any(child => child.Visible);
}
set
{
if (Visible != value)
{
if (ChildrenLoaded)
{
foreach (ObjectWithRef child in Children)
child.Visible = value;
}
SceneTree parent = Parent as SceneTree;
while (parent != null)
{
(Parent as SceneTree).RaisePropertyChanged("Visible");
parent = parent.Parent as SceneTree;
}
m_visible = value;
RaisePropertyChanged("Visible");
}
}
}
public string UniqueName
{
get { return m_uniqueName; }
set
{
if (m_uniqueName != value)
{
m_uniqueName = value;
RaisePropertyChanged("UniqueName");
}
}
}
public string Path
{
get { return m_path; }
set
{
if (m_path != value)
{
m_path = value;
FilePath = m_path;
RaisePropertyChanged("Path");
}
}
}
public uint RelatedActorRef
{
get { return m_relatedActorRef; }
set
{
if (m_relatedActorRef != value)
{
m_relatedActorRef = value;
RaisePropertyChanged("RelatedActorRef");
}
}
}
public virtual bool IsLockAvailable { get { return false; } }
/// <summary>
/// Applies filter to elements of the current scene.
/// </summary>
/// <param name="filterData">The filter string. </param>
/// <returns>True if the scene is filtered, false otherwise. </returns>
public override bool SetFilter(object filterData)
{
return SetFilter(filterData, System.Threading.CancellationToken.None);
}
public override bool SetFilter(object filterData, System.Threading.CancellationToken cancellationToken)
{
bool isSceneFiltered = base.SetFilter(filterData, cancellationToken);
TagSelectorData newFilter = (TagSelectorData)filterData;
if (isSceneFiltered || (newFilter != null && !newFilter.IsFiltering))
{
foreach (Actor actor in m_actors)
{
cancellationToken.ThrowIfCancellationRequested();
IsFiltered &= actor.SetFilter(newFilter, cancellationToken);
}
foreach (Frise frise in m_frises)
{
cancellationToken.ThrowIfCancellationRequested();
IsFiltered &= frise.SetFilter(newFilter, cancellationToken);
}
foreach (SceneTree subScene in m_childScenes)
{
cancellationToken.ThrowIfCancellationRequested();
IsFiltered &= subScene.SetFilter(newFilter, cancellationToken);
}
if (newFilter.IsFiltering && !IsFiltered)
IsExpanded = true;
}
else
{
SetFilterAndForceVisibility(newFilter);
IsFiltered = false;
}
return IsFiltered;
}
/// <summary>
/// Applies filter to elements of the current scene. All child elements are visible. This function acts only on expanded state.
/// </summary>
/// <param name="newFilter">The filter string. </param>
/// <returns>True if the scene is filtered, false otherwise. </returns>
private bool SetFilterAndForceVisibility(TagSelectorData newFilter)
{
return SetFilterAndForceVisibility(newFilter, System.Threading.CancellationToken.None);
}
private bool SetFilterAndForceVisibility(TagSelectorData newFilter, System.Threading.CancellationToken cancellationToken)
{
// Force parent expand, but do not expand the current node. It will stay collapsed unless a child matches the filter.
IsExpanded = true;
bool childMatchFilter = false;
foreach (Actor actor in m_actors)
{
cancellationToken.ThrowIfCancellationRequested();
childMatchFilter |= !actor.SetFilter(newFilter, cancellationToken);
actor.IsFiltered = false;
}
foreach (Frise frise in m_frises)
{
cancellationToken.ThrowIfCancellationRequested();
childMatchFilter |= !frise.SetFilter(newFilter, cancellationToken);
frise.IsFiltered = false;
}
foreach (SceneTree subScene in m_childScenes)
{
cancellationToken.ThrowIfCancellationRequested();
bool sceneFiltered = subScene.SetFilterAndForceVisibility(newFilter, cancellationToken);
childMatchFilter |= !sceneFiltered;
subScene.IsFiltered = sceneFiltered;
subScene.IsFiltered = false;
}
if (!childMatchFilter)
{
IsExpanded = false;
}
return !childMatchFilter;
}
public ReadOnlyObservableCollection<SceneTree> ChildScenes { get { return m_childScenes; } }
public ReadOnlyObservableCollection<Actor> Actors { get { return m_actors; } }
public ReadOnlyObservableCollection<Frise> Frises { get { return m_frises; } }
public static SceneTree LoadFromBlob(JD.Remoting.IBlob _blob, SceneTree parent)
{
string uniqueName = _blob.ExtractString();
string path = _blob.ExtractPath();
string friendlyName = _blob.ExtractString();
uint objectRef = _blob.ExtractUInt();
uint relatedActorRef = _blob.ExtractUInt();
bool isWorldActive = _blob.ExtractBool();
uint tagsCount = _blob.ExtractUInt();
List<string> tags = new List<string>();
for (int i = 0; i < tagsCount; i++)
tags.Add(_blob.ExtractString());
SceneTree newSceneTree = new SceneTree(objectRef, parent);
newSceneTree.LoadFromBlobInternal(_blob, uniqueName, path, friendlyName, relatedActorRef, isWorldActive, tags);
return newSceneTree;
}
protected void LoadFromBlobInternal(JD.Remoting.IBlob _blob, string uniqueName, string path, string friendlyName, uint relatedActorRef, bool isWorldActive, List<string> tags)
{
UniqueName = uniqueName;
Path = path;
FriendlyName = friendlyName;
RelatedActorRef = relatedActorRef;
Active = isWorldActive;
foreach (string tag in tags)
Tags.Add(tag);
AddSpecialTags();
//extract actors
uint actorCount = _blob.ExtractUInt();
for (uint actorIndex = 0; actorIndex < actorCount; actorIndex++)
{
var newActor = Actor.LoadFromBlob(_blob, this);
InsertActor(newActor);
}
//extract frises
uint friseCount = _blob.ExtractUInt();
for (uint friseIndex = 0; friseIndex < friseCount; friseIndex++)
{
var newFrise = Frise.LoadFromBlob(_blob, this);
InsertFrise(newFrise);
}
ExtractSubScenes(_blob);
IconSource = EditorIconsManager.Instance.GetIconLocationFromTags(Tags);
NeedUpdate = true;
}
protected void AddSpecialTags()
{
if (Path.EndsWith(".isc") && (Tags.FirstOrDefault(tag => tag == "isc") == null))
Tags.Add("isc");
if (FriendlyName.EndsWith("_LD"))
Tags.Add("isc_ld_square");
else if (FriendlyName.EndsWith("_GRAPH"))
Tags.Add("isc_graph_square");
else if (FriendlyName.EndsWith("_CINE"))
Tags.Add("isc_cine_square");
else if (FriendlyName.EndsWith("_SOUND"))
Tags.Add("isc_sound_square");
else if (FriendlyName.EndsWith("_FX"))
Tags.Add("isc_fx_square");
}
protected virtual void ExtractSubScenes(JD.Remoting.IBlob _blob)
{
//extract subscenes
uint childCount = _blob.ExtractUInt();
for (uint childIndex = 0; childIndex < childCount; childIndex++)
{
var newChildSceneTree = SubSceneTree.LoadFromBlob(_blob, this);
InsertSubScene(newChildSceneTree);
}
}
/// <summary>
/// find a scene by sceneRef among all hierarchy
/// </summary>
/// <param name="_sceneRef"></param>
/// <returns></returns>
public SceneTree FindScene(uint _sceneRef)
{
if (_sceneRef == m_objectRef)
return this;
foreach (SceneTree subscene in m_childScenes)
{
SceneTree foundScene = subscene.FindScene(_sceneRef);
if (foundScene != null)
{
return foundScene;
}
}
return null;
}
public SceneTree FindScene(string friendlyName, bool recursive = true)
{
foreach (SceneTree subScene in m_childScenes)
{
if (subScene.FriendlyName == friendlyName)
return subScene;
if (recursive)
{
SceneTree foundScene = subScene.FindScene(friendlyName);
if (foundScene != null)
return foundScene;
}
}
return null;
}
public SceneTree FindSceneActor(uint _sceneActorRef)
{
if (_sceneActorRef == m_relatedActorRef)
return this;
foreach (SceneTree subscene in m_childScenes)
{
SceneTree foundScene = subscene.FindSceneActor(_sceneActorRef);
if (foundScene != null)
{
return foundScene;
}
}
return null;
}
public ObjectWithRef FindObject(uint _objRef)
{
// FIXME : Ugly.
return FindObject(new ObjectWithRef(_objRef, null, null));
}
public ObjectWithRef FindObject(ObjectWithRef _objRef)
{
if (Equals(_objRef))
return _objRef;
ObjectWithRef objRef;
objRef = Actors.FirstOrDefault(actor => actor.Equals(_objRef));
if (objRef != null)
return objRef;
objRef = Frises.FirstOrDefault(frise => frise.Equals(_objRef));
if (objRef != null)
return objRef;
foreach (SceneTree subScene in ChildScenes)
{
if (subScene.RelatedActorRef == _objRef.ObjectRef)
return subScene;
objRef = subScene.FindObject(_objRef);
if (objRef != null)
return objRef;
}
return null;
}
public ObjectWithRef FindObject(string friendlyName, bool recursive = true)
{
foreach (Actor actor in m_actors)
{
if (actor.FriendlyName == friendlyName)
return actor;
}
foreach (Frise frise in m_frises)
{
if (frise.FriendlyName == friendlyName)
return frise;
}
SceneTree subScene = FindScene(friendlyName, recursive);
if (subScene != null)
return subScene;
return null;
}
//private int FindInsertPosition(List<ObjectWithRef> targetList, ObjectWithRef objectRef)
//{
// return FindInsertPosition(targetList, objectRef, 0, targetList.Count);
//}
//private int FindInsertPosition(List<ObjectWithRef> targetList, ObjectWithRef objectRef, int begin, int end)
//{
// if (begin == end)
// return begin;
// int current = (begin + end) / 2;
// ObjectWithRef curObjectRef = targetList[current] as ObjectWithRef;
// int compareResult = curObjectRef.CompareTo(objectRef);
// if (compareResult == 0)
// return current;
// else if (compareResult < 0)
// return FindInsertPosition(targetList, objectRef, current + 1, end);
// else
// return FindInsertPosition(targetList, objectRef, begin, current);
//}
private int FindInsertPosition(ObjectWithRef objectRef)
{
return FindInsertPosition(objectRef, 0, Children.Count);
}
private int FindInsertPosition(ObjectWithRef objectRef, int begin, int end)
{
if (begin == end)
return begin;
int current = (begin + end) / 2;
ObjectWithRef curObjectRef = Children[current] as ObjectWithRef;
int compareResult = curObjectRef.CompareTo(objectRef);
if (compareResult == 0)
return current;
else if (compareResult < 0)
return FindInsertPosition(objectRef, current + 1, end);
else
return FindInsertPosition(objectRef, begin, current);
}
public void InsertActor(Actor newActor)
{
m_actorsInternal.Add(newActor);
NeedUpdate = true;
}
public void InsertFrise(Frise newFrise)
{
m_frisesInternal.Add(newFrise);
NeedUpdate = true;
}
public void InsertSubScene(SceneTree newSubScene)
{
m_childScenesInternal.Add(newSubScene);
Actor actor = m_actorsInternal.FirstOrDefault(act => act.ObjectRef == newSubScene.m_relatedActorRef);
if (actor != null)
m_actorsInternal.Remove(actor);
NeedUpdate = true;
}
public void RemoveActor(Actor actor)
{
Actor objectRef = m_actorsInternal.FirstOrDefault(curActor => curActor == actor);
if (objectRef != null)
{
m_actorsInternal.Remove(objectRef);
NeedUpdate = true;
}
}
public void RemoveFrise(Frise frise)
{
Frise objectRef = m_frisesInternal.FirstOrDefault(curFrise => curFrise == frise);
if (objectRef != null)
{
m_frisesInternal.Remove(objectRef);
NeedUpdate = true;
}
}
public void ClearScene()
{
m_actorsInternal.Clear();
m_frisesInternal.Clear();
m_childScenesInternal.Clear();
UnregisterPropertyChanged();
NeedUpdate = true;
}
public void ClearSelection()
{
IsSelected = false;
foreach (Actor actor in Actors)
actor.IsSelected = false;
foreach (Frise frise in Frises)
frise.IsSelected = false;
foreach (SceneTree subSceneTree in ChildScenes)
{
subSceneTree.IsSelected = false;
subSceneTree.ClearSelection();
}
}
public void GetSelection(List<ObjectWithRef> selectedItems, bool includeScenes)
{
selectedItems.AddRange(Actors.Union<ObjectWithRef>(Frises).Where(objRef => objRef.IsSelected));
if (includeScenes)
selectedItems.AddRange(ChildScenes.Where(objRef => objRef.IsSelected));
foreach (SceneTree subScene in ChildScenes)
subScene.GetSelection(selectedItems, includeScenes);
}
}
public class RootSceneTree : SceneTree, IEquatable<RootSceneTree>
{
public RootSceneTree(System.ComponentModel.PropertyChangedEventHandler propertyChangedHandler)
: base(0, null, propertyChangedHandler)
{
ShouldShowRoot = false;
}
public override ObjectWithRefType Type { get { return ObjectWithRefType.RootScene; } }
public void LoadFromBlob(JD.Remoting.IBlob _blob)
{
string uniqueName = _blob.ExtractString();
string path = _blob.ExtractPath();
string friendlyName = _blob.ExtractString();
uint objectRef = _blob.ExtractUInt();
uint relatedActorRef = _blob.ExtractUInt();
bool isWorldActive = _blob.ExtractBool();
uint tagsCount = _blob.ExtractUInt();
List<string> tags = new List<string>();
for (int i = 0; i < tagsCount; i++)
tags.Add(_blob.ExtractString());
if (ObjectRef != objectRef)
throw new InvalidOperationException("Object references not matching. ");
LoadFromBlobInternal(_blob, uniqueName, path, friendlyName, relatedActorRef, isWorldActive, tags);
NeedUpdate = true;
}
protected override void ExtractSubScenes(JD.Remoting.IBlob _blob)
{
//extract subscenes
uint childCount = _blob.ExtractUInt();
for (uint childIndex = 0; childIndex < childCount; childIndex++)
{
var newChildSceneTree = SceneTree.LoadFromBlob(_blob, this);
InsertSubScene(newChildSceneTree);
}
}
public override uint PickableRef
{
get { return ObjectWithRef.InvalidObject.ObjectRef; }
}
/// <inheritdoc />
public bool Equals(RootSceneTree other)
{
return this.Id == (other?.Id).GetValueOrDefault(Guid.Empty);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((RootSceneTree) obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
public Guid Id { get; } = Guid.NewGuid();
}
public class SubSceneTree : SceneTree
{
public SubSceneTree(uint objectRef, SceneTree parent)
: base(objectRef, parent)
{
}
public override ObjectWithRefType Type { get { return ObjectWithRefType.SubScene; } }
// TODO : Find a clean way to remove this duplicate code ...
public new static SubSceneTree LoadFromBlob(JD.Remoting.IBlob _blob, SceneTree parent)
{
string uniqueName = _blob.ExtractString();
string path = _blob.ExtractPath();
string friendlyName = _blob.ExtractString();
uint objectRef = _blob.ExtractUInt();
uint relatedActorRef = _blob.ExtractUInt();
bool isWorldActive = _blob.ExtractBool();
uint tagsCount = _blob.ExtractUInt();
List<string> tags = new List<string>();
for (int i = 0; i < tagsCount; i++)
tags.Add(_blob.ExtractString());
SubSceneTree newSceneTree = new SubSceneTree(objectRef, parent);
newSceneTree.LoadFromBlobInternal(_blob, uniqueName, path, friendlyName, relatedActorRef, isWorldActive, tags);
return newSceneTree;
}
public override bool CanHide { get { return true; } }
public override bool Visible
{
get
{
if (!ChildrenLoaded)
return false;
if (Children.Count == 0)
return m_visible;
else
return Children.Cast<ObjectWithRef>().Any(child => child.Visible);
}
set
{
base.Visible = value;
}
}
public override uint PickableRef
{
get
{
if (RelatedActorRef != ObjectWithRef.InvalidObject.ObjectRef)
return RelatedActorRef;
else
return base.PickableRef;
}
}
public override bool IsLockAvailable
{
get
{
if (Parent is SceneTree && !(Parent is SubSceneTree))
return true;
return base.IsLockAvailable;
}
}
}
}