You're breathtaking!
This commit is contained in:
258
Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
Normal file
258
Source/Editor/Viewport/Previews/AnimatedModelPreview.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Animated model asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class AnimatedModelPreview : AssetPreview
|
||||
{
|
||||
private AnimatedModel _previewModel;
|
||||
private StaticModel _previewNodesActor;
|
||||
private Model _previewNodesModel;
|
||||
private int _previewNodesCounter;
|
||||
private List<Vector3> _previewNodesVB;
|
||||
private List<int> _previewNodesIB;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the skinned model asset to preview.
|
||||
/// </summary>
|
||||
public SkinnedModel SkinnedModel
|
||||
{
|
||||
get => _previewModel.SkinnedModel;
|
||||
set => _previewModel.SkinnedModel = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the skinned model actor used to preview selected asset.
|
||||
/// </summary>
|
||||
public AnimatedModel PreviewActor => _previewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether play the animation in editor.
|
||||
/// </summary>
|
||||
public bool PlayAnimation { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show animated model skeleton nodes debug view.
|
||||
/// </summary>
|
||||
public bool ShowNodes { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether scale the model to the normalized bounds.
|
||||
/// </summary>
|
||||
public bool ScaleToFit { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom mask for the skeleton nodes. Nodes missing from this list will be skipped during rendering. Works only if <see cref="ShowNodes"/> is set to true and the array matches the attached <see cref="SkinnedModel"/> nodes hierarchy.
|
||||
/// </summary>
|
||||
public bool[] NodesMask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnimatedModelPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public AnimatedModelPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
Task.Begin += OnBegin;
|
||||
|
||||
// Setup preview scene
|
||||
_previewModel = new AnimatedModel();
|
||||
_previewModel.UseTimeScale = false;
|
||||
_previewModel.UpdateWhenOffscreen = true;
|
||||
//_previewModel.BoundsScale = 1000.0f;
|
||||
_previewModel.UpdateMode = AnimatedModel.AnimationUpdateMode.Manual;
|
||||
_previewNodesModel = FlaxEngine.Content.CreateVirtualAsset<Model>();
|
||||
_previewNodesModel.SetupLODs(new[] { 1 });
|
||||
_previewNodesActor = new StaticModel();
|
||||
_previewNodesActor.Model = _previewNodesModel;
|
||||
_previewNodesActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.WiresDebugMaterial));
|
||||
|
||||
// Link actors for rendering
|
||||
Task.AddCustomActor(_previewModel);
|
||||
Task.AddCustomActor(_previewNodesActor);
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
// Preview LOD
|
||||
{
|
||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
||||
var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f);
|
||||
previewLODValue.Parent = previewLOD;
|
||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
if (!ScaleToFit)
|
||||
{
|
||||
_previewModel.Scale = Vector3.One;
|
||||
_previewModel.Position = Vector3.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update preview model scale to fit the preview
|
||||
var skinnedModel = SkinnedModel;
|
||||
if (skinnedModel && skinnedModel.IsLoaded)
|
||||
{
|
||||
float targetSize = 50.0f;
|
||||
BoundingBox box = skinnedModel.GetBox();
|
||||
float maxSize = Mathf.Max(0.001f, box.Size.MaxValue);
|
||||
float scale = targetSize / maxSize;
|
||||
_previewModel.Scale = new Vector3(scale);
|
||||
_previewModel.Position = box.Center * (-0.5f * scale) + new Vector3(0, -10, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
// Manually update animation
|
||||
if (PlayAnimation)
|
||||
{
|
||||
_previewModel.UpdateAnimation();
|
||||
}
|
||||
|
||||
// Update the nodes debug (once every few frames)
|
||||
_previewNodesActor.Transform = _previewModel.Transform;
|
||||
var updateNodesCount = PlayAnimation || _previewNodesVB?.Count == 0 ? 1 : 10;
|
||||
_previewNodesActor.IsActive = ShowNodes;
|
||||
if (_previewNodesCounter++ % updateNodesCount == 0 && ShowNodes)
|
||||
{
|
||||
_previewModel.GetCurrentPose(out var pose);
|
||||
var nodes = _previewModel.SkinnedModel?.Nodes;
|
||||
if (pose == null || pose.Length == 0 || nodes == null)
|
||||
{
|
||||
_previewNodesActor.IsActive = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_previewNodesVB == null)
|
||||
_previewNodesVB = new List<Vector3>(1024 * 2);
|
||||
else
|
||||
_previewNodesVB.Clear();
|
||||
if (_previewNodesIB == null)
|
||||
_previewNodesIB = new List<int>(1024 * 3);
|
||||
else
|
||||
_previewNodesIB.Clear();
|
||||
|
||||
// Draw bounding box at the node locations
|
||||
var nodesMask = NodesMask != null && NodesMask.Length == nodes.Length ? NodesMask : null;
|
||||
var localBox = new OrientedBoundingBox(new Vector3(-1.0f), new Vector3(1.0f));
|
||||
for (int nodeIndex = 0; nodeIndex < pose.Length; nodeIndex++)
|
||||
{
|
||||
if (nodesMask != null && !nodesMask[nodeIndex])
|
||||
continue;
|
||||
|
||||
var transform = pose[nodeIndex];
|
||||
transform.Decompose(out var scale, out Matrix _, out _);
|
||||
transform = Matrix.Invert(Matrix.Scaling(scale)) * transform;
|
||||
|
||||
// Some inlined code to improve performance
|
||||
var box = localBox * transform;
|
||||
//
|
||||
var iStart = _previewNodesVB.Count;
|
||||
box.GetCorners(_previewNodesVB);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 0);
|
||||
_previewNodesIB.Add(iStart + 1);
|
||||
_previewNodesIB.Add(iStart + 0);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 0);
|
||||
_previewNodesIB.Add(iStart + 4);
|
||||
_previewNodesIB.Add(iStart + 0);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 1);
|
||||
_previewNodesIB.Add(iStart + 2);
|
||||
_previewNodesIB.Add(iStart + 1);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 1);
|
||||
_previewNodesIB.Add(iStart + 5);
|
||||
_previewNodesIB.Add(iStart + 1);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 2);
|
||||
_previewNodesIB.Add(iStart + 3);
|
||||
_previewNodesIB.Add(iStart + 2);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 2);
|
||||
_previewNodesIB.Add(iStart + 6);
|
||||
_previewNodesIB.Add(iStart + 2);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 3);
|
||||
_previewNodesIB.Add(iStart + 7);
|
||||
_previewNodesIB.Add(iStart + 3);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 4);
|
||||
_previewNodesIB.Add(iStart + 5);
|
||||
_previewNodesIB.Add(iStart + 4);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 4);
|
||||
_previewNodesIB.Add(iStart + 7);
|
||||
_previewNodesIB.Add(iStart + 4);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 5);
|
||||
_previewNodesIB.Add(iStart + 6);
|
||||
_previewNodesIB.Add(iStart + 5);
|
||||
//
|
||||
_previewNodesIB.Add(iStart + 6);
|
||||
_previewNodesIB.Add(iStart + 7);
|
||||
_previewNodesIB.Add(iStart + 6);
|
||||
//
|
||||
}
|
||||
|
||||
// Nodes connections
|
||||
for (int nodeIndex = 0; nodeIndex < nodes.Length; nodeIndex++)
|
||||
{
|
||||
int parentIndex = nodes[nodeIndex].ParentIndex;
|
||||
|
||||
if (parentIndex != -1)
|
||||
{
|
||||
if (nodesMask != null && (!nodesMask[nodeIndex] || !nodesMask[parentIndex]))
|
||||
continue;
|
||||
|
||||
var parentPos = pose[parentIndex].TranslationVector;
|
||||
var bonePos = pose[nodeIndex].TranslationVector;
|
||||
|
||||
var iStart = _previewNodesVB.Count;
|
||||
_previewNodesVB.Add(parentPos);
|
||||
_previewNodesVB.Add(bonePos);
|
||||
_previewNodesIB.Add(iStart + 0);
|
||||
_previewNodesIB.Add(iStart + 1);
|
||||
_previewNodesIB.Add(iStart + 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (_previewNodesIB.Count > 0)
|
||||
_previewNodesModel.LODs[0].Meshes[0].UpdateMesh(_previewNodesVB, _previewNodesIB);
|
||||
else
|
||||
_previewNodesActor.IsActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Ensure to cleanup created actor objects
|
||||
_previewNodesActor.Model = null;
|
||||
Object.Destroy(ref _previewModel);
|
||||
Object.Destroy(ref _previewNodesActor);
|
||||
Object.Destroy(ref _previewNodesModel);
|
||||
NodesMask = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
144
Source/Editor/Viewport/Previews/AssetPreview.cs
Normal file
144
Source/Editor/Viewport/Previews/AssetPreview.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic asset preview editor viewport base class.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Viewport.EditorViewport" />
|
||||
public abstract class AssetPreview : EditorViewport
|
||||
{
|
||||
private ContextMenuButton _showDefaultSceneButton;
|
||||
|
||||
/// <summary>
|
||||
/// The preview light. Allows to modify rendering settings.
|
||||
/// </summary>
|
||||
public DirectionalLight PreviewLight;
|
||||
|
||||
/// <summary>
|
||||
/// The env probe. Allows to modify rendering settings.
|
||||
/// </summary>
|
||||
public EnvironmentProbe EnvProbe;
|
||||
|
||||
/// <summary>
|
||||
/// The sky. Allows to modify rendering settings.
|
||||
/// </summary>
|
||||
public Sky Sky;
|
||||
|
||||
/// <summary>
|
||||
/// The sky light. Allows to modify rendering settings.
|
||||
/// </summary>
|
||||
public SkyLight SkyLight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the post fx volume. Allows to modify rendering settings.
|
||||
/// </summary>
|
||||
public PostFxVolume PostFxVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show default scene actors (sky, env probe, skylight, directional light, etc.).
|
||||
/// </summary>
|
||||
public bool ShowDefaultSceneActors
|
||||
{
|
||||
get => PreviewLight.IsActive;
|
||||
set
|
||||
{
|
||||
if (ShowDefaultSceneActors != value)
|
||||
{
|
||||
PreviewLight.IsActive = value;
|
||||
EnvProbe.IsActive = value;
|
||||
Sky.IsActive = value;
|
||||
SkyLight.IsActive = value;
|
||||
|
||||
if (_showDefaultSceneButton != null)
|
||||
_showDefaultSceneButton.Checked = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">If set to <c>true</c> use widgets for viewport, otherwise hide them.</param>
|
||||
/// <param name="orbitRadius">The initial orbit radius.</param>
|
||||
protected AssetPreview(bool useWidgets, float orbitRadius = 50.0f)
|
||||
: this(useWidgets, new ArcBallCamera(Vector3.Zero, orbitRadius))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssetPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">If set to <c>true</c> use widgets for viewport, otherwise hide them.</param>
|
||||
/// <param name="camera">The camera controller.</param>
|
||||
protected AssetPreview(bool useWidgets, ViewportCamera camera)
|
||||
: base(Object.New<SceneRenderTask>(), camera, useWidgets)
|
||||
{
|
||||
Task.ViewFlags = ViewFlags.DefaultAssetPreview;
|
||||
Task.AllowGlobalCustomPostFx = false;
|
||||
|
||||
var orbitRadius = 200.0f;
|
||||
if (camera is ArcBallCamera arcBallCamera)
|
||||
orbitRadius = arcBallCamera.OrbitRadius;
|
||||
camera.SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius);
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
// Show Default Scene
|
||||
_showDefaultSceneButton = ViewWidgetShowMenu.AddButton("Default Scene", () => ShowDefaultSceneActors = !ShowDefaultSceneActors);
|
||||
_showDefaultSceneButton.Checked = true;
|
||||
}
|
||||
|
||||
// Setup preview scene
|
||||
PreviewLight = new DirectionalLight();
|
||||
PreviewLight.Brightness = 8.0f;
|
||||
PreviewLight.ShadowsMode = ShadowsCastingMode.None;
|
||||
PreviewLight.Orientation = Quaternion.Euler(new Vector3(52.1477f, -109.109f, -111.739f));
|
||||
//
|
||||
EnvProbe = new EnvironmentProbe();
|
||||
EnvProbe.AutoUpdate = false;
|
||||
EnvProbe.CustomProbe = FlaxEngine.Content.LoadAsyncInternal<CubeTexture>(EditorAssets.DefaultSkyCubeTexture);
|
||||
//
|
||||
Sky = new Sky();
|
||||
Sky.SunLight = PreviewLight;
|
||||
Sky.SunPower = 9.0f;
|
||||
//
|
||||
SkyLight = new SkyLight();
|
||||
SkyLight.Mode = SkyLight.Modes.CustomTexture;
|
||||
SkyLight.Brightness = 2.1f;
|
||||
SkyLight.CustomTexture = EnvProbe.CustomProbe;
|
||||
//
|
||||
PostFxVolume = new PostFxVolume();
|
||||
PostFxVolume.IsBounded = false;
|
||||
|
||||
// Link actors for rendering
|
||||
Task.ActorsSource = ActorsSources.CustomActors;
|
||||
Task.AddCustomActor(PreviewLight);
|
||||
Task.AddCustomActor(EnvProbe);
|
||||
Task.AddCustomActor(Sky);
|
||||
Task.AddCustomActor(SkyLight);
|
||||
Task.AddCustomActor(PostFxVolume);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasLoadedAssets => base.HasLoadedAssets && Sky.HasContentLoaded && EnvProbe.Probe.IsLoaded && PostFxVolume.HasContentLoaded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Ensure to cleanup created actor objects
|
||||
Object.Destroy(ref PreviewLight);
|
||||
Object.Destroy(ref EnvProbe);
|
||||
Object.Destroy(ref Sky);
|
||||
Object.Destroy(ref SkyLight);
|
||||
Object.Destroy(ref PostFxVolume);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Source/Editor/Viewport/Previews/AudioClipPreview.cs
Normal file
236
Source/Editor/Viewport/Previews/AudioClipPreview.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Audio clip PCM data editor preview.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public class AudioClipPreview : ContainerControl
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio clip drawing modes.
|
||||
/// </summary>
|
||||
public enum DrawModes
|
||||
{
|
||||
/// <summary>
|
||||
/// Fills the whole control area with the full clip duration.
|
||||
/// </summary>
|
||||
Fill,
|
||||
|
||||
/// <summary>
|
||||
/// Draws single audio clip. Uses the view scale parameter.
|
||||
/// </summary>
|
||||
Single,
|
||||
|
||||
/// <summary>
|
||||
/// Draws the looped audio clip. Uses the view scale parameter.
|
||||
/// </summary>
|
||||
Looped,
|
||||
};
|
||||
|
||||
private readonly object _locker = new object();
|
||||
private AudioClip _asset;
|
||||
private float[] _pcmData;
|
||||
private AudioDataInfo _pcmInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the clip to preview.
|
||||
/// </summary>
|
||||
public AudioClip Asset
|
||||
{
|
||||
get => _asset;
|
||||
set
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_asset == value)
|
||||
return;
|
||||
|
||||
_asset = value;
|
||||
_pcmData = null;
|
||||
|
||||
if (_asset)
|
||||
{
|
||||
// Use async task to gather PCM data (engine loads it from the asset)
|
||||
Task.Run(DownloadData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether audio data has been fetched from the asset (done as an async task). It is required to be valid in order to draw the audio buffer preview.
|
||||
/// </summary>
|
||||
public bool HasData
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
return _pcmData != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The draw mode.
|
||||
/// </summary>
|
||||
public DrawModes DrawMode = DrawModes.Fill;
|
||||
|
||||
/// <summary>
|
||||
/// The view scale parameter. Increase it to zoom in the audio. Usage depends on the current <see cref="DrawMode"/>.
|
||||
/// </summary>
|
||||
public float ViewScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the audio PCM data spectrum.
|
||||
/// </summary>
|
||||
public Color Color = Color.White;
|
||||
|
||||
/// <summary>
|
||||
/// The audio units per second (on time axis).
|
||||
/// </summary>
|
||||
public static readonly float UnitsPerSecond = 100.0f;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
var info = _pcmInfo;
|
||||
if (_asset == null || _pcmData == null || info.NumSamples == 0)
|
||||
return;
|
||||
var height = Height;
|
||||
var width = Width;
|
||||
var samplesPerChannel = info.NumSamples / info.NumChannels;
|
||||
var length = (float)samplesPerChannel / info.SampleRate;
|
||||
var color = Color;
|
||||
if (!EnabledInHierarchy)
|
||||
color *= 0.4f;
|
||||
|
||||
// Compute the scaled y-value used to render the channel data
|
||||
float sampleYScale = height / info.NumChannels;
|
||||
|
||||
// Compute amount of samples that are contained in the view
|
||||
float unitsPerSecond = UnitsPerSecond * ViewScale;
|
||||
float clipDefaultWidth = length * unitsPerSecond;
|
||||
float clipsInView = width / clipDefaultWidth;
|
||||
float clipWidth;
|
||||
uint samplesPerIndex;
|
||||
switch (DrawMode)
|
||||
{
|
||||
case DrawModes.Fill:
|
||||
clipsInView = 1.0f;
|
||||
clipWidth = width;
|
||||
samplesPerIndex = (uint)(samplesPerChannel / width);
|
||||
break;
|
||||
case DrawModes.Single:
|
||||
clipsInView = Mathf.Min(clipsInView, 1.0f);
|
||||
clipWidth = clipDefaultWidth;
|
||||
samplesPerIndex = (uint)(info.SampleRate / unitsPerSecond) * info.NumChannels;
|
||||
break;
|
||||
case DrawModes.Looped:
|
||||
clipWidth = width / clipsInView;
|
||||
samplesPerIndex = (uint)(info.SampleRate / unitsPerSecond) * info.NumChannels;
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
const uint maxSamplesPerIndex = 64;
|
||||
uint samplesPerIndexDiff = Math.Max(1, samplesPerIndex / Math.Min(samplesPerIndex, maxSamplesPerIndex));
|
||||
|
||||
// Render each clip separately
|
||||
for (uint clipIndex = 0; clipIndex < Mathf.CeilToInt(clipsInView); clipIndex++)
|
||||
{
|
||||
var clipX = clipWidth * clipIndex;
|
||||
var clipRight = Mathf.Min(width, clipX + clipWidth);
|
||||
|
||||
// Render each channel separately so outer loop is the sound wave channel index
|
||||
for (uint channelIndex = 0; channelIndex < info.NumChannels; channelIndex++)
|
||||
{
|
||||
uint currentSample = channelIndex;
|
||||
float yCenter = Y + ((2 * channelIndex) + 1) * height / (2.0f * info.NumChannels);
|
||||
|
||||
// Loop through each pixel (in x direction)
|
||||
for (float pixelX = clipX; pixelX < clipRight; pixelX++)
|
||||
{
|
||||
// Reset the sample sum and num samples in pixel for each pixel
|
||||
float samplesSum = 0;
|
||||
int samplesInPixel = 0;
|
||||
|
||||
// Loop through all pixels in this x-frame and sum all audio data
|
||||
uint samplesEnd = Math.Min(currentSample + samplesPerIndex, info.NumSamples);
|
||||
for (uint sampleIndex = currentSample; sampleIndex < samplesEnd; sampleIndex += samplesPerIndexDiff)
|
||||
{
|
||||
samplesSum += Mathf.Abs(_pcmData[sampleIndex]);
|
||||
samplesInPixel++;
|
||||
}
|
||||
currentSample = samplesEnd;
|
||||
if (samplesInPixel > 0)
|
||||
{
|
||||
float averageSampleValue = samplesSum / samplesInPixel;
|
||||
float averageSampleValueScaled = averageSampleValue * sampleYScale;
|
||||
|
||||
// Don't try to draw anything if the audio data was too quiet
|
||||
if (averageSampleValueScaled > 0.1f)
|
||||
{
|
||||
// Draw vertical line mirrored around x-axis for channel equal to average sample value height
|
||||
Render2D.DrawLine(new Vector2(pixelX, yCenter - averageSampleValueScaled), new Vector2(pixelX, yCenter + averageSampleValueScaled), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the audio clip raw PCM data. Use it from async thread to prevent blocking,
|
||||
/// </summary>
|
||||
private void DownloadData()
|
||||
{
|
||||
var asset = _asset;
|
||||
|
||||
if (!asset)
|
||||
{
|
||||
Editor.LogWarning("Failed to get audio clip PCM data. Missing asset.");
|
||||
return;
|
||||
}
|
||||
|
||||
float[] data;
|
||||
AudioDataInfo dataInfo;
|
||||
try
|
||||
{
|
||||
asset.ExtractDataFloat(out data, out dataInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning("Failed to get audio clip PCM data. " + ex.Message);
|
||||
Editor.LogWarning(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.Length != dataInfo.NumSamples)
|
||||
{
|
||||
Editor.LogWarning("Failed to get audio clip PCM data. Invalid samples count. Returned buffer has other size.");
|
||||
}
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
// If asset has been modified during data fetching, ignore it
|
||||
if (_asset == asset)
|
||||
{
|
||||
_pcmData = data;
|
||||
_pcmInfo = dataInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
251
Source/Editor/Viewport/Previews/CubeTexturePreview.cs
Normal file
251
Source/Editor/Viewport/Previews/CubeTexturePreview.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Cube Texture asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Viewport.Previews.MaterialPreview" />
|
||||
public class CubeTexturePreview : MaterialPreview
|
||||
{
|
||||
private ChannelFlags _channelFlags = ChannelFlags.All;
|
||||
private bool _usePointSampler = false;
|
||||
private float _mipLevel = -1;
|
||||
private ContextMenu _mipWidgetMenu;
|
||||
private ContextMenuButton _filterWidgetPointButton;
|
||||
private ContextMenuButton _filterWidgetLinearButton;
|
||||
|
||||
/// <summary>
|
||||
/// The preview material instance used to draw texture.
|
||||
/// </summary>
|
||||
protected MaterialInstance _previewMaterial;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cube texture to preview.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The cube texture.
|
||||
/// </value>
|
||||
public CubeTexture CubeTexture
|
||||
{
|
||||
set
|
||||
{
|
||||
// Prepare material and assign texture asset as a parameter
|
||||
if (_previewMaterial == null || _previewMaterial.WaitForLoaded())
|
||||
{
|
||||
// Error
|
||||
Editor.LogError("Cannot load preview material.");
|
||||
return;
|
||||
}
|
||||
|
||||
var baseMaterial = _previewMaterial.BaseMaterial;
|
||||
if (baseMaterial == null || baseMaterial.WaitForLoaded())
|
||||
{
|
||||
// Error
|
||||
Editor.LogError("Cannot load base material for preview material.");
|
||||
return;
|
||||
}
|
||||
|
||||
_previewMaterial.SetParameterValue("CubeTexture", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view channels to show.
|
||||
/// </summary>
|
||||
public ChannelFlags ViewChannels
|
||||
{
|
||||
get => _channelFlags;
|
||||
set
|
||||
{
|
||||
if (_channelFlags != value)
|
||||
{
|
||||
_channelFlags = value;
|
||||
UpdateMask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use point sampler when drawing the texture. The default value is false.
|
||||
/// </summary>
|
||||
public bool UsePointSampler
|
||||
{
|
||||
get => _usePointSampler;
|
||||
set
|
||||
{
|
||||
if (_usePointSampler != value)
|
||||
{
|
||||
_usePointSampler = value;
|
||||
_previewMaterial.SetParameterValue("PointSampler", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mip level to show. The default value is -1.
|
||||
/// </summary>
|
||||
public float MipLevel
|
||||
{
|
||||
get => _mipLevel;
|
||||
set
|
||||
{
|
||||
if (!Mathf.NearEqual(_mipLevel, value))
|
||||
{
|
||||
_mipLevel = value;
|
||||
_previewMaterial.SetParameterValue("Mip", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public CubeTexturePreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
// Create virtual material material
|
||||
_previewMaterial = FlaxEngine.Content.CreateVirtualAsset<MaterialInstance>();
|
||||
if (_previewMaterial != null)
|
||||
_previewMaterial.BaseMaterial = FlaxEngine.Content.LoadAsyncInternal<Material>("Editor/CubeTexturePreviewMaterial");
|
||||
|
||||
// Add widgets
|
||||
if (useWidgets)
|
||||
{
|
||||
// Channels widget
|
||||
var channelsWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
//
|
||||
var channelR = new ViewportWidgetButton("R", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture red channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelR.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Red : (ViewChannels & ~ChannelFlags.Red);
|
||||
var channelG = new ViewportWidgetButton("G", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture green channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelG.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Green : (ViewChannels & ~ChannelFlags.Green);
|
||||
var channelB = new ViewportWidgetButton("B", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture blue channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelB.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Blue : (ViewChannels & ~ChannelFlags.Blue);
|
||||
var channelA = new ViewportWidgetButton("A", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture alpha channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelA.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Alpha : (ViewChannels & ~ChannelFlags.Alpha);
|
||||
//
|
||||
channelsWidget.Parent = this;
|
||||
|
||||
// Mip widget
|
||||
var mipWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
_mipWidgetMenu = new ContextMenu();
|
||||
_mipWidgetMenu.VisibleChanged += OnMipWidgetMenuOnVisibleChanged;
|
||||
var mipWidgetButton = new ViewportWidgetButton("Mip", SpriteHandle.Invalid, _mipWidgetMenu)
|
||||
{
|
||||
TooltipText = "The mip level to show. The default is -1.",
|
||||
Parent = mipWidget
|
||||
};
|
||||
//
|
||||
mipWidget.Parent = this;
|
||||
|
||||
// Filter widget
|
||||
var filterWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
var filterWidgetMenu = new ContextMenu();
|
||||
filterWidgetMenu.VisibleChanged += OnFilterWidgetMenuVisibleChanged;
|
||||
_filterWidgetPointButton = filterWidgetMenu.AddButton("Point", () => UsePointSampler = true);
|
||||
_filterWidgetLinearButton = filterWidgetMenu.AddButton("Linear", () => UsePointSampler = false);
|
||||
var filterWidgetButton = new ViewportWidgetButton("Filter", SpriteHandle.Invalid, filterWidgetMenu)
|
||||
{
|
||||
TooltipText = "The texture preview filtering mode. The default is Linear.",
|
||||
Parent = filterWidget
|
||||
};
|
||||
//
|
||||
filterWidget.Parent = this;
|
||||
}
|
||||
|
||||
// Link it
|
||||
Material = _previewMaterial;
|
||||
}
|
||||
|
||||
private void OnFilterWidgetMenuVisibleChanged(Control control)
|
||||
{
|
||||
if (!control.Visible)
|
||||
return;
|
||||
|
||||
_filterWidgetPointButton.Checked = UsePointSampler;
|
||||
_filterWidgetLinearButton.Checked = !UsePointSampler;
|
||||
}
|
||||
|
||||
private void OnMipWidgetMenuOnVisibleChanged(Control control)
|
||||
{
|
||||
if (!control.Visible)
|
||||
return;
|
||||
|
||||
var textureObj = _previewMaterial.GetParameterValue("CubeTexture");
|
||||
|
||||
if (textureObj is TextureBase texture && !texture.WaitForLoaded())
|
||||
{
|
||||
_mipWidgetMenu.ItemsContainer.DisposeChildren();
|
||||
var mipLevels = texture.MipLevels;
|
||||
for (int i = -1; i < mipLevels; i++)
|
||||
{
|
||||
var button = _mipWidgetMenu.AddButton(i.ToString(), OnMipWidgetClicked);
|
||||
button.Tag = i;
|
||||
button.Checked = Mathf.Abs(MipLevel - i) < 0.9f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMipWidgetClicked(ContextMenuButton button)
|
||||
{
|
||||
MipLevel = (int)button.Tag;
|
||||
}
|
||||
|
||||
private void UpdateMask()
|
||||
{
|
||||
Vector4 mask = Vector4.One;
|
||||
if ((_channelFlags & ChannelFlags.Red) == 0)
|
||||
mask.X = 0;
|
||||
if ((_channelFlags & ChannelFlags.Green) == 0)
|
||||
mask.Y = 0;
|
||||
if ((_channelFlags & ChannelFlags.Blue) == 0)
|
||||
mask.Z = 0;
|
||||
if ((_channelFlags & ChannelFlags.Alpha) == 0)
|
||||
mask.W = 0;
|
||||
_previewMaterial.SetParameterValue("Mask", mask);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
ViewportWidgetsContainer.ArrangeWidgets(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasLoadedAssets => base.HasLoadedAssets && _previewMaterial.IsLoaded && _previewMaterial.BaseMaterial.IsLoaded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Material = null;
|
||||
Object.Destroy(ref _previewMaterial);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Source/Editor/Viewport/Previews/IESProfilePreview.cs
Normal file
72
Source/Editor/Viewport/Previews/IESProfilePreview.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Preview control for <see cref="IESProfile"/> asset.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Viewport.Previews.TexturePreviewBase" />
|
||||
public class IESProfilePreview : TexturePreviewBase
|
||||
{
|
||||
private IESProfile _asset;
|
||||
private MaterialInstance _previewMaterial;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the asset to preview.
|
||||
/// </summary>
|
||||
public IESProfile Asset
|
||||
{
|
||||
get => _asset;
|
||||
set
|
||||
{
|
||||
if (_asset != value)
|
||||
{
|
||||
_asset = value;
|
||||
_previewMaterial.SetParameterValue("Texture", value);
|
||||
UpdateTextureRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IESProfilePreview"/> class.
|
||||
/// </summary>
|
||||
public IESProfilePreview()
|
||||
{
|
||||
var baseMaterial = FlaxEngine.Content.LoadAsyncInternal<Material>(EditorAssets.IesProfilePreviewMaterial);
|
||||
|
||||
// Wait for base (don't want to async material parameters set due to async loading)
|
||||
if (baseMaterial == null || baseMaterial.WaitForLoaded())
|
||||
throw new FlaxException("Cannot load IES Profile preview material.");
|
||||
|
||||
// Create preview material (virtual)
|
||||
_previewMaterial = baseMaterial.CreateVirtualInstance();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Object.Destroy(ref _previewMaterial);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(new Vector2(256), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawTexture(ref Rectangle rect)
|
||||
{
|
||||
// Check if has loaded asset
|
||||
if (_asset && _asset.IsLoaded)
|
||||
{
|
||||
Render2D.DrawMaterial(_previewMaterial, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
342
Source/Editor/Viewport/Previews/MaterialPreview.cs
Normal file
342
Source/Editor/Viewport/Previews/MaterialPreview.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Surface;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Material or Material Instance asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class MaterialPreview : AssetPreview, IVisjectSurfaceOwner
|
||||
{
|
||||
private static readonly string[] Models =
|
||||
{
|
||||
"Sphere",
|
||||
"Cube",
|
||||
"Plane",
|
||||
"Cylinder",
|
||||
"Cone"
|
||||
};
|
||||
|
||||
private StaticModel _previewModel;
|
||||
private Decal _decal;
|
||||
private Terrain _terrain;
|
||||
private ParticleEffect _particleEffect;
|
||||
private MaterialBase _particleEffectMaterial;
|
||||
private ParticleEmitter _particleEffectEmitter;
|
||||
private ParticleSystem _particleEffectSystem;
|
||||
private ParticleEmitterSurface _particleEffectSurface;
|
||||
private MaterialBase _material;
|
||||
private int _selectedModelIndex;
|
||||
private Image _guiMaterialControl;
|
||||
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
||||
/// </summary>
|
||||
public MaterialBase Material
|
||||
{
|
||||
get => _material;
|
||||
set
|
||||
{
|
||||
if (_material != value)
|
||||
{
|
||||
_material = value;
|
||||
UpdateMaterial();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected preview model index.
|
||||
/// </summary>
|
||||
public int SelectedModelIndex
|
||||
{
|
||||
get => _selectedModelIndex;
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > Models.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
_selectedModelIndex = value;
|
||||
_previewModel.Model = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/" + Models[value]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MaterialPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public MaterialPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
// Setup preview scene
|
||||
_previewModel = new StaticModel();
|
||||
_previewModel.Transform = new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f));
|
||||
SelectedModelIndex = 0;
|
||||
|
||||
// Link actors for rendering
|
||||
Task.AddCustomActor(_previewModel);
|
||||
|
||||
// Create context menu for primitive switching
|
||||
if (useWidgets && ViewWidgetButtonMenu != null)
|
||||
{
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
var modelSelect = ViewWidgetButtonMenu.AddChildMenu("Model").ContextMenu;
|
||||
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
{
|
||||
var button = modelSelect.AddButton(Models[i]);
|
||||
button.Tag = i;
|
||||
}
|
||||
|
||||
// Link the action
|
||||
modelSelect.ButtonClicked += (button) => SelectedModelIndex = (int)button.Tag;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasLoadedAssets
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!base.HasLoadedAssets)
|
||||
return false;
|
||||
UpdateMaterial();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
private void UpdateMaterial()
|
||||
{
|
||||
// If material is a surface link it to the preview model.
|
||||
// Otherwise use postFx volume to render custom postFx material.
|
||||
MaterialBase surfaceMaterial = null;
|
||||
MaterialBase postFxMaterial = null;
|
||||
MaterialBase decalMaterial = null;
|
||||
MaterialBase guiMaterial = null;
|
||||
MaterialBase terrainMaterial = null;
|
||||
MaterialBase particleMaterial = null;
|
||||
bool usePreviewActor = true;
|
||||
if (_material != null)
|
||||
{
|
||||
if (_material is MaterialInstance materialInstance && materialInstance.BaseMaterial == null)
|
||||
{
|
||||
// Material instance without a base material should not be used
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_material.Info.Domain)
|
||||
{
|
||||
case MaterialDomain.Surface:
|
||||
surfaceMaterial = _material;
|
||||
break;
|
||||
case MaterialDomain.PostProcess:
|
||||
postFxMaterial = _material;
|
||||
break;
|
||||
case MaterialDomain.Decal:
|
||||
decalMaterial = _material;
|
||||
break;
|
||||
case MaterialDomain.GUI:
|
||||
usePreviewActor = false;
|
||||
guiMaterial = _material;
|
||||
break;
|
||||
case MaterialDomain.Terrain:
|
||||
usePreviewActor = false;
|
||||
terrainMaterial = _material;
|
||||
break;
|
||||
case MaterialDomain.Particle:
|
||||
usePreviewActor = false;
|
||||
particleMaterial = _material;
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Surface
|
||||
if (_previewModel.Model == null)
|
||||
throw new Exception("Missing preview model asset.");
|
||||
if (_previewModel.Model.WaitForLoaded())
|
||||
throw new Exception("Preview model asset failed to load.");
|
||||
_previewModel.SetMaterial(0, surfaceMaterial);
|
||||
_previewModel.IsActive = usePreviewActor;
|
||||
|
||||
// PostFx
|
||||
_postFxMaterialsCache[0] = postFxMaterial;
|
||||
PostFxVolume.PostFxMaterials = new PostFxMaterialsSettings
|
||||
{
|
||||
Materials = _postFxMaterialsCache,
|
||||
};
|
||||
|
||||
// Decal
|
||||
if (decalMaterial && _decal == null)
|
||||
{
|
||||
_decal = new Decal();
|
||||
_decal.Size = new Vector3(100.0f);
|
||||
_decal.LocalOrientation = Quaternion.RotationZ(Mathf.PiOverTwo);
|
||||
Task.AddCustomActor(_decal);
|
||||
}
|
||||
if (_decal)
|
||||
{
|
||||
_decal.Material = decalMaterial;
|
||||
}
|
||||
|
||||
// GUI
|
||||
if (guiMaterial && _guiMaterialControl == null)
|
||||
{
|
||||
_guiMaterialControl = new Image
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
KeepAspectRatio = false,
|
||||
Brush = new MaterialBrush(),
|
||||
Parent = this,
|
||||
IndexInParent = 0,
|
||||
};
|
||||
}
|
||||
if (_guiMaterialControl != null)
|
||||
{
|
||||
((MaterialBrush)_guiMaterialControl.Brush).Material = guiMaterial;
|
||||
_guiMaterialControl.Enabled = _guiMaterialControl.Visible = guiMaterial != null;
|
||||
}
|
||||
|
||||
// Terrain
|
||||
if (terrainMaterial && _terrain == null)
|
||||
{
|
||||
_terrain = new Terrain();
|
||||
_terrain.Setup(1, 63);
|
||||
var chunkSize = _terrain.ChunkSize;
|
||||
var heightMapSize = chunkSize * Terrain.PatchEdgeChunksCount + 1;
|
||||
var heightMapLength = heightMapSize * heightMapSize;
|
||||
var heightmap = new float[heightMapLength];
|
||||
var patchCoord = new Int2(0, 0);
|
||||
_terrain.AddPatch(ref patchCoord);
|
||||
_terrain.SetupPatchHeightMap(ref patchCoord, heightmap, null, true);
|
||||
_terrain.LocalPosition = new Vector3(-1000, 0, -1000);
|
||||
Task.AddCustomActor(_terrain);
|
||||
}
|
||||
if (_terrain != null)
|
||||
{
|
||||
_terrain.IsActive = terrainMaterial != null;
|
||||
_terrain.Material = terrainMaterial;
|
||||
}
|
||||
|
||||
// Particle
|
||||
if (particleMaterial && _particleEffect == null)
|
||||
{
|
||||
_particleEffect = new ParticleEffect();
|
||||
_particleEffect.IsLooping = true;
|
||||
_particleEffect.UseTimeScale = false;
|
||||
Task.AddCustomActor(_particleEffect);
|
||||
}
|
||||
if (_particleEffect != null)
|
||||
{
|
||||
_particleEffect.IsActive = particleMaterial != null;
|
||||
if (particleMaterial)
|
||||
_particleEffect.UpdateSimulation();
|
||||
if (_particleEffectMaterial != particleMaterial && particleMaterial)
|
||||
{
|
||||
_particleEffectMaterial = particleMaterial;
|
||||
if (!_particleEffectEmitter)
|
||||
{
|
||||
var srcAsset = FlaxEngine.Content.LoadInternal<ParticleEmitter>("Editor/Particles/Particle Material Preview");
|
||||
Editor.Instance.ContentEditing.FastTempAssetClone(srcAsset.Path, out var clonedPath);
|
||||
_particleEffectEmitter = FlaxEngine.Content.Load<ParticleEmitter>(clonedPath);
|
||||
}
|
||||
if (_particleEffectSurface == null)
|
||||
_particleEffectSurface = new ParticleEmitterSurface(this, null, null);
|
||||
if (_particleEffectEmitter)
|
||||
{
|
||||
if (!_particleEffectSurface.Load())
|
||||
{
|
||||
var spriteModuleNode = _particleEffectSurface.FindNode(15, 400);
|
||||
spriteModuleNode.Values[2] = particleMaterial.ID;
|
||||
_particleEffectSurface.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_material = null;
|
||||
|
||||
if (_guiMaterialControl != null)
|
||||
{
|
||||
_guiMaterialControl.Dispose();
|
||||
_guiMaterialControl = null;
|
||||
}
|
||||
|
||||
Object.Destroy(ref _previewModel);
|
||||
Object.Destroy(ref _decal);
|
||||
Object.Destroy(ref _terrain);
|
||||
Object.Destroy(ref _particleEffect);
|
||||
Object.Destroy(ref _particleEffectEmitter);
|
||||
Object.Destroy(ref _particleEffectSystem);
|
||||
_particleEffectMaterial = null;
|
||||
_particleEffectSurface = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
string ISurfaceContext.SurfaceName => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
byte[] ISurfaceContext.SurfaceData
|
||||
{
|
||||
get => _particleEffectEmitter.LoadSurface(false);
|
||||
set
|
||||
{
|
||||
_particleEffectEmitter.SaveSurface(value);
|
||||
_particleEffectEmitter.Reload();
|
||||
|
||||
if (!_particleEffectSystem)
|
||||
{
|
||||
_particleEffectSystem = FlaxEngine.Content.CreateVirtualAsset<ParticleSystem>();
|
||||
_particleEffectSystem.Init(_particleEffectEmitter, 5.0f);
|
||||
_particleEffect.ParticleSystem = _particleEffectSystem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void ISurfaceContext.OnContextCreated(VisjectSurfaceContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Undo Undo => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
void IVisjectSurfaceOwner.OnSurfaceEditedChanged()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IVisjectSurfaceOwner.OnSurfaceGraphEdited()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IVisjectSurfaceOwner.OnSurfaceClose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
95
Source/Editor/Viewport/Previews/ModelPreview.cs
Normal file
95
Source/Editor/Viewport/Previews/ModelPreview.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Model asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class ModelPreview : AssetPreview
|
||||
{
|
||||
private StaticModel _previewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model asset to preview.
|
||||
/// </summary>
|
||||
public Model Model
|
||||
{
|
||||
get => _previewModel.Model;
|
||||
set => _previewModel.Model = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model actor used to preview selected asset.
|
||||
/// </summary>
|
||||
public StaticModel PreviewActor => _previewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether scale the model to the normalized bounds.
|
||||
/// </summary>
|
||||
public bool ScaleToFit { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public ModelPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
Task.Begin += OnBegin;
|
||||
|
||||
// Setup preview scene
|
||||
_previewModel = new StaticModel();
|
||||
|
||||
// Link actors for rendering
|
||||
Task.AddCustomActor(_previewModel);
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
// Preview LOD
|
||||
{
|
||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
||||
var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f);
|
||||
previewLODValue.Parent = previewLOD;
|
||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
if (!ScaleToFit)
|
||||
{
|
||||
_previewModel.Scale = Vector3.One;
|
||||
_previewModel.Position = Vector3.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update preview model scale to fit the preview
|
||||
var model = Model;
|
||||
if (model && model.IsLoaded)
|
||||
{
|
||||
float targetSize = 50.0f;
|
||||
BoundingBox box = model.GetBox();
|
||||
float maxSize = Mathf.Max(0.001f, box.Size.MaxValue);
|
||||
float scale = targetSize / maxSize;
|
||||
_previewModel.Scale = new Vector3(scale);
|
||||
_previewModel.Position = box.Center * (-0.5f * scale) + new Vector3(0, -10, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Ensure to cleanup created actor objects
|
||||
Object.Destroy(ref _previewModel);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
90
Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs
Normal file
90
Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Particle Emitter asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class ParticleEmitterPreview : ParticleSystemPreview
|
||||
{
|
||||
private ParticleEmitter _emitter;
|
||||
private ParticleSystem _system;
|
||||
private float _playbackDuration = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the particle emitter asset to preview.
|
||||
/// </summary>
|
||||
public ParticleEmitter Emitter
|
||||
{
|
||||
get => _emitter;
|
||||
set
|
||||
{
|
||||
if (_emitter != value)
|
||||
{
|
||||
_emitter = value;
|
||||
_system.Init(value, _playbackDuration);
|
||||
PreviewActor.ResetSimulation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration of the emitter playback (in seconds).
|
||||
/// </summary>
|
||||
public float PlaybackDuration
|
||||
{
|
||||
get => _playbackDuration;
|
||||
set
|
||||
{
|
||||
value = Mathf.Clamp(value, 0.1f, 100000000000.0f);
|
||||
if (Mathf.NearEqual(_playbackDuration, value))
|
||||
return;
|
||||
|
||||
_playbackDuration = value;
|
||||
if (_system != null)
|
||||
{
|
||||
_system.Init(_emitter, _playbackDuration);
|
||||
PreviewActor.ResetSimulation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParticleEmitterPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public ParticleEmitterPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
_system = FlaxEngine.Content.CreateVirtualAsset<ParticleSystem>();
|
||||
System = _system;
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
var playbackDuration = ViewWidgetButtonMenu.AddButton("Duration");
|
||||
var playbackDurationValue = new FloatValueBox(_playbackDuration, 75, 2, 50.0f, 0.1f, 1000000.0f, 0.1f);
|
||||
playbackDurationValue.Parent = playbackDuration;
|
||||
playbackDurationValue.ValueChanged += () => PlaybackDuration = playbackDurationValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => playbackDurationValue.Value = PlaybackDuration;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasLoadedAssets => (_emitter == null || _emitter.IsLoaded) && base.HasLoadedAssets;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Cleanup objects
|
||||
_emitter = null;
|
||||
Object.Destroy(ref _system);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
240
Source/Editor/Viewport/Previews/ParticleSystemPreview.cs
Normal file
240
Source/Editor/Viewport/Previews/ParticleSystemPreview.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Particle System asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class ParticleSystemPreview : AssetPreview
|
||||
{
|
||||
private ParticleEffect _previewEffect;
|
||||
private ContextMenuButton _showBoundsButton;
|
||||
private ContextMenuButton _showOriginButton;
|
||||
private ContextMenuButton _showParticleCounterButton;
|
||||
private StaticModel _boundsModel;
|
||||
private StaticModel _originModel;
|
||||
private bool _showParticlesCounter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the particle system asset to preview.
|
||||
/// </summary>
|
||||
public ParticleSystem System
|
||||
{
|
||||
get => _previewEffect.ParticleSystem;
|
||||
set => _previewEffect.ParticleSystem = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the particle effect actor used to preview selected asset.
|
||||
/// </summary>
|
||||
public ParticleEffect PreviewActor => _previewEffect;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to play the particles simulation in editor.
|
||||
/// </summary>
|
||||
public bool PlaySimulation { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show particle effect bounding box.
|
||||
/// </summary>
|
||||
public bool ShowBounds
|
||||
{
|
||||
get => _boundsModel?.IsActive ?? false;
|
||||
set
|
||||
{
|
||||
if (value == ShowBounds)
|
||||
return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (!_boundsModel)
|
||||
{
|
||||
_boundsModel = new StaticModel();
|
||||
_boundsModel.Model = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/WireBox");
|
||||
_boundsModel.Model.WaitForLoaded();
|
||||
_boundsModel.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>("Editor/Gizmo/MaterialWireFocus"));
|
||||
Task.AddCustomActor(_boundsModel);
|
||||
}
|
||||
else if (!_boundsModel.IsActive)
|
||||
{
|
||||
_boundsModel.IsActive = true;
|
||||
Task.AddCustomActor(_boundsModel);
|
||||
}
|
||||
|
||||
UpdateBoundsModel();
|
||||
}
|
||||
else
|
||||
{
|
||||
_boundsModel.IsActive = false;
|
||||
Task.RemoveCustomActor(_boundsModel);
|
||||
}
|
||||
|
||||
if (_showBoundsButton != null)
|
||||
_showBoundsButton.Checked = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show particle effect origin point.
|
||||
/// </summary>
|
||||
public bool ShowOrigin
|
||||
{
|
||||
get => _originModel?.IsActive ?? false;
|
||||
set
|
||||
{
|
||||
if (value == ShowOrigin)
|
||||
return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (!_originModel)
|
||||
{
|
||||
_originModel = new StaticModel();
|
||||
_originModel.Model = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/Sphere");
|
||||
_originModel.Model.WaitForLoaded();
|
||||
_originModel.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal<MaterialBase>("Editor/Gizmo/MaterialAxisFocus"));
|
||||
_originModel.Position = _previewEffect.Position;
|
||||
_originModel.Scale = new Vector3(0.1f);
|
||||
Task.AddCustomActor(_originModel);
|
||||
}
|
||||
else if (!_originModel.IsActive)
|
||||
{
|
||||
_originModel.IsActive = true;
|
||||
Task.AddCustomActor(_originModel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_originModel.IsActive = false;
|
||||
Task.RemoveCustomActor(_originModel);
|
||||
}
|
||||
|
||||
if (_showOriginButton != null)
|
||||
_showOriginButton.Checked = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show spawned particles counter (for CPU particles only).
|
||||
/// </summary>
|
||||
public bool ShowParticlesCounter
|
||||
{
|
||||
get => _showParticlesCounter;
|
||||
set
|
||||
{
|
||||
if (value == _showParticlesCounter)
|
||||
return;
|
||||
|
||||
_showParticlesCounter = value;
|
||||
|
||||
if (_showParticleCounterButton != null)
|
||||
_showParticleCounterButton.Checked = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParticleSystemPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public ParticleSystemPreview(bool useWidgets)
|
||||
: base(useWidgets, new FPSCamera())
|
||||
{
|
||||
// Setup preview scene
|
||||
_previewEffect = new ParticleEffect();
|
||||
_previewEffect.UseTimeScale = false;
|
||||
_previewEffect.IsLooping = true;
|
||||
_previewEffect.CustomViewRenderTask = Task;
|
||||
|
||||
// Link actors for rendering
|
||||
Task.AddCustomActor(_previewEffect);
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
_showBoundsButton = ViewWidgetShowMenu.AddButton("Bounds", () => ShowBounds = !ShowBounds);
|
||||
_showOriginButton = ViewWidgetShowMenu.AddButton("Origin", () => ShowOrigin = !ShowOrigin);
|
||||
_showParticleCounterButton = ViewWidgetShowMenu.AddButton("Particles Counter", () => ShowParticlesCounter = !ShowParticlesCounter);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBoundsModel()
|
||||
{
|
||||
var bounds = _previewEffect.Box;
|
||||
Transform t = Transform.Identity;
|
||||
t.Translation = bounds.Center;
|
||||
t.Scale = bounds.Size;
|
||||
_boundsModel.Transform = t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fits the particle system into view (scales the emitter based on the current bounds of the system).
|
||||
/// </summary>
|
||||
/// <param name="targetSize">The target size of the effect.</param>
|
||||
public void FitIntoView(float targetSize = 300.0f)
|
||||
{
|
||||
_previewEffect.Scale = Vector3.One;
|
||||
float maxSize = Mathf.Max(0.001f, _previewEffect.Box.Size.MaxValue);
|
||||
_previewEffect.Scale = new Vector3(targetSize / maxSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasLoadedAssets => _previewEffect.HasContentLoaded && base.HasLoadedAssets;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
// Manually update simulation
|
||||
if (PlaySimulation)
|
||||
{
|
||||
_previewEffect.UpdateSimulation();
|
||||
}
|
||||
|
||||
// Keep bounds matching the model
|
||||
if (_boundsModel && _boundsModel.IsActive)
|
||||
{
|
||||
UpdateBoundsModel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
if (_showParticlesCounter)
|
||||
{
|
||||
var count = _previewEffect.ParticlesCount;
|
||||
Render2D.DrawText(
|
||||
Style.Current.FontSmall,
|
||||
"Particles: " + count,
|
||||
new Rectangle(Vector2.Zero, Size),
|
||||
Color.Wheat,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Far);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Cleanup objects
|
||||
_previewEffect.ParticleSystem = null;
|
||||
Object.Destroy(ref _previewEffect);
|
||||
Object.Destroy(ref _boundsModel);
|
||||
Object.Destroy(ref _originModel);
|
||||
_showBoundsButton = null;
|
||||
_showOriginButton = null;
|
||||
_showParticleCounterButton = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
96
Source/Editor/Viewport/Previews/PrefabPreview.cs
Normal file
96
Source/Editor/Viewport/Previews/PrefabPreview.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Prefab asset preview editor viewport.
|
||||
/// </summary>
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class PrefabPreview : AssetPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// The preview that is during prefab instance spawning. Used to link some actors such as UIControl to preview scene and view.
|
||||
/// </summary>
|
||||
internal static PrefabPreview LoadingPreview;
|
||||
|
||||
private Prefab _prefab;
|
||||
private Actor _instance;
|
||||
internal Control customControlLinked;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefab asset to preview.
|
||||
/// </summary>
|
||||
public Prefab Prefab
|
||||
{
|
||||
get => _prefab;
|
||||
set
|
||||
{
|
||||
if (_prefab != value)
|
||||
{
|
||||
if (_instance)
|
||||
{
|
||||
if (customControlLinked != null)
|
||||
{
|
||||
customControlLinked.Parent = null;
|
||||
customControlLinked = null;
|
||||
}
|
||||
Task.RemoveCustomActor(_instance);
|
||||
Object.Destroy(_instance);
|
||||
}
|
||||
|
||||
_prefab = value;
|
||||
|
||||
if (_prefab)
|
||||
{
|
||||
_prefab.WaitForLoaded(); // TODO: use lazy prefab spawning to reduce stalls
|
||||
|
||||
var prevPreview = LoadingPreview;
|
||||
LoadingPreview = this;
|
||||
|
||||
_instance = PrefabManager.SpawnPrefab(_prefab, null);
|
||||
|
||||
LoadingPreview = prevPreview;
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
_prefab = null;
|
||||
throw new FlaxException("Failed to spawn a prefab for the preview.");
|
||||
}
|
||||
Task.AddCustomActor(_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the prefab spawned for the preview.
|
||||
/// </summary>
|
||||
public Actor Instance
|
||||
{
|
||||
get => _instance;
|
||||
internal set => _instance = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PrefabPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||
public PrefabPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// Cleanup
|
||||
Prefab = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
677
Source/Editor/Viewport/Previews/TexturePreview.cs
Normal file
677
Source/Editor/Viewport/Previews/TexturePreview.cs
Normal file
@@ -0,0 +1,677 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for texture previews. Draws a surface in the UI and supports view moving/zooming.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
public abstract class TexturePreviewBase : ContainerControl
|
||||
{
|
||||
private Rectangle _textureRect;
|
||||
private Vector2 _lastMosuePos;
|
||||
private Vector2 _viewPos;
|
||||
private float _viewScale = 1.0f;
|
||||
private bool _isMouseDown;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected TexturePreviewBase()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the view to the center.
|
||||
/// </summary>
|
||||
public void CenterView()
|
||||
{
|
||||
_viewScale = 1.0f;
|
||||
_viewPos = Vector2.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the texture rectangle.
|
||||
/// </summary>
|
||||
protected void UpdateTextureRect()
|
||||
{
|
||||
CalculateTextureRect(out _textureRect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the texture rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle.</param>
|
||||
protected abstract void CalculateTextureRect(out Rectangle rect);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the texture rect fr the given texture and the view size.
|
||||
/// </summary>
|
||||
/// <param name="textureSize">Size of the texture.</param>
|
||||
/// <param name="viewSize">Size of the view.</param>
|
||||
/// <param name="result">The result.</param>
|
||||
protected static void CalculateTextureRect(Vector2 textureSize, Vector2 viewSize, out Rectangle result)
|
||||
{
|
||||
Vector2 size = Vector2.Max(textureSize, Vector2.One);
|
||||
float aspectRatio = size.X / size.Y;
|
||||
float h = viewSize.X / aspectRatio;
|
||||
float w = viewSize.Y * aspectRatio;
|
||||
if (w > h)
|
||||
{
|
||||
float diff = (viewSize.Y - h) * 0.5f;
|
||||
result = new Rectangle(0, diff, viewSize.X, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
float diff = (viewSize.X - w) * 0.5f;
|
||||
result = new Rectangle(diff, 0, w, viewSize.Y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the texture.
|
||||
/// </summary>
|
||||
/// <param name="rect">The target texture view rectangle.</param>
|
||||
protected abstract void DrawTexture(ref Rectangle rect);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture view rect (scaled and offseted).
|
||||
/// </summary>
|
||||
protected Rectangle TextureViewRect => (_textureRect + _viewPos) * _viewScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
Render2D.PushClip(new Rectangle(Vector2.Zero, Size));
|
||||
|
||||
// Calculate texture view rectangle
|
||||
UpdateTextureRect();
|
||||
var textureRect = TextureViewRect;
|
||||
|
||||
// Call drawing
|
||||
DrawTexture(ref textureRect);
|
||||
|
||||
// Add overlay during debugger breakpoint hang
|
||||
if (Editor.Instance.Simulation.IsDuringBreakpointHang)
|
||||
{
|
||||
var bounds = new Rectangle(Vector2.Zero, Size);
|
||||
Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f));
|
||||
Render2D.DrawText(Style.Current.FontLarge, "Debugger breakpoint hit...", bounds, Color.White, TextAlignment.Center, TextAlignment.Center);
|
||||
}
|
||||
|
||||
Render2D.PopClip();
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseEnter(Vector2 location)
|
||||
{
|
||||
// Store mouse position
|
||||
_lastMosuePos = location;
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Vector2 location)
|
||||
{
|
||||
// Check if mouse is down
|
||||
if (_isMouseDown)
|
||||
{
|
||||
// Calculate mouse delta
|
||||
Vector2 delta = location - _lastMosuePos;
|
||||
|
||||
// Move view
|
||||
_viewPos += delta;
|
||||
}
|
||||
|
||||
// Store mouse position
|
||||
_lastMosuePos = location;
|
||||
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseWheel(Vector2 location, float delta)
|
||||
{
|
||||
if (base.OnMouseWheel(location, delta))
|
||||
return true;
|
||||
|
||||
// Zoom
|
||||
float prevScale = _viewScale;
|
||||
_viewScale = Mathf.Clamp(_viewScale + delta * 0.24f, 0.001f, 20.0f);
|
||||
|
||||
// Move view to make use of the control much more soother
|
||||
//float coeff = (prevScale + (_viewScale - prevScale)) / prevScale;
|
||||
//_viewPos += (location * coeff - location) / _viewScale;
|
||||
//_viewPos += location / _viewScale;
|
||||
Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size;
|
||||
_viewPos += sizeDelta * 0.5f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDown(location, button))
|
||||
return true;
|
||||
|
||||
// Set flag
|
||||
_isMouseDown = true;
|
||||
_lastMosuePos = location;
|
||||
Cursor = CursorType.SizeAll;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseUp(location, button))
|
||||
return true;
|
||||
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSizeChanged()
|
||||
{
|
||||
base.OnSizeChanged();
|
||||
|
||||
// Update texture rectangle and move view to the center
|
||||
UpdateTextureRect();
|
||||
CenterView();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture channel flags.
|
||||
/// </summary>
|
||||
[Flags, HideInEditor]
|
||||
public enum ChannelFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The red channel.
|
||||
/// </summary>
|
||||
Red = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The green channel.
|
||||
/// </summary>
|
||||
Green = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The blue channel.
|
||||
/// </summary>
|
||||
Blue = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The alpha channel.
|
||||
/// </summary>
|
||||
Alpha = 8,
|
||||
|
||||
/// <summary>
|
||||
/// All texture channels.
|
||||
/// </summary>
|
||||
All = Red | Green | Blue | Alpha
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for texture previews with custom drawing features. Uses in-build postFx material to render a texture.
|
||||
/// </summary>
|
||||
/// <seealso cref="TexturePreviewBase" />
|
||||
public abstract class TexturePreviewCustomBase : TexturePreviewBase
|
||||
{
|
||||
private ChannelFlags _channelFlags = ChannelFlags.All;
|
||||
private bool _usePointSampler = false;
|
||||
private float _mipLevel = -1;
|
||||
private ContextMenu _mipWidgetMenu;
|
||||
private ContextMenuButton _filterWidgetPointButton;
|
||||
private ContextMenuButton _filterWidgetLinearButton;
|
||||
|
||||
/// <summary>
|
||||
/// The preview material instance used to draw texture.
|
||||
/// </summary>
|
||||
protected MaterialInstance _previewMaterial;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view channels to show.
|
||||
/// </summary>
|
||||
public ChannelFlags ViewChannels
|
||||
{
|
||||
get => _channelFlags;
|
||||
set
|
||||
{
|
||||
if (_channelFlags != value)
|
||||
{
|
||||
_channelFlags = value;
|
||||
UpdateMask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether use point sampler when drawing the texture. The default value is false.
|
||||
/// </summary>
|
||||
public bool UsePointSampler
|
||||
{
|
||||
get => _usePointSampler;
|
||||
set
|
||||
{
|
||||
if (_usePointSampler != value)
|
||||
{
|
||||
_usePointSampler = value;
|
||||
_previewMaterial.SetParameterValue("PointSampler", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the mip level to show. The default value is -1.
|
||||
/// </summary>
|
||||
public float MipLevel
|
||||
{
|
||||
get => _mipLevel;
|
||||
set
|
||||
{
|
||||
if (!Mathf.NearEqual(_mipLevel, value))
|
||||
{
|
||||
_mipLevel = value;
|
||||
_previewMaterial.SetParameterValue("Mip", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <param name="useWidgets">True if show viewport widgets.</param>
|
||||
protected TexturePreviewCustomBase(bool useWidgets)
|
||||
{
|
||||
// Create preview material (virtual)
|
||||
var baseMaterial = FlaxEngine.Content.LoadAsyncInternal<Material>("Editor/TexturePreviewMaterial");
|
||||
if (baseMaterial == null)
|
||||
throw new FlaxException("Cannot load texture preview material.");
|
||||
_previewMaterial = baseMaterial.CreateVirtualInstance();
|
||||
if (_previewMaterial == null)
|
||||
throw new FlaxException("Failed to create virtual material instance for preview material.");
|
||||
|
||||
// Add widgets
|
||||
if (useWidgets)
|
||||
{
|
||||
// Channels widget
|
||||
var channelsWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
//
|
||||
var channelR = new ViewportWidgetButton("R", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture red channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelR.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Red : (ViewChannels & ~ChannelFlags.Red);
|
||||
var channelG = new ViewportWidgetButton("G", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture green channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelG.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Green : (ViewChannels & ~ChannelFlags.Green);
|
||||
var channelB = new ViewportWidgetButton("B", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture blue channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelB.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Blue : (ViewChannels & ~ChannelFlags.Blue);
|
||||
var channelA = new ViewportWidgetButton("A", SpriteHandle.Invalid, null, true)
|
||||
{
|
||||
Checked = true,
|
||||
TooltipText = "Show/hide texture alpha channel",
|
||||
Parent = channelsWidget
|
||||
};
|
||||
channelA.Toggled += button => ViewChannels = button.Checked ? ViewChannels | ChannelFlags.Alpha : (ViewChannels & ~ChannelFlags.Alpha);
|
||||
//
|
||||
channelsWidget.Parent = this;
|
||||
|
||||
// Mip widget
|
||||
var mipWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
_mipWidgetMenu = new ContextMenu();
|
||||
_mipWidgetMenu.VisibleChanged += OnMipWidgetMenuOnVisibleChanged;
|
||||
var mipWidgetButton = new ViewportWidgetButton("Mip", SpriteHandle.Invalid, _mipWidgetMenu)
|
||||
{
|
||||
TooltipText = "The mip level to show. The default is -1.",
|
||||
Parent = mipWidget
|
||||
};
|
||||
//
|
||||
mipWidget.Parent = this;
|
||||
|
||||
// Filter widget
|
||||
var filterWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
var filterWidgetMenu = new ContextMenu();
|
||||
filterWidgetMenu.VisibleChanged += OnFilterWidgetMenuVisibleChanged;
|
||||
_filterWidgetPointButton = filterWidgetMenu.AddButton("Point", () => UsePointSampler = true);
|
||||
_filterWidgetLinearButton = filterWidgetMenu.AddButton("Linear", () => UsePointSampler = false);
|
||||
var filterWidgetButton = new ViewportWidgetButton("Filter", SpriteHandle.Invalid, filterWidgetMenu)
|
||||
{
|
||||
TooltipText = "The texture preview filtering mode. The default is Linear.",
|
||||
Parent = filterWidget
|
||||
};
|
||||
//
|
||||
filterWidget.Parent = this;
|
||||
}
|
||||
|
||||
// Wait for base (don't want to async material parameters set due to async loading)
|
||||
baseMaterial.WaitForLoaded();
|
||||
}
|
||||
|
||||
private void OnFilterWidgetMenuVisibleChanged(Control control)
|
||||
{
|
||||
if (!control.Visible)
|
||||
return;
|
||||
|
||||
_filterWidgetPointButton.Checked = UsePointSampler;
|
||||
_filterWidgetLinearButton.Checked = !UsePointSampler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the texture to draw (material parameter).
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
protected void SetTexture(object value)
|
||||
{
|
||||
_previewMaterial.SetParameterValue("Texture", value);
|
||||
UpdateTextureRect();
|
||||
}
|
||||
|
||||
private void OnMipWidgetMenuOnVisibleChanged(Control control)
|
||||
{
|
||||
if (!control.Visible)
|
||||
return;
|
||||
|
||||
var textureObj = _previewMaterial.GetParameterValue("Texture");
|
||||
|
||||
if (textureObj is TextureBase texture && !texture.WaitForLoaded())
|
||||
{
|
||||
_mipWidgetMenu.ItemsContainer.DisposeChildren();
|
||||
var mipLevels = texture.MipLevels;
|
||||
for (int i = -1; i < mipLevels; i++)
|
||||
{
|
||||
var button = _mipWidgetMenu.AddButton(i.ToString(), OnMipWidgetClicked);
|
||||
button.Tag = i;
|
||||
if (i == -1)
|
||||
button.TooltipText = "Default mip.";
|
||||
button.Checked = Mathf.Abs(MipLevel - i) < 0.9f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMipWidgetClicked(ContextMenuButton button)
|
||||
{
|
||||
MipLevel = (int)button.Tag;
|
||||
}
|
||||
|
||||
private void UpdateMask()
|
||||
{
|
||||
Vector4 mask = Vector4.One;
|
||||
if ((_channelFlags & ChannelFlags.Red) == 0)
|
||||
mask.X = 0;
|
||||
if ((_channelFlags & ChannelFlags.Green) == 0)
|
||||
mask.Y = 0;
|
||||
if ((_channelFlags & ChannelFlags.Blue) == 0)
|
||||
mask.Z = 0;
|
||||
if ((_channelFlags & ChannelFlags.Alpha) == 0)
|
||||
mask.W = 0;
|
||||
_previewMaterial.SetParameterValue("Mask", mask);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PerformLayoutBeforeChildren()
|
||||
{
|
||||
base.PerformLayoutBeforeChildren();
|
||||
|
||||
ViewportWidgetsContainer.ArrangeWidgets(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Object.Destroy(ref _previewMaterial);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture preview GUI control. Draws <see cref="FlaxEngine.Texture"/> in the UI and supports view moving/zomming.
|
||||
/// </summary>
|
||||
/// <seealso cref="TexturePreviewBase" />
|
||||
public class SimpleTexturePreview : TexturePreviewBase
|
||||
{
|
||||
private Texture _asset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the asset to preview.
|
||||
/// </summary>
|
||||
public Texture Asset
|
||||
{
|
||||
get => _asset;
|
||||
set
|
||||
{
|
||||
if (_asset != value)
|
||||
{
|
||||
_asset = value;
|
||||
UpdateTextureRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to multiply texture colors.
|
||||
/// </summary>
|
||||
public Color Color { get; set; } = Color.White;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Vector2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawTexture(ref Rectangle rect)
|
||||
{
|
||||
// Background
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
|
||||
// Check if has loaded asset
|
||||
if (_asset && _asset.IsLoaded)
|
||||
{
|
||||
Render2D.DrawTexture(_asset, rect, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sprite atlas preview GUI control. Draws <see cref="SpriteAtlas"/> in the UI and supports view moving/zomming.
|
||||
/// </summary>
|
||||
/// <seealso cref="TexturePreviewBase" />
|
||||
public class SimpleSpriteAtlasPreview : TexturePreviewBase
|
||||
{
|
||||
private SpriteAtlas _asset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the asset to preview.
|
||||
/// </summary>
|
||||
public SpriteAtlas Asset
|
||||
{
|
||||
get => _asset;
|
||||
set
|
||||
{
|
||||
if (_asset != value)
|
||||
{
|
||||
_asset = value;
|
||||
UpdateTextureRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color used to multiply texture colors.
|
||||
/// </summary>
|
||||
public Color Color { get; set; } = Color.White;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Vector2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawTexture(ref Rectangle rect)
|
||||
{
|
||||
// Background
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
|
||||
// Check if has loaded asset
|
||||
if (_asset && _asset.IsLoaded)
|
||||
{
|
||||
Render2D.DrawTexture(_asset, rect, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture preview GUI control. Draws <see cref="FlaxEngine.Texture"/> in the UI and supports view moving/zooming.
|
||||
/// Supports texture channels masking and color transformations.
|
||||
/// </summary>
|
||||
/// <seealso cref="TexturePreviewCustomBase" />
|
||||
public class TexturePreview : TexturePreviewCustomBase
|
||||
{
|
||||
private TextureBase _asset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the texture to preview.
|
||||
/// </summary>
|
||||
public TextureBase Asset
|
||||
{
|
||||
get => _asset;
|
||||
set
|
||||
{
|
||||
if (_asset != value)
|
||||
{
|
||||
_asset = value;
|
||||
SetTexture(_asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TexturePreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">True if show viewport widgets.</param>
|
||||
/// <inheritdoc />
|
||||
public TexturePreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Vector2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawTexture(ref Rectangle rect)
|
||||
{
|
||||
// Background
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
|
||||
// Check if has loaded asset
|
||||
if (_asset && _asset.IsLoaded)
|
||||
{
|
||||
Render2D.DrawMaterial(_previewMaterial, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sprite atlas preview GUI control. Draws <see cref="FlaxEngine.SpriteAtlas"/> in the UI and supports view moving/zomming.
|
||||
/// Supports texture channels masking and color transformations.
|
||||
/// </summary>
|
||||
/// <seealso cref="TexturePreviewCustomBase" />
|
||||
public class SpriteAtlasPreview : TexturePreviewCustomBase
|
||||
{
|
||||
private SpriteAtlas _asset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sprite atlas to preview.
|
||||
/// </summary>
|
||||
public SpriteAtlas Asset
|
||||
{
|
||||
get => _asset;
|
||||
set
|
||||
{
|
||||
if (_asset != value)
|
||||
{
|
||||
_asset = value;
|
||||
SetTexture(_asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SpriteAtlasPreview"/> class.
|
||||
/// </summary>
|
||||
/// <param name="useWidgets">True if show viewport widgets.</param>
|
||||
/// <inheritdoc />
|
||||
public SpriteAtlasPreview(bool useWidgets)
|
||||
: base(useWidgets)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void CalculateTextureRect(out Rectangle rect)
|
||||
{
|
||||
CalculateTextureRect(_asset?.Size ?? new Vector2(100), Size, out rect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void DrawTexture(ref Rectangle rect)
|
||||
{
|
||||
// Background
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
|
||||
// Check if has loaded asset
|
||||
if (_asset && _asset.IsLoaded)
|
||||
{
|
||||
Render2D.DrawMaterial(_previewMaterial, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user