Merge remote-tracking branch 'origin/1.1' into linux-editor
# Conflicts: # Source/Engine/Core/Math/BoundingSphere.cs # Source/Engine/Debug/DebugDraw.cpp # Source/Engine/Platform/Win32/Win32Platform.cpp # Source/Engine/Platform/Win32/Win32Platform.h
This commit is contained in:
@@ -142,9 +142,7 @@ GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, flo
|
||||
{
|
||||
GeometryData output = (GeometryData)0;
|
||||
output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2;
|
||||
#if USE_LIGHTMAP
|
||||
output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2;
|
||||
#endif
|
||||
#if USE_VERTEX_COLOR
|
||||
output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2;
|
||||
#endif
|
||||
@@ -581,7 +579,7 @@ VertexOutput VS_Skinned(ModelInput_Skinned input)
|
||||
|
||||
void ClipLODTransition(PixelInput input)
|
||||
{
|
||||
float ditherFactor = input.InstanceParams.y;
|
||||
float ditherFactor = input.Geometry.InstanceParams.y;
|
||||
if (abs(ditherFactor) > 0.001)
|
||||
{
|
||||
float randGrid = cos(dot(floor(input.Position.xy), float2(347.83452793, 3343.28371863)));
|
||||
|
||||
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Normal file
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -34,7 +34,7 @@ Flax Visual Studio extension provides better programming workflow, C# scripts de
|
||||
|
||||
* Install Visual Studio 2015 or newer
|
||||
* Install Windows 8.1 SDK or newer
|
||||
* Install Microsoft Visual C++ 2015.3 v140 toolset for desktop (x86, x64)
|
||||
* Install Microsoft Visual C++ 2015 v140 toolset or newer
|
||||
* Clone repo (with LFS)
|
||||
* Run **GenerateProjectFiles.bat**
|
||||
* Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64**
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Types/DateTime.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
|
||||
@@ -46,24 +46,47 @@ namespace FlaxEditor.Content
|
||||
/// Create collision data from model.
|
||||
/// </summary>
|
||||
/// <param name="model">The associated model.</param>
|
||||
public void CreateCollisionDataFromModel(Model model)
|
||||
/// <param name="created">The action to call once the collision data gets created (or reused from existing).</param>
|
||||
public void CreateCollisionDataFromModel(Model model, Action<CollisionData> created = null)
|
||||
{
|
||||
Action<ContentItem> created = contentItem =>
|
||||
// Check if there already is collision data for that model to reuse
|
||||
var modelItem = (AssetItem)Editor.Instance.ContentDatabase.Find(model.ID);
|
||||
if (modelItem?.ParentFolder != null)
|
||||
{
|
||||
var ai = (AssetItem)contentItem;
|
||||
var cd = FlaxEngine.Content.LoadAsync<CollisionData>(ai.ID);
|
||||
if (cd == null || cd.WaitForLoaded())
|
||||
foreach (var child in modelItem.ParentFolder.Children)
|
||||
{
|
||||
if (child is BinaryAssetItem b && b.IsOfType<CollisionData>())
|
||||
{
|
||||
var collisionData = FlaxEngine.Content.Load<CollisionData>(b.ID);
|
||||
if (collisionData && collisionData.Options.Model == model.ID)
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.Select(b);
|
||||
if (created != null)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => created(collisionData));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new item so user can name it and then generate collision for it in async
|
||||
Action<ContentItem> create = contentItem =>
|
||||
{
|
||||
var assetItem = (AssetItem)contentItem;
|
||||
var collisionData = FlaxEngine.Content.LoadAsync<CollisionData>(assetItem.ID);
|
||||
if (collisionData == null || collisionData.WaitForLoaded())
|
||||
{
|
||||
Editor.LogError("Failed to load created collision data.");
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Editor.CookMeshCollision(ai.Path, CollisionDataType.TriangleMesh, model);
|
||||
});
|
||||
Editor.CookMeshCollision(assetItem.Path, CollisionDataType.TriangleMesh, model);
|
||||
if (created != null)
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() => created(collisionData));
|
||||
});
|
||||
};
|
||||
Editor.Instance.Windows.ContentWin.NewItem(this, null, created);
|
||||
Editor.Instance.Windows.ContentWin.NewItem(this, null, create);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +45,11 @@ namespace FlaxEditor.Content
|
||||
{
|
||||
base.OnContentWindowContextMenu(menu, item);
|
||||
|
||||
menu.AddButton("Generate collision data", () =>
|
||||
menu.AddButton("Create collision data", () =>
|
||||
{
|
||||
var model = FlaxEngine.Content.LoadAsync<Model>(((ModelAssetItem)item).ID);
|
||||
var cdProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy<CollisionData>();
|
||||
cdProxy.CreateCollisionDataFromModel(model);
|
||||
var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy<CollisionData>();
|
||||
collisionDataProxy.CreateCollisionDataFromModel(model);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "AndroidPlatformTools.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/Android/AndroidPlatformSettings.h"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Editor/Cooker/PlatformTools.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
|
||||
bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, const String& projectFolderPath)
|
||||
{
|
||||
|
||||
@@ -97,6 +97,8 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
data.AddRootEngineAsset(PRE_INTEGRATED_GF_ASSET_NAME);
|
||||
data.AddRootEngineAsset(SMAA_AREA_TEX);
|
||||
data.AddRootEngineAsset(SMAA_SEARCH_TEX);
|
||||
if (data.Configuration != BuildConfiguration::Release)
|
||||
data.AddRootEngineAsset(TEXT("Editor/Fonts/Roboto-Regular"));
|
||||
|
||||
// Register game assets
|
||||
data.StepProgress(TEXT("Deploying game data"), 50);
|
||||
|
||||
@@ -520,15 +520,46 @@ namespace FlaxEditor.CustomEditors
|
||||
try
|
||||
{
|
||||
string text;
|
||||
if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(Values.Type))
|
||||
if (ParentEditor is Dedicated.ScriptsEditor)
|
||||
{
|
||||
// Script
|
||||
text = JsonSerializer.Serialize(Values[0]);
|
||||
|
||||
// Remove properties that should be ignored when copy/pasting data
|
||||
if (text == null)
|
||||
text = string.Empty;
|
||||
int idx = text.IndexOf("\"Actor\":");
|
||||
if (idx != -1)
|
||||
{
|
||||
int endIdx = text.IndexOf("\n", idx);
|
||||
if (endIdx != -1)
|
||||
text = text.Remove(idx, endIdx - idx);
|
||||
}
|
||||
idx = text.IndexOf("\"Parent\":");
|
||||
if (idx != -1)
|
||||
{
|
||||
int endIdx = text.IndexOf("\n", idx);
|
||||
if (endIdx != -1)
|
||||
text = text.Remove(idx, endIdx - idx);
|
||||
}
|
||||
idx = text.IndexOf("\"OrderInParent\":");
|
||||
if (idx != -1)
|
||||
{
|
||||
int endIdx = text.IndexOf("\n", idx);
|
||||
if (endIdx != -1)
|
||||
text = text.Remove(idx, endIdx - idx);
|
||||
}
|
||||
}
|
||||
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(Values.Type))
|
||||
{
|
||||
// Object reference
|
||||
text = JsonSerializer.GetStringID(Values[0] as FlaxEngine.Object);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default
|
||||
text = JsonSerializer.Serialize(Values[0]);
|
||||
}
|
||||
|
||||
Clipboard.Text = text;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -538,7 +569,7 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetClipboardObject(out object result)
|
||||
private bool GetClipboardObject(out object result, bool deserialize)
|
||||
{
|
||||
result = null;
|
||||
var text = Clipboard.Text;
|
||||
@@ -546,8 +577,32 @@ namespace FlaxEditor.CustomEditors
|
||||
return false;
|
||||
|
||||
object obj;
|
||||
if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(Values.Type))
|
||||
if (ParentEditor is Dedicated.ScriptsEditor)
|
||||
{
|
||||
// Script
|
||||
obj = Values[0];
|
||||
if (deserialize)
|
||||
{
|
||||
if (Presenter.Undo != null && Presenter.Undo.Enabled)
|
||||
{
|
||||
using (new UndoBlock(Presenter.Undo, obj, "Paste values"))
|
||||
JsonSerializer.Deserialize(obj, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonSerializer.Deserialize(obj, text);
|
||||
}
|
||||
}
|
||||
#pragma warning disable 618
|
||||
else if (Newtonsoft.Json.Schema.JsonSchema.Parse(text) == null)
|
||||
#pragma warning restore 618
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (new ScriptType(typeof(FlaxEngine.Object)).IsAssignableFrom(Values.Type))
|
||||
{
|
||||
// Object reference
|
||||
if (text.Length != 32)
|
||||
return false;
|
||||
JsonSerializer.ParseID(text, out var id);
|
||||
@@ -555,6 +610,7 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default
|
||||
obj = JsonConvert.DeserializeObject(text, TypeUtils.GetType(Values.Type), JsonSerializer.Settings);
|
||||
}
|
||||
|
||||
@@ -576,7 +632,7 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetClipboardObject(out _);
|
||||
return GetClipboardObject(out _, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -594,7 +650,7 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
try
|
||||
{
|
||||
if (GetClipboardObject(out var obj))
|
||||
if (GetClipboardObject(out var obj, true))
|
||||
{
|
||||
SetValue(obj);
|
||||
}
|
||||
|
||||
89
Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs
Normal file
89
Source/Editor/CustomEditors/Dedicated/LayersMaskEditor.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for <see cref="LayersMask"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(LayersMask)), DefaultEditor]
|
||||
internal class LayersMaskEditor : CustomEditor
|
||||
{
|
||||
private CheckBox[] _checkBoxes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var layers = LayersAndTagsSettings.GetCurrentLayers();
|
||||
if (layers == null || layers.Length == 0)
|
||||
{
|
||||
layout.Label("Missing layers and tags settings");
|
||||
return;
|
||||
}
|
||||
|
||||
_checkBoxes = new CheckBox[layers.Length];
|
||||
for (int i = 0; i < layers.Length; i++)
|
||||
{
|
||||
var layer = layers[i];
|
||||
var property = layout.AddPropertyItem(layer);
|
||||
var checkbox = property.Checkbox().CheckBox;
|
||||
UpdateCheckbox(checkbox, i);
|
||||
checkbox.Tag = i;
|
||||
checkbox.StateChanged += OnCheckboxStateChanged;
|
||||
_checkBoxes[i] = checkbox;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_checkBoxes = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
if (_checkBoxes != null)
|
||||
{
|
||||
for (int i = 0; i < _checkBoxes.Length; i++)
|
||||
{
|
||||
UpdateCheckbox(_checkBoxes[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
base.Refresh();
|
||||
}
|
||||
|
||||
private void OnCheckboxStateChanged(CheckBox checkBox)
|
||||
{
|
||||
var i = (int)checkBox.Tag;
|
||||
var value = (LayersMask)Values[0];
|
||||
var mask = 1u << i;
|
||||
value.Mask &= ~mask;
|
||||
value.Mask |= checkBox.Checked ? mask : 0;
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
private void UpdateCheckbox(CheckBox checkbox, int i)
|
||||
{
|
||||
for (var j = 0; j < Values.Count; j++)
|
||||
{
|
||||
var value = (((LayersMask)Values[j]).Mask & (1 << i)) != 0;
|
||||
if (j == 0)
|
||||
{
|
||||
checkbox.Checked = value;
|
||||
}
|
||||
else if (checkbox.State != CheckBoxState.Intermediate)
|
||||
{
|
||||
if (checkbox.Checked != value)
|
||||
checkbox.State = CheckBoxState.Intermediate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -15,6 +16,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
private readonly List<CheckablePropertyNameLabel> _labels = new List<CheckablePropertyNameLabel>(64);
|
||||
private const int MassOrder = 110;
|
||||
private Label _infoLabel;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
|
||||
@@ -63,6 +65,22 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
check = value.OverrideMass;
|
||||
_labels[i].CheckBox.Checked = check;
|
||||
}
|
||||
if (_infoLabel != null)
|
||||
{
|
||||
string text = string.Empty;
|
||||
if (Editor.IsPlayMode)
|
||||
{
|
||||
var linearVelocity = value.LinearVelocity;
|
||||
var centerOfMass = value.CenterOfMass;
|
||||
text = $"Speed: {linearVelocity.Length}\n" +
|
||||
$"Linear Velocity: {linearVelocity}\n" +
|
||||
$"Angular Velocity: {value.AngularVelocity}\n" +
|
||||
$"Center of Mass (local): {centerOfMass}\n" +
|
||||
$"Center Of Mass (world): {value.Transform.LocalToWorld(centerOfMass)}\n" +
|
||||
$"Is Sleeping: {value.IsSleeping}";
|
||||
}
|
||||
_infoLabel.Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -71,6 +89,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_labels.Clear();
|
||||
|
||||
base.Initialize(layout);
|
||||
|
||||
// Add info box
|
||||
if (IsSingleObject && Values[0] is RigidBody && Editor.IsPlayMode)
|
||||
{
|
||||
_infoLabel = layout.Label(string.Empty).Label;
|
||||
_infoLabel.AutoHeight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +624,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
// Create group
|
||||
var title = CustomEditorsUtil.GetPropertyNameUI(scriptType.Name);
|
||||
var group = layout.Group(title);
|
||||
var group = layout.Group(title, editor);
|
||||
if (Presenter.CacheExpandedGroups)
|
||||
{
|
||||
if (Editor.Instance.ProjectCache.IsCollapsedGroup(title))
|
||||
|
||||
@@ -35,6 +35,38 @@ namespace FlaxEditor.CustomEditors
|
||||
/// </summary>
|
||||
public abstract ContainerControl ContainerControl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds new group element.
|
||||
/// </summary>
|
||||
/// <param name="title">The title.</param>
|
||||
/// <param name="linkedEditor">The custom editor to be linked for a group. Used to provide more utility functions for a drop panel UI via context menu.</param>
|
||||
/// <param name="useTransparentHeader">True if use drop down icon and transparent group header, otherwise use normal style.</param>
|
||||
/// <returns>The created element.</returns>
|
||||
public GroupElement Group(string title, CustomEditor linkedEditor, bool useTransparentHeader = false)
|
||||
{
|
||||
var element = Group(title, useTransparentHeader);
|
||||
element.Panel.Tag = linkedEditor;
|
||||
element.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
|
||||
return element;
|
||||
}
|
||||
|
||||
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Vector2 location)
|
||||
{
|
||||
var linkedEditor = (CustomEditor)groupPanel.Tag;
|
||||
var menu = new ContextMenu();
|
||||
|
||||
var revertToPrefab = menu.AddButton("Revert to Prefab", linkedEditor.RevertToReferenceValue);
|
||||
revertToPrefab.Enabled = linkedEditor.CanRevertReferenceValue;
|
||||
var resetToDefault = menu.AddButton("Reset to default", linkedEditor.RevertToDefaultValue);
|
||||
resetToDefault.Enabled = linkedEditor.CanRevertDefaultValue;
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
|
||||
menu.Show(groupPanel, location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds new group element.
|
||||
/// </summary>
|
||||
@@ -551,11 +583,9 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
if (style == DisplayStyle.Group)
|
||||
{
|
||||
var group = Group(name, true);
|
||||
var group = Group(name, editor, true);
|
||||
group.Panel.Close(false);
|
||||
group.Panel.TooltipText = tooltip;
|
||||
group.Panel.Tag = editor;
|
||||
group.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
|
||||
return group.Object(values, editor);
|
||||
}
|
||||
|
||||
@@ -563,23 +593,6 @@ namespace FlaxEditor.CustomEditors
|
||||
return property.Object(values, editor);
|
||||
}
|
||||
|
||||
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Vector2 location)
|
||||
{
|
||||
var linkedEditor = (CustomEditor)groupPanel.Tag;
|
||||
var menu = new ContextMenu();
|
||||
|
||||
var revertToPrefab = menu.AddButton("Revert to Prefab", linkedEditor.RevertToReferenceValue);
|
||||
revertToPrefab.Enabled = linkedEditor.CanRevertReferenceValue;
|
||||
var resetToDefault = menu.AddButton("Reset to default", linkedEditor.RevertToDefaultValue);
|
||||
resetToDefault.Enabled = linkedEditor.CanRevertDefaultValue;
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
|
||||
menu.Show(groupPanel, location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds object property editor. Selects proper <see cref="CustomEditor"/> based on overrides.
|
||||
/// </summary>
|
||||
@@ -600,8 +613,9 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
if (style == DisplayStyle.Group)
|
||||
{
|
||||
var group = Group(label.Text, true);
|
||||
var group = Group(label.Text, editor, true);
|
||||
group.Panel.Close(false);
|
||||
group.Panel.TooltipText = tooltip;
|
||||
return group.Object(values, editor);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Editor.h"
|
||||
#include "ProjectInfo.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Scripting/ScriptsBuilder.h"
|
||||
#include "Windows/SplashScreen.h"
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Engine/Base/ApplicationBase.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
|
||||
class Actor;
|
||||
class SplashScreen;
|
||||
class ProjectInfo;
|
||||
class ManagedEditor;
|
||||
|
||||
static_assert(USE_EDITOR, "Don't include Editor in non-editor builds.");
|
||||
|
||||
@@ -97,6 +97,11 @@ namespace FlaxEditor
|
||||
/// </summary>
|
||||
public static string DefaultSkyCubeTexture = "Editor/SimplySky";
|
||||
|
||||
/// <summary>
|
||||
/// The default sprite material.
|
||||
/// </summary>
|
||||
public static string DefaultSpriteMaterial = "Editor/SpriteMaterial";
|
||||
|
||||
/// <summary>
|
||||
/// The IES Profile assets preview material.
|
||||
/// </summary>
|
||||
@@ -112,6 +117,16 @@ namespace FlaxEditor
|
||||
/// </summary>
|
||||
public static string VertexColorsPreviewMaterial = "Editor/Gizmo/VertexColorsPreviewMaterial";
|
||||
|
||||
/// <summary>
|
||||
/// The Flax icon texture.
|
||||
/// </summary>
|
||||
public static string FlaxIconTexture = "Engine/Textures/FlaxIcon";
|
||||
|
||||
/// <summary>
|
||||
/// The Flax icon (blue) texture.
|
||||
/// </summary>
|
||||
public static string FlaxIconBlueTexture = "Engine/Textures/FlaxIconBlue";
|
||||
|
||||
/// <summary>
|
||||
/// The icon lists used by editor from the SegMDL2 font.
|
||||
/// </summary>
|
||||
|
||||
@@ -194,11 +194,22 @@ namespace FlaxEditor.GUI.Docking
|
||||
bool isMouseOver = IsMouseOver && headerRect.Contains(MousePosition);
|
||||
Render2D.FillRectangle(headerRect, containsFocus ? style.BackgroundSelected : isMouseOver ? style.BackgroundHighlighted : style.LightBackground);
|
||||
|
||||
float iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
|
||||
|
||||
if (tab.Icon.IsValid)
|
||||
{
|
||||
Render2D.DrawSprite(
|
||||
tab.Icon,
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
style.Foreground);
|
||||
|
||||
}
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
tab.Title,
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight),
|
||||
new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Center);
|
||||
@@ -223,7 +234,8 @@ namespace FlaxEditor.GUI.Docking
|
||||
var tab = _panel.GetTab(i);
|
||||
Color tabColor = Color.Black;
|
||||
var titleSize = tab.TitleSize;
|
||||
float width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin;
|
||||
float iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0;
|
||||
float width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth;
|
||||
var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
|
||||
bool isMouseOver = IsMouseOver && tabRect.Contains(MousePosition);
|
||||
bool isSelected = _panel.SelectedTab == tab;
|
||||
@@ -241,11 +253,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
Render2D.FillRectangle(tabRect, tabColor);
|
||||
}
|
||||
|
||||
if (tab.Icon.IsValid)
|
||||
{
|
||||
Render2D.DrawSprite(
|
||||
tab.Icon,
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize),
|
||||
style.Foreground);
|
||||
|
||||
}
|
||||
|
||||
// Draw text
|
||||
Render2D.DrawText(
|
||||
style.FontMedium,
|
||||
tab.Title,
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin, 0, 10000, DockPanel.DefaultHeaderHeight),
|
||||
new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight),
|
||||
style.Foreground,
|
||||
TextAlignment.Near,
|
||||
TextAlignment.Center);
|
||||
|
||||
@@ -88,6 +88,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the window icon
|
||||
/// </summary>
|
||||
public SpriteHandle Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the title.
|
||||
/// </summary>
|
||||
|
||||
@@ -165,7 +165,10 @@ namespace FlaxEditor.Gizmo
|
||||
if (_selectionParents[i] is ActorNode actorNode)
|
||||
{
|
||||
bounds = BoundingBox.Merge(bounds, actorNode.Actor.BoxWithChildren);
|
||||
navigationDirty |= (actorNode.Actor.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation;
|
||||
if (actorNode.AffectsNavigationWithChildren)
|
||||
{
|
||||
navigationDirty |= actorNode.Actor.HasStaticFlag(StaticFlags.Navigation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "ManagedEditor.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
|
||||
@@ -195,6 +195,56 @@ namespace FlaxEditor.Modules
|
||||
OnSelectionChanged();
|
||||
}
|
||||
|
||||
private void OnDirty(ActorNode node)
|
||||
{
|
||||
var options = Editor.Options.Options;
|
||||
var isPlayMode = Editor.StateMachine.IsPlayMode;
|
||||
var actor = node.Actor;
|
||||
|
||||
// Auto CSG mesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildCSG)
|
||||
{
|
||||
if (actor is BoxBrush && actor.Scene)
|
||||
actor.Scene.BuildCSG(options.General.AutoRebuildCSGTimeoutMs);
|
||||
}
|
||||
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildNavMesh && actor.Scene && node.AffectsNavigationWithChildren)
|
||||
{
|
||||
var bounds = actor.BoxWithChildren;
|
||||
Navigation.BuildNavMesh(actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDirty(IEnumerable<SceneGraphNode> objects)
|
||||
{
|
||||
var options = Editor.Options.Options;
|
||||
var isPlayMode = Editor.StateMachine.IsPlayMode;
|
||||
|
||||
// Auto CSG mesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildCSG)
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (obj is ActorNode node && node.Actor is BoxBrush)
|
||||
node.Actor.Scene.BuildCSG(options.General.AutoRebuildCSGTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildNavMesh)
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (obj is ActorNode node && node.Actor.Scene && node.AffectsNavigationWithChildren)
|
||||
{
|
||||
var bounds = node.Actor.BoxWithChildren;
|
||||
Navigation.BuildNavMesh(node.Actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the specified actor to the game (with undo).
|
||||
/// </summary>
|
||||
@@ -242,21 +292,92 @@ namespace FlaxEditor.Modules
|
||||
|
||||
SpawnEnd?.Invoke();
|
||||
|
||||
var options = Editor.Options.Options;
|
||||
OnDirty(actorNode);
|
||||
}
|
||||
|
||||
// Auto CSG mesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildCSG)
|
||||
/// <summary>
|
||||
/// Converts the selected actor to another type.
|
||||
/// </summary>
|
||||
/// <param name="to">The type to convert in.</param>
|
||||
public void Convert(Type to)
|
||||
{
|
||||
if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode))
|
||||
return;
|
||||
|
||||
if (Level.IsAnySceneLoaded == false)
|
||||
throw new InvalidOperationException("Cannot spawn actor when no scene is loaded.");
|
||||
|
||||
var actionList = new IUndoAction[4];
|
||||
Actor old = ((ActorNode)Editor.SceneEditing.Selection[0]).Actor;
|
||||
Actor actor = (Actor)FlaxEngine.Object.New(to);
|
||||
var parent = old.Parent;
|
||||
var orderInParent = old.OrderInParent;
|
||||
|
||||
SelectionDeleteBegin?.Invoke();
|
||||
|
||||
actionList[0] = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo);
|
||||
actionList[0].Do();
|
||||
|
||||
actionList[1] = new DeleteActorsAction(new List<SceneGraphNode>
|
||||
{
|
||||
if (actor is BoxBrush && actor.Scene)
|
||||
actor.Scene.BuildCSG(options.General.AutoRebuildCSGTimeoutMs);
|
||||
Editor.Instance.Scene.GetActorNode(old)
|
||||
});
|
||||
actionList[1].Do();
|
||||
|
||||
SelectionDeleteEnd?.Invoke();
|
||||
|
||||
SpawnBegin?.Invoke();
|
||||
|
||||
// Copy properties
|
||||
actor.Transform = old.Transform;
|
||||
actor.StaticFlags = old.StaticFlags;
|
||||
actor.HideFlags = old.HideFlags;
|
||||
actor.Layer = old.Layer;
|
||||
actor.Tag = old.Tag;
|
||||
actor.Name = old.Name;
|
||||
actor.IsActive = old.IsActive;
|
||||
|
||||
// Spawn actor
|
||||
Level.SpawnActor(actor, parent);
|
||||
if (parent != null)
|
||||
actor.OrderInParent = orderInParent;
|
||||
if (Editor.StateMachine.IsPlayMode)
|
||||
actor.StaticFlags = StaticFlags.None;
|
||||
|
||||
// Move children
|
||||
for (var i = old.ScriptsCount - 1; i >= 0; i--)
|
||||
{
|
||||
var script = old.Scripts[i];
|
||||
script.Actor = actor;
|
||||
Guid newid = Guid.NewGuid();
|
||||
FlaxEngine.Object.Internal_ChangeID(FlaxEngine.Object.GetUnmanagedPtr(script), ref newid);
|
||||
}
|
||||
for (var i = old.Children.Length - 1; i >= 0; i--)
|
||||
{
|
||||
old.Children[i].Parent = actor;
|
||||
}
|
||||
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildNavMesh && actor.Scene && (actor.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation)
|
||||
var actorNode = Editor.Instance.Scene.GetActorNode(actor);
|
||||
if (actorNode == null)
|
||||
throw new InvalidOperationException("Failed to create scene node for the spawned actor.");
|
||||
|
||||
actorNode.PostSpawn();
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
|
||||
actionList[2] = new DeleteActorsAction(new List<SceneGraphNode>
|
||||
{
|
||||
var bounds = actor.BoxWithChildren;
|
||||
Navigation.BuildNavMesh(actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
actorNode
|
||||
}, true);
|
||||
|
||||
actionList[3] = new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[] { actorNode }, OnSelectionUndo);
|
||||
actionList[3].Do();
|
||||
|
||||
var actions = new MultiUndoAction(actionList);
|
||||
Undo.AddAction(actions);
|
||||
|
||||
SpawnEnd?.Invoke();
|
||||
|
||||
OnDirty(actorNode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -269,8 +390,6 @@ namespace FlaxEditor.Modules
|
||||
if (objects.Count == 0)
|
||||
return;
|
||||
|
||||
bool isPlayMode = Editor.StateMachine.IsPlayMode;
|
||||
|
||||
SelectionDeleteBegin?.Invoke();
|
||||
|
||||
// Change selection
|
||||
@@ -290,30 +409,7 @@ namespace FlaxEditor.Modules
|
||||
|
||||
SelectionDeleteEnd?.Invoke();
|
||||
|
||||
var options = Editor.Options.Options;
|
||||
|
||||
// Auto CSG mesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildCSG)
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (obj is ActorNode node && node.Actor is BoxBrush)
|
||||
node.Actor.Scene.BuildCSG(options.General.AutoRebuildCSGTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && options.General.AutoRebuildNavMesh)
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (obj is ActorNode node && node.Actor.Scene && (node.Actor.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation)
|
||||
{
|
||||
var bounds = node.Actor.BoxWithChildren;
|
||||
Navigation.BuildNavMesh(node.Actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
OnDirty(objects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -76,6 +76,29 @@ namespace FlaxEditor.SceneGraph
|
||||
_treeNode.LinkNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor affects navigation.
|
||||
/// </summary>
|
||||
public virtual bool AffectsNavigation => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this actor affects navigation or any of its children (recursive).
|
||||
/// </summary>
|
||||
public bool AffectsNavigationWithChildren
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_actor.HasStaticFlag(StaticFlags.Navigation) && AffectsNavigation)
|
||||
return true;
|
||||
for (var i = 0; i < ChildNodes.Count; i++)
|
||||
{
|
||||
if (ChildNodes[i] is ActorNode actorChild && actorChild.AffectsNavigationWithChildren)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the tree node for the specified actor.
|
||||
/// </summary>
|
||||
|
||||
@@ -18,6 +18,9 @@ namespace FlaxEditor.SceneGraph
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AffectsNavigation => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
|
||||
@@ -78,6 +78,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
AddChildNode(new LinkNode(this, new Guid(bytes), false));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AffectsNavigation => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
|
||||
@@ -16,5 +16,8 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AffectsNavigation => true;
|
||||
}
|
||||
}
|
||||
|
||||
63
Source/Editor/SceneGraph/Actors/SpriteRenderNode.cs
Normal file
63
Source/Editor/SceneGraph/Actors/SpriteRenderNode.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="SpriteRender"/> actor type.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorNode" />
|
||||
[HideInEditor]
|
||||
public sealed class SpriteRenderNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SpriteRenderNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
|
||||
{
|
||||
SpriteRender sprite = (SpriteRender)Actor;
|
||||
Vector3 viewPosition = ray.View.Position;
|
||||
Vector3 viewDirection = ray.View.Direction;
|
||||
Matrix m1, m2, m3, world;
|
||||
var size = sprite.Size;
|
||||
Matrix.Scaling(size.X, size.Y, 1.0f, out m1);
|
||||
var transform = sprite.Transform;
|
||||
if (sprite.FaceCamera)
|
||||
{
|
||||
var up = Vector3.Up;
|
||||
Matrix.Billboard(ref transform.Translation, ref viewPosition, ref up, ref viewDirection, out m2);
|
||||
Matrix.Multiply(ref m1, ref m2, out m3);
|
||||
Matrix.Scaling(ref transform.Scale, out m1);
|
||||
Matrix.Multiply(ref m1, ref m3, out world);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.GetWorld(out m2);
|
||||
Matrix.Multiply(ref m1, ref m2, out world);
|
||||
}
|
||||
|
||||
OrientedBoundingBox bounds;
|
||||
bounds.Extents = Vector3.Half;
|
||||
bounds.Transformation = world;
|
||||
|
||||
normal = -ray.Ray.Direction;
|
||||
return bounds.Intersects(ref ray.Ray, out distance);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostSpawn()
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
// Setup for default values
|
||||
var text = (SpriteRender)Actor;
|
||||
text.Material = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.DefaultSpriteMaterial);
|
||||
text.Image = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.FlaxIconTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
@@ -16,5 +19,32 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnContextMenu(ContextMenu contextMenu)
|
||||
{
|
||||
base.OnContextMenu(contextMenu);
|
||||
|
||||
contextMenu.AddButton("Add mesh collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null;
|
||||
}
|
||||
|
||||
private void OnAddMeshCollider()
|
||||
{
|
||||
var model = ((StaticModel)Actor).Model;
|
||||
if (!model)
|
||||
return;
|
||||
Action<CollisionData> created = collisionData =>
|
||||
{
|
||||
var actor = new MeshCollider
|
||||
{
|
||||
StaticFlags = Actor.StaticFlags,
|
||||
Transform = Actor.Transform,
|
||||
CollisionData = collisionData,
|
||||
};
|
||||
Editor.Instance.SceneEditing.Spawn(actor, Actor);
|
||||
};
|
||||
var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy<CollisionData>();
|
||||
collisionDataProxy.CreateCollisionDataFromModel(model, created);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,5 +15,8 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool AffectsNavigation => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace FlaxEditor.SceneGraph
|
||||
CustomNodesTypes.Add(typeof(SplineCollider), typeof(ColliderNode));
|
||||
CustomNodesTypes.Add(typeof(SplineRopeBody), typeof(ActorNode));
|
||||
CustomNodesTypes.Add(typeof(NavMesh), typeof(ActorNode));
|
||||
CustomNodesTypes.Add(typeof(SpriteRender), typeof(SpriteRenderNode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -197,12 +197,12 @@ namespace FlaxEditor.SceneGraph
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ray.
|
||||
/// The ray (for intersection raycasting).
|
||||
/// </summary>
|
||||
public Ray Ray;
|
||||
|
||||
/// <summary>
|
||||
/// The camera view ray.
|
||||
/// The camera view ray (camera position and direction).
|
||||
/// </summary>
|
||||
public Ray View;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Scripting/ScriptsBuilder.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Scripting/ScriptsBuilder.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ScriptsBuilder.h"
|
||||
#include "CodeEditor.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/StringBuilder.h"
|
||||
|
||||
@@ -579,6 +579,20 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Output(0, "YZ", typeof(Vector2), 1)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
{
|
||||
TypeID = 47,
|
||||
Title = "Mask ZW",
|
||||
Description = "Unpack ZW components from Vector",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
ConnectionsHints = ConnectionsHint.Vector,
|
||||
Size = new Vector2(110, 30),
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Value", true, null, 0),
|
||||
NodeElementArchetype.Factory.Output(0, "ZW", typeof(Vector2), 1)
|
||||
}
|
||||
},
|
||||
|
||||
// Mask XYZ
|
||||
new NodeArchetype
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace FlaxEditor.Tools.Terrain
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh)
|
||||
{
|
||||
if (terrain.Scene && (terrain.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation)
|
||||
if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation))
|
||||
{
|
||||
Navigation.BuildNavMesh(terrain.Scene, patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace FlaxEditor.Tools.Terrain
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh)
|
||||
{
|
||||
if (terrain.Scene && (terrain.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation)
|
||||
if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation))
|
||||
{
|
||||
Navigation.BuildNavMesh(terrain.Scene, patchBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt
|
||||
// Auto NavMesh rebuild
|
||||
if (!isPlayMode && editorOptions.General.AutoRebuildNavMesh)
|
||||
{
|
||||
if (terrain.Scene && (terrain.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation)
|
||||
if (terrain.Scene && terrain.HasStaticFlag(StaticFlags.Navigation))
|
||||
{
|
||||
Navigation.BuildNavMesh(terrain.Scene, brushBounds, editorOptions.General.AutoRebuildNavMeshTimeoutMs);
|
||||
}
|
||||
|
||||
25
Source/Editor/Utilities/EditorScene.cpp
Normal file
25
Source/Editor/Utilities/EditorScene.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "EditorScene.h"
|
||||
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
|
||||
EditorScene::EditorScene(const SpawnParams& params)
|
||||
: Scene(params)
|
||||
{
|
||||
// Mock editor preview scene to be in gameplay
|
||||
EditorScene::PostSpawn();
|
||||
SceneBeginData beginData;
|
||||
EditorScene::BeginPlay(&beginData);
|
||||
beginData.OnDone();
|
||||
}
|
||||
|
||||
void EditorScene::Update()
|
||||
{
|
||||
for (auto& e : Ticking.Update.Ticks)
|
||||
e.Call();
|
||||
for (auto& e : Ticking.LateUpdate.Ticks)
|
||||
e.Call();
|
||||
for (auto& e : Ticking.FixedUpdate.Ticks)
|
||||
e.Call();
|
||||
}
|
||||
19
Source/Editor/Utilities/EditorScene.h
Normal file
19
Source/Editor/Utilities/EditorScene.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
|
||||
/// <summary>
|
||||
/// Scene for editor previews with support of object drawing and updating in separation of global scenes collection. It mocks the gameplay to preview scene objects.
|
||||
/// </summary>
|
||||
API_CLASS() class EditorScene final : public Scene
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(EditorScene);
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Updates the gameplay.
|
||||
/// </summary>
|
||||
API_FUNCTION() void Update();
|
||||
};
|
||||
@@ -57,30 +57,46 @@ public:
|
||||
|
||||
ViewportIconsRendererService ViewportIconsRendererServiceInstance;
|
||||
|
||||
void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Scene* scene)
|
||||
namespace
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
if ((view.Flags & ViewFlags::EditorSprites) == 0 || QuadModel == nullptr || !QuadModel->IsLoaded())
|
||||
return;
|
||||
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
auto& icons = scene->GetSceneRendering()->ViewportIcons;
|
||||
Matrix m1, m2, world;
|
||||
Mesh::DrawInfo draw;
|
||||
draw.Lightmap = nullptr;
|
||||
draw.LightmapUVs = nullptr;
|
||||
draw.Flags = StaticFlags::Transform;
|
||||
draw.DrawModes = DrawPass::Forward;
|
||||
draw.PerInstanceRandom = 0;
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
|
||||
for (Actor* icon : icons)
|
||||
void DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw)
|
||||
{
|
||||
BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS);
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
auto& icons = scene->GetSceneRendering()->ViewportIcons;
|
||||
Matrix m1, m2, world;
|
||||
for (Actor* icon : icons)
|
||||
{
|
||||
BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS);
|
||||
IconTypes iconType;
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType))
|
||||
{
|
||||
// Create world matrix
|
||||
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
|
||||
Matrix::RotationY(PI, world);
|
||||
Matrix::Multiply(m2, world, m1);
|
||||
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);
|
||||
Matrix::Multiply(m1, m2, world);
|
||||
|
||||
// Draw icon
|
||||
GeometryDrawStateData drawState;
|
||||
draw.DrawState = &drawState;
|
||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
||||
draw.World = &world;
|
||||
draw.Bounds = sphere;
|
||||
QuadModel->Draw(renderContext, draw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
Matrix m1, m2, world;
|
||||
BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS);
|
||||
IconTypes iconType;
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType))
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
|
||||
{
|
||||
// Create world matrix
|
||||
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
|
||||
@@ -97,6 +113,35 @@ void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Scene* scene
|
||||
draw.Bounds = sphere;
|
||||
QuadModel->Draw(renderContext, draw);
|
||||
}
|
||||
|
||||
for (auto child : actor->Children)
|
||||
DrawIcons(renderContext, child, draw);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
if ((view.Flags & ViewFlags::EditorSprites) == 0 || QuadModel == nullptr || !QuadModel->IsLoaded())
|
||||
return;
|
||||
|
||||
Mesh::DrawInfo draw;
|
||||
draw.Lightmap = nullptr;
|
||||
draw.LightmapUVs = nullptr;
|
||||
draw.Flags = StaticFlags::Transform;
|
||||
draw.DrawModes = DrawPass::Forward;
|
||||
draw.PerInstanceRandom = 0;
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
|
||||
if (const auto scene = SceneObject::Cast<Scene>(actor))
|
||||
{
|
||||
::DrawIcons(renderContext, scene, draw);
|
||||
}
|
||||
else
|
||||
{
|
||||
::DrawIcons(renderContext, actor, draw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
struct RenderContext;
|
||||
class SceneRenderTask;
|
||||
class Scene;
|
||||
class Actor;
|
||||
|
||||
/// <summary>
|
||||
/// Editor viewports icons rendering service.
|
||||
@@ -17,9 +17,9 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(ViewportIconsRenderer);
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Draws the icons for the actors in the given scene.
|
||||
/// Draws the icons for the actors in the given scene (or actor tree).
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="scene">The scene.</param>
|
||||
API_FUNCTION() static void DrawIcons(API_PARAM(Ref) RenderContext& renderContext, Scene* scene);
|
||||
/// <param name="actor">The actor (use scene for faster rendering).</param>
|
||||
API_FUNCTION() static void DrawIcons(API_PARAM(Ref) RenderContext& renderContext, Actor* actor);
|
||||
};
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.PostProcessEffect" />
|
||||
[HideInEditor]
|
||||
public sealed class EditorSpritesRenderer : PostProcessEffect
|
||||
public class EditorSpritesRenderer : PostProcessEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// The rendering task.
|
||||
@@ -100,11 +100,7 @@ namespace FlaxEditor.Viewport
|
||||
context.SetRenderTarget(depthBufferHandle, input.View());
|
||||
|
||||
// Collect draw calls
|
||||
for (int i = 0; i < Level.ScenesCount; i++)
|
||||
{
|
||||
var scene = Level.GetScene(i);
|
||||
ViewportIconsRenderer.DrawIcons(ref renderContext, scene);
|
||||
}
|
||||
Draw(ref renderContext);
|
||||
|
||||
// Sort draw calls
|
||||
renderList.SortDrawCalls(ref renderContext, true, DrawCallsListType.Forward);
|
||||
@@ -118,6 +114,18 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
Profiler.EndEventGPU();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the icons.
|
||||
/// </summary>
|
||||
protected virtual void Draw(ref RenderContext renderContext)
|
||||
{
|
||||
for (int i = 0; i < Level.ScenesCount; i++)
|
||||
{
|
||||
var scene = Level.GetScene(i);
|
||||
ViewportIconsRenderer.DrawIcons(ref renderContext, scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||
@@ -175,10 +183,10 @@ namespace FlaxEditor.Viewport
|
||||
_editor = editor;
|
||||
|
||||
// Prepare rendering task
|
||||
Task.ActorsSource = ActorsSources.ScenesAndCustomActors;
|
||||
Task.ActorsSource = ActorsSources.Scenes;
|
||||
Task.ViewFlags = ViewFlags.DefaultEditor;
|
||||
Task.Begin += RenderTaskOnBegin;
|
||||
Task.CollectDrawCalls += RenderTaskOnCollectDrawCalls;
|
||||
Task.Begin += OnBegin;
|
||||
Task.CollectDrawCalls += OnCollectDrawCalls;
|
||||
Task.PostRender += OnPostRender;
|
||||
|
||||
// Render task after the main game task so streaming and render state data will use main game task instead of editor preview
|
||||
@@ -396,7 +404,7 @@ namespace FlaxEditor.Viewport
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
}
|
||||
|
||||
private void RenderTaskOnBegin(RenderTask task, GPUContext context)
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
_debugDrawData.Clear();
|
||||
|
||||
@@ -412,7 +420,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderTaskOnCollectDrawCalls(RenderContext renderContext)
|
||||
private void OnCollectDrawCalls(RenderContext renderContext)
|
||||
{
|
||||
if (_previewStaticModel)
|
||||
{
|
||||
@@ -441,7 +449,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
||||
{
|
||||
DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount);
|
||||
DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,18 @@ namespace FlaxEditor.Viewport
|
||||
/// <seealso cref="IGizmoOwner" />
|
||||
public class PrefabWindowViewport : PrefabPreview, IEditorPrimitivesOwner
|
||||
{
|
||||
private sealed class PrefabSpritesRenderer : MainEditorGizmoViewport.EditorSpritesRenderer
|
||||
{
|
||||
public PrefabWindowViewport Viewport;
|
||||
|
||||
public override bool CanRender => (Task.View.Flags & ViewFlags.EditorSprites) == ViewFlags.EditorSprites && Enabled;
|
||||
|
||||
protected override void Draw(ref RenderContext renderContext)
|
||||
{
|
||||
ViewportIconsRenderer.DrawIcons(ref renderContext, Viewport.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly PrefabWindow _window;
|
||||
private UpdateDelegate _update;
|
||||
|
||||
@@ -37,6 +49,9 @@ namespace FlaxEditor.Viewport
|
||||
private ViewportWidgetButton _rotateSnapping;
|
||||
private ViewportWidgetButton _scaleSnapping;
|
||||
|
||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||
private IntPtr _debugDrawContext;
|
||||
private PrefabSpritesRenderer _spritesRenderer;
|
||||
private readonly DragAssets _dragAssets = new DragAssets(ValidateDragItem);
|
||||
private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType);
|
||||
private readonly DragHandlers _dragHandlers = new DragHandlers();
|
||||
@@ -56,6 +71,11 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public EditorPrimitives EditorPrimitives;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether draw <see cref="DebugDraw"/> shapes.
|
||||
/// </summary>
|
||||
public bool DrawDebugDraw = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PrefabWindowViewport"/> class.
|
||||
/// </summary>
|
||||
@@ -67,10 +87,13 @@ namespace FlaxEditor.Viewport
|
||||
_window.SelectionChanged += OnSelectionChanged;
|
||||
Undo = window.Undo;
|
||||
ViewportCamera = new FPSCamera();
|
||||
_debugDrawContext = DebugDraw.AllocateContext();
|
||||
|
||||
// Prepare rendering task
|
||||
Task.ActorsSource = ActorsSources.CustomActors;
|
||||
Task.ViewFlags = ViewFlags.DefaultEditor & ~ViewFlags.EditorSprites;
|
||||
Task.ViewFlags = ViewFlags.DefaultEditor;
|
||||
Task.Begin += OnBegin;
|
||||
Task.CollectDrawCalls += OnCollectDrawCalls;
|
||||
Task.PostRender += OnPostRender;
|
||||
|
||||
// Create post effects
|
||||
@@ -80,6 +103,10 @@ namespace FlaxEditor.Viewport
|
||||
EditorPrimitives = FlaxEngine.Object.New<EditorPrimitives>();
|
||||
EditorPrimitives.Viewport = this;
|
||||
Task.CustomPostFx.Add(EditorPrimitives);
|
||||
_spritesRenderer = FlaxEngine.Object.New<PrefabSpritesRenderer>();
|
||||
_spritesRenderer.Task = Task;
|
||||
_spritesRenderer.Viewport = this;
|
||||
Task.CustomPostFx.Add(_spritesRenderer);
|
||||
|
||||
// Add transformation gizmo
|
||||
TransformGizmo = new TransformGizmo(this);
|
||||
@@ -226,6 +253,27 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
{
|
||||
_debugDrawData.Clear();
|
||||
|
||||
// Collect selected objects debug shapes and visuals
|
||||
var selectedParents = TransformGizmo.SelectedParents;
|
||||
if (selectedParents.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < selectedParents.Count; i++)
|
||||
{
|
||||
if (selectedParents[i].IsActiveInHierarchy)
|
||||
selectedParents[i].OnDebugDraw(_debugDrawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollectDrawCalls(RenderContext renderContext)
|
||||
{
|
||||
_debugDrawData.OnDraw(ref renderContext);
|
||||
}
|
||||
|
||||
private void OnPostRender(GPUContext context, RenderContext renderContext)
|
||||
{
|
||||
if (renderContext.View.Mode != ViewMode.Default)
|
||||
@@ -234,7 +282,30 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Render editor primitives, gizmo and debug shapes in debug view modes
|
||||
// Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers
|
||||
EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output);
|
||||
if (EditorPrimitives && EditorPrimitives.CanRender)
|
||||
{
|
||||
EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output);
|
||||
}
|
||||
|
||||
// Render editor sprites
|
||||
if (_spritesRenderer && _spritesRenderer.CanRender)
|
||||
{
|
||||
_spritesRenderer.Render(context, ref renderContext, task.Output, task.Output);
|
||||
}
|
||||
|
||||
// Render selection outline
|
||||
if (SelectionOutline && SelectionOutline.CanRender)
|
||||
{
|
||||
// Use temporary intermediate buffer
|
||||
var desc = task.Output.Description;
|
||||
var temp = RenderTargetPool.Get(ref desc);
|
||||
SelectionOutline.Render(context, ref renderContext, task.Output, temp);
|
||||
|
||||
// Copy the results back to the output
|
||||
context.CopyTexture(task.Output, 0, 0, 0, 0, temp, 0);
|
||||
|
||||
RenderTargetPool.Release(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,8 +890,14 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (_debugDrawContext != IntPtr.Zero)
|
||||
{
|
||||
DebugDraw.FreeContext(_debugDrawContext);
|
||||
_debugDrawContext = IntPtr.Zero;
|
||||
}
|
||||
FlaxEngine.Object.Destroy(ref SelectionOutline);
|
||||
FlaxEngine.Object.Destroy(ref EditorPrimitives);
|
||||
FlaxEngine.Object.Destroy(ref _spritesRenderer);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
@@ -828,6 +905,21 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth)
|
||||
{
|
||||
// Draw selected objects debug shapes and visuals
|
||||
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
|
||||
{
|
||||
DebugDraw.SetContext(_debugDrawContext);
|
||||
DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Engine.FramesPerSecond);
|
||||
unsafe
|
||||
{
|
||||
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
||||
{
|
||||
DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, false);
|
||||
}
|
||||
}
|
||||
DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true);
|
||||
DebugDraw.SetContext(IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using FlaxEditor.Content;
|
||||
using FlaxEditor.Content.Import;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -114,6 +115,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
private readonly SplitPanel _split;
|
||||
private readonly AudioClipPreview _preview;
|
||||
private readonly CustomEditorPresenter _propertiesEditor;
|
||||
private readonly ToolStripButton _playButton;
|
||||
private readonly ToolStripButton _pauseButton;
|
||||
private EditorScene _previewScene;
|
||||
private AudioSource _previewSource;
|
||||
|
||||
private readonly PropertiesProxy _properties;
|
||||
private bool _isWaitingForLoad;
|
||||
@@ -131,7 +136,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// AudioClip preview
|
||||
// Preview
|
||||
_preview = new AudioClipPreview
|
||||
{
|
||||
DrawMode = AudioClipPreview.DrawModes.Fill,
|
||||
@@ -140,7 +145,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
Parent = _split.Panel1
|
||||
};
|
||||
|
||||
// AudioClip properties editor
|
||||
// Properties editor
|
||||
_propertiesEditor = new CustomEditorPresenter(null);
|
||||
_propertiesEditor.Panel.Parent = _split.Panel2;
|
||||
_properties = new PropertiesProxy();
|
||||
@@ -149,14 +154,47 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Toolstrip
|
||||
_toolstrip.AddButton(Editor.Icons.Import32, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport");
|
||||
_toolstrip.AddSeparator();
|
||||
_playButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Play32, OnPlay).LinkTooltip("Play/stop audio");
|
||||
_pauseButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Pause32, OnPause).LinkTooltip("Pause audio");
|
||||
_toolstrip.AddSeparator();
|
||||
_toolstrip.AddButton(editor.Icons.Docs32, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/audio/audio-clip.html")).LinkTooltip("See documentation to learn more");
|
||||
}
|
||||
|
||||
private void OnPlay()
|
||||
{
|
||||
if (!_previewScene)
|
||||
{
|
||||
_previewScene = new EditorScene();
|
||||
}
|
||||
if (!_previewSource)
|
||||
{
|
||||
_previewSource = new AudioSource
|
||||
{
|
||||
Parent = _previewScene,
|
||||
Clip = _asset,
|
||||
};
|
||||
}
|
||||
if (_previewSource.State == AudioSource.States.Playing)
|
||||
_previewSource.Stop();
|
||||
else
|
||||
_previewSource.Play();
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
private void OnPause()
|
||||
{
|
||||
if (_previewSource)
|
||||
_previewSource.Pause();
|
||||
UpdateToolstrip();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UnlinkItem()
|
||||
{
|
||||
_properties.OnClean();
|
||||
_preview.Asset = null;
|
||||
if (_previewSource)
|
||||
_previewSource.Clip = null;
|
||||
_isWaitingForLoad = false;
|
||||
|
||||
base.UnlinkItem();
|
||||
@@ -187,6 +225,29 @@ namespace FlaxEditor.Windows.Assets
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (_previewSource)
|
||||
{
|
||||
_previewSource.Stop();
|
||||
Object.Destroy(_previewSource);
|
||||
_previewSource = null;
|
||||
}
|
||||
Object.Destroy(ref _previewScene);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateToolstrip()
|
||||
{
|
||||
base.UpdateToolstrip();
|
||||
|
||||
_playButton.Icon = _previewSource && _previewSource.State == AudioSource.States.Playing ? Editor.Icons.Stop32 : Editor.Icons.Play32;
|
||||
_pauseButton.Enabled = _previewSource && _previewSource.State == AudioSource.States.Playing;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
@@ -195,16 +256,21 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Check if need to load
|
||||
if (_isWaitingForLoad && _asset.IsLoaded)
|
||||
{
|
||||
// Clear flag
|
||||
_isWaitingForLoad = false;
|
||||
|
||||
// Init properties and parameters proxy
|
||||
_properties.OnLoad(this);
|
||||
_propertiesEditor.BuildLayout();
|
||||
if (_previewSource)
|
||||
_previewSource.Stop();
|
||||
|
||||
// Setup
|
||||
ClearEditedFlag();
|
||||
}
|
||||
|
||||
// Tick scene
|
||||
if (_previewScene)
|
||||
_previewScene.Update();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -10,6 +10,7 @@ using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Viewport;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
@@ -22,7 +23,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
private readonly SplitPanel _split1;
|
||||
private readonly SplitPanel _split2;
|
||||
private TextBox _searchBox;
|
||||
private readonly TextBox _searchBox;
|
||||
private readonly PrefabTree _tree;
|
||||
private readonly PrefabWindowViewport _viewport;
|
||||
private readonly CustomEditorPresenter _propertiesEditor;
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace FlaxEditor.Windows
|
||||
: base(editor, true, ScrollBars.None)
|
||||
{
|
||||
Title = "Content";
|
||||
Icon = editor.Icons.Folder64;
|
||||
|
||||
// Content database events
|
||||
editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace FlaxEditor.Windows
|
||||
/// <summary>
|
||||
/// The default height of the entries.
|
||||
/// </summary>
|
||||
public const float DefaultHeight = 48.0f;
|
||||
public const float DefaultHeight = 32.0f;
|
||||
|
||||
private DebugLogWindow _window;
|
||||
public LogGroup Group;
|
||||
@@ -128,10 +128,11 @@ namespace FlaxEditor.Windows
|
||||
Render2D.FillRectangle(clientRect, style.Background * 0.9f);
|
||||
|
||||
// Icon
|
||||
Render2D.DrawSprite(Icon, new Rectangle(5, 8, 32, 32), style.Foreground);
|
||||
var iconColor = Group == LogGroup.Error ? Color.Red : (Group == LogGroup.Warning ? Color.Yellow : style.Foreground);
|
||||
Render2D.DrawSprite(Icon, new Rectangle(5, 0, 32, 32), iconColor);
|
||||
|
||||
// Title
|
||||
var textRect = new Rectangle(38, 6, clientRect.Width - 40, clientRect.Height - 10);
|
||||
var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10);
|
||||
Render2D.PushClip(ref clientRect);
|
||||
Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
|
||||
Render2D.PopClip();
|
||||
@@ -267,6 +268,8 @@ namespace FlaxEditor.Windows
|
||||
private readonly ToolStripButton _pauseOnErrorButton;
|
||||
private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3];
|
||||
|
||||
private LogType _iconType = LogType.Info;
|
||||
|
||||
internal SpriteHandle IconInfo;
|
||||
internal SpriteHandle IconWarning;
|
||||
internal SpriteHandle IconError;
|
||||
@@ -279,6 +282,7 @@ namespace FlaxEditor.Windows
|
||||
: base(editor, true, ScrollBars.None)
|
||||
{
|
||||
Title = "Debug Log";
|
||||
Icon = IconInfo;
|
||||
OnEditorOptionsChanged(Editor.Options.Options);
|
||||
|
||||
// Toolstrip
|
||||
@@ -333,6 +337,7 @@ namespace FlaxEditor.Windows
|
||||
Editor.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
Debug.Logger.LogHandler.SendLog += LogHandlerOnSendLog;
|
||||
Debug.Logger.LogHandler.SendExceptionLog += LogHandlerOnSendExceptionLog;
|
||||
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
@@ -381,6 +386,18 @@ namespace FlaxEditor.Windows
|
||||
_pendingEntries.Add(newEntry);
|
||||
}
|
||||
|
||||
if (newEntry.Group == LogGroup.Warning && _iconType < LogType.Warning)
|
||||
{
|
||||
_iconType = LogType.Warning;
|
||||
UpdateIcon();
|
||||
}
|
||||
|
||||
if (newEntry.Group == LogGroup.Error && _iconType < LogType.Error)
|
||||
{
|
||||
_iconType = LogType.Error;
|
||||
UpdateIcon();
|
||||
}
|
||||
|
||||
// Pause on Error (we should do it as fast as possible)
|
||||
if (newEntry.Group == LogGroup.Error && _pauseOnErrorButton.Checked && Editor.StateMachine.CurrentState == Editor.StateMachine.PlayingState)
|
||||
{
|
||||
@@ -426,6 +443,12 @@ namespace FlaxEditor.Windows
|
||||
UpdateCount((int)LogGroup.Error, " Error");
|
||||
UpdateCount((int)LogGroup.Warning, " Warning");
|
||||
UpdateCount((int)LogGroup.Info, " Message");
|
||||
|
||||
if (_logCountPerGroup[(int)LogGroup.Error] == 0)
|
||||
{
|
||||
_iconType = _logCountPerGroup[(int)LogGroup.Warning] == 0 ? LogType.Info : LogType.Warning;
|
||||
UpdateIcon();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCount(int group, string msg)
|
||||
@@ -435,6 +458,22 @@ namespace FlaxEditor.Windows
|
||||
_groupButtons[group].Text = _logCountPerGroup[group] + msg;
|
||||
}
|
||||
|
||||
private void UpdateIcon()
|
||||
{
|
||||
if (_iconType == LogType.Warning)
|
||||
{
|
||||
Icon = IconWarning;
|
||||
}
|
||||
else if (_iconType == LogType.Error)
|
||||
{
|
||||
Icon = IconError;
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon = IconInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private void LogHandlerOnSendLog(LogType level, string msg, Object o, string stackTrace)
|
||||
{
|
||||
var desc = new LogEntryDescription
|
||||
@@ -566,6 +605,14 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnStartContainsFocus()
|
||||
{
|
||||
_iconType = LogType.Info;
|
||||
UpdateIcon();
|
||||
base.OnStartContainsFocus();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -210,10 +210,11 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (Editor.Undo.Enabled)
|
||||
{
|
||||
bool navigationDirty = (_pilotActor.StaticFlags & StaticFlags.Navigation) == StaticFlags.Navigation;
|
||||
ActorNode node = Editor.Scene.GetActorNode(_pilotActor);
|
||||
bool navigationDirty = node.AffectsNavigationWithChildren;
|
||||
var action = new TransformObjectsAction
|
||||
(
|
||||
new List<SceneGraphNode> { Editor.Scene.GetActorNode(_pilotActor) },
|
||||
new List<SceneGraphNode> { node },
|
||||
new List<Transform> { _pilotStart },
|
||||
ref _pilotBounds,
|
||||
navigationDirty
|
||||
|
||||
@@ -110,6 +110,7 @@ namespace FlaxEditor.Windows
|
||||
new KeyValuePair<string, Type>("UI Control", typeof(UIControl)),
|
||||
new KeyValuePair<string, Type>("UI Canvas", typeof(UICanvas)),
|
||||
new KeyValuePair<string, Type>("Text Render", typeof(TextRender)),
|
||||
new KeyValuePair<string, Type>("Sprite Render", typeof(SpriteRender)),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEngine;
|
||||
@@ -58,6 +57,30 @@ namespace FlaxEditor.Windows
|
||||
b = contextMenu.AddButton("Duplicate", Editor.SceneEditing.Duplicate);
|
||||
b.Enabled = hasSthSelected;
|
||||
|
||||
if (Editor.SceneEditing.SelectionCount == 1)
|
||||
{
|
||||
var convertMenu = contextMenu.AddChildMenu("Convert");
|
||||
var convertActorCm = convertMenu.ContextMenu;
|
||||
for (int i = 0; i < SpawnActorsGroups.Length; i++)
|
||||
{
|
||||
var group = SpawnActorsGroups[i];
|
||||
|
||||
if (group.Types.Length == 1)
|
||||
{
|
||||
var type = group.Types[0].Value;
|
||||
convertActorCm.AddButton(group.Types[0].Key, () => Editor.SceneEditing.Convert(type));
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupCm = convertActorCm.AddChildMenu(group.Name).ContextMenu;
|
||||
for (int j = 0; j < group.Types.Length; j++)
|
||||
{
|
||||
var type = group.Types[j].Value;
|
||||
groupCm.AddButton(group.Types[j].Key, () => Editor.SceneEditing.Convert(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
b = contextMenu.AddButton("Delete", Editor.SceneEditing.Delete);
|
||||
b.Enabled = hasSthSelected;
|
||||
|
||||
|
||||
@@ -173,6 +173,7 @@ namespace FlaxEditor.Windows
|
||||
groupGui.AddChild(CreateActorItem("UI Control", typeof(UIControl)));
|
||||
groupGui.AddChild(CreateActorItem("UI Canvas", typeof(UICanvas)));
|
||||
groupGui.AddChild(CreateActorItem("Text Render", typeof(TextRender)));
|
||||
groupGui.AddChild(CreateActorItem("Sprite Render", typeof(SpriteRender)));
|
||||
|
||||
actorGroups.SelectedTabIndex = 1;
|
||||
}
|
||||
|
||||
@@ -213,6 +213,11 @@ void SceneAnimationPlayer::Tick(float dt)
|
||||
_lastTime = _time = time;
|
||||
}
|
||||
|
||||
void SceneAnimationPlayer::MapObject(const Guid& from, const Guid& to)
|
||||
{
|
||||
_objectsMapping[from] = to;
|
||||
}
|
||||
|
||||
void SceneAnimationPlayer::Restore(SceneAnimation* anim, int32 stateIndexOffset)
|
||||
{
|
||||
// Restore all tracks
|
||||
@@ -268,7 +273,9 @@ void SceneAnimationPlayer::Restore(SceneAnimation* anim, int32 stateIndexOffset)
|
||||
case SceneAnimation::Track::Types::ObjectReferenceProperty:
|
||||
{
|
||||
value = &_restoreData[state.RestoreStateIndex];
|
||||
auto obj = Scripting::FindObject<ScriptingObject>(*(Guid*)value);
|
||||
Guid id = *(Guid*)value;
|
||||
_objectsMapping.TryGet(id, id);
|
||||
auto obj = Scripting::FindObject<ScriptingObject>(id);
|
||||
value = obj ? obj->GetOrCreateManagedInstance() : nullptr;
|
||||
break;
|
||||
}
|
||||
@@ -358,7 +365,9 @@ bool SceneAnimationPlayer::TickPropertyTrack(int32 trackIndex, int32 stateIndexO
|
||||
void* value = (void*)((byte*)trackDataKeyframes->Keyframes + keyframeSize * (leftKey) + sizeof(float));
|
||||
if (track.Type == SceneAnimation::Track::Types::ObjectReferenceProperty)
|
||||
{
|
||||
auto obj = Scripting::FindObject<ScriptingObject>(*(Guid*)value);
|
||||
Guid id = *(Guid*)value;
|
||||
_objectsMapping.TryGet(id, id);
|
||||
auto obj = Scripting::FindObject<ScriptingObject>(id);
|
||||
value = obj ? obj->GetOrCreateManagedInstance() : nullptr;
|
||||
*(void**)target = value;
|
||||
}
|
||||
@@ -680,10 +689,12 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
|
||||
|
||||
// Find actor
|
||||
const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>();
|
||||
Guid id = trackData->ID;
|
||||
_objectsMapping.TryGet(id, id);
|
||||
state.Object = Scripting::FindObject<Actor>(trackData->ID);
|
||||
if (!state.Object)
|
||||
{
|
||||
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", trackData->ID, track.Name, anim->ToString(), TEXT("actor"));
|
||||
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("actor"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -708,10 +719,12 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
|
||||
break;
|
||||
|
||||
// Find script
|
||||
state.Object = Scripting::FindObject<Script>(trackData->ID);
|
||||
Guid id = trackData->ID;
|
||||
_objectsMapping.TryGet(id, id);
|
||||
state.Object = Scripting::FindObject<Script>(id);
|
||||
if (!state.Object)
|
||||
{
|
||||
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", trackData->ID, track.Name, anim->ToString(), TEXT("script"));
|
||||
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("script"));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -940,10 +953,12 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
|
||||
state.ManagedObject = nullptr;
|
||||
|
||||
// Find actor
|
||||
state.Object = Scripting::FindObject<Camera>(trackData->ID);
|
||||
Guid id = trackData->ID;
|
||||
_objectsMapping.TryGet(id, id);
|
||||
state.Object = Scripting::FindObject<Camera>(id);
|
||||
if (!state.Object)
|
||||
{
|
||||
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", trackData->ID, track.Name, anim->ToString(), TEXT("actor"));
|
||||
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("actor"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ private:
|
||||
Array<byte> _restoreData;
|
||||
Camera* _cameraCutCam = nullptr;
|
||||
bool _isUsingCameraCuts = false;
|
||||
Dictionary<Guid, Guid> _objectsMapping;
|
||||
|
||||
// PostFx settings to use
|
||||
struct
|
||||
@@ -189,6 +190,13 @@ public:
|
||||
/// <param name="dt">The update delta time (in seconds). It does not get scaled by player Speed parameter.</param>
|
||||
API_FUNCTION() void Tick(float dt);
|
||||
|
||||
/// <summary>
|
||||
/// Adds an object mapping. The object `from` represented by it's unique ID will be redirected to the specified `to`. Can be used to reuse the same animation for different objects.
|
||||
/// </summary>
|
||||
/// <param name="from">The source object from the scene animation asset to replace.</param>
|
||||
/// <param name="to">The destination object to animate.</param>
|
||||
API_FUNCTION() void MapObject(const Guid& from, const Guid& to);
|
||||
|
||||
private:
|
||||
|
||||
void Restore(SceneAnimation* anim, int32 stateIndexOffset);
|
||||
|
||||
@@ -342,7 +342,7 @@ void AudioSource::Update()
|
||||
{
|
||||
// Update the velocity
|
||||
const Vector3 pos = GetPosition();
|
||||
const float dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
||||
const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), ZeroTolerance);
|
||||
const auto prevVelocity = _velocity;
|
||||
_velocity = (pos - _prevPos) / dt;
|
||||
_prevPos = pos;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#endif
|
||||
#if ENABLE_ASSETS_DISCOVERY
|
||||
#include "Engine/Core/Collections/HashSet.h"
|
||||
|
||||
@@ -200,3 +200,38 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
|
||||
DESERIALIZE(XboxScarlettPlatform);
|
||||
DESERIALIZE(AndroidPlatform);
|
||||
}
|
||||
|
||||
void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
{
|
||||
const auto tags = stream.FindMember("Tags");
|
||||
if (tags != stream.MemberEnd() && tags->value.IsArray())
|
||||
{
|
||||
auto& tagsArray = tags->value;
|
||||
Tags.Clear();
|
||||
Tags.EnsureCapacity(tagsArray.Size());
|
||||
for (uint32 i = 0; i < tagsArray.Size(); i++)
|
||||
{
|
||||
auto& v = tagsArray[i];
|
||||
if (v.IsString())
|
||||
Tags.Add(v.GetText());
|
||||
}
|
||||
}
|
||||
|
||||
const auto layers = stream.FindMember("Layers");
|
||||
if (layers != stream.MemberEnd() && layers->value.IsArray())
|
||||
{
|
||||
auto& layersArray = layers->value;
|
||||
for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
|
||||
{
|
||||
auto& v = layersArray[i];
|
||||
if (v.IsString())
|
||||
Layers[i] = v.GetText();
|
||||
else
|
||||
Layers[i].Clear();
|
||||
}
|
||||
for (uint32 i = layersArray.Size(); i < 32; i++)
|
||||
{
|
||||
Layers[i].Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
Source/Engine/Core/Config/LayersMask.h
Normal file
47
Source/Engine/Core/Config/LayersMask.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Engine/Serialization/SerializationFwd.h"
|
||||
|
||||
/// <summary>
|
||||
/// The objects layers selection mask (from layers and tags settings). Uses 1 bit per layer (up to 32 layers).
|
||||
/// </summary>
|
||||
API_STRUCT() struct FLAXENGINE_API LayersMask
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(LayersMask);
|
||||
|
||||
/// <summary>
|
||||
/// The layers selection mask.
|
||||
/// </summary>
|
||||
API_FIELD() uint32 Mask = MAX_uint32;
|
||||
|
||||
FORCE_INLINE bool HasLayer(int32 layerIndex) const
|
||||
{
|
||||
return (Mask & (1 << layerIndex)) != 0;
|
||||
}
|
||||
|
||||
bool HasLayer(const StringView& layerName) const;
|
||||
|
||||
bool operator==(const LayersMask& other) const;
|
||||
bool operator!=(const LayersMask& other) const;
|
||||
};
|
||||
|
||||
// @formatter:off
|
||||
namespace Serialization
|
||||
{
|
||||
inline bool ShouldSerialize(const LayersMask& v, const void* otherObj)
|
||||
{
|
||||
return !otherObj || v != *(LayersMask*)otherObj;
|
||||
}
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const LayersMask& v, const void* otherObj)
|
||||
{
|
||||
stream.Uint(v.Mask);
|
||||
}
|
||||
inline void Deserialize(ISerializable::DeserializeStream& stream, LayersMask& v, ISerializeModifier* modifier)
|
||||
{
|
||||
v.Mask = stream.GetUint();
|
||||
}
|
||||
}
|
||||
// @formatter:on
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Config/Settings.h"
|
||||
#include "Engine/Serialization/Json.h"
|
||||
|
||||
/// <summary>
|
||||
/// Layers and objects tags settings.
|
||||
@@ -14,12 +13,12 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings);
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The tags names.
|
||||
/// The tag names.
|
||||
/// </summary>
|
||||
Array<String> Tags;
|
||||
|
||||
/// <summary>
|
||||
/// The layers names.
|
||||
/// The layer names.
|
||||
/// </summary>
|
||||
String Layers[32];
|
||||
|
||||
@@ -31,44 +30,6 @@ public:
|
||||
static LayersAndTagsSettings* Get();
|
||||
|
||||
// [SettingsBase]
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
|
||||
{
|
||||
const auto tags = stream.FindMember("Tags");
|
||||
if (tags != stream.MemberEnd())
|
||||
{
|
||||
auto& tagsArray = tags->value;
|
||||
ASSERT(tagsArray.IsArray());
|
||||
Tags.EnsureCapacity(tagsArray.Size());
|
||||
|
||||
// Note: we cannot remove tags at runtime so this should deserialize them in additive mode
|
||||
// Tags are stored as tagIndex in actors so collection change would break the linkage
|
||||
|
||||
for (uint32 i = 0; i < tagsArray.Size(); i++)
|
||||
{
|
||||
auto& v = tagsArray[i];
|
||||
if (v.IsString())
|
||||
{
|
||||
const String tag = v.GetText();
|
||||
if (!Tags.Contains(tag))
|
||||
Tags.Add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto layers = stream.FindMember("Layers");
|
||||
if (layers != stream.MemberEnd())
|
||||
{
|
||||
auto& layersArray = layers->value;
|
||||
ASSERT(layersArray.IsArray());
|
||||
|
||||
for (uint32 i = 0; i < layersArray.Size() && i < 32; i++)
|
||||
{
|
||||
auto& v = layersArray[i];
|
||||
if (v.IsString())
|
||||
Layers[i] = v.GetText();
|
||||
else
|
||||
Layers[i].Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
void Apply() override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "BoundingSphere.h"
|
||||
#include "BoundingBox.h"
|
||||
#include "Matrix.h"
|
||||
#include "Ray.h"
|
||||
#include "../Types/String.h"
|
||||
|
||||
@@ -194,3 +195,9 @@ void BoundingSphere::Merge(const BoundingSphere& value1, const Vector3& value2,
|
||||
result.Center = value1.Center + vector * (max + min);
|
||||
result.Radius = max;
|
||||
}
|
||||
|
||||
void BoundingSphere::Transform(const BoundingSphere& sphere, const Matrix& matrix, BoundingSphere& result)
|
||||
{
|
||||
Vector3::Transform(sphere.Center, matrix, result.Center);
|
||||
result.Radius = sphere.Radius * matrix.GetScaleVector().GetAbsolute().MaxValue();
|
||||
}
|
||||
|
||||
@@ -88,10 +88,7 @@ namespace FlaxEngine
|
||||
/// Determines if there is an intersection between the current object and a <see cref="Ray" />.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test.</param>
|
||||
/// <param name="distance">
|
||||
/// When the method completes, contains the distance of the intersection,
|
||||
/// or 0 if there was no intersection.
|
||||
/// </param>
|
||||
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
|
||||
/// <returns>Whether the two objects intersected.</returns>
|
||||
public bool Intersects(ref Ray ray, out float distance)
|
||||
{
|
||||
@@ -102,10 +99,7 @@ namespace FlaxEngine
|
||||
/// Determines if there is an intersection between the current object and a <see cref="Ray" />.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test.</param>
|
||||
/// <param name="point">
|
||||
/// When the method completes, contains the point of intersection,
|
||||
/// or <see cref="Vector3.Zero" /> if there was no intersection.
|
||||
/// </param>
|
||||
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>
|
||||
/// <returns>Whether the two objects intersected.</returns>
|
||||
public bool Intersects(ref Ray ray, out Vector3 point)
|
||||
{
|
||||
@@ -224,11 +218,7 @@ namespace FlaxEngine
|
||||
/// <param name="count">The count of points to process to compute the bounding sphere.</param>
|
||||
/// <param name="result">When the method completes, contains the newly constructed bounding sphere.</param>
|
||||
/// <exception cref="System.ArgumentNullException">points</exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">
|
||||
/// start
|
||||
/// or
|
||||
/// count
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentOutOfRangeException">start or count</exception>
|
||||
public static void FromPoints(Vector3[] points, int start, int count, out BoundingSphere result)
|
||||
{
|
||||
if (points == null)
|
||||
@@ -244,30 +234,30 @@ namespace FlaxEngine
|
||||
|
||||
int upperEnd = start + count;
|
||||
|
||||
//Find the center of all points.
|
||||
// Find the center of all points
|
||||
Vector3 center = Vector3.Zero;
|
||||
for (int i = start; i < upperEnd; ++i)
|
||||
Vector3.Add(ref points[i], ref center, out center);
|
||||
|
||||
//This is the center of our sphere.
|
||||
// This is the center of our sphere
|
||||
center /= (float)count;
|
||||
|
||||
//Find the radius of the sphere
|
||||
var radius = 0f;
|
||||
for (int i = start; i < upperEnd; ++i)
|
||||
{
|
||||
//We are doing a relative distance comparison to find the maximum distance
|
||||
//from the center of our sphere.
|
||||
// We are doing a relative distance comparison to find the maximum distance from the center of our sphere
|
||||
float distance;
|
||||
Vector3.DistanceSquared(ref center, ref points[i], out float distance);
|
||||
|
||||
if (distance > radius)
|
||||
radius = distance;
|
||||
}
|
||||
|
||||
//Find the real distance from the DistanceSquared.
|
||||
// Find the real distance from the DistanceSquared
|
||||
radius = (float)Math.Sqrt(radius);
|
||||
|
||||
//Construct the sphere.
|
||||
// Construct the sphere
|
||||
result.Center = center;
|
||||
result.Radius = radius;
|
||||
}
|
||||
@@ -281,7 +271,6 @@ namespace FlaxEngine
|
||||
{
|
||||
if (points == null)
|
||||
throw new ArgumentNullException(nameof(points));
|
||||
|
||||
FromPoints(points, 0, points.Length, out result);
|
||||
}
|
||||
|
||||
@@ -292,7 +281,7 @@ namespace FlaxEngine
|
||||
/// <returns>The newly constructed bounding sphere.</returns>
|
||||
public static BoundingSphere FromPoints(Vector3[] points)
|
||||
{
|
||||
FromPoints(points, out BoundingSphere result);
|
||||
FromPoints(points, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -320,7 +309,7 @@ namespace FlaxEngine
|
||||
/// <returns>The newly constructed bounding sphere.</returns>
|
||||
public static BoundingSphere FromBox(BoundingBox box)
|
||||
{
|
||||
FromBox(ref box, out BoundingSphere result);
|
||||
FromBox(ref box, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -383,19 +372,40 @@ namespace FlaxEngine
|
||||
/// <returns>The newly constructed bounding sphere.</returns>
|
||||
public static BoundingSphere Merge(BoundingSphere value1, BoundingSphere value2)
|
||||
{
|
||||
Merge(ref value1, ref value2, out BoundingSphere result);
|
||||
Merge(ref value1, ref value2, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the bounding sphere using the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="sphere">The sphere.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <remarks>The result transformed sphere.</remarks>
|
||||
public static BoundingSphere Transform(BoundingSphere sphere, Matrix matrix)
|
||||
{
|
||||
Transform(ref sphere, ref matrix, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the bounding sphere using the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="sphere">The sphere.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <param name="result">The result transformed sphere.</param>
|
||||
public static void Transform(ref BoundingSphere sphere, ref Matrix matrix, out BoundingSphere result)
|
||||
{
|
||||
Vector3.Transform(ref sphere.Center, ref matrix, out result.Center);
|
||||
result.Radius = sphere.Radius * matrix.ScaleVector.Absolute.MaxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for equality between two objects.
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(BoundingSphere left, BoundingSphere right)
|
||||
{
|
||||
@@ -407,10 +417,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(BoundingSphere left, BoundingSphere right)
|
||||
{
|
||||
@@ -420,9 +427,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture,
|
||||
@@ -435,9 +440,7 @@ namespace FlaxEngine
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
if (format == null)
|
||||
@@ -453,9 +456,7 @@ namespace FlaxEngine
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <param name="formatProvider">The format provider.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public string ToString(IFormatProvider formatProvider)
|
||||
{
|
||||
return string.Format(formatProvider,
|
||||
@@ -469,9 +470,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="formatProvider">The format provider.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public string ToString(string format, IFormatProvider formatProvider)
|
||||
{
|
||||
if (format == null)
|
||||
@@ -486,9 +485,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
@@ -501,9 +498,7 @@ namespace FlaxEngine
|
||||
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ref BoundingSphere value)
|
||||
{
|
||||
@@ -514,9 +509,7 @@ namespace FlaxEngine
|
||||
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(BoundingSphere value)
|
||||
{
|
||||
@@ -527,14 +520,11 @@ namespace FlaxEngine
|
||||
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object value)
|
||||
{
|
||||
if (!(value is BoundingSphere))
|
||||
return false;
|
||||
|
||||
var strongValue = (BoundingSphere)value;
|
||||
return Equals(ref strongValue);
|
||||
}
|
||||
|
||||
@@ -218,6 +218,14 @@ public:
|
||||
/// <param name="value2">The point to merge.</param>
|
||||
/// <param name="result">When the method completes, contains the newly constructed bounding sphere.</param>
|
||||
static void Merge(const BoundingSphere& value1, const Vector3& value2, BoundingSphere& result);
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the bounding sphere using the specified matrix.
|
||||
/// </summary>
|
||||
/// <param name="sphere">The sphere.</param>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <param name="result">The result transformed sphere.</param>
|
||||
static void Transform(const BoundingSphere& sphere, const Matrix& matrix, BoundingSphere& result);
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -53,9 +53,7 @@ namespace FlaxEngine
|
||||
/// Creates an <see cref="OrientedBoundingBox" /> from a BoundingBox.
|
||||
/// </summary>
|
||||
/// <param name="bb">The BoundingBox to create from.</param>
|
||||
/// <remarks>
|
||||
/// Initially, the OBB is axis-aligned box, but it can be rotated and transformed later.
|
||||
/// </remarks>
|
||||
/// <remarks>Initially, the OBB is axis-aligned box, but it can be rotated and transformed later.</remarks>
|
||||
public OrientedBoundingBox(BoundingBox bb)
|
||||
{
|
||||
Vector3 center = bb.Minimum + (bb.Maximum - bb.Minimum) / 2f;
|
||||
@@ -79,9 +77,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="minimum">The minimum vertex of the bounding box.</param>
|
||||
/// <param name="maximum">The maximum vertex of the bounding box.</param>
|
||||
/// <remarks>
|
||||
/// Initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.
|
||||
/// </remarks>
|
||||
/// <remarks>Initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.</remarks>
|
||||
public OrientedBoundingBox(Vector3 minimum, Vector3 maximum)
|
||||
{
|
||||
Vector3 center = minimum + (maximum - minimum) / 2f;
|
||||
@@ -93,10 +89,7 @@ namespace FlaxEngine
|
||||
/// Creates an <see cref="OrientedBoundingBox" /> that fully contains the given points.
|
||||
/// </summary>
|
||||
/// <param name="points">The points that will be contained by the box.</param>
|
||||
/// <remarks>
|
||||
/// This method is not for computing the best tight-fitting OrientedBoundingBox.
|
||||
/// And initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.
|
||||
/// </remarks>
|
||||
/// <remarks>This method is not for computing the best tight-fitting OrientedBoundingBox. And initially, the OrientedBoundingBox is axis-aligned box, but it can be rotated and transformed later.</remarks>
|
||||
public OrientedBoundingBox(Vector3[] points)
|
||||
{
|
||||
if ((points == null) || (points.Length == 0))
|
||||
@@ -231,8 +224,7 @@ namespace FlaxEngine
|
||||
/// <param name="mat">The transformation matrix.</param>
|
||||
/// <remarks>
|
||||
/// While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which
|
||||
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection
|
||||
/// accuracy.
|
||||
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.
|
||||
/// </remarks>
|
||||
public void Transform(ref Matrix mat)
|
||||
{
|
||||
@@ -245,8 +237,7 @@ namespace FlaxEngine
|
||||
/// <param name="mat">The transformation matrix.</param>
|
||||
/// <remarks>
|
||||
/// While any kind of transformation can be applied, it is recommended to apply scaling using scale method instead, which
|
||||
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection
|
||||
/// accuracy.
|
||||
/// scales the Extents and keeps the Transformation matrix for rotation only, and that preserves collision detection accuracy.
|
||||
/// </remarks>
|
||||
public void Transform(Matrix mat)
|
||||
{
|
||||
@@ -304,21 +295,15 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// The size of the <see cref="OrientedBoundingBox" /> if no scaling is applied to the transformation matrix.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The property will return the actual size even if the scaling is applied using Scale method,
|
||||
/// but if the scaling is applied to transformation matrix, use GetSize Function instead.
|
||||
/// <remarks>The property will return the actual size even if the scaling is applied using Scale method, but if the scaling is applied to transformation matrix, use GetSize Function instead.
|
||||
/// </remarks>
|
||||
public Vector3 Size => Extents * 2;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to the
|
||||
/// transformation matrix.
|
||||
/// Returns the size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to the transformation matrix.
|
||||
/// </summary>
|
||||
/// <returns>The size of the consideration</returns>
|
||||
/// <remarks>
|
||||
/// This method is computationally expensive, so if no scale is applied to the transformation matrix
|
||||
/// use <see cref="OrientedBoundingBox.Size" /> property instead.
|
||||
/// </remarks>
|
||||
/// <remarks>This method is computationally expensive, so if no scale is applied to the transformation matrix use <see cref="OrientedBoundingBox.Size" /> property instead.</remarks>
|
||||
public Vector3 GetSize()
|
||||
{
|
||||
var xv = new Vector3(Extents.X * 2, 0, 0);
|
||||
@@ -332,8 +317,7 @@ namespace FlaxEngine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the square size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to
|
||||
/// the transformation matrix.
|
||||
/// Returns the square size of the <see cref="OrientedBoundingBox" /> taking into consideration the scaling applied to the transformation matrix.
|
||||
/// </summary>
|
||||
/// <returns>The size of the consideration</returns>
|
||||
public Vector3 GetSizeSquared()
|
||||
@@ -432,16 +416,9 @@ namespace FlaxEngine
|
||||
/// Determines whether a <see cref="OrientedBoundingBox" /> contains a <see cref="BoundingSphere" />.
|
||||
/// </summary>
|
||||
/// <param name="sphere">The sphere to test.</param>
|
||||
/// <param name="ignoreScale">
|
||||
/// Optimize the check operation by assuming that <see cref="OrientedBoundingBox" /> has no
|
||||
/// scaling applied
|
||||
/// </param>
|
||||
/// <param name="ignoreScale">Optimize the check operation by assuming that <see cref="OrientedBoundingBox" /> has no scaling applied.</param>
|
||||
/// <returns>The type of containment the two objects have.</returns>
|
||||
/// <remarks>
|
||||
/// This method is not designed for <see cref="OrientedBoundingBox" /> which has a non-uniform scaling applied to its
|
||||
/// transformation matrix.
|
||||
/// But any type of scaling applied using Scale method will keep this method accurate.
|
||||
/// </remarks>
|
||||
/// <remarks>This method is not designed for <see cref="OrientedBoundingBox" /> which has a non-uniform scaling applied to its transformation matrix. But any type of scaling applied using Scale method will keep this method accurate.</remarks>
|
||||
public ContainmentType Contains(BoundingSphere sphere, bool ignoreScale = false)
|
||||
{
|
||||
Matrix.Invert(ref Transformation, out Matrix invTrans);
|
||||
@@ -489,11 +466,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="obb">The OrientedBoundingBox to test.</param>
|
||||
/// <returns>The type of containment the two objects have.</returns>
|
||||
/// <remarks>
|
||||
/// For accuracy, The transformation matrix for both <see cref="OrientedBoundingBox" /> must not have any scaling applied
|
||||
/// to it.
|
||||
/// Anyway, scaling using Scale method will keep this method accurate.
|
||||
/// </remarks>
|
||||
/// <remarks>For accuracy, The transformation matrix for both <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
|
||||
public ContainmentType Contains(ref OrientedBoundingBox obb)
|
||||
{
|
||||
ContainmentType cornersCheck = Contains(obb.GetCorners());
|
||||
@@ -570,11 +543,7 @@ namespace FlaxEngine
|
||||
/// <param name="L1">The first point in the line.</param>
|
||||
/// <param name="L2">The second point in the line.</param>
|
||||
/// <returns>The type of containment the two objects have.</returns>
|
||||
/// <remarks>
|
||||
/// For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied
|
||||
/// to it.
|
||||
/// Anyway, scaling using Scale method will keep this method accurate.
|
||||
/// </remarks>
|
||||
/// <remarks>For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
|
||||
public ContainmentType ContainsLine(ref Vector3 L1, ref Vector3 L2)
|
||||
{
|
||||
ContainmentType cornersCheck = Contains(new[]
|
||||
@@ -627,11 +596,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="box">The BoundingBox to test.</param>
|
||||
/// <returns>The type of containment the two objects have.</returns>
|
||||
/// <remarks>
|
||||
/// For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied
|
||||
/// to it.
|
||||
/// Anyway, scaling using Scale method will keep this method accurate.
|
||||
/// </remarks>
|
||||
/// <remarks>For accuracy, The transformation matrix for the <see cref="OrientedBoundingBox" /> must not have any scaling applied to it. Anyway, scaling using Scale method will keep this method accurate.</remarks>
|
||||
public ContainmentType Contains(ref BoundingBox box)
|
||||
{
|
||||
ContainmentType cornersCheck = Contains(box.GetCorners());
|
||||
@@ -704,10 +669,7 @@ namespace FlaxEngine
|
||||
/// Determines whether there is an intersection between a <see cref="Ray" /> and a <see cref="OrientedBoundingBox" />.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test.</param>
|
||||
/// <param name="point">
|
||||
/// When the method completes, contains the point of intersection,
|
||||
/// or <see cref="Vector3.Zero" /> if there was no intersection.
|
||||
/// </param>
|
||||
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>
|
||||
/// <returns>Whether the two objects intersected.</returns>
|
||||
public bool Intersects(ref Ray ray, out Vector3 point)
|
||||
{
|
||||
@@ -718,11 +680,11 @@ namespace FlaxEngine
|
||||
Vector3.TransformNormal(ref ray.Direction, ref invTrans, out bRay.Direction);
|
||||
Vector3.TransformCoordinate(ref ray.Position, ref invTrans, out bRay.Position);
|
||||
|
||||
//Perform a regular ray to BoundingBox check
|
||||
// Perform a regular ray to BoundingBox check
|
||||
var bb = new BoundingBox(-Extents, Extents);
|
||||
bool intersects = CollisionsHelper.RayIntersectsBox(ref bRay, ref bb, out point);
|
||||
|
||||
//Put the result intersection back to world
|
||||
// Put the result intersection back to world
|
||||
if (intersects)
|
||||
Vector3.TransformCoordinate(ref point, ref Transformation, out point);
|
||||
|
||||
@@ -733,10 +695,7 @@ namespace FlaxEngine
|
||||
/// Determines whether there is an intersection between a <see cref="Ray" /> and a <see cref="OrientedBoundingBox" />.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test.</param>
|
||||
/// <param name="distance">
|
||||
/// When the method completes, contains the distance of intersection from the ray start,
|
||||
/// or 0 if there was no intersection.
|
||||
/// </param>
|
||||
/// <param name="distance">When the method completes, contains the distance of intersection from the ray start, or 0 if there was no intersection.</param>
|
||||
/// <returns>Whether the two objects intersected.</returns>
|
||||
public bool Intersects(ref Ray ray, out float distance)
|
||||
{
|
||||
@@ -788,16 +747,12 @@ namespace FlaxEngine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the matrix required to transfer any point from one <see cref="OrientedBoundingBox" /> local coordinates to
|
||||
/// another.
|
||||
/// Calculates the matrix required to transfer any point from one <see cref="OrientedBoundingBox" /> local coordinates to another.
|
||||
/// </summary>
|
||||
/// <param name="A">The source OrientedBoundingBox.</param>
|
||||
/// <param name="B">The target OrientedBoundingBox.</param>
|
||||
/// <param name="NoMatrixScaleApplied">
|
||||
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
|
||||
/// matrix of the OrientedBoundingBox.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
|
||||
/// <returns>The matrix.</returns>
|
||||
public static Matrix GetBoxToBoxMatrix(ref OrientedBoundingBox A, ref OrientedBoundingBox B, bool NoMatrixScaleApplied = false)
|
||||
{
|
||||
Matrix AtoB_Matrix;
|
||||
@@ -828,19 +783,12 @@ namespace FlaxEngine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge an OrientedBoundingBox B into another OrientedBoundingBox A, by expanding A to contain B and keeping A
|
||||
/// orientation.
|
||||
/// Merge an OrientedBoundingBox B into another OrientedBoundingBox A, by expanding A to contain B and keeping A orientation.
|
||||
/// </summary>
|
||||
/// <param name="A">The <see cref="OrientedBoundingBox" /> to merge into it.</param>
|
||||
/// <param name="B">The <see cref="OrientedBoundingBox" /> to be merged</param>
|
||||
/// <param name="NoMatrixScaleApplied">
|
||||
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
|
||||
/// matrix of the OrientedBoundingBox.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// Unlike merging axis aligned boxes, The operation is not interchangeable, because it keeps A orientation and merge B
|
||||
/// into it.
|
||||
/// </remarks>
|
||||
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
|
||||
/// <remarks>Unlike merging axis aligned boxes, The operation is not interchangeable, because it keeps A orientation and merge B into it.</remarks>
|
||||
public static void Merge(ref OrientedBoundingBox A, ref OrientedBoundingBox B, bool NoMatrixScaleApplied = false)
|
||||
{
|
||||
Matrix AtoB_Matrix = GetBoxToBoxMatrix(ref A, ref B, NoMatrixScaleApplied);
|
||||
@@ -869,10 +817,7 @@ namespace FlaxEngine
|
||||
/// Merge this OrientedBoundingBox into another OrientedBoundingBox, keeping the other OrientedBoundingBox orientation.
|
||||
/// </summary>
|
||||
/// <param name="OBB">The other <see cref="OrientedBoundingBox" /> to merge into.</param>
|
||||
/// <param name="NoMatrixScaleApplied">
|
||||
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
|
||||
/// matrix of the OrientedBoundingBox.
|
||||
/// </param>
|
||||
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
|
||||
public void MergeInto(ref OrientedBoundingBox OBB, bool NoMatrixScaleApplied = false)
|
||||
{
|
||||
Merge(ref OBB, ref this, NoMatrixScaleApplied);
|
||||
@@ -882,10 +827,7 @@ namespace FlaxEngine
|
||||
/// Merge another OrientedBoundingBox into this OrientedBoundingBox.
|
||||
/// </summary>
|
||||
/// <param name="OBB">The other <see cref="OrientedBoundingBox" /> to merge into this OrientedBoundingBox.</param>
|
||||
/// <param name="NoMatrixScaleApplied">
|
||||
/// If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation
|
||||
/// matrix of the OrientedBoundingBox.
|
||||
/// </param>
|
||||
/// <param name="NoMatrixScaleApplied">If true, the method will use a fast algorithm which is inapplicable if a scale is applied to the transformation matrix of the OrientedBoundingBox.</param>
|
||||
public void Add(ref OrientedBoundingBox OBB, bool NoMatrixScaleApplied = false)
|
||||
{
|
||||
Merge(ref this, ref OBB, NoMatrixScaleApplied);
|
||||
@@ -895,9 +837,7 @@ namespace FlaxEngine
|
||||
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ref OrientedBoundingBox value)
|
||||
{
|
||||
@@ -908,9 +848,7 @@ namespace FlaxEngine
|
||||
/// Determines whether the specified <see cref="Vector4" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Vector4" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if the specified <see cref="Vector4" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(OrientedBoundingBox value)
|
||||
{
|
||||
@@ -921,9 +859,7 @@ namespace FlaxEngine
|
||||
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="System.Object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object value)
|
||||
{
|
||||
if (!(value is OrientedBoundingBox))
|
||||
@@ -952,10 +888,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(OrientedBoundingBox left, OrientedBoundingBox right)
|
||||
{
|
||||
@@ -967,10 +900,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="left">The first value to compare.</param>
|
||||
/// <param name="right">The second value to compare.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(OrientedBoundingBox left, OrientedBoundingBox right)
|
||||
{
|
||||
@@ -980,9 +910,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Extents.GetHashCode() + Transformation.GetHashCode();
|
||||
@@ -991,9 +919,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, "Center: {0}, Extents: {1}", Center, Extents);
|
||||
@@ -1003,9 +929,7 @@ namespace FlaxEngine
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
if (format == null)
|
||||
@@ -1019,9 +943,7 @@ namespace FlaxEngine
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <param name="formatProvider">The format provider.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public string ToString(IFormatProvider formatProvider)
|
||||
{
|
||||
return string.Format(formatProvider, "Center: {0}, Extents: {1}", Center.ToString(), Extents.ToString());
|
||||
@@ -1032,9 +954,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="formatProvider">The format provider.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
|
||||
public string ToString(string format, IFormatProvider formatProvider)
|
||||
{
|
||||
if (format == null)
|
||||
|
||||
@@ -250,6 +250,16 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public float ValuesSum => X + Y + Z;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector with values being absolute values of that vector.
|
||||
/// </summary>
|
||||
public Vector3 Absolute => new Vector3(Mathf.Abs(X), Mathf.Abs(Y), Mathf.Abs(Z));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a vector with values being opposite to values of that vector.
|
||||
/// </summary>
|
||||
public Vector3 Negative => new Vector3(-X, -Y, -Z);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the component at the specified index.
|
||||
/// </summary>
|
||||
@@ -294,8 +304,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <returns>The length of the vector.</returns>
|
||||
/// <remarks>
|
||||
/// <see cref="Vector3.LengthSquared" /> may be preferred when only the relative length is needed
|
||||
/// and speed is of the essence.
|
||||
/// <see cref="Vector3.LengthSquared" /> may be preferred when only the relative length is needed and speed is of the essence.
|
||||
/// </remarks>
|
||||
public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z);
|
||||
|
||||
@@ -304,8 +313,7 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
/// <returns>The squared length of the vector.</returns>
|
||||
/// <remarks>
|
||||
/// This method may be preferred to <see cref="Vector3.Length" /> when only a relative length is needed
|
||||
/// and speed is of the essence.
|
||||
/// This method may be preferred to <see cref="Vector3.Length" /> when only a relative length is needed and speed is of the essence.
|
||||
/// </remarks>
|
||||
public float LengthSquared => X * X + Y * Y + Z * Z;
|
||||
|
||||
|
||||
@@ -11,21 +11,21 @@ namespace Utilities
|
||||
template<typename T>
|
||||
FORCE_INLINE T RoundTo1DecimalPlace(T value)
|
||||
{
|
||||
return round(value * 10) / 10;
|
||||
return (T)round((double)value * 10) / (T)10;
|
||||
}
|
||||
|
||||
// Round floating point value up to 2 decimal places
|
||||
template<typename T>
|
||||
FORCE_INLINE T RoundTo2DecimalPlaces(T value)
|
||||
{
|
||||
return round(value * 100) / 100;
|
||||
return (T)round((double)value * 100.0) / (T)100;
|
||||
}
|
||||
|
||||
// Round floating point value up to 3 decimal places
|
||||
template<typename T>
|
||||
FORCE_INLINE T RoundTo3DecimalPlaces(T value)
|
||||
{
|
||||
return round(value * 1000) / 1000;
|
||||
return (T)round((double)value * 1000.0) / (T)1000;
|
||||
}
|
||||
|
||||
// Converts size of the file (in bytes) to the best fitting string
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "Engine/Animations/AnimationUtils.h"
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
#include "Engine/Debug/DebugLog.h"
|
||||
#include "Engine/Render2D/Render2D.h"
|
||||
#include "Engine/Render2D/FontAsset.h"
|
||||
|
||||
// Debug draw service configuration
|
||||
#define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024)
|
||||
@@ -54,6 +56,25 @@ struct DebugTriangle
|
||||
float TimeLeft;
|
||||
};
|
||||
|
||||
struct DebugText2D
|
||||
{
|
||||
Array<Char, InlinedAllocation<64>> Text;
|
||||
Vector2 Position;
|
||||
int32 Size;
|
||||
Color Color;
|
||||
float TimeLeft;
|
||||
};
|
||||
|
||||
struct DebugText3D
|
||||
{
|
||||
Array<Char, InlinedAllocation<64>> Text;
|
||||
Transform Transform;
|
||||
bool FaceCamera;
|
||||
int32 Size;
|
||||
Color Color;
|
||||
float TimeLeft;
|
||||
};
|
||||
|
||||
PACK_STRUCT(struct Vertex {
|
||||
Vector3 Position;
|
||||
Color32 Color;
|
||||
@@ -133,10 +154,14 @@ struct DebugDrawData
|
||||
Array<DebugTriangle> OneFrameTriangles;
|
||||
Array<DebugTriangle> DefaultWireTriangles;
|
||||
Array<DebugTriangle> OneFrameWireTriangles;
|
||||
Array<DebugText2D> DefaultText2D;
|
||||
Array<DebugText2D> OneFrameText2D;
|
||||
Array<DebugText3D> DefaultText3D;
|
||||
Array<DebugText3D> OneFrameText3D;
|
||||
|
||||
inline int32 Count() const
|
||||
{
|
||||
return LinesCount() + TrianglesCount();
|
||||
return LinesCount() + TrianglesCount() + TextCount();
|
||||
}
|
||||
|
||||
inline int32 LinesCount() const
|
||||
@@ -149,6 +174,11 @@ struct DebugDrawData
|
||||
return DefaultTriangles.Count() + OneFrameTriangles.Count() + DefaultWireTriangles.Count() + OneFrameWireTriangles.Count();
|
||||
}
|
||||
|
||||
inline int32 TextCount() const
|
||||
{
|
||||
return DefaultText2D.Count() + OneFrameText2D.Count() + DefaultText3D.Count() + OneFrameText3D.Count();
|
||||
}
|
||||
|
||||
inline void Add(const DebugLine& l)
|
||||
{
|
||||
if (l.TimeLeft > 0)
|
||||
@@ -178,10 +208,14 @@ struct DebugDrawData
|
||||
UpdateList(deltaTime, DefaultLines);
|
||||
UpdateList(deltaTime, DefaultTriangles);
|
||||
UpdateList(deltaTime, DefaultWireTriangles);
|
||||
UpdateList(deltaTime, DefaultText2D);
|
||||
UpdateList(deltaTime, DefaultText3D);
|
||||
|
||||
OneFrameLines.Clear();
|
||||
OneFrameTriangles.Clear();
|
||||
OneFrameWireTriangles.Clear();
|
||||
OneFrameText2D.Clear();
|
||||
OneFrameText3D.Clear();
|
||||
}
|
||||
|
||||
inline void Clear()
|
||||
@@ -192,6 +226,10 @@ struct DebugDrawData
|
||||
OneFrameTriangles.Clear();
|
||||
DefaultWireTriangles.Clear();
|
||||
OneFrameWireTriangles.Clear();
|
||||
DefaultText2D.Clear();
|
||||
OneFrameText2D.Clear();
|
||||
DefaultText3D.Clear();
|
||||
OneFrameText3D.Clear();
|
||||
}
|
||||
|
||||
inline void Release()
|
||||
@@ -202,23 +240,38 @@ struct DebugDrawData
|
||||
OneFrameTriangles.Resize(0);
|
||||
DefaultWireTriangles.Resize(0);
|
||||
OneFrameWireTriangles.Resize(0);
|
||||
DefaultText2D.Resize(0);
|
||||
OneFrameText2D.Resize(0);
|
||||
DefaultText3D.Resize(0);
|
||||
OneFrameText3D.Resize(0);
|
||||
}
|
||||
};
|
||||
|
||||
DebugDrawData DebugDrawDefault;
|
||||
DebugDrawData DebugDrawDepthTest;
|
||||
AssetReference<Shader> DebugDrawShader;
|
||||
PsData DebugDrawPsLinesDefault;
|
||||
PsData DebugDrawPsLinesDepthTest;
|
||||
PsData DebugDrawPsWireTrianglesDefault;
|
||||
PsData DebugDrawPsWireTrianglesDepthTest;
|
||||
PsData DebugDrawPsTrianglesDefault;
|
||||
PsData DebugDrawPsTrianglesDepthTest;
|
||||
DynamicVertexBuffer* DebugDrawVB = nullptr;
|
||||
Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES];
|
||||
Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES];
|
||||
Vector3 CylinderCache[DEBUG_DRAW_CYLINDER_VERTICES];
|
||||
Array<Vector3> SphereTriangleCache;
|
||||
struct DebugDrawContext
|
||||
{
|
||||
DebugDrawData DebugDrawDefault;
|
||||
DebugDrawData DebugDrawDepthTest;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
DebugDrawContext GlobalContext;
|
||||
DebugDrawContext* Context;
|
||||
AssetReference<Shader> DebugDrawShader;
|
||||
AssetReference<FontAsset> DebugDrawFont;
|
||||
PsData DebugDrawPsLinesDefault;
|
||||
PsData DebugDrawPsLinesDepthTest;
|
||||
PsData DebugDrawPsWireTrianglesDefault;
|
||||
PsData DebugDrawPsWireTrianglesDepthTest;
|
||||
PsData DebugDrawPsTrianglesDefault;
|
||||
PsData DebugDrawPsTrianglesDepthTest;
|
||||
DynamicVertexBuffer* DebugDrawVB = nullptr;
|
||||
Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES];
|
||||
Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES];
|
||||
Vector3 CylinderCache[DEBUG_DRAW_CYLINDER_VERTICES];
|
||||
Array<Vector3> SphereTriangleCache;
|
||||
};
|
||||
|
||||
extern int32 BoxTrianglesIndicesCache[];
|
||||
|
||||
struct DebugDrawCall
|
||||
@@ -289,6 +342,21 @@ DebugDrawCall WriteLists(int32& vertexCounter, const Array<T>& listA, const Arra
|
||||
return drawCall;
|
||||
}
|
||||
|
||||
inline void DrawText3D(const DebugText3D& t, const RenderContext& renderContext, const Vector3& viewUp, const Matrix& f, const Matrix& vp, const Viewport& viewport, GPUContext* context, GPUTextureView* target, GPUTextureView* depthBuffer)
|
||||
{
|
||||
Matrix w, fw, m;
|
||||
if (t.FaceCamera)
|
||||
Matrix::CreateWorld(t.Transform.Translation, renderContext.View.Direction, viewUp, w);
|
||||
else
|
||||
t.Transform.GetWorld(w);
|
||||
Matrix::Multiply(f, w, fw);
|
||||
Matrix::Multiply(fw, vp, m);
|
||||
Render2D::Begin(context, target, depthBuffer, viewport, m);
|
||||
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
||||
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, Vector2::Zero);
|
||||
Render2D::End();
|
||||
}
|
||||
|
||||
class DebugDrawService : public EngineService
|
||||
{
|
||||
public:
|
||||
@@ -307,16 +375,18 @@ DebugDrawService DebugDrawServiceInstance;
|
||||
|
||||
bool DebugDrawService::Init()
|
||||
{
|
||||
Context = &GlobalContext;
|
||||
|
||||
// Init wireframe sphere cache
|
||||
int32 index = 0;
|
||||
float step = TWO_PI / DEBUG_DRAW_SPHERE_RESOLUTION;
|
||||
for (float a = 0.0f; a < TWO_PI; a += step)
|
||||
{
|
||||
// Calculate sines and cosines
|
||||
float sinA = sin(a);
|
||||
float cosA = cos(a);
|
||||
float sinB = sin(a + step);
|
||||
float cosB = cos(a + step);
|
||||
float sinA = Math::Sin(a);
|
||||
float cosA = Math::Cos(a);
|
||||
float sinB = Math::Sin(a + step);
|
||||
float cosB = Math::Cos(a + step);
|
||||
|
||||
// XY loop
|
||||
SphereCache[index++] = Vector3(cosA, sinA, 0.0f);
|
||||
@@ -337,10 +407,10 @@ bool DebugDrawService::Init()
|
||||
for (float a = 0.0f; a < TWO_PI; a += step)
|
||||
{
|
||||
// Calculate sines and cosines
|
||||
float sinA = sin(a);
|
||||
float cosA = cos(a);
|
||||
float sinB = sin(a + step);
|
||||
float cosB = cos(a + step);
|
||||
float sinA = Math::Sin(a);
|
||||
float cosA = Math::Cos(a);
|
||||
float sinB = Math::Sin(a + step);
|
||||
float cosB = Math::Cos(a + step);
|
||||
|
||||
CircleCache[index++] = Vector3(cosA, sinA, 0.0f);
|
||||
CircleCache[index++] = Vector3(cosB, sinB, 0.0f);
|
||||
@@ -436,8 +506,8 @@ void DebugDrawService::Update()
|
||||
// Special case for Null renderer
|
||||
if (GPUDevice::Instance->GetRendererType() == RendererType::Null)
|
||||
{
|
||||
DebugDrawDefault.Clear();
|
||||
DebugDrawDepthTest.Clear();
|
||||
GlobalContext.DebugDrawDefault.Clear();
|
||||
GlobalContext.DebugDrawDepthTest.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -445,8 +515,8 @@ void DebugDrawService::Update()
|
||||
|
||||
// Update lists
|
||||
const float deltaTime = Time::Update.DeltaTime.GetTotalSeconds();
|
||||
DebugDrawDefault.Update(deltaTime);
|
||||
DebugDrawDepthTest.Update(deltaTime);
|
||||
GlobalContext.DebugDrawDefault.Update(deltaTime);
|
||||
GlobalContext.DebugDrawDepthTest.Update(deltaTime);
|
||||
|
||||
// Check if need to setup a resources
|
||||
if (DebugDrawShader == nullptr)
|
||||
@@ -501,8 +571,8 @@ void DebugDrawService::Update()
|
||||
void DebugDrawService::Dispose()
|
||||
{
|
||||
// Clear lists
|
||||
DebugDrawDefault.Release();
|
||||
DebugDrawDepthTest.Release();
|
||||
GlobalContext.DebugDrawDefault.Release();
|
||||
GlobalContext.DebugDrawDepthTest.Release();
|
||||
|
||||
// Release resources
|
||||
SphereTriangleCache.Resize(0);
|
||||
@@ -516,20 +586,48 @@ void DebugDrawService::Dispose()
|
||||
DebugDrawShader = nullptr;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void* DebugDraw::AllocateContext()
|
||||
{
|
||||
auto context = (DebugDrawContext*)Allocator::Allocate(sizeof(DebugDrawContext));
|
||||
Memory::ConstructItem(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
void DebugDraw::FreeContext(void* context)
|
||||
{
|
||||
Memory::DestructItem((DebugDrawContext*)context);
|
||||
Allocator::Free(context);
|
||||
}
|
||||
|
||||
void DebugDraw::UpdateContext(void* context, float deltaTime)
|
||||
{
|
||||
((DebugDrawContext*)context)->DebugDrawDefault.Update(deltaTime);
|
||||
((DebugDrawContext*)context)->DebugDrawDepthTest.Update(deltaTime);
|
||||
}
|
||||
|
||||
void DebugDraw::SetContext(void* context)
|
||||
{
|
||||
Context = context ? (DebugDrawContext*)context : &GlobalContext;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTextureView* depthBuffer, bool enableDepthTest)
|
||||
{
|
||||
PROFILE_GPU_CPU("Debug Draw");
|
||||
|
||||
// Ensure to have shader loaded and any lines to render
|
||||
const int32 debugDrawDepthTestCount = DebugDrawDepthTest.Count();
|
||||
const int32 debugDrawDefaultCount = DebugDrawDefault.Count();
|
||||
const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count();
|
||||
const int32 debugDrawDefaultCount = Context->DebugDrawDefault.Count();
|
||||
if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0)
|
||||
return;
|
||||
if (renderContext.Buffers == nullptr || !DebugDrawVB)
|
||||
return;
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
|
||||
// Fallback to task backbuffer
|
||||
// Fallback to task buffers
|
||||
if (target == nullptr && renderContext.Task)
|
||||
target = renderContext.Task->GetOutputView();
|
||||
|
||||
@@ -539,12 +637,12 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
#endif
|
||||
DebugDrawVB->Clear();
|
||||
int32 vertexCounter = 0;
|
||||
const DebugDrawCall depthTestLines = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultLines, DebugDrawDepthTest.OneFrameLines);
|
||||
const DebugDrawCall defaultLines = WriteLists(vertexCounter, DebugDrawDefault.DefaultLines, DebugDrawDefault.OneFrameLines);
|
||||
const DebugDrawCall depthTestTriangles = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultTriangles, DebugDrawDepthTest.OneFrameTriangles);
|
||||
const DebugDrawCall defaultTriangles = WriteLists(vertexCounter, DebugDrawDefault.DefaultTriangles, DebugDrawDefault.OneFrameTriangles);
|
||||
const DebugDrawCall depthTestWireTriangles = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultWireTriangles, DebugDrawDepthTest.OneFrameWireTriangles);
|
||||
const DebugDrawCall defaultWireTriangles = WriteLists(vertexCounter, DebugDrawDefault.DefaultWireTriangles, DebugDrawDefault.OneFrameWireTriangles);
|
||||
const DebugDrawCall depthTestLines = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultLines, Context->DebugDrawDepthTest.OneFrameLines);
|
||||
const DebugDrawCall defaultLines = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultLines, Context->DebugDrawDefault.OneFrameLines);
|
||||
const DebugDrawCall depthTestTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultTriangles, Context->DebugDrawDepthTest.OneFrameTriangles);
|
||||
const DebugDrawCall defaultTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultTriangles, Context->DebugDrawDefault.OneFrameTriangles);
|
||||
const DebugDrawCall depthTestWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultWireTriangles, Context->DebugDrawDepthTest.OneFrameWireTriangles);
|
||||
const DebugDrawCall defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles);
|
||||
DebugDrawVB->Flush(context);
|
||||
#if COMPILE_WITH_PROFILER
|
||||
ProfilerCPU::EndEvent(updateBufferProfileKey);
|
||||
@@ -561,8 +659,6 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
context->BindCB(0, cb);
|
||||
auto vb = DebugDrawVB->GetBuffer();
|
||||
|
||||
#define DRAW(drawCall) if (drawCall.VertexCount)
|
||||
|
||||
// Draw with depth test
|
||||
if (depthTestLines.VertexCount + depthTestTriangles.VertexCount + depthTestWireTriangles.VertexCount > 0)
|
||||
{
|
||||
@@ -580,7 +676,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
context->BindVB(ToSpan(&vb, 1));
|
||||
context->Draw(depthTestLines.StartVertex, depthTestLines.VertexCount);
|
||||
}
|
||||
|
||||
|
||||
// Wire Triangles
|
||||
if (depthTestWireTriangles.VertexCount)
|
||||
{
|
||||
@@ -633,7 +729,50 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
||||
}
|
||||
}
|
||||
|
||||
#undef DRAW
|
||||
// Text
|
||||
if (Context->DebugDrawDefault.TextCount() + Context->DebugDrawDepthTest.TextCount())
|
||||
{
|
||||
PROFILE_GPU_CPU_NAMED("Text");
|
||||
auto features = Render2D::Features;
|
||||
Render2D::Features = (Render2D::RenderingFeatures)((uint32)features & ~(uint32)Render2D::RenderingFeatures::VertexSnapping);
|
||||
|
||||
if (!DebugDrawFont)
|
||||
DebugDrawFont = Content::LoadAsyncInternal<FontAsset>(TEXT("Editor/Fonts/Roboto-Regular"));
|
||||
if (DebugDrawFont && DebugDrawFont->IsLoaded())
|
||||
{
|
||||
Viewport viewport = renderContext.Task->GetViewport();
|
||||
|
||||
if (Context->DebugDrawDefault.DefaultText2D.Count() + Context->DebugDrawDefault.OneFrameText2D.Count())
|
||||
{
|
||||
Render2D::Begin(context, target, nullptr, viewport);
|
||||
for (auto& t : Context->DebugDrawDefault.DefaultText2D)
|
||||
{
|
||||
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
||||
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
|
||||
}
|
||||
for (auto& t : Context->DebugDrawDefault.OneFrameText2D)
|
||||
{
|
||||
const StringView text(t.Text.Get(), t.Text.Count() - 1);
|
||||
Render2D::DrawText(DebugDrawFont->CreateFont(t.Size), text, t.Color, t.Position);
|
||||
}
|
||||
Render2D::End();
|
||||
}
|
||||
|
||||
if (Context->DebugDrawDefault.DefaultText3D.Count() + Context->DebugDrawDefault.OneFrameText3D.Count())
|
||||
{
|
||||
Matrix f;
|
||||
Matrix::RotationZ(PI, f);
|
||||
Vector3 viewUp;
|
||||
Vector3::Transform(Vector3::Up, Quaternion::LookRotation(renderContext.View.Direction, Vector3::Up), viewUp);
|
||||
for (auto& t : Context->DebugDrawDefault.DefaultText3D)
|
||||
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
|
||||
for (auto& t : Context->DebugDrawDefault.OneFrameText3D)
|
||||
DrawText3D(t, renderContext, viewUp, f, vp, viewport, context, target, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Render2D::Features = features;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
@@ -649,9 +788,10 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount)
|
||||
void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
if (selectedActors)
|
||||
{
|
||||
for (int32 i = 0; i < selectedActorsCount; i++)
|
||||
@@ -662,9 +802,19 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount)
|
||||
}
|
||||
}
|
||||
|
||||
Function<bool(Actor*)> function;
|
||||
function.Bind(&DrawActorsTreeWalk);
|
||||
SceneQuery::TreeExecute(function);
|
||||
if (drawScenes)
|
||||
{
|
||||
Function<bool(Actor*)> function = [](Actor* actor)-> bool
|
||||
{
|
||||
if (actor->IsActiveInHierarchy())
|
||||
{
|
||||
actor->OnDebugDraw();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
SceneQuery::TreeExecute(function);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest)
|
||||
@@ -674,9 +824,9 @@ void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color&
|
||||
|
||||
// Add line
|
||||
if (depthTest)
|
||||
DebugDrawDepthTest.Add(l);
|
||||
Context->DebugDrawDepthTest.Add(l);
|
||||
else
|
||||
DebugDrawDefault.Add(l);
|
||||
Context->DebugDrawDefault.Add(l);
|
||||
}
|
||||
|
||||
void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
||||
@@ -694,10 +844,10 @@ void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, c
|
||||
const Vector3* p = lines.Get();
|
||||
Array<DebugLine>* list;
|
||||
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultLines : &Context->DebugDrawDepthTest.OneFrameLines;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultLines : &Context->DebugDrawDefault.OneFrameLines;
|
||||
|
||||
list->EnsureCapacity(list->Count() + lines.Length());
|
||||
for (int32 i = 0; i < lines.Length(); i += 2)
|
||||
@@ -713,9 +863,9 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3&
|
||||
// Create draw call entry
|
||||
Array<DebugLine>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultLines : &Context->DebugDrawDepthTest.OneFrameLines;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultLines : &Context->DebugDrawDefault.OneFrameLines;
|
||||
DebugLine l = { p1, Vector3::Zero, Color32(color), duration };
|
||||
|
||||
// Find amount of segments to use
|
||||
@@ -737,7 +887,7 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3&
|
||||
}
|
||||
}
|
||||
|
||||
#define DRAW_WIRE_BOX_LINE(i0, i1, list) l.Start = corners[i0]; l.End = corners[i1]; list.Add(l)
|
||||
#define DRAW_WIRE_BOX_LINE(i0, i1, list) l.Start = corners[i0]; l.End = corners[i1]; Context->list.Add(l)
|
||||
#define DRAW_WIRE_BOX(list) \
|
||||
DRAW_WIRE_BOX_LINE(0, 1, list); \
|
||||
DRAW_WIRE_BOX_LINE(0, 3, list); \
|
||||
@@ -824,7 +974,7 @@ void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color,
|
||||
{
|
||||
l.Start = sphere.Center + SphereCache[i++] * sphere.Radius;
|
||||
l.End = sphere.Center + SphereCache[i++] * sphere.Radius;
|
||||
DebugDrawDepthTest.Add(l);
|
||||
Context->DebugDrawDepthTest.Add(l);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -833,7 +983,7 @@ void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color,
|
||||
{
|
||||
l.Start = sphere.Center + SphereCache[i++] * sphere.Radius;
|
||||
l.End = sphere.Center + SphereCache[i++] * sphere.Radius;
|
||||
DebugDrawDefault.Add(l);
|
||||
Context->DebugDrawDefault.Add(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -846,9 +996,9 @@ void DebugDraw::DrawSphere(const BoundingSphere& sphere, const Color& color, flo
|
||||
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
||||
list->EnsureCapacity(list->Count() + SphereTriangleCache.Count());
|
||||
|
||||
for (int32 i = 0; i < SphereTriangleCache.Count();)
|
||||
@@ -905,11 +1055,11 @@ void DebugDraw::DrawTriangle(const Vector3& v0, const Vector3& v1, const Vector3
|
||||
|
||||
if (depthTest)
|
||||
{
|
||||
DebugDrawDepthTest.Add(t);
|
||||
Context->DebugDrawDepthTest.Add(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugDrawDefault.Add(t);
|
||||
Context->DebugDrawDefault.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,9 +1073,9 @@ void DebugDraw::DrawTriangles(const Span<Vector3>& vertices, const Color& color,
|
||||
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
||||
list->EnsureCapacity(list->Count() + vertices.Length() / 3);
|
||||
|
||||
for (int32 i = 0; i < vertices.Length();)
|
||||
@@ -952,9 +1102,9 @@ void DebugDraw::DrawTriangles(const Span<Vector3>& vertices, const Span<int32>&
|
||||
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
||||
list->EnsureCapacity(list->Count() + indices.Length() / 3);
|
||||
|
||||
for (int32 i = 0; i < indices.Length();)
|
||||
@@ -981,9 +1131,9 @@ void DebugDraw::DrawWireTriangles(const Span<Vector3>& vertices, const Color& co
|
||||
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultWireTriangles : &DebugDrawDepthTest.OneFrameWireTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultWireTriangles : &Context->DebugDrawDepthTest.OneFrameWireTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultWireTriangles : &DebugDrawDefault.OneFrameWireTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultWireTriangles : &Context->DebugDrawDefault.OneFrameWireTriangles;
|
||||
list->EnsureCapacity(list->Count() + vertices.Length() / 3);
|
||||
|
||||
for (int32 i = 0; i < vertices.Length();)
|
||||
@@ -1010,9 +1160,9 @@ void DebugDraw::DrawWireTriangles(const Span<Vector3>& vertices, const Span<int3
|
||||
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultWireTriangles : &DebugDrawDepthTest.OneFrameWireTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultWireTriangles : &Context->DebugDrawDepthTest.OneFrameWireTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultWireTriangles : &DebugDrawDefault.OneFrameWireTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultWireTriangles : &Context->DebugDrawDefault.OneFrameWireTriangles;
|
||||
list->EnsureCapacity(list->Count() + indices.Length() / 3);
|
||||
|
||||
for (int32 i = 0; i < indices.Length();)
|
||||
@@ -1055,10 +1205,10 @@ void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientat
|
||||
{
|
||||
// Calculate sines and cosines
|
||||
// TODO: optimize this stuff
|
||||
float sinA = sin(a) * radius;
|
||||
float cosA = cos(a) * radius;
|
||||
float sinB = sin(a + step) * radius;
|
||||
float cosB = cos(a + step) * radius;
|
||||
float sinA = Math::Sin(a) * radius;
|
||||
float cosA = Math::Cos(a) * radius;
|
||||
float sinB = Math::Sin(a + step) * radius;
|
||||
float cosB = Math::Cos(a + step) * radius;
|
||||
|
||||
// First XY loop
|
||||
DRAW_WIRE_BOX_LINE(cosA, sinA, -halfLength, cosB, sinB, -halfLength);
|
||||
@@ -1104,8 +1254,8 @@ void DebugDraw::DrawWireCylinder(const Vector3& position, const Quaternion& orie
|
||||
{
|
||||
// Cache data
|
||||
float theta = i * angleBetweenFacets;
|
||||
float x = cos(theta) * radius;
|
||||
float z = sin(theta) * radius;
|
||||
float x = Math::Cos(theta) * radius;
|
||||
float z = Math::Sin(theta) * radius;
|
||||
|
||||
// Top cap
|
||||
CylinderCache[index++] = Vector3(x, verticalOffset, z);
|
||||
@@ -1198,9 +1348,9 @@ void DebugDraw::DrawBox(const BoundingBox& box, const Color& color, float durati
|
||||
t.TimeLeft = duration;
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
||||
list->EnsureCapacity(list->Count() + 36);
|
||||
for (int i0 = 0; i0 < 36;)
|
||||
{
|
||||
@@ -1224,9 +1374,9 @@ void DebugDraw::DrawBox(const OrientedBoundingBox& box, const Color& color, floa
|
||||
t.TimeLeft = duration;
|
||||
Array<DebugTriangle>* list;
|
||||
if (depthTest)
|
||||
list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultTriangles : &Context->DebugDrawDepthTest.OneFrameTriangles;
|
||||
else
|
||||
list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles;
|
||||
list = duration > 0 ? &Context->DebugDrawDefault.DefaultTriangles : &Context->DebugDrawDefault.OneFrameTriangles;
|
||||
list->EnsureCapacity(list->Count() + 36);
|
||||
for (int i0 = 0; i0 < 36;)
|
||||
{
|
||||
@@ -1238,4 +1388,51 @@ void DebugDraw::DrawBox(const OrientedBoundingBox& box, const Color& color, floa
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::DrawText(const StringView& text, const Vector2& position, const Color& color, int32 size, float duration)
|
||||
{
|
||||
if (text.Length() == 0 || size < 4)
|
||||
return;
|
||||
Array<DebugText2D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText2D : &Context->DebugDrawDefault.OneFrameText2D;
|
||||
auto& t = list->AddOne();
|
||||
t.Text.Resize(text.Length() + 1);
|
||||
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
|
||||
t.Text[text.Length()] = 0;
|
||||
t.Position = position;
|
||||
t.Size = size;
|
||||
t.Color = color;
|
||||
t.TimeLeft = duration;
|
||||
}
|
||||
|
||||
void DebugDraw::DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size, float duration)
|
||||
{
|
||||
if (text.Length() == 0 || size < 4)
|
||||
return;
|
||||
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
|
||||
auto& t = list->AddOne();
|
||||
t.Text.Resize(text.Length() + 1);
|
||||
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
|
||||
t.Text[text.Length()] = 0;
|
||||
t.Transform = position;
|
||||
t.FaceCamera = true;
|
||||
t.Size = size;
|
||||
t.Color = color;
|
||||
t.TimeLeft = duration;
|
||||
}
|
||||
|
||||
void DebugDraw::DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size, float duration)
|
||||
{
|
||||
if (text.Length() == 0 || size < 4)
|
||||
return;
|
||||
Array<DebugText3D>* list = duration > 0 ? &Context->DebugDrawDefault.DefaultText3D : &Context->DebugDrawDefault.OneFrameText3D;
|
||||
auto& t = list->AddOne();
|
||||
t.Text.Resize(text.Length() + 1);
|
||||
Platform::MemoryCopy(t.Text.Get(), text.Get(), text.Length() * sizeof(Char));
|
||||
t.Text[text.Length()] = 0;
|
||||
t.Transform = transform;
|
||||
t.FaceCamera = false;
|
||||
t.Size = size;
|
||||
t.Color = color;
|
||||
t.TimeLeft = duration;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@ class GPUContext;
|
||||
class RenderTask;
|
||||
class SceneRenderTask;
|
||||
class Actor;
|
||||
struct Transform;
|
||||
|
||||
/// <summary>
|
||||
/// The debug shapes rendering service. Not available in final game. For use only in the editor.
|
||||
@@ -23,6 +24,35 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
|
||||
/// </summary>
|
||||
/// <returns>The context object. Release it wil FreeContext. Returns null if failed.</returns>
|
||||
API_FUNCTION() static void* AllocateContext();
|
||||
|
||||
/// <summary>
|
||||
/// Frees the context for Debug Drawing.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
API_FUNCTION() static void FreeContext(void* context);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the context for Debug Drawing.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="deltaTime">The update delta time (in seconds).</param>
|
||||
API_FUNCTION() static void UpdateContext(void* context, float deltaTime);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context for Debug Drawing to a custom or null to use global default.
|
||||
/// </summary>
|
||||
/// <param name="context">The context or null.</param>
|
||||
API_FUNCTION() static void SetContext(void* context);
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Draws the collected debug shapes to the output.
|
||||
/// </summary>
|
||||
@@ -37,7 +67,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||
/// </summary>
|
||||
/// <param name="selectedActors">The list of actors to draw.</param>
|
||||
/// <param name="selectedActorsCount">The size of the list of actors.</param>
|
||||
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount);
|
||||
/// <param name="drawScenes">True if draw all debug shapes from scenes too or false if draw just from specified actor list.</param>
|
||||
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the line.
|
||||
@@ -277,6 +308,36 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
|
||||
API_FUNCTION() static void DrawBox(const OrientedBoundingBox& box, const Color& color, float duration = 0.0f, bool depthTest = true);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the text on a screen (2D).
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="position">The position of the text on the screen (in screen-space coordinates).</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="size">The font size.</param>
|
||||
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||
API_FUNCTION() static void DrawText(const StringView& text, const Vector2& position, const Color& color, int32 size = 20, float duration = 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the text (3D) that automatically faces the camera.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="position">The position of the text (world-space).</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="size">The font size.</param>
|
||||
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||
API_FUNCTION() static void DrawText(const StringView& text, const Vector3& position, const Color& color, int32 size = 32, float duration = 0.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the text (3D).
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="transform">The transformation of the text (world-space).</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="size">The font size.</param>
|
||||
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
|
||||
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f);
|
||||
};
|
||||
|
||||
#define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest)
|
||||
@@ -296,7 +357,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||
#define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, length, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, radius, height, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireTube(position, orientation, scale, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest) DebugDraw::DrawWireArrow(position, orientation, scale, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_TEXT(text, position, color, size, duration) DebugDraw::DrawText(text, position, color, size, duration)
|
||||
|
||||
#else
|
||||
|
||||
@@ -318,5 +380,6 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||
#define DEBUG_DRAW_WIRE_TUBE(position, orientation, radius, length, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_WIRE_CYLINDER(position, orientation, radius, height, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_WIRE_ARROW(position, orientation, scale, color, duration, depthTest)
|
||||
#define DEBUG_DRAW_TEXT(text, position, color, size, duration)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "Engine/Profiler/Profiler.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
#else
|
||||
#include "Engine/Utilities/Encryption.h"
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Represents the reference to the scene asset. Stores the unique ID of the scene to reference. Can be used to load the selected scene.
|
||||
/// </summary>
|
||||
public struct SceneReference
|
||||
public struct SceneReference : IComparable, IComparable<Guid>, IComparable<SceneReference>
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier of the scene asset (and the scene object).
|
||||
@@ -22,5 +22,93 @@ namespace FlaxEngine
|
||||
{
|
||||
ID = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two values and returns true if are equal or false if unequal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>True if values are equal, otherwise false.</returns>
|
||||
public static bool operator ==(SceneReference left, SceneReference right)
|
||||
{
|
||||
return left.ID == right.ID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two values and returns false if are equal or true if unequal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>True if values are not equal, otherwise false.</returns>
|
||||
public static bool operator !=(SceneReference left, SceneReference right)
|
||||
{
|
||||
return left.ID != right.ID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two values and returns true if are equal or false if unequal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>True if values are equal, otherwise false.</returns>
|
||||
public static bool operator ==(SceneReference left, Guid right)
|
||||
{
|
||||
return left.ID == right;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two values and returns false if are equal or true if unequal.
|
||||
/// </summary>
|
||||
/// <param name="left">The left value.</param>
|
||||
/// <param name="right">The right value.</param>
|
||||
/// <returns>True if values are not equal, otherwise false.</returns>
|
||||
public static bool operator !=(SceneReference left, Guid right)
|
||||
{
|
||||
return left.ID != right;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is Guid id)
|
||||
return CompareTo(id);
|
||||
if (obj is SceneReference other)
|
||||
return CompareTo(other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(Guid other)
|
||||
{
|
||||
return ID.CompareTo(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(SceneReference other)
|
||||
{
|
||||
return ID.CompareTo(other.ID);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Guid id)
|
||||
return ID == id;
|
||||
if (obj is SceneReference other)
|
||||
return ID == other.ID;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return ID.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ void Mesh::Render(GPUContext* context) const
|
||||
context->DrawIndexedInstanced(_triangles * 3, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals) const
|
||||
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom) const
|
||||
{
|
||||
if (!material || !material->IsSurface())
|
||||
return;
|
||||
@@ -399,8 +399,8 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
|
||||
drawCall.Surface.Skinning = nullptr;
|
||||
drawCall.Surface.LODDitherFactor = 0.0f;
|
||||
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
|
||||
drawCall.PerInstanceRandom = 0.0f;
|
||||
renderContext.List->AddDrawCall(DrawPass::Default, flags, drawCall, receiveDecals);
|
||||
drawCall.PerInstanceRandom = perInstanceRandom;
|
||||
renderContext.List->AddDrawCall(drawModes, flags, drawCall, receiveDecals);
|
||||
}
|
||||
|
||||
void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const
|
||||
|
||||
@@ -459,7 +459,9 @@ public:
|
||||
/// <param name="world">The world transformation of the model.</param>
|
||||
/// <param name="flags">The object static flags.</param>
|
||||
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
|
||||
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true) const;
|
||||
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
|
||||
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
|
||||
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f) const;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mesh.
|
||||
|
||||
@@ -122,11 +122,13 @@ public:
|
||||
/// <param name="world">The world transformation of the model.</param>
|
||||
/// <param name="flags">The object static flags.</param>
|
||||
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
|
||||
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true) const
|
||||
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
|
||||
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
|
||||
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f) const
|
||||
{
|
||||
for (int32 i = 0; i < Meshes.Count(); i++)
|
||||
{
|
||||
Meshes[i].Draw(renderContext, material, world, flags, receiveDecals);
|
||||
Meshes[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +222,11 @@ SceneRenderTask::~SceneRenderTask()
|
||||
Buffers->DeleteObjectNow();
|
||||
}
|
||||
|
||||
void SceneRenderTask::CameraCut()
|
||||
{
|
||||
IsCameraCut = true;
|
||||
}
|
||||
|
||||
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
|
||||
{
|
||||
if ((ActorsSource & ActorsSources::Scenes) != 0)
|
||||
|
||||
@@ -236,10 +236,7 @@ public:
|
||||
/// <summary>
|
||||
/// Marks the next rendered frame as camera cut. Used to clear the temporal effects history and prevent visual artifacts blended from the previous frames.
|
||||
/// </summary>
|
||||
API_FUNCTION() void CameraCut()
|
||||
{
|
||||
IsCameraCut = true;
|
||||
}
|
||||
API_FUNCTION() void CameraCut();
|
||||
|
||||
/// <summary>
|
||||
/// The output texture (can be null if using rendering to window swap chain). Can be sued to redirect the default scene rendering output to a texture.
|
||||
|
||||
@@ -115,6 +115,7 @@ void RenderView::CopyFrom(Camera* camera)
|
||||
Matrix::Invert(Projection, IP);
|
||||
Frustum.GetInvMatrix(IVP);
|
||||
CullingFrustum = Frustum;
|
||||
RenderLayersMask = camera->RenderLayersMask;
|
||||
}
|
||||
|
||||
void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
|
||||
@@ -131,6 +132,7 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
|
||||
Matrix::Invert(Projection, IP);
|
||||
Frustum.GetInvMatrix(IVP);
|
||||
CullingFrustum = Frustum;
|
||||
RenderLayersMask = camera->RenderLayersMask;
|
||||
}
|
||||
|
||||
DrawPass RenderView::GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const
|
||||
|
||||
@@ -89,6 +89,7 @@ namespace FlaxEngine
|
||||
Projection = camera.Projection;
|
||||
NonJitteredProjection = Projection;
|
||||
TemporalAAJitter = Vector4.Zero;
|
||||
RenderLayersMask = camera.RenderLayersMask;
|
||||
|
||||
UpdateCachedData();
|
||||
}
|
||||
@@ -107,6 +108,7 @@ namespace FlaxEngine
|
||||
camera.GetMatrices(out View, out Projection, ref customViewport);
|
||||
NonJitteredProjection = Projection;
|
||||
TemporalAAJitter = Vector4.Zero;
|
||||
RenderLayersMask = camera.RenderLayersMask;
|
||||
|
||||
UpdateCachedData();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Core/Math/BoundingFrustum.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Config/LayersMask.h"
|
||||
#include "Engine/Level/Types.h"
|
||||
#include "Enums.h"
|
||||
|
||||
@@ -141,6 +142,11 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD() int32 TaaFrameIndex = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The rendering mask for layers. Used to exclude objects from rendering.
|
||||
/// </summary>
|
||||
API_FIELD() LayersMask RenderLayersMask;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1825,7 +1825,9 @@ bool GPUDeviceVulkan::Init()
|
||||
#endif
|
||||
#undef INIT_FUNC
|
||||
VmaAllocatorCreateInfo allocatorInfo = {};
|
||||
allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0;
|
||||
allocatorInfo.physicalDevice = gpu;
|
||||
allocatorInfo.instance = Instance;
|
||||
allocatorInfo.device = Device;
|
||||
allocatorInfo.pVulkanFunctions = &vulkanFunctions;
|
||||
VALIDATE_VULKAN_RESULT(vmaCreateAllocator(&allocatorInfo, &Allocator));
|
||||
|
||||
@@ -198,7 +198,7 @@ void Actor::SetParent(Actor* value, bool worldPositionsStays, bool canBreakPrefa
|
||||
// Check if value won't change
|
||||
if (_parent == value)
|
||||
return;
|
||||
if (!IsInMainThread())
|
||||
if (IsDuringPlay() && !IsInMainThread())
|
||||
{
|
||||
LOG(Error, "Editing scene hierarchy is only allowed on a main thread.");
|
||||
return;
|
||||
@@ -406,7 +406,7 @@ const String& Actor::GetTag() const
|
||||
|
||||
void Actor::SetLayer(int32 layerIndex)
|
||||
{
|
||||
layerIndex = Math::Min<int32>(layerIndex, 31);
|
||||
layerIndex = Math::Clamp(layerIndex, 0, 31);
|
||||
if (layerIndex == _layer)
|
||||
return;
|
||||
|
||||
|
||||
@@ -330,6 +330,7 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
SERIALIZE_MEMBER(Near, _near);
|
||||
SERIALIZE_MEMBER(Far, _far);
|
||||
SERIALIZE_MEMBER(OrthoScale, _orthoScale);
|
||||
SERIALIZE(RenderLayersMask);
|
||||
}
|
||||
|
||||
void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
@@ -343,6 +344,7 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
|
||||
DESERIALIZE_MEMBER(Near, _near);
|
||||
DESERIALIZE_MEMBER(Far, _far);
|
||||
DESERIALIZE_MEMBER(OrthoScale, _orthoScale);
|
||||
DESERIALIZE(RenderLayersMask);
|
||||
}
|
||||
|
||||
void Camera::OnEnable()
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Actor.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "Engine/Core/Math/BoundingFrustum.h"
|
||||
#include "Engine/Core/Math/Viewport.h"
|
||||
#include "Engine/Core/Math/Ray.h"
|
||||
#include "Engine/Core/Config/LayersMask.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Describes the camera projection and view. Provides information about how to render scene (viewport location and direction, etc.).
|
||||
@@ -21,6 +24,7 @@ DECLARE_SCENE_OBJECT(Camera);
|
||||
// List with all created cameras actors on the scene
|
||||
static Array<Camera*> Cameras;
|
||||
|
||||
// The current cut-scene camera. Set by the Scene Animation Player to the current shot camera.
|
||||
static Camera* CutSceneCamera;
|
||||
|
||||
// The overriden main camera.
|
||||
@@ -161,6 +165,12 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetOrthographicScale(float value);
|
||||
|
||||
/// <summary>
|
||||
/// The layers mask used for rendering using this camera. Can be used to include or exclude specific actor layers from the drawing.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
|
||||
LayersMask RenderLayersMask;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -198,8 +198,8 @@ void SpotLight::OnDebugDrawSelected()
|
||||
Vector3 up = _transform.GetUp();
|
||||
Vector3 forward = GetDirection();
|
||||
float radius = GetScaledRadius();
|
||||
float discRadius = radius * tan(_outerConeAngle * DegreesToRadians);
|
||||
float falloffDiscRadius = radius * tan(_innerConeAngle * DegreesToRadians);
|
||||
float discRadius = radius * Math::Tan(_outerConeAngle * DegreesToRadians);
|
||||
float falloffDiscRadius = radius * Math::Tan(_innerConeAngle * DegreesToRadians);
|
||||
Vector3 position = GetPosition();
|
||||
|
||||
DEBUG_DRAW_LINE(position, position + forward * radius + up * discRadius, color, 0, true);
|
||||
|
||||
@@ -37,6 +37,21 @@
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#endif
|
||||
|
||||
bool LayersMask::HasLayer(const StringView& layerName) const
|
||||
{
|
||||
return HasLayer(Level::GetLayerIndex(layerName));
|
||||
}
|
||||
|
||||
bool LayersMask::operator==(const LayersMask& other) const
|
||||
{
|
||||
return Mask == other.Mask;
|
||||
}
|
||||
|
||||
bool LayersMask::operator!=(const LayersMask& other) const
|
||||
{
|
||||
return Mask != other.Mask;
|
||||
}
|
||||
|
||||
enum class SceneEventType
|
||||
{
|
||||
OnSceneSaving = 0,
|
||||
@@ -98,7 +113,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Update() override;
|
||||
void LateUpdate() override;
|
||||
void FixedUpdate() override;
|
||||
@@ -162,13 +176,22 @@ bool LevelImpl::deleteActor(Actor* actor)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LevelService::Init()
|
||||
void LayersAndTagsSettings::Apply()
|
||||
{
|
||||
auto& settings = *LayersAndTagsSettings::Get();
|
||||
Level::Tags = settings.Tags;
|
||||
// Note: we cannot remove tags/layers at runtime so this should deserialize them in additive mode
|
||||
// Tags/Layers are stored as index in actors so collection change would break the linkage
|
||||
for (auto& tag : Tags)
|
||||
{
|
||||
if (!Level::Tags.Contains(tag))
|
||||
Level::Tags.Add(tag);
|
||||
}
|
||||
for (int32 i = 0; i < ARRAY_COUNT(Level::Layers); i++)
|
||||
Level::Layers[i] = settings.Layers[i];
|
||||
return false;
|
||||
{
|
||||
const auto& src = Layers[i];
|
||||
auto& dst = Level::Layers[i];
|
||||
if (dst.IsEmpty() || !src.IsEmpty())
|
||||
dst = src;
|
||||
}
|
||||
}
|
||||
|
||||
void LevelService::Update()
|
||||
@@ -194,7 +217,7 @@ void LevelService::Update()
|
||||
for (int32 i = 0; i < scenes.Count(); i++)
|
||||
{
|
||||
if (scenes[i]->GetIsActive())
|
||||
scenes[i]->Ticking.Update.TickEditorScripts();
|
||||
scenes[i]->Ticking.Update.TickExecuteInEditor();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -223,7 +246,7 @@ void LevelService::LateUpdate()
|
||||
for (int32 i = 0; i < scenes.Count(); i++)
|
||||
{
|
||||
if (scenes[i]->GetIsActive())
|
||||
scenes[i]->Ticking.LateUpdate.TickEditorScripts();
|
||||
scenes[i]->Ticking.LateUpdate.TickExecuteInEditor();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -255,7 +278,7 @@ void LevelService::FixedUpdate()
|
||||
for (int32 i = 0; i < scenes.Count(); i++)
|
||||
{
|
||||
if (scenes[i]->GetIsActive())
|
||||
scenes[i]->Ticking.FixedUpdate.TickEditorScripts();
|
||||
scenes[i]->Ticking.FixedUpdate.TickExecuteInEditor();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -674,6 +697,20 @@ int32 Level::GetNonEmptyLayerNamesCount()
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
int32 Level::GetLayerIndex(const StringView& layer)
|
||||
{
|
||||
int32 result = -1;
|
||||
for (int32 i = 0; i < 32; i++)
|
||||
{
|
||||
if (Layers[i] == layer)
|
||||
{
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
@@ -445,7 +445,7 @@ public:
|
||||
/// The layers names.
|
||||
/// </summary>
|
||||
static String Layers[32];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or adds the tag (returns the tag index).
|
||||
/// </summary>
|
||||
@@ -459,6 +459,11 @@ public:
|
||||
/// <returns>The layers count.</returns>
|
||||
static int32 GetNonEmptyLayerNamesCount();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the zero-based index of the layer.
|
||||
/// </summary>
|
||||
static int32 GetLayerIndex(const StringView& layer);
|
||||
|
||||
private:
|
||||
|
||||
// Actor API
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
public sealed partial class Scene
|
||||
partial class Scene
|
||||
{
|
||||
/// <summary>
|
||||
/// The scene asset typename. Type of the serialized scene asset data. Hidden class for the scene assets. Actors deserialization rules are strictly controlled under the hood by the C++ core parts. Mostly because scene asset has the same ID as scene root actor so loading both managed objects for scene asset and scene will crash (due to object ids conflict).
|
||||
|
||||
@@ -19,7 +19,7 @@ class NavMesh;
|
||||
/// <summary>
|
||||
/// The scene root object that contains a hierarchy of actors.
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API Scene final : public Actor
|
||||
API_CLASS() class FLAXENGINE_API Scene : public Actor
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(Scene);
|
||||
friend Level;
|
||||
|
||||
@@ -28,8 +28,8 @@ SceneRendering::SceneRendering(::Scene* scene)
|
||||
|
||||
void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
|
||||
{
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
auto& view = renderContext.View;
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
CullDataSIMD cullData;
|
||||
{
|
||||
// Near
|
||||
@@ -126,7 +126,7 @@ void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, c
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
auto actor = actors[i];
|
||||
if (frustum.Intersects(actor->GetSphere()))
|
||||
if (view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
#endif
|
||||
@@ -134,8 +134,8 @@ void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, c
|
||||
|
||||
void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
|
||||
{
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
auto& view = renderContext.View;
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
CullDataSIMD cullData;
|
||||
{
|
||||
// Near
|
||||
@@ -233,7 +233,7 @@ void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderCon
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
auto actor = actors[i];
|
||||
if (actor->GetStaticFlags() & renderContext.View.StaticFlagsMask && frustum.Intersects(actor->GetSphere()))
|
||||
if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
#endif
|
||||
@@ -257,7 +257,7 @@ void SceneRendering::Draw(RenderContext& renderContext)
|
||||
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
|
||||
{
|
||||
auto actor = CommonNoCulling[i];
|
||||
if (actor->GetStaticFlags() & view.StaticFlagsMask)
|
||||
if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()))
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
@@ -271,7 +271,8 @@ void SceneRendering::Draw(RenderContext& renderContext)
|
||||
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
|
||||
{
|
||||
auto actor = CommonNoCulling[i];
|
||||
actor->Draw(renderContext);
|
||||
if (view.RenderLayersMask.HasLayer(actor->GetLayer()))
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,9 @@ public:
|
||||
/// <summary>
|
||||
/// Tick function type.
|
||||
/// </summary>
|
||||
class Tick
|
||||
struct Tick
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Signature of the function to call
|
||||
/// </summary>
|
||||
typedef void (*Signature)();
|
||||
|
||||
typedef void (*SignatureObj)(void*);
|
||||
|
||||
template<class T, void(T::*Method)()>
|
||||
@@ -51,8 +45,7 @@ public:
|
||||
/// <summary>
|
||||
/// Calls the binded function.
|
||||
/// </summary>
|
||||
/// <returns>Function result</returns>
|
||||
void Call() const
|
||||
FORCE_INLINE void Call() const
|
||||
{
|
||||
(*FunctionObj)(Callee);
|
||||
}
|
||||
@@ -63,10 +56,11 @@ public:
|
||||
public:
|
||||
|
||||
Array<Script*> Scripts;
|
||||
Array<Tick> Ticks;
|
||||
#if USE_EDITOR
|
||||
Array<Script*> ScriptsExecuteInEditor;
|
||||
Array<Tick> TicksExecuteInEditor;
|
||||
#endif
|
||||
Array<Tick> Ticks;
|
||||
|
||||
TickData(int32 capacity)
|
||||
: Scripts(capacity)
|
||||
@@ -81,18 +75,17 @@ public:
|
||||
TickScripts(Scripts);
|
||||
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
{
|
||||
Ticks[i].Call();
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void TickEditorScripts()
|
||||
void TickExecuteInEditor()
|
||||
{
|
||||
TickScripts(ScriptsExecuteInEditor);
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
TicksExecuteInEditor[i].Call();
|
||||
}
|
||||
#endif
|
||||
|
||||
void AddScript(Script* script);
|
||||
@@ -118,12 +111,35 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
template<class T, void(T::*Method)()>
|
||||
void AddTickExecuteInEditor(T* callee)
|
||||
{
|
||||
SceneTicking::Tick tick;
|
||||
tick.Bind<T, Method>(callee);
|
||||
TicksExecuteInEditor.Add(tick);
|
||||
}
|
||||
|
||||
void RemoveTickExecuteInEditor(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
{
|
||||
if (TicksExecuteInEditor[i].Callee == callee)
|
||||
{
|
||||
TicksExecuteInEditor.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Scripts.Clear();
|
||||
Ticks.Clear();
|
||||
#if USE_EDITOR
|
||||
ScriptsExecuteInEditor.Clear();
|
||||
TicksExecuteInEditor.Clear();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@@ -245,7 +245,7 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa
|
||||
_emitter = emitter;
|
||||
_effect = effect;
|
||||
_deltaTime = 0.0f;
|
||||
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
|
||||
_viewTask = effect->GetRenderTask();
|
||||
_callStack.Clear();
|
||||
|
||||
// Find the maximum radius of the particle light
|
||||
@@ -351,7 +351,7 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff
|
||||
_emitter = emitter;
|
||||
_effect = effect;
|
||||
_deltaTime = 0.0f;
|
||||
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
|
||||
_viewTask = effect->GetRenderTask();
|
||||
_callStack.Clear();
|
||||
|
||||
// Draw lights
|
||||
@@ -411,7 +411,7 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE
|
||||
_effect = effect;
|
||||
_particleIndex = 0;
|
||||
_deltaTime = dt;
|
||||
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
|
||||
_viewTask = effect->GetRenderTask();
|
||||
_callStack.Clear();
|
||||
auto& cpu = data.Buffer->CPU;
|
||||
|
||||
@@ -557,7 +557,7 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par
|
||||
_effect = effect;
|
||||
_particleIndex = 0;
|
||||
_deltaTime = dt;
|
||||
_viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
|
||||
_viewTask = effect->GetRenderTask();
|
||||
_callStack.Clear();
|
||||
|
||||
// Spawn particles
|
||||
|
||||
@@ -27,11 +27,6 @@ class ParticleEmitterGraphCPUExecutor;
|
||||
|
||||
#define PARTICLE_EMITTER_MAX_CALL_STACK 100
|
||||
|
||||
// Gets the render task for the given effect update
|
||||
#define PARTICLE_EMITTER_GET_VIEW_TASK(effect) \
|
||||
(SceneRenderTask*)((effect)->CustomViewRenderTask && (effect)->CustomViewRenderTask->LastUsedFrame != 0 ? (effect)->CustomViewRenderTask.Get() : \
|
||||
(MainRenderTask::Instance && MainRenderTask::Instance->LastUsedFrame != 0 ? MainRenderTask::Instance : nullptr))
|
||||
|
||||
class ParticleEmitterGraphCPUBox : public VisjectGraphBox
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -144,7 +144,7 @@ void GPUParticles::Execute(GPUContext* context, ParticleEmitter* emitter, Partic
|
||||
}
|
||||
|
||||
// Skip if can
|
||||
SceneRenderTask* viewTask = PARTICLE_EMITTER_GET_VIEW_TASK(effect);
|
||||
SceneRenderTask* viewTask = effect->GetRenderTask();
|
||||
const int32 threads = data.Buffer->GPU.ParticlesCountMax + data.GPU.SpawnCount;
|
||||
if (data.GPU.DeltaTime <= 0.0f || threads == 0 || !_mainCS)
|
||||
return;
|
||||
|
||||
@@ -339,6 +339,39 @@ void ParticleEffect::Sync()
|
||||
}
|
||||
}
|
||||
|
||||
SceneRenderTask* ParticleEffect::GetRenderTask() const
|
||||
{
|
||||
const uint64 minFrame = Engine::FrameCount - 2;
|
||||
|
||||
// Custom task
|
||||
const auto customViewRenderTask = CustomViewRenderTask.Get();
|
||||
if (customViewRenderTask && customViewRenderTask->Enabled && customViewRenderTask->LastUsedFrame >= minFrame)
|
||||
return customViewRenderTask;
|
||||
|
||||
// Main task
|
||||
const auto mainRenderTask = MainRenderTask::Instance;
|
||||
if (mainRenderTask && mainRenderTask->Enabled && mainRenderTask->LastUsedFrame >= minFrame)
|
||||
return mainRenderTask;
|
||||
|
||||
// Editor viewport
|
||||
#if USE_EDITOR
|
||||
for (auto task : RenderTask::Tasks)
|
||||
{
|
||||
if (task->LastUsedFrame >= minFrame && task->Enabled)
|
||||
{
|
||||
if (const auto sceneRenderTask = Cast<SceneRenderTask>(task))
|
||||
{
|
||||
if (sceneRenderTask->ActorsSource == ActorsSources::Scenes)
|
||||
{
|
||||
return sceneRenderTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
Array<ParticleEffect::ParameterOverride> ParticleEffect::GetParametersOverrides()
|
||||
@@ -373,6 +406,18 @@ void ParticleEffect::Update()
|
||||
UpdateSimulation();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Editor/Editor.h"
|
||||
|
||||
void ParticleEffect::UpdateExecuteInEditor()
|
||||
{
|
||||
if (!Editor::IsPlayMode)
|
||||
Update();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void ParticleEffect::CacheModifiedParameters()
|
||||
{
|
||||
if (_parameters.IsEmpty())
|
||||
@@ -649,6 +694,7 @@ void ParticleEffect::OnEnable()
|
||||
GetSceneRendering()->AddGeometry(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
GetScene()->Ticking.Update.AddTickExecuteInEditor<ParticleEffect, &ParticleEffect::UpdateExecuteInEditor>(this);
|
||||
#endif
|
||||
|
||||
// Base
|
||||
@@ -658,6 +704,7 @@ void ParticleEffect::OnEnable()
|
||||
void ParticleEffect::OnDisable()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
GetScene()->Ticking.Update.RemoveTickExecuteInEditor(this);
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveGeometry(this);
|
||||
|
||||
@@ -356,6 +356,11 @@ public:
|
||||
/// </summary>
|
||||
void Sync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the render task to use for particles simulation (eg. depth buffer collisions or view information).
|
||||
/// </summary>
|
||||
SceneRenderTask* GetRenderTask() const;
|
||||
|
||||
#if USE_EDITOR
|
||||
protected:
|
||||
// Exposed parameters overrides for Editor Undo.
|
||||
@@ -366,6 +371,9 @@ protected:
|
||||
private:
|
||||
|
||||
void Update();
|
||||
#if USE_EDITOR
|
||||
void UpdateExecuteInEditor();
|
||||
#endif
|
||||
void CacheModifiedParameters();
|
||||
void ApplyModifiedParameters();
|
||||
void OnParticleSystemModified();
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include "Engine/Profiler/ProfilerGPU.h"
|
||||
#include "Engine/Renderer/Utils/BitonicSort.h"
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#endif
|
||||
|
||||
struct SpriteParticleVertex
|
||||
{
|
||||
@@ -1155,6 +1158,13 @@ void ParticleManagerService::Update()
|
||||
// Simulation delta time can be based on a time since last update or the current delta time
|
||||
float dt = effect->UseTimeScale ? deltaTime : deltaTimeUnscaled;
|
||||
float t = effect->UseTimeScale ? time : timeUnscaled;
|
||||
#if USE_EDITOR
|
||||
if (!Editor::IsPlayMode)
|
||||
{
|
||||
dt = deltaTimeUnscaled;
|
||||
t = timeUnscaled;
|
||||
}
|
||||
#endif
|
||||
const float lastUpdateTime = instance.LastUpdateTime;
|
||||
if (lastUpdateTime > 0 && t > lastUpdateTime)
|
||||
{
|
||||
|
||||
@@ -316,6 +316,20 @@ public:
|
||||
/// <param name="ptr">A pointer to the memory block to deallocate.</param>
|
||||
static void Free(void* ptr) = delete;
|
||||
|
||||
/// <summary>
|
||||
/// Allocates pages memory block.
|
||||
/// </summary>
|
||||
/// <param name="numPages">The number of pages to allocate.</param>
|
||||
/// <param name="pageSize">The size of single page. Use Platform::GetCPUInfo().PageSize or provide compatible, custom size.</param>
|
||||
/// <returns>The pointer to the allocated pages in memory.</returns>
|
||||
static void* AllocatePages(uint64 numPages, uint64 pageSize) = delete;
|
||||
|
||||
/// <summary>
|
||||
/// Frees allocated pages memory block.
|
||||
/// </summary>
|
||||
/// <param name="ptr">The pointer to the pages to deallocate.</param>
|
||||
static void FreePages(void* ptr) = delete;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -51,6 +51,20 @@ void UnixPlatform::Free(void* ptr)
|
||||
}
|
||||
}
|
||||
|
||||
void* UnixPlatform::AllocatePages(uint64 numPages, uint64 pageSize)
|
||||
{
|
||||
const uint64 numBytes = numPages * pageSize;
|
||||
|
||||
// Fallback to malloc
|
||||
return malloc(numBytes);
|
||||
}
|
||||
|
||||
void UnixPlatform::FreePages(void* ptr)
|
||||
{
|
||||
// Fallback to free
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
uint64 UnixPlatform::GetCurrentProcessId()
|
||||
{
|
||||
return getpid();
|
||||
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
// [PlatformBase]
|
||||
static void* Allocate(uint64 size, uint64 alignment);
|
||||
static void Free(void* ptr);
|
||||
static void* AllocatePages(uint64 numPages, uint64 pageSize);
|
||||
static void FreePages(void* ptr);
|
||||
static uint64 GetCurrentProcessId();
|
||||
};
|
||||
|
||||
|
||||
@@ -316,6 +316,20 @@ void Win32Platform::Free(void* ptr)
|
||||
_aligned_free(ptr);
|
||||
}
|
||||
|
||||
void* Win32Platform::AllocatePages(uint64 numPages, uint64 pageSize)
|
||||
{
|
||||
const uint64 numBytes = numPages * pageSize;
|
||||
|
||||
// Use VirtualAlloc to allocate page-aligned memory
|
||||
return VirtualAlloc(nullptr, numBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
void Win32Platform::FreePages(void* ptr)
|
||||
{
|
||||
// Free page-aligned memory
|
||||
VirtualFree(ptr, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
bool Win32Platform::Is64BitPlatform()
|
||||
{
|
||||
#ifdef PLATFORM_64BITS
|
||||
|
||||
@@ -30,6 +30,8 @@ public:
|
||||
static void Prefetch(void const* ptr);
|
||||
static void* Allocate(uint64 size, uint64 alignment);
|
||||
static void Free(void* ptr);
|
||||
static void* AllocatePages(uint64 numPages, uint64 pageSize);
|
||||
static void FreePages(void* ptr);
|
||||
static bool Is64BitPlatform();
|
||||
static BatteryInfo GetBatteryInfo();
|
||||
static CPUInfo GetCPUInfo();
|
||||
|
||||
@@ -307,13 +307,14 @@ int32 Font::HitTestText(const StringView& text, const Vector2& location, const T
|
||||
ProcessText(text, lines, layout);
|
||||
ASSERT(lines.HasItems());
|
||||
float scale = layout.Scale / FontManager::FontScale;
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
|
||||
// Offset position to match lines origin space
|
||||
Vector2 rootOffset = layout.Bounds.Location + lines.First().Location;
|
||||
Vector2 testPoint = location - rootOffset;
|
||||
|
||||
// Get line which may intersect with the position (it's possible because lines have fixed height)
|
||||
int32 lineIndex = Math::Clamp(Math::FloorToInt(testPoint.Y / GetHeight()), 0, lines.Count() - 1);
|
||||
int32 lineIndex = Math::Clamp(Math::FloorToInt(testPoint.Y / baseLinesDistance), 0, lines.Count() - 1);
|
||||
const FontLineCache& line = lines[lineIndex];
|
||||
float x = line.Location.X;
|
||||
|
||||
@@ -380,6 +381,7 @@ Vector2 Font::GetCharPosition(const StringView& text, int32 index, const TextLay
|
||||
ProcessText(text, lines, layout);
|
||||
ASSERT(lines.HasItems());
|
||||
float scale = layout.Scale / FontManager::FontScale;
|
||||
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
|
||||
Vector2 rootOffset = layout.Bounds.Location + lines.First().Location;
|
||||
|
||||
// Find line with that position
|
||||
@@ -414,12 +416,12 @@ Vector2 Font::GetCharPosition(const StringView& text, int32 index, const TextLay
|
||||
}
|
||||
|
||||
// Upper left corner of the character
|
||||
return rootOffset + Vector2(x, static_cast<float>(lineIndex * GetHeight()));
|
||||
return rootOffset + Vector2(x, static_cast<float>(lineIndex * baseLinesDistance));
|
||||
}
|
||||
}
|
||||
|
||||
// Position after last character in the last line
|
||||
return rootOffset + Vector2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * GetHeight()));
|
||||
return rootOffset + Vector2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * baseLinesDistance));
|
||||
}
|
||||
|
||||
void Font::FlushFaceSize() const
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user