This commit is contained in:
Wojtek Figat
2024-09-30 12:22:40 +02:00
113 changed files with 1375 additions and 1707 deletions

View File

@@ -4,7 +4,7 @@
"Major": 1, "Major": 1,
"Minor": 9, "Minor": 9,
"Revision": 0, "Revision": 0,
"Build": 6603 "Build": 6604
}, },
"Company": "Flax", "Company": "Flax",
"Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.", "Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.",

View File

@@ -229,7 +229,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
} }
// Copy fresh Gradle project template // Copy fresh Gradle project template
if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project"), true)) if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project")))
{ {
LOG(Error, "Failed to deploy Gradle project to {0} from {1}", data.OriginalOutputPath, platformDataPath); LOG(Error, "Failed to deploy Gradle project to {0} from {1}", data.OriginalOutputPath, platformDataPath);
return true; return true;

View File

@@ -123,7 +123,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data)
const auto srcAssetsPath = uwpDataPath / TEXT("Assets"); const auto srcAssetsPath = uwpDataPath / TEXT("Assets");
if (!FileSystem::DirectoryExists(dstAssetsPath)) if (!FileSystem::DirectoryExists(dstAssetsPath))
{ {
if (FileSystem::CopyDirectory(dstAssetsPath, srcAssetsPath, true)) if (FileSystem::CopyDirectory(dstAssetsPath, srcAssetsPath))
{ {
data.Error(TEXT("Failed to copy Assets directory.")); data.Error(TEXT("Failed to copy Assets directory."));
return true; return true;

View File

@@ -203,7 +203,7 @@ bool iOSPlatformTools::OnPostProcess(CookingData& data)
return true; return true;
// Copy fresh XCode project template // Copy fresh XCode project template
if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project"), true)) if (FileSystem::CopyDirectory(data.OriginalOutputPath, platformDataPath / TEXT("Project")))
{ {
LOG(Error, "Failed to deploy XCode project to {0} from {1}", data.OriginalOutputPath, platformDataPath); LOG(Error, "Failed to deploy XCode project to {0} from {1}", data.OriginalOutputPath, platformDataPath);
return true; return true;

View File

@@ -156,12 +156,12 @@ bool DeployDataStep::Perform(CookingData& data)
FileSystem::CopyFile(dstDotnet / TEXT("THIRD-PARTY-NOTICES.TXT"), srcDotnet / TEXT("THIRD-PARTY-NOTICES.TXT")); FileSystem::CopyFile(dstDotnet / TEXT("THIRD-PARTY-NOTICES.TXT"), srcDotnet / TEXT("THIRD-PARTY-NOTICES.TXT"));
if (usAOT) if (usAOT)
{ {
failed |= EditorUtilities::CopyDirectoryIfNewer(dstDotnet, srcDotnet / TEXT("shared/Microsoft.NETCore.App") / version, true); failed |= EditorUtilities::CopyDirectoryIfNewer(dstDotnet, srcDotnet / TEXT("shared/Microsoft.NETCore.App") / version);
} }
else else
{ {
#if 1 #if 1
failed |= EditorUtilities::CopyDirectoryIfNewer(dstDotnet / TEXT("host/fxr") / version, srcDotnet / TEXT("host/fxr") / version, true); failed |= EditorUtilities::CopyDirectoryIfNewer(dstDotnet / TEXT("host/fxr") / version, srcDotnet / TEXT("host/fxr") / version);
#else #else
// TODO: hostfxr for target platform should be copied from nuget package location: microsoft.netcore.app.runtime.<RID>/<VERSION>/runtimes/<RID>/native/hostfxr.dll // TODO: hostfxr for target platform should be copied from nuget package location: microsoft.netcore.app.runtime.<RID>/<VERSION>/runtimes/<RID>/native/hostfxr.dll
String dstHostfxr = dstDotnet / TEXT("host/fxr") / version; String dstHostfxr = dstDotnet / TEXT("host/fxr") / version;
@@ -330,7 +330,7 @@ bool DeployDataStep::Perform(CookingData& data)
data.Error(TEXT("Missing Mono runtime data files.")); data.Error(TEXT("Missing Mono runtime data files."));
return true; return true;
} }
if (FileSystem::CopyDirectory(dstMono, srcMono, true)) if (FileSystem::CopyDirectory(dstMono, srcMono))
{ {
data.Error(TEXT("Failed to copy Mono runtime data files.")); data.Error(TEXT("Failed to copy Mono runtime data files."));
return true; return true;
@@ -416,7 +416,7 @@ bool DeployDataStep::Perform(CookingData& data)
for (auto& e : buildSettings.AdditionalAssetFolders) for (auto& e : buildSettings.AdditionalAssetFolders)
{ {
String path = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, e); String path = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, e);
if (FileSystem::DirectoryGetFiles(files, path, TEXT("*"), DirectorySearchOption::AllDirectories)) if (FileSystem::DirectoryGetFiles(files, path))
{ {
data.Error(TEXT("Failed to find additional assets to deploy.")); data.Error(TEXT("Failed to find additional assets to deploy."));
return true; return true;

View File

@@ -296,6 +296,16 @@ namespace FlaxEditor.CustomEditors
_values.Set(_parent.Values, value); _values.Set(_parent.Values, value);
} }
private bool SyncParent()
{
// TODO: add attribute for types that want to sync their contents with a parent
var type = Values.Type.Type;
if (type == typeof(LocalizedString) ||
type == typeof(FontReference))
return true;
return _parent != null && !(_parent is SyncPointEditor);
}
internal virtual void RefreshInternal() internal virtual void RefreshInternal()
{ {
if (_values == null) if (_values == null)
@@ -317,7 +327,7 @@ namespace FlaxEditor.CustomEditors
// Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object) // Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object)
var obj = _parent; var obj = _parent;
while (obj._parent != null && !(obj._parent is SyncPointEditor)) while (obj.SyncParent())
{ {
obj.Values.Set(obj._parent.Values, obj.Values); obj.Values.Set(obj._parent.Values, obj.Values);
obj = obj._parent; obj = obj._parent;

View File

@@ -93,6 +93,9 @@ namespace FlaxEditor.CustomEditors
catch (Exception ex) catch (Exception ex)
{ {
FlaxEditor.Editor.LogWarning(ex); FlaxEditor.Editor.LogWarning(ex);
// Refresh layout on errors to reduce lgo spam
_presenter.BuildLayout();
} }
base.Update(deltaTime); base.Update(deltaTime);
@@ -397,6 +400,7 @@ namespace FlaxEditor.CustomEditors
Panel.DisposeChildren(); Panel.DisposeChildren();
ClearLayout(); ClearLayout();
_buildOnUpdate = false;
Editor.Setup(this); Editor.Setup(this);
Panel.IsLayoutLocked = false; Panel.IsLayoutLocked = false;
@@ -484,7 +488,6 @@ namespace FlaxEditor.CustomEditors
{ {
if (_buildOnUpdate) if (_buildOnUpdate)
{ {
_buildOnUpdate = false;
BuildLayout(); BuildLayout();
} }

View File

@@ -72,6 +72,8 @@ namespace FlaxEditor.CustomEditors
return new GenericEditor(); return new GenericEditor();
if (targetType.IsArray) if (targetType.IsArray)
{ {
if (targetType.Type == null)
return new ArrayEditor();
if (targetType.Type.GetArrayRank() != 1) if (targetType.Type.GetArrayRank() != 1)
return new GenericEditor(); // Not-supported multidimensional array return new GenericEditor(); // Not-supported multidimensional array

View File

@@ -7,6 +7,7 @@ using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.GUI; using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEditor.Scripting; using FlaxEditor.Scripting;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
@@ -634,26 +635,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
LayoutElementsContainer vEl; LayoutElementsContainer vEl;
Color axisColorX = ActorTransformEditor.AxisColorX; Color axisColorX = ActorTransformEditor.AxisColorX;
Color axisColorY = ActorTransformEditor.AxisColorY; Color axisColorY = ActorTransformEditor.AxisColorY;
FloatValueBox xV, yV, wV, hV;
if (xEq) if (xEq)
{ {
xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX); xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX, out xV);
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX); vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX, out wV);
} }
else else
{ {
xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX); xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX, out xV);
vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX); vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX, out wV);
} }
if (yEq) if (yEq)
{ {
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY); yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY, out yV);
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY); hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY, out hV);
} }
else else
{ {
yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY); yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY, out yV);
hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY); hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY, out hV);
} }
// Anchors
xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y); xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y);
xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y); xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y);
@@ -665,6 +669,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
hEl.Control.AnchorMin = new Float2(0.5f, xEl.Control.AnchorMin.Y); hEl.Control.AnchorMin = new Float2(0.5f, xEl.Control.AnchorMin.Y);
hEl.Control.AnchorMax = new Float2(1, xEl.Control.AnchorMax.Y); hEl.Control.AnchorMax = new Float2(1, xEl.Control.AnchorMax.Y);
// Navigation path
xV.NavTargetRight = yV;
yV.NavTargetRight = wV;
wV.NavTargetRight = hV;
yV.NavTargetLeft = xV;
wV.NavTargetLeft = yV;
hV.NavTargetLeft = wV;
} }
private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont) private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont)
@@ -684,17 +697,19 @@ namespace FlaxEditor.CustomEditors.Dedicated
return grid; return grid;
} }
private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor) private CustomElementsContainer<UniformGridPanel> UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor, out FloatValueBox valueBox)
{ {
valueBox = null;
var grid = UniformGridTwoByOne(el); var grid = UniformGridTwoByOne(el);
grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1); grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1);
var label = grid.Label(text, TextAlignment.Far); var label = grid.Label(text, TextAlignment.Far);
var editor = grid.Object(values); var editor = grid.Object(values);
if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement) if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement)
{ {
valueBox = floatEditorElement.ValueBox;
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor); valueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor);
floatEditorElement.ValueBox.BorderSelectedColor = borderColor; valueBox.BorderSelectedColor = borderColor;
} }
return grid; return grid;
} }

View File

@@ -69,7 +69,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding;
@@ -158,7 +158,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding;
@@ -247,7 +247,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding;

View File

@@ -143,7 +143,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var xValue = XElement.ValueBox.Value; var xValue = XElement.ValueBox.Value;
@@ -318,7 +318,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
@@ -418,7 +418,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;

View File

@@ -89,7 +89,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
@@ -200,7 +200,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
@@ -311,7 +311,7 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnValueChanged() private void OnValueChanged()
{ {
if (IsSetBlocked) if (IsSetBlocked || Values == null)
return; return;
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding; var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;

View File

@@ -141,7 +141,7 @@ bool Editor::CheckProjectUpgrade()
FileSystem::DeleteDirectory(tempSourceSetup); FileSystem::DeleteDirectory(tempSourceSetup);
FileSystem::CreateDirectory(tempSourceSetup); FileSystem::CreateDirectory(tempSourceSetup);
Array<String> files; Array<String> files;
FileSystem::DirectoryGetFiles(files, sourceFolder, TEXT("*"), DirectorySearchOption::AllDirectories); FileSystem::DirectoryGetFiles(files, sourceFolder);
bool useEditorModule = false; bool useEditorModule = false;
for (auto& file : files) for (auto& file : files)
{ {
@@ -159,7 +159,7 @@ bool Editor::CheckProjectUpgrade()
FileSystem::CopyFile(tempSourceFile, file); FileSystem::CopyFile(tempSourceFile, file);
} }
FileSystem::DeleteDirectory(sourceFolder); FileSystem::DeleteDirectory(sourceFolder);
FileSystem::CopyDirectory(sourceFolder, tempSourceSetup, true); FileSystem::CopyDirectory(sourceFolder, tempSourceSetup);
FileSystem::DeleteDirectory(tempSourceSetup); FileSystem::DeleteDirectory(tempSourceSetup);
// Generate module files // Generate module files
@@ -364,7 +364,7 @@ bool Editor::BackupProject()
LOG(Info, "Backup project to \"{0}\"", dstPath); LOG(Info, "Backup project to \"{0}\"", dstPath);
// Copy everything // Copy everything
return FileSystem::CopyDirectory(dstPath, Globals::ProjectFolder, true); return FileSystem::CopyDirectory(dstPath, Globals::ProjectFolder);
} }
int32 Editor::LoadProduct() int32 Editor::LoadProduct()

View File

@@ -218,6 +218,13 @@ namespace FlaxEditor.GUI
Render2D.FillRectangle(bounds, style.Selection); Render2D.FillRectangle(bounds, style.Selection);
Render2D.DrawRectangle(bounds, style.SelectionBorder); Render2D.DrawRectangle(bounds, style.SelectionBorder);
} }
// Navigation focus highlight
if (IsNavFocused)
{
var bounds = new Rectangle(Float2.Zero, Size);
Render2D.DrawRectangle(bounds, style.BackgroundSelected);
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -286,35 +293,7 @@ namespace FlaxEditor.GUI
else if (Button1Rect.Contains(location)) else if (Button1Rect.Contains(location))
{ {
Focus(); Focus();
if (Validator.AssetType != ScriptType.Null) OnSubmit();
{
// Show asset picker popup
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
{
Validator.SelectedItem = item;
RootWindow.Focus();
Focus();
});
if (Validator.SelectedAsset != null)
{
var selectedAssetName = Path.GetFileNameWithoutExtension(Validator.SelectedAsset.Path);
popup.ScrollToAndHighlightItemByName(selectedAssetName);
}
}
else
{
// Show content item picker popup
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
{
Validator.SelectedItem = item;
RootWindow.Focus();
Focus();
});
if (Validator.SelectedItem != null)
{
popup.ScrollToAndHighlightItemByName(Validator.SelectedItem.ShortName);
}
}
} }
else if (Validator.SelectedAsset != null || Validator.SelectedItem != null) else if (Validator.SelectedAsset != null || Validator.SelectedItem != null)
{ {
@@ -412,5 +391,41 @@ namespace FlaxEditor.GUI
return DragDropEffect.Move; return DragDropEffect.Move;
} }
/// <inheritdoc />
public override void OnSubmit()
{
base.OnSubmit();
if (Validator.AssetType != ScriptType.Null)
{
// Show asset picker popup
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
{
Validator.SelectedItem = item;
RootWindow.Focus();
Focus();
});
if (Validator.SelectedAsset != null)
{
var selectedAssetName = Path.GetFileNameWithoutExtension(Validator.SelectedAsset.Path);
popup.ScrollToAndHighlightItemByName(selectedAssetName);
}
}
else
{
// Show content item picker popup
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, Validator.IsValid, item =>
{
Validator.SelectedItem = item;
RootWindow.Focus();
Focus();
});
if (Validator.SelectedItem != null)
{
popup.ScrollToAndHighlightItemByName(Validator.SelectedItem.ShortName);
}
}
}
} }
} }

View File

@@ -155,6 +155,11 @@ namespace FlaxEditor.Modules.SourceCodeEditing
/// </summary> /// </summary>
public readonly CachedTypesCollection All = new CachedAllTypesCollection(8096, ScriptType.Null, type => true, HasAssemblyValidAnyTypes); public readonly CachedTypesCollection All = new CachedAllTypesCollection(8096, ScriptType.Null, type => true, HasAssemblyValidAnyTypes);
/// <summary>
/// The all types collection from all assemblies (including C# system libraries).
/// </summary>
public readonly CachedTypesCollection AllWithStd = new CachedTypesCollection(8096, ScriptType.Null, type => true, assembly => true);
/// <summary> /// <summary>
/// The all valid types collection for the Visual Script property types (includes basic types like int/float, structures, object references). /// The all valid types collection for the Visual Script property types (includes basic types like int/float, structures, object references).
/// </summary> /// </summary>
@@ -574,21 +579,17 @@ namespace FlaxEditor.Modules.SourceCodeEditing
private static bool HasAssemblyValidAnyTypes(Assembly assembly) private static bool HasAssemblyValidAnyTypes(Assembly assembly)
{ {
var codeBase = Utils.GetAssemblyLocation(assembly); var codeBase = Utils.GetAssemblyLocation(assembly);
if (string.IsNullOrEmpty(codeBase))
return true;
#if USE_NETCORE #if USE_NETCORE
if (assembly.ManifestModule.FullyQualifiedName == "<In Memory Module>") if (assembly.ManifestModule.FullyQualifiedName == "<In Memory Module>")
return false; return false;
if (string.IsNullOrEmpty(codeBase))
return true;
// Skip runtime related assemblies // Skip runtime related assemblies
string repositoryUrl = assembly.GetCustomAttributes<AssemblyMetadataAttribute>().FirstOrDefault(x => x.Key == "RepositoryUrl")?.Value ?? ""; string repositoryUrl = assembly.GetCustomAttributes<AssemblyMetadataAttribute>().FirstOrDefault(x => x.Key == "RepositoryUrl")?.Value ?? "";
if (repositoryUrl != "https://github.com/dotnet/runtime") if (repositoryUrl != "https://github.com/dotnet/runtime")
return true; return true;
#else #else
if (string.IsNullOrEmpty(codeBase))
return true;
// Skip assemblies from in-build Mono directory // Skip assemblies from in-build Mono directory
if (!codeBase.Contains("/Mono/lib/mono/")) if (!codeBase.Contains("/Mono/lib/mono/"))
return true; return true;

View File

@@ -211,10 +211,10 @@ namespace FlaxEditor.Options
public bool SeparateValueAndUnit { get; set; } public bool SeparateValueAndUnit { get; set; }
/// <summary> /// <summary>
/// Gets or sets the option to put a space between numbers and units for unit formatting. /// Gets or sets tree line visibility.
/// </summary> /// </summary>
[DefaultValue(true)] [DefaultValue(true)]
[EditorDisplay("Interface"), EditorOrder(320)] [EditorDisplay("Interface"), EditorOrder(320), Tooltip("Toggles tree line visibility in places like the Scene or Content Panel.")]
public bool ShowTreeLines { get; set; } = true; public bool ShowTreeLines { get; set; } = true;
/// <summary> /// <summary>
@@ -369,7 +369,7 @@ namespace FlaxEditor.Options
public int NumberOfGameClientsToLaunch = 1; public int NumberOfGameClientsToLaunch = 1;
/// <summary> /// <summary>
/// Gets or sets the visject connection curvature. /// Gets or sets the curvature of the line connecting to connected visject nodes.
/// </summary> /// </summary>
[DefaultValue(1.0f), Range(0.0f, 2.0f)] [DefaultValue(1.0f), Range(0.0f, 2.0f)]
[EditorDisplay("Visject"), EditorOrder(550)] [EditorDisplay("Visject"), EditorOrder(550)]

View File

@@ -183,12 +183,6 @@ namespace VisualStudio
} }
}; };
// Scans the running processes to find a running Visual Studio instance with the specified version and open solution.
//
// @param[in] clsID Class ID of the specific Visual Studio version we are looking for.
// @param[in] solutionPath Path to the solution the instance needs to have open.
// @return DTE object that may be used to interact with the Visual Studio instance, or null if
// not found.
static Result FindRunningInstance(ConnectionHandle connection) static Result FindRunningInstance(ConnectionHandle connection)
{ {
HRESULT result; HRESULT result;

View File

@@ -74,6 +74,7 @@ namespace FlaxEditor.Surface
Visible = false, Visible = false,
Parent = this, Parent = this,
EndEditOnClick = false, // We have to handle this ourselves, otherwise the textbox instantly loses focus when double-clicking the header EndEditOnClick = false, // We have to handle this ourselves, otherwise the textbox instantly loses focus when double-clicking the header
HorizontalAlignment = TextAlignment.Center,
}; };
} }

View File

@@ -448,6 +448,8 @@ namespace FlaxEditor.Surface
sb.Append("virtual "); sb.Append("virtual ");
sb.Append(valueType.Name); sb.Append(valueType.Name);
sb.Append(' '); sb.Append(' ');
if (member.IsMethod)
sb.Append(member.DeclaringType.Namespace).Append('.');
sb.Append(declaringType.Name); sb.Append(declaringType.Name);
sb.Append('.'); sb.Append('.');
sb.Append(name); sb.Append(name);

View File

@@ -140,12 +140,12 @@ namespace FlaxEditor.Surface
var searchStartTime = DateTime.Now; var searchStartTime = DateTime.Now;
#endif #endif
foreach (var scriptType in Editor.Instance.CodeEditing.All.Get()) foreach (var scriptType in Editor.Instance.CodeEditing.AllWithStd.Get())
{ {
if (!SurfaceUtils.IsValidVisualScriptType(scriptType)) if (SurfaceUtils.IsValidVisualScriptType(scriptType))
continue; {
_iterator(scriptType, _cache, _version);
_iterator(scriptType, _cache, _version); }
} }
// Add group to context menu (on a main thread) // Add group to context menu (on a main thread)

View File

@@ -17,7 +17,6 @@ using FlaxEditor.Surface.Elements;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Utilities; using FlaxEngine.Utilities;
using Object = FlaxEngine.Object;
namespace FlaxEditor.Surface namespace FlaxEditor.Surface
{ {
@@ -36,6 +35,14 @@ namespace FlaxEditor.Surface
Archetypes = new List<NodeArchetype>(), Archetypes = new List<NodeArchetype>(),
}; };
private static readonly string[] _blacklistedTypeNames =
{
"Newtonsoft.Json.",
"System.Array",
"System.Linq.Expressions.",
"System.Reflection.",
};
private static NodesCache _nodesCache = new NodesCache(IterateNodesCache); private static NodesCache _nodesCache = new NodesCache(IterateNodesCache);
private DragActors _dragActors; private DragActors _dragActors;
@@ -269,8 +276,11 @@ namespace FlaxEditor.Surface
{ {
// Skip Newtonsoft.Json stuff // Skip Newtonsoft.Json stuff
var scriptTypeTypeName = scriptType.TypeName; var scriptTypeTypeName = scriptType.TypeName;
if (scriptTypeTypeName.StartsWith("Newtonsoft.Json.")) for (var i = 0; i < _blacklistedTypeNames.Length; i++)
return; {
if (scriptTypeTypeName.StartsWith(_blacklistedTypeNames[i]))
return;
}
var scriptTypeName = scriptType.Name; var scriptTypeName = scriptType.Name;
// Enum // Enum

View File

@@ -79,5 +79,5 @@ public:
static bool ReplaceInFile(const StringView& file, const Dictionary<String, String, HeapAllocation>& replaceMap); static bool ReplaceInFile(const StringView& file, const Dictionary<String, String, HeapAllocation>& replaceMap);
static bool CopyFileIfNewer(const StringView& dst, const StringView& src); static bool CopyFileIfNewer(const StringView& dst, const StringView& src);
static bool CopyDirectoryIfNewer(const StringView& dst, const StringView& src, bool withSubDirectories); static bool CopyDirectoryIfNewer(const StringView& dst, const StringView& src, bool withSubDirectories = true);
}; };

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using FlaxEditor.Gizmo; using FlaxEditor.Gizmo;
using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Input;
using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph;
using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Cameras;
using FlaxEditor.Viewport.Widgets; using FlaxEditor.Viewport.Widgets;
@@ -298,9 +299,33 @@ namespace FlaxEditor.Viewport
} }
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume"); var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
buttonBB.Tag = -1.0f; buttonBB.Tag = -1.0f;
translateSnappingCM.ButtonClicked += button => var buttonCustom = translateSnappingCM.AddButton("Custom");
buttonCustom.LinkTooltip("Custom grid size");
const float defaultCustomTranslateSnappingValue = 250.0f;
float customTranslateSnappingValue = transformGizmo.TranslationSnapValue;
if (customTranslateSnappingValue < 0.0f)
customTranslateSnappingValue = defaultCustomTranslateSnappingValue;
foreach (var v in TranslateSnapValues)
{ {
var v = (float)button.Tag; if (Mathf.Abs(transformGizmo.TranslationSnapValue - v) < 0.001f)
{
customTranslateSnappingValue = defaultCustomTranslateSnappingValue;
break;
}
}
buttonCustom.Tag = customTranslateSnappingValue;
var customValue = new FloatValueBox(customTranslateSnappingValue, Style.Current.FontMedium.MeasureText(buttonCustom.Text).X + 5, 2, 70.0f, 0.001f, float.MaxValue, 0.1f)
{
Parent = buttonCustom
};
customValue.ValueChanged += () =>
{
buttonCustom.Tag = customValue.Value;
buttonCustom.Click();
};
translateSnappingCM.ButtonClicked += b =>
{
var v = (float)b.Tag;
transformGizmo.TranslationSnapValue = v; transformGizmo.TranslationSnapValue = v;
if (v < 0.0f) if (v < 0.0f)
translateSnapping.Text = "Bounding Box"; translateSnapping.Text = "Bounding Box";

View File

@@ -221,7 +221,7 @@ namespace FlaxEditor.Viewport
editor.SceneEditing.SelectionChanged += OnSelectionChanged; editor.SceneEditing.SelectionChanged += OnSelectionChanged;
// Gizmo widgets // Gizmo widgets
AddGizmoViewportWidgets(this, TransformGizmo); AddGizmoViewportWidgets(this, TransformGizmo, true);
// Show grid widget // Show grid widget
_showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled); _showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled);

View File

@@ -32,6 +32,7 @@ namespace FlaxEditor.Windows.Assets
_surface.ContextChanged += OnSurfaceContextChanged; _surface.ContextChanged += OnSurfaceContextChanged;
// Toolstrip // Toolstrip
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
_toolstrip.AddSeparator(); _toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/anim-graph/index.html")).LinkTooltip("See documentation to learn more"); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/anim-graph/index.html")).LinkTooltip("See documentation to learn more");

View File

@@ -28,6 +28,7 @@ namespace FlaxEditor.Windows.Assets
}; };
// Toolstrip // Toolstrip
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
_toolstrip.AddSeparator(); _toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/index.html")).LinkTooltip("See documentation to learn more"); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/index.html")).LinkTooltip("See documentation to learn more");
} }

View File

@@ -28,6 +28,7 @@ namespace FlaxEditor.Windows.Assets
}; };
// Toolstrip // Toolstrip
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
_toolstrip.AddSeparator(); _toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more"); _toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
} }

View File

@@ -153,6 +153,14 @@ namespace FlaxEditor.Windows.Assets
{ {
var menu = new ContextMenu(); var menu = new ContextMenu();
var copySprite = menu.AddButton("Copy sprite");
copySprite.Tag = groupPanel.Tag;
copySprite.ButtonClicked += OnCopySpriteClicked;
var pasteSprite = menu.AddButton("Paste sprite");
pasteSprite.Tag = groupPanel.Tag;
pasteSprite.ButtonClicked += OnPasteSpriteClicked;
var deleteSprite = menu.AddButton("Delete sprite"); var deleteSprite = menu.AddButton("Delete sprite");
deleteSprite.Tag = groupPanel.Tag; deleteSprite.Tag = groupPanel.Tag;
deleteSprite.ButtonClicked += OnDeleteSpriteClicked; deleteSprite.ButtonClicked += OnDeleteSpriteClicked;
@@ -160,6 +168,24 @@ namespace FlaxEditor.Windows.Assets
menu.Show(groupPanel, location); menu.Show(groupPanel, location);
} }
private void OnCopySpriteClicked(ContextMenuButton button)
{
var window = ((PropertiesProxy)ParentEditor.Values[0])._window;
var index = (int)button.Tag;
var sprite = window.Asset.GetSprite(index);
Clipboard.Text = FlaxEngine.Json.JsonSerializer.Serialize(sprite, typeof(Sprite));
}
private void OnPasteSpriteClicked(ContextMenuButton button)
{
var window = ((PropertiesProxy)ParentEditor.Values[0])._window;
var index = (int)button.Tag;
var sprite = window.Asset.GetSprite(index);
var pasted = FlaxEngine.Json.JsonSerializer.Deserialize<Sprite>(Clipboard.Text);
sprite.Area = pasted.Area;
window.Asset.SetSprite(index, ref sprite);
}
private void OnDeleteSpriteClicked(ContextMenuButton button) private void OnDeleteSpriteClicked(ContextMenuButton button)
{ {
var window = ((PropertiesProxy)ParentEditor.Values[0])._window; var window = ((PropertiesProxy)ParentEditor.Values[0])._window;

View File

@@ -27,9 +27,21 @@ namespace FlaxEditor.Windows.Assets
/// </summary> /// </summary>
protected readonly Panel _panel; protected readonly Panel _panel;
private readonly ToolStripButton _saveButton; /// <summary>
private readonly ToolStripButton _undoButton; /// Save button.
private readonly ToolStripButton _redoButton; /// </summary>
protected ToolStripButton _saveButton;
/// <summary>
/// Undo button.
/// </summary>
protected ToolStripButton _undoButton;
/// <summary>
/// Redo button.
/// </summary>
protected ToolStripButton _redoButton;
private bool _showWholeGraphOnLoad = true; private bool _showWholeGraphOnLoad = true;
/// <summary> /// <summary>
@@ -61,17 +73,12 @@ namespace FlaxEditor.Windows.Assets
protected VisjectFunctionSurfaceWindow(Editor editor, AssetItem item) protected VisjectFunctionSurfaceWindow(Editor editor, AssetItem item)
: base(editor, item) : base(editor, item)
{ {
var inputOptions = Editor.Options.Options.Input;
// Undo // Undo
_undo = new Undo(); _undo = new Undo();
_undo.UndoDone += OnUndoRedo; _undo.UndoDone += OnUndoRedo;
_undo.RedoDone += OnUndoRedo; _undo.RedoDone += OnUndoRedo;
_undo.ActionDone += OnUndoRedo; _undo.ActionDone += OnUndoRedo;
// Toolstrip
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
// Panel // Panel
_panel = new Panel(ScrollBars.None) _panel = new Panel(ScrollBars.None)
{ {

View File

@@ -146,12 +146,11 @@ namespace FlaxEditor.Windows.Profiler
avgStats.TotalDataSent += e.TotalDataSent; avgStats.TotalDataSent += e.TotalDataSent;
avgStats.TotalDataReceived += e.TotalDataReceived; avgStats.TotalDataReceived += e.TotalDataReceived;
} }
avgStats.TotalDataSent /= (uint)_stats.Count; //avgStats.TotalDataSent /= (uint)_stats.Count;
avgStats.TotalDataReceived /= (uint)_stats.Count; //avgStats.TotalDataReceived /= (uint)_stats.Count;
_dataSentRateChart.AddSample(avgStats.TotalDataSent); _dataSentRateChart.AddSample(avgStats.TotalDataSent);
_dataReceivedRateChart.AddSample(avgStats.TotalDataReceived); _dataReceivedRateChart.AddSample(avgStats.TotalDataReceived);
// Gather network events // Gather network events
var events = ProfilingTools.EventsNetwork; var events = ProfilingTools.EventsNetwork;
if (_events == null) if (_events == null)
@@ -319,7 +318,7 @@ namespace FlaxEditor.Windows.Profiler
private static string FormatCellBytes(object x) private static string FormatCellBytes(object x)
{ {
return Utilities.Utils.FormatBytesCount((ulong)x); return Utilities.Utils.FormatBytesCount((ulong)(int)x);
} }
private static int SortRows(Control x, Control y) private static int SortRows(Control x, Control y)

View File

@@ -135,6 +135,7 @@ const Char* SplashScreenQuotes[] =
TEXT("Drum roll please"), TEXT("Drum roll please"),
TEXT("Good Luck Have Fun"), TEXT("Good Luck Have Fun"),
TEXT("GG Well Played"), TEXT("GG Well Played"),
TEXT("Now with documentation."),
}; };
SplashScreen::~SplashScreen() SplashScreen::~SplashScreen()

View File

@@ -396,8 +396,8 @@ void BehaviorTreeMoveToNode::GetAgentSize(Actor* agent, float& outRadius, float&
// Estimate actor bounds to extract capsule information // Estimate actor bounds to extract capsule information
const BoundingBox box = agent->GetBox(); const BoundingBox box = agent->GetBox();
const BoundingSphere sphere = agent->GetSphere(); const BoundingSphere sphere = agent->GetSphere();
outRadius = sphere.Radius; outRadius = (float)sphere.Radius;
outHeight = box.GetSize().Y; outHeight = (float)box.GetSize().Y;
} }
int32 BehaviorTreeMoveToNode::GetStateSize() const int32 BehaviorTreeMoveToNode::GetStateSize() const
@@ -522,7 +522,7 @@ String BehaviorTreeMoveToNode::GetDebugInfo(const BehaviorUpdateContext& context
goal = state->GoalLocation.ToString(); goal = state->GoalLocation.ToString();
const Vector3 agentLocation = state->Agent->GetPosition(); const Vector3 agentLocation = state->Agent->GetPosition();
const Vector3 agentLocationOnPath = agentLocation + state->AgentOffset; const Vector3 agentLocationOnPath = agentLocation + state->AgentOffset;
float distanceLeft = state->Path.Count() > state->TargetPathIndex ? Vector3::Distance(state->Path[state->TargetPathIndex], agentLocationOnPath) : 0; Real distanceLeft = state->Path.Count() > state->TargetPathIndex ? Vector3::Distance(state->Path[state->TargetPathIndex], agentLocationOnPath) : 0;
for (int32 i = state->TargetPathIndex; i < state->Path.Count(); i++) for (int32 i = state->TargetPathIndex; i < state->Path.Count(); i++)
distanceLeft += Vector3::Distance(state->Path[i - 1], state->Path[i]); distanceLeft += Vector3::Distance(state->Path[i - 1], state->Path[i]);
return String::Format(TEXT("Agent: '{}'\nGoal: '{}'\nDistance: {}"), agent, goal, (int32)distanceLeft); return String::Format(TEXT("Agent: '{}'\nGoal: '{}'\nDistance: {}"), agent, goal, (int32)distanceLeft);
@@ -559,7 +559,7 @@ void BehaviorTreeMoveToNode::State::OnUpdate()
const float acceptableHeightPercentage = 1.05f; const float acceptableHeightPercentage = 1.05f;
const float testHeight = agentHeight * acceptableHeightPercentage; const float testHeight = agentHeight * acceptableHeightPercentage;
const Vector3 toGoal = agentLocationOnPath - pathSegmentEnd; const Vector3 toGoal = agentLocationOnPath - pathSegmentEnd;
const float toGoalHeightDiff = (toGoal * UpVector).SumValues(); const Real toGoalHeightDiff = (toGoal * UpVector).SumValues();
if (toGoal.Length() <= testRadius && toGoalHeightDiff <= testHeight) if (toGoal.Length() <= testRadius && toGoalHeightDiff <= testHeight)
{ {
TargetPathIndex++; TargetPathIndex++;

View File

@@ -465,7 +465,7 @@ namespace FlaxEngine
/// <summary> /// <summary>
/// A single keyframe that can be injected into linear curve. /// A single keyframe that can be injected into linear curve.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct Keyframe : IComparable, IComparable<Keyframe> public struct Keyframe : IComparable, IComparable<Keyframe>
{ {
/// <summary> /// <summary>
@@ -720,7 +720,7 @@ namespace FlaxEngine
/// <summary> /// <summary>
/// A single keyframe that can be injected into Bezier curve. /// A single keyframe that can be injected into Bezier curve.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct Keyframe : IComparable, IComparable<Keyframe> public struct Keyframe : IComparable, IComparable<Keyframe>
{ {
/// <summary> /// <summary>

View File

@@ -113,7 +113,7 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ
newMidJointPos = rootTransform.Translation + projJointDist * toTargetDir + jointLineDist * bendDirection; newMidJointPos = rootTransform.Translation + projJointDist * toTargetDir + jointLineDist * bendDirection;
} }
// TODO: fix the new IK impl (https://github.com/FlaxEngine/FlaxEngine/pull/2421) to properly work for character from https://github.com/PrecisionRender/CharacterControllerPro // TODO: fix the new IK impl (https://github.com/FlaxEngine/FlaxEngine/pull/2421) to properly work for character from https://github.com/PrecisionRender/CharacterControllerPro
#define OLD 0 #define OLD 1
// Update root joint orientation // Update root joint orientation
{ {
#if OLD #if OLD

View File

@@ -7,6 +7,7 @@
#include "Loading/ContentLoadingManager.h" #include "Loading/ContentLoadingManager.h"
#include "Loading/Tasks/LoadAssetTask.h" #include "Loading/Tasks/LoadAssetTask.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h"
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
@@ -596,9 +597,10 @@ bool Asset::IsInternalType() const
bool Asset::onLoad(LoadAssetTask* task) bool Asset::onLoad(LoadAssetTask* task)
{ {
if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0)
// It may fail when task is cancelled and new one was created later (don't crash but just end with an error) // It may fail when task is cancelled and new one was created later (don't crash but just end with an error)
if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0)
return true; return true;
LogContextScope logContext(GetID());
Locker.Lock(); Locker.Lock();

View File

@@ -9,6 +9,7 @@
#include "Storage/JsonStorageProxy.h" #include "Storage/JsonStorageProxy.h"
#include "Factories/IAssetFactory.h" #include "Factories/IAssetFactory.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/LogContext.h"
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
@@ -970,6 +971,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type)) if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type))
{ {
LOG(Warning, "Different loaded asset type! Asset: \'{0}\'. Expected type: {1}", result->ToString(), type.ToString()); LOG(Warning, "Different loaded asset type! Asset: \'{0}\'. Expected type: {1}", result->ToString(), type.ToString());
LogContext::Print(LogType::Warning);
return nullptr; return nullptr;
} }
return result; return result;
@@ -1004,6 +1006,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type)
if (!GetAssetInfo(id, assetInfo)) if (!GetAssetInfo(id, assetInfo))
{ {
LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString()); LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString());
LogContext::Print(LogType::Warning);
LOAD_FAILED(); LOAD_FAILED();
} }
#if ASSETS_LOADING_EXTRA_VERIFICATION #if ASSETS_LOADING_EXTRA_VERIFICATION
@@ -1064,7 +1067,7 @@ bool findAsset(const Guid& id, const String& directory, Array<String>& tmpCache,
{ {
// Get all asset files // Get all asset files
tmpCache.Clear(); tmpCache.Clear();
if (FileSystem::DirectoryGetFiles(tmpCache, directory, TEXT("*"), DirectorySearchOption::AllDirectories)) if (FileSystem::DirectoryGetFiles(tmpCache, directory))
{ {
if (FileSystem::DirectoryExists(directory)) if (FileSystem::DirectoryExists(directory))
LOG(Error, "Cannot query files in folder '{0}'.", directory); LOG(Error, "Cannot query files in folder '{0}'.", directory);

View File

@@ -5,6 +5,7 @@
#if COMPILE_WITH_ASSETS_IMPORTER #if COMPILE_WITH_ASSETS_IMPORTER
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Cache.h"
#include "Engine/Core/Collections/Sorting.h" #include "Engine/Core/Collections/Sorting.h"
#include "Engine/Core/Collections/ArrayExtensions.h" #include "Engine/Core/Collections/ArrayExtensions.h"
#include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Serialization/MemoryWriteStream.h"
@@ -766,6 +767,7 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
// Link with object from prefab (if reimporting) // Link with object from prefab (if reimporting)
if (prefab) if (prefab)
{ {
rapidjson_flax::StringBuffer buffer;
for (Actor* a : nodeActors) for (Actor* a : nodeActors)
{ {
for (const auto& i : prefab->ObjectsCache) for (const auto& i : prefab->ObjectsCache)
@@ -776,6 +778,32 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
if (o->GetName() != a->GetName()) // Name match if (o->GetName() != a->GetName()) // Name match
continue; continue;
// Preserve local changes made in the prefab
{
// Serialize
buffer.Clear();
CompactJsonWriter writer(buffer);
writer.StartObject();
const void* defaultInstance = o->GetType().GetDefaultInstance();
o->Serialize(writer, defaultInstance);
writer.EndObject();
// Parse json
rapidjson_flax::Document document;
document.Parse(buffer.GetString(), buffer.GetSize());
// Strip unwanted data
document.RemoveMember("ID");
document.RemoveMember("ParentID");
document.RemoveMember("PrefabID");
document.RemoveMember("PrefabObjectID");
document.RemoveMember("Name");
// Deserialize object
auto modifier = Cache::ISerializeModifier.Get();
a->Deserialize(document, &*modifier);
}
// Mark as this object already exists in prefab so will be preserved when updating it // Mark as this object already exists in prefab so will be preserved when updating it
a->LinkPrefab(o->GetPrefabID(), o->GetPrefabObjectID()); a->LinkPrefab(o->GetPrefabID(), o->GetPrefabObjectID());
break; break;

View File

@@ -1,9 +1,16 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "LogContext.h" #include "LogContext.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h" #include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/StringBuilder.h"
#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Script.h"
#include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h"
#include "Engine/Level/Actor.h"
#include "Engine/Threading/ThreadLocal.h" #include "Engine/Threading/ThreadLocal.h"
struct LogContextThreadData struct LogContextThreadData
@@ -30,7 +37,7 @@ struct LogContextThreadData
Count--; Count--;
} }
LogContextData Peek() LogContextData Peek() const
{ {
return Count > 0 ? Ptr[Count - 1] : LogContextData(); return Count > 0 ? Ptr[Count - 1] : LogContextData();
} }
@@ -38,12 +45,58 @@ struct LogContextThreadData
ThreadLocal<LogContextThreadData> GlobalLogContexts; ThreadLocal<LogContextThreadData> GlobalLogContexts;
String LogContext::GetInfo() void LogContext::Print(LogType verbosity)
{ {
LogContextData context = LogContext::Get(); auto& stack = GlobalLogContexts.Get();
if (context.ObjectID != Guid::Empty) if (stack.Count == 0)
return String::Format(TEXT("(Loading source was {0})"), context.ObjectID); return;
return String::Empty; const StringView indentation(TEXT(" "));
StringBuilder msg;
for (int32 index = (int32)stack.Count - 1; index >= 0; index--)
{
// Build call hierarchy via indentation
msg.Clear();
for (uint32 i = index; i < stack.Count; i++)
msg.Append(indentation);
LogContextData& context = stack.Ptr[index];
if (context.ObjectID != Guid::Empty)
{
// Object reference context
msg.Append(TEXT(" Referenced by "));
if (ScriptingObject* object = Scripting::TryFindObject(context.ObjectID))
{
const StringAnsiView typeName(object->GetType().Fullname);
if (Asset* asset = ScriptingObject::Cast<Asset>(object))
{
msg.AppendFormat(TEXT("asset '{}' ({}, {})"), asset->GetPath(), asset->GetTypeName(), context.ObjectID);
}
else if (Actor* actor = ScriptingObject::Cast<Actor>(object))
{
msg.AppendFormat(TEXT("actor '{}' ({}, {})"), actor->GetNamePath(), String(typeName), context.ObjectID);
}
else if (Script* script = ScriptingObject::Cast<Script>(object))
{
msg.AppendFormat(TEXT("script '{}' ({}, {})"), script->GetNamePath(), String(typeName), context.ObjectID);
}
else
{
msg.AppendFormat(TEXT("object {} ({})"), String(typeName), context.ObjectID);
}
}
else if (Asset* asset = Content::GetAsset(context.ObjectID))
{
msg.AppendFormat(TEXT("asset '{}' ({}, {})"), asset->GetPath(), asset->GetTypeName(), context.ObjectID);
}
else
{
msg.AppendFormat(TEXT("object {}"), context.ObjectID);
}
}
// Print message
Log::Logger::Write(verbosity, msg.ToStringView());
}
} }
void LogContext::Push(const Guid& id) void LogContext::Push(const Guid& id)

View File

@@ -2,6 +2,7 @@
#pragma once #pragma once
#include "Engine/Core/Log.h"
#include "Engine/Core/Config.h" #include "Engine/Core/Config.h"
#include "Engine/Scripting/ScriptingType.h" #include "Engine/Scripting/ScriptingType.h"
@@ -9,7 +10,7 @@ class String;
struct Guid; struct Guid;
/// <summary> /// <summary>
/// Log context data structure. Contains different kinds of context data for different situtations. /// Log context data structure. Contains different kinds of context data for different situations.
/// </summary> /// </summary>
API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData
{ {
@@ -54,10 +55,10 @@ API_CLASS(Static) class FLAXENGINE_API LogContext
API_FUNCTION() static LogContextData Get(); API_FUNCTION() static LogContextData Get();
/// <summary> /// <summary>
/// Returns a string which represents the current log context on the stack. /// Prints the current log context to the log. Does nothing it
/// </summary> /// </summary>
/// <returns>The formatted string representing the current log context.</returns> /// <param name="verbosity">The verbosity of the log.</param>
API_FUNCTION() static String GetInfo(); API_FUNCTION() static void Print(LogType verbosity);
}; };
/// <summary> /// <summary>

View File

@@ -304,7 +304,7 @@ public:
public: public:
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between box and a ray.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -315,7 +315,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between box and a ray.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -326,7 +326,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between box and a ray.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -338,7 +338,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between box and a ray.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero"/> if there was no intersection.</param> /// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero"/> if there was no intersection.</param>
@@ -349,7 +349,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Plane. /// Determines if there is an intersection between box and a plane.
/// </summary> /// </summary>
/// <param name="plane">The plane to test.</param> /// <param name="plane">The plane to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -359,7 +359,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Bounding Box. /// Determines if there is an intersection between two boxes.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -369,7 +369,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Bounding Sphere. /// Determines if there is an intersection between box and a sphere.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -379,7 +379,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines whether the current objects contains a point. /// Determines whether box contains a point.
/// </summary> /// </summary>
/// <param name="point">The point to test.</param> /// <param name="point">The point to test.</param>
/// <returns>The type of containment the two objects have.</returns> /// <returns>The type of containment the two objects have.</returns>
@@ -389,7 +389,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines whether the current objects contains a Bounding Box. /// Determines whether box contains a Bounding Box.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <returns>The type of containment the two objects have.</returns> /// <returns>The type of containment the two objects have.</returns>
@@ -399,7 +399,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines whether the current objects contains a Bounding Sphere. /// Determines whether box contains a Bounding Sphere.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <returns>The type of containment the two objects have.</returns> /// <returns>The type of containment the two objects have.</returns>
@@ -409,7 +409,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines the distance between a Bounding Box and a point. /// Determines the distance between box and a point.
/// </summary> /// </summary>
/// <param name="point">The point to test.</param> /// <param name="point">The point to test.</param>
/// <returns>The distance between bounding box and a point.</returns> /// <returns>The distance between bounding box and a point.</returns>
@@ -419,7 +419,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines the distance between two Bounding Boxed. /// Determines the distance between two boxes.
/// </summary> /// </summary>
/// <param name="box">The bounding box to test.</param> /// <param name="box">The bounding box to test.</param>
/// <returns>The distance between bounding boxes.</returns> /// <returns>The distance between bounding boxes.</returns>

View File

@@ -74,14 +74,14 @@ public:
public: public:
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between sphere and a rat.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
bool Intersects(const Ray& ray) const; bool Intersects(const Ray& ray) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between sphere and a rat.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -89,7 +89,7 @@ public:
bool Intersects(const Ray& ray, Real& distance) const; bool Intersects(const Ray& ray, Real& distance) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between sphere and a rat.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -98,7 +98,7 @@ public:
bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const; bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between sphere and a rat.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="point">When the method completes, contains the point of intersection, or Vector3::Zero if there was no intersection.</param> /// <param name="point">When the method completes, contains the point of intersection, or Vector3::Zero if there was no intersection.</param>
@@ -106,14 +106,14 @@ public:
bool Intersects(const Ray& ray, Vector3& point) const; bool Intersects(const Ray& ray, Vector3& point) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Plane. /// Determines if there is an intersection between sphere and a plane.
/// </summary> /// </summary>
/// <param name="plane">The plane to test.</param> /// <param name="plane">The plane to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
PlaneIntersectionType Intersects(const Plane& plane) const; PlaneIntersectionType Intersects(const Plane& plane) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a triangle. /// Determines if there is an intersection between sphere and a triangle.
/// </summary> /// </summary>
/// <param name="vertex1">The first vertex of the triangle to test.</param> /// <param name="vertex1">The first vertex of the triangle to test.</param>
/// <param name="vertex2">The second vertex of the triangle to test.</param> /// <param name="vertex2">The second vertex of the triangle to test.</param>
@@ -122,28 +122,28 @@ public:
bool Intersects(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) const; bool Intersects(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Bounding Box. /// Determines if there is an intersection between sphere and a box.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
bool Intersects(const BoundingBox& box) const; bool Intersects(const BoundingBox& box) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Bounding Sphere. /// Determines if there is an intersection between two spheres.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
bool Intersects(const BoundingSphere& sphere) const; bool Intersects(const BoundingSphere& sphere) const;
/// <summary> /// <summary>
/// Determines whether the current objects contains a point. /// Determines whether sphere contains a point.
/// </summary> /// </summary>
/// <param name="point">The point to test.</param> /// <param name="point">The point to test.</param>
/// <returns> The type of containment the two objects have.</returns> /// <returns> The type of containment the two objects have.</returns>
ContainmentType Contains(const Vector3& point) const; ContainmentType Contains(const Vector3& point) const;
/// <summary> /// <summary>
/// Determines whether the current objects contains a triangle. /// Determines whether sphere contains a triangle.
/// </summary> /// </summary>
/// <param name="vertex1">The first vertex of the triangle to test.</param> /// <param name="vertex1">The first vertex of the triangle to test.</param>
/// <param name="vertex2">The second vertex of the triangle to test.</param> /// <param name="vertex2">The second vertex of the triangle to test.</param>
@@ -152,14 +152,14 @@ public:
ContainmentType Contains(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) const; ContainmentType Contains(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) const;
/// <summary> /// <summary>
/// Determines whether the current objects contains a Bounding Box /// Determines whether sphere contains a box.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <returns>The type of containment the two objects have.</returns> /// <returns>The type of containment the two objects have.</returns>
ContainmentType Contains(const BoundingBox& box) const; ContainmentType Contains(const BoundingBox& box) const;
/// <summary> /// <summary>
/// Determines whether the current objects contains a Bounding Sphere. /// Determines whether sphere contains a sphere.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <returns>The type of containment the two objects have.</returns> /// <returns>The type of containment the two objects have.</returns>

View File

@@ -290,20 +290,14 @@ namespace Math
return dividend / divisor; return dividend / divisor;
} }
// Check if value is inside the given range // Checks if value is inside the given range.
// @param value value to check
// @param min Minimum value
// @param max Maximum value
template<class T> template<class T>
static bool IsInRange(const T value, const T min, const T max) static bool IsInRange(const T value, const T min, const T max)
{ {
return value >= min && value <= max; return value >= min && value <= max;
} }
// Check if value isn't inside the given range // Checks if value isn't inside the given range.
// @param value value to check
// @param min Minimum value
// @param max Maximum value
template<class T> template<class T>
static bool IsNotInRange(const T value, const T min, const T max) static bool IsNotInRange(const T value, const T min, const T max)
{ {
@@ -311,21 +305,19 @@ namespace Math
} }
// Checks whether a number is a power of two. // Checks whether a number is a power of two.
// @param value Number to check
// @returns True if value is a power of two
static bool IsPowerOfTwo(uint32 value) static bool IsPowerOfTwo(uint32 value)
{ {
return (value & value - 1) == 0; return (value & value - 1) == 0;
} }
// Clamp value to be between minimum and maximum values, inclusive // Clamps value to be between minimum and maximum values, inclusive
template<class T> template<class T>
static T Clamp(const T value, const T min, const T max) static T Clamp(const T value, const T min, const T max)
{ {
return value < min ? min : value < max ? value : max; return value < min ? min : value < max ? value : max;
} }
// Clamp value to be between 0 and 1 range, inclusive // Clamps value to be between 0 and 1 range, inclusive
template<class T> template<class T>
static T Saturate(const T value) static T Saturate(const T value)
{ {
@@ -478,54 +470,43 @@ namespace Math
return Saturate((value - a) / (b - a)); return Saturate((value - a) / (b - a));
} }
// Performs smooth (cubic Hermite) interpolation between 0 and 1 // Performs smooth (cubic Hermite) interpolation between 0 and 1.
// @param amount Value between 0 and 1 indicating interpolation amount
static float SmoothStep(float amount) static float SmoothStep(float amount)
{ {
return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * (3 - 2 * amount); return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * (3 - 2 * amount);
} }
// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints // Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints.
// @param amount Value between 0 and 1 indicating interpolation amount
static float SmootherStep(float amount) static float SmootherStep(float amount)
{ {
return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * amount * (amount * (amount * 6 - 15) + 10); return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * amount * (amount * (amount * 6 - 15) + 10);
} }
// Determines whether the specified value is close to zero (0.0) // Determines whether the specified value is close to zero (0.0).
// @param a The integer value
// @returns True if the specified value is close to zero (0.0). otherwise false
inline int32 IsZero(int32 a) inline int32 IsZero(int32 a)
{ {
return a == 0; return a == 0;
} }
// Determines whether the specified value is close to zero (0.0f) // Determines whether the specified value is close to zero (0.0f).
// @param a The floating value
// @returns True if the specified value is close to zero (0.0f). otherwise false
inline bool IsZero(float a) inline bool IsZero(float a)
{ {
return Abs(a) < ZeroTolerance; return Abs(a) < ZeroTolerance;
} }
// Determines whether the specified value is close to one (1.0) // Determines whether the specified value is close to one (1.0).
// @param a The integer value
// @returns True if the specified value is close to one (1.0). otherwise false
inline bool IsOne(int32 a) inline bool IsOne(int32 a)
{ {
return a == 1; return a == 1;
} }
// Determines whether the specified value is close to one (1.0f) // Determines whether the specified value is close to one (1.0f).
// @param a The floating value
// @returns True if the specified value is close to one (1.0f). otherwise false
inline bool IsOne(float a) inline bool IsOne(float a)
{ {
return IsZero(a - 1.0f); return IsZero(a - 1.0f);
} }
// Returns a value indicating the sign of a number // Returns a value indicating the sign of a number.
// @returns A number that indicates the sign of value
inline float Sign(float v) inline float Sign(float v)
{ {
return v > 0.0f ? 1.0f : v < 0.0f ? -1.0f : 0.0f; return v > 0.0f ? 1.0f : v < 0.0f ? -1.0f : 0.0f;

View File

@@ -165,38 +165,31 @@ namespace Math
return TruncToInt(ceil(value)); return TruncToInt(ceil(value));
} }
// Performs smooth (cubic Hermite) interpolation between 0 and 1 // Performs smooth (cubic Hermite) interpolation between 0 and 1.
// @param amount Value between 0 and 1 indicating interpolation amount
static double SmoothStep(double amount) static double SmoothStep(double amount)
{ {
return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * (3. - 2. * amount); return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * (3. - 2. * amount);
} }
// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints // Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints.
// @param amount Value between 0 and 1 indicating interpolation amount
static double SmootherStep(double amount) static double SmootherStep(double amount)
{ {
return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * amount * (amount * (amount * 6. - 15.) + 10.); return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * amount * (amount * (amount * 6. - 15.) + 10.);
} }
// Determines whether the specified value is close to zero (0.0) // Determines whether the specified value is close to zero (0.0).
// @param a The floating value
// @returns True if the specified value is close to zero (0.0). otherwise false
inline bool IsZero(double a) inline bool IsZero(double a)
{ {
return Abs(a) < ZeroTolerance; return Abs(a) < ZeroTolerance;
} }
// Determines whether the specified value is close to one (1.0f) // Determines whether the specified value is close to one (1.0f).
// @param a The floating value
// @returns True if the specified value is close to one (1.0f). otherwise false
inline bool IsOne(double a) inline bool IsOne(double a)
{ {
return IsZero(a - 1.); return IsZero(a - 1.);
} }
// Returns a value indicating the sign of a number // Returns a value indicating the sign of a number.
// @returns A number that indicates the sign of value
inline double Sign(double v) inline double Sign(double v)
{ {
return v > 0. ? 1. : v < 0. ? -1. : 0.; return v > 0. ? 1. : v < 0. ? -1. : 0.;

View File

@@ -531,27 +531,15 @@ public:
public: public:
// Calculates the sum of two matrices. // Calculates the sum of two matrices.
// @param left The first matrix to add.
// @param right The second matrix to add.
// @param result When the method completes, contains the sum of the two matrices.
static void Add(const Matrix& left, const Matrix& right, Matrix& result); static void Add(const Matrix& left, const Matrix& right, Matrix& result);
// Calculates the difference between two matrices. // Calculates the difference between two matrices.
// @param left The first matrix to subtract.
// @param right The second matrix to subtract.
// @param result When the method completes, contains the difference between the two matrices.
static void Subtract(const Matrix& left, const Matrix& right, Matrix& result); static void Subtract(const Matrix& left, const Matrix& right, Matrix& result);
// Scales a matrix by the given value. // Scales a matrix by the given value.
// @param left The matrix to scale.
// @param right The amount by which to scale.
// @param result When the method completes, contains the scaled matrix.
static void Multiply(const Matrix& left, float right, Matrix& result); static void Multiply(const Matrix& left, float right, Matrix& result);
// Calculates the product of two matrices. // Calculates the product of two matrices.
// @param left The first matrix to multiply.
// @param right The second matrix to multiply.
// @returns The product of the two matrices.
static Matrix Multiply(const Matrix& left, const Matrix& right) static Matrix Multiply(const Matrix& left, const Matrix& right)
{ {
Matrix result; Matrix result;
@@ -560,40 +548,21 @@ public:
} }
// Calculates the product of two matrices. // Calculates the product of two matrices.
// @param left The first matrix to multiply.
// @param right The second matrix to multiply.
// @param result The product of the two matrices.
static void Multiply(const Matrix& left, const Matrix& right, Matrix& result); static void Multiply(const Matrix& left, const Matrix& right, Matrix& result);
// Scales a matrix by the given value. // Scales a matrix by the given value.
// @param left The matrix to scale.
// @param right The amount by which to scale.
// @param result When the method completes, contains the scaled matrix.
static void Divide(const Matrix& left, float right, Matrix& result); static void Divide(const Matrix& left, float right, Matrix& result);
// Calculates the quotient of two matrices. // Calculates the quotient of two matrices.
// @param left The first matrix to divide.
// @param right The second matrix to divide.
// @param result When the method completes, contains the quotient of the two matrices.
static void Divide(const Matrix& left, const Matrix& right, Matrix& result); static void Divide(const Matrix& left, const Matrix& right, Matrix& result);
// Negates a matrix. // Negates a matrix.
// @param value The matrix to be negated.
// @param result When the method completes, contains the negated matrix.
static void Negate(const Matrix& value, Matrix& result); static void Negate(const Matrix& value, Matrix& result);
// Performs a linear interpolation between two matrices. // Performs a linear interpolation between two matrices.
// @param start Start matrix.
// @param end End matrix.
// @param amount Value between 0 and 1 indicating the weight of end.
// @param result When the method completes, contains the linear interpolation of the two matrices.
static void Lerp(const Matrix& start, const Matrix& end, float amount, Matrix& result); static void Lerp(const Matrix& start, const Matrix& end, float amount, Matrix& result);
// Performs a cubic interpolation between two matrices. // Performs a cubic interpolation between two matrices.
// @param start Start matrix.
// @param end End matrix.
// @param amount Value between 0 and 1 indicating the weight of end.
// @param result When the method completes, contains the cubic interpolation of the two matrices.
static void SmoothStep(const Matrix& start, const Matrix& end, float amount, Matrix& result) static void SmoothStep(const Matrix& start, const Matrix& end, float amount, Matrix& result)
{ {
amount = Math::SmoothStep(amount); amount = Math::SmoothStep(amount);
@@ -601,13 +570,9 @@ public:
} }
// Calculates the transpose of the specified matrix. // Calculates the transpose of the specified matrix.
// @param value The matrix whose transpose is to be calculated.
// @returns The transpose of the specified matrix.
static Matrix Transpose(const Matrix& value); static Matrix Transpose(const Matrix& value);
// Calculates the transpose of the specified matrix. // Calculates the transpose of the specified matrix.
// @param value The matrix whose transpose is to be calculated.
// @param result When the method completes, contains the transpose of the specified matrix.
static void Transpose(const Matrix& value, Matrix& result); static void Transpose(const Matrix& value, Matrix& result);
/// <summary> /// <summary>
@@ -630,26 +595,12 @@ public:
static void Invert(const Matrix& value, Matrix& result); static void Invert(const Matrix& value, Matrix& result);
// Creates a left-handed spherical billboard that rotates around a specified object position. // Creates a left-handed spherical billboard that rotates around a specified object position.
// @param objectPosition The position of the object around which the billboard will rotate.
// @param cameraPosition The position of the camera.
// @param cameraUpFloat The up vector of the camera.
// @param cameraForwardFloat The forward vector of the camera.
// @param result When the method completes, contains the created billboard matrix.
static void Billboard(const Float3& objectPosition, const Float3& cameraPosition, const Float3& cameraUpFloat, const Float3& cameraForwardFloat, Matrix& result); static void Billboard(const Float3& objectPosition, const Float3& cameraPosition, const Float3& cameraUpFloat, const Float3& cameraForwardFloat, Matrix& result);
// Creates a left-handed, look-at matrix. // Creates a left-handed, look-at matrix.
// @param eye The position of the viewer's eye.
// @param target The camera look-at target.
// @param up The camera's up vector.
// @param result When the method completes, contains the created look-at matrix.
static void LookAt(const Float3& eye, const Float3& target, const Float3& up, Matrix& result); static void LookAt(const Float3& eye, const Float3& target, const Float3& up, Matrix& result);
// Creates a left-handed, orthographic projection matrix. // Creates a left-handed, orthographic projection matrix.
// @param width Width of the viewing volume.
// @param height Height of the viewing volume.
// @param zNear Minimum z-value of the viewing volume.
// @param zFar Maximum z-value of the viewing volume.
// @param result When the method completes, contains the created projection matrix.
static void Ortho(float width, float height, float zNear, float zFar, Matrix& result) static void Ortho(float width, float height, float zNear, float zFar, Matrix& result)
{ {
const float halfWidth = width * 0.5f; const float halfWidth = width * 0.5f;
@@ -658,21 +609,9 @@ public:
} }
// Creates a left-handed, customized orthographic projection matrix. // Creates a left-handed, customized orthographic projection matrix.
// @param left Minimum x-value of the viewing volume.
// @param right Maximum x-value of the viewing volume.
// @param bottom Minimum y-value of the viewing volume.
// @param top Maximum y-value of the viewing volume.
// @param zNear Minimum z-value of the viewing volume.
// @param zFar Maximum z-value of the viewing volume.
// @param result When the method completes, contains the created projection matrix.
static void OrthoOffCenter(float left, float right, float bottom, float top, float zNear, float zFar, Matrix& result); static void OrthoOffCenter(float left, float right, float bottom, float top, float zNear, float zFar, Matrix& result);
// Creates a left-handed, perspective projection matrix. // Creates a left-handed, perspective projection matrix.
// @param width Width of the viewing volume.
// @param height Height of the viewing volume.
// @param zNear Minimum z-value of the viewing volume.
// @param zFar Maximum z-value of the viewing volume.
// @param result When the method completes, contains the created projection matrix.
static void Perspective(float width, float height, float zNear, float zFar, Matrix& result) static void Perspective(float width, float height, float zNear, float zFar, Matrix& result)
{ {
const float halfWidth = width * 0.5f; const float halfWidth = width * 0.5f;
@@ -681,44 +620,24 @@ public:
} }
// Creates a left-handed, perspective projection matrix based on a field of view. // Creates a left-handed, perspective projection matrix based on a field of view.
// @param fov Field of view in the y direction, in radians.
// @param aspect Aspect ratio, defined as view space width divided by height.
// @param zNear Minimum z-value of the viewing volume.
// @param zFar Maximum z-value of the viewing volume.
// @param result When the method completes, contains the created projection matrix.
static void PerspectiveFov(float fov, float aspect, float zNear, float zFar, Matrix& result); static void PerspectiveFov(float fov, float aspect, float zNear, float zFar, Matrix& result);
// Creates a left-handed, customized perspective projection matrix. // Creates a left-handed, customized perspective projection matrix.
// @param left Minimum x-value of the viewing volume.
// @param right Maximum x-value of the viewing volume.
// @param bottom Minimum y-value of the viewing volume.
// @param top Maximum y-value of the viewing volume.
// @param zNear Minimum z-value of the viewing volume.
// @param zFar Maximum z-value of the viewing volume.
// @param result When the method completes, contains the created projection matrix.
static void PerspectiveOffCenter(float left, float right, float bottom, float top, float zNear, float zFar, Matrix& result); static void PerspectiveOffCenter(float left, float right, float bottom, float top, float zNear, float zFar, Matrix& result);
// Creates a matrix that scales along the x-axis, y-axis, and y-axis. // Creates a matrix that scales along the x-axis, y-axis, and y-axis.
// @param scale Scaling factor for all three axes.
// @param result The created scaling matrix.
static Matrix Scaling(const Float3& scale) static Matrix Scaling(const Float3& scale)
{ {
return Scaling(scale.X, scale.Y, scale.Z); return Scaling(scale.X, scale.Y, scale.Z);
} }
// Creates a matrix that scales along the x-axis, y-axis, and y-axis. // Creates a matrix that scales along the x-axis, y-axis, and y-axis.
// @param scale Scaling factor for all three axes.
// @param result When the method completes, contains the created scaling matrix.
static void Scaling(const Float3& scale, Matrix& result) static void Scaling(const Float3& scale, Matrix& result)
{ {
Scaling(scale.X, scale.Y, scale.Z, result); Scaling(scale.X, scale.Y, scale.Z, result);
} }
// Creates a matrix that scales along the x-axis, y-axis, and y-axis. // Creates a matrix that scales along the x-axis, y-axis, and y-axis.
// @param x Scaling factor that is applied along the x-axis.
// @param y Scaling factor that is applied along the y-axis.
// @param z Scaling factor that is applied along the z-axis.
// @returns The created scaling matrix.
static Matrix Scaling(float x, float y, float z) static Matrix Scaling(float x, float y, float z)
{ {
Matrix result = Identity; Matrix result = Identity;
@@ -729,10 +648,6 @@ public:
} }
// Creates a matrix that scales along the x-axis, y-axis, and y-axis. // Creates a matrix that scales along the x-axis, y-axis, and y-axis.
// @param x Scaling factor that is applied along the x-axis.
// @param y Scaling factor that is applied along the y-axis.
// @param z Scaling factor that is applied along the z-axis.
// @param result When the method completes, contains the created scaling matrix.
static void Scaling(float x, float y, float z, Matrix& result) static void Scaling(float x, float y, float z, Matrix& result)
{ {
result = Identity; result = Identity;
@@ -742,8 +657,6 @@ public:
} }
// Creates a matrix that uniformly scales along all three axis. // Creates a matrix that uniformly scales along all three axis.
// @param scale The uniform scale that is applied along all axis.
// @returns The created scaling matrix.
static Matrix Scaling(float scale) static Matrix Scaling(float scale)
{ {
Matrix result = Identity; Matrix result = Identity;
@@ -752,17 +665,13 @@ public:
} }
// Creates a matrix that uniformly scales along all three axis. // Creates a matrix that uniformly scales along all three axis.
// @param scale The uniform scale that is applied along all axis.
// @param result When the method completes, contains the created scaling matrix.
static void Scaling(float scale, Matrix& result) static void Scaling(float scale, Matrix& result)
{ {
result = Identity; result = Identity;
result.M11 = result.M22 = result.M33 = scale; result.M11 = result.M22 = result.M33 = scale;
} }
// Creates a matrix that rotates around the x-axis. // Creates a matrix that rotates around the x-axis. Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @returns The created rotation matrix.
static Matrix RotationX(float angle) static Matrix RotationX(float angle)
{ {
Matrix result; Matrix result;
@@ -770,14 +679,10 @@ public:
return result; return result;
} }
// Creates a matrix that rotates around the x-axis. // Creates a matrix that rotates around the x-axis. Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param result When the method completes, contains the created rotation matrix.
static void RotationX(float angle, Matrix& result); static void RotationX(float angle, Matrix& result);
// Creates a matrix that rotates around the y-axis. // Creates a matrix that rotates around the y-axis. Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @returns The created rotation matrix.
static Matrix RotationY(float angle) static Matrix RotationY(float angle)
{ {
Matrix result; Matrix result;
@@ -785,14 +690,10 @@ public:
return result; return result;
} }
// Creates a matrix that rotates around the y-axis. // Creates a matrix that rotates around the y-axis. Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param result When the method completes, contains the created rotation matrix.
static void RotationY(float angle, Matrix& result); static void RotationY(float angle, Matrix& result);
// Creates a matrix that rotates around the z-axis. // Creates a matrix that rotates around the z-axis. Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @returns The created rotation matrix.
static Matrix RotationZ(float angle) static Matrix RotationZ(float angle)
{ {
Matrix result; Matrix result;
@@ -800,15 +701,10 @@ public:
return result; return result;
} }
// Creates a matrix that rotates around the z-axis. // Creates a matrix that rotates around the z-axis. Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param result When the method completes, contains the created rotation matrix.
static void RotationZ(float angle, Matrix& result); static void RotationZ(float angle, Matrix& result);
// Creates a matrix that rotates around an arbitrary axis. // Creates a matrix that rotates around an arbitrary axis (normalized). Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param axis The axis around which to rotate. This parameter is assumed to be normalized.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @returns The created rotation matrix
static Matrix RotationAxis(const Float3& axis, float angle) static Matrix RotationAxis(const Float3& axis, float angle)
{ {
Matrix result; Matrix result;
@@ -816,10 +712,7 @@ public:
return result; return result;
} }
// Creates a matrix that rotates around an arbitrary axis. // Creates a matrix that rotates around an arbitrary axis (normalized). Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param axis The axis around which to rotate. This parameter is assumed to be normalized.
// @param angle Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
// @param result When the method completes, contains the created rotation matrix.
static void RotationAxis(const Float3& axis, float angle, Matrix& result); static void RotationAxis(const Float3& axis, float angle, Matrix& result);
/// <summary> /// <summary>
@@ -842,10 +735,6 @@ public:
static void RotationQuaternion(const Quaternion& rotation, Matrix& result); static void RotationQuaternion(const Quaternion& rotation, Matrix& result);
// Creates a rotation matrix with a specified yaw, pitch, and roll. // Creates a rotation matrix with a specified yaw, pitch, and roll.
// @param yaw Yaw around the y-axis, in radians.
// @param pitch Pitch around the x-axis, in radians.
// @param roll Roll around the z-axis, in radians.
// @returns The created rotation matrix.
static Matrix RotationYawPitchRoll(float yaw, float pitch, float roll) static Matrix RotationYawPitchRoll(float yaw, float pitch, float roll)
{ {
Matrix result; Matrix result;
@@ -854,34 +743,18 @@ public:
} }
// Creates a rotation matrix with a specified yaw, pitch, and roll. // Creates a rotation matrix with a specified yaw, pitch, and roll.
// @param yaw Yaw around the y-axis, in radians.
// @param pitch Pitch around the x-axis, in radians.
// @param roll Roll around the z-axis, in radians.
// @param result When the method completes, contains the created rotation matrix.
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Matrix& result); static void RotationYawPitchRoll(float yaw, float pitch, float roll, Matrix& result);
// Creates a translation matrix using the specified offsets. // Creates a translation matrix using the specified offsets.
// @param value The offset for all three coordinate planes.
// @returns The created translation matrix.
static Matrix Translation(const Float3& value); static Matrix Translation(const Float3& value);
// Creates a translation matrix using the specified offsets. // Creates a translation matrix using the specified offsets.
// @param value The offset for all three coordinate planes.
// @param result When the method completes, contains the created translation matrix.
static void Translation(const Float3& value, Matrix& result); static void Translation(const Float3& value, Matrix& result);
// Creates a translation matrix using the specified offsets. // Creates a translation matrix using the specified offsets.
// @param x X-coordinate offset.
// @param y Y-coordinate offset.
// @param z Z-coordinate offset.
// @param result When the method completes, contains the created translation matrix.
static void Translation(float x, float y, float z, Matrix& result); static void Translation(float x, float y, float z, Matrix& result);
// Creates a skew/shear matrix by means of a translation vector, a rotation vector, and a rotation angle. // Creates a skew/shear matrix by means of a translation vector, a rotation vector, and a rotation angle.
// @param angle The rotation angle.
// @param rotationVec The rotation vector.
// @param transVec The translation vector.
// @param matrix Contains the created skew/shear matrix.
static void Skew(float angle, const Float3& rotationVec, const Float3& transVec, Matrix& matrix); static void Skew(float angle, const Float3& rotationVec, const Float3& transVec, Matrix& matrix);
/// <summary> /// <summary>
@@ -894,79 +767,55 @@ public:
static void Transformation(const Float3& scaling, const Quaternion& rotation, const Float3& translation, Matrix& result); static void Transformation(const Float3& scaling, const Quaternion& rotation, const Float3& translation, Matrix& result);
// Creates a 3D affine transformation matrix. // Creates a 3D affine transformation matrix.
// @param scaling Scaling factor.
// @param rotation The rotation of the transformation.
// @param translation The translation factor of the transformation.
// @param result When the method completes, contains the created affine transformation matrix.
static void AffineTransformation(float scaling, const Quaternion& rotation, const Float3& translation, Matrix& result); static void AffineTransformation(float scaling, const Quaternion& rotation, const Float3& translation, Matrix& result);
// Creates a 3D affine transformation matrix. // Creates a 3D affine transformation matrix.
// @param scaling Scaling factor.
// @param rotationCenter The center of the rotation.
// @param rotation The rotation of the transformation.
// @param translation The translation factor of the transformation.
// @param result When the method completes, contains the created affine transformation matrix.
static void AffineTransformation(float scaling, const Float3& rotationCenter, const Quaternion& rotation, const Float3& translation, Matrix& result); static void AffineTransformation(float scaling, const Float3& rotationCenter, const Quaternion& rotation, const Float3& translation, Matrix& result);
// Creates a 2D affine transformation matrix. // Creates a 2D affine transformation matrix.
// @param scaling Scaling factor.
// @param rotation The rotation of the transformation.
// @param translation The translation factor of the transformation.
// @param result When the method completes, contains the created affine transformation matrix.
static void AffineTransformation2D(float scaling, float rotation, const Float2& translation, Matrix& result); static void AffineTransformation2D(float scaling, float rotation, const Float2& translation, Matrix& result);
// Creates a 2D affine transformation matrix. // Creates a 2D affine transformation matrix.
// @param scaling Scaling factor.
// @param rotationCenter The center of the rotation.
// @param rotation The rotation of the transformation.
// @param translation The translation factor of the transformation.
// @param result When the method completes, contains the created affine transformation matrix.
static void AffineTransformation2D(float scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result); static void AffineTransformation2D(float scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result);
// Creates a transformation matrix. // Creates a transformation matrix.
// @param scalingCenter Center point of the scaling operation.
// @param scalingRotation Scaling rotation amount.
// @param scaling Scaling factor.
// @param rotationCenter The center of the rotation.
// @param rotation The rotation of the transformation.
// @param translation The translation factor of the transformation.
// @param result When the method completes, contains the created transformation matrix.
static void Transformation(const Float3& scalingCenter, const Quaternion& scalingRotation, const Float3& scaling, const Float3& rotationCenter, const Quaternion& rotation, const Float3& translation, Matrix& result); static void Transformation(const Float3& scalingCenter, const Quaternion& scalingRotation, const Float3& scaling, const Float3& rotationCenter, const Quaternion& rotation, const Float3& translation, Matrix& result);
// Creates a 2D transformation matrix. // Creates a 2D transformation matrix.
// @param scalingCenter Center point of the scaling operation.
// @param scalingRotation Scaling rotation amount.
// @param scaling Scaling factor.
// @param rotationCenter The center of the rotation.
// @param rotation The rotation of the transformation.
// @param translation The translation factor of the transformation.
// @param result When the method completes, contains the created transformation matrix.
static void Transformation2D(const Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result); static void Transformation2D(const Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result);
// Creates a world matrix with the specified parameters. /// <summary>
// @param position Position of the object. This value is used in translation operations. /// Creates a world matrix with the specified parameters.
// @param forward Forward direction of the object. /// </summary>
// @param up Upward direction of the object; usually [0, 1, 0]. /// <param name="position">Position of the object. This value is used in translation operations.</param>
// @returns Created world matrix of given transformation world. /// <param name="forward">Forward direction of the object.</param>
/// <param name="up">Upward direction of the object; usually [0, 1, 0].</param>
/// <returns>Created world matrix of given transformation world.</returns>
static Matrix CreateWorld(const Float3& position, const Float3& forward, const Float3& up); static Matrix CreateWorld(const Float3& position, const Float3& forward, const Float3& up);
// Creates a world matrix with the specified parameters. /// <summary>
// @param position Position of the object. This value is used in translation operations. /// Creates a world matrix with the specified parameters.
// @param forward Forward direction of the object. /// </summary>
// @param up Upward direction of the object; usually [0, 1, 0]. /// <param name="position">Position of the object. This value is used in translation operations.</param>
// @param result Created world matrix of given transformation world. /// <param name="forward">Forward direction of the object.</param>
/// <param name="up">Upward direction of the object; usually [0, 1, 0].</param>
/// <param name="result">Created world matrix of given transformation world.</param>
static void CreateWorld(const Float3& position, const Float3& forward, const Float3& up, Matrix& result); static void CreateWorld(const Float3& position, const Float3& forward, const Float3& up, Matrix& result);
// Creates a new Matrix that rotates around an arbitrary vector. /// <summary>
// @param axis The axis to rotate around. /// Creates a new Matrix that rotates around an arbitrary vector.
// @param angle The angle to rotate around the vector. /// </summary>
// @returns Result matrix. /// <param name="axis">The axis to rotate around.</param>
/// <param name="angle">The angle to rotate around the vector.</param>
/// <returns>Result matrix.</returns>
static Matrix CreateFromAxisAngle(const Float3& axis, float angle); static Matrix CreateFromAxisAngle(const Float3& axis, float angle);
// Creates a new Matrix that rotates around an arbitrary vector. /// <summary>
// @param axis The axis to rotate around. /// Creates a new Matrix that rotates around an arbitrary vector.
// @param angle The angle to rotate around the vector. /// </summary>
// @param result Result matrix. /// <param name="axis"The axis to rotate around.></param>
/// <param name="angle">The angle to rotate around the vector.</param>
/// <param name="result">Result matrix.</param>
static void CreateFromAxisAngle(const Float3& axis, float angle, Matrix& result); static void CreateFromAxisAngle(const Float3& axis, float angle, Matrix& result);
public: public:

View File

@@ -62,7 +62,6 @@ public:
Vector3 GetSize() const; Vector3 GetSize() const;
// Returns the square size of the OBB taking into consideration the scaling applied to the transformation matrix. // Returns the square size of the OBB taking into consideration the scaling applied to the transformation matrix.
// @returns The size of the consideration.
Vector3 GetSizeSquared() const; Vector3 GetSizeSquared() const;
// Gets the center of the OBB. // Gets the center of the OBB.
@@ -85,13 +84,11 @@ public:
public: public:
// Transforms this box using a transformation matrix. // Transforms this box using a transformation matrix.
// @param mat The transformation matrix.
void Transform(const Matrix& matrix); void Transform(const Matrix& matrix);
void Transform(const ::Transform& transform); void Transform(const ::Transform& transform);
// Scales the OBB by scaling its Extents without affecting the Transformation matrix. // Scales the OBB by scaling its Extents without affecting the Transformation matrix.
// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate. // By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
// @param scaling Scale to apply to the box.
void Scale(const Vector3& scaling) void Scale(const Vector3& scaling)
{ {
Extents *= scaling; Extents *= scaling;
@@ -99,14 +96,12 @@ public:
// Scales the OBB by scaling its Extents without affecting the Transformation matrix. // Scales the OBB by scaling its Extents without affecting the Transformation matrix.
// By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate. // By keeping Transformation matrix scaling-free, the collision detection methods will be more accurate.
// @param scaling Scale to apply to the box.
void Scale(Real scaling) void Scale(Real scaling)
{ {
Extents *= scaling; Extents *= scaling;
} }
// Translates the OBB to a new position using a translation vector. // Translates the OBB to a new position using a translation vector.
// @param translation the translation vector.
void Translate(const Vector3& translation) void Translate(const Vector3& translation)
{ {
Transformation.Translation += translation; Transformation.Translation += translation;
@@ -165,38 +160,26 @@ public:
public: public:
// Determines whether a OBB contains a point. // Determines whether a OBB contains a point.
// @param point The point to test.
// @returns The type of containment the two objects have.
ContainmentType Contains(const Vector3& point, Real* distance = nullptr) const; ContainmentType Contains(const Vector3& point, Real* distance = nullptr) const;
// Determines whether a OBB contains a BoundingSphere. /// <summary>
// @param sphere The sphere to test. /// Determines whether a OBB contains a sphere.
// @param ignoreScale Optimize the check operation by assuming that OBB has no scaling applied. /// </summary>
// @returns The type of containment the two objects have. /// <param name="sphere">The sphere to test.</param>
/// <param name="ignoreScale">Optimize the check operation by assuming that OBB has no scaling applied.</param>
/// <returns>The type of containment the two objects have.</returns>
ContainmentType Contains(const BoundingSphere& sphere, bool ignoreScale = false) const; ContainmentType Contains(const BoundingSphere& sphere, bool ignoreScale = false) const;
// Determines whether there is an intersection between a Ray and a OBB. // Determines whether there is an intersection between oriented box and a ray. Returns point of the intersection.
// @param ray The ray to test.
// @param point When the method completes, contains the point of intersection, or Vector3.Zero if there was no intersection.
// @returns Whether the two objects intersected.
bool Intersects(const Ray& ray, Vector3& point) const; bool Intersects(const Ray& ray, Vector3& point) const;
// Determines if there is an intersection between the current object and a Ray. // Determines if there is an intersection between oriented box and a ray. Returns distance to the intersection.
// @param ray The ray to test.
// @param distance When the method completes, contains the distance of the intersection, or 0 if there was no intersection.
// @returns Whether the two objects intersected.
bool Intersects(const Ray& ray, Real& distance) const; bool Intersects(const Ray& ray, Real& distance) const;
// Determines if there is an intersection between the current object and a Ray. // Determines if there is an intersection between oriented box and a ray. Returns distance to the intersection and surface normal.
// @param ray The ray to test.
// @param distance When the method completes, contains the distance of the intersection, or 0 if there was no intersection.
// @param normal When the method completes, contains the intersection surface normal vector, or Vector3::Up if there was no intersection.
// @returns Whether the two objects intersected.
bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const; bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const;
// Determines whether there is an intersection between a Ray and a OBB. // Determines whether there is an intersection between a Ray and a OBB.
// @param ray The ray to test.
// @returns Whether the two objects intersected.
bool Intersects(const Ray& ray) const bool Intersects(const Ray& ray) const
{ {
Vector3 point; Vector3 point;

View File

@@ -7,9 +7,6 @@
#include "Quaternion.h" #include "Quaternion.h"
#include "../Types/String.h" #include "../Types/String.h"
const Real Plane::DistanceEpsilon = 0.0001f;
const Real Plane::NormalEpsilon = 1.0f / 65535.0f;
Plane::Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3) Plane::Plane(const Vector3& point1, const Vector3& point2, const Vector3& point3)
{ {
Vector3 cross; Vector3 cross;

View File

@@ -11,9 +11,9 @@
API_STRUCT() struct FLAXENGINE_API Plane API_STRUCT() struct FLAXENGINE_API Plane
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(Plane); DECLARE_SCRIPTING_TYPE_MINIMAL(Plane);
public:
static const Real DistanceEpsilon; static constexpr Real DistanceEpsilon = 0.0001f;
static const Real NormalEpsilon; static constexpr Real NormalEpsilon = 1.0f / 65535.0f;
public: public:
/// <summary> /// <summary>
@@ -132,170 +132,102 @@ public:
public: public:
static Vector3 Intersection(const Plane& inPlane1, const Plane& inPlane2, const Plane& inPlane3); static Vector3 Intersection(const Plane& inPlane1, const Plane& inPlane2, const Plane& inPlane3);
// Determines if there is an intersection between the current object and a point // Determines if there is an intersection between plane and a point.
// @param point The point to test
// @returns Whether the two objects intersected
PlaneIntersectionType Intersects(const Vector3& point) const PlaneIntersectionType Intersects(const Vector3& point) const
{ {
return CollisionsHelper::PlaneIntersectsPoint(*this, point); return CollisionsHelper::PlaneIntersectsPoint(*this, point);
} }
// summary> // Determines if there is an intersection between plane and a ray.
// Determines if there is an intersection between the current object and a Ray
// @param ray The ray to test
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray) const bool Intersects(const Ray& ray) const
{ {
Real distance; Real distance;
return CollisionsHelper::RayIntersectsPlane(ray, *this, distance); return CollisionsHelper::RayIntersectsPlane(ray, *this, distance);
} }
// summary> // Determines if there is an intersection between plane and a ray. Returns distance to the intersection.
// Determines if there is an intersection between the current object and a Ray.
// /summary>
// @param ray The ray to test
// @param distance When the method completes, contains the distance of the intersection, or 0 if there was no intersection
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray, Real& distance) const bool Intersects(const Ray& ray, Real& distance) const
{ {
return CollisionsHelper::RayIntersectsPlane(ray, *this, distance); return CollisionsHelper::RayIntersectsPlane(ray, *this, distance);
} }
// summary> // Determines if there is an intersection between plane and a ray.
// Determines if there is an intersection between the current object and a Ray.
// /summary>
// @param ray The ray to test
// @param point When the method completes, contains the point of intersection, or <see const="Vector3.Zero"/> if there was no intersection
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray, Vector3& point) const bool Intersects(const Ray& ray, Vector3& point) const
{ {
return CollisionsHelper::RayIntersectsPlane(ray, *this, point); return CollisionsHelper::RayIntersectsPlane(ray, *this, point);
} }
// Determines if there is an intersection between the current object and a Plane // Determines if there is an intersection between two planes.
// @param plane The plane to test
// @returns Whether the two objects intersected
bool Intersects(const Plane& plane) const bool Intersects(const Plane& plane) const
{ {
return CollisionsHelper::PlaneIntersectsPlane(*this, plane); return CollisionsHelper::PlaneIntersectsPlane(*this, plane);
} }
// Determines if there is an intersection between the current object and a Plane // Determines if there is an intersection between two planes. Returns ray that defines a line of intersection.
// @param plane The plane to test
// @param line When the method completes, contains the line of intersection as a Ray, or a zero ray if there was no intersection
// @returns Whether the two objects intersected
bool Intersects(const Plane& plane, Ray& line) const bool Intersects(const Plane& plane, Ray& line) const
{ {
return CollisionsHelper::PlaneIntersectsPlane(*this, plane, line); return CollisionsHelper::PlaneIntersectsPlane(*this, plane, line);
} }
// Determines if there is an intersection between the current object and a triangle // Determines if there is an intersection between plane and a triangle.
// @param vertex1 The first vertex of the triangle to test
// @param vertex2 The second vertex of the triangle to test
// @param vertex3 The third vertex of the triangle to test
// @returns Whether the two objects intersected
PlaneIntersectionType Intersects(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) const PlaneIntersectionType Intersects(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) const
{ {
return CollisionsHelper::PlaneIntersectsTriangle(*this, vertex1, vertex2, vertex3); return CollisionsHelper::PlaneIntersectsTriangle(*this, vertex1, vertex2, vertex3);
} }
// Determines if there is an intersection between the current object and a Bounding Box // Determines if there is an intersection between plane and a box.
// @param box The box to test
// @returns Whether the two objects intersected
PlaneIntersectionType Intersects(const BoundingBox& box) const PlaneIntersectionType Intersects(const BoundingBox& box) const
{ {
return CollisionsHelper::PlaneIntersectsBox(*this, box); return CollisionsHelper::PlaneIntersectsBox(*this, box);
} }
// Determines if there is an intersection between the current object and a Bounding Sphere // Determines if there is an intersection between plane and a sphere.
// @param sphere The sphere to test
// @returns Whether the two objects intersected
PlaneIntersectionType Intersects(const BoundingSphere& sphere) const PlaneIntersectionType Intersects(const BoundingSphere& sphere) const
{ {
return CollisionsHelper::PlaneIntersectsSphere(*this, sphere); return CollisionsHelper::PlaneIntersectsSphere(*this, sphere);
} }
public: public:
// Scales the plane by the given scaling factor // Scales the plane by the given factor.
// @param value The plane to scale
// @param scale The amount by which to scale the plane
// @param result When the method completes, contains the scaled plane
static void Multiply(const Plane& value, Real scale, Plane& result); static void Multiply(const Plane& value, Real scale, Plane& result);
// Scales the plane by the given scaling factor // Scales the plane by the given factor.
// @param value The plane to scale
// @param scale The amount by which to scale the plane
// @returns The scaled plane
static Plane Multiply(const Plane& value, Real scale); static Plane Multiply(const Plane& value, Real scale);
// Calculates the dot product of the specified vector and plane // Calculates the dot product of the specified vector and plane.
// @param left The source plane
// @param right The source vector
// @param result When the method completes, contains the dot product of the specified plane and vector
static void Dot(const Plane& left, const Vector4& right, Real& result); static void Dot(const Plane& left, const Vector4& right, Real& result);
// Calculates the dot product of the specified vector and plane // Calculates the dot product of the specified vector and plane.
// @param left The source plane
// @param right The source vector
// @returns The dot product of the specified plane and vector
static Real Dot(const Plane& left, const Vector4& right); static Real Dot(const Plane& left, const Vector4& right);
// Calculates the dot product of a specified vector and the normal of the plane plus the distance value of the plane // Calculates the dot product of a specified vector and the normal of the plane plus the distance value of the plane.
// @param left The source plane
// @param right The source vector
// @param result When the method completes, contains the dot product of a specified vector and the normal of the Plane plus the distance value of the plane
static void DotCoordinate(const Plane& left, const Vector3& right, Real& result); static void DotCoordinate(const Plane& left, const Vector3& right, Real& result);
// Calculates the dot product of a specified vector and the normal of the plane plus the distance value of the plane // Calculates the dot product of a specified vector and the normal of the plane plus the distance value of the plane.
// @param left The source plane
// @param right The source vector
// @returns The dot product of a specified vector and the normal of the Plane plus the distance value of the plane
static Real DotCoordinate(const Plane& left, const Vector3& right); static Real DotCoordinate(const Plane& left, const Vector3& right);
// Calculates the dot product of the specified vector and the normal of the plane // Calculates the dot product of the specified vector and the normal of the plane.
// @param left The source plane
// @param right The source vector
// @param result When the method completes, contains the dot product of the specified vector and the normal of the plane
static void DotNormal(const Plane& left, const Vector3& right, Real& result); static void DotNormal(const Plane& left, const Vector3& right, Real& result);
// Calculates the dot product of the specified vector and the normal of the plane // Calculates the dot product of the specified vector and the normal of the plane.
// @param left The source plane
// @param right The source vector
// @returns The dot product of the specified vector and the normal of the plane
static Real DotNormal(const Plane& left, const Vector3& right); static Real DotNormal(const Plane& left, const Vector3& right);
// Changes the coefficients of the normal vector of the plane to make it of unit length // Changes the coefficients of the normal vector of the plane to make it of unit length.
// @param plane The source plane
// @param result When the method completes, contains the normalized plane
static void Normalize(const Plane& plane, Plane& result); static void Normalize(const Plane& plane, Plane& result);
// Changes the coefficients of the normal vector of the plane to make it of unit length // Changes the coefficients of the normal vector of the plane to make it of unit length.
// @param plane The source plane
// @returns The normalized plane
static Plane Normalize(const Plane& plane); static Plane Normalize(const Plane& plane);
// Transforms a normalized plane by a quaternion rotation // Transforms a normalized plane by a quaternion rotation.
// @param plane The normalized source plane
// @param rotation The quaternion rotation
// @param result When the method completes, contains the transformed plane
static void Transform(const Plane& plane, const Quaternion& rotation, Plane& result); static void Transform(const Plane& plane, const Quaternion& rotation, Plane& result);
// Transforms a normalized plane by a quaternion rotation // Transforms a normalized plane by a quaternion rotation.
// @param plane The normalized source plane
// @param rotation The quaternion rotation
// @returns The transformed plane
static Plane Transform(const Plane& plane, const Quaternion& rotation); static Plane Transform(const Plane& plane, const Quaternion& rotation);
// Transforms a normalized plane by a matrix // Transforms a normalized plane by a matrix.
// @param plane The normalized source plane
// @param transformation The transformation matrix
// @param result When the method completes, contains the transformed plane
static void Transform(const Plane& plane, const Matrix& transformation, Plane& result); static void Transform(const Plane& plane, const Matrix& transformation, Plane& result);
// Transforms a normalized plane by a matrix // Transforms a normalized plane by a matrix.
// @param plane The normalized source plane
// @param transformation The transformation matrix
// @returns When the method completes, contains the transformed plane
static Plane Transform(const Plane& plane, const Matrix& transformation); static Plane Transform(const Plane& plane, const Matrix& transformation);
}; };

View File

@@ -100,6 +100,22 @@ Float3 Quaternion::operator*(const Float3& vector) const
return Float3::Transform(vector, *this); return Float3::Transform(vector, *this);
} }
void Quaternion::Add(const Quaternion& left, const Quaternion& right, Quaternion& result)
{
result.X = left.X + right.X;
result.Y = left.Y + right.Y;
result.Z = left.Z + right.Z;
result.W = left.W + right.W;
}
void Quaternion::Subtract(const Quaternion& left, const Quaternion& right, Quaternion& result)
{
result.X = left.X - right.X;
result.Y = left.Y - right.Y;
result.Z = left.Z - right.Z;
result.W = left.W - right.W;
}
void Quaternion::Multiply(const Quaternion& left, const Quaternion& right, Quaternion& result) void Quaternion::Multiply(const Quaternion& left, const Quaternion& right, Quaternion& result)
{ {
const float a = left.Y * right.Z - left.Z * right.Y; const float a = left.Y * right.Z - left.Z * right.Y;
@@ -112,6 +128,14 @@ void Quaternion::Multiply(const Quaternion& left, const Quaternion& right, Quate
result.W = left.W * right.W - d; result.W = left.W * right.W - d;
} }
void Quaternion::Negate(const Quaternion& value, Quaternion& result)
{
result.X = -value.X;
result.Y = -value.Y;
result.Z = -value.Z;
result.W = -value.W;
}
void Quaternion::Lerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result) void Quaternion::Lerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result)
{ {
const float inverse = 1.0f - amount; const float inverse = 1.0f - amount;
@@ -542,10 +566,8 @@ void Quaternion::RotationYawPitchRoll(float yaw, float pitch, float roll, Quater
Quaternion Quaternion::GetRotationFromNormal(const Vector3& normal, const Transform& reference) Quaternion Quaternion::GetRotationFromNormal(const Vector3& normal, const Transform& reference)
{ {
Float3 up = reference.GetUp(); Float3 up = reference.GetUp();
const float dot = Vector3::Dot(normal, up); const Real dot = Vector3::Dot(normal, up);
if (Math::NearEqual(Math::Abs(dot), 1)) if (Math::NearEqual(Math::Abs(dot), 1))
{
up = reference.GetRight(); up = reference.GetRight();
}
return Quaternion::LookRotation(normal, up); return Quaternion::LookRotation(normal, up);
} }

View File

@@ -580,7 +580,7 @@ namespace FlaxEngine
public static float AngleBetween(Quaternion a, Quaternion b) public static float AngleBetween(Quaternion a, Quaternion b)
{ {
float num = Dot(a, b); float num = Dot(a, b);
return num > 0.9999999f ? 0 : Mathf.Acos(Mathf.Min(Mathf.Abs(num), 1f)) * 2f * 57.29578f; return num > Tolerance ? 0 : Mathf.Acos(Mathf.Min(Mathf.Abs(num), 1f)) * 2f * 57.29578f;
} }
/// <summary> /// <summary>
@@ -1602,7 +1602,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Quaternion left, Quaternion right) public static bool operator ==(Quaternion left, Quaternion right)
{ {
return Dot(ref left, ref right) > 0.9999999f; return Dot(ref left, ref right) > Tolerance;
} }
/// <summary> /// <summary>
@@ -1614,7 +1614,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Quaternion left, Quaternion right) public static bool operator !=(Quaternion left, Quaternion right)
{ {
return Dot(ref left, ref right) <= 0.9999999f; return Dot(ref left, ref right) <= Tolerance;
} }
/// <summary> /// <summary>
@@ -1714,7 +1714,7 @@ namespace FlaxEngine
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ref Quaternion other) public bool Equals(ref Quaternion other)
{ {
//return Dot(ref this, ref other) > 0.9999999f; //return Dot(ref this, ref other) > Tolerance;
return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W);
} }

View File

@@ -15,6 +15,12 @@ struct Matrix3x3;
API_STRUCT() struct FLAXENGINE_API Quaternion API_STRUCT() struct FLAXENGINE_API Quaternion
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(Quaternion); DECLARE_SCRIPTING_TYPE_MINIMAL(Quaternion);
/// <summary>
/// Equality tolerance factor used when comparing quaternions via dot operation.
/// </summary>
API_FIELD() static constexpr Real Tolerance = 0.9999999f;
public: public:
union union
{ {
@@ -342,7 +348,7 @@ public:
/// <returns><c>true</c> if the specified <see cref="Quaternion" /> is equal to this instance; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified <see cref="Quaternion" /> is equal to this instance; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool operator==(const Quaternion& other) const FORCE_INLINE bool operator==(const Quaternion& other) const
{ {
return Dot(*this, other) > 0.9999999f; return Dot(*this, other) > Tolerance;
} }
/// <summary> /// <summary>
@@ -364,7 +370,7 @@ public:
/// <returns><c>true</c> if the specified <see cref="Quaternion" /> structures are equal; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified <see cref="Quaternion" /> structures are equal; otherwise, <c>false</c>.</returns>
static bool NearEqual(const Quaternion& a, const Quaternion& b) static bool NearEqual(const Quaternion& a, const Quaternion& b)
{ {
return Dot(a, b) > 0.9999999f; return Dot(a, b) > Tolerance;
} }
/// <summary> /// <summary>
@@ -423,37 +429,16 @@ public:
static float AngleBetween(const Quaternion& a, const Quaternion& b) static float AngleBetween(const Quaternion& a, const Quaternion& b)
{ {
const float dot = Dot(a, b); const float dot = Dot(a, b);
return dot > 0.9999999f ? 0 : Math::Acos(Math::Min(Math::Abs(dot), 1.0f)) * 2.0f * 57.29578f; return dot > Tolerance ? 0 : Math::Acos(Math::Min(Math::Abs(dot), 1.0f)) * 2.0f * 57.29578f;
} }
// Adds two quaternions // Adds two quaternions.
// @param left The first quaternion to add static void Add(const Quaternion& left, const Quaternion& right, Quaternion& result);
// @param right The second quaternion to add
// @param result When the method completes, contains the sum of the two quaternions
static void Add(const Quaternion& left, const Quaternion& right, Quaternion& result)
{
result.X = left.X + right.X;
result.Y = left.Y + right.Y;
result.Z = left.Z + right.Z;
result.W = left.W + right.W;
}
// Subtracts two quaternions // Subtracts two quaternions.
// @param left The first quaternion to subtract static void Subtract(const Quaternion& left, const Quaternion& right, Quaternion& result);
// @param right The second quaternion to subtract
// @param result When the method completes, contains the difference of the two quaternions
static void Subtract(const Quaternion& left, const Quaternion& right, Quaternion& result)
{
result.X = left.X - right.X;
result.Y = left.Y - right.Y;
result.Z = left.Z - right.Z;
result.W = left.W - right.W;
}
// Scales a quaternion by the given value // Scales a quaternion by the given value.
// @param value The quaternion to scale
// @param scale The amount by which to scale the quaternion
// @param result When the method completes, contains the scaled quaternion
static void Multiply(const Quaternion& value, float scale, Quaternion& result) static void Multiply(const Quaternion& value, float scale, Quaternion& result)
{ {
result.X = value.X * scale; result.X = value.X * scale;
@@ -462,35 +447,16 @@ public:
result.W = value.W * scale; result.W = value.W * scale;
} }
// Multiplies a quaternion by another // Multiplies a quaternion by another.
// @param left The first quaternion to multiply
// @param right The second quaternion to multiply
// @param result When the method completes, contains the multiplied quaternion
static void Multiply(const Quaternion& left, const Quaternion& right, Quaternion& result); static void Multiply(const Quaternion& left, const Quaternion& right, Quaternion& result);
// Reverses the direction of a given quaternion // Reverses the direction of a given quaternion.
// @param value The quaternion to negate static void Negate(const Quaternion& value, Quaternion& result);
// @param result When the method completes, contains a quaternion facing in the opposite direction
static void Negate(const Quaternion& value, Quaternion& result)
{
result.X = -value.X;
result.Y = -value.Y;
result.Z = -value.Z;
result.W = -value.W;
}
// Performs a linear interpolation between two quaternions // Performs a linear interpolation between two quaternions.
// @param start Start quaternion
// @param end End quaternion
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the linear interpolation of the two quaternions
static void Lerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result); static void Lerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
// Performs a linear interpolation between two quaternion // Performs a linear interpolation between two quaternion.
// @param start Start quaternion
// @param end End quaternion
// @param amount Value between 0 and 1 indicating the weight of end
// @returns The linear interpolation of the two quaternions
static Quaternion Lerp(const Quaternion& start, const Quaternion& end, float amount) static Quaternion Lerp(const Quaternion& start, const Quaternion& end, float amount)
{ {
Quaternion result; Quaternion result;
@@ -498,47 +464,25 @@ public:
return result; return result;
} }
// Creates a quaternion given a rotation and an axis // Creates a quaternion given an angle cosine (radians in range [-1,1]) and an axis of rotation (normalized).
// @param axis The axis of rotation
// @param angle The angle of rotation (in radians).
// @param result When the method completes, contains the newly created quaternion
static void RotationAxis(const Float3& axis, float angle, Quaternion& result); static void RotationAxis(const Float3& axis, float angle, Quaternion& result);
// Creates a quaternion given a angle cosine and an axis // Creates a quaternion given an angle cosine (radians in range [-1,1]) and an axis of rotation (normalized).
// @param axis The axis of rotation
// @param cos The angle cosine, it must be within [-1,1] range (in radians).
// @param result When the method completes, contains the newly created quaternion
static void RotationCosAxis(const Float3& axis, float cos, Quaternion& result); static void RotationCosAxis(const Float3& axis, float cos, Quaternion& result);
// Creates a quaternion given a rotation matrix // Creates a quaternion given a rotation matrix.
// @param matrix The rotation matrix
// @param result When the method completes, contains the newly created quaternion
static void RotationMatrix(const Matrix& matrix, Quaternion& result); static void RotationMatrix(const Matrix& matrix, Quaternion& result);
// Creates a quaternion given a rotation matrix // Creates a quaternion given a rotation matrix.
// @param matrix The rotation matrix
// @param result When the method completes, contains the newly created quaternion
static void RotationMatrix(const Matrix3x3& matrix, Quaternion& result); static void RotationMatrix(const Matrix3x3& matrix, Quaternion& result);
// Creates a left-handed, look-at quaternion // Creates a left-handed, look-at quaternion.
// @param eye The position of the viewer's eye
// @param target The camera look-at target
// @param up The camera's up vector
// @param result When the method completes, contains the created look-at quaternion
static void LookAt(const Float3& eye, const Float3& target, const Float3& up, Quaternion& result); static void LookAt(const Float3& eye, const Float3& target, const Float3& up, Quaternion& result);
// Creates a left-handed, look-at quaternion // Creates a left-handed, look-at quaternion.
// @param forward The camera's forward direction
// @param up The camera's up vector
// @param result When the method completes, contains the created look-at quaternion
static void RotationLookAt(const Float3& forward, const Float3& up, Quaternion& result); static void RotationLookAt(const Float3& forward, const Float3& up, Quaternion& result);
// Creates a left-handed spherical billboard that rotates around a specified object position // Creates a left-handed spherical billboard that rotates around a specified object position.
// @param objectPosition The position of the object around which the billboard will rotate
// @param cameraPosition The position of the camera
// @param cameraUpFloat The up vector of the camera
// @param cameraForwardFloat The forward vector of the camera
// @param result When the method completes, contains the created billboard quaternion
static void Billboard(const Float3& objectPosition, const Float3& cameraPosition, const Float3& cameraUpFloat, const Float3& cameraForwardFloat, Quaternion& result); static void Billboard(const Float3& objectPosition, const Float3& cameraPosition, const Float3& cameraUpFloat, const Float3& cameraForwardFloat, Quaternion& result);
/// <summary> /// <summary>
@@ -613,9 +557,7 @@ public:
return result; return result;
} }
// Creates a quaternion given a rotation matrix // Creates a quaternion given a rotation matrix.
// @param matrix The rotation matrix
// @returns The newly created quaternion
static Quaternion RotationMatrix(const Matrix& matrix) static Quaternion RotationMatrix(const Matrix& matrix)
{ {
Quaternion result; Quaternion result;
@@ -623,30 +565,16 @@ public:
return result; return result;
} }
// Interpolates between two quaternions, using spherical linear interpolation // Interpolates between two quaternions, using spherical linear interpolation.
// @param start Start quaternion
// @param end End quaternion
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the spherical linear interpolation of the two quaternions
static void Slerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result); static void Slerp(const Quaternion& start, const Quaternion& end, float amount, Quaternion& result);
// Creates a quaternion given a yaw, pitch, and roll value (is using degrees) // Creates a quaternion given a yaw, pitch, and roll value (in degrees).
// @param x Roll (in degrees)
// @param x Pitch (in degrees)
// @param x Yaw (in degrees)
// @returns Result rotation
static Quaternion Euler(float x, float y, float z); static Quaternion Euler(float x, float y, float z);
// Creates a quaternion given a yaw, pitch, and roll value (is using degrees) // Creates a quaternion given a yaw, pitch, and roll value in order: X:roll, Y:pitch, Z:yaw (in degrees).
// @param euler Euler angle rotation with values in order: X:roll, Y:pitch, Z:yaw (in degrees)
// @returns Result rotation
static Quaternion Euler(const Float3& euler); static Quaternion Euler(const Float3& euler);
// Creates a quaternion given a yaw, pitch, and roll value (is using radians) // Creates a quaternion given a yaw, pitch, and roll value (in radians).
// @param yaw The yaw of rotation (in radians)
// @param pitch The pitch of rotation (in radians)
// @param roll The roll of rotation (in radians)
// @param result When the method completes, contains the newly created quaternion
static Quaternion RotationYawPitchRoll(float yaw, float pitch, float roll) static Quaternion RotationYawPitchRoll(float yaw, float pitch, float roll)
{ {
Quaternion result; Quaternion result;
@@ -654,11 +582,7 @@ public:
return result; return result;
} }
// Creates a quaternion given a yaw, pitch, and roll value (is using radians) // Creates a quaternion given a yaw, pitch, and roll value (in radians).
// @param yaw The yaw of rotation (in radians)
// @param pitch The pitch of rotation (in radians)
// @param roll The roll of rotation (in radians)
// @param result When the method completes, contains the newly created quaternion
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result); static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result);
/// <summary> /// <summary>

View File

@@ -82,7 +82,7 @@ public:
Vector3 GetPoint(Real distance) const; Vector3 GetPoint(Real distance) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a point. /// Determines if there is an intersection between ray and a point.
/// </summary> /// </summary>
/// <param name="point">The point to test.</param> /// <param name="point">The point to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -92,7 +92,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="Ray" />. /// Determines if there is an intersection between two rays.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -103,7 +103,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="Ray" />. /// Determines if there is an intersection between ray and a <see cref="Ray" />.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection. /// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.
@@ -115,7 +115,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="Plane" />. /// Determines if there is an intersection between ray and a <see cref="Plane" />.
/// </summary> /// </summary>
/// <param name="plane">The plane to test</param> /// <param name="plane">The plane to test</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -126,7 +126,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="Plane" />. /// Determines if there is an intersection between ray and a <see cref="Plane" />.
/// </summary> /// </summary>
/// <param name="plane">The plane to test.</param> /// <param name="plane">The plane to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -137,7 +137,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="Plane" />. /// Determines if there is an intersection between ray and a <see cref="Plane" />.
/// </summary> /// </summary>
/// <param name="plane">The plane to test.</param> /// <param name="plane">The plane to test.</param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param> /// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>
@@ -148,7 +148,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a triangle. /// Determines if there is an intersection between ray and a triangle.
/// </summary> /// </summary>
/// <param name="vertex1">The first vertex of the triangle to test.</param> /// <param name="vertex1">The first vertex of the triangle to test.</param>
/// <param name="vertex2">The second vertex of the triangle to test.</param> /// <param name="vertex2">The second vertex of the triangle to test.</param>
@@ -161,7 +161,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a triangle. /// Determines if there is an intersection between ray and a triangle.
/// </summary> /// </summary>
/// <param name="vertex1">The first vertex of the triangle to test.</param> /// <param name="vertex1">The first vertex of the triangle to test.</param>
/// <param name="vertex2">The second vertex of the triangle to test.</param> /// <param name="vertex2">The second vertex of the triangle to test.</param>
@@ -174,7 +174,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a triangle. /// Determines if there is an intersection between ray and a triangle.
/// </summary> /// </summary>
/// <param name="vertex1">The first vertex of the triangle to test.</param> /// <param name="vertex1">The first vertex of the triangle to test.</param>
/// <param name="vertex2">The second vertex of the triangle to test.</param> /// <param name="vertex2">The second vertex of the triangle to test.</param>
@@ -187,7 +187,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="BoundingBox" />. /// Determines if there is an intersection between ray and a <see cref="BoundingBox" />.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -198,7 +198,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="BoundingBox" />. /// Determines if there is an intersection between ray and a <see cref="BoundingBox" />.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -209,7 +209,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="BoundingBox" />. /// Determines if there is an intersection between ray and a <see cref="BoundingBox" />.
/// </summary> /// </summary>
/// <param name="box">The box to test.</param> /// <param name="box">The box to test.</param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param> /// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>
@@ -220,7 +220,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="BoundingSphere" />. /// Determines if there is an intersection between ray and a <see cref="BoundingSphere" />.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <returns>Whether the two objects intersected.</returns> /// <returns>Whether the two objects intersected.</returns>
@@ -231,7 +231,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="BoundingSphere" />. /// Determines if there is an intersection between ray and a <see cref="BoundingSphere" />.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param> /// <param name="distance">When the method completes, contains the distance of the intersection, or 0 if there was no intersection.</param>
@@ -242,7 +242,7 @@ public:
} }
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a <see cref="BoundingSphere" />. /// Determines if there is an intersection between ray and a <see cref="BoundingSphere" />.
/// </summary> /// </summary>
/// <param name="sphere">The sphere to test.</param> /// <param name="sphere">The sphere to test.</param>
/// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param> /// <param name="point">When the method completes, contains the point of intersection, or <see cref="Vector3.Zero" /> if there was no intersection.</param>

View File

@@ -33,20 +33,24 @@ public:
/// </summary> /// </summary>
Rectangle() = default; Rectangle() = default;
// Init /// <summary>
// @param x Rectangle location X coordinate /// Initializes a new instance of the <see cref="Rectangle"/> struct.
// @param y Rectangle location Y coordinate /// </summary>
// @param width Rectangle width /// <param name="x">The X coordinate of the upper left corner.</param>
// @param height Rectangle height /// <param name="y">The Y coordinate of the upper left corner.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
Rectangle(float x, float y, float width, float height) Rectangle(float x, float y, float width, float height)
: Location(x, y) : Location(x, y)
, Size(width, height) , Size(width, height)
{ {
} }
// Init /// <summary>
// @param location Rectangle location point /// Initializes a new instance of the <see cref="Rectangle"/> struct.
// @param width Rectangle size /// </summary>
/// <param name="location">The location of the upper left corner.</param>
/// <param name="size">The size.</param>
Rectangle(const Float2& location, const Float2& size) Rectangle(const Float2& location, const Float2& size)
: Location(location) : Location(location)
, Size(size) , Size(size)
@@ -57,123 +61,114 @@ public:
String ToString() const; String ToString() const;
public: public:
// Returns width of the rectangle // Gets width of the rectangle.
float GetWidth() const float GetWidth() const
{ {
return Size.X; return Size.X;
} }
// Returns height of the rectangle // Gets height of the rectangle.
float GetHeight() const float GetHeight() const
{ {
return Size.Y; return Size.Y;
} }
// Gets Y coordinate of the top edge of the rectangle // Gets Y coordinate of the top edge of the rectangle.
float GetY() const float GetY() const
{ {
return Location.Y; return Location.Y;
} }
// Gets Y coordinate of the top edge of the rectangle // Gets Y coordinate of the top edge of the rectangle.
float GetTop() const float GetTop() const
{ {
return Location.Y; return Location.Y;
} }
// Gets Y coordinate of the bottom edge of the rectangle // Gets Y coordinate of the bottom edge of the rectangle.
float GetBottom() const float GetBottom() const
{ {
return Location.Y + Size.Y; return Location.Y + Size.Y;
} }
// Gets X coordinate of the left edge of the rectangle // Gets X coordinate of the left edge of the rectangle.
float GetX() const float GetX() const
{ {
return Location.X; return Location.X;
} }
// Gets X coordinate of the left edge of the rectangle // Gets X coordinate of the left edge of the rectangle.
float GetLeft() const float GetLeft() const
{ {
return Location.X; return Location.X;
} }
// Gets X coordinate of the right edge of the rectangle // Gets X coordinate of the right edge of the rectangle.
float GetRight() const float GetRight() const
{ {
return Location.X + Size.X; return Location.X + Size.X;
} }
// Gets position of the upper left corner of the rectangle // Gets position of the upper left corner of the rectangle.
Float2 GetUpperLeft() const Float2 GetUpperLeft() const
{ {
return Location; return Location;
} }
// Gets position of the upper right corner of the rectangle // Gets position of the upper right corner of the rectangle.
Float2 GetUpperRight() const Float2 GetUpperRight() const
{ {
return Location + Float2(Size.X, 0); return Location + Float2(Size.X, 0);
} }
// Gets position of the bottom right corner of the rectangle // Gets position of the bottom right corner of the rectangle.
Float2 GetLowerRight() const Float2 GetLowerRight() const
{ {
return Location + Size; return Location + Size;
} }
// Gets position of the bottom left corner of the rectangle // Gets position of the bottom left corner of the rectangle.
Float2 GetLowerLeft() const Float2 GetLowerLeft() const
{ {
return Location + Float2(0, Size.Y); return Location + Float2(0, Size.Y);
} }
// Gets position of the upper left corner of the rectangle // Gets position of the upper left corner of the rectangle.
Float2 GetTopLeft() const Float2 GetTopLeft() const
{ {
return Location; return Location;
} }
// Gets position of the upper right corner of the rectangle // Gets position of the upper right corner of the rectangle.
Float2 GetTopRight() const Float2 GetTopRight() const
{ {
return Location + Float2(Size.X, 0); return Location + Float2(Size.X, 0);
} }
// Gets position of the bottom right corner of the rectangle // Gets position of the bottom right corner of the rectangle.
Float2 GetBottomRight() const Float2 GetBottomRight() const
{ {
return Location + Size; return Location + Size;
} }
// Gets position of the bottom left corner of the rectangle // Gets position of the bottom left corner of the rectangle.
Float2 GetBottomLeft() const Float2 GetBottomLeft() const
{ {
return Location + Float2(0, Size.Y); return Location + Float2(0, Size.Y);
} }
/// <summary> // Gets center position of the rectangle.
/// Gets center position of the rectangle
/// </summary>
/// <returns>Center point</returns>
Float2 GetCenter() const Float2 GetCenter() const
{ {
return Location + Size * 0.5f; return Location + Size * 0.5f;
} }
public: public:
// Offset rectangle Location point
// @param v Offset to add
// @returns Result rectangle
Rectangle operator+(const Float2& v) const Rectangle operator+(const Float2& v) const
{ {
return Rectangle(Location + v, Size); return Rectangle(Location + v, Size);
} }
// Offset rectangle Location point
// @param v Offset to subtract
// @returns Result rectangle
Rectangle operator-(const Float2& v) const Rectangle operator-(const Float2& v) const
{ {
return Rectangle(Location - v, Size); return Rectangle(Location - v, Size);
@@ -236,81 +231,51 @@ public:
} }
public: public:
// Checks if rectangle contains given point // Checks if rectangle contains given point.
// @param location Point location to check
// @returns True if point is inside rectangle's area
bool Contains(const Float2& location) const; bool Contains(const Float2& location) const;
// Determines whether this rectangle entirely contains a specified rectangle // Determines whether this rectangle entirely contains a specified rectangle.
// @param value The rectangle to evaluate
// @returns True if this rectangle entirely contains the specified rectangle, or false if not
bool Contains(const Rectangle& value) const; bool Contains(const Rectangle& value) const;
// Determines whether a specified rectangle intersects with this rectangle // Determines whether a specified rectangle intersects with this rectangle.
// @ value The rectangle to evaluate
// @returns True if the specified rectangle intersects with this one, otherwise false
bool Intersects(const Rectangle& value) const; bool Intersects(const Rectangle& value) const;
public: public:
// Offset rectangle position // Offsets rectangle position.
// @param x X coordinate offset
// @param y Y coordinate offset
void Offset(float x, float y); void Offset(float x, float y);
// Offset rectangle position // Offsets rectangle position.
// @param offset X and Y coordinate offset
void Offset(const Float2& offset); void Offset(const Float2& offset);
// Make offseted rectangle // Make offseted rectangle.
// @param offset X and Y coordinate offset
// @returns Offseted rectangle
Rectangle MakeOffsetted(const Float2& offset) const; Rectangle MakeOffsetted(const Float2& offset) const;
public: // Expands rectangle area in all directions by given amount.
// Expand rectangle area in all directions by given amount
// @param toExpand Amount of units to expand a rectangle
void Expand(float toExpand); void Expand(float toExpand);
// Make expanded rectangle area in all directions by given amount // Makes expanded rectangle area in all directions by given amount.
// @param toExpand Amount of units to expand a rectangle
// @returns Expanded rectangle
Rectangle MakeExpanded(float toExpand) const; Rectangle MakeExpanded(float toExpand) const;
public: // Scale rectangle area in all directions by given amount.
// Scale rectangle area in all directions by given amount
// @param scale Scale value to expand a rectangle
void Scale(float scale); void Scale(float scale);
// Make scaled rectangle area in all directions by given amount // Makes scaled rectangle area in all directions by given amount.
// @param scale Scale value to expand a rectangle
// @returns Scaled rectangle
Rectangle MakeScaled(float scale) const; Rectangle MakeScaled(float scale) const;
public: public:
// Calculates a rectangle that contains the union of rectangle and the arbitrary point // Calculates a rectangle that contains the union of rectangle and the arbitrary point.
// @param a The rectangle
// @param b The point
// @returns Rectangle that contains both rectangle and the point
static Rectangle Union(const Rectangle& a, const Float2& b); static Rectangle Union(const Rectangle& a, const Float2& b);
// Calculates a rectangle that contains the union of a and b rectangles // Calculates a rectangle that contains the union of a and b rectangles.
// @param a First rectangle
// @param b Second rectangle
// @returns Rectangle that contains both a and b rectangles
static Rectangle Union(const Rectangle& a, const Rectangle& b); static Rectangle Union(const Rectangle& a, const Rectangle& b);
// Calculates a rectangle that contains the shared part of a and b rectangles // Calculates a rectangle that contains the shared part of a and b rectangles.
// @param a First rectangle
// @param b Second rectangle
// @returns Rectangle that contains shared part of a and b rectangles
static Rectangle Shared(const Rectangle& a, const Rectangle& b); static Rectangle Shared(const Rectangle& a, const Rectangle& b);
// Create rectangle from two points // Creates rectangle from two points.
// @param p1 First point
// @param p2 Second point
// @returns Rectangle that contains both p1 and p2
static Rectangle FromPoints(const Float2& p1, const Float2& p2); static Rectangle FromPoints(const Float2& p1, const Float2& p2);
// Creates rectangle from list of points.
static Rectangle FromPoints(const Float2* points, int32 pointsCount); static Rectangle FromPoints(const Float2* points, int32 pointsCount);
}; };

View File

@@ -6,7 +6,7 @@
#include "CollisionsHelper.h" #include "CollisionsHelper.h"
/// <summary> /// <summary>
/// Represents a three dimensional triangle. /// Represents a three-dimensional triangle.
/// </summary> /// </summary>
struct FLAXENGINE_API Triangle struct FLAXENGINE_API Triangle
{ {
@@ -52,38 +52,26 @@ public:
} }
public: public:
// Determines if there is an intersection between the current object and a Ray // Determines if there is an intersection between triangle and a ray.
// @param ray The ray to test
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray) const bool Intersects(const Ray& ray) const
{ {
Real distance; Real distance;
return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, distance); return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, distance);
} }
// Determines if there is an intersection between the current object and a Ray /// Determines if there is an intersection between triangle and a ray. Returns distance to the intersection.
// @param ray The ray to test
// @param distance When the method completes, contains the distance of the intersection, or 0 if there was no intersection
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray, Real& distance) const bool Intersects(const Ray& ray, Real& distance) const
{ {
return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, distance); return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, distance);
} }
// Determines if there is an intersection between the current object and a Ray // Determines if there is an intersection between triangle and a ray. Returns distance to the intersection and surface normal vector.
// @param ray The ray to test
// @param distance When the method completes, contains the distance of the intersection, or 0 if there was no intersection
// @param normal When the method completes, contains the intersection surface normal vector, or Vector3::Up if there was no intersection
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const bool Intersects(const Ray& ray, Real& distance, Vector3& normal) const
{ {
return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, distance, normal); return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, distance, normal);
} }
// Determines if there is an intersection between the current object and a Ray // Determines if there is an intersection between triangle and a ray. Returns point of intersection.
// @param ray The ray to test
// @param point When the method completes, contains the point of intersection, or <see cref="Vector3.Zero"/> if there was no intersection
// @returns Whether the two objects intersected
bool Intersects(const Ray& ray, Vector3& point) const bool Intersects(const Ray& ray, Vector3& point) const
{ {
return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, point); return CollisionsHelper::RayIntersectsTriangle(ray, V0, V1, V2, point);

View File

@@ -499,11 +499,7 @@ public:
} }
public: public:
// Clamp vector values within given range // Restricts a value to be within a specified range (inclusive min/max).
// @param v Vector to clamp
// @param min Minimum value
// @param max Maximum value
// @returns Clamped vector
static Vector2Base Clamp(const Vector2Base& v, const Vector2Base& min, const Vector2Base& max) static Vector2Base Clamp(const Vector2Base& v, const Vector2Base& min, const Vector2Base& max)
{ {
Vector2Base result; Vector2Base result;
@@ -511,20 +507,13 @@ public:
return result; return result;
} }
// Restricts a value to be within a specified range // Restricts a value to be within a specified range (inclusive min/max).
// @param v The value to clamp
// @param min The minimum value,
// @param max The maximum value
// @param result When the method completes, contains the clamped value
static void Clamp(const Vector2Base& v, const Vector2Base& min, const Vector2Base& max, Vector2Base& result) static void Clamp(const Vector2Base& v, const Vector2Base& min, const Vector2Base& max, Vector2Base& result)
{ {
result = Vector2Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y)); result = Vector2Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y));
} }
// Calculates distance between two points in 2D // Calculates distance between two points in 2D.
// @param a 1st point
// @param b 2nd point
// @returns Distance
static T Distance(const Vector2Base& a, const Vector2Base& b) static T Distance(const Vector2Base& a, const Vector2Base& b)
{ {
const T x = a.X - b.X; const T x = a.X - b.X;
@@ -532,10 +521,7 @@ public:
return Math::Sqrt(x * x + y * y); return Math::Sqrt(x * x + y * y);
} }
// Calculates the squared distance between two points in 2D // Calculates the squared distance between two points in 2D.
// @param a 1st point
// @param b 2nd point
// @returns Distance
static T DistanceSquared(const Vector2Base& a, const Vector2Base& b) static T DistanceSquared(const Vector2Base& a, const Vector2Base& b)
{ {
const T x = a.X - b.X; const T x = a.X - b.X;
@@ -557,24 +543,14 @@ public:
return r; return r;
} }
// Performs a linear interpolation between two vectors // Performs a linear interpolation between two vectors.
// @param start Start vector
// @param end End vector
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the linear interpolation of the two vectors
static void Lerp(const Vector2Base& start, const Vector2Base& end, T amount, Vector2Base& result) static void Lerp(const Vector2Base& start, const Vector2Base& end, T amount, Vector2Base& result)
{ {
result.X = Math::Lerp(start.X, end.X, amount); result.X = Math::Lerp(start.X, end.X, amount);
result.Y = Math::Lerp(start.Y, end.Y, amount); result.Y = Math::Lerp(start.Y, end.Y, amount);
} }
// <summary>
// Performs a linear interpolation between two vectors. // Performs a linear interpolation between two vectors.
// </summary>
// @param start Start vector,
// @param end End vector,
// @param amount Value between 0 and 1 indicating the weight of @paramref end"/>,
// @returns The linear interpolation of the two vectors
static Vector2Base Lerp(const Vector2Base& start, const Vector2Base& end, T amount) static Vector2Base Lerp(const Vector2Base& start, const Vector2Base& end, T amount)
{ {
Vector2Base result; Vector2Base result;

View File

@@ -542,11 +542,7 @@ public:
} }
public: public:
// Restricts a value to be within a specified range // Restricts a value to be within a specified range (inclusive min/max).
// @param v The value to clamp
// @param min The minimum value,
// @param max The maximum value
// @returns Clamped value
static Vector3Base Clamp(const Vector3Base& v, const Vector3Base& min, const Vector3Base& max) static Vector3Base Clamp(const Vector3Base& v, const Vector3Base& min, const Vector3Base& max)
{ {
Vector3Base result; Vector3Base result;
@@ -554,11 +550,7 @@ public:
return result; return result;
} }
// Restricts a value to be within a specified range // Restricts a value to be within a specified range (inclusive min/max).
// @param v The value to clamp
// @param min The minimum value,
// @param max The maximum value
// @param result When the method completes, contains the clamped value
static void Clamp(const Vector3Base& v, const Vector3Base& min, const Vector3Base& max, Vector3Base& result) static void Clamp(const Vector3Base& v, const Vector3Base& min, const Vector3Base& max, Vector3Base& result)
{ {
result = Vector3Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y), Math::Clamp(v.Z, min.Z, max.Z)); result = Vector3Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y), Math::Clamp(v.Z, min.Z, max.Z));
@@ -614,10 +606,7 @@ public:
} }
} }
// Calculates the distance between two vectors // Calculates the distance between two vectors.
// @param a The first vector
// @param b The second vector
// @returns The distance between the two vectors
static T Distance(const Vector3Base& a, const Vector3Base& b) static T Distance(const Vector3Base& a, const Vector3Base& b)
{ {
const T x = a.X - b.X; const T x = a.X - b.X;
@@ -626,10 +615,7 @@ public:
return Math::Sqrt(x * x + y * y + z * z); return Math::Sqrt(x * x + y * y + z * z);
} }
// Calculates the squared distance between two vectors // Calculates the squared distance between two vectors.
// @param a The first vector
// @param b The second vector
// @returns The squared distance between the two vectors
static T DistanceSquared(const Vector3Base& a, const Vector3Base& b) static T DistanceSquared(const Vector3Base& a, const Vector3Base& b)
{ {
const T x = a.X - b.X; const T x = a.X - b.X;
@@ -653,52 +639,38 @@ public:
return r; return r;
} }
// Performs vector normalization (scales vector up to unit length). This is a faster version that does not performs check for length equal 0 (it assumes that input vector is not empty). // Performs vector normalization (scales vector up to unit length). This is a faster version that does not perform check for length equal 0 (it assumes that input vector is not empty).
// @param inout Input vector to normalize (cannot be zero).
// @returns Output vector that is normalized (has unit length)
static Vector3Base NormalizeFast(const Vector3Base& v) static Vector3Base NormalizeFast(const Vector3Base& v)
{ {
const T inv = 1.0f / v.Length(); const T inv = 1.0f / v.Length();
return Vector3Base(v.X * inv, v.Y * inv, v.Z * inv); return Vector3Base(v.X * inv, v.Y * inv, v.Z * inv);
} }
// Performs vector normalization (scales vector up to unit length) // Performs vector normalization (scales vector up to unit length).
// @param inout Input vector to normalize
// @param output Output vector that is normalized (has unit length)
static FORCE_INLINE void Normalize(const Vector3Base& input, Vector3Base& result) static FORCE_INLINE void Normalize(const Vector3Base& input, Vector3Base& result)
{ {
result = Normalize(input); result = Normalize(input);
} }
// dot product with another vector // Calculates the dot product of two vectors.
FORCE_INLINE static T Dot(const Vector3Base& a, const Vector3Base& b) FORCE_INLINE static T Dot(const Vector3Base& a, const Vector3Base& b)
{ {
return a.X * b.X + a.Y * b.Y + a.Z * b.Z; return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
} }
// Calculates the cross product of two vectors // Calculates the cross product of two vectors.
// @param a First source vector
// @param b Second source vector
// @param result When the method completes, contains the cross product of the two vectors
static void Cross(const Vector3Base& a, const Vector3Base& b, Vector3Base& result) static void Cross(const Vector3Base& a, const Vector3Base& b, Vector3Base& result)
{ {
result = Vector3Base(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); result = Vector3Base(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
} }
// Calculates the cross product of two vectors // Calculates the cross product of two vectors.
// @param a First source vector
// @param b Second source vector
// @returns Cross product of the two vectors
static Vector3Base Cross(const Vector3Base& a, const Vector3Base& b) static Vector3Base Cross(const Vector3Base& a, const Vector3Base& b)
{ {
return Vector3Base(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X); return Vector3Base(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
} }
// Performs a linear interpolation between two vectors // Performs a linear interpolation between two vectors.
// @param start Start vector
// @param end End vector
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the linear interpolation of the two vectors
static void Lerp(const Vector3Base& start, const Vector3Base& end, T amount, Vector3Base& result) static void Lerp(const Vector3Base& start, const Vector3Base& end, T amount, Vector3Base& result)
{ {
result.X = Math::Lerp(start.X, end.X, amount); result.X = Math::Lerp(start.X, end.X, amount);
@@ -706,9 +678,7 @@ public:
result.Z = Math::Lerp(start.Z, end.Z, amount); result.Z = Math::Lerp(start.Z, end.Z, amount);
} }
// <summary>
// Performs a linear interpolation between two vectors. // Performs a linear interpolation between two vectors.
// </summary>
static Vector3Base Lerp(const Vector3Base& start, const Vector3Base& end, T amount) static Vector3Base Lerp(const Vector3Base& start, const Vector3Base& end, T amount)
{ {
Vector3Base result; Vector3Base result;
@@ -716,11 +686,7 @@ public:
return result; return result;
} }
// Performs a cubic interpolation between two vectors // Performs a cubic interpolation between two vectors.
// @param start Start vector
// @param end End vector
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the cubic interpolation of the two vectors
static void SmoothStep(const Vector3Base& start, const Vector3Base& end, T amount, Vector3Base& result) static void SmoothStep(const Vector3Base& start, const Vector3Base& end, T amount, Vector3Base& result)
{ {
amount = Math::SmoothStep(amount); amount = Math::SmoothStep(amount);
@@ -745,78 +711,39 @@ public:
} }
// Performs a Hermite spline interpolation. // Performs a Hermite spline interpolation.
// @param value1 First source position vector
// @param tangent1 First source tangent vector
// @param value2 Second source position vector
// @param tangent2 Second source tangent vector
// @param amount Weighting factor,
// @param result When the method completes, contains the result of the Hermite spline interpolation,
static FLAXENGINE_API void Hermite(const Vector3Base& value1, const Vector3Base& tangent1, const Vector3Base& value2, const Vector3Base& tangent2, T amount, Vector3Base& result); static FLAXENGINE_API void Hermite(const Vector3Base& value1, const Vector3Base& tangent1, const Vector3Base& value2, const Vector3Base& tangent2, T amount, Vector3Base& result);
// Returns the reflection of a vector off a surface that has the specified normal // Returns the reflection of a vector off a surface that has the specified normal.
// @param vector The source vector
// @param normal Normal of the surface
// @param result When the method completes, contains the reflected vector
static FLAXENGINE_API void Reflect(const Vector3Base& vector, const Vector3Base& normal, Vector3Base& result); static FLAXENGINE_API void Reflect(const Vector3Base& vector, const Vector3Base& normal, Vector3Base& result);
// Transforms a 3D vector by the given Quaternion rotation // Transforms a 3D vector by the given Quaternion rotation.
// @param vector The vector to rotate
// @param rotation The Quaternion rotation to apply
// @param result When the method completes, contains the transformed Vector3
static FLAXENGINE_API void Transform(const Vector3Base& vector, const Quaternion& rotation, Vector3Base& result); static FLAXENGINE_API void Transform(const Vector3Base& vector, const Quaternion& rotation, Vector3Base& result);
// Transforms a 3D vector by the given Quaternion rotation // Transforms a 3D vector by the given Quaternion rotation.
// @param vector The vector to rotate
// @param rotation The Quaternion rotation to apply
// @returns The transformed Vector3
static FLAXENGINE_API Vector3Base Transform(const Vector3Base& vector, const Quaternion& rotation); static FLAXENGINE_API Vector3Base Transform(const Vector3Base& vector, const Quaternion& rotation);
// Transforms a 3D vector by the given matrix // Transforms a 3D vector by the given matrix.
// @param vector The source vector
// @param transform The transformation matrix
// @param result When the method completes, contains the transformed Vector3
static FLAXENGINE_API void Transform(const Vector3Base& vector, const Matrix& transform, Vector3Base& result); static FLAXENGINE_API void Transform(const Vector3Base& vector, const Matrix& transform, Vector3Base& result);
// Transforms a 3D vector by the given matrix // Transforms a 3D vector by the given matrix.
// @param vector The source vector
// @param transform The transformation matrix
// @param result When the method completes, contains the transformed Vector3
static FLAXENGINE_API void Transform(const Vector3Base& vector, const Matrix3x3& transform, Vector3Base& result); static FLAXENGINE_API void Transform(const Vector3Base& vector, const Matrix3x3& transform, Vector3Base& result);
// Transforms a 3D vector by the given transformation // Transforms a 3D vector by the given transformation.
// @param vector The source vector
// @param transform The transformation
// @param result When the method completes, contains the transformed Vector3
static FLAXENGINE_API void Transform(const Vector3Base& vector, const ::Transform& transform, Vector3Base& result); static FLAXENGINE_API void Transform(const Vector3Base& vector, const ::Transform& transform, Vector3Base& result);
// Transforms a 3D vector by the given matrix // Transforms a 3D vector by the given matrix.
// @param vector The source vector
// @param transform The transformation matrix
// @returns Transformed Vector3
static FLAXENGINE_API Vector3Base Transform(const Vector3Base& vector, const Matrix& transform); static FLAXENGINE_API Vector3Base Transform(const Vector3Base& vector, const Matrix& transform);
// Transforms a 3D vector by the given transformation // Transforms a 3D vector by the given transformation.
// @param vector The source vector
// @param transform The transformation
// @returns Transformed Vector3
static FLAXENGINE_API Vector3Base Transform(const Vector3Base& vector, const ::Transform& transform); static FLAXENGINE_API Vector3Base Transform(const Vector3Base& vector, const ::Transform& transform);
// Transforms a 3D vector by the given matrix // Transforms a 3D vector by the given matrix.
// @param vector The source vector
// @param transform The transformation matrix
// @param result When the method completes, contains the transformed Vector4
static FLAXENGINE_API void Transform(const Vector3Base& vector, const Matrix& transform, Vector4Base<T>& result); static FLAXENGINE_API void Transform(const Vector3Base& vector, const Matrix& transform, Vector4Base<T>& result);
// Performs a coordinate transformation using the given matrix // Performs a coordinate transformation using the given matrix.
// @param coordinate The coordinate vector to transform
// @param transform The transformation matrix
// @param result When the method completes, contains the transformed coordinates
static FLAXENGINE_API void TransformCoordinate(const Vector3Base& coordinate, const Matrix& transform, Vector3Base& result); static FLAXENGINE_API void TransformCoordinate(const Vector3Base& coordinate, const Matrix& transform, Vector3Base& result);
// Performs a normal transformation using the given matrix // Performs a normal transformation using the given matrix.
// @param normal The normal vector to transform
// @param transform The transformation matrix
// @param result When the method completes, contains the transformed normal
static FLAXENGINE_API void TransformNormal(const Vector3Base& normal, const Matrix& transform, Vector3Base& result); static FLAXENGINE_API void TransformNormal(const Vector3Base& normal, const Matrix& transform, Vector3Base& result);
/// <summary> /// <summary>
@@ -838,28 +765,10 @@ public:
return vector - Project(vector, planeNormal); return vector - Project(vector, planeNormal);
} }
// Projects a 3D vector from object space into screen space // Projects a 3D vector from object space into screen space using the provided viewport and transformation matrix.
// @param vector The vector to project
// @param x The X position of the viewport
// @param y The Y position of the viewport
// @param width The width of the viewport
// @param height The height of the viewport
// @param minZ The minimum depth of the viewport
// @param maxZ The maximum depth of the viewport
// @param worldViewProjection The combined world-view-projection matrix
// @param result When the method completes, contains the vector in screen space
static FLAXENGINE_API void Project(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection, Vector3Base& result); static FLAXENGINE_API void Project(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection, Vector3Base& result);
// Projects a 3D vector from object space into screen space // Projects a 3D vector from object space into screen space using the provided viewport and transformation matrix.
// @param vector The vector to project
// @param x The X position of the viewport
// @param y The Y position of the viewport
// @param width The width of the viewport
// @param height The height of the viewport
// @param minZ The minimum depth of the viewport
// @param maxZ The maximum depth of the viewport
// @param worldViewProjection The combined world-view-projection matrix
// @returns The vector in screen space
static Vector3Base Project(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection) static Vector3Base Project(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection)
{ {
Vector3Base result; Vector3Base result;
@@ -867,28 +776,10 @@ public:
return result; return result;
} }
// Projects a 3D vector from screen space into object space // Projects a 3D vector from screen space into object space using the provided viewport and transformation matrix.
// @param vector The vector to project
// @param x The X position of the viewport
// @param y The Y position of the viewport
// @param width The width of the viewport
// @param height The height of the viewport
// @param minZ The minimum depth of the viewport
// @param maxZ The maximum depth of the viewport
// @param worldViewProjection The combined world-view-projection matrix
// @param result When the method completes, contains the vector in object space
static FLAXENGINE_API void Unproject(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection, Vector3Base& result); static FLAXENGINE_API void Unproject(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection, Vector3Base& result);
// Projects a 3D vector from screen space into object space // Projects a 3D vector from screen space into object space using the provided viewport and transformation matrix.
// @param vector The vector to project
// @param x The X position of the viewport
// @param y The Y position of the viewport
// @param width The width of the viewport
// @param height The height of the viewport
// @param minZ The minimum depth of the viewport
// @param maxZ The maximum depth of the viewport
// @param worldViewProjection The combined world-view-projection matrix
// @returns The vector in object space
static Vector3Base Unproject(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection) static Vector3Base Unproject(const Vector3Base& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix& worldViewProjection)
{ {
Vector3Base result; Vector3Base result;

View File

@@ -455,11 +455,7 @@ public:
} }
public: public:
// Restricts a value to be within a specified range // Restricts a value to be within a specified range (inclusive min/max).
// @param v The value to clamp
// @param min The minimum value,
// @param max The maximum value
// @returns Clamped value
static Vector4Base Clamp(const Vector4Base& v, const Vector4Base& min, const Vector4Base& max) static Vector4Base Clamp(const Vector4Base& v, const Vector4Base& min, const Vector4Base& max)
{ {
Vector4Base result; Vector4Base result;
@@ -467,21 +463,13 @@ public:
return result; return result;
} }
// Restricts a value to be within a specified range // Restricts a value to be within a specified range (inclusive min/max).
// @param v The value to clamp
// @param min The minimum value,
// @param max The maximum value
// @param result When the method completes, contains the clamped value
static void Clamp(const Vector4Base& v, const Vector4Base& min, const Vector4Base& max, Vector4Base& result) static void Clamp(const Vector4Base& v, const Vector4Base& min, const Vector4Base& max, Vector4Base& result)
{ {
result = Vector4Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y), Math::Clamp(v.Z, min.Z, max.Z), Math::Clamp(v.W, min.W, max.W)); result = Vector4Base(Math::Clamp(v.X, min.X, max.X), Math::Clamp(v.Y, min.Y, max.Y), Math::Clamp(v.Z, min.Z, max.Z), Math::Clamp(v.W, min.W, max.W));
} }
// Performs a linear interpolation between two vectors // Performs a linear interpolation between two vectors.
// @param start Start vector
// @param end End vector
// @param amount Value between 0 and 1 indicating the weight of end
// @param result When the method completes, contains the linear interpolation of the two vectors
static void Lerp(const Vector4Base& start, const Vector4Base& end, T amount, Vector4Base& result) static void Lerp(const Vector4Base& start, const Vector4Base& end, T amount, Vector4Base& result)
{ {
result.X = Math::Lerp(start.X, end.X, amount); result.X = Math::Lerp(start.X, end.X, amount);
@@ -490,13 +478,7 @@ public:
result.W = Math::Lerp(start.W, end.W, amount); result.W = Math::Lerp(start.W, end.W, amount);
} }
// <summary>
// Performs a linear interpolation between two vectors. // Performs a linear interpolation between two vectors.
// </summary>
// @param start Start vector,
// @param end End vector,
// @param amount Value between 0 and 1 indicating the weight of @paramref end"/>,
// @returns The linear interpolation of the two vectors
static Vector4Base Lerp(const Vector4Base& start, const Vector4Base& end, T amount) static Vector4Base Lerp(const Vector4Base& start, const Vector4Base& end, T amount)
{ {
Vector4Base result; Vector4Base result;
@@ -558,7 +540,7 @@ inline Vector4Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector4Ba
template<typename T> template<typename T>
inline uint32 GetHash(const Vector4Base<T>& key) inline uint32 GetHash(const Vector4Base<T>& key)
{ {
return (((((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z) * 397) ^*(uint32*)&key.W; return (((((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z) * 397) ^ *(uint32*)&key.W;
} }
namespace Math namespace Math

View File

@@ -8,7 +8,9 @@
struct Matrix; struct Matrix;
struct Rectangle; struct Rectangle;
// Describes the viewport dimensions. /// <summary>
/// Describes the viewport dimensions.
/// </summary>
API_STRUCT(InBuild) struct FLAXENGINE_API Viewport API_STRUCT(InBuild) struct FLAXENGINE_API Viewport
{ {
public: public:
@@ -54,29 +56,16 @@ public:
/// </summary> /// </summary>
Viewport() = default; Viewport() = default;
// Init /// <summary>
// @param x The x coordinate of the upper-left corner of the viewport in pixels /// Initializes a new instance of the <see cref="Viewport"/> struct.
// @param y The y coordinate of the upper-left corner of the viewport in pixels /// </summary>
// @param width The width of the viewport in pixels /// <param name="x">The x coordinate of the upper-left corner of the viewport in pixels.</param>
// @param height The height of the viewport in pixels /// <param name="y">The y coordinate of the upper-left corner of the viewport in pixels.</param>
Viewport(float x, float y, float width, float height) /// <param name="width">The width of the viewport in pixels.</param>
: X(x) /// <param name="height">The height of the viewport in pixels.</param>
, Y(y) /// <param name="minDepth">The minimum depth of the clip volume.</param>
, Width(width) /// <param name="maxDepth">The maximum depth of the clip volumes.</param>
, Height(height) Viewport(float x, float y, float width, float height, float minDepth = 0.0f, float maxDepth = 1.0f)
, MinDepth(0.0f)
, MaxDepth(1.0f)
{
}
// Init
// @param x The x coordinate of the upper-left corner of the viewport in pixels
// @param y The y coordinate of the upper-left corner of the viewport in pixels
// @param width The width of the viewport in pixels
// @param height The height of the viewport in pixels
// @param minDepth The minimum depth of the clip volume
// @param maxDepth The maximum depth of the clip volume
Viewport(float x, float y, float width, float height, float minDepth, float maxDepth)
: X(x) : X(x)
, Y(y) , Y(y)
, Width(width) , Width(width)
@@ -100,31 +89,26 @@ public:
{ {
} }
// Init /// <summary>
// @param bounds A bounding box that defines the location and size of the viewport in a render target /// Initializes a new instance of the <see cref="Viewport"/> struct.
/// </summary>
/// <param name="bounds">A bounding rectangle that defines the location and size of the viewport in a render target.</param>
Viewport(const Rectangle& bounds); Viewport(const Rectangle& bounds);
public: public:
String ToString() const; String ToString() const;
public: public:
// Gets the aspect ratio used by the viewport // Gets the aspect ratio used by the viewport.
// @returns The aspect ratio
float GetAspectRatio() const float GetAspectRatio() const
{ {
if (Height != 0.0f) return Height != 0.0f ? Width / Height : 0.0f;
{
return Width / Height;
}
return 0.0f;
} }
// Gets the size of the viewport // Gets the size of the viewport.
// @eturns The bounds
Rectangle GetBounds() const; Rectangle GetBounds() const;
// Sets the size of the viewport // Sets the size of the viewport.
// @param bounds The bounds
void SetBounds(const Rectangle& bounds); void SetBounds(const Rectangle& bounds);
public: public:
@@ -139,16 +123,20 @@ public:
} }
public: public:
// Projects a 3D vector from object space into screen space /// <summary>
// @param source The vector to project /// Projects a 3D vector from object space into screen space.
// @param vp A combined WorldViewProjection matrix /// </summary>
// @param vector The projected vector /// <param name="source">The vector to project.</param>
/// <param name="vp">A combined World*View*Projection matrix.</param>
/// <param name="result">The projected vector.</param>
void Project(const Vector3& source, const Matrix& vp, Vector3& result) const; void Project(const Vector3& source, const Matrix& vp, Vector3& result) const;
// Converts a screen space point into a corresponding point in world space /// <summary>
// @param source The vector to project /// Converts a screen space point into a corresponding point in world space.
// @param vp An inverted combined WorldViewProjection matrix /// </summary>
// @param vector The unprojected vector /// <param name="source">The vector to un-project.</param>
/// <param name="ivp">An inverted combined World*View*Projection matrix.</param>
/// <param name="result">The un-projected vector</param>
void Unproject(const Vector3& source, const Matrix& ivp, Vector3& result) const; void Unproject(const Vector3& source, const Matrix& ivp, Vector3& result) const;
}; };

View File

@@ -7,16 +7,14 @@
#include "Engine/Core/Formatting.h" #include "Engine/Core/Formatting.h"
#include "Engine/Core/Templates.h" #include "Engine/Core/Templates.h"
class String;
/// <summary> /// <summary>
/// Globally Unique Identifier /// Globally Unique Identifier (GUID) represented by 128-bit integer (16 bytes) that can be used across all computers and networks wherever a unique identifier is required. Such an identifier has a very low probability of being duplicated.
/// </summary> /// </summary>
API_STRUCT(InBuild, Namespace="System") struct FLAXENGINE_API Guid API_STRUCT(InBuild, Namespace="System") struct FLAXENGINE_API Guid
{ {
public: public:
/// <summary> /// <summary>
/// Accepted format specifiers for the format parameter /// Accepted format specifiers for the format parameter.
/// </summary> /// </summary>
enum class FormatType enum class FormatType
{ {
@@ -42,28 +40,28 @@ public:
{ {
struct struct
{ {
// The first component // The first component.
uint32 A; uint32 A;
// The second component // The second component.
uint32 B; uint32 B;
// The third component // The third component.
uint32 C; uint32 C;
// The fourth component // The fourth component.
uint32 D; uint32 D;
}; };
// Raw bytes with the GUID // Raw bytes with the Guid.
byte Raw[16]; byte Raw[16];
// Raw values with the GUID // Raw values with the Guid.
uint32 Values[4]; uint32 Values[4];
}; };
public: public:
// Empty Guid (considered as invalid ID) // Empty Guid (considered as invalid ID).
static Guid Empty; static Guid Empty;
public: public:
@@ -74,11 +72,13 @@ public:
{ {
} }
// Creates and initializes a new Guid from the specified components /// <summary>
// @param a The first component /// Initializes a new instance of the <see cref="Guid"/> struct.
// @param b The second component /// </summary>
// @param c The third component /// <param name="a">The first component.</param>
// @param d The fourth component /// <param name="b">The second component.</param>
/// <param name="c">The third component.</param>
/// <param name="d">The fourth component.</param>
Guid(uint32 a, uint32 b, uint32 c, uint32 d) Guid(uint32 a, uint32 b, uint32 c, uint32 d)
: A(a) : A(a)
, B(b) , B(b)
@@ -98,41 +98,27 @@ public:
return ((A ^ other.A) | (B ^ other.B) | (C ^ other.C) | (D ^ other.D)) != 0; return ((A ^ other.A) | (B ^ other.B) | (C ^ other.C) | (D ^ other.D)) != 0;
} }
// Provides access to the GUIDs components // Provides access to the Guid components (0...3).
// @param index The index of the component to return (0...3)
// @returns The component value
uint32& operator[](int32 index) uint32& operator[](int32 index)
{ {
ASSERT(index >= 0 && index < 4); ASSERT(index >= 0 && index < 4);
return Values[index]; return Values[index];
} }
// Provides read-only access to the GUIDs components. // Provides read-only access to the Guid components (0...3).
// @param index The index of the component to return (0...3).
// @return The component
const uint32& operator[](int index) const const uint32& operator[](int index) const
{ {
ASSERT(index >= 0 && index < 4); ASSERT(index >= 0 && index < 4);
return Values[index]; return Values[index];
} }
// Invalidates the Guid
FORCE_INLINE void Invalidate()
{
A = B = C = D = 0;
}
// Checks whether this Guid is valid or not. A Guid that has all its components set to zero is considered invalid. // Checks whether this Guid is valid or not. A Guid that has all its components set to zero is considered invalid.
// @return true if valid, otherwise false
FORCE_INLINE bool IsValid() const FORCE_INLINE bool IsValid() const
{ {
return (A | B | C | D) != 0; return (A | B | C | D) != 0;
} }
/// <summary> /// Checks if Guid is valid.
/// Checks if Guid is valid
/// </summary>
/// <returns>True if Guid isn't empty</returns>
explicit operator bool() const explicit operator bool() const
{ {
return (A | B | C | D) != 0; return (A | B | C | D) != 0;

View File

@@ -26,7 +26,7 @@ public:
} }
/// <summary> /// <summary>
/// Create string builder with initial capacity /// Create string builder with initial capacity.
/// </summary> /// </summary>
/// <param name="capacity">Initial capacity for chars count</param> /// <param name="capacity">Initial capacity for chars count</param>
StringBuilder(int32 capacity) StringBuilder(int32 capacity)
@@ -36,16 +36,15 @@ public:
public: public:
/// <summary> /// <summary>
/// Gets capacity /// Gets the buffer capacity.
/// </summary> /// </summary>
/// <returns>Capacity of the string builder</returns>
FORCE_INLINE int32 Capacity() const FORCE_INLINE int32 Capacity() const
{ {
return _data.Capacity(); return _data.Capacity();
} }
/// <summary> /// <summary>
/// Sets capacity. /// Sets the buffer capacity.
/// </summary> /// </summary>
/// <param name="capacity">Capacity to set</param> /// <param name="capacity">Capacity to set</param>
FORCE_INLINE void SetCapacity(const int32 capacity) FORCE_INLINE void SetCapacity(const int32 capacity)
@@ -71,7 +70,7 @@ public:
} }
/// <summary> /// <summary>
/// Gets string /// Gets the string.
/// </summary> /// </summary>
/// <param name="result">String</param> /// <param name="result">String</param>
void ToString(String& result) const void ToString(String& result) const
@@ -80,27 +79,18 @@ public:
} }
public: public:
// Append single character to the string
// @param c Character to append
// @return Current String Builder instance
StringBuilder& Append(const Char c) StringBuilder& Append(const Char c)
{ {
_data.Add(c); _data.Add(c);
return *this; return *this;
} }
// Append single character to the string
// @param c Character to append
// @return Current String Builder instance
StringBuilder& Append(const char c) StringBuilder& Append(const char c)
{ {
_data.Add(c); _data.Add(c);
return *this; return *this;
} }
// Append characters sequence to the string
// @param str String to append
// @return Current String Builder instance
StringBuilder& Append(const Char* str) StringBuilder& Append(const Char* str)
{ {
const int32 length = StringUtils::Length(str); const int32 length = StringUtils::Length(str);
@@ -108,19 +98,12 @@ public:
return *this; return *this;
} }
// Append characters sequence to the string
// @param str String to append
// @param length String length
// @return Current String Builder instance
StringBuilder& Append(const Char* str, int32 length) StringBuilder& Append(const Char* str, int32 length)
{ {
_data.Add(str, length); _data.Add(str, length);
return *this; return *this;
} }
// Append characters sequence to the string
// @param str String to append
// @return Current String Builder instance
StringBuilder& Append(const char* str) StringBuilder& Append(const char* str)
{ {
const int32 length = str && *str ? StringUtils::Length(str) : 0; const int32 length = str && *str ? StringUtils::Length(str) : 0;
@@ -131,23 +114,18 @@ public:
return *this; return *this;
} }
// Append String to the string
// @param str String to append
// @return Current String Builder instance
StringBuilder& Append(const String& str) StringBuilder& Append(const String& str)
{ {
_data.Add(*str, str.Length()); _data.Add(*str, str.Length());
return *this; return *this;
} }
StringBuilder& Append(const StringView& str) StringBuilder& Append(const StringView& str)
{ {
_data.Add(*str, str.Length()); _data.Add(*str, str.Length());
return *this; return *this;
} }
// Append int to the string
// @param val Value to append
// @return Current String Builder instance
StringBuilder& Append(int32 val) StringBuilder& Append(int32 val)
{ {
auto str = StringUtils::ToString(val); auto str = StringUtils::ToString(val);
@@ -155,9 +133,6 @@ public:
return *this; return *this;
} }
// Append int to the string
// @param val Value to append
// @return Current String Builder instance
StringBuilder& Append(uint32 val) StringBuilder& Append(uint32 val)
{ {
auto str = StringUtils::ToString(val); auto str = StringUtils::ToString(val);
@@ -165,11 +140,8 @@ public:
return *this; return *this;
} }
// Append formatted message to the string
// @param format Format string
// @param args Array with custom arguments for the message
template<typename... Args> template<typename... Args>
StringBuilder& AppendFormat(const Char* format, const Args& ... args) StringBuilder& AppendFormat(const Char* format, const Args&... args)
{ {
fmt_flax::allocator allocator; fmt_flax::allocator allocator;
fmt_flax::memory_buffer buffer(allocator); fmt_flax::memory_buffer buffer(allocator);
@@ -177,16 +149,12 @@ public:
return Append(buffer.data(), (int32)buffer.size()); return Append(buffer.data(), (int32)buffer.size());
} }
public:
StringBuilder& AppendLine() StringBuilder& AppendLine()
{ {
Append(TEXT(PLATFORM_LINE_TERMINATOR)); Append(TEXT(PLATFORM_LINE_TERMINATOR));
return *this; return *this;
} }
// Append int to the string
// @param val Value to append
// @return Current String Builder instance
StringBuilder& AppendLine(int32 val) StringBuilder& AppendLine(int32 val)
{ {
Append(val); Append(val);
@@ -194,9 +162,6 @@ public:
return *this; return *this;
} }
// Append int to the string
// @param val Value to append
// @return Current String Builder instance
StringBuilder& AppendLine(uint32 val) StringBuilder& AppendLine(uint32 val)
{ {
Append(val); Append(val);
@@ -204,9 +169,6 @@ public:
return *this; return *this;
} }
// Append String to the string
// @param str String to append
// @return Current String Builder instance
StringBuilder& AppendLine(const Char* str) StringBuilder& AppendLine(const Char* str)
{ {
const int32 length = StringUtils::Length(str); const int32 length = StringUtils::Length(str);
@@ -215,9 +177,6 @@ public:
return *this; return *this;
} }
// Append String to the string
// @param str String to append
// @return Current String Builder instance
StringBuilder& AppendLine(const String& str) StringBuilder& AppendLine(const String& str)
{ {
_data.Add(*str, str.Length()); _data.Add(*str, str.Length());
@@ -226,38 +185,25 @@ public:
} }
public: public:
// Retrieves substring created from characters starting from startIndex // Gets pointer to the string.
// @param startIndex Index of the first character to subtract
// @param count Amount of characters to retrieve
// @return Substring created from StringBuilder data
String Substring(int32 startIndex, int32 count) const
{
ASSERT(startIndex >= 0 && startIndex + count <= _data.Count() && count > 0);
return String(_data.Get() + startIndex, count);
}
public:
// Get pointer to the string
// @returns Pointer to Array of Chars if Num, otherwise the empty string
FORCE_INLINE const Char* operator*() const FORCE_INLINE const Char* operator*() const
{ {
return _data.Count() > 0 ? _data.Get() : TEXT(""); return _data.Count() > 0 ? _data.Get() : TEXT("");
} }
// Get pointer to the string // Gets pointer to the string.
// @returns Pointer to Array of Chars if Num, otherwise the empty string
FORCE_INLINE Char* operator*() FORCE_INLINE Char* operator*()
{ {
return _data.Count() > 0 ? _data.Get() : (Char*)TEXT(""); return _data.Count() > 0 ? _data.Get() : (Char*)TEXT("");
} }
// Get string as array of Chars // Gets string buffer as array of Chars.
FORCE_INLINE Array<Char>& GetCharArray() FORCE_INLINE Array<Char>& GetCharArray()
{ {
return _data; return _data;
} }
// Get string as const array of Chars // Gets string buffer as const array of Chars.
FORCE_INLINE const Array<Char>& GetCharArray() const FORCE_INLINE const Array<Char>& GetCharArray() const
{ {
return _data; return _data;

View File

@@ -38,11 +38,14 @@ namespace Utilities
return (T)round((double)value * 1000.0) / (T)1000; return (T)round((double)value * 1000.0) / (T)1000;
} }
// Converts units to the best fitting human-readable denominator /// <summary>
// @param units Units count /// Converts units to the best fitting human-readable denominator.
// @param divider Amount of units required for the next size /// </summary>
// @param sizes Array with human-readable sizes to convert from /// <typeparam name="T">Value type.</typeparam>
// @return The best fitting string of the units /// <param name="units">Units count</param>
/// <param name="divider">Amount of units required for the next size.</param>
/// <param name="sizes">Array with human-readable sizes to convert from.</param>
/// <returns>The best fitting string of the units.</returns>
template<typename T> template<typename T>
String UnitsToText(T units, int32 divider, const Span<const Char*> sizes) String UnitsToText(T units, int32 divider, const Span<const Char*> sizes)
{ {
@@ -61,18 +64,24 @@ namespace Utilities
return String::Format(TEXT("{0} {1}"), text, sizes[i]); return String::Format(TEXT("{0} {1}"), text, sizes[i]);
} }
// Converts size of the file (in bytes) to the best fitting string /// <summary>
// @param bytes Size of the file in bytes /// Converts size of the data (in bytes) to the best fitting string.
// @return The best fitting string of the file size /// </summary>
/// <typeparam name="T">Value type.</typeparam>
/// <param name="bytes">Size of the data in bytes.</param>
/// <returns>The best fitting string of the data size.</returns>
template<typename T> template<typename T>
String BytesToText(T bytes) String BytesToText(T bytes)
{ {
return UnitsToText(bytes, 1024, Private::BytesSizes); return UnitsToText(bytes, 1024, Private::BytesSizes);
} }
// Converts hertz to the best fitting string /// <summary>
// @param hertz Hertz for convertion /// Converts hertz to the best fitting string.
// @return The best fitting string /// </summary>
/// <typeparam name="T">Value type.</typeparam>
/// <param name="hertz">Value in hertz for conversion.</param>
/// <returns>The best fitting string.</returns>
template<typename T> template<typename T>
String HertzToText(T hertz) String HertzToText(T hertz)
{ {

View File

@@ -15,8 +15,8 @@ namespace FlaxEngine.Interop
#if FLAX_EDITOR #if FLAX_EDITOR
[HideInEditor] [HideInEditor]
#endif #endif
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNativeState))]
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNativeState))]
[CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))]
[CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedHandleMarshaller.NativeToManaged))] [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedHandleMarshaller.NativeToManaged))]
[CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(ManagedHandleMarshaller.NativeToManaged))] [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(ManagedHandleMarshaller.NativeToManaged))]
@@ -31,7 +31,20 @@ namespace FlaxEngine.Interop
#endif #endif
public static class NativeToManaged public static class NativeToManaged
{ {
public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target; public static object ConvertToManaged(IntPtr unmanaged)
{
if (unmanaged == IntPtr.Zero)
return null;
object managed = ManagedHandle.FromIntPtr(unmanaged).Target;
if (managed is ManagedArray managedArray)
{
var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal);
managed = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType);
managedArrayHandle.Free();
}
return managed;
}
public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero; public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
@@ -40,6 +53,52 @@ namespace FlaxEngine.Interop
} }
} }
#if FLAX_EDITOR
[HideInEditor]
#endif
public struct ManagedToNativeState
{
ManagedArray managedArray;
IntPtr handle;
public void FromManaged(object managed)
{
if (managed == null)
return;
if (managed is Array arr)
{
var type = managed.GetType();
var elementType = type.GetElementType();
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
{
// Use pooled managed array wrapper to be passed around as handle to it
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr);
handle = ManagedHandle.ToIntPtr(tmp);
}
else
{
// Convert array contents to be properly accessed by the native code (as GCHandles array)
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray));
managedArray = null; // It's not pooled
}
}
else
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
}
public IntPtr ToUnmanaged()
{
return handle;
}
public void Free()
{
managedArray?.FreePooled();
}
}
#if FLAX_EDITOR #if FLAX_EDITOR
[HideInEditor] [HideInEditor]
#endif #endif
@@ -50,12 +109,6 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
// This is a weak handle, no need to free it
/*
if (unmanaged == IntPtr.Zero)
return;
ManagedHandle.FromIntPtr(unmanaged).Free();
*/
} }
} }
@@ -342,6 +395,7 @@ namespace FlaxEngine.Interop
{ {
public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged); public static Dictionary<T, U> ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller<T, U>.ToManaged(unmanaged);
public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak); public static IntPtr ConvertToUnmanaged(Dictionary<T, U> managed) => DictionaryMarshaller<T, U>.ToNative(managed, GCHandleType.Weak);
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles //DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
@@ -614,6 +668,7 @@ namespace FlaxEngine.Interop
{ {
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
//ManagedString.Free(unmanaged); // No need to free weak handles //ManagedString.Free(unmanaged); // No need to free weak handles

View File

@@ -600,31 +600,19 @@ void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Flo
resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias
} }
int32 MipLevelsCount(int32 width, bool useMipLevels) int32 MipLevelsCount(int32 width)
{ {
if (!useMipLevels)
return 1;
int32 result = 1; int32 result = 1;
while (width > 1) while (width > 1)
{ {
width >>= 1; width >>= 1;
result++; result++;
} }
return result; return result;
} }
int32 MipLevelsCount(int32 width, int32 height, bool useMipLevels) int32 MipLevelsCount(int32 width, int32 height)
{ {
// Check if use mip maps
if (!useMipLevels)
{
// No mipmaps chain, only single mip map
return 1;
}
// Count mip maps
int32 result = 1; int32 result = 1;
while (width > 1 || height > 1) while (width > 1 || height > 1)
{ {
@@ -637,11 +625,8 @@ int32 MipLevelsCount(int32 width, int32 height, bool useMipLevels)
return result; return result;
} }
int32 MipLevelsCount(int32 width, int32 height, int32 depth, bool useMipLevels) int32 MipLevelsCount(int32 width, int32 height, int32 depth)
{ {
if (!useMipLevels)
return 1;
int32 result = 1; int32 result = 1;
while (width > 1 || height > 1 || depth > 1) while (width > 1 || height > 1 || depth > 1)
{ {
@@ -653,7 +638,6 @@ int32 MipLevelsCount(int32 width, int32 height, int32 depth, bool useMipLevels)
depth >>= 1; depth >>= 1;
result++; result++;
} }
return result; return result;
} }

View File

@@ -136,23 +136,11 @@ public:
static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside); static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside);
}; };
// Calculate mip levels count for a texture 1D // Calculates mip levels count for a texture 1D.
// @param width Most detailed mip width extern int32 MipLevelsCount(int32 width);
// @param useMipLevels True if use mip levels, otherwise false (use only 1 mip)
// @returns Mip levels count
extern int32 MipLevelsCount(int32 width, bool useMipLevels = true);
// Calculate mip levels count for a texture 2D // Calculates mip levels count for a texture 2D.
// @param width Most detailed mip width extern int32 MipLevelsCount(int32 width, int32 height);
// @param height Most detailed mip height
// @param useMipLevels True if use mip levels, otherwise false (use only 1 mip)
// @returns Mip levels count
extern int32 MipLevelsCount(int32 width, int32 height, bool useMipLevels = true);
// Calculate mip levels count for a texture 3D // Calculates mip levels count for a texture 3D.
// @param width Most detailed mip width extern int32 MipLevelsCount(int32 width, int32 height, int32 depth);
// @param height Most detailed mip height
// @param depth Most detailed mip depths
// @param useMipLevels True if use mip levels, otherwise false (use only 1 mip)
// @returns Mip levels count
extern int32 MipLevelsCount(int32 width, int32 height, int32 depth, bool useMipLevels = true);

View File

@@ -852,7 +852,7 @@ public:
API_FUNCTION() bool HasActorInChildren(Actor* a) const; API_FUNCTION() bool HasActorInChildren(Actor* a) const;
/// <summary> /// <summary>
/// Determines if there is an intersection between the current object and a Ray. /// Determines if there is an intersection between the current object and a ray.
/// </summary> /// </summary>
/// <param name="ray">The ray to test.</param> /// <param name="ray">The ray to test.</param>
/// <param name="distance">When the method completes, contains the distance of the intersection (if any valid).</param> /// <param name="distance">When the method completes, contains the distance of the intersection (if any valid).</param>
@@ -879,7 +879,7 @@ public:
/// Rotates actor to orient it towards the specified world position with upwards direction. /// Rotates actor to orient it towards the specified world position with upwards direction.
/// </summary> /// </summary>
/// <param name="worldPos">The world position to orient towards.</param> /// <param name="worldPos">The world position to orient towards.</param>
/// <param name="worldUp">The up direction that Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param> /// <param name="worldUp">The up direction that constrains up axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel.</param>
API_FUNCTION() void LookAt(const Vector3& worldPos, const Vector3& worldUp); API_FUNCTION() void LookAt(const Vector3& worldPos, const Vector3& worldUp);
/// <summary> /// <summary>

View File

@@ -109,6 +109,8 @@ namespace FlaxEngine
public static T[] GetScripts<T>() where T : Script public static T[] GetScripts<T>() where T : Script
{ {
var scripts = GetScripts(typeof(T)); var scripts = GetScripts(typeof(T));
if (typeof(T) == typeof(Script))
return (T[])scripts;
if (scripts.Length == 0) if (scripts.Length == 0)
return Array.Empty<T>(); return Array.Empty<T>();
var result = new T[scripts.Length]; var result = new T[scripts.Length];
@@ -126,6 +128,8 @@ namespace FlaxEngine
public static T[] GetActors<T>(bool activeOnly = false) where T : Actor public static T[] GetActors<T>(bool activeOnly = false) where T : Actor
{ {
var actors = GetActors(typeof(T), activeOnly); var actors = GetActors(typeof(T), activeOnly);
if (typeof(T) == typeof(Actor))
return (T[])actors;
if (actors.Length == 0) if (actors.Length == 0)
return Array.Empty<T>(); return Array.Empty<T>();
var result = new T[actors.Length]; var result = new T[actors.Length];

View File

@@ -14,6 +14,7 @@
#include "Engine/Core/Collections/CollectionPoolCache.h" #include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Core/Cache.h" #include "Engine/Core/Cache.h"
#include "Engine/Core/LogContext.h"
#include "Engine/Debug/Exceptions/ArgumentException.h" #include "Engine/Debug/Exceptions/ArgumentException.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Scripting/Script.h" #include "Engine/Scripting/Script.h"
@@ -122,6 +123,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
} }
auto& data = *prefab->Data; auto& data = *prefab->Data;
SceneObjectsFactory::Context context(modifier.Value); SceneObjectsFactory::Context context(modifier.Value);
LogContextScope logContext(prefabId);
// Deserialize prefab objects // Deserialize prefab objects
auto prevIdMapping = Scripting::ObjectsLookupIdMapping.Get(); auto prevIdMapping = Scripting::ObjectsLookupIdMapping.Get();

View File

@@ -1302,6 +1302,15 @@ bool NetworkReplicator::HasObject(const ScriptingObject* obj)
return false; return false;
} }
void NetworkReplicator::MapObjectId(Guid& objectId)
{
if (!IdsRemappingTable.TryGet(objectId, objectId))
{
// Try inverse mapping
IdsRemappingTable.KeyOf(objectId, &objectId);
}
}
ScriptingObject* NetworkReplicator::ResolveForeignObject(Guid objectId) ScriptingObject* NetworkReplicator::ResolveForeignObject(Guid objectId)
{ {
if (const auto& object = ResolveObject(objectId)) if (const auto& object = ResolveObject(objectId))

View File

@@ -116,6 +116,12 @@ public:
/// <param name="obj">The network object.</param> /// <param name="obj">The network object.</param>
/// <returns>True if object exists in networking, otherwise false.</returns> /// <returns>True if object exists in networking, otherwise false.</returns>
API_FUNCTION() static bool HasObject(const ScriptingObject* obj); API_FUNCTION() static bool HasObject(const ScriptingObject* obj);
/// <summary>
/// Maps object ID into server or client ID (depending on the source ID). Leaves source value unchanged if that specific ID is unused.
/// </summary>
/// <param name="objectId">The network object identifier to map. Contains result ID once the method completes.</param>
API_FUNCTION() static void MapObjectId(API_PARAM(Ref) Guid& objectId);
/// <summary> /// <summary>
/// Resolves foreign Guid into a local ScriptingObject /// Resolves foreign Guid into a local ScriptingObject

View File

@@ -494,28 +494,28 @@ void PhysicsScene::CollectResults()
bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, uint32 layerMask, bool hitTriggers) bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, uint32 layerMask, bool hitTriggers)
{ {
Vector3 directionToEnd = end - start; Vector3 directionToEnd = end - start;
const float distanceToEnd = directionToEnd.Length(); const Real distanceToEnd = directionToEnd.Length();
if (distanceToEnd >= ZeroTolerance) if (distanceToEnd >= ZeroTolerance)
directionToEnd /= distanceToEnd; directionToEnd /= distanceToEnd;
return PhysicsBackend::RayCast(_scene, start, directionToEnd, distanceToEnd, layerMask, hitTriggers); return PhysicsBackend::RayCast(_scene, start, directionToEnd, (float)distanceToEnd, layerMask, hitTriggers);
} }
bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, RayCastHit& hitInfo, uint32 layerMask, bool hitTriggers) bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, RayCastHit& hitInfo, uint32 layerMask, bool hitTriggers)
{ {
Vector3 directionToEnd = end - start; Vector3 directionToEnd = end - start;
const float distanceToEnd = directionToEnd.Length(); const Real distanceToEnd = directionToEnd.Length();
if (distanceToEnd >= ZeroTolerance) if (distanceToEnd >= ZeroTolerance)
directionToEnd /= distanceToEnd; directionToEnd /= distanceToEnd;
return PhysicsBackend::RayCast(_scene, start, directionToEnd, hitInfo, distanceToEnd, layerMask, hitTriggers); return PhysicsBackend::RayCast(_scene, start, directionToEnd, hitInfo, (float)distanceToEnd, layerMask, hitTriggers);
} }
bool PhysicsScene::LineCastAll(const Vector3& start, const Vector3& end, Array<RayCastHit>& results, uint32 layerMask, bool hitTriggers) bool PhysicsScene::LineCastAll(const Vector3& start, const Vector3& end, Array<RayCastHit>& results, uint32 layerMask, bool hitTriggers)
{ {
Vector3 directionToEnd = end - start; Vector3 directionToEnd = end - start;
const float distanceToEnd = directionToEnd.Length(); const Real distanceToEnd = directionToEnd.Length();
if (distanceToEnd >= ZeroTolerance) if (distanceToEnd >= ZeroTolerance)
directionToEnd /= distanceToEnd; directionToEnd /= distanceToEnd;
return PhysicsBackend::RayCastAll(_scene, start, directionToEnd, results, distanceToEnd, layerMask, hitTriggers); return PhysicsBackend::RayCastAll(_scene, start, directionToEnd, results, (float)distanceToEnd, layerMask, hitTriggers);
} }
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers) bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)

View File

@@ -167,18 +167,18 @@ bool AndroidFileSystem::DirectoryGetFiles(Array<String>& results, const String&
return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get()); return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get());
} }
bool AndroidFileSystem::GetChildDirectories(Array<String>& results, const String& directory) bool AndroidFileSystem::GetChildDirectories(Array<String>& results, const String& path)
{ {
size_t pathLength; size_t pathLength;
struct stat statPath, statEntry; struct stat statPath, statEntry;
struct dirent* entry; struct dirent* entry;
const StringAsANSI<> pathANSI(*directory, directory.Length()); const StringAsANSI<> pathANSI(*path, path.Length());
const char* path = pathANSI.Get(); const char* pathStr = pathANSI.Get();
// Stat for the path // Stat for the path
stat(path, &statPath); stat(pathStr, &statPath);
// If path does not exists or is not dir - exit with status -1 // If path does not exist or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0) if (S_ISDIR(statPath.st_mode) == 0)
{ {
// Is not directory // Is not directory
@@ -186,7 +186,7 @@ bool AndroidFileSystem::GetChildDirectories(Array<String>& results, const String
} }
// If not possible to read the directory for this user // If not possible to read the directory for this user
DIR* dir = opendir(path); DIR* dir = opendir(pathStr);
if (dir == nullptr) if (dir == nullptr)
{ {
// Cannot open directory // Cannot open directory
@@ -194,7 +194,7 @@ bool AndroidFileSystem::GetChildDirectories(Array<String>& results, const String
} }
// The length of the path // The length of the path
pathLength = strlen(path); pathLength = strlen(pathStr);
// Iteration through entries in the directory // Iteration through entries in the directory
while ((entry = readdir(dir)) != nullptr) while ((entry = readdir(dir)) != nullptr)
@@ -204,20 +204,20 @@ bool AndroidFileSystem::GetChildDirectories(Array<String>& results, const String
continue; continue;
// Determinate a full path of an entry // Determinate a full path of an entry
char full_path[256]; char fullPath[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(fullPath));
strcpy(full_path, path); strcpy(fullPath, pathStr);
strcat(full_path, "/"); strcat(fullPath, "/");
strcat(full_path, entry->d_name); strcat(fullPath, entry->d_name);
// Stat for the entry // Stat for the entry
stat(full_path, &statEntry); stat(fullPath, &statEntry);
// Check for directory // Check for directory
if (S_ISDIR(statEntry.st_mode) != 0) if (S_ISDIR(statEntry.st_mode) != 0)
{ {
// Add directory // Add directory
results.Add(String(full_path)); results.Add(String(fullPath));
} }
} }

View File

@@ -12,12 +12,11 @@
class FLAXENGINE_API AndroidFileSystem : public FileSystemBase class FLAXENGINE_API AndroidFileSystem : public FileSystemBase
{ {
public: public:
static bool CreateDirectory(const StringView& path); static bool CreateDirectory(const StringView& path);
static bool DeleteDirectory(const String& path, bool deleteContents = true); static bool DeleteDirectory(const String& path, bool deleteContents = true);
static bool DirectoryExists(const StringView& path); static bool DirectoryExists(const StringView& path);
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories); static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern = TEXT("*"), DirectorySearchOption option = DirectorySearchOption::AllDirectories);
static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& directory); static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& path);
static bool FileExists(const StringView& path); static bool FileExists(const StringView& path);
static bool DeleteFile(const StringView& path); static bool DeleteFile(const StringView& path);
static uint64 GetFileSize(const StringView& path); static uint64 GetFileSize(const StringView& path);
@@ -25,25 +24,10 @@ public:
static bool SetReadOnly(const StringView& path, bool isReadOnly); static bool SetReadOnly(const StringView& path, bool isReadOnly);
static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false); static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false);
static bool CopyFile(const StringView& dst, const StringView& src); static bool CopyFile(const StringView& dst, const StringView& src);
public:
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path); static DateTime GetFileLastEditTime(const StringView& path);
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result); static void GetSpecialFolderPath(const SpecialFolder type, String& result);
private: private:
static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern); static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern); static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
}; };

View File

@@ -145,19 +145,19 @@ bool AppleFileSystem::DirectoryGetFiles(Array<String>& results, const String& pa
return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get()); return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get());
} }
bool AppleFileSystem::GetChildDirectories(Array<String>& results, const String& directory) bool AppleFileSystem::GetChildDirectories(Array<String>& results, const String& path)
{ {
size_t pathLength; size_t pathLength;
DIR* dir; DIR* dir;
struct stat statPath, statEntry; struct stat statPath, statEntry;
struct dirent* entry; struct dirent* entry;
const StringAsANSI<> pathANSI(*directory, directory.Length()); const StringAsANSI<> pathANSI(*path, path.Length());
const char* path = pathANSI.Get(); const char* pathStr = pathANSI.Get();
// Stat for the path // Stat for the path
stat(path, &statPath); stat(pathStr, &statPath);
// If path does not exists or is not dir - exit with status -1 // If path does not exist or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0) if (S_ISDIR(statPath.st_mode) == 0)
{ {
// Is not directory // Is not directory
@@ -165,14 +165,14 @@ bool AppleFileSystem::GetChildDirectories(Array<String>& results, const String&
} }
// If not possible to read the directory for this user // If not possible to read the directory for this user
if ((dir = opendir(path)) == NULL) if ((dir = opendir(pathStr)) == NULL)
{ {
// Cannot open directory // Cannot open directory
return true; return true;
} }
// The length of the path // The length of the path
pathLength = strlen(path); pathLength = strlen(pathStr);
// Iteration through entries in the directory // Iteration through entries in the directory
while ((entry = readdir(dir)) != NULL) while ((entry = readdir(dir)) != NULL)
@@ -182,20 +182,20 @@ bool AppleFileSystem::GetChildDirectories(Array<String>& results, const String&
continue; continue;
// Determinate a full path of an entry // Determinate a full path of an entry
char full_path[256]; char fullPath[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(fullPath));
strcpy(full_path, path); strcpy(fullPath, path);
strcat(full_path, "/"); strcat(fullPath, "/");
strcat(full_path, entry->d_name); strcat(fullPath, entry->d_name);
// Stat for the entry // Stat for the entry
stat(full_path, &statEntry); stat(fullPath, &statEntry);
// Check for directory // Check for directory
if (S_ISDIR(statEntry.st_mode) != 0) if (S_ISDIR(statEntry.st_mode) != 0)
{ {
// Add directory // Add directory
results.Add(String(full_path)); results.Add(String(fullPath));
} }
} }

View File

@@ -12,13 +12,12 @@
class FLAXENGINE_API AppleFileSystem : public FileSystemBase class FLAXENGINE_API AppleFileSystem : public FileSystemBase
{ {
public: public:
// [FileSystemBase] // [FileSystemBase]
static bool CreateDirectory(const StringView& path); static bool CreateDirectory(const StringView& path);
static bool DeleteDirectory(const String& path, bool deleteContents = true); static bool DeleteDirectory(const String& path, bool deleteContents = true);
static bool DirectoryExists(const StringView& path); static bool DirectoryExists(const StringView& path);
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories); static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern = TEXT("*"), DirectorySearchOption option = DirectorySearchOption::AllDirectories);
static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& directory); static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& path);
static bool FileExists(const StringView& path); static bool FileExists(const StringView& path);
static bool DeleteFile(const StringView& path); static bool DeleteFile(const StringView& path);
static uint64 GetFileSize(const StringView& path); static uint64 GetFileSize(const StringView& path);
@@ -26,25 +25,10 @@ public:
static bool SetReadOnly(const StringView& path, bool isReadOnly); static bool SetReadOnly(const StringView& path, bool isReadOnly);
static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false); static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false);
static bool CopyFile(const StringView& dst, const StringView& src); static bool CopyFile(const StringView& dst, const StringView& src);
public:
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path); static DateTime GetFileLastEditTime(const StringView& path);
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result); static void GetSpecialFolderPath(const SpecialFolder type, String& result);
private: private:
static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern); static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern); static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
}; };

View File

@@ -195,7 +195,7 @@ String FileSystemBase::ConvertAbsolutePathToRelative(const String& basePath, con
return output; return output;
} }
bool FileSystemBase::CopyFile(const String& dst, const String& src) bool FileSystemBase::CopyFile(const StringView& dst, const StringView& src)
{ {
// Open and create files // Open and create files
const auto srcFile = File::Open(src, FileMode::OpenExisting, FileAccess::Read); const auto srcFile = File::Open(src, FileMode::OpenExisting, FileAccess::Read);
@@ -247,7 +247,7 @@ uint64 FileSystemBase::GetDirectorySize(const StringView& path)
{ {
uint64 result = 0; uint64 result = 0;
Array<String> files; Array<String> files;
FileSystem::DirectoryGetFiles(files, path, TEXT("*"), DirectorySearchOption::AllDirectories); FileSystem::DirectoryGetFiles(files, path);
for (const String& file : files) for (const String& file : files)
result += FileSystem::GetFileSize(file); result += FileSystem::GetFileSize(file);
return result; return result;

View File

@@ -43,8 +43,133 @@ API_INJECT_CODE(cpp, "#include \"Engine/Platform/FileSystem.h\"");
API_CLASS(Static, Name="FileSystem", Tag="NativeInvokeUseName") API_CLASS(Static, Name="FileSystem", Tag="NativeInvokeUseName")
class FLAXENGINE_API FileSystemBase class FLAXENGINE_API FileSystemBase
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase); DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase);
/// <summary>
/// Creates a new directory.
/// </summary>
/// <param name="path">Directory path</param>
/// <returns>True if failed to create directory, otherwise false.</returns>
static bool CreateDirectory(const StringView& path) = delete;
/// <summary>
/// Deletes an existing directory.
/// </summary>
/// <param name="path">Directory path</param>
/// <param name="deleteContents">True if delete all subdirectories and files, otherwise false.</param>
/// <returns>True if failed to delete directory, otherwise false.</returns>
static bool DeleteDirectory(const String& path, bool deleteContents = true) = delete;
/// <summary>
/// Checks if directory exists.
/// </summary>
/// <param name="path">Directory path.</param>
/// <returns>True if directory exists, otherwise false.</returns>
static bool DirectoryExists(const StringView& path) = delete;
/// <summary>
/// Finds the paths of files that match the specified search pattern in the specified directory, using a value to determine whether to search subdirectories.
/// </summary>
/// <param name="results">Output list with all found file paths. Items are appended without clearing the list first.</param>
/// <param name="path">Path of the directory to search in it.</param>
/// <param name="searchPattern">Custom search pattern to use during that operation Use asterisk character (*) for name-based filtering (eg. `*.txt` to find all files with `.txt` extension)..</param>
/// <param name="option">Additional search options that define rules.</param>
/// <returns>True if an error occurred, otherwise false.</returns>
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern = TEXT("*"), DirectorySearchOption option = DirectorySearchOption::AllDirectories) = delete;
/// <summary>
/// Finds the paths of directories that are inside the specified directory.
/// </summary>
/// <param name="results">Output list with all found directory paths. Items are appended without clearing the list first.</param>
/// <param name="path">Path of the directory to search in it.</param>
/// <returns>True if an error occurred, otherwise false.</returns>
static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& path) = delete;
/// <summary>
/// Copies the directory.
/// </summary>
/// <param name="dst">Destination path.</param>
/// <param name="src">Source directory path.</param>
/// <param name="withSubDirectories">True if copy subdirectories of the source folder, otherwise only top-level files will be cloned.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool CopyDirectory(const String& dst, const String& src, bool withSubDirectories = true);
/// <summary>
/// Gets the size of the directory (in bytes) defined by size of all files contained by it.
/// </summary>
/// <param name="path">Directory path.</param>
/// <returns>Amount of bytes in directory, or 0 if failed.</returns>
static uint64 GetDirectorySize(const StringView& path);
public:
/// <summary>
/// Checks if a given file exists.
/// </summary>
/// <param name="path">File path to check.</param>
/// <returns>True if file exists, otherwise false.</returns>
static bool FileExists(const StringView& path) = delete;
/// <summary>
/// Deletes an existing file.
/// </summary>
/// <param name="path">File path</param>
/// <returns>True if operation failed, otherwise false.</returns>
static bool DeleteFile(const StringView& path) = delete;
/// <summary>
/// Gets the size of the file (in bytes).
/// </summary>
/// <param name="path">File path</param>
/// <returns>Amount of bytes in file, or 0 if failed.</returns>
static uint64 GetFileSize(const StringView& path) = delete;
/// <summary>
/// Checks if file is read-only.
/// </summary>
/// <param name="path">File path.</param>
/// <returns>True if file is read-only, otherwise false. Returns false if failed or path is invalid.</returns>
static bool IsReadOnly(const StringView& path) = delete;
/// <summary>
/// Sets file read-only flag.
/// </summary>
/// <param name="path">File path.</param>
/// <param name="isReadOnly">Read-only flag value to set.</param>
/// <returns>True if operation failed, otherwise false.</returns>
static bool SetReadOnly(const StringView& path, bool isReadOnly) = delete;
/// <summary>
/// Moves the file.
/// </summary>
/// <param name="dst">Destination path.</param>
/// <param name="src">Source file path.</param>
/// <param name="overwrite">True if allow overriding destination file if it already exists, otherwise action will fail.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false) = delete;
/// <summary>
/// Copies the file.
/// </summary>
/// <param name="dst">Destination path.</param>
/// <param name="src">Source file path.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool CopyFile(const StringView& dst, const StringView& src);
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path) = delete;
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result) = delete;
public:
/// <summary> /// <summary>
/// Displays a standard dialog box that prompts the user to open a file(s). /// Displays a standard dialog box that prompts the user to open a file(s).
/// </summary> /// </summary>
@@ -97,28 +222,26 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase);
API_FUNCTION() static bool ShowFileExplorer(const StringView& path); API_FUNCTION() static bool ShowFileExplorer(const StringView& path);
public: public:
static void SaveBitmapToFile(byte* data, uint32 width, uint32 height, uint32 bitsPerPixel, const uint32 padding, const String& path); static void SaveBitmapToFile(byte* data, uint32 width, uint32 height, uint32 bitsPerPixel, const uint32 padding, const String& path);
public: public:
static bool AreFilePathsEqual(const StringView& path1, const StringView& path2); static bool AreFilePathsEqual(const StringView& path1, const StringView& path2);
/// <summary> /// <summary>
/// Normalize input path for valid path name for current platform file system /// Normalizes input path for valid path name for current platform file system.
/// </summary> /// </summary>
/// <param name="path">Path to normalize</param> /// <param name="path">Path to normalize</param>
static void NormalizePath(String& path); static void NormalizePath(String& path);
/// <summary> /// <summary>
/// Check if path type is relative /// Checks if path type is relative.
/// </summary> /// </summary>
/// <param name="path">Input path to check</param> /// <param name="path">Input path to check</param>
/// <returns>True if input path is relative one, otherwise false</returns> /// <returns>True if input path is relative one, otherwise false</returns>
static bool IsRelative(const StringView& path); static bool IsRelative(const StringView& path);
/// <summary> /// <summary>
/// Retrieves file extension (without a dot) /// Retrieves file extension (without a dot).
/// </summary> /// </summary>
/// <param name="path">Input path to process</param> /// <param name="path">Input path to process</param>
/// <returns>File extension</returns> /// <returns>File extension</returns>
@@ -131,22 +254,15 @@ public:
static void GetTempFilePath(String& tmpPath); static void GetTempFilePath(String& tmpPath);
public: public:
static bool CopyFile(const String& dst, const String& src);
static bool CopyDirectory(const String& dst, const String& src, bool withSubDirectories);
static uint64 GetDirectorySize(const StringView& path);
public:
/// <summary> /// <summary>
/// Converts path relative to the engine startup folder into absolute path /// Converts path relative to the engine startup folder into absolute path.
/// </summary> /// </summary>
/// <param name="path">Path relative to the engine directory</param> /// <param name="path">Path relative to the engine directory</param>
/// <returns>Absolute path</returns> /// <returns>Absolute path</returns>
static String ConvertRelativePathToAbsolute(const String& path); static String ConvertRelativePathToAbsolute(const String& path);
/// <summary> /// <summary>
/// Converts path relative to basePath into absolute path /// Converts path relative to basePath into absolute path.
/// </summary> /// </summary>
/// <param name="basePath">Base path</param> /// <param name="basePath">Base path</param>
/// <param name="path">Path relative to basePath</param> /// <param name="path">Path relative to basePath</param>
@@ -154,14 +270,14 @@ public:
static String ConvertRelativePathToAbsolute(const String& basePath, const String& path); static String ConvertRelativePathToAbsolute(const String& basePath, const String& path);
/// <summary> /// <summary>
/// Converts absolute path into relative path to engine startup folder /// Converts absolute path into relative path to engine startup folder.
/// </summary> /// </summary>
/// <param name="path">Absolute path</param> /// <param name="path">Absolute path</param>
/// <returns>Relative path</returns> /// <returns>Relative path</returns>
static String ConvertAbsolutePathToRelative(const String& path); static String ConvertAbsolutePathToRelative(const String& path);
/// <summary> /// <summary>
/// Converts absolute path into relative path to basePath /// Converts absolute path into relative path to basePath.
/// </summary> /// </summary>
/// <param name="basePath">Base path</param> /// <param name="basePath">Base path</param>
/// <param name="path">Absolute path</param> /// <param name="path">Absolute path</param>
@@ -169,6 +285,5 @@ public:
static String ConvertAbsolutePathToRelative(const String& basePath, const String& path); static String ConvertAbsolutePathToRelative(const String& basePath, const String& path);
private: private:
static bool DirectoryCopyHelper(const String& dst, const String& src, bool withSubDirectories); static bool DirectoryCopyHelper(const String& dst, const String& src, bool withSubDirectories);
}; };

View File

@@ -291,17 +291,17 @@ bool LinuxFileSystem::DirectoryGetFiles(Array<String>& results, const String& pa
return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get()); return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get());
} }
bool LinuxFileSystem::GetChildDirectories(Array<String>& results, const String& directory) bool LinuxFileSystem::GetChildDirectories(Array<String>& results, const String& path)
{ {
size_t pathLength; size_t pathLength;
DIR* dir; DIR* dir;
struct stat statPath, statEntry; struct stat statPath, statEntry;
struct dirent* entry; struct dirent* entry;
const StringAsUTF8<> pathANSI(*directory, directory.Length()); const StringAsUTF8<> pathANSI(*path, path.Length());
const char* path = pathANSI.Get(); const char* pathStr = pathANSI.Get();
// Stat for the path // Stat for the path
stat(path, &statPath); stat(pathStr, &statPath);
// If path does not exists or is not dir - exit with status -1 // If path does not exists or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0) if (S_ISDIR(statPath.st_mode) == 0)
@@ -311,14 +311,14 @@ bool LinuxFileSystem::GetChildDirectories(Array<String>& results, const String&
} }
// If not possible to read the directory for this user // If not possible to read the directory for this user
if ((dir = opendir(path)) == NULL) if ((dir = opendir(pathStr)) == NULL)
{ {
// Cannot open directory // Cannot open directory
return true; return true;
} }
// The length of the path // The length of the path
pathLength = strlen(path); pathLength = strlen(pathStr);
// Iteration through entries in the directory // Iteration through entries in the directory
while ((entry = readdir(dir)) != NULL) while ((entry = readdir(dir)) != NULL)
@@ -328,20 +328,20 @@ bool LinuxFileSystem::GetChildDirectories(Array<String>& results, const String&
continue; continue;
// Determinate a full path of an entry // Determinate a full path of an entry
char full_path[256]; char fullPath[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path)); ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(fullPath));
strcpy(full_path, path); strcpy(fullPath, path);
strcat(full_path, "/"); strcat(fullPath, "/");
strcat(full_path, entry->d_name); strcat(fullPath, entry->d_name);
// Stat for the entry // Stat for the entry
stat(full_path, &statEntry); stat(fullPath, &statEntry);
// Check for directory // Check for directory
if (S_ISDIR(statEntry.st_mode) != 0) if (S_ISDIR(statEntry.st_mode) != 0)
{ {
// Add directory // Add directory
results.Add(String(full_path)); results.Add(String(fullPath));
} }
} }

View File

@@ -12,7 +12,6 @@
class FLAXENGINE_API LinuxFileSystem : public FileSystemBase class FLAXENGINE_API LinuxFileSystem : public FileSystemBase
{ {
public: public:
// [FileSystemBase] // [FileSystemBase]
static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames); static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
static bool ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path); static bool ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path);
@@ -20,8 +19,8 @@ public:
static bool CreateDirectory(const StringView& path); static bool CreateDirectory(const StringView& path);
static bool DeleteDirectory(const String& path, bool deleteContents = true); static bool DeleteDirectory(const String& path, bool deleteContents = true);
static bool DirectoryExists(const StringView& path); static bool DirectoryExists(const StringView& path);
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories); static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern = TEXT("*"), DirectorySearchOption option = DirectorySearchOption::AllDirectories);
static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& directory); static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& path);
static bool FileExists(const StringView& path); static bool FileExists(const StringView& path);
static bool DeleteFile(const StringView& path); static bool DeleteFile(const StringView& path);
static bool MoveFileToRecycleBin(const StringView& path); static bool MoveFileToRecycleBin(const StringView& path);
@@ -30,25 +29,10 @@ public:
static bool SetReadOnly(const StringView& path, bool isReadOnly); static bool SetReadOnly(const StringView& path, bool isReadOnly);
static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false); static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false);
static bool CopyFile(const StringView& dst, const StringView& src); static bool CopyFile(const StringView& dst, const StringView& src);
public:
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path); static DateTime GetFileLastEditTime(const StringView& path);
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result); static void GetSpecialFolderPath(const SpecialFolder type, String& result);
private:
private:
static bool UrnEncodePath(const char *path, char *result, int maxLength); static bool UrnEncodePath(const char *path, char *result, int maxLength);
static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern); static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern); static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);

View File

@@ -12,7 +12,6 @@
class FLAXENGINE_API MacFileSystem : public AppleFileSystem class FLAXENGINE_API MacFileSystem : public AppleFileSystem
{ {
public: public:
// [AppleFileSystem] // [AppleFileSystem]
static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames); static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames); static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);

View File

@@ -64,55 +64,27 @@ public:
} }
public: public:
// Returns true if character is uppercase
static bool IsUpper(char c); static bool IsUpper(char c);
// Returns true if character is lowercase
static bool IsLower(char c); static bool IsLower(char c);
static bool IsAlpha(char c); static bool IsAlpha(char c);
static bool IsPunct(char c); static bool IsPunct(char c);
static bool IsAlnum(char c); static bool IsAlnum(char c);
static bool IsDigit(char c); static bool IsDigit(char c);
static bool IsHexDigit(char c); static bool IsHexDigit(char c);
// Returns true if character is a whitespace
static bool IsWhitespace(char c); static bool IsWhitespace(char c);
// Convert wide character to upper case
static char ToUpper(char c); static char ToUpper(char c);
// Convert wide character to lower case
static char ToLower(char c); static char ToLower(char c);
public: public:
// Returns true if character is uppercase
static bool IsUpper(Char c); static bool IsUpper(Char c);
// Returns true if character is lowercase
static bool IsLower(Char c); static bool IsLower(Char c);
static bool IsAlpha(Char c); static bool IsAlpha(Char c);
static bool IsPunct(Char c); static bool IsPunct(Char c);
static bool IsAlnum(Char c); static bool IsAlnum(Char c);
static bool IsDigit(Char c); static bool IsDigit(Char c);
static bool IsHexDigit(Char c); static bool IsHexDigit(Char c);
// Returns true if character is a whitespace
static bool IsWhitespace(Char c); static bool IsWhitespace(Char c);
// Convert wide character to upper case
static Char ToUpper(Char c); static Char ToUpper(Char c);
// Convert wide character to lower case
static Char ToLower(Char c); static Char ToLower(Char c);
public: public:
@@ -141,40 +113,28 @@ public:
static int32 CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount); static int32 CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount);
public: public:
// Get string length. Returns 0 if str is null. // Gets the string length. Returns 0 if str is null.
static int32 Length(const Char* str); static int32 Length(const Char* str);
// Get string length. Returns 0 if str is null. // Gets the string length. Returns 0 if str is null.
static int32 Length(const char* str); static int32 Length(const char* str);
// Copy string // Copies the string.
static Char* Copy(Char* dst, const Char* src); static Char* Copy(Char* dst, const Char* src);
// Copy string (count is maximum amount of characters to copy) // Copies the string (count is maximum amount of characters to copy).
static Char* Copy(Char* dst, const Char* src, int32 count); static Char* Copy(Char* dst, const Char* src, int32 count);
// Finds string in string, case sensitive // Finds specific sub-string in the input string. Returns the first found position in the input string or nulll if failed.
// @param str The string to look through
// @param toFind The string to find inside str
// @return Position in str if Find was found, otherwise null
static const Char* Find(const Char* str, const Char* toFind); static const Char* Find(const Char* str, const Char* toFind);
// Finds string in string, case sensitive // Finds specific sub-string in the input string, case sensitive. Returns the first found position in the input string or nulll if failed.
// @param str The string to look through
// @param toFind The string to find inside str
// @return Position in str if Find was found, otherwise null
static const char* Find(const char* str, const char* toFind); static const char* Find(const char* str, const char* toFind);
// Finds string in string, case insensitive // Finds specific sub-string in the input string, case insensitive. Returns the first found position in the input string or nulll if failed.
// @param str The string to look through
// @param toFind The string to find inside str
// @return Position in str if toFind was found, otherwise null
static const Char* FindIgnoreCase(const Char* str, const Char* toFind); static const Char* FindIgnoreCase(const Char* str, const Char* toFind);
// Finds string in string, case insensitive // Finds specific sub-string in the input string, case insensitive. Returns the first found position in the input string or nulll if failed.
// @param str The string to look through
// @param toFind The string to find inside str
// @return Position in str if toFind was found, otherwise null
static const char* FindIgnoreCase(const char* str, const char* toFind); static const char* FindIgnoreCase(const char* str, const char* toFind);
public: public:
@@ -197,101 +157,94 @@ public:
static char* ConvertUTF162UTF8(const Char* from, int32 fromLength, int32& toLength); static char* ConvertUTF162UTF8(const Char* from, int32 fromLength, int32& toLength);
public: public:
// Returns the directory name of the specified path string /// <summary>
// @param path The path string from which to obtain the directory name /// Gets the directory name of the specified path string.
// @returns Directory name /// </summary>
/// <param name="path">The path string from which to obtain the directory name.</param>
/// <returns>Directory name.</returns>
static StringView GetDirectoryName(const StringView& path); static StringView GetDirectoryName(const StringView& path);
// Returns the file name and extension of the specified path string /// <summary>
// @param path The path string from which to obtain the file name and extension /// Gets the file name and extension of the specified path string
// @returns File name with extension /// </summary>
/// <param name="path">The path string from which to obtain the file name and extension.</param>
/// <returns>File name with extension.</returns>
static StringView GetFileName(const StringView& path); static StringView GetFileName(const StringView& path);
// Returns the file name without extension of the specified path string /// <summary>
// @param path The path string from which to obtain the file name /// Gets the file name without extension of the specified path string.
// @returns File name without extension /// </summary>
/// <param name="path">The path string from which to obtain the file name.</param>
/// <returns>File name without extension.</returns>
static StringView GetFileNameWithoutExtension(const StringView& path); static StringView GetFileNameWithoutExtension(const StringView& path);
/// <summary>
/// Gets the path without extension.
/// </summary>
/// <param name="path">The path string.</param>
/// <returns>The path string without extension.</returns>
static StringView GetPathWithoutExtension(const StringView& path); static StringView GetPathWithoutExtension(const StringView& path);
/// <summary>
/// Normalizes path string and removes any relative parts such as `/../` and `/./`.
/// </summary>
/// <param name="path">The input and output string with path to process.</param>
static void PathRemoveRelativeParts(String& path); static void PathRemoveRelativeParts(String& path);
public: public:
// Converts hexadecimal character into the value. // Converts hexadecimal character into the value.
static int32 HexDigit(Char c); static int32 HexDigit(Char c);
// Parse text to unsigned integer value // Parses text to unsigned integer value. Returns true if failed to convert the value.
// @param str String to parse
// @return Result value
// @returns True if cannot convert data, otherwise false
template<typename CharType> template<typename CharType>
static bool ParseHex(const CharType* str, uint32* result) static bool ParseHex(const CharType* str, uint32* result)
{ {
uint32 sum = 0; uint32 sum = 0;
const CharType* p = str; const CharType* p = str;
if (*p == '0' && *(p + 1) == 'x') if (*p == '0' && *(p + 1) == 'x')
p += 2; p += 2;
while (*p) while (*p)
{ {
int32 c = *p - '0'; int32 c = *p - '0';
if (c < 0 || c > 9) if (c < 0 || c > 9)
{ {
c = ToLower(*p) - 'a' + 10; c = ToLower(*p) - 'a' + 10;
if (c < 10 || c > 15) if (c < 10 || c > 15)
return true; return true;
} }
sum = 16 * sum + c; sum = 16 * sum + c;
p++; p++;
} }
*result = sum; *result = sum;
return false; return false;
} }
// Parse text to unsigned integer value // Parses text to unsigned integer value. Returns true if failed to convert the value.
// @param str String to parse
// @param length Text length
// @return Result value
// @returns True if cannot convert data, otherwise false
template<typename CharType> template<typename CharType>
static bool ParseHex(const CharType* str, int32 length, uint32* result) static bool ParseHex(const CharType* str, int32 length, uint32* result)
{ {
uint32 sum = 0; uint32 sum = 0;
const CharType* p = str; const CharType* p = str;
const CharType* end = str + length; const CharType* end = str + length;
if (*p == '0' && *(p + 1) == 'x') if (*p == '0' && *(p + 1) == 'x')
p += 2; p += 2;
while (*p && p < end) while (*p && p < end)
{ {
int32 c = *p - '0'; int32 c = *p - '0';
if (c < 0 || c > 9) if (c < 0 || c > 9)
{ {
c = ToLower(*p) - 'a' + 10; c = ToLower(*p) - 'a' + 10;
if (c < 10 || c > 15) if (c < 10 || c > 15)
return true; return true;
} }
sum = 16 * sum + c; sum = 16 * sum + c;
p++; p++;
} }
*result = sum; *result = sum;
return false; return false;
} }
// Parse text to scalar value // Parses text to scalar integer value. Returns true if failed to convert the value.
// @param str String to parse
// @return Result value
// @returns True if cannot convert data, otherwise false
template<typename T, typename U> template<typename T, typename U>
static bool Parse(const T* str, U* result) static bool Parse(const T* str, U* result)
{ {
@@ -308,11 +261,7 @@ public:
return false; return false;
} }
// Parse text to scalar value // Parses text to scalar integer value. Returns true if failed to convert the value.
// @param str String to parse
// @param length Text length to read
// @return Result value
// @returns True if cannot convert data, otherwise false
template<typename T, typename U> template<typename T, typename U>
static bool Parse(const T* str, uint32 length, U* result) static bool Parse(const T* str, uint32 length, U* result)
{ {
@@ -329,11 +278,10 @@ public:
return false; return false;
} }
// Parse Unicode text to float value // Parses text to the scalar value. Returns true if failed to convert the value.
// @param str String to parse
// @return Result value
// @returns True if cannot convert data, otherwise false
static bool Parse(const Char* str, float* result); static bool Parse(const Char* str, float* result);
// Parses text to the scalar value. Returns true if failed to convert the value.
static bool Parse(const char* str, float* result); static bool Parse(const char* str, float* result);
public: public:
@@ -345,9 +293,7 @@ public:
static String ToString(double value); static String ToString(double value);
public: public:
// Returns the String to double null-terminated string // Converts the string to double null-terminated string.
// @param str Double null-terminated string
// @return Double null-terminated String
static String GetZZString(const Char* str); static String GetZZString(const Char* str);
}; };

View File

@@ -12,12 +12,7 @@
class FLAXENGINE_API UWPFileSystem : public Win32FileSystem class FLAXENGINE_API UWPFileSystem : public Win32FileSystem
{ {
public: public:
// [FileSystemBase]
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result); static void GetSpecialFolderPath(const SpecialFolder type, String& result);
}; };

View File

@@ -129,11 +129,11 @@ bool Win32FileSystem::DirectoryGetFiles(Array<String>& results, const String& pa
return getFilesFromDirectoryAll(results, path, searchPattern); return getFilesFromDirectoryAll(results, path, searchPattern);
} }
bool Win32FileSystem::GetChildDirectories(Array<String>& results, const String& directory) bool Win32FileSystem::GetChildDirectories(Array<String>& results, const String& path)
{ {
// Try to find first file // Try to find first file
WIN32_FIND_DATA info; WIN32_FIND_DATA info;
String pattern = directory / TEXT('*'); String pattern = path / TEXT('*');
const HANDLE handle = FindFirstFileW(*pattern, &info); const HANDLE handle = FindFirstFileW(*pattern, &info);
if (INVALID_HANDLE_VALUE == handle) if (INVALID_HANDLE_VALUE == handle)
{ {
@@ -151,7 +151,7 @@ bool Win32FileSystem::GetChildDirectories(Array<String>& results, const String&
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{ {
// Add directory // Add directory
results.Add(directory / info.cFileName); results.Add(path / info.cFileName);
} }
} while (FindNextFileW(handle, &info) != 0); } while (FindNextFileW(handle, &info) != 0);
FindClose(handle); FindClose(handle);

View File

@@ -11,81 +11,23 @@
/// </summary> /// </summary>
class FLAXENGINE_API Win32FileSystem : public FileSystemBase class FLAXENGINE_API Win32FileSystem : public FileSystemBase
{ {
// TODO: fix docs
public: public:
// [FileSystemBase]
// Creates a new directory
// @param path Directory path
// @returns True if cannot create directory, otherwise false
static bool CreateDirectory(const StringView& path); static bool CreateDirectory(const StringView& path);
// Deletes an existing directory
// @param path Directory path
// @param deleteSubdirectories True if delete all subdirectories and files, otherwise false
// @returns True if cannot delete directory, otherwise false
static bool DeleteDirectory(const String& path, bool deleteContents = true); static bool DeleteDirectory(const String& path, bool deleteContents = true);
// Check if directory exists
// @param path Directory path to check
// @returns True if directory exists, otherwise false
static bool DirectoryExists(const StringView& path); static bool DirectoryExists(const StringView& path);
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern = TEXT("*"), DirectorySearchOption option = DirectorySearchOption::AllDirectories);
// Finds the names of files (including their paths) that match the specified search pattern in the specified directory, using a value to determine whether to search subdirectories static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& path);
// @param results When this method completes, this list contains list of all filenames that match the specified search pattern
// @param path Path of the directory to search in it
// @param searchPattern Custom search pattern to use during that operation
// @param option Additional search options
// @returns True if an error occurred, otherwise false
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories);
// Finds the names of directories (including their paths) that are inside the specified directory
// @param results When this method completes, this list contains list of all filenames that match the specified search pattern
// @param directory Path of the directory to search in it
// @returns True if an error occurred, otherwise false
static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& directory);
public:
// Check if file exists
// @param path File path to check
// @returns True if file exists, otherwise false
static bool FileExists(const StringView& path); static bool FileExists(const StringView& path);
// Deletes an existing file
// @param path File path
// @returns True if cannot delete file, otherwise false
static bool DeleteFile(const StringView& path); static bool DeleteFile(const StringView& path);
// Tries to get size of the file
// @param path File path to check
// @returns Amount of bytes in file
static uint64 GetFileSize(const StringView& path); static uint64 GetFileSize(const StringView& path);
// Check if file is read-only
// @param path File path to check
// @returns True if file is read-only
static bool IsReadOnly(const StringView& path); static bool IsReadOnly(const StringView& path);
// Sets file read-only flag
// @param path File path
// @returns True if cannot update file
static bool SetReadOnly(const StringView& path, bool isReadOnly); static bool SetReadOnly(const StringView& path, bool isReadOnly);
// Move file
// @param dst Destination path
// @param src Source path
// @returns True if cannot move file
static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false); static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false);
// Clone file
// @param dst Destination path
// @param src Source path
// @returns True if cannot copy file
static bool CopyFile(const StringView& dst, const StringView& src); static bool CopyFile(const StringView& dst, const StringView& src);
static DateTime GetFileLastEditTime(const StringView& path);
public: public:
/// <summary> /// <summary>
/// Converts the UNIX style line endings into DOS style (from \n into \r\n). /// Converts the UNIX style line endings into DOS style (from \n into \r\n).
/// </summary> /// </summary>
@@ -93,17 +35,7 @@ public:
/// <param name="output">The output result.</param> /// <param name="output">The output result.</param>
static void ConvertLineEndingsToDos(const StringView& text, Array<Char, HeapAllocation>& output); static void ConvertLineEndingsToDos(const StringView& text, Array<Char, HeapAllocation>& output);
public:
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path);
private: private:
static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const String& directory, const Char* searchPattern); static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const String& directory, const Char* searchPattern);
static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const String& directory, const Char* searchPattern); static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const String& directory, const Char* searchPattern);
}; };

View File

@@ -12,7 +12,6 @@
class FLAXENGINE_API WindowsFileSystem : public Win32FileSystem class FLAXENGINE_API WindowsFileSystem : public Win32FileSystem
{ {
public: public:
/// <summary> /// <summary>
/// Moves a file to the recycle bin with possible undo instead of removing it permanently. /// Moves a file to the recycle bin with possible undo instead of removing it permanently.
/// </summary> /// </summary>
@@ -20,26 +19,15 @@ public:
/// <returns>True if cannot perform that operation, otherwise false.</returns> /// <returns>True if cannot perform that operation, otherwise false.</returns>
static bool MoveFileToRecycleBin(const StringView& path); static bool MoveFileToRecycleBin(const StringView& path);
public:
static bool AreFilePathsEqual(const StringView& path1, const StringView& path2); static bool AreFilePathsEqual(const StringView& path1, const StringView& path2);
public: public:
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result);
public:
// [Win32FileSystem] // [Win32FileSystem]
static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames); static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames); static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array<String, HeapAllocation>& filenames);
static bool ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path); static bool ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path);
static bool ShowFileExplorer(const StringView& path); static bool ShowFileExplorer(const StringView& path);
static void GetSpecialFolderPath(const SpecialFolder type, String& result);
}; };
#endif #endif

View File

@@ -339,11 +339,10 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale; float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
// Offset position to match lines origin space // Offset position to match lines origin space
Float2 rootOffset = layout.Bounds.Location + lines.First().Location; Float2 testPoint = location - layout.Bounds.Location;
Float2 testPoint = location - rootOffset;
// Get line which may intersect with the position (it's possible because lines have fixed height) // Get line which may intersect with the position (it's possible because lines have fixed height)
int32 lineIndex = Math::Clamp(Math::FloorToInt(testPoint.Y / baseLinesDistance), 0, lines.Count() - 1); int32 lineIndex = Math::Clamp(Math::FloorToInt((testPoint.Y - lines.First().Location.Y) / baseLinesDistance), 0, lines.Count() - 1);
const FontLineCache& line = lines[lineIndex]; const FontLineCache& line = lines[lineIndex];
float x = line.Location.X; float x = line.Location.X;
@@ -403,7 +402,19 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
{ {
// Check if there is no need to do anything // Check if there is no need to do anything
if (text.IsEmpty()) if (text.IsEmpty())
return layout.Bounds.Location; {
Float2 location = layout.Bounds.Location;
if (layout.VerticalAlignment == TextAlignment::Center)
location.Y += layout.Bounds.Size.Y * 0.5f - static_cast<float>(_height) * 0.5f;
else if (layout.VerticalAlignment == TextAlignment::Far)
location.Y += layout.Bounds.Size.Y - static_cast<float>(_height) * 0.5f;
if (layout.HorizontalAlignment == TextAlignment::Center)
location.X += layout.Bounds.Size.X * 0.5f;
else if (layout.HorizontalAlignment == TextAlignment::Far)
location.X += layout.Bounds.Size.X;
return location;
}
// Process text // Process text
Array<FontLineCache> lines; Array<FontLineCache> lines;
@@ -411,7 +422,6 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
ASSERT(lines.HasItems()); ASSERT(lines.HasItems());
float scale = layout.Scale / FontManager::FontScale; float scale = layout.Scale / FontManager::FontScale;
float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale; float baseLinesDistance = static_cast<float>(_height) * layout.BaseLinesGapScale * scale;
Float2 rootOffset = layout.Bounds.Location + lines.First().Location;
// Find line with that position // Find line with that position
FontCharacterEntry previous; FontCharacterEntry previous;
@@ -423,7 +433,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
// Check if desire position is somewhere inside characters in line range // Check if desire position is somewhere inside characters in line range
if (Math::IsInRange(index, line.FirstCharIndex, line.LastCharIndex)) if (Math::IsInRange(index, line.FirstCharIndex, line.LastCharIndex))
{ {
float x = line.Location.X; Float2 charPos = line.Location;
// Check all characters in the line // Check all characters in the line
for (int32 currentIndex = line.FirstCharIndex; currentIndex < index; currentIndex++) for (int32 currentIndex = line.FirstCharIndex; currentIndex < index; currentIndex++)
@@ -436,21 +446,21 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
// Apply kerning // Apply kerning
if (!isWhitespace && previous.IsValid) if (!isWhitespace && previous.IsValid)
{ {
x += entry.Font->GetKerning(previous.Character, entry.Character); charPos.X += entry.Font->GetKerning(previous.Character, entry.Character);
} }
previous = entry; previous = entry;
// Move // Move
x += entry.AdvanceX * scale; charPos.X += entry.AdvanceX * scale;
} }
// Upper left corner of the character // Upper left corner of the character
return rootOffset + Float2(x, static_cast<float>(lineIndex * baseLinesDistance)); return layout.Bounds.Location + charPos;
} }
} }
// Position after last character in the last line // Position after last character in the last line
return rootOffset + Float2(lines.Last().Size.X, static_cast<float>((lines.Count() - 1) * baseLinesDistance)); return layout.Bounds.Location + lines.Last().Location + Float2(lines.Last().Size.X, 0.0f);
} }
void Font::FlushFaceSize() const void Font::FlushFaceSize() const

View File

@@ -90,7 +90,7 @@ namespace FlaxEngine
/// <summary> /// <summary>
/// The size of the font characters. /// The size of the font characters.
/// </summary> /// </summary>
[EditorOrder(10), Limit(1, 500, 0.1f), Tooltip("The size of the font characters.")] [EditorOrder(10), Limit(1, 500, 0.5f), Tooltip("The size of the font characters.")]
public float Size public float Size
{ {
get => _size; get => _size;

View File

@@ -841,10 +841,11 @@ void Render2D::PushClip(const Rectangle& clipRect)
{ {
RENDER2D_CHECK_RENDERING_STATE; RENDER2D_CHECK_RENDERING_STATE;
const auto& mask = ClipLayersStack.Peek();
RotatedRectangle clipRectTransformed; RotatedRectangle clipRectTransformed;
ApplyTransform(clipRect, clipRectTransformed); ApplyTransform(clipRect, clipRectTransformed);
const Rectangle bounds = Rectangle::Shared(clipRectTransformed.ToBoundingRect(), ClipLayersStack.Peek().Bounds); const Rectangle bounds = Rectangle::Shared(clipRectTransformed.ToBoundingRect(), mask.Bounds);
ClipLayersStack.Push({ clipRectTransformed, bounds }); ClipLayersStack.Push({ RotatedRectangle::Shared(clipRectTransformed, mask.Bounds), bounds });
OnClipScissors(); OnClipScissors();
} }

View File

@@ -10,7 +10,6 @@
struct RotatedRectangle struct RotatedRectangle
{ {
public: public:
/// <summary> /// <summary>
/// The transformed top left corner. /// The transformed top left corner.
/// </summary> /// </summary>
@@ -27,7 +26,6 @@ public:
Float2 ExtentY; Float2 ExtentY;
public: public:
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RotatedRectangle"/> struct. /// Initializes a new instance of the <see cref="RotatedRectangle"/> struct.
/// </summary> /// </summary>
@@ -60,7 +58,6 @@ public:
} }
public: public:
/// <summary> /// <summary>
/// Convert rotated rectangle to the axis-aligned rectangle that builds rotated rectangle bounding box. /// Convert rotated rectangle to the axis-aligned rectangle that builds rotated rectangle bounding box.
/// </summary> /// </summary>
@@ -95,8 +92,24 @@ public:
return false; return false;
} }
public: /// <summary>
/// Calculates a rectangle that contains the shared part of both rectangles.
/// </summary>
/// <param name="a">The first rectangle.</param>
/// <param name="b">The second rectangle.</param>
/// <returns>Rectangle that contains shared part of a and b rectangles.</returns>
static RotatedRectangle Shared(const RotatedRectangle& a, const Rectangle& b)
{
// Clip the rotated rectangle bounds within the given AABB
RotatedRectangle result = a;
result.TopLeft = Float2::Max(a.TopLeft, b.GetTopLeft());
// TODO: do a little better math below (in case of actually rotated rectangle)
result.ExtentX.X = Math::Min(result.TopLeft.X + result.ExtentX.X, b.GetRight()) - result.TopLeft.X;
result.ExtentY.Y = Math::Min(result.TopLeft.Y + result.ExtentY.Y, b.GetBottom()) - result.TopLeft.Y;
return result;
}
public:
bool operator ==(const RotatedRectangle& other) const bool operator ==(const RotatedRectangle& other) const
{ {
return return

View File

@@ -1,10 +1,11 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System; using System;
using System.Runtime.CompilerServices;
namespace FlaxEngine namespace FlaxEngine
{ {
partial struct SpriteHandle partial struct SpriteHandle : IEquatable<SpriteHandle>
{ {
/// <summary> /// <summary>
/// Invalid sprite handle. /// Invalid sprite handle.
@@ -107,5 +108,53 @@ namespace FlaxEngine
Atlas.SetSprite(Index, ref sprite); Atlas.SetSprite(Index, ref sprite);
} }
} }
/// <summary>
/// Tests for equality between two objects.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns><c>true</c> if <paramref name="left" /> has the same value as <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator ==(SpriteHandle left, SpriteHandle right)
{
return left.Index == right.Index && left.Atlas == right.Atlas;
}
/// <summary>
/// Tests for inequality between two objects.
/// </summary>
/// <param name="left">The first value to compare.</param>
/// <param name="right">The second value to compare.</param>
/// <returns><c>true</c> if <paramref name="left" /> has a different value than <paramref name="right" />; otherwise, <c>false</c>.</returns>
public static bool operator !=(SpriteHandle left, SpriteHandle right)
{
return left.Index != right.Index || left.Atlas != right.Atlas;
}
/// <inheritdoc />
public bool Equals(SpriteHandle other)
{
return Index == other.Index && Atlas == other.Atlas;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return obj is SpriteHandle other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
return HashCode.Combine(Atlas, Index);
}
/// <inheritdoc />
public override string ToString()
{
if (Atlas)
return $"{System.IO.Path.GetFileNameWithoutExtension(Atlas.Path)}[{Index}]";
return "Invalid";
}
} }
} }

View File

@@ -757,43 +757,51 @@ bool DynamicDiffuseGlobalIlluminationPass::Render(RenderContext& renderContext,
if (_debugModel && _debugModel->IsLoaded() && _debugModel->CanBeRendered() && _debugMaterial && _debugMaterial->IsLoaded()) if (_debugModel && _debugModel->IsLoaded() && _debugModel->CanBeRendered() && _debugMaterial && _debugMaterial->IsLoaded())
{ {
RenderContext debugRenderContext(renderContext); RenderContext debugRenderContext(renderContext);
debugRenderContext.List = RenderList::GetFromPool();
debugRenderContext.View.Pass = DrawPass::GBuffer;
debugRenderContext.View.Prepare(debugRenderContext);
Matrix world; Matrix world;
Matrix::Scaling(Float3(0.2f), world); Matrix::Scaling(Float3(0.2f), world);
const Mesh& debugMesh = _debugModel->LODs[0].Meshes[0]; const Mesh& debugMesh = _debugModel->LODs[0].Meshes[0];
for (int32 probeIndex = 0; probeIndex < ddgiData.ProbesCountTotal; probeIndex++) constexpr int32 maxProbesPerDrawing = 32 * 1024;
debugMesh.Draw(debugRenderContext, _debugMaterial, world, StaticFlags::None, true, DrawPass::GBuffer, (float)probeIndex); int32 probesDrawingStart = 0;
debugRenderContext.List->SortDrawCalls(debugRenderContext, false, DrawCallsListType::GBuffer); while (probesDrawingStart < ddgiData.ProbesCountTotal)
context->SetViewportAndScissors(debugRenderContext.View.ScreenSize.X, debugRenderContext.View.ScreenSize.Y);
GPUTextureView* targetBuffers[5] =
{ {
lightBuffer, debugRenderContext.List = RenderList::GetFromPool();
renderContext.Buffers->GBuffer0->View(), debugRenderContext.View.Pass = DrawPass::GBuffer;
renderContext.Buffers->GBuffer1->View(), debugRenderContext.View.Prepare(debugRenderContext);
renderContext.Buffers->GBuffer2->View(), // TODO: refactor this into BatchedDrawCalls for faster draw calls processing
renderContext.Buffers->GBuffer3->View(), const int32 probesDrawingEnd = Math::Min(probesDrawingStart + maxProbesPerDrawing, ddgiData.ProbesCountTotal);
}; for (int32 probeIndex = probesDrawingStart; probeIndex < probesDrawingEnd; probeIndex++)
context->SetRenderTarget(*renderContext.Buffers->DepthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers))); debugMesh.Draw(debugRenderContext, _debugMaterial, world, StaticFlags::None, true, DrawPass::GBuffer, (float)probeIndex);
{ debugRenderContext.List->SortDrawCalls(debugRenderContext, false, DrawCallsListType::GBuffer);
// Pass DDGI data to the material context->SetViewportAndScissors(debugRenderContext.View.ScreenSize.X, debugRenderContext.View.ScreenSize.Y);
_debugMaterial->SetParameterValue(TEXT("ProbesData"), Variant(ddgiData.ProbesData)); GPUTextureView* targetBuffers[5] =
#if DDGI_DEBUG_INSTABILITY
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesInstability));
#else
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesIrradiance));
#endif
_debugMaterial->SetParameterValue(TEXT("ProbesDistance"), Variant(ddgiData.ProbesDistance));
auto cb = _debugMaterial->GetShader()->GetCB(3);
if (cb)
{ {
context->UpdateCB(cb, &ddgiData.Result.Constants); lightBuffer,
context->BindCB(3, cb); renderContext.Buffers->GBuffer0->View(),
renderContext.Buffers->GBuffer1->View(),
renderContext.Buffers->GBuffer2->View(),
renderContext.Buffers->GBuffer3->View(),
};
context->SetRenderTarget(*renderContext.Buffers->DepthBuffer, ToSpan(targetBuffers, ARRAY_COUNT(targetBuffers)));
{
// Pass DDGI data to the material
_debugMaterial->SetParameterValue(TEXT("ProbesData"), Variant(ddgiData.ProbesData));
#if DDGI_DEBUG_INSTABILITY
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesInstability));
#else
_debugMaterial->SetParameterValue(TEXT("ProbesIrradiance"), Variant(ddgiData.ProbesIrradiance));
#endif
_debugMaterial->SetParameterValue(TEXT("ProbesDistance"), Variant(ddgiData.ProbesDistance));
auto cb = _debugMaterial->GetShader()->GetCB(3);
if (cb)
{
context->UpdateCB(cb, &ddgiData.Result.Constants);
context->BindCB(3, cb);
}
} }
debugRenderContext.List->ExecuteDrawCalls(debugRenderContext, DrawCallsListType::GBuffer);
RenderList::ReturnToPool(debugRenderContext.List);
probesDrawingStart = probesDrawingEnd;
} }
debugRenderContext.List->ExecuteDrawCalls(debugRenderContext, DrawCallsListType::GBuffer);
RenderList::ReturnToPool(debugRenderContext.List);
} }
} }
#endif #endif

View File

@@ -318,6 +318,11 @@ void ProbesRendererService::Update()
// Calculate time delta since last update // Calculate time delta since last update
auto timeNow = Time::Update.UnscaledTime; auto timeNow = Time::Update.UnscaledTime;
auto timeSinceUpdate = timeNow - _lastProbeUpdate; auto timeSinceUpdate = timeNow - _lastProbeUpdate;
if (timeSinceUpdate < 0)
{
_lastProbeUpdate = timeNow;
timeSinceUpdate = 0;
}
// Check if render job is done // Check if render job is done
if (isUpdateSynced()) if (isUpdateSynced())
@@ -351,8 +356,9 @@ void ProbesRendererService::Update()
auto dt = (float)Time::Update.UnscaledDeltaTime.GetTotalSeconds(); auto dt = (float)Time::Update.UnscaledDeltaTime.GetTotalSeconds();
for (int32 i = 0; i < _probesToBake.Count(); i++) for (int32 i = 0; i < _probesToBake.Count(); i++)
{ {
_probesToBake[i].Timeout -= dt; auto& e = _probesToBake[i];
if (_probesToBake[i].Timeout <= 0) e.Timeout -= dt;
if (e.Timeout <= 0)
{ {
firstValidEntryIndex = i; firstValidEntryIndex = i;
break; break;
@@ -417,6 +423,9 @@ void ProbesRenderer::OnRender(RenderTask* task, GPUContext* context)
if (_current.Actor == nullptr) if (_current.Actor == nullptr)
{ {
// Probe has been unlinked (or deleted) // Probe has been unlinked (or deleted)
_task->Enabled = false;
_updateFrameNumber = 0;
_current.Type = EntryType::Invalid;
return; return;
} }
break; break;

View File

@@ -28,21 +28,9 @@ public:
struct Entry struct Entry
{ {
EntryType Type; EntryType Type = EntryType::Invalid;
ScriptingObjectReference<Actor> Actor; ScriptingObjectReference<Actor> Actor;
float Timeout; float Timeout = 0.0f;
Entry()
{
Type = EntryType::Invalid;
}
Entry(const Entry& other)
{
Type = other.Type;
Actor = other.Actor;
Timeout = other.Timeout;
}
bool UseTextureData() const; bool UseTextureData() const;
int32 GetResolution() const; int32 GetResolution() const;

View File

@@ -887,7 +887,8 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
// Check type // Check type
if (!type || result->Is(type)) if (!type || result->Is(type))
return result; return result;
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", id, String(result->GetType().Fullname), String(type->GetFullName()), LogContext::GetInfo()); LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}", id, String(result->GetType().Fullname), String(type->GetFullName()));
LogContext::Print(LogType::Warning);
return nullptr; return nullptr;
} }
@@ -906,7 +907,8 @@ ScriptingObject* Scripting::FindObject(Guid id, const MClass* type)
return asset; return asset;
} }
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", id, String(type->GetFullName()), LogContext::GetInfo()); LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}", id, String(type->GetFullName()));
LogContext::Print(LogType::Warning);
return nullptr; return nullptr;
} }

View File

@@ -737,7 +737,10 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject*
if (klass && !obj->Is(klass)) if (klass && !obj->Is(klass))
{ {
if (!skipLog) if (!skipLog)
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", *id, String(obj->GetType().Fullname), String(klass->GetFullName()), LogContext::GetInfo()); {
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}", *id, String(obj->GetType().Fullname), String(klass->GetFullName()));
LogContext::Print(LogType::Warning);
}
return nullptr; return nullptr;
} }
return obj->GetOrCreateManagedInstance(); return obj->GetOrCreateManagedInstance();
@@ -746,9 +749,10 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject*
if (!skipLog) if (!skipLog)
{ {
if (klass) if (klass)
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", *id, String(klass->GetFullName()), LogContext::GetInfo()); LOG(Warning, "Unable to find scripting object with ID={0} of type {1}", *id, String(klass->GetFullName()));
else else
LOG(Warning, "Unable to find scripting object with ID={0}", *id); LOG(Warning, "Unable to find scripting object with ID={0}", *id);
LogContext::Print(LogType::Warning);
} }
return nullptr; return nullptr;
} }

View File

@@ -22,8 +22,6 @@ public:
virtual void ReadBytes(void* data, uint32 bytes) = 0; virtual void ReadBytes(void* data, uint32 bytes) = 0;
public: public:
// Reads byte from the stream
// @returns Single byte
byte ReadByte() byte ReadByte()
{ {
byte data; byte data;
@@ -31,7 +29,6 @@ public:
return data; return data;
} }
// Reads bool from the stream
bool ReadBool() bool ReadBool()
{ {
byte data; byte data;
@@ -39,8 +36,6 @@ public:
return data != 0; return data != 0;
} }
// Reads char from the stream
// @param data Data to read
char ReadChar() char ReadChar()
{ {
char data; char data;
@@ -53,78 +48,56 @@ public:
ReadBytes(data, sizeof(byte)); ReadBytes(data, sizeof(byte));
} }
// Reads Char from the stream
// @param data Data to read
FORCE_INLINE void ReadChar(Char* data) FORCE_INLINE void ReadChar(Char* data)
{ {
ReadBytes(data, sizeof(Char)); ReadBytes(data, sizeof(Char));
} }
// Reads uint8 from the stream
// @param data Data to read
FORCE_INLINE void ReadUint8(uint8* data) FORCE_INLINE void ReadUint8(uint8* data)
{ {
ReadBytes(data, sizeof(uint8)); ReadBytes(data, sizeof(uint8));
} }
// Reads int8 from the stream
// @param data Data to read
FORCE_INLINE void ReadInt8(int8* data) FORCE_INLINE void ReadInt8(int8* data)
{ {
ReadBytes(data, sizeof(int8)); ReadBytes(data, sizeof(int8));
} }
// Reads uint16 from the stream
// @param data Data to read
FORCE_INLINE void ReadUint16(uint16* data) FORCE_INLINE void ReadUint16(uint16* data)
{ {
ReadBytes(data, sizeof(uint16)); ReadBytes(data, sizeof(uint16));
} }
// Reads int16 from the stream
// @param data Data to read
FORCE_INLINE void ReadInt16(int16* data) FORCE_INLINE void ReadInt16(int16* data)
{ {
ReadBytes(data, sizeof(int16)); ReadBytes(data, sizeof(int16));
} }
// Reads uint32 from the stream
// @param data Data to read
FORCE_INLINE void ReadUint32(uint32* data) FORCE_INLINE void ReadUint32(uint32* data)
{ {
ReadBytes(data, sizeof(uint32)); ReadBytes(data, sizeof(uint32));
} }
// Reads int32 from the stream
// @param data Data to read
FORCE_INLINE void ReadInt32(int32* data) FORCE_INLINE void ReadInt32(int32* data)
{ {
ReadBytes(data, sizeof(int32)); ReadBytes(data, sizeof(int32));
} }
// Reads uint64 from the stream
// @param data Data to read
FORCE_INLINE void ReadUint64(uint64* data) FORCE_INLINE void ReadUint64(uint64* data)
{ {
ReadBytes(data, sizeof(uint64)); ReadBytes(data, sizeof(uint64));
} }
// Reads int64 from the stream
// @param data Data to read
FORCE_INLINE void ReadInt64(int64* data) FORCE_INLINE void ReadInt64(int64* data)
{ {
ReadBytes(data, sizeof(int64)); ReadBytes(data, sizeof(int64));
} }
// Reads float from the stream
// @param data Data to read
FORCE_INLINE void ReadFloat(float* data) FORCE_INLINE void ReadFloat(float* data)
{ {
ReadBytes(data, sizeof(float)); ReadBytes(data, sizeof(float));
} }
// Reads double from the stream
// @param data Data to read
FORCE_INLINE void ReadDouble(double* data) FORCE_INLINE void ReadDouble(double* data)
{ {
ReadBytes(data, sizeof(double)); ReadBytes(data, sizeof(double));
@@ -160,6 +133,7 @@ public:
Read(ptr); Read(ptr);
v = ptr; v = ptr;
} }
template<typename T> template<typename T>
FORCE_INLINE void Read(SoftObjectReference<T>& v) FORCE_INLINE void Read(SoftObjectReference<T>& v)
{ {
@@ -167,6 +141,7 @@ public:
ReadBytes(id, sizeof(id)); ReadBytes(id, sizeof(id));
v.Set(*(Guid*)id); v.Set(*(Guid*)id);
} }
template<typename T> template<typename T>
FORCE_INLINE void Read(AssetReference<T>& v) FORCE_INLINE void Read(AssetReference<T>& v)
{ {
@@ -174,6 +149,7 @@ public:
ReadBytes(id, sizeof(id)); ReadBytes(id, sizeof(id));
v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer); v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer);
} }
template<typename T> template<typename T>
FORCE_INLINE void Read(WeakAssetReference<T>& v) FORCE_INLINE void Read(WeakAssetReference<T>& v)
{ {
@@ -181,6 +157,7 @@ public:
ReadBytes(id, sizeof(id)); ReadBytes(id, sizeof(id));
v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer); v = (T*)::LoadAsset(*(Guid*)id, T::TypeInitializer);
} }
template<typename T> template<typename T>
FORCE_INLINE void Read(SoftAssetReference<T>& v) FORCE_INLINE void Read(SoftAssetReference<T>& v)
{ {
@@ -240,38 +217,30 @@ public:
public: public:
// Reads StringAnsi from the stream // Reads StringAnsi from the stream
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
void ReadStringAnsi(StringAnsi* data); void ReadStringAnsi(StringAnsi* data);
// Reads StringAnsi from the stream with a key // Reads StringAnsi from the stream with a key
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
void ReadStringAnsi(StringAnsi* data, int8 lock); void ReadStringAnsi(StringAnsi* data, int8 lock);
// Reads String from the stream // Reads String from the stream
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
void ReadString(String* data); void ReadString(String* data);
// Reads String from the stream // Reads String from the stream
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
// @param lock Characters pass in the stream
void ReadString(String* data, int16 lock); void ReadString(String* data, int16 lock);
// Reads CommonValue from the stream // Reads CommonValue from the stream
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
void ReadCommonValue(CommonValue* data); void ReadCommonValue(CommonValue* data);
// Reads VariantType from the stream // Reads VariantType from the stream
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
void ReadVariantType(VariantType* data); void ReadVariantType(VariantType* data);
// Reads Variant from the stream // Reads Variant from the stream
/// [Deprecated on 11.10.2022, expires on 11.10.2024] /// [Deprecated on 11.10.2022, expires on 11.10.2024]
// @param data Data to read
void ReadVariant(Variant* data); void ReadVariant(Variant* data);
/// <summary> /// <summary>

View File

@@ -13,35 +13,44 @@
class IRunnable : public Object class IRunnable : public Object
{ {
public: public:
// Virtual destructor // Virtual destructor
virtual ~IRunnable() virtual ~IRunnable()
{ {
} }
// Initializes the runnable object /// <summary>
// @return True if initialization was successful, otherwise false /// Initializes the runnable object.
/// </summary>
/// <returns>True if initialization was successful, otherwise false.</returns>
virtual bool Init() virtual bool Init()
{ {
return true; return true;
} }
// Runs the runnable object. /// <summary>
// @return The exit code /// Executes the runnable object.
/// </summary>
/// <returns>The exit code. Non-zero on error.</returns>
virtual int32 Run() = 0; virtual int32 Run() = 0;
// Stops the runnable object. Called when thread is being terminated /// <summary>
/// Stops the runnable object. Called when thread is being terminated
/// </summary>
virtual void Stop() virtual void Stop()
{ {
} }
// Exits the runnable object /// <summary>
/// Exits the runnable object
/// </summary>
virtual void Exit() virtual void Exit()
{ {
} }
// Called when thread ends work (via Kill or normally) /// <summary>
// @param wasKilled True if thread has been killed /// Called when thread ends work (via Kill or normally).
/// </summary>
/// <param name="wasKilled">True if thread has been killed.</param>
virtual void AfterWork(bool wasKilled) virtual void AfterWork(bool wasKilled)
{ {
} }
@@ -53,18 +62,15 @@ public:
class SimpleRunnable : public IRunnable class SimpleRunnable : public IRunnable
{ {
private: private:
bool _autoDelete; bool _autoDelete;
public: public:
/// <summary> /// <summary>
/// Working function /// Working function
/// </summary> /// </summary>
Function<int32()> OnWork; Function<int32()> OnWork;
public: public:
/// <summary> /// <summary>
/// Init /// Init
/// </summary> /// </summary>
@@ -75,7 +81,6 @@ public:
} }
public: public:
// [IRunnable] // [IRunnable]
String ToString() const override String ToString() const override
{ {

View File

@@ -183,11 +183,15 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
break; break;
// Pre-skinned Local Position // Pre-skinned Local Position
case 13: case 13:
value = _treeType == MaterialTreeType::VertexShader ? Value(VariantType::Float3, TEXT("input.PreSkinnedPosition")) : Value::Zero; value = Value(VariantType::Float3, TEXT("input.PreSkinnedPosition"));
if (_treeType != MaterialTreeType::VertexShader)
value = VsToPs(node, box).AsFloat3();
break; break;
// Pre-skinned Local Normal // Pre-skinned Local Normal
case 14: case 14:
value = _treeType == MaterialTreeType::VertexShader ? Value(VariantType::Float3, TEXT("input.PreSkinnedNormal")) : Value::Zero; value = Value(VariantType::Float3, TEXT("input.PreSkinnedNormal"));
if (_treeType != MaterialTreeType::VertexShader)
value = VsToPs(node, box).AsFloat3();
break; break;
// Depth // Depth
case 15: case 15:
@@ -211,38 +215,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
break; break;
// Interpolate VS To PS // Interpolate VS To PS
case 20: case 20:
{ value = VsToPs(node, node->GetBox(0));
const auto input = node->GetBox(0);
// If used in VS then pass the value from the input box
if (_treeType == MaterialTreeType::VertexShader)
{
value = tryGetValue(input, Value::Zero).AsFloat4();
break;
}
// Check if can use more interpolants
if (_vsToPsInterpolants.Count() == 16)
{
OnError(node, box, TEXT("Too many VS to PS interpolants used."));
value = Value::Zero;
break;
}
// Check if can use interpolants
const auto layer = GetRootLayer();
if (!layer || layer->Domain == MaterialDomain::Decal || layer->Domain == MaterialDomain::PostProcess)
{
OnError(node, box, TEXT("VS to PS interpolants are not supported in Decal or Post Process materials."));
value = Value::Zero;
break;
}
// Indicate the interpolator slot usage
value = Value(VariantType::Float4, String::Format(TEXT("input.CustomVSToPS[{0}]"), _vsToPsInterpolants.Count()));
_vsToPsInterpolants.Add(input);
break; break;
}
// Terrain Holes Mask // Terrain Holes Mask
case 21: case 21:
{ {

View File

@@ -832,4 +832,32 @@ void MaterialGenerator::WriteCustomGlobalCode(const Array<const MaterialGraph::N
} }
} }
ShaderGenerator::Value MaterialGenerator::VsToPs(Node* node, Box* input)
{
// If used in VS then pass the value from the input box
if (_treeType == MaterialTreeType::VertexShader)
{
return tryGetValue(input, Value::Zero).AsFloat4();
}
// Check if can use more interpolants
if (_vsToPsInterpolants.Count() == 16)
{
OnError(node, input, TEXT("Too many VS to PS interpolants used."));
return Value::Zero;
}
// Check if can use interpolants
const auto layer = GetRootLayer();
if (!layer || layer->Domain == MaterialDomain::Decal || layer->Domain == MaterialDomain::PostProcess)
{
OnError(node, input, TEXT("VS to PS interpolants are not supported in Decal or Post Process materials."));
return Value::Zero;
}
// Indicate the interpolator slot usage
_vsToPsInterpolants.Add(input);
return Value(VariantType::Float4, String::Format(TEXT("input.CustomVSToPS[{0}]"), _vsToPsInterpolants.Count() - 1));
}
#endif #endif

View File

@@ -205,6 +205,7 @@ private:
MaterialValue AccessParticleAttribute(Node* caller, const StringView& name, ParticleAttributeValueTypes valueType, const Char* index = nullptr, ParticleAttributeSpace space = ParticleAttributeSpace::AsIs); MaterialValue AccessParticleAttribute(Node* caller, const StringView& name, ParticleAttributeValueTypes valueType, const Char* index = nullptr, ParticleAttributeSpace space = ParticleAttributeSpace::AsIs);
void prepareLayer(MaterialLayer* layer, bool allowVisibleParams); void prepareLayer(MaterialLayer* layer, bool allowVisibleParams);
void WriteCustomGlobalCode(const Array<const MaterialGraph::Node*, InlinedAllocation<8>>& nodes, int32 templateInputsMapping); void WriteCustomGlobalCode(const Array<const MaterialGraph::Node*, InlinedAllocation<8>>& nodes, int32 templateInputsMapping);
Value VsToPs(Node* node, Box* input);
public: public:

View File

@@ -380,7 +380,7 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, ModelData* modelData, float
sdf.ResolutionScale = resolutionScale; sdf.ResolutionScale = resolutionScale;
sdf.LOD = lodIndex; sdf.LOD = lodIndex;
const int32 maxMips = 3; const int32 maxMips = 3;
const int32 mipCount = Math::Min(MipLevelsCount(resolution.X, resolution.Y, resolution.Z, true), maxMips); const int32 mipCount = Math::Min(MipLevelsCount(resolution.X, resolution.Y, resolution.Z), maxMips);
PixelFormat format = PixelFormat::R16_UNorm; PixelFormat format = PixelFormat::R16_UNorm;
int32 formatStride = 2; int32 formatStride = 2;
float formatMaxValue = MAX_uint16; float formatMaxValue = MAX_uint16;

View File

@@ -710,7 +710,7 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
bool hasSourceMipLevels = isPowerOfTwo && sourceMipLevels > 1; bool hasSourceMipLevels = isPowerOfTwo && sourceMipLevels > 1;
bool useMipLevels = isPowerOfTwo && (options.GenerateMipMaps || hasSourceMipLevels) && (width > 1 || height > 1); bool useMipLevels = isPowerOfTwo && (options.GenerateMipMaps || hasSourceMipLevels) && (width > 1 || height > 1);
int32 arraySize = (int32)currentImage->GetMetadata().arraySize; int32 arraySize = (int32)currentImage->GetMetadata().arraySize;
int32 mipLevels = MipLevelsCount(width, height, useMipLevels); int32 mipLevels = useMipLevels ? MipLevelsCount(width, height) : 1;
if (useMipLevels && !options.GenerateMipMaps && mipLevels != sourceMipLevels) if (useMipLevels && !options.GenerateMipMaps && mipLevels != sourceMipLevels)
{ {
errorMsg = String::Format(TEXT("Imported texture has not full mip chain, loaded mips count: {0}, expected: {1}"), sourceMipLevels, mipLevels); errorMsg = String::Format(TEXT("Imported texture has not full mip chain, loaded mips count: {0}, expected: {1}"), sourceMipLevels, mipLevels);

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