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

This commit is contained in:
Wojtek Figat
2023-02-19 15:04:02 +01:00
57 changed files with 784 additions and 513 deletions

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

BIN
Content/Shaders/GI/DDGI.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@@ -174,9 +174,7 @@ namespace FlaxEditor.CustomEditors.Editors
for (int i = 0; i < subInputs.Length; i++)
{
if (string.IsNullOrEmpty(subInputs[i]))
{
continue;
}
// Check all entered subtags and create any that dont exist
for (int j = 0; j <= i; j++)
@@ -296,6 +294,7 @@ namespace FlaxEditor.CustomEditors.Editors
settingsObj.Tags.Add(tagName);
settingsObj.Tags.Sort();
settingsAsset.SetInstance(settingsObj);
settingsAsset.Save();
// Reload editor window to reflect new tag
assetWindow?.RefreshAsset();
@@ -417,7 +416,6 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (!uniqueText)
return;
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
};

View File

@@ -1,3 +1,7 @@
#if PLATFORM_WINDOWS
#define USE_IS_FOREGROUND
#else
#endif
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
@@ -326,6 +330,7 @@ namespace FlaxEditor.GUI.ContextMenu
// Nothing to do
}
#if USE_IS_FOREGROUND
/// <summary>
/// Returns true if context menu is in foreground (eg. context window or any child window has user focus or user opened additional popup within this context).
/// </summary>
@@ -385,6 +390,57 @@ namespace FlaxEditor.GUI.ContextMenu
}
}
}
#else
private void OnWindowGotFocus()
{
}
private void OnWindowLostFocus()
{
// Skip for parent menus (child should handle lost of focus)
if (_childCM != null)
return;
if (_parentCM != null)
{
if (IsMouseOver)
return;
// Check if any external popup is focused
foreach (var externalPopup in ExternalPopups)
{
if (externalPopup && externalPopup.IsFocused)
return;
}
// Check if mouse is over any of the parents
ContextMenuBase focusCM = null;
var cm = _parentCM;
while (cm != null)
{
if (cm.IsMouseOver)
focusCM = cm;
cm = cm._parentCM;
}
if (focusCM != null)
{
// Focus on the clicked parent and hide any open sub-menus
focusCM.HideChild();
focusCM._window?.Focus();
}
else
{
// User clicked outside the context menus, hide the whole context menu tree
TopmostCM.Hide();
}
}
else if (!IsMouseOver)
{
Hide();
}
}
#endif
/// <inheritdoc />
public override bool IsMouseOver
@@ -405,6 +461,7 @@ namespace FlaxEditor.GUI.ContextMenu
}
}
#if USE_IS_FOREGROUND
/// <inheritdoc />
public override void Update(float deltaTime)
{
@@ -416,6 +473,7 @@ namespace FlaxEditor.GUI.ContextMenu
Hide();
}
}
#endif
/// <inheritdoc />
public override void Draw()

View File

@@ -55,8 +55,6 @@ namespace FlaxEditor.GUI.ContextMenu
/// <inheritdoc />
public override void OnMouseEnter(Float2 location)
{
base.OnMouseEnter(location);
// Skip if has no children
if (ContextMenu.HasChildren == false)
return;
@@ -66,8 +64,28 @@ namespace FlaxEditor.GUI.ContextMenu
if (parentContextMenu == ContextMenu)
return;
if (ContextMenu.IsOpened)
return;
base.OnMouseEnter(location);
// Hide parent CM popups and set itself as child
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
}
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
// Skip if already shown
var parentContextMenu = ParentContextMenu;
if (parentContextMenu == ContextMenu)
return true;
if (ContextMenu.IsOpened)
return true;
// Hide parent CM popups and set itself as child
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
return base.OnMouseUp(location, button);
}
}
}

View File

@@ -700,6 +700,8 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{
UpdateMouseOverFlags(location);
// Check if mouse hits bar and node isn't a root
if (_mouseOverHeader)
{
@@ -728,6 +730,8 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{
UpdateMouseOverFlags(location);
// Clear flag for left button
if (button == MouseButton.Left)
{
@@ -815,21 +819,7 @@ namespace FlaxEditor.GUI.Tree
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{
// Cache flags
_mouseOverArrow = HasAnyVisibleChild && ArrowRect.Contains(location);
_mouseOverHeader = new Rectangle(0, 0, Width, _headerHeight - 1).Contains(location);
if (_mouseOverHeader)
{
// Allow non-scrollable controls to stay on top of the header and override the mouse behaviour
for (int i = 0; i < Children.Count; i++)
{
if (!Children[i].IsScrollable && IntersectsChildContent(Children[i], location, out _))
{
_mouseOverHeader = false;
break;
}
}
}
UpdateMouseOverFlags(location);
// Check if start drag and drop
if (_isMouseDown && Float2.Distance(_mouseDownPos, location) > 10.0f)
@@ -852,6 +842,25 @@ namespace FlaxEditor.GUI.Tree
}
}
private void UpdateMouseOverFlags(Vector2 location)
{
// Cache flags
_mouseOverArrow = HasAnyVisibleChild && ArrowRect.Contains(location);
_mouseOverHeader = new Rectangle(0, 0, Width, _headerHeight - 1).Contains(location);
if (_mouseOverHeader)
{
// Allow non-scrollable controls to stay on top of the header and override the mouse behaviour
for (int i = 0; i < Children.Count; i++)
{
if (!Children[i].IsScrollable && IntersectsChildContent(Children[i], location, out _))
{
_mouseOverHeader = false;
break;
}
}
}
}
/// <inheritdoc />
public override void OnMouseLeave()
{

View File

@@ -6,44 +6,48 @@ namespace FlaxEditor.Gizmo
{
public partial class TransformGizmoBase
{
private Model _modelTranslateAxis;
// Models
private Model _modelTranslationAxis;
private Model _modelScaleAxis;
private Model _modelBox;
private Model _modelCircle;
private Model _modelRotationAxis;
private Model _modelSphere;
private Model _modelCube;
// Materials
private MaterialInstance _materialAxisX;
private MaterialInstance _materialAxisY;
private MaterialInstance _materialAxisZ;
private MaterialInstance _materialAxisFocus;
private MaterialBase _materialWire;
private MaterialBase _materialWireFocus;
private MaterialBase _materialSphere;
private void InitDrawing()
{
// Load content (but async - don't wait and don't block execution)
_modelTranslateAxis = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/TranslateAxis");
// Axis Models
_modelTranslationAxis = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/TranslationAxis");
_modelScaleAxis = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/ScaleAxis");
_modelBox = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/WireBox");
_modelCircle = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/WireCircle");
_modelRotationAxis = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Gizmo/RotationAxis");
_modelSphere = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/Sphere");
_modelCube = FlaxEngine.Content.LoadAsyncInternal<Model>("Editor/Primitives/Cube");
// Axis Materials
_materialAxisX = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisX");
_materialAxisY = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisY");
_materialAxisZ = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisZ");
_materialAxisFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialAxisFocus");
_materialWire = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>("Editor/Gizmo/MaterialWire");
_materialWireFocus = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>("Editor/Gizmo/MaterialWireFocus");
_materialSphere = FlaxEngine.Content.LoadAsyncInternal<MaterialInstance>("Editor/Gizmo/MaterialSphere");
// Ensure that every asset was loaded
if (_modelTranslateAxis == null ||
if (_modelTranslationAxis == null ||
_modelScaleAxis == null ||
_modelBox == null ||
_modelCircle == null ||
_modelRotationAxis == null ||
_modelSphere == null ||
_modelCube == null ||
_materialAxisX == null ||
_materialAxisY == null ||
_materialAxisZ == null ||
_materialAxisFocus == null ||
_materialWire == null ||
_materialWireFocus == null)
_materialSphere == null)
{
// Error
Platform.Fatal("Failed to load transform gizmo resources.");
}
}
@@ -58,6 +62,8 @@ namespace FlaxEditor.Gizmo
// https://github.com/FlaxEngine/FlaxEngine/issues/680
Matrix m1, m2, m3, mx1;
float boxScale = 300f;
float boxSize = 0.085f;
bool isXAxis = _activeAxis == Axis.X || _activeAxis == Axis.XY || _activeAxis == Axis.ZX;
bool isYAxis = _activeAxis == Axis.Y || _activeAxis == Axis.XY || _activeAxis == Axis.YZ;
bool isZAxis = _activeAxis == Axis.Z || _activeAxis == Axis.YZ || _activeAxis == Axis.ZX;
@@ -65,114 +71,137 @@ namespace FlaxEditor.Gizmo
renderContext.View.GetWorldMatrix(ref _gizmoWorld, out Matrix world);
const float gizmoModelsScale2RealGizmoSize = 0.075f;
Mesh sphereMesh, cubeMesh;
switch (_activeMode)
{
case Mode.Translate:
{
if (!_modelTranslateAxis || !_modelTranslateAxis.IsLoaded || !_modelBox || !_modelBox.IsLoaded)
if (!_modelTranslationAxis || !_modelTranslationAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var transAxisMesh = _modelTranslationAxis.LODs[0].Meshes[0];
cubeMesh = _modelCube.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 += 0.05f;
var axisMesh = _modelTranslateAxis.LODs[0].Meshes[0];
var boxMesh = _modelBox.LODs[0].Meshes[0];
var boxSize = 10.0f;
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, 1.0f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * 0.5f, boxSize * 0.5f, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
boxMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialWireFocus : _materialWire, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, 1.0f, boxSize), Quaternion.Identity, new Vector3(boxSize * 0.5f, 0.0f, boxSize * 0.5f));
Matrix.Multiply(ref m2, ref m1, out m3);
boxMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialWireFocus : _materialWire, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, 1.0f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * 0.5f, boxSize * 0.5f));
Matrix.Multiply(ref m2, ref m1, out m3);
boxMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialWireFocus : _materialWire, ref m3);
// X axis
axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref mx1);
// Y axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
axisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// Z axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
axisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
transAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
transAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
// Center sphere
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
case Mode.Rotate:
{
if (!_modelCircle || !_modelCircle.IsLoaded || !_modelBox || !_modelBox.IsLoaded)
if (!_modelRotationAxis || !_modelRotationAxis.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var circleMesh = _modelCircle.LODs[0].Meshes[0];
var boxMesh = _modelBox.LODs[0].Meshes[0];
Matrix.Scaling(8.0f, out m3);
var rotationAxisMesh = _modelRotationAxis.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 += 0.05f;
// X axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
circleMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
rotationAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis
circleMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
rotationAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m1);
// Z axis
Matrix.RotationX(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
circleMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
rotationAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// Center box
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
Matrix.Scaling(1.0f, out m2);
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
boxMesh.Draw(ref renderContext, isCenter ? _materialWireFocus : _materialWire, ref m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}
case Mode.Scale:
{
if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelBox || !_modelBox.IsLoaded)
if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelCube || !_modelCube.IsLoaded || !_modelSphere || !_modelSphere.IsLoaded)
break;
var scaleAxisMesh = _modelScaleAxis.LODs[0].Meshes[0];
cubeMesh = _modelCube.LODs[0].Meshes[0];
sphereMesh = _modelSphere.LODs[0].Meshes[0];
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 -= 0.05f;
var axisMesh = _modelScaleAxis.LODs[0].Meshes[0];
var boxMesh = _modelBox.LODs[0].Meshes[0];
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref mx1, out m3);
axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
scaleAxisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis
Matrix.RotationX(Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
axisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
scaleAxisMesh.Draw(ref renderContext, isYAxis ? _materialAxisFocus : _materialAxisY, ref m3);
// Z axis
Matrix.RotationX(Mathf.Pi, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
axisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
scaleAxisMesh.Draw(ref renderContext, isZAxis ? _materialAxisFocus : _materialAxisZ, ref m3);
// XY plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationX(Mathf.PiOverTwo), new Vector3(boxSize * boxScale, boxSize * boxScale, 0.0f));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.XY ? _materialAxisFocus : _materialAxisX, ref m3);
// ZX plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.Identity, new Vector3(boxSize * boxScale, 0.0f, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.ZX ? _materialAxisFocus : _materialAxisZ, ref m3);
// YZ plane
m2 = Matrix.Transformation(new Vector3(boxSize, boxSize * 0.1f, boxSize), Quaternion.RotationZ(Mathf.PiOverTwo), new Vector3(0.0f, boxSize * boxScale, boxSize * boxScale));
Matrix.Multiply(ref m2, ref m1, out m3);
cubeMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialAxisFocus : _materialAxisY, ref m3);
// Center box
Matrix.Scaling(10.0f, out m2);
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
boxMesh.Draw(ref renderContext, isCenter ? _materialWireFocus : _materialWire, ref m3);
sphereMesh.Draw(ref renderContext, isCenter ? _materialAxisFocus : _materialSphere, ref m3);
break;
}

View File

@@ -102,9 +102,15 @@ namespace FlaxEditor.Gizmo
closestIntersection = intersection;
}
/*// Center
if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
{
_activeAxis = Axis.Center;
closestIntersection = intersection;
}*/
break;
}
case Mode.Rotate:
{
// Circles
@@ -124,41 +130,53 @@ namespace FlaxEditor.Gizmo
closestIntersection = intersection;
}
// Center
/*if (CenterSphere.Intersects(ref ray, out intersection) && intersection < closestintersection)
{
_activeAxis = Axis.Center;
closestintersection = intersection;
}*/
break;
}
case Mode.Scale:
{
// Spheres collision
if (ScaleXSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection)
// Boxes collision
if (XAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
{
_activeAxis = Axis.X;
closestIntersection = intersection;
}
if (ScaleYSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection)
if (YAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
{
_activeAxis = Axis.Y;
closestIntersection = intersection;
}
if (ScaleZSphere.Intersects(ref ray, out intersection) && intersection < closestIntersection)
if (ZAxisBox.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
{
_activeAxis = Axis.Z;
closestIntersection = intersection;
}
// Center
if (CenterBox.Intersects(ref ray, out intersection) && intersection < closestIntersection)
// Quad planes collision
if (closestIntersection >= float.MaxValue)
closestIntersection = float.MinValue;
if (XYBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
{
_activeAxis = Axis.XY;
closestIntersection = intersection;
}
if (XZBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
{
_activeAxis = Axis.ZX;
closestIntersection = intersection;
}
if (YZBox.Intersects(ref localRay, out intersection) && intersection > closestIntersection)
{
_activeAxis = Axis.YZ;
closestIntersection = intersection;
}
/*// Center
if (CenterBoxRaw.Intersects(ref localRay, out intersection) && intersection < closestIntersection)
{
_activeAxis = Axis.Center;
closestIntersection = intersection;
}
}*/
break;
}

View File

@@ -7,56 +7,53 @@ namespace FlaxEditor.Gizmo
{
public partial class TransformGizmoBase
{
private const float GizmoScaleFactor = 18;
private const float LineLength = 3.0f;
private const float LineOffset = 1.0f;
private const float MultiAxisThickness = 0.05f;
private const float SingleAxisThickness = 0.3f;
private const float ScaleSpheresRadius = 0.7f;
private const float CenterBoxSize = 0.8f;
private const float CenterSphereRadius = 0.1f;
private const float HalfLineOffset = LineOffset / 2;
/// <summary>
/// Scale of the gizmo itself
/// </summary>
private const float GizmoScaleFactor = 24;
private readonly Vector3[] _translationLineVertices =
{
// -- X Axis -- // index 0 - 5
new Vector3(HalfLineOffset, 0, 0),
new Vector3(LineLength, 0, 0),
new Vector3(LineOffset, 0, 0),
new Vector3(LineOffset, LineOffset, 0),
new Vector3(LineOffset, 0, 0),
new Vector3(LineOffset, 0, LineOffset),
/// <summary>
/// The length of each axis (outwards)
/// </summary>
private const float AxisLength = 3.5f;
// -- Y Axis -- // index 6 - 11
new Vector3(0, HalfLineOffset, 0),
new Vector3(0, LineLength, 0),
new Vector3(0, LineOffset, 0),
new Vector3(LineOffset, LineOffset, 0),
new Vector3(0, LineOffset, 0),
new Vector3(0, LineOffset, LineOffset),
/// <summary>
/// Offset to move axis away from center
/// </summary>
private const float AxisOffset = 0.8f;
// -- Z Axis -- // index 12 - 17
new Vector3(0, 0, HalfLineOffset),
new Vector3(0, 0, LineLength),
new Vector3(0, 0, LineOffset),
new Vector3(LineOffset, 0, LineOffset),
new Vector3(0, 0, LineOffset),
new Vector3(0, LineOffset, LineOffset)
};
/// <summary>
/// How thick the axis should be
/// </summary>
private const float AxisThickness = 0.3f;
private BoundingBox XAxisBox = new BoundingBox(new Vector3(LineOffset, -SingleAxisThickness, -SingleAxisThickness), new Vector3(LineOffset + LineLength, SingleAxisThickness, SingleAxisThickness));
private BoundingBox YAxisBox = new BoundingBox(new Vector3(-SingleAxisThickness, LineOffset, -SingleAxisThickness), new Vector3(SingleAxisThickness, LineOffset + LineLength, SingleAxisThickness));
private BoundingBox ZAxisBox = new BoundingBox(new Vector3(-SingleAxisThickness, -SingleAxisThickness, LineOffset), new Vector3(SingleAxisThickness, SingleAxisThickness, LineOffset + LineLength));
private BoundingBox XZBox = new BoundingBox(Vector3.Zero, new Vector3(LineOffset, MultiAxisThickness, LineOffset));
private BoundingBox XYBox = new BoundingBox(Vector3.Zero, new Vector3(LineOffset, LineOffset, MultiAxisThickness));
private BoundingBox YZBox = new BoundingBox(Vector3.Zero, new Vector3(MultiAxisThickness, LineOffset, LineOffset));
private BoundingBox CenterBoxRaw = new BoundingBox(new Vector3(-0.5f * CenterBoxSize), new Vector3(0.5f * CenterBoxSize));
private float RotateRadiusRaw = 4.0f;
/// <summary>
/// Center box scale
/// </summary>
private const float CenterBoxScale = 0.8f;
private BoundingSphere ScaleXSphere => new BoundingSphere(Vector3.Transform(_translationLineVertices[1], _gizmoWorld), ScaleSpheresRadius * _screenScale);
private BoundingSphere ScaleYSphere => new BoundingSphere(Vector3.Transform(_translationLineVertices[7], _gizmoWorld), ScaleSpheresRadius * _screenScale);
private BoundingSphere ScaleZSphere => new BoundingSphere(Vector3.Transform(_translationLineVertices[13], _gizmoWorld), ScaleSpheresRadius * _screenScale);
/// <summary>
/// The inner minimum of the multiscale
/// </summary>
private const float InnerExtend = AxisOffset + 0.5f;
/// <summary>
/// The outer maximum of the multiscale
/// </summary>
private const float OuterExtend = AxisOffset * 3.5f;
// Cube with the size AxisThickness, then moves it along the axis (AxisThickness) and finally makes it really long (AxisLength)
private BoundingBox XAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitX).Merge(AxisLength * Vector3.UnitX);
private BoundingBox YAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitY).Merge(AxisLength * Vector3.UnitY);
private BoundingBox ZAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitZ).Merge(AxisLength * Vector3.UnitZ);
private BoundingBox XZBox = new BoundingBox(new Vector3(InnerExtend, 0, InnerExtend), new Vector3(OuterExtend, 0, OuterExtend));
private BoundingBox XYBox = new BoundingBox(new Vector3(InnerExtend, InnerExtend, 0), new Vector3(OuterExtend, OuterExtend, 0));
private BoundingBox YZBox = new BoundingBox(new Vector3(0, InnerExtend, InnerExtend), new Vector3(0, OuterExtend, OuterExtend));
private BoundingBox CenterBoxRaw = new BoundingBox(new Vector3(-0.5f * CenterBoxScale), new Vector3(0.5f * CenterBoxScale));
private OrientedBoundingBox CenterBox => new OrientedBoundingBox(CenterBoxRaw) * _gizmoWorld;
private const float RotateRadiusRaw = 4.0f;
private Mode _activeMode = Mode.Translate;
private Axis _activeAxis = Axis.None;

View File

@@ -36,7 +36,7 @@ namespace
// Load product info
Array<byte> productInfoData;
const String productInfoPath = directory / TEXT("product-info.json");
if (File::ReadAllBytes(productInfoPath, productInfoData))
if (!FileSystem::FileExists(productInfoPath) || File::ReadAllBytes(productInfoPath, productInfoData))
return;
rapidjson_flax::Document document;
document.Parse((char*)productInfoData.Get(), productInfoData.Count());
@@ -193,6 +193,7 @@ void RiderCodeEditor::FindEditors(Array<CodeEditor*>* output)
// TODO: detect Snap installations
// TODO: detect by reading the jetbrains-rider.desktop file from ~/.local/share/applications and /usr/share/applications?
SearchDirectory(&installations, TEXT("/usr/share/rider/"));
FileSystem::GetChildDirectories(subDirectories, TEXT("/usr/share/rider"));
// Default suggested location for standalone installations

View File

@@ -543,7 +543,9 @@ bool EditorUtilities::GetTexture(const Guid& textureId, TextureData& textureData
}
else
{
// TODO: disable streaming for a texture or set max quality override
const bool useGPU = texture->IsVirtual();
if (useGPU)
{
int32 waits = 1000;
const auto targetResidency = texture->StreamingTexture()->GetMaxResidency();
ASSERT(targetResidency > 0);
@@ -555,6 +557,7 @@ bool EditorUtilities::GetTexture(const Guid& textureId, TextureData& textureData
// Get texture data from GPU
if (!texture->GetTexture()->DownloadData(textureData))
return false;
}
// Get texture data from asset
if (!texture->GetTextureData(textureData))

View File

@@ -37,12 +37,8 @@ String JsonAssetBase::GetData() const
if (Data == nullptr)
return String::Empty;
PROFILE_CPU_NAMED("JsonAsset.GetData");
// Get serialized data
rapidjson_flax::StringBuffer buffer;
rapidjson_flax::Writer<rapidjson_flax::StringBuffer> writer(buffer);
Data->Accept(writer);
OnGetData(buffer);
return String((const char*)buffer.GetString(), (int32)buffer.GetSize());
}
@@ -83,6 +79,12 @@ bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& d
return loadAsset() != LoadResult::Ok;
}
void JsonAssetBase::OnGetData(rapidjson_flax::StringBuffer& buffer) const
{
PrettyJsonWriter writerObj(buffer);
Data->Accept(writerObj.GetWriter());
}
const String& JsonAssetBase::GetPath() const
{
#if USE_EDITOR
@@ -144,6 +146,53 @@ void JsonAssetBase::GetReferences(const StringAnsiView& json, Array<Guid>& outpu
FindIds(document, output);
}
bool JsonAssetBase::Save(const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
// Serialize to json file
rapidjson_flax::StringBuffer buffer;
PrettyJsonWriter writerObj(buffer);
JsonWriter& writer = writerObj;
writer.StartObject();
{
// Json resource header
writer.JKEY("ID");
writer.Guid(GetID());
writer.JKEY("TypeName");
writer.String(DataTypeName);
writer.JKEY("EngineBuild");
writer.Int(FLAXENGINE_VERSION_BUILD);
// Json resource data
rapidjson_flax::StringBuffer dataBuffer;
OnGetData(dataBuffer);
writer.JKEY("Data");
writer.RawValue(dataBuffer.GetString(), (int32)dataBuffer.GetSize());
}
writer.EndObject();
// Save json to file
if (File::WriteAllBytes(path.HasChars() ? path : StringView(GetPath()), (byte*)buffer.GetString(), (int32)buffer.GetSize()))
{
LOG(Error, "Cannot save \'{0}\'", ToString());
return true;
}
return false;
}
void JsonAssetBase::GetReferences(Array<Guid>& output) const
{
if (Data == nullptr)

View File

@@ -72,8 +72,19 @@ public:
/// <param name="json">The Json string.</param>
/// <param name="output">The output list of object IDs references by the asset (appended, not cleared).</param>
API_FUNCTION() static void GetReferences(const StringAnsiView& json, API_PARAM(Out) Array<Guid, HeapAllocation>& output);
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
/// <returns>True if cannot save data, otherwise false.</returns>
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
protected:
// Gets the serialized Json data (from runtime state).
virtual void OnGetData(rapidjson_flax::StringBuffer& buffer) const;
public:
// [Asset]
const String& GetPath() const override;

View File

@@ -560,12 +560,8 @@ bool FlaxStorage::Load()
bool FlaxStorage::Reload()
{
// Check if wasn't already loaded
if (!IsLoaded())
{
LOG(Warning, "{0} isn't loaded.", ToString());
return false;
}
OnReloading(this);

View File

@@ -22,7 +22,7 @@
#include "Engine/Engine/Globals.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Streaming/StreamingSettings.h"
#if FLAX_TESTS
#if FLAX_TESTS || USE_EDITOR
#include "Engine/Platform/FileSystem.h"
#endif
@@ -91,6 +91,19 @@ GameSettings* GameSettings::Get()
// Silence missing GameSettings during test run before Editor creates it (not important)
if (!FileSystem::FileExists(assetPath))
return nullptr;
#endif
#if USE_EDITOR
// Log once missing GameSettings in Editor
if (!FileSystem::FileExists(assetPath))
{
static bool LogOnce = true;
if (LogOnce)
{
LogOnce = false;
LOG(Error, "Missing file game settings asset ({0})", assetPath);
}
return nullptr;
}
#endif
GameSettingsAsset = Content::LoadAsync<JsonAsset>(assetPath);
if (GameSettingsAsset == nullptr)

View File

@@ -617,12 +617,11 @@ public:
FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&_ptr);
for (intptr i = 0; i < size; i++)
{
auto function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings[i]._function);
if (function != nullptr)
{
auto callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings[i]._callee);
auto function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings->_function);
auto callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings->_callee);
if (function != nullptr && function == (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings->_function))
function(callee, Forward<Params>(params)...);
}
++bindings;
}
}
};

View File

@@ -419,6 +419,31 @@ namespace FlaxEngine
return box;
}
/// <summary>
/// Constructs a <see cref="BoundingBox" /> that is as large as the box and point.
/// </summary>
/// <param name="value1">The box to merge.</param>
/// <param name="value2">The point to merge.</param>
/// <param name="result">When the method completes, contains the newly constructed bounding box.</param>
public static void Merge(ref BoundingBox value1, ref Vector3 value2, out BoundingBox result)
{
Vector3.Min(ref value1.Minimum, ref value2, out result.Minimum);
Vector3.Max(ref value1.Maximum, ref value2, out result.Maximum);
}
/// <summary>
/// Constructs a <see cref="BoundingBox" /> that is as large as the box and point.
/// </summary>
/// <param name="value2">The point to merge.</param>
/// <returns>The newly constructed bounding box.</returns>
public BoundingBox Merge(Vector3 value2)
{
BoundingBox result;
Vector3.Min(ref Minimum, ref value2, out result.Minimum);
Vector3.Max(ref Maximum, ref value2, out result.Maximum);
return result;
}
/// <summary>
/// Transforms bounding box using the given transformation matrix.
/// </summary>
@@ -498,6 +523,19 @@ namespace FlaxEngine
result = new BoundingBox(min, max);
}
/// <summary>
/// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points.
/// </summary>
/// <param name="offset">The bounds offset.</param>
/// <returns>The offsetted bounds.</returns>
public BoundingBox MakeOffsetted(Vector3 offset)
{
BoundingBox result;
Vector3.Add(ref Minimum, ref offset, out result.Minimum);
Vector3.Add(ref Maximum, ref offset, out result.Maximum);
return result;
}
/// <summary>
/// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points.
/// </summary>

View File

@@ -377,12 +377,9 @@ void GPUBuffer::SetData(const void* data, uint32 size)
Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData"));
return;
}
void* mapped = Map(GPUResourceMapMode::Write);
if (!mapped)
{
return;
}
Platform::MemoryCopy(mapped, data, size);
Unmap();
}

View File

@@ -190,6 +190,7 @@ public:
/// <summary>
/// Gets a CPU pointer to the resource by mapping its contents. Denies the GPU access to that resource.
/// </summary>
/// <remarks>Always call Unmap if the returned pointer is valid to release resources.</remarks>
/// <param name="mode">The map operation mode.</param>
/// <returns>The pointer of the mapped CPU buffer with resource data or null if failed.</returns>
API_FUNCTION() virtual void* Map(GPUResourceMapMode mode) = 0;

View File

@@ -231,6 +231,7 @@ bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInf
_cb = nullptr;
}
_cbData.Resize(cbSize, false);
Platform::MemoryClear(_cbData.Get(), cbSize);
}
// Initialize the material based on type (create pipeline states and setup)

View File

@@ -527,9 +527,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public Vertex0[] DownloadVertexBuffer0(bool forceGpu = false)
{
var vertices = VertexCount;
var result = new Vertex0[vertices];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB0))
var result = (Vertex0[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex0), (int)InternalBufferType.VB0);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}
@@ -541,9 +540,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public Vertex1[] DownloadVertexBuffer1(bool forceGpu = false)
{
var vertices = VertexCount;
var result = new Vertex1[vertices];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB1))
var result = (Vertex1[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex1), (int)InternalBufferType.VB1);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}
@@ -560,10 +558,8 @@ namespace FlaxEngine
{
if (!HasVertexColors)
return null;
var vertices = VertexCount;
var result = new Vertex2[vertices];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB2))
var result = (Vertex2[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex2), (int)InternalBufferType.VB2);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}
@@ -582,7 +578,7 @@ namespace FlaxEngine
var vb1 = DownloadVertexBuffer1(forceGpu);
var vb2 = DownloadVertexBuffer2(forceGpu);
var vertices = VertexCount;
var vertices = vb0.Length;
var result = new Vertex[vertices];
for (int i = 0; i < vertices; i++)
{
@@ -618,9 +614,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public uint[] DownloadIndexBuffer(bool forceGpu = false)
{
var triangles = TriangleCount;
var result = new uint[triangles * 3];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB32))
var result = (uint[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(uint), (int)InternalBufferType.IB32);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}
@@ -633,9 +628,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public ushort[] DownloadIndexBufferUShort(bool forceGpu = false)
{
var triangles = TriangleCount;
var result = new ushort[triangles * 3];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB16))
var result = (ushort[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(ushort), (int)InternalBufferType.IB16);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}

View File

@@ -746,75 +746,11 @@ enum class InternalBufferType
IB32 = 4,
};
void ConvertMeshData(Mesh* mesh, InternalBufferType type, MonoArray* resultObj, void* srcData)
MonoArray* Mesh::DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI)
{
auto vertices = mesh->GetVertexCount();
auto triangles = mesh->GetTriangleCount();
auto indices = triangles * 3;
auto use16BitIndexBuffer = mesh->Use16BitIndexBuffer();
void* managedArrayPtr = mono_array_addr_with_size(resultObj, 0, 0);
switch (type)
{
case InternalBufferType::VB0:
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB0ElementType) * vertices);
break;
}
case InternalBufferType::VB1:
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB1ElementType) * vertices);
break;
}
case InternalBufferType::VB2:
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB2ElementType) * vertices);
break;
}
case InternalBufferType::IB16:
{
if (use16BitIndexBuffer)
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint16) * indices);
}
else
{
auto dst = (uint16*)managedArrayPtr;
auto src = (uint32*)srcData;
for (int32 i = 0; i < indices; i++)
{
dst[i] = src[i];
}
}
break;
}
case InternalBufferType::IB32:
{
if (use16BitIndexBuffer)
{
auto dst = (uint32*)managedArrayPtr;
auto src = (uint16*)srcData;
for (int32 i = 0; i < indices; i++)
{
dst[i] = src[i];
}
}
else
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint32) * indices);
}
break;
}
}
}
bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI)
{
Mesh* mesh = this;
InternalBufferType type = (InternalBufferType)typeI;
auto mesh = this;
auto type = (InternalBufferType)typeI;
auto model = mesh->GetModel();
ASSERT(model && resultObj);
ScopeLock lock(model->Locker);
// Virtual assets always fetch from GPU memory
@@ -823,23 +759,7 @@ bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI)
if (!mesh->IsInitialized() && forceGpu)
{
LOG(Error, "Cannot load mesh data from GPU if it's not loaded.");
return true;
}
if (type == InternalBufferType::IB16 || type == InternalBufferType::IB32)
{
if (mono_array_length(resultObj) != mesh->GetTriangleCount() * 3)
{
LOG(Error, "Invalid buffer size.");
return true;
}
}
else
{
if (mono_array_length(resultObj) != mesh->GetVertexCount())
{
LOG(Error, "Invalid buffer size.");
return true;
}
return nullptr;
}
MeshBufferType bufferType;
@@ -859,35 +779,96 @@ bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI)
bufferType = MeshBufferType::Index;
break;
default:
return true;
return nullptr;
}
BytesContainer data;
int32 dataCount;
if (forceGpu)
{
// Get data from GPU
// TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer
auto task = mesh->DownloadDataGPUAsync(bufferType, data);
if (task == nullptr)
return true;
return nullptr;
task->Start();
model->Locker.Unlock();
if (task->Wait())
{
LOG(Error, "Task failed.");
return true;
return nullptr;
}
model->Locker.Lock();
// Extract elements count from result data
switch (bufferType)
{
case MeshBufferType::Index:
dataCount = data.Length() / (Use16BitIndexBuffer() ? sizeof(uint16) : sizeof(uint32));
break;
case MeshBufferType::Vertex0:
dataCount = data.Length() / sizeof(VB0ElementType);
break;
case MeshBufferType::Vertex1:
dataCount = data.Length() / sizeof(VB1ElementType);
break;
case MeshBufferType::Vertex2:
dataCount = data.Length() / sizeof(VB2ElementType);
break;
}
}
else
{
// Get data from CPU
int32 count;
if (DownloadDataCPU(bufferType, data, count))
return true;
if (DownloadDataCPU(bufferType, data, dataCount))
return nullptr;
}
ConvertMeshData(mesh, type, resultObj, data.Get());
return false;
// Convert into managed array
MonoArray* result = mono_array_new(mono_domain_get(), mono_type_get_class(mono_reflection_type_get_type(resultType)), dataCount);
void* managedArrayPtr = mono_array_addr_with_size(result, 0, 0);
const int32 elementSize = data.Length() / dataCount;
switch (type)
{
case InternalBufferType::VB0:
case InternalBufferType::VB1:
case InternalBufferType::VB2:
{
Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length());
break;
}
case InternalBufferType::IB16:
{
if (elementSize == sizeof(uint16))
{
Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length());
}
else
{
auto dst = (uint16*)managedArrayPtr;
auto src = (uint32*)data.Get();
for (int32 i = 0; i < dataCount; i++)
dst[i] = src[i];
}
break;
}
case InternalBufferType::IB32:
{
if (elementSize == sizeof(uint16))
{
auto dst = (uint32*)managedArrayPtr;
auto src = (uint16*)data.Get();
for (int32 i = 0; i < dataCount; i++)
dst[i] = src[i];
}
else
{
Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length());
}
break;
}
}
return result;
}
#endif

View File

@@ -321,6 +321,6 @@ private:
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj);
API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, MonoArray* trianglesObj);
API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj);
API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI);
API_FUNCTION(NoProxy) MonoArray* DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI);
#endif
};

View File

@@ -516,64 +516,12 @@ enum class InternalBufferType
IB32 = 4,
};
void ConvertMeshData(SkinnedMesh* mesh, InternalBufferType type, MonoArray* resultObj, void* srcData)
{
auto vertices = mesh->GetVertexCount();
auto triangles = mesh->GetTriangleCount();
auto indices = triangles * 3;
auto use16BitIndexBuffer = mesh->Use16BitIndexBuffer();
void* managedArrayPtr = mono_array_addr_with_size(resultObj, 0, 0);
switch (type)
{
case InternalBufferType::VB0:
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(VB0SkinnedElementType) * vertices);
break;
}
case InternalBufferType::IB16:
{
if (use16BitIndexBuffer)
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint16) * indices);
}
else
{
auto dst = (uint16*)managedArrayPtr;
auto src = (uint32*)srcData;
for (int32 i = 0; i < indices; i++)
{
dst[i] = src[i];
}
}
break;
}
case InternalBufferType::IB32:
{
if (use16BitIndexBuffer)
{
auto dst = (uint32*)managedArrayPtr;
auto src = (uint16*)srcData;
for (int32 i = 0; i < indices; i++)
{
dst[i] = src[i];
}
}
else
{
Platform::MemoryCopy(managedArrayPtr, srcData, sizeof(uint32) * indices);
}
break;
}
}
}
bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI)
MonoArray* SkinnedMesh::DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI)
{
SkinnedMesh* mesh = this;
InternalBufferType type = (InternalBufferType)typeI;
auto model = mesh->GetSkinnedModel();
ASSERT(model && resultObj);
ScopeLock lock(model->Locker);
// Virtual assets always fetch from GPU memory
forceGpu |= model->IsVirtual();
@@ -581,23 +529,7 @@ bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 type
if (!mesh->IsInitialized() && forceGpu)
{
LOG(Error, "Cannot load mesh data from GPU if it's not loaded.");
return true;
}
if (type == InternalBufferType::IB16 || type == InternalBufferType::IB32)
{
if (mono_array_length(resultObj) != mesh->GetTriangleCount() * 3)
{
LOG(Error, "Invalid buffer size.");
return true;
}
}
else
{
if (mono_array_length(resultObj) != mesh->GetVertexCount())
{
LOG(Error, "Invalid buffer size.");
return true;
}
return nullptr;
}
MeshBufferType bufferType;
@@ -610,37 +542,89 @@ bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 type
case InternalBufferType::IB32:
bufferType = MeshBufferType::Index;
break;
default: CRASH;
return true;
default:
return nullptr;
}
BytesContainer data;
int32 dataCount;
if (forceGpu)
{
// Get data from GPU
// TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer
auto task = mesh->DownloadDataGPUAsync(bufferType, data);
if (task == nullptr)
return true;
return nullptr;
task->Start();
model->Locker.Unlock();
if (task->Wait())
{
LOG(Error, "Task failed.");
return true;
return nullptr;
}
model->Locker.Lock();
// Extract elements count from result data
switch (bufferType)
{
case MeshBufferType::Index:
dataCount = data.Length() / (Use16BitIndexBuffer() ? sizeof(uint16) : sizeof(uint32));
break;
case MeshBufferType::Vertex0:
dataCount = data.Length() / sizeof(VB0SkinnedElementType);
break;
}
}
else
{
// Get data from CPU
int32 count;
if (DownloadDataCPU(bufferType, data, count))
return true;
if (DownloadDataCPU(bufferType, data, dataCount))
return nullptr;
}
// Convert into managed memory
ConvertMeshData(mesh, type, resultObj, data.Get());
return false;
// Convert into managed array
MonoArray* result = mono_array_new(mono_domain_get(), mono_type_get_class(mono_reflection_type_get_type(resultType)), dataCount);
void* managedArrayPtr = mono_array_addr_with_size(result, 0, 0);
const int32 elementSize = data.Length() / dataCount;
switch (type)
{
case InternalBufferType::VB0:
{
Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length());
break;
}
case InternalBufferType::IB16:
{
if (elementSize == sizeof(uint16))
{
Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length());
}
else
{
auto dst = (uint16*)managedArrayPtr;
auto src = (uint32*)data.Get();
for (int32 i = 0; i < dataCount; i++)
dst[i] = src[i];
}
break;
}
case InternalBufferType::IB32:
{
if (elementSize == sizeof(uint16))
{
auto dst = (uint32*)managedArrayPtr;
auto src = (uint16*)data.Get();
for (int32 i = 0; i < dataCount; i++)
dst[i] = src[i];
}
else
{
Platform::MemoryCopy(managedArrayPtr, data.Get(), data.Length());
}
break;
}
}
return result;
}
#endif

View File

@@ -199,6 +199,6 @@ private:
#if !COMPILE_WITHOUT_CSHARP
API_FUNCTION(NoProxy) bool UpdateMeshUInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
API_FUNCTION(NoProxy) bool UpdateMeshUShort(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI);
API_FUNCTION(NoProxy) MonoArray* DownloadBuffer(bool forceGpu, MonoReflectionType* resultType, int32 typeI);
#endif
};

View File

@@ -17,23 +17,24 @@ struct RenderContext;
/// </remarks>
API_CLASS(Abstract) class FLAXENGINE_API PostProcessEffect : public Script
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(PostProcessEffect);
public:
/// <summary>
/// Effect rendering location within rendering pipeline.
/// </summary>
API_FIELD() PostProcessEffectLocation Location = PostProcessEffectLocation::Default;
API_FIELD(Attributes="EditorDisplay(\"Post Process Effect\"), ExpandGroups") PostProcessEffectLocation Location = PostProcessEffectLocation::Default;
/// <summary>
/// True whether use a single render target as both input and output. Use this if your effect doesn't need to copy the input buffer to the output but can render directly to the single texture. Can be used to optimize game performance.
/// </summary>
API_FIELD() bool UseSingleTarget = false;
API_FIELD(Attributes="EditorDisplay(\"Post Process Effect\")") bool UseSingleTarget = false;
/// <summary>
/// Effect rendering order. Post effects are sorted before rendering (from the lowest order to the highest order).
/// </summary>
API_FIELD() int32 Order = 0;
API_FIELD(Attributes="EditorDisplay(\"Post Process Effect\")") int32 Order = 0;
public:
/// <summary>

View File

@@ -253,6 +253,14 @@ public:
}
public:
// Ignore deprecation warnings in defaults
PRAGMA_DISABLE_DEPRECATION_WARNINGS
RenderView() = default;
RenderView(const RenderView& other) = default;
RenderView(RenderView&& other) = default;
RenderView& operator=(const RenderView& other) = default;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
// Set up view with custom params
// @param viewProjection View * Projection matrix
void SetUp(const Matrix& viewProjection);

View File

@@ -274,9 +274,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public Vertex0[] DownloadVertexBuffer0(bool forceGpu = false)
{
var vertices = VertexCount;
var result = new Vertex0[vertices];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.VB0))
var result = (Vertex0[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(Vertex0), (int)InternalBufferType.VB0);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}
@@ -292,7 +291,7 @@ namespace FlaxEngine
var vb0 = DownloadVertexBuffer0(forceGpu);
var vertices = VertexCount;
var vertices = vb0.Length;
var result = new Vertex[vertices];
for (int i = 0; i < vertices; i++)
{
@@ -319,9 +318,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public uint[] DownloadIndexBuffer(bool forceGpu = false)
{
var triangles = TriangleCount;
var result = new uint[triangles * 3];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB32))
var result = (uint[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(uint), (int)InternalBufferType.IB32);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}
@@ -334,9 +332,8 @@ namespace FlaxEngine
/// <returns>The gathered data.</returns>
public ushort[] DownloadIndexBufferUShort(bool forceGpu = false)
{
var triangles = TriangleCount;
var result = new ushort[triangles * 3];
if (Internal_DownloadBuffer(__unmanagedPtr, forceGpu, result, (int)InternalBufferType.IB16))
var result = (ushort[])Internal_DownloadBuffer(__unmanagedPtr, forceGpu, typeof(ushort), (int)InternalBufferType.IB16);
if (result == null)
throw new Exception("Failed to download mesh data.");
return result;
}

View File

@@ -19,7 +19,8 @@ GPUBufferView* GPUBufferDX11::View() const
void* GPUBufferDX11::Map(GPUResourceMapMode mode)
{
if (!IsInMainThread())
const bool isMainThread = IsInMainThread();
if (!isMainThread)
_device->Locker.Lock();
ASSERT(!_mapped);
@@ -31,7 +32,7 @@ void* GPUBufferDX11::Map(GPUResourceMapMode mode)
{
case GPUResourceMapMode::Read:
mapType = D3D11_MAP_READ;
if (_desc.Usage == GPUResourceUsage::StagingReadback)
if (_desc.Usage == GPUResourceUsage::StagingReadback && isMainThread)
mapFlags = D3D11_MAP_FLAG_DO_NOT_WAIT;
break;
case GPUResourceMapMode::Write:
@@ -47,18 +48,19 @@ void* GPUBufferDX11::Map(GPUResourceMapMode mode)
const HRESULT result = _device->GetIM()->Map(_resource, 0, mapType, mapFlags, &map);
if (result != DXGI_ERROR_WAS_STILL_DRAWING)
LOG_DIRECTX_RESULT(result);
_mapped = map.pData != nullptr;
if (!_mapped && !isMainThread)
_device->Locker.Unlock();
return map.pData;
}
void GPUBufferDX11::Unmap()
{
if (_mapped)
{
ASSERT(_mapped);
_mapped = false;
_device->GetIM()->Unmap(_resource, 0);
}
if (!IsInMainThread())
_device->Locker.Unlock();
}

View File

@@ -471,6 +471,26 @@ bool Actor::HasTag(const StringView& tag) const
return Tags.Contains(tag);
}
void Actor::AddTag(const Tag& tag)
{
Tags.AddUnique(tag);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const String& Actor::GetTag() const
{
return Tags.Count() != 0 ? Tags[0].ToString() : String::Empty;
}
void Actor::SetTag(const StringView& value)
{
const Tag tag = Tags::Get(value);
Tags.Set(&tag, 1);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
void Actor::SetLayer(int32 layerIndex)
{
layerIndex = Math::Clamp(layerIndex, 0, 31);

View File

@@ -130,6 +130,25 @@ public:
/// <param name="tag">The tag to check.</param>
API_FUNCTION() bool HasTag(const StringView& tag) const;
/// <summary>
/// Adds a tag to the actor
/// </summary>
/// <param name="tag">The tag to add.</param>
API_FUNCTION() void AddTag(const Tag& tag);
/// <summary>
/// Gets the name of the tag.
/// [Deprecated in v1.5]
/// </summary>
API_PROPERTY(Attributes="HideInEditor, NoSerialize, NoAnimate")
DEPRECATED const String& GetTag() const;
/// <summary>
/// Sets the name of the tag.
/// [Deprecated in v1.5]
/// </summary>
API_PROPERTY() DEPRECATED void SetTag(const StringView& value);
/// <summary>
/// Gets the actor name.
/// </summary>

View File

@@ -103,7 +103,7 @@ void Lightmap::EnsureSize(int32 size)
#if COMPILE_WITH_ASSETS_IMPORTER
Guid id = Guid::New();
LOG(Info, "Cannot load lightmap {0} ({1}:{2}). Creating new one.", id, _index, textureIndex);
LOG(Info, "Cannot load lightmap ({1}:{2}). Creating new one with ID={0}.", id, _index, textureIndex);
String assetPath;
_manager->GetCachedLightmapPath(&assetPath, _index, textureIndex);

View File

@@ -48,7 +48,7 @@ public:
/// Gets attached texture objects
/// </summary>
/// <param name="lightmaps">Lightmaps textures array</param>
void GetTextures(GPUTexture* lightmaps[]) const
void GetTextures(GPUTexture* lightmaps[3]) const
{
lightmaps[0] = _textures[0] ? _textures[0]->GetTexture() : nullptr;
lightmaps[1] = _textures[1] ? _textures[1]->GetTexture() : nullptr;
@@ -59,7 +59,7 @@ public:
/// Gets attached texture objects
/// </summary>
/// <param name="lightmaps">Lightmaps textures array</param>
void GetTextures(Texture* lightmaps[]) const
void GetTextures(Texture* lightmaps[3]) const
{
lightmaps[0] = _textures[0].Get();
lightmaps[1] = _textures[1].Get();

View File

@@ -2,11 +2,10 @@
#include "LocalizedStringTable.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Serialization/SerializationFwd.h"
#include "Engine/Content/Factories/JsonAssetFactory.h"
#if USE_EDITOR
#include "Engine/ContentImporters/CreateJson.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Core/Log.h"
#endif
@@ -55,75 +54,6 @@ String LocalizedStringTable::GetPluralString(const String& id, int32 n) const
return String::Format(result.GetText(), n);
}
#if USE_EDITOR
bool LocalizedStringTable::Save(const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
// Serialize data
rapidjson_flax::StringBuffer outputData;
PrettyJsonWriter writerObj(outputData);
JsonWriter& writer = writerObj;
writer.StartObject();
{
writer.JKEY("Locale");
writer.String(Locale);
if (FallbackTable.GetID().IsValid())
{
writer.JKEY("FallbackTable");
writer.Guid(FallbackTable.GetID());
}
writer.JKEY("Entries");
writer.StartObject();
for (auto& e : Entries)
{
writer.Key(e.Key);
if (e.Value.Count() == 1)
{
writer.String(e.Value[0]);
}
else
{
writer.StartArray();
for (auto& q : e.Value)
writer.String(q);
writer.EndArray();
}
}
writer.EndObject();
}
writer.EndObject();
// Save asset
#if COMPILE_WITH_ASSETS_IMPORTER
const bool saveResult = CreateJson::Create(path.HasChars() ? path : StringView(GetPath()), outputData, TypeName);
if (saveResult)
#endif
{
LOG(Error, "Cannot save \'{0}\'", ToString());
return true;
}
return false;
}
#endif
Asset::LoadResult LocalizedStringTable::loadAsset()
{
// Base
@@ -170,3 +100,41 @@ void LocalizedStringTable::unload(bool isReloading)
FallbackTable = nullptr;
Entries.Clear();
}
void LocalizedStringTable::OnGetData(rapidjson_flax::StringBuffer& buffer) const
{
PrettyJsonWriter writerObj(buffer);
JsonWriter& writer = writerObj;
writer.StartObject();
{
writer.JKEY("Locale");
writer.String(Locale);
if (FallbackTable.GetID().IsValid())
{
writer.JKEY("FallbackTable");
writer.Guid(FallbackTable.GetID());
}
writer.JKEY("Entries");
writer.StartObject();
for (auto& e : Entries)
{
writer.Key(e.Key);
if (e.Value.Count() == 1)
{
writer.String(e.Value[0]);
}
else
{
writer.StartArray();
for (auto& q : e.Value)
writer.String(q);
writer.EndArray();
}
}
writer.EndObject();
}
writer.EndObject();
}

View File

@@ -61,19 +61,9 @@ public:
/// <returns>The localized text.</returns>
API_FUNCTION() String GetPluralString(const String& id, int32 n) const;
#if USE_EDITOR
/// <summary>
/// Saves this asset to the file. Supported only in Editor.
/// </summary>
/// <param name="path">The custom asset path to use for the saving. Use empty value to save this asset to its own storage location. Can be used to duplicate asset. Must be specified when saving virtual asset.</param>
/// <returns>True if cannot save data, otherwise false.</returns>
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
protected:
// [JsonAssetBase]
LoadResult loadAsset() override;
void unload(bool isReloading) override;
void OnGetData(rapidjson_flax::StringBuffer& buffer) const override;
};

View File

@@ -36,7 +36,7 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi
/// [Deprecated in v1.3]
/// </summary>
API_FIELD()
DEPRECATED NetworkDriverType NetworkDriverType = NetworkDriverType::ENet;
DEPRECATED NetworkDriverType NetworkDriverType;
/// <summary>
/// The network driver instance (implements INetworkDriver) that will be used to create and manage the peer, send and receive messages.
@@ -81,4 +81,12 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi
/// </remarks>
API_FIELD()
uint16 MessagePoolSize = 2048;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
/// <summary>Ctor.</summary>
NetworkConfig()
{
NetworkDriverType = NetworkDriverType::ENet;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
};

View File

@@ -104,6 +104,8 @@ int32 ParticleSystemInstance::GetParticlesCount() const
if (GPUParticlesCountReadback && GPUParticlesCountReadback->IsAllocated())
{
auto data = static_cast<uint32*>(GPUParticlesCountReadback->Map(GPUResourceMapMode::Read));
if (data)
{
for (const auto& emitter : Emitters)
{
if (emitter.Buffer && emitter.Buffer->Mode == ParticlesSimulationMode::GPU && emitter.Buffer->GPU.HasValidCount)
@@ -114,6 +116,7 @@ int32 ParticleSystemInstance::GetParticlesCount() const
}
GPUParticlesCountReadback->Unmap();
}
}
else if (Emitters.HasItems())
{
// Initialize readback buffer (next GPU particles simulation update will copy the particle counters)

View File

@@ -537,8 +537,7 @@ String StringUtils::GetZZString(const Char* str)
if (*end == '\0')
end++;
}
const int len = end - str;
return String(str, len);
return String(str, (int32)(end - str));
}
#undef STRING_UTILS_ITOSTR_BUFFER_SIZE

View File

@@ -689,8 +689,8 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
objectsBufferCapacity = counter;
notReady = false;
}
}
_culledObjectsSizeBuffer->Unmap();
}
// Allow to be ready if the buffer was already used
if (notReady && surfaceAtlasData.CulledObjectsBuffer && surfaceAtlasData.CulledObjectsBuffer->IsAllocated())

View File

@@ -549,8 +549,8 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex
_cascadeIndex = cascadeIndex;
_sdfData = &sdfData;
const float objectMargin = _voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN;
_sdfDataOriginMax = sdfData.Origin - objectMargin;
_sdfDataOriginMax = sdfData.Origin + objectMargin;
_sdfDataOriginMin = -sdfData.Origin - objectMargin;
_sdfDataOriginMax = -sdfData.Origin + objectMargin;
{
PROFILE_CPU_NAMED("Draw");
BoundingBox cascadeBoundsWorld = cascadeBounds.MakeOffsetted(sdfData.Origin);
@@ -958,9 +958,9 @@ void GlobalSignDistanceFieldPass::RasterizeModelSDF(Actor* actor, const ModelBas
{
// Setup object data
BoundingBox objectBoundsCascade;
Vector3::Clamp(objectBounds.Minimum - _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum);
Vector3::Clamp(objectBounds.Minimum + _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum);
Vector3::Subtract(objectBoundsCascade.Minimum, _cascadeBounds.Minimum, objectBoundsCascade.Minimum);
Vector3::Clamp(objectBounds.Maximum - _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum);
Vector3::Clamp(objectBounds.Maximum + _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum);
Vector3::Subtract(objectBoundsCascade.Maximum, _cascadeBounds.Minimum, objectBoundsCascade.Maximum);
const Int3 objectChunkMin(objectBoundsCascade.Minimum / _chunkSize);
const Int3 objectChunkMax(objectBoundsCascade.Maximum / _chunkSize);
@@ -1019,9 +1019,9 @@ void GlobalSignDistanceFieldPass::RasterizeHeightfield(Actor* actor, GPUTexture*
{
// Setup object data
BoundingBox objectBoundsCascade;
Vector3::Clamp(objectBounds.Minimum - _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum);
Vector3::Clamp(objectBounds.Minimum + _sdfDataOriginMin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum);
Vector3::Subtract(objectBoundsCascade.Minimum, _cascadeBounds.Minimum, objectBoundsCascade.Minimum);
Vector3::Clamp(objectBounds.Maximum - _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum);
Vector3::Clamp(objectBounds.Maximum + _sdfDataOriginMax, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum);
Vector3::Subtract(objectBoundsCascade.Maximum, _cascadeBounds.Minimum, objectBoundsCascade.Maximum);
const Int3 objectChunkMin(objectBoundsCascade.Minimum / _chunkSize);
const Int3 objectChunkMax(objectBoundsCascade.Maximum / _chunkSize);

View File

@@ -406,7 +406,10 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
// Get the light accumulation buffer
auto outputFormat = renderContext.Buffers->GetOutputFormat();
auto tempDesc = GPUTextureDescription::New2D(renderContext.Buffers->GetWidth(), renderContext.Buffers->GetHeight(), outputFormat, GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget | GPUTextureFlags::UnorderedAccess);
auto tempFlags = GPUTextureFlags::ShaderResource | GPUTextureFlags::RenderTarget;
if (GPUDevice::Instance->Limits.HasCompute)
tempFlags |= GPUTextureFlags::UnorderedAccess;
auto tempDesc = GPUTextureDescription::New2D(renderContext.Buffers->GetWidth(), renderContext.Buffers->GetHeight(), outputFormat, tempFlags);
auto lightBuffer = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(lightBuffer, "LightBuffer");

View File

@@ -103,7 +103,7 @@ void ShadowsOfMordor::Builder::SceneBuildCache::UpdateLightmaps()
// Download buffer data
if (lightmapEntry.LightmapData->DownloadData(ImportLightmapTextureData))
{
LOG(Warning, "Cannot download LightmapData.");
LOG(Error, "Cannot download LightmapData.");
return;
}

View File

@@ -152,7 +152,7 @@ void ShadowsOfMordor::Builder::updateLightmaps()
{
auto texture = textures[textureIndex];
GPUDevice::Instance->Locker.Unlock();
if (texture->WaitForLoaded())
if (texture == nullptr || texture->WaitForLoaded())
{
LOG(Error, "Lightmap load failed.");
return;

View File

@@ -276,6 +276,7 @@ void ShadowsOfMordor::Builder::saveState()
context->Flush();
Platform::Sleep(10);
void* mapped = lightmapDataStaging->Map(GPUResourceMapMode::Read);
ASSERT(mapped);
stream->WriteInt32(lightmapDataSize);
stream->WriteBytes(mapped, lightmapDataSize);
lightmapDataStaging->Unmap();

View File

@@ -637,8 +637,8 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
// Write operations
auto framesCount = writeLocal(VariantType::Float, String::Format(TEXT("{0}.x * {1}.y"), framesXY.Value, framesXY.Value), node);
frame = writeLocal(VariantType::Float, String::Format(TEXT("fmod(floor({0}), {1})"), frame.Value, framesCount.Value), node);
auto framesXYInv = writeOperation2(node, Value::One, framesXY, '/');
frame = writeLocal(VariantType::Float, String::Format(TEXT("fmod({0}, {1})"), frame.Value, framesCount.Value), node);
auto framesXYInv = writeOperation2(node, Value::One.AsFloat2(), framesXY, '/');
auto frameY = writeLocal(VariantType::Float, String::Format(TEXT("abs({0} * {1}.y - (floor({2} * {3}.x) + {0} * 1))"), invertY.Value, framesXY.Value, frame.Value, framesXYInv.Value), node);
auto frameX = writeLocal(VariantType::Float, String::Format(TEXT("abs({0} * {1}.x - (({2} - {1}.x * floor({2} * {3}.x)) + {0} * 1))"), invertX.Value, framesXY.Value, frame.Value, framesXYInv.Value), node);
value = writeLocal(VariantType::Float2, String::Format(TEXT("({3} + float2({0}, {1})) * {2}"), frameX.Value, frameY.Value, framesXYInv.Value, uv.Value), node);

View File

@@ -23,7 +23,7 @@ namespace FlaxEngine.GUI
// Scrolling
private float _clickChange = 20, _scrollChange = 30;
private float _clickChange = 20, _scrollChange = 75;
private float _minimum, _maximum = 100;
private float _value, _targetValue;
private readonly Orientation _orientation;
@@ -146,6 +146,24 @@ namespace FlaxEngine.GUI
}
}
/// <summary>
/// Gets or sets the speed for the scroll on mouse wheel.
/// </summary>
public float ScrollSpeedWheel
{
get => _scrollChange;
set => _scrollChange = value;
}
/// <summary>
/// Gets or sets the speed for the scroll on mouse click.
/// </summary>
public float ScrollSpeedClick
{
get => _clickChange;
set => _clickChange = value;
}
/// <summary>
/// Gets the value slow down.
/// </summary>

View File

@@ -179,6 +179,8 @@ namespace FlaxEngine.GUI
/// <param name="target">The target.</param>
public void OnMouseLeaveControl(Control target)
{
if (Visible)
Hide();
_lastTarget = null;
}

View File

@@ -273,7 +273,7 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID)
float4 probeData = LoadDDGIProbeData(DDGI, ProbesData, CascadeIndex, probeIndex);
uint probeState = DecodeDDGIProbeState(probeData);
uint probeRaysCount = GetProbeRaysCount(DDGI, probeState);
if (probeState == DDGI_PROBE_STATE_INACTIVE || probeRaysCount < rayIndex)
if (probeState == DDGI_PROBE_STATE_INACTIVE || rayIndex >= probeRaysCount)
return; // Skip disabled probes or if current thread's ray is unused
float3 probePosition = DecodeDDGIProbePosition(DDGI, probeData, CascadeIndex, probeIndex, probeCoords);
float3 probeRayDirection = GetProbeRayDirection(DDGI, rayIndex);

View File

@@ -342,6 +342,16 @@ namespace Flax.Build.NativeCpp
}
}
// Relative to ThirdParty includes for library includes
if (!isValid && isLibraryInclude)
{
includedFilePath = Path.Combine(Globals.Root, "Source", "ThirdParty", includedFile);
if (FileExists(includedFilePath))
{
isValid = true;
}
}
if (!isValid)
{
// Invalid include

View File

@@ -105,6 +105,7 @@ namespace Flax.Build.Projects.VisualStudio
vcProjectFileContent.AppendLine(" <Keyword>MakeFileProj</Keyword>");
if (Version >= VisualStudioVersion.VisualStudio2022)
vcProjectFileContent.AppendLine(" <ResolveNuGetPackages>false</ResolveNuGetPackages>");
vcProjectFileContent.AppendLine(" <VCTargetsPath Condition=\"$(Configuration.Contains('Linux'))\">./</VCTargetsPath>");
vcProjectFileContent.AppendLine(" </PropertyGroup>");
// Default properties
@@ -343,6 +344,32 @@ namespace Flax.Build.Projects.VisualStudio
vcUserFileContent.AppendLine("</Project>");
if (platforms.Any(x => x.Target == TargetPlatform.Linux))
{
// Override MSBuild .targets file with one that runs NMake commands (workaround for Rider on Linux)
var cppTargetsFileContent = new StringBuilder();
cppTargetsFileContent.AppendLine("<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" TreatAsLocalProperty=\"Platform\">");
cppTargetsFileContent.AppendLine(" <Target Name=\"Build\">");
cppTargetsFileContent.AppendLine(" <Exec Command='$(NMakeBuildCommandLine)'/>");
cppTargetsFileContent.AppendLine(" </Target>");
cppTargetsFileContent.AppendLine(" <Target Name=\"Rebuild\">");
cppTargetsFileContent.AppendLine(" <Exec Command='$(NMakeReBuildCommandLine)'/>");
cppTargetsFileContent.AppendLine(" </Target>");
cppTargetsFileContent.AppendLine(" <Target Name=\"Clean\">");
cppTargetsFileContent.AppendLine(" <Exec Command='$(NMakeCleanCommandLine)'/>");
cppTargetsFileContent.AppendLine(" </Target>");
cppTargetsFileContent.AppendLine(" <PropertyGroup>");
cppTargetsFileContent.AppendLine(" <TargetExt></TargetExt>");
cppTargetsFileContent.AppendLine(" <TargetName>$(RootNamespace)$(Configuration.Split('.')[0])</TargetName>");
cppTargetsFileContent.AppendLine(" <TargetPath>$(OutDir)/$(TargetName)$(TargetExt)</TargetPath>");
cppTargetsFileContent.AppendLine(" </PropertyGroup>");
cppTargetsFileContent.AppendLine("</Project>");
Utilities.WriteFileIfChanged(Path.Combine(projectDirectory, "Microsoft.Cpp.targets"), cppTargetsFileContent.ToString());
Utilities.WriteFileIfChanged(Path.Combine(projectDirectory, "Microsoft.Cpp.Default.props"), vcUserFileContent.ToString());
Utilities.WriteFileIfChanged(Path.Combine(projectDirectory, "Microsoft.Cpp.props"), vcUserFileContent.ToString());
}
// Save the files
Utilities.WriteFileIfChanged(project.Path, vcProjectFileContent.ToString());
Utilities.WriteFileIfChanged(project.Path + ".filters", vcFiltersFileContent.ToString());