Merge remote-tracking branch 'origin/master' into 1.10

# Conflicts:
#	Content/Editor/Gizmo/Material.flax
#	Content/Engine/DefaultTerrainMaterial.flax
#	Source/Editor/Windows/Assets/ModelWindow.cs
#	Source/Editor/Windows/Assets/SkinnedModelWindow.cs
#	Source/Engine/Core/Types/Variant.cpp
This commit is contained in:
Wojtek Figat
2025-01-13 18:07:54 +01:00
87 changed files with 1153 additions and 199 deletions

View File

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

View File

@@ -35,12 +35,12 @@ jobs:
run: |
.\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Windows-Editor
path: Output/Editor.zip
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Windows-EditorDebugSymbols
path: Output/EditorDebugSymbols.zip
@@ -68,7 +68,7 @@ jobs:
run: |
.\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Windows-Game
path: Output/Windows.zip
@@ -101,7 +101,7 @@ jobs:
run: |
./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Linux-Editor
path: Output/FlaxEditorLinux.zip
@@ -132,7 +132,7 @@ jobs:
run: |
./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Linux-Game
path: Output/Linux.zip
@@ -162,7 +162,7 @@ jobs:
run: |
./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mac-Editor
path: Output/FlaxEditorMac.zip
@@ -190,7 +190,7 @@ jobs:
run: |
./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: Mac-Game
path: Output/Mac.zip

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
%copyright%using System;
using System.Collections.Generic;
using FlaxEngine;
namespace %namespace%;
/// <summary>
/// %class% GamePlugin.
/// </summary>
public class %class% : GamePlugin
{
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
}
/// <inheritdoc/>
public override void Deinitialize()
{
base.Deinitialize();
}
}

View File

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

View File

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

View File

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

View File

@@ -671,11 +671,14 @@ bool GameCookerImpl::Build()
MCore::Thread::Attach();
// Build Started
CallEvent(GameCooker::EventType::BuildStarted);
data.Tools->OnBuildStarted(data);
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildStarted(data);
data.InitProgress(Steps.Count());
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
{
CallEvent(GameCooker::EventType::BuildStarted);
data.Tools->OnBuildStarted(data);
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildStarted(data);
data.InitProgress(Steps.Count());
}
// Execute all steps in a sequence
bool failed = false;
@@ -741,10 +744,13 @@ bool GameCookerImpl::Build()
}
IsRunning = false;
CancelFlag = 0;
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildEnded(data, failed);
data.Tools->OnBuildEnded(data, failed);
CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
if (!EnumHasAnyFlags(data.Options, BuildOptions::NoCook))
{
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildEnded(data, failed);
data.Tools->OnBuildEnded(data, failed);
CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
}
Delete(Data);
Data = nullptr;

View File

@@ -364,6 +364,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
}
#endif
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release;
if (platformSettings->BuildAAB)
{
// .aab
{
CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT(":app:bundle") : TEXT(":app:bundleDebug"));
procSettings.WorkingDirectory = data.OriginalOutputPath;
const int32 result = Platform::CreateProcess(procSettings);
if (result != 0)
{
data.Error(String::Format(TEXT("Failed to build Gradle project into .aab package (result code: {0}). See log for more info."), result));
return true;
}
}
// Copy result package
const String aab = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/bundle/release/app-release.aab") : TEXT("app/build/outputs/bundle/debug/app-debug.aab"));
const String outputAab = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".aab");
if (FileSystem::CopyFile(outputAab, aab))
{
LOG(Error, "Failed to copy .aab package from {0} to {1}", aab, outputAab);
return true;
}
LOG(Info, "Output Android AAB application package: {0} (size: {1} MB)", outputAab, FileSystem::GetFileSize(outputAab) / 1024 / 1024);
}
// .apk
{
CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
@@ -371,20 +398,20 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
const int32 result = Platform::CreateProcess(procSettings);
if (result != 0)
{
data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result));
data.Error(String::Format(TEXT("Failed to build Gradle project into .apk package (result code: {0}). See log for more info."), result));
return true;
}
}
// Copy result package
const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk"));
const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk");
if (FileSystem::CopyFile(outputApk, apk))
{
LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk);
LOG(Error, "Failed to copy .apk package from {0} to {1}", apk, outputApk);
return true;
}
LOG(Info, "Output Android application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
LOG(Info, "Output Android APK application package: {0} (size: {1} MB)", outputApk, FileSystem::GetFileSize(outputApk) / 1024 / 1024);
return false;
}

View File

@@ -214,6 +214,33 @@ bool DeployDataStep::Perform(CookingData& data)
FileSystem::NormalizePath(srcDotnet);
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet);
// Get major Version
Array<String> pathParts;
srcDotnet.Split('/', pathParts);
String version;
for (int i = 0; i < pathParts.Count(); i++)
{
if (pathParts[i] == TEXT("runtimes"))
{
Array<String> versionParts;
pathParts[i - 1].Split('.', versionParts);
if (!versionParts.IsEmpty())
{
const String majorVersion = versionParts[0].TrimTrailing();
int32 versionNum;
StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum);
if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part
version = majorVersion;
}
}
}
if (version.IsEmpty())
{
data.Error(TEXT("Failed to find supported .NET version for the current host platform."));
return true;
}
// Deploy runtime files
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms"));
@@ -226,14 +253,14 @@ bool DeployDataStep::Perform(CookingData& data)
{
// AOT runtime files inside Engine Platform folder
packFolder /= TEXT("Dotnet");
dstDotnetLibs /= TEXT("lib/net8.0");
srcDotnetLibs = packFolder / TEXT("lib/net8.0");
dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs = packFolder / String::Format(TEXT("lib/net{}.0"), version);
}
else
{
// Runtime files inside Dotnet SDK folder but placed for AOT
dstDotnetLibs /= TEXT("lib/net8.0");
srcDotnetLibs /= TEXT("../lib/net8.0");
dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
}
}
else
@@ -241,14 +268,14 @@ bool DeployDataStep::Perform(CookingData& data)
if (srcDotnetFromEngine)
{
// Runtime files inside Engine Platform folder
dstDotnetLibs /= TEXT("lib/net8.0");
srcDotnetLibs /= TEXT("lib/net8.0");
dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
}
else
{
// Runtime files inside Dotnet SDK folder
dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App");
srcDotnetLibs /= TEXT("../lib/net8.0");
srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
}
}
LOG(Info, "Copying .NET files from {} to {}", packFolder, dstDotnet);
@@ -273,6 +300,7 @@ bool DeployDataStep::Perform(CookingData& data)
DEPLOY_NATIVE_FILE("libmonosgen-2.0.so");
DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Globalization.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so");
break;
case BuildPlatform::iOSARM64:

View File

@@ -301,7 +301,7 @@ namespace FlaxEditor.CustomEditors
_valueToSet = null;
// Assign value
if (val is IList l && l.Count == _values.Count)
if (val is IList l && l.Count == _values.Count && _values.Count > 1)
{
for (int i = 0; i < _values.Count; i++)
_values[i] = l[i];

View File

@@ -9,6 +9,7 @@ using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tree;
using FlaxEditor.Modules;
using FlaxEditor.Scripting;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
@@ -85,12 +86,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Edit selected prefab asset
var editPrefab = panel.Button("Edit Prefab");
editPrefab.Button.Clicked += () =>
{
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
Editor.Instance.Windows.ContentWin.Select(prefab);
Editor.Instance.Windows.ContentWin.Open(Editor.Instance.Windows.ContentWin.View.Selection[0]);
};
editPrefab.Button.Clicked += () => Editor.Instance.Windows.ContentWin.Open(Editor.Instance.ContentDatabase.FindAsset(prefab.ID));
// Viewing changes applied to this actor
var viewChanges = panel.Button("View Changes");

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Linq;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Surface;
using FlaxEngine;
@@ -35,6 +36,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
(instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier),
(instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value),
Values);
if (!parameters.Any())
group.Label("No parameters", TextAlignment.Center);
_parametersAdded = true;
}
}

View File

@@ -53,6 +53,9 @@ public class ModelPrefabEditor : GenericEditor
}
}
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, modelPrefab.ImportPath, false);
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
_reimportButton = button.Button;
_reimportButton.Clicked += OnReimport;

View File

@@ -117,6 +117,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
var data = SurfaceUtils.InitGraphParameters(parametersGroup);
SurfaceUtils.DisplayGraphParameters(group, data, ParameterGet, ParameterSet, Values, ParameterDefaultValue);
}
if (!parameters.Any())
groups.Label("No parameters", TextAlignment.Center);
}
/// <inheritdoc />

View File

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

View File

@@ -723,10 +723,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
}
// Refresh anchors
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
if (xEq != _cachedXEq || yEq != _cachedYEq)
if (Values != null && Values[0] != null)
{
RebuildLayout();
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
if (xEq != _cachedXEq || yEq != _cachedYEq)
{
RebuildLayout();
}
}
base.Refresh();

View File

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

View File

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

View File

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

View File

@@ -228,7 +228,6 @@ namespace FlaxEditor.CustomEditors.Editors
// Handle Sliding
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
{
// TODO: handle linked values
Float3 average = Float3.Zero;
for (int i = 0; i < Values.Count; i++)
{
@@ -251,12 +250,24 @@ namespace FlaxEditor.CustomEditors.Editors
for (int i = 0; i < Values.Count; i++)
{
var v = Values[i];
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
if (LinkValues)
{
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(newValue.X, newValue.Y, newValue.Z);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(newValue.X, newValue.Y, newValue.Z);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(newValue.X, newValue.Y, newValue.Z);
}
else
{
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
}
newObjects[i] = v;
}
@@ -267,16 +278,27 @@ namespace FlaxEditor.CustomEditors.Editors
}
else
{
// TODO: handle linked values
for (int i = 0; i < Values.Count; i++)
{
object v = Values[i];
if (v is Vector3 asVector3)
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
else if (v is Float3 asFloat3)
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
else if (v is Double3 asDouble3)
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
if (LinkValues)
{
if (v is Vector3 asVector3)
v = asVector3 + new Vector3(xValue, yValue, zValue);
else if (v is Float3 asFloat3)
v = asFloat3 + new Float3(xValue, yValue, zValue);
else if (v is Double3 asDouble3)
v = asDouble3 + new Double3(xValue, yValue, zValue);
}
else
{
if (v is Vector3 asVector3)
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
else if (v is Float3 asFloat3)
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
else if (v is Double3 asDouble3)
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
}
newObjects[i] = v;
}

View File

@@ -386,7 +386,7 @@ namespace FlaxEditor.CustomEditors
if (instanceValues.Count != Count)
throw new ArgumentException();
if (value is IList l && l.Count == Count)
if (value is IList l && l.Count == Count && Count > 1)
{
for (int i = 0; i < Count; i++)
{

View File

@@ -550,7 +550,7 @@ int32 Editor::LoadProduct()
}
if (!FileSystem::FileExists(files[0]))
{
Platform::Fatal(TEXT("Cannot opoen selected project file because it doesn't exist."));
Platform::Fatal(TEXT("Cannot open selected project file because it doesn't exist."));
return -1;
}
projectPath = StringUtils::GetDirectoryName(files[0]);

View File

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

View File

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

View File

@@ -1,6 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using FlaxEditor.Options;
using FlaxEditor.SceneGraph;
using FlaxEngine;
using System;
namespace FlaxEditor.Gizmo
{
@@ -18,8 +21,13 @@ namespace FlaxEditor.Gizmo
private MaterialInstance _materialAxisY;
private MaterialInstance _materialAxisZ;
private MaterialInstance _materialAxisFocus;
private MaterialInstance _materialAxisLocked;
private MaterialBase _materialSphere;
// Material Parameter Names
const String _brightnessParamName = "Brightness";
const String _opacityParamName = "Opacity";
private void InitDrawing()
{
// Axis Models
@@ -34,6 +42,7 @@ namespace FlaxEditor.Gizmo
_materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY");
_materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ");
_materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus");
_materialAxisLocked = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisLocked");
_materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere");
// Ensure that every asset was loaded
@@ -50,6 +59,25 @@ namespace FlaxEditor.Gizmo
{
Platform.Fatal("Failed to load transform gizmo resources.");
}
// Setup editor options
OnEditorOptionsChanged(Editor.Instance.Options.Options);
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
}
private void OnEditorOptionsChanged(EditorOptions options)
{
float brightness = options.Visual.TransformGizmoBrightness;
_materialAxisX.SetParameterValue(_brightnessParamName, brightness);
_materialAxisY.SetParameterValue(_brightnessParamName, brightness);
_materialAxisZ.SetParameterValue(_brightnessParamName, brightness);
_materialAxisLocked.SetParameterValue(_brightnessParamName, brightness);
float opacity = options.Visual.TransformGizmoOpacity;
_materialAxisX.SetParameterValue(_opacityParamName, opacity);
_materialAxisY.SetParameterValue(_opacityParamName, opacity);
_materialAxisZ.SetParameterValue(_opacityParamName, opacity);
_materialAxisLocked.SetParameterValue(_opacityParamName, opacity);
}
/// <inheritdoc />
@@ -60,6 +88,21 @@ namespace FlaxEditor.Gizmo
if (!_modelCube || !_modelCube.IsLoaded)
return;
// Find out if any of the selected objects can not be moved
bool gizmoLocked = false;
if (Editor.Instance.StateMachine.IsPlayMode)
{
for (int i = 0; i < SelectionCount; i++)
{
var obj = GetSelectedObject(i);
if (obj.CanTransform == false)
{
gizmoLocked = true;
break;
}
}
}
// As all axisMesh have the same pivot, add a little offset to the x axisMesh, this way SortDrawCalls is able to sort the draw order
// https://github.com/FlaxEngine/FlaxEngine/issues/680
@@ -92,32 +135,38 @@ namespace FlaxEditor.Gizmo
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
transAxisMesh.Draw(ref renderContext, xAxisMaterialTransform, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
transAxisMesh.Draw(ref renderContext, yAxisMaterialTransform, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zAxisMaterialTransform = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
transAxisMesh.Draw(ref renderContext, zAxisMaterialTransform, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xyPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
cubeMesh.Draw(ref renderContext, xyPlaneMaterialTransform, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zxPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisY);
cubeMesh.Draw(ref renderContext, zxPlaneMaterialTransform, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yzPlaneMaterialTransform = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisZ);
cubeMesh.Draw(ref renderContext, yzPlaneMaterialTransform, ref m3);
// Center sphere
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
@@ -136,15 +185,18 @@ namespace FlaxEditor.Gizmo
// X axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
rotationAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3);
// Y axis
rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
rotationAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m1);
// Z axis
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
rotationAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
@@ -163,32 +215,38 @@ namespace FlaxEditor.Gizmo
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref mx1, out m3);
scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isXAxis ? _materialAxisFocus : _materialAxisX);
scaleAxisMesh.Draw(ref renderContext, xAxisMaterialRotate, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isYAxis ? _materialAxisFocus : _materialAxisY);
scaleAxisMesh.Draw(ref renderContext, yAxisMaterialRotate, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zAxisMaterialRotate = gizmoLocked ? _materialAxisLocked : (isZAxis ? _materialAxisFocus : _materialAxisZ);
scaleAxisMesh.Draw(ref renderContext, zAxisMaterialRotate, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
MaterialInstance xyPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX);
cubeMesh.Draw(ref renderContext, xyPlaneMaterialScale, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
MaterialInstance zxPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ);
cubeMesh.Draw(ref renderContext, zxPlaneMaterialScale, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
MaterialInstance yzPlaneMaterialScale = gizmoLocked ? _materialAxisLocked : (_activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY);
cubeMesh.Draw(ref renderContext, yzPlaneMaterialScale, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);

View File

@@ -169,12 +169,12 @@ namespace FlaxEditor.Gizmo
closestIntersection = intersection;
}
/*// Center
if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
// Center
if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
{
_activeAxis = Axis.Center;
closestIntersection = intersection;
}*/
}
break;
}

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.Gizmo
/// <summary>
/// Offset to move axis away from center
/// </summary>
private const float AxisOffset = 0.8f;
private const float AxisOffset = 1.2f;
/// <summary>
/// How thick the axis should be

View File

@@ -501,7 +501,7 @@ namespace FlaxEditor.Gizmo
_scaleDelta = Vector3.Zero;
if (ActiveAxis == Axis.Center)
scaleDelta = new Vector3(scaleDelta.AvgValue);
scaleDelta = new Vector3(scaleDelta.ValuesSum);
}
// Apply transformation (but to the parents, not whole selection pool)

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Gizmo;
@@ -647,7 +648,36 @@ namespace FlaxEditor
private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, float scale, Float2 resizeAxis, CursorType cursor)
{
var style = Style.Current;
var rect = new Rectangle((pos + resizeAxis * 10 * scale) - size * 0.5f, size);
var control = uiControl.Control;
var rotation = control.Rotation;
var rotationInRadians = rotation * Mathf.DegreesToRadians;
var rect = new Rectangle(
(pos +
new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians),
resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 10 * scale) - size * 0.5f,
size);
// Find more correct cursor at different angles
var unwindRotation = Mathf.UnwindDegrees(rotation);
if (unwindRotation is (>= 45 and < 135) or (> -135 and <= -45) )
{
switch (cursor)
{
case CursorType.SizeNESW:
cursor = CursorType.SizeNWSE;
break;
case CursorType.SizeNS:
cursor = CursorType.SizeWE;
break;
case CursorType.SizeNWSE:
cursor = CursorType.SizeNESW;
break;
case CursorType.SizeWE:
cursor = CursorType.SizeNS;
break;
default: break;
}
}
if (rect.Contains(ref mousePos))
{
Render2D.FillRectangle(rect, style.Foreground);

View File

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

View File

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

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Text;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
@@ -31,11 +33,33 @@ namespace FlaxEditor.Options
private void OnResetButtonClicked()
{
var obj = new T();
var str = JsonSerializer.Serialize(obj);
JsonSerializer.Deserialize(Values[0], str);
SetValue(Values[0]);
Refresh();
var editorClassName = typeof(T).Name;
var editorName = new StringBuilder();
editorName.Append(editorClassName[0]);
for (var i = 1; i < editorClassName.Length; i++)
{
// Whenever there is an uppercase letter, add a space to make it more pretty for the end user
if (char.IsUpper(editorClassName[i]))
{
editorName.Append(' ');
}
editorName.Append(editorClassName[i]);
}
var result = MessageBox.Show($"Are you sure you want to reset \"{editorName}\" to default values?",
"Reset values?",
MessageBoxButtons.YesNo
);
if (result == DialogResult.Yes || result == DialogResult.OK)
{
var obj = new T();
var str = JsonSerializer.Serialize(obj);
JsonSerializer.Deserialize(Values[0], str);
SetValue(Values[0]);
Refresh();
}
}
}
}

View File

@@ -258,6 +258,13 @@ namespace FlaxEditor.Options
private TextAlignment _tooltipTextAlignment = TextAlignment.Center;
/// <summary>
/// Whether to scroll to the script when a script is added to an actor.
/// </summary>
[DefaultValue(true)]
[EditorDisplay("Interface"), EditorOrder(322)]
public bool ScrollToScriptOnAdd { get; set; } = true;
/// <summary>
/// Gets or sets the timestamps prefix mode for output log messages.
/// </summary>

View File

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

View File

@@ -596,11 +596,17 @@ namespace FlaxEditor.SceneGraph.GUI
{
bool worldPositionsStays = Root.GetKey(KeyboardKeys.Control) == false;
var objects = new SceneObject[_dragActors.Objects.Count];
var treeNodes = new TreeNode[_dragActors.Objects.Count];
for (int i = 0; i < objects.Length; i++)
{
objects[i] = _dragActors.Objects[i].Actor;
treeNodes[i] = _dragActors.Objects[i].TreeNode;
}
var action = new ParentActorsAction(objects, newParent, newOrder, worldPositionsStays);
ActorNode.Root.Undo?.AddAction(action);
action.Do();
ParentTree.Focus();
ParentTree.Select(treeNodes.ToList());
result = DragDropEffect.Move;
}
// Drag scripts

View File

@@ -1216,13 +1216,16 @@ namespace FlaxEditor.Surface.Archetypes
// Highlight selected blend point
var style = Style.Current;
var selectedIndex = _selectedAnimation.SelectedIndex;
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
if (selectedIndex != -1 && selectedIndex < _editor.BlendPoints.Count && (ContainsFocus || IsMouseOver))
{
var point = _editor.BlendPoints[selectedIndex];
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
Render2D.PushTint(ref highlightColor);
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
Render2D.PopTint();
if (point != null)
{
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
Render2D.PushTint(ref highlightColor);
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
Render2D.PopTint();
}
}
}

View File

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

View File

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

View File

@@ -383,6 +383,40 @@ namespace FlaxEditor.Utilities
File.Delete(file);
}
/// <summary>
/// Creates an Import path ui that show the asset import path and adds a button to show the folder in the file system.
/// </summary>
/// <param name="parentLayout">The parent layout container.</param>
/// <param name="assetItem">The asset item to get the import path of.</param>
public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, Content.BinaryAssetItem assetItem)
{
assetItem.GetImportPath(out var path);
CreateImportPathUI(parentLayout, path);
}
/// <summary>
/// Creates an Import path ui that show the import path and adds a button to show the folder in the file system.
/// </summary>
/// <param name="parentLayout">The parent layout container.</param>
/// <param name="path">The import path.</param>
/// <param name="useInitialSpacing">Whether to use an initial layout space of 5 for separation.</param>
public static void CreateImportPathUI(CustomEditors.LayoutElementsContainer parentLayout, string path, bool useInitialSpacing = true)
{
if (!string.IsNullOrEmpty(path))
{
if (useInitialSpacing)
parentLayout.Space(5);
parentLayout.Label("Import Path:").Label.TooltipText = "Source asset path (can be relative or absolute to the project)";
var textBox = parentLayout.TextBox().TextBox;
textBox.TooltipText = "Path is not editable here.";
textBox.IsReadOnly = true;
textBox.Text = path;
parentLayout.Space(2);
var button = parentLayout.Button(Constants.ShowInExplorer).Button;
button.Clicked += () => FileSystem.ShowFileExplorer(Path.GetDirectoryName(path));
}
}
/// <summary>
/// Copies the directory. Supports subdirectories copy with files override option.
/// </summary>

View File

@@ -2,6 +2,7 @@
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;
using FlaxEditor.Content;
@@ -207,8 +208,11 @@ namespace FlaxEditor.Windows.Assets
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = group.Button("Reimport");
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
@@ -100,7 +101,10 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
layout.Space(10);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
@@ -53,7 +54,10 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout);
layout.Space(10);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}

View File

@@ -751,18 +751,18 @@ namespace FlaxEditor.Windows.Assets
if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return;
// Import Settings
{
var group = layout.Group("Import Settings");
var group = layout.Group("Import Settings");
var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
var importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues);
layout.Space(5);
var reimportButton = group.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport();
}
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
layout.Space(5);
var reimportButton = group.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport();
}
}
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Linq;
using System.Xml;
using FlaxEditor.Content;
@@ -110,9 +111,19 @@ namespace FlaxEditor.Windows.Assets
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (PropertiesProxy)Values[0];
if (proxy._window == null)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
base.Initialize(layout);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem);
layout.Space(10);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
@@ -131,11 +132,21 @@ namespace FlaxEditor.Windows.Assets
{
public override void Initialize(LayoutElementsContainer layout)
{
var proxy = (ImportPropertiesProxy)Values[0];
if (proxy._window == null)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
// Import settings
base.Initialize(layout);
// Creates the import path UI
Utilities.Utils.CreateImportPathUI(layout, proxy._window.Item as BinaryAssetItem);
// Reimport
layout.Space(10);
layout.Space(5);
var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((ImportPropertiesProxy)Values[0]).Reimport();
}

View File

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

View File

@@ -35,7 +35,7 @@ void MaterialBase::SetParameterValue(const StringView& name, const Variant& valu
}
else if (warnIfMissing)
{
LOG(Warning, "Missing material parameter '{0}' in material {1}", String(name), ToString());
LOG(Warning, "Missing material parameter '{0}' in material {1}", name, ToString());
}
}

View File

@@ -65,7 +65,7 @@ public:
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The value to set.</param>
/// <param name="warnIfMissing">True if warn if parameter is missing, otherwise will do nothing.</param>
/// <param name="warnIfMissing">True to warn if parameter is missing, otherwise will do nothing.</param>
API_FUNCTION() void SetParameterValue(const StringView& name, const Variant& value, bool warnIfMissing = true);
/// <summary>

View File

@@ -25,6 +25,7 @@ namespace
bool IsWindowsSingleNewLineChar = false;
#endif
int LogTotalErrorsCnt = 0;
int32 LogTotalWriteSize = 0;
FileWriteStream* LogFile = nullptr;
CriticalSection LogLocker;
DateTime LogStartTime;
@@ -149,10 +150,17 @@ void Log::Logger::Write(const StringView& msg)
Platform::Log(msg);
// Write message to log file
if (LogAfterInit)
constexpr int32 LogMaxWriteSize = 1 * 1024 * 1024; // 1GB
if (LogAfterInit && LogTotalWriteSize < LogMaxWriteSize)
{
LogTotalWriteSize += length;
LogFile->WriteBytes(ptr, length * sizeof(Char));
LogFile->WriteBytes(TEXT(PLATFORM_LINE_TERMINATOR), (ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1) * sizeof(Char));
if (LogTotalWriteSize >= LogMaxWriteSize)
{
StringView endMessage(TEXT("Trimming log file.\n\n"));
LogFile->WriteBytes(endMessage.Get(), endMessage.Length() * sizeof(Char));
}
#if LOG_ENABLE_AUTO_FLUSH
LogFile->Flush();
#endif

View File

@@ -3453,6 +3453,16 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
default:
return false;
}
case VariantType::Null:
switch (to.Type)
{
case VariantType::Asset:
case VariantType::ManagedObject:
case VariantType::Object:
return true;
default:
return false;
}
default:
return false;
}
@@ -3963,6 +3973,23 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
return Variant((double)v.AsEnum);
}
break;
case VariantType::Null:
switch (to.Type)
{
case VariantType::Asset:
return Variant((Asset*)nullptr);
case VariantType::Object:
return Variant((ScriptingObject*)nullptr);
case VariantType::ManagedObject:
{
Variant result;
result.SetType(VariantType(VariantType::ManagedObject));
result.MANAGED_GC_HANDLE = 0;
return result;
}
default:
return false;
}
default: ;
}
LOG(Error, "Cannot cast Variant from {0} to {1}", v.Type, to);

View File

@@ -214,9 +214,11 @@ void MaterialParameter::SetValue(const Variant& value)
break;
case VariantType::Object:
_asAsset = Cast<TextureBase>(value.AsObject);
invalidType = _asAsset == nullptr && value.AsObject != nullptr;
break;
case VariantType::Asset:
_asAsset = Cast<TextureBase>(value.AsAsset);
invalidType = _asAsset == nullptr && value.AsAsset != nullptr;
break;
default:
invalidType = true;
@@ -239,6 +241,7 @@ void MaterialParameter::SetValue(const Variant& value)
break;
case VariantType::Object:
_asGPUTexture = Cast<GPUTexture>(value.AsObject);
invalidType = _asGPUTexture == nullptr && value.AsObject != nullptr;
break;
default:
invalidType = true;
@@ -258,9 +261,11 @@ void MaterialParameter::SetValue(const Variant& value)
break;
case VariantType::Object:
_asAsset = Cast<GameplayGlobals>(value.AsObject);
invalidType = _asAsset == nullptr && value.AsObject != nullptr;
break;
case VariantType::Asset:
_asAsset = Cast<GameplayGlobals>(value.AsAsset);
invalidType = _asAsset == nullptr && value.AsAsset != nullptr;
break;
default:
invalidType = true;
@@ -273,7 +278,7 @@ void MaterialParameter::SetValue(const Variant& value)
}
if (invalidType)
{
LOG(Error, "Invalid material parameter value type {0} to set (param type: {1})", value.Type, ScriptingEnum::ToString(_type));
LOG(Error, "Invalid material parameter value '{}' of type '{}' to set (expected type: {})", value.ToString(), value.Type, ScriptingEnum::ToString(_type));
}
}

View File

@@ -293,7 +293,12 @@ void AnimatedModel::SetParameterValue(const StringView& name, const Variant& val
{
if (param.Name == name)
{
param.Value = value;
if (param.Value.Type == value.Type)
param.Value = value;
else if (Variant::CanCast(value, param.Value.Type))
param.Value = Variant::Cast(value, param.Value.Type);
else
LOG(Warning, "Animation Graph parameter '{0}' in AnimatedModel {1} is type '{2}' and not type '{3}'.", name, ToString(), param.Value.Type, value.Type);
return;
}
}

View File

@@ -19,7 +19,7 @@ API_CLASS(Attributes="HideInEditor") class FLAXENGINE_API ModelPrefab : public S
/// <summary>
/// Source model file path (absolute or relative to the project).
/// </summary>
API_FIELD(Attributes="ReadOnly") String ImportPath;
API_FIELD(Attributes="ReadOnly, HideInEditor") String ImportPath;
/// <summary>
/// Model file import settings.

View File

@@ -108,6 +108,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API
API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\")")
TextureQuality TexturesQuality = TextureQuality::ASTC_Medium;
/// <summary>
/// Whether to build Android App Bundle (aab) side by side with apk.
/// </summary>
API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\", \"Build .aab\")")
bool BuildAAB = true;
/// <summary>
/// Custom icon texture to use for the application (overrides the default one).
/// </summary>

View File

@@ -1386,22 +1386,26 @@ void Render2D::DrawText(Font* font, const StringView& text, const TextRange& tex
FORCE_INLINE bool NeedAlphaWithTint(const Color& color)
{
return (color.A * TintLayersStack.Peek().A) < 1.0f;
const float tint = TintLayersStack.Peek().A;
return color.A * tint < 1.0f;
}
FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2)
{
return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f;
const float tint = TintLayersStack.Peek().A;
return color1.A * tint < 1.0f || color2.A * tint < 1.0f;
}
FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2, const Color& color3)
{
return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f || (color3.A * TintLayersStack.Peek().A) < 1.0f;
const float tint = TintLayersStack.Peek().A;
return color1.A * tint < 1.0f || color2.A * tint < 1.0f || color3.A * tint < 1.0f;
}
FORCE_INLINE bool NeedAlphaWithTint(const Color& color1, const Color& color2, const Color& color3, const Color& color4)
{
return (color1.A * TintLayersStack.Peek().A) < 1.0f || (color2.A * TintLayersStack.Peek().A) < 1.0f || (color3.A * TintLayersStack.Peek().A) < 1.0f || (color4.A * TintLayersStack.Peek().A) < 1.0f;
const float tint = TintLayersStack.Peek().A;
return color1.A * tint < 1.0f || color2.A * tint < 1.0f || color3.A * tint < 1.0f || color4.A * tint < 1.0f;
}
void Render2D::FillRectangle(const Rectangle& rect, const Color& color)

View File

@@ -623,10 +623,6 @@ void ShadowsPass::SetupRenderContext(RenderContext& renderContext, RenderContext
void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderLightData& light, ShadowAtlasLight& atlasLight)
{
// Initialize frame-data
atlasLight.ContextIndex = 0;
atlasLight.ContextCount = 0;
// Copy light properties
atlasLight.Sharpness = light.ShadowsSharpness;
atlasLight.Fade = light.ShadowsStrength;
@@ -1299,6 +1295,11 @@ RETRY_ATLAS_SETUP:
for (RenderLightData* light : shadowedLights)
{
auto& atlasLight = shadows.Lights[light->ID];
// Reset frame-data
atlasLight.ContextIndex = 0;
atlasLight.ContextCount = 0;
if (atlasLight.Tiles[0].RectTile && atlasLight.Tiles[0].RectTile->Width == atlasLight.Resolution)
{
// Invalidate cache when whole atlas will be cleared

View File

@@ -66,10 +66,6 @@ void ShaderProcessing::Parser::init()
bool ShaderProcessing::Parser::process()
{
const Token defineToken("#define");
const Separator singleLineCommentSeparator('/', '/');
const Separator multiLineCommentSeparator('/', '*');
// TODO: split parsing into two phrases: comments preprocessing and parsing
// Read whole source code
Token token;
@@ -77,36 +73,8 @@ bool ShaderProcessing::Parser::process()
{
text.ReadToken(&token);
// Single line comment
if (token.Separator == singleLineCommentSeparator)
{
// Read whole line
text.ReadLine();
}
// Multi line comment
else if (token.Separator == multiLineCommentSeparator)
{
// Read tokens until end sequence
char prev = ' ';
char c;
while (text.CanRead())
{
c = text.ReadChar();
if (prev == '*' && c == '/')
{
break;
}
prev = c;
}
// Check if comment is valid (has end before file end)
if (!text.CanRead())
{
OnWarning(TEXT("Missing multiline comment ending"));
}
}
// Preprocessor definition
else if (token == defineToken)
// Preprocessor definition
if (token == defineToken)
{
// Skip
text.ReadLine();

View File

@@ -717,6 +717,8 @@ bool ModelTool::ImportDataAssimp(const String& path, ModelData& data, Options& o
flags |= aiProcess_FixInfacingNormals | aiProcess_GenSmoothNormals;
if (options.CalculateTangents)
flags |= aiProcess_CalcTangentSpace;
if (options.ReverseWindingOrder)
flags &= ~aiProcess_FlipWindingOrder;
if (options.OptimizeMeshes)
flags |= aiProcess_OptimizeMeshes | aiProcess_SplitLargeMeshes | aiProcess_ImproveCacheLocality;
if (options.MergeMeshes)

View File

@@ -385,6 +385,19 @@ bool ProcessMesh(ImporterData& data, FbxMesh* fbxMesh, MeshData& mesh, String& e
mesh.Indices[i] = fbxIndices[i];
}
if (data.Options.ReverseWindingOrder)
{
for (int32 i = 0; i < vertexCount; i += 3)
{
Swap(meshIndices[i + 1], meshIndices[i + 2]);
Swap(meshPositions[i + 1], meshPositions[i + 2]);
if (meshNormals)
Swap(meshNormals[i + 1], meshNormals[i + 2]);
if (meshTangents)
Swap(meshTangents[i + 1], meshTangents[i + 2]);
}
}
// Texture coordinates
for (int32 channelIndex = 0; channelIndex < MODEL_MAX_UV && fbxMesh->GetElementUV(channelIndex); channelIndex++)
{

View File

@@ -777,6 +777,24 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
}
}
// Reverse winding order
if (data.Options.ReverseWindingOrder)
{
uint32* meshIndices = mesh.Indices.Get();
Float3* meshPositions = mesh.Positions.Get();
Float3* meshNormals = mesh.Normals.HasItems() ? mesh.Normals.Get() : nullptr;
Float3* meshTangents = mesh.Tangents.HasItems() ? mesh.Tangents.Get() : nullptr;
for (int i = 0; i < vertexCount; i += 3) {
Swap(meshIndices[i + 1], meshIndices[i + 2]);
Swap(meshPositions[i + 1], meshPositions[i + 2]);
if (meshNormals)
Swap(meshNormals[i + 1], meshNormals[i + 2]);
if (meshTangents)
Swap(meshTangents[i + 1], meshTangents[i + 2]);
}
}
// Lightmap UVs
mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource);

View File

@@ -667,6 +667,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
SERIALIZE(FlipNormals);
SERIALIZE(CalculateTangents);
SERIALIZE(SmoothingTangentsAngle);
SERIALIZE(ReverseWindingOrder);
SERIALIZE(OptimizeMeshes);
SERIALIZE(MergeMeshes);
SERIALIZE(ImportLODs);
@@ -717,6 +718,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
DESERIALIZE(FlipNormals);
DESERIALIZE(CalculateTangents);
DESERIALIZE(SmoothingTangentsAngle);
DESERIALIZE(ReverseWindingOrder);
DESERIALIZE(OptimizeMeshes);
DESERIALIZE(MergeMeshes);
DESERIALIZE(ImportLODs);

View File

@@ -170,6 +170,9 @@ public:
// Specifies the maximum angle (in degrees) that may be between two vertex tangents before their tangents and bi-tangents are smoothed. The default value is 45.
API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingTangentsAngle)), Limit(0, 45, 0.1f)")
float SmoothingTangentsAngle = 45.0f;
// If checked, the winding order of the vertices will be reversed.
API_FIELD(Attributes="EditorOrder(47), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
bool ReverseWindingOrder = false;
// Enable/disable meshes geometry optimization.
API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
bool OptimizeMeshes = true;

View File

@@ -730,6 +730,9 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
if (!options.FlipY &&
!options.FlipX &&
!options.InvertGreenChannel &&
!options.InvertRedChannel &&
!options.InvertAlphaChannel &&
!options.InvertBlueChannel &&
!options.ReconstructZChannel &&
options.Compress &&
type == ImageType::DDS &&
@@ -824,16 +827,12 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y)
{
static const DirectX::XMVECTORU32 s_selecty = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0 } } };
const DirectX::XMVECTORU32 s_selecty = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0 } } };
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j)
{
const DirectX::XMVECTOR value = inPixels[j];
const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value);
outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selecty);
}
}, timage);
@@ -844,6 +843,78 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
}
SET_CURRENT_IMG(timage);
}
// Check if invert red channel
if (!keepAsIs && options.InvertRedChannel)
{
auto& timage = GET_TMP_IMG();
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y)
{
const DirectX::XMVECTORU32 s_selectx = { { { DirectX::XM_SELECT_1, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0 } } };
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j)
{
const DirectX::XMVECTOR value = inPixels[j];
const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value);
outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selectx);
}
}, timage);
if (FAILED(result))
{
errorMsg = String::Format(TEXT("Cannot invert red channel in texture, error: {0:x}"), static_cast<uint32>(result));
return true;
}
SET_CURRENT_IMG(timage);
}
// Check if invert blue channel
if (!keepAsIs && options.InvertBlueChannel)
{
auto& timage = GET_TMP_IMG();
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y)
{
const DirectX::XMVECTORU32 s_selectz = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0 } } };
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j)
{
const DirectX::XMVECTOR value = inPixels[j];
const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value);
outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selectz);
}
}, timage);
if (FAILED(result))
{
errorMsg = String::Format(TEXT("Cannot invert blue channel in texture, error: {0:x}"), static_cast<uint32>(result));
return true;
}
SET_CURRENT_IMG(timage);
}
// Check if invert alpha channel
if (!keepAsIs && options.InvertAlphaChannel)
{
auto& timage = GET_TMP_IMG();
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y)
{
const DirectX::XMVECTORU32 s_selectw = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1 } } };
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j)
{
const DirectX::XMVECTOR value = inPixels[j];
const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value);
outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selectw);
}
}, timage);
if (FAILED(result))
{
errorMsg = String::Format(TEXT("Cannot invert alpha channel in texture, error: {0:x}"), static_cast<uint32>(result));
return true;
}
SET_CURRENT_IMG(timage);
}
// Reconstruct Z Channel
if (!keepAsIs & options.ReconstructZChannel)
@@ -853,10 +924,8 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y)
{
static const DirectX::XMVECTORU32 s_selectz = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0 } } };
const DirectX::XMVECTORU32 s_selectz = { { { DirectX::XM_SELECT_0, DirectX::XM_SELECT_0, DirectX::XM_SELECT_1, DirectX::XM_SELECT_0 } } };
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j)
{
const DirectX::XMVECTOR value = inPixels[j];

View File

@@ -25,7 +25,7 @@ namespace
String TextureTool::Options::ToString() const
{
return String::Format(TEXT("Type: {}, IsAtlas: {}, NeverStream: {}, IndependentChannels: {}, sRGB: {}, GenerateMipMaps: {}, FlipY: {}, InvertGreen: {} Scale: {}, MaxSize: {}, Resize: {}, PreserveAlphaCoverage: {}, PreserveAlphaCoverageReference: {}, SizeX: {}, SizeY: {}"),
return String::Format(TEXT("Type: {}, IsAtlas: {}, NeverStream: {}, IndependentChannels: {}, sRGB: {}, GenerateMipMaps: {}, FlipY: {}, InvertRed: {}, InvertGreen: {}, InvertBlue {}, Invert Alpha {}, Scale: {}, MaxSize: {}, Resize: {}, PreserveAlphaCoverage: {}, PreserveAlphaCoverageReference: {}, SizeX: {}, SizeY: {}"),
ScriptingEnum::ToString(Type),
IsAtlas,
NeverStream,
@@ -33,7 +33,10 @@ String TextureTool::Options::ToString() const
sRGB,
GenerateMipMaps,
FlipY,
InvertRedChannel,
InvertGreenChannel,
InvertBlueChannel,
InvertAlphaChannel,
Scale,
MaxSize,
MaxSize,
@@ -74,9 +77,18 @@ void TextureTool::Options::Serialize(SerializeStream& stream, const void* otherO
stream.JKEY("FlipX");
stream.Bool(FlipX);
stream.JKEY("InvertRedChannel");
stream.Bool(InvertRedChannel);
stream.JKEY("InvertGreenChannel");
stream.Bool(InvertGreenChannel);
stream.JKEY("InvertBlueChannel");
stream.Bool(InvertBlueChannel);
stream.JKEY("InvertAlphaChannel");
stream.Bool(InvertAlphaChannel);
stream.JKEY("ReconstructZChannel");
stream.Bool(ReconstructZChannel);
@@ -141,7 +153,10 @@ void TextureTool::Options::Deserialize(DeserializeStream& stream, ISerializeModi
GenerateMipMaps = JsonTools::GetBool(stream, "GenerateMipMaps", GenerateMipMaps);
FlipY = JsonTools::GetBool(stream, "FlipY", FlipY);
FlipX = JsonTools::GetBool(stream, "FlipX", FlipX);
InvertRedChannel = JsonTools::GetBool(stream, "InvertRedChannel", InvertRedChannel);
InvertGreenChannel = JsonTools::GetBool(stream, "InvertGreenChannel", InvertGreenChannel);
InvertBlueChannel = JsonTools::GetBool(stream, "InvertBlueChannel", InvertBlueChannel);
InvertAlphaChannel = JsonTools::GetBool(stream, "InvertAlphaChannel", InvertAlphaChannel);
ReconstructZChannel = JsonTools::GetBool(stream, "ReconstructZChannel", ReconstructZChannel);
Resize = JsonTools::GetBool(stream, "Resize", Resize);
KeepAspectRatio = JsonTools::GetBool(stream, "KeepAspectRatio", KeepAspectRatio);

View File

@@ -61,12 +61,24 @@ API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API TextureTool
API_FIELD(Attributes="EditorOrder(71)")
bool FlipX = false;
// True if to invert the green channel on a normal map. Good for OpenGL to DirectX conversion.
API_FIELD(Attributes = "EditorOrder(72)")
// Invert the red channel.
API_FIELD(Attributes = "EditorOrder(72), EditorDisplay(\"Invert Channels\"), ExpandGroups")
bool InvertRedChannel = false;
// Invert the green channel. Good for OpenGL to DirectX conversion.
API_FIELD(Attributes = "EditorOrder(73), EditorDisplay(\"Invert Channels\")")
bool InvertGreenChannel = false;
// Invert the blue channel.
API_FIELD(Attributes = "EditorOrder(74), EditorDisplay(\"Invert Channels\")")
bool InvertBlueChannel = false;
// Invert the alpha channel.
API_FIELD(Attributes = "EditorOrder(75), EditorDisplay(\"Invert Channels\")")
bool InvertAlphaChannel = false;
// Rebuild Z (blue) channel assuming X/Y are normals.
API_FIELD(Attributes = "EditorOrder(73)")
API_FIELD(Attributes = "EditorOrder(76)")
bool ReconstructZChannel = false;
// Texture size scale. Allows increasing or decreasing the imported texture resolution. Default is 1.

View File

@@ -542,10 +542,10 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
// TODO: impl this
LOG(Warning, "Option 'Flip X' is not supported");
}
if (options.InvertGreenChannel)
if (options.InvertRedChannel || options.InvertGreenChannel || options.InvertBlueChannel || options.InvertAlphaChannel)
{
// TODO: impl this
LOG(Warning, "Option 'Invert Green Channel' is not supported");
LOG(Warning, "Option to invert channels is not supported");
}
if (options.ReconstructZChannel)
{

View File

@@ -249,7 +249,7 @@ namespace FlaxEngine.GUI
var text = ConvertedText();
// Check if sth is selected to draw selection
if (HasSelection)
if (HasSelection && IsFocused)
{
var leftEdge = font.GetCharPosition(text, SelectionLeft, ref _layout);
var rightEdge = font.GetCharPosition(text, SelectionRight, ref _layout);
@@ -294,6 +294,8 @@ namespace FlaxEngine.GUI
var color = TextColor;
if (!enabled)
color *= 0.6f;
else if (_isReadOnly)
color *= 0.85f;
Render2D.DrawText(font, text, color, ref _layout, TextMaterial);
}
else if (!string.IsNullOrEmpty(_watermarkText))

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "TextProcessing.h"
#include "Engine/Core/Log.h"
TextProcessing::TextProcessing(const char* input, int32 length)
: _buffer(input)
@@ -54,6 +55,8 @@ void TextProcessing::Setup_HLSL()
32,
};
Whitespaces.Set(whitespaces, ARRAY_COUNT(whitespaces));
SingleLineComment = SeparatorData('/', '/');
MultiLineCommentSeparator = SeparatorData('/', '*');
}
char TextProcessing::ReadChar()
@@ -123,6 +126,38 @@ void TextProcessing::ReadToken(Token* token)
token->Separator = Separators[s];
ReadChar();
// Check for comments
if (token->Separator == SingleLineComment)
{
// Read whole line
ReadLine();
// Read another token
ReadToken(token);
}
else if (token->Separator == MultiLineCommentSeparator)
{
// Read tokens until end sequence
char prev = ' ';
while (CanRead())
{
c = ReadChar();
if (prev == '*' && c == '/')
break;
prev = c;
}
// Check if comment is valid (has end before file end)
if (!CanRead())
{
LOG(Warning, "Missing multiline comment ending");
}
// Read another token
ReadToken(token);
}
return;
}
}

View File

@@ -9,13 +9,13 @@
#include "Engine/Platform/StringUtils.h"
/// <summary>
/// Helper class to fast ANSI text processing (tokenization, reading, streaming etc.)
/// Helper class to fast ANSI text processing (tokenization, reading, streaming etc.).
/// </summary>
class FLAXENGINE_API TextProcessing : public NonCopyable
{
public:
/// <summary>
/// Separator structure
/// Separator structure.
/// </summary>
struct SeparatorData
{
@@ -200,18 +200,21 @@ public:
public:
/// <summary>
/// Array with all token separators
/// Array with all token separators.
/// </summary>
Array<SeparatorData> Separators;
/// /// <summary>
/// Array with all white characters
/// Array with all white characters.
/// </summary>
Array<char> Whitespaces;
SeparatorData SingleLineComment;
SeparatorData MultiLineCommentSeparator;
public:
/// <summary>
/// Set separators and white chars for HLSL language
/// Sets up separators and white chars for HLSL language.
/// </summary>
void Setup_HLSL();
@@ -219,25 +222,22 @@ public:
/// <summary>
/// Returns true if there are still characters in the buffer and can read data from it
/// </summary>
/// <returns>True if can read data, otherwise false</returns>
FORCE_INLINE bool CanRead() const
{
return _position < _length;
}
/// <summary>
/// Peeks single character without moving forward in the buffer
/// Peeks a single character without moving forward in the buffer.
/// </summary>
/// <returns>First character</returns>
FORCE_INLINE char PeekChar() const
{
return *_cursor;
}
/// <summary>
/// Gets current line number
/// Gets the current line number.
/// </summary>
/// <returns>Current line number</returns>
FORCE_INLINE int32 GetLine() const
{
return _line;

View File

@@ -256,6 +256,13 @@ namespace Flax.Build
#endif
if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
args.Add("-nowarn:1591");
if (buildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any())
{
foreach (var warningString in buildOptions.ScriptingAPI.IgnoreSpecificWarnings)
{
args.Add($"-nowarn:{warningString}");
}
}
// Optimizations prevent debugging, only enable in release builds by default
var optimize = optimizeAssembly.HasValue ? optimizeAssembly.Value : buildData.Configuration == TargetConfiguration.Release;

View File

@@ -235,6 +235,11 @@ namespace Flax.Build.NativeCpp
/// </summary>
public HashSet<string> Analyzers;
/// <summary>
/// The specific warnings to ignore.
/// </summary>
public HashSet<string> IgnoreSpecificWarnings;
/// <summary>
/// True if ignore compilation warnings due to missing code documentation comments.
/// </summary>
@@ -265,6 +270,7 @@ namespace Flax.Build.NativeCpp
FileReferences.AddRange(other.FileReferences);
Analyzers.AddRange(other.Analyzers);
IgnoreMissingDocumentationWarnings |= other.IgnoreMissingDocumentationWarnings;
IgnoreSpecificWarnings.AddRange(other.IgnoreSpecificWarnings);
}
}
@@ -338,6 +344,7 @@ namespace Flax.Build.NativeCpp
},
FileReferences = new HashSet<string>(),
Analyzers = new HashSet<string>(),
IgnoreSpecificWarnings = new HashSet<string>(),
};
/// <summary>

View File

@@ -1,5 +1,7 @@
// Copyright (c) 2012-2024 Flax Engine. All rights reserved.
#define USE_STD
using System;
using System.IO;
using System.Text;
@@ -99,6 +101,9 @@ namespace Flax.Deploy
Log.Info("Compressing game debug symbols files...");
var gamePackageZipPath = Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols.zip");
Utilities.FileDelete(gamePackageZipPath);
#if USE_STD
System.IO.Compression.ZipFile.CreateFromDirectory(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols"), gamePackageZipPath, System.IO.Compression.CompressionLevel.Optimal, false);
#else
using (var zip = new Ionic.Zip.ZipFile())
{
zip.AddDirectory(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols"));
@@ -106,6 +111,7 @@ namespace Flax.Deploy
zip.Comment = string.Format("Flax Game {0}.{1}.{2}\nDate: {3}", Deployer.VersionMajor, Deployer.VersionMinor, Deployer.VersionBuild, DateTime.UtcNow);
zip.Save(gamePackageZipPath);
}
#endif
Log.Info("Compressed game debug symbols package size: " + Utilities.GetFileSize(gamePackageZipPath));
}
Utilities.DirectoryDelete(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols"));

View File

@@ -32,16 +32,32 @@ namespace Flax.Build.Platforms
return;
try
{
// Get path
RootPath = Utilities.ReadProcessOutput("xcode-select", "--print-path");
if (string.IsNullOrEmpty(RootPath) || !Directory.Exists(RootPath))
return;
IsValid = true;
Version = new Version(1, 0);
Log.Verbose(string.Format("Found XCode at {0}", RootPath));
// Get version (optional)
var versionText = Utilities.ReadProcessOutput("xcodebuild", "-version");
var versionLines = versionText?.Split('\n') ?? null;
if (versionLines != null && versionLines.Length != 0)
{
versionText = versionLines[0].Trim();
var versionParts = versionText.Split(' ');
if (versionParts != null && versionParts.Length > 1)
{
versionText = versionParts[1].Trim();
if (Version.TryParse(versionText, out var v))
Version = v;
}
}
}
catch
{
}
Log.Verbose(string.Format("Found XCode {1} at {0}", RootPath, Version));
}
}
}

View File

@@ -125,6 +125,13 @@ namespace Flax.Build.Projects.VisualStudio
csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any())
{
foreach (var warningString in configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings)
{
csProjectFileContent.AppendLine($" <NoWarn>{warningString}</NoWarn>");
}
}
csProjectFileContent.AppendLine(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");
csProjectFileContent.AppendLine(" </PropertyGroup>");
@@ -156,6 +163,13 @@ namespace Flax.Build.Projects.VisualStudio
csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any())
{
foreach (var warningString in configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings)
{
csProjectFileContent.AppendLine($" <NoWarn>{warningString}</NoWarn>");
}
}
csProjectFileContent.AppendLine(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");
csProjectFileContent.AppendLine(" </PropertyGroup>");

View File

@@ -284,6 +284,13 @@ namespace Flax.Build.Projects.VisualStudio
csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings.Any())
{
foreach (var warningString in configuration.TargetBuildOptions.ScriptingAPI.IgnoreSpecificWarnings)
{
csProjectFileContent.AppendLine($" <NoWarn>{warningString}</NoWarn>");
}
}
csProjectFileContent.AppendLine(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");