Compare commits
149 Commits
sdl_platfo
...
3d71b3c430
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d71b3c430 | |||
| 72dcb7d5dd | |||
| 13bbaab1f3 | |||
| 4e7889bb24 | |||
| 96cc570e67 | |||
| e77f12904c | |||
| b53b1340ad | |||
| 3ea807468e | |||
| 0f135597fa | |||
|
|
8da0d2c4ce | ||
|
|
20f576783b | ||
|
|
ef2c551cee | ||
|
|
65cf59642c | ||
|
|
e2fc8a6283 | ||
|
|
73976f3ed9 | ||
|
|
7ab6bafe39 | ||
|
|
deb2ad7c8f | ||
|
|
cd7fc3242e | ||
|
|
31a0a77e7c | ||
|
|
f0ec4a901a | ||
|
|
3682159da6 | ||
|
|
e9f83f77bb | ||
|
|
8f3b80492e | ||
|
|
c61c013517 | ||
|
|
a2170ffd8a | ||
|
|
4f1f77fb32 | ||
|
|
6ae370f8fc | ||
|
|
da5c8555e5 | ||
|
|
c4c7ee941f | ||
|
|
4f45b3c1d0 | ||
|
|
4c640b915f | ||
|
|
1f3f1ea67e | ||
|
|
60c19303f6 | ||
|
|
adcfc50218 | ||
|
|
9b812ec34a | ||
|
|
7e1ac5e167 | ||
|
|
028b5fedec | ||
|
|
9cc2c1da40 | ||
|
|
5ed8564293 | ||
|
|
7c87ade12b | ||
|
|
7f87678282 | ||
|
|
e429d29d17 | ||
|
|
537d8b57ca | ||
|
|
4a3fb41035 | ||
|
|
797cb3c3f2 | ||
|
|
47670251ef | ||
|
|
cf1ef91246 | ||
|
|
7060cb5696 | ||
|
|
c449833d35 | ||
|
|
284aeca51a | ||
|
|
d8f7199c11 | ||
|
|
dc05bbbbcd | ||
|
|
3fcc9ed01f | ||
|
|
70ba750a5e | ||
|
|
50271199ac | ||
|
|
d1a99c9396 | ||
|
|
4ae3d57adc | ||
|
|
99b9967806 | ||
|
|
2f7e84253a | ||
|
|
2f55cb938f | ||
|
|
a86661a855 | ||
|
|
a68ce6633a | ||
|
|
5d57afe3aa | ||
|
|
6705138247 | ||
|
|
276caf771c | ||
|
|
5eea5a72c9 | ||
|
|
e9a7b1c8eb | ||
|
|
a151c78412 | ||
|
|
5f1e905e8f | ||
|
|
a0f764a774 | ||
|
|
7da5ce3ed4 | ||
|
|
1186833b2d | ||
|
|
bb180b0f59 | ||
|
|
5d1c79929a | ||
|
|
3f5a4cc4c9 | ||
|
|
62fd8ac967 | ||
|
|
d904b92f2e | ||
|
|
87e2b76ffa | ||
|
|
c5e11aed15 | ||
|
|
85d61b334b | ||
|
|
26e94f6f8e | ||
|
|
902744a0ce | ||
|
|
1c581bceaf | ||
|
|
9cc44825c6 | ||
|
|
92f4327fc2 | ||
|
|
47711ec5be | ||
|
|
d1fbc66cb9 | ||
|
|
7183a3306e | ||
|
|
fd191f7ffb | ||
|
|
2604d58687 | ||
|
|
01d1d634c2 | ||
|
|
c7e403661d | ||
|
|
1196db6d17 | ||
|
|
553a007508 | ||
|
|
de2ee36529 | ||
|
|
eea44ac897 | ||
|
|
c124713e99 | ||
|
|
364a523375 | ||
|
|
afdd264e63 | ||
|
|
8ec54f7b1c | ||
|
|
521518bde4 | ||
|
|
56077a268a | ||
|
|
a0ca000793 | ||
|
|
ae4ae7a638 | ||
|
|
e2a4c8ab03 | ||
|
|
7a40722964 | ||
|
|
1de8909d05 | ||
|
|
9749487e24 | ||
|
|
55968a8ddc | ||
|
|
e325b190ea | ||
|
|
ada6b9140f | ||
|
|
5582579173 | ||
|
|
88773e71e5 | ||
|
|
9e1f488f22 | ||
|
|
a471861e92 | ||
|
|
3d182c89f3 | ||
|
|
e3810a9938 | ||
| f0dea9d528 | |||
|
|
e27880c1e6 | ||
|
|
6f15ef7690 | ||
|
|
0e3a22faa0 | ||
|
|
0dc1e04c89 | ||
|
|
5c7712daad | ||
|
|
52b64540ab | ||
|
|
b9cfd054c1 | ||
|
|
53d4ea51af | ||
|
|
a4d3ede368 | ||
|
|
92edb996f2 | ||
|
|
2e0c35e6e4 | ||
|
|
baba151d8a | ||
|
|
b44d4107c0 | ||
|
|
f40c67ddf0 | ||
|
|
ec154b4998 | ||
|
|
6997cbeb47 | ||
|
|
c4130aa20f | ||
|
|
c9fe9213b3 | ||
|
|
608839b6a5 | ||
|
|
f71bdd0962 | ||
|
|
47a6da9e40 | ||
|
|
40dae18b76 | ||
|
|
1dfd717093 | ||
|
|
68ef6f08c6 | ||
|
|
a0b80c6096 | ||
|
|
59ac8a3f60 | ||
|
|
dd281bbca8 | ||
|
|
2e48be97b6 | ||
|
|
0b7550e5ca | ||
|
|
e665cc7500 | ||
|
|
0a516ac98d |
42
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please attach any minimal reproduction projects!
|
||||
- type: textarea
|
||||
id: description-area
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a description of the bug and what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-area
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please provide reproduction steps if possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Flax are you running?
|
||||
options:
|
||||
- '1.8'
|
||||
- '1.9'
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- master branch
|
||||
default: 2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant logs
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
22
.github/ISSUE_TEMPLATE/2-feature-request.yaml
vendored
Normal file
22
.github/ISSUE_TEMPLATE/2-feature-request.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Feature Request
|
||||
description: File a feature request.
|
||||
title: "[Request]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out a feature request!
|
||||
- type: textarea
|
||||
id: description-area
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please provide a description of the feature!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: benefits-area
|
||||
attributes:
|
||||
label: Benefits
|
||||
description: Please provide what benefits this feature would provide to the engine!
|
||||
validations:
|
||||
required: true
|
||||
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
@@ -27,6 +27,7 @@ TextureCube EnvProbe : register(t__SRV__);
|
||||
TextureCube SkyLightTexture : register(t__SRV__);
|
||||
Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
||||
Texture2D<float> ShadowMap : register(t__SRV__);
|
||||
Texture3D VolumetricFogTexture : register(t__SRV__);
|
||||
@4// Forward Shading: Utilities
|
||||
@5// Forward Shading: Shaders
|
||||
|
||||
@@ -147,6 +148,18 @@ void PS_Forward(
|
||||
// Calculate exponential height fog
|
||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z);
|
||||
|
||||
if (ExponentialHeightFog.VolumetricFogMaxDistance > 0)
|
||||
{
|
||||
// Sample volumetric fog and mix it in
|
||||
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
|
||||
float3 viewVector = materialInput.WorldPosition - ViewPos;
|
||||
float sceneDepth = length(viewVector);
|
||||
float depthSlice = sceneDepth / ExponentialHeightFog.VolumetricFogMaxDistance;
|
||||
float3 volumeUV = float3(screenUV, depthSlice);
|
||||
float4 volumetricFog = VolumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
fog = CombineVolumetricFog(fog, volumetricFog);
|
||||
}
|
||||
|
||||
// Apply fog to the output color
|
||||
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
|
||||
output = float4(output.rgb * fog.a + fog.rgb, output.a);
|
||||
|
||||
@@ -645,7 +645,7 @@ VertexOutput VS_Ribbon(RibbonInput input, uint vertexIndex : SV_VertexID)
|
||||
materialInput.TBN = output.TBN;
|
||||
materialInput.TwoSidedSign = 1;
|
||||
materialInput.SvPosition = output.Position;
|
||||
materialInput.PreSkinnedPosition = Position;
|
||||
materialInput.PreSkinnedPosition = position;
|
||||
materialInput.PreSkinnedNormal = tangentToLocal[2].xyz;
|
||||
materialInput.InstanceOrigin = output.InstanceOrigin;
|
||||
materialInput.InstanceParams = output.InstanceParams;
|
||||
|
||||
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultRadialMenu.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Sky.flax
(Stored with Git LFS)
BIN
Content/Shaders/Sky.flax
(Stored with Git LFS)
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
<!-- Please search existing issues for potential duplicates before filing yours:
|
||||
https://github.com/flaxengine/FlaxEngine/issues?q=is%3Aissue
|
||||
-->
|
||||
|
||||
**Issue description:**
|
||||
<!-- What happened, and what was expected. -->
|
||||
<!-- Log file, can be found in the project directory's `Logs` folder (optional) -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
<!-- Enter minimal reproduction steps if available. -->
|
||||
|
||||
|
||||
**Minimal reproduction project:**
|
||||
<!-- Recommended as it greatly speeds up debugging. Drag and drop a zip archive to upload it. -->
|
||||
|
||||
|
||||
**Flax version:**
|
||||
<!-- Specify version number. -->
|
||||
|
||||
@@ -57,9 +57,9 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Arch: `sudo pacman -S git git-lfs`
|
||||
* `git-lfs install`
|
||||
* Install the required packages:
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev zenity wayland-protocols libportal-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel zenity wayland-protocols-devel libportal`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib zenity wayland-protocols libportal`
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
|
||||
@@ -117,7 +117,8 @@ namespace FlaxEditor.Content.Create
|
||||
|
||||
private static bool IsValid(Type type)
|
||||
{
|
||||
return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType;
|
||||
var controlTypes = Editor.Instance.CodeEditing.Controls.Get();
|
||||
return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType && controlTypes.Contains(new ScriptType(type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,8 +87,11 @@ namespace FlaxEditor.CustomEditors
|
||||
var targetTypeType = TypeUtils.GetType(targetType);
|
||||
if (canUseRefPicker)
|
||||
{
|
||||
// TODO: add generic way of CustomEditor for ref pickers (use it on AssetRefEditor/GPUTextureEditor/...)
|
||||
if (typeof(Asset).IsAssignableFrom(targetTypeType))
|
||||
return new AssetRefEditor();
|
||||
if (typeof(GPUTexture).IsAssignableFrom(targetTypeType))
|
||||
return new GPUTextureEditor();
|
||||
if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType))
|
||||
return new FlaxObjectRefEditor();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
public class AudioSourceEditor : ActorEditor
|
||||
{
|
||||
private Label _infoLabel;
|
||||
private Slider _slider;
|
||||
private AudioSource.States _slideStartState;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -28,6 +30,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_infoLabel = playbackGroup.Label(string.Empty).Label;
|
||||
_infoLabel.AutoHeight = true;
|
||||
|
||||
// Play back slider
|
||||
var sliderElement = playbackGroup.CustomContainer<Slider>();
|
||||
_slider = sliderElement.CustomControl;
|
||||
_slider.ThumbSize = new Float2(_slider.ThumbSize.X * 0.5f, _slider.ThumbSize.Y);
|
||||
_slider.SlidingStart += OnSlidingStart;
|
||||
_slider.SlidingEnd += OnSlidingEnd;
|
||||
|
||||
var grid = playbackGroup.UniformGrid();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
@@ -40,6 +49,38 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSlidingEnd()
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is AudioSource audioSource && audioSource.Clip)
|
||||
{
|
||||
switch (_slideStartState)
|
||||
{
|
||||
case AudioSource.States.Playing:
|
||||
audioSource.Play();
|
||||
break;
|
||||
case AudioSource.States.Paused:
|
||||
case AudioSource.States.Stopped:
|
||||
audioSource.Pause();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSlidingStart()
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is AudioSource audioSource && audioSource.Clip)
|
||||
{
|
||||
_slideStartState = audioSource.State;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -51,7 +92,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is AudioSource audioSource && audioSource.Clip)
|
||||
{
|
||||
text += $"Time: {audioSource.Time:##0.0}s / {audioSource.Clip.Length:##0.0}s\n";
|
||||
_slider.Maximum = audioSource.Clip.Length;
|
||||
_slider.Minimum = 0;
|
||||
if (_slider.IsSliding)
|
||||
{
|
||||
if (audioSource.State != AudioSource.States.Playing)
|
||||
{
|
||||
// Play to move slider correctly
|
||||
audioSource.Play();
|
||||
audioSource.Time = _slider.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
audioSource.Time = _slider.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_slider.Value = audioSource.Time;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_infoLabel.Text = text;
|
||||
}
|
||||
|
||||
68
Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs
Normal file
68
Source/Editor/CustomEditors/Dedicated/GPUTextureEditor.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic editor/viewer for <see cref="GPUTexture"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(GPUTexture)), DefaultEditor]
|
||||
public class GPUTextureEditor : CustomEditor
|
||||
{
|
||||
private Image _image;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_image = new Image
|
||||
{
|
||||
Brush = new GPUTextureBrush(),
|
||||
Size = new Float2(200, 100),
|
||||
Parent = layout.ContainerControl,
|
||||
};
|
||||
_image.Clicked += OnImageClicked;
|
||||
}
|
||||
|
||||
private void OnImageClicked(Image image, MouseButton button)
|
||||
{
|
||||
var texture = Values[0] as GPUTexture;
|
||||
if (!texture || button != MouseButton.Right)
|
||||
return;
|
||||
var menu = new ContextMenu();
|
||||
menu.AddButton("Save...", () => Screenshot.Capture(Values[0] as GPUTexture));
|
||||
menu.AddButton("Enlarge", () => _image.Size *= 2);
|
||||
menu.AddButton("Shrink", () => _image.Size /= 2).Enabled = _image.Height > 32;
|
||||
var location = image.PointFromScreen(Input.MouseScreenPosition);
|
||||
menu.Show(image, location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
var texture = Values[0] as GPUTexture;
|
||||
((GPUTextureBrush)_image.Brush).Texture = texture;
|
||||
if (texture)
|
||||
{
|
||||
var desc = texture.Description;
|
||||
#if BUILD_RELEASE
|
||||
var name = string.Empty;
|
||||
#else
|
||||
var name = texture.Name;
|
||||
#endif
|
||||
_image.TooltipText = $"{name}\nType: {texture.ResourceType}\nSize: {desc.Width}x{desc.Height}\nFormat: {desc.Format}\nMemory: {Utilities.Utils.FormatBytesCount(texture.MemoryUsage)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
_image.TooltipText = "None";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
ScriptName = scriptName;
|
||||
TooltipText = "Create a new script";
|
||||
DrawHighlights = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var buttonHeight = (textSize.Y < 18) ? 18 : textSize.Y + 4;
|
||||
_addScriptsButton = new Button
|
||||
{
|
||||
TooltipText = "Add new scripts to the actor",
|
||||
TooltipText = "Add new scripts to the actor.",
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
Text = buttonText,
|
||||
Parent = this,
|
||||
@@ -114,7 +115,16 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
cm.TextChanged += text =>
|
||||
{
|
||||
if (!IsValidScriptName(text))
|
||||
{
|
||||
// Remove NewScriptItems
|
||||
List<Control> newScriptItems = cm.ItemsPanel.Children.FindAll(c => c is NewScriptItem);
|
||||
foreach (var item in newScriptItems)
|
||||
{
|
||||
cm.ItemsPanel.RemoveChild(item);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (!cm.ItemsPanel.Children.Any(x => x.Visible && x is not NewScriptItem))
|
||||
{
|
||||
// If there are no visible items, that means the search failed so we can find the create script button or create one if it's the first time
|
||||
@@ -876,7 +886,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Add drag button to the group
|
||||
var scriptDrag = new DragImage
|
||||
{
|
||||
TooltipText = "Script reference",
|
||||
TooltipText = "Script reference.",
|
||||
AutoFocus = true,
|
||||
IsScrollable = false,
|
||||
Color = FlaxEngine.GUI.Style.Current.ForegroundGrey,
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -422,7 +422,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
moveDownButton.Enabled = Index + 1 < Editor.Count;
|
||||
}
|
||||
|
||||
menu.AddButton("Remove", OnRemoveClicked);
|
||||
b = menu.AddButton("Remove", OnRemoveClicked);
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
|
||||
menu.Show(panel, location);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
@@ -81,9 +82,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
private OptionType[] _options;
|
||||
private ScriptType _type;
|
||||
private Elements.PropertiesListElement _typeItem;
|
||||
|
||||
private ScriptType Type => Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RevertValueWithChildren => false; // Always revert value for a whole object
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
@@ -98,7 +103,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
_type = type;
|
||||
|
||||
// Type
|
||||
var typeEditor = layout.ComboBox(TypeComboBoxName, "Type of the object value. Use it to change the object.");
|
||||
_typeItem = layout.AddPropertyItem(TypeComboBoxName, "Type of the object value. Use it to change the object.");
|
||||
var typeEditor = _typeItem.ComboBox();
|
||||
for (int i = 0; i < _options.Length; i++)
|
||||
{
|
||||
typeEditor.ComboBox.AddItem(_options[i].Name);
|
||||
@@ -126,6 +132,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
// Value
|
||||
var values = new CustomValueContainer(type, (instance, index) => instance);
|
||||
if (Values.HasReferenceValue)
|
||||
values.SetReferenceValue(Values.ReferenceValue);
|
||||
values.AddRange(Values);
|
||||
var editor = CustomEditorsUtil.CreateEditor(type);
|
||||
var style = editor.Style;
|
||||
@@ -170,6 +178,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
// Show prefab diff when reference value type is different
|
||||
var color = Color.Transparent;
|
||||
if (Values.HasReferenceValue && CanRevertReferenceValue && Values[0]?.GetType() != Values.ReferenceValue?.GetType())
|
||||
color = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||
_typeItem.Labels[0].HighlightStripColor = color;
|
||||
|
||||
// Check if type has been modified outside the editor (eg. from code)
|
||||
if (Type != _type)
|
||||
{
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace FlaxEditor
|
||||
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
|
||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit;
|
||||
private string _projectToOpen;
|
||||
private bool _projectIsNew;
|
||||
private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f;
|
||||
private Button _saveNowButton;
|
||||
private Button _cancelSaveButton;
|
||||
@@ -737,11 +738,12 @@ namespace FlaxEditor
|
||||
var procSettings = new CreateProcessSettings
|
||||
{
|
||||
FileName = Platform.ExecutableFilePath,
|
||||
Arguments = string.Format("-project \"{0}\"", _projectToOpen),
|
||||
Arguments = string.Format("-project \"{0}\"" + (_projectIsNew ? " -new" : string.Empty), _projectToOpen),
|
||||
ShellExecute = true,
|
||||
WaitForEnd = false,
|
||||
HiddenWindow = false,
|
||||
};
|
||||
_projectIsNew = false;
|
||||
_projectToOpen = null;
|
||||
Platform.CreateProcess(ref procSettings);
|
||||
}
|
||||
@@ -790,6 +792,24 @@ namespace FlaxEditor
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the given project. Afterwards closes this project with running editor and opens the given project.
|
||||
/// </summary>
|
||||
/// <param name="projectFilePath">The project file path.</param>
|
||||
public void NewProject(string projectFilePath)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
MessageBox.Show("Missing project");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache project path and start editor exit (it will open new instance on valid closing)
|
||||
_projectToOpen = StringUtils.NormalizePath(Path.GetDirectoryName(projectFilePath));
|
||||
_projectIsNew = true;
|
||||
Windows.MainWindow.Close(ClosingReason.User);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes this project with running editor and opens the given project.
|
||||
/// </summary>
|
||||
|
||||
@@ -51,6 +51,11 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
public float SortScore;
|
||||
|
||||
/// <summary>
|
||||
/// Wether the query highlights should be draw.
|
||||
/// </summary>
|
||||
public bool DrawHighlights = true;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when items gets clicked by the user.
|
||||
/// </summary>
|
||||
@@ -165,7 +170,7 @@ namespace FlaxEditor.GUI
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), style.BackgroundHighlighted);
|
||||
|
||||
// Draw all highlights
|
||||
if (_highlights != null)
|
||||
if (DrawHighlights && _highlights != null)
|
||||
{
|
||||
var color = style.ProgressNormal * 0.6f;
|
||||
for (int i = 0; i < _highlights.Count; i++)
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Text;
|
||||
using FlaxEditor.GUI.Timeline.Undo;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
@@ -54,7 +55,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
var paramTypeName = LoadName(stream);
|
||||
e.EventParamsTypes[i] = TypeUtils.GetManagedType(paramTypeName);
|
||||
if (e.EventParamsTypes[i] == null)
|
||||
{
|
||||
Editor.LogError($"Unknown type {paramTypeName}.");
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvalid)
|
||||
@@ -82,7 +86,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
for (int j = 0; j < paramsCount; j++)
|
||||
{
|
||||
stream.Read(dataBuffer, 0, e.EventParamsSizes[j]);
|
||||
key.Parameters[j] = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), e.EventParamsTypes[j]);
|
||||
key.Parameters[j] = Utilities.Utils.ByteArrayToStructure(handle.AddrOfPinnedObject(), e.EventParamsTypes[j], e.EventParamsSizes[j]);
|
||||
}
|
||||
|
||||
events[i] = new KeyframesEditor.Keyframe
|
||||
@@ -125,8 +129,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
|
||||
for (int j = 0; j < paramsCount; j++)
|
||||
{
|
||||
Marshal.StructureToPtr(key.Parameters[j], ptr, true);
|
||||
Marshal.Copy(ptr, dataBuffer, 0, e.EventParamsSizes[j]);
|
||||
Utilities.Utils.StructureToByteArray(key.Parameters[j], e.EventParamsSizes[j], ptr, dataBuffer);
|
||||
stream.Write(dataBuffer, 0, e.EventParamsSizes[j]);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +156,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// <summary>
|
||||
/// The event key data.
|
||||
/// </summary>
|
||||
public struct EventKey
|
||||
public struct EventKey : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// The parameters values.
|
||||
@@ -178,6 +181,26 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
sb.Append(')');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Clone()
|
||||
{
|
||||
if (Parameters == null)
|
||||
return new EventKey();
|
||||
|
||||
// Deep clone parameter values (especially boxed value types need to be duplicated to avoid referencing the same ones)
|
||||
var parameters = new object[Parameters.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var p = Parameters[i];
|
||||
if (p == null || p is FlaxEngine.Object)
|
||||
parameters[i] = Parameters[i];
|
||||
else
|
||||
parameters[i] = JsonSerializer.Deserialize(JsonSerializer.Serialize(p), p.GetType());
|
||||
}
|
||||
|
||||
return new EventKey { Parameters = parameters };
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -234,6 +257,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
var time = Timeline.CurrentTime;
|
||||
if (!TryGetValue(out var value))
|
||||
value = Events.Evaluate(time);
|
||||
value = ((ICloneable)value).Clone();
|
||||
|
||||
// Find event at the current location
|
||||
for (int i = Events.Keyframes.Count - 1; i >= 0; i--)
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
{
|
||||
var time = stream.ReadSingle();
|
||||
stream.Read(dataBuffer, 0, e.ValueSize);
|
||||
var value = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), propertyType);
|
||||
var value = Utilities.Utils.ByteArrayToStructure(handle.AddrOfPinnedObject(), propertyType, e.ValueSize);
|
||||
|
||||
keyframes[i] = new KeyframesEditor.Keyframe
|
||||
{
|
||||
@@ -142,8 +142,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
for (int i = 0; i < keyframes.Count; i++)
|
||||
{
|
||||
var keyframe = keyframes[i];
|
||||
Marshal.StructureToPtr(keyframe.Value, ptr, true);
|
||||
Marshal.Copy(ptr, dataBuffer, 0, e.ValueSize);
|
||||
Utilities.Utils.StructureToByteArray(keyframe.Value, e.ValueSize, ptr, dataBuffer);
|
||||
stream.Write(keyframe.Time);
|
||||
stream.Write(dataBuffer);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEngine;
|
||||
using System;
|
||||
|
||||
namespace FlaxEditor.Gizmo
|
||||
{
|
||||
@@ -21,12 +19,16 @@ namespace FlaxEditor.Gizmo
|
||||
private MaterialInstance _materialAxisY;
|
||||
private MaterialInstance _materialAxisZ;
|
||||
private MaterialInstance _materialAxisFocus;
|
||||
private MaterialInstance _materialAxisLocked;
|
||||
private MaterialBase _materialSphere;
|
||||
|
||||
// Material Parameter Names
|
||||
const String _brightnessParamName = "Brightness";
|
||||
const String _opacityParamName = "Opacity";
|
||||
private const string _brightnessParamName = "Brightness";
|
||||
private const string _opacityParamName = "Opacity";
|
||||
|
||||
/// <summary>
|
||||
/// Used for example when the selection can't be moved because one actor is static.
|
||||
/// </summary>
|
||||
private bool _isDisabled;
|
||||
|
||||
private void InitDrawing()
|
||||
{
|
||||
@@ -42,7 +44,6 @@ namespace FlaxEditor.Gizmo
|
||||
_materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY");
|
||||
_materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ");
|
||||
_materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus");
|
||||
_materialAxisLocked = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisLocked");
|
||||
_materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere");
|
||||
|
||||
// Ensure that every asset was loaded
|
||||
@@ -67,17 +68,42 @@ namespace FlaxEditor.Gizmo
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
float brightness = options.Visual.TransformGizmoBrightness;
|
||||
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisZ.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisLocked.SetParameterValue(_brightnessParamName, brightness);
|
||||
|
||||
UpdateGizmoBrightness(options);
|
||||
|
||||
float opacity = options.Visual.TransformGizmoOpacity;
|
||||
_materialAxisX.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisY.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisZ.SetParameterValue(_opacityParamName, opacity);
|
||||
_materialAxisLocked.SetParameterValue(_opacityParamName, opacity);
|
||||
}
|
||||
|
||||
private void UpdateGizmoBrightness(EditorOptions options)
|
||||
{
|
||||
_isDisabled = ShouldGizmoBeLocked();
|
||||
|
||||
float brightness = _isDisabled ? options.Visual.TransformGizmoBrightnessDisabled : options.Visual.TransformGizmoBrightness;
|
||||
if (Mathf.NearEqual(brightness, (float)_materialAxisX.GetParameterValue(_brightnessParamName)))
|
||||
return;
|
||||
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
|
||||
_materialAxisZ.SetParameterValue(_brightnessParamName, brightness);
|
||||
}
|
||||
|
||||
private bool ShouldGizmoBeLocked()
|
||||
{
|
||||
bool gizmoLocked = false;
|
||||
if (Editor.Instance.StateMachine.IsPlayMode && Owner is Viewport.EditorGizmoViewport)
|
||||
{
|
||||
// Block editing static scene objects in main view during play mode
|
||||
foreach (var obj in Editor.Instance.SceneEditing.Selection)
|
||||
{
|
||||
if (obj.CanTransform == false)
|
||||
{
|
||||
gizmoLocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return gizmoLocked;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -88,20 +114,8 @@ namespace FlaxEditor.Gizmo
|
||||
if (!_modelCube || !_modelCube.IsLoaded)
|
||||
return;
|
||||
|
||||
// Find out if any of the selected objects can not be moved
|
||||
bool gizmoLocked = false;
|
||||
if (Editor.Instance.StateMachine.IsPlayMode)
|
||||
{
|
||||
for (int i = 0; i < SelectionCount; i++)
|
||||
{
|
||||
var obj = GetSelectedObject(i);
|
||||
if (obj.CanTransform == false)
|
||||
{
|
||||
gizmoLocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the gizmo brightness every frame to ensure it updates correctly
|
||||
UpdateGizmoBrightness(Editor.Instance.Options.Options);
|
||||
|
||||
// As all axisMesh have the same pivot, add a little offset to the x axisMesh, this way SortDrawCalls is able to sort the draw order
|
||||
// https://github.com/FlaxEngine/FlaxEngine/issues/680
|
||||
@@ -136,37 +150,37 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xAxisMaterialTransform = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Y axis
|
||||
Matrix.RotationX(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yAxisMaterialTransform = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(Mathf.Pi, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zAxisMaterialTransform = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// XY plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xyPlaneMaterialTransform = (_activeAxis == Axis.XY && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// ZX plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance zxPlaneMaterialTransform = (_activeAxis == Axis.ZX && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// YZ plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance yzPlaneMaterialTransform = (_activeAxis == Axis.YZ && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Center sphere
|
||||
@@ -186,17 +200,17 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xAxisMaterialRotate = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Y axis
|
||||
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yAxisMaterialRotate = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zAxisMaterialRotate = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Center box
|
||||
@@ -216,37 +230,37 @@ namespace FlaxEditor.Gizmo
|
||||
// X axis
|
||||
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref mx1, out m3);
|
||||
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xAxisMaterialRotate = (isXAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Y axis
|
||||
Matrix.RotationX(Mathf.PiOverTwo, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yAxisMaterialRotate = (isYAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Z axis
|
||||
Matrix.RotationX(Mathf.Pi, out m2);
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zAxisMaterialRotate = (isZAxis && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// XY plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
|
||||
MaterialInstance xyPlaneMaterialScale = (_activeAxis == Axis.XY && !_isDisabled) ? _materialAxisFocus : _materialAxisX;
|
||||
cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// ZX plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ);
|
||||
MaterialInstance zxPlaneMaterialScale = (_activeAxis == Axis.ZX && !_isDisabled) ? _materialAxisFocus : _materialAxisZ;
|
||||
cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// YZ plane
|
||||
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
|
||||
Matrix.Multiply(ref m2, ref m1, out m3);
|
||||
MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY);
|
||||
MaterialInstance yzPlaneMaterialScale = (_activeAxis == Axis.YZ && !_isDisabled) ? _materialAxisFocus : _materialAxisY;
|
||||
cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3, StaticFlags.None, true, DrawPass.Default, 0.0f, sortOrder);
|
||||
|
||||
// Center box
|
||||
|
||||
@@ -155,6 +155,16 @@ namespace FlaxEditor
|
||||
private List<Widget> _widgets;
|
||||
private Widget _activeWidget;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the view size.
|
||||
/// </summary>
|
||||
/// <param name="size">The new size.</param>
|
||||
public void SetViewSize(Float2 size)
|
||||
{
|
||||
_view.Size = size;
|
||||
_view.PerformLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if enable displaying UI editing background and grid elements.
|
||||
/// </summary>
|
||||
|
||||
@@ -673,6 +673,7 @@ namespace FlaxEditor.Modules
|
||||
pasteAction.Do(out _, out var nodeParents);
|
||||
|
||||
// Select spawned objects (parents only)
|
||||
newSelection.Clear();
|
||||
newSelection.AddRange(nodeParents);
|
||||
var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo);
|
||||
selectAction.Do();
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
case CodeEditorTypes.VS2022:
|
||||
Name = "Visual Studio 2022";
|
||||
break;
|
||||
case CodeEditorTypes.VS2026:
|
||||
Name = "Visual Studio 2026";
|
||||
break;
|
||||
case CodeEditorTypes.VSCode:
|
||||
Name = "Visual Studio Code";
|
||||
break;
|
||||
@@ -110,6 +113,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
case CodeEditorTypes.VS2017:
|
||||
case CodeEditorTypes.VS2019:
|
||||
case CodeEditorTypes.VS2022:
|
||||
case CodeEditorTypes.VS2026:
|
||||
// TODO: finish dynamic files adding to the project
|
||||
//Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
break;
|
||||
|
||||
@@ -70,6 +70,53 @@ namespace FlaxEditor.Modules
|
||||
private bool _progressFailed;
|
||||
|
||||
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
|
||||
|
||||
/// <summary>
|
||||
/// Defines a viewport scaling option.
|
||||
/// </summary>
|
||||
public class ViewportScaleOption
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the viewport scale type.
|
||||
/// </summary>
|
||||
public enum ViewportScaleType
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolution.
|
||||
/// </summary>
|
||||
Resolution = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Aspect Ratio.
|
||||
/// </summary>
|
||||
Aspect = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name.
|
||||
/// </summary>
|
||||
public string Label;
|
||||
|
||||
/// <summary>
|
||||
/// The Type of scaling to do.
|
||||
/// </summary>
|
||||
public ViewportScaleType ScaleType;
|
||||
|
||||
/// <summary>
|
||||
/// The width and height to scale by.
|
||||
/// </summary>
|
||||
public Int2 Size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default viewport scaling options.
|
||||
/// </summary>
|
||||
public List<ViewportScaleOption> DefaultViewportScaleOptions = new List<ViewportScaleOption>();
|
||||
|
||||
/// <summary>
|
||||
/// The user defined viewport scaling options.
|
||||
/// </summary>
|
||||
public List<ViewportScaleOption> CustomViewportScaleOptions = new List<ViewportScaleOption>();
|
||||
|
||||
private ContextMenuButton _menuFileSaveScenes;
|
||||
private ContextMenuButton _menuFileReloadScenes;
|
||||
@@ -409,6 +456,8 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Update window background
|
||||
mainWindow.BackgroundColor = Style.Current.Background;
|
||||
|
||||
InitViewportScaleOptions();
|
||||
|
||||
InitSharedMenus();
|
||||
InitMainMenu(mainWindow);
|
||||
@@ -422,6 +471,57 @@ namespace FlaxEditor.Modules
|
||||
mainWindow.PerformLayout(true);
|
||||
}
|
||||
|
||||
private void InitViewportScaleOptions()
|
||||
{
|
||||
if (DefaultViewportScaleOptions.Count == 0)
|
||||
{
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "Free Aspect",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
|
||||
Size = new Int2(1, 1),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "16:9 Aspect",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
|
||||
Size = new Int2(16, 9),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "16:10 Aspect",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
|
||||
Size = new Int2(16, 10),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "1920x1080 Resolution (Full HD)",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Resolution,
|
||||
Size = new Int2(1920, 1080),
|
||||
});
|
||||
DefaultViewportScaleOptions.Add(new ViewportScaleOption
|
||||
{
|
||||
Label = "2560x1440 Resolution (2K)",
|
||||
ScaleType = ViewportScaleOption.ViewportScaleType.Resolution,
|
||||
Size = new Int2(2560, 1440),
|
||||
});
|
||||
}
|
||||
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData("CustomViewportScalingOptions", out string data))
|
||||
{
|
||||
CustomViewportScaleOptions = JsonSerializer.Deserialize<List<ViewportScaleOption>>(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the custom viewport scaling options.
|
||||
/// </summary>
|
||||
public void SaveCustomViewportScalingOptions()
|
||||
{
|
||||
var customOptions = JsonSerializer.Serialize(CustomViewportScaleOptions);
|
||||
Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnUpdate()
|
||||
{
|
||||
@@ -546,6 +646,7 @@ namespace FlaxEditor.Modules
|
||||
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
|
||||
_menuFileRecompileScripts = cm.AddButton("Recompile scripts", inputOptions.RecompileScripts, ScriptsBuilder.Compile);
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("New project", NewProject);
|
||||
cm.AddButton("Open project...", OpenProject);
|
||||
cm.AddButton("Reload project", ReloadProject);
|
||||
cm.AddButton("Open project folder", () => FileSystem.ShowFileExplorer(Editor.Instance.GameProject.ProjectFolderPath));
|
||||
@@ -576,7 +677,7 @@ namespace FlaxEditor.Modules
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
@@ -851,6 +952,17 @@ namespace FlaxEditor.Modules
|
||||
MasterPanel.Offsets = new Margin(0, 0, ToolStrip.Bottom, StatusBar.Height);
|
||||
}
|
||||
|
||||
private void NewProject()
|
||||
{
|
||||
// Ask user to create project file
|
||||
if (FileSystem.ShowSaveFileDialog(Editor.Windows.MainWindow, null, "Project files (*.flaxproj)\0*.flaxproj\0All files (*.*)\0*.*\0", false, "Create project file", out var files))
|
||||
return;
|
||||
if (files != null && files.Length > 0)
|
||||
{
|
||||
Editor.NewProject(files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenProject()
|
||||
{
|
||||
// Ask user to select project file
|
||||
@@ -1108,5 +1220,267 @@ namespace FlaxEditor.Modules
|
||||
MenuTools = null;
|
||||
MenuHelp = null;
|
||||
}
|
||||
|
||||
internal void CreateViewportSizingContextMenu(ContextMenu vsMenu, int defaultScaleActiveIndex, int customScaleActiveIndex, bool prefabViewport, Action<ViewportScaleOption> changeView, Action<int, int> changeActiveIndices)
|
||||
{
|
||||
// Add default viewport sizing options
|
||||
var defaultOptions = DefaultViewportScaleOptions;
|
||||
for (int i = 0; i < defaultOptions.Count; i++)
|
||||
{
|
||||
var viewportScale = defaultOptions[i];
|
||||
if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect)
|
||||
continue; // Skip aspect ratio types in prefab
|
||||
var button = vsMenu.AddButton(viewportScale.Label);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Tag = viewportScale;
|
||||
|
||||
// No default index is active
|
||||
if (defaultScaleActiveIndex == -1)
|
||||
{
|
||||
button.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
// This is the active index
|
||||
else if (defaultScaleActiveIndex == i)
|
||||
{
|
||||
button.Icon = Style.Current.CheckBoxTick;
|
||||
changeView(viewportScale);
|
||||
}
|
||||
|
||||
button.Clicked += () =>
|
||||
{
|
||||
if (button.Tag == null)
|
||||
return;
|
||||
|
||||
// Reset selected icon on all buttons
|
||||
foreach (var child in vsMenu.Items)
|
||||
{
|
||||
if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v)
|
||||
{
|
||||
if (cmb == button)
|
||||
{
|
||||
button.Icon = Style.Current.CheckBoxTick;
|
||||
var index = defaultOptions.FindIndex(x => x == v);
|
||||
changeActiveIndices(index, -1); // Reset custom index because default was chosen
|
||||
changeView(v);
|
||||
}
|
||||
else if (cmb.Icon != SpriteHandle.Invalid)
|
||||
{
|
||||
cmb.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (defaultOptions.Count != 0)
|
||||
vsMenu.AddSeparator();
|
||||
|
||||
// Add custom viewport options
|
||||
var customOptions = CustomViewportScaleOptions;
|
||||
for (int i = 0; i < customOptions.Count; i++)
|
||||
{
|
||||
var viewportScale = customOptions[i];
|
||||
if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect)
|
||||
continue; // Skip aspect ratio types in prefab
|
||||
var childCM = vsMenu.AddChildMenu(viewportScale.Label);
|
||||
childCM.CloseMenuOnClick = false;
|
||||
childCM.Tag = viewportScale;
|
||||
|
||||
// No custom index is active
|
||||
if (customScaleActiveIndex == -1)
|
||||
{
|
||||
childCM.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
// This is the active index
|
||||
else if (customScaleActiveIndex == i)
|
||||
{
|
||||
childCM.Icon = Style.Current.CheckBoxTick;
|
||||
changeView(viewportScale);
|
||||
}
|
||||
|
||||
var applyButton = childCM.ContextMenu.AddButton("Apply");
|
||||
applyButton.Tag = childCM.Tag = viewportScale;
|
||||
applyButton.CloseMenuOnClick = false;
|
||||
applyButton.Clicked += () =>
|
||||
{
|
||||
if (childCM.Tag == null)
|
||||
return;
|
||||
|
||||
// Reset selected icon on all buttons
|
||||
foreach (var child in vsMenu.Items)
|
||||
{
|
||||
if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v)
|
||||
{
|
||||
if (child == childCM)
|
||||
{
|
||||
childCM.Icon = Style.Current.CheckBoxTick;
|
||||
var index = customOptions.FindIndex(x => x == v);
|
||||
changeActiveIndices(-1, index); // Reset default index because custom was chosen
|
||||
changeView(v);
|
||||
}
|
||||
else if (cmb.Icon != SpriteHandle.Invalid)
|
||||
{
|
||||
cmb.Icon = SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var deleteButton = childCM.ContextMenu.AddButton("Delete");
|
||||
deleteButton.CloseMenuOnClick = false;
|
||||
deleteButton.Clicked += () =>
|
||||
{
|
||||
if (childCM.Tag == null)
|
||||
return;
|
||||
|
||||
var v = (ViewportScaleOption)childCM.Tag;
|
||||
if (childCM.Icon != SpriteHandle.Invalid)
|
||||
{
|
||||
changeActiveIndices(-1, 0);
|
||||
changeView(defaultOptions[0]);
|
||||
}
|
||||
customOptions.Remove(v);
|
||||
SaveCustomViewportScalingOptions();
|
||||
vsMenu.DisposeAllItems();
|
||||
CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices);
|
||||
vsMenu.PerformLayout();
|
||||
};
|
||||
}
|
||||
if (customOptions.Count != 0)
|
||||
vsMenu.AddSeparator();
|
||||
|
||||
// Add button
|
||||
var add = vsMenu.AddButton("Add...");
|
||||
add.CloseMenuOnClick = false;
|
||||
add.Clicked += () =>
|
||||
{
|
||||
var popup = new ContextMenuBase
|
||||
{
|
||||
Size = new Float2(230, 125),
|
||||
ClipChildren = false,
|
||||
CullChildren = false,
|
||||
};
|
||||
popup.Show(add, new Float2(add.Width, 0));
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Name",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
nameLabel.LocalX += 10;
|
||||
nameLabel.LocalY += 10;
|
||||
|
||||
var nameTextBox = new TextBox
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
IsMultiline = false,
|
||||
};
|
||||
nameTextBox.LocalX += 100;
|
||||
nameTextBox.LocalY += 10;
|
||||
|
||||
var typeLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Type",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
typeLabel.LocalX += 10;
|
||||
typeLabel.LocalY += 35;
|
||||
|
||||
var typeDropdown = new Dropdown
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Items = { "Aspect", "Resolution" },
|
||||
SelectedItem = "Aspect",
|
||||
Visible = !prefabViewport,
|
||||
Width = nameTextBox.Width
|
||||
};
|
||||
typeDropdown.LocalY += 35;
|
||||
typeDropdown.LocalX += 100;
|
||||
|
||||
var whLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Width & Height",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
whLabel.LocalX += 10;
|
||||
whLabel.LocalY += 60;
|
||||
|
||||
var wValue = new IntValueBox(16)
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
MinValue = 1,
|
||||
Width = 55,
|
||||
};
|
||||
wValue.LocalY += 60;
|
||||
wValue.LocalX += 100;
|
||||
|
||||
var hValue = new IntValueBox(9)
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
MinValue = 1,
|
||||
Width = 55,
|
||||
};
|
||||
hValue.LocalY += 60;
|
||||
hValue.LocalX += 165;
|
||||
|
||||
var submitButton = new Button
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Submit",
|
||||
Width = 70,
|
||||
};
|
||||
submitButton.LocalX += 40;
|
||||
submitButton.LocalY += 90;
|
||||
submitButton.Clicked += () =>
|
||||
{
|
||||
Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleOption.ViewportScaleType type);
|
||||
if (prefabViewport)
|
||||
type = ViewportScaleOption.ViewportScaleType.Resolution;
|
||||
|
||||
var combineString = type == ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x";
|
||||
var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem;
|
||||
var newViewportOption = new ViewportScaleOption
|
||||
{
|
||||
ScaleType = type,
|
||||
Label = name,
|
||||
Size = new Int2(wValue.Value, hValue.Value),
|
||||
};
|
||||
|
||||
customOptions.Add(newViewportOption);
|
||||
SaveCustomViewportScalingOptions();
|
||||
vsMenu.DisposeAllItems();
|
||||
CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices);
|
||||
vsMenu.PerformLayout();
|
||||
};
|
||||
|
||||
var cancelButton = new Button
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Cancel",
|
||||
Width = 70,
|
||||
};
|
||||
cancelButton.LocalX += 120;
|
||||
cancelButton.LocalY += 90;
|
||||
cancelButton.Clicked += () =>
|
||||
{
|
||||
nameTextBox.Clear();
|
||||
typeDropdown.SelectedItem = "Aspect";
|
||||
hValue.Value = 9;
|
||||
wValue.Value = 16;
|
||||
popup.Hide();
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,6 +650,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Windows"), EditorOrder(4020)]
|
||||
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Control+Comma")]
|
||||
[EditorDisplay("Windows"), EditorOrder(4030)]
|
||||
public InputBinding EditorOptionsWindow = new InputBinding(KeyboardKeys.Comma, KeyboardKeys.Control);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Node Editors
|
||||
|
||||
@@ -150,5 +150,26 @@ namespace FlaxEditor.Options
|
||||
[DefaultValue(typeof(Color), "0.5,0.5,0.5,1.0")]
|
||||
[EditorDisplay("Grid"), EditorOrder(310), Tooltip("The color for the viewport grid.")]
|
||||
public Color ViewportGridColor { get; set; } = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum size used for viewport icons.
|
||||
/// </summary>
|
||||
[DefaultValue(7.0f), Limit(1.0f, 1000.0f, 5.0f)]
|
||||
[EditorDisplay("Viewport Icons"), EditorOrder(400)]
|
||||
public float IconsMinimumSize { get; set; } = 7.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum size used for viewport icons.
|
||||
/// </summary>
|
||||
[DefaultValue(30.0f), Limit(1.0f, 1000.0f, 5.0f)]
|
||||
[EditorDisplay("Viewport Icons"), EditorOrder(410)]
|
||||
public float IconsMaximumSize { get; set; } = 30.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the distance towards the camera at which the max icon scale will be applied. Set to 0 to disable scaling the icons based on the distance to the camera.
|
||||
/// </summary>
|
||||
[DefaultValue(1000.0f), Limit(0.0f, 20000.0f, 5.0f)]
|
||||
[EditorDisplay("Viewport Icons"), EditorOrder(410)]
|
||||
public float MaxSizeDistance { get; set; } = 1000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Transform Gizmo", "Gizmo Opacity"), EditorOrder(211)]
|
||||
public float TransformGizmoOpacity { get; set; } = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set a value indicating how bright the transform gizmo is when it is disabled, for example when one of the selected actors is static in play mode. Use a value of 0 to make the gizmo fully gray. Value over 1 will result in the gizmo emitting light.
|
||||
/// </summary>
|
||||
[DefaultValue(0.25f), Range(0f, 5f)]
|
||||
[EditorDisplay("Transform Gizmo", "Disabled Gizmo Brightness"), EditorOrder(212)]
|
||||
public float TransformGizmoBrightnessDisabled { get; set; } = 0.25f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance.
|
||||
/// </summary>
|
||||
|
||||
@@ -62,6 +62,11 @@ API_ENUM(Namespace="FlaxEditor", Attributes="HideInEditor") enum class CodeEdito
|
||||
/// </summary>
|
||||
VS2022,
|
||||
|
||||
/// <summary>
|
||||
/// Visual Studio 2026
|
||||
/// </summary>
|
||||
VS2026,
|
||||
|
||||
/// <summary>
|
||||
/// Visual Studio Code
|
||||
/// </summary>
|
||||
|
||||
@@ -43,6 +43,9 @@ VisualStudioEditor::VisualStudioEditor(VisualStudioVersion version, const String
|
||||
case VisualStudioVersion::VS2022:
|
||||
_type = CodeEditorTypes::VS2022;
|
||||
break;
|
||||
case VisualStudioVersion::VS2026:
|
||||
_type = CodeEditorTypes::VS2026;
|
||||
break;
|
||||
default: CRASH;
|
||||
break;
|
||||
}
|
||||
@@ -70,6 +73,9 @@ void VisualStudioEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
VisualStudioVersion version;
|
||||
switch (info.VersionMajor)
|
||||
{
|
||||
case 18:
|
||||
version = VisualStudioVersion::VS2026;
|
||||
break;
|
||||
case 17:
|
||||
version = VisualStudioVersion::VS2022;
|
||||
break;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Microsoft Visual Studio version types
|
||||
/// </summary>
|
||||
DECLARE_ENUM_8(VisualStudioVersion, VS2008, VS2010, VS2012, VS2013, VS2015, VS2017, VS2019, VS2022);
|
||||
DECLARE_ENUM_9(VisualStudioVersion, VS2008, VS2010, VS2012, VS2013, VS2015, VS2017, VS2019, VS2022, VS2026);
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of code editor utility that is using Microsoft Visual Studio.
|
||||
|
||||
@@ -229,20 +229,20 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox)
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List<Box> startBoxes)
|
||||
{
|
||||
// Check if show additional nodes in the current surface context
|
||||
if (activeCM != _cmStateMachineMenu)
|
||||
{
|
||||
_nodesCache.Get(activeCM);
|
||||
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBox);
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBoxes);
|
||||
|
||||
activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBox);
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBoxes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,12 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox)
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List<Box> startBoxes)
|
||||
{
|
||||
activeCM.ShowExpanded = true;
|
||||
_nodesCache.Get(activeCM);
|
||||
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBox);
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBoxes);
|
||||
|
||||
activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// Visject context menu item clicked delegate.
|
||||
/// </summary>
|
||||
/// <param name="clickedItem">The item that was clicked</param>
|
||||
/// <param name="selectedBox">The currently user-selected box. Can be null.</param>
|
||||
public delegate void ItemClickedDelegate(VisjectCMItem clickedItem, Elements.Box selectedBox);
|
||||
/// <param name="selectedBoxes">The currently user-selected boxes. Can be empty/ null.</param>
|
||||
public delegate void ItemClickedDelegate(VisjectCMItem clickedItem, List<Elements.Box> selectedBoxes);
|
||||
|
||||
/// <summary>
|
||||
/// Visject Surface node archetype spawn ability checking delegate.
|
||||
@@ -53,7 +53,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
private Panel _panel1;
|
||||
private VerticalPanel _groupsPanel;
|
||||
private readonly ParameterGetterDelegate _parametersGetter;
|
||||
private Elements.Box _selectedBox;
|
||||
private List<Elements.Box> _selectedBoxes = new List<Elements.Box>();
|
||||
private NodeArchetype _parameterGetNodeArchetype;
|
||||
private NodeArchetype _parameterSetNodeArchetype;
|
||||
|
||||
@@ -411,7 +411,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
if (!IsLayoutLocked)
|
||||
{
|
||||
group.UnlockChildrenRecursive();
|
||||
if (_contextSensitiveSearchEnabled && _selectedBox != null)
|
||||
// TODO: Improve filtering to be based on boxes with the most common things instead of first box
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null)
|
||||
UpdateFilters();
|
||||
else
|
||||
SortGroups();
|
||||
@@ -425,7 +426,8 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
}
|
||||
else if (_contextSensitiveSearchEnabled)
|
||||
{
|
||||
group.EvaluateVisibilityWithBox(_selectedBox);
|
||||
// TODO: Filtering could be improved here as well
|
||||
group.EvaluateVisibilityWithBox(_selectedBoxes[0]);
|
||||
}
|
||||
|
||||
Profiler.EndEvent();
|
||||
@@ -461,7 +463,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
};
|
||||
}
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
group.EvaluateVisibilityWithBox(_selectedBox);
|
||||
group.EvaluateVisibilityWithBox(_selectedBoxes[0]);
|
||||
group.SortChildren();
|
||||
if (ShowExpanded)
|
||||
group.Open(false);
|
||||
@@ -474,7 +476,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
if (!isLayoutLocked)
|
||||
{
|
||||
if (_contextSensitiveSearchEnabled && _selectedBox != null)
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes[0] != null)
|
||||
UpdateFilters();
|
||||
else
|
||||
SortGroups();
|
||||
@@ -583,7 +585,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
private void UpdateFilters()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBox == null)
|
||||
if (string.IsNullOrEmpty(_searchBox.Text) && _selectedBoxes[0] == null)
|
||||
{
|
||||
ResetView();
|
||||
Profiler.EndEvent();
|
||||
@@ -592,7 +594,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
|
||||
// Update groups
|
||||
LockChildrenRecursive();
|
||||
var contextSensitiveSelectedBox = _contextSensitiveSearchEnabled ? _selectedBox : null;
|
||||
var contextSensitiveSelectedBox = _contextSensitiveSearchEnabled && _selectedBoxes.Count > 0 ? _selectedBoxes[0] : null;
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
_groups[i].UpdateFilter(_searchBox.Text, contextSensitiveSelectedBox);
|
||||
@@ -640,7 +642,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
public void OnClickItem(VisjectCMItem item)
|
||||
{
|
||||
Hide();
|
||||
ItemClicked?.Invoke(item, _selectedBox);
|
||||
ItemClicked?.Invoke(item, _selectedBoxes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -666,12 +668,12 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
_groups[i].ResetView();
|
||||
if (_contextSensitiveSearchEnabled)
|
||||
_groups[i].EvaluateVisibilityWithBox(_selectedBox);
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0)
|
||||
_groups[i].EvaluateVisibilityWithBox(_selectedBoxes[0]);
|
||||
}
|
||||
UnlockChildrenRecursive();
|
||||
|
||||
if (_contextSensitiveSearchEnabled && _selectedBox != null)
|
||||
if (_contextSensitiveSearchEnabled && _selectedBoxes.Count > 0 && _selectedBoxes[0] != null)
|
||||
UpdateFilters();
|
||||
else
|
||||
SortGroups();
|
||||
@@ -772,10 +774,10 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent control to attach to it.</param>
|
||||
/// <param name="location">Popup menu origin location in parent control coordinates.</param>
|
||||
/// <param name="startBox">The currently selected box that the new node will get connected to. Can be null</param>
|
||||
public void Show(Control parent, Float2 location, Elements.Box startBox)
|
||||
/// <param name="startBoxes">The currently selected boxes that the new node will get connected to. Can be empty/ null</param>
|
||||
public void Show(Control parent, Float2 location, List<Elements.Box> startBoxes)
|
||||
{
|
||||
_selectedBox = startBox;
|
||||
_selectedBoxes = startBoxes;
|
||||
base.Show(parent, location);
|
||||
}
|
||||
|
||||
|
||||
@@ -544,35 +544,39 @@ namespace FlaxEditor.Surface.Elements
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
if (_originalTooltipText != null)
|
||||
{
|
||||
TooltipText = _originalTooltipText;
|
||||
}
|
||||
if (_isMouseDown)
|
||||
{
|
||||
_isMouseDown = false;
|
||||
if (Surface.CanEdit)
|
||||
{
|
||||
if (!IsOutput && HasSingleConnection)
|
||||
if (IsOutput && Input.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
var connectedBox = Connections[0];
|
||||
List<Box> connectedBoxes = new List<Box>(Connections);
|
||||
|
||||
for (int i = 0; i < connectedBoxes.Count; i++)
|
||||
{
|
||||
BreakConnection(connectedBoxes[i]);
|
||||
Surface.ConnectingStart(connectedBoxes[i], true);
|
||||
}
|
||||
}
|
||||
else if (!IsOutput && HasSingleConnection)
|
||||
{
|
||||
var otherBox = Connections[0];
|
||||
if (Surface.Undo != null && Surface.Undo.Enabled)
|
||||
{
|
||||
var action = new ConnectBoxesAction((InputBox)this, (OutputBox)connectedBox, false);
|
||||
BreakConnection(connectedBox);
|
||||
var action = new ConnectBoxesAction((InputBox)this, (OutputBox)otherBox, false);
|
||||
BreakConnection(otherBox);
|
||||
action.End();
|
||||
Surface.AddBatchedUndoAction(action);
|
||||
Surface.MarkAsEdited();
|
||||
}
|
||||
else
|
||||
{
|
||||
BreakConnection(connectedBox);
|
||||
}
|
||||
Surface.ConnectingStart(connectedBox);
|
||||
BreakConnection(otherBox);
|
||||
Surface.ConnectingStart(otherBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
Surface.ConnectingStart(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
base.OnMouseLeave();
|
||||
|
||||
@@ -151,6 +151,8 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
protected virtual Color FooterColor => GroupArchetype.Color;
|
||||
|
||||
private Float2 mouseDownMousePosition;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the size of the node including header, footer, and margins.
|
||||
/// </summary>
|
||||
@@ -917,7 +919,7 @@ namespace FlaxEditor.Surface
|
||||
/// <inheritdoc />
|
||||
public override bool OnTestTooltipOverControl(ref Float2 location)
|
||||
{
|
||||
return _headerRect.Contains(ref location) && ShowTooltip && !Surface.IsConnecting && !Surface.IsBoxSelecting;
|
||||
return _headerRect.Contains(ref location) && ShowTooltip && !Surface.IsConnecting && !Surface.IsSelecting;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1075,7 +1077,7 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Header
|
||||
var headerColor = style.BackgroundHighlighted;
|
||||
if (_headerRect.Contains(ref _mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting)
|
||||
if (_headerRect.Contains(ref _mousePosition) && !Surface.IsConnecting && !Surface.IsSelecting)
|
||||
headerColor *= 1.07f;
|
||||
Render2D.FillRectangle(_headerRect, headerColor);
|
||||
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||
@@ -1083,7 +1085,7 @@ namespace FlaxEditor.Surface
|
||||
// Close button
|
||||
if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit)
|
||||
{
|
||||
bool highlightClose = _closeButtonRect.Contains(_mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting;
|
||||
bool highlightClose = _closeButtonRect.Contains(_mousePosition) && !Surface.IsConnecting && !Surface.IsSelecting;
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, highlightClose ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
@@ -1121,7 +1123,7 @@ namespace FlaxEditor.Surface
|
||||
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||
return true;
|
||||
if (button == MouseButton.Right)
|
||||
return true;
|
||||
mouseDownMousePosition = Input.Mouse.Position;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1133,7 +1135,7 @@ namespace FlaxEditor.Surface
|
||||
return true;
|
||||
|
||||
// Close/ delete
|
||||
bool canDelete = !Surface.IsConnecting && !Surface.WasBoxSelecting && !Surface.WasMovingSelection;
|
||||
bool canDelete = !Surface.IsConnecting && !Surface.WasSelecting && !Surface.WasMovingSelection;
|
||||
if (button == MouseButton.Left && canDelete && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||
{
|
||||
Surface.Delete(this);
|
||||
@@ -1143,6 +1145,10 @@ namespace FlaxEditor.Surface
|
||||
// Secondary Context Menu
|
||||
if (button == MouseButton.Right)
|
||||
{
|
||||
float distance = Float2.Distance(mouseDownMousePosition, Input.Mouse.Position);
|
||||
if (distance > 2.5f)
|
||||
return true;
|
||||
|
||||
if (!IsSelected)
|
||||
Surface.Select(this);
|
||||
var tmp = PointToParent(ref location);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Surface
|
||||
@@ -233,11 +235,15 @@ namespace FlaxEditor.Surface
|
||||
/// Begins connecting surface objects action.
|
||||
/// </summary>
|
||||
/// <param name="instigator">The connection instigator (eg. start box).</param>
|
||||
public void ConnectingStart(IConnectionInstigator instigator)
|
||||
/// <param name="additive">If the instigator should be added to the list of instigators.</param>
|
||||
public void ConnectingStart(IConnectionInstigator instigator, bool additive = false)
|
||||
{
|
||||
if (instigator != null && instigator != _connectionInstigator)
|
||||
if (instigator != null && instigator != _connectionInstigators)
|
||||
{
|
||||
_connectionInstigator = instigator;
|
||||
if (!additive)
|
||||
_connectionInstigators.Clear();
|
||||
|
||||
_connectionInstigators.Add(instigator);
|
||||
StartMouseCapture();
|
||||
}
|
||||
}
|
||||
@@ -257,22 +263,30 @@ namespace FlaxEditor.Surface
|
||||
/// <param name="end">The end object (eg. end box).</param>
|
||||
public void ConnectingEnd(IConnectionInstigator end)
|
||||
{
|
||||
// Ensure that there was a proper start box
|
||||
if (_connectionInstigator == null)
|
||||
// Ensure that there is at least one connection instigator
|
||||
if (_connectionInstigators.Count == 0)
|
||||
return;
|
||||
|
||||
var start = _connectionInstigator;
|
||||
_connectionInstigator = null;
|
||||
|
||||
// Check if boxes are different and end box is specified
|
||||
if (start == end || end == null)
|
||||
return;
|
||||
|
||||
// Connect them
|
||||
if (start.CanConnectWith(end))
|
||||
List<IConnectionInstigator> instigators = new List<IConnectionInstigator>(_connectionInstigators);
|
||||
for (int i = 0; i < instigators.Count; i++)
|
||||
{
|
||||
start.Connect(end);
|
||||
var start = instigators[i];
|
||||
|
||||
// Check if boxes are different and end box is specified
|
||||
if (start == end || end == null)
|
||||
return;
|
||||
|
||||
// Properly handle connecting to a socket that already has a connection
|
||||
if (end is Box e && !e.IsOutput && start is Box s && e.AreConnected(s))
|
||||
e.BreakConnection(s);
|
||||
|
||||
// Connect them
|
||||
if (start.CanConnectWith(end))
|
||||
start.Connect(end);
|
||||
}
|
||||
|
||||
// Reset instigator list
|
||||
_connectionInstigators.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,10 +261,10 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
/// <param name="activeCM">The active context menu to show.</param>
|
||||
/// <param name="location">The display location on the surface control.</param>
|
||||
/// <param name="startBox">The start box.</param>
|
||||
protected virtual void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox)
|
||||
/// <param name="startBoxes">The start boxes.</param>
|
||||
protected virtual void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List<Box> startBoxes)
|
||||
{
|
||||
activeCM.Show(this, location, startBox);
|
||||
activeCM.Show(this, location, startBoxes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,8 +298,10 @@ namespace FlaxEditor.Surface
|
||||
|
||||
_cmStartPos = location;
|
||||
|
||||
// Offset added in case the user doesn't like the box and wants to quickly get rid of it by clicking
|
||||
OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, _connectionInstigator as Box);
|
||||
List<Box> startBoxes = new List<Box>(_connectionInstigators.Where(c => c is Box).Cast<Box>());
|
||||
|
||||
// Position offset added so the user can quickly close the menu by clicking
|
||||
OnShowPrimaryMenu(_activeVisjectCM, _cmStartPos + ContextMenuOffset, startBoxes);
|
||||
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
{
|
||||
@@ -513,17 +515,15 @@ namespace FlaxEditor.Surface
|
||||
private void OnPrimaryMenuVisibleChanged(Control primaryMenu)
|
||||
{
|
||||
if (!primaryMenu.Visible)
|
||||
{
|
||||
_connectionInstigator = null;
|
||||
}
|
||||
_connectionInstigators.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Visject CM item click event by spawning the selected item.
|
||||
/// </summary>
|
||||
/// <param name="visjectCmItem">The item.</param>
|
||||
/// <param name="selectedBox">The selected box.</param>
|
||||
protected virtual void OnPrimaryMenuButtonClick(VisjectCMItem visjectCmItem, Box selectedBox)
|
||||
/// <param name="selectedBoxes">The selected boxes.</param>
|
||||
protected virtual void OnPrimaryMenuButtonClick(VisjectCMItem visjectCmItem, List<Box> selectedBoxes)
|
||||
{
|
||||
if (!CanEdit)
|
||||
return;
|
||||
@@ -550,34 +550,36 @@ namespace FlaxEditor.Surface
|
||||
// Auto select new node
|
||||
Select(node);
|
||||
|
||||
if (selectedBox != null)
|
||||
for (int i = 0; i < selectedBoxes.Count; i++)
|
||||
{
|
||||
Box endBox = null;
|
||||
foreach (var box in node.GetBoxes().Where(box => box.IsOutput != selectedBox.IsOutput))
|
||||
Box currentBox = selectedBoxes[i];
|
||||
if (currentBox != null)
|
||||
{
|
||||
if (selectedBox.IsOutput)
|
||||
Box endBox = null;
|
||||
foreach (var box in node.GetBoxes().Where(box => box.IsOutput != currentBox.IsOutput))
|
||||
{
|
||||
if (box.CanUseType(selectedBox.CurrentType))
|
||||
if (currentBox.IsOutput)
|
||||
{
|
||||
endBox = box;
|
||||
break;
|
||||
if (box.CanUseType(currentBox.CurrentType))
|
||||
{
|
||||
endBox = box;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedBox.CanUseType(box.CurrentType))
|
||||
else
|
||||
{
|
||||
endBox = box;
|
||||
break;
|
||||
if (currentBox.CanUseType(box.CurrentType))
|
||||
{
|
||||
endBox = box;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (endBox == null && selectedBox.CanUseType(box.CurrentType))
|
||||
{
|
||||
endBox = box;
|
||||
if (endBox == null && currentBox.CanUseType(box.CurrentType))
|
||||
endBox = box;
|
||||
}
|
||||
TryConnect(currentBox, endBox);
|
||||
}
|
||||
TryConnect(selectedBox, endBox);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,13 +595,8 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// If the user is patiently waiting for his box to get connected to the newly created one fulfill his wish!
|
||||
|
||||
_connectionInstigator = startBox;
|
||||
|
||||
if (!IsConnecting)
|
||||
{
|
||||
ConnectingStart(startBox);
|
||||
}
|
||||
ConnectingEnd(endBox);
|
||||
|
||||
// Smart-Select next box
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
@@ -126,40 +127,45 @@ namespace FlaxEditor.Surface
|
||||
/// <remarks>Called only when user is connecting nodes.</remarks>
|
||||
protected virtual void DrawConnectingLine()
|
||||
{
|
||||
// Get start position
|
||||
var startPos = _connectionInstigator.ConnectionOrigin;
|
||||
|
||||
// Check if mouse is over any of box
|
||||
var cmVisible = _activeVisjectCM != null && _activeVisjectCM.Visible;
|
||||
var endPos = cmVisible ? _rootControl.PointFromParent(ref _cmStartPos) : _rootControl.PointFromParent(ref _mousePos);
|
||||
Color lineColor = Style.Colors.Connecting;
|
||||
if (_lastInstigatorUnderMouse != null && !cmVisible)
|
||||
{
|
||||
// Check if can connect objects
|
||||
bool canConnect = _connectionInstigator.CanConnectWith(_lastInstigatorUnderMouse);
|
||||
lineColor = canConnect ? Style.Colors.ConnectingValid : Style.Colors.ConnectingInvalid;
|
||||
endPos = _lastInstigatorUnderMouse.ConnectionOrigin;
|
||||
}
|
||||
|
||||
Float2 actualStartPos = startPos;
|
||||
Float2 actualEndPos = endPos;
|
||||
|
||||
if (_connectionInstigator is Archetypes.Tools.RerouteNode)
|
||||
List<IConnectionInstigator> instigators = new List<IConnectionInstigator>(_connectionInstigators);
|
||||
for (int i = 0; i < instigators.Count; i++)
|
||||
{
|
||||
if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true })
|
||||
IConnectionInstigator currentInstigator = instigators[i];
|
||||
Float2 currentStartPosition = currentInstigator.ConnectionOrigin;
|
||||
|
||||
// Check if mouse is over any box
|
||||
if (_lastInstigatorUnderMouse != null && !cmVisible)
|
||||
{
|
||||
// Check if can connect objects
|
||||
bool canConnect = currentInstigator.CanConnectWith(_lastInstigatorUnderMouse);
|
||||
lineColor = canConnect ? Style.Colors.ConnectingValid : Style.Colors.ConnectingInvalid;
|
||||
endPos = _lastInstigatorUnderMouse.ConnectionOrigin;
|
||||
}
|
||||
|
||||
Float2 actualStartPos = currentStartPosition;
|
||||
Float2 actualEndPos = endPos;
|
||||
|
||||
if (currentInstigator is Archetypes.Tools.RerouteNode)
|
||||
{
|
||||
if (endPos.X < currentStartPosition.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true })
|
||||
{
|
||||
actualStartPos = endPos;
|
||||
actualEndPos = currentStartPosition;
|
||||
}
|
||||
}
|
||||
else if (currentInstigator is Box { IsOutput: false })
|
||||
{
|
||||
actualStartPos = endPos;
|
||||
actualEndPos = startPos;
|
||||
actualEndPos = currentStartPosition;
|
||||
}
|
||||
}
|
||||
else if (_connectionInstigator is Box { IsOutput: false })
|
||||
{
|
||||
actualStartPos = endPos;
|
||||
actualEndPos = startPos;
|
||||
}
|
||||
|
||||
// Draw connection
|
||||
_connectionInstigator.DrawConnectingLine(ref actualStartPos, ref actualEndPos, ref lineColor);
|
||||
// Draw connection
|
||||
currentInstigator.DrawConnectingLine(ref actualStartPos, ref actualEndPos, ref lineColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -226,10 +232,10 @@ namespace FlaxEditor.Surface
|
||||
_rootControl.DrawComments();
|
||||
|
||||
// Reset input flags here because this is the closest to Update we have
|
||||
WasBoxSelecting = IsBoxSelecting;
|
||||
WasSelecting = IsSelecting;
|
||||
WasMovingSelection = IsMovingSelection;
|
||||
|
||||
if (IsBoxSelecting)
|
||||
if (IsSelecting)
|
||||
{
|
||||
DrawSelection();
|
||||
}
|
||||
|
||||
@@ -176,10 +176,10 @@ namespace FlaxEditor.Surface
|
||||
if (connectedNodes.Count == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < connectedNodes.Count - 1; i++)
|
||||
for (int i = 0; i < connectedNodes.Count; i++)
|
||||
{
|
||||
SurfaceNode nodeA = connectedNodes[i];
|
||||
List<Box> connectedOutputBoxes = nodeA.GetBoxes().Where(b => b.IsOutput && b.HasAnyConnection).ToList();
|
||||
List<Box> connectedOutputBoxes = nodeA.GetBoxes().Where(b => b.HasAnyConnection).ToList();
|
||||
|
||||
for (int j = 0; j < connectedOutputBoxes.Count; j++)
|
||||
{
|
||||
|
||||
@@ -290,7 +290,7 @@ namespace FlaxEditor.Surface
|
||||
if (_leftMouseDown)
|
||||
{
|
||||
// Connecting
|
||||
if (_connectionInstigator != null)
|
||||
if (_connectionInstigators.Count > 0)
|
||||
{
|
||||
}
|
||||
// Moving
|
||||
@@ -460,7 +460,7 @@ namespace FlaxEditor.Surface
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
// Check if user is connecting boxes
|
||||
if (_connectionInstigator != null)
|
||||
if (_connectionInstigators.Count > 0)
|
||||
return true;
|
||||
|
||||
// Base
|
||||
@@ -606,7 +606,7 @@ namespace FlaxEditor.Surface
|
||||
_movingNodesDelta = Float2.Zero;
|
||||
}
|
||||
// Connecting
|
||||
else if (_connectionInstigator != null)
|
||||
else if (_connectionInstigators.Count > 0)
|
||||
{
|
||||
}
|
||||
// Selecting
|
||||
@@ -678,7 +678,7 @@ namespace FlaxEditor.Surface
|
||||
ShowPrimaryMenu(_cmStartPos);
|
||||
}
|
||||
// Letting go of a connection or right clicking while creating a connection
|
||||
else if (!_isMovingSelection && _connectionInstigator != null && !IsPrimaryMenuOpened)
|
||||
else if (!_isMovingSelection && _connectionInstigators.Count > 0 && !IsPrimaryMenuOpened)
|
||||
{
|
||||
_cmStartPos = location;
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace FlaxEditor.Surface
|
||||
Enabled = false;
|
||||
|
||||
// Clean data
|
||||
_connectionInstigator = null;
|
||||
_connectionInstigators.Clear();
|
||||
_lastInstigatorUnderMouse = null;
|
||||
|
||||
var failed = RootContext.Load();
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace FlaxEditor.Surface
|
||||
/// <summary>
|
||||
/// The connection start.
|
||||
/// </summary>
|
||||
protected IConnectionInstigator _connectionInstigator;
|
||||
protected List<IConnectionInstigator> _connectionInstigators = new List<IConnectionInstigator>();
|
||||
|
||||
/// <summary>
|
||||
/// The last connection instigator under mouse.
|
||||
@@ -232,19 +232,19 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user is box selecting nodes.
|
||||
/// Gets a value indicating whether user is selecting nodes.
|
||||
/// </summary>
|
||||
public bool IsBoxSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
|
||||
public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigators.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user was previously box selecting nodes.
|
||||
/// Gets a value indicating whether user was previously selecting nodes.
|
||||
/// </summary>
|
||||
public bool WasBoxSelecting { get; private set; }
|
||||
public bool WasSelecting { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user is moving selected nodes.
|
||||
/// </summary>
|
||||
public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator == null;
|
||||
public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigators.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user was previously moving selected nodes.
|
||||
@@ -254,7 +254,7 @@ namespace FlaxEditor.Surface
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether user is connecting nodes.
|
||||
/// </summary>
|
||||
public bool IsConnecting => _connectionInstigator != null;
|
||||
public bool IsConnecting => _connectionInstigators.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the left mouse button is down.
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, Box startBox)
|
||||
protected override void OnShowPrimaryMenu(VisjectCM activeCM, Float2 location, List<Box> startBoxes)
|
||||
{
|
||||
// Update nodes for method overrides
|
||||
Profiler.BeginEvent("Overrides");
|
||||
@@ -268,7 +268,7 @@ namespace FlaxEditor.Surface
|
||||
// Update nodes for invoke methods (async)
|
||||
_nodesCache.Get(activeCM);
|
||||
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBox);
|
||||
base.OnShowPrimaryMenu(activeCM, location, startBoxes);
|
||||
|
||||
activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged;
|
||||
}
|
||||
|
||||
@@ -149,13 +149,13 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool h
|
||||
|
||||
bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches, Texture* heightmap, float heightmapScale, Texture* splatmap1, Texture* splatmap2)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Terrain.GenerateTerrain");
|
||||
CHECK_RETURN(terrain && terrain->GetChunkSize() != 0, true);
|
||||
if (numberOfPatches.X < 1 || numberOfPatches.Y < 1)
|
||||
{
|
||||
LOG(Warning, "Cannot setup terain with no patches.");
|
||||
LOG(Warning, "Cannot setup terrain with no patches.");
|
||||
return false;
|
||||
}
|
||||
PROFILE_CPU_NAMED("Terrain.GenerateTerrain");
|
||||
|
||||
// Wait for assets to be loaded
|
||||
if (heightmap && heightmap->WaitForLoaded())
|
||||
@@ -178,7 +178,9 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
||||
terrain->AddPatches(numberOfPatches);
|
||||
|
||||
// Prepare data
|
||||
const auto heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
|
||||
const int32 heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
|
||||
const float heightmapSizeInv = 1.0f / (float)(heightmapSize - 1);
|
||||
const Float2 uvPerPatch = Float2::One / Float2(numberOfPatches);
|
||||
Array<float> heightmapData;
|
||||
heightmapData.Resize(heightmapSize * heightmapSize);
|
||||
|
||||
@@ -192,19 +194,17 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
||||
const auto sampler = PixelFormatSampler::Get(dataHeightmap.Format);
|
||||
|
||||
// Initialize with sub-range of the input heightmap
|
||||
const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches);
|
||||
const float heightmapSizeInv = 1.0f / (heightmapSize - 1);
|
||||
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
|
||||
{
|
||||
auto patch = terrain->GetPatch(patchIndex);
|
||||
const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
|
||||
const Float2 uvStart = Float2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
|
||||
|
||||
// Sample heightmap pixels with interpolation to get actual heightmap vertices locations
|
||||
for (int32 z = 0; z < heightmapSize; z++)
|
||||
{
|
||||
for (int32 x = 0; x < heightmapSize; x++)
|
||||
{
|
||||
const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
|
||||
const Float2 uv = uvStart + Float2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
|
||||
const Color color = sampler->SampleLinear(dataHeightmap.Mip0DataPtr->Get(), uv, dataHeightmap.Mip0Size, dataHeightmap.RowPitch);
|
||||
heightmapData[z * heightmapSize + x] = color.R * heightmapScale;
|
||||
}
|
||||
@@ -230,37 +230,30 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
||||
Texture* splatmaps[2] = { splatmap1, splatmap2 };
|
||||
Array<Color32> splatmapData;
|
||||
TextureDataResult data1;
|
||||
const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches);
|
||||
const float heightmapSizeInv = 1.0f / (heightmapSize - 1);
|
||||
for (int32 index = 0; index < ARRAY_COUNT(splatmaps); index++)
|
||||
{
|
||||
const auto splatmap = splatmaps[index];
|
||||
if (!splatmap)
|
||||
continue;
|
||||
|
||||
// Prepare data
|
||||
if (splatmapData.IsEmpty())
|
||||
splatmapData.Resize(heightmapSize * heightmapSize);
|
||||
|
||||
// Get splatmap data
|
||||
if (GetTextureDataForSampling(splatmap, data1))
|
||||
return true;
|
||||
const auto sampler = PixelFormatSampler::Get(data1.Format);
|
||||
|
||||
// Modify heightmap splatmaps with sub-range of the input splatmaps
|
||||
splatmapData.Resize(heightmapSize * heightmapSize);
|
||||
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
|
||||
{
|
||||
auto patch = terrain->GetPatch(patchIndex);
|
||||
|
||||
const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
|
||||
const Float2 uvStart = Float2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
|
||||
|
||||
// Sample splatmap pixels with interpolation to get actual splatmap values
|
||||
for (int32 z = 0; z < heightmapSize; z++)
|
||||
{
|
||||
for (int32 x = 0; x < heightmapSize; x++)
|
||||
{
|
||||
const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
|
||||
|
||||
const Float2 uv = uvStart + Float2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
|
||||
const Color color = sampler->SampleLinear(data1.Mip0DataPtr->Get(), uv, data1.Mip0Size, data1.RowPitch);
|
||||
|
||||
Color32 layers;
|
||||
@@ -374,63 +367,38 @@ Color32* TerrainTools::GetSplatMapData(Terrain* terrain, const Int2& patchCoord,
|
||||
|
||||
bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Terrain.ExportTerrain");
|
||||
CHECK_RETURN(terrain && terrain->GetPatchesCount() != 0, true);
|
||||
const auto firstPatch = terrain->GetPatch(0);
|
||||
|
||||
// Calculate texture size
|
||||
const int32 patchEdgeVertexCount = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
|
||||
const int32 patchVertexCount = patchEdgeVertexCount * patchEdgeVertexCount;
|
||||
|
||||
// Find size of heightmap in patches
|
||||
const auto firstPatch = terrain->GetPatch(0);
|
||||
Int2 start(firstPatch->GetX(), firstPatch->GetZ());
|
||||
Int2 end(start);
|
||||
for (int32 i = 0; i < terrain->GetPatchesCount(); i++)
|
||||
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
|
||||
{
|
||||
const int32 x = terrain->GetPatch(i)->GetX();
|
||||
const int32 y = terrain->GetPatch(i)->GetZ();
|
||||
|
||||
if (x < start.X)
|
||||
start.X = x;
|
||||
if (y < start.Y)
|
||||
start.Y = y;
|
||||
if (x > end.X)
|
||||
end.X = x;
|
||||
if (y > end.Y)
|
||||
end.Y = y;
|
||||
const auto patch = terrain->GetPatch(patchIndex);
|
||||
const Int2 pos(patch->GetX(), patch->GetZ());
|
||||
start = Int2::Min(start, pos);
|
||||
end = Int2::Max(end, pos);
|
||||
}
|
||||
const Int2 size = (end + 1) - start;
|
||||
|
||||
// Allocate - with space for non-existent patches
|
||||
// Allocate heightmap for a whole terrain (NumberOfPatches * 4x4 * ChunkSize + 1)
|
||||
const Int2 heightmapSize = size * Terrain::ChunksCountEdge * terrain->GetChunkSize() + 1;
|
||||
Array<float> heightmap;
|
||||
heightmap.Resize(patchVertexCount * size.X * size.Y);
|
||||
|
||||
// Set to any element, where: min < elem < max
|
||||
heightmap.Resize(heightmapSize.X * heightmapSize.Y);
|
||||
heightmap.SetAll(firstPatch->GetHeightmapData()[0]);
|
||||
|
||||
const int32 heightmapWidth = patchEdgeVertexCount * size.X;
|
||||
|
||||
// Fill heightmap with data
|
||||
// Fill heightmap with data from all patches
|
||||
const int32 rowSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
|
||||
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
|
||||
{
|
||||
// Pick a patch
|
||||
const auto patch = terrain->GetPatch(patchIndex);
|
||||
const float* data = patch->GetHeightmapData();
|
||||
|
||||
// Beginning of patch
|
||||
int32 dstIndex = (patch->GetX() - start.X) * patchEdgeVertexCount +
|
||||
(patch->GetZ() - start.Y) * size.Y * patchVertexCount;
|
||||
|
||||
// Iterate over lines in patch
|
||||
for (int32 z = 0; z < patchEdgeVertexCount; z++)
|
||||
{
|
||||
// Iterate over vertices in line
|
||||
for (int32 x = 0; x < patchEdgeVertexCount; x++)
|
||||
{
|
||||
heightmap[dstIndex + x] = data[z * patchEdgeVertexCount + x];
|
||||
}
|
||||
|
||||
dstIndex += heightmapWidth;
|
||||
}
|
||||
const Int2 pos(patch->GetX() - start.X, patch->GetZ() - start.Y);
|
||||
const float* src = patch->GetHeightmapData();
|
||||
float* dst = heightmap.Get() + pos.X * (rowSize - 1) + pos.Y * heightmapSize.X * (rowSize - 1);
|
||||
for (int32 row = 0; row < rowSize; row++)
|
||||
Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float));
|
||||
}
|
||||
|
||||
// Interpolate to 16-bit int
|
||||
@@ -438,44 +406,42 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
|
||||
maxHeight = minHeight = heightmap[0];
|
||||
for (int32 i = 1; i < heightmap.Count(); i++)
|
||||
{
|
||||
float h = heightmap[i];
|
||||
float h = heightmap.Get()[i];
|
||||
if (maxHeight < h)
|
||||
maxHeight = h;
|
||||
else if (minHeight > h)
|
||||
minHeight = h;
|
||||
}
|
||||
|
||||
const float maxValue = 65535.0f;
|
||||
const float alpha = maxValue / (maxHeight - minHeight);
|
||||
const float alpha = MAX_uint16 / (maxHeight - minHeight);
|
||||
|
||||
// Storage for pixel data
|
||||
Array<uint16> byteHeightmap(heightmap.Capacity());
|
||||
|
||||
for (auto& elem : heightmap)
|
||||
Array<uint16> byteHeightmap;
|
||||
byteHeightmap.Resize(heightmap.Count());
|
||||
for (int32 i = 0; i < heightmap.Count(); i++)
|
||||
{
|
||||
byteHeightmap.Add(static_cast<uint16>(alpha * (elem - minHeight)));
|
||||
float height = heightmap.Get()[i];
|
||||
byteHeightmap.Get()[i] = static_cast<uint16>(alpha * (height - minHeight));
|
||||
}
|
||||
|
||||
// Create texture
|
||||
TextureData textureData;
|
||||
textureData.Height = textureData.Width = heightmapWidth;
|
||||
textureData.Width = heightmapSize.X;
|
||||
textureData.Height = heightmapSize.Y;
|
||||
textureData.Depth = 1;
|
||||
textureData.Format = PixelFormat::R16_UNorm;
|
||||
textureData.Items.Resize(1);
|
||||
textureData.Items[0].Mips.Resize(1);
|
||||
|
||||
// Fill mip data
|
||||
TextureMipData* srcMip = textureData.GetData(0, 0);
|
||||
srcMip->Data.Link(byteHeightmap.Get());
|
||||
srcMip->Lines = textureData.Height;
|
||||
srcMip->RowPitch = textureData.Width * 2; // 2 bytes per pixel for format R16
|
||||
srcMip->RowPitch = textureData.Width * sizeof(uint16);
|
||||
srcMip->DepthPitch = srcMip->Lines * srcMip->RowPitch;
|
||||
|
||||
// Find next non-existing file heightmap file
|
||||
FileSystem::NormalizePath(outputFolder);
|
||||
const String baseFileName(TEXT("heightmap"));
|
||||
String outputPath;
|
||||
for (int32 i = 0; i < MAX_int32; i++)
|
||||
for (int32 i = 0; i < 100; i++)
|
||||
{
|
||||
outputPath = outputFolder / baseFileName + StringUtils::ToString(i) + TEXT(".png");
|
||||
if (!FileSystem::FileExists(outputPath))
|
||||
|
||||
@@ -212,6 +212,10 @@ namespace FlaxEditor.Utilities
|
||||
if (value is FlaxEngine.Object)
|
||||
return value;
|
||||
|
||||
// For custom types use interface
|
||||
if (value is ICloneable clonable)
|
||||
return clonable.Clone();
|
||||
|
||||
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
|
||||
if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass))
|
||||
{
|
||||
@@ -548,6 +552,26 @@ namespace FlaxEditor.Utilities
|
||||
return arr;
|
||||
}
|
||||
|
||||
internal static void StructureToByteArray(object value, int valueSize, IntPtr tempBuffer, byte[] dataBuffer)
|
||||
{
|
||||
var valueType = value.GetType();
|
||||
if (valueType.IsEnum)
|
||||
{
|
||||
var ptr = FlaxEngine.Interop.NativeInterop.ValueTypeUnboxer.GetPointer(value, valueType);
|
||||
FlaxEngine.Utils.MemoryCopy(tempBuffer, ptr, (ulong)valueSize);
|
||||
}
|
||||
else
|
||||
Marshal.StructureToPtr(value, tempBuffer, true);
|
||||
Marshal.Copy(tempBuffer, dataBuffer, 0, valueSize);
|
||||
}
|
||||
|
||||
internal static object ByteArrayToStructure(IntPtr valuePtr, Type valueType, int valueSize)
|
||||
{
|
||||
if (valueType.IsEnum)
|
||||
return FlaxEngine.Interop.NativeInterop.MarshalToManaged(valuePtr, valueType);
|
||||
return Marshal.PtrToStructure(valuePtr, valueType);
|
||||
}
|
||||
|
||||
internal static unsafe string ReadStr(this BinaryReader stream, int check)
|
||||
{
|
||||
int length = stream.ReadInt32();
|
||||
|
||||
@@ -65,13 +65,14 @@ public:
|
||||
|
||||
ViewportIconsRendererService ViewportIconsRendererServiceInstance;
|
||||
float ViewportIconsRenderer::Scale = 1.0f;
|
||||
Real ViewportIconsRenderer::MinSize = 7.0f;
|
||||
Real ViewportIconsRenderer::MaxSize = 30.0f;
|
||||
Real ViewportIconsRenderer::MaxSizeDistance = 1000.0f;
|
||||
|
||||
void ViewportIconsRenderer::GetBounds(const Vector3& position, const Vector3& viewPosition, BoundingSphere& bounds)
|
||||
{
|
||||
constexpr Real minSize = 7.0;
|
||||
constexpr Real maxSize = 30.0;
|
||||
Real scale = Math::Square(Vector3::Distance(position, viewPosition) / 1000.0f);
|
||||
Real radius = minSize + Math::Min<Real>(scale, 1.0f) * (maxSize - minSize);
|
||||
Real scale = Math::Square(Vector3::Distance(position, viewPosition) / MaxSizeDistance);
|
||||
Real radius = MinSize + Math::Min<Real>(scale, 1.0f) * (MaxSize - MinSize);
|
||||
bounds = BoundingSphere(position, radius * Scale);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,21 @@ public:
|
||||
/// </summary>
|
||||
API_FIELD() static float Scale;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum size of the icons.
|
||||
/// </summary>
|
||||
API_FIELD() static Real MinSize;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum size of the icons.
|
||||
/// </summary>
|
||||
API_FIELD() static Real MaxSize;
|
||||
|
||||
/// <summary>
|
||||
/// The distance to the camera at which the icons will be drawn at their maximum size.
|
||||
/// </summary>
|
||||
API_FIELD() static Real MaxSizeDistance;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the icons for the actors in the given scene (or actor tree).
|
||||
/// </summary>
|
||||
|
||||
@@ -541,7 +541,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Setup options
|
||||
{
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
_editor.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
SetupViewportOptions();
|
||||
}
|
||||
|
||||
@@ -587,7 +587,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Camera Settings Menu
|
||||
var cameraCM = new ContextMenu();
|
||||
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), Editor.Instance.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
|
||||
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), _editor.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
|
||||
{
|
||||
Tag = this,
|
||||
TooltipText = "Camera Settings",
|
||||
@@ -596,7 +596,7 @@ namespace FlaxEditor.Viewport
|
||||
_cameraWidget.Parent = this;
|
||||
|
||||
// Orthographic/Perspective Mode Widget
|
||||
_orthographicModeButton = new ViewportWidgetButton(string.Empty, Editor.Instance.Icons.CamSpeed32, null, true)
|
||||
_orthographicModeButton = new ViewportWidgetButton(string.Empty, _editor.Icons.CamSpeed32, null, true)
|
||||
{
|
||||
Checked = !_isOrtho,
|
||||
TooltipText = "Toggle Orthographic/Perspective Mode",
|
||||
@@ -869,8 +869,8 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
}
|
||||
});
|
||||
viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32;
|
||||
viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32;
|
||||
viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = _editor.Icons.Rotate32;
|
||||
viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0));
|
||||
viewLayers.AddSeparator();
|
||||
var layers = LayersAndTagsSettings.GetCurrentLayers();
|
||||
if (layers != null && layers.Length > 0)
|
||||
@@ -910,8 +910,8 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
}
|
||||
});
|
||||
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
|
||||
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
|
||||
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = _editor.Icons.Rotate32;
|
||||
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None);
|
||||
viewFlags.AddSeparator();
|
||||
for (int i = 0; i < ViewFlagsValues.Length; i++)
|
||||
{
|
||||
@@ -1091,7 +1091,7 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
private void SetupViewportOptions()
|
||||
{
|
||||
var options = Editor.Instance.Options.Options;
|
||||
var options = _editor.Options.Options;
|
||||
_minMovementSpeed = options.Viewport.MinMovementSpeed;
|
||||
MovementSpeed = options.Viewport.MovementSpeed;
|
||||
_maxMovementSpeed = options.Viewport.MaxMovementSpeed;
|
||||
@@ -1298,6 +1298,11 @@ namespace FlaxEditor.Viewport
|
||||
_mouseSensitivity = options.Viewport.MouseSensitivity;
|
||||
_maxSpeedSteps = options.Viewport.TotalCameraSpeedSteps;
|
||||
_cameraEasingDegree = options.Viewport.CameraEasingDegree;
|
||||
|
||||
ViewportIconsRenderer.MinSize = options.Viewport.IconsMinimumSize;
|
||||
ViewportIconsRenderer.MaxSize = options.Viewport.IconsMaximumSize;
|
||||
ViewportIconsRenderer.MaxSizeDistance = options.Viewport.MaxSizeDistance;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
}
|
||||
|
||||
@@ -1711,7 +1716,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Check if update mouse
|
||||
var size = Size;
|
||||
var options = Editor.Instance.Options.Options;
|
||||
var options = _editor.Options.Options;
|
||||
if (_isControllingMouse)
|
||||
{
|
||||
var rmbWheel = false;
|
||||
@@ -1952,7 +1957,7 @@ namespace FlaxEditor.Viewport
|
||||
return true;
|
||||
|
||||
// Custom input events
|
||||
return InputActions.Process(Editor.Instance, this, key);
|
||||
return InputActions.Process(_editor, this, key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1969,7 +1974,7 @@ namespace FlaxEditor.Viewport
|
||||
base.Draw();
|
||||
|
||||
// Add overlay during debugger breakpoint hang
|
||||
if (Editor.Instance.Simulation.IsDuringBreakpointHang)
|
||||
if (_editor.Simulation.IsDuringBreakpointHang)
|
||||
{
|
||||
var bounds = new Rectangle(Float2.Zero, Size);
|
||||
Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f));
|
||||
@@ -1994,7 +1999,7 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
Editor.Instance.Options.OptionsChanged -= OnEditorOptionsChanged;
|
||||
_editor.Options.OptionsChanged -= OnEditorOptionsChanged;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
@@ -13,6 +15,7 @@ using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using Utils = FlaxEditor.Utilities.Utils;
|
||||
|
||||
namespace FlaxEditor.Viewport
|
||||
@@ -70,8 +73,11 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private PrefabUIEditorRoot _uiRoot;
|
||||
private bool _showUI = false;
|
||||
|
||||
|
||||
private int _defaultScaleActiveIndex = 0;
|
||||
private int _customScaleActiveIndex = -1;
|
||||
private ContextMenuButton _uiModeButton;
|
||||
private ContextMenuChildMenu _uiViewOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the UI Mode is toggled.
|
||||
@@ -137,6 +143,8 @@ namespace FlaxEditor.Viewport
|
||||
UseAutomaticTaskManagement = defaultFeatures;
|
||||
ShowDefaultSceneActors = defaultFeatures;
|
||||
TintColor = defaultFeatures ? Color.White : Color.Transparent;
|
||||
if (_uiViewOptions != null)
|
||||
_uiViewOptions.Visible = _showUI;
|
||||
UIModeToggled?.Invoke(_showUI);
|
||||
}
|
||||
}
|
||||
@@ -210,7 +218,7 @@ namespace FlaxEditor.Viewport
|
||||
_uiParentLink = _uiRoot.UIRoot;
|
||||
|
||||
// UI mode buton
|
||||
_uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", (button) => ShowUI = button.Checked);
|
||||
_uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", button => ShowUI = button.Checked);
|
||||
_uiModeButton.AutoCheck = true;
|
||||
_uiModeButton.VisibleChanged += control => (control as ContextMenuButton).Checked = ShowUI;
|
||||
|
||||
@@ -222,6 +230,84 @@ namespace FlaxEditor.Viewport
|
||||
SetUpdate(ref _update, OnUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the view scaling options. Needs to be called after a Prefab is valid and loaded.
|
||||
/// </summary>
|
||||
public void CreateViewScalingOptions()
|
||||
{
|
||||
if (_uiViewOptions != null)
|
||||
return;
|
||||
_uiViewOptions = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling");
|
||||
_uiViewOptions.Visible = _showUI;
|
||||
LoadCustomUIScalingOption();
|
||||
Editor.Instance.UI.CreateViewportSizingContextMenu(_uiViewOptions.ContextMenu, _defaultScaleActiveIndex, _customScaleActiveIndex, true, ChangeUIView, (a, b) =>
|
||||
{
|
||||
_defaultScaleActiveIndex = a;
|
||||
_customScaleActiveIndex = b;
|
||||
});
|
||||
}
|
||||
|
||||
private void ChangeUIView(UIModule.ViewportScaleOption uiViewScaleOption)
|
||||
{
|
||||
_uiRoot.SetViewSize((Float2)uiViewScaleOption.Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the active ui scaling option.
|
||||
/// </summary>
|
||||
public void SaveActiveUIScalingOption()
|
||||
{
|
||||
var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex";
|
||||
Editor.Instance.ProjectCache.SetCustomData(defaultKey, _defaultScaleActiveIndex.ToString());
|
||||
var customKey = $"{Prefab.ID}:CustomViewportScalingIndex";
|
||||
Editor.Instance.ProjectCache.SetCustomData(customKey, _customScaleActiveIndex.ToString());
|
||||
}
|
||||
|
||||
private void LoadCustomUIScalingOption()
|
||||
{
|
||||
Prefab.WaitForLoaded();
|
||||
var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex";
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData(defaultKey, out string defaultData))
|
||||
{
|
||||
if (int.TryParse(defaultData, out var index))
|
||||
{
|
||||
var options = Editor.Instance.UI.DefaultViewportScaleOptions;
|
||||
if (options.Count > index)
|
||||
{
|
||||
_defaultScaleActiveIndex = index;
|
||||
if (index != -1)
|
||||
ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]);
|
||||
}
|
||||
// Assume option does not exist anymore so move to default.
|
||||
else if (index != -1)
|
||||
{
|
||||
_defaultScaleActiveIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var customKey = $"{Prefab.ID}:CustomViewportScalingIndex";
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data))
|
||||
{
|
||||
if (int.TryParse(data, out var index))
|
||||
{
|
||||
var options = Editor.Instance.UI.CustomViewportScaleOptions;
|
||||
if (options.Count > index)
|
||||
{
|
||||
_customScaleActiveIndex = index;
|
||||
if (index != -1)
|
||||
ChangeUIView(options[index]);
|
||||
}
|
||||
// Assume option does not exist anymore so move to default.
|
||||
else if (index != -1)
|
||||
{
|
||||
_defaultScaleActiveIndex = 0;
|
||||
_customScaleActiveIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpdate(float deltaTime)
|
||||
{
|
||||
for (int i = 0; i < Gizmos.Count; i++)
|
||||
|
||||
@@ -112,8 +112,9 @@ namespace FlaxEditor.Viewport.Previews
|
||||
LinkCanvas(_instance);
|
||||
|
||||
// Link UI control to the preview
|
||||
var uiControl = _instance as UIControl;
|
||||
if (_uiControlLinked == null &&
|
||||
_instance is UIControl uiControl &&
|
||||
uiControl != null &&
|
||||
uiControl.Control != null &&
|
||||
uiControl.Control.Parent == null)
|
||||
{
|
||||
@@ -128,6 +129,12 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_uiControlLinked.Control.Parent = _uiParentLink;
|
||||
_hasUILinked = true;
|
||||
}
|
||||
|
||||
// Use UI mode when root is empty UI Control
|
||||
if (_uiControlLinked == null && uiControl != null && uiControl.Control == null)
|
||||
{
|
||||
_hasUILinked = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LinkCanvas(Actor actor)
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace FlaxEditor.Windows
|
||||
"Jean-Baptiste Perrier",
|
||||
"Chandler Cox",
|
||||
"Ari Vuollet",
|
||||
"Vincent Saarmann",
|
||||
});
|
||||
authors.Sort();
|
||||
var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70)
|
||||
|
||||
@@ -371,6 +371,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
else
|
||||
_viewport.SetInitialUIMode(_viewport._hasUILinked);
|
||||
_viewport.UIModeToggled += OnUIModeToggled;
|
||||
_viewport.CreateViewScalingOptions();
|
||||
Graph.MainActor = _viewport.Instance;
|
||||
Selection.Clear();
|
||||
Select(Graph.Main);
|
||||
@@ -567,6 +568,15 @@ namespace FlaxEditor.Windows.Assets
|
||||
Graph.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnClose()
|
||||
{
|
||||
// Save current UI view size state.
|
||||
_viewport.SaveActiveUIScalingOption();
|
||||
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EditorViewport PresenterViewport => _viewport;
|
||||
|
||||
|
||||
@@ -116,6 +116,11 @@ namespace FlaxEditor.Windows
|
||||
if (InputOptions.WindowShortcutsAvaliable)
|
||||
Editor.Windows.VisualScriptDebuggerWin.FocusOrShow();
|
||||
});
|
||||
InputActions.Add(options => options.EditorOptionsWindow, () =>
|
||||
{
|
||||
if (InputOptions.WindowShortcutsAvaliable)
|
||||
Editor.Windows.EditorOptionsWin.FocusOrShow();
|
||||
});
|
||||
|
||||
// Register
|
||||
Editor.Windows.OnWindowAdd(this);
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Xml;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -34,8 +35,8 @@ namespace FlaxEditor.Windows
|
||||
private CursorLockMode _cursorLockMode = CursorLockMode.None;
|
||||
|
||||
// Viewport scaling variables
|
||||
private List<ViewportScaleOptions> _defaultViewportScaling = new List<ViewportScaleOptions>();
|
||||
private List<ViewportScaleOptions> _customViewportScaling = new List<ViewportScaleOptions>();
|
||||
private int _defaultScaleActiveIndex = 0;
|
||||
private int _customScaleActiveIndex = -1;
|
||||
private float _viewportAspectRatio = 1;
|
||||
private float _windowAspectRatio = 1;
|
||||
private bool _useAspect = false;
|
||||
@@ -246,35 +247,6 @@ namespace FlaxEditor.Windows
|
||||
/// </summary>
|
||||
public InterfaceOptions.PlayModeFocus FocusOnPlayOption { get; set; }
|
||||
|
||||
private enum ViewportScaleType
|
||||
{
|
||||
Resolution = 0,
|
||||
Aspect = 1,
|
||||
}
|
||||
|
||||
private class ViewportScaleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The name.
|
||||
/// </summary>
|
||||
public string Label;
|
||||
|
||||
/// <summary>
|
||||
/// The Type of scaling to do.
|
||||
/// </summary>
|
||||
public ViewportScaleType ScaleType;
|
||||
|
||||
/// <summary>
|
||||
/// The width and height to scale by.
|
||||
/// </summary>
|
||||
public Int2 Size;
|
||||
|
||||
/// <summary>
|
||||
/// If the scaling is active.
|
||||
/// </summary>
|
||||
public bool Active;
|
||||
}
|
||||
|
||||
private class PlayModeFocusOptions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -420,7 +392,7 @@ namespace FlaxEditor.Windows
|
||||
InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
|
||||
}
|
||||
|
||||
private void ChangeViewportRatio(ViewportScaleOptions v)
|
||||
private void ChangeViewportRatio(UIModule.ViewportScaleOption v)
|
||||
{
|
||||
if (v == null)
|
||||
return;
|
||||
@@ -439,11 +411,11 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
switch (v.ScaleType)
|
||||
{
|
||||
case ViewportScaleType.Aspect:
|
||||
case UIModule.ViewportScaleOption.ViewportScaleType.Aspect:
|
||||
_useAspect = true;
|
||||
_freeAspect = false;
|
||||
break;
|
||||
case ViewportScaleType.Resolution:
|
||||
case UIModule.ViewportScaleOption.ViewportScaleType.Resolution:
|
||||
_useAspect = false;
|
||||
_freeAspect = false;
|
||||
break;
|
||||
@@ -634,49 +606,12 @@ namespace FlaxEditor.Windows
|
||||
|
||||
// Viewport aspect ratio
|
||||
{
|
||||
// Create default scaling options if they dont exist from deserialization.
|
||||
if (_defaultViewportScaling.Count == 0)
|
||||
{
|
||||
_defaultViewportScaling.Add(new ViewportScaleOptions
|
||||
{
|
||||
Label = "Free Aspect",
|
||||
ScaleType = ViewportScaleType.Aspect,
|
||||
Size = new Int2(1, 1),
|
||||
Active = true,
|
||||
});
|
||||
_defaultViewportScaling.Add(new ViewportScaleOptions
|
||||
{
|
||||
Label = "16:9 Aspect",
|
||||
ScaleType = ViewportScaleType.Aspect,
|
||||
Size = new Int2(16, 9),
|
||||
Active = false,
|
||||
});
|
||||
_defaultViewportScaling.Add(new ViewportScaleOptions
|
||||
{
|
||||
Label = "16:10 Aspect",
|
||||
ScaleType = ViewportScaleType.Aspect,
|
||||
Size = new Int2(16, 10),
|
||||
Active = false,
|
||||
});
|
||||
_defaultViewportScaling.Add(new ViewportScaleOptions
|
||||
{
|
||||
Label = "1920x1080 Resolution (Full HD)",
|
||||
ScaleType = ViewportScaleType.Resolution,
|
||||
Size = new Int2(1920, 1080),
|
||||
Active = false,
|
||||
});
|
||||
_defaultViewportScaling.Add(new ViewportScaleOptions
|
||||
{
|
||||
Label = "2560x1440 Resolution (2K)",
|
||||
ScaleType = ViewportScaleType.Resolution,
|
||||
Size = new Int2(2560, 1440),
|
||||
Active = false,
|
||||
});
|
||||
}
|
||||
|
||||
var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu;
|
||||
|
||||
CreateViewportSizingContextMenu(vsMenu);
|
||||
Editor.UI.CreateViewportSizingContextMenu(vsMenu, _defaultScaleActiveIndex, _customScaleActiveIndex, false, ChangeViewportRatio, (a, b) =>
|
||||
{
|
||||
_defaultScaleActiveIndex = a;
|
||||
_customScaleActiveIndex = b;
|
||||
});
|
||||
}
|
||||
|
||||
// Take Screenshot
|
||||
@@ -774,243 +709,6 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateViewportSizingContextMenu(ContextMenu vsMenu)
|
||||
{
|
||||
// Add default viewport sizing options
|
||||
for (int i = 0; i < _defaultViewportScaling.Count; i++)
|
||||
{
|
||||
var viewportScale = _defaultViewportScaling[i];
|
||||
var button = vsMenu.AddButton(viewportScale.Label);
|
||||
button.CloseMenuOnClick = false;
|
||||
button.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
button.Tag = viewportScale;
|
||||
if (viewportScale.Active)
|
||||
ChangeViewportRatio(viewportScale);
|
||||
|
||||
button.Clicked += () =>
|
||||
{
|
||||
if (button.Tag == null)
|
||||
return;
|
||||
|
||||
// Reset selected icon on all buttons
|
||||
foreach (var child in vsMenu.Items)
|
||||
{
|
||||
if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v)
|
||||
{
|
||||
if (cmb == button)
|
||||
{
|
||||
v.Active = true;
|
||||
button.Icon = Style.Current.CheckBoxTick;
|
||||
ChangeViewportRatio(v);
|
||||
}
|
||||
else if (v.Active)
|
||||
{
|
||||
cmb.Icon = SpriteHandle.Invalid;
|
||||
v.Active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
if (_defaultViewportScaling.Count != 0)
|
||||
vsMenu.AddSeparator();
|
||||
|
||||
// Add custom viewport options
|
||||
for (int i = 0; i < _customViewportScaling.Count; i++)
|
||||
{
|
||||
var viewportScale = _customViewportScaling[i];
|
||||
var childCM = vsMenu.AddChildMenu(viewportScale.Label);
|
||||
childCM.CloseMenuOnClick = false;
|
||||
childCM.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
childCM.Tag = viewportScale;
|
||||
if (viewportScale.Active)
|
||||
ChangeViewportRatio(viewportScale);
|
||||
|
||||
var applyButton = childCM.ContextMenu.AddButton("Apply");
|
||||
applyButton.Tag = childCM.Tag = viewportScale;
|
||||
applyButton.CloseMenuOnClick = false;
|
||||
applyButton.Clicked += () =>
|
||||
{
|
||||
if (childCM.Tag == null)
|
||||
return;
|
||||
|
||||
// Reset selected icon on all buttons
|
||||
foreach (var child in vsMenu.Items)
|
||||
{
|
||||
if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v)
|
||||
{
|
||||
if (child == childCM)
|
||||
{
|
||||
v.Active = true;
|
||||
childCM.Icon = Style.Current.CheckBoxTick;
|
||||
ChangeViewportRatio(v);
|
||||
}
|
||||
else if (v.Active)
|
||||
{
|
||||
cmb.Icon = SpriteHandle.Invalid;
|
||||
v.Active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var deleteButton = childCM.ContextMenu.AddButton("Delete");
|
||||
deleteButton.CloseMenuOnClick = false;
|
||||
deleteButton.Clicked += () =>
|
||||
{
|
||||
if (childCM.Tag == null)
|
||||
return;
|
||||
|
||||
var v = (ViewportScaleOptions)childCM.Tag;
|
||||
if (v.Active)
|
||||
{
|
||||
v.Active = false;
|
||||
_defaultViewportScaling[0].Active = true;
|
||||
ChangeViewportRatio(_defaultViewportScaling[0]);
|
||||
}
|
||||
_customViewportScaling.Remove(v);
|
||||
vsMenu.DisposeAllItems();
|
||||
CreateViewportSizingContextMenu(vsMenu);
|
||||
vsMenu.PerformLayout();
|
||||
};
|
||||
}
|
||||
if (_customViewportScaling.Count != 0)
|
||||
vsMenu.AddSeparator();
|
||||
|
||||
// Add button
|
||||
var add = vsMenu.AddButton("Add...");
|
||||
add.CloseMenuOnClick = false;
|
||||
add.Clicked += () =>
|
||||
{
|
||||
var popup = new ContextMenuBase
|
||||
{
|
||||
Size = new Float2(230, 125),
|
||||
ClipChildren = false,
|
||||
CullChildren = false,
|
||||
};
|
||||
popup.Show(add, new Float2(add.Width, 0));
|
||||
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Name",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
nameLabel.LocalX += 10;
|
||||
nameLabel.LocalY += 10;
|
||||
|
||||
var nameTextBox = new TextBox
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
IsMultiline = false,
|
||||
};
|
||||
nameTextBox.LocalX += 100;
|
||||
nameTextBox.LocalY += 10;
|
||||
|
||||
var typeLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Type",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
typeLabel.LocalX += 10;
|
||||
typeLabel.LocalY += 35;
|
||||
|
||||
var typeDropdown = new Dropdown
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Items = { "Aspect", "Resolution" },
|
||||
SelectedItem = "Aspect",
|
||||
Width = nameTextBox.Width
|
||||
};
|
||||
typeDropdown.LocalY += 35;
|
||||
typeDropdown.LocalX += 100;
|
||||
|
||||
var whLabel = new Label
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Width & Height",
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
};
|
||||
whLabel.LocalX += 10;
|
||||
whLabel.LocalY += 60;
|
||||
|
||||
var wValue = new IntValueBox(16)
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
MinValue = 1,
|
||||
Width = 55,
|
||||
};
|
||||
wValue.LocalY += 60;
|
||||
wValue.LocalX += 100;
|
||||
|
||||
var hValue = new IntValueBox(9)
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
MinValue = 1,
|
||||
Width = 55,
|
||||
};
|
||||
hValue.LocalY += 60;
|
||||
hValue.LocalX += 165;
|
||||
|
||||
var submitButton = new Button
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Submit",
|
||||
Width = 70,
|
||||
};
|
||||
submitButton.LocalX += 40;
|
||||
submitButton.LocalY += 90;
|
||||
|
||||
submitButton.Clicked += () =>
|
||||
{
|
||||
Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleType type);
|
||||
|
||||
var combineString = type == ViewportScaleType.Aspect ? ":" : "x";
|
||||
var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem;
|
||||
|
||||
var newViewportOption = new ViewportScaleOptions
|
||||
{
|
||||
ScaleType = type,
|
||||
Label = name,
|
||||
Size = new Int2(wValue.Value, hValue.Value),
|
||||
};
|
||||
|
||||
_customViewportScaling.Add(newViewportOption);
|
||||
vsMenu.DisposeAllItems();
|
||||
CreateViewportSizingContextMenu(vsMenu);
|
||||
vsMenu.PerformLayout();
|
||||
};
|
||||
|
||||
var cancelButton = new Button
|
||||
{
|
||||
Parent = popup,
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Text = "Cancel",
|
||||
Width = 70,
|
||||
};
|
||||
cancelButton.LocalX += 120;
|
||||
cancelButton.LocalY += 90;
|
||||
|
||||
cancelButton.Clicked += () =>
|
||||
{
|
||||
nameTextBox.Clear();
|
||||
typeDropdown.SelectedItem = "Aspect";
|
||||
hValue.Value = 9;
|
||||
wValue.Value = 16;
|
||||
popup.Hide();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
@@ -1231,8 +929,8 @@ namespace FlaxEditor.Windows
|
||||
writer.WriteAttributeString("ShowGUI", ShowGUI.ToString());
|
||||
writer.WriteAttributeString("EditGUI", EditGUI.ToString());
|
||||
writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString());
|
||||
writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling));
|
||||
writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling));
|
||||
writer.WriteAttributeString("DefaultViewportScalingIndex", _defaultScaleActiveIndex.ToString());
|
||||
writer.WriteAttributeString("CustomViewportScalingIndex", _customScaleActiveIndex.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1244,22 +942,30 @@ namespace FlaxEditor.Windows
|
||||
EditGUI = value1;
|
||||
if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1))
|
||||
ShowDebugDraw = value1;
|
||||
if (node.HasAttribute("CustomViewportScaling"))
|
||||
_customViewportScaling = JsonSerializer.Deserialize<List<ViewportScaleOptions>>(node.GetAttribute("CustomViewportScaling"));
|
||||
if (int.TryParse(node.GetAttribute("DefaultViewportScalingIndex"), out int value2))
|
||||
_defaultScaleActiveIndex = value2;
|
||||
if (int.TryParse(node.GetAttribute("CustomViewportScalingIndex"), out value2))
|
||||
_customScaleActiveIndex = value2;
|
||||
|
||||
for (int i = 0; i < _customViewportScaling.Count; i++)
|
||||
if (_defaultScaleActiveIndex != -1)
|
||||
{
|
||||
if (_customViewportScaling[i].Active)
|
||||
ChangeViewportRatio(_customViewportScaling[i]);
|
||||
var options = Editor.UI.DefaultViewportScaleOptions;
|
||||
if (options.Count > _defaultScaleActiveIndex)
|
||||
ChangeViewportRatio(options[_defaultScaleActiveIndex]);
|
||||
else
|
||||
_defaultScaleActiveIndex = 0;
|
||||
}
|
||||
|
||||
if (node.HasAttribute("DefaultViewportScaling"))
|
||||
_defaultViewportScaling = JsonSerializer.Deserialize<List<ViewportScaleOptions>>(node.GetAttribute("DefaultViewportScaling"));
|
||||
|
||||
for (int i = 0; i < _defaultViewportScaling.Count; i++)
|
||||
|
||||
if (_customScaleActiveIndex != -1)
|
||||
{
|
||||
if (_defaultViewportScaling[i].Active)
|
||||
ChangeViewportRatio(_defaultViewportScaling[i]);
|
||||
var options = Editor.UI.CustomViewportScaleOptions;
|
||||
if (options.Count > _customScaleActiveIndex)
|
||||
ChangeViewportRatio(options[_customScaleActiveIndex]);
|
||||
else
|
||||
{
|
||||
_defaultScaleActiveIndex = 0;
|
||||
_customScaleActiveIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,10 @@ namespace FlaxEditor.Windows
|
||||
else
|
||||
cm.ClearItems();
|
||||
|
||||
float longestItemWidth = 0.0f;
|
||||
|
||||
// Add items
|
||||
var font = Style.Current.FontMedium;
|
||||
ItemsListContextMenu.Item lastItem = null;
|
||||
foreach (var command in commands)
|
||||
{
|
||||
@@ -244,7 +247,7 @@ namespace FlaxEditor.Windows
|
||||
});
|
||||
var flags = DebugCommands.GetCommandFlags(command);
|
||||
if (flags.HasFlag(DebugCommands.CommandFlags.Exec))
|
||||
lastItem.TintColor = new Color(0.85f, 0.85f, 1.0f, 1.0f);
|
||||
lastItem.TintColor = new Color(0.75f, 0.75f, 1.0f, 1.0f);
|
||||
else if (flags.HasFlag(DebugCommands.CommandFlags.Read) && !flags.HasFlag(DebugCommands.CommandFlags.Write))
|
||||
lastItem.TintColor = new Color(0.85f, 0.85f, 0.85f, 1.0f);
|
||||
lastItem.Focused += item =>
|
||||
@@ -252,6 +255,10 @@ namespace FlaxEditor.Windows
|
||||
// Set command
|
||||
Set(item.Name);
|
||||
};
|
||||
|
||||
float width = font.MeasureText(command).X;
|
||||
if (width > longestItemWidth)
|
||||
longestItemWidth = width;
|
||||
}
|
||||
cm.ItemClicked += item =>
|
||||
{
|
||||
@@ -262,6 +269,10 @@ namespace FlaxEditor.Windows
|
||||
// Setup popup
|
||||
var count = commands.Count();
|
||||
var totalHeight = count * lastItem.Height + cm.ItemsPanel.Margin.Height + cm.ItemsPanel.Spacing * (count - 1);
|
||||
|
||||
// Account for scroll bars taking up a part of the width
|
||||
longestItemWidth += 25f;
|
||||
cm.Width = longestItemWidth;
|
||||
cm.Height = 220;
|
||||
if (cm.Height > totalHeight)
|
||||
cm.Height = totalHeight; // Limit popup height if list is small
|
||||
@@ -314,12 +325,25 @@ namespace FlaxEditor.Windows
|
||||
|
||||
// Show commands search popup based on current text input
|
||||
var text = Text.Trim();
|
||||
if (text.Length != 0)
|
||||
bool isWhitespaceOnly = string.IsNullOrWhiteSpace(Text) && !string.IsNullOrEmpty(Text);
|
||||
if (text.Length != 0 || isWhitespaceOnly)
|
||||
{
|
||||
DebugCommands.Search(text, out var matches);
|
||||
if (matches.Length != 0)
|
||||
if (matches.Length != 0 || isWhitespaceOnly)
|
||||
{
|
||||
ShowPopup(ref _searchPopup, matches, text);
|
||||
string[] commands = [];
|
||||
if (isWhitespaceOnly)
|
||||
DebugCommands.GetAllCommands(out commands);
|
||||
|
||||
ShowPopup(ref _searchPopup, isWhitespaceOnly ? commands : matches, text);
|
||||
|
||||
if (isWhitespaceOnly)
|
||||
{
|
||||
// Scroll to and select first item for consistent behaviour
|
||||
var firstItem = _searchPopup.ItemsPanel.Children[0] as Item;
|
||||
_searchPopup.ScrollToAndHighlightItemByName(firstItem.Name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ void MultiBlendBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
||||
void BlendPoseBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
||||
{
|
||||
bucket.BlendPose.TransitionPosition = 0.0f;
|
||||
bucket.BlendPose.BlendPoseIndex = -1;
|
||||
bucket.BlendPose.PreviousBlendPoseIndex = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +239,8 @@ public:
|
||||
struct BlendPoseBucket
|
||||
{
|
||||
float TransitionPosition;
|
||||
int32 PreviousBlendPoseIndex;
|
||||
int16 BlendPoseIndex;
|
||||
int16 PreviousBlendPoseIndex;
|
||||
};
|
||||
|
||||
struct StateMachineBucket
|
||||
@@ -810,6 +811,7 @@ public:
|
||||
{
|
||||
// Copy the node transformations
|
||||
Platform::MemoryCopy(dstNodes->Nodes.Get(), srcNodes->Nodes.Get(), sizeof(Transform) * _skeletonNodesCount);
|
||||
dstNodes->RootMotion = srcNodes->RootMotion;
|
||||
|
||||
// Copy the animation playback state
|
||||
dstNodes->Position = srcNodes->Position;
|
||||
|
||||
@@ -676,9 +676,12 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const
|
||||
if (!ANIM_GRAPH_IS_VALID_PTR(poseB))
|
||||
nodesB = GetEmptyNodes();
|
||||
|
||||
const Transform* srcA = nodesA->Nodes.Get();
|
||||
const Transform* srcB = nodesB->Nodes.Get();
|
||||
Transform* dst = nodes->Nodes.Get();
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]);
|
||||
Transform::Lerp(srcA[i], srcB[i], alpha, dst[i]);
|
||||
}
|
||||
Transform::Lerp(nodesA->RootMotion, nodesB->RootMotion, alpha, nodes->RootMotion);
|
||||
nodes->Position = Math::Lerp(nodesA->Position, nodesB->Position, alpha);
|
||||
@@ -1263,21 +1266,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
const auto valueA = tryGetValue(node->GetBox(1), Value::Null);
|
||||
const auto valueB = tryGetValue(node->GetBox(2), Value::Null);
|
||||
const auto nodes = node->GetNodes(this);
|
||||
|
||||
auto nodesA = static_cast<AnimGraphImpulse*>(valueA.AsPointer);
|
||||
auto nodesB = static_cast<AnimGraphImpulse*>(valueB.AsPointer);
|
||||
if (!ANIM_GRAPH_IS_VALID_PTR(valueA))
|
||||
nodesA = GetEmptyNodes();
|
||||
if (!ANIM_GRAPH_IS_VALID_PTR(valueB))
|
||||
nodesB = GetEmptyNodes();
|
||||
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]);
|
||||
}
|
||||
Transform::Lerp(nodesA->RootMotion, nodesB->RootMotion, alpha, nodes->RootMotion);
|
||||
value = nodes;
|
||||
value = Blend(node, valueA, valueB, alpha, AlphaBlendMode::Linear);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1758,35 +1747,38 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// [2]: int Pose Count
|
||||
// [3]: AlphaBlendMode Mode
|
||||
|
||||
// Prepare
|
||||
auto& bucket = context.Data->State[node->BucketIndex].BlendPose;
|
||||
const int32 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]);
|
||||
const int16 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]);
|
||||
const float blendDuration = (float)tryGetValue(node->GetBox(2), node->Values[1]);
|
||||
const int32 poseCount = Math::Clamp(node->Values[2].AsInt, 0, MaxBlendPoses);
|
||||
const AlphaBlendMode mode = (AlphaBlendMode)node->Values[3].AsInt;
|
||||
|
||||
// Skip if nothing to blend
|
||||
if (poseCount == 0 || poseIndex < 0 || poseIndex >= poseCount)
|
||||
{
|
||||
break;
|
||||
|
||||
// Check if swap transition end points
|
||||
if (bucket.PreviousBlendPoseIndex == poseIndex && bucket.BlendPoseIndex != poseIndex && bucket.TransitionPosition >= ANIM_GRAPH_BLEND_THRESHOLD)
|
||||
{
|
||||
bucket.TransitionPosition = blendDuration - bucket.TransitionPosition;
|
||||
Swap(bucket.BlendPoseIndex, bucket.PreviousBlendPoseIndex);
|
||||
}
|
||||
|
||||
// Check if transition is not active (first update, pose not changing or transition ended)
|
||||
bucket.TransitionPosition += context.DeltaTime;
|
||||
bucket.BlendPoseIndex = poseIndex;
|
||||
if (bucket.PreviousBlendPoseIndex == -1 || bucket.PreviousBlendPoseIndex == poseIndex || bucket.TransitionPosition >= blendDuration || blendDuration <= ANIM_GRAPH_BLEND_THRESHOLD)
|
||||
{
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
bucket.BlendPoseIndex = poseIndex;
|
||||
bucket.PreviousBlendPoseIndex = poseIndex;
|
||||
value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null);
|
||||
value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.BlendPoseIndex), Value::Null);
|
||||
break;
|
||||
}
|
||||
ASSERT(bucket.PreviousBlendPoseIndex >= 0 && bucket.PreviousBlendPoseIndex < poseCount);
|
||||
|
||||
// Blend two animations
|
||||
{
|
||||
const float alpha = bucket.TransitionPosition / blendDuration;
|
||||
const auto valueA = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.PreviousBlendPoseIndex), Value::Null);
|
||||
const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null);
|
||||
const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.BlendPoseIndex), Value::Null);
|
||||
value = Blend(node, valueA, valueB, alpha, mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,8 +167,8 @@ void AudioSource::Play()
|
||||
}
|
||||
else
|
||||
{
|
||||
// Source was nt properly added to the Audio Backend
|
||||
LOG(Warning, "Cannot play unitialized audio source.");
|
||||
// Source was not properly added to the Audio Backend
|
||||
LOG(Warning, "Cannot play uninitialized audio source.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +401,9 @@ void AudioSource::Update()
|
||||
_startingToPlay = false;
|
||||
}
|
||||
|
||||
if (Math::NearEqual(GetTime(), _startTime) && _isActuallyPlayingSth && _startingToPlay)
|
||||
ClipStarted();
|
||||
|
||||
if (!UseStreaming() && Math::NearEqual(GetTime(), 0.0f) && _isActuallyPlayingSth && !_startingToPlay)
|
||||
{
|
||||
int32 queuedBuffers;
|
||||
@@ -416,6 +419,7 @@ void AudioSource::Update()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
ClipFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,6 +490,7 @@ void AudioSource::Update()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
ClipFinished();
|
||||
}
|
||||
ASSERT(_streamingFirstChunk < clip->Buffers.Count());
|
||||
|
||||
|
||||
@@ -76,6 +76,16 @@ public:
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Audio Source\")")
|
||||
AssetReference<AudioClip> Clip;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the audio clip starts.
|
||||
/// </summary>
|
||||
API_EVENT() Action ClipStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the audio clip finishes.
|
||||
/// </summary>
|
||||
API_EVENT() Action ClipFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the velocity of the source. Determines pitch in relation to AudioListener's position. Only relevant for spatial (3D) sources.
|
||||
/// </summary>
|
||||
|
||||
@@ -90,9 +90,11 @@ void MaterialInstance::OnBaseParamsChanged()
|
||||
// Get the newest parameters
|
||||
baseParams->Clone(Params);
|
||||
|
||||
#if 0
|
||||
// Override all public parameters by default
|
||||
for (auto& param : Params)
|
||||
param.SetIsOverride(param.IsPublic());
|
||||
#endif
|
||||
|
||||
// Copy previous parameters values
|
||||
for (int32 i = 0; i < oldParams.Count(); i++)
|
||||
|
||||
@@ -36,7 +36,7 @@ bool Texture::Save(const StringView& path)
|
||||
|
||||
bool Texture::Save(const StringView& path, const InitData* customData)
|
||||
{
|
||||
if (OnCheckSave())
|
||||
if (OnCheckSave(path))
|
||||
return true;
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
|
||||
@@ -93,14 +93,15 @@ namespace
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void AddInput(MaterialLayer* layer, Meta11 meta, MaterialGraphBoxes box, const Guid& texture, const T& value, const T& defaultValue, const Float2& pos, ShaderGraphNode<>** outTextureNode = nullptr)
|
||||
void AddInput(MaterialLayer* layer, Meta11 meta, MaterialGraphBoxes box, const Guid& texture, const T& value, const T& defaultValue, const Float2& pos, ShaderGraphNode<>** outTextureNode = nullptr, uint8 channel = MAX_uint8)
|
||||
{
|
||||
auto textureNode = AddTextureNode(layer, texture);
|
||||
auto valueNode = AddValueNode<T>(layer, value, defaultValue);
|
||||
auto textureNodeBox = channel == MAX_uint8 ? 1 : channel + 2; // Color or specific channel (RGBA)
|
||||
if (textureNode && valueNode)
|
||||
{
|
||||
auto diffuseMultiply = AddMultiplyNode(layer);
|
||||
CONNECT(diffuseMultiply->Boxes[0], textureNode->Boxes[1]);
|
||||
CONNECT(diffuseMultiply->Boxes[0], textureNode->Boxes[textureNodeBox]);
|
||||
CONNECT(diffuseMultiply->Boxes[1], valueNode->Boxes[0]);
|
||||
CONNECT(layer->Root->Boxes[static_cast<int32>(box)], diffuseMultiply->Boxes[2]);
|
||||
SET_POS(valueNode, pos + Float2(-467.7404, 91.41332));
|
||||
@@ -109,7 +110,7 @@ namespace
|
||||
}
|
||||
else if (textureNode)
|
||||
{
|
||||
CONNECT(layer->Root->Boxes[static_cast<int32>(box)], textureNode->Boxes[1]);
|
||||
CONNECT(layer->Root->Boxes[static_cast<int32>(box)], textureNode->Boxes[textureNodeBox]);
|
||||
SET_POS(textureNode, pos + Float2(-293.5272f, -2.926111f));
|
||||
}
|
||||
else if (valueNode)
|
||||
@@ -178,8 +179,9 @@ CreateAssetResult CreateMaterial::Create(CreateAssetContext& context)
|
||||
// Opacity
|
||||
AddInput(layer, meta, MaterialGraphBoxes::Opacity, options.Opacity.Texture, options.Opacity.Value, 1.0f, Float2(0, 400));
|
||||
|
||||
// Opacity
|
||||
AddInput(layer, meta, MaterialGraphBoxes::Roughness, options.Roughness.Texture, options.Roughness.Value, 0.5f, Float2(200, 400));
|
||||
// Roughness + Metalness
|
||||
AddInput(layer, meta, MaterialGraphBoxes::Roughness, options.Roughness.Texture, options.Roughness.Value, 0.5f, Float2(200, 400), nullptr, options.Roughness.Channel);
|
||||
AddInput(layer, meta, MaterialGraphBoxes::Metalness, options.Metalness.Texture, options.Metalness.Value, 0.0f, Float2(200, 600), nullptr, options.Metalness.Channel);
|
||||
|
||||
// Normal
|
||||
auto normalMap = AddTextureNode(layer, options.Normals.Texture, true);
|
||||
|
||||
@@ -40,9 +40,17 @@ public:
|
||||
struct
|
||||
{
|
||||
float Value = 0.5f;
|
||||
uint8 Channel = 0;
|
||||
Guid Texture = Guid::Empty;
|
||||
} Roughness;
|
||||
|
||||
struct
|
||||
{
|
||||
float Value = 0.0f;
|
||||
uint8 Channel = 0;
|
||||
Guid Texture = Guid::Empty;
|
||||
} Metalness;
|
||||
|
||||
struct
|
||||
{
|
||||
Guid Texture = Guid::Empty;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "Engine/Utilities/RectPack.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
#include "AssetsImportingManager.h"
|
||||
|
||||
bool ImportModel::TryGetImportOptions(const StringView& path, Options& options)
|
||||
@@ -281,13 +282,19 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context)
|
||||
|
||||
// Import all of the objects recursive but use current model data to skip loading file again
|
||||
options.Cached = &cached;
|
||||
Function<bool(Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData)> splitImport = [&context, &autoImportOutput](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData)
|
||||
HashSet<String> objectNames;
|
||||
Function<bool(Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData)> splitImport = [&context, &autoImportOutput, &objectNames](Options& splitOptions, const StringView& objectName, String& outputPath, MeshData* meshData)
|
||||
{
|
||||
// Recursive importing of the split object
|
||||
String postFix = objectName;
|
||||
const int32 splitPos = postFix.FindLast(TEXT('|'));
|
||||
if (splitPos != -1)
|
||||
if (splitPos != -1 && splitPos + 1 < postFix.Length())
|
||||
postFix = postFix.Substring(splitPos + 1);
|
||||
EditorUtilities::ValidatePathChars(postFix); // Ensure name is valid path
|
||||
int32 duplicate = 0;
|
||||
String postFixPre = postFix;
|
||||
while (!objectNames.Add(postFix)) // Ensure name is unique
|
||||
postFix = String::Format(TEXT("{} {}"), postFixPre, duplicate++);
|
||||
// TODO: check for name collisions with material/texture assets
|
||||
outputPath = autoImportOutput / String(StringUtils::GetFileNameWithoutExtension(context.TargetAssetPath)) + TEXT(" ") + postFix + TEXT(".flax");
|
||||
splitOptions.SubAssetFolder = TEXT(" "); // Use the same folder as asset as they all are imported to the subdir for the prefab (see SubAssetFolder usage above)
|
||||
|
||||
@@ -63,9 +63,16 @@ void BoundingFrustum::SetMatrix(const Matrix& matrix)
|
||||
|
||||
Plane BoundingFrustum::GetPlane(int32 index) const
|
||||
{
|
||||
if (index > 5)
|
||||
return Plane();
|
||||
return _planes[index];
|
||||
switch (index)
|
||||
{
|
||||
case 0: return _pLeft;
|
||||
case 1: return _pRight;
|
||||
case 2: return _pTop;
|
||||
case 3: return _pBottom;
|
||||
case 4: return _pNear;
|
||||
case 5: return _pFar;
|
||||
default: return Plane();
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3 Get3PlanesInterPoint(const Plane& p1, const Plane& p2, const Plane& p3)
|
||||
|
||||
@@ -182,7 +182,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Returns one of the 6 planes related to this frustum.
|
||||
/// </summary>
|
||||
/// <param name="index">Plane index where 0 fro Left, 1 for Right, 2 for Top, 3 for Bottom, 4 for Near, 5 for Far</param>
|
||||
/// <param name="index">Plane index where 0 for Left, 1 for Right, 2 for Top, 3 for Bottom, 4 for Near, 5 for Far</param>
|
||||
/// <returns>The frustum plane.</returns>
|
||||
public Plane GetPlane(int index)
|
||||
{
|
||||
|
||||
@@ -148,13 +148,13 @@ public:
|
||||
Plane GetPlane(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
|
||||
/// Gets the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
|
||||
/// </summary>
|
||||
/// <param name="corners">The corners.</param>
|
||||
void GetCorners(Float3 corners[8]) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
|
||||
/// Gets the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
|
||||
/// </summary>
|
||||
/// <param name="corners">The corners.</param>
|
||||
void GetCorners(Double3 corners[8]) const;
|
||||
|
||||
@@ -435,6 +435,14 @@ void DebugCommands::InitAsync()
|
||||
AsyncTask = Task::StartNew(InitCommands);
|
||||
}
|
||||
|
||||
void DebugCommands::GetAllCommands(Array<StringView>& commands)
|
||||
{
|
||||
EnsureInited();
|
||||
ScopeLock lock(Locker);
|
||||
for (const auto& command : Commands)
|
||||
commands.Add(command.Name);
|
||||
}
|
||||
|
||||
DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command)
|
||||
{
|
||||
CommandFlags result = CommandFlags::None;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user