// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using FlaxEditor.Content; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Timeline.Tracks; using FlaxEditor.Utilities; using FlaxEditor.Viewport.Previews; using FlaxEngine; namespace FlaxEditor.GUI.Timeline { /// /// The timeline editor for particle system asset. /// /// public sealed class ParticleSystemTimeline : Timeline { private sealed class Proxy : ProxyBase { /// public Proxy(ParticleSystemTimeline timeline) : base(timeline) { } } private ParticleSystemPreview _preview; internal List Emitters; /// /// Initializes a new instance of the class. /// /// The particle system preview. /// The undo/redo to use for the history actions recording. Optional, can be null to disable undo support. public ParticleSystemTimeline(ParticleSystemPreview preview, FlaxEditor.Undo undo = null) : base(PlaybackButtons.Play | PlaybackButtons.Stop, undo) { PlaybackState = PlaybackStates.Playing; PropertiesEditObject = new Proxy(this); _preview = preview; // Setup track types TrackArchetypes.Add(ParticleEmitterTrack.GetArchetype()); TrackArchetypes.Add(FolderTrack.GetArchetype()); } /// /// Called when emitters parameters overrides collection gets edited. /// public void OnEmittersParametersOverridesEdited() { _isModified = true; } /// protected override void SetupDragDrop() { base.SetupDragDrop(); DragHandlers.Add(new DragHandler(new DragAssets(IsValidAsset), OnDragAsset)); } private static bool IsValidAsset(AssetItem assetItem) { if (assetItem is BinaryAssetItem binaryAssetItem) { if (typeof(ParticleEmitter).IsAssignableFrom(binaryAssetItem.Type)) { var emitter = FlaxEngine.Content.LoadAsync(binaryAssetItem.ID); if (emitter) return true; } } return false; } private static void OnDragAsset(Timeline timeline, DragHelper drag) { foreach (var assetItem in ((DragAssets)drag).Objects) { if (assetItem is BinaryAssetItem binaryAssetItem) { if (typeof(ParticleEmitter).IsAssignableFrom(binaryAssetItem.Type)) { var emitter = FlaxEngine.Content.LoadAsync(binaryAssetItem.ID); if (emitter) { var track = (ParticleEmitterTrack)timeline.NewTrack(ParticleEmitterTrack.GetArchetype()); track.Asset = emitter; track.TrackMedia.DurationFrames = timeline.DurationFrames; track.Rename(assetItem.ShortName); timeline.AddTrack(track); } } } } } /// public override void Update(float deltaTime) { CurrentFrame = (int)(_preview.PreviewActor.Time * _preview.System.FramesPerSecond); base.Update(deltaTime); } /// public override void OnPlay() { _preview.PlaySimulation = true; base.OnPlay(); } /// public override void OnPause() { _preview.PlaySimulation = false; _preview.PreviewActor.LastUpdateTime = -1.0f; base.OnPause(); } /// public override void OnStop() { _preview.PlaySimulation = false; _preview.PreviewActor.ResetSimulation(); _preview.PreviewActor.UpdateSimulation(); base.OnStop(); } /// /// Loads the timeline from the specified asset. /// /// The asset. public void Load(ParticleSystem asset) { Profiler.BeginEvent("Asset.LoadTimeline"); var data = asset.LoadTimeline(); Profiler.EndEvent(); Profiler.BeginEvent("Timeline.Load"); Load(data); Profiler.EndEvent(); } /// /// Saves the timeline data to the asset. /// /// The asset. public void Save(ParticleSystem asset) { var data = Save(); asset.SaveTimeline(data); asset.Reload(); } /// protected override void LoadTimelineData(int version, BinaryReader stream) { base.LoadTimelineData(version, stream); // Load emitters int emittersCount = stream.ReadInt32(); } /// protected override void SaveTimelineData(BinaryWriter stream) { base.SaveTimelineData(stream); // Save emitters Emitters = Tracks.Where(track => track is ParticleEmitterTrack).Cast().ToList(); int emittersCount = Emitters.Count; stream.Write(emittersCount); } /// protected override void LoadTimelineCustomData(int version, BinaryReader stream) { base.LoadTimelineCustomData(version, stream); Emitters = Tracks.Where(track => track is ParticleEmitterTrack).Cast().ToList(); // Load parameters overrides int overridesCount = stream.BaseStream.Position != stream.BaseStream.Length ? stream.ReadInt32() : 0; if (overridesCount != 0) { for (int i = 0; i < overridesCount; i++) { var idx = stream.ReadInt32(); var id = stream.ReadGuid(); object value = null; if (version == 2) stream.ReadCommonValue(ref value); else value = stream.ReadVariant(); Emitters[idx].ParametersOverrides.Add(id, value); } } } /// protected override void SaveTimelineCustomData(BinaryWriter stream) { base.SaveTimelineCustomData(stream); // Save parameters overrides var emittersParametersOverrides = new Dictionary, object>(); for (var i = 0; i < Emitters.Count; i++) { var emitterTrack = Emitters[i]; if (emitterTrack.ParametersOverrides.Count != 0) { foreach (var e in emitterTrack.ParametersOverrides) { emittersParametersOverrides.Add(new KeyValuePair(i, e.Key), e.Value); } } } if (emittersParametersOverrides.Count != 0) { stream.Write(emittersParametersOverrides.Count); foreach (var e in emittersParametersOverrides) { var id = e.Key.Value; stream.Write(e.Key.Key); stream.WriteGuid(ref id); stream.WriteVariant(e.Value); } } } /// public override void Load(byte[] data) { base.Load(data); OnPlay(); } /// public override void OnDestroy() { _preview = null; if (Emitters != null) { Emitters.Clear(); Emitters = null; } base.OnDestroy(); } } }