Compare commits

...

85 Commits

Author SHA1 Message Date
mafiesto4 23e722a9fb Add timeout check for drag&drop on Linux to prevent deadlock 2021-04-23 22:58:09 +02:00
mafiesto4 27fbd896f7 Fix Win32 stack traces issue due to invalid search path for debug symbols
#457
2021-04-23 21:53:10 +02:00
mafiesto4 2b698ca8b0 Add support for fixed-array fields in scripting 2021-04-22 21:03:40 +02:00
mafiesto4 3e945fbe34 Bump up version number 2021-04-22 20:24:21 +02:00
mafiesto4 6821e3c370 Fix progress bar drawing precision and stability
#471
2021-04-22 19:25:01 +02:00
mafiesto4 d11a9d4d8f Fix typo 2021-04-21 23:47:28 +02:00
mafiesto4 3ed55c89f0 Add drag&drop support for Linux 2021-04-21 23:47:28 +02:00
mafiesto4 9381d0f5f1 Add automatic destination folder creation when saving Json asset 2021-04-21 23:47:27 +02:00
mafiesto4 a7a6dc7671 Add path utils to support StringView 2021-04-21 23:47:27 +02:00
mafiesto4 02856273ad Add error message box on Editor Options saving error 2021-04-21 23:47:27 +02:00
mafiesto4 8ba1affe9e Fix PrefabSpritesRenderer from script list 2021-04-21 10:14:59 +02:00
mafiesto4 ba2b7939a3 Merge branch 'TaylerMauk-fix-issue-468' 2021-04-21 10:12:08 +02:00
mafiesto4 7739078aad Merge branch 'fix-issue-468' of git://github.com/TaylerMauk/FlaxEngine into TaylerMauk-fix-issue-468 2021-04-21 10:12:05 +02:00
mafiesto4 851c6d972c Merge branch 'TaylerMauk-fix-issue-460' 2021-04-21 10:04:38 +02:00
Tayler Mauk 3410bd5380 Standardize variable names 2021-04-20 22:31:15 -07:00
Tayler Mauk dc0f77ae2a Fix issue #468 2021-04-20 22:24:37 -07:00
Tayler Mauk 3e73f40c0f Fix issue #460 2021-04-20 21:23:42 -07:00
mafiesto4 28c0552e0a Fix diff-serialization and deserialization of C# lists
#347
2021-04-20 16:06:32 +02:00
mafiesto4 9857fb12c4 Fix crash when using C# script that inherits from C++ script 2021-04-20 11:47:32 +02:00
mafiesto4 5b31a8222c Fix crash when using C# script that inherits from C++ script 2021-04-20 11:47:21 +02:00
mafiesto4 1eacb36256 Minor tweaks 2021-04-20 10:58:14 +02:00
mafiesto4 eabab8301d Fix compilation of bindings code with API_EVENT in some cases
#459
2021-04-20 10:35:09 +02:00
mafiesto4 20331bc858 Fix crash when using Actor GetScript or GetChild with invalid index
#462
2021-04-20 10:12:57 +02:00
mafiesto4 6387e87a30 Merge branch 'stefnotch-fix-get-child-at' 2021-04-20 10:05:31 +02:00
stefnotch 08d08133c1 GetChildAt should do it in reverse order to respect z order 2021-04-18 09:49:25 +02:00
mafiesto4 0d422ec169 Remove unused include 2021-04-17 19:38:31 +02:00
mafiesto4 c5dd3674c1 Optimize WriteStream::WriteText 2021-04-17 19:38:23 +02:00
mafiesto4 cf0a16e875 Fix handling crash when loading scripting api bindings cache during build 2021-04-17 19:37:51 +02:00
mafiesto4 841a336581 Fix using AssetsCache in game code 2021-04-17 19:37:11 +02:00
mafiesto4 b2729e35c2 Fixes for UWP build 2021-04-17 19:36:38 +02:00
mafiesto4 698ce4c0ce Fix crash on bokeh in dof on d3d12 2021-04-13 19:03:04 +02:00
mafiesto4 df07529f21 Fix game build 2021-04-13 09:34:23 +02:00
mafiesto4 bc7660fbf8 Merge branch 'TaylerMauk-bugfix-3d-audio-placement' 2021-04-13 09:32:10 +02:00
mafiesto4 857b7e522f Merge branch 'bugfix-3d-audio-placement' of git://github.com/TaylerMauk/FlaxEngine into TaylerMauk-bugfix-3d-audio-placement 2021-04-13 09:32:05 +02:00
mafiesto4 af9962c30f Fix render layers mask usage for shadows rendering 2021-04-13 09:28:34 +02:00
Tayler Mauk a7e29c5784 Fix 3D audio placement for XAudio2 and OpenAL
Corrected inverted x-axis for XAudio2
Corrected inverted z-axis for OpenAL
2021-04-12 22:06:01 -07:00
mafiesto4 3a03fe5f7f Format code 2021-04-12 23:26:49 +02:00
mafiesto4 f1eebd3835 Merge branch 'jb-perrier-pxupvector' 2021-04-12 23:25:32 +02:00
mafiesto4 40ed499889 Merge branch 'pxupvector' of git://github.com/jb-perrier/FlaxEngine into jb-perrier-pxupvector 2021-04-12 23:25:24 +02:00
mafiesto4 55155630ca Fix duplicating spline
#452
2021-04-12 21:06:34 +02:00
mafiesto4 3bfeb1db76 Add BoxBrush::SetMaterial 2021-04-12 19:07:18 +02:00
mafiesto4 b2d041c9b3 Merge branch 'stefnotch-tweak-random-byte' 2021-04-12 18:37:22 +02:00
mafiesto4 e5ab536db4 Merge branch 'tweak-random-byte' of git://github.com/stefnotch/FlaxEngine into stefnotch-tweak-random-byte 2021-04-12 18:37:14 +02:00
mafiesto4 0dafa2f562 Expose TickData to scripting API 2021-04-12 13:24:59 +02:00
mafiesto4 5ab554f61c Fix using Camera refs 2021-04-12 12:57:15 +02:00
mafiesto4 8a725ec243 Fix natvis for StringBuilder 2021-04-12 12:51:26 +02:00
mafiesto4 cd1f57b634 Fix doc comments 2021-04-12 12:51:10 +02:00
mafiesto4 c47db14c83 Fix using Sorting in C++ game code 2021-04-12 12:50:59 +02:00
mafiesto4 81305a10cf Fix crash on relocation in BitArray 2021-04-12 12:50:46 +02:00
stefnotch a58645da0f Slightly improve the random byte generation code 2021-04-12 10:46:00 +02:00
mafiesto4 27400cffe4 Merge branch 'stefnotch-fix-spline-precision' 2021-04-10 11:12:48 +02:00
stefnotch 5b1c5824c8 Fix #408 by being more precise in some places
IEEE floating point numbers are fun ™️
2021-04-10 09:13:19 +02:00
mafiesto4 3e080c44c6 Fix using curl lib on Linux (use self compiled static lib) 2021-04-08 22:12:55 +02:00
jb-perrier eff5d0899d Order + Attributes + Return _up. 2021-04-08 20:35:01 +02:00
mafiesto4 9a17f2141b Add lock for Gameplay Globals access 2021-04-07 22:21:37 +02:00
mafiesto4 d073a13f57 Fix removing Gameplay Globals
#427
2021-04-07 21:10:09 +02:00
mafiesto4 7e622a69a6 Fix AutoFocus saved in data for controls 2021-04-07 20:53:18 +02:00
mafiesto4 6001eb88fa Fix Dropdown control
#334
2021-04-07 20:52:22 +02:00
jb-perrier 1610af3f3f Merge branch 'master' of https://github.com/FlaxEngine/FlaxEngine into pxupvector 2021-04-07 17:01:52 +02:00
jb-perrier 60e926a62f Serialize UpDirection. 2021-04-07 17:01:37 +02:00
mafiesto4 ecc055d8b8 Fix compilation warnings 2021-04-06 12:40:24 +02:00
mafiesto4 2622482b12 Change libcurl dependency on Linux 2021-04-06 12:17:32 +02:00
mafiesto4 ca6a075d8c Fix typos and code style 2021-04-06 11:46:30 +02:00
mafiesto4 ca3d4a84ad Fix text wrapping when no scene is loaded 2021-04-06 11:46:23 +02:00
mafiesto4 6eec0b396d Merge branch 'GoaLitiuM-capsule_physics_queries' 2021-04-06 11:42:20 +02:00
GoaLitiuM e7edb9d38c Add sweep and overlap physics queries for capsule shapes 2021-04-05 19:36:36 +03:00
mafiesto4 2f42321551 Merge branch 'stefnotch-dpi-scale-tool-windows' 2021-03-31 23:54:08 +02:00
mafiesto4 98da5525f3 Merge branch 'dpi-scale-tool-windows' of git://github.com/stefnotch/FlaxEngine into stefnotch-dpi-scale-tool-windows 2021-03-31 23:54:02 +02:00
mafiesto4 20f9bdc209 Fix regression in adding model material slots 2021-03-31 21:25:12 +02:00
stefnotch 93f23af92e Scale dockwindow size by DPI scale
For those gangstas with 4k screens ;)
2021-03-30 23:03:56 +02:00
mafiesto4 d8a4814542 Merge branch 'jb-perrier-overwriteprompt' 2021-03-30 22:30:43 +02:00
mafiesto4 6c37673787 Merge branch 'overwriteprompt' of git://github.com/jb-perrier/FlaxEngine into jb-perrier-overwriteprompt 2021-03-30 22:30:41 +02:00
mafiesto4 77f9716932 Fix selecting spline points
#409
2021-03-30 22:27:24 +02:00
mafiesto4 ece77065ca Merge branch 'stefnotch-patch-8' 2021-03-30 22:10:44 +02:00
mafiesto4 15186cafe0 Merge branch 'patch-8' of git://github.com/stefnotch/FlaxEngine into stefnotch-patch-8 2021-03-30 22:10:39 +02:00
mafiesto4 b35795f1ff Merge branch 'stefnotch-dpi-root-window-fix' 2021-03-30 22:09:24 +02:00
mafiesto4 d667084d11 Merge branch 'dpi-root-window-fix' of git://github.com/stefnotch/FlaxEngine into stefnotch-dpi-root-window-fix 2021-03-30 22:09:18 +02:00
mafiesto4 f8d6f8ffb5 Fix using references to projects outside the main project
#397
2021-03-30 22:07:40 +02:00
jb-perrier a3ca6b0dca Add overwrite prompt for SaveDialog. 2021-03-30 21:47:18 +02:00
jb-perrier 2524d93f6b Add Set/Get Up direction. 2021-03-30 21:43:03 +02:00
mafiesto4 9581f7b10e Fix using cross-module references inside C++ game project 2021-03-29 20:37:43 +02:00
stefnotch 2abb215034 Update CONTRIBUTING.md 2021-03-29 12:06:24 +02:00
stefnotch 12fd7274a9 Fix DPI regression: use the overrideable RootWindow instead of _root 2021-03-29 10:54:48 +02:00
mafiesto4 7456bf38f7 Fix for editor on linux 2021-03-28 23:26:08 +02:00
mafiesto4 d85a020971 Fix picking game binaries target when EditorTarget is not SetupTargetEnvironment
#398
2021-03-28 23:25:52 +02:00
90 changed files with 1565 additions and 408 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Install dependencies
run: |
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext curl libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libcurl4-gnutls-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
- name: Checkout repo
uses: actions/checkout@v2
- name: Checkout LFS
+16
View File
@@ -32,3 +32,19 @@ Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap).
Thank you for taking interest in contributing to Flax!
## **Common issues**
Below are some common issues that someone working with the FlaxEngine source code might run into. Hopefully some of those issues will get fixed in the future. If you know how, please contribute!
* Missing MSVC toolset
* Install it through the Visual Studio Installer
* Building or attaching fails
* Run `GenerateProjectFiles.bat`
* Rebuild `Flax.Build`
* Make sure that there isn't a stray FlaxEngine process running in the background
* First start Flax and then attach the C# debugger
* Configure the C# FlaxEngine project by going into the project properties, then the debug tab and selecting "Start external program" `Flax\FlaxEngine\Binaries\Editor\Win64\Debug\FlaxEditor.exe`
* Then you can also set command line arguments such as `-project "C:\Users\PROFILE\Documents\Flax Projects\FlaxSamples\BasicTemplate"`
* Git LFS
* Push with `git push --no-verify`
+1 -1
View File
@@ -3,7 +3,7 @@
"Version": {
"Major": 1,
"Minor": 1,
"Build": 6217
"Build": 6218
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",
+1 -1
View File
@@ -47,7 +47,7 @@ Flax Visual Studio extension provides better programming workflow, C# scripts de
* Install Visual Studio Code
* Install Mono ([https://www.mono-project.com/download/stable](https://www.mono-project.com/download/stable))
* Install Git with LFS
* Install requried packages: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev nuget autoconf libogg-dev automake build-essential gettext cmake python curl libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libcurl4-gnutls-dev`
* Install requried packages: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev nuget autoconf libogg-dev automake build-essential gettext cmake python libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev`
* Install compiler `sudo apt-get install clang lldb lld` (Clang 6 or newer)
* Clone repo (with LFS)
* Run `./GenerateProjectFiles.sh`
@@ -43,7 +43,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
const auto uwpDataPath = platformDataPath / (isXboxOne ? TEXT("XboxOne") : TEXT("UWP")) / TEXT("Binaries");
const auto gameSettings = GameSettings::Get();
const auto platformSettings = UWPPlatformSettings::Get();
Array<byte> fileTemplate;
StringAnsi fileTemplate;
// Copy binaries
const auto binPath = data.GetGameBinariesPath();
@@ -149,12 +149,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (!FileSystem::FileExists(dstAssemblyInfoPath))
{
// Get template
if (File::ReadAllBytes(srcAssemblyInfoPath, fileTemplate))
if (File::ReadAllText(srcAssemblyInfoPath, fileTemplate))
{
data.Error(TEXT("Failed to load AssemblyInfo.cs template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstAssemblyInfoPath);
@@ -163,7 +162,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
{
auto now = DateTime::Now();
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, gameSettings->ProductName.ToStringAnsi()
, gameSettings->CompanyName.ToStringAnsi()
, now.GetYear()
@@ -182,12 +181,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (!FileSystem::FileExists(dstAppPath))
{
// Get template
if (File::ReadAllBytes(srcAppPath, fileTemplate))
if (File::ReadAllText(srcAppPath, fileTemplate))
{
data.Error(TEXT("Failed to load App.cs template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstAppPath);
@@ -195,7 +193,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, defaultNamespace.ToStringAnsi() // {0} Default Namespace
);
hasError = file->HasError();
@@ -211,12 +209,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
const auto srcFlaxGeneratedPath = uwpDataPath / TEXT("FlaxGenerated.cs");
{
// Get template
if (File::ReadAllBytes(srcFlaxGeneratedPath, fileTemplate))
if (File::ReadAllText(srcFlaxGeneratedPath, fileTemplate))
{
data.Error(TEXT("Failed to load FlaxGenerated.cs template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Prepare
StringAnsi autoRotationPreferences;
@@ -252,7 +249,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, autoRotationPreferences.Get()
, preferredLaunchWindowingMode.Get()
);
@@ -272,12 +269,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (!FileSystem::FileExists(dstSolutionPath))
{
// Get template
if (File::ReadAllBytes(srcSolutionPath, fileTemplate))
if (File::ReadAllText(srcSolutionPath, fileTemplate))
{
data.Error(TEXT("Failed to load Solution.sln template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Write data to file
auto file = FileWriteStream::Open(dstSolutionPath);
@@ -285,7 +281,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Project Name
, mode // {1} Platform Mode
, projectGuid.ToStringAnsi() // {2} Project ID
@@ -305,12 +301,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
const auto srcProjectPath = uwpDataPath / TEXT("Project.csproj");
{
// Get template
if (File::ReadAllBytes(srcProjectPath, fileTemplate))
if (File::ReadAllText(srcProjectPath, fileTemplate))
{
data.Error(TEXT("Failed to load Project.csproj template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Build included files data
StringBuilder filesInclude(2048);
@@ -334,7 +329,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Project Name
, mode // {1} Platform Mode
, projectGuid.Get() // {2} Project ID
@@ -357,12 +352,11 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (!FileSystem::FileExists(dstManifestPath))
{
// Get template
if (File::ReadAllBytes(srcManifestPath, fileTemplate))
if (File::ReadAllText(srcManifestPath, fileTemplate))
{
data.Error(TEXT("Failed to load Package.appxmanifest template."));
return true;
}
fileTemplate[fileTemplate.Count() - 1] = 0;
// Build included files data
StringBuilder filesInclude(2048);
@@ -385,7 +379,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
if (file)
{
file->WriteTextFormatted(
(char*)fileTemplate.Get()
fileTemplate.Get()
, projectName.ToStringAnsi() // {0} Display Name
, gameSettings->CompanyName.ToStringAnsi() // {1} Company Name
, productId.ToStringAnsi() // {2} Product ID
@@ -46,10 +46,7 @@ namespace FlaxEditor.CustomEditors
if (values.HasReferenceValue)
{
var v = (IList)values.ReferenceValue;
// Get the reference value if collections are the same size
if (v != null && values.Count == v.Count)
if (values.ReferenceValue is IList v && values.Count == v.Count && v.Count > index)
{
_referenceValue = v[index];
_hasReferenceValue = true;
+4 -1
View File
@@ -59,7 +59,10 @@ namespace FlaxEditor.GUI.Docking
/// <summary>
/// Gets the default window size.
/// </summary>
public virtual Vector2 DefaultSize => new Vector2(900, 580);
/// <remarks>
/// Scaled by the DPI, because the window should be large enough for its content on every monitor
/// </remarks>
public virtual Vector2 DefaultSize => new Vector2(900, 580) * DpiScale;
/// <summary>
/// Gets the serialization typename.
+12 -1
View File
@@ -22,14 +22,25 @@ namespace FlaxEditor.Modules
/// <seealso cref="FlaxEditor.SceneGraph.RootNode" />
public class ScenesRootNode : RootNode
{
private readonly Editor _editor;
/// <inheritdoc />
public ScenesRootNode()
{
_editor = Editor.Instance;
}
/// <inheritdoc />
public override void Spawn(Actor actor, Actor parent)
{
Editor.Instance.SceneEditing.Spawn(actor, parent);
_editor.SceneEditing.Spawn(actor, parent);
}
/// <inheritdoc />
public override Undo Undo => Editor.Instance.Undo;
/// <inheritdoc />
public override List<SceneGraphNode> Selection => _editor.SceneEditing.Selection;
}
/// <summary>
+4 -1
View File
@@ -150,7 +150,10 @@ namespace FlaxEditor.Options
private void Save()
{
// Update file
Editor.SaveJsonAsset(_optionsFilePath, Options);
if (Editor.SaveJsonAsset(_optionsFilePath, Options))
{
MessageBox.Show(string.Format("Failed to save editor option to '{0}'. Ensure that directory exists and program has access to it.", _optionsFilePath), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Special case for editor analytics
var editorAnalyticsTrackingFile = Path.Combine(Editor.LocalCachePath, "noTracking");
+10 -1
View File
@@ -29,7 +29,7 @@ namespace FlaxEditor.SceneGraph.Actors
public override bool CanBeSelectedDirectly => true;
public override bool CanDuplicate => true;
public override bool CanDuplicate => !Root?.Selection.Contains(ParentNode) ?? true;
public override bool CanDelete => true;
@@ -367,6 +367,15 @@ namespace FlaxEditor.SceneGraph.Actors
}
}
/// <inheritdoc />
public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal)
{
// Select only spline points
normal = Vector3.Up;
distance = float.MaxValue;
return false;
}
/// <inheritdoc />
public override void OnDispose()
{
+6
View File
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
@@ -146,5 +147,10 @@ namespace FlaxEditor.SceneGraph
/// Gets the undo.
/// </summary>
public abstract Undo Undo { get; }
/// <summary>
/// Gets the list of selected scene graph nodes in the editor context.
/// </summary>
public abstract List<SceneGraphNode> Selection { get; }
}
}
+3 -1
View File
@@ -57,7 +57,9 @@ namespace FlaxEditor.SceneGraph
/// </summary>
public virtual RootNode Root => ParentNode?.Root;
/// <inheritdoc />
/// <summary>
/// Gets or sets the transform of the node.
/// </summary>
public abstract Transform Transform { get; set; }
/// <summary>
+21 -13
View File
@@ -341,7 +341,21 @@ void ScriptsBuilder::GetBinariesConfiguration(const Char*& target, const Char*&
target = platform = architecture = configuration = nullptr;
return;
}
target = Editor::Project->EditorTarget.GetText();
// Pick game target
if (Editor::Project->EditorTarget.HasChars())
{
target = Editor::Project->EditorTarget.Get();
}
else if (Editor::Project->GameTarget.HasChars())
{
target = Editor::Project->GameTarget.Get();
}
else
{
target = TEXT("");
LOG(Error, "Missing editor/game targets in project. Please specify EditorTarget and GameTarget properties in .flaxproj file.");
}
#if PLATFORM_WINDOWS
platform = TEXT("Windows");
@@ -551,19 +565,13 @@ bool ScriptsBuilderService::Init()
}
// Verify project
if (project->EditorTarget.IsEmpty() || project->GameTarget.IsEmpty())
if (project->EditorTarget.IsEmpty())
{
const String& name = project->Name;
String codeName;
for (int32 i = 0; i < name.Length(); i++)
{
Char c = name[i];
if (StringUtils::IsAlnum(c) && c != ' ' && c != '.')
codeName += c;
}
project->GameTarget = codeName + TEXT("Target");
project->EditorTarget = codeName + TEXT("EditorTarget");
LOG(Warning, "Missing EditorTarget property in opened project, using deducted target name {0}", Editor::Project->EditorTarget);
LOG(Warning, "Missing {0} property in opened project", TEXT("EditorTarget"));
}
if (project->GameTarget.IsEmpty())
{
LOG(Warning, "Missing {0} property in opened project", TEXT("GameTarget"));
}
// Remove any remaining files from previous Editor run hot-reloads
@@ -26,6 +26,7 @@ namespace FlaxEditor.Viewport
/// <seealso cref="IGizmoOwner" />
public class PrefabWindowViewport : PrefabPreview, IEditorPrimitivesOwner
{
[HideInEditor]
private sealed class PrefabSpritesRenderer : MainEditorGizmoViewport.EditorSpritesRenderer
{
public PrefabWindowViewport Viewport;
+12 -3
View File
@@ -399,9 +399,18 @@ namespace FlaxEditor.Windows.Assets
ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length];
for (int i = 0; i < value.Length; i++)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
if (value[i] != null)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
}
else
{
materials[i] = null;
names[i] = "Material " + i;
shadowsModes[i] = ShadowsCastingMode.All;
}
}
Asset.SetupMaterialSlots(value.Length);
@@ -36,6 +36,9 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
public override Undo Undo => _window.Undo;
/// <inheritdoc />
public override List<SceneGraphNode> Selection => _window.Selection;
}
/// <summary>
@@ -511,9 +511,18 @@ namespace FlaxEditor.Windows.Assets
ShadowsCastingMode[] shadowsModes = new ShadowsCastingMode[value.Length];
for (int i = 0; i < value.Length; i++)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
if (value[i] != null)
{
materials[i] = value[i].Material;
names[i] = value[i].Name;
shadowsModes[i] = value[i].ShadowsMode;
}
else
{
materials[i] = null;
names[i] = "Material " + i;
shadowsModes[i] = ShadowsCastingMode.All;
}
}
Asset.SetupMaterialSlots(value.Length);
+4 -1
View File
@@ -209,9 +209,11 @@ namespace FlaxEditor.Windows
public override void Draw()
{
var style = Style.Current;
// Draw overlay
string overlayText = null;
var state = Editor.StateMachine.CurrentState;
var textWrap = TextWrapping.NoWrap;
if (state is LoadingState)
{
overlayText = "Loading...";
@@ -223,10 +225,11 @@ namespace FlaxEditor.Windows
else if (((ContainerControl)_tree.GetChild(0)).ChildrenCount == 0)
{
overlayText = "No scene\nOpen one from the content window";
textWrap = TextWrapping.WrapWords;
}
if (overlayText != null)
{
Render2D.DrawText(Style.Current.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center);
Render2D.DrawText(style.FontLarge, overlayText, GetClientArea(), style.ForegroundDisabled, TextAlignment.Center, TextAlignment.Center, textWrap);
}
base.Draw();
@@ -18,7 +18,7 @@
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#define FLAX_POS_TO_OAL(vec) -vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f
#define FLAX_POS_TO_OAL(vec) -vec.X * 0.01f, vec.Y * 0.01f, -vec.Z * 0.01f
namespace ALC
{
@@ -26,7 +26,7 @@
#define MAX_OUTPUT_CHANNELS 8
#define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS)
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(-vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f)
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * 0.01f, vec.Y * 0.01f, vec.Z * 0.01f)
#define FLAX_VEC_TO_XAUDIO(vec) (*((X3DAUDIO_VECTOR*)&vec))
namespace XAudio2
+1 -1
View File
@@ -31,7 +31,7 @@ DECLARE_ENUM_OPERATORS(AssetsCacheFlags);
/// <summary>
/// Flax Game Engine assets cache container
/// </summary>
class AssetsCache
class FLAXENGINE_API AssetsCache
{
public:
@@ -41,6 +41,18 @@ bool CreateJson::Create(const StringView& path, StringAnsiView& data, StringAnsi
LOG(Warning, "Asset will have different type name {0} -> {1}", typeName, String(dataTypename.Get()));
}
}
else
{
const String directory = StringUtils::GetDirectoryName(path);
if (!FileSystem::DirectoryExists(directory))
{
if (FileSystem::CreateDirectory(directory))
{
LOG(Warning, "Failed to create directory");
return true;
}
}
}
rapidjson_flax::StringBuffer buffer;
+1 -1
View File
@@ -236,7 +236,7 @@ public:
return;
ASSERT(capacity >= 0);
const int32 count = preserveContents ? (_count < capacity ? _count : capacity) : 0;
_allocation.Relocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1), _count, count);
_allocation.Relocate(Math::Max<ItemType>(capacity / sizeof(ItemType), 1), _count / sizeof(ItemType), count / sizeof(ItemType));
_capacity = capacity;
_count = count;
}
+1 -1
View File
@@ -17,7 +17,7 @@ public:
/// <summary>
/// Helper collection used by the sorting algorithms. Implements stack using single linear allocation with variable capacity.
/// </summary>
class SortingStack
class FLAXENGINE_API SortingStack
{
public:
+3 -3
View File
@@ -37,21 +37,21 @@ public:
virtual ~ISerializable() = default;
/// <summary>
/// Serialize object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties.
/// Serializes object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties.
/// </summary>
/// <param name="stream">The output stream.</param>
/// <param name="otherObj">The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties.</param>
virtual void Serialize(SerializeStream& stream, const void* otherObj) = 0;
/// <summary>
/// Deserialize object from the input stream
/// Deserializes object from the input stream.
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="modifier">The deserialization modifier object. Always valid.</param>
virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0;
/// <summary>
/// Deserialize object from the input stream child member. Won't deserialize it if member is missing.
/// Deserializes object from the input stream child member. Won't deserialize it if member is missing.
/// </summary>
/// <param name="stream">The input stream.</param>
/// <param name="memberName">The input stream member to lookup.</param>
+12
View File
@@ -27,6 +27,18 @@ bool StringView::operator!=(const String& other) const
return StringUtils::Compare(this->GetText(), *other) != 0;
}
String StringView::Left(int32 count) const
{
const int32 countClamped = count < 0 ? 0 : count < Length() ? count : Length();
return String(**this, countClamped);
}
String StringView::Right(int32 count) const
{
const int32 countClamped = count < 0 ? 0 : count < Length() ? count : Length();
return String(**this + Length() - countClamped);
}
String StringView::Substring(int32 startIndex) const
{
ASSERT(startIndex >= 0 && startIndex < Length());
+14
View File
@@ -321,6 +321,20 @@ public:
public:
/// <summary>
/// Gets the left most given number of characters.
/// </summary>
/// <param name="count">The characters count.</param>
/// <returns>The substring.</returns>
String Left(int32 count) const;
/// <summary>
/// Gets the string of characters from the right (end of the string).
/// </summary>
/// <param name="count">The characters count.</param>
/// <returns>The substring.</returns>
String Right(int32 count) const;
/// <summary>
/// Retrieves substring created from characters starting from startIndex to the String end.
/// </summary>
+1 -2
View File
@@ -4,11 +4,10 @@
#include "BaseTypes.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Formatting.h"
#include "Engine/Core/Templates.h"
/// <summary>
/// Represents the version number made of major, minor, patch and build numbers.
/// Represents the version number made of major, minor, build and revision numbers.
/// </summary>
struct FLAXENGINE_API Version
{
+7
View File
@@ -59,6 +59,7 @@ GameplayGlobals::GameplayGlobals(const SpawnParams& params, const AssetInfo* inf
Dictionary<String, Variant> GameplayGlobals::GetValues() const
{
ScopeLock lock(Locker);
Dictionary<String, Variant> result;
for (auto& e : Variables)
result.Add(e.Key, e.Value.Value);
@@ -67,6 +68,7 @@ Dictionary<String, Variant> GameplayGlobals::GetValues() const
void GameplayGlobals::SetValues(const Dictionary<String, Variant>& values)
{
ScopeLock lock(Locker);
for (auto& e : values)
{
bool hasKey = false;
@@ -97,6 +99,7 @@ void GameplayGlobals::SetValues(const Dictionary<String, Variant>& values)
Dictionary<String, Variant> GameplayGlobals::GetDefaultValues() const
{
ScopeLock lock(Locker);
Dictionary<String, Variant> result;
for (auto& e : Variables)
result.Add(e.Key, e.Value.DefaultValue);
@@ -105,6 +108,7 @@ Dictionary<String, Variant> GameplayGlobals::GetDefaultValues() const
void GameplayGlobals::SetDefaultValues(const Dictionary<String, Variant>& values)
{
ScopeLock lock(Locker);
for (auto& e : values)
{
bool hasKey = false;
@@ -135,12 +139,14 @@ void GameplayGlobals::SetDefaultValues(const Dictionary<String, Variant>& values
Variant GameplayGlobals::GetValue(const StringView& name) const
{
ScopeLock lock(Locker);
auto e = Variables.TryGet(name);
return e ? e->Value : Variant::Zero;
}
void GameplayGlobals::SetValue(const StringView& name, const Variant& value)
{
ScopeLock lock(Locker);
auto e = Variables.TryGet(name);
if (e)
{
@@ -150,6 +156,7 @@ void GameplayGlobals::SetValue(const StringView& name, const Variant& value)
void GameplayGlobals::ResetValues()
{
ScopeLock lock(Locker);
for (auto& e : Variables)
{
e.Value.Value = e.Value.DefaultValue;
+1 -1
View File
@@ -22,7 +22,7 @@ public:
/// Engine subsystem updating data.
/// Used to invoke game logic updates, physics updates and rendering with possibly different frequencies.
/// </summary>
class TickData
class FLAXENGINE_API TickData
{
public:
+1 -1
View File
@@ -251,7 +251,7 @@ public:
/// <summary>
/// The scene rendering camera. Can be used to override the rendering view properties based on the current camera setup.
/// </summary>
API_FIELD() Camera* Camera = nullptr;
API_FIELD() ScriptingObjectReference<Camera> Camera;
/// <summary>
/// The render view description.
@@ -1032,7 +1032,7 @@ void StagingManagerVulkan::Dispose()
ScopeLock lock(_locker);
#if !BUILD_RELEASE
LOG(Info, "Vulakn staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize));
LOG(Info, "Vulkan staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize));
#endif
// Release buffers and clear memory
+12 -1
View File
@@ -347,6 +347,12 @@ void Actor::SetOrderInParent(int32 index)
}
}
Actor* Actor::GetChild(int32 index) const
{
CHECK_RETURN(index >= 0 && index < Children.Count(), nullptr);
return Children[index];
}
Actor* Actor::GetChild(const StringView& name) const
{
for (int32 i = 0; i < Children.Count(); i++)
@@ -354,7 +360,6 @@ Actor* Actor::GetChild(const StringView& name) const
if (Children[i]->GetName() == name)
return Children[i];
}
return nullptr;
}
@@ -482,6 +487,12 @@ void Actor::SetName(const StringView& value)
Level::callActorEvent(Level::ActorEventType::OnActorNameChanged, this, nullptr);
}
Script* Actor::GetScript(int32 index) const
{
CHECK_RETURN(index >= 0 && index < Scripts.Count(), nullptr);
return Scripts[index];
}
Script* Actor::GetScript(const MClass* type) const
{
CHECK_RETURN(type, nullptr);
+2 -8
View File
@@ -192,10 +192,7 @@ public:
/// </summary>
/// <param name="index">The child actor index.</param>
/// <returns>The child actor (always valid).</returns>
API_FUNCTION() FORCE_INLINE Actor* GetChild(int32 index) const
{
return Children[index];
}
API_FUNCTION() Actor* GetChild(int32 index) const;
/// <summary>
/// Gets the child actor with the given name.
@@ -266,10 +263,7 @@ public:
/// </summary>
/// <param name="index">The script index.</param>
/// <returns>The script (always valid).</returns>
API_FUNCTION() FORCE_INLINE Script* GetScript(int32 index) const
{
return Scripts[index];
}
API_FUNCTION() Script* GetScript(int32 index) const;
/// <summary>
/// Gets the script of the given type from this actor.
+33 -1
View File
@@ -128,6 +128,13 @@ void BoxBrush::GetSurfaces(CSG::Surface surfaces[6])
}
}
void BoxBrush::SetMaterial(int32 surfaceIndex, MaterialBase* material)
{
CHECK(Math::IsInRange(surfaceIndex, 0, 5));
Surfaces[surfaceIndex].Material = material;
OnBrushModified();
}
bool BoxBrush::Intersects(int32 surfaceIndex, const Ray& ray, float& distance, Vector3& normal) const
{
distance = MAX_float;
@@ -232,6 +239,26 @@ void BoxBrush::OnDebugDrawSelected()
#endif
Scene* BoxBrush::GetBrushScene() const
{
return GetScene();
}
Guid BoxBrush::GetBrushID() const
{
return GetID();
}
bool BoxBrush::CanUseCSG() const
{
return IsActiveInHierarchy();
}
CSG::Mode BoxBrush::GetBrushMode() const
{
return _mode;
}
void BoxBrush::GetSurfaces(Array<CSG::Surface>& surfaces)
{
surfaces.Clear();
@@ -240,6 +267,11 @@ void BoxBrush::GetSurfaces(Array<CSG::Surface>& surfaces)
GetSurfaces(surfaces.Get());
}
int32 BoxBrush::GetSurfacesCount()
{
return 6;
}
void BoxBrush::OnTransformChanged()
{
// Base
@@ -272,7 +304,7 @@ void BoxBrush::OnParentChanged()
{
// Base
Actor::OnParentChanged();
if (!IsDuringPlay())
return;
+14 -27
View File
@@ -153,6 +153,13 @@ public:
/// <param name="surfaces">Surfaces</param>
void GetSurfaces(CSG::Surface surfaces[6]);
/// <summary>
/// Sets the brush surface material.
/// </summary>
/// <param name="surfaceIndex">The brush surface index.</param>
/// <param name="material">The material.</param>
API_FUNCTION() void SetMaterial(int32 surfaceIndex, MaterialBase* material);
public:
/// <summary>
@@ -169,7 +176,7 @@ public:
/// Otherwise performs simple <see cref="BoundingBox"/> vs <see cref="Ray"/> test.
/// For more efficient collisions detection and ray casting use physics.
/// </summary>
/// <param name="surfaceIndex">The brush surface index..</param>
/// <param name="surfaceIndex">The brush surface index.</param>
/// <param name="ray">The ray to test.</param>
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
@@ -179,7 +186,7 @@ public:
/// <summary>
/// Gets the brush surface triangles array (group by 3 vertices).
/// </summary>
/// <param name="surfaceIndex">The brush surface index..</param>
/// <param name="surfaceIndex">The brush surface index.</param>
/// <param name="outputData">The output vertices buffer with triangles or empty if no data loaded.</param>
API_FUNCTION() void GetVertices(int32 surfaceIndex, API_PARAM(Out) Array<Vector3>& outputData) const;
@@ -204,32 +211,12 @@ public:
#endif
// [CSG::Brush]
Scene* GetBrushScene() const override
{
return GetScene();
}
Guid GetBrushID() const override
{
return GetID();
}
bool CanUseCSG() const override
{
return IsActiveInHierarchy();
}
CSG::Mode GetBrushMode() const override
{
return _mode;
}
Scene* GetBrushScene() const override;
Guid GetBrushID() const override;
bool CanUseCSG() const override;
CSG::Mode GetBrushMode() const override;
void GetSurfaces(Array<CSG::Surface>& surfaces) override;
int32 GetSurfacesCount() override
{
return 6;
}
int32 GetSurfacesCount() override;
protected:
+4 -3
View File
@@ -17,12 +17,13 @@
Array<Camera*> Camera::Cameras;
Camera* Camera::CutSceneCamera = nullptr;
Camera* Camera::OverrideMainCamera = nullptr;
ScriptingObjectReference<Camera> Camera::OverrideMainCamera;
Camera* Camera::GetMainCamera()
{
if (OverrideMainCamera)
return OverrideMainCamera;
Camera* overrideMainCamera = OverrideMainCamera.Get();
if (overrideMainCamera)
return overrideMainCamera;
if (CutSceneCamera)
return CutSceneCamera;
return Cameras.HasItems() ? Cameras.First() : nullptr;
+2 -1
View File
@@ -8,6 +8,7 @@
#include "Engine/Core/Math/Viewport.h"
#include "Engine/Core/Math/Ray.h"
#include "Engine/Core/Types/LayersMask.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#if USE_EDITOR
#include "Engine/Content/AssetReference.h"
#include "Engine/Graphics/Models/ModelInstanceEntry.h"
@@ -28,7 +29,7 @@ DECLARE_SCENE_OBJECT(Camera);
static Camera* CutSceneCamera;
// The overriden main camera.
API_FIELD() static Camera* OverrideMainCamera;
API_FIELD() static ScriptingObjectReference<Camera> OverrideMainCamera;
// Gets the main camera.
API_PROPERTY() static Camera* GetMainCamera();
+5 -6
View File
@@ -138,11 +138,10 @@ void SplineModel::OnSplineUpdated()
Vector3 tmp = corners[i] * _preTransform.Scale;
double rotation[4] = { (double)_preTransform.Orientation.X, (double)_preTransform.Orientation.Y, (double)_preTransform.Orientation.Z, (double)_preTransform.Orientation.W };
const double length = sqrt(rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2] + rotation[3] * rotation[3]);
const double inv = 1.0 / length;
rotation[0] *= inv;
rotation[1] *= inv;
rotation[2] *= inv;
rotation[3] *= inv;
rotation[0] /= length;
rotation[1] /= length;
rotation[2] /= length;
rotation[3] /= length;
double pos[3] = { (double)tmp.X, (double)tmp.Y, (double)tmp.Z };
const double x = rotation[0] + rotation[0];
const double y = rotation[1] + rotation[1];
@@ -251,7 +250,7 @@ void SplineModel::UpdateDeformationBuffer()
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
for (int32 chunk = 0; chunk < chunksPerSegment; chunk++)
{
const float alpha = (float)chunk * chunksPerSegmentInv;
const float alpha = (chunk == chunksPerSegment - 1)? 1.0f : ((float)chunk * chunksPerSegmentInv);
// Evaluate transformation at the curve
AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform);
+3 -18
View File
@@ -65,22 +65,6 @@ void Game::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs
void Game::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
{
/*
// Save app state asynchronously after requesting a deferral. Holding a deferral
// indicates that the application is busy performing suspending operations. Be
// aware that a deferral may not be held indefinitely. After about five seconds,
// the app will be forced to exit.
SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
create_task([this, deferral]()
{
m_deviceResources->Trim();
m_main->Suspend();
deferral->Complete();
});
*/
}
void Game::OnResuming(Platform::Object^ sender, Platform::Object^ args)
@@ -396,9 +380,10 @@ int PlatformImpl::GetSpecialFolderPath(const SpecialFolder type, wchar_t* buffer
path = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
break;
case SpecialFolder::ProgramData:
path = Windows::Storage::ApplicationData::Current->RoamingFolder->Path;
break;
//case SpecialFolder::Temporary: path = Windows::Storage::ApplicationData::Current->TemporaryFolder->Path; break;
case SpecialFolder::Temporary:
//path = Windows::Storage::ApplicationData::Current->TemporaryFolder->Path;
path = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
break;
}
@@ -409,7 +394,7 @@ int PlatformImpl::GetSpecialFolderPath(const SpecialFolder type, wchar_t* buffer
if (length >= bufferLength)
length = bufferLength - 1;
const wchar_t* data = path->Data();
for (int i = 0; i<length; i++)
for (int i = 0; i < length; i++)
buffer[i] = data[i];
buffer[length] = 0;
return length;
@@ -23,7 +23,8 @@ CharacterController::CharacterController(const SpawnParams& params)
, _height(150.0f)
, _minMoveDistance(0.0f)
, _isUpdatingTransform(false)
, _nonWalkableMode(CharacterController::NonWalkableModes::PreventClimbing)
, _upDirection(Vector3::Up)
, _nonWalkableMode(NonWalkableModes::PreventClimbing)
, _lastFlags(CollisionFlags::None)
{
static_assert(sizeof(_filterData) == sizeof(PxFilterData), "Invalid filter data size.");
@@ -60,7 +61,12 @@ void CharacterController::SetSlopeLimit(float value)
_slopeLimit = value;
if (_controller)
_controller->setSlopeLimit(cosf(value * DegreesToRadians));
_controller->setSlopeLimit(Math::Cos(value * DegreesToRadians));
}
CharacterController::NonWalkableModes CharacterController::GetNonWalkableMode() const
{
return _nonWalkableMode;
}
void CharacterController::SetNonWalkableMode(NonWalkableModes value)
@@ -71,6 +77,11 @@ void CharacterController::SetNonWalkableMode(NonWalkableModes value)
_controller->setNonWalkableMode(static_cast<PxControllerNonWalkableMode::Enum>(value));
}
float CharacterController::GetStepOffset() const
{
return _stepOffset;
}
void CharacterController::SetStepOffset(float value)
{
if (Math::NearEqual(value, _stepOffset))
@@ -82,6 +93,18 @@ void CharacterController::SetStepOffset(float value)
_controller->setStepOffset(value);
}
void CharacterController::SetUpDirection(const Vector3& up)
{
if (_controller)
_controller->setUpDirection(C2P(up));
_upDirection = up;
}
Vector3 CharacterController::GetUpDirection() const
{
return _controller ? P2C(_controller->getUpDirection()) : _upDirection;
}
void CharacterController::SetMinMoveDistance(float value)
{
_minMoveDistance = Math::Max(value, 0.0f);
@@ -180,6 +203,7 @@ void CharacterController::CreateActor()
// Create controller
_controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc);
ASSERT(_controller);
_controller->setUpDirection(C2P(_upDirection));
const auto actor = _controller->getActor();
ASSERT(actor && actor->getNbShapes() == 1);
actor->getShapes(&_shape, 1);
@@ -363,6 +387,7 @@ void CharacterController::Serialize(SerializeStream& stream, const void* otherOb
SERIALIZE_MEMBER(Radius, _radius);
SERIALIZE_MEMBER(Height, _height);
SERIALIZE_MEMBER(MinMoveDistance, _minMoveDistance);
SERIALIZE_MEMBER(UpDirection, _upDirection);
}
void CharacterController::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
@@ -376,4 +401,5 @@ void CharacterController::Deserialize(DeserializeStream& stream, ISerializeModif
DESERIALIZE_MEMBER(Radius, _radius);
DESERIALIZE_MEMBER(Height, _height);
DESERIALIZE_MEMBER(MinMoveDistance, _minMoveDistance);
DESERIALIZE_MEMBER(UpDirection, _upDirection);
}
@@ -65,6 +65,7 @@ private:
float _height;
float _minMoveDistance;
bool _isUpdatingTransform;
Vector3 _upDirection;
NonWalkableModes _nonWalkableMode;
CollisionFlags _lastFlags;
uint32 _filterData[4];
@@ -117,10 +118,7 @@ public:
/// Gets the non-walkable mode for the character controller.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(215), DefaultValue(NonWalkableModes.PreventClimbing), EditorDisplay(\"Character Controller\")")
FORCE_INLINE NonWalkableModes GetNonWalkableMode() const
{
return _nonWalkableMode;
}
NonWalkableModes GetNonWalkableMode() const;
/// <summary>
/// Sets the non-walkable mode for the character controller.
@@ -131,16 +129,24 @@ public:
/// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controllers height or it will generate an error.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(220), DefaultValue(30.0f), Limit(0), EditorDisplay(\"Character Controller\")")
FORCE_INLINE float GetStepOffset() const
{
return _stepOffset;
}
float GetStepOffset() const;
/// <summary>
/// Sets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controllers height or it will generate an error.
/// </summary>
API_PROPERTY() void SetStepOffset(float value);
/// <summary>
/// Gets the character up vector.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(240), DefaultValue(true), EditorDisplay(\"Character Controller\")")
Vector3 GetUpDirection() const;
/// <summary>
/// Sets the character up vector.
/// </summary>
API_PROPERTY() void SetUpDirection(const Vector3& up);
/// <summary>
/// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small.
/// </summary>
+90
View File
@@ -358,6 +358,51 @@ bool Physics::SphereCastAll(const Vector3& center, const float radius, const Vec
return true;
}
bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center), C2P(rotation));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
// Perform sweep test
return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
}
bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center), C2P(rotation));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
// Perform sweep test
if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
return false;
// Collect results
SCENE_QUERY_COLLECT_SINGLE();
return true;
}
bool Physics::CapsuleCastAll(const Vector3& center, const float radius, const float height, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP_SWEEP();
const PxTransform pose(C2P(center), C2P(rotation));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
// Perform sweep test
if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
return false;
// Collect results
SCENE_QUERY_COLLECT_ALL();
return true;
}
bool Physics::CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
// Prepare data
@@ -380,6 +425,17 @@ bool Physics::CheckSphere(const Vector3& center, const float radius, uint32 laye
return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
}
bool Physics::CheckCapsule(const Vector3& center, const float radius, const float height, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP_OVERLAP_1();
const PxTransform pose(C2P(center));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
// Perform overlap test
return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
}
bool Physics::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
// Prepare data
@@ -414,6 +470,23 @@ bool Physics::OverlapSphere(const Vector3& center, const float radius, Array<Col
return true;
}
bool Physics::OverlapCapsule(const Vector3& center, const float radius, const float height, Array<Collider*>& results, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP_OVERLAP();
const PxTransform pose(C2P(center));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
// Perform overlap test
if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
return false;
// Collect results
SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
return true;
}
bool Physics::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
// Prepare data
@@ -447,3 +520,20 @@ bool Physics::OverlapSphere(const Vector3& center, const float radius, Array<Phy
return true;
}
bool Physics::OverlapCapsule(const Vector3& center, const float radius, const float height, Array<PhysicsColliderActor*>& results, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP_OVERLAP();
const PxTransform pose(C2P(center));
const PxCapsuleGeometry geometry(radius, height * 0.5f);
// Perform overlap test
if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
return false;
// Collect results
SCENE_QUERY_COLLECT_OVERLAP();
return true;
}
+79
View File
@@ -276,6 +276,50 @@ public:
/// <returns>True if sphere hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool SphereCastAll(const Vector3& center, float radius, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a sweep test against objects in the scene using a capsule geometry.
/// </summary>
/// <param name="center">The box center.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
/// <param name="direction">The normalized direction in which cast a box.</param>
/// <param name="rotation">The capsule rotation.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if capsule hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool CapsuleCast(const Vector3& center, float radius, float height, const Vector3& direction, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a sweep test against objects in the scene using a capsule geometry.
/// </summary>
/// <param name="center">The box center.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
/// <param name="direction">The normalized direction in which cast a box.</param>
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="rotation">The capsule rotation.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if capsule hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool CapsuleCast(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a sweep test against objects in the scene using a capsule geometry.
/// </summary>
/// <param name="center">The box center.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
/// <param name="direction">The normalized direction in which cast a box.</param>
/// <param name="results">The result hits. Valid only when method returns true.</param>
/// <param name="rotation">The capsule rotation.</param>
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if capsule hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool CapsuleCastAll(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Checks whether the given box overlaps with other colliders or not.
/// </summary>
@@ -297,6 +341,17 @@ public:
/// <returns>True if sphere overlaps any matching object, otherwise false.</returns>
API_FUNCTION() static bool CheckSphere(const Vector3& center, float radius, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Checks whether the given capsule overlaps with other colliders or not.
/// </summary>
/// <param name="center">The capsule center.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if capsule overlaps any matching object, otherwise false.</returns>
API_FUNCTION() static bool CheckCapsule(const Vector3& center, float radius, float height, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Finds all colliders touching or inside of the given box.
/// </summary>
@@ -320,6 +375,18 @@ public:
/// <returns>True if sphere overlaps any matching object, otherwise false.</returns>
API_FUNCTION() static bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array<Collider*, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Finds all colliders touching or inside of the given sphere.
/// </summary>
/// <param name="center">The sphere center.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
/// <param name="results">The result colliders that overlap with the given sphere. Valid only when method returns true.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if capsule overlaps any matching object, otherwise false.</returns>
API_FUNCTION() static bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array<Collider*, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Finds all colliders touching or inside of the given box.
/// </summary>
@@ -343,6 +410,18 @@ public:
/// <returns>True if sphere overlaps any matching object, otherwise false.</returns>
API_FUNCTION() static bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array<PhysicsColliderActor*, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Finds all colliders touching or inside of the given sphere.
/// </summary>
/// <param name="center">The capsule center.</param>
/// <param name="radius">The radius of the capsule.</param>
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
/// <param name="results">The result colliders that overlap with the given sphere. Valid only when method returns true.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if capsule overlaps any matching object, otherwise false.</returns>
API_FUNCTION() static bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array<PhysicsColliderActor*, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
public:
/// <summary>
@@ -289,7 +289,7 @@ void RemoveLongPathPrefix(const String& path, String& result)
result.Remove(2, 6);
}
String StringUtils::GetDirectoryName(const String& path)
String StringUtils::GetDirectoryName(const StringView& path)
{
const int32 lastFrontSlash = path.FindLast('\\');
const int32 lastBackSlash = path.FindLast('/');
@@ -297,24 +297,22 @@ String StringUtils::GetDirectoryName(const String& path)
return splitIndex != INVALID_INDEX ? path.Left(splitIndex) : String::Empty;
}
String StringUtils::GetFileName(const String& path)
String StringUtils::GetFileName(const StringView& path)
{
Char chr;
const int32 length = path.Length();
int32 num = length;
do
{
num--;
if (num < 0)
return path;
return String(path);
chr = path[num];
} while (chr != DirectorySeparatorChar && chr != AltDirectorySeparatorChar && chr != VolumeSeparatorChar);
return path.Substring(num + 1, length - num - 1);
}
String StringUtils::GetFileNameWithoutExtension(const String& path)
String StringUtils::GetFileNameWithoutExtension(const StringView& path)
{
String filename = GetFileName(path);
const int32 num = filename.FindLast('.');
@@ -325,14 +323,14 @@ String StringUtils::GetFileNameWithoutExtension(const String& path)
return filename;
}
String StringUtils::GetPathWithoutExtension(const String& path)
String StringUtils::GetPathWithoutExtension(const StringView& path)
{
const int32 num = path.FindLast('.');
if (num != -1)
{
return path.Substring(0, num);
}
return path;
return String(path);
}
void StringUtils::PathRemoveRelativeParts(String& path)
+554 -1
View File
@@ -20,6 +20,7 @@
#include "Engine/Platform/MessageBox.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/Clipboard.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Engine.h"
@@ -58,6 +59,15 @@ X11::Display* xDisplay = nullptr;
X11::XIM IM = nullptr;
X11::XIC IC = nullptr;
X11::Atom xAtomDeleteWindow;
X11::Atom xAtomXdndEnter;
X11::Atom xAtomXdndPosition;
X11::Atom xAtomXdndLeave;
X11::Atom xAtomXdndDrop;
X11::Atom xAtomXdndActionCopy;
X11::Atom xAtomXdndStatus;
X11::Atom xAtomXdndSelection;
X11::Atom xAtomXdndFinished;
X11::Atom xAtomXdndAware;
X11::Atom xAtomWmState;
X11::Atom xAtomWmStateHidden;
X11::Atom xAtomWmStateMaxVert;
@@ -65,6 +75,11 @@ X11::Atom xAtomWmStateMaxHorz;
X11::Atom xAtomWmWindowOpacity;
X11::Atom xAtomWmName;
X11::Atom xAtomClipboard;
X11::Atom xDnDRequested = 0;
X11::Window xDndSourceWindow = 0;
DragDropEffect xDndResult;
Vector2 xDndPos;
int32 xDnDVersion = 0;
int32 SystemDpi = 96;
X11::Cursor Cursors[(int32)CursorType::MAX];
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
@@ -1171,6 +1186,13 @@ public:
}
};
struct Property
{
unsigned char* data;
int format, nitems;
X11::Atom type;
};
namespace Impl
{
LinuxKeyboard Keyboard;
@@ -1222,6 +1244,407 @@ namespace Impl
}
}
}
Property ReadProperty(X11::Display* display, X11::Window window, X11::Atom property)
{
X11::Atom readType = 0;
int readFormat = 0;
unsigned long nitems = 0;
unsigned long readBytes = 0;
unsigned char* result = nullptr;
int bytesCount = 1024;
if (property != 0)
{
do
{
if (result != nullptr)
X11::XFree(result);
XGetWindowProperty(display, window, property, 0, bytesCount, 0, AnyPropertyType, &readType, &readFormat, &nitems, &readBytes, &result);
bytesCount *= 2;
} while (readBytes != 0);
}
Property p = { result, readFormat, (int)nitems, readType };
return p;
}
static X11::Atom SelectTargetFromList(X11::Display* display, const char* targetType, X11::Atom* list, int count)
{
for (int i = 0; i < count; i++)
{
X11::Atom atom = list[i];
if (atom != 0 && StringAnsi(XGetAtomName(display, atom)) == targetType)
return atom;
}
return 0;
}
static X11::Atom SelectTargetFromAtoms(X11::Display* display, const char* targetType, X11::Atom t1, X11::Atom t2, X11::Atom t3)
{
if (t1 != 0 && StringAnsi(XGetAtomName(display, t1)) == targetType)
return t1;
if (t2 != 0 && StringAnsi(XGetAtomName(display, t2)) == targetType)
return t2;
if (t3 != 0 && StringAnsi(XGetAtomName(display, t3)) == targetType)
return t3;
return 0;
}
static X11::Window FindAppWindow(X11::Display* display, X11::Window w)
{
int nprops, i = 0;
X11::Atom* a;
if (w == 0)
return 0;
a = X11::XListProperties(display, w, &nprops);
for (i = 0; i < nprops; i++)
{
if (a[i] == xAtomXdndAware)
break;
}
if (nprops)
X11::XFree(a);
if (i != nprops)
return w;
X11::Window child, wtmp;
int tmp;
unsigned int utmp;
X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
return FindAppWindow(display, child);
}
}
class LinuxDropFilesData : public IGuiData
{
public:
Array<String> Files;
Type GetType() const override
{
return Type::Files;
}
String GetAsText() const override
{
return String::Empty;
}
void GetAsFiles(Array<String>* files) const override
{
files->Add(Files);
}
};
class LinuxDropTextData : public IGuiData
{
public:
StringView Text;
Type GetType() const override
{
return Type::Text;
}
String GetAsText() const override
{
return String(Text);
}
void GetAsFiles(Array<String>* files) const override
{
}
};
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
{
auto cursorWrong = X11::XCreateFontCursor(xDisplay, 54);
auto cursorTransient = X11::XCreateFontCursor(xDisplay, 24);
auto cursorGood = X11::XCreateFontCursor(xDisplay, 4);
Array<X11::Atom, FixedAllocation<3>> formats;
formats.Add(X11::XInternAtom(xDisplay, "text/plain", 0));
formats.Add(X11::XInternAtom(xDisplay, "TEXT", 0));
formats.Add((X11::Atom)31);
StringAnsi dataAnsi(data);
LinuxDropTextData dropData;
dropData.Text = data;
// Begin dragging
auto screen = X11::XDefaultScreen(xDisplay);
auto rootWindow = X11::XRootWindow(xDisplay, screen);
if (X11::XGrabPointer(xDisplay, _window, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
return DragDropEffect::None;
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, _window, CurrentTime);
// Process events
X11::XEvent event;
enum Status
{
Unaware,
Unreceptive,
CanDrop,
};
int status = Unaware, previousVersion = -1;
X11::Window previousWindow = 0;
DragDropEffect result = DragDropEffect::None;
float lastDraw = Platform::GetTimeSeconds();
float startTime = lastDraw;
while (true)
{
X11::XNextEvent(xDisplay, &event);
if (event.type == SelectionClear)
break;
if (event.type == SelectionRequest)
{
// Extract the relavent data
X11::Window owner = event.xselectionrequest.owner;
X11::Atom selection = event.xselectionrequest.selection;
X11::Atom target = event.xselectionrequest.target;
X11::Atom property = event.xselectionrequest.property;
X11::Window requestor = event.xselectionrequest.requestor;
X11::Time timestamp = event.xselectionrequest.time;
X11::Display* disp = event.xselection.display;
X11::XEvent s;
s.xselection.type = SelectionNotify;
s.xselection.requestor = requestor;
s.xselection.selection = selection;
s.xselection.target = target;
s.xselection.property = 0;
s.xselection.time = timestamp;
if (target == X11::XInternAtom(disp, "TARGETS", 0))
{
Array<X11::Atom> targets;
targets.Add(target);
targets.Add(X11::XInternAtom(disp, "MULTIPLE", 0));
targets.Add(formats.Get(), formats.Count());
X11::XChangeProperty(disp, requestor, property, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)targets.Get(), targets.Count());
s.xselection.property = property;
}
else if (formats.Contains(target))
{
s.xselection.property = property;
X11::XChangeProperty(disp, requestor, property, target, 8, PropModeReplace, reinterpret_cast<const unsigned char*>(dataAnsi.Get()), dataAnsi.Length());
}
X11::XSendEvent(event.xselection.display, event.xselectionrequest.requestor, 1, 0, &s);
}
else if (event.type == MotionNotify)
{
// Find window under mouse
auto window = Impl::FindAppWindow(xDisplay, rootWindow);
int fmt, version = -1;
X11::Atom atmp;
unsigned long nitems, bytesLeft;
unsigned char* data = nullptr;
if (window == previousWindow)
version = previousVersion;
else if(window == 0)
;
else if (X11::XGetWindowProperty(xDisplay, window, xAtomXdndAware, 0, 2, 0, AnyPropertyType, &atmp, &fmt, &nitems, &bytesLeft, &data) != Success)
continue;
else if (data == 0)
continue;
else if (fmt != 32)
continue;
else if (nitems != 1)
continue;
else
version = data[0];
if (status == Unaware && version != -1)
status = Unreceptive;
else if(version == -1)
status = Unaware;
xDndPos = Vector2((float)event.xmotion.x_root, (float)event.xmotion.y_root);
// Update mouse grab
if (status == Unaware)
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorWrong, CurrentTime);
else if(status == Unreceptive)
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorTransient, CurrentTime);
else
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorGood, CurrentTime);
if (window != previousWindow && previousVersion != -1)
{
// Send drag left event
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
if (ww)
{
ww->_dragOver = false;
ww->OnDragLeave();
}
else
{
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = previousWindow;
m.message_type = xAtomXdndLeave;
m.format = 32;
m.data.l[0] = _window;
m.data.l[1] = 0;
m.data.l[2] = 0;
m.data.l[3] = 0;
m.data.l[4] = 0;
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
X11::XFlush(xDisplay);
}
}
if (window != previousWindow && version != -1)
{
// Send drag enter event
auto ww = WindowsManager::GetByNativePtr((void*)window);
if (ww)
{
xDndPos = ww->ScreenToClient(Platform::GetMousePosition());
xDndResult = DragDropEffect::None;
ww->OnDragEnter(&dropData, xDndPos, xDndResult);
}
else
{
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = window;
m.message_type = xAtomXdndEnter;
m.format = 32;
m.data.l[0] = _window;
m.data.l[1] = Math::Min(5, version) << 24 | (formats.Count() > 3);
m.data.l[2] = formats.Count() > 0 ? formats[0] : 0;
m.data.l[3] = formats.Count() > 1 ? formats[1] : 0;
m.data.l[4] = formats.Count() > 2 ? formats[2] : 0;
X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m);
X11::XFlush(xDisplay);
}
}
if (version != -1)
{
// Send position event
auto ww = WindowsManager::GetByNativePtr((void*)window);
if (ww)
{
xDndPos = ww->ScreenToClient(Platform::GetMousePosition());
ww->_dragOver = true;
xDndResult = DragDropEffect::None;
ww->OnDragOver(&dropData, xDndPos, xDndResult);
status = CanDrop;
}
else
{
int x, y, tmp;
unsigned int utmp;
X11::Window wtmp;
X11::XQueryPointer(xDisplay, window, &wtmp, &wtmp, &tmp, &tmp, &x, &y, &utmp);
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = window;
m.message_type = xAtomXdndPosition;
m.format = 32;
m.data.l[0] = _window;
m.data.l[1] = 0;
m.data.l[2] = (x << 16) | y;
m.data.l[3] = CurrentTime;
m.data.l[4] = xAtomXdndActionCopy;
X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m);
X11::XFlush(xDisplay);
}
}
previousWindow = window;
previousVersion = version;
}
else if (event.type == ClientMessage && event.xclient.message_type == xAtomXdndStatus)
{
if ((event.xclient.data.l[1]&1) && status != Unaware)
status = CanDrop;
if (!(event.xclient.data.l[1]&1) && status != Unaware)
status = Unreceptive;
}
else if (event.type == ButtonRelease && event.xbutton.button == Button1)
{
if (status == CanDrop)
{
// Send drop event
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
if (ww)
{
xDndPos = ww->ScreenToClient(Platform::GetMousePosition());
xDndResult = DragDropEffect::None;
ww->OnDragDrop(&dropData, xDndPos, xDndResult);
ww->Focus();
result = xDndResult;
}
else
{
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = previousWindow;
m.message_type = xAtomXdndDrop;
m.format = 32;
m.data.l[0] = _window;
m.data.l[1] = 0;
m.data.l[2] = CurrentTime;
m.data.l[3] = 0;
m.data.l[4] = 0;
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
X11::XFlush(xDisplay);
result = DragDropEffect::Copy;
}
}
break;
}
// Redraw
const float time = Platform::GetTimeSeconds();
if (time - lastDraw >= 1.0f / 20.0f)
{
lastDraw = time;
Engine::OnDraw();
}
// Prevent dead-loop
if (time - startTime >= 10.0f)
{
break;
}
}
// Drag end
if (previousWindow != 0 && previousVersion != -1)
{
// Send drag left event
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
if (ww)
{
ww->_dragOver = false;
ww->OnDragLeave();
}
else
{
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = previousWindow;
m.message_type = xAtomXdndLeave;
m.format = 32;
m.data.l[0] = _window;
m.data.l[1] = 0;
m.data.l[2] = 0;
m.data.l[3] = 0;
m.data.l[4] = 0;
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
X11::XFlush(xDisplay);
}
}
// End grabbing
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, 0, CurrentTime);
XUngrabPointer(xDisplay, CurrentTime);
return result;
}
void LinuxClipboard::Clear()
@@ -1651,7 +2074,15 @@ bool LinuxPlatform::Init()
}
xAtomDeleteWindow = X11::XInternAtom(xDisplay, "WM_DELETE_WINDOW", 0);
xAtomWmState = X11::XInternAtom(xDisplay, "_NET_WM_STATE", 0);
xAtomXdndEnter = X11::XInternAtom(xDisplay, "XdndEnter", 0);
xAtomXdndPosition = X11::XInternAtom(xDisplay, "XdndPosition", 0);
xAtomXdndLeave = X11::XInternAtom(xDisplay, "XdndLeave", 0);
xAtomXdndDrop = X11::XInternAtom(xDisplay, "XdndDrop", 0);
xAtomXdndActionCopy = X11::XInternAtom(xDisplay, "XdndActionCopy", 0);
xAtomXdndStatus = X11::XInternAtom(xDisplay, "XdndStatus", 0);
xAtomXdndSelection = X11::XInternAtom(xDisplay, "XdndSelection", 0);
xAtomXdndFinished = X11::XInternAtom(xDisplay, "XdndFinished", 0);
xAtomXdndAware = X11::XInternAtom(xDisplay, "XdndAware", 0);
xAtomWmStateHidden = X11::XInternAtom(xDisplay, "_NET_WM_STATE_HIDDEN", 0);
xAtomWmStateMaxHorz = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
xAtomWmStateMaxVert = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
@@ -1799,6 +2230,93 @@ void LinuxPlatform::Tick()
window->Close(ClosingReason::User);
}
}
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndEnter)
{
// Drag&drop enter
X11::Window source = event.xclient.data.l[0];
xDnDVersion = (int32)(event.xclient.data.l[1] >> 24);
const char* targetTypeFiles = "text/uri-list";
if (event.xclient.data.l[1] & 1)
{
Property p = Impl::ReadProperty(xDisplay, source, XInternAtom(xDisplay, "XdndTypeList", 0));
xDnDRequested = Impl::SelectTargetFromList(xDisplay, targetTypeFiles, (X11::Atom*)p.data, p.nitems);
X11::XFree(p.data);
}
else
{
xDnDRequested = Impl::SelectTargetFromAtoms(xDisplay, targetTypeFiles, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
}
}
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndPosition)
{
// Drag&drop move
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = event.xclient.data.l[0];
m.message_type = xAtomXdndStatus;
m.format = 32;
m.data.l[0] = event.xany.window;
m.data.l[1] = (xDnDRequested != 0);
m.data.l[2] = 0;
m.data.l[3] = 0;
m.data.l[4] = xAtomXdndActionCopy;
X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m);
X11::XFlush(xDisplay);
xDndPos = Vector2((float)(event.xclient.data.l[2] >> 16), (float)(event.xclient.data.l[2] & 0xffff));
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
if (window)
{
LinuxDropFilesData dropData;
xDndResult = DragDropEffect::None;
if (window->_dragOver)
{
window->OnDragEnter(&dropData, xDndPos, xDndResult);
}
else
{
window->_dragOver = true;
window->OnDragOver(&dropData, xDndPos, xDndResult);
}
}
}
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndLeave)
{
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
if (window && window->_dragOver)
{
window->_dragOver = false;
window->OnDragLeave();
}
}
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndDrop)
{
auto w = event.xany.window;
if (xDnDRequested != 0)
{
xDndSourceWindow = event.xclient.data.l[0];
auto primary = XInternAtom(xDisplay, "PRIMARY", 0);
if (xDnDVersion >= 1)
XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, primary, w, event.xclient.data.l[2]);
else
XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, primary, w, CurrentTime);
}
else
{
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = event.xclient.display;
m.window = event.xclient.data.l[0];
m.message_type = xAtomXdndFinished;
m.format = 32;
m.data.l[0] = w;
m.data.l[1] = 0;
m.data.l[2] = 0;
X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m);
}
}
break;
case MapNotify:
// Auto-focus shown windows
@@ -1980,6 +2498,41 @@ void LinuxPlatform::Tick()
X11::XSendEvent(xDisplay, ev.requestor, 0, 0, (X11::XEvent*)&ev);
break;
}
case SelectionNotify:
if (event.xselection.target == xDnDRequested)
{
// Drag&drop
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
if (window)
{
Property p = Impl::ReadProperty(xDisplay, event.xany.window, X11::XInternAtom(xDisplay, "PRIMARY", 0));
if (xDndResult != DragDropEffect::None)
{
LinuxDropFilesData dropData;
const String filesList((const char*)p.data);
filesList.Split('\n', dropData.Files);
for (auto& e : dropData.Files)
{
e.Replace(TEXT("file://"), TEXT(""));
e = e.TrimTrailing();
}
xDndResult = DragDropEffect::None;
window->OnDragDrop(&dropData, xDndPos, xDndResult);
}
}
X11::XClientMessageEvent m;
memset(&m, 0, sizeof(m));
m.type = ClientMessage;
m.display = xDisplay;
m.window = xDndSourceWindow;
m.message_type = xAtomXdndFinished;
m.format = 32;
m.data.l[0] = event.xany.window;
m.data.l[1] = 1;
m.data.l[2] = xAtomXdndActionCopy;
XSendEvent(xDisplay, xDndSourceWindow, 0, NoEventMask, (X11::XEvent*)&m);
}
break;
default:
break;
}
+9 -7
View File
@@ -103,7 +103,6 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
bool Fullscreen;
bool AllowMinimize;
bool AllowMaximize;
bool AllowDragAndDrop;
*/
const X11::Window window = X11::XCreateWindow(
@@ -223,6 +222,15 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
}
X11::XChangeProperty(display, window, wmState, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)states, statesCount);
// Drag&drop support
if (settings.AllowDragAndDrop)
{
auto xdndVersion = 5;
auto xdndAware = XInternAtom(display, "XdndAware", 0);
if (xdndAware != 0)
X11::XChangeProperty(display, window, xdndAware, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&xdndVersion, 1);
}
// Sync
X11::XFlush(display);
X11::XSync(display, 0);
@@ -725,12 +733,6 @@ void LinuxWindow::SetTitle(const StringView& title)
_title = title;
}
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
{
// TODO: impl drag and drop on Linux
return DragDropEffect::None;
}
void LinuxWindow::StartTrackingMouse(bool useMouseScreenOffset)
{
// TODO: impl this
+1 -2
View File
@@ -13,14 +13,13 @@
class LinuxWindow : public WindowBase
{
friend LinuxPlatform;
public:
typedef unsigned long HandleType;
private:
bool _resizeDisabled, _focusOnMapped = false;
bool _resizeDisabled, _focusOnMapped = false, _dragOver = false;
float _opacity = 1.0f;
HandleType _window;
+4 -4
View File
@@ -207,19 +207,19 @@ public:
// Returns the directory name of the specified path string
// @param path The path string from which to obtain the directory name
// @returns Directory name
static String GetDirectoryName(const String& path);
static String GetDirectoryName(const StringView& path);
// Returns the file name and extension of the specified path string
// @param path The path string from which to obtain the file name and extension
// @returns File name with extension
static String GetFileName(const String& path);
static String GetFileName(const StringView& path);
// Returns the file name without extension of the specified path string
// @param path The path string from which to obtain the file name
// @returns File name without extension
static String GetFileNameWithoutExtension(const String& path);
static String GetFileNameWithoutExtension(const StringView& path);
static String GetPathWithoutExtension(const String& path);
static String GetPathWithoutExtension(const StringView& path);
static void PathRemoveRelativeParts(String& path);
@@ -318,14 +318,15 @@ void Win32Platform::Free(void* ptr)
void* Win32Platform::AllocatePages(uint64 numPages, uint64 pageSize)
{
const uint64 numBytes = numPages * pageSize;
// Use VirtualAlloc to allocate page-aligned memory
#if PLATFORM_UWP
return VirtualAllocFromApp(nullptr, (SIZE_T)numBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
return VirtualAlloc(nullptr, (SIZE_T)numBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#endif
}
void Win32Platform::FreePages(void* ptr)
{
// Free page-aligned memory
VirtualFree(ptr, 0, MEM_RELEASE);
}
@@ -260,7 +260,7 @@ bool WindowsFileSystem::ShowSaveFileDialog(Window* parentWindow, const StringVie
of.lpstrFilter = filter.HasChars() ? filter.Get() : nullptr;
of.lpstrFile = fileNamesBuffer.Get();
of.nMaxFile = maxFilenamesSize;
of.Flags = OFN_EXPLORER | OFN_ENABLESIZING;
of.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT;
of.lpstrTitle = title.HasChars() ? title.Get() : nullptr;
of.lpstrInitialDir = initialDirectory.HasChars() ? initialDirectory.Get() : nullptr;
if (parentWindow)
@@ -38,7 +38,7 @@ namespace
CriticalSection SymLocker;
bool SymInitialized = false;
bool SymModulesDirty = true;
char* SymPath = nullptr;
Array<String> SymbolsPath;
#endif
}
@@ -617,9 +617,8 @@ void WindowsPlatform::Exit()
{
SymInitialized = false;
SymCleanup(GetCurrentProcess());
free(SymPath);
SymPath = nullptr;
}
SymbolsPath.Resize(0);
SymLocker.Unlock();
#endif
@@ -1116,7 +1115,13 @@ void* WindowsPlatform::LoadLibrary(const Char* filename)
#if CRASH_LOG_ENABLE
// Refresh modules info during next stack trace collecting to have valid debug symbols information
SymLocker.Lock();
SymModulesDirty = true;
const auto folder = StringUtils::GetDirectoryName(filename);
if (!SymbolsPath.Contains(folder))
SymbolsPath.Add(folder);
if (SymInitialized)
{
SymModulesDirty = true;
}
SymLocker.Unlock();
#endif
@@ -1137,80 +1142,43 @@ Array<PlatformBase::StackFrame> WindowsPlatform::GetStackFrames(int32 skipCount,
SymInitialized = true;
// Build search path
const size_t nSymPathLen = 4096;
SymPath = (char*)malloc(nSymPathLen);
SymPath[0] = 0;
strcat_s(SymPath, nSymPathLen, ".;");
const size_t nTempLen = 1024;
char szTemp[nTempLen];
// Current directory path
if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
String symbolSearchPath;
TCHAR ModulePath[MAX_PATH] = { 0 };
if (::GetModuleFileName(::GetModuleHandle(nullptr), ModulePath, MAX_PATH))
{
szTemp[nTempLen - 1] = 0;
strcat_s(SymPath, nSymPathLen, szTemp);
strcat_s(SymPath, nSymPathLen, ";");
symbolSearchPath += StringUtils::GetDirectoryName(ModulePath);
symbolSearchPath += ";";
}
// Main module path
if (GetModuleFileNameA(nullptr, szTemp, nTempLen) > 0)
for (auto& path : SymbolsPath)
{
szTemp[nTempLen - 1] = 0;
for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p)
{
// Locate the rightmost path separator
if ((*p == '\\') || (*p == '/') || (*p == ':'))
{
*p = 0;
break;
}
}
if (strlen(szTemp) > 0)
{
strcat_s(SymPath, nSymPathLen, szTemp);
strcat_s(SymPath, nSymPathLen, ";");
}
symbolSearchPath += path;
symbolSearchPath += ";";
}
// System symbols paths
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
String _NT_SYMBOL_PATH;
if (!Platform::GetEnvironmentVariable(TEXT("_NT_SYMBOL_PATH"), _NT_SYMBOL_PATH))
{
szTemp[nTempLen - 1] = 0;
strcat_s(SymPath, nSymPathLen, szTemp);
strcat_s(SymPath, nSymPathLen, ";");
symbolSearchPath += _NT_SYMBOL_PATH;
symbolSearchPath += ";";
}
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
{
szTemp[nTempLen - 1] = 0;
strcat_s(SymPath, nSymPathLen, szTemp);
strcat_s(SymPath, nSymPathLen, ";");
}
if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
{
szTemp[nTempLen - 1] = 0;
strcat_s(SymPath, nSymPathLen, szTemp);
strcat_s(SymPath, nSymPathLen, ";");
strcat_s(szTemp, nTempLen, "\\system32");
strcat_s(SymPath, nSymPathLen, szTemp);
strcat_s(SymPath, nSymPathLen, ";");
}
SymInitialize(process, SymPath, FALSE);
symbolSearchPath += Platform::GetWorkingDirectory();
symbolSearchPath += ";";
DWORD options = SymGetOptions();
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
options |= SYMOPT_DEFERRED_LOADS;
options |= SYMOPT_EXACT_SYMBOLS;
SymSetOptions(options);
SymInitializeW(process, *symbolSearchPath, TRUE);
}
// Load modules
// Refresh modules if needed
if (SymModulesDirty)
{
SymModulesDirty = false;
GetModuleListPSAPI(process);
SymRefreshModuleList(process);
}
SymRefreshModuleList(process);
// Capture the context if missing
/*EXCEPTION_POINTERS exceptionPointers;
@@ -34,6 +34,8 @@ bool DepthOfFieldPass::Init()
_platformSupportsDoF = limits.HasCompute;
_platformSupportsBokeh = _platformSupportsDoF && limits.HasGeometryShaders && limits.HasDrawIndirect && limits.HasAppendConsumeBuffers;
_platformSupportsBokeh &= GPUDevice::Instance->GetRendererType() != RendererType::DirectX12; // TODO: fix bokeh crash on d3d12 (driver issue probably - started to happen recently)
// Create pipeline states
if (_platformSupportsDoF)
{
+1
View File
@@ -251,6 +251,7 @@ void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context)
auto& shadowView = _shadowContext.View;
shadowView.Flags = view.Flags;
shadowView.StaticFlagsMask = view.StaticFlagsMask;
shadowView.RenderLayersMask = view.RenderLayersMask;
shadowView.IsOfflinePass = view.IsOfflinePass;
shadowView.ModelLODBias = view.ModelLODBias + view.ShadowModelLODBias;
shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor * view.ShadowModelLODDistanceFactor;
@@ -326,6 +326,24 @@ bool MAssembly::LoadWithImage(const String& assemblyPath)
mono_debug_open_image_from_memory(assemblyImage, _debugData.Get(), _debugData.Count());
}
}
#if 0
// Hack to load debug information for Newtonsoft.Json (enable it to debug C# code of json lib)
if (assemblyPath.EndsWith(TEXT("FlaxEngine.CSharp.dll")))
{
static Array<byte> NewtonsoftJsonDebugData;
File::ReadAllBytes(StringUtils::GetDirectoryName(assemblyPath) / TEXT("Newtonsoft.Json.pdb"), NewtonsoftJsonDebugData);
if (NewtonsoftJsonDebugData.HasItems())
{
StringAnsi tmp(StringUtils::GetDirectoryName(assemblyPath) / TEXT("Newtonsoft.Json.dll"));
MonoAssembly* a = mono_assembly_open(tmp.Get(), &status);
if (a)
{
mono_debug_open_image_from_memory(mono_assembly_get_image(a), NewtonsoftJsonDebugData.Get(), NewtonsoftJsonDebugData.Count());
}
}
}
#endif
#endif
// Set state
+6 -4
View File
@@ -178,13 +178,15 @@ void Script::SetupType()
{
// Enable tick functions based on the method overriden in C# or Visual Script
ScriptingTypeHandle typeHandle = GetTypeHandle();
_tickUpdate = _tickLateUpdate = _tickFixedUpdate = 0;
while (typeHandle != Script::TypeInitializer)
{
auto& type = typeHandle.GetType();
_tickUpdate |= type.Script.ScriptVTable[8] != nullptr;
_tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr;
_tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr;
if (type.Script.ScriptVTable)
{
_tickUpdate |= type.Script.ScriptVTable[8] != nullptr;
_tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr;
_tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr;
}
typeHandle = type.GetBaseType();
}
}
-1
View File
@@ -27,7 +27,6 @@
#include "Engine/Engine/EngineService.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Utilities/StringConverter.h"
#include <ThirdParty/mono-2.0/mono/metadata/mono-debug.h>
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
+2 -4
View File
@@ -154,8 +154,7 @@ public:
// @param length Text length
void WriteText(const char* text, int32 length)
{
for (int32 i = 0; i < length; i++)
WriteChar(text[i]);
WriteBytes((const void*)text, sizeof(char) * length);
}
// Writes text to the stream
@@ -163,8 +162,7 @@ public:
// @param length Text length
void WriteText(const Char* text, int32 length)
{
for (int32 i = 0; i < length; i++)
WriteChar(text[i]);
WriteBytes((const void*)text, sizeof(Char) * length);
}
template<typename... Args>
+128 -64
View File
@@ -8,8 +8,8 @@ namespace FlaxEngine.GUI
/// <summary>
/// Dropdown menu control allows to choose one item from the provided collection of options.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.Control" />
public class Dropdown : Control
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public class Dropdown : ContainerControl
{
/// <summary>
/// The root control used by the <see cref="Dropdown"/> to show the items collections and track item selecting event.
@@ -18,7 +18,7 @@ namespace FlaxEngine.GUI
[HideInEditor]
protected class DropdownRoot : Panel
{
private bool isMouseDown;
private bool _isMouseDown;
/// <summary>
/// Occurs when item gets clicked. Argument is item index.
@@ -38,9 +38,9 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
isMouseDown = true;
_isMouseDown = true;
var result = base.OnMouseDown(location, button);
isMouseDown = false;
_isMouseDown = false;
if (!result)
return false;
@@ -57,8 +57,10 @@ namespace FlaxEngine.GUI
{
base.OnLostFocus();
if (!isMouseDown)
LostFocus();
if (!_isMouseDown)
{
LostFocus?.Invoke();
}
}
/// <inheritdoc />
@@ -82,7 +84,7 @@ namespace FlaxEngine.GUI
/// </summary>
protected DropdownRoot _popup;
private bool _mouseDown;
private bool _touchDown;
/// <summary>
/// The selected index of the item (-1 for no selection).
@@ -118,13 +120,9 @@ namespace FlaxEngine.GUI
get => _selectedIndex;
set
{
// Clamp index
value = Mathf.Min(value, _items.Count - 1);
// Check if index will change
if (value != _selectedIndex)
{
// Select
_selectedIndex = value;
OnSelectedIndexChanged();
}
@@ -225,6 +223,8 @@ namespace FlaxEngine.GUI
public Dropdown()
: base(0, 0, 120, 18.0f)
{
AutoFocus = false;
var style = Style.Current;
Font = new FontReference(style.FontMedium);
TextColor = style.Foreground;
@@ -334,6 +334,7 @@ namespace FlaxEngine.GUI
}
*/
var itemsWidth = Width;
var height = container.Margin.Height;
for (int i = 0; i < _items.Count; i++)
{
@@ -347,35 +348,51 @@ namespace FlaxEngine.GUI
var label = new Label
{
X = itemsMargin,
Width = itemsWidth - itemsMargin,
Size = new Vector2(itemsWidth - itemsMargin, itemsHeight),
Font = Font,
TextColor = Color.White * 0.9f,
TextColorHighlighted = Color.White,
HorizontalAlignment = TextAlignment.Near,
AnchorPreset = AnchorPresets.VerticalStretchRight,
Text = _items[i],
Parent = item,
};
height += itemsHeight;
if (i != 0)
height += container.Spacing;
if (_selectedIndex == i)
{
var icon = new Image
{
Brush = CheckedImage,
Width = itemsMargin,
Size = new Vector2(itemsMargin, itemsHeight),
Margin = new Margin(4.0f, 6.0f, 4.0f, 4.0f),
AnchorPreset = AnchorPresets.VerticalStretchLeft,
//AnchorPreset = AnchorPresets.VerticalStretchLeft,
Parent = item,
};
}
}
popup.Size = new Vector2(itemsWidth, (itemsHeight + container.Spacing) * _items.Count + container.Margin.Height);
popup.Size = new Vector2(itemsWidth, height);
popup.ItemsContainer = container;
return popup;
}
/// <summary>
/// Called when popup menu gets shown.
/// </summary>
protected virtual void OnPopupShow()
{
}
/// <summary>
/// Called when popup menu gets hidden.
/// </summary>
protected virtual void OnPopupHide()
{
}
/// <summary>
/// Destroys the popup.
/// </summary>
@@ -383,11 +400,59 @@ namespace FlaxEngine.GUI
{
if (_popup != null)
{
OnPopupHide();
_popup.Dispose();
_popup = null;
}
}
/// <summary>
/// Shows the popup.
/// </summary>
public void ShowPopup()
{
var root = Root;
if (_items.Count == 0 || root == null)
return;
// Setup popup
DestroyPopup();
_popup = CreatePopup();
// Update layout
_popup.UnlockChildrenRecursive();
_popup.PerformLayout();
// Bind events
_popup.ItemClicked += index =>
{
OnItemClicked(index);
DestroyPopup();
};
_popup.LostFocus += DestroyPopup;
// Show dropdown popup
Vector2 locationRootSpace = Location + new Vector2(0, Height);
var parent = Parent;
while (parent != null && parent != Root)
{
locationRootSpace = parent.PointToParent(ref locationRootSpace);
parent = parent.Parent;
}
_popup.Location = locationRootSpace;
_popup.Parent = root;
_popup.Focus();
OnPopupShow();
}
/// <summary>
/// Hides the popup.
/// </summary>
public void HidePopup()
{
DestroyPopup();
}
/// <inheritdoc />
public override void OnDestroy()
{
@@ -397,7 +462,7 @@ namespace FlaxEngine.GUI
}
/// <inheritdoc />
public override void Draw()
public override void DrawSelf()
{
// Cache data
var clientRect = new Rectangle(Vector2.Zero, Size);
@@ -413,7 +478,7 @@ namespace FlaxEngine.GUI
backgroundColor *= 0.5f;
arrowColor *= 0.7f;
}
else if (isOpened || _mouseDown)
else if (isOpened || _touchDown)
{
backgroundColor = BackgroundColorSelected;
borderColor = BorderColorSelected;
@@ -448,17 +513,15 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override void OnLostFocus()
{
base.OnLostFocus();
_touchDown = false;
// Clear flags
_mouseDown = false;
base.OnLostFocus();
}
/// <inheritdoc />
public override void OnMouseLeave()
{
// Clear flags
_mouseDown = false;
_touchDown = false;
base.OnMouseLeave();
}
@@ -466,59 +529,60 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
// Check mouse buttons
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Left)
{
// Set flag
_mouseDown = true;
_touchDown = true;
return true;
}
return base.OnMouseDown(location, button);
return false;
}
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
// Check flags
if (_mouseDown)
if (_touchDown && button == MouseButton.Left)
{
// Clear flag
_mouseDown = false;
var root = Root;
if (_items.Count > 0 && root != null)
{
// Setup popup
DestroyPopup();
_popup = CreatePopup();
// Update layout
_popup.UnlockChildrenRecursive();
_popup.PerformLayout();
// Bind events
_popup.ItemClicked += (index) =>
{
OnItemClicked(index);
DestroyPopup();
};
_popup.LostFocus += DestroyPopup;
// Show dropdown popup
Vector2 locationRootSpace = Location + new Vector2(0, Height);
var parent = Parent;
while (parent != null && parent != Root)
{
locationRootSpace = parent.PointToParent(ref location);
parent = parent.Parent;
}
_popup.Location = locationRootSpace;
_popup.Parent = root;
_popup.Focus();
}
_touchDown = false;
ShowPopup();
return true;
}
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override bool OnTouchDown(Vector2 location, int pointerId)
{
if (base.OnTouchDown(location, pointerId))
return true;
_touchDown = true;
return true;
}
/// <inheritdoc />
public override bool OnTouchUp(Vector2 location, int pointerId)
{
if (base.OnTouchUp(location, pointerId))
return true;
if (_touchDown)
{
ShowPopup();
}
return true;
}
/// <inheritdoc />
public override void OnTouchLeave(int pointerId)
{
_touchDown = false;
base.OnTouchLeave(pointerId);
}
}
}
+6 -11
View File
@@ -39,9 +39,6 @@ namespace FlaxEngine.GUI
/// <summary>
/// Gets a value indicating whether use progress value smoothing.
/// </summary>
/// <value>
/// <c>true</c> if use progress value smoothing; otherwise, <c>false</c>.
/// </value>
public bool UseSmoothing => !Mathf.IsZero(SmoothingScale);
/// <summary>
@@ -91,8 +88,6 @@ namespace FlaxEngine.GUI
if (!Mathf.NearEqual(value, _value))
{
_value = value;
// Check if skip smoothing
if (!UseSmoothing)
{
_current = _value;
@@ -138,22 +133,22 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override void Update(float deltaTime)
{
bool isDeltaSlow = deltaTime > (1 / 20.0f);
// Ensure progress bar is visible
if (Visible)
{
// Value smoothing
var value = _value;
if (Mathf.Abs(_current - _value) > 0.01f)
{
// Lerp or not if running slow
float value;
bool isDeltaSlow = deltaTime > (1 / 20.0f);
if (!isDeltaSlow && UseSmoothing)
value = Mathf.Lerp(_current, _value, Mathf.Saturate(deltaTime * 5.0f * SmoothingScale));
else
value = _value;
_current = value;
}
else
{
_current = _value;
}
}
base.Update(deltaTime);
+4 -4
View File
@@ -274,7 +274,7 @@ namespace FlaxEngine.GUI
public int GetChildIndexAt(Vector2 point)
{
int result = -1;
for (int i = 0; i < _children.Count; i++)
for (int i = _children.Count - 1; i >= 0; i--)
{
var child = _children[i];
@@ -296,7 +296,7 @@ namespace FlaxEngine.GUI
public Control GetChildAt(Vector2 point)
{
Control result = null;
for (int i = 0; i < _children.Count; i++)
for (int i = _children.Count - 1; i >= 0; i--)
{
var child = _children[i];
@@ -322,7 +322,7 @@ namespace FlaxEngine.GUI
throw new ArgumentNullException(nameof(isValid));
Control result = null;
for (int i = 0; i < _children.Count; i++)
for (int i = _children.Count - 1; i >= 0; i--)
{
var child = _children[i];
@@ -344,7 +344,7 @@ namespace FlaxEngine.GUI
public Control GetChildAtRecursive(Vector2 point)
{
Control result = null;
for (int i = 0; i < _children.Count; i++)
for (int i = _children.Count - 1; i >= 0; i--)
{
var child = _children[i];
+3 -3
View File
@@ -313,7 +313,7 @@ namespace FlaxEngine.GUI
/// <summary>
/// Gets the control DPI scale factor (1 is default). Includes custom DPI scale.
/// </summary>
public float DpiScale => _root?.RootWindow?.Window.DpiScale ?? Platform.DpiScale;
public float DpiScale => RootWindow?.Window.DpiScale ?? Platform.DpiScale;
/// <summary>
/// Gets screen position of the control (upper left corner).
@@ -456,9 +456,9 @@ namespace FlaxEngine.GUI
#region Focus
/// <summary>
/// Gets a value indicating whether the control can receive automatic focus on user events (eg. mouse down.
/// Gets a value indicating whether the control can receive automatic focus on user events (eg. mouse down).
/// </summary>
[HideInEditor]
[HideInEditor, NoSerialize]
public bool AutoFocus
{
get => _autoFocus;
+8 -3
View File
@@ -73,13 +73,18 @@ namespace FlaxEngine.GUI
Vector2 locationWS = target.PointToWindow(location);
Vector2 locationSS = parentWin.PointToScreen(locationWS);
Vector2 screenSize = Platform.VirtualDesktopSize;
Vector2 parentWinLocationSS = parentWin.PointToScreen(Vector2.Zero);
float parentWinRightSS = parentWinLocationSS.Y + parentWin.Size.Y;
float parentWinBottomSS = parentWinLocationSS.X + parentWin.Size.X;
Vector2 rightBottomLocationSS = locationSS + dpiSize;
if (screenSize.Y < rightBottomLocationSS.Y)
// Prioritize tooltip placement within parent window, fall back to virtual desktop
if (parentWinRightSS < rightBottomLocationSS.Y || screenSize.Y < rightBottomLocationSS.Y)
{
// Direction: up
locationSS.Y -= dpiSize.Y;
}
if (screenSize.X < rightBottomLocationSS.X)
if (parentWinBottomSS < rightBottomLocationSS.X || screenSize.X < rightBottomLocationSS.X)
{
// Direction: left
locationSS.X -= dpiSize.X;
@@ -155,7 +160,7 @@ namespace FlaxEngine.GUI
/// <param name="dt">The delta time.</param>
public void OnMouseOverControl(Control target, float dt)
{
if (!Visible)
if (!Visible && _timeToPopupLeft > 0.0f)
{
_lastTarget = target;
_timeToPopupLeft -= dt;
+13 -2
View File
@@ -211,6 +211,17 @@ namespace FlaxEngine.Utilities
/// <returns>A <see cref="bool"/> thats either true or false.</returns>
public static bool NextBool(this Random random, float weight = 0.5f) => random.NextDouble() < weight;
/// <summary>
/// Generates a random <see cref="byte"/> value up until an exclusive maximum.
/// </summary>
/// <param name="random">An instance of <see cref="Random"/>.</param>
/// <param name="max">The maximum value. If it's zero, a maximum of 256 is used</param>
/// <returns>A random <see cref="byte"/> between min and max.</returns>
public static byte NextByte(this Random random, byte max = 0)
{
return max == 0 ? (byte)(random.Next() % 256) : (byte)random.Next(max);
}
/// <summary>
/// Generates a random <see cref="byte"/> value between min and max.
/// </summary>
@@ -218,9 +229,9 @@ namespace FlaxEngine.Utilities
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <returns>A random <see cref="byte"/> between min and max.</returns>
public static byte NextByte(this Random random, byte min = 0, byte max = byte.MaxValue)
public static byte NextByte(this Random random, byte min, byte max)
{
return (byte)random.Next(min, max == byte.MaxValue ? byte.MaxValue + 1 : max);
return (byte)random.Next(min, max);
}
/// <summary>
+2 -2
View File
@@ -13,5 +13,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")]
[assembly: AssemblyVersion("1.1.6217")]
[assembly: AssemblyFileVersion("1.1.6217")]
[assembly: AssemblyVersion("1.1.6218")]
[assembly: AssemblyFileVersion("1.1.6218")]
+3 -3
View File
@@ -3,11 +3,11 @@
#pragma once
#define FLAXENGINE_NAME "FlaxEngine"
#define FLAXENGINE_VERSION Version(1, 1, 6217)
#define FLAXENGINE_VERSION_TEXT "1.1.6217"
#define FLAXENGINE_VERSION Version(1, 1, 6218)
#define FLAXENGINE_VERSION_TEXT "1.1.6218"
#define FLAXENGINE_VERSION_MAJOR 1
#define FLAXENGINE_VERSION_MINOR 1
#define FLAXENGINE_VERSION_BUILD 6217
#define FLAXENGINE_VERSION_BUILD 6218
#define FLAXENGINE_COMPANY "Flax"
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved."
Binary file not shown.
Binary file not shown.
+129 -3
View File
@@ -1865,6 +1865,15 @@
<param name="message">The error message that explains the reason for the exception.</param>
<param name="innerException">The exception that is the cause of the current exception, or <c>null</c> if no inner exception is specified.</param>
</member>
<member name="M:Newtonsoft.Json.JsonException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonException"/> class.
</summary>
<param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
<param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
<exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is <c>null</c>.</exception>
<exception cref="T:System.Runtime.Serialization.SerializationException">The class name is <c>null</c> or <see cref="P:System.Exception.HResult"/> is zero (0).</exception>
</member>
<member name="T:Newtonsoft.Json.JsonExtensionDataAttribute">
<summary>
Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to deserialize properties with no matching class member into the specified collection
@@ -2471,6 +2480,15 @@
<param name="message">The error message that explains the reason for the exception.</param>
<param name="innerException">The exception that is the cause of the current exception, or <c>null</c> if no inner exception is specified.</param>
</member>
<member name="M:Newtonsoft.Json.JsonReaderException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReaderException"/> class.
</summary>
<param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
<param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
<exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is <c>null</c>.</exception>
<exception cref="T:System.Runtime.Serialization.SerializationException">The class name is <c>null</c> or <see cref="P:System.Exception.HResult"/> is zero (0).</exception>
</member>
<member name="M:Newtonsoft.Json.JsonReaderException.#ctor(System.String,System.String,System.Int32,System.Int32,System.Exception)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReaderException"/> class
@@ -2512,6 +2530,15 @@
<param name="message">The error message that explains the reason for the exception.</param>
<param name="innerException">The exception that is the cause of the current exception, or <c>null</c> if no inner exception is specified.</param>
</member>
<member name="M:Newtonsoft.Json.JsonSerializationException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonSerializationException"/> class.
</summary>
<param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
<param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
<exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is <c>null</c>.</exception>
<exception cref="T:System.Runtime.Serialization.SerializationException">The class name is <c>null</c> or <see cref="P:System.Exception.HResult"/> is zero (0).</exception>
</member>
<member name="T:Newtonsoft.Json.JsonSerializer">
<summary>
Serializes and deserializes objects into and from the JSON format.
@@ -2553,7 +2580,7 @@
<member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormatHandling">
<summary>
Gets or sets how a type name assembly is written and resolved by the serializer.
The default value is <see cref="!:FormatterAssemblyStyle.Simple" />.
The default value is <see cref="F:System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple" />.
</summary>
<value>The type name assembly format.</value>
</member>
@@ -2620,6 +2647,12 @@
serializing .NET objects to JSON and vice versa.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonSerializer.Context">
<summary>
Gets or sets the <see cref="T:System.Runtime.Serialization.StreamingContext"/> used by the serializer when invoking serialization callback methods.
</summary>
<value>The context.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializer.Formatting">
<summary>
Indicates how JSON text output is formatted.
@@ -3003,6 +3036,12 @@
</summary>
<value>The error handler called during serialization and deserialization.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.Context">
<summary>
Gets or sets the <see cref="T:System.Runtime.Serialization.StreamingContext"/> used by the serializer when invoking serialization callback methods.
</summary>
<value>The context.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.DateFormatString">
<summary>
Gets or sets how <see cref="T:System.DateTime"/> and <see cref="T:System.DateTimeOffset"/> values are formatted when writing JSON text,
@@ -5479,6 +5518,15 @@
<param name="message">The error message that explains the reason for the exception.</param>
<param name="innerException">The exception that is the cause of the current exception, or <c>null</c> if no inner exception is specified.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriterException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonWriterException"/> class.
</summary>
<param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
<param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
<exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is <c>null</c>.</exception>
<exception cref="T:System.Runtime.Serialization.SerializationException">The class name is <c>null</c> or <see cref="P:System.Exception.HResult"/> is zero (0).</exception>
</member>
<member name="M:Newtonsoft.Json.JsonWriterException.#ctor(System.String,System.String,System.Exception)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonWriterException"/> class
@@ -8898,6 +8946,15 @@
<param name="message">The error message that explains the reason for the exception.</param>
<param name="innerException">The exception that is the cause of the current exception, or <c>null</c> if no inner exception is specified.</param>
</member>
<member name="M:Newtonsoft.Json.Schema.JsonSchemaException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Schema.JsonSchemaException"/> class.
</summary>
<param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
<param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
<exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is <c>null</c>.</exception>
<exception cref="T:System.Runtime.Serialization.SerializationException">The class name is <c>null</c> or <see cref="P:System.Exception.HResult"/> is zero (0).</exception>
</member>
<member name="T:Newtonsoft.Json.Schema.JsonSchemaGenerator">
<summary>
<para>
@@ -9188,10 +9245,10 @@
</member>
<member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.IgnoreSerializableInterface">
<summary>
Gets or sets a value indicating whether to ignore the <see cref="!:ISerializable"/> interface when serializing and deserializing types.
Gets or sets a value indicating whether to ignore the <see cref="T:System.Runtime.Serialization.ISerializable"/> interface when serializing and deserializing types.
</summary>
<value>
<c>true</c> if the <see cref="!:ISerializable"/> interface will be ignored when serializing and deserializing types; otherwise, <c>false</c>.
<c>true</c> if the <see cref="T:System.Runtime.Serialization.ISerializable"/> interface will be ignored when serializing and deserializing types; otherwise, <c>false</c>.
</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.IgnoreSerializableAttribute">
@@ -9301,6 +9358,13 @@
<param name="objectType">Type of the object.</param>
<returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonLinqContract"/> for the given type.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateISerializableContract(System.Type)">
<summary>
Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonISerializableContract"/> for the given type.
</summary>
<param name="objectType">Type of the object.</param>
<returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonISerializableContract"/> for the given type.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateDynamicContract(System.Type)">
<summary>
Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonDynamicContract"/> for the given type.
@@ -9707,6 +9771,21 @@
</summary>
<param name="underlyingType">The underlying type for the contract.</param>
</member>
<member name="T:Newtonsoft.Json.Serialization.SerializationCallback">
<summary>
Handles <see cref="T:Newtonsoft.Json.JsonSerializer"/> serialization callback events.
</summary>
<param name="o">The object that raised the callback event.</param>
<param name="context">The streaming context.</param>
</member>
<member name="T:Newtonsoft.Json.Serialization.SerializationErrorCallback">
<summary>
Handles <see cref="T:Newtonsoft.Json.JsonSerializer"/> serialization error callback events.
</summary>
<param name="o">The object that raised the callback event.</param>
<param name="context">The streaming context.</param>
<param name="errorContext">The error context.</param>
</member>
<member name="T:Newtonsoft.Json.Serialization.ExtensionDataSetter">
<summary>
Sets extension data for an object during deserialization.
@@ -9750,6 +9829,36 @@
</summary>
<value>The converter.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonContract.OnDeserializedCallbacks">
<summary>
Gets or sets all methods called immediately after deserialization of the object.
</summary>
<value>The methods called immediately after deserialization of the object.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonContract.OnDeserializingCallbacks">
<summary>
Gets or sets all methods called during deserialization of the object.
</summary>
<value>The methods called during deserialization of the object.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonContract.OnSerializedCallbacks">
<summary>
Gets or sets all methods called after serialization of the object graph.
</summary>
<value>The methods called after serialization of the object graph.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonContract.OnSerializingCallbacks">
<summary>
Gets or sets all methods called before serialization of the object.
</summary>
<value>The methods called before serialization of the object.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonContract.OnErrorCallbacks">
<summary>
Gets or sets all method called when an error is thrown during the serialization of the object.
</summary>
<value>The methods called when an error is thrown during the serialization of the object.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonContract.DefaultCreator">
<summary>
Gets or sets the default creator method used to create the object.
@@ -9826,6 +9935,23 @@
</summary>
<param name="underlyingType">The underlying type for the contract.</param>
</member>
<member name="T:Newtonsoft.Json.Serialization.JsonISerializableContract">
<summary>
Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonISerializableContract.ISerializableCreator">
<summary>
Gets or sets the <see cref="T:System.Runtime.Serialization.ISerializable"/> object constructor.
</summary>
<value>The <see cref="T:System.Runtime.Serialization.ISerializable"/> object constructor.</value>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonISerializableContract.#ctor(System.Type)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonISerializableContract"/> class.
</summary>
<param name="underlyingType">The underlying type for the contract.</param>
</member>
<member name="T:Newtonsoft.Json.Serialization.JsonLinqContract">
<summary>
Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -54,6 +54,7 @@
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<None Include="Game.Build.json" />
<None Include="WSACertificate.pfx" />
</ItemGroup>
<ItemGroup>{3}
@@ -70,7 +71,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="Content\**" />
<MonoDataFiles Include="Mono\**" />
<MonoDataFiles Include="$(ProjectDir)Mono\**" />
<DataSecondary Include="DataSecondary\**" />
</ItemGroup>
<ItemGroup>
Binary file not shown.
@@ -54,6 +54,7 @@
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<None Include="Game.Build.json" />
<None Include="WSACertificate.pfx" />
</ItemGroup>
<ItemGroup>{3}
@@ -70,7 +71,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="Content\**" />
<MonoDataFiles Include="Mono\**" />
<MonoDataFiles Include="$(ProjectDir)Mono\**" />
<DataSecondary Include="DataSecondary\**" />
</ItemGroup>
<ItemGroup>
+8
View File
@@ -3172,6 +3172,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator
node->bone_link_property = con.property;
}
break;
default:
break;
}
switch (parent->getType())
@@ -3190,6 +3192,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator
mesh->geometry = (Geometry*)child;
break;
case Object::Type::MATERIAL: mesh->materials.push_back((Material*)child); break;
default:
break;
}
break;
}
@@ -3323,6 +3327,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator
}
break;
}
default:
break;
}
}
@@ -3350,6 +3356,8 @@ static bool parseObjects(const Element& root, Scene* scene, u64 flags, Allocator
return false;
}
break;
default:
break;
}
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ public class curl : DepsModule
options.OutputFiles.Add("crypt32.lib");
break;
case TargetPlatform.Linux:
options.Libraries.Add("curl");
options.OutputFiles.Add(Path.Combine(depsRoot, "libcurl.a"));
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}
@@ -250,66 +250,66 @@ namespace Flax.Build.Bindings
var path = GetCachePath(moduleInfo.Module, moduleOptions);
if (!File.Exists(path))
return false;
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var reader = new BinaryReader(stream, Encoding.UTF8))
try
{
// Version
var version = reader.ReadInt32();
if (version != CacheVersion)
return false;
if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64())
return false;
// Build options
if (reader.ReadString() != moduleOptions.IntermediateFolder ||
reader.ReadInt32() != (int)moduleOptions.Platform.Target ||
reader.ReadInt32() != (int)moduleOptions.Architecture ||
reader.ReadInt32() != (int)moduleOptions.Configuration)
return false;
var publicDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
if (publicDefinitions.Length != moduleOptions.PublicDefinitions.Count || publicDefinitions.Any(x => !moduleOptions.PublicDefinitions.Contains(x)))
return false;
var privateDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
if (privateDefinitions.Length != moduleOptions.PrivateDefinitions.Count || privateDefinitions.Any(x => !moduleOptions.PrivateDefinitions.Contains(x)))
return false;
var preprocessorDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
if (preprocessorDefinitions.Length != moduleOptions.CompileEnv.PreprocessorDefinitions.Count || preprocessorDefinitions.Any(x => !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(x)))
return false;
// Header files
var headerFilesCount = reader.ReadInt32();
if (headerFilesCount != headerFiles.Count)
return false;
for (int i = 0; i < headerFilesCount; i++)
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var reader = new BinaryReader(stream, Encoding.UTF8))
{
var headerFile = headerFiles[i];
if (headerFile != reader.ReadString())
// Version
var version = reader.ReadInt32();
if (version != CacheVersion)
return false;
if (File.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64())
if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64())
return false;
}
// Info
var newModuleInfo = new ModuleInfo
{
Module = moduleInfo.Module,
Name = moduleInfo.Name,
Namespace = moduleInfo.Namespace,
IsFromCache = true,
};
try
{
// Build options
if (reader.ReadString() != moduleOptions.IntermediateFolder ||
reader.ReadInt32() != (int)moduleOptions.Platform.Target ||
reader.ReadInt32() != (int)moduleOptions.Architecture ||
reader.ReadInt32() != (int)moduleOptions.Configuration)
return false;
var publicDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
if (publicDefinitions.Length != moduleOptions.PublicDefinitions.Count || publicDefinitions.Any(x => !moduleOptions.PublicDefinitions.Contains(x)))
return false;
var privateDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
if (privateDefinitions.Length != moduleOptions.PrivateDefinitions.Count || privateDefinitions.Any(x => !moduleOptions.PrivateDefinitions.Contains(x)))
return false;
var preprocessorDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
if (preprocessorDefinitions.Length != moduleOptions.CompileEnv.PreprocessorDefinitions.Count || preprocessorDefinitions.Any(x => !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(x)))
return false;
// Header files
var headerFilesCount = reader.ReadInt32();
if (headerFilesCount != headerFiles.Count)
return false;
for (int i = 0; i < headerFilesCount; i++)
{
var headerFile = headerFiles[i];
if (headerFile != reader.ReadString())
return false;
if (File.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64())
return false;
}
// Info
var newModuleInfo = new ModuleInfo
{
Module = moduleInfo.Module,
Name = moduleInfo.Name,
Namespace = moduleInfo.Namespace,
IsFromCache = true,
};
newModuleInfo.Read(reader);
// Skip parsing and use data loaded from cache
moduleInfo = newModuleInfo;
return true;
}
catch
{
// Skip loading cache
return false;
}
}
catch
{
// Skip loading cache
return false;
}
}
}
@@ -1129,6 +1129,7 @@ namespace Flax.Build.Bindings
var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0;
// C# event invoking wrapper (calls C# event from C++ delegate)
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h");
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h");
contents.Append(" ");
if (eventInfo.IsStatic)
@@ -1251,7 +1252,13 @@ namespace Flax.Build.Bindings
if (fieldInfo.Getter != null)
GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Getter, "{0}");
if (fieldInfo.Setter != null)
GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Setter, "{0} = {1}");
{
var callFormat = "{0} = {1}";
var type = fieldInfo.Setter.Parameters[0].Type;
if (type.IsArray)
callFormat = $"auto __tmp = {{1}}; for (int32 i = 0; i < {type.ArraySize}; i++) {{0}}[i] = __tmp[i]";
GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Setter, callFormat);
}
}
// Properties
@@ -623,9 +623,6 @@ namespace Flax.Build.Bindings
if (!fieldInfo.IsReadOnly)
{
if (fieldInfo.Type.IsArray)
throw new NotImplementedException("Use ReadOnly on field. TODO: add support for setter for fixed-array fields.");
fieldInfo.Setter = new FunctionInfo
{
Name = "Set" + fieldInfo.Name,
@@ -246,7 +246,7 @@ namespace Flax.Build
// Get all modules aggregated into all binary modules used in all configurations of this target
foreach (var configurationData in mainProject.Configurations)
{
var configurationBinaryModules = GetBinaryModules(rootProject, configurationData.Target, configurationData.Modules);
var configurationBinaryModules = GetBinaryModules(projectInfo, configurationData.Target, configurationData.Modules);
foreach (var configurationBinaryModule in configurationBinaryModules)
{
// Skip if none of the included binary modules is inside the project workspace (eg. merged external binary modules from engine to game project)
@@ -272,7 +272,7 @@ namespace Flax.Build
{
var referenceBuildOptions = GetBuildOptions(referenceTarget, configurationData.TargetBuildOptions.Platform, configurationData.TargetBuildOptions.Toolchain, configurationData.Architecture, configurationData.Configuration, reference.Project.ProjectFolderPath);
var referenceModules = CollectModules(rules, referenceBuildOptions.Platform, referenceTarget, referenceBuildOptions, referenceBuildOptions.Toolchain, referenceBuildOptions.Architecture, referenceBuildOptions.Configuration);
var referenceBinaryModules = GetBinaryModules(rootProject, referenceTarget, referenceModules);
var referenceBinaryModules = GetBinaryModules(projectInfo, referenceTarget, referenceModules);
foreach (var binaryModule in referenceBinaryModules)
{
project.Defines.Add(binaryModule.Key.ToUpperInvariant() + "_API=");
+11
View File
@@ -82,6 +82,17 @@ namespace Flax.Build
return GetModuleProject(module, buildData.Project);
}
/// <summary>
/// Checks if the project that contains a given module (checks for modules located in the given project Source folder).
/// </summary>
/// <param name="module">The module.</param>
/// <param name="project">The project to check.</param>
/// <returns>True if project contains that module inside, otherwise it's external or referenced.</returns>
public static bool IsModuleFromProject(Module module, ProjectInfo project)
{
return GetModuleProject(module, project) == project;
}
/// <summary>
/// Builds the targets.
/// </summary>
@@ -101,7 +101,7 @@ namespace Flax.Build
}
// Mono on Linux is using dynamic linking and needs additional link files
if (buildOptions.Platform.Target == TargetPlatform.Linux && Platform.BuildTargetPlatform == TargetPlatform.Linux)
if (buildOptions.Platform.Target == TargetPlatform.Linux && Platform.BuildTargetPlatform == TargetPlatform.Linux && !IsPreBuilt)
{
var task = graph.Add<Task>();
task.PrerequisiteFiles.Add(Path.Combine(buildOptions.OutputFolder, "libmonosgen-2.0.so"));
@@ -141,7 +141,7 @@ namespace Flax.Build
// Build Main module
var mainModule = rules.GetModule("Main");
var mainModuleOutputPath = Path.Combine(exeBuildOptions.IntermediateFolder, mainModule.Name);
if (!IsPreBuilt && !Directory.Exists(mainModuleOutputPath))
if (!Directory.Exists(mainModuleOutputPath))
Directory.CreateDirectory(mainModuleOutputPath);
var mainModuleOptions = new BuildOptions
{
@@ -696,6 +696,26 @@ namespace Flax.Build
{
// Export symbols from binary module
moduleOptions.CompileEnv.PreprocessorDefinitions.Add(binaryModuleNameUpper + (target.UseSymbolsExports ? "_API=" + toolchain.DllExport : "_API="));
// Import symbols from binary modules containing the referenced modules (from this project only, external ones are handled via ReferenceBuilds below)
foreach (var moduleName in moduleOptions.PrivateDependencies)
{
var dependencyModule = buildData.Rules.GetModule(moduleName);
if (dependencyModule != null && !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && dependencyModule.BinaryModuleName != binaryModule.Key && IsModuleFromProject(dependencyModule, project) && buildData.Modules.TryGetValue(dependencyModule, out var dependencyOptions))
{
// Import symbols from referenced binary module
moduleOptions.CompileEnv.PreprocessorDefinitions.Add(dependencyModule.BinaryModuleName.ToUpperInvariant() + "_API=" + toolchain.DllImport);
}
}
foreach (var moduleName in moduleOptions.PublicDependencies)
{
var dependencyModule = buildData.Rules.GetModule(moduleName);
if (dependencyModule != null && !string.IsNullOrEmpty(dependencyModule.BinaryModuleName) && dependencyModule.BinaryModuleName != binaryModule.Key && IsModuleFromProject(dependencyModule, project) && buildData.Modules.TryGetValue(dependencyModule, out var dependencyOptions))
{
// Import symbols from referenced binary module
moduleOptions.CompileEnv.PreprocessorDefinitions.Add(dependencyModule.BinaryModuleName.ToUpperInvariant() + "_API=" + toolchain.DllImport);
}
}
}
else
{
@@ -45,7 +45,7 @@ namespace Flax.Build
throw new Exception($"Invalid or missing editor target {project.EditorTarget} specified in project {project.Name} (referenced by project {Project.Name}).");
return result;
}
if (!IsEditor && !string.IsNullOrEmpty(project.GameTarget))
if (!string.IsNullOrEmpty(project.GameTarget))
{
var result = projectTargets.FirstOrDefault(x => x.Name == project.GameTarget);
if (result == null)
@@ -82,10 +82,8 @@ namespace Flax.Deps.Dependencies
case TargetPlatform.PS4:
case TargetPlatform.XboxScarlett:
{
foreach (var file in outputFileNames)
{
Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, platform.ToString(), file));
}
var file = "Newtonsoft.Json.dll";
Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, platform.ToString(), "Binaries", file));
break;
}
}
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -25,6 +26,11 @@ namespace Flax.Deps.Dependencies
{
TargetPlatform.Windows,
};
case TargetPlatform.Linux:
return new[]
{
TargetPlatform.Linux,
};
default: return new TargetPlatform[0];
}
}
@@ -76,7 +82,37 @@ namespace Flax.Deps.Dependencies
foreach (var filename in binariesToCopyWin)
Utilities.FileCopy(Path.Combine(root, "build", "Win64", vcVersion, configuration, filename), Path.Combine(depsFolder, Path.GetFileName(filename)));
}
break;
}
case TargetPlatform.Linux:
{
// Build for Linux
var settings = new []
{
"-without-librtmp",
"--without-ssl",
"--with-gnutls",
"--disable-ipv6",
"--disable-manual",
"--disable-verbose",
"--disable-shared",
"--enable-static",
"-disable-ldap --disable-sspi --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb",
};
var envVars = new Dictionary<string, string>
{
{ "CC", "clang-7" },
{ "CC_FOR_BUILD", "clang-7" }
};
var buildDir = Path.Combine(root, "build");
SetupDirectory(buildDir, true);
Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.None);
Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.None, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.None);
Utilities.Run("make", "install", null, root, Utilities.RunOptions.None);
var depsFolder = GetThirdPartyFolder(options, TargetPlatform.Linux, TargetArchitecture.x64);
var filename = "libcurl.a";
Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename));
break;
}
}
@@ -67,10 +67,10 @@ namespace Flax.Build.Platforms
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_UWP");
options.CompileEnv.PreprocessorDefinitions.Add("WINAPI_FAMILY=WINAPI_FAMILY_PC_APP");
options.CompileEnv.PreprocessorDefinitions.Add("_WINRT_DLL");
options.CompileEnv.PreprocessorDefinitions.Add("_WINDLL");
options.CompileEnv.PreprocessorDefinitions.Add("__WRL_NO_DEFAULT_LIB__");
options.LinkEnv.InputLibraries.Add("WindowsApp.lib");
options.LinkEnv.InputLibraries.Add("dloadhelper.lib");
}
}
}
@@ -680,6 +680,8 @@ namespace Flax.Build.Platforms
args.Add("/WINMD");
args.Add(string.Format("/WINMDFILE:\"{0}\"", Path.ChangeExtension(outputFilePath, "winmd")));
args.Add("/APPCONTAINER");
if (linkEnvironment.Output == LinkerOutput.SharedLibrary)
args.Add("/DYNAMICBASE");
}
if (linkEnvironment.LinkTimeCodeGeneration)
@@ -937,7 +939,7 @@ namespace Flax.Build.Platforms
xmlTextWriter.WriteStartElement("Properties");
// TODO: better logo handling
var logoSrcPath = Path.Combine(Environment.CurrentDirectory, "Source", "Logo.png");
var logoSrcPath = Path.Combine(Globals.EngineRoot, "Source", "Logo.png");
var logoDstPath = Path.Combine(options.IntermediateFolder, "Logo.png");
if (!File.Exists(logoDstPath))
Utilities.FileCopy(logoSrcPath, logoDstPath);
+2
View File
@@ -119,6 +119,8 @@ namespace Flax.Build
private bool IsTargetCSharpOnly(string name)
{
if (string.IsNullOrWhiteSpace(name))
return true;
var rules = Builder.GenerateRulesAssembly();
var target = rules.GetTarget(name);
return target == null || target.Modules.TrueForAll(x => !rules.GetModule(x).BuildNativeCode);
+2 -2
View File
@@ -143,9 +143,9 @@
<!-- StringBuilder -->
<Type Name="StringBuilder">
<DisplayString>{_data_allocation.._data,su}</DisplayString>
<DisplayString>{_data._allocation._data,su}</DisplayString>
<Expand>
<Item Name="[Raw]" ExcludeView="simple">_data._data</Item>
<Item Name="[Raw]" ExcludeView="simple">_data._allocation._data</Item>
<Item Name="[Size]" ExcludeView="simple">_data._count</Item>
<ArrayItems>
<Size>_data._count</Size>