153 Commits

Author SHA1 Message Date
fe313773d1 Fix casting error in CPU profiler tab
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Cooker / Cook (Mac) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2024-12-19 20:09:43 +02:00
34346e4111 Force cursor to center of Game Window when tab handle is clicked 2024-12-19 20:09:43 +02:00
7cbe44c4e5 _update sdl 2024-12-19 20:09:43 +02:00
846f44b882 _sdl binary 2024-12-19 20:09:42 +02:00
68764580f2 Update SDL 2024-12-19 20:09:42 +02:00
Chandler Cox
cdf0e24c12 Fix rotation using SDL 2024-12-19 20:09:42 +02:00
ac7087c4ce Update SDL3 binaries 2024-12-19 20:09:41 +02:00
56e6e53d0c Update SDL to 3.1.3 preview 2024-12-19 20:09:41 +02:00
25b6605027 _window positioning that works on windows 2024-12-19 20:09:41 +02:00
c97f6f3b7e _lfsconfig 2024-12-19 20:09:40 +02:00
bc33d1a0f5 _noprog? 2024-12-19 20:09:40 +02:00
5e950833ff _works 0,0 primary screen, returned positions correct but visually wrong 2024-12-19 20:09:40 +02:00
9a04cee71f _works 2024-12-19 20:09:39 +02:00
b79c9402fd _wip parent-child positioning fixes 2024-12-19 20:09:39 +02:00
b0aed96d18 Fix Linux compilation without SDL 2024-12-19 20:09:39 +02:00
c003506906 Fix compilation 2024-12-19 20:09:38 +02:00
473dc4a1a0 Update SDL3 2024-12-19 20:09:38 +02:00
8d97e21b28 Fix compilation issues 2024-12-19 20:09:38 +02:00
ce932b1739 Fix windows not being hidden initially 2024-12-19 20:09:37 +02:00
506ad85cad Fix parent window position handling with popup/tooltip windows 2024-12-19 20:09:37 +02:00
e86ccefd3c Fix compilation errors in other platforms 2024-12-19 20:09:37 +02:00
71147cd4e9 Fix CI for Linux 2024-12-19 20:09:36 +02:00
d6fe1f0519 Prevent building with SDL in unsupported platforms 2024-12-19 20:09:36 +02:00
ed3d1ce366 Fallback to X11 message box implementation when SDL fails 2024-12-19 20:09:36 +02:00
37177edeaa Fix popup and context menus not working on Wayland 2024-12-19 20:09:35 +02:00
7981483a5b Hide warnings for unsupported SDL operations on Wayland 2024-12-19 20:09:35 +02:00
c3ca359b4b Log a warning for not implemented Wayland functionality 2024-12-19 20:09:35 +02:00
99cf7292aa Fix compilation in Linux 2024-12-19 20:09:34 +02:00
4c6f39d489 Enable warning sound in question dialogs 2024-12-19 20:09:34 +02:00
dcfc265433 Enable modern Windows dialog boxes 2024-12-19 20:09:34 +02:00
4a7f6c34f3 Implement relative mouse mode (raw input) for SDL platform 2024-12-19 20:09:33 +02:00
ba248a61bd Add flag for Window types 2024-12-19 20:09:33 +02:00
eacd23bdbb Enable native windowing system settings with SDL platform 2024-12-19 20:09:33 +02:00
62df728452 Add command-line switches to force X11 and Wayland SDL drivers 2024-12-19 20:09:32 +02:00
fe0d97aad1 Implement SDL platform, windowing and input handling 2024-12-19 20:09:32 +02:00
7f7d839f23 Refactor application window class name 2024-12-19 20:09:32 +02:00
c445bfd5d4 Move Window related enums to separate header file 2024-12-19 20:09:31 +02:00
62843476b7 Refactor Windows drag and drop implementation 2024-12-19 20:09:31 +02:00
7595c572d7 Refactor ScreenUtilities 2024-12-19 20:09:31 +02:00
13ae091273 Add more helper methods for managing Git repos 2024-12-19 20:09:31 +02:00
a9dccd2949 Fix centered window location on X11 2024-12-19 20:09:30 +02:00
f8dd3f23c6 Fix initial position of Tooltips 2024-12-19 20:09:30 +02:00
Wojtek Figat
0f847335c3 Fix typo on mac 2024-12-19 14:56:22 +01:00
Wojtek Figat
12f3f00f9f Fix log file spam and limit size to 1GB
#2980
2024-12-19 13:21:47 +01:00
Wojtek Figat
2175837f33 Add XCode version parsing 2024-12-19 13:07:59 +01:00
Wojtek Figat
1e2493d4ff Merge branch 'xxSeys1-GizmoImprovements' 2024-12-16 23:42:00 +01:00
Wojtek Figat
622de6ebcb Minor improvements to new gizmo changes and add new assets
#3088
2024-12-16 23:41:55 +01:00
Wojtek Figat
4de324f2b0 Merge branch 'GizmoImprovements' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-GizmoImprovements 2024-12-16 23:25:07 +01:00
Wojtek Figat
01bd482ba7 Merge branch 'Chikinsupu-Visject-CommentRenameImprovementsTwo' 2024-12-16 23:21:59 +01:00
Wojtek Figat
cedacdba3e Simplify Rename context option activity and use input option for F2 remapping in Editor
#3093
2024-12-16 23:21:45 +01:00
Wojtek Figat
06dcc7ba8e Merge branch 'Visject-CommentRenameImprovementsTwo' of https://github.com/Chikinsupu/FlaxEngine into Chikinsupu-Visject-CommentRenameImprovementsTwo 2024-12-16 23:17:56 +01:00
Wojtek Figat
ff0840d6dd Merge branch 'Tryibion-scroll-script-add' 2024-12-16 23:16:09 +01:00
Wojtek Figat
b81435505d Merge branch 'scroll-script-add' of https://github.com/Tryibion/FlaxEngine into Tryibion-scroll-script-add 2024-12-16 23:16:04 +01:00
Wojtek Figat
526010f523 Merge branch 'xxSeys1-ActorRefClickHighlight' 2024-12-16 23:15:01 +01:00
Wojtek Figat
222a614a2b Simplify and cleanup code
#3096
2024-12-16 23:14:50 +01:00
Wojtek Figat
bd4a042619 Merge branch 'ActorRefClickHighlight' of https://github.com/xxSeys1/FlaxEngine into xxSeys1-ActorRefClickHighlight 2024-12-16 23:09:09 +01:00
Wojtek Figat
3e9048af2f Merge branch 'Tryibion-ignore-specific-warnings' 2024-12-16 23:03:57 +01:00
Wojtek Figat
0a2341674f Merge branch 'ignore-specific-warnings' of https://github.com/Tryibion/FlaxEngine into Tryibion-ignore-specific-warnings 2024-12-16 23:03:53 +01:00
Wojtek Figat
bb844a7606 Merge branch 'Tryibion-show-hidden-game-win-play' 2024-12-16 23:02:19 +01:00
Wojtek Figat
ef60cc30ff Merge branch 'show-hidden-game-win-play' of https://github.com/Tryibion/FlaxEngine into Tryibion-show-hidden-game-win-play 2024-12-16 23:02:15 +01:00
Wojtek Figat
f8bb17a945 Merge branch 'GaryMcWhorter-feature/negative-model-import-reverse-winding' 2024-12-16 23:00:28 +01:00
Wojtek Figat
9ad1a9bfd0 Merge branch 'feature/negative-model-import-reverse-winding' of https://github.com/GaryMcWhorter/FlaxEngine into GaryMcWhorter-feature/negative-model-import-reverse-winding 2024-12-16 23:00:24 +01:00
Wojtek Figat
8ec279c1dd Merge branch 'Tryibion-fix-collection-value' 2024-12-16 22:58:07 +01:00
Wojtek Figat
bbed25653a Merge branch 'fix-collection-value' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-collection-value 2024-12-16 22:58:02 +01:00
Wojtek Figat
902d14c334 Merge branch 'Tryibion-fix-uieditor-null' 2024-12-16 22:54:49 +01:00
Wojtek Figat
fb44f0458c Merge branch 'fix-uieditor-null' of https://github.com/Tryibion/FlaxEngine into Tryibion-fix-uieditor-null 2024-12-16 22:54:43 +01:00
Wojtek Figat
fa8f50ead1 Fix crash on incorrect light shadow state after shadowmap resizing failure
#3106
2024-12-16 22:52:05 +01:00
Chandler Cox
f57f57423c Fix Collection value containers to use correct value assignment. 2024-12-13 14:31:44 -06:00
Gary M
c6b7077c2d Remove unnecessary comments 2024-12-12 22:23:21 -08:00
Gary M
21d1419e74 Added import option to reverse winding order 2024-12-12 22:19:55 -08:00
Chandler Cox
696b1c0d0c Fix comment. 2024-12-12 11:57:29 -06:00
Chandler Cox
7e0c0559dd Add the ability to ignore specific warnings during build. 2024-12-12 11:53:12 -06:00
Chandler Cox
a0216746b9 Add value null check in UiControlEditor to fix exception 2024-12-12 11:04:28 -06:00
Chandler Cox
76b84cca4e Scroll to script on add. 2024-12-12 10:40:08 -06:00
Saas
a780f513db Merge pull request #1 from Tryibion/actor-ref-high
Fix valid drop not resetting, add debounce timer to highlighting for showing on double click prevention
2024-12-12 16:38:46 +01:00
Chandler Cox
0906a561c4 Show game window if hidden when start play. 2024-12-11 20:55:49 -06:00
Chandler Cox
99e836b1fb Fix valid drop not resetting, add debounce timer to highlighting for showing on double click prevention 2024-12-11 20:43:52 -06:00
xxSeys1
1e61abdfef add highlight when actor reference is single clicked 2024-12-11 17:11:00 +01:00
Wojtek Figat
4a2f4a98ec Revert unwanted change from 24266b64da 2024-12-11 14:42:13 +01:00
Wojtek Figat
24266b64da Fix Continuous Deployment 2024-12-11 14:24:06 +01:00
Wojtek Figat
139a431614 Fix crash due to alive reference to old asset validator
#3081
2024-12-10 17:01:41 +01:00
Wojtek Figat
4f281b4732 Merge branch 'Tryibion-more-scripting-templates' 2024-12-10 16:48:18 +01:00
Wojtek Figat
4fe46dfc83 Merge branch 'more-scripting-templates' of https://github.com/Tryibion/FlaxEngine into Tryibion-more-scripting-templates 2024-12-10 16:48:11 +01:00
Wojtek Figat
04a3435200 Fix shaders parsing to skip comments in between special macros 2024-12-10 16:45:59 +01:00
Wojtek Figat
84c65b92d0 Revert macos change and use older Vulkan SDK that uses proper dmg image for mac 2024-12-10 15:18:48 +01:00
Wojtek Figat
5ffc06d6ee Fix Vulkan SDK install on Mac to use explicit image file name 2024-12-10 13:00:19 +01:00
Wojtek Figat
23015009b3 Update Vulkan SDK to 1.3.296.0 for Github Actions 2024-12-10 12:50:28 +01:00
Wojtek Figat
09414f9002 Add blend space drawing in Multi Blend 2D editor
#1980
2024-12-10 10:42:40 +01:00
Wojtek Figat
3e5cb09381 Fix vertical axis and blend points center in Multi Blend space editor
#1980
2024-12-09 22:48:02 +01:00
Wojtek Figat
eb5dfcd6bf Add support for multi-control editing
#1897
2024-12-09 18:47:32 +01:00
Wojtek Figat
6a082e9dd7 Merge branch 'Tryibion-multi-select-values' 2024-12-09 18:36:55 +01:00
Wojtek Figat
667661dd90 Merge branch 'multi-select-values' of https://github.com/Tryibion/FlaxEngine into Tryibion-multi-select-values 2024-12-09 18:34:39 +01:00
Wojtek Figat
3e344e789a Optimize terrain debug shape drawing by caching lines into a vertex buffer
#2841
2024-12-09 18:26:53 +01:00
Nils Hausfeld
e9243d0358 - Fixed regression of commit ec7840f36b that caused surface comments created with "// <text>" to throw an error 2024-12-07 19:15:14 +01:00
Nils Hausfeld
82453acf53 - Added the ability to rename selected visject comments by pressing F2
- Added rename option to context menu for visject comments
2024-12-07 18:42:00 +01:00
Wojtek Figat
0cf31395b5 Add support for Quaternion in GPU particles graph
#2901
2024-12-06 17:38:16 +01:00
Wojtek Figat
ce23c2efaf Add hiding unrelated properties in Material properties for Decal, PostFX or GUI materials
#2919
2024-12-06 17:12:10 +01:00
Wojtek Figat
b6d2a3683c Add ButtonAttribute to display methods in editor properties panel
#1917
2024-12-06 16:10:50 +01:00
Wojtek Figat
1088a71e69 Another iteration on cooking job 2024-12-06 13:45:37 +01:00
Wojtek Figat
31e870b086 Add C++ version and compiler path into VS Code project files
#3040
2024-12-06 13:13:25 +01:00
Wojtek Figat
2f239fe405 Add missing license file and build script for WinPixEventRuntime header module 2024-12-06 12:45:44 +01:00
Wojtek Figat
64a674f9bf Another iteration on cooking job 2024-12-06 11:57:12 +01:00
Wojtek Figat
e53b2b5736 Fix various compilation warnings 2024-12-06 11:37:25 +01:00
Wojtek Figat
a2087297e0 Add deploying game debug symbols for Windows builds 2024-12-06 11:37:01 +01:00
Wojtek Figat
ca15318ade Another iteration on cooking job 2024-12-06 11:27:04 +01:00
Wojtek Figat
47959ac901 Another iteration on cooking job 2024-12-06 10:41:40 +01:00
Wojtek Figat
56e6176e9d Another iteration on cooking job 2024-12-06 10:25:36 +01:00
Wojtek Figat
6af46bb764 Another iteration on cooking job 2024-12-06 09:57:33 +01:00
Wojtek Figat
6f00d664bb Another iteration on cooking job 2024-12-06 09:37:21 +01:00
Wojtek Figat
2441a35611 Another iteration on cooking job 2024-12-05 23:23:19 +01:00
xxSeys1
cee0b24b9f add options for gizmo brightness and opacity 2024-12-05 21:12:02 +01:00
xxSeys1
66cc3196e1 improve "Editor Options -> Visual" categories 2024-12-05 20:50:32 +01:00
Wojtek Figat
76518ac051 Another iteration on cooking job 2024-12-05 17:24:08 +01:00
Wojtek Figat
3aed3f1954 Another iteration on cooking job 2024-12-05 16:59:02 +01:00
Wojtek Figat
0ef1220846 Another iteration on cooking job 2024-12-05 15:29:24 +01:00
Wojtek Figat
987916cc1c Another iteration on cooking job 2024-12-05 12:23:32 +01:00
Wojtek Figat
4ac334acac Another iteration on cooking job 2024-12-05 11:21:51 +01:00
Wojtek Figat
2230f907fd Fix duplicated newlines on Github Action output 2024-12-05 10:33:30 +01:00
Wojtek Figat
6fabd0c26d Add iOS cooking test on Mac 2024-12-05 10:33:14 +01:00
Wojtek Figat
24a9ec5dd5 Fix Github macos image version to be explicit 2024-12-04 23:52:44 +01:00
Wojtek Figat
2328096200 Fix path to be inside root folder 2024-12-04 23:45:52 +01:00
Wojtek Figat
dcce8581c5 Another iteration on cooking job 2024-12-04 23:39:00 +01:00
Wojtek Figat
992d907b9c Add initial test for game cooking (use one of the Flax Samples project) 2024-12-04 18:46:31 +01:00
Wojtek Figat
93fefc9af3 Fix showing splash screen window when running Editor in headless mode 2024-12-04 18:45:41 +01:00
Wojtek Figat
e27d18ef87 Fix default build target preset 2024-12-04 18:44:09 +01:00
Wojtek Figat
848dbdf532 Add auto-exit command line to editor 2024-12-04 18:41:36 +01:00
Wojtek Figat
2f16694529 Fix some not important warning to be less verbose in some cases 2024-12-04 18:40:52 +01:00
Wojtek Figat
12d9d94138 Add support for curves in C++ scripting api
#2546
2024-12-04 13:30:56 +01:00
Wojtek Figat
c01824cd09 Add support for using template type inside MarshalAs tag value 2024-12-03 13:33:16 +01:00
Wojtek Figat
10caaf4fe9 Fix parsing scripting type inheritance with generics 2024-12-02 23:36:11 +01:00
Wojtek Figat
81737083a0 Add support for using API_TYPEDEF macro on using typedefs 2024-12-02 23:35:39 +01:00
Wojtek Figat
57628c3d5f Refactor Bezier splines drawing and editing to property evaluate value and match curve evaluation
#3051
2024-12-02 19:10:28 +01:00
Wojtek Figat
0a4a431f74 Fix exception regression 2024-12-01 21:55:19 +01:00
Wojtek Figat
7e2d45012e Fix scroll panel regression from 6f0a2c0288 2024-12-01 11:31:44 +01:00
Wojtek Figat
f0f631a48b Add splitter bar for resizing curve editor inside properties panel 2024-11-30 23:44:23 +01:00
Wojtek Figat
0295e1ca90 Add adding keyframes to curve on double-click 2024-11-30 23:28:07 +01:00
Wojtek Figat
8ef7f7cb1a Add F key shortcut to show whole curve in editor view 2024-11-30 23:21:23 +01:00
Wojtek Figat
44d96ad100 Fix curve editor panning to be stable 2024-11-30 23:20:57 +01:00
Wojtek Figat
77184c7b52 Fix crash when curve zoom was too high 2024-11-29 14:34:07 +01:00
Wojtek Figat
9b43f2f03a Add zooming in curve editor relative to the mouse cursor and adapt to curve size 2024-11-29 14:33:46 +01:00
Wojtek Figat
8ee011c7f5 Fix curve editor zoom to be relative to the curve size 2024-11-28 18:28:16 +01:00
Wojtek Figat
0509fe10be Add displaying whole curve in properties panel upon show 2024-11-28 18:27:45 +01:00
Wojtek Figat
6f0a2c0288 Fix curve editor showing whole curve in a view 2024-11-28 18:27:09 +01:00
Wojtek Figat
e2ed618056 Fix regression from 07aafea5af 2024-11-27 23:28:22 +01:00
Wojtek Figat
dc91e55cec Fix scroll bars when using negative content area (eg. curve editor) 2024-11-27 20:22:38 +01:00
Wojtek Figat
47919bd434 Add shift zoom to curve for a Y-axis only 2024-11-27 13:54:56 +01:00
Chandler Cox
c7fd1999db Remove c# template menus. 2024-11-26 20:22:36 -06:00
Chandler Cox
0335086df8 Add more scripting templates. 2024-11-22 17:07:10 -06:00
Chandler Cox
0cc3026b07 Small code style fix. 2024-08-21 08:53:19 -05:00
Chandler Cox
8802cfa32a Simplify code. 2024-08-20 20:58:07 -05:00
Chandler Cox
ce06809970 Small code style fix. 2024-08-20 20:43:07 -05:00
Chandler Cox
252de16c13 Fix bugs with quaternion editor. 2024-08-20 20:40:50 -05:00
Chandler Cox
43952fdc31 Add support for Vector3, Float3, Double3, and Quaternion mutliselect value changing. 2024-08-20 20:33:36 -05:00
307 changed files with 81322 additions and 1924 deletions

View File

@@ -3,7 +3,7 @@ description: Downloads and installs Vulkan SDK.
inputs:
vulkan-version:
description: 'Vulkan SDK release version (e.g. 1.2.198.1).'
default: '1.2.198.1'
default: '1.3.290.0'
required: false
runs:
using: "composite"

87
.github/data/Build Settings.json vendored Normal file
View File

@@ -0,0 +1,87 @@
{
"ID": "2364031e4e327637c1ad88b415fa756e",
"TypeName": "FlaxEditor.Content.Settings.BuildSettings",
"EngineBuild": 6605,
"Data": {
"OutputName": "${PROJECT_NAME}",
"MaxAssetsPerPackage": 4096,
"MaxPackageSizeMB": 1024,
"ContentKey": 0,
"ForDistribution": false,
"SkipPackaging": true,
"AdditionalAssets": null,
"AdditionalScenes": null,
"AdditionalAssetFolders": null,
"ShadersNoOptimize": false,
"ShadersGenerateDebugData": false,
"SkipDefaultFonts": false,
"SkipDotnetPackaging": false,
"SkipUnusedDotnetLibsPackaging": true,
"Presets": [
{
"Name": "Development",
"Targets": [
{
"Name": "Windows",
"Output": "Output\\Windows",
"Platform": 2,
"Mode": 1,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
},
{
"Name": "Linux",
"Output": "Output\\Linux",
"Platform": 6,
"Mode": 1,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
},
{
"Name": "Mac",
"Output": "Output\\Mac",
"Platform": 13,
"Mode": 1,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
},
{
"Name": "Android",
"Output": "Output\\Android",
"Platform": 9,
"Mode": 1,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
},
{
"Name": "iOS",
"Output": "Output\\iOS",
"Platform": 14,
"Mode": 1,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
}
]
},
{
"Name": "Release",
"Targets": [
{
"Name": "Windows",
"Output": "Output\\Windows",
"Platform": 2,
"Mode": 2,
"CustomDefines": null,
"PreBuildAction": null,
"PostBuildAction": null
}
]
}
]
}
}

5
.github/data/Cook.ps1 vendored Normal file
View File

@@ -0,0 +1,5 @@
Write-Output "Cooking Game"
Start-Process -filepath "Binaries\Editor\Win64\Development\FlaxEditor.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows"'
Write-Output "Testing Game"
Start-Process -filepath "FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null'

11
.github/data/ExitOnEsc.cs vendored Normal file
View File

@@ -0,0 +1,11 @@
using FlaxEngine;
public class ExitOnEsc : Script
{
/// <inheritdoc />
public override void OnUpdate()
{
// Exit as soon as game starts update loaded level
Engine.RequestExit();
}
}

View File

@@ -10,7 +10,7 @@ jobs:
# Game
game-windows:
name: Game (iOS, Release ARM64)
runs-on: "macos-latest"
runs-on: "macos-14"
steps:
- name: Checkout repo
uses: actions/checkout@v3

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
@@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET

View File

@@ -10,7 +10,7 @@ jobs:
# Editor
editor-mac:
name: Editor (Mac, Development ARM64)
runs-on: "macos-latest"
runs-on: "macos-14"
steps:
- name: Checkout repo
uses: actions/checkout@v3
@@ -35,7 +35,7 @@ jobs:
# Game
game-mac:
name: Game (Mac, Release ARM64)
runs-on: "macos-latest"
runs-on: "macos-14"
steps:
- name: Checkout repo
uses: actions/checkout@v3

View File

@@ -140,7 +140,7 @@ jobs:
# Mac
package-mac-editor:
name: Editor (Mac)
runs-on: "macos-latest"
runs-on: "macos-14"
steps:
- name: Checkout repo
uses: actions/checkout@v3
@@ -168,7 +168,7 @@ jobs:
path: Output/FlaxEditorMac.zip
package-mac-game:
name: Game (Mac)
runs-on: "macos-latest"
runs-on: "macos-14"
steps:
- name: Checkout repo
uses: actions/checkout@v3

48
.github/workflows/cooking.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Cooker
on: [push, pull_request]
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: false
jobs:
# Cook on Mac
cook-mac:
name: Cook (Mac)
runs-on: "macos-14"
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x
- name: Setup .NET Workload
run: |
dotnet workload install ios
- name: Print .NET info
run: |
dotnet --info
dotnet workload --info
- name: Checkout LFS
run: |
git lfs version
git lfs pull
- name: Get Flax Samples
uses: actions/checkout@v3
with:
fetch-depth: 1
repository: FlaxEngine/FlaxSamples
path: FlaxSamples
- name: Patch Files
run: |
cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game
cp ".github/data/Build Settings.json" "FlaxSamples/MaterialsFeaturesTour/Content/Settings"
- name: Build Editor
run: |
./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor
- name: Cook Game (iOS)
run: |
./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS"

View File

@@ -28,7 +28,7 @@ jobs:
git lfs pull
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
- name: Build
run: |
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8

View File

@@ -1,4 +1,4 @@
# Redirect to our own Git LFS server
[lfs]
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
#url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
locksverify = false

BIN
Content/Editor/Gizmo/Material.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialAxisLocked.flax (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialAxisX.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialAxisY.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialAxisZ.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialSphere.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -0,0 +1,19 @@
%copyright%#include "%filename%.h"
%class%::%class%(const SpawnParams& params)
: Actor(params)
{
}
void %class%::OnEnable()
{
Actor::OnEnable();
// Here you can add code that needs to be called when script is enabled (eg. register for events)
}
void %class%::OnDisable()
{
Actor::OnDisable();
// Here you can add code that needs to be called when script is disabled (eg. unregister from events)
}

View File

@@ -0,0 +1,39 @@
%copyright%using System;
using System.Collections.Generic;
using FlaxEngine;
namespace %namespace%;
/// <summary>
/// %class% Actor.
/// </summary>
public class %class% : Actor
{
/// <inheritdoc/>
public override void OnBeginPlay()
{
base.OnBeginPlay();
// Here you can add code that needs to be called when Actor added to the game. This is called during edit time as well.
}
/// <inheritdoc/>
public override void OnEndPlay()
{
base.OnEndPlay();
// Here you can add code that needs to be called when Actor removed to the game. This is called during edit time as well.
}
/// <inheritdoc/>
public override void OnEnable()
{
base.OnEnable();
// Here you can add code that needs to be called when Actor is enabled (eg. register for events). This is called during edit time as well.
}
/// <inheritdoc/>
public override void OnDisable()
{
base.OnDisable();
// Here you can add code that needs to be called when Actor is disabled (eg. unregister from events). This is called during edit time as well.
}
}

View File

@@ -0,0 +1,13 @@
%copyright%#pragma once
#include "Engine/Level/Actor.h"
API_CLASS() class %module%%class% : public Actor
{
API_AUTO_SERIALIZATION();
DECLARE_SCENE_OBJECT(%class%);
// [Actor]
void OnEnable() override;
void OnDisable() override;
};

View File

@@ -0,0 +1,13 @@
%copyright%using System;
using System.Collections.Generic;
using FlaxEngine;
namespace %namespace%;
/// <summary>
/// %class% class.
/// </summary>
public class %class%
{
}

View File

@@ -0,0 +1,13 @@
%copyright%using System;
using System.Collections.Generic;
using FlaxEngine;
namespace %namespace%;
/// <summary>
/// %class% interface.
/// </summary>
public interface %class%
{
}

View File

@@ -0,0 +1,13 @@
%copyright%using System;
using System.Collections.Generic;
using FlaxEngine;
namespace %namespace%;
/// <summary>
/// %class% struct.
/// </summary>
public struct %class%
{
}

View File

@@ -4,7 +4,7 @@
"Major": 1,
"Minor": 9,
"Revision": 0,
"Build": 6605
"Build": 6606
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",
@@ -13,6 +13,7 @@
"Configuration": {
"UseCSharp": true,
"UseLargeWorlds": false,
"UseDotNet": true
"UseDotNet": true,
"UseSDL": true
}
}

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.IO;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.Utilities;

View File

@@ -112,6 +112,12 @@ namespace FlaxEditor.Content
throw new TargetException("Missing Visual Script asset.");
_type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value);
}
/// <inheritdoc />
public object Invoke(object obj, object[] parameters)
{
throw new NotSupportedException();
}
}
sealed class VisualScriptMethodInfo : IScriptMemberInfo
@@ -240,6 +246,14 @@ namespace FlaxEditor.Content
{
throw new NotSupportedException();
}
/// <inheritdoc />
public object Invoke(object obj, object[] parameters)
{
if (!_type.Asset)
throw new TargetException("Missing Visual Script asset.");
return _type.Asset.InvokeMethod(_index, obj, parameters);
}
}
/// <summary>

View File

@@ -73,7 +73,7 @@ namespace FlaxEditor.Content
}
/// <summary>
/// Context proxy object for C# script files.
/// Context proxy object for C# Script files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# Script")]
@@ -89,6 +89,23 @@ namespace FlaxEditor.Content
}
}
/// <summary>
/// Context proxy object for C# Actor files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# Actor")]
public class CSharpActorProxy : CSharpProxy
{
/// <inheritdoc />
public override string Name => "C# Actor";
/// <inheritdoc />
protected override void GetTemplatePath(out string path)
{
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.cs");
}
}
/// <summary>
/// Context proxy object for empty C# files.
/// </summary>
@@ -105,4 +122,55 @@ namespace FlaxEditor.Content
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/CSharpEmptyTemplate.cs");
}
}
/// <summary>
/// Context proxy object for empty C# class files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# Class")]
public class CSharpEmptyClassProxy : CSharpProxy
{
/// <inheritdoc />
public override string Name => "C# Class";
/// <inheritdoc />
protected override void GetTemplatePath(out string path)
{
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyClassTemplate.cs");
}
}
/// <summary>
/// Context proxy object for empty C# struct files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# Struct")]
public class CSharpEmptyStructProxy : CSharpProxy
{
/// <inheritdoc />
public override string Name => "C# Struct";
/// <inheritdoc />
protected override void GetTemplatePath(out string path)
{
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyStructTemplate.cs");
}
}
/// <summary>
/// Context proxy object for empty C# interface files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# Interface")]
public class CSharpEmptyInterfaceProxy : CSharpProxy
{
/// <inheritdoc />
public override string Name => "C# Interface";
/// <inheritdoc />
protected override void GetTemplatePath(out string path)
{
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/EmptyInterfaceTemplate.cs");
}
}
}

View File

@@ -100,6 +100,24 @@ namespace FlaxEditor.Content
sourceTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cpp");
}
}
/// <summary>
/// Context proxy object for C++ Actor files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.CppProxy" />
[ContentContextMenu("New/C++/C++ Actor")]
public class CppActorProxy : CppProxy
{
/// <inheritdoc />
public override string Name => "C++ Actor";
/// <inheritdoc />
protected override void GetTemplatePaths(out string headerTemplate, out string sourceTemplate)
{
headerTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.h");
sourceTemplate = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ActorTemplate.cpp");
}
}
/// <summary>
/// Context proxy object for C++ Json Asset files.

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using FlaxEditor.CustomEditors.GUI;
@@ -127,12 +128,41 @@ namespace FlaxEditor.CustomEditors
_isSetBlocked = true;
Initialize(layout);
ShowButtons();
Refresh();
_isSetBlocked = false;
CurrentCustomEditor = prev;
}
private void ShowButtons()
{
var values = Values;
if (values == null || values.HasDifferentTypes)
return;
var type = TypeUtils.GetObjectType(values[0]);
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var method in methods)
{
if (!method.HasAttribute(typeof(ButtonAttribute)) ||
method.ParametersCount != 0)
continue;
var attribute = method.GetAttribute<ButtonAttribute>();
var text = string.IsNullOrEmpty(attribute.Text) ? Utilities.Utils.GetPropertyNameUI(method.Name) : attribute.Text;
var tooltip = string.IsNullOrEmpty(attribute.Tooltip) ? Editor.Instance.CodeDocs.GetTooltip(method) : attribute.Tooltip;
var button = _layout.Button(text, tooltip);
button.Button.Tag = method;
button.Button.ButtonClicked += OnButtonClicked;
}
}
private void OnButtonClicked(Button button)
{
var method = (ScriptMemberInfo)button.Tag;
var obj = method.IsStatic ? null : Values[0];
method.Invoke(obj);
}
internal static CustomEditor CurrentCustomEditor;
internal void OnChildCreated(CustomEditor child)
@@ -271,8 +301,16 @@ namespace FlaxEditor.CustomEditors
_valueToSet = null;
// Assign value
for (int i = 0; i < _values.Count; i++)
_values[i] = val;
if (val is IList l && l.Count == _values.Count && _values.Count > 1)
{
for (int i = 0; i < _values.Count; i++)
_values[i] = l[i];
}
else
{
for (int i = 0; i < _values.Count; i++)
_values[i] = val;
}
}
finally
{

View File

@@ -2,6 +2,7 @@
using FlaxEditor.GUI;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Dedicated
{
@@ -11,7 +12,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
class BezierCurveObjectEditor<T> : CustomEditor where T : struct
{
private bool _isSetting;
private int _firstTimeShow;
private BezierCurveEditor<T> _curve;
private Splitter _splitter;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -20,6 +23,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
_curve = item.CustomControl;
_curve.Height = 120.0f;
_curve.Edited += OnCurveEdited;
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
_splitter = new Splitter
{
Moved = OnSplitterMoved,
Parent = _curve,
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
};
}
private void OnCurveEdited()
@@ -32,6 +43,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
_isSetting = false;
}
private void OnSplitterMoved(Float2 location)
{
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
}
/// <inheritdoc />
public override void Refresh()
{
@@ -44,12 +60,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
_curve.SetKeyframes(value.Keyframes);
_isSetting = false;
}
if (_firstTimeShow-- > 0)
_curve.ShowWholeCurve();
}
/// <inheritdoc />
protected override void Deinitialize()
{
_curve = null;
_splitter = null;
base.Deinitialize();
}
@@ -111,7 +130,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
class LinearCurveObjectEditor<T> : CustomEditor where T : struct
{
private bool _isSetting;
private int _firstTimeShow;
private LinearCurveEditor<T> _curve;
private Splitter _splitter;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -120,6 +141,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
_curve = item.CustomControl;
_curve.Height = 120.0f;
_curve.Edited += OnCurveEdited;
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
_splitter = new Splitter
{
Moved = OnSplitterMoved,
Parent = _curve,
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
};
}
private void OnCurveEdited()
@@ -132,6 +161,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
_isSetting = false;
}
private void OnSplitterMoved(Float2 location)
{
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
}
/// <inheritdoc />
public override void Refresh()
{
@@ -144,12 +178,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
_curve.SetKeyframes(value.Keyframes);
_isSetting = false;
}
if (_firstTimeShow-- > 0)
_curve.ShowWholeCurve();
}
/// <inheritdoc />
protected override void Deinitialize()
{
_curve = null;
_splitter = null;
base.Deinitialize();
}

View File

@@ -424,6 +424,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
presenter.Undo.AddAction(multiAction);
presenter.Control.Focus();
// Scroll to bottom of script control where a new script is added.
if (presenter.Panel.Parent is Panel p && Editor.Instance.Options.Options.Interface.ScrollToScriptOnAdd)
{
var loc = ScriptsEditor.Layout.Control.BottomLeft;
p.ScrollViewTo(loc);
}
}
}
}

View File

@@ -407,20 +407,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
public class UIControlControlEditor : GenericEditor
{
private Type _cachedType;
private ScriptType[] _valueTypes;
private bool _anchorDropDownClosed = true;
private Button _pivotRelativeButton;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
{
_cachedType = null;
if (HasDifferentTypes)
{
// TODO: support stable editing multiple different control types (via generic way or for transform-only)
return;
}
// Set control type button
var space = layout.Space(20);
var buttonText = "Set Type";
@@ -445,12 +438,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
}
// Add control type helper label
if (!Values.HasDifferentTypes)
{
var type = Values[0].GetType();
_cachedType = type;
var label = layout.AddPropertyItem("Type", "The type of the created control.");
label.Label(type.FullName);
label.Label(Values[0].GetType().FullName);
}
_valueTypes = Values.ValuesTypes;
// Show control properties
base.Initialize(layout);
@@ -720,17 +713,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// <inheritdoc />
public override void Refresh()
{
if (_cachedType != null)
// Automatic layout rebuild if control type gets changed
if (_valueTypes != null &&
!Values.HasNull &&
!Utils.ArraysEqual(_valueTypes, Values.ValuesTypes))
{
// Automatic layout rebuild if control type gets changed
var type = Values.HasNull ? null : Values[0].GetType();
if (type != _cachedType)
{
RebuildLayout();
return;
}
RebuildLayout();
return;
}
// Refresh anchors
// Refresh anchors
if (Values != null && Values[0] != null)
{
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
if (xEq != _cachedXEq || yEq != _cachedYEq)
{

View File

@@ -292,5 +292,17 @@ namespace FlaxEditor.CustomEditors.Editors
_isRefreshing = false;
}
}
/// <inheritdoc />
protected override void Deinitialize()
{
if (_validator != null)
{
_validator.OnDestroy();
_validator = null;
}
base.Deinitialize();
}
}
}

View File

@@ -898,9 +898,13 @@ namespace FlaxEditor.CustomEditors.Editors
base.Draw();
}
/// <inheritdoc />
public override void OnDestroy()
{
_pickerValidator.OnDestroy();
_pickerValidator = null;
base.OnDestroy();
}
private bool ValidateActors(ActorNode node)

View File

@@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Drag;
using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.GUI;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -23,6 +24,7 @@ namespace FlaxEditor.CustomEditors.Editors
public class FlaxObjectRefPickerControl : Control
{
private ScriptType _type;
private ActorTreeNode _linkedTreeNode;
private Object _value;
private string _valueName;
private bool _supportsPickDropDown;
@@ -300,7 +302,43 @@ namespace FlaxEditor.CustomEditors.Editors
// Picker dropdown menu
if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location))
{
ShowDropDownMenu();
return true;
}
if (button == MouseButton.Left)
{
_isMouseDown = false;
// Highlight actor or script reference
if (!_hasValidDragOver && !IsDragOver)
{
Actor actor = _value as Actor;
if (actor == null && _value is Script script)
actor = script.Actor;
if (actor != null)
{
if (_linkedTreeNode != null && _linkedTreeNode.Actor == actor)
{
_linkedTreeNode.ExpandAllParents();
_linkedTreeNode.StartHighlight();
}
else
{
_linkedTreeNode = Editor.Instance.Scene.GetActorNode(actor).TreeNode;
_linkedTreeNode.ExpandAllParents();
Editor.Instance.Windows.SceneWin.SceneTreePanel.ScrollViewTo(_linkedTreeNode, true);
_linkedTreeNode.StartHighlight();
}
return true;
}
}
// Reset valid drag over if still true at this point
if (_hasValidDragOver)
_hasValidDragOver = false;
}
return base.OnMouseUp(location, button);
}
@@ -326,6 +364,12 @@ namespace FlaxEditor.CustomEditors.Editors
// Check if has object selected
if (_value != null)
{
if (_linkedTreeNode != null)
{
_linkedTreeNode.StopHighlight();
_linkedTreeNode = null;
}
// Select object
if (_value is Actor actor)
Editor.Instance.SceneEditing.Select(actor);
@@ -469,6 +513,7 @@ namespace FlaxEditor.CustomEditors.Editors
_value = null;
_type = ScriptType.Null;
_valueName = null;
_linkedTreeNode = null;
base.OnDestroy();
}

View File

@@ -33,6 +33,15 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc />
public override DisplayStyle Style => DisplayStyle.Inline;
/// <summary>
/// Whether to use the average for sliding different values.
/// </summary>
public virtual bool AllowSlidingForDifferentValues => true;
private ValueChanged _valueChanged;
private float _defaultSlidingSpeed;
private bool _slidingEnded = false;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -46,18 +55,31 @@ namespace FlaxEditor.CustomEditors.Editors
XElement = grid.FloatValue();
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
XElement.ValueBox.ValueChanged += OnValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken;
XElement.ValueBox.ValueChanged += OnXValueChanged;
XElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
YElement = grid.FloatValue();
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
YElement.ValueBox.ValueChanged += OnValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken;
YElement.ValueBox.ValueChanged += OnYValueChanged;
YElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
ZElement = grid.FloatValue();
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
ZElement.ValueBox.ValueChanged += OnValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken;
ZElement.ValueBox.ValueChanged += OnZValueChanged;
ZElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
if (LinkedLabel != null)
{
@@ -69,33 +91,118 @@ namespace FlaxEditor.CustomEditors.Editors
};
}
}
private void OnValueChanged()
private void OnXValueChanged()
{
if (IsSetBlocked)
return;
_valueChanged = ValueChanged.X;
OnValueChanged();
}
private void OnYValueChanged()
{
if (IsSetBlocked)
return;
_valueChanged = ValueChanged.Y;
OnValueChanged();
}
private void OnZValueChanged()
{
if (IsSetBlocked)
return;
_valueChanged = ValueChanged.Z;
OnValueChanged();
}
private void OnValueChanged()
{
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
var token = isSliding ? this : null;
var useCachedAngles = isSliding && token == _cachedToken;
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
x = Mathf.UnwindDegrees(x);
y = Mathf.UnwindDegrees(y);
z = Mathf.UnwindDegrees(z);
if (!useCachedAngles)
if (HasDifferentValues && Values.Count > 1)
{
_cachedAngles = new Float3(x, y, z);
var xValue = XElement.ValueBox.Value;
var yValue = YElement.ValueBox.Value;
var zValue = ZElement.ValueBox.Value;
xValue = Mathf.UnwindDegrees(xValue);
yValue = Mathf.UnwindDegrees(yValue);
zValue = Mathf.UnwindDegrees(zValue);
var value = new Float3(xValue, yValue, zValue);
_cachedToken = token;
var newObjects = new object[Values.Count];
// Handle Sliding
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
{
Float3 average = Float3.Zero;
for (int i = 0; i < Values.Count; i++)
{
var v = (Quaternion)Values[0];
var euler = v.EulerAngles;
average += euler;
}
average /= Values.Count;
var newValue = value - average;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
var val = (Quaternion)v;
Quaternion.Euler(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0, out Quaternion qVal);
v = val * qVal;
newObjects[i] = v;
}
// Capture last sliding value
if (_slidingEnded)
_slidingEnded = false;
}
else
{
for (int i = 0; i < Values.Count; i++)
{
object v = Values[i];
var val = (Quaternion)v;
var euler = val.EulerAngles;
Quaternion.Euler(_valueChanged == ValueChanged.X ? xValue : euler.X, _valueChanged == ValueChanged.Y ? yValue : euler.Y, _valueChanged == ValueChanged.Z ? zValue : euler.Z, out Quaternion qVal);
v = val * qVal;
newObjects[i] = v;
}
}
SetValue(newObjects, token);
}
else
{
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
_cachedToken = token;
x = Mathf.UnwindDegrees(x);
y = Mathf.UnwindDegrees(y);
z = Mathf.UnwindDegrees(z);
Quaternion.Euler(x, y, z, out Quaternion value);
SetValue(value, token);
if (!useCachedAngles)
{
_cachedAngles = new Float3(x, y, z);
}
_cachedToken = token;
Quaternion.Euler(x, y, z, out Quaternion value);
SetValue(value, token);
}
}
/// <inheritdoc />
@@ -112,7 +219,73 @@ namespace FlaxEditor.CustomEditors.Editors
if (HasDifferentValues)
{
// TODO: support different values for ValueBox<T>
// Get which values are different
bool xDifferent = false;
bool yDifferent = false;
bool zDifferent = false;
Float3 cachedFirstValue = Float3.Zero;
Float3 average = Float3.Zero;
for (int i = 0; i < Values.Count; i++)
{
var value = (Quaternion)Values[i];
var euler = value.EulerAngles;
average += euler;
if (i == 0)
{
cachedFirstValue = euler;
continue;
}
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
xDifferent = true;
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
yDifferent = true;
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
zDifferent = true;
}
average /= Values.Count;
if (!xDifferent)
{
XElement.ValueBox.Value = cachedFirstValue.X;
}
else
{
if (AllowSlidingForDifferentValues)
XElement.ValueBox.Value = average.X;
else
XElement.ValueBox.SlideSpeed = 0;
XElement.ValueBox.Text = "---";
}
if (!yDifferent)
{
YElement.ValueBox.Value = cachedFirstValue.Y;
}
else
{
if (AllowSlidingForDifferentValues)
YElement.ValueBox.Value = average.Y;
else
YElement.ValueBox.SlideSpeed = 0;
YElement.ValueBox.Text = "---";
}
if (!zDifferent)
{
ZElement.ValueBox.Value = cachedFirstValue.Z;
}
else
{
if (AllowSlidingForDifferentValues)
ZElement.ValueBox.Value = average.Z;
else
ZElement.ValueBox.SlideSpeed = 0;
ZElement.ValueBox.Text = "---";
}
}
else
{
@@ -121,6 +294,13 @@ namespace FlaxEditor.CustomEditors.Editors
XElement.ValueBox.Value = euler.X;
YElement.ValueBox.Value = euler.Y;
ZElement.ValueBox.Value = euler.Z;
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
}
}
}

View File

@@ -7,6 +7,27 @@ using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Editors
{
/// <summary>
/// The value changed by custom Vector3 editors.
/// </summary>
public enum ValueChanged
{
/// <summary>
/// X value changed.
/// </summary>
X = 0,
/// <summary>
/// Y value changed.
/// </summary>
Y = 1,
/// <summary>
/// Z value changed.
/// </summary>
Z = 2
}
/// <summary>
/// Default implementation of the inspector used to edit Vector3 value type properties.
/// </summary>
@@ -49,14 +70,14 @@ namespace FlaxEditor.CustomEditors.Editors
/// </summary>
public bool LinkValues = false;
private enum ValueChanged
{
X = 0,
Y = 1,
Z = 2
}
/// <summary>
/// Whether to use the average for sliding different values.
/// </summary>
public virtual bool AllowSlidingForDifferentValues => true;
private ValueChanged _valueChanged;
private float _defaultSlidingSpeed;
private bool _slidingEnded = false;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -83,19 +104,32 @@ namespace FlaxEditor.CustomEditors.Editors
XElement.SetLimits(limit);
XElement.SetCategory(category);
XElement.ValueBox.ValueChanged += OnXValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken;
XElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
YElement = grid.FloatValue();
YElement.SetLimits(limit);
YElement.SetCategory(category);
YElement.ValueBox.ValueChanged += OnYValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken;
YElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
ZElement = grid.FloatValue();
ZElement.SetLimits(limit);
ZElement.SetCategory(category);
ZElement.ValueBox.ValueChanged += OnZValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken;
ZElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
if (LinkedLabel != null)
{
@@ -118,8 +152,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (IsSetBlocked)
return;
if (LinkValues)
_valueChanged = ValueChanged.X;
_valueChanged = ValueChanged.X;
OnValueChanged();
}
@@ -127,8 +160,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (IsSetBlocked)
return;
if (LinkValues)
_valueChanged = ValueChanged.Y;
_valueChanged = ValueChanged.Y;
OnValueChanged();
}
@@ -136,8 +168,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (IsSetBlocked)
return;
if (LinkValues)
_valueChanged = ValueChanged.Z;
_valueChanged = ValueChanged.Z;
OnValueChanged();
}
@@ -191,14 +222,79 @@ namespace FlaxEditor.CustomEditors.Editors
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
var token = isSliding ? this : null;
var value = new Float3(xValue, yValue, zValue);
object v = Values[0];
if (v is Vector3)
v = (Vector3)value;
else if (v is Float3)
v = (Float3)value;
else if (v is Double3)
v = (Double3)value;
SetValue(v, token);
if (HasDifferentValues && Values.Count > 1)
{
var newObjects = new object[Values.Count];
// Handle Sliding
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
{
// TODO: handle linked values
Float3 average = Float3.Zero;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
var castedValue = Float3.Zero;
if (v is Vector3 asVector3)
castedValue = asVector3;
else if (v is Float3 asFloat3)
castedValue = asFloat3;
else if (v is Double3 asDouble3)
castedValue = asDouble3;
average += castedValue;
}
average /= Values.Count;
var newValue = value - average;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
newObjects[i] = v;
}
// Capture last sliding value
if (_slidingEnded)
_slidingEnded = false;
}
else
{
// TODO: handle linked values
for (int i = 0; i < Values.Count; i++)
{
object v = Values[i];
if (v is Vector3 asVector3)
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
else if (v is Float3 asFloat3)
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
else if (v is Double3 asDouble3)
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
newObjects[i] = v;
}
}
SetValue(newObjects, token);
}
else
{
object v = Values[0];
if (v is Vector3)
v = (Vector3)value;
else if (v is Float3)
v = (Float3)value;
else if (v is Double3)
v = (Double3)value;
SetValue(v, token);
}
}
private float GetRatio(float value, float initialValue)
@@ -218,7 +314,79 @@ namespace FlaxEditor.CustomEditors.Editors
if (HasDifferentValues)
{
// TODO: support different values for ValueBox<T>
// Get which values are different
bool xDifferent = false;
bool yDifferent = false;
bool zDifferent = false;
Float3 cachedFirstValue = Float3.Zero;
Float3 average = Float3.Zero;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
var value = Float3.Zero;
if (v is Vector3 asVector3)
value = asVector3;
else if (v is Float3 asFloat3)
value = asFloat3;
else if (v is Double3 asDouble3)
value = asDouble3;
average += value;
if (i == 0)
{
cachedFirstValue = value;
continue;
}
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
xDifferent = true;
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
yDifferent = true;
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
zDifferent = true;
}
average /= Values.Count;
if (!xDifferent)
{
XElement.ValueBox.Value = cachedFirstValue.X;
}
else
{
if (AllowSlidingForDifferentValues)
XElement.ValueBox.Value = average.X;
else
XElement.ValueBox.SlideSpeed = 0;
XElement.ValueBox.Text = "---";
}
if (!yDifferent)
{
YElement.ValueBox.Value = cachedFirstValue.Y;
}
else
{
if (AllowSlidingForDifferentValues)
YElement.ValueBox.Value = average.Y;
else
YElement.ValueBox.SlideSpeed = 0;
YElement.ValueBox.Text = "---";
}
if (!zDifferent)
{
ZElement.ValueBox.Value = cachedFirstValue.Z;
}
else
{
if (AllowSlidingForDifferentValues)
ZElement.ValueBox.Value = average.Z;
else
ZElement.ValueBox.SlideSpeed = 0;
ZElement.ValueBox.Text = "---";
}
}
else
{
@@ -232,6 +400,13 @@ namespace FlaxEditor.CustomEditors.Editors
XElement.ValueBox.Value = value.X;
YElement.ValueBox.Value = value.Y;
ZElement.ValueBox.Value = value.Z;
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
}
}
}
@@ -259,6 +434,15 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc />
public override DisplayStyle Style => DisplayStyle.Inline;
/// <summary>
/// Whether to use the average for sliding different values.
/// </summary>
public virtual bool AllowSlidingForDifferentValues => true;
private ValueChanged _valueChanged;
private float _defaultSlidingSpeed;
private bool _slidingEnded = false;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -284,20 +468,33 @@ namespace FlaxEditor.CustomEditors.Editors
XElement = grid.DoubleValue();
XElement.SetLimits(limit);
XElement.SetCategory(category);
XElement.ValueBox.ValueChanged += OnValueChanged;
XElement.ValueBox.SlidingEnd += ClearToken;
XElement.ValueBox.ValueChanged += OnXValueChanged;
XElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
YElement = grid.DoubleValue();
YElement.SetLimits(limit);
YElement.SetCategory(category);
YElement.ValueBox.ValueChanged += OnValueChanged;
YElement.ValueBox.SlidingEnd += ClearToken;
YElement.ValueBox.ValueChanged += OnYValueChanged;
YElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
ZElement = grid.DoubleValue();
ZElement.SetLimits(limit);
ZElement.SetCategory(category);
ZElement.ValueBox.ValueChanged += OnValueChanged;
ZElement.ValueBox.SlidingEnd += ClearToken;
ZElement.ValueBox.ValueChanged += OnZValueChanged;
ZElement.ValueBox.SlidingEnd += () =>
{
_slidingEnded = true;
ClearToken();
};
if (LinkedLabel != null)
{
@@ -316,22 +513,115 @@ namespace FlaxEditor.CustomEditors.Editors
}
}
private void OnXValueChanged()
{
if (IsSetBlocked)
return;
_valueChanged = ValueChanged.X;
OnValueChanged();
}
private void OnYValueChanged()
{
if (IsSetBlocked)
return;
_valueChanged = ValueChanged.Y;
OnValueChanged();
}
private void OnZValueChanged()
{
if (IsSetBlocked)
return;
_valueChanged = ValueChanged.Z;
OnValueChanged();
}
private void OnValueChanged()
{
if (IsSetBlocked || Values == null)
return;
var xValue = XElement.ValueBox.Value;
var yValue = YElement.ValueBox.Value;
var zValue = ZElement.ValueBox.Value;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
var token = isSliding ? this : null;
var value = new Double3(XElement.ValueBox.Value, YElement.ValueBox.Value, ZElement.ValueBox.Value);
object v = Values[0];
if (v is Vector3)
v = (Vector3)value;
else if (v is Float3)
v = (Float3)value;
else if (v is Double3)
v = (Double3)value;
SetValue(v, token);
var value = new Double3(xValue, yValue, zValue);
if (HasDifferentValues && Values.Count > 1)
{
var newObjects = new object[Values.Count];
// Handle Sliding
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
{
// TODO: handle linked values
Double3 average = Double3.Zero;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
var castedValue = Double3.Zero;
if (v is Vector3 asVector3)
castedValue = asVector3;
else if (v is Float3 asFloat3)
castedValue = asFloat3;
else if (v is Double3 asDouble3)
castedValue = asDouble3;
average += castedValue;
}
average /= Values.Count;
var newValue = value - average;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? (float)newValue.X : 0, _valueChanged == ValueChanged.Y ? (float)newValue.Y : 0, _valueChanged == ValueChanged.Z ? (float)newValue.Z : 0);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
newObjects[i] = v;
}
// Capture last sliding value
if (_slidingEnded)
_slidingEnded = false;
}
else
{
// TODO: handle linked values
for (int i = 0; i < Values.Count; i++)
{
object v = Values[i];
if (v is Vector3 asVector3)
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
else if (v is Float3 asFloat3)
v = new Float3(_valueChanged == ValueChanged.X ? (float)xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? (float)yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? (float)zValue : asFloat3.Z);
else if (v is Double3 asDouble3)
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
newObjects[i] = v;
}
}
SetValue(newObjects, token);
}
else
{
object v = Values[0];
if (v is Vector3)
v = (Vector3)value;
else if (v is Float3)
v = (Float3)value;
else if (v is Double3)
v = (Double3)value;
SetValue(v, token);
}
}
/// <inheritdoc />
@@ -341,7 +631,79 @@ namespace FlaxEditor.CustomEditors.Editors
if (HasDifferentValues)
{
// TODO: support different values for ValueBox<T>
// Get which values are different
bool xDifferent = false;
bool yDifferent = false;
bool zDifferent = false;
Double3 cachedFirstValue = Double3.Zero;
Double3 average = Double3.Zero;
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
var value = Double3.Zero;
if (v is Vector3 asVector3)
value = asVector3;
else if (v is Float3 asFloat3)
value = asFloat3;
else if (v is Double3 asDouble3)
value = asDouble3;
average += value;
if (i == 0)
{
cachedFirstValue = value;
continue;
}
if (!Mathd.NearEqual(cachedFirstValue.X, value.X))
xDifferent = true;
if (!Mathd.NearEqual(cachedFirstValue.Y, value.Y))
yDifferent = true;
if (!Mathd.NearEqual(cachedFirstValue.Z, value.Z))
zDifferent = true;
}
average /= Values.Count;
if (!xDifferent)
{
XElement.ValueBox.Value = cachedFirstValue.X;
}
else
{
if (AllowSlidingForDifferentValues)
XElement.ValueBox.Value = average.X;
else
XElement.ValueBox.SlideSpeed = 0;
XElement.ValueBox.Text = "---";
}
if (!yDifferent)
{
YElement.ValueBox.Value = cachedFirstValue.Y;
}
else
{
if (AllowSlidingForDifferentValues)
YElement.ValueBox.Value = average.Y;
else
YElement.ValueBox.SlideSpeed = 0;
YElement.ValueBox.Text = "---";
}
if (!zDifferent)
{
ZElement.ValueBox.Value = cachedFirstValue.Z;
}
else
{
if (AllowSlidingForDifferentValues)
ZElement.ValueBox.Value = average.Z;
else
ZElement.ValueBox.SlideSpeed = 0;
ZElement.ValueBox.Text = "---";
}
}
else
{
@@ -355,6 +717,19 @@ namespace FlaxEditor.CustomEditors.Editors
XElement.ValueBox.Value = value.X;
YElement.ValueBox.Value = value.Y;
ZElement.ValueBox.Value = value.Z;
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
{
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
}
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
{
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
}
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
{
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
}
}
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
@@ -385,10 +386,21 @@ namespace FlaxEditor.CustomEditors
if (instanceValues.Count != Count)
throw new ArgumentException();
for (int i = 0; i < Count; i++)
if (value is IList l && l.Count == Count && Count > 1)
{
Info.SetValue(instanceValues[i], value);
this[i] = Info.GetValue(instanceValues[i]);
for (int i = 0; i < Count; i++)
{
Info.SetValue(instanceValues[i], l[i]);
this[i] = Info.GetValue(instanceValues[i]);
}
}
else
{
for (int i = 0; i < Count; i++)
{
Info.SetValue(instanceValues[i], value);
this[i] = Info.GetValue(instanceValues[i]);
}
}
}

View File

@@ -673,6 +673,7 @@ bool Editor::Init()
Managed = New<ManagedEditor>();
// Show splash screen
if (!CommandLine::Options.Headless.IsTrue())
{
PROFILE_CPU_NAMED("Splash");
if (EditorImpl::Splash == nullptr)

View File

@@ -49,9 +49,9 @@ namespace FlaxEditor
}
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode;
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit;
private string _projectToOpen;
private float _lastAutoSaveTimer;
private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f;
private Button _saveNowButton;
private Button _cancelSaveButton;
private bool _autoSaveNow;
@@ -258,10 +258,11 @@ namespace FlaxEditor
Instance = this;
}
internal void Init(bool isHeadless, bool skipCompile, bool newProject, Guid startupScene)
internal void Init(StartupFlags flags, Guid startupScene)
{
Log("Setting up C# Editor...");
_isHeadlessMode = isHeadless;
_isHeadlessMode = flags.HasFlag(StartupFlags.Headless);
_autoExit = flags.HasFlag(StartupFlags.Exit);
_startupSceneCmdLine = startupScene;
Profiler.BeginEvent("Projects");
@@ -297,11 +298,11 @@ namespace FlaxEditor
StateMachine = new EditorStateMachine(this);
Undo = new EditorUndo(this);
if (newProject)
if (flags.HasFlag(StartupFlags.NewProject))
InitProject();
EnsureState<LoadingState>();
Log("Editor init");
if (isHeadless)
if (_isHeadlessMode)
Log("Running in headless mode");
// Note: we don't sort modules before Init (optimized)
@@ -357,7 +358,7 @@ namespace FlaxEditor
InitializationStart?.Invoke();
// Start Editor initialization ending phrase (will wait for scripts compilation result)
StateMachine.LoadingState.StartInitEnding(skipCompile);
StateMachine.LoadingState.StartInitEnding(flags.HasFlag(StartupFlags.SkipCompile));
}
internal void RegisterModule(EditorModule module)
@@ -486,6 +487,15 @@ namespace FlaxEditor
{
StateMachine.Update();
UpdateAutoSave();
if (_autoExit && StateMachine.CurrentState == StateMachine.EditingSceneState)
{
_autoExitTimeout -= Time.UnscaledGameTime;
if (_autoExitTimeout < 0.0f)
{
Log("Auto exit");
Engine.RequestExit(0);
}
}
if (!StateMachine.IsPlayMode)
{

View File

@@ -59,7 +59,7 @@ namespace FlaxEditor.GUI
/// <summary>
/// Initializes a new instance of the <see cref="AssetPicker"/> class.
/// </summary>
/// <param name="assetType">The assets types that this picker accepts.</param>
/// <param name="assetType">The asset types that this picker accepts.</param>
/// <param name="location">The control location.</param>
public AssetPicker(ScriptType assetType, Float2 location)
: base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize))

View File

@@ -1,7 +1,10 @@
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
#define USE_IS_FOREGROUND
#else
#endif
#if PLATFORM_SDL
#define USE_SDL_WORKAROUNDS
#endif
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
@@ -111,7 +114,7 @@ namespace FlaxEditor.GUI.ContextMenu
}
/// <summary>
/// Shows the empty menu popup o na screen.
/// Shows the empty menu popup on a screen.
/// </summary>
/// <param name="control">The target control.</param>
/// <param name="area">The target control area to cover.</param>
@@ -215,21 +218,26 @@ namespace FlaxEditor.GUI.ContextMenu
desc.AllowMaximize = false;
desc.AllowDragAndDrop = false;
desc.IsTopmost = true;
desc.IsRegularWindow = false;
desc.Type = WindowType.Popup;
desc.Parent = parentWin.Window;
desc.HasSizingFrame = false;
OnWindowCreating(ref desc);
_window = Platform.CreateWindow(ref desc);
_window.GotFocus += OnWindowGotFocus;
_window.LostFocus += OnWindowLostFocus;
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
parentWin.Window.MouseDown += OnWindowMouseDown;
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
#endif
// Attach to the window
_parentCM = parent as ContextMenuBase;
Parent = _window.GUI;
// Show
Visible = true;
if (_window == null)
return;
_window.Show();
PerformLayout();
_previouslyFocused = parentWin.FocusedControl;
@@ -378,6 +386,17 @@ namespace FlaxEditor.GUI.ContextMenu
}
}
#if USE_SDL_WORKAROUNDS
private void OnWindowGotFocus()
{
}
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
{
// The user clicked outside the popup window
Hide();
}
#else
private void OnWindowGotFocus()
{
var child = _childCM;
@@ -391,6 +410,7 @@ namespace FlaxEditor.GUI.ContextMenu
});
}
}
#endif
private void OnWindowLostFocus()
{
@@ -489,7 +509,12 @@ namespace FlaxEditor.GUI.ContextMenu
// Let root context menu to check if none of the popup windows
if (_parentCM == null && !IsForeground)
{
#if USE_SDL_WORKAROUNDS
if (!IsMouseOver)
Hide();
#else
Hide();
#endif
}
}
#endif

View File

@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
@@ -29,6 +28,7 @@ namespace FlaxEditor.GUI
internal Float2 _mousePos = Float2.Minimum;
internal bool _isMovingSelection;
internal bool _isMovingTangent;
internal bool _movedView;
internal bool _movedKeyframes;
private TangentPoint _movingTangent;
private Float2 _movingSelectionStart;
@@ -190,31 +190,28 @@ namespace FlaxEditor.GUI
// Moving view
if (_rightMouseDown)
{
var delta = location - _movingViewLastPos;
var movingViewPos = Parent.PointToParent(PointToParent(location));
var delta = movingViewPos - _movingViewLastPos;
if (_editor.CustomViewPanning != null)
delta = _editor.CustomViewPanning(delta);
delta *= GetUseModeMask(_editor.EnablePanning) * _editor.ViewScale;
delta *= GetUseModeMask(_editor.EnablePanning);
if (delta.LengthSquared > 0.01f)
{
_editor._mainPanel.ViewOffset += delta;
_movingViewLastPos = location;
_movingViewLastPos = movingViewPos;
_movedView = true;
if (_editor.CustomViewPanning != null)
{
Cursor = CursorType.SizeAll;
if (Cursor == CursorType.Default)
Cursor = CursorType.SizeAll;
}
else
{
switch (_editor.EnablePanning)
{
case UseMode.Vertical:
Cursor = CursorType.SizeNS;
break;
case UseMode.Horizontal:
Cursor = CursorType.SizeWE;
break;
case UseMode.On:
Cursor = CursorType.SizeAll;
break;
case UseMode.Vertical: Cursor = CursorType.SizeNS; break;
case UseMode.Horizontal: Cursor = CursorType.SizeWE; break;
case UseMode.On: Cursor = CursorType.SizeAll; break;
}
}
}
@@ -234,11 +231,10 @@ namespace FlaxEditor.GUI
else if (_isMovingTangent)
{
var viewRect = _editor._mainPanel.GetClientArea();
var direction = _movingTangent.IsIn ? -1.0f : 1.0f;
var k = _editor.GetKeyframe(_movingTangent.Index);
var kv = _editor.GetKeyframeValue(k);
var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component);
_movingTangent.TangentValue = direction * (PointToKeyframes(location, ref viewRect).Y - value);
_movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value;
_editor.UpdateTangents();
Cursor = CursorType.SizeNS;
_movedKeyframes = true;
@@ -299,7 +295,8 @@ namespace FlaxEditor.GUI
{
_rightMouseDown = true;
_rightMouseDownPos = location;
_movingViewLastPos = location;
_movedView = false;
_movingViewLastPos = Parent.PointToParent(PointToParent(location));
}
// Check if any node is under the mouse
@@ -444,7 +441,7 @@ namespace FlaxEditor.GUI
Cursor = CursorType.Default;
// Check if no move has been made at all
if (Float2.Distance(ref location, ref _rightMouseDownPos) < 2.0f)
if (!_movedView)
{
var selectionCount = _editor.SelectionCount;
var point = GetChildAt(location) as KeyframePoint;
@@ -512,6 +509,27 @@ namespace FlaxEditor.GUI
return true;
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (base.OnMouseDoubleClick(location, button))
return true;
// Add keyframe on double click
var child = GetChildAt(location);
if (child is not KeyframePoint &&
child is not TangentPoint &&
_editor.KeyframesCount < _editor.MaxKeyframes)
{
var viewRect = _editor._mainPanel.GetClientArea();
var pos = PointToKeyframes(location, ref viewRect);
_editor.AddKeyframe(pos);
return true;
}
return false;
}
/// <inheritdoc />
public override bool OnMouseWheel(Float2 location, float delta)
{
@@ -519,10 +537,29 @@ namespace FlaxEditor.GUI
return true;
// Zoom in/out
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && RootWindow.GetKey(KeyboardKeys.Control))
var zoom = RootWindow.GetKey(KeyboardKeys.Control);
var zoomAlt = RootWindow.GetKey(KeyboardKeys.Shift);
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && (zoom || zoomAlt))
{
// TODO: preserve the view center point for easier zooming
_editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * (delta * 0.1f);
// Cache mouse location in curve-space
var viewRect = _editor._mainPanel.GetClientArea();
var locationInKeyframes = PointToKeyframes(location, ref viewRect);
var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
// Scale relative to the curve size
var scale = new Float2(delta * 0.1f);
_editor._mainPanel.GetDesireClientArea(out var mainPanelArea);
var curveScale = mainPanelArea.Size / _editor._contents.Size;
scale *= curveScale;
if (zoomAlt)
scale.X = 0; // Scale Y axis only
scale *= GetUseModeMask(_editor.EnableZoom); // Mask scale depending on allowed usage
_editor.ViewScale += scale;
// Zoom towards the mouse position
var locationInEditorAfter = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
var locationInEditorDelta = locationInEditorAfter - locationInEditorBefore;
_editor.ViewOffset -= locationInEditorDelta;
return true;
}

View File

@@ -671,8 +671,22 @@ namespace FlaxEditor.GUI
/// <inheritdoc />
public override void ShowWholeCurve()
{
ViewScale = ApplyUseModeMask(EnableZoom, _mainPanel.Size / _contents.Size, ViewScale);
ViewOffset = ApplyUseModeMask(EnablePanning, -_mainPanel.ControlsBounds.Location, ViewOffset);
_mainPanel.GetDesireClientArea(out var mainPanelArea);
ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale);
Float2 minPos = Float2.Maximum;
foreach (var point in _points)
{
var pos = point.PointToParent(point.Location);
Float2.Min(ref minPos, ref pos, out minPos);
}
var minPosPoint = _contents.PointToParent(ref minPos);
var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0);
scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll);
if (_mainPanel.HScrollBar != null)
_mainPanel.HScrollBar.TargetValue = scroll.X;
if (_mainPanel.VScrollBar != null)
_mainPanel.VScrollBar.TargetValue = scroll.Y;
UpdateKeyframes();
}
@@ -923,6 +937,11 @@ namespace FlaxEditor.GUI
KeyframesEditorUtils.Paste(this);
return true;
}
else if (options.FocusSelection.Process(this))
{
ShowWholeCurve();
return true;
}
return false;
}
@@ -1384,9 +1403,7 @@ namespace FlaxEditor.GUI
// Calculate bounds
var bounds = _points[0].Bounds;
for (var i = 1; i < _points.Count; i++)
{
bounds = Rectangle.Union(bounds, _points[i].Bounds);
}
// Adjust contents bounds to fill the curve area
if (EnablePanning != UseMode.Off || !ShowCollapsed)
@@ -1632,6 +1649,7 @@ namespace FlaxEditor.GUI
var o = _keyframes[p.Index - 1];
var oValue = Accessor.GetCurveValue(ref o.Value, p.Component);
var slope = (value - oValue) / (k.Time - o.Time);
slope = -slope;
Accessor.SetCurveValue(slope, ref k.TangentIn, p.Component);
}
@@ -2116,9 +2134,7 @@ namespace FlaxEditor.GUI
// Calculate bounds
var bounds = _points[0].Bounds;
for (int i = 1; i < _points.Count; i++)
{
bounds = Rectangle.Union(bounds, _points[i].Bounds);
}
// Adjust contents bounds to fill the curve area
if (EnablePanning != UseMode.Off || !ShowCollapsed)
@@ -2184,12 +2200,12 @@ namespace FlaxEditor.GUI
var tangent = t.TangentValue;
var direction = t.IsIn ? -1.0f : 1.0f;
var offset = 30.0f * direction;
var offset = 30.0f;
var location = GetKeyframePoint(ref k, selectedComponent);
t.Size = KeyframesSize / ViewScale;
t.Location = new Float2
(
location.X * UnitsPerSecond - t.Width * 0.5f + offset,
location.X * UnitsPerSecond - t.Width * 0.5f + offset * direction,
location.Y * -UnitsPerSecond - t.Height * 0.5f + curveContentAreaBounds.Height - offset * tangent
);
@@ -2265,14 +2281,13 @@ namespace FlaxEditor.GUI
var startTangent = Accessor.GetCurveValue(ref startK.TangentOut, component);
var endTangent = Accessor.GetCurveValue(ref endK.TangentIn, component);
var offset = (end.X - start.X) * 0.5f;
var tangentScale = (endK.Time - startK.Time) / 3.0f;
var p1 = PointFromKeyframes(start, ref viewRect);
var p2 = PointFromKeyframes(start + new Float2(offset, startTangent * offset), ref viewRect);
var p3 = PointFromKeyframes(end - new Float2(offset, endTangent * offset), ref viewRect);
var p2 = PointFromKeyframes(start + new Float2(0, startTangent * tangentScale), ref viewRect);
var p3 = PointFromKeyframes(end + new Float2(0, endTangent * tangentScale), ref viewRect);
var p4 = PointFromKeyframes(end, ref viewRect);
Render2D.DrawBezier(p1, p2, p3, p4, color);
Render2D.DrawSpline(p1, p2, p3, p4, color);
}
}
}

View File

@@ -67,9 +67,7 @@ namespace FlaxEditor.GUI.Docking
Proxy.Window.MouseUp += OnMouseUp;
Proxy.Window.MouseMove += OnMouseMove;
Proxy.Window.LostFocus += OnLostFocus;
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
_toMove.Window.Window.MouseUp += OnMouseUp; // Intercept the drag release mouse event from source window
// Update window GUI
Proxy.Window.GUI.PerformLayout();
@@ -77,13 +75,16 @@ namespace FlaxEditor.GUI.Docking
// Update rectangles
UpdateRects();
// Hide base window
window.Hide();
// Enable hit window presentation
Proxy.Window.RenderingEnabled = true;
Proxy.Window.Show();
Proxy.Window.Focus();
// Hide base window
window.Hide();
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
}
/// <summary>
@@ -101,6 +102,8 @@ namespace FlaxEditor.GUI.Docking
Proxy.Window.MouseUp -= OnMouseUp;
Proxy.Window.MouseMove -= OnMouseMove;
Proxy.Window.LostFocus -= OnLostFocus;
if (_toMove?.Window?.Window)
_toMove.Window.Window.MouseUp -= OnMouseUp;
// Hide the proxy
Proxy.Hide();
@@ -438,7 +441,7 @@ namespace FlaxEditor.GUI.Docking
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.Type = WindowType.Utility;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ShowAfterFirstPaint = false;
@@ -470,7 +473,7 @@ namespace FlaxEditor.GUI.Docking
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.Type = WindowType.Utility;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ActivateWhenFirstShown = false;

View File

@@ -82,7 +82,7 @@ namespace FlaxEditor.GUI.Docking
settings.AllowMaximize = true;
settings.AllowDragAndDrop = true;
settings.IsTopmost = false;
settings.IsRegularWindow = true;
settings.Type = WindowType.Regular;
settings.HasSizingFrame = true;
settings.ShowAfterFirstPaint = false;
settings.ShowInTaskbar = true;

View File

@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseDown(location, button);
}
#if !PLATFORM_SDL
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{
@@ -292,6 +293,36 @@ namespace FlaxEditor.GUI.Input
base.OnMouseMove(location);
}
#else
/// <inheritdoc />
public override void OnMouseMoveRelative(Float2 mouseMotion)
{
var location = Root.TrackingMouseOffset;
if (_isSliding)
{
// Update sliding
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
return;
}
// Update cursor type so user knows they can slide value
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
{
Cursor = CursorType.SizeWE;
_cursorChanged = true;
}
else if (_cursorChanged && !_isSliding)
{
Cursor = CursorType.Default;
_cursorChanged = false;
}
base.OnMouseMoveRelative(mouseMotion);
}
#endif
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{

View File

@@ -12,7 +12,7 @@ namespace FlaxEditor.GUI
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public sealed class MainMenu : ContainerControl
{
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
private bool _useCustomWindowSystem;
private Image _icon;
private Label _title;
@@ -67,7 +67,7 @@ namespace FlaxEditor.GUI
AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
if (_useCustomWindowSystem)
{
@@ -166,7 +166,7 @@ namespace FlaxEditor.GUI
}
}
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc />
public override void Update(float deltaTime)
{
@@ -291,7 +291,7 @@ namespace FlaxEditor.GUI
if (base.OnMouseDoubleClick(location, button))
return true;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
var child = GetChildAtRecursive(location);
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
{
@@ -321,7 +321,7 @@ namespace FlaxEditor.GUI
{
float x = 0;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem)
{
// Icon
@@ -349,7 +349,7 @@ namespace FlaxEditor.GUI
}
}
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem)
{
// Buttons
@@ -367,7 +367,7 @@ namespace FlaxEditor.GUI
#endif
}
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc />
public override void OnDestroy()
{

View File

@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI
Text = text;
var style = Style.Current;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
{
BackgroundColorMouseOver = style.BackgroundHighlighted;

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI
{
sealed class Splitter : Control
{
private bool _clicked;
public Action<Float2> Moved;
public const float DefaultHeight = 5.0f;
public override void Draw()
{
var style = Style.Current;
if (IsMouseOver || _clicked)
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
}
public override void OnEndMouseCapture()
{
base.OnEndMouseCapture();
_clicked = false;
}
public override void Defocus()
{
base.Defocus();
_clicked = false;
}
public override void OnMouseEnter(Float2 location)
{
base.OnMouseEnter(location);
Cursor = CursorType.SizeNS;
}
public override void OnMouseLeave()
{
Cursor = CursorType.Default;
base.OnMouseLeave();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
_clicked = true;
Focus();
StartMouseCapture();
return true;
}
return base.OnMouseDown(location, button);
}
public override void OnMouseMove(Float2 location)
{
base.OnMouseMove(location);
if (_clicked)
{
Moved(location);
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _clicked)
{
_clicked = false;
EndMouseCapture();
return true;
}
return base.OnMouseUp(location, button);
}
}
}

View File

@@ -230,7 +230,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
continue;
// Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 2);
var lStep = _tickSteps[l];
var lNextStep = _tickSteps[l + 1];
int startTick = Mathf.FloorToInt(min / lStep);

View File

@@ -19,87 +19,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// <seealso cref="MemberTrack" />
public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext
{
private sealed class Splitter : Control
{
private bool _clicked;
internal CurvePropertyTrackBase _track;
public override void Draw()
{
var style = Style.Current;
if (IsMouseOver || _clicked)
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
}
public override void OnEndMouseCapture()
{
base.OnEndMouseCapture();
_clicked = false;
}
public override void Defocus()
{
base.Defocus();
_clicked = false;
}
public override void OnMouseEnter(Float2 location)
{
base.OnMouseEnter(location);
Cursor = CursorType.SizeNS;
}
public override void OnMouseLeave()
{
Cursor = CursorType.Default;
base.OnMouseLeave();
}
public override bool OnMouseDown(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
_clicked = true;
Focus();
StartMouseCapture();
return true;
}
return base.OnMouseDown(location, button);
}
public override void OnMouseMove(Float2 location)
{
base.OnMouseMove(location);
if (_clicked)
{
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
if (!Mathf.NearEqual(height, _track._expandedHeight))
{
_track.Height = _track._expandedHeight = height;
_track.Timeline.ArrangeTracks();
}
}
}
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (button == MouseButton.Left && _clicked)
{
_clicked = false;
EndMouseCapture();
return true;
}
return base.OnMouseUp(location, button);
}
}
private byte[] _curveEditingStartData;
private float _expandedHeight = 120.0f;
private Splitter _splitter;
@@ -251,12 +170,21 @@ namespace FlaxEditor.GUI.Timeline.Tracks
{
_splitter = new Splitter
{
_track = this,
Moved = OnSplitterMoved,
Parent = Curve,
};
}
var splitterHeight = 5.0f;
_splitter.Bounds = new Rectangle(0, Curve.Height - splitterHeight, Curve.Width, splitterHeight);
_splitter.Bounds = new Rectangle(0, Curve.Height - Splitter.DefaultHeight, Curve.Width, Splitter.DefaultHeight);
}
}
private void OnSplitterMoved(Float2 location)
{
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
if (!Mathf.NearEqual(height, _expandedHeight))
{
Height = _expandedHeight = height;
Timeline.ArrangeTracks();
}
}

View File

@@ -23,10 +23,19 @@ namespace FlaxEditor.GUI.Tree
/// </summary>
public const float DefaultNodeOffsetY = 0;
private const float _targetHighlightScale = 1.25f;
private const float _highlightScaleAnimDuration = 0.85f;
private Tree _tree;
private bool _opened, _canChangeOrder;
private float _animationProgress, _cachedHeight;
private bool _isHightlighted;
private float _targetHighlightTimeSec;
private float _currentHighlightTimeSec;
// Used to prevent showing highlight on double mouse click
private float _debounceHighlightTime;
private float _highlightScale;
private bool _mouseOverArrow, _mouseOverHeader;
private float _xOffset, _textWidth;
private float _headerHeight = 16.0f;
@@ -605,9 +614,47 @@ namespace FlaxEditor.GUI.Tree
}
}
/// <summary>
/// Adds a box around the text to highlight the node.
/// </summary>
/// <param name="durationSec">The duration of the highlight in seconds.</param>
public void StartHighlight(float durationSec = 3)
{
_isHightlighted = true;
_targetHighlightTimeSec = durationSec;
_currentHighlightTimeSec = 0;
_debounceHighlightTime = 0;
_highlightScale = 2f;
}
/// <summary>
/// Stops any current highlight.
/// </summary>
public void StopHighlight()
{
_isHightlighted = false;
_targetHighlightTimeSec = 0;
_currentHighlightTimeSec = 0;
_debounceHighlightTime = 0;
}
/// <inheritdoc />
public override void Update(float deltaTime)
{
// Highlight animations
if (_isHightlighted)
{
_debounceHighlightTime += deltaTime;
_currentHighlightTimeSec += deltaTime;
// In the first second, animate the highlight to shrink into it's resting position
if (_currentHighlightTimeSec < _highlightScaleAnimDuration)
_highlightScale = Mathf.Lerp(_highlightScale, _targetHighlightScale, _currentHighlightTimeSec);
if (_currentHighlightTimeSec >= _targetHighlightTimeSec)
_isHightlighted = false;
}
// Drop/down animation
if (_animationProgress < 1.0f)
{
@@ -676,6 +723,18 @@ namespace FlaxEditor.GUI.Tree
textRect.Width -= 18.0f;
}
float textWidth = TextFont.GetFont().MeasureText(_text).X;
Rectangle trueTextRect = textRect;
trueTextRect.Width = textWidth;
trueTextRect.Scale(_highlightScale);
if (_isHightlighted && _debounceHighlightTime > 0.1f)
{
Color highlightBackgroundColor = Editor.Instance.Options.Options.Visual.HighlightColor;
highlightBackgroundColor = highlightBackgroundColor.AlphaMultiplied(0.3f);
Render2D.FillRectangle(trueTextRect, highlightBackgroundColor);
}
// Draw text
Color textColor = CacheTextColor();
Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center);
@@ -730,6 +789,12 @@ namespace FlaxEditor.GUI.Tree
}
}
if (_isHightlighted && _debounceHighlightTime > 0.1f)
{
// Draw highlights
Render2D.DrawRectangle(trueTextRect, Editor.Instance.Options.Options.Visual.HighlightColor, 3);
}
// Base
if (_opened)
{

View File

@@ -1,6 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using FlaxEditor.Options;
using FlaxEditor.SceneGraph;
using FlaxEngine;
using System;
namespace FlaxEditor.Gizmo
{
@@ -18,8 +21,13 @@ 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 void InitDrawing()
{
// Axis Models
@@ -34,6 +42,7 @@ 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
@@ -50,6 +59,25 @@ namespace FlaxEditor.Gizmo
{
Platform.Fatal("Failed to load transform gizmo resources.");
}
// Setup editor options
OnEditorOptionsChanged(Editor.Instance.Options.Options);
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
}
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);
float opacity = options.Visual.TransformGizmoOpacity;
_materialAxisX.SetParameterValue(_opacityParamName, opacity);
_materialAxisY.SetParameterValue(_opacityParamName, opacity);
_materialAxisZ.SetParameterValue(_opacityParamName, opacity);
_materialAxisLocked.SetParameterValue(_opacityParamName, opacity);
}
/// <inheritdoc />
@@ -60,6 +88,21 @@ 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;
}
}
}
// 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
@@ -92,32 +135,38 @@ namespace FlaxEditor.Gizmo
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3);
// 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);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3);
// 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);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY);
cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3);
// 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);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ);
cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3);
// Center sphere
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
@@ -136,15 +185,18 @@ namespace FlaxEditor.Gizmo
// X axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3);
// Y axis
rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1);
// Z axis
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
@@ -163,32 +215,38 @@ namespace FlaxEditor.Gizmo
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref mx1, out m3);
scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3);
// 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);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3);
// 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);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ);
cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3);
// 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);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY);
cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);

View File

@@ -435,6 +435,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave:
window->OnMouseLeave();
break;

View File

@@ -176,7 +176,7 @@ ManagedEditor::~ManagedEditor()
void ManagedEditor::Init()
{
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
void* args[4];
void* args[2];
MClass* mclass = GetClass();
if (mclass == nullptr)
{
@@ -193,18 +193,22 @@ void ManagedEditor::Init()
LOG(Fatal, "Failed to create editor instance.");
}
MObject* exception = nullptr;
bool isHeadless = CommandLine::Options.Headless.IsTrue();
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
bool newProject = CommandLine::Options.NewProject.IsTrue();
args[0] = &isHeadless;
args[1] = &skipCompile;
args[2] = &newProject;
StartupFlags flags = StartupFlags::None;
if (CommandLine::Options.Headless.IsTrue())
flags |= StartupFlags::Headless;
if (CommandLine::Options.SkipCompile.IsTrue())
flags |= StartupFlags::SkipCompile;
if (CommandLine::Options.NewProject.IsTrue())
flags |= StartupFlags::NewProject;
if (CommandLine::Options.Exit.IsTrue())
flags |= StartupFlags::Exit;
args[0] = &flags;
Guid sceneId;
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
{
sceneId = Guid::Empty;
}
args[3] = &sceneId;
args[1] = &sceneId;
initMethod->Invoke(instance, args, &exception);
if (exception)
{
@@ -219,7 +223,7 @@ void ManagedEditor::Init()
WasExitCalled = false;
// Load scripts if auto-load on startup is disabled
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || skipCompile)
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || EnumHasAllFlags(flags, StartupFlags::SkipCompile))
{
LOG(Info, "Loading managed assemblies (due to disabled compilation on startup)");
Scripting::Load();

View File

@@ -22,6 +22,15 @@ API_CLASS(Namespace="FlaxEditor", Name="Editor", NoSpawn, NoConstructor) class M
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
static Guid ObjectID;
API_ENUM(Attributes="Flags", Internal) enum class StartupFlags
{
None = 0,
Headless = 1,
SkipCompile = 2,
NewProject = 4,
Exit = 8,
};
struct InternalOptions
{
byte AutoReloadScriptsOnMainWindowFocus = 1;
@@ -258,3 +267,5 @@ public:
// [ScriptingObject]
void DestroyManaged() override;
};
DECLARE_ENUM_OPERATORS(ManagedEditor::StartupFlags);

View File

@@ -1136,9 +1136,14 @@ namespace FlaxEditor.Modules
Proxy.Add(new SceneAnimationProxy());
Proxy.Add(new CSharpScriptProxy());
Proxy.Add(new CSharpEmptyProxy());
Proxy.Add(new CSharpEmptyClassProxy());
Proxy.Add(new CSharpEmptyStructProxy());
Proxy.Add(new CSharpEmptyInterfaceProxy());
Proxy.Add(new CSharpActorProxy());
Proxy.Add(new CppAssetProxy());
Proxy.Add(new CppStaticClassProxy());
Proxy.Add(new CppScriptProxy());
Proxy.Add(new CppActorProxy());
Proxy.Add(new SceneProxy());
Proxy.Add(new PrefabProxy());
Proxy.Add(new IESProfileProxy());

View File

@@ -274,11 +274,7 @@ namespace FlaxEditor.Modules
private void Load()
{
if (!File.Exists(_cachePath))
{
Editor.LogWarning("Missing editor cache file.");
return;
}
_lastSaveTime = DateTime.UtcNow;
try

View File

@@ -99,6 +99,10 @@ namespace FlaxEditor.Modules
{
if (Editor.StateMachine.IsEditMode)
{
// Show Game window if hidden
if (Editor.Windows.GameWin.IsHidden)
Editor.Windows.GameWin.Show();
Editor.Log("[PlayMode] Start");
if (Editor.Options.Options.General.AutoReloadScriptsOnMainWindowFocus)
@@ -131,6 +135,10 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.IsEditMode)
return;
// Show Game window if hidden
if (Editor.Windows.GameWin.IsHidden)
Editor.Windows.GameWin.Show();
var firstScene = Content.Settings.GameSettings.Load().FirstScene;
if (firstScene == Guid.Empty)
{

View File

@@ -381,7 +381,7 @@ namespace FlaxEditor.Modules
Editor.Options.OptionsChanged += OnOptionsChanged;
// Add dummy control for drawing the main window borders if using a custom style
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
#endif
{

View File

@@ -760,8 +760,10 @@ namespace FlaxEditor.Modules
{
settings.HasBorder = false;
#if !PLATFORM_SDL
// Skip OS sizing frame and implement it using LeftButtonHit
settings.HasSizingFrame = false;
#endif
}
#elif PLATFORM_LINUX
settings.HasBorder = false;
@@ -950,7 +952,10 @@ namespace FlaxEditor.Modules
MainWindow = null;
// Capture project icon screenshot (not in play mode and if editor was used for some time)
if (!Editor.StateMachine.IsPlayMode && Time.TimeSinceStartup >= 5.0f && GPUDevice.Instance?.RendererType != RendererType.Null)
if (!Editor.StateMachine.IsPlayMode &&
Time.TimeSinceStartup >= 5.0f &&
!Editor.IsHeadlessMode &&
GPUDevice.Instance?.RendererType != RendererType.Null)
{
Editor.Log("Capture project icon screenshot");
_projectIconScreenshotTimeout = Time.TimeSinceStartup + 0.8f; // wait 800ms for a screenshot task

View File

@@ -167,7 +167,7 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")]
public float InterfaceScale { get; set; } = 1.0f;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <summary>
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
/// </summary>
@@ -258,6 +258,13 @@ namespace FlaxEditor.Options
private TextAlignment _tooltipTextAlignment = TextAlignment.Center;
/// <summary>
/// Whether to scroll to the script when a script is added to an actor.
/// </summary>
[DefaultValue(true)]
[EditorDisplay("Interface"), EditorOrder(322)]
public bool ScrollToScriptOnAdd { get; set; } = true;
/// <summary>
/// Gets or sets the timestamps prefix mode for output log messages.
/// </summary>

View File

@@ -22,37 +22,51 @@ namespace FlaxEditor.Options
/// Gets or sets the first outline color.
/// </summary>
[DefaultValue(typeof(Color), "0.039,0.827,0.156")]
[EditorDisplay("Gizmo"), EditorOrder(100), Tooltip("The first color of the selection outline gradient.")]
[EditorDisplay("Transform Gizmo"), EditorOrder(100), Tooltip("The first color of the selection outline gradient.")]
public Color SelectionOutlineColor0 { get; set; } = new Color(0.039f, 0.827f, 0.156f);
/// <summary>
/// Gets or sets the second outline color.
/// </summary>
[DefaultValue(typeof(Color), "0.019,0.615,0.101")]
[EditorDisplay("Gizmo"), EditorOrder(101), Tooltip("The second color of the selection outline gradient.")]
[EditorDisplay("Transform Gizmo"), EditorOrder(101), Tooltip("The second color of the selection outline gradient.")]
public Color SelectionOutlineColor1 { get; set; } = new Color(0.019f, 0.615f, 0.101f);
/// <summary>
/// Gets or sets the selection outline size for UI controls.
/// </summary>
[DefaultValue(2.0f)]
[EditorDisplay("Gizmo", "UI Control Outline Size"), EditorOrder(100), Tooltip("The size of the selection outline for UI controls.")]
[EditorDisplay("UI Gizmo", "UI Control Outline Size"), EditorOrder(103), Tooltip("The size of the selection outline for UI controls.")]
public float UISelectionOutlineSize { get; set; } = 2.0f;
/// <summary>
/// Gets or sets the transform gizmo size.
/// </summary>
[DefaultValue(1.0f), Limit(0.01f, 100.0f, 0.01f)]
[EditorDisplay("Gizmo"), EditorOrder(110), Tooltip("The transform gizmo size.")]
[EditorDisplay("Transform Gizmo"), EditorOrder(110), Tooltip("The transform gizmo size.")]
public float GizmoSize { get; set; } = 1.0f;
/// <summary>
/// Gets or sets the color used to highlight selected meshes and CSG surfaces.
/// </summary>
[DefaultValue(typeof(Color), "0.0,0.533,1.0,1.0")]
[EditorDisplay("Gizmo"), EditorOrder(200), Tooltip("The color used to highlight selected meshes and CSG surfaces.")]
[EditorDisplay("Transform Gizmo"), EditorOrder(200), Tooltip("The color used to highlight selected meshes and CSG surfaces.")]
public Color HighlightColor { get; set; } = new Color(0.0f, 0.533f, 1.0f, 1.0f);
/// <summary>
/// Gets or set a value indicating how bright the transform gizmo is. Value over 1 will result in the gizmo emitting light.
/// </summary>
[DefaultValue(1f), Range(0f, 5f)]
[EditorDisplay("Transform Gizmo", "Gizmo Brightness"), EditorOrder(210)]
public float TransformGizmoBrightness { get; set; } = 1f;
/// <summary>
/// Gets or set a value indicating the opacity of the transform gizmo.
/// </summary>
[DefaultValue(1f), Range(0f, 1f)]
[EditorDisplay("Transform Gizmo", "Gizmo Opacity"), EditorOrder(211)]
public float TransformGizmoOpacity { get; set; } = 1f;
/// <summary>
/// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance.
/// </summary>

View File

@@ -293,6 +293,14 @@ namespace FlaxEditor.Scripting
/// <param name="obj">The object whose member value will be modified.</param>
/// <param name="value">The new member value.</param>
void SetValue(object obj, object value);
/// <summary>
/// Invokes the method on a specific object (null if static) using the provided parameters.
/// </summary>
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
/// <param name="parameters">List of parameters to provide.</param>
/// <returns>The value returned by the method.</returns>
object Invoke(object obj, object[] parameters);
}
/// <summary>

View File

@@ -691,6 +691,23 @@ namespace FlaxEditor.Scripting
else
_custom.SetValue(obj, value);
}
/// <summary>
/// Invokes the method on a specific object (null if static) using the provided parameters.
/// </summary>
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
/// <param name="parameters">List of parameters to provide.</param>
/// <returns>The value returned by the method.</returns>
public object Invoke(object obj = null, object[] parameters = null)
{
if (parameters == null)
parameters = Array.Empty<object>();
if (_managed is MethodInfo methodInfo)
return methodInfo.Invoke(obj, parameters);
if (_managed != null)
throw new NotSupportedException();
return _custom.Invoke(obj, parameters);
}
}
/// <summary>

View File

@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes
/// Represents single blend point.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.Control" />
protected class BlendPoint : Control
internal class BlendPoint : Control
{
private readonly BlendPointsEditor _editor;
private readonly int _index;
@@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes
/// </summary>
public int Index => _index;
/// <summary>
/// Flag that indicates that user is moving this point with a mouse.
/// </summary>
public bool IsMouseDown => _isMouseDown;
/// <summary>
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
/// </summary>
@@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes
/// </summary>
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
/// <summary>
/// BLend points array.
/// </summary>
internal IReadOnlyList<BlendPoint> BlendPoints => _blendPoints;
/// <summary>
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
/// </summary>
@@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes
/// <returns>The blend point control position.</returns>
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
{
if (_rangeX.IsZero)
{
var data0 = (Float4)_node.Values[0];
_rangeX = new Float2(data0.X, data0.Y);
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
}
GetPointsArea(out var pointsArea);
if (_is2D)
{
@@ -389,7 +405,7 @@ namespace FlaxEditor.Surface.Archetypes
pointsArea.Center.Y
);
}
return pos - new Float2(BlendPoint.DefaultSize * 0.5f);
return pos;
}
/// <inheritdoc />
@@ -424,7 +440,7 @@ namespace FlaxEditor.Surface.Archetypes
}
// Update blend point
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location) - BlendPoint.DefaultSize * 0.5f;
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
var tooltip = asset?.ShortName ?? string.Empty;
tooltip += "\nX: " + location.X;
@@ -532,81 +548,18 @@ namespace FlaxEditor.Surface.Archetypes
SetAsset((int)b.Tag, Guid.Empty);
}
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
{
// Draw line
Render2D.DrawLine(start, end, gridColor);
// Draw label
var labelWidth = 50.0f;
var labelHeight = 10.0f;
var labelMargin = 2.0f;
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
var hAlign = TextAlignment.Near;
Rectangle labelRect;
if (vertical)
{
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
if (isLast)
return; // Don't overlap with the first horizontal label
}
else
{
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
if (isLast)
{
labelRect.X = start.X - labelMargin - labelRect.Width;
hAlign = TextAlignment.Far;
}
}
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
}
/// <inheritdoc />
public override void Draw()
{
var style = Style.Current;
var rect = new Rectangle(Float2.Zero, Size);
var containsFocus = ContainsFocus;
GetPointsArea(out var pointsArea);
var data0 = (Float4)_node.Values[0];
var rangeX = new Float2(data0.X, data0.Y);
// Background
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
//Render2D.DrawRectangle(pointsArea, Color.Red);
_node.DrawEditorBackground(ref rect);
// Grid
int splits = 10;
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
var labelColor = style.ForegroundDisabled;
var labelFont = style.FontSmall;
//var blendArea = BlendAreaRect;
var blendArea = pointsArea;
for (int i = 0; i <= splits; i++)
{
float alpha = (float)i / splits;
float x = blendArea.Left + blendArea.Width * alpha;
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
}
if (_is2D)
{
var rangeY = new Float2(data0.Z, data0.W);
for (int i = 0; i <= splits; i++)
{
float alpha = (float)i / splits;
float y = blendArea.Top + blendArea.Height * alpha;
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
}
}
else
{
float y = blendArea.Center.Y;
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
}
_node.DrawEditorGrid(ref rect);
base.Draw();
@@ -808,6 +761,87 @@ namespace FlaxEditor.Surface.Archetypes
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
}
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
{
// Draw line
Render2D.DrawLine(start, end, gridColor);
// Draw label
var labelWidth = 50.0f;
var labelHeight = 10.0f;
var labelMargin = 2.0f;
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
var hAlign = TextAlignment.Near;
Rectangle labelRect;
if (vertical)
{
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
if (isLast)
return; // Don't overlap with the first horizontal label
}
else
{
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
if (isLast)
{
labelRect.X = start.X - labelMargin - labelRect.Width;
hAlign = TextAlignment.Far;
}
}
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
}
/// <summary>
/// Custom drawing logic for blend space background.
/// </summary>
public virtual void DrawEditorBackground(ref Rectangle rect)
{
var style = Style.Current;
Render2D.FillRectangle(rect, style.Background.AlphaMultiplied(0.5f));
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
}
/// <summary>
/// Custom drawing logic for blend space grid.
/// </summary>
public virtual void DrawEditorGrid(ref Rectangle rect)
{
var style = Style.Current;
_editor.GetPointsArea(out var pointsArea);
var data0 = (Float4)Values[0];
var rangeX = new Float2(data0.X, data0.Y);
int splits = 10;
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
var labelColor = style.ForegroundDisabled;
var labelFont = style.FontSmall;
//var blendArea = BlendAreaRect;
var blendArea = pointsArea;
for (int i = 0; i <= splits; i++)
{
float alpha = (float)i / splits;
float x = blendArea.Left + blendArea.Width * alpha;
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
}
if (_editor.Is2D)
{
var rangeY = new Float2(data0.Z, data0.W);
for (int i = 0; i <= splits; i++)
{
float alpha = (float)i / splits;
float y = blendArea.Top + blendArea.Height * alpha;
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
}
}
else
{
float y = blendArea.Center.Y;
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
}
}
/// <summary>
/// Updates the editor UI.
/// </summary>
@@ -972,6 +1006,10 @@ namespace FlaxEditor.Surface.Archetypes
private readonly FloatValueBox _animationX;
private readonly Label _animationYLabel;
private readonly FloatValueBox _animationY;
private Float2[] _triangles;
private Color[] _triangleColors;
private Float2[] _selectedTriangles;
private Color[] _selectedColors;
/// <inheritdoc />
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
@@ -1040,6 +1078,143 @@ namespace FlaxEditor.Surface.Archetypes
}
}
private void ClearTriangles()
{
// Remove cache
_triangles = null;
_triangleColors = null;
_selectedTriangles = null;
_selectedColors = null;
}
private void CacheTriangles()
{
// Get locations of blend point vertices
int pointsCount = _editor.PointsCount;
int count = 0, j = 0;
for (int i = 0; i < pointsCount; i++)
{
var animId = (Guid)Values[5 + i * 2];
if (animId != Guid.Empty)
count++;
}
var vertices = new Float2[count];
for (int i = 0; i < pointsCount; i++)
{
var animId = (Guid)Values[5 + i * 2];
if (animId != Guid.Empty)
{
var dataA = (Float4)Values[4 + i * 2];
vertices[j++] = new Float2(dataA.X, dataA.Y);
}
}
// Triangulate
_triangles = FlaxEngine.Utilities.Delaunay2D.Triangulate(vertices);
_triangleColors = null;
// Fix incorrect triangles (mirror logic in AnimGraphBase::onNodeLoaded)
if (_triangles == null || _triangles.Length == 0)
{
// Insert dummy triangles to have something working (eg. blend points are on the same axis)
var triangles = new List<Float2>();
int verticesLeft = vertices.Length;
while (verticesLeft >= 3)
{
verticesLeft -= 3;
triangles.Add(vertices[verticesLeft + 0]);
triangles.Add(vertices[verticesLeft + 1]);
triangles.Add(vertices[verticesLeft + 2]);
}
if (verticesLeft == 1)
{
triangles.Add(vertices[0]);
triangles.Add(vertices[0]);
triangles.Add(vertices[0]);
}
else if (verticesLeft == 2)
{
triangles.Add(vertices[0]);
triangles.Add(vertices[1]);
triangles.Add(vertices[0]);
}
_triangles = triangles.ToArray();
}
// Project to the blend space for drawing
for (int i = 0; i < _triangles.Length; i++)
_triangles[i] = _editor.BlendSpacePosToBlendPointPos(_triangles[i]);
// Check if anything is selected
var selectedIndex = _selectedAnimation.SelectedIndex;
if (selectedIndex != -1)
{
// Find triangles that contain selected point
var dataA = (Float4)Values[4 + selectedIndex * 2];
var pos = _editor.BlendSpacePosToBlendPointPos(new Float2(dataA.X, dataA.Y));
var selectedTriangles = new List<Float2>();
var selectedColors = new List<Color>();
var style = Style.Current;
var triangleColor = style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f);
var selectedTriangleColor = style.BackgroundSelected.AlphaMultiplied(0.6f);
_triangleColors = new Color[_triangles.Length];
for (int i = 0; i < _triangles.Length; i += 3)
{
var is0 = Float2.NearEqual(ref _triangles[i + 0], ref pos);
var is1 = Float2.NearEqual(ref _triangles[i + 1], ref pos);
var is2 = Float2.NearEqual(ref _triangles[i + 2], ref pos);
if (is0 || is1 || is2)
{
selectedTriangles.Add(_triangles[i + 0]);
selectedTriangles.Add(_triangles[i + 1]);
selectedTriangles.Add(_triangles[i + 2]);
selectedColors.Add(is0 ? Color.White : Color.Transparent);
selectedColors.Add(is1 ? Color.White : Color.Transparent);
selectedColors.Add(is2 ? Color.White : Color.Transparent);
}
_triangleColors[i + 0] = is0 ? selectedTriangleColor : triangleColor;
_triangleColors[i + 1] = is1 ? selectedTriangleColor : triangleColor;
_triangleColors[i + 2] = is2 ? selectedTriangleColor : triangleColor;
}
_selectedTriangles = selectedTriangles.ToArray();
_selectedColors = selectedColors.ToArray();
}
}
/// <inheritdoc />
public override void DrawEditorBackground(ref Rectangle rect)
{
base.DrawEditorBackground(ref rect);
// Draw triangulated multi blend space
var style = Style.Current;
if (_triangles == null)
CacheTriangles();
if (_triangleColors != null && (ContainsFocus || IsMouseOver))
Render2D.FillTriangles(_triangles, _triangleColors);
else
Render2D.FillTriangles(_triangles, style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f));
Render2D.DrawTriangles(_triangles, style.Foreground);
}
/// <inheritdoc />
public override void DrawEditorGrid(ref Rectangle rect)
{
base.DrawEditorGrid(ref rect);
// Highlight selected blend point
var style = Style.Current;
var selectedIndex = _selectedAnimation.SelectedIndex;
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
{
var point = _editor.BlendPoints[selectedIndex];
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
Render2D.PushTint(ref highlightColor);
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
Render2D.PopTint();
}
}
/// <inheritdoc />
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
{
@@ -1064,6 +1239,23 @@ namespace FlaxEditor.Surface.Archetypes
_animationX.Enabled = isValid;
_animationYLabel.Enabled = isValid;
_animationY.Enabled = isValid;
ClearTriangles();
}
/// <inheritdoc />
public override void OnValuesChanged()
{
base.OnValuesChanged();
ClearTriangles();
}
/// <inheritdoc />
public override void OnLoaded(SurfaceNodeActions action)
{
base.OnLoaded(action);
ClearTriangles();
}
}
}

View File

@@ -1502,9 +1502,10 @@ namespace FlaxEditor.Surface.Archetypes
{
data = new object[]
{
filterText.Substring(2),
new Color(1.0f, 1.0f, 1.0f, 0.2f),
new Float2(400.0f, 400.0f),
filterText.Substring(2), // Title
new Color(1.0f, 1.0f, 1.0f, 0.2f), // Color
new Float2(400.0f, 400.0f), // Size
-1, // Order
};
return true;
}

View File

@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
typeof(TooltipAttribute),
typeof(HideInEditorAttribute),
typeof(NoAnimateAttribute),
typeof(ButtonAttribute),
};
/// <summary>

View File

@@ -172,10 +172,22 @@ namespace FlaxEditor.Surface
/// <inheritdoc />
public override void Update(float deltaTime)
{
if (_isRenaming && (!_renameTextBox.IsFocused || !RootWindow.IsFocused))
if (_isRenaming)
{
Rename(_renameTextBox.Text);
StopRenaming();
// Stop renaming when clicking anywhere else
if (!_renameTextBox.IsFocused || !RootWindow.IsFocused)
{
Rename(_renameTextBox.Text);
StopRenaming();
}
}
else
{
// Rename on F2
if (IsSelected && Editor.Instance.Options.Options.Input.Rename.Process(this))
{
StartRenaming();
}
}
base.Update(deltaTime);
@@ -417,6 +429,7 @@ namespace FlaxEditor.Surface
base.OnShowSecondaryContextMenu(menu, location);
menu.AddSeparator();
menu.AddButton("Rename", StartRenaming);
ContextMenuChildMenu cmOrder = menu.AddChildMenu("Order");
{
cmOrder.ContextMenu.AddButton("Bring Forward", () =>

View File

@@ -1,131 +0,0 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
Delegate<Color32> ScreenUtilities::PickColorDone;
#if PLATFORM_WINDOWS
#include <Windows.h>
#pragma comment(lib, "Gdi32.lib")
static HHOOK MouseCallbackHook;
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
{
UnhookWindowsHookEx(MouseCallbackHook);
// Push event with the picked color
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
ScreenUtilities::PickColorDone(colorPicked);
return 1;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
PROFILE_CPU();
HDC deviceContext = GetDC(NULL);
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
ReleaseDC(NULL, deviceContext);
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
}
void ScreenUtilities::PickColor()
{
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
if (MouseCallbackHook == NULL)
{
LOG(Warning, "Failed to set mouse hook.");
LOG(Warning, "Error: {0}", GetLastError());
}
}
#elif PLATFORM_LINUX
#include "Engine/Platform/Linux/LinuxPlatform.h"
#include "Engine/Platform/Linux/IncludeX11.h"
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
X11::XColor color;
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
int defaultScreen = X11::XDefaultScreen(display);
X11::XImage* image;
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
color.pixel = XGetPixel(image, 0, 0);
X11::XFree(image);
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
Color32 outputColor;
outputColor.R = color.red / 256;
outputColor.G = color.green / 256;
outputColor.B = color.blue / 256;
outputColor.A = 255;
return outputColor;
}
void OnScreenUtilsXEventCallback(void* eventPtr)
{
X11::XEvent* event = (X11::XEvent*) eventPtr;
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
if (event->type == ButtonPress)
{
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
X11::XUngrabPointer(display, CurrentTime);
ScreenUtilities::PickColorDone(colorPicked);
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
}
}
void ScreenUtilities::PickColor()
{
PROFILE_CPU();
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
X11::Cursor cursor = XCreateFontCursor(display, 130);
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
if (grabbedPointer != GrabSuccess)
{
LOG(Error, "Failed to grab cursor for events.");
X11::XFreeCursor(display, cursor);
return;
}
X11::XFreeCursor(display, cursor);
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
}
#elif PLATFORM_MAC
#include <Cocoa/Cocoa.h>
#include <AppKit/AppKit.h>
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
// TODO: implement ScreenUtilities for macOS
return { 0, 0, 0, 255 };
}
void ScreenUtilities::PickColor()
{
// This is what C# calls to start the color picking sequence
// This should stop mouse clicks from working for one click, and that click is on the selected color
// There is a class called NSColorSample that might implement that for you, but maybe not.
}
#endif

View File

@@ -2,32 +2,4 @@
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
/// <summary>
/// Gets the pixel color at the specified coordinates.
/// </summary>
/// <param name="pos">Screen-space coordinate to read.</param>
/// <returns>Pixel color at the specified coordinates.</returns>
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
/// <summary>
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
/// </summary>
API_FUNCTION() static void PickColor();
/// <summary>
/// Called when PickColor action is finished.
/// </summary>
API_EVENT() static Delegate<Color32> PickColorDone;
};
#include "Engine/Platform/ScreenUtilities.h"

View File

@@ -250,6 +250,8 @@ namespace FlaxEditor.Utilities
internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60)
{
if (pixelRange <= Mathf.Epsilon || maxDistanceBetweenTicks <= minDistanceBetweenTicks)
return Int2.Zero;
if (tickStrengths == null || tickStrengths.Length != tickSteps.Length)
tickStrengths = new float[tickSteps.Length];
@@ -286,7 +288,7 @@ namespace FlaxEditor.Utilities
continue;
// Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1);
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 2);
var lStep = tickSteps[l];
var lNextStep = tickSteps[l + 1];
int startTick = Mathf.FloorToInt(min / lStep);

View File

@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc />
public Float2 MouseDelta => _mouseDelta;
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
/// <inheritdoc />
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;

View File

@@ -158,18 +158,22 @@ namespace FlaxEditor.Viewport
private float _movementSpeed;
private float _minMovementSpeed;
private float _maxMovementSpeed;
#if !PLATFORM_SDL
private float _mouseAccelerationScale;
private bool _useMouseFiltering;
private bool _useMouseAcceleration;
#endif
// Input
internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos;
#if !PLATFORM_SDL
private Float2 _mouseDeltaLast;
private int _deltaFilteringStep;
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
#endif
/// <summary>
/// The previous input (from the previous update).
@@ -525,10 +529,11 @@ namespace FlaxEditor.Viewport
: base(task)
{
_editor = Editor.Instance;
#if !PLATFORM_SDL
_mouseAccelerationScale = 0.1f;
_useMouseFiltering = false;
_useMouseAcceleration = false;
#endif
_camera = camera;
if (_camera != null)
_camera.Viewport = this;
@@ -1456,7 +1461,9 @@ namespace FlaxEditor.Viewport
// Hide cursor and start tracking mouse movement
win.StartTrackingMouse(false);
win.Cursor = CursorType.Hidden;
win.MouseMoveRelative += OnMouseMoveRelative;
#if !PLATFORM_SDL
// Center mouse position if it's too close to the edge
var size = Size;
var center = Float2.Round(size * 0.5f);
@@ -1465,6 +1472,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos);
}
#endif
}
/// <summary>
@@ -1476,6 +1484,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default;
win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
}
/// <summary>
@@ -1580,6 +1589,14 @@ namespace FlaxEditor.Viewport
else
EndMouseCapture();
}
#if PLATFORM_SDL
bool useMouse = IsControllingMouse || true;
_prevInput = _input;
if (canUseInput && ContainsFocus)
_input.Gather(win.Window, useMouse, ref _prevInput);
else
_input.Clear();
#else
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
@@ -1587,6 +1604,7 @@ namespace FlaxEditor.Viewport
_input.Gather(win.Window, useMouse, ref _prevInput);
else
_input.Clear();
#endif
// Track controlling mouse state change
bool wasControllingMouse = _prevInput.IsControllingMouse;
@@ -1695,6 +1713,10 @@ namespace FlaxEditor.Viewport
if (_input.IsControlDown)
moveDelta *= 0.3f;
#if PLATFORM_SDL
var mouseDelta = _mouseDelta;
_mouseDelta = Float2.Zero;
#else
// Calculate smooth mouse delta not dependant on viewport size
var offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
@@ -1736,6 +1758,7 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta;
}
#endif
// Update
moveDelta *= dt * (60.0f * 4.0f);
@@ -1744,12 +1767,14 @@ namespace FlaxEditor.Viewport
mouseDelta *= new Float2(1, -1);
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
#if !PLATFORM_SDL
// Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
{
var center = PointToWindow(_startPos);
win.MousePosition = center;
}
#endif
// Change Ortho size on mouse scroll
if (_isOrtho && !rmbWheel)
@@ -1761,6 +1786,8 @@ namespace FlaxEditor.Viewport
}
else
{
#if PLATFORM_SDL
#else
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
{
// Calculate smooth mouse delta not dependant on viewport size
@@ -1775,6 +1802,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero;
}
_mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus)
{
@@ -1824,6 +1852,12 @@ namespace FlaxEditor.Viewport
_input.MouseWheelDelta = 0;
}
/// <inheritdoc />
public void OnMouseMoveRelative(ref Float2 mouseMotion)
{
_mouseDelta += mouseMotion;
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{

View File

@@ -54,24 +54,24 @@ namespace FlaxEditor.Windows.Assets
[EditorOrder(10), EditorDisplay("General"), Tooltip("Material domain type.")]
public MaterialDomain Domain;
[EditorOrder(20), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
[EditorOrder(20), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
public MaterialShadingModel ShadingModel;
[EditorOrder(30), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
[EditorOrder(30), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
public MaterialBlendMode BlendMode;
// Rendering
[EditorOrder(100), DefaultValue(CullMode.Normal), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
[EditorOrder(100), DefaultValue(CullMode.Normal), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
public CullMode CullMode;
[EditorOrder(110), DefaultValue(false), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
[EditorOrder(110), DefaultValue(false), VisibleIf(nameof(IsStandardOrGUI)), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
public bool Wireframe;
[EditorOrder(120), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
[EditorOrder(120), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
public bool DepthTest;
[EditorOrder(130), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
[EditorOrder(130), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
public bool DepthWrite;
// Transparency
@@ -111,13 +111,13 @@ namespace FlaxEditor.Windows.Assets
// Misc
[EditorOrder(400), DefaultValue(false), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
[EditorOrder(400), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
public bool InputWorldSpaceNormal;
[EditorOrder(410), DefaultValue(false), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
[EditorOrder(410), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
public bool DitheredLODTransition;
[EditorOrder(420), DefaultValue(0.3f), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
[EditorOrder(420), DefaultValue(0.3f), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
public float MaskThreshold;
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
@@ -144,7 +144,9 @@ namespace FlaxEditor.Windows.Assets
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
private bool IsDecal => Domain == MaterialDomain.Decal;
private bool IsGUI => Domain == MaterialDomain.GUI;
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
private bool IsStandardOrGUI => IsStandard || IsGUI;
/// <summary>
/// Gathers parameters from the specified material.

View File

@@ -1068,8 +1068,11 @@ namespace FlaxEditor.Windows
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
{
// Make sure the cursor is always in the viewport when cursor is locked
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
// Center mouse in play mode
if (CenterMouseOnFocus)
if (CenterMouseOnFocus || forceCenter)
{
var center = PointToWindow(Size * 0.5f);
Root.MousePosition = center;

View File

@@ -175,7 +175,7 @@ namespace FlaxEditor.Windows.Profiler
private string FormatCellBytes(object x)
{
return Utilities.Utils.FormatBytesCount((ulong)x);
return Utilities.Utils.FormatBytesCount((ulong)(int)x);
}
/// <inheritdoc />

View File

@@ -28,7 +28,7 @@ const Char* SplashScreenQuotes[] =
#elif PLATFORM_LINUX
TEXT("Try it on a Raspberry"),
TEXT("Trying to exit vim"),
TEXT("Sudo flax --loadproject"),
TEXT("sudo flax --project HelloWorld.flaxproj"),
#elif PLATFORM_MAC
TEXT("don't compare Macbooks to oranges."),
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
@@ -104,6 +104,7 @@ const Char* SplashScreenQuotes[] =
TEXT("You have my bow.\nAnd my axe!"),
TEXT("To the bridge of Khazad-dum."),
TEXT("One ring to rule them all.\nOne ring to find them."),
TEXT("Where there's a whip, there's a way."),
TEXT("That's what she said"),
TEXT("We could be compiling shaders here"),
TEXT("Hello There"),
@@ -164,7 +165,7 @@ void SplashScreen::Show()
settings.AllowMaximize = false;
settings.AllowDragAndDrop = false;
settings.IsTopmost = false;
settings.IsRegularWindow = false;
settings.Type = WindowType::Utility;
settings.HasSizingFrame = false;
settings.ShowAfterFirstPaint = true;
settings.StartPosition = WindowStartPosition::CenterScreen;

View File

@@ -66,27 +66,23 @@ namespace AnimationUtils
}
template<class T>
FORCE_INLINE static void GetTangent(const T& a, const T& b, float length, T& result)
FORCE_INLINE static void GetTangent(const T& value, const T& tangent, float tangentScale, T& result)
{
const float oneThird = 1.0f / 3.0f;
result = a + b * (length * oneThird);
result = value + tangent * tangentScale;
}
template<>
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& a, const Quaternion& b, float length, Quaternion& result)
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& value, const Quaternion& tangent, float tangentScale, Quaternion& result)
{
const float oneThird = 1.0f / 3.0f;
Quaternion::Slerp(a, b, oneThird, result);
Quaternion::Slerp(value, tangent, 1.0f / 3.0f, result);
}
template<>
FORCE_INLINE void GetTangent<Transform>(const Transform& a, const Transform& b, float length, Transform& result)
FORCE_INLINE void GetTangent<Transform>(const Transform& value, const Transform& tangent, float tangentScale, Transform& result)
{
const float oneThird = 1.0f / 3.0f;
const float oneThirdLength = length * oneThird;
result.Translation = a.Translation + b.Translation * oneThirdLength;
Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation);
result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength;
GetTangent(value.Translation, tangent.Translation, tangentScale, result.Translation);
GetTangent(value.Orientation, tangent.Orientation, tangentScale, result.Orientation);
GetTangent(value.Scale, tangent.Scale, tangentScale, result.Scale);
}
template<class T>

View File

@@ -1,8 +1,10 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using FlaxEngine.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace FlaxEngine
@@ -24,9 +26,9 @@ namespace FlaxEngine
/// </summary>
/// <param name="value">The value.</param>
/// <param name="tangent">The tangent.</param>
/// <param name="lengthThird">The length divided by 3.</param>
/// <param name="tangentScale">The tangent scale factor.</param>
/// <param name="result">The result.</param>
void GetTangent(ref U value, ref U tangent, float lengthThird, out U result);
void GetTangent(ref U value, ref U tangent, float tangentScale, out U result);
/// <summary>
/// Calculates the linear interpolation at the specified alpha.
@@ -67,7 +69,7 @@ namespace FlaxEngine
IKeyframeAccess<Color32>,
IKeyframeAccess<Color>
{
public void GetTangent(ref bool value, ref bool tangent, float lengthThird, out bool result)
public void GetTangent(ref bool value, ref bool tangent, float tangentScale, out bool result)
{
result = value;
}
@@ -82,9 +84,9 @@ namespace FlaxEngine
result = p0;
}
public void GetTangent(ref int value, ref int tangent, float lengthThird, out int result)
public void GetTangent(ref int value, ref int tangent, float tangentScale, out int result)
{
result = value + (int)(tangent * lengthThird);
result = value + (int)(tangent * tangentScale);
}
public void Linear(ref int a, ref int b, float alpha, out int result)
@@ -102,9 +104,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref double value, ref double tangent, float lengthThird, out double result)
public void GetTangent(ref double value, ref double tangent, float tangentScale, out double result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref double a, ref double b, float alpha, out double result)
@@ -122,9 +124,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref float value, ref float tangent, float lengthThird, out float result)
public void GetTangent(ref float value, ref float tangent, float tangentScale, out float result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref float a, ref float b, float alpha, out float result)
@@ -142,9 +144,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float lengthThird, out Vector2 result)
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float tangentScale, out Vector2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result)
@@ -162,9 +164,9 @@ namespace FlaxEngine
Vector2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float lengthThird, out Vector3 result)
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float tangentScale, out Vector3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result)
@@ -182,9 +184,9 @@ namespace FlaxEngine
Vector3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float lengthThird, out Vector4 result)
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float tangentScale, out Vector4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result)
@@ -202,9 +204,9 @@ namespace FlaxEngine
Vector4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float2 value, ref Float2 tangent, float lengthThird, out Float2 result)
public void GetTangent(ref Float2 value, ref Float2 tangent, float tangentScale, out Float2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result)
@@ -222,9 +224,9 @@ namespace FlaxEngine
Float2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float3 value, ref Float3 tangent, float lengthThird, out Float3 result)
public void GetTangent(ref Float3 value, ref Float3 tangent, float tangentScale, out Float3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result)
@@ -242,9 +244,9 @@ namespace FlaxEngine
Float3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float4 value, ref Float4 tangent, float lengthThird, out Float4 result)
public void GetTangent(ref Float4 value, ref Float4 tangent, float tangentScale, out Float4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result)
@@ -262,9 +264,9 @@ namespace FlaxEngine
Float4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double2 value, ref Double2 tangent, float lengthThird, out Double2 result)
public void GetTangent(ref Double2 value, ref Double2 tangent, float tangentScale, out Double2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result)
@@ -282,9 +284,9 @@ namespace FlaxEngine
Double2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double3 value, ref Double3 tangent, float lengthThird, out Double3 result)
public void GetTangent(ref Double3 value, ref Double3 tangent, float tangentScale, out Double3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result)
@@ -302,9 +304,9 @@ namespace FlaxEngine
Double3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double4 value, ref Double4 tangent, float lengthThird, out Double4 result)
public void GetTangent(ref Double4 value, ref Double4 tangent, float tangentScale, out Double4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result)
@@ -322,7 +324,7 @@ namespace FlaxEngine
Double4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float lengthThird, out Quaternion result)
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float tangentScale, out Quaternion result)
{
Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result);
}
@@ -342,9 +344,9 @@ namespace FlaxEngine
Quaternion.Slerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Color32 value, ref Color32 tangent, float lengthThird, out Color32 result)
public void GetTangent(ref Color32 value, ref Color32 tangent, float tangentScale, out Color32 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result)
@@ -362,9 +364,9 @@ namespace FlaxEngine
Color32.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Color value, ref Color tangent, float lengthThird, out Color result)
public void GetTangent(ref Color value, ref Color tangent, float tangentScale, out Color result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Color a, ref Color b, float alpha, out Color result)
@@ -454,6 +456,40 @@ namespace FlaxEngine
time = end;
}
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="keyframes">The keyframes array.</param>
/// <returns>The raw keyframes data.</returns>
protected static unsafe byte[] MarshalKeyframes<Keyframe>(Keyframe[] keyframes)
{
if (keyframes == null || keyframes.Length == 0)
return null;
var keyframeSize = Unsafe.SizeOf<Keyframe>();
var result = new byte[keyframes.Length * keyframeSize];
fixed (byte* resultPtr = result)
{
var keyframesHandle = ManagedHandle.Alloc(keyframes, GCHandleType.Pinned);
var keyframesPtr = Marshal.UnsafeAddrOfPinnedArrayElement(keyframes, 0);
Buffer.MemoryCopy((void*)keyframesPtr, resultPtr, (uint)result.Length, (uint)result.Length);
keyframesHandle.Free();
}
return result;
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="raw">The raw keyframes data.</param>
/// <returns>The keyframes array.</returns>
protected static unsafe Keyframe[] MarshalKeyframes<Keyframe>(byte[] raw) where Keyframe : struct
{
if (raw == null || raw.Length == 0)
return null;
fixed (byte* rawPtr = raw)
return MemoryMarshal.Cast<byte, Keyframe>(new Span<byte>(rawPtr, raw.Length)).ToArray();
}
}
/// <summary>
@@ -709,6 +745,30 @@ namespace FlaxEngine
leftKey = Mathf.Max(0, start - 1);
rightKey = Mathf.Min(start, Keyframes.Length - 1);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="curve">The curve to copy.</param>
/// <returns>The raw keyframes data.</returns>
public static unsafe implicit operator byte[](LinearCurve<T> curve)
{
if (curve == null)
return null;
return MarshalKeyframes<Keyframe>(curve.Keyframes);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="raw">The raw keyframes data.</param>
/// <returns>The curve.</returns>
public static unsafe implicit operator LinearCurve<T>(byte[] raw)
{
if (raw == null || raw.Length == 0)
return null;
return new LinearCurve<T>(MarshalKeyframes<Keyframe>(raw));
}
}
/// <summary>
@@ -860,9 +920,9 @@ namespace FlaxEngine
// Evaluate the key at the curve
result.Time = leftKey.Time + length * t;
float lengthThird = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
float tangentScale = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value);
result.TangentIn = leftKey.TangentOut;
result.TangentOut = rightKey.TangentIn;
@@ -895,9 +955,9 @@ namespace FlaxEngine
float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
// Evaluate the value at the curve
float lengthThird = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
float tangentScale = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result);
}
@@ -1000,5 +1060,29 @@ namespace FlaxEngine
leftKey = Mathf.Max(0, start - 1);
rightKey = Mathf.Min(start, Keyframes.Length - 1);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="curve">The curve to copy.</param>
/// <returns>The raw keyframes data.</returns>
public static unsafe implicit operator byte[](BezierCurve<T> curve)
{
if (curve == null)
return null;
return MarshalKeyframes<Keyframe>(curve.Keyframes);
}
/// <summary>
/// Raw memory copy (used by scripting bindings - see MarshalAs tag).
/// </summary>
/// <param name="raw">The raw keyframes data.</param>
/// <returns>The curve.</returns>
public static unsafe implicit operator BezierCurve<T>(byte[] raw)
{
if (raw == null || raw.Length == 0)
return null;
return new BezierCurve<T>(MarshalKeyframes<Keyframe>(raw));
}
}
}

View File

@@ -247,16 +247,18 @@ public:
static void Interpolate(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
{
T leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
}
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
{
T leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
}
@@ -264,8 +266,9 @@ public:
{
result.Time = a.Time + length * alpha;
T leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
result.TangentIn = a.TangentOut;
result.TangentOut = b.TangentIn;
@@ -498,7 +501,7 @@ protected:
/// An animation spline represented by a set of keyframes, each representing an endpoint of a curve.
/// </summary>
template<class T, typename KeyFrame = LinearCurveKeyframe<T>>
class Curve : public CurveBase<T, KeyFrame>
API_CLASS(InBuild, Template, MarshalAs=Span<byte>) class Curve : public CurveBase<T, KeyFrame>
{
public:
typedef CurveBase<T, KeyFrame> Base;
@@ -760,28 +763,42 @@ public:
}
return true;
}
// Raw memory copy (used by scripting bindings - see MarshalAs tag).
Curve& operator=(const Span<byte>& raw)
{
ASSERT((raw.Length() % sizeof(KeyFrame)) == 0);
const int32 count = raw.Length() / sizeof(KeyFrame);
_keyframes.Resize(count, false);
Platform::MemoryCopy(_keyframes.Get(), raw.Get(), sizeof(KeyFrame) * count);
return *this;
}
operator Span<byte>()
{
return Span<byte>((const byte*)_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame));
}
};
/// <summary>
/// An animation spline represented by a set of keyframes, each representing a value point.
/// </summary>
template<typename T>
using StepCurve = Curve<T, StepCurveKeyframe<T>>;
API_TYPEDEF() using StepCurve = Curve<T, StepCurveKeyframe<T>>;
/// <summary>
/// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve.
/// </summary>
template<typename T>
using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
API_TYPEDEF() using LinearCurve = Curve<T, LinearCurveKeyframe<T>>;
/// <summary>
/// An animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve.
/// </summary>
template<typename T>
using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
API_TYPEDEF() using HermiteCurve = Curve<T, HermiteCurveKeyframe<T>>;
/// <summary>
/// An animation spline represented by a set of keyframes, each representing an endpoint of Bezier curve.
/// </summary>
template<typename T>
using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;
API_TYPEDEF() using BezierCurve = Curve<T, BezierCurveKeyframe<T>>;

View File

@@ -2263,6 +2263,14 @@ void VisualScript::GetMethodSignature(int32 index, String& name, byte& flags, St
}
}
Variant VisualScript::InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const
{
auto& method = _methods[index];
Variant result;
VisualScriptingModule.InvokeMethod((void*)&method, instance, parameters, result);
return result;
}
Span<byte> VisualScript::GetMetaData(int32 typeID)
{
auto meta = Graph.Meta.GetEntry(typeID);

View File

@@ -267,6 +267,9 @@ public:
// Gets the signature data of the method.
API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array<String>& paramNames, API_PARAM(Out) Array<String>& paramTypeNames, API_PARAM(Out) Array<bool>& paramOuts);
// Invokes the method.
API_FUNCTION() Variant InvokeMethod(int32 index, const Variant& instance, Span<Variant> parameters) const;
// Gets the metadata of the script surface.
API_FUNCTION() Span<byte> GetMetaData(int32 typeID);

View File

@@ -21,8 +21,8 @@ namespace FlaxEditor.Content.Settings
{
new BuildTarget
{
Name = "Windows 64bit",
Output = "Output\\Win64",
Name = "Windows",
Output = "Output\\Windows",
Platform = BuildPlatform.Windows64,
Mode = BuildConfiguration.Development,
},
@@ -35,8 +35,8 @@ namespace FlaxEditor.Content.Settings
{
new BuildTarget
{
Name = "Windows 64bit",
Output = "Output\\Win64",
Name = "Windows",
Output = "Output\\Windows",
Platform = BuildPlatform.Windows64,
Mode = BuildConfiguration.Release,
},

View File

@@ -16,11 +16,16 @@
#include <iostream>
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE))
namespace
{
bool LogAfterInit = false, IsDuringLog = false;
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
bool IsWindowsSingleNewLineChar = false;
#endif
int LogTotalErrorsCnt = 0;
int32 LogTotalWriteSize = 0;
FileWriteStream* LogFile = nullptr;
CriticalSection LogLocker;
DateTime LogStartTime;
@@ -86,6 +91,11 @@ bool Log::Logger::Init()
}
LogTotalErrorsCnt = 0;
LogAfterInit = true;
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
String envVar;
Platform::GetEnvironmentVariable(TEXT("GITHUB_ACTION"), envVar);
IsWindowsSingleNewLineChar = envVar.HasChars();
#endif
// Write BOM (UTF-16 (LE); BOM: FF FE)
byte bom[] = { 0xFF, 0xFE };
@@ -127,6 +137,11 @@ void Log::Logger::Write(const StringView& msg)
printf("%s", ansi.Get());
#else
std::wcout.write(ptr, length);
#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR
if (IsWindowsSingleNewLineChar)
std::wcout.write(TEXT("\n"), 1); // Github Actions show logs with duplicated new-line characters so skip \r
else
#endif
std::wcout.write(TEXT(PLATFORM_LINE_TERMINATOR), ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1);
#endif
}
@@ -135,10 +150,17 @@ void Log::Logger::Write(const StringView& msg)
Platform::Log(msg);
// Write message to log file
if (LogAfterInit)
constexpr int32 LogMaxWriteSize = 1 * 1024 * 1024; // 1GB
if (LogAfterInit && LogTotalWriteSize < LogMaxWriteSize)
{
LogTotalWriteSize += length;
LogFile->WriteBytes(ptr, length * sizeof(Char));
LogFile->WriteBytes(TEXT(PLATFORM_LINE_TERMINATOR), (ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1) * sizeof(Char));
if (LogTotalWriteSize >= LogMaxWriteSize)
{
StringView endMessage(TEXT("Trimming log file.\n\n"));
LogFile->WriteBytes(endMessage.Get(), endMessage.Length() * sizeof(Char));
}
#if LOG_ENABLE_AUTO_FLUSH
LogFile->Flush();
#endif

View File

@@ -124,9 +124,9 @@ void BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix, Boundi
const auto ya = up * box.Minimum.Y;
const auto yb = up * box.Maximum.Y;
const auto backward = matrix.GetBackward();
const auto za = backward * box.Minimum.Z;
const auto zb = backward * box.Maximum.Z;
const auto forward = matrix.GetForward();
const auto za = forward * box.Minimum.Z;
const auto zb = forward * box.Maximum.Z;
const auto translation = matrix.GetTranslation();
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + translation;
@@ -146,9 +146,9 @@ void BoundingBox::Transform(const BoundingBox& box, const ::Transform& transform
const auto ya = up * box.Minimum.Y;
const auto yb = up * box.Maximum.Y;
const auto backward = Float3::Transform(Float3::Backward, transform.Orientation);
const auto za = backward * box.Minimum.Z;
const auto zb = backward * box.Maximum.Z;
const auto forward = Float3::Transform(Float3::Forward, transform.Orientation);
const auto za = forward * box.Minimum.Z;
const auto zb = forward * box.Maximum.Z;
const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + transform.Translation;
const auto max = Vector3::Max(xa, xb) + Vector3::Max(ya, yb) + Vector3::Max(za, zb) + transform.Translation;

View File

@@ -474,9 +474,9 @@ namespace FlaxEngine
var ya = up * box.Minimum.Y;
var yb = up * box.Maximum.Y;
Double3 backward = transform.Backward;
var za = backward * box.Minimum.Z;
var zb = backward * box.Maximum.Z;
Double3 forward = transform.Forward;
var za = forward * box.Minimum.Z;
var zb = forward * box.Maximum.Z;
var translation = transform.TranslationVector;
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + translation;
@@ -514,9 +514,9 @@ namespace FlaxEngine
var ya = up * box.Minimum.Y;
var yb = up * box.Maximum.Y;
Double3 backward = transform.Backward;
var za = backward * box.Minimum.Z;
var zb = backward * box.Maximum.Z;
Double3 forward = transform.Forward;
var za = forward * box.Minimum.Z;
var zb = forward * box.Maximum.Z;
var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + transform.Translation;
var max = Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + transform.Translation;

View File

@@ -136,12 +136,12 @@ void Matrix::Decompose(Float3& scale, Matrix3x3& rotation, Float3& translation)
const auto right = Float3::Cross(up, at);
rotation.SetRight(right);
rotation.SetUp(up);
rotation.SetBackward(at);
rotation.SetForward(at);
// In case of reflexions
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;
scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y;
scale.Z = Float3::Dot(at, GetBackward()) > 0.0f ? scale.Z : -scale.Z;
scale.Z = Float3::Dot(at, GetForward()) > 0.0f ? scale.Z : -scale.Z;
}
void Matrix::Decompose(Float3& scale, Matrix& rotation, Float3& translation) const

View File

@@ -225,7 +225,7 @@ void Matrix3x3::Decompose(Float3& scale, Matrix3x3& rotation) const
const auto right = Float3::Cross(up, at);
rotation.SetRight(right);
rotation.SetUp(up);
rotation.SetBackward(at);
rotation.SetForward(at);
// In case of reflexions
scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X;

View File

@@ -1824,6 +1824,7 @@ Variant::operator Float4() const
return Float4(*(Float3*)AsData, 0.0f);
case VariantType::Float4:
case VariantType::Color:
case VariantType::Quaternion:
return *(Float4*)AsData;
case VariantType::Double2:
return Float4(AsDouble2(), 0.0f, 0.0f);

View File

@@ -94,6 +94,13 @@ struct DebugLine
float TimeLeft;
};
struct DebugGeometryBuffer
{
GPUBuffer* Buffer;
float TimeLeft;
Matrix Transform;
};
struct DebugTriangle
{
Float3 V0;
@@ -122,12 +129,9 @@ struct DebugText3D
float TimeLeft;
};
PACK_STRUCT(struct Vertex {
Float3 Position;
Color32 Color;
});
typedef DebugDraw::Vertex Vertex;
GPU_CB_STRUCT(Data {
GPU_CB_STRUCT(ShaderData {
Matrix ViewProjection;
Float2 Padding;
float ClipPosZBias;
@@ -231,6 +235,7 @@ void TeleportList(const Float3& delta, Array<DebugText3D>& list)
struct DebugDrawData
{
Array<DebugGeometryBuffer> GeometryBuffers;
Array<DebugLine> DefaultLines;
Array<Vertex> OneFrameLines;
Array<DebugTriangle> DefaultTriangles;
@@ -244,7 +249,7 @@ struct DebugDrawData
inline int32 Count() const
{
return LinesCount() + TrianglesCount() + TextCount();
return LinesCount() + TrianglesCount() + TextCount() + GeometryBuffers.Count();
}
inline int32 LinesCount() const
@@ -280,6 +285,7 @@ struct DebugDrawData
inline void Update(float deltaTime)
{
UpdateList(deltaTime, GeometryBuffers);
UpdateList(deltaTime, DefaultLines);
UpdateList(deltaTime, DefaultTriangles);
UpdateList(deltaTime, DefaultWireTriangles);
@@ -784,7 +790,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
// Update constant buffer
const auto cb = DebugDrawShader->GetShader()->GetCB(0);
Data data;
ShaderData data;
Matrix vp;
Matrix::Multiply(view.View, view.Projection, vp);
Matrix::Transpose(vp, data.ViewProjection);
@@ -830,6 +836,22 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount);
}
// Geometries
for (auto& geometry : Context->DebugDrawDepthTest.GeometryBuffers)
{
auto tmp = data;
Matrix mvp;
Matrix::Multiply(geometry.Transform, vp, mvp);
Matrix::Transpose(mvp, tmp.ViewProjection);
context->UpdateCB(cb, &tmp);
auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault;
context->SetState(state->Get(enableDepthWrite, true));
context->BindVB(ToSpan(&geometry.Buffer, 1));
context->Draw(0, geometry.Buffer->GetElementsCount());
}
if (Context->DebugDrawDepthTest.GeometryBuffers.HasItems())
context->UpdateCB(cb, &data);
if (data.EnableDepthTest)
context->UnBindSR(0);
}
@@ -862,6 +884,19 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
context->BindVB(ToSpan(&vb, 1));
context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount);
}
// Geometries
for (auto& geometry : Context->DebugDrawDefault.GeometryBuffers)
{
auto tmp = data;
Matrix mvp;
Matrix::Multiply(geometry.Transform, vp, mvp);
Matrix::Transpose(mvp, tmp.ViewProjection);
context->UpdateCB(cb, &tmp);
context->SetState(DebugDrawPsLinesDefault.Get(false, false));
context->BindVB(ToSpan(&geometry.Buffer, 1));
context->Draw(0, geometry.Buffer->GetElementsCount());
}
}
// Text
@@ -1088,6 +1123,24 @@ void DebugDraw::DrawLines(const Span<Float3>& lines, const Matrix& transform, co
}
}
void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float duration, bool depthTest)
{
if (lines == nullptr || lines->GetSize() == 0)
return;
if (lines->GetSize() % (sizeof(Vertex) * 2) != 0)
{
DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array");
return;
}
// Draw lines
auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault;
auto& geometry = debugDrawData.GeometryBuffers.AddOne();
geometry.Buffer = lines;
geometry.TimeLeft = duration;
geometry.Transform = transform * Matrix::Translation(-Context->Origin);
}
void DebugDraw::DrawLines(const Array<Float3>& lines, const Matrix& transform, const Color& color, float duration, bool depthTest)
{
DrawLines(Span<Float3>(lines.Get(), lines.Count()), transform, color, duration, depthTest);
@@ -2147,6 +2200,7 @@ void DebugDraw::DrawText(const StringView& text, const Transform& transform, con
void DebugDraw::Clear(void* context)
{
DebugDraw::UpdateContext(context, MAX_float);
UpdateContext(context, MAX_float);
}
#endif

View File

@@ -6,6 +6,8 @@
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Core/Math/Color.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Types/Span.h"
struct RenderView;
@@ -14,6 +16,7 @@ class Light;
struct RenderContext;
class GPUTextureView;
class GPUContext;
class GPUBuffer;
class RenderTask;
class SceneRenderTask;
class Actor;
@@ -26,6 +29,14 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw);
/// <summary>
/// Vertex data for debug shapes.
/// </summary>
PACK_STRUCT(struct Vertex {
Float3 Position;
Color32 Color;
});
#if USE_EDITOR
/// <summary>
/// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state).
@@ -175,6 +186,15 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLines(const Span<Float3>& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines using the provided vertex buffer that contains pairs of Vertex elements. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
/// </summary>
/// <param name="lines">The GPU buffer with vertices for lines (must have multiple of 2 elements).</param>
/// <param name="transform">The custom matrix used to transform all line vertices.</param>
/// <param name="duration">The duration (in seconds). Use 0 to draw it only once.</param>
/// <param name="depthTest">If set to <c>true</c> depth test will be performed, otherwise depth will be ignored.</param>
API_FUNCTION() static void DrawLines(GPUBuffer* lines, const Matrix& transform, float duration = 0.0f, bool depthTest = true);
/// <summary>
/// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...).
/// </summary>
@@ -691,9 +711,9 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f);
/// <summary>
/// Clear all debug draw displayed on sceen.
/// Clears all debug shapes displayed on screen.
/// </summary>
/// <returns></returns>
/// <param name="context">The context.</param>
API_FUNCTION() static void Clear(void* context = nullptr);
};

View File

@@ -145,6 +145,12 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_BOOL_SWITCH("-monolog ", MonoLog);
PARSE_BOOL_SWITCH("-mute ", Mute);
PARSE_BOOL_SWITCH("-lowdpi ", LowDPI);
#if PLATFORM_LINUX && PLATFORM_SDL
PARSE_BOOL_SWITCH("-wayland ", Wayland);
PARSE_BOOL_SWITCH("-x11 ", X11);
#endif
#if USE_EDITOR
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
@@ -154,6 +160,7 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_ARG_SWITCH("-build ", Build);
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug);
PARSE_BOOL_SWITCH("-exit ", Exit);
PARSE_ARG_OPT_SWITCH("-play ", Play);
#endif
#if USE_EDITOR || !BUILD_RELEASE

View File

@@ -127,6 +127,20 @@ public:
/// </summary>
Nullable<bool> LowDPI;
#if PLATFORM_LINUX && PLATFORM_SDL
/// <summary>
/// -wayland (prefer Wayland over X11 as display server)
/// </summary>
Nullable<bool> Wayland;
/// <summary>
/// -x11 (prefer X11 over Wayland as display server)
/// </summary>
Nullable<bool> X11;
#endif
#if USE_EDITOR
/// <summary>
/// -project !path! (Startup project path)
@@ -168,6 +182,11 @@ public:
/// </summary>
Nullable<bool> ShaderDebug;
/// <summary>
/// -exit (exits the editor after startup and performing all queued actions). Usefull when invoking editor from CL/CD.
/// </summary>
Nullable<bool> Exit;
/// <summary>
/// -play !guid! ( Scene to play, can be empty to use default )
/// </summary>

View File

@@ -95,13 +95,14 @@ int32 Engine::Main(const Char* cmdLine)
CommandLine::Options.Std = true;
#endif
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
if (Platform::Init())
{
Platform::Fatal(TEXT("Cannot init platform."));
return -1;
}
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
Time::StartupTime = DateTime::Now();
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
#if USE_EDITOR

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Types/Nullable.h"
#include "Engine/Platform/Window.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/Managed/ManagedEditor.h"
@@ -13,10 +15,14 @@
#include "Engine/Engine/Engine.h"
#endif
Nullable<bool> Fullscreen;
Nullable<Float2> Size;
bool CursorVisible = true;
CursorLockMode CursorLock = CursorLockMode::None;
namespace
{
Nullable<bool> Fullscreen;
Nullable<Float2> Size;
bool CursorVisible = true;
CursorLockMode CursorLock = CursorLockMode::None;
bool LastGameViewportFocus = false;
}
class ScreenService : public EngineService
{
@@ -100,11 +106,25 @@ void Screen::SetCursorVisible(const bool value)
#else
const auto win = Engine::MainWindow;
#endif
bool focused = false;
if (win && Engine::HasGameViewportFocus())
{
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
focused = true;
}
else if (win)
win->SetCursor(CursorType::Default);
CursorVisible = value;
// Just enable relative mode when cursor is constrained and not visible
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
{
Input::Mouse->SetRelativeMode(true, win);
}
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
{
Input::Mouse->SetRelativeMode(false, win);
}
}
CursorLockMode Screen::GetCursorLock()
@@ -133,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode)
win->EndClippingCursor();
}
CursorLock = mode;
// Just enable relative mode when cursor is constrained and not visible
bool focused = win && Engine::HasGameViewportFocus();
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
{
Input::Mouse->SetRelativeMode(true, win);
}
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
{
Input::Mouse->SetRelativeMode(false, win);
}
}
GameWindowMode Screen::GetGameWindowMode()
@@ -190,7 +221,11 @@ void ScreenService::Update()
{
#if USE_EDITOR
// Sync current cursor state in Editor (eg. when viewport focus can change)
Screen::SetCursorVisible(CursorVisible);
const auto win = Editor::Managed->GetGameWindow(true);
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
if (gameViewportFocus != LastGameViewportFocus)
Screen::SetCursorVisible(CursorVisible);
LastGameViewportFocus = gameViewportFocus;
#endif
}

View File

@@ -5,6 +5,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Globals.h"
#define GPU_TASKS_USE_DEDICATED_CONTEXT 0
@@ -36,7 +37,8 @@ GPUTasksContext::~GPUTasksContext()
auto task = tasks[i];
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
{
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
if (!Globals::IsRequestingExit)
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
task->CancelSync();
}
}
@@ -60,7 +62,8 @@ void GPUTasksContext::OnCancelSync(GPUTask* task)
_tasksDone.Remove(task);
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
if (!Globals::IsRequestingExit)
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
}
void GPUTasksContext::OnFrameBegin()

View File

@@ -5,6 +5,7 @@
#include "GPUResourceProperty.h"
#include "GPUBufferDescription.h"
#include "PixelFormatExtensions.h"
#include "RenderTask.h"
#include "Async/Tasks/GPUCopyResourceTask.h"
#include "Engine/Core/Utilities.h"
#include "Engine/Core/Types/String.h"
@@ -358,6 +359,16 @@ void GPUBuffer::SetData(const void* data, uint32 size)
Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData"));
return;
}
if (_desc.Usage == GPUResourceUsage::Default && GPUDevice::Instance->IsRendering())
{
// Upload using the context (will use internal staging buffer inside command buffer)
RenderContext::GPULocker.Lock();
GPUDevice::Instance->GetMainContext()->UpdateBuffer(this, data, size);
RenderContext::GPULocker.Unlock();
return;
}
void* mapped = Map(GPUResourceMapMode::Write);
if (!mapped)
return;

Some files were not shown because too many files have changed in this diff Show More