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: inputs:
vulkan-version: vulkan-version:
description: 'Vulkan SDK release version (e.g. 1.2.198.1).' description: 'Vulkan SDK release version (e.g. 1.2.198.1).'
default: '1.2.198.1' default: '1.3.290.0'
required: false required: false
runs: runs:
using: "composite" using: "composite"

View File

@@ -35,12 +35,12 @@ jobs:
run: | run: |
.\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Windows-Editor name: Windows-Editor
path: Output/Editor.zip path: Output/Editor.zip
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Windows-EditorDebugSymbols name: Windows-EditorDebugSymbols
path: Output/EditorDebugSymbols.zip path: Output/EditorDebugSymbols.zip
@@ -68,7 +68,7 @@ jobs:
run: | run: |
.\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Windows-Game name: Windows-Game
path: Output/Windows.zip path: Output/Windows.zip
@@ -101,7 +101,7 @@ jobs:
run: | run: |
./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output ./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Linux-Editor name: Linux-Editor
path: Output/FlaxEditorLinux.zip path: Output/FlaxEditorLinux.zip
@@ -132,7 +132,7 @@ jobs:
run: | run: |
./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output ./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Linux-Game name: Linux-Game
path: Output/Linux.zip path: Output/Linux.zip
@@ -162,7 +162,7 @@ jobs:
run: | run: |
./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output ./PackageEditor.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Mac-Editor name: Mac-Editor
path: Output/FlaxEditorMac.zip path: Output/FlaxEditorMac.zip
@@ -190,7 +190,7 @@ jobs:
run: | run: |
./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output ./PackagePlatforms.command -arch=ARM64 -platform=Mac -deployOutput=Output
- name: Upload - name: Upload
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: Mac-Game name: Mac-Game
path: Output/Mac.zip 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. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System; using System;
using System.IO;
using FlaxEditor.Scripting; using FlaxEditor.Scripting;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.Utilities; using FlaxEngine.Utilities;

View File

@@ -73,7 +73,7 @@ namespace FlaxEditor.Content
} }
/// <summary> /// <summary>
/// Context proxy object for C# script files. /// Context proxy object for C# Script files.
/// </summary> /// </summary>
/// <seealso cref="FlaxEditor.Content.CSharpProxy" /> /// <seealso cref="FlaxEditor.Content.CSharpProxy" />
[ContentContextMenu("New/C#/C# Script")] [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> /// <summary>
/// Context proxy object for empty C# files. /// Context proxy object for empty C# files.
/// </summary> /// </summary>
@@ -105,4 +139,55 @@ namespace FlaxEditor.Content
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/CSharpEmptyTemplate.cs"); 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"); 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> /// <summary>
/// Context proxy object for C++ Json Asset files. /// Context proxy object for C++ Json Asset files.

View File

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

View File

@@ -364,6 +364,33 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
} }
#endif #endif
const bool distributionPackage = buildSettings->ForDistribution || data.Configuration == BuildConfiguration::Release; 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; CreateProcessSettings procSettings;
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug")); 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); const int32 result = Platform::CreateProcess(procSettings);
if (result != 0) 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; return true;
} }
} }
// Copy result package // 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 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"); const String outputApk = data.OriginalOutputPath / EditorUtilities::GetOutputName() + TEXT(".apk");
if (FileSystem::CopyFile(outputApk, 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; 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; return false;
} }

View File

@@ -214,6 +214,33 @@ bool DeployDataStep::Perform(CookingData& data)
FileSystem::NormalizePath(srcDotnet); FileSystem::NormalizePath(srcDotnet);
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), 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 // Deploy runtime files
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll"); const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms")); const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms"));
@@ -226,14 +253,14 @@ bool DeployDataStep::Perform(CookingData& data)
{ {
// AOT runtime files inside Engine Platform folder // AOT runtime files inside Engine Platform folder
packFolder /= TEXT("Dotnet"); packFolder /= TEXT("Dotnet");
dstDotnetLibs /= TEXT("lib/net8.0"); dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs = packFolder / TEXT("lib/net8.0"); srcDotnetLibs = packFolder / String::Format(TEXT("lib/net{}.0"), version);
} }
else else
{ {
// Runtime files inside Dotnet SDK folder but placed for AOT // Runtime files inside Dotnet SDK folder but placed for AOT
dstDotnetLibs /= TEXT("lib/net8.0"); dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs /= TEXT("../lib/net8.0"); srcDotnetLibs /= String::Format(TEXT("../lib/net{}.0"), version);
} }
} }
else else
@@ -241,14 +268,14 @@ bool DeployDataStep::Perform(CookingData& data)
if (srcDotnetFromEngine) if (srcDotnetFromEngine)
{ {
// Runtime files inside Engine Platform folder // Runtime files inside Engine Platform folder
dstDotnetLibs /= TEXT("lib/net8.0"); dstDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
srcDotnetLibs /= TEXT("lib/net8.0"); srcDotnetLibs /= String::Format(TEXT("lib/net{}.0"), version);
} }
else else
{ {
// Runtime files inside Dotnet SDK folder // Runtime files inside Dotnet SDK folder
dstDotnetLibs /= TEXT("shared/Microsoft.NETCore.App"); 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); 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("libmonosgen-2.0.so");
DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so"); DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Native.so"); DEPLOY_NATIVE_FILE("libSystem.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Globalization.Native.so");
DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so"); DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Android.so");
break; break;
case BuildPlatform::iOSARM64: case BuildPlatform::iOSARM64:

View File

@@ -301,7 +301,7 @@ namespace FlaxEditor.CustomEditors
_valueToSet = null; _valueToSet = null;
// Assign value // 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++) for (int i = 0; i < _values.Count; i++)
_values[i] = l[i]; _values[i] = l[i];

View File

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

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System; using System;
using System.Linq;
using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Surface; using FlaxEditor.Surface;
using FlaxEngine; using FlaxEngine;
@@ -35,6 +36,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
(instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier), (instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier),
(instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value), (instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value),
Values); Values);
if (!parameters.Any())
group.Label("No parameters", TextAlignment.Center);
_parametersAdded = true; _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."); var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
_reimportButton = button.Button; _reimportButton = button.Button;
_reimportButton.Clicked += OnReimport; _reimportButton.Clicked += OnReimport;

View File

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

View File

@@ -424,6 +424,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
{ {
presenter.Undo.AddAction(multiAction); presenter.Undo.AddAction(multiAction);
presenter.Control.Focus(); 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 // Refresh anchors
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes); if (Values != null && Values[0] != null)
if (xEq != _cachedXEq || yEq != _cachedYEq)
{ {
RebuildLayout(); GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
if (xEq != _cachedXEq || yEq != _cachedYEq)
{
RebuildLayout();
}
} }
base.Refresh(); base.Refresh();

View File

@@ -292,5 +292,17 @@ namespace FlaxEditor.CustomEditors.Editors
_isRefreshing = false; _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(); base.Draw();
} }
/// <inheritdoc />
public override void OnDestroy() public override void OnDestroy()
{ {
_pickerValidator.OnDestroy(); _pickerValidator.OnDestroy();
_pickerValidator = null;
base.OnDestroy();
} }
private bool ValidateActors(ActorNode node) private bool ValidateActors(ActorNode node)

View File

@@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI; using FlaxEditor.GUI;
using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Drag;
using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph;
using FlaxEditor.SceneGraph.GUI;
using FlaxEditor.Scripting; using FlaxEditor.Scripting;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
@@ -23,6 +24,7 @@ namespace FlaxEditor.CustomEditors.Editors
public class FlaxObjectRefPickerControl : Control public class FlaxObjectRefPickerControl : Control
{ {
private ScriptType _type; private ScriptType _type;
private ActorTreeNode _linkedTreeNode;
private Object _value; private Object _value;
private string _valueName; private string _valueName;
private bool _supportsPickDropDown; private bool _supportsPickDropDown;
@@ -300,7 +302,43 @@ namespace FlaxEditor.CustomEditors.Editors
// Picker dropdown menu // Picker dropdown menu
if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location)) if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location))
{
ShowDropDownMenu(); 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); return base.OnMouseUp(location, button);
} }
@@ -326,6 +364,12 @@ namespace FlaxEditor.CustomEditors.Editors
// Check if has object selected // Check if has object selected
if (_value != null) if (_value != null)
{ {
if (_linkedTreeNode != null)
{
_linkedTreeNode.StopHighlight();
_linkedTreeNode = null;
}
// Select object // Select object
if (_value is Actor actor) if (_value is Actor actor)
Select(actor); Select(actor);
@@ -491,6 +535,7 @@ namespace FlaxEditor.CustomEditors.Editors
_value = null; _value = null;
_type = ScriptType.Null; _type = ScriptType.Null;
_valueName = null; _valueName = null;
_linkedTreeNode = null;
base.OnDestroy(); base.OnDestroy();
} }

View File

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

View File

@@ -386,7 +386,7 @@ namespace FlaxEditor.CustomEditors
if (instanceValues.Count != Count) if (instanceValues.Count != Count)
throw new ArgumentException(); 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++) for (int i = 0; i < Count; i++)
{ {

View File

@@ -550,7 +550,7 @@ int32 Editor::LoadProduct()
} }
if (!FileSystem::FileExists(files[0])) 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; return -1;
} }
projectPath = StringUtils::GetDirectoryName(files[0]); projectPath = StringUtils::GetDirectoryName(files[0]);

View File

@@ -59,7 +59,7 @@ namespace FlaxEditor.GUI
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AssetPicker"/> class. /// Initializes a new instance of the <see cref="AssetPicker"/> class.
/// </summary> /// </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> /// <param name="location">The control location.</param>
public AssetPicker(ScriptType assetType, Float2 location) public AssetPicker(ScriptType assetType, Float2 location)
: base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize)) : base(location, new Float2(DefaultIconSize + ButtonsOffset + ButtonsSize, DefaultIconSize))

View File

@@ -23,10 +23,19 @@ namespace FlaxEditor.GUI.Tree
/// </summary> /// </summary>
public const float DefaultNodeOffsetY = 0; public const float DefaultNodeOffsetY = 0;
private const float _targetHighlightScale = 1.25f;
private const float _highlightScaleAnimDuration = 0.85f;
private Tree _tree; private Tree _tree;
private bool _opened, _canChangeOrder; private bool _opened, _canChangeOrder;
private float _animationProgress, _cachedHeight; 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 bool _mouseOverArrow, _mouseOverHeader;
private float _xOffset, _textWidth; private float _xOffset, _textWidth;
private float _headerHeight = 16.0f; 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 /> /// <inheritdoc />
public override void Update(float deltaTime) 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 // Drop/down animation
if (_animationProgress < 1.0f) if (_animationProgress < 1.0f)
{ {
@@ -676,6 +723,18 @@ namespace FlaxEditor.GUI.Tree
textRect.Width -= 18.0f; 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 // Draw text
Color textColor = CacheTextColor(); Color textColor = CacheTextColor();
Render2D.DrawText(TextFont.GetFont(), _text, textRect, textColor, TextAlignment.Near, TextAlignment.Center); 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 // Base
if (_opened) if (_opened)
{ {

View File

@@ -1,6 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using FlaxEditor.Options;
using FlaxEditor.SceneGraph;
using FlaxEngine; using FlaxEngine;
using System;
namespace FlaxEditor.Gizmo namespace FlaxEditor.Gizmo
{ {
@@ -18,8 +21,13 @@ namespace FlaxEditor.Gizmo
private MaterialInstance _materialAxisY; private MaterialInstance _materialAxisY;
private MaterialInstance _materialAxisZ; private MaterialInstance _materialAxisZ;
private MaterialInstance _materialAxisFocus; private MaterialInstance _materialAxisFocus;
private MaterialInstance _materialAxisLocked;
private MaterialBase _materialSphere; private MaterialBase _materialSphere;
// Material Parameter Names
const String _brightnessParamName = "Brightness";
const String _opacityParamName = "Opacity";
private void InitDrawing() private void InitDrawing()
{ {
// Axis Models // Axis Models
@@ -34,6 +42,7 @@ namespace FlaxEditor.Gizmo
_materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY"); _materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY");
_materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ"); _materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ");
_materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus"); _materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus");
_materialAxisLocked = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisLocked");
_materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere"); _materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere");
// Ensure that every asset was loaded // Ensure that every asset was loaded
@@ -50,6 +59,25 @@ namespace FlaxEditor.Gizmo
{ {
Platform.Fatal("Failed to load transform gizmo resources."); 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 /> /// <inheritdoc />
@@ -60,6 +88,21 @@ namespace FlaxEditor.Gizmo
if (!_modelCube || !_modelCube.IsLoaded) if (!_modelCube || !_modelCube.IsLoaded)
return; 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 // 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 // https://github.com/FlaxEngine/FlaxEngine/issues/680
@@ -92,32 +135,38 @@ namespace FlaxEditor.Gizmo
// X axis // X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // Z axis
Matrix.RotationX(Mathf.Pi, out m2); Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f)); 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); 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 // ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale)); 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); 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 // YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale)); 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); 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 // Center sphere
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
@@ -136,15 +185,18 @@ namespace FlaxEditor.Gizmo
// X axis // X axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2); Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // 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 // Z axis
Matrix.RotationX(-Mathf.PiOverTwo, out m2); Matrix.RotationX(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
@@ -163,32 +215,38 @@ namespace FlaxEditor.Gizmo
// X axis // X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2); Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref mx1, out m3); 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 // Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2); Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // Z axis
Matrix.RotationX(Mathf.Pi, out m2); Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3); 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 // XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f)); 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); 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 // ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale)); 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); 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 // YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale)); 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); 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 // Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2); Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);

View File

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

View File

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

View File

@@ -501,7 +501,7 @@ namespace FlaxEditor.Gizmo
_scaleDelta = Vector3.Zero; _scaleDelta = Vector3.Zero;
if (ActiveAxis == Axis.Center) if (ActiveAxis == Axis.Center)
scaleDelta = new Vector3(scaleDelta.AvgValue); scaleDelta = new Vector3(scaleDelta.ValuesSum);
} }
// Apply transformation (but to the parents, not whole selection pool) // 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. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FlaxEditor.Gizmo; 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) 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 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)) if (rect.Contains(ref mousePos))
{ {
Render2D.FillRectangle(rect, style.Foreground); Render2D.FillRectangle(rect, style.Foreground);

View File

@@ -1136,9 +1136,15 @@ namespace FlaxEditor.Modules
Proxy.Add(new SceneAnimationProxy()); Proxy.Add(new SceneAnimationProxy());
Proxy.Add(new CSharpScriptProxy()); Proxy.Add(new CSharpScriptProxy());
Proxy.Add(new CSharpEmptyProxy()); 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 CppAssetProxy());
Proxy.Add(new CppStaticClassProxy()); Proxy.Add(new CppStaticClassProxy());
Proxy.Add(new CppScriptProxy()); Proxy.Add(new CppScriptProxy());
Proxy.Add(new CppActorProxy());
Proxy.Add(new SceneProxy()); Proxy.Add(new SceneProxy());
Proxy.Add(new PrefabProxy()); Proxy.Add(new PrefabProxy());
Proxy.Add(new IESProfileProxy()); Proxy.Add(new IESProfileProxy());

View File

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

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Text;
using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors; using FlaxEditor.CustomEditors.Editors;
using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Json; using FlaxEngine.Json;
@@ -31,11 +33,33 @@ namespace FlaxEditor.Options
private void OnResetButtonClicked() private void OnResetButtonClicked()
{ {
var obj = new T(); var editorClassName = typeof(T).Name;
var str = JsonSerializer.Serialize(obj);
JsonSerializer.Deserialize(Values[0], str); var editorName = new StringBuilder();
SetValue(Values[0]); editorName.Append(editorClassName[0]);
Refresh(); 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; 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> /// <summary>
/// Gets or sets the timestamps prefix mode for output log messages. /// Gets or sets the timestamps prefix mode for output log messages.
/// </summary> /// </summary>

View File

@@ -22,37 +22,51 @@ namespace FlaxEditor.Options
/// Gets or sets the first outline color. /// Gets or sets the first outline color.
/// </summary> /// </summary>
[DefaultValue(typeof(Color), "0.039,0.827,0.156")] [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); public Color SelectionOutlineColor0 { get; set; } = new Color(0.039f, 0.827f, 0.156f);
/// <summary> /// <summary>
/// Gets or sets the second outline color. /// Gets or sets the second outline color.
/// </summary> /// </summary>
[DefaultValue(typeof(Color), "0.019,0.615,0.101")] [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); public Color SelectionOutlineColor1 { get; set; } = new Color(0.019f, 0.615f, 0.101f);
/// <summary> /// <summary>
/// Gets or sets the selection outline size for UI controls. /// Gets or sets the selection outline size for UI controls.
/// </summary> /// </summary>
[DefaultValue(2.0f)] [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; public float UISelectionOutlineSize { get; set; } = 2.0f;
/// <summary> /// <summary>
/// Gets or sets the transform gizmo size. /// Gets or sets the transform gizmo size.
/// </summary> /// </summary>
[DefaultValue(1.0f), Limit(0.01f, 100.0f, 0.01f)] [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; public float GizmoSize { get; set; } = 1.0f;
/// <summary> /// <summary>
/// Gets or sets the color used to highlight selected meshes and CSG surfaces. /// Gets or sets the color used to highlight selected meshes and CSG surfaces.
/// </summary> /// </summary>
[DefaultValue(typeof(Color), "0.0,0.533,1.0,1.0")] [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); 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> /// <summary>
/// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance. /// Gets or sets a value indicating whether enable MSAA for DebugDraw primitives rendering. Helps with pixel aliasing but reduces performance.
/// </summary> /// </summary>

View File

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

View File

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

View File

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

View File

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

View File

@@ -383,6 +383,40 @@ namespace FlaxEditor.Utilities
File.Delete(file); 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> /// <summary>
/// Copies the directory. Supports subdirectories copy with files override option. /// Copies the directory. Supports subdirectories copy with files override option.
/// </summary> /// </summary>

View File

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

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml; using System.Xml;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.Content.Import; using FlaxEditor.Content.Import;
@@ -100,7 +101,10 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout); 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"); var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
} }

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Xml; using System.Xml;
using FlaxEditor.Content; using FlaxEditor.Content;
using FlaxEditor.Content.Import; using FlaxEditor.Content.Import;
@@ -53,7 +54,10 @@ namespace FlaxEditor.Windows.Assets
base.Initialize(layout); 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"); var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).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)) if (Utilities.Utils.OnAssetProperties(layout, proxy.Asset))
return; 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 importSettingsField = typeof(ImportPropertiesProxyBase).GetField(nameof(ImportSettings), BindingFlags.NonPublic | BindingFlags.Instance);
var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings }; var importSettingsValues = new ValueContainer(new ScriptMemberInfo(importSettingsField)) { proxy.ImportSettings };
group.Object(importSettingsValues); group.Object(importSettingsValues);
layout.Space(5); // Creates the import path UI
var reimportButton = group.Button("Reimport"); Utilities.Utils.CreateImportPathUI(layout, proxy.Window.Item as BinaryAssetItem);
reimportButton.Button.Clicked += () => ((ImportPropertiesProxyBase)Values[0]).Reimport();
} 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. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.IO;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
using FlaxEditor.Content; using FlaxEditor.Content;
@@ -110,9 +111,19 @@ namespace FlaxEditor.Windows.Assets
{ {
public override void Initialize(LayoutElementsContainer layout) public override void Initialize(LayoutElementsContainer layout)
{ {
var proxy = (PropertiesProxy)Values[0];
if (proxy._window == null)
{
layout.Label("Loading...", TextAlignment.Center);
return;
}
base.Initialize(layout); 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"); var reimportButton = layout.Button("Reimport");
reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport();
} }

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ void MaterialBase::SetParameterValue(const StringView& name, const Variant& valu
} }
else if (warnIfMissing) 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> /// </summary>
/// <param name="name">The parameter name.</param> /// <param name="name">The parameter name.</param>
/// <param name="value">The value to set.</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); API_FUNCTION() void SetParameterValue(const StringView& name, const Variant& value, bool warnIfMissing = true);
/// <summary> /// <summary>

View File

@@ -25,6 +25,7 @@ namespace
bool IsWindowsSingleNewLineChar = false; bool IsWindowsSingleNewLineChar = false;
#endif #endif
int LogTotalErrorsCnt = 0; int LogTotalErrorsCnt = 0;
int32 LogTotalWriteSize = 0;
FileWriteStream* LogFile = nullptr; FileWriteStream* LogFile = nullptr;
CriticalSection LogLocker; CriticalSection LogLocker;
DateTime LogStartTime; DateTime LogStartTime;
@@ -149,10 +150,17 @@ void Log::Logger::Write(const StringView& msg)
Platform::Log(msg); Platform::Log(msg);
// Write message to log file // 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(ptr, length * sizeof(Char));
LogFile->WriteBytes(TEXT(PLATFORM_LINE_TERMINATOR), (ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1) * 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 #if LOG_ENABLE_AUTO_FLUSH
LogFile->Flush(); LogFile->Flush();
#endif #endif

View File

@@ -3453,6 +3453,16 @@ bool Variant::CanCast(const Variant& v, const VariantType& to)
default: default:
return false; return false;
} }
case VariantType::Null:
switch (to.Type)
{
case VariantType::Asset:
case VariantType::ManagedObject:
case VariantType::Object:
return true;
default:
return false;
}
default: default:
return false; return false;
} }
@@ -3963,6 +3973,23 @@ Variant Variant::Cast(const Variant& v, const VariantType& to)
return Variant((double)v.AsEnum); return Variant((double)v.AsEnum);
} }
break; 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: ; default: ;
} }
LOG(Error, "Cannot cast Variant from {0} to {1}", v.Type, to); 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; break;
case VariantType::Object: case VariantType::Object:
_asAsset = Cast<TextureBase>(value.AsObject); _asAsset = Cast<TextureBase>(value.AsObject);
invalidType = _asAsset == nullptr && value.AsObject != nullptr;
break; break;
case VariantType::Asset: case VariantType::Asset:
_asAsset = Cast<TextureBase>(value.AsAsset); _asAsset = Cast<TextureBase>(value.AsAsset);
invalidType = _asAsset == nullptr && value.AsAsset != nullptr;
break; break;
default: default:
invalidType = true; invalidType = true;
@@ -239,6 +241,7 @@ void MaterialParameter::SetValue(const Variant& value)
break; break;
case VariantType::Object: case VariantType::Object:
_asGPUTexture = Cast<GPUTexture>(value.AsObject); _asGPUTexture = Cast<GPUTexture>(value.AsObject);
invalidType = _asGPUTexture == nullptr && value.AsObject != nullptr;
break; break;
default: default:
invalidType = true; invalidType = true;
@@ -258,9 +261,11 @@ void MaterialParameter::SetValue(const Variant& value)
break; break;
case VariantType::Object: case VariantType::Object:
_asAsset = Cast<GameplayGlobals>(value.AsObject); _asAsset = Cast<GameplayGlobals>(value.AsObject);
invalidType = _asAsset == nullptr && value.AsObject != nullptr;
break; break;
case VariantType::Asset: case VariantType::Asset:
_asAsset = Cast<GameplayGlobals>(value.AsAsset); _asAsset = Cast<GameplayGlobals>(value.AsAsset);
invalidType = _asAsset == nullptr && value.AsAsset != nullptr;
break; break;
default: default:
invalidType = true; invalidType = true;
@@ -273,7 +278,7 @@ void MaterialParameter::SetValue(const Variant& value)
} }
if (invalidType) 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) 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; return;
} }
} }

View File

@@ -19,7 +19,7 @@ API_CLASS(Attributes="HideInEditor") class FLAXENGINE_API ModelPrefab : public S
/// <summary> /// <summary>
/// Source model file path (absolute or relative to the project). /// Source model file path (absolute or relative to the project).
/// </summary> /// </summary>
API_FIELD(Attributes="ReadOnly") String ImportPath; API_FIELD(Attributes="ReadOnly, HideInEditor") String ImportPath;
/// <summary> /// <summary>
/// Model file import settings. /// 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\")") API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\")")
TextureQuality TexturesQuality = TextureQuality::ASTC_Medium; 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> /// <summary>
/// Custom icon texture to use for the application (overrides the default one). /// Custom icon texture to use for the application (overrides the default one).
/// </summary> /// </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) 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) 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) 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) 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) 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) 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 // Copy light properties
atlasLight.Sharpness = light.ShadowsSharpness; atlasLight.Sharpness = light.ShadowsSharpness;
atlasLight.Fade = light.ShadowsStrength; atlasLight.Fade = light.ShadowsStrength;
@@ -1299,6 +1295,11 @@ RETRY_ATLAS_SETUP:
for (RenderLightData* light : shadowedLights) for (RenderLightData* light : shadowedLights)
{ {
auto& atlasLight = shadows.Lights[light->ID]; 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) if (atlasLight.Tiles[0].RectTile && atlasLight.Tiles[0].RectTile->Width == atlasLight.Resolution)
{ {
// Invalidate cache when whole atlas will be cleared // Invalidate cache when whole atlas will be cleared

View File

@@ -66,10 +66,6 @@ void ShaderProcessing::Parser::init()
bool ShaderProcessing::Parser::process() bool ShaderProcessing::Parser::process()
{ {
const Token defineToken("#define"); const Token defineToken("#define");
const Separator singleLineCommentSeparator('/', '/');
const Separator multiLineCommentSeparator('/', '*');
// TODO: split parsing into two phrases: comments preprocessing and parsing
// Read whole source code // Read whole source code
Token token; Token token;
@@ -77,36 +73,8 @@ bool ShaderProcessing::Parser::process()
{ {
text.ReadToken(&token); text.ReadToken(&token);
// Single line comment // Preprocessor definition
if (token.Separator == singleLineCommentSeparator) if (token == defineToken)
{
// 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)
{ {
// Skip // Skip
text.ReadLine(); text.ReadLine();

View File

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

View File

@@ -385,6 +385,19 @@ bool ProcessMesh(ImporterData& data, FbxMesh* fbxMesh, MeshData& mesh, String& e
mesh.Indices[i] = fbxIndices[i]; 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 // Texture coordinates
for (int32 channelIndex = 0; channelIndex < MODEL_MAX_UV && fbxMesh->GetElementUV(channelIndex); channelIndex++) 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 // Lightmap UVs
mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource); mesh.SetLightmapUVsSource(data.Options.LightmapUVsSource);

View File

@@ -667,6 +667,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
SERIALIZE(FlipNormals); SERIALIZE(FlipNormals);
SERIALIZE(CalculateTangents); SERIALIZE(CalculateTangents);
SERIALIZE(SmoothingTangentsAngle); SERIALIZE(SmoothingTangentsAngle);
SERIALIZE(ReverseWindingOrder);
SERIALIZE(OptimizeMeshes); SERIALIZE(OptimizeMeshes);
SERIALIZE(MergeMeshes); SERIALIZE(MergeMeshes);
SERIALIZE(ImportLODs); SERIALIZE(ImportLODs);
@@ -717,6 +718,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
DESERIALIZE(FlipNormals); DESERIALIZE(FlipNormals);
DESERIALIZE(CalculateTangents); DESERIALIZE(CalculateTangents);
DESERIALIZE(SmoothingTangentsAngle); DESERIALIZE(SmoothingTangentsAngle);
DESERIALIZE(ReverseWindingOrder);
DESERIALIZE(OptimizeMeshes); DESERIALIZE(OptimizeMeshes);
DESERIALIZE(MergeMeshes); DESERIALIZE(MergeMeshes);
DESERIALIZE(ImportLODs); 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. // 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)") API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowSmoothingTangentsAngle)), Limit(0, 45, 0.1f)")
float SmoothingTangentsAngle = 45.0f; 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. // Enable/disable meshes geometry optimization.
API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))")
bool OptimizeMeshes = true; bool OptimizeMeshes = true;

View File

@@ -730,6 +730,9 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
if (!options.FlipY && if (!options.FlipY &&
!options.FlipX && !options.FlipX &&
!options.InvertGreenChannel && !options.InvertGreenChannel &&
!options.InvertRedChannel &&
!options.InvertAlphaChannel &&
!options.InvertBlueChannel &&
!options.ReconstructZChannel && !options.ReconstructZChannel &&
options.Compress && options.Compress &&
type == ImageType::DDS && type == ImageType::DDS &&
@@ -824,16 +827,12 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) [&](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); UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j) for (size_t j = 0; j < w; ++j)
{ {
const DirectX::XMVECTOR value = inPixels[j]; const DirectX::XMVECTOR value = inPixels[j];
const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value); const DirectX::XMVECTOR inverty = DirectX::XMVectorSubtract(DirectX::g_XMOne, value);
outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selecty); outPixels[j] = DirectX::XMVectorSelect(value, inverty, s_selecty);
} }
}, timage); }, timage);
@@ -844,6 +843,78 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
} }
SET_CURRENT_IMG(timage); 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 // Reconstruct Z Channel
if (!keepAsIs & options.ReconstructZChannel) if (!keepAsIs & options.ReconstructZChannel)
@@ -853,10 +924,8 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(), result = TransformImage(currentImage->GetImages(), currentImage->GetImageCount(), currentImage->GetMetadata(),
[&](DirectX::XMVECTOR* outPixels, const DirectX::XMVECTOR* inPixels, size_t w, size_t y) [&](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); UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j) for (size_t j = 0; j < w; ++j)
{ {
const DirectX::XMVECTOR value = inPixels[j]; const DirectX::XMVECTOR value = inPixels[j];

View File

@@ -25,7 +25,7 @@ namespace
String TextureTool::Options::ToString() const 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), ScriptingEnum::ToString(Type),
IsAtlas, IsAtlas,
NeverStream, NeverStream,
@@ -33,7 +33,10 @@ String TextureTool::Options::ToString() const
sRGB, sRGB,
GenerateMipMaps, GenerateMipMaps,
FlipY, FlipY,
InvertRedChannel,
InvertGreenChannel, InvertGreenChannel,
InvertBlueChannel,
InvertAlphaChannel,
Scale, Scale,
MaxSize, MaxSize,
MaxSize, MaxSize,
@@ -74,9 +77,18 @@ void TextureTool::Options::Serialize(SerializeStream& stream, const void* otherO
stream.JKEY("FlipX"); stream.JKEY("FlipX");
stream.Bool(FlipX); stream.Bool(FlipX);
stream.JKEY("InvertRedChannel");
stream.Bool(InvertRedChannel);
stream.JKEY("InvertGreenChannel"); stream.JKEY("InvertGreenChannel");
stream.Bool(InvertGreenChannel); stream.Bool(InvertGreenChannel);
stream.JKEY("InvertBlueChannel");
stream.Bool(InvertBlueChannel);
stream.JKEY("InvertAlphaChannel");
stream.Bool(InvertAlphaChannel);
stream.JKEY("ReconstructZChannel"); stream.JKEY("ReconstructZChannel");
stream.Bool(ReconstructZChannel); stream.Bool(ReconstructZChannel);
@@ -141,7 +153,10 @@ void TextureTool::Options::Deserialize(DeserializeStream& stream, ISerializeModi
GenerateMipMaps = JsonTools::GetBool(stream, "GenerateMipMaps", GenerateMipMaps); GenerateMipMaps = JsonTools::GetBool(stream, "GenerateMipMaps", GenerateMipMaps);
FlipY = JsonTools::GetBool(stream, "FlipY", FlipY); FlipY = JsonTools::GetBool(stream, "FlipY", FlipY);
FlipX = JsonTools::GetBool(stream, "FlipX", FlipX); FlipX = JsonTools::GetBool(stream, "FlipX", FlipX);
InvertRedChannel = JsonTools::GetBool(stream, "InvertRedChannel", InvertRedChannel);
InvertGreenChannel = JsonTools::GetBool(stream, "InvertGreenChannel", InvertGreenChannel); InvertGreenChannel = JsonTools::GetBool(stream, "InvertGreenChannel", InvertGreenChannel);
InvertBlueChannel = JsonTools::GetBool(stream, "InvertBlueChannel", InvertBlueChannel);
InvertAlphaChannel = JsonTools::GetBool(stream, "InvertAlphaChannel", InvertAlphaChannel);
ReconstructZChannel = JsonTools::GetBool(stream, "ReconstructZChannel", ReconstructZChannel); ReconstructZChannel = JsonTools::GetBool(stream, "ReconstructZChannel", ReconstructZChannel);
Resize = JsonTools::GetBool(stream, "Resize", Resize); Resize = JsonTools::GetBool(stream, "Resize", Resize);
KeepAspectRatio = JsonTools::GetBool(stream, "KeepAspectRatio", KeepAspectRatio); 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)") API_FIELD(Attributes="EditorOrder(71)")
bool FlipX = false; bool FlipX = false;
// True if to invert the green channel on a normal map. Good for OpenGL to DirectX conversion. // Invert the red channel.
API_FIELD(Attributes = "EditorOrder(72)") 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; 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. // Rebuild Z (blue) channel assuming X/Y are normals.
API_FIELD(Attributes = "EditorOrder(73)") API_FIELD(Attributes = "EditorOrder(76)")
bool ReconstructZChannel = false; bool ReconstructZChannel = false;
// Texture size scale. Allows increasing or decreasing the imported texture resolution. Default is 1. // 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 // TODO: impl this
LOG(Warning, "Option 'Flip X' is not supported"); LOG(Warning, "Option 'Flip X' is not supported");
} }
if (options.InvertGreenChannel) if (options.InvertRedChannel || options.InvertGreenChannel || options.InvertBlueChannel || options.InvertAlphaChannel)
{ {
// TODO: impl this // TODO: impl this
LOG(Warning, "Option 'Invert Green Channel' is not supported"); LOG(Warning, "Option to invert channels is not supported");
} }
if (options.ReconstructZChannel) if (options.ReconstructZChannel)
{ {

View File

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

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "TextProcessing.h" #include "TextProcessing.h"
#include "Engine/Core/Log.h"
TextProcessing::TextProcessing(const char* input, int32 length) TextProcessing::TextProcessing(const char* input, int32 length)
: _buffer(input) : _buffer(input)
@@ -54,6 +55,8 @@ void TextProcessing::Setup_HLSL()
32, 32,
}; };
Whitespaces.Set(whitespaces, ARRAY_COUNT(whitespaces)); Whitespaces.Set(whitespaces, ARRAY_COUNT(whitespaces));
SingleLineComment = SeparatorData('/', '/');
MultiLineCommentSeparator = SeparatorData('/', '*');
} }
char TextProcessing::ReadChar() char TextProcessing::ReadChar()
@@ -123,6 +126,38 @@ void TextProcessing::ReadToken(Token* token)
token->Separator = Separators[s]; token->Separator = Separators[s];
ReadChar(); 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; return;
} }
} }

View File

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

View File

@@ -256,6 +256,13 @@ namespace Flax.Build
#endif #endif
if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) if (buildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
args.Add("-nowarn:1591"); 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 // Optimizations prevent debugging, only enable in release builds by default
var optimize = optimizeAssembly.HasValue ? optimizeAssembly.Value : buildData.Configuration == TargetConfiguration.Release; var optimize = optimizeAssembly.HasValue ? optimizeAssembly.Value : buildData.Configuration == TargetConfiguration.Release;

View File

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

View File

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

View File

@@ -32,16 +32,32 @@ namespace Flax.Build.Platforms
return; return;
try try
{ {
// Get path
RootPath = Utilities.ReadProcessOutput("xcode-select", "--print-path"); RootPath = Utilities.ReadProcessOutput("xcode-select", "--print-path");
if (string.IsNullOrEmpty(RootPath) || !Directory.Exists(RootPath)) if (string.IsNullOrEmpty(RootPath) || !Directory.Exists(RootPath))
return; return;
IsValid = true; IsValid = true;
Version = new Version(1, 0); 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 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>"); csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>"); 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(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>"); csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");
csProjectFileContent.AppendLine(" </PropertyGroup>"); csProjectFileContent.AppendLine(" </PropertyGroup>");
@@ -156,6 +163,13 @@ namespace Flax.Build.Projects.VisualStudio
csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>"); csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>"); 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(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>"); csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");
csProjectFileContent.AppendLine(" </PropertyGroup>"); csProjectFileContent.AppendLine(" </PropertyGroup>");

View File

@@ -284,6 +284,13 @@ namespace Flax.Build.Projects.VisualStudio
csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>"); csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>");
if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings)
csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>"); 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(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>"); csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");