Merge remote-tracking branch 'origin/master' into 1.10
This commit is contained in:
87
.github/data/Build Settings.json
vendored
Normal file
87
.github/data/Build Settings.json
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"ID": "2364031e4e327637c1ad88b415fa756e",
|
||||||
|
"TypeName": "FlaxEditor.Content.Settings.BuildSettings",
|
||||||
|
"EngineBuild": 6605,
|
||||||
|
"Data": {
|
||||||
|
"OutputName": "${PROJECT_NAME}",
|
||||||
|
"MaxAssetsPerPackage": 4096,
|
||||||
|
"MaxPackageSizeMB": 1024,
|
||||||
|
"ContentKey": 0,
|
||||||
|
"ForDistribution": false,
|
||||||
|
"SkipPackaging": true,
|
||||||
|
"AdditionalAssets": null,
|
||||||
|
"AdditionalScenes": null,
|
||||||
|
"AdditionalAssetFolders": null,
|
||||||
|
"ShadersNoOptimize": false,
|
||||||
|
"ShadersGenerateDebugData": false,
|
||||||
|
"SkipDefaultFonts": false,
|
||||||
|
"SkipDotnetPackaging": false,
|
||||||
|
"SkipUnusedDotnetLibsPackaging": true,
|
||||||
|
"Presets": [
|
||||||
|
{
|
||||||
|
"Name": "Development",
|
||||||
|
"Targets": [
|
||||||
|
{
|
||||||
|
"Name": "Windows",
|
||||||
|
"Output": "Output\\Windows",
|
||||||
|
"Platform": 2,
|
||||||
|
"Mode": 1,
|
||||||
|
"CustomDefines": null,
|
||||||
|
"PreBuildAction": null,
|
||||||
|
"PostBuildAction": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Linux",
|
||||||
|
"Output": "Output\\Linux",
|
||||||
|
"Platform": 6,
|
||||||
|
"Mode": 1,
|
||||||
|
"CustomDefines": null,
|
||||||
|
"PreBuildAction": null,
|
||||||
|
"PostBuildAction": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Mac",
|
||||||
|
"Output": "Output\\Mac",
|
||||||
|
"Platform": 13,
|
||||||
|
"Mode": 1,
|
||||||
|
"CustomDefines": null,
|
||||||
|
"PreBuildAction": null,
|
||||||
|
"PostBuildAction": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Android",
|
||||||
|
"Output": "Output\\Android",
|
||||||
|
"Platform": 9,
|
||||||
|
"Mode": 1,
|
||||||
|
"CustomDefines": null,
|
||||||
|
"PreBuildAction": null,
|
||||||
|
"PostBuildAction": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "iOS",
|
||||||
|
"Output": "Output\\iOS",
|
||||||
|
"Platform": 14,
|
||||||
|
"Mode": 1,
|
||||||
|
"CustomDefines": null,
|
||||||
|
"PreBuildAction": null,
|
||||||
|
"PostBuildAction": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Release",
|
||||||
|
"Targets": [
|
||||||
|
{
|
||||||
|
"Name": "Windows",
|
||||||
|
"Output": "Output\\Windows",
|
||||||
|
"Platform": 2,
|
||||||
|
"Mode": 2,
|
||||||
|
"CustomDefines": null,
|
||||||
|
"PreBuildAction": null,
|
||||||
|
"PostBuildAction": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
5
.github/data/Cook.ps1
vendored
Normal file
5
.github/data/Cook.ps1
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Write-Output "Cooking Game"
|
||||||
|
Start-Process -filepath "Binaries\Editor\Win64\Development\FlaxEditor.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows"'
|
||||||
|
|
||||||
|
Write-Output "Testing Game"
|
||||||
|
Start-Process -filepath "FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null'
|
||||||
11
.github/data/ExitOnEsc.cs
vendored
Normal file
11
.github/data/ExitOnEsc.cs
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using FlaxEngine;
|
||||||
|
|
||||||
|
public class ExitOnEsc : Script
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnUpdate()
|
||||||
|
{
|
||||||
|
// Exit as soon as game starts update loaded level
|
||||||
|
Engine.RequestExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
2
.github/workflows/build_ios.yml
vendored
2
.github/workflows/build_ios.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
# Game
|
# Game
|
||||||
game-windows:
|
game-windows:
|
||||||
name: Game (iOS, Release ARM64)
|
name: Game (iOS, Release ARM64)
|
||||||
runs-on: "macos-latest"
|
runs-on: "macos-14"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|||||||
4
.github/workflows/build_mac.yml
vendored
4
.github/workflows/build_mac.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
# Editor
|
# Editor
|
||||||
editor-mac:
|
editor-mac:
|
||||||
name: Editor (Mac, Development ARM64)
|
name: Editor (Mac, Development ARM64)
|
||||||
runs-on: "macos-latest"
|
runs-on: "macos-14"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
# Game
|
# Game
|
||||||
game-mac:
|
game-mac:
|
||||||
name: Game (Mac, Release ARM64)
|
name: Game (Mac, Release ARM64)
|
||||||
runs-on: "macos-latest"
|
runs-on: "macos-14"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|||||||
4
.github/workflows/cd.yml
vendored
4
.github/workflows/cd.yml
vendored
@@ -140,7 +140,7 @@ jobs:
|
|||||||
# Mac
|
# Mac
|
||||||
package-mac-editor:
|
package-mac-editor:
|
||||||
name: Editor (Mac)
|
name: Editor (Mac)
|
||||||
runs-on: "macos-latest"
|
runs-on: "macos-14"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
path: Output/FlaxEditorMac.zip
|
path: Output/FlaxEditorMac.zip
|
||||||
package-mac-game:
|
package-mac-game:
|
||||||
name: Game (Mac)
|
name: Game (Mac)
|
||||||
runs-on: "macos-latest"
|
runs-on: "macos-14"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|||||||
48
.github/workflows/cooking.yml
vendored
Normal file
48
.github/workflows/cooking.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Cooker
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOTNET_NOLOGO: true
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Cook on Mac
|
||||||
|
cook-mac:
|
||||||
|
name: Cook (Mac)
|
||||||
|
runs-on: "macos-14"
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Setup Vulkan
|
||||||
|
uses: ./.github/actions/vulkan
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
- name: Setup .NET Workload
|
||||||
|
run: |
|
||||||
|
dotnet workload install ios
|
||||||
|
- name: Print .NET info
|
||||||
|
run: |
|
||||||
|
dotnet --info
|
||||||
|
dotnet workload --info
|
||||||
|
- name: Checkout LFS
|
||||||
|
run: |
|
||||||
|
git lfs version
|
||||||
|
git lfs pull
|
||||||
|
- name: Get Flax Samples
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
repository: FlaxEngine/FlaxSamples
|
||||||
|
path: FlaxSamples
|
||||||
|
- name: Patch Files
|
||||||
|
run: |
|
||||||
|
cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game
|
||||||
|
cp ".github/data/Build Settings.json" "FlaxSamples/MaterialsFeaturesTour/Content/Settings"
|
||||||
|
- name: Build Editor
|
||||||
|
run: |
|
||||||
|
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
|
||||||
|
- name: Cook Game (iOS)
|
||||||
|
run: |
|
||||||
|
./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS"
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"Major": 1,
|
"Major": 1,
|
||||||
"Minor": 9,
|
"Minor": 9,
|
||||||
"Revision": 0,
|
"Revision": 0,
|
||||||
"Build": 6605
|
"Build": 6606
|
||||||
},
|
},
|
||||||
"Company": "Flax",
|
"Company": "Flax",
|
||||||
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
|
||||||
|
|||||||
@@ -112,6 +112,12 @@ namespace FlaxEditor.Content
|
|||||||
throw new TargetException("Missing Visual Script asset.");
|
throw new TargetException("Missing Visual Script asset.");
|
||||||
_type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value);
|
_type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object Invoke(object obj, object[] parameters)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class VisualScriptMethodInfo : IScriptMemberInfo
|
sealed class VisualScriptMethodInfo : IScriptMemberInfo
|
||||||
@@ -240,6 +246,14 @@ namespace FlaxEditor.Content
|
|||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public object Invoke(object obj, object[] parameters)
|
||||||
|
{
|
||||||
|
if (!_type.Asset)
|
||||||
|
throw new TargetException("Missing Visual Script asset.");
|
||||||
|
return _type.Asset.InvokeMethod(_index, obj, parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FlaxEditor.CustomEditors.GUI;
|
using FlaxEditor.CustomEditors.GUI;
|
||||||
@@ -127,12 +128,41 @@ namespace FlaxEditor.CustomEditors
|
|||||||
|
|
||||||
_isSetBlocked = true;
|
_isSetBlocked = true;
|
||||||
Initialize(layout);
|
Initialize(layout);
|
||||||
|
ShowButtons();
|
||||||
Refresh();
|
Refresh();
|
||||||
_isSetBlocked = false;
|
_isSetBlocked = false;
|
||||||
|
|
||||||
CurrentCustomEditor = prev;
|
CurrentCustomEditor = prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowButtons()
|
||||||
|
{
|
||||||
|
var values = Values;
|
||||||
|
if (values == null || values.HasDifferentTypes)
|
||||||
|
return;
|
||||||
|
var type = TypeUtils.GetObjectType(values[0]);
|
||||||
|
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
if (!method.HasAttribute(typeof(ButtonAttribute)) ||
|
||||||
|
method.ParametersCount != 0)
|
||||||
|
continue;
|
||||||
|
var attribute = method.GetAttribute<ButtonAttribute>();
|
||||||
|
var text = string.IsNullOrEmpty(attribute.Text) ? Utilities.Utils.GetPropertyNameUI(method.Name) : attribute.Text;
|
||||||
|
var tooltip = string.IsNullOrEmpty(attribute.Tooltip) ? Editor.Instance.CodeDocs.GetTooltip(method) : attribute.Tooltip;
|
||||||
|
var button = _layout.Button(text, tooltip);
|
||||||
|
button.Button.Tag = method;
|
||||||
|
button.Button.ButtonClicked += OnButtonClicked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnButtonClicked(Button button)
|
||||||
|
{
|
||||||
|
var method = (ScriptMemberInfo)button.Tag;
|
||||||
|
var obj = method.IsStatic ? null : Values[0];
|
||||||
|
method.Invoke(obj);
|
||||||
|
}
|
||||||
|
|
||||||
internal static CustomEditor CurrentCustomEditor;
|
internal static CustomEditor CurrentCustomEditor;
|
||||||
|
|
||||||
internal void OnChildCreated(CustomEditor child)
|
internal void OnChildCreated(CustomEditor child)
|
||||||
@@ -271,8 +301,16 @@ namespace FlaxEditor.CustomEditors
|
|||||||
_valueToSet = null;
|
_valueToSet = null;
|
||||||
|
|
||||||
// Assign value
|
// Assign value
|
||||||
for (int i = 0; i < _values.Count; i++)
|
if (val is IList l && l.Count == _values.Count)
|
||||||
_values[i] = val;
|
{
|
||||||
|
for (int i = 0; i < _values.Count; i++)
|
||||||
|
_values[i] = l[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _values.Count; i++)
|
||||||
|
_values[i] = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
namespace FlaxEditor.CustomEditors.Dedicated
|
namespace FlaxEditor.CustomEditors.Dedicated
|
||||||
{
|
{
|
||||||
@@ -11,7 +12,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
class BezierCurveObjectEditor<T> : CustomEditor where T : struct
|
class BezierCurveObjectEditor<T> : CustomEditor where T : struct
|
||||||
{
|
{
|
||||||
private bool _isSetting;
|
private bool _isSetting;
|
||||||
|
private int _firstTimeShow;
|
||||||
private BezierCurveEditor<T> _curve;
|
private BezierCurveEditor<T> _curve;
|
||||||
|
private Splitter _splitter;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
@@ -20,6 +23,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
_curve = item.CustomControl;
|
_curve = item.CustomControl;
|
||||||
_curve.Height = 120.0f;
|
_curve.Height = 120.0f;
|
||||||
_curve.Edited += OnCurveEdited;
|
_curve.Edited += OnCurveEdited;
|
||||||
|
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
|
||||||
|
_splitter = new Splitter
|
||||||
|
{
|
||||||
|
Moved = OnSplitterMoved,
|
||||||
|
Parent = _curve,
|
||||||
|
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||||
|
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCurveEdited()
|
private void OnCurveEdited()
|
||||||
@@ -32,6 +43,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
_isSetting = false;
|
_isSetting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSplitterMoved(Float2 location)
|
||||||
|
{
|
||||||
|
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Refresh()
|
public override void Refresh()
|
||||||
{
|
{
|
||||||
@@ -44,12 +60,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
_curve.SetKeyframes(value.Keyframes);
|
_curve.SetKeyframes(value.Keyframes);
|
||||||
_isSetting = false;
|
_isSetting = false;
|
||||||
}
|
}
|
||||||
|
if (_firstTimeShow-- > 0)
|
||||||
|
_curve.ShowWholeCurve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Deinitialize()
|
protected override void Deinitialize()
|
||||||
{
|
{
|
||||||
_curve = null;
|
_curve = null;
|
||||||
|
_splitter = null;
|
||||||
|
|
||||||
base.Deinitialize();
|
base.Deinitialize();
|
||||||
}
|
}
|
||||||
@@ -111,7 +130,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
class LinearCurveObjectEditor<T> : CustomEditor where T : struct
|
class LinearCurveObjectEditor<T> : CustomEditor where T : struct
|
||||||
{
|
{
|
||||||
private bool _isSetting;
|
private bool _isSetting;
|
||||||
|
private int _firstTimeShow;
|
||||||
private LinearCurveEditor<T> _curve;
|
private LinearCurveEditor<T> _curve;
|
||||||
|
private Splitter _splitter;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
@@ -120,6 +141,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
_curve = item.CustomControl;
|
_curve = item.CustomControl;
|
||||||
_curve.Height = 120.0f;
|
_curve.Height = 120.0f;
|
||||||
_curve.Edited += OnCurveEdited;
|
_curve.Edited += OnCurveEdited;
|
||||||
|
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
|
||||||
|
_splitter = new Splitter
|
||||||
|
{
|
||||||
|
Moved = OnSplitterMoved,
|
||||||
|
Parent = _curve,
|
||||||
|
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||||
|
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCurveEdited()
|
private void OnCurveEdited()
|
||||||
@@ -132,6 +161,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
_isSetting = false;
|
_isSetting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSplitterMoved(Float2 location)
|
||||||
|
{
|
||||||
|
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Refresh()
|
public override void Refresh()
|
||||||
{
|
{
|
||||||
@@ -144,12 +178,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
_curve.SetKeyframes(value.Keyframes);
|
_curve.SetKeyframes(value.Keyframes);
|
||||||
_isSetting = false;
|
_isSetting = false;
|
||||||
}
|
}
|
||||||
|
if (_firstTimeShow-- > 0)
|
||||||
|
_curve.ShowWholeCurve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Deinitialize()
|
protected override void Deinitialize()
|
||||||
{
|
{
|
||||||
_curve = null;
|
_curve = null;
|
||||||
|
_splitter = null;
|
||||||
|
|
||||||
base.Deinitialize();
|
base.Deinitialize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,20 +407,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
|
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
|
||||||
public class UIControlControlEditor : GenericEditor
|
public class UIControlControlEditor : GenericEditor
|
||||||
{
|
{
|
||||||
private Type _cachedType;
|
private ScriptType[] _valueTypes;
|
||||||
private bool _anchorDropDownClosed = true;
|
private bool _anchorDropDownClosed = true;
|
||||||
private Button _pivotRelativeButton;
|
private Button _pivotRelativeButton;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
_cachedType = null;
|
|
||||||
if (HasDifferentTypes)
|
|
||||||
{
|
|
||||||
// TODO: support stable editing multiple different control types (via generic way or for transform-only)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set control type button
|
// Set control type button
|
||||||
var space = layout.Space(20);
|
var space = layout.Space(20);
|
||||||
var buttonText = "Set Type";
|
var buttonText = "Set Type";
|
||||||
@@ -445,12 +438,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add control type helper label
|
// Add control type helper label
|
||||||
|
if (!Values.HasDifferentTypes)
|
||||||
{
|
{
|
||||||
var type = Values[0].GetType();
|
|
||||||
_cachedType = type;
|
|
||||||
var label = layout.AddPropertyItem("Type", "The type of the created control.");
|
var label = layout.AddPropertyItem("Type", "The type of the created control.");
|
||||||
label.Label(type.FullName);
|
label.Label(Values[0].GetType().FullName);
|
||||||
}
|
}
|
||||||
|
_valueTypes = Values.ValuesTypes;
|
||||||
|
|
||||||
// Show control properties
|
// Show control properties
|
||||||
base.Initialize(layout);
|
base.Initialize(layout);
|
||||||
@@ -720,22 +713,20 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Refresh()
|
public override void Refresh()
|
||||||
{
|
{
|
||||||
if (_cachedType != null)
|
// Automatic layout rebuild if control type gets changed
|
||||||
|
if (_valueTypes != null &&
|
||||||
|
!Values.HasNull &&
|
||||||
|
!Utils.ArraysEqual(_valueTypes, Values.ValuesTypes))
|
||||||
{
|
{
|
||||||
// Automatic layout rebuild if control type gets changed
|
RebuildLayout();
|
||||||
var type = Values.HasNull ? null : Values[0].GetType();
|
return;
|
||||||
if (type != _cachedType)
|
}
|
||||||
{
|
|
||||||
RebuildLayout();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh anchors
|
// Refresh anchors
|
||||||
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
|
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
|
||||||
if (xEq != _cachedXEq || yEq != _cachedYEq)
|
if (xEq != _cachedXEq || yEq != _cachedYEq)
|
||||||
{
|
{
|
||||||
RebuildLayout();
|
RebuildLayout();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Refresh();
|
base.Refresh();
|
||||||
|
|||||||
@@ -34,6 +34,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to use the average for sliding different values.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool AllowSlidingForDifferentValues => true;
|
||||||
|
|
||||||
|
private ValueChanged _valueChanged;
|
||||||
|
private float _defaultSlidingSpeed;
|
||||||
|
private bool _slidingEnded = false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
@@ -46,18 +55,31 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
XElement = grid.FloatValue();
|
XElement = grid.FloatValue();
|
||||||
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
XElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||||
|
|
||||||
YElement = grid.FloatValue();
|
YElement = grid.FloatValue();
|
||||||
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
YElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
|
||||||
ZElement = grid.FloatValue();
|
ZElement = grid.FloatValue();
|
||||||
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
ZElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
|
||||||
if (LinkedLabel != null)
|
if (LinkedLabel != null)
|
||||||
{
|
{
|
||||||
@@ -70,32 +92,117 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnValueChanged()
|
private void OnXValueChanged()
|
||||||
{
|
{
|
||||||
if (IsSetBlocked)
|
if (IsSetBlocked)
|
||||||
return;
|
return;
|
||||||
|
_valueChanged = ValueChanged.X;
|
||||||
|
OnValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnYValueChanged()
|
||||||
|
{
|
||||||
|
if (IsSetBlocked)
|
||||||
|
return;
|
||||||
|
_valueChanged = ValueChanged.Y;
|
||||||
|
OnValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnZValueChanged()
|
||||||
|
{
|
||||||
|
if (IsSetBlocked)
|
||||||
|
return;
|
||||||
|
_valueChanged = ValueChanged.Z;
|
||||||
|
OnValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValueChanged()
|
||||||
|
{
|
||||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||||
var token = isSliding ? this : null;
|
var token = isSliding ? this : null;
|
||||||
var useCachedAngles = isSliding && token == _cachedToken;
|
var useCachedAngles = isSliding && token == _cachedToken;
|
||||||
|
|
||||||
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
|
if (HasDifferentValues && Values.Count > 1)
|
||||||
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
|
|
||||||
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
|
|
||||||
|
|
||||||
x = Mathf.UnwindDegrees(x);
|
|
||||||
y = Mathf.UnwindDegrees(y);
|
|
||||||
z = Mathf.UnwindDegrees(z);
|
|
||||||
|
|
||||||
if (!useCachedAngles)
|
|
||||||
{
|
{
|
||||||
_cachedAngles = new Float3(x, y, z);
|
var xValue = XElement.ValueBox.Value;
|
||||||
|
var yValue = YElement.ValueBox.Value;
|
||||||
|
var zValue = ZElement.ValueBox.Value;
|
||||||
|
|
||||||
|
xValue = Mathf.UnwindDegrees(xValue);
|
||||||
|
yValue = Mathf.UnwindDegrees(yValue);
|
||||||
|
zValue = Mathf.UnwindDegrees(zValue);
|
||||||
|
|
||||||
|
var value = new Float3(xValue, yValue, zValue);
|
||||||
|
|
||||||
|
_cachedToken = token;
|
||||||
|
|
||||||
|
var newObjects = new object[Values.Count];
|
||||||
|
// Handle Sliding
|
||||||
|
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||||
|
{
|
||||||
|
Float3 average = Float3.Zero;
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = (Quaternion)Values[0];
|
||||||
|
var euler = v.EulerAngles;
|
||||||
|
|
||||||
|
average += euler;
|
||||||
|
}
|
||||||
|
|
||||||
|
average /= Values.Count;
|
||||||
|
|
||||||
|
var newValue = value - average;
|
||||||
|
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
var val = (Quaternion)v;
|
||||||
|
Quaternion.Euler(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0, out Quaternion qVal);
|
||||||
|
v = val * qVal;
|
||||||
|
|
||||||
|
newObjects[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture last sliding value
|
||||||
|
if (_slidingEnded)
|
||||||
|
_slidingEnded = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
object v = Values[i];
|
||||||
|
var val = (Quaternion)v;
|
||||||
|
var euler = val.EulerAngles;
|
||||||
|
Quaternion.Euler(_valueChanged == ValueChanged.X ? xValue : euler.X, _valueChanged == ValueChanged.Y ? yValue : euler.Y, _valueChanged == ValueChanged.Z ? zValue : euler.Z, out Quaternion qVal);
|
||||||
|
v = val * qVal;
|
||||||
|
|
||||||
|
newObjects[i] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetValue(newObjects, token);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
|
||||||
|
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
|
||||||
|
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
|
||||||
|
|
||||||
_cachedToken = token;
|
x = Mathf.UnwindDegrees(x);
|
||||||
|
y = Mathf.UnwindDegrees(y);
|
||||||
|
z = Mathf.UnwindDegrees(z);
|
||||||
|
|
||||||
Quaternion.Euler(x, y, z, out Quaternion value);
|
if (!useCachedAngles)
|
||||||
SetValue(value, token);
|
{
|
||||||
|
_cachedAngles = new Float3(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cachedToken = token;
|
||||||
|
|
||||||
|
Quaternion.Euler(x, y, z, out Quaternion value);
|
||||||
|
SetValue(value, token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -112,7 +219,73 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
if (HasDifferentValues)
|
if (HasDifferentValues)
|
||||||
{
|
{
|
||||||
// TODO: support different values for ValueBox<T>
|
// Get which values are different
|
||||||
|
bool xDifferent = false;
|
||||||
|
bool yDifferent = false;
|
||||||
|
bool zDifferent = false;
|
||||||
|
Float3 cachedFirstValue = Float3.Zero;
|
||||||
|
Float3 average = Float3.Zero;
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var value = (Quaternion)Values[i];
|
||||||
|
var euler = value.EulerAngles;
|
||||||
|
|
||||||
|
average += euler;
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
cachedFirstValue = euler;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
|
||||||
|
xDifferent = true;
|
||||||
|
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
|
||||||
|
yDifferent = true;
|
||||||
|
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
|
||||||
|
zDifferent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
average /= Values.Count;
|
||||||
|
|
||||||
|
if (!xDifferent)
|
||||||
|
{
|
||||||
|
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
XElement.ValueBox.Value = average.X;
|
||||||
|
else
|
||||||
|
XElement.ValueBox.SlideSpeed = 0;
|
||||||
|
XElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!yDifferent)
|
||||||
|
{
|
||||||
|
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
YElement.ValueBox.Value = average.Y;
|
||||||
|
else
|
||||||
|
YElement.ValueBox.SlideSpeed = 0;
|
||||||
|
YElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zDifferent)
|
||||||
|
{
|
||||||
|
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
ZElement.ValueBox.Value = average.Z;
|
||||||
|
else
|
||||||
|
ZElement.ValueBox.SlideSpeed = 0;
|
||||||
|
ZElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -121,6 +294,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
XElement.ValueBox.Value = euler.X;
|
XElement.ValueBox.Value = euler.X;
|
||||||
YElement.ValueBox.Value = euler.Y;
|
YElement.ValueBox.Value = euler.Y;
|
||||||
ZElement.ValueBox.Value = euler.Z;
|
ZElement.ValueBox.Value = euler.Z;
|
||||||
|
|
||||||
|
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs
Normal file
45
Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using FlaxEditor.Content.Settings;
|
||||||
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
|
using FlaxEditor.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEditor.CustomEditors.Editors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Custom editor for picking terrain layers. Instead of choosing bit mask or layer index it shows a combo box with simple layer picking by name.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TerrainLayerEditor : CustomEditor
|
||||||
|
{
|
||||||
|
private ComboBoxElement element;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
|
{
|
||||||
|
element = layout.ComboBox();
|
||||||
|
element.ComboBox.SetItems(LayersAndTagsSettings.GetCurrentTerrainLayers());
|
||||||
|
element.ComboBox.SelectedIndex = (int)Values[0];
|
||||||
|
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||||
|
{
|
||||||
|
int value = comboBox.SelectedIndex;
|
||||||
|
if (value == -1)
|
||||||
|
value = 0;
|
||||||
|
|
||||||
|
SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Refresh()
|
||||||
|
{
|
||||||
|
base.Refresh();
|
||||||
|
|
||||||
|
element.ComboBox.SelectedIndex = (int)Values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,27 @@ using FlaxEngine.GUI;
|
|||||||
|
|
||||||
namespace FlaxEditor.CustomEditors.Editors
|
namespace FlaxEditor.CustomEditors.Editors
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The value changed by custom Vector3 editors.
|
||||||
|
/// </summary>
|
||||||
|
public enum ValueChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// X value changed.
|
||||||
|
/// </summary>
|
||||||
|
X = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Y value changed.
|
||||||
|
/// </summary>
|
||||||
|
Y = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Z value changed.
|
||||||
|
/// </summary>
|
||||||
|
Z = 2
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default implementation of the inspector used to edit Vector3 value type properties.
|
/// Default implementation of the inspector used to edit Vector3 value type properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -49,14 +70,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool LinkValues = false;
|
public bool LinkValues = false;
|
||||||
|
|
||||||
private enum ValueChanged
|
/// <summary>
|
||||||
{
|
/// Whether to use the average for sliding different values.
|
||||||
X = 0,
|
/// </summary>
|
||||||
Y = 1,
|
public virtual bool AllowSlidingForDifferentValues => true;
|
||||||
Z = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
private ValueChanged _valueChanged;
|
private ValueChanged _valueChanged;
|
||||||
|
private float _defaultSlidingSpeed;
|
||||||
|
private bool _slidingEnded = false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
@@ -83,19 +104,32 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
XElement.SetLimits(limit);
|
XElement.SetLimits(limit);
|
||||||
XElement.SetCategory(category);
|
XElement.SetCategory(category);
|
||||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
XElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||||
|
|
||||||
YElement = grid.FloatValue();
|
YElement = grid.FloatValue();
|
||||||
YElement.SetLimits(limit);
|
YElement.SetLimits(limit);
|
||||||
YElement.SetCategory(category);
|
YElement.SetCategory(category);
|
||||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
YElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
|
||||||
ZElement = grid.FloatValue();
|
ZElement = grid.FloatValue();
|
||||||
ZElement.SetLimits(limit);
|
ZElement.SetLimits(limit);
|
||||||
ZElement.SetCategory(category);
|
ZElement.SetCategory(category);
|
||||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
ZElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
|
||||||
if (LinkedLabel != null)
|
if (LinkedLabel != null)
|
||||||
{
|
{
|
||||||
@@ -118,8 +152,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (IsSetBlocked)
|
if (IsSetBlocked)
|
||||||
return;
|
return;
|
||||||
if (LinkValues)
|
_valueChanged = ValueChanged.X;
|
||||||
_valueChanged = ValueChanged.X;
|
|
||||||
OnValueChanged();
|
OnValueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +160,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (IsSetBlocked)
|
if (IsSetBlocked)
|
||||||
return;
|
return;
|
||||||
if (LinkValues)
|
_valueChanged = ValueChanged.Y;
|
||||||
_valueChanged = ValueChanged.Y;
|
|
||||||
OnValueChanged();
|
OnValueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,8 +168,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (IsSetBlocked)
|
if (IsSetBlocked)
|
||||||
return;
|
return;
|
||||||
if (LinkValues)
|
_valueChanged = ValueChanged.Z;
|
||||||
_valueChanged = ValueChanged.Z;
|
|
||||||
OnValueChanged();
|
OnValueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,14 +222,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||||
var token = isSliding ? this : null;
|
var token = isSliding ? this : null;
|
||||||
var value = new Float3(xValue, yValue, zValue);
|
var value = new Float3(xValue, yValue, zValue);
|
||||||
object v = Values[0];
|
if (HasDifferentValues && Values.Count > 1)
|
||||||
if (v is Vector3)
|
{
|
||||||
v = (Vector3)value;
|
var newObjects = new object[Values.Count];
|
||||||
else if (v is Float3)
|
// Handle Sliding
|
||||||
v = (Float3)value;
|
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||||
else if (v is Double3)
|
{
|
||||||
v = (Double3)value;
|
// TODO: handle linked values
|
||||||
SetValue(v, token);
|
Float3 average = Float3.Zero;
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
var castedValue = Float3.Zero;
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
castedValue = asVector3;
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
castedValue = asFloat3;
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
castedValue = asDouble3;
|
||||||
|
|
||||||
|
average += castedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
average /= Values.Count;
|
||||||
|
|
||||||
|
var newValue = value - average;
|
||||||
|
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||||
|
|
||||||
|
newObjects[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture last sliding value
|
||||||
|
if (_slidingEnded)
|
||||||
|
_slidingEnded = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: handle linked values
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
object v = Values[i];
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
|
||||||
|
|
||||||
|
newObjects[i] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetValue(newObjects, token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object v = Values[0];
|
||||||
|
if (v is Vector3)
|
||||||
|
v = (Vector3)value;
|
||||||
|
else if (v is Float3)
|
||||||
|
v = (Float3)value;
|
||||||
|
else if (v is Double3)
|
||||||
|
v = (Double3)value;
|
||||||
|
SetValue(v, token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetRatio(float value, float initialValue)
|
private float GetRatio(float value, float initialValue)
|
||||||
@@ -218,7 +314,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
if (HasDifferentValues)
|
if (HasDifferentValues)
|
||||||
{
|
{
|
||||||
// TODO: support different values for ValueBox<T>
|
// Get which values are different
|
||||||
|
bool xDifferent = false;
|
||||||
|
bool yDifferent = false;
|
||||||
|
bool zDifferent = false;
|
||||||
|
Float3 cachedFirstValue = Float3.Zero;
|
||||||
|
Float3 average = Float3.Zero;
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
var value = Float3.Zero;
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
value = asVector3;
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
value = asFloat3;
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
value = asDouble3;
|
||||||
|
|
||||||
|
average += value;
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
cachedFirstValue = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
|
||||||
|
xDifferent = true;
|
||||||
|
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
|
||||||
|
yDifferent = true;
|
||||||
|
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
|
||||||
|
zDifferent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
average /= Values.Count;
|
||||||
|
|
||||||
|
if (!xDifferent)
|
||||||
|
{
|
||||||
|
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
XElement.ValueBox.Value = average.X;
|
||||||
|
else
|
||||||
|
XElement.ValueBox.SlideSpeed = 0;
|
||||||
|
XElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!yDifferent)
|
||||||
|
{
|
||||||
|
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
YElement.ValueBox.Value = average.Y;
|
||||||
|
else
|
||||||
|
YElement.ValueBox.SlideSpeed = 0;
|
||||||
|
YElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zDifferent)
|
||||||
|
{
|
||||||
|
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
ZElement.ValueBox.Value = average.Z;
|
||||||
|
else
|
||||||
|
ZElement.ValueBox.SlideSpeed = 0;
|
||||||
|
ZElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -232,6 +400,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
XElement.ValueBox.Value = value.X;
|
XElement.ValueBox.Value = value.X;
|
||||||
YElement.ValueBox.Value = value.Y;
|
YElement.ValueBox.Value = value.Y;
|
||||||
ZElement.ValueBox.Value = value.Z;
|
ZElement.ValueBox.Value = value.Z;
|
||||||
|
|
||||||
|
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,6 +435,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to use the average for sliding different values.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool AllowSlidingForDifferentValues => true;
|
||||||
|
|
||||||
|
private ValueChanged _valueChanged;
|
||||||
|
private float _defaultSlidingSpeed;
|
||||||
|
private bool _slidingEnded = false;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
@@ -284,20 +468,33 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
XElement = grid.DoubleValue();
|
XElement = grid.DoubleValue();
|
||||||
XElement.SetLimits(limit);
|
XElement.SetLimits(limit);
|
||||||
XElement.SetCategory(category);
|
XElement.SetCategory(category);
|
||||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
XElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||||
|
|
||||||
YElement = grid.DoubleValue();
|
YElement = grid.DoubleValue();
|
||||||
YElement.SetLimits(limit);
|
YElement.SetLimits(limit);
|
||||||
YElement.SetCategory(category);
|
YElement.SetCategory(category);
|
||||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
YElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
|
||||||
ZElement = grid.DoubleValue();
|
ZElement = grid.DoubleValue();
|
||||||
ZElement.SetLimits(limit);
|
ZElement.SetLimits(limit);
|
||||||
ZElement.SetCategory(category);
|
ZElement.SetCategory(category);
|
||||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
ZElement.ValueBox.SlidingEnd += () =>
|
||||||
|
{
|
||||||
|
_slidingEnded = true;
|
||||||
|
ClearToken();
|
||||||
|
};
|
||||||
|
|
||||||
if (LinkedLabel != null)
|
if (LinkedLabel != null)
|
||||||
{
|
{
|
||||||
@@ -316,22 +513,115 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnXValueChanged()
|
||||||
|
{
|
||||||
|
if (IsSetBlocked)
|
||||||
|
return;
|
||||||
|
_valueChanged = ValueChanged.X;
|
||||||
|
OnValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnYValueChanged()
|
||||||
|
{
|
||||||
|
if (IsSetBlocked)
|
||||||
|
return;
|
||||||
|
_valueChanged = ValueChanged.Y;
|
||||||
|
OnValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnZValueChanged()
|
||||||
|
{
|
||||||
|
if (IsSetBlocked)
|
||||||
|
return;
|
||||||
|
_valueChanged = ValueChanged.Z;
|
||||||
|
OnValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnValueChanged()
|
private void OnValueChanged()
|
||||||
{
|
{
|
||||||
if (IsSetBlocked || Values == null)
|
if (IsSetBlocked || Values == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var xValue = XElement.ValueBox.Value;
|
||||||
|
var yValue = YElement.ValueBox.Value;
|
||||||
|
var zValue = ZElement.ValueBox.Value;
|
||||||
|
|
||||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||||
var token = isSliding ? this : null;
|
var token = isSliding ? this : null;
|
||||||
var value = new Double3(XElement.ValueBox.Value, YElement.ValueBox.Value, ZElement.ValueBox.Value);
|
var value = new Double3(xValue, yValue, zValue);
|
||||||
object v = Values[0];
|
if (HasDifferentValues && Values.Count > 1)
|
||||||
if (v is Vector3)
|
{
|
||||||
v = (Vector3)value;
|
var newObjects = new object[Values.Count];
|
||||||
else if (v is Float3)
|
// Handle Sliding
|
||||||
v = (Float3)value;
|
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||||
else if (v is Double3)
|
{
|
||||||
v = (Double3)value;
|
// TODO: handle linked values
|
||||||
SetValue(v, token);
|
Double3 average = Double3.Zero;
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
var castedValue = Double3.Zero;
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
castedValue = asVector3;
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
castedValue = asFloat3;
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
castedValue = asDouble3;
|
||||||
|
|
||||||
|
average += castedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
average /= Values.Count;
|
||||||
|
|
||||||
|
var newValue = value - average;
|
||||||
|
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? (float)newValue.X : 0, _valueChanged == ValueChanged.Y ? (float)newValue.Y : 0, _valueChanged == ValueChanged.Z ? (float)newValue.Z : 0);
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||||
|
|
||||||
|
newObjects[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture last sliding value
|
||||||
|
if (_slidingEnded)
|
||||||
|
_slidingEnded = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: handle linked values
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
object v = Values[i];
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
v = new Float3(_valueChanged == ValueChanged.X ? (float)xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? (float)yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? (float)zValue : asFloat3.Z);
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
|
||||||
|
|
||||||
|
newObjects[i] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetValue(newObjects, token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object v = Values[0];
|
||||||
|
if (v is Vector3)
|
||||||
|
v = (Vector3)value;
|
||||||
|
else if (v is Float3)
|
||||||
|
v = (Float3)value;
|
||||||
|
else if (v is Double3)
|
||||||
|
v = (Double3)value;
|
||||||
|
SetValue(v, token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -341,7 +631,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
if (HasDifferentValues)
|
if (HasDifferentValues)
|
||||||
{
|
{
|
||||||
// TODO: support different values for ValueBox<T>
|
// Get which values are different
|
||||||
|
bool xDifferent = false;
|
||||||
|
bool yDifferent = false;
|
||||||
|
bool zDifferent = false;
|
||||||
|
Double3 cachedFirstValue = Double3.Zero;
|
||||||
|
Double3 average = Double3.Zero;
|
||||||
|
for (int i = 0; i < Values.Count; i++)
|
||||||
|
{
|
||||||
|
var v = Values[i];
|
||||||
|
var value = Double3.Zero;
|
||||||
|
if (v is Vector3 asVector3)
|
||||||
|
value = asVector3;
|
||||||
|
else if (v is Float3 asFloat3)
|
||||||
|
value = asFloat3;
|
||||||
|
else if (v is Double3 asDouble3)
|
||||||
|
value = asDouble3;
|
||||||
|
|
||||||
|
average += value;
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
cachedFirstValue = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Mathd.NearEqual(cachedFirstValue.X, value.X))
|
||||||
|
xDifferent = true;
|
||||||
|
if (!Mathd.NearEqual(cachedFirstValue.Y, value.Y))
|
||||||
|
yDifferent = true;
|
||||||
|
if (!Mathd.NearEqual(cachedFirstValue.Z, value.Z))
|
||||||
|
zDifferent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
average /= Values.Count;
|
||||||
|
|
||||||
|
if (!xDifferent)
|
||||||
|
{
|
||||||
|
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
XElement.ValueBox.Value = average.X;
|
||||||
|
else
|
||||||
|
XElement.ValueBox.SlideSpeed = 0;
|
||||||
|
XElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!yDifferent)
|
||||||
|
{
|
||||||
|
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
YElement.ValueBox.Value = average.Y;
|
||||||
|
else
|
||||||
|
YElement.ValueBox.SlideSpeed = 0;
|
||||||
|
YElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zDifferent)
|
||||||
|
{
|
||||||
|
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (AllowSlidingForDifferentValues)
|
||||||
|
ZElement.ValueBox.Value = average.Z;
|
||||||
|
else
|
||||||
|
ZElement.ValueBox.SlideSpeed = 0;
|
||||||
|
ZElement.ValueBox.Text = "---";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -355,6 +717,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
XElement.ValueBox.Value = value.X;
|
XElement.ValueBox.Value = value.X;
|
||||||
YElement.ValueBox.Value = value.Y;
|
YElement.ValueBox.Value = value.Y;
|
||||||
ZElement.ValueBox.Value = value.Z;
|
ZElement.ValueBox.Value = value.Z;
|
||||||
|
|
||||||
|
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
{
|
||||||
|
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
}
|
||||||
|
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
{
|
||||||
|
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
}
|
||||||
|
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||||
|
{
|
||||||
|
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -385,10 +386,21 @@ namespace FlaxEditor.CustomEditors
|
|||||||
if (instanceValues.Count != Count)
|
if (instanceValues.Count != Count)
|
||||||
throw new ArgumentException();
|
throw new ArgumentException();
|
||||||
|
|
||||||
for (int i = 0; i < Count; i++)
|
if (value is IList l && l.Count == Count)
|
||||||
{
|
{
|
||||||
Info.SetValue(instanceValues[i], value);
|
for (int i = 0; i < Count; i++)
|
||||||
this[i] = Info.GetValue(instanceValues[i]);
|
{
|
||||||
|
Info.SetValue(instanceValues[i], l[i]);
|
||||||
|
this[i] = Info.GetValue(instanceValues[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
Info.SetValue(instanceValues[i], value);
|
||||||
|
this[i] = Info.GetValue(instanceValues[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -673,6 +673,7 @@ bool Editor::Init()
|
|||||||
Managed = New<ManagedEditor>();
|
Managed = New<ManagedEditor>();
|
||||||
|
|
||||||
// Show splash screen
|
// Show splash screen
|
||||||
|
if (!CommandLine::Options.Headless.IsTrue())
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Splash");
|
PROFILE_CPU_NAMED("Splash");
|
||||||
if (EditorImpl::Splash == nullptr)
|
if (EditorImpl::Splash == nullptr)
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ namespace FlaxEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
|
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
|
||||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode;
|
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit;
|
||||||
private string _projectToOpen;
|
private string _projectToOpen;
|
||||||
private float _lastAutoSaveTimer;
|
private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f;
|
||||||
private Button _saveNowButton;
|
private Button _saveNowButton;
|
||||||
private Button _cancelSaveButton;
|
private Button _cancelSaveButton;
|
||||||
private bool _autoSaveNow;
|
private bool _autoSaveNow;
|
||||||
@@ -258,10 +258,11 @@ namespace FlaxEditor
|
|||||||
Instance = this;
|
Instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Init(bool isHeadless, bool skipCompile, bool newProject, Guid startupScene)
|
internal void Init(StartupFlags flags, Guid startupScene)
|
||||||
{
|
{
|
||||||
Log("Setting up C# Editor...");
|
Log("Setting up C# Editor...");
|
||||||
_isHeadlessMode = isHeadless;
|
_isHeadlessMode = flags.HasFlag(StartupFlags.Headless);
|
||||||
|
_autoExit = flags.HasFlag(StartupFlags.Exit);
|
||||||
_startupSceneCmdLine = startupScene;
|
_startupSceneCmdLine = startupScene;
|
||||||
|
|
||||||
Profiler.BeginEvent("Projects");
|
Profiler.BeginEvent("Projects");
|
||||||
@@ -297,11 +298,11 @@ namespace FlaxEditor
|
|||||||
StateMachine = new EditorStateMachine(this);
|
StateMachine = new EditorStateMachine(this);
|
||||||
Undo = new EditorUndo(this);
|
Undo = new EditorUndo(this);
|
||||||
|
|
||||||
if (newProject)
|
if (flags.HasFlag(StartupFlags.NewProject))
|
||||||
InitProject();
|
InitProject();
|
||||||
EnsureState<LoadingState>();
|
EnsureState<LoadingState>();
|
||||||
Log("Editor init");
|
Log("Editor init");
|
||||||
if (isHeadless)
|
if (_isHeadlessMode)
|
||||||
Log("Running in headless mode");
|
Log("Running in headless mode");
|
||||||
|
|
||||||
// Note: we don't sort modules before Init (optimized)
|
// Note: we don't sort modules before Init (optimized)
|
||||||
@@ -357,7 +358,7 @@ namespace FlaxEditor
|
|||||||
InitializationStart?.Invoke();
|
InitializationStart?.Invoke();
|
||||||
|
|
||||||
// Start Editor initialization ending phrase (will wait for scripts compilation result)
|
// Start Editor initialization ending phrase (will wait for scripts compilation result)
|
||||||
StateMachine.LoadingState.StartInitEnding(skipCompile);
|
StateMachine.LoadingState.StartInitEnding(flags.HasFlag(StartupFlags.SkipCompile));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RegisterModule(EditorModule module)
|
internal void RegisterModule(EditorModule module)
|
||||||
@@ -486,6 +487,15 @@ namespace FlaxEditor
|
|||||||
{
|
{
|
||||||
StateMachine.Update();
|
StateMachine.Update();
|
||||||
UpdateAutoSave();
|
UpdateAutoSave();
|
||||||
|
if (_autoExit && StateMachine.CurrentState == StateMachine.EditingSceneState)
|
||||||
|
{
|
||||||
|
_autoExitTimeout -= Time.UnscaledGameTime;
|
||||||
|
if (_autoExitTimeout < 0.0f)
|
||||||
|
{
|
||||||
|
Log("Auto exit");
|
||||||
|
Engine.RequestExit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!StateMachine.IsPlayMode)
|
if (!StateMachine.IsPlayMode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using FlaxEditor.Scripting;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Json;
|
using FlaxEngine.Json;
|
||||||
@@ -29,6 +28,7 @@ namespace FlaxEditor.GUI
|
|||||||
internal Float2 _mousePos = Float2.Minimum;
|
internal Float2 _mousePos = Float2.Minimum;
|
||||||
internal bool _isMovingSelection;
|
internal bool _isMovingSelection;
|
||||||
internal bool _isMovingTangent;
|
internal bool _isMovingTangent;
|
||||||
|
internal bool _movedView;
|
||||||
internal bool _movedKeyframes;
|
internal bool _movedKeyframes;
|
||||||
private TangentPoint _movingTangent;
|
private TangentPoint _movingTangent;
|
||||||
private Float2 _movingSelectionStart;
|
private Float2 _movingSelectionStart;
|
||||||
@@ -190,31 +190,28 @@ namespace FlaxEditor.GUI
|
|||||||
// Moving view
|
// Moving view
|
||||||
if (_rightMouseDown)
|
if (_rightMouseDown)
|
||||||
{
|
{
|
||||||
var delta = location - _movingViewLastPos;
|
var movingViewPos = Parent.PointToParent(PointToParent(location));
|
||||||
|
var delta = movingViewPos - _movingViewLastPos;
|
||||||
if (_editor.CustomViewPanning != null)
|
if (_editor.CustomViewPanning != null)
|
||||||
delta = _editor.CustomViewPanning(delta);
|
delta = _editor.CustomViewPanning(delta);
|
||||||
delta *= GetUseModeMask(_editor.EnablePanning) * _editor.ViewScale;
|
delta *= GetUseModeMask(_editor.EnablePanning);
|
||||||
if (delta.LengthSquared > 0.01f)
|
if (delta.LengthSquared > 0.01f)
|
||||||
{
|
{
|
||||||
_editor._mainPanel.ViewOffset += delta;
|
_editor._mainPanel.ViewOffset += delta;
|
||||||
_movingViewLastPos = location;
|
_movingViewLastPos = movingViewPos;
|
||||||
|
_movedView = true;
|
||||||
if (_editor.CustomViewPanning != null)
|
if (_editor.CustomViewPanning != null)
|
||||||
{
|
{
|
||||||
Cursor = CursorType.SizeAll;
|
if (Cursor == CursorType.Default)
|
||||||
|
Cursor = CursorType.SizeAll;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (_editor.EnablePanning)
|
switch (_editor.EnablePanning)
|
||||||
{
|
{
|
||||||
case UseMode.Vertical:
|
case UseMode.Vertical: Cursor = CursorType.SizeNS; break;
|
||||||
Cursor = CursorType.SizeNS;
|
case UseMode.Horizontal: Cursor = CursorType.SizeWE; break;
|
||||||
break;
|
case UseMode.On: Cursor = CursorType.SizeAll; break;
|
||||||
case UseMode.Horizontal:
|
|
||||||
Cursor = CursorType.SizeWE;
|
|
||||||
break;
|
|
||||||
case UseMode.On:
|
|
||||||
Cursor = CursorType.SizeAll;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,11 +231,10 @@ namespace FlaxEditor.GUI
|
|||||||
else if (_isMovingTangent)
|
else if (_isMovingTangent)
|
||||||
{
|
{
|
||||||
var viewRect = _editor._mainPanel.GetClientArea();
|
var viewRect = _editor._mainPanel.GetClientArea();
|
||||||
var direction = _movingTangent.IsIn ? -1.0f : 1.0f;
|
|
||||||
var k = _editor.GetKeyframe(_movingTangent.Index);
|
var k = _editor.GetKeyframe(_movingTangent.Index);
|
||||||
var kv = _editor.GetKeyframeValue(k);
|
var kv = _editor.GetKeyframeValue(k);
|
||||||
var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component);
|
var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component);
|
||||||
_movingTangent.TangentValue = direction * (PointToKeyframes(location, ref viewRect).Y - value);
|
_movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value;
|
||||||
_editor.UpdateTangents();
|
_editor.UpdateTangents();
|
||||||
Cursor = CursorType.SizeNS;
|
Cursor = CursorType.SizeNS;
|
||||||
_movedKeyframes = true;
|
_movedKeyframes = true;
|
||||||
@@ -299,7 +295,8 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
_rightMouseDown = true;
|
_rightMouseDown = true;
|
||||||
_rightMouseDownPos = location;
|
_rightMouseDownPos = location;
|
||||||
_movingViewLastPos = location;
|
_movedView = false;
|
||||||
|
_movingViewLastPos = Parent.PointToParent(PointToParent(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any node is under the mouse
|
// Check if any node is under the mouse
|
||||||
@@ -444,7 +441,7 @@ namespace FlaxEditor.GUI
|
|||||||
Cursor = CursorType.Default;
|
Cursor = CursorType.Default;
|
||||||
|
|
||||||
// Check if no move has been made at all
|
// Check if no move has been made at all
|
||||||
if (Float2.Distance(ref location, ref _rightMouseDownPos) < 2.0f)
|
if (!_movedView)
|
||||||
{
|
{
|
||||||
var selectionCount = _editor.SelectionCount;
|
var selectionCount = _editor.SelectionCount;
|
||||||
var point = GetChildAt(location) as KeyframePoint;
|
var point = GetChildAt(location) as KeyframePoint;
|
||||||
@@ -512,6 +509,27 @@ namespace FlaxEditor.GUI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDoubleClick(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Add keyframe on double click
|
||||||
|
var child = GetChildAt(location);
|
||||||
|
if (child is not KeyframePoint &&
|
||||||
|
child is not TangentPoint &&
|
||||||
|
_editor.KeyframesCount < _editor.MaxKeyframes)
|
||||||
|
{
|
||||||
|
var viewRect = _editor._mainPanel.GetClientArea();
|
||||||
|
var pos = PointToKeyframes(location, ref viewRect);
|
||||||
|
_editor.AddKeyframe(pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnMouseWheel(Float2 location, float delta)
|
public override bool OnMouseWheel(Float2 location, float delta)
|
||||||
{
|
{
|
||||||
@@ -519,10 +537,29 @@ namespace FlaxEditor.GUI
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Zoom in/out
|
// Zoom in/out
|
||||||
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && RootWindow.GetKey(KeyboardKeys.Control))
|
var zoom = RootWindow.GetKey(KeyboardKeys.Control);
|
||||||
|
var zoomAlt = RootWindow.GetKey(KeyboardKeys.Shift);
|
||||||
|
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && (zoom || zoomAlt))
|
||||||
{
|
{
|
||||||
// TODO: preserve the view center point for easier zooming
|
// Cache mouse location in curve-space
|
||||||
_editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * (delta * 0.1f);
|
var viewRect = _editor._mainPanel.GetClientArea();
|
||||||
|
var locationInKeyframes = PointToKeyframes(location, ref viewRect);
|
||||||
|
var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
|
||||||
|
|
||||||
|
// Scale relative to the curve size
|
||||||
|
var scale = new Float2(delta * 0.1f);
|
||||||
|
_editor._mainPanel.GetDesireClientArea(out var mainPanelArea);
|
||||||
|
var curveScale = mainPanelArea.Size / _editor._contents.Size;
|
||||||
|
scale *= curveScale;
|
||||||
|
if (zoomAlt)
|
||||||
|
scale.X = 0; // Scale Y axis only
|
||||||
|
scale *= GetUseModeMask(_editor.EnableZoom); // Mask scale depending on allowed usage
|
||||||
|
_editor.ViewScale += scale;
|
||||||
|
|
||||||
|
// Zoom towards the mouse position
|
||||||
|
var locationInEditorAfter = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
|
||||||
|
var locationInEditorDelta = locationInEditorAfter - locationInEditorBefore;
|
||||||
|
_editor.ViewOffset -= locationInEditorDelta;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -671,8 +671,22 @@ namespace FlaxEditor.GUI
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ShowWholeCurve()
|
public override void ShowWholeCurve()
|
||||||
{
|
{
|
||||||
ViewScale = ApplyUseModeMask(EnableZoom, _mainPanel.Size / _contents.Size, ViewScale);
|
_mainPanel.GetDesireClientArea(out var mainPanelArea);
|
||||||
ViewOffset = ApplyUseModeMask(EnablePanning, -_mainPanel.ControlsBounds.Location, ViewOffset);
|
ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale);
|
||||||
|
Float2 minPos = Float2.Maximum;
|
||||||
|
foreach (var point in _points)
|
||||||
|
{
|
||||||
|
var pos = point.PointToParent(point.Location);
|
||||||
|
Float2.Min(ref minPos, ref pos, out minPos);
|
||||||
|
}
|
||||||
|
var minPosPoint = _contents.PointToParent(ref minPos);
|
||||||
|
var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0);
|
||||||
|
scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll);
|
||||||
|
if (_mainPanel.HScrollBar != null)
|
||||||
|
_mainPanel.HScrollBar.TargetValue = scroll.X;
|
||||||
|
if (_mainPanel.VScrollBar != null)
|
||||||
|
_mainPanel.VScrollBar.TargetValue = scroll.Y;
|
||||||
|
|
||||||
UpdateKeyframes();
|
UpdateKeyframes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -923,6 +937,11 @@ namespace FlaxEditor.GUI
|
|||||||
KeyframesEditorUtils.Paste(this);
|
KeyframesEditorUtils.Paste(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (options.FocusSelection.Process(this))
|
||||||
|
{
|
||||||
|
ShowWholeCurve();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1384,9 +1403,7 @@ namespace FlaxEditor.GUI
|
|||||||
// Calculate bounds
|
// Calculate bounds
|
||||||
var bounds = _points[0].Bounds;
|
var bounds = _points[0].Bounds;
|
||||||
for (var i = 1; i < _points.Count; i++)
|
for (var i = 1; i < _points.Count; i++)
|
||||||
{
|
|
||||||
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust contents bounds to fill the curve area
|
// Adjust contents bounds to fill the curve area
|
||||||
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
||||||
@@ -1632,6 +1649,7 @@ namespace FlaxEditor.GUI
|
|||||||
var o = _keyframes[p.Index - 1];
|
var o = _keyframes[p.Index - 1];
|
||||||
var oValue = Accessor.GetCurveValue(ref o.Value, p.Component);
|
var oValue = Accessor.GetCurveValue(ref o.Value, p.Component);
|
||||||
var slope = (value - oValue) / (k.Time - o.Time);
|
var slope = (value - oValue) / (k.Time - o.Time);
|
||||||
|
slope = -slope;
|
||||||
Accessor.SetCurveValue(slope, ref k.TangentIn, p.Component);
|
Accessor.SetCurveValue(slope, ref k.TangentIn, p.Component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2116,9 +2134,7 @@ namespace FlaxEditor.GUI
|
|||||||
// Calculate bounds
|
// Calculate bounds
|
||||||
var bounds = _points[0].Bounds;
|
var bounds = _points[0].Bounds;
|
||||||
for (int i = 1; i < _points.Count; i++)
|
for (int i = 1; i < _points.Count; i++)
|
||||||
{
|
|
||||||
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust contents bounds to fill the curve area
|
// Adjust contents bounds to fill the curve area
|
||||||
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
||||||
@@ -2184,12 +2200,12 @@ namespace FlaxEditor.GUI
|
|||||||
|
|
||||||
var tangent = t.TangentValue;
|
var tangent = t.TangentValue;
|
||||||
var direction = t.IsIn ? -1.0f : 1.0f;
|
var direction = t.IsIn ? -1.0f : 1.0f;
|
||||||
var offset = 30.0f * direction;
|
var offset = 30.0f;
|
||||||
var location = GetKeyframePoint(ref k, selectedComponent);
|
var location = GetKeyframePoint(ref k, selectedComponent);
|
||||||
t.Size = KeyframesSize / ViewScale;
|
t.Size = KeyframesSize / ViewScale;
|
||||||
t.Location = new Float2
|
t.Location = new Float2
|
||||||
(
|
(
|
||||||
location.X * UnitsPerSecond - t.Width * 0.5f + offset,
|
location.X * UnitsPerSecond - t.Width * 0.5f + offset * direction,
|
||||||
location.Y * -UnitsPerSecond - t.Height * 0.5f + curveContentAreaBounds.Height - offset * tangent
|
location.Y * -UnitsPerSecond - t.Height * 0.5f + curveContentAreaBounds.Height - offset * tangent
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2265,14 +2281,13 @@ namespace FlaxEditor.GUI
|
|||||||
var startTangent = Accessor.GetCurveValue(ref startK.TangentOut, component);
|
var startTangent = Accessor.GetCurveValue(ref startK.TangentOut, component);
|
||||||
var endTangent = Accessor.GetCurveValue(ref endK.TangentIn, component);
|
var endTangent = Accessor.GetCurveValue(ref endK.TangentIn, component);
|
||||||
|
|
||||||
var offset = (end.X - start.X) * 0.5f;
|
var tangentScale = (endK.Time - startK.Time) / 3.0f;
|
||||||
|
|
||||||
var p1 = PointFromKeyframes(start, ref viewRect);
|
var p1 = PointFromKeyframes(start, ref viewRect);
|
||||||
var p2 = PointFromKeyframes(start + new Float2(offset, startTangent * offset), ref viewRect);
|
var p2 = PointFromKeyframes(start + new Float2(0, startTangent * tangentScale), ref viewRect);
|
||||||
var p3 = PointFromKeyframes(end - new Float2(offset, endTangent * offset), ref viewRect);
|
var p3 = PointFromKeyframes(end + new Float2(0, endTangent * tangentScale), ref viewRect);
|
||||||
var p4 = PointFromKeyframes(end, ref viewRect);
|
var p4 = PointFromKeyframes(end, ref viewRect);
|
||||||
|
|
||||||
Render2D.DrawBezier(p1, p2, p3, p4, color);
|
Render2D.DrawSpline(p1, p2, p3, p4, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
Source/Editor/GUI/Splitter.cs
Normal file
86
Source/Editor/GUI/Splitter.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEditor.GUI
|
||||||
|
{
|
||||||
|
sealed class Splitter : Control
|
||||||
|
{
|
||||||
|
private bool _clicked;
|
||||||
|
|
||||||
|
public Action<Float2> Moved;
|
||||||
|
public const float DefaultHeight = 5.0f;
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
if (IsMouseOver || _clicked)
|
||||||
|
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEndMouseCapture()
|
||||||
|
{
|
||||||
|
base.OnEndMouseCapture();
|
||||||
|
|
||||||
|
_clicked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Defocus()
|
||||||
|
{
|
||||||
|
base.Defocus();
|
||||||
|
|
||||||
|
_clicked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseEnter(Float2 location)
|
||||||
|
{
|
||||||
|
base.OnMouseEnter(location);
|
||||||
|
|
||||||
|
Cursor = CursorType.SizeNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseLeave()
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
|
||||||
|
base.OnMouseLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
_clicked = true;
|
||||||
|
Focus();
|
||||||
|
StartMouseCapture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnMouseDown(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseMove(Float2 location)
|
||||||
|
{
|
||||||
|
base.OnMouseMove(location);
|
||||||
|
|
||||||
|
if (_clicked)
|
||||||
|
{
|
||||||
|
Moved(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (button == MouseButton.Left && _clicked)
|
||||||
|
{
|
||||||
|
_clicked = false;
|
||||||
|
EndMouseCapture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnMouseUp(location, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -230,7 +230,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Draw all ticks
|
// Draw all ticks
|
||||||
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
|
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 2);
|
||||||
var lStep = _tickSteps[l];
|
var lStep = _tickSteps[l];
|
||||||
var lNextStep = _tickSteps[l + 1];
|
var lNextStep = _tickSteps[l + 1];
|
||||||
int startTick = Mathf.FloorToInt(min / lStep);
|
int startTick = Mathf.FloorToInt(min / lStep);
|
||||||
|
|||||||
@@ -1446,6 +1446,17 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
{
|
{
|
||||||
GetTracks(SelectedTracks[i], tracks);
|
GetTracks(SelectedTracks[i], tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the lowest track position for selection
|
||||||
|
int lowestTrackLocation = Tracks.Count - 1;
|
||||||
|
for (int i = 0; i < tracks.Count; i++)
|
||||||
|
{
|
||||||
|
var trackToDelete = tracks[i];
|
||||||
|
if (trackToDelete.TrackIndex < lowestTrackLocation)
|
||||||
|
{
|
||||||
|
lowestTrackLocation = trackToDelete.TrackIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
SelectedTracks.Clear();
|
SelectedTracks.Clear();
|
||||||
if (withUndo && Undo != null && Undo.Enabled)
|
if (withUndo && Undo != null && Undo.Enabled)
|
||||||
{
|
{
|
||||||
@@ -1468,6 +1479,18 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
OnTracksChanged();
|
OnTracksChanged();
|
||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
|
|
||||||
|
// Select track above deleted tracks unless track is first track
|
||||||
|
if (Tracks.Count > 0)
|
||||||
|
{
|
||||||
|
if (lowestTrackLocation - 1 >= 0)
|
||||||
|
Select(Tracks[lowestTrackLocation - 1]);
|
||||||
|
else
|
||||||
|
Select(Tracks[0]);
|
||||||
|
|
||||||
|
SelectedTracks[0].Focus();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1655,6 +1678,14 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
}
|
}
|
||||||
OnTracksChanged();
|
OnTracksChanged();
|
||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
|
|
||||||
|
// Deselect and select new clones.
|
||||||
|
Deselect();
|
||||||
|
foreach (var clone in clones)
|
||||||
|
{
|
||||||
|
Select(clone, true);
|
||||||
|
}
|
||||||
|
|
||||||
SelectedTracks[0].Focus();
|
SelectedTracks[0].Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,87 +19,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// <seealso cref="MemberTrack" />
|
/// <seealso cref="MemberTrack" />
|
||||||
public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext
|
public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext
|
||||||
{
|
{
|
||||||
private sealed class Splitter : Control
|
|
||||||
{
|
|
||||||
private bool _clicked;
|
|
||||||
internal CurvePropertyTrackBase _track;
|
|
||||||
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
var style = Style.Current;
|
|
||||||
if (IsMouseOver || _clicked)
|
|
||||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnEndMouseCapture()
|
|
||||||
{
|
|
||||||
base.OnEndMouseCapture();
|
|
||||||
|
|
||||||
_clicked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Defocus()
|
|
||||||
{
|
|
||||||
base.Defocus();
|
|
||||||
|
|
||||||
_clicked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMouseEnter(Float2 location)
|
|
||||||
{
|
|
||||||
base.OnMouseEnter(location);
|
|
||||||
|
|
||||||
Cursor = CursorType.SizeNS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMouseLeave()
|
|
||||||
{
|
|
||||||
Cursor = CursorType.Default;
|
|
||||||
|
|
||||||
base.OnMouseLeave();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
|
||||||
{
|
|
||||||
if (button == MouseButton.Left)
|
|
||||||
{
|
|
||||||
_clicked = true;
|
|
||||||
Focus();
|
|
||||||
StartMouseCapture();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnMouseDown(location, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnMouseMove(Float2 location)
|
|
||||||
{
|
|
||||||
base.OnMouseMove(location);
|
|
||||||
|
|
||||||
if (_clicked)
|
|
||||||
{
|
|
||||||
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
|
|
||||||
if (!Mathf.NearEqual(height, _track._expandedHeight))
|
|
||||||
{
|
|
||||||
_track.Height = _track._expandedHeight = height;
|
|
||||||
_track.Timeline.ArrangeTracks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
|
||||||
{
|
|
||||||
if (button == MouseButton.Left && _clicked)
|
|
||||||
{
|
|
||||||
_clicked = false;
|
|
||||||
EndMouseCapture();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnMouseUp(location, button);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] _curveEditingStartData;
|
private byte[] _curveEditingStartData;
|
||||||
private float _expandedHeight = 120.0f;
|
private float _expandedHeight = 120.0f;
|
||||||
private Splitter _splitter;
|
private Splitter _splitter;
|
||||||
@@ -251,12 +170,21 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{
|
{
|
||||||
_splitter = new Splitter
|
_splitter = new Splitter
|
||||||
{
|
{
|
||||||
_track = this,
|
Moved = OnSplitterMoved,
|
||||||
Parent = Curve,
|
Parent = Curve,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var splitterHeight = 5.0f;
|
_splitter.Bounds = new Rectangle(0, Curve.Height - Splitter.DefaultHeight, Curve.Width, Splitter.DefaultHeight);
|
||||||
_splitter.Bounds = new Rectangle(0, Curve.Height - splitterHeight, Curve.Width, splitterHeight);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSplitterMoved(Float2 location)
|
||||||
|
{
|
||||||
|
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
|
||||||
|
if (!Mathf.NearEqual(height, _expandedHeight))
|
||||||
|
{
|
||||||
|
Height = _expandedHeight = height;
|
||||||
|
Timeline.ArrangeTracks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,19 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
MaxMediaCount = 1;
|
MaxMediaCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDuplicated(Track clone)
|
||||||
|
{
|
||||||
|
base.OnDuplicated(clone);
|
||||||
|
|
||||||
|
// Clone overriden parameters
|
||||||
|
if (clone is ParticleEmitterTrack cloneTrack)
|
||||||
|
{
|
||||||
|
foreach (var e in ParametersOverrides)
|
||||||
|
cloneTrack.ParametersOverrides.Add(e.Key, e.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
|||||||
_timeline.AddTrack(track, false);
|
_timeline.AddTrack(track, false);
|
||||||
track.TrackIndex = _order;
|
track.TrackIndex = _order;
|
||||||
_timeline.OnTracksOrderChanged();
|
_timeline.OnTracksOrderChanged();
|
||||||
|
_timeline.Focus();
|
||||||
|
_timeline.Select(track);
|
||||||
|
track.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Remove()
|
private void Remove()
|
||||||
@@ -68,10 +71,11 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
|||||||
var track = _timeline.FindTrack(_name);
|
var track = _timeline.FindTrack(_name);
|
||||||
if (track == null)
|
if (track == null)
|
||||||
{
|
{
|
||||||
Editor.LogWarning($"Cannot remove track {_name}. It doesn't already exists.");
|
Editor.LogWarning($"Cannot remove track {_name}. It doesn't exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_timeline.Delete(track, false);
|
_timeline.Delete(track, false);
|
||||||
|
_timeline.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ActionString => _isAdd ? "Add track" : "Remove track";
|
public string ActionString => _isAdd ? "Add track" : "Remove track";
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ ManagedEditor::~ManagedEditor()
|
|||||||
void ManagedEditor::Init()
|
void ManagedEditor::Init()
|
||||||
{
|
{
|
||||||
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
|
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
|
||||||
void* args[4];
|
void* args[2];
|
||||||
MClass* mclass = GetClass();
|
MClass* mclass = GetClass();
|
||||||
if (mclass == nullptr)
|
if (mclass == nullptr)
|
||||||
{
|
{
|
||||||
@@ -193,18 +193,22 @@ void ManagedEditor::Init()
|
|||||||
LOG(Fatal, "Failed to create editor instance.");
|
LOG(Fatal, "Failed to create editor instance.");
|
||||||
}
|
}
|
||||||
MObject* exception = nullptr;
|
MObject* exception = nullptr;
|
||||||
bool isHeadless = CommandLine::Options.Headless.IsTrue();
|
StartupFlags flags = StartupFlags::None;
|
||||||
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
|
if (CommandLine::Options.Headless.IsTrue())
|
||||||
bool newProject = CommandLine::Options.NewProject.IsTrue();
|
flags |= StartupFlags::Headless;
|
||||||
args[0] = &isHeadless;
|
if (CommandLine::Options.SkipCompile.IsTrue())
|
||||||
args[1] = &skipCompile;
|
flags |= StartupFlags::SkipCompile;
|
||||||
args[2] = &newProject;
|
if (CommandLine::Options.NewProject.IsTrue())
|
||||||
|
flags |= StartupFlags::NewProject;
|
||||||
|
if (CommandLine::Options.Exit.IsTrue())
|
||||||
|
flags |= StartupFlags::Exit;
|
||||||
|
args[0] = &flags;
|
||||||
Guid sceneId;
|
Guid sceneId;
|
||||||
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
|
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
|
||||||
{
|
{
|
||||||
sceneId = Guid::Empty;
|
sceneId = Guid::Empty;
|
||||||
}
|
}
|
||||||
args[3] = &sceneId;
|
args[1] = &sceneId;
|
||||||
initMethod->Invoke(instance, args, &exception);
|
initMethod->Invoke(instance, args, &exception);
|
||||||
if (exception)
|
if (exception)
|
||||||
{
|
{
|
||||||
@@ -219,7 +223,7 @@ void ManagedEditor::Init()
|
|||||||
WasExitCalled = false;
|
WasExitCalled = false;
|
||||||
|
|
||||||
// Load scripts if auto-load on startup is disabled
|
// Load scripts if auto-load on startup is disabled
|
||||||
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || skipCompile)
|
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || EnumHasAllFlags(flags, StartupFlags::SkipCompile))
|
||||||
{
|
{
|
||||||
LOG(Info, "Loading managed assemblies (due to disabled compilation on startup)");
|
LOG(Info, "Loading managed assemblies (due to disabled compilation on startup)");
|
||||||
Scripting::Load();
|
Scripting::Load();
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ API_CLASS(Namespace="FlaxEditor", Name="Editor", NoSpawn, NoConstructor) class M
|
|||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
|
||||||
static Guid ObjectID;
|
static Guid ObjectID;
|
||||||
|
|
||||||
|
API_ENUM(Attributes="Flags", Internal) enum class StartupFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Headless = 1,
|
||||||
|
SkipCompile = 2,
|
||||||
|
NewProject = 4,
|
||||||
|
Exit = 8,
|
||||||
|
};
|
||||||
|
|
||||||
struct InternalOptions
|
struct InternalOptions
|
||||||
{
|
{
|
||||||
byte AutoReloadScriptsOnMainWindowFocus = 1;
|
byte AutoReloadScriptsOnMainWindowFocus = 1;
|
||||||
@@ -258,3 +267,5 @@ public:
|
|||||||
// [ScriptingObject]
|
// [ScriptingObject]
|
||||||
void DestroyManaged() override;
|
void DestroyManaged() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DECLARE_ENUM_OPERATORS(ManagedEditor::StartupFlags);
|
||||||
|
|||||||
@@ -274,11 +274,7 @@ namespace FlaxEditor.Modules
|
|||||||
private void Load()
|
private void Load()
|
||||||
{
|
{
|
||||||
if (!File.Exists(_cachePath))
|
if (!File.Exists(_cachePath))
|
||||||
{
|
|
||||||
Editor.LogWarning("Missing editor cache file.");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
_lastSaveTime = DateTime.UtcNow;
|
_lastSaveTime = DateTime.UtcNow;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -950,7 +950,10 @@ namespace FlaxEditor.Modules
|
|||||||
MainWindow = null;
|
MainWindow = null;
|
||||||
|
|
||||||
// Capture project icon screenshot (not in play mode and if editor was used for some time)
|
// Capture project icon screenshot (not in play mode and if editor was used for some time)
|
||||||
if (!Editor.StateMachine.IsPlayMode && Time.TimeSinceStartup >= 5.0f && GPUDevice.Instance?.RendererType != RendererType.Null)
|
if (!Editor.StateMachine.IsPlayMode &&
|
||||||
|
Time.TimeSinceStartup >= 5.0f &&
|
||||||
|
!Editor.IsHeadlessMode &&
|
||||||
|
GPUDevice.Instance?.RendererType != RendererType.Null)
|
||||||
{
|
{
|
||||||
Editor.Log("Capture project icon screenshot");
|
Editor.Log("Capture project icon screenshot");
|
||||||
_projectIconScreenshotTimeout = Time.TimeSinceStartup + 0.8f; // wait 800ms for a screenshot task
|
_projectIconScreenshotTimeout = Time.TimeSinceStartup + 0.8f; // wait 800ms for a screenshot task
|
||||||
|
|||||||
@@ -449,6 +449,13 @@ namespace FlaxEditor.Options
|
|||||||
[EditorDisplay("Visject", "Grid Snapping Size"), EditorOrder(551), Tooltip("Defines the size of the grid for nodes snapping."), VisibleIf(nameof(SurfaceGridSnapping))]
|
[EditorDisplay("Visject", "Grid Snapping Size"), EditorOrder(551), Tooltip("Defines the size of the grid for nodes snapping."), VisibleIf(nameof(SurfaceGridSnapping))]
|
||||||
public float SurfaceGridSnappingSize { get; set; } = 20.0f;
|
public float SurfaceGridSnappingSize { get; set; } = 20.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that indicates if a warning should be displayed when deleting a Visject parameter that is used in a graph.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(true)]
|
||||||
|
[EditorDisplay("Visject", "Warn when deleting used parameter"), EditorOrder(552)]
|
||||||
|
public bool WarnOnDeletingUsedVisjectParameter { get; set; } = true;
|
||||||
|
|
||||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||||
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
if (Level.ScenesCount > 1)
|
if (Level.ScenesCount > 1)
|
||||||
contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
|
contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
|
||||||
|
|
||||||
|
contextMenu.MaximumItemsInViewCount += 3;
|
||||||
base.OnContextMenu(contextMenu, window);
|
base.OnContextMenu(contextMenu, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,6 +293,14 @@ namespace FlaxEditor.Scripting
|
|||||||
/// <param name="obj">The object whose member value will be modified.</param>
|
/// <param name="obj">The object whose member value will be modified.</param>
|
||||||
/// <param name="value">The new member value.</param>
|
/// <param name="value">The new member value.</param>
|
||||||
void SetValue(object obj, object value);
|
void SetValue(object obj, object value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the method on a specific object (null if static) using the provided parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
|
||||||
|
/// <param name="parameters">List of parameters to provide.</param>
|
||||||
|
/// <returns>The value returned by the method.</returns>
|
||||||
|
object Invoke(object obj, object[] parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -691,6 +691,23 @@ namespace FlaxEditor.Scripting
|
|||||||
else
|
else
|
||||||
_custom.SetValue(obj, value);
|
_custom.SetValue(obj, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the method on a specific object (null if static) using the provided parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
|
||||||
|
/// <param name="parameters">List of parameters to provide.</param>
|
||||||
|
/// <returns>The value returned by the method.</returns>
|
||||||
|
public object Invoke(object obj = null, object[] parameters = null)
|
||||||
|
{
|
||||||
|
if (parameters == null)
|
||||||
|
parameters = Array.Empty<object>();
|
||||||
|
if (_managed is MethodInfo methodInfo)
|
||||||
|
return methodInfo.Invoke(obj, parameters);
|
||||||
|
if (_managed != null)
|
||||||
|
throw new NotSupportedException();
|
||||||
|
return _custom.Invoke(obj, parameters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// Represents single blend point.
|
/// Represents single blend point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||||
protected class BlendPoint : Control
|
internal class BlendPoint : Control
|
||||||
{
|
{
|
||||||
private readonly BlendPointsEditor _editor;
|
private readonly BlendPointsEditor _editor;
|
||||||
private readonly int _index;
|
private readonly int _index;
|
||||||
@@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Index => _index;
|
public int Index => _index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag that indicates that user is moving this point with a mouse.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMouseDown => _isMouseDown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BLend points array.
|
||||||
|
/// </summary>
|
||||||
|
internal IReadOnlyList<BlendPoint> BlendPoints => _blendPoints;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
/// <returns>The blend point control position.</returns>
|
/// <returns>The blend point control position.</returns>
|
||||||
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
||||||
{
|
{
|
||||||
|
if (_rangeX.IsZero)
|
||||||
|
{
|
||||||
|
var data0 = (Float4)_node.Values[0];
|
||||||
|
_rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
|
||||||
|
}
|
||||||
GetPointsArea(out var pointsArea);
|
GetPointsArea(out var pointsArea);
|
||||||
if (_is2D)
|
if (_is2D)
|
||||||
{
|
{
|
||||||
@@ -389,7 +405,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
pointsArea.Center.Y
|
pointsArea.Center.Y
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return pos - new Float2(BlendPoint.DefaultSize * 0.5f);
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -424,7 +440,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update blend point
|
// Update blend point
|
||||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
|
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location) - BlendPoint.DefaultSize * 0.5f;
|
||||||
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
|
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
|
||||||
var tooltip = asset?.ShortName ?? string.Empty;
|
var tooltip = asset?.ShortName ?? string.Empty;
|
||||||
tooltip += "\nX: " + location.X;
|
tooltip += "\nX: " + location.X;
|
||||||
@@ -532,81 +548,18 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
SetAsset((int)b.Tag, Guid.Empty);
|
SetAsset((int)b.Tag, Guid.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
|
||||||
{
|
|
||||||
// Draw line
|
|
||||||
Render2D.DrawLine(start, end, gridColor);
|
|
||||||
|
|
||||||
// Draw label
|
|
||||||
var labelWidth = 50.0f;
|
|
||||||
var labelHeight = 10.0f;
|
|
||||||
var labelMargin = 2.0f;
|
|
||||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
|
||||||
var hAlign = TextAlignment.Near;
|
|
||||||
Rectangle labelRect;
|
|
||||||
if (vertical)
|
|
||||||
{
|
|
||||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
|
||||||
if (isLast)
|
|
||||||
return; // Don't overlap with the first horizontal label
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
|
||||||
if (isLast)
|
|
||||||
{
|
|
||||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
|
||||||
hAlign = TextAlignment.Far;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
var rect = new Rectangle(Float2.Zero, Size);
|
var rect = new Rectangle(Float2.Zero, Size);
|
||||||
var containsFocus = ContainsFocus;
|
var containsFocus = ContainsFocus;
|
||||||
GetPointsArea(out var pointsArea);
|
|
||||||
var data0 = (Float4)_node.Values[0];
|
|
||||||
var rangeX = new Float2(data0.X, data0.Y);
|
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
_node.DrawEditorBackground(ref rect);
|
||||||
//Render2D.DrawRectangle(pointsArea, Color.Red);
|
|
||||||
|
|
||||||
// Grid
|
// Grid
|
||||||
int splits = 10;
|
_node.DrawEditorGrid(ref rect);
|
||||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
|
||||||
var labelColor = style.ForegroundDisabled;
|
|
||||||
var labelFont = style.FontSmall;
|
|
||||||
//var blendArea = BlendAreaRect;
|
|
||||||
var blendArea = pointsArea;
|
|
||||||
|
|
||||||
for (int i = 0; i <= splits; i++)
|
|
||||||
{
|
|
||||||
float alpha = (float)i / splits;
|
|
||||||
float x = blendArea.Left + blendArea.Width * alpha;
|
|
||||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
|
||||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
|
||||||
}
|
|
||||||
if (_is2D)
|
|
||||||
{
|
|
||||||
var rangeY = new Float2(data0.Z, data0.W);
|
|
||||||
for (int i = 0; i <= splits; i++)
|
|
||||||
{
|
|
||||||
float alpha = (float)i / splits;
|
|
||||||
float y = blendArea.Top + blendArea.Height * alpha;
|
|
||||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
|
|
||||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float y = blendArea.Center.Y;
|
|
||||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|
||||||
@@ -808,6 +761,87 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||||
|
{
|
||||||
|
// Draw line
|
||||||
|
Render2D.DrawLine(start, end, gridColor);
|
||||||
|
|
||||||
|
// Draw label
|
||||||
|
var labelWidth = 50.0f;
|
||||||
|
var labelHeight = 10.0f;
|
||||||
|
var labelMargin = 2.0f;
|
||||||
|
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
var hAlign = TextAlignment.Near;
|
||||||
|
Rectangle labelRect;
|
||||||
|
if (vertical)
|
||||||
|
{
|
||||||
|
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||||
|
if (isLast)
|
||||||
|
return; // Don't overlap with the first horizontal label
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||||
|
if (isLast)
|
||||||
|
{
|
||||||
|
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||||
|
hAlign = TextAlignment.Far;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom drawing logic for blend space background.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void DrawEditorBackground(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
Render2D.FillRectangle(rect, style.Background.AlphaMultiplied(0.5f));
|
||||||
|
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom drawing logic for blend space grid.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void DrawEditorGrid(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
var style = Style.Current;
|
||||||
|
_editor.GetPointsArea(out var pointsArea);
|
||||||
|
var data0 = (Float4)Values[0];
|
||||||
|
var rangeX = new Float2(data0.X, data0.Y);
|
||||||
|
int splits = 10;
|
||||||
|
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||||
|
var labelColor = style.ForegroundDisabled;
|
||||||
|
var labelFont = style.FontSmall;
|
||||||
|
//var blendArea = BlendAreaRect;
|
||||||
|
var blendArea = pointsArea;
|
||||||
|
|
||||||
|
for (int i = 0; i <= splits; i++)
|
||||||
|
{
|
||||||
|
float alpha = (float)i / splits;
|
||||||
|
float x = blendArea.Left + blendArea.Width * alpha;
|
||||||
|
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||||
|
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||||
|
}
|
||||||
|
if (_editor.Is2D)
|
||||||
|
{
|
||||||
|
var rangeY = new Float2(data0.Z, data0.W);
|
||||||
|
for (int i = 0; i <= splits; i++)
|
||||||
|
{
|
||||||
|
float alpha = (float)i / splits;
|
||||||
|
float y = blendArea.Top + blendArea.Height * alpha;
|
||||||
|
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
|
||||||
|
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float y = blendArea.Center.Y;
|
||||||
|
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the editor UI.
|
/// Updates the editor UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -983,6 +1017,10 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
private readonly FloatValueBox _animationX;
|
private readonly FloatValueBox _animationX;
|
||||||
private readonly Label _animationYLabel;
|
private readonly Label _animationYLabel;
|
||||||
private readonly FloatValueBox _animationY;
|
private readonly FloatValueBox _animationY;
|
||||||
|
private Float2[] _triangles;
|
||||||
|
private Color[] _triangleColors;
|
||||||
|
private Float2[] _selectedTriangles;
|
||||||
|
private Color[] _selectedColors;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
@@ -1051,6 +1089,143 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ClearTriangles()
|
||||||
|
{
|
||||||
|
// Remove cache
|
||||||
|
_triangles = null;
|
||||||
|
_triangleColors = null;
|
||||||
|
_selectedTriangles = null;
|
||||||
|
_selectedColors = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheTriangles()
|
||||||
|
{
|
||||||
|
// Get locations of blend point vertices
|
||||||
|
int pointsCount = _editor.PointsCount;
|
||||||
|
int count = 0, j = 0;
|
||||||
|
for (int i = 0; i < pointsCount; i++)
|
||||||
|
{
|
||||||
|
var animId = (Guid)Values[5 + i * 2];
|
||||||
|
if (animId != Guid.Empty)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
var vertices = new Float2[count];
|
||||||
|
for (int i = 0; i < pointsCount; i++)
|
||||||
|
{
|
||||||
|
var animId = (Guid)Values[5 + i * 2];
|
||||||
|
if (animId != Guid.Empty)
|
||||||
|
{
|
||||||
|
var dataA = (Float4)Values[4 + i * 2];
|
||||||
|
vertices[j++] = new Float2(dataA.X, dataA.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangulate
|
||||||
|
_triangles = FlaxEngine.Utilities.Delaunay2D.Triangulate(vertices);
|
||||||
|
_triangleColors = null;
|
||||||
|
|
||||||
|
// Fix incorrect triangles (mirror logic in AnimGraphBase::onNodeLoaded)
|
||||||
|
if (_triangles == null || _triangles.Length == 0)
|
||||||
|
{
|
||||||
|
// Insert dummy triangles to have something working (eg. blend points are on the same axis)
|
||||||
|
var triangles = new List<Float2>();
|
||||||
|
int verticesLeft = vertices.Length;
|
||||||
|
while (verticesLeft >= 3)
|
||||||
|
{
|
||||||
|
verticesLeft -= 3;
|
||||||
|
triangles.Add(vertices[verticesLeft + 0]);
|
||||||
|
triangles.Add(vertices[verticesLeft + 1]);
|
||||||
|
triangles.Add(vertices[verticesLeft + 2]);
|
||||||
|
}
|
||||||
|
if (verticesLeft == 1)
|
||||||
|
{
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
}
|
||||||
|
else if (verticesLeft == 2)
|
||||||
|
{
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
triangles.Add(vertices[1]);
|
||||||
|
triangles.Add(vertices[0]);
|
||||||
|
}
|
||||||
|
_triangles = triangles.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project to the blend space for drawing
|
||||||
|
for (int i = 0; i < _triangles.Length; i++)
|
||||||
|
_triangles[i] = _editor.BlendSpacePosToBlendPointPos(_triangles[i]);
|
||||||
|
|
||||||
|
// Check if anything is selected
|
||||||
|
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||||
|
if (selectedIndex != -1)
|
||||||
|
{
|
||||||
|
// Find triangles that contain selected point
|
||||||
|
var dataA = (Float4)Values[4 + selectedIndex * 2];
|
||||||
|
var pos = _editor.BlendSpacePosToBlendPointPos(new Float2(dataA.X, dataA.Y));
|
||||||
|
var selectedTriangles = new List<Float2>();
|
||||||
|
var selectedColors = new List<Color>();
|
||||||
|
var style = Style.Current;
|
||||||
|
var triangleColor = style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f);
|
||||||
|
var selectedTriangleColor = style.BackgroundSelected.AlphaMultiplied(0.6f);
|
||||||
|
_triangleColors = new Color[_triangles.Length];
|
||||||
|
for (int i = 0; i < _triangles.Length; i += 3)
|
||||||
|
{
|
||||||
|
var is0 = Float2.NearEqual(ref _triangles[i + 0], ref pos);
|
||||||
|
var is1 = Float2.NearEqual(ref _triangles[i + 1], ref pos);
|
||||||
|
var is2 = Float2.NearEqual(ref _triangles[i + 2], ref pos);
|
||||||
|
if (is0 || is1 || is2)
|
||||||
|
{
|
||||||
|
selectedTriangles.Add(_triangles[i + 0]);
|
||||||
|
selectedTriangles.Add(_triangles[i + 1]);
|
||||||
|
selectedTriangles.Add(_triangles[i + 2]);
|
||||||
|
selectedColors.Add(is0 ? Color.White : Color.Transparent);
|
||||||
|
selectedColors.Add(is1 ? Color.White : Color.Transparent);
|
||||||
|
selectedColors.Add(is2 ? Color.White : Color.Transparent);
|
||||||
|
}
|
||||||
|
_triangleColors[i + 0] = is0 ? selectedTriangleColor : triangleColor;
|
||||||
|
_triangleColors[i + 1] = is1 ? selectedTriangleColor : triangleColor;
|
||||||
|
_triangleColors[i + 2] = is2 ? selectedTriangleColor : triangleColor;
|
||||||
|
}
|
||||||
|
_selectedTriangles = selectedTriangles.ToArray();
|
||||||
|
_selectedColors = selectedColors.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void DrawEditorBackground(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
base.DrawEditorBackground(ref rect);
|
||||||
|
|
||||||
|
// Draw triangulated multi blend space
|
||||||
|
var style = Style.Current;
|
||||||
|
if (_triangles == null)
|
||||||
|
CacheTriangles();
|
||||||
|
if (_triangleColors != null && (ContainsFocus || IsMouseOver))
|
||||||
|
Render2D.FillTriangles(_triangles, _triangleColors);
|
||||||
|
else
|
||||||
|
Render2D.FillTriangles(_triangles, style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f));
|
||||||
|
Render2D.DrawTriangles(_triangles, style.Foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void DrawEditorGrid(ref Rectangle rect)
|
||||||
|
{
|
||||||
|
base.DrawEditorGrid(ref rect);
|
||||||
|
|
||||||
|
// Highlight selected blend point
|
||||||
|
var style = Style.Current;
|
||||||
|
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||||
|
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
|
||||||
|
{
|
||||||
|
var point = _editor.BlendPoints[selectedIndex];
|
||||||
|
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
|
||||||
|
Render2D.PushTint(ref highlightColor);
|
||||||
|
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
|
||||||
|
Render2D.PopTint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||||
{
|
{
|
||||||
@@ -1075,6 +1250,23 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_animationX.Enabled = isValid;
|
_animationX.Enabled = isValid;
|
||||||
_animationYLabel.Enabled = isValid;
|
_animationYLabel.Enabled = isValid;
|
||||||
_animationY.Enabled = isValid;
|
_animationY.Enabled = isValid;
|
||||||
|
ClearTriangles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnValuesChanged()
|
||||||
|
{
|
||||||
|
base.OnValuesChanged();
|
||||||
|
|
||||||
|
ClearTriangles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
|
{
|
||||||
|
base.OnLoaded(action);
|
||||||
|
|
||||||
|
ClearTriangles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using FlaxEditor.Content.Settings;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Surface.Elements;
|
using FlaxEditor.Surface.Elements;
|
||||||
using FlaxEditor.Windows.Assets;
|
using FlaxEditor.Windows.Assets;
|
||||||
@@ -590,7 +591,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
},
|
},
|
||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, FlaxEditor.Tools.Terrain.PaintTerrainGizmoMode.TerrainLayerNames),
|
NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()),
|
||||||
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),
|
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -425,6 +425,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
UpdateCombo();
|
UpdateCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsParamUsed(SurfaceParameter param)
|
||||||
|
{
|
||||||
|
return (Guid)Values[0] == param.ID;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnLoaded(SurfaceNodeActions action)
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
{
|
{
|
||||||
@@ -937,13 +943,17 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
// Deselect if that parameter is selected
|
// Deselect if that parameter is selected
|
||||||
if ((Guid)Values[0] == param.ID)
|
if ((Guid)Values[0] == param.ID)
|
||||||
{
|
|
||||||
_combobox.SelectedIndex = -1;
|
_combobox.SelectedIndex = -1;
|
||||||
}
|
|
||||||
|
|
||||||
UpdateCombo();
|
UpdateCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsParamUsed(SurfaceParameter param)
|
||||||
|
{
|
||||||
|
return (Guid)Values[0] == param.ID;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnLoaded(SurfaceNodeActions action)
|
public override void OnLoaded(SurfaceNodeActions action)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,5 +33,12 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="param">The parameter.</param>
|
/// <param name="param">The parameter.</param>
|
||||||
void OnParamDeleted(SurfaceParameter param);
|
void OnParamDeleted(SurfaceParameter param);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get if the parameter is referenced in a graph. Referenced in this case means in a graph and at least one node in-/output connected to another node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="param">The parameter.</param>
|
||||||
|
/// <returns>If the parameter is referenced.</returns>
|
||||||
|
bool IsParamUsed(SurfaceParameter param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
|
|||||||
typeof(TooltipAttribute),
|
typeof(TooltipAttribute),
|
||||||
typeof(HideInEditorAttribute),
|
typeof(HideInEditorAttribute),
|
||||||
typeof(NoAnimateAttribute),
|
typeof(NoAnimateAttribute),
|
||||||
|
typeof(ButtonAttribute),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -84,5 +84,16 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsParamUsed(SurfaceParameter param)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Nodes.Count; i++)
|
||||||
|
{
|
||||||
|
if (Nodes[i] is IParametersDependantNode node && node.IsParamUsed(param))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -776,6 +776,15 @@ namespace FlaxEditor.Surface
|
|||||||
private void DeleteParameter(int index)
|
private void DeleteParameter(int index)
|
||||||
{
|
{
|
||||||
var window = (IVisjectSurfaceWindow)Values[0];
|
var window = (IVisjectSurfaceWindow)Values[0];
|
||||||
|
SurfaceParameter param = window.VisjectSurface.Parameters[index];
|
||||||
|
|
||||||
|
if (Editor.Instance.Options.Options.Interface.WarnOnDeletingUsedVisjectParameter && window.VisjectSurface.IsParamUsed(param))
|
||||||
|
{
|
||||||
|
string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph.\n\nYou can disable this warning in the editor settings.";
|
||||||
|
if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var action = new AddRemoveParamAction
|
var action = new AddRemoveParamAction
|
||||||
{
|
{
|
||||||
Window = window,
|
Window = window,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace FlaxEditor.Tools.Terrain.Paint
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The layer to paint with it.
|
/// The layer to paint with it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorOrder(10), Tooltip("The layer to paint with it. Terrain material can access per-layer blend weight to perform materials or textures blending.")]
|
[EditorOrder(10), Tooltip("The layer to paint on. Terrain material can access a per-layer blend weight to perform material or texture blending."), CustomEditorAlias("FlaxEditor.CustomEditors.Editors.TerrainLayerEditor")]
|
||||||
public Layers Layer = Layers.Layer0;
|
public Layers Layer = Layers.Layer0;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -21,21 +21,6 @@ namespace FlaxEditor.Tools.Terrain
|
|||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public class PaintTerrainGizmoMode : EditorGizmoMode
|
public class PaintTerrainGizmoMode : EditorGizmoMode
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The terrain layer names.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string[] TerrainLayerNames =
|
|
||||||
{
|
|
||||||
"Layer 0",
|
|
||||||
"Layer 1",
|
|
||||||
"Layer 2",
|
|
||||||
"Layer 3",
|
|
||||||
"Layer 4",
|
|
||||||
"Layer 5",
|
|
||||||
"Layer 6",
|
|
||||||
"Layer 7",
|
|
||||||
};
|
|
||||||
|
|
||||||
private struct SplatmapData
|
private struct SplatmapData
|
||||||
{
|
{
|
||||||
public IntPtr DataPtr;
|
public IntPtr DataPtr;
|
||||||
|
|||||||
@@ -250,6 +250,8 @@ namespace FlaxEditor.Utilities
|
|||||||
|
|
||||||
internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60)
|
internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60)
|
||||||
{
|
{
|
||||||
|
if (pixelRange <= Mathf.Epsilon || maxDistanceBetweenTicks <= minDistanceBetweenTicks)
|
||||||
|
return Int2.Zero;
|
||||||
if (tickStrengths == null || tickStrengths.Length != tickSteps.Length)
|
if (tickStrengths == null || tickStrengths.Length != tickSteps.Length)
|
||||||
tickStrengths = new float[tickSteps.Length];
|
tickStrengths = new float[tickSteps.Length];
|
||||||
|
|
||||||
@@ -286,7 +288,7 @@ namespace FlaxEditor.Utilities
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Draw all ticks
|
// Draw all ticks
|
||||||
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1);
|
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 2);
|
||||||
var lStep = tickSteps[l];
|
var lStep = tickSteps[l];
|
||||||
var lNextStep = tickSteps[l + 1];
|
var lNextStep = tickSteps[l + 1];
|
||||||
int startTick = Mathf.FloorToInt(min / lStep);
|
int startTick = Mathf.FloorToInt(min / lStep);
|
||||||
|
|||||||
@@ -79,6 +79,13 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_uiControlLinked.Control.Parent = null;
|
_uiControlLinked.Control.Parent = null;
|
||||||
_uiControlLinked = null;
|
_uiControlLinked = null;
|
||||||
}
|
}
|
||||||
|
foreach (var child in _uiParentLink.Children.ToArray())
|
||||||
|
{
|
||||||
|
if (child is CanvasRootControl canvasRoot)
|
||||||
|
{
|
||||||
|
canvasRoot.Canvas.EditorOverride(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove for the preview
|
// Remove for the preview
|
||||||
Task.RemoveCustomActor(_instance);
|
Task.RemoveCustomActor(_instance);
|
||||||
|
|||||||
@@ -54,24 +54,24 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
[EditorOrder(10), EditorDisplay("General"), Tooltip("Material domain type.")]
|
[EditorOrder(10), EditorDisplay("General"), Tooltip("Material domain type.")]
|
||||||
public MaterialDomain Domain;
|
public MaterialDomain Domain;
|
||||||
|
|
||||||
[EditorOrder(20), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
[EditorOrder(20), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
||||||
public MaterialShadingModel ShadingModel;
|
public MaterialShadingModel ShadingModel;
|
||||||
|
|
||||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
[EditorOrder(30), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||||
public MaterialBlendMode BlendMode;
|
public MaterialBlendMode BlendMode;
|
||||||
|
|
||||||
// Rendering
|
// Rendering
|
||||||
|
|
||||||
[EditorOrder(100), DefaultValue(CullMode.Normal), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
|
[EditorOrder(100), DefaultValue(CullMode.Normal), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
|
||||||
public CullMode CullMode;
|
public CullMode CullMode;
|
||||||
|
|
||||||
[EditorOrder(110), DefaultValue(false), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
|
[EditorOrder(110), DefaultValue(false), VisibleIf(nameof(IsStandardOrGUI)), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
|
||||||
public bool Wireframe;
|
public bool Wireframe;
|
||||||
|
|
||||||
[EditorOrder(120), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
|
[EditorOrder(120), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
|
||||||
public bool DepthTest;
|
public bool DepthTest;
|
||||||
|
|
||||||
[EditorOrder(130), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
|
[EditorOrder(130), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
|
||||||
public bool DepthWrite;
|
public bool DepthWrite;
|
||||||
|
|
||||||
// Transparency
|
// Transparency
|
||||||
@@ -111,13 +111,13 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
|
||||||
[EditorOrder(400), DefaultValue(false), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
|
[EditorOrder(400), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
|
||||||
public bool InputWorldSpaceNormal;
|
public bool InputWorldSpaceNormal;
|
||||||
|
|
||||||
[EditorOrder(410), DefaultValue(false), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
|
[EditorOrder(410), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
|
||||||
public bool DitheredLODTransition;
|
public bool DitheredLODTransition;
|
||||||
|
|
||||||
[EditorOrder(420), DefaultValue(0.3f), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
[EditorOrder(420), DefaultValue(0.3f), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
||||||
public float MaskThreshold;
|
public float MaskThreshold;
|
||||||
|
|
||||||
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
|
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
|
||||||
@@ -144,7 +144,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
|
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
|
||||||
private bool IsDecal => Domain == MaterialDomain.Decal;
|
private bool IsDecal => Domain == MaterialDomain.Decal;
|
||||||
|
private bool IsGUI => Domain == MaterialDomain.GUI;
|
||||||
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
|
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
|
||||||
|
private bool IsStandardOrGUI => IsStandard || IsGUI;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gathers parameters from the specified material.
|
/// Gathers parameters from the specified material.
|
||||||
|
|||||||
@@ -52,12 +52,13 @@ namespace FlaxEditor.Windows
|
|||||||
contextMenu.AddSeparator();
|
contextMenu.AddSeparator();
|
||||||
|
|
||||||
// Basic editing options
|
// Basic editing options
|
||||||
|
var firstSelection = hasSthSelected ? Editor.SceneEditing.Selection[0] as ActorNode : null;
|
||||||
b = contextMenu.AddButton("Rename", inputOptions.Rename, Rename);
|
b = contextMenu.AddButton("Rename", inputOptions.Rename, Rename);
|
||||||
b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
|
||||||
b.Enabled = hasSthSelected;
|
b.Enabled = hasSthSelected;
|
||||||
|
b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
||||||
|
b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanDuplicate : true);
|
||||||
|
|
||||||
if (isSingleActorSelected)
|
if (isSingleActorSelected && firstSelection?.Actor is not Scene)
|
||||||
{
|
{
|
||||||
var convertMenu = contextMenu.AddChildMenu("Convert");
|
var convertMenu = contextMenu.AddChildMenu("Convert");
|
||||||
convertMenu.ContextMenu.AutoSort = true;
|
convertMenu.ContextMenu.AutoSort = true;
|
||||||
@@ -117,31 +118,31 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
b = contextMenu.AddButton("Delete", inputOptions.Delete, Editor.SceneEditing.Delete);
|
b = contextMenu.AddButton("Delete", inputOptions.Delete, Editor.SceneEditing.Delete);
|
||||||
b.Enabled = hasSthSelected;
|
b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanDelete : true);
|
||||||
|
|
||||||
contextMenu.AddSeparator();
|
contextMenu.AddSeparator();
|
||||||
|
|
||||||
b = contextMenu.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy);
|
b = contextMenu.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy);
|
||||||
|
b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanCopyPaste : true);
|
||||||
|
|
||||||
b.Enabled = hasSthSelected;
|
|
||||||
contextMenu.AddButton("Paste", inputOptions.Paste, Editor.SceneEditing.Paste);
|
contextMenu.AddButton("Paste", inputOptions.Paste, Editor.SceneEditing.Paste);
|
||||||
|
|
||||||
b = contextMenu.AddButton("Cut", inputOptions.Cut, Editor.SceneEditing.Cut);
|
b = contextMenu.AddButton("Cut", inputOptions.Cut, Editor.SceneEditing.Cut);
|
||||||
b.Enabled = canEditScene;
|
b.Enabled = canEditScene && hasSthSelected && (firstSelection != null ? firstSelection.CanCopyPaste : true);
|
||||||
|
|
||||||
// Create option
|
// Create option
|
||||||
|
|
||||||
contextMenu.AddSeparator();
|
contextMenu.AddSeparator();
|
||||||
|
|
||||||
b = contextMenu.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors);
|
b = contextMenu.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors);
|
||||||
b.Enabled = canEditScene && hasSthSelected;
|
b.Enabled = canEditScene && hasSthSelected && firstSelection?.Actor is not Scene;
|
||||||
|
|
||||||
b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab);
|
b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab);
|
||||||
b.Enabled = isSingleActorSelected &&
|
b.Enabled = isSingleActorSelected &&
|
||||||
((ActorNode)Editor.SceneEditing.Selection[0]).CanCreatePrefab &&
|
(firstSelection != null ? firstSelection.CanCreatePrefab : false) &&
|
||||||
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
||||||
|
|
||||||
bool hasPrefabLink = canEditScene && isSingleActorSelected && (Editor.SceneEditing.Selection[0] as ActorNode).HasPrefabLink;
|
bool hasPrefabLink = canEditScene && isSingleActorSelected && (firstSelection != null ? firstSelection.HasPrefabLink : false);
|
||||||
if (hasPrefabLink)
|
if (hasPrefabLink)
|
||||||
{
|
{
|
||||||
contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
|
contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
|
||||||
|
|||||||
@@ -66,27 +66,23 @@ namespace AnimationUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
FORCE_INLINE static void GetTangent(const T& a, const T& b, float length, T& result)
|
FORCE_INLINE static void GetTangent(const T& value, const T& tangent, float tangentScale, T& result)
|
||||||
{
|
{
|
||||||
const float oneThird = 1.0f / 3.0f;
|
result = value + tangent * tangentScale;
|
||||||
result = a + b * (length * oneThird);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& a, const Quaternion& b, float length, Quaternion& result)
|
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& value, const Quaternion& tangent, float tangentScale, Quaternion& result)
|
||||||
{
|
{
|
||||||
const float oneThird = 1.0f / 3.0f;
|
Quaternion::Slerp(value, tangent, 1.0f / 3.0f, result);
|
||||||
Quaternion::Slerp(a, b, oneThird, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
FORCE_INLINE void GetTangent<Transform>(const Transform& a, const Transform& b, float length, Transform& result)
|
FORCE_INLINE void GetTangent<Transform>(const Transform& value, const Transform& tangent, float tangentScale, Transform& result)
|
||||||
{
|
{
|
||||||
const float oneThird = 1.0f / 3.0f;
|
GetTangent(value.Translation, tangent.Translation, tangentScale, result.Translation);
|
||||||
const float oneThirdLength = length * oneThird;
|
GetTangent(value.Orientation, tangent.Orientation, tangentScale, result.Orientation);
|
||||||
result.Translation = a.Translation + b.Translation * oneThirdLength;
|
GetTangent(value.Scale, tangent.Scale, tangentScale, result.Scale);
|
||||||
Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation);
|
|
||||||
result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using FlaxEngine.Interop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace FlaxEngine
|
namespace FlaxEngine
|
||||||
@@ -24,9 +26,9 @@ namespace FlaxEngine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="tangent">The tangent.</param>
|
/// <param name="tangent">The tangent.</param>
|
||||||
/// <param name="lengthThird">The length divided by 3.</param>
|
/// <param name="tangentScale">The tangent scale factor.</param>
|
||||||
/// <param name="result">The result.</param>
|
/// <param name="result">The result.</param>
|
||||||
void GetTangent(ref U value, ref U tangent, float lengthThird, out U result);
|
void GetTangent(ref U value, ref U tangent, float tangentScale, out U result);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the linear interpolation at the specified alpha.
|
/// Calculates the linear interpolation at the specified alpha.
|
||||||
@@ -67,7 +69,7 @@ namespace FlaxEngine
|
|||||||
IKeyframeAccess<Color32>,
|
IKeyframeAccess<Color32>,
|
||||||
IKeyframeAccess<Color>
|
IKeyframeAccess<Color>
|
||||||
{
|
{
|
||||||
public void GetTangent(ref bool value, ref bool tangent, float lengthThird, out bool result)
|
public void GetTangent(ref bool value, ref bool tangent, float tangentScale, out bool result)
|
||||||
{
|
{
|
||||||
result = value;
|
result = value;
|
||||||
}
|
}
|
||||||
@@ -82,9 +84,9 @@ namespace FlaxEngine
|
|||||||
result = p0;
|
result = p0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref int value, ref int tangent, float lengthThird, out int result)
|
public void GetTangent(ref int value, ref int tangent, float tangentScale, out int result)
|
||||||
{
|
{
|
||||||
result = value + (int)(tangent * lengthThird);
|
result = value + (int)(tangent * tangentScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref int a, ref int b, float alpha, out int result)
|
public void Linear(ref int a, ref int b, float alpha, out int result)
|
||||||
@@ -102,9 +104,9 @@ namespace FlaxEngine
|
|||||||
result = Mathf.Lerp(p012, p123, alpha);
|
result = Mathf.Lerp(p012, p123, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref double value, ref double tangent, float lengthThird, out double result)
|
public void GetTangent(ref double value, ref double tangent, float tangentScale, out double result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref double a, ref double b, float alpha, out double result)
|
public void Linear(ref double a, ref double b, float alpha, out double result)
|
||||||
@@ -122,9 +124,9 @@ namespace FlaxEngine
|
|||||||
result = Mathf.Lerp(p012, p123, alpha);
|
result = Mathf.Lerp(p012, p123, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref float value, ref float tangent, float lengthThird, out float result)
|
public void GetTangent(ref float value, ref float tangent, float tangentScale, out float result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref float a, ref float b, float alpha, out float result)
|
public void Linear(ref float a, ref float b, float alpha, out float result)
|
||||||
@@ -142,9 +144,9 @@ namespace FlaxEngine
|
|||||||
result = Mathf.Lerp(p012, p123, alpha);
|
result = Mathf.Lerp(p012, p123, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float lengthThird, out Vector2 result)
|
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float tangentScale, out Vector2 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result)
|
public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result)
|
||||||
@@ -162,9 +164,9 @@ namespace FlaxEngine
|
|||||||
Vector2.Lerp(ref p012, ref p123, alpha, out result);
|
Vector2.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float lengthThird, out Vector3 result)
|
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float tangentScale, out Vector3 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result)
|
public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result)
|
||||||
@@ -182,9 +184,9 @@ namespace FlaxEngine
|
|||||||
Vector3.Lerp(ref p012, ref p123, alpha, out result);
|
Vector3.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float lengthThird, out Vector4 result)
|
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float tangentScale, out Vector4 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result)
|
public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result)
|
||||||
@@ -202,9 +204,9 @@ namespace FlaxEngine
|
|||||||
Vector4.Lerp(ref p012, ref p123, alpha, out result);
|
Vector4.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Float2 value, ref Float2 tangent, float lengthThird, out Float2 result)
|
public void GetTangent(ref Float2 value, ref Float2 tangent, float tangentScale, out Float2 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result)
|
public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result)
|
||||||
@@ -222,9 +224,9 @@ namespace FlaxEngine
|
|||||||
Float2.Lerp(ref p012, ref p123, alpha, out result);
|
Float2.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Float3 value, ref Float3 tangent, float lengthThird, out Float3 result)
|
public void GetTangent(ref Float3 value, ref Float3 tangent, float tangentScale, out Float3 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result)
|
public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result)
|
||||||
@@ -242,9 +244,9 @@ namespace FlaxEngine
|
|||||||
Float3.Lerp(ref p012, ref p123, alpha, out result);
|
Float3.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Float4 value, ref Float4 tangent, float lengthThird, out Float4 result)
|
public void GetTangent(ref Float4 value, ref Float4 tangent, float tangentScale, out Float4 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result)
|
public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result)
|
||||||
@@ -262,9 +264,9 @@ namespace FlaxEngine
|
|||||||
Float4.Lerp(ref p012, ref p123, alpha, out result);
|
Float4.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Double2 value, ref Double2 tangent, float lengthThird, out Double2 result)
|
public void GetTangent(ref Double2 value, ref Double2 tangent, float tangentScale, out Double2 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result)
|
public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result)
|
||||||
@@ -282,9 +284,9 @@ namespace FlaxEngine
|
|||||||
Double2.Lerp(ref p012, ref p123, alpha, out result);
|
Double2.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Double3 value, ref Double3 tangent, float lengthThird, out Double3 result)
|
public void GetTangent(ref Double3 value, ref Double3 tangent, float tangentScale, out Double3 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result)
|
public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result)
|
||||||
@@ -302,9 +304,9 @@ namespace FlaxEngine
|
|||||||
Double3.Lerp(ref p012, ref p123, alpha, out result);
|
Double3.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Double4 value, ref Double4 tangent, float lengthThird, out Double4 result)
|
public void GetTangent(ref Double4 value, ref Double4 tangent, float tangentScale, out Double4 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result)
|
public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result)
|
||||||
@@ -322,7 +324,7 @@ namespace FlaxEngine
|
|||||||
Double4.Lerp(ref p012, ref p123, alpha, out result);
|
Double4.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float lengthThird, out Quaternion result)
|
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float tangentScale, out Quaternion result)
|
||||||
{
|
{
|
||||||
Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result);
|
Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result);
|
||||||
}
|
}
|
||||||
@@ -342,9 +344,9 @@ namespace FlaxEngine
|
|||||||
Quaternion.Slerp(ref p012, ref p123, alpha, out result);
|
Quaternion.Slerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Color32 value, ref Color32 tangent, float lengthThird, out Color32 result)
|
public void GetTangent(ref Color32 value, ref Color32 tangent, float tangentScale, out Color32 result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result)
|
public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result)
|
||||||
@@ -362,9 +364,9 @@ namespace FlaxEngine
|
|||||||
Color32.Lerp(ref p012, ref p123, alpha, out result);
|
Color32.Lerp(ref p012, ref p123, alpha, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetTangent(ref Color value, ref Color tangent, float lengthThird, out Color result)
|
public void GetTangent(ref Color value, ref Color tangent, float tangentScale, out Color result)
|
||||||
{
|
{
|
||||||
result = value + tangent * lengthThird;
|
result = value + tangent * tangentScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Linear(ref Color a, ref Color b, float alpha, out Color result)
|
public void Linear(ref Color a, ref Color b, float alpha, out Color result)
|
||||||
@@ -454,6 +456,40 @@ namespace FlaxEngine
|
|||||||
time = end;
|
time = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframes">The keyframes array.</param>
|
||||||
|
/// <returns>The raw keyframes data.</returns>
|
||||||
|
protected static unsafe byte[] MarshalKeyframes<Keyframe>(Keyframe[] keyframes)
|
||||||
|
{
|
||||||
|
if (keyframes == null || keyframes.Length == 0)
|
||||||
|
return null;
|
||||||
|
var keyframeSize = Unsafe.SizeOf<Keyframe>();
|
||||||
|
var result = new byte[keyframes.Length * keyframeSize];
|
||||||
|
fixed (byte* resultPtr = result)
|
||||||
|
{
|
||||||
|
var keyframesHandle = ManagedHandle.Alloc(keyframes, GCHandleType.Pinned);
|
||||||
|
var keyframesPtr = Marshal.UnsafeAddrOfPinnedArrayElement(keyframes, 0);
|
||||||
|
Buffer.MemoryCopy((void*)keyframesPtr, resultPtr, (uint)result.Length, (uint)result.Length);
|
||||||
|
keyframesHandle.Free();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="raw">The raw keyframes data.</param>
|
||||||
|
/// <returns>The keyframes array.</returns>
|
||||||
|
protected static unsafe Keyframe[] MarshalKeyframes<Keyframe>(byte[] raw) where Keyframe : struct
|
||||||
|
{
|
||||||
|
if (raw == null || raw.Length == 0)
|
||||||
|
return null;
|
||||||
|
fixed (byte* rawPtr = raw)
|
||||||
|
return MemoryMarshal.Cast<byte, Keyframe>(new Span<byte>(rawPtr, raw.Length)).ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -709,6 +745,30 @@ namespace FlaxEngine
|
|||||||
leftKey = Mathf.Max(0, start - 1);
|
leftKey = Mathf.Max(0, start - 1);
|
||||||
rightKey = Mathf.Min(start, Keyframes.Length - 1);
|
rightKey = Mathf.Min(start, Keyframes.Length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="curve">The curve to copy.</param>
|
||||||
|
/// <returns>The raw keyframes data.</returns>
|
||||||
|
public static unsafe implicit operator byte[](LinearCurve<T> curve)
|
||||||
|
{
|
||||||
|
if (curve == null)
|
||||||
|
return null;
|
||||||
|
return MarshalKeyframes<Keyframe>(curve.Keyframes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="raw">The raw keyframes data.</param>
|
||||||
|
/// <returns>The curve.</returns>
|
||||||
|
public static unsafe implicit operator LinearCurve<T>(byte[] raw)
|
||||||
|
{
|
||||||
|
if (raw == null || raw.Length == 0)
|
||||||
|
return null;
|
||||||
|
return new LinearCurve<T>(MarshalKeyframes<Keyframe>(raw));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -860,9 +920,9 @@ namespace FlaxEngine
|
|||||||
|
|
||||||
// Evaluate the key at the curve
|
// Evaluate the key at the curve
|
||||||
result.Time = leftKey.Time + length * t;
|
result.Time = leftKey.Time + length * t;
|
||||||
float lengthThird = length / 3.0f;
|
float tangentScale = length / 3.0f;
|
||||||
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
|
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
|
||||||
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
|
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
|
||||||
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value);
|
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value);
|
||||||
result.TangentIn = leftKey.TangentOut;
|
result.TangentIn = leftKey.TangentOut;
|
||||||
result.TangentOut = rightKey.TangentIn;
|
result.TangentOut = rightKey.TangentIn;
|
||||||
@@ -895,9 +955,9 @@ namespace FlaxEngine
|
|||||||
float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
|
float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
|
||||||
|
|
||||||
// Evaluate the value at the curve
|
// Evaluate the value at the curve
|
||||||
float lengthThird = length / 3.0f;
|
float tangentScale = length / 3.0f;
|
||||||
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
|
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
|
||||||
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
|
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
|
||||||
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result);
|
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,5 +1060,29 @@ namespace FlaxEngine
|
|||||||
leftKey = Mathf.Max(0, start - 1);
|
leftKey = Mathf.Max(0, start - 1);
|
||||||
rightKey = Mathf.Min(start, Keyframes.Length - 1);
|
rightKey = Mathf.Min(start, Keyframes.Length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="curve">The curve to copy.</param>
|
||||||
|
/// <returns>The raw keyframes data.</returns>
|
||||||
|
public static unsafe implicit operator byte[](BezierCurve<T> curve)
|
||||||
|
{
|
||||||
|
if (curve == null)
|
||||||
|
return null;
|
||||||
|
return MarshalKeyframes<Keyframe>(curve.Keyframes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="raw">The raw keyframes data.</param>
|
||||||
|
/// <returns>The curve.</returns>
|
||||||
|
public static unsafe implicit operator BezierCurve<T>(byte[] raw)
|
||||||
|
{
|
||||||
|
if (raw == null || raw.Length == 0)
|
||||||
|
return null;
|
||||||
|
return new BezierCurve<T>(MarshalKeyframes<Keyframe>(raw));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,16 +247,18 @@ public:
|
|||||||
static void Interpolate(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
static void Interpolate(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
||||||
{
|
{
|
||||||
T leftTangent, rightTangent;
|
T leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
const float tangentScale = length / 3.0f;
|
||||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
|
||||||
|
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
|
||||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
||||||
{
|
{
|
||||||
T leftTangent, rightTangent;
|
T leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
const float tangentScale = length / 3.0f;
|
||||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
|
||||||
|
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
|
||||||
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,8 +266,9 @@ public:
|
|||||||
{
|
{
|
||||||
result.Time = a.Time + length * alpha;
|
result.Time = a.Time + length * alpha;
|
||||||
T leftTangent, rightTangent;
|
T leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
const float tangentScale = length / 3.0f;
|
||||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
|
||||||
|
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
|
||||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
|
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
|
||||||
result.TangentIn = a.TangentOut;
|
result.TangentIn = a.TangentOut;
|
||||||
result.TangentOut = b.TangentIn;
|
result.TangentOut = b.TangentIn;
|
||||||
@@ -498,7 +501,7 @@ protected:
|
|||||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a curve.
|
/// An animation spline represented by a set of keyframes, each representing an endpoint of a curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
template<class T, typename KeyFrame = LinearCurveKeyframe<T>>
|
template<class T, typename KeyFrame = LinearCurveKeyframe<T>>
|
||||||
class Curve : public CurveBase<T, KeyFrame>
|
API_CLASS(InBuild, Template, MarshalAs=Span<byte>) class Curve : public CurveBase<T, KeyFrame>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef CurveBase<T, KeyFrame> Base;
|
typedef CurveBase<T, KeyFrame> Base;
|
||||||
@@ -760,28 +763,42 @@ public:
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raw memory copy (used by scripting bindings - see MarshalAs tag).
|
||||||
|
Curve& operator=(const Span<byte>& raw)
|
||||||
|
{
|
||||||
|
ASSERT((raw.Length() % sizeof(KeyFrame)) == 0);
|
||||||
|
const int32 count = raw.Length() / sizeof(KeyFrame);
|
||||||
|
_keyframes.Resize(count, false);
|
||||||
|
Platform::MemoryCopy(_keyframes.Get(), raw.Get(), sizeof(KeyFrame) * count);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
operator Span<byte>()
|
||||||
|
{
|
||||||
|
return Span<byte>((const byte*)_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An animation spline represented by a set of keyframes, each representing a value point.
|
/// An animation spline represented by a set of keyframes, each representing a value point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using StepCurve = Curve<T, StepCurveKeyframe<T>>;
|
API_TYPEDEF() using StepCurve = Curve<T, StepCurveKeyframe<T>>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
|
API_TYPEDEF() using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve.
|
/// An animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
|
API_TYPEDEF() using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An animation spline represented by a set of keyframes, each representing an endpoint of Bezier curve.
|
/// An animation spline represented by a set of keyframes, each representing an endpoint of Bezier curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;
|
API_TYPEDEF() using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;
|
||||||
|
|||||||
@@ -2263,6 +2263,14 @@ void VisualScript::GetMethodSignature(int32 index, String& name, byte& flags, St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variant VisualScript::InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const
|
||||||
|
{
|
||||||
|
auto& method = _methods[index];
|
||||||
|
Variant result;
|
||||||
|
VisualScriptingModule.InvokeMethod((void*)&method, instance, parameters, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Span<byte> VisualScript::GetMetaData(int32 typeID)
|
Span<byte> VisualScript::GetMetaData(int32 typeID)
|
||||||
{
|
{
|
||||||
auto meta = Graph.Meta.GetEntry(typeID);
|
auto meta = Graph.Meta.GetEntry(typeID);
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ public:
|
|||||||
// Gets the signature data of the method.
|
// Gets the signature data of the method.
|
||||||
API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array<String>& paramNames, API_PARAM(Out) Array<String>& paramTypeNames, API_PARAM(Out) Array<bool>& paramOuts);
|
API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array<String>& paramNames, API_PARAM(Out) Array<String>& paramTypeNames, API_PARAM(Out) Array<bool>& paramOuts);
|
||||||
|
|
||||||
|
// Invokes the method.
|
||||||
|
API_FUNCTION() Variant InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const;
|
||||||
|
|
||||||
// Gets the metadata of the script surface.
|
// Gets the metadata of the script surface.
|
||||||
API_FUNCTION() Span<byte> GetMetaData(int32 typeID);
|
API_FUNCTION() Span<byte> GetMetaData(int32 typeID);
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace FlaxEditor.Content.Settings
|
|||||||
{
|
{
|
||||||
new BuildTarget
|
new BuildTarget
|
||||||
{
|
{
|
||||||
Name = "Windows 64bit",
|
Name = "Windows",
|
||||||
Output = "Output\\Win64",
|
Output = "Output\\Windows",
|
||||||
Platform = BuildPlatform.Windows64,
|
Platform = BuildPlatform.Windows64,
|
||||||
Mode = BuildConfiguration.Development,
|
Mode = BuildConfiguration.Development,
|
||||||
},
|
},
|
||||||
@@ -35,8 +35,8 @@ namespace FlaxEditor.Content.Settings
|
|||||||
{
|
{
|
||||||
new BuildTarget
|
new BuildTarget
|
||||||
{
|
{
|
||||||
Name = "Windows 64bit",
|
Name = "Windows",
|
||||||
Output = "Output\\Win64",
|
Output = "Output\\Windows",
|
||||||
Platform = BuildPlatform.Windows64,
|
Platform = BuildPlatform.Windows64,
|
||||||
Mode = BuildConfiguration.Release,
|
Mode = BuildConfiguration.Release,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace FlaxEditor.Content.Settings
|
|||||||
public List<string> Tags = new List<string>();
|
public List<string> Tags = new List<string>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The layers names.
|
/// The layer names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)]
|
[EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)]
|
||||||
public string[] Layers = new string[32];
|
public string[] Layers = new string[32];
|
||||||
@@ -30,6 +30,31 @@ namespace FlaxEditor.Content.Settings
|
|||||||
return GetCurrentLayers(out int _);
|
return GetCurrentLayers(out int _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The layer names.
|
||||||
|
/// </summary>
|
||||||
|
[EditorOrder(10), EditorDisplay("Terrain Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)]
|
||||||
|
public string[] TerrainLayers = new string[8];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current terrain layer names. Returns "Layer" + index for layers without a name.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The layer names.</returns>
|
||||||
|
public static string[] GetCurrentTerrainLayers()
|
||||||
|
{
|
||||||
|
#if FLAX_TESTS
|
||||||
|
return System.Array.Empty<string>();
|
||||||
|
#else
|
||||||
|
string[] layerNames = GameSettings.Load<LayersAndTagsSettings>().TerrainLayers;
|
||||||
|
for (int i = 0; i < layerNames.Length; i++)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(layerNames[i]))
|
||||||
|
layerNames[i] = $"Layer {i}";
|
||||||
|
}
|
||||||
|
return layerNames;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
[LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
|
[LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))]
|
||||||
[return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "layerCount")]
|
[return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "layerCount")]
|
||||||
internal static partial string[] GetCurrentLayers(out int layerCount);
|
internal static partial string[] GetCurrentLayers(out int layerCount);
|
||||||
|
|||||||
@@ -16,10 +16,14 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
|
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
|
||||||
|
#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE))
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
bool LogAfterInit = false, IsDuringLog = false;
|
bool LogAfterInit = false, IsDuringLog = false;
|
||||||
|
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
|
||||||
|
bool IsWindowsSingleNewLineChar = false;
|
||||||
|
#endif
|
||||||
int LogTotalErrorsCnt = 0;
|
int LogTotalErrorsCnt = 0;
|
||||||
FileWriteStream* LogFile = nullptr;
|
FileWriteStream* LogFile = nullptr;
|
||||||
CriticalSection LogLocker;
|
CriticalSection LogLocker;
|
||||||
@@ -86,6 +90,11 @@ bool Log::Logger::Init()
|
|||||||
}
|
}
|
||||||
LogTotalErrorsCnt = 0;
|
LogTotalErrorsCnt = 0;
|
||||||
LogAfterInit = true;
|
LogAfterInit = true;
|
||||||
|
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
|
||||||
|
String envVar;
|
||||||
|
Platform::GetEnvironmentVariable(TEXT("GITHUB_ACTION"), envVar);
|
||||||
|
IsWindowsSingleNewLineChar = envVar.HasChars();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Write BOM (UTF-16 (LE); BOM: FF FE)
|
// Write BOM (UTF-16 (LE); BOM: FF FE)
|
||||||
byte bom[] = { 0xFF, 0xFE };
|
byte bom[] = { 0xFF, 0xFE };
|
||||||
@@ -127,6 +136,11 @@ void Log::Logger::Write(const StringView& msg)
|
|||||||
printf("%s", ansi.Get());
|
printf("%s", ansi.Get());
|
||||||
#else
|
#else
|
||||||
std::wcout.write(ptr, length);
|
std::wcout.write(ptr, length);
|
||||||
|
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
|
||||||
|
if (IsWindowsSingleNewLineChar)
|
||||||
|
std::wcout.write(TEXT("\n"), 1); // Github Actions show logs with duplicated new-line characters so skip \r
|
||||||
|
else
|
||||||
|
#endif
|
||||||
std::wcout.write(TEXT(PLATFORM_LINE_TERMINATOR), ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1);
|
std::wcout.write(TEXT(PLATFORM_LINE_TERMINATOR), ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,9 +124,9 @@ void BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix, Boundi
|
|||||||
const auto ya = up * box.Minimum.Y;
|
const auto ya = up * box.Minimum.Y;
|
||||||
const auto yb = up * box.Maximum.Y;
|
const auto yb = up * box.Maximum.Y;
|
||||||
|
|
||||||
const auto backward = matrix.GetBackward();
|
const auto forward = matrix.GetForward();
|
||||||
const auto za = backward * box.Minimum.Z;
|
const auto za = forward * box.Minimum.Z;
|
||||||
const auto zb = backward * box.Maximum.Z;
|
const auto zb = forward * box.Maximum.Z;
|
||||||
|
|
||||||
const auto translation = matrix.GetTranslation();
|
const auto translation = matrix.GetTranslation();
|
||||||
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + translation;
|
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + translation;
|
||||||
@@ -146,9 +146,9 @@ void BoundingBox::Transform(const BoundingBox& box, const ::Transform& transform
|
|||||||
const auto ya = up * box.Minimum.Y;
|
const auto ya = up * box.Minimum.Y;
|
||||||
const auto yb = up * box.Maximum.Y;
|
const auto yb = up * box.Maximum.Y;
|
||||||
|
|
||||||
const auto backward = Float3::Transform(Float3::Backward, transform.Orientation);
|
const auto forward = Float3::Transform(Float3::Forward, transform.Orientation);
|
||||||
const auto za = backward * box.Minimum.Z;
|
const auto za = forward * box.Minimum.Z;
|
||||||
const auto zb = backward * box.Maximum.Z;
|
const auto zb = forward * box.Maximum.Z;
|
||||||
|
|
||||||
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + transform.Translation;
|
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + transform.Translation;
|
||||||
const auto max = Vector3::Max(xa, xb) + Vector3::Max(ya, yb) + Vector3::Max(za, zb) + transform.Translation;
|
const auto max = Vector3::Max(xa, xb) + Vector3::Max(ya, yb) + Vector3::Max(za, zb) + transform.Translation;
|
||||||
|
|||||||
@@ -474,9 +474,9 @@ namespace FlaxEngine
|
|||||||
var ya = up * box.Minimum.Y;
|
var ya = up * box.Minimum.Y;
|
||||||
var yb = up * box.Maximum.Y;
|
var yb = up * box.Maximum.Y;
|
||||||
|
|
||||||
Double3 backward = transform.Backward;
|
Double3 forward = transform.Forward;
|
||||||
var za = backward * box.Minimum.Z;
|
var za = forward * box.Minimum.Z;
|
||||||
var zb = backward * box.Maximum.Z;
|
var zb = forward * box.Maximum.Z;
|
||||||
|
|
||||||
var translation = transform.TranslationVector;
|
var translation = transform.TranslationVector;
|
||||||
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + translation;
|
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + translation;
|
||||||
@@ -514,9 +514,9 @@ namespace FlaxEngine
|
|||||||
var ya = up * box.Minimum.Y;
|
var ya = up * box.Minimum.Y;
|
||||||
var yb = up * box.Maximum.Y;
|
var yb = up * box.Maximum.Y;
|
||||||
|
|
||||||
Double3 backward = transform.Backward;
|
Double3 forward = transform.Forward;
|
||||||
var za = backward * box.Minimum.Z;
|
var za = forward * box.Minimum.Z;
|
||||||
var zb = backward * box.Maximum.Z;
|
var zb = forward * box.Maximum.Z;
|
||||||
|
|
||||||
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + transform.Translation;
|
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + transform.Translation;
|
||||||
var max = Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + transform.Translation;
|
var max = Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + transform.Translation;
|
||||||
|
|||||||
@@ -136,12 +136,12 @@ void Matrix::Decompose(Float3& scale, Matrix3x3& rotation, Float3& translation)
|
|||||||
const auto right = Float3::Cross(up, at);
|
const auto right = Float3::Cross(up, at);
|
||||||
rotation.SetRight(right);
|
rotation.SetRight(right);
|
||||||
rotation.SetUp(up);
|
rotation.SetUp(up);
|
||||||
rotation.SetBackward(at);
|
rotation.SetForward(at);
|
||||||
|
|
||||||
// In case of reflexions
|
// In case of reflexions
|
||||||
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
|
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
|
||||||
scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y;
|
scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y;
|
||||||
scale.Z = Float3::Dot(at, GetBackward()) > 0.0f ? scale.Z : -scale.Z;
|
scale.Z = Float3::Dot(at, GetForward()) > 0.0f ? scale.Z : -scale.Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Matrix::Decompose(Float3& scale, Matrix& rotation, Float3& translation) const
|
void Matrix::Decompose(Float3& scale, Matrix& rotation, Float3& translation) const
|
||||||
|
|||||||
@@ -215,23 +215,9 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the forward <see cref="Float3" /> of the matrix; that is -M31, -M32, and -M33.
|
/// Gets or sets the forward <see cref="Float3" /> of the matrix; that is M31, M32, and M33.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Float3 Forward
|
public Float3 Forward
|
||||||
{
|
|
||||||
get => new Float3(-M31, -M32, -M33);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
M31 = -value.X;
|
|
||||||
M32 = -value.Y;
|
|
||||||
M33 = -value.Z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the backward <see cref="Float3" /> of the matrix; that is M31, M32, and M33.
|
|
||||||
/// </summary>
|
|
||||||
public Float3 Backward
|
|
||||||
{
|
{
|
||||||
get => new Float3(M31, M32, M33);
|
get => new Float3(M31, M32, M33);
|
||||||
set
|
set
|
||||||
@@ -242,6 +228,20 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the backward <see cref="Float3" /> of the matrix; that is -M31, -M32, and -M33.
|
||||||
|
/// </summary>
|
||||||
|
public Float3 Backward
|
||||||
|
{
|
||||||
|
get => new Float3(-M31, -M32, -M33);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
M31 = -value.X;
|
||||||
|
M32 = -value.Y;
|
||||||
|
M33 = -value.Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Matrix" /> struct.
|
/// Initializes a new instance of the <see cref="Matrix" /> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -210,31 +210,31 @@ public:
|
|||||||
// Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
|
// Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
|
||||||
Float3 GetForward() const
|
Float3 GetForward() const
|
||||||
{
|
{
|
||||||
return -Float3(M31, M32, M33);
|
return Float3(M31, M32, M33);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
|
// Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
|
||||||
void SetForward(const Float3& value)
|
void SetForward(const Float3& value)
|
||||||
{
|
|
||||||
M31 = -value.X;
|
|
||||||
M32 = -value.Y;
|
|
||||||
M33 = -value.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the backward Float3 of the matrix; that is M31, M32, and M33.
|
|
||||||
Float3 GetBackward() const
|
|
||||||
{
|
|
||||||
return Float3(M31, M32, M33);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the backward Float3 of the matrix; that is M31, M32, and M33.
|
|
||||||
void SetBackward(const Float3& value)
|
|
||||||
{
|
{
|
||||||
M31 = value.X;
|
M31 = value.X;
|
||||||
M32 = value.Y;
|
M32 = value.Y;
|
||||||
M33 = value.Z;
|
M33 = value.Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
|
||||||
|
Float3 GetBackward() const
|
||||||
|
{
|
||||||
|
return Float3(-M31, -M32, -M33);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
|
||||||
|
void SetBackward(const Float3& value)
|
||||||
|
{
|
||||||
|
M31 = -value.X;
|
||||||
|
M32 = -value.Y;
|
||||||
|
M33 = -value.Z;
|
||||||
|
}
|
||||||
|
|
||||||
// Gets the first row in the matrix; that is M11, M12, M13, and M14.
|
// Gets the first row in the matrix; that is M11, M12, M13, and M14.
|
||||||
Float4 GetRow1() const
|
Float4 GetRow1() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ void Matrix3x3::Decompose(Float3& scale, Matrix3x3& rotation) const
|
|||||||
const auto right = Float3::Cross(up, at);
|
const auto right = Float3::Cross(up, at);
|
||||||
rotation.SetRight(right);
|
rotation.SetRight(right);
|
||||||
rotation.SetUp(up);
|
rotation.SetUp(up);
|
||||||
rotation.SetBackward(at);
|
rotation.SetForward(at);
|
||||||
|
|
||||||
// In case of reflexions
|
// In case of reflexions
|
||||||
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
|
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
|
||||||
|
|||||||
@@ -303,9 +303,6 @@ namespace FlaxEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is an identity Matrix3x3.
|
/// Gets a value indicating whether this instance is an identity Matrix3x3.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>
|
|
||||||
/// <c>true</c> if this instance is an identity Matrix3x3; otherwise, <c>false</c>.
|
|
||||||
/// </value>
|
|
||||||
public bool IsIdentity => Equals(Identity);
|
public bool IsIdentity => Equals(Identity);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -566,19 +563,19 @@ namespace FlaxEngine
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public bool DecomposeUniformScale(out float scale, out Quaternion rotation)
|
public bool DecomposeUniformScale(out float scale, out Quaternion rotation)
|
||||||
{
|
{
|
||||||
//Scaling is the length of the rows. ( just take one row since this is a uniform matrix)
|
// Scaling is the length of the rows. ( just take one row since this is a uniform matrix)
|
||||||
scale = (float)Math.Sqrt((M11 * M11) + (M12 * M12) + (M13 * M13));
|
scale = (float)Math.Sqrt((M11 * M11) + (M12 * M12) + (M13 * M13));
|
||||||
var invScale = 1f / scale;
|
var invScale = 1f / scale;
|
||||||
|
|
||||||
//If any of the scaling factors are zero, then the rotation matrix can not exist.
|
// If any of the scaling factors are zero, then the rotation matrix can not exist
|
||||||
if (Math.Abs(scale) < Mathf.Epsilon)
|
if (Math.Abs(scale) < Mathf.Epsilon)
|
||||||
{
|
{
|
||||||
rotation = Quaternion.Identity;
|
rotation = Quaternion.Identity;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The rotation is the left over matrix after dividing out the scaling.
|
// The rotation is the leftover matrix after dividing out the scaling
|
||||||
Matrix3x3 rotationmatrix = new Matrix3x3
|
var rotationMatrix = new Matrix3x3
|
||||||
{
|
{
|
||||||
M11 = M11 * invScale,
|
M11 = M11 * invScale,
|
||||||
M12 = M12 * invScale,
|
M12 = M12 * invScale,
|
||||||
@@ -590,8 +587,7 @@ namespace FlaxEngine
|
|||||||
M32 = M32 * invScale,
|
M32 = M32 * invScale,
|
||||||
M33 = M33 * invScale
|
M33 = M33 * invScale
|
||||||
};
|
};
|
||||||
|
Quaternion.RotationMatrix(ref rotationMatrix, out rotation);
|
||||||
Quaternion.RotationMatrix(ref rotationmatrix, out rotation);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,34 +175,34 @@ public:
|
|||||||
M13 = -value.Z;
|
M13 = -value.Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
|
// Gets the forward Float3 of the matrix; that is M31, M32, and M33.
|
||||||
Float3 GetForward() const
|
Float3 GetForward() const
|
||||||
{
|
{
|
||||||
return -Float3(M31, M32, M33);
|
return -Float3(M31, M32, M33);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33.
|
// Sets the forward Float3 of the matrix; that is M31, M32, and M33.
|
||||||
void SetForward(const Float3& value)
|
void SetForward(const Float3& value)
|
||||||
{
|
|
||||||
M31 = -value.X;
|
|
||||||
M32 = -value.Y;
|
|
||||||
M33 = -value.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the backward Float3 of the matrix; that is M31, M32, and M33.
|
|
||||||
Float3 GetBackward() const
|
|
||||||
{
|
|
||||||
return Float3(M31, M32, M33);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the backward Float3 of the matrix; that is M31, M32, and M33.
|
|
||||||
void SetBackward(const Float3& value)
|
|
||||||
{
|
{
|
||||||
M31 = value.X;
|
M31 = value.X;
|
||||||
M32 = value.Y;
|
M32 = value.Y;
|
||||||
M33 = value.Z;
|
M33 = value.Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
|
||||||
|
Float3 GetBackward() const
|
||||||
|
{
|
||||||
|
return Float3(-M31, -M32, -M33);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the backward Float3 of the matrix; that is -M31, -M32, and -M33.
|
||||||
|
void SetBackward(const Float3& value)
|
||||||
|
{
|
||||||
|
M31 = -value.X;
|
||||||
|
M32 = -value.Y;
|
||||||
|
M33 = -value.Z;
|
||||||
|
}
|
||||||
|
|
||||||
// Gets the first row in the matrix; that is M11, M12 and M13.
|
// Gets the first row in the matrix; that is M11, M12 and M13.
|
||||||
Float3 GetRow1() const
|
Float3 GetRow1() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1827,6 +1827,7 @@ Variant::operator Float4() const
|
|||||||
return Float4(*(Float3*)AsData, 0.0f);
|
return Float4(*(Float3*)AsData, 0.0f);
|
||||||
case VariantType::Float4:
|
case VariantType::Float4:
|
||||||
case VariantType::Color:
|
case VariantType::Color:
|
||||||
|
case VariantType::Quaternion:
|
||||||
return *(Float4*)AsData;
|
return *(Float4*)AsData;
|
||||||
case VariantType::Double2:
|
case VariantType::Double2:
|
||||||
return Float4(AsDouble2(), 0.0f, 0.0f);
|
return Float4(AsDouble2(), 0.0f, 0.0f);
|
||||||
|
|||||||
@@ -94,6 +94,13 @@ struct DebugLine
|
|||||||
float TimeLeft;
|
float TimeLeft;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DebugGeometryBuffer
|
||||||
|
{
|
||||||
|
GPUBuffer* Buffer;
|
||||||
|
float TimeLeft;
|
||||||
|
Matrix Transform;
|
||||||
|
};
|
||||||
|
|
||||||
struct DebugTriangle
|
struct DebugTriangle
|
||||||
{
|
{
|
||||||
Float3 V0;
|
Float3 V0;
|
||||||
@@ -122,12 +129,9 @@ struct DebugText3D
|
|||||||
float TimeLeft;
|
float TimeLeft;
|
||||||
};
|
};
|
||||||
|
|
||||||
PACK_STRUCT(struct Vertex {
|
typedef DebugDraw::Vertex Vertex;
|
||||||
Float3 Position;
|
|
||||||
Color32 Color;
|
|
||||||
});
|
|
||||||
|
|
||||||
GPU_CB_STRUCT(Data {
|
GPU_CB_STRUCT(ShaderData {
|
||||||
Matrix ViewProjection;
|
Matrix ViewProjection;
|
||||||
Float2 Padding;
|
Float2 Padding;
|
||||||
float ClipPosZBias;
|
float ClipPosZBias;
|
||||||
@@ -231,6 +235,7 @@ void TeleportList(const Float3& delta, Array<DebugText3D>& list)
|
|||||||
|
|
||||||
struct DebugDrawData
|
struct DebugDrawData
|
||||||
{
|
{
|
||||||
|
Array<DebugGeometryBuffer> GeometryBuffers;
|
||||||
Array<DebugLine> DefaultLines;
|
Array<DebugLine> DefaultLines;
|
||||||
Array<Vertex> OneFrameLines;
|
Array<Vertex> OneFrameLines;
|
||||||
Array<DebugTriangle> DefaultTriangles;
|
Array<DebugTriangle> DefaultTriangles;
|
||||||
@@ -244,7 +249,7 @@ struct DebugDrawData
|
|||||||
|
|
||||||
inline int32 Count() const
|
inline int32 Count() const
|
||||||
{
|
{
|
||||||
return LinesCount() + TrianglesCount() + TextCount();
|
return LinesCount() + TrianglesCount() + TextCount() + GeometryBuffers.Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int32 LinesCount() const
|
inline int32 LinesCount() const
|
||||||
@@ -280,6 +285,7 @@ struct DebugDrawData
|
|||||||
|
|
||||||
inline void Update(float deltaTime)
|
inline void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
|
UpdateList(deltaTime, GeometryBuffers);
|
||||||
UpdateList(deltaTime, DefaultLines);
|
UpdateList(deltaTime, DefaultLines);
|
||||||
UpdateList(deltaTime, DefaultTriangles);
|
UpdateList(deltaTime, DefaultTriangles);
|
||||||
UpdateList(deltaTime, DefaultWireTriangles);
|
UpdateList(deltaTime, DefaultWireTriangles);
|
||||||
@@ -784,7 +790,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
|
|
||||||
// Update constant buffer
|
// Update constant buffer
|
||||||
const auto cb = DebugDrawShader->GetShader()->GetCB(0);
|
const auto cb = DebugDrawShader->GetShader()->GetCB(0);
|
||||||
Data data;
|
ShaderData data;
|
||||||
Matrix vp;
|
Matrix vp;
|
||||||
Matrix::Multiply(view.View, view.Projection, vp);
|
Matrix::Multiply(view.View, view.Projection, vp);
|
||||||
Matrix::Transpose(vp, data.ViewProjection);
|
Matrix::Transpose(vp, data.ViewProjection);
|
||||||
@@ -830,6 +836,22 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount);
|
context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Geometries
|
||||||
|
for (auto& geometry : Context->DebugDrawDepthTest.GeometryBuffers)
|
||||||
|
{
|
||||||
|
auto tmp = data;
|
||||||
|
Matrix mvp;
|
||||||
|
Matrix::Multiply(geometry.Transform, vp, mvp);
|
||||||
|
Matrix::Transpose(mvp, tmp.ViewProjection);
|
||||||
|
context->UpdateCB(cb, &tmp);
|
||||||
|
auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault;
|
||||||
|
context->SetState(state->Get(enableDepthWrite, true));
|
||||||
|
context->BindVB(ToSpan(&geometry.Buffer, 1));
|
||||||
|
context->Draw(0, geometry.Buffer->GetElementsCount());
|
||||||
|
}
|
||||||
|
if (Context->DebugDrawDepthTest.GeometryBuffers.HasItems())
|
||||||
|
context->UpdateCB(cb, &data);
|
||||||
|
|
||||||
if (data.EnableDepthTest)
|
if (data.EnableDepthTest)
|
||||||
context->UnBindSR(0);
|
context->UnBindSR(0);
|
||||||
}
|
}
|
||||||
@@ -862,6 +884,19 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
context->BindVB(ToSpan(&vb, 1));
|
context->BindVB(ToSpan(&vb, 1));
|
||||||
context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount);
|
context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Geometries
|
||||||
|
for (auto& geometry : Context->DebugDrawDefault.GeometryBuffers)
|
||||||
|
{
|
||||||
|
auto tmp = data;
|
||||||
|
Matrix mvp;
|
||||||
|
Matrix::Multiply(geometry.Transform, vp, mvp);
|
||||||
|
Matrix::Transpose(mvp, tmp.ViewProjection);
|
||||||
|
context->UpdateCB(cb, &tmp);
|
||||||
|
context->SetState(DebugDrawPsLinesDefault.Get(false, false));
|
||||||
|
context->BindVB(ToSpan(&geometry.Buffer, 1));
|
||||||
|
context->Draw(0, geometry.Buffer->GetElementsCount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
@@ -1088,6 +1123,24 @@ void DebugDraw::DrawLines(const Span<Float3>& lines, const Matrix& transform, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float duration, bool depthTest)
|
||||||
|
{
|
||||||
|
if (lines == nullptr || lines->GetSize() == 0)
|
||||||
|
return;
|
||||||
|
if (lines->GetSize() % (sizeof(Vertex) * 2) != 0)
|
||||||
|
{
|
||||||
|
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw lines
|
||||||
|
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
|
||||||
|
auto& geometry = debugDrawData.GeometryBuffers.AddOne();
|
||||||
|
geometry.Buffer = lines;
|
||||||
|
geometry.TimeLeft = duration;
|
||||||
|
geometry.Transform = transform * Matrix::Translation(-Context->Origin);
|
||||||
|
}
|
||||||
|
|
||||||
void DebugDraw::DrawLines(const Array<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
void DebugDraw::DrawLines(const Array<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
|
||||||
{
|
{
|
||||||
DrawLines(Span<Float3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
|
DrawLines(Span<Float3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
|
||||||
@@ -2147,6 +2200,7 @@ void DebugDraw::DrawText(const StringView& text, const Transform& transform, con
|
|||||||
|
|
||||||
void DebugDraw::Clear(void* context)
|
void DebugDraw::Clear(void* context)
|
||||||
{
|
{
|
||||||
DebugDraw::UpdateContext(context, MAX_float);
|
UpdateContext(context, MAX_float);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include "Engine/Scripting/ScriptingType.h"
|
#include "Engine/Scripting/ScriptingType.h"
|
||||||
#include "Engine/Core/Math/Color.h"
|
#include "Engine/Core/Math/Color.h"
|
||||||
|
#include "Engine/Core/Math/Color32.h"
|
||||||
|
#include "Engine/Core/Math/Vector3.h"
|
||||||
#include "Engine/Core/Types/Span.h"
|
#include "Engine/Core/Types/Span.h"
|
||||||
|
|
||||||
struct RenderView;
|
struct RenderView;
|
||||||
@@ -14,6 +16,7 @@ class Light;
|
|||||||
struct RenderContext;
|
struct RenderContext;
|
||||||
class GPUTextureView;
|
class GPUTextureView;
|
||||||
class GPUContext;
|
class GPUContext;
|
||||||
|
class GPUBuffer;
|
||||||
class RenderTask;
|
class RenderTask;
|
||||||
class SceneRenderTask;
|
class SceneRenderTask;
|
||||||
class Actor;
|
class Actor;
|
||||||
@@ -26,6 +29,14 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
|||||||
{
|
{
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Vertex data for debug shapes.
|
||||||
|
/// </summary>
|
||||||
|
PACK_STRUCT(struct Vertex {
|
||||||
|
Float3 Position;
|
||||||
|
Color32 Color;
|
||||||
|
});
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
|
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
|
||||||
@@ -175,6 +186,15 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
|||||||
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</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 DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
|
API_FUNCTION() static void DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the lines using the provided vertex buffer that contains pairs of Vertex elements. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lines">The GPU buffer with vertices for lines (must have multiple of 2 elements).</param>
|
||||||
|
/// <param name="transform">The custom matrix used to transform all line vertices.</param>
|
||||||
|
/// <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 DrawLines(GPUBuffer* lines, const Matrix& transform, float duration = 0.0f, bool depthTest = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
|
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -691,9 +711,9 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
|||||||
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f);
|
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clear all debug draw displayed on sceen.
|
/// Clears all debug shapes displayed on screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <param name="context">The context.</param>
|
||||||
API_FUNCTION() static void Clear(void* context = nullptr);
|
API_FUNCTION() static void Clear(void* context = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ bool CommandLine::Parse(const Char* cmdLine)
|
|||||||
PARSE_ARG_SWITCH("-build ", Build);
|
PARSE_ARG_SWITCH("-build ", Build);
|
||||||
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
|
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
|
||||||
PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug);
|
PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug);
|
||||||
|
PARSE_BOOL_SWITCH("-exit ", Exit);
|
||||||
PARSE_ARG_OPT_SWITCH("-play ", Play);
|
PARSE_ARG_OPT_SWITCH("-play ", Play);
|
||||||
#endif
|
#endif
|
||||||
#if USE_EDITOR || !BUILD_RELEASE
|
#if USE_EDITOR || !BUILD_RELEASE
|
||||||
|
|||||||
@@ -168,6 +168,11 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Nullable<bool> ShaderDebug;
|
Nullable<bool> ShaderDebug;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -exit (exits the editor after startup and performing all queued actions). Usefull when invoking editor from CL/CD.
|
||||||
|
/// </summary>
|
||||||
|
Nullable<bool> Exit;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// -play !guid! ( Scene to play, can be empty to use default )
|
/// -play !guid! ( Scene to play, can be empty to use default )
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Graphics/GPUDevice.h"
|
#include "Engine/Graphics/GPUDevice.h"
|
||||||
#include "Engine/Threading/Threading.h"
|
#include "Engine/Threading/Threading.h"
|
||||||
|
#include "Engine/Engine/Globals.h"
|
||||||
|
|
||||||
#define GPU_TASKS_USE_DEDICATED_CONTEXT 0
|
#define GPU_TASKS_USE_DEDICATED_CONTEXT 0
|
||||||
|
|
||||||
@@ -36,7 +37,8 @@ GPUTasksContext::~GPUTasksContext()
|
|||||||
auto task = tasks[i];
|
auto task = tasks[i];
|
||||||
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
|
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
|
||||||
{
|
{
|
||||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
if (!Globals::IsRequestingExit)
|
||||||
|
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||||
task->CancelSync();
|
task->CancelSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +62,8 @@ void GPUTasksContext::OnCancelSync(GPUTask* task)
|
|||||||
|
|
||||||
_tasksDone.Remove(task);
|
_tasksDone.Remove(task);
|
||||||
|
|
||||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
if (!Globals::IsRequestingExit)
|
||||||
|
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUTasksContext::OnFrameBegin()
|
void GPUTasksContext::OnFrameBegin()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "GPUResourceProperty.h"
|
#include "GPUResourceProperty.h"
|
||||||
#include "GPUBufferDescription.h"
|
#include "GPUBufferDescription.h"
|
||||||
#include "PixelFormatExtensions.h"
|
#include "PixelFormatExtensions.h"
|
||||||
|
#include "RenderTask.h"
|
||||||
#include "Async/Tasks/GPUCopyResourceTask.h"
|
#include "Async/Tasks/GPUCopyResourceTask.h"
|
||||||
#include "Engine/Core/Utilities.h"
|
#include "Engine/Core/Utilities.h"
|
||||||
#include "Engine/Core/Types/String.h"
|
#include "Engine/Core/Types/String.h"
|
||||||
@@ -358,6 +359,16 @@ void GPUBuffer::SetData(const void* data, uint32 size)
|
|||||||
Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData"));
|
Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_desc.Usage == GPUResourceUsage::Default && GPUDevice::Instance->IsRendering())
|
||||||
|
{
|
||||||
|
// Upload using the context (will use internal staging buffer inside command buffer)
|
||||||
|
RenderContext::GPULocker.Lock();
|
||||||
|
GPUDevice::Instance->GetMainContext()->UpdateBuffer(this, data, size);
|
||||||
|
RenderContext::GPULocker.Unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void* mapped = Map(GPUResourceMapMode::Write);
|
void* mapped = Map(GPUResourceMapMode::Write);
|
||||||
if (!mapped)
|
if (!mapped)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -74,8 +74,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY() FORCE_INLINE uint32 GetElementsCount() const
|
API_PROPERTY() FORCE_INLINE uint32 GetElementsCount() const
|
||||||
{
|
{
|
||||||
ASSERT(_desc.Stride > 0);
|
return _desc.Stride > 0 ? _desc.Size / _desc.Stride : 0;
|
||||||
return _desc.Size / _desc.Stride;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -282,35 +282,35 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
|||||||
switch (_type)
|
switch (_type)
|
||||||
{
|
{
|
||||||
case MaterialParameterType::Bool:
|
case MaterialParameterType::Bool:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(bool));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(bool)));
|
||||||
*((int32*)(meta.Constants.Get() + _offset)) = _asBool;
|
*((int32*)(meta.Constants.Get() + _offset)) = _asBool;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Integer:
|
case MaterialParameterType::Integer:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(int32));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(int32)));
|
||||||
*((int32*)(meta.Constants.Get() + _offset)) = _asInteger;
|
*((int32*)(meta.Constants.Get() + _offset)) = _asInteger;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Float:
|
case MaterialParameterType::Float:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(float));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(float)));
|
||||||
*((float*)(meta.Constants.Get() + _offset)) = _asFloat;
|
*((float*)(meta.Constants.Get() + _offset)) = _asFloat;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Vector2:
|
case MaterialParameterType::Vector2:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2)));
|
||||||
*((Float2*)(meta.Constants.Get() + _offset)) = _asVector2;
|
*((Float2*)(meta.Constants.Get() + _offset)) = _asVector2;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Vector3:
|
case MaterialParameterType::Vector3:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3)));
|
||||||
*((Float3*)(meta.Constants.Get() + _offset)) = _asVector3;
|
*((Float3*)(meta.Constants.Get() + _offset)) = _asVector3;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Vector4:
|
case MaterialParameterType::Vector4:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
|
||||||
*((Float4*)(meta.Constants.Get() + _offset)) = *(Float4*)&AsData;
|
*((Float4*)(meta.Constants.Get() + _offset)) = *(Float4*)&AsData;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Color:
|
case MaterialParameterType::Color:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
|
||||||
*((Color*)(meta.Constants.Get() + _offset)) = _asColor;
|
*((Color*)(meta.Constants.Get() + _offset)) = _asColor;
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::Matrix:
|
case MaterialParameterType::Matrix:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Matrix));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Matrix)));
|
||||||
Matrix::Transpose(*(Matrix*)&AsData, *(Matrix*)(meta.Constants.Get() + _offset));
|
Matrix::Transpose(*(Matrix*)&AsData, *(Matrix*)(meta.Constants.Get() + _offset));
|
||||||
break;
|
break;
|
||||||
case MaterialParameterType::NormalMap:
|
case MaterialParameterType::NormalMap:
|
||||||
@@ -409,44 +409,44 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
|||||||
switch (e->Value.Type.Type)
|
switch (e->Value.Type.Type)
|
||||||
{
|
{
|
||||||
case VariantType::Bool:
|
case VariantType::Bool:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(bool));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(bool)));
|
||||||
*((bool*)(meta.Constants.Get() + _offset)) = e->Value.AsBool;
|
*((bool*)(meta.Constants.Get() + _offset)) = e->Value.AsBool;
|
||||||
break;
|
break;
|
||||||
case VariantType::Int:
|
case VariantType::Int:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(int32));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(int32)));
|
||||||
*((int32*)(meta.Constants.Get() + _offset)) = e->Value.AsInt;
|
*((int32*)(meta.Constants.Get() + _offset)) = e->Value.AsInt;
|
||||||
break;
|
break;
|
||||||
case VariantType::Uint:
|
case VariantType::Uint:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(uint32));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(uint32)));
|
||||||
*((uint32*)(meta.Constants.Get() + _offset)) = e->Value.AsUint;
|
*((uint32*)(meta.Constants.Get() + _offset)) = e->Value.AsUint;
|
||||||
break;
|
break;
|
||||||
case VariantType::Float:
|
case VariantType::Float:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(float));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(float)));
|
||||||
*((float*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat;
|
*((float*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat;
|
||||||
break;
|
break;
|
||||||
case VariantType::Float2:
|
case VariantType::Float2:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2)));
|
||||||
*((Float2*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat2();
|
*((Float2*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat2();
|
||||||
break;
|
break;
|
||||||
case VariantType::Float3:
|
case VariantType::Float3:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3)));
|
||||||
*((Float3*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat3();
|
*((Float3*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat3();
|
||||||
break;
|
break;
|
||||||
case VariantType::Float4:
|
case VariantType::Float4:
|
||||||
case VariantType::Color:
|
case VariantType::Color:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
|
||||||
*((Float4*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat4();
|
*((Float4*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat4();
|
||||||
break;
|
break;
|
||||||
case VariantType::Double2:
|
case VariantType::Double2:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2)));
|
||||||
*((Float2*)(meta.Constants.Get() + _offset)) = (Float2)e->Value.AsDouble2();
|
*((Float2*)(meta.Constants.Get() + _offset)) = (Float2)e->Value.AsDouble2();
|
||||||
break;
|
break;
|
||||||
case VariantType::Double3:
|
case VariantType::Double3:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3)));
|
||||||
*((Float3*)(meta.Constants.Get() + _offset)) = (Float3)e->Value.AsDouble3();
|
*((Float3*)(meta.Constants.Get() + _offset)) = (Float3)e->Value.AsDouble3();
|
||||||
break;
|
break;
|
||||||
case VariantType::Double4:
|
case VariantType::Double4:
|
||||||
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4));
|
ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4)));
|
||||||
*((Float4*)(meta.Constants.Get() + _offset)) = (Float4)e->Value.AsDouble4();
|
*((Float4*)(meta.Constants.Get() + _offset)) = (Float4)e->Value.AsDouble4();
|
||||||
break;
|
break;
|
||||||
default: ;
|
default: ;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ void ParticleMaterialShader::Bind(BindParameters& params)
|
|||||||
{
|
{
|
||||||
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
|
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
|
||||||
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
|
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
|
||||||
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)param.GetBindOffset() + sizeof(int32));
|
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)(param.GetBindOffset() + sizeof(int32)));
|
||||||
*((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset;
|
*((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void VolumeParticleMaterialShader::Bind(BindParameters& params)
|
|||||||
{
|
{
|
||||||
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
|
const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9);
|
||||||
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
|
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
|
||||||
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)param.GetBindOffset() + sizeof(int32));
|
ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)(param.GetBindOffset() + sizeof(int32)));
|
||||||
*((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset;
|
*((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,7 +285,10 @@ void GPUContextDX11::SetBlendFactor(const Float4& value)
|
|||||||
void GPUContextDX11::SetStencilRef(uint32 value)
|
void GPUContextDX11::SetStencilRef(uint32 value)
|
||||||
{
|
{
|
||||||
if (CurrentStencilRef != value)
|
if (CurrentStencilRef != value)
|
||||||
|
{
|
||||||
|
CurrentStencilRef = value;
|
||||||
_context->OMSetDepthStencilState(CurrentDepthStencilState, CurrentStencilRef);
|
_context->OMSetDepthStencilState(CurrentDepthStencilState, CurrentStencilRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUContextDX11::ResetSR()
|
void GPUContextDX11::ResetSR()
|
||||||
|
|||||||
@@ -158,10 +158,10 @@ float Spline::GetSplineLength() const
|
|||||||
const auto& b = Curve[i];
|
const auto& b = Curve[i];
|
||||||
Vector3 prevPoint = a.Value.Translation * scale;
|
Vector3 prevPoint = a.Value.Translation * scale;
|
||||||
|
|
||||||
const float length = Math::Abs(b.Time - a.Time);
|
const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f;
|
||||||
Vector3 leftTangent, rightTangent;
|
Vector3 leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent);
|
||||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent);
|
||||||
|
|
||||||
for (int32 slice = 1; slice < slices; slice++)
|
for (int32 slice = 1; slice < slices; slice++)
|
||||||
{
|
{
|
||||||
@@ -189,10 +189,10 @@ float Spline::GetSplineSegmentLength(int32 index) const
|
|||||||
const Vector3 scale = _transform.Scale;
|
const Vector3 scale = _transform.Scale;
|
||||||
Vector3 prevPoint = a.Value.Translation * scale;
|
Vector3 prevPoint = a.Value.Translation * scale;
|
||||||
{
|
{
|
||||||
const float length = Math::Abs(b.Time - a.Time);
|
const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f;
|
||||||
Vector3 leftTangent, rightTangent;
|
Vector3 leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent);
|
||||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent);
|
||||||
|
|
||||||
for (int32 slice = 1; slice < slices; slice++)
|
for (int32 slice = 1; slice < slices; slice++)
|
||||||
{
|
{
|
||||||
@@ -478,9 +478,7 @@ void Spline::GetKeyframes(MArray* data)
|
|||||||
|
|
||||||
void Spline::SetKeyframes(MArray* data)
|
void Spline::SetKeyframes(MArray* data)
|
||||||
{
|
{
|
||||||
const int32 count = MCore::Array::GetLength(data);
|
Curve = Span<byte>((const byte*)MCore::Array::GetAddress(data), MCore::Array::GetLength(data));
|
||||||
Curve.GetKeyframes().Resize(count, false);
|
|
||||||
Platform::MemoryCopy(Curve.GetKeyframes().Get(), MCore::Array::GetAddress(data), sizeof(Keyframe) * count);
|
|
||||||
UpdateSpline();
|
UpdateSpline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,9 +184,9 @@ void SplineModel::OnSplineUpdated()
|
|||||||
auto& instance = _instances[segment];
|
auto& instance = _instances[segment];
|
||||||
const auto& start = keyframes[segment];
|
const auto& start = keyframes[segment];
|
||||||
const auto& end = keyframes[segment + 1];
|
const auto& end = keyframes[segment + 1];
|
||||||
const float length = end.Time - start.Time;
|
const float tangentScale = (end.Time - start.Time) / 3.0f;
|
||||||
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
|
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
|
||||||
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
|
||||||
|
|
||||||
// Find maximum scale over the segment spline and collect the segment positions for bounds
|
// Find maximum scale over the segment spline and collect the segment positions for bounds
|
||||||
segmentPoints.Clear();
|
segmentPoints.Clear();
|
||||||
@@ -256,9 +256,9 @@ void SplineModel::UpdateDeformationBuffer()
|
|||||||
auto& instance = _instances[segment];
|
auto& instance = _instances[segment];
|
||||||
const auto& start = keyframes[segment];
|
const auto& start = keyframes[segment];
|
||||||
const auto& end = keyframes[segment + 1];
|
const auto& end = keyframes[segment + 1];
|
||||||
const float length = end.Time - start.Time;
|
const float tangentScale = (end.Time - start.Time) / 3.0f;
|
||||||
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
|
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
|
||||||
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
|
||||||
for (int32 chunk = 0; chunk < chunksPerSegment; chunk++)
|
for (int32 chunk = 0; chunk < chunksPerSegment; chunk++)
|
||||||
{
|
{
|
||||||
const float alpha = (chunk == chunksPerSegment - 1) ? 1.0f : ((float)chunk * chunksPerSegmentInv);
|
const float alpha = (chunk == chunksPerSegment - 1) ? 1.0f : ((float)chunk * chunksPerSegmentInv);
|
||||||
@@ -291,10 +291,10 @@ void SplineModel::UpdateDeformationBuffer()
|
|||||||
{
|
{
|
||||||
const auto& start = keyframes[segments - 1];
|
const auto& start = keyframes[segments - 1];
|
||||||
const auto& end = keyframes[segments];
|
const auto& end = keyframes[segments];
|
||||||
const float length = end.Time - start.Time;
|
const float tangentScale = (end.Time - start.Time) / 3.0f;
|
||||||
const float alpha = 1.0f - ZeroTolerance; // Offset to prevent zero derivative at the end of the curve
|
const float alpha = 1.0f - ZeroTolerance; // Offset to prevent zero derivative at the end of the curve
|
||||||
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
|
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
|
||||||
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
|
||||||
AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform);
|
AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform);
|
||||||
Vector3 direction;
|
Vector3 direction;
|
||||||
AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction);
|
AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction);
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
|
|||||||
// Draw physics shapes
|
// Draw physics shapes
|
||||||
if (EnumHasAnyFlags(view.Flags, ViewFlags::PhysicsDebug) || view.Mode == ViewMode::PhysicsColliders)
|
if (EnumHasAnyFlags(view.Flags, ViewFlags::PhysicsDebug) || view.Mode == ViewMode::PhysicsColliders)
|
||||||
{
|
{
|
||||||
|
PROFILE_CPU_NAMED("PhysicsDebug");
|
||||||
const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get();
|
const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get();
|
||||||
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
|
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
|
||||||
{
|
{
|
||||||
@@ -100,6 +101,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
|
|||||||
// Draw light shapes
|
// Draw light shapes
|
||||||
if (EnumHasAnyFlags(view.Flags, ViewFlags::LightsDebug))
|
if (EnumHasAnyFlags(view.Flags, ViewFlags::LightsDebug))
|
||||||
{
|
{
|
||||||
|
PROFILE_CPU_NAMED("LightsDebug");
|
||||||
const LightsDebugCallback* lightsDebugData = LightsDebug.Get();
|
const LightsDebugCallback* lightsDebugData = LightsDebug.Get();
|
||||||
for (int32 i = 0; i < LightsDebug.Count(); i++)
|
for (int32 i = 0; i < LightsDebug.Count(); i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -456,6 +456,7 @@ void ParticleEmitterGPUGenerator::PrepareGraph(ParticleEmitterGraphGPU* graph)
|
|||||||
mp.AsFloat3 = param->Value.AsFloat3();
|
mp.AsFloat3 = param->Value.AsFloat3();
|
||||||
break;
|
break;
|
||||||
case VariantType::Float4:
|
case VariantType::Float4:
|
||||||
|
case VariantType::Quaternion:
|
||||||
mp.Type = MaterialParameterType::Vector4;
|
mp.Type = MaterialParameterType::Vector4;
|
||||||
*(Float4*)&mp.AsData = param->Value.AsFloat4();
|
*(Float4*)&mp.AsData = param->Value.AsFloat4();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -214,9 +214,9 @@ void SplineCollider::GetGeometry(CollisionShape& collision)
|
|||||||
auto offsetIndices = segment * collisionIndices.Count();
|
auto offsetIndices = segment * collisionIndices.Count();
|
||||||
const auto& start = keyframes[segment];
|
const auto& start = keyframes[segment];
|
||||||
const auto& end = keyframes[segment + 1];
|
const auto& end = keyframes[segment + 1];
|
||||||
const float length = end.Time - start.Time;
|
const float tangentScale = (end.Time - start.Time) / 3.0f;
|
||||||
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
|
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
|
||||||
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
|
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
|
||||||
|
|
||||||
// Vertex buffer is deformed along the spline
|
// Vertex buffer is deformed along the spline
|
||||||
auto srcVertices = collisionVertices.Get();
|
auto srcVertices = collisionVertices.Get();
|
||||||
|
|||||||
@@ -468,12 +468,12 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
|||||||
if (settings.WaitForEnd)
|
if (settings.WaitForEnd)
|
||||||
{
|
{
|
||||||
id<NSObject> outputObserver = nil;
|
id<NSObject> outputObserver = nil;
|
||||||
|
id<NSObject> outputObserverError = nil;
|
||||||
|
|
||||||
if (captureStdOut)
|
if (captureStdOut)
|
||||||
{
|
{
|
||||||
NSPipe *stdoutPipe = [NSPipe pipe];
|
NSPipe* stdoutPipe = [NSPipe pipe];
|
||||||
[task setStandardOutput:stdoutPipe];
|
[task setStandardOutput:stdoutPipe];
|
||||||
|
|
||||||
outputObserver = [[NSNotificationCenter defaultCenter]
|
outputObserver = [[NSNotificationCenter defaultCenter]
|
||||||
addObserverForName: NSFileHandleDataAvailableNotification
|
addObserverForName: NSFileHandleDataAvailableNotification
|
||||||
object: [stdoutPipe fileHandleForReading]
|
object: [stdoutPipe fileHandleForReading]
|
||||||
@@ -497,8 +497,34 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
[[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
|
[[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
|
||||||
|
|
||||||
|
NSPipe *stderrPipe = [NSPipe pipe];
|
||||||
|
[task setStandardError:stderrPipe];
|
||||||
|
outputObserverError = [[NSNotificationCenter defaultCenter]
|
||||||
|
addObserverForName: NSFileHandleDataAvailableNotification
|
||||||
|
object: [stderrPipe fileHandleForReading]
|
||||||
|
queue: nil
|
||||||
|
usingBlock:^(NSNotification* notification)
|
||||||
|
{
|
||||||
|
NSData* data = [stderrPipe fileHandleForReading].availableData;
|
||||||
|
if (data.length)
|
||||||
|
{
|
||||||
|
String line((const char*)data.bytes, data.length);
|
||||||
|
if (settings.SaveOutput)
|
||||||
|
settings.Output.Add(line.Get(), line.Length());
|
||||||
|
if (settings.LogOutput)
|
||||||
|
{
|
||||||
|
StringView lineView(line);
|
||||||
|
if (line[line.Length() - 1] == '\n')
|
||||||
|
lineView = StringView(line.Get(), line.Length() - 1);
|
||||||
|
Log::Logger::Write(LogType::Error, lineView);
|
||||||
|
}
|
||||||
|
[[stderrPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
[[stderrPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
|
||||||
}
|
}
|
||||||
|
|
||||||
String exception;
|
String exception;
|
||||||
|
|||||||
@@ -301,12 +301,12 @@ void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2
|
|||||||
IBIndex += 3;
|
IBIndex += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color0, const Color& color1, const Color& color2)
|
FORCE_INLINE void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color0, const Color& color1, const Color& color2)
|
||||||
{
|
{
|
||||||
WriteTri(p0, p1, p2, Float2::Zero, Float2::Zero, Float2::Zero, color0, color1, color2);
|
WriteTri(p0, p1, p2, Float2::Zero, Float2::Zero, Float2::Zero, color0, color1, color2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2& uv0, const Float2& uv1, const Float2& uv2)
|
FORCE_INLINE void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2& uv0, const Float2& uv1, const Float2& uv2)
|
||||||
{
|
{
|
||||||
WriteTri(p0, p1, p2, uv0, uv1, uv2, Color::Black, Color::Black, Color::Black);
|
WriteTri(p0, p1, p2, uv0, uv1, uv2, Color::Black, Color::Black, Color::Black);
|
||||||
}
|
}
|
||||||
@@ -1816,8 +1816,8 @@ void DrawLines(const Float2* points, int32 pointsCount, const Color& color1, con
|
|||||||
|
|
||||||
// Ending cap
|
// Ending cap
|
||||||
{
|
{
|
||||||
ApplyTransform(points[0], p1t);
|
ApplyTransform(points[pointsCount - 2], p1t);
|
||||||
ApplyTransform(points[1], p2t);
|
//ApplyTransform(points[pointsCount - 1], p2t);
|
||||||
|
|
||||||
const Float2 capDirection = thicknessHalf * Float2::Normalize(p2t - p1t);
|
const Float2 capDirection = thicknessHalf * Float2::Normalize(p2t - p1t);
|
||||||
|
|
||||||
@@ -1882,19 +1882,46 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3,
|
|||||||
const Float2 d3 = p4 - p3;
|
const Float2 d3 = p4 - p3;
|
||||||
const float len = d1.Length() + d2.Length() + d3.Length();
|
const float len = d1.Length() + d2.Length() + d3.Length();
|
||||||
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
|
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
|
||||||
const float segmentCountInv = 1.0f / segmentCount;
|
const float segmentCountInv = 1.0f / (float)segmentCount;
|
||||||
|
|
||||||
// Draw segmented curve
|
// Draw segmented curve
|
||||||
Float2 p;
|
|
||||||
AnimationUtils::Bezier(p1, p2, p3, p4, 0, p);
|
|
||||||
Lines2.Clear();
|
Lines2.Clear();
|
||||||
Lines2.Add(p);
|
Lines2.Add(p1);
|
||||||
for (int32 i = 1; i <= segmentCount; i++)
|
for (int32 i = 1; i < segmentCount; i++)
|
||||||
{
|
{
|
||||||
const float t = i * segmentCountInv;
|
const float t = (float)i * segmentCountInv;
|
||||||
|
Float2 p;
|
||||||
AnimationUtils::Bezier(p1, p2, p3, p4, t, p);
|
AnimationUtils::Bezier(p1, p2, p3, p4, t, p);
|
||||||
Lines2.Add(p);
|
Lines2.Add(p);
|
||||||
}
|
}
|
||||||
|
Lines2.Add(p4);
|
||||||
|
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render2D::DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
|
||||||
|
// Find amount of segments to use
|
||||||
|
const Float2 d1 = p2 - p1;
|
||||||
|
const Float2 d2 = p3 - p2;
|
||||||
|
const Float2 d3 = p4 - p3;
|
||||||
|
const float len = d1.Length() + d2.Length() + d3.Length();
|
||||||
|
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
|
||||||
|
const float segmentCountInv = 1.0f / (float)segmentCount;
|
||||||
|
|
||||||
|
// Draw segmented curve
|
||||||
|
Lines2.Clear();
|
||||||
|
Lines2.Add(p1);
|
||||||
|
for (int32 i = 1; i < segmentCount; i++)
|
||||||
|
{
|
||||||
|
const float t = (float)i * segmentCountInv;
|
||||||
|
Float2 p;
|
||||||
|
p.X = Math::Lerp(p1.X, p4.X, t);
|
||||||
|
AnimationUtils::Bezier(p1.Y, p2.Y, p3.Y, p4.Y, t, p.Y);
|
||||||
|
Lines2.Add(p);
|
||||||
|
}
|
||||||
|
Lines2.Add(p4);
|
||||||
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
|
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1935,9 +1962,56 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
|
|||||||
WriteRect(rect, Color::White);
|
WriteRect(rect, Color::White);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Render2D::DrawTriangles(const Span<Float2>& vertices, const Color& color, float thickness)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
|
|
||||||
|
Float2 points[2];
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
// TODO: fix this
|
||||||
|
DrawLines(&vertices.Get()[i], 3, color, color, thickness);
|
||||||
|
#else
|
||||||
|
points[0] = vertices.Get()[i + 0];
|
||||||
|
points[1] = vertices.Get()[i + 1];
|
||||||
|
DrawLines(points, 2, color, color, thickness);
|
||||||
|
points[0] = vertices.Get()[i + 2];
|
||||||
|
DrawLines(points, 2, color, color, thickness);
|
||||||
|
points[1] = vertices.Get()[i + 0];
|
||||||
|
DrawLines(points, 2, color, color, thickness);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render2D::DrawTriangles(const Span<Float2>& vertices, const Span<Color>& colors, float thickness)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
|
|
||||||
|
Float2 points[2];
|
||||||
|
Color cols[2];
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
{
|
||||||
|
points[0] = vertices.Get()[i + 0];
|
||||||
|
points[1] = vertices.Get()[i + 1];
|
||||||
|
cols[0] = colors.Get()[i + 0];
|
||||||
|
cols[1] = colors.Get()[i + 1];
|
||||||
|
DrawLines(points, 2, cols[0], cols[1], thickness);
|
||||||
|
points[0] = vertices.Get()[i + 2];
|
||||||
|
cols[0] = colors.Get()[i + 2];
|
||||||
|
DrawLines(points, 2, cols[0], cols[1], thickness);
|
||||||
|
points[1] = vertices.Get()[i + 0];
|
||||||
|
cols[1] = colors.Get()[i + 0];
|
||||||
|
DrawLines(points, 2, cols[0], cols[1], thickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs)
|
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs)
|
||||||
{
|
{
|
||||||
RENDER2D_CHECK_RENDERING_STATE;
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
CHECK(vertices.Length() == uvs.Length());
|
CHECK(vertices.Length() == uvs.Length());
|
||||||
|
|
||||||
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||||
@@ -1952,14 +2026,24 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices
|
|||||||
|
|
||||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Color& color)
|
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Color& color)
|
||||||
{
|
{
|
||||||
Color colors[3] = { (Color)color, (Color)color, (Color)color };
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
Span<Color> spancolor(colors, 3);
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
DrawTexturedTriangles(t, vertices, uvs, spancolor);
|
CHECK(vertices.Length() == uvs.Length());
|
||||||
|
|
||||||
|
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||||
|
drawCall.Type = DrawCallType::FillTexture;
|
||||||
|
drawCall.StartIB = IBIndex;
|
||||||
|
drawCall.CountIB = vertices.Length();
|
||||||
|
drawCall.AsTexture.Ptr = t;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], uvs[i], uvs[i + 1], uvs[i + 2], color, color, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors)
|
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors)
|
||||||
{
|
{
|
||||||
RENDER2D_CHECK_RENDERING_STATE;
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
CHECK(vertices.Length() % 3 == 0);
|
||||||
CHECK(vertices.Length() == uvs.Length());
|
CHECK(vertices.Length() == uvs.Length());
|
||||||
CHECK(vertices.Length() == colors.Length());
|
CHECK(vertices.Length() == colors.Length());
|
||||||
|
|
||||||
@@ -1994,6 +2078,19 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Render2D::FillTriangles(const Span<Float2>& vertices, const Color& color)
|
||||||
|
{
|
||||||
|
RENDER2D_CHECK_RENDERING_STATE;
|
||||||
|
|
||||||
|
Render2DDrawCall& drawCall = DrawCalls.AddOne();
|
||||||
|
drawCall.Type = NeedAlphaWithTint(color) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha;
|
||||||
|
drawCall.StartIB = IBIndex;
|
||||||
|
drawCall.CountIB = vertices.Length();
|
||||||
|
|
||||||
|
for (int32 i = 0; i < vertices.Length(); i += 3)
|
||||||
|
WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], color, color, color);
|
||||||
|
}
|
||||||
|
|
||||||
void Render2D::FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha)
|
void Render2D::FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha)
|
||||||
{
|
{
|
||||||
CHECK(vertices.Length() == colors.Length());
|
CHECK(vertices.Length() == colors.Length());
|
||||||
|
|||||||
@@ -389,6 +389,17 @@ public:
|
|||||||
/// <param name="thickness">The line thickness.</param>
|
/// <param name="thickness">The line thickness.</param>
|
||||||
API_FUNCTION() static void DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
|
API_FUNCTION() static void DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a spline curve (Bezier but X axis represents uniform time).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="p1">The start point.</param>
|
||||||
|
/// <param name="p2">The first control point.</param>
|
||||||
|
/// <param name="p3">The second control point.</param>
|
||||||
|
/// <param name="p4">The end point.</param>
|
||||||
|
/// <param name="color">The line color</param>
|
||||||
|
/// <param name="thickness">The line thickness.</param>
|
||||||
|
API_FUNCTION() static void DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the GUI material.
|
/// Draws the GUI material.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -404,6 +415,22 @@ public:
|
|||||||
/// <param name="blurStrength">The blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.</param>
|
/// <param name="blurStrength">The blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU.</param>
|
||||||
API_FUNCTION() static void DrawBlur(const Rectangle& rect, float blurStrength);
|
API_FUNCTION() static void DrawBlur(const Rectangle& rect, float blurStrength);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws vertices array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices array.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
/// <param name="thickness">The line thickness.</param>
|
||||||
|
API_FUNCTION() static void DrawTriangles(const Span<Float2>& vertices, const Color& color, float thickness = 1.0f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws vertices array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices array.</param>
|
||||||
|
/// <param name="colors">The colors array.</param>
|
||||||
|
/// <param name="thickness">The line thickness.</param>
|
||||||
|
API_FUNCTION() static void DrawTriangles(const Span<Float2>& vertices, const Span<Color>& colors, float thickness = 1.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws vertices array.
|
/// Draws vertices array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -440,13 +467,20 @@ public:
|
|||||||
/// <param name="colors">The colors array.</param>
|
/// <param name="colors">The colors array.</param>
|
||||||
API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors);
|
API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span<uint16>& indices, const Span<Float2>& vertices, const Span<Float2>& uvs, const Span<Color>& colors);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws vertices array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">The vertices array.</param>
|
||||||
|
/// <param name="color">The color.</param>
|
||||||
|
API_FUNCTION() static void FillTriangles(const Span<Float2>& vertices, const Color& color);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws vertices array.
|
/// Draws vertices array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="vertices">The vertices array.</param>
|
/// <param name="vertices">The vertices array.</param>
|
||||||
/// <param name="colors">The colors array.</param>
|
/// <param name="colors">The colors array.</param>
|
||||||
/// <param name="useAlpha">If true alpha blending will be enabled.</param>
|
/// <param name="useAlpha">If true alpha blending will be enabled.</param>
|
||||||
API_FUNCTION() static void FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha);
|
API_FUNCTION() static void FillTriangles(const Span<Float2>& vertices, const Span<Color>& colors, bool useAlpha = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fills a triangular area.
|
/// Fills a triangular area.
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
|
|||||||
|
|
||||||
// Calculate star texture rotation matrix
|
// Calculate star texture rotation matrix
|
||||||
Float3 camX = renderContext.View.View.GetRight();
|
Float3 camX = renderContext.View.View.GetRight();
|
||||||
Float3 camZ = renderContext.View.View.GetForward();
|
Float3 camZ = renderContext.View.View.GetBackward();
|
||||||
float camRot = Float3::Dot(camX, Float3::Forward) + Float3::Dot(camZ, Float3::Up);
|
float camRot = Float3::Dot(camX, Float3::Forward) + Float3::Dot(camZ, Float3::Up);
|
||||||
float camRotCos = Math::Cos(camRot) * 0.8f;
|
float camRotCos = Math::Cos(camRot) * 0.8f;
|
||||||
float camRotSin = Math::Sin(camRot) * 0.8f;
|
float camRotSin = Math::Sin(camRot) * 0.8f;
|
||||||
|
|||||||
42
Source/Engine/Scripting/Attributes/Editor/ButtonAttribute.cs
Normal file
42
Source/Engine/Scripting/Attributes/Editor/ButtonAttribute.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FlaxEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Displays the method in the properties panel where user can click and invoke this method.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Supported on both static and member methods that are parameterless.</remarks>
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public sealed class ButtonAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The button text. Empty value will use method name (auto-formatted).
|
||||||
|
/// </summary>
|
||||||
|
public string Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The button tooltip text. Empty value will use method documentation.
|
||||||
|
/// </summary>
|
||||||
|
public string Tooltip;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ButtonAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ButtonAttribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ButtonAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The button text.</param>
|
||||||
|
/// <param name="tooltip">The button tooltip.</param>
|
||||||
|
public ButtonAttribute(string text, string tooltip = null)
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
|
Tooltip = tooltip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,11 @@
|
|||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
#include "Engine/Debug/DebugDraw.h"
|
#include "Engine/Debug/DebugDraw.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
|
#include "Engine/Graphics/GPUDevice.h"
|
||||||
|
#include "Engine/Graphics/DynamicBuffer.h"
|
||||||
|
#include "Engine/Engine/Units.h"
|
||||||
|
#endif
|
||||||
#include "Engine/Content/Content.h"
|
#include "Engine/Content/Content.h"
|
||||||
#include "Engine/Content/Assets/RawDataAsset.h"
|
#include "Engine/Content/Assets/RawDataAsset.h"
|
||||||
|
|
||||||
@@ -94,7 +99,8 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
_debugLines.Resize(0);
|
SAFE_DELETE(_debugLines);
|
||||||
|
_debugLinesDirty = true;
|
||||||
#endif
|
#endif
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
_collisionTriangles.Resize(0);
|
_collisionTriangles.Resize(0);
|
||||||
@@ -1822,7 +1828,7 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod
|
|||||||
|
|
||||||
// Invalidate cache
|
// Invalidate cache
|
||||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
_debugLines.Resize(0);
|
_debugLinesDirty = true;
|
||||||
#endif
|
#endif
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
_collisionTriangles.Resize(0);
|
_collisionTriangles.Resize(0);
|
||||||
@@ -1940,7 +1946,7 @@ bool TerrainPatch::UpdateCollision()
|
|||||||
{
|
{
|
||||||
// Invalidate cache
|
// Invalidate cache
|
||||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
_debugLines.Resize(0);
|
_debugLinesDirty = true;
|
||||||
#endif
|
#endif
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
_collisionTriangles.Resize(0);
|
_collisionTriangles.Resize(0);
|
||||||
@@ -2082,7 +2088,7 @@ void TerrainPatch::UpdatePostManualDeserialization()
|
|||||||
{
|
{
|
||||||
// Invalidate cache
|
// Invalidate cache
|
||||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
_debugLines.Resize(0);
|
_debugLinesDirty = true;
|
||||||
#endif
|
#endif
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
_collisionTriangles.Resize(0);
|
_collisionTriangles.Resize(0);
|
||||||
@@ -2211,7 +2217,8 @@ void TerrainPatch::DestroyCollision()
|
|||||||
_physicsShape = nullptr;
|
_physicsShape = nullptr;
|
||||||
_physicsHeightField = nullptr;
|
_physicsHeightField = nullptr;
|
||||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
_debugLines.Resize(0);
|
_debugLinesDirty = true;
|
||||||
|
SAFE_DELETE(_debugLines);
|
||||||
#endif
|
#endif
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
_collisionTriangles.Resize(0);
|
_collisionTriangles.Resize(0);
|
||||||
@@ -2224,15 +2231,26 @@ void TerrainPatch::DestroyCollision()
|
|||||||
void TerrainPatch::CacheDebugLines()
|
void TerrainPatch::CacheDebugLines()
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
ASSERT(_debugLines.IsEmpty() && _physicsHeightField);
|
ASSERT(_physicsHeightField);
|
||||||
|
_debugLinesDirty = false;
|
||||||
|
if (!_debugLines)
|
||||||
|
_debugLines = GPUDevice::Instance->CreateBuffer(TEXT("Terrain.DebugLines"));
|
||||||
|
|
||||||
int32 rows, cols;
|
int32 rows, cols;
|
||||||
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
|
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
|
||||||
|
const int32 count = (rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2;
|
||||||
|
typedef DebugDraw::Vertex Vertex;
|
||||||
|
if (_debugLines->GetElementsCount() != count)
|
||||||
|
{
|
||||||
|
if (_debugLines->Init(GPUBufferDescription::Vertex(sizeof(Vertex), count)))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Array<Vertex> debugLines;
|
||||||
|
debugLines.Resize(count);
|
||||||
|
auto* data = debugLines.Get();
|
||||||
|
const Color32 color(Color::GreenYellow * 0.8f);
|
||||||
|
|
||||||
_debugLines.Resize((rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2);
|
#define GET_VERTEX(x, y) const Vertex v##x##y = { Float3((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))), color }
|
||||||
Vector3* data = _debugLines.Get();
|
|
||||||
|
|
||||||
#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y)))
|
|
||||||
|
|
||||||
for (int32 row = 0; row < rows - 1; row++)
|
for (int32 row = 0; row < rows - 1; row++)
|
||||||
{
|
{
|
||||||
@@ -2243,7 +2261,7 @@ void TerrainPatch::CacheDebugLines()
|
|||||||
if (sample.MaterialIndex0 == (uint8)PhysicsBackend::HeightFieldMaterial::Hole)
|
if (sample.MaterialIndex0 == (uint8)PhysicsBackend::HeightFieldMaterial::Hole)
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < 6; i++)
|
for (int32 i = 0; i < 6; i++)
|
||||||
*data++ = Vector3::Zero;
|
*data++ = Vertex { Float3::Zero, Color32::Black };
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2284,18 +2302,16 @@ void TerrainPatch::CacheDebugLines()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#undef GET_VERTEX
|
#undef GET_VERTEX
|
||||||
|
|
||||||
|
_debugLines->SetData(debugLines.Get(), _debugLines->GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainPatch::DrawPhysicsDebug(RenderView& view)
|
void TerrainPatch::DrawPhysicsDebug(RenderView& view)
|
||||||
{
|
{
|
||||||
|
#if COMPILE_WITH_DEBUG_DRAW
|
||||||
const BoundingBox bounds(_bounds.Minimum - view.Origin, _bounds.Maximum - view.Origin);
|
const BoundingBox bounds(_bounds.Minimum - view.Origin, _bounds.Maximum - view.Origin);
|
||||||
if (!_physicsShape || !view.CullingFrustum.Intersects(bounds))
|
if (!_physicsShape || !view.CullingFrustum.Intersects(bounds))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Transform terrainTransform = _terrain->_transform;
|
|
||||||
const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
|
|
||||||
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
|
|
||||||
|
|
||||||
if (view.Mode == ViewMode::PhysicsColliders)
|
if (view.Mode == ViewMode::PhysicsColliders)
|
||||||
{
|
{
|
||||||
DEBUG_DRAW_TRIANGLES(GetCollisionTriangles(), Color::DarkOliveGreen, 0, true);
|
DEBUG_DRAW_TRIANGLES(GetCollisionTriangles(), Color::DarkOliveGreen, 0, true);
|
||||||
@@ -2304,13 +2320,17 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view)
|
|||||||
{
|
{
|
||||||
BoundingSphere sphere;
|
BoundingSphere sphere;
|
||||||
BoundingSphere::FromBox(bounds, sphere);
|
BoundingSphere::FromBox(bounds, sphere);
|
||||||
if (Vector3::Distance(sphere.Center, view.Position) - sphere.Radius < 4000.0f)
|
if (Vector3::Distance(sphere.Center, view.Position) - sphere.Radius < METERS_TO_UNITS(500))
|
||||||
{
|
{
|
||||||
if (_debugLines.IsEmpty())
|
if (!_debugLines || _debugLinesDirty)
|
||||||
CacheDebugLines();
|
CacheDebugLines();
|
||||||
DEBUG_DRAW_LINES(_debugLines, world, Color::GreenYellow * 0.8f, 0, true);
|
const Transform terrainTransform = _terrain->_transform;
|
||||||
|
const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
|
||||||
|
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
|
||||||
|
DebugDraw::DrawLines(_debugLines, world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -40,12 +40,13 @@ private:
|
|||||||
Array<Color32> _cachedSplatMap[TERRAIN_MAX_SPLATMAPS_COUNT];
|
Array<Color32> _cachedSplatMap[TERRAIN_MAX_SPLATMAPS_COUNT];
|
||||||
bool _wasHeightModified;
|
bool _wasHeightModified;
|
||||||
bool _wasSplatmapModified[TERRAIN_MAX_SPLATMAPS_COUNT];
|
bool _wasSplatmapModified[TERRAIN_MAX_SPLATMAPS_COUNT];
|
||||||
|
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||||
|
bool _debugLinesDirty = true;
|
||||||
|
class GPUBuffer* _debugLines = nullptr;
|
||||||
|
#endif
|
||||||
TextureBase::InitData* _dataHeightmap = nullptr;
|
TextureBase::InitData* _dataHeightmap = nullptr;
|
||||||
TextureBase::InitData* _dataSplatmap[TERRAIN_MAX_SPLATMAPS_COUNT] = {};
|
TextureBase::InitData* _dataSplatmap[TERRAIN_MAX_SPLATMAPS_COUNT] = {};
|
||||||
#endif
|
#endif
|
||||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
|
||||||
Array<Vector3> _debugLines; // TODO: large-worlds
|
|
||||||
#endif
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
Array<Vector3> _collisionTriangles; // TODO: large-worlds
|
Array<Vector3> _collisionTriangles; // TODO: large-worlds
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -263,16 +263,21 @@ namespace FlaxEngine.GUI
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void SetViewOffset(ref Float2 value)
|
protected override void SetViewOffset(ref Float2 value)
|
||||||
{
|
{
|
||||||
|
// Update scroll bars but with locked layout
|
||||||
bool wasLocked = _isLayoutLocked;
|
bool wasLocked = _isLayoutLocked;
|
||||||
|
int layoutUpdateLock = _layoutUpdateLock;
|
||||||
_isLayoutLocked = true;
|
_isLayoutLocked = true;
|
||||||
|
_layoutUpdateLock = 999;
|
||||||
if (HScrollBar != null)
|
if (HScrollBar != null)
|
||||||
HScrollBar.Value = -value.X;
|
HScrollBar.TargetValue = -value.X;
|
||||||
if (VScrollBar != null)
|
if (VScrollBar != null)
|
||||||
VScrollBar.Value = -value.Y;
|
VScrollBar.TargetValue = -value.Y;
|
||||||
|
_layoutUpdateLock = layoutUpdateLock;
|
||||||
_isLayoutLocked = wasLocked;
|
_isLayoutLocked = wasLocked;
|
||||||
|
|
||||||
base.SetViewOffset(ref value);
|
base.SetViewOffset(ref value);
|
||||||
|
|
||||||
|
PerformLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -553,7 +558,12 @@ namespace FlaxEngine.GUI
|
|||||||
|
|
||||||
if (vScrollEnabled)
|
if (vScrollEnabled)
|
||||||
{
|
{
|
||||||
VScrollBar.SetScrollRange(scrollBounds.Top, Mathf.Max(Mathf.Max(0, scrollBounds.Top), scrollBounds.Height - height));
|
float max;
|
||||||
|
if (scrollBounds.Top < 0)
|
||||||
|
max = Mathf.Max(scrollBounds.Bottom, scrollBounds.Top + scrollBounds.Height - height);
|
||||||
|
else
|
||||||
|
max = Mathf.Max(scrollBounds.Top, scrollBounds.Height - height);
|
||||||
|
VScrollBar.SetScrollRange(scrollBounds.Top, max);
|
||||||
}
|
}
|
||||||
VScrollBar.Bounds = new Rectangle(Width - _scrollBarsSize, 0, _scrollBarsSize, Height);
|
VScrollBar.Bounds = new Rectangle(Width - _scrollBarsSize, 0, _scrollBarsSize, Height);
|
||||||
}
|
}
|
||||||
@@ -580,7 +590,12 @@ namespace FlaxEngine.GUI
|
|||||||
|
|
||||||
if (hScrollEnabled)
|
if (hScrollEnabled)
|
||||||
{
|
{
|
||||||
HScrollBar.SetScrollRange(scrollBounds.Left, Mathf.Max(Mathf.Max(0, scrollBounds.Left), scrollBounds.Width - width));
|
float max;
|
||||||
|
if (scrollBounds.Left < 0)
|
||||||
|
max = Mathf.Max(scrollBounds.Right, scrollBounds.Left + scrollBounds.Width - width);
|
||||||
|
else
|
||||||
|
max = Mathf.Max(scrollBounds.Left, scrollBounds.Width - width);
|
||||||
|
HScrollBar.SetScrollRange(scrollBounds.Left, max);
|
||||||
}
|
}
|
||||||
HScrollBar.Bounds = new Rectangle(0, Height - _scrollBarsSize, Width - (VScrollBar != null && VScrollBar.Visible ? VScrollBar.Width : 0), _scrollBarsSize);
|
HScrollBar.Bounds = new Rectangle(0, Height - _scrollBarsSize, Width - (VScrollBar != null && VScrollBar.Visible ? VScrollBar.Width : 0), _scrollBarsSize);
|
||||||
}
|
}
|
||||||
@@ -596,17 +611,29 @@ namespace FlaxEngine.GUI
|
|||||||
// Calculate scroll area bounds
|
// Calculate scroll area bounds
|
||||||
var totalMin = Float2.Zero;
|
var totalMin = Float2.Zero;
|
||||||
var totalMax = Float2.Zero;
|
var totalMax = Float2.Zero;
|
||||||
|
var hasTotal = false;
|
||||||
for (int i = 0; i < _children.Count; i++)
|
for (int i = 0; i < _children.Count; i++)
|
||||||
{
|
{
|
||||||
var c = _children[i];
|
var c = _children[i];
|
||||||
if (c.Visible && c.IsScrollable)
|
if (c.Visible && c.IsScrollable)
|
||||||
{
|
{
|
||||||
var min = Float2.Zero;
|
var upperLeft = Float2.Zero;
|
||||||
var max = c.Size;
|
var bottomRight = c.Size;
|
||||||
Matrix3x3.Transform2D(ref min, ref c._cachedTransform, out min);
|
Matrix3x3.Transform2D(ref upperLeft, ref c._cachedTransform, out upperLeft);
|
||||||
Matrix3x3.Transform2D(ref max, ref c._cachedTransform, out max);
|
Matrix3x3.Transform2D(ref bottomRight, ref c._cachedTransform, out bottomRight);
|
||||||
Float2.Min(ref min, ref totalMin, out totalMin);
|
Float2.Min(ref upperLeft, ref bottomRight, out var min);
|
||||||
Float2.Max(ref max, ref totalMax, out totalMax);
|
Float2.Max(ref upperLeft, ref bottomRight, out var max);
|
||||||
|
if (hasTotal)
|
||||||
|
{
|
||||||
|
Float2.Min(ref min, ref totalMin, out totalMin);
|
||||||
|
Float2.Max(ref max, ref totalMax, out totalMax);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalMin = min;
|
||||||
|
totalMax = max;
|
||||||
|
hasTotal = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -348,6 +348,8 @@ namespace FlaxEngine.GUI
|
|||||||
|
|
||||||
// https://easings.net/#easeOutSine
|
// https://easings.net/#easeOutSine
|
||||||
var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2);
|
var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2);
|
||||||
|
if (progress >= 1.0f)
|
||||||
|
easedProgress = 1.0f;
|
||||||
value = Mathf.Lerp(_startValue, _targetValue, easedProgress);
|
value = Mathf.Lerp(_startValue, _targetValue, easedProgress);
|
||||||
|
|
||||||
_scrollAnimationProgress = progress;
|
_scrollAnimationProgress = progress;
|
||||||
|
|||||||
@@ -7,10 +7,11 @@
|
|||||||
#include "Engine/Core/Collections/Array.h"
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper class with Delaunay triangulation algorithm implementation,
|
/// Helper class with Delaunay triangulation algorithm implementation (2D space).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class Delaunay2D
|
API_CLASS(Internal, Static, Namespace="FlaxEngine.Utilities") class Delaunay2D
|
||||||
{
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(Delaunay2D);
|
||||||
public:
|
public:
|
||||||
struct Triangle
|
struct Triangle
|
||||||
{
|
{
|
||||||
@@ -31,14 +32,35 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TVertexArray, typename TTrianglesArray>
|
/// <summary>
|
||||||
static void Triangulate(const TVertexArray& vertices, TTrianglesArray& triangles)
|
/// Triangulates input vertices array into the list of triangle vertices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vertices">Input list of vertices.</param>
|
||||||
|
/// <returns>Result list of triangles. Each triangle is made out of sequence of 3 vertices. Empty if no valid triangle built.</returns>
|
||||||
|
API_FUNCTION() static Array<Float2> Triangulate(const Array<Float2>& vertices)
|
||||||
|
{
|
||||||
|
Array<Triangle> triangles;
|
||||||
|
Triangulate(vertices, triangles);
|
||||||
|
Array<Float2> result;
|
||||||
|
result.Resize(triangles.Count() * 3);
|
||||||
|
int32 c = 0;
|
||||||
|
for (const Triangle& t : triangles)
|
||||||
|
{
|
||||||
|
result.Get()[c++] = vertices[t.Indices[0]];
|
||||||
|
result.Get()[c++] = vertices[t.Indices[1]];
|
||||||
|
result.Get()[c++] = vertices[t.Indices[2]];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TrianglesArray = Triangle>
|
||||||
|
static void Triangulate(const Array<Float2>& vertices, TrianglesArray& triangles)
|
||||||
{
|
{
|
||||||
// Skip if no change to produce any triangles
|
// Skip if no change to produce any triangles
|
||||||
if (vertices.Count() < 3)
|
if (vertices.Count() < 3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TVertexArray points = vertices;
|
Array<Float2> points = vertices;
|
||||||
Array<Edge> polygon;
|
Array<Edge> polygon;
|
||||||
|
|
||||||
Rectangle rect;
|
Rectangle rect;
|
||||||
@@ -142,8 +164,7 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TVertexArray>
|
static bool CircumCircleContains(const Array<Float2>& vertices, const Triangle& triangle, int32 vertexIndex)
|
||||||
static bool CircumCircleContains(const TVertexArray& vertices, const Triangle& triangle, int vertexIndex)
|
|
||||||
{
|
{
|
||||||
Float2 p1 = vertices[triangle.Indices[0]];
|
Float2 p1 = vertices[triangle.Indices[0]];
|
||||||
Float2 p2 = vertices[triangle.Indices[1]];
|
Float2 p2 = vertices[triangle.Indices[1]];
|
||||||
@@ -157,25 +178,20 @@ private:
|
|||||||
(ab * (p3.Y - p2.Y) + cd * (p1.Y - p3.Y) + ef * (p2.Y - p1.Y)) / (p1.X * (p3.Y - p2.Y) + p2.X * (p1.Y - p3.Y) + p3.X * (p2.Y - p1.Y)),
|
(ab * (p3.Y - p2.Y) + cd * (p1.Y - p3.Y) + ef * (p2.Y - p1.Y)) / (p1.X * (p3.Y - p2.Y) + p2.X * (p1.Y - p3.Y) + p3.X * (p2.Y - p1.Y)),
|
||||||
(ab * (p3.X - p2.X) + cd * (p1.X - p3.X) + ef * (p2.X - p1.X)) / (p1.Y * (p3.X - p2.X) + p2.Y * (p1.X - p3.X) + p3.Y * (p2.X - p1.X)));
|
(ab * (p3.X - p2.X) + cd * (p1.X - p3.X) + ef * (p2.X - p1.X)) / (p1.Y * (p3.X - p2.X) + p2.Y * (p1.X - p3.X) + p3.Y * (p2.X - p1.X)));
|
||||||
|
|
||||||
circum *= 0.5;
|
circum *= 0.5f;
|
||||||
float r = Float2::DistanceSquared(p1, circum);
|
float r = Float2::DistanceSquared(p1, circum);
|
||||||
float d = Float2::DistanceSquared(vertices[vertexIndex], circum);
|
float d = Float2::DistanceSquared(vertices[vertexIndex], circum);
|
||||||
return d <= r;
|
return d <= r;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TVertexArray>
|
static bool EdgeCompare(const Array<Float2>& vertices, const Edge& a, const Edge& b)
|
||||||
static bool EdgeCompare(const TVertexArray& vertices, const Edge& a, const Edge& b)
|
|
||||||
{
|
{
|
||||||
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[0]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[1]]) < ZeroTolerance)
|
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[0]]) < ZeroTolerance &&
|
||||||
{
|
Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[1]]) < ZeroTolerance)
|
||||||
return true;
|
return true;
|
||||||
}
|
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance &&
|
||||||
|
Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance)
|
||||||
if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -92,9 +92,13 @@ void ShaderGenerator::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
|||||||
value = Value(cv.W);
|
value = Value(cv.W);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Rotation
|
||||||
case 8:
|
case 8:
|
||||||
{
|
{
|
||||||
value = Value::Zero;
|
const float pitch = (float)node->Values[0];
|
||||||
|
const float yaw = (float)node->Values[1];
|
||||||
|
const float roll = (float)node->Values[2];
|
||||||
|
value = Value(Quaternion::Euler(pitch, yaw, roll));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// PI
|
// PI
|
||||||
|
|||||||
@@ -460,6 +460,7 @@ ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType::
|
|||||||
case VariantType::Types::Float4:
|
case VariantType::Types::Float4:
|
||||||
case VariantType::Types::Double4:
|
case VariantType::Types::Double4:
|
||||||
case VariantType::Types::Color:
|
case VariantType::Types::Color:
|
||||||
|
case VariantType::Types::Quaternion:
|
||||||
switch (v.Type)
|
switch (v.Type)
|
||||||
{
|
{
|
||||||
case VariantType::Types::Bool:
|
case VariantType::Types::Bool:
|
||||||
@@ -485,16 +486,6 @@ ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType::
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VariantType::Types::Quaternion:
|
|
||||||
switch (v.Type)
|
|
||||||
{
|
|
||||||
case VariantType::Types::Color:
|
|
||||||
case VariantType::Types::Float4:
|
|
||||||
case VariantType::Types::Double4:
|
|
||||||
format = TEXT("{}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (format == nullptr)
|
if (format == nullptr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
|
|||||||
value = cv.W;
|
value = cv.W;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Rotation
|
||||||
case 8:
|
case 8:
|
||||||
{
|
{
|
||||||
const float pitch = (float)node->Values[0];
|
const float pitch = (float)node->Values[0];
|
||||||
|
|||||||
21
Source/ThirdParty/WinPixEventRuntime/LICENSE.txt
vendored
Normal file
21
Source/ThirdParty/WinPixEventRuntime/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
21
Source/ThirdParty/WinPixEventRuntime/WinPixEventRuntime.Build.cs
vendored
Normal file
21
Source/ThirdParty/WinPixEventRuntime/WinPixEventRuntime.Build.cs
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using Flax.Build;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://github.com/microsoft/PixEvents
|
||||||
|
/// </summary>
|
||||||
|
public class WinPixEventRuntime : HeaderOnlyModule
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Init()
|
||||||
|
{
|
||||||
|
base.Init();
|
||||||
|
|
||||||
|
LicenseType = LicenseTypes.MIT;
|
||||||
|
LicenseFilePath = "LICENSE.txt";
|
||||||
|
|
||||||
|
// Merge third-party modules into engine binary
|
||||||
|
BinaryModuleName = "FlaxEngine";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,12 +20,12 @@ namespace Flax.Build.Bindings
|
|||||||
private static readonly Dictionary<string, string> CSharpAdditionalCodeCache = new Dictionary<string, string>();
|
private static readonly Dictionary<string, string> CSharpAdditionalCodeCache = new Dictionary<string, string>();
|
||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
private static readonly TypeInfo CSharpEventBindReturn = new TypeInfo("void");
|
private static readonly TypeInfo CSharpEventBindReturn = new TypeInfo("void");
|
||||||
private static readonly List<FunctionInfo.ParameterInfo> CSharpEventBindParams = new List<FunctionInfo.ParameterInfo>() { new FunctionInfo.ParameterInfo() { Name = "bind", Type = new TypeInfo("bool") } };
|
private static readonly List<FunctionInfo.ParameterInfo> CSharpEventBindParams = new List<FunctionInfo.ParameterInfo> { new FunctionInfo.ParameterInfo { Name = "bind", Type = new TypeInfo("bool") } };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public static event Action<BuildData, ApiTypeInfo, StringBuilder, string> GenerateCSharpTypeInternals;
|
public static event Action<BuildData, ApiTypeInfo, StringBuilder, string> GenerateCSharpTypeInternals;
|
||||||
|
|
||||||
internal static readonly Dictionary<string, string> CSharpNativeToManagedBasicTypes = new Dictionary<string, string>()
|
internal static readonly Dictionary<string, string> CSharpNativeToManagedBasicTypes = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
// Language types
|
// Language types
|
||||||
{ "bool", "bool" },
|
{ "bool", "bool" },
|
||||||
@@ -46,7 +46,7 @@ namespace Flax.Build.Bindings
|
|||||||
{ "double", "double" },
|
{ "double", "double" },
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static readonly Dictionary<string, string> CSharpNativeToManagedDefault = new Dictionary<string, string>()
|
internal static readonly Dictionary<string, string> CSharpNativeToManagedDefault = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
// Engine types
|
// Engine types
|
||||||
{ "String", "string" },
|
{ "String", "string" },
|
||||||
@@ -632,11 +632,11 @@ namespace Flax.Build.Bindings
|
|||||||
else if (returnValueType == "object[]")
|
else if (returnValueType == "object[]")
|
||||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
|
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
|
||||||
else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array")
|
else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array")
|
||||||
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
|
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
|
||||||
else if (functionInfo.ReturnType.Type == "Dictionary")
|
else if (functionInfo.ReturnType.Type == "Dictionary")
|
||||||
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
||||||
else if (returnValueType == "byte[]")
|
else if (returnValueType == "byte[]")
|
||||||
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")";
|
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")";
|
||||||
else if (returnValueType == "bool[]")
|
else if (returnValueType == "bool[]")
|
||||||
{
|
{
|
||||||
// Boolean arrays does not support custom marshalling for some unknown reason
|
// Boolean arrays does not support custom marshalling for some unknown reason
|
||||||
@@ -691,11 +691,15 @@ namespace Flax.Build.Bindings
|
|||||||
parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters.
|
parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters.
|
||||||
}
|
}
|
||||||
else if (parameterInfo.Type.Type == "Dictionary")
|
else if (parameterInfo.Type.Type == "Dictionary")
|
||||||
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
||||||
else if (nativeType == "bool")
|
else if (nativeType == "bool")
|
||||||
parameterMarshalType = "MarshalAs(UnmanagedType.U1)";
|
parameterMarshalType = "MarshalAs(UnmanagedType.U1)";
|
||||||
else if (nativeType == "char")
|
else if (nativeType == "char")
|
||||||
parameterMarshalType = "MarshalAs(UnmanagedType.I2)";
|
parameterMarshalType = "MarshalAs(UnmanagedType.I2)";
|
||||||
|
else if (nativeType.EndsWith("[]"))
|
||||||
|
{
|
||||||
|
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>))";
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(parameterMarshalType))
|
if (!string.IsNullOrEmpty(parameterMarshalType))
|
||||||
contents.Append($"[{parameterMarshalType}] ");
|
contents.Append($"[{parameterMarshalType}] ");
|
||||||
@@ -730,7 +734,7 @@ namespace Flax.Build.Bindings
|
|||||||
if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer")
|
if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer")
|
||||||
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
|
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
|
||||||
else if (parameterInfo.Type.Type == "Dictionary")
|
else if (parameterInfo.Type.Type == "Dictionary")
|
||||||
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
||||||
}
|
}
|
||||||
if (nativeType == "System.Type")
|
if (nativeType == "System.Type")
|
||||||
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemTypeMarshaller))";
|
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemTypeMarshaller))";
|
||||||
|
|||||||
@@ -980,6 +980,10 @@ namespace Flax.Build.Bindings
|
|||||||
UseReferenceForResult = UsePassByReference(buildData, functionInfo.ReturnType, caller),
|
UseReferenceForResult = UsePassByReference(buildData, functionInfo.ReturnType, caller),
|
||||||
CustomParameters = new List<FunctionInfo.ParameterInfo>(),
|
CustomParameters = new List<FunctionInfo.ParameterInfo>(),
|
||||||
};
|
};
|
||||||
|
var returnType = functionInfo.ReturnType;
|
||||||
|
var returnApiType = FindApiTypeInfo(buildData, returnType, caller);
|
||||||
|
if (returnApiType != null && returnApiType.MarshalAs != null)
|
||||||
|
returnType = returnApiType.MarshalAs;
|
||||||
|
|
||||||
bool returnTypeIsContainer = false;
|
bool returnTypeIsContainer = false;
|
||||||
var returnValueConvert = GenerateCppWrapperNativeToManaged(buildData, functionInfo.ReturnType, caller, out var returnValueType, functionInfo);
|
var returnValueConvert = GenerateCppWrapperNativeToManaged(buildData, functionInfo.ReturnType, caller, out var returnValueType, functionInfo);
|
||||||
@@ -999,7 +1003,7 @@ namespace Flax.Build.Bindings
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BitArray" || functionInfo.ReturnType.Type == "BytesContainer")
|
else if (returnType.Type == "Array" || returnType.Type == "Span" || returnType.Type == "DataContainer" || returnType.Type == "BitArray" || returnType.Type == "BytesContainer")
|
||||||
{
|
{
|
||||||
returnTypeIsContainer = true;
|
returnTypeIsContainer = true;
|
||||||
functionInfo.Glue.CustomParameters.Add(new FunctionInfo.ParameterInfo
|
functionInfo.Glue.CustomParameters.Add(new FunctionInfo.ParameterInfo
|
||||||
@@ -1173,7 +1177,7 @@ namespace Flax.Build.Bindings
|
|||||||
{
|
{
|
||||||
callBegin += "*__resultAsRef = ";
|
callBegin += "*__resultAsRef = ";
|
||||||
}
|
}
|
||||||
else if (!functionInfo.ReturnType.IsVoid)
|
else if (!returnType.IsVoid)
|
||||||
{
|
{
|
||||||
if (useInlinedReturn)
|
if (useInlinedReturn)
|
||||||
callBegin += "return ";
|
callBegin += "return ";
|
||||||
@@ -1186,7 +1190,7 @@ namespace Flax.Build.Bindings
|
|||||||
if (returnTypeIsContainer)
|
if (returnTypeIsContainer)
|
||||||
{
|
{
|
||||||
callReturnCount = indent;
|
callReturnCount = indent;
|
||||||
if (functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "BytesContainer")
|
if (returnType.Type == "Span" || returnType.Type == "BytesContainer")
|
||||||
callReturnCount += "*__returnCount = {0}.Length();";
|
callReturnCount += "*__returnCount = {0}.Length();";
|
||||||
else
|
else
|
||||||
callReturnCount += "*__returnCount = {0}.Count();";
|
callReturnCount += "*__returnCount = {0}.Count();";
|
||||||
@@ -1278,7 +1282,8 @@ namespace Flax.Build.Bindings
|
|||||||
#if USE_NETCORE
|
#if USE_NETCORE
|
||||||
if (!string.IsNullOrEmpty(callReturnCount))
|
if (!string.IsNullOrEmpty(callReturnCount))
|
||||||
{
|
{
|
||||||
contents.Append(indent).Append("const auto& __callTemp = ").Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine();
|
var tempVar = returnTypeIsContainer && returnType != functionInfo.ReturnType ? $"{returnType} __callTemp = " : "const auto& __callTemp = ";
|
||||||
|
contents.Append(indent).Append(tempVar).Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine();
|
||||||
call = "__callTemp";
|
call = "__callTemp";
|
||||||
contents.Append(string.Format(callReturnCount, call));
|
contents.Append(string.Format(callReturnCount, call));
|
||||||
contents.AppendLine();
|
contents.AppendLine();
|
||||||
@@ -1357,7 +1362,7 @@ namespace Flax.Build.Bindings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!useInlinedReturn && !functionInfo.Glue.UseReferenceForResult && !functionInfo.ReturnType.IsVoid)
|
if (!useInlinedReturn && !functionInfo.Glue.UseReferenceForResult && !returnType.IsVoid)
|
||||||
{
|
{
|
||||||
contents.Append(indent).Append("return __result;").AppendLine();
|
contents.Append(indent).Append("return __result;").AppendLine();
|
||||||
}
|
}
|
||||||
@@ -1823,6 +1828,10 @@ namespace Flax.Build.Bindings
|
|||||||
// Add includes to properly compile bindings (eg. SoftObjectReference<class Texture>)
|
// Add includes to properly compile bindings (eg. SoftObjectReference<class Texture>)
|
||||||
GenerateCppAddFileReference(buildData, caller, typeInfo, apiTypeInfo);
|
GenerateCppAddFileReference(buildData, caller, typeInfo, apiTypeInfo);
|
||||||
|
|
||||||
|
// TODO: find a better way to reference other include files for types that have separate serialization header
|
||||||
|
if (typeInfo.Type.EndsWith("Curve") && typeInfo.GenericArgs != null)
|
||||||
|
CppIncludeFilesList.Add("Engine/Animations/CurveSerialization.h");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3132,13 +3141,12 @@ namespace Flax.Build.Bindings
|
|||||||
// Includes
|
// Includes
|
||||||
header.Clear();
|
header.Clear();
|
||||||
CppReferencesFiles.Remove(null);
|
CppReferencesFiles.Remove(null);
|
||||||
CppIncludeFilesList.Clear();
|
|
||||||
foreach (var fileInfo in CppReferencesFiles)
|
foreach (var fileInfo in CppReferencesFiles)
|
||||||
CppIncludeFilesList.Add(fileInfo.Name);
|
CppIncludeFilesList.Add(fileInfo.Name);
|
||||||
CppIncludeFilesList.AddRange(CppIncludeFiles);
|
CppIncludeFilesList.AddRange(CppIncludeFiles);
|
||||||
CppIncludeFilesList.Sort();
|
CppIncludeFilesList.Sort();
|
||||||
if (CppIncludeFilesList.Remove("Engine/Serialization/Serialization.h"))
|
if (CppIncludeFilesList.Remove("Engine/Serialization/Serialization.h"))
|
||||||
CppIncludeFilesList.Add("Engine/Serialization/Serialization.h");
|
CppIncludeFilesList.Add("Engine/Serialization/Serialization.h"); // Include serialization header as the last one to properly handle specialization of custom types serialization
|
||||||
foreach (var path in CppIncludeFilesList)
|
foreach (var path in CppIncludeFilesList)
|
||||||
header.AppendFormat("#include \"{0}\"", path).AppendLine();
|
header.AppendFormat("#include \"{0}\"", path).AppendLine();
|
||||||
contents.Insert(headerPos, header.ToString());
|
contents.Insert(headerPos, header.ToString());
|
||||||
|
|||||||
@@ -188,6 +188,13 @@ namespace Flax.Build.Bindings
|
|||||||
token = context.Tokenizer.NextToken();
|
token = context.Tokenizer.NextToken();
|
||||||
if (token.Type == TokenType.Multiply)
|
if (token.Type == TokenType.Multiply)
|
||||||
tag.Value += token.Value;
|
tag.Value += token.Value;
|
||||||
|
else if (token.Type == TokenType.LeftAngleBracket)
|
||||||
|
{
|
||||||
|
context.Tokenizer.SkipUntil(TokenType.RightAngleBracket, out var s);
|
||||||
|
tag.Value += '<';
|
||||||
|
tag.Value += s;
|
||||||
|
tag.Value += '>';
|
||||||
|
}
|
||||||
else
|
else
|
||||||
context.Tokenizer.PreviousToken();
|
context.Tokenizer.PreviousToken();
|
||||||
parameters.Add(tag);
|
parameters.Add(tag);
|
||||||
@@ -273,7 +280,6 @@ namespace Flax.Build.Bindings
|
|||||||
type.GenericArgs.Add(argType);
|
type.GenericArgs.Add(argType);
|
||||||
token = context.Tokenizer.NextToken();
|
token = context.Tokenizer.NextToken();
|
||||||
} while (token.Type != TokenType.RightAngleBracket);
|
} while (token.Type != TokenType.RightAngleBracket);
|
||||||
|
|
||||||
token = context.Tokenizer.NextToken();
|
token = context.Tokenizer.NextToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,15 +551,19 @@ namespace Flax.Build.Bindings
|
|||||||
}
|
}
|
||||||
if (token.Type == TokenType.LeftAngleBracket)
|
if (token.Type == TokenType.LeftAngleBracket)
|
||||||
{
|
{
|
||||||
var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
inheritType.GenericArgs = new List<TypeInfo>();
|
||||||
token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket);
|
while (true)
|
||||||
inheritType.GenericArgs = new List<TypeInfo>
|
|
||||||
{
|
{
|
||||||
new TypeInfo
|
token = context.Tokenizer.NextToken();
|
||||||
{
|
if (token.Type == TokenType.RightAngleBracket)
|
||||||
Type = genericType.Value,
|
break;
|
||||||
}
|
if (token.Type == TokenType.Comma)
|
||||||
};
|
continue;
|
||||||
|
if (token.Type == TokenType.Identifier)
|
||||||
|
inheritType.GenericArgs.Add(new TypeInfo { Type = token.Value });
|
||||||
|
else
|
||||||
|
throw new ParseException(ref context, "Incorrect inheritance");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: find better way to resolve this (custom base type attribute?)
|
// TODO: find better way to resolve this (custom base type attribute?)
|
||||||
if (inheritType.Type == "ShaderAssetTypeBase")
|
if (inheritType.Type == "ShaderAssetTypeBase")
|
||||||
@@ -1590,16 +1600,30 @@ namespace Flax.Build.Bindings
|
|||||||
// Read parameters from the tag
|
// Read parameters from the tag
|
||||||
var tagParams = ParseTagParameters(ref context);
|
var tagParams = ParseTagParameters(ref context);
|
||||||
|
|
||||||
// Read 'typedef' keyword
|
// Read 'typedef' or 'using' keyword
|
||||||
var token = context.Tokenizer.NextToken();
|
var token = context.Tokenizer.NextToken();
|
||||||
if (token.Value != "typedef")
|
var isUsing = token.Value == "using";
|
||||||
throw new ParseException(ref context, $"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
|
if (token.Value != "typedef" && !isUsing)
|
||||||
|
throw new ParseException(ref context, $"Invalid {ApiTokens.Typedef} usage (expected 'typedef' or 'using' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
|
||||||
|
|
||||||
// Read type definition
|
if (isUsing)
|
||||||
desc.Type = ParseType(ref context);
|
{
|
||||||
|
// Read name
|
||||||
|
desc.Name = ParseName(ref context);
|
||||||
|
|
||||||
// Read name
|
context.Tokenizer.ExpectToken(TokenType.Equal);
|
||||||
desc.Name = ParseName(ref context);
|
|
||||||
|
// Read type definition
|
||||||
|
desc.Type = ParseType(ref context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Read type definition
|
||||||
|
desc.Type = ParseType(ref context);
|
||||||
|
|
||||||
|
// Read name
|
||||||
|
desc.Name = ParseName(ref context);
|
||||||
|
}
|
||||||
|
|
||||||
// Read ';'
|
// Read ';'
|
||||||
context.Tokenizer.ExpectToken(TokenType.SemiColon);
|
context.Tokenizer.ExpectToken(TokenType.SemiColon);
|
||||||
|
|||||||
@@ -182,11 +182,40 @@ namespace Flax.Build.Bindings
|
|||||||
|
|
||||||
public static TypeInfo FromString(string text)
|
public static TypeInfo FromString(string text)
|
||||||
{
|
{
|
||||||
var result = new TypeInfo(text);
|
var result = new TypeInfo(text.Trim());
|
||||||
|
if (result.Type.StartsWith("const"))
|
||||||
|
{
|
||||||
|
// Const
|
||||||
|
result.IsConst = true;
|
||||||
|
result.Type = result.Type.Substring(5).Trim();
|
||||||
|
}
|
||||||
if (result.Type.EndsWith('*'))
|
if (result.Type.EndsWith('*'))
|
||||||
{
|
{
|
||||||
|
// Pointer
|
||||||
result.IsPtr = true;
|
result.IsPtr = true;
|
||||||
result.Type = result.Type.Substring(0, result.Type.Length - 1);
|
result.Type = result.Type.Substring(0, result.Type.Length - 1).Trim();
|
||||||
|
}
|
||||||
|
if (result.Type.EndsWith('&'))
|
||||||
|
{
|
||||||
|
// Reference
|
||||||
|
result.IsRef = true;
|
||||||
|
result.Type = result.Type.Substring(0, result.Type.Length - 1).Trim();
|
||||||
|
if (result.Type.EndsWith('&'))
|
||||||
|
{
|
||||||
|
// Move reference
|
||||||
|
result.IsMoveRef = true;
|
||||||
|
result.Type = result.Type.Substring(0, result.Type.Length - 1).Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var idx = result.Type.IndexOf('<');
|
||||||
|
if (idx != -1)
|
||||||
|
{
|
||||||
|
// Generic
|
||||||
|
result.GenericArgs = new List<TypeInfo>();
|
||||||
|
var generics = result.Type.Substring(idx + 1, result.Type.Length - idx - 2);
|
||||||
|
foreach (var generic in generics.Split(','))
|
||||||
|
result.GenericArgs.Add(FromString(generic));
|
||||||
|
result.Type = result.Type.Substring(0, idx);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ namespace Flax.Build
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract TargetCompiler Compiler { get; }
|
public abstract TargetCompiler Compiler { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the main native files compiler path.
|
||||||
|
/// </summary>
|
||||||
|
public virtual string NativeCompilerPath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Toolchain"/> class.
|
/// Initializes a new instance of the <see cref="Toolchain"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user