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

# Conflicts:
#	Source/Engine/Level/Actors/Sky.cpp
This commit is contained in:
Wojtek Figat
2025-10-03 22:37:32 +02:00
26 changed files with 152 additions and 258 deletions

View File

@@ -5,19 +5,19 @@ body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please attach any minimal repoduction projects!
Thanks for taking the time to fill out this bug report! Please attach any minimal reproduction projects!
- type: textarea
id: description-area
attributes:
label: Description
description: Please provide a description and what you expected to happen.
description: Please provide a description of the bug and what you expected to happen.
validations:
required: true
- type: textarea
id: steps-area
attributes:
label: Steps to reproduce
description: Please provide an repoduction steps.
description: Please provide reproduction steps if possible.
validations:
required: true
- type: dropdown
@@ -26,10 +26,10 @@ body:
label: Version
description: What version of Flax are you running?
options:
- 1.8
- 1.9
- 1.10
- 1.11
- '1.8'
- '1.9'
- '1.10'
- '1.11'
- master branch
default: 2
validations:

View File

@@ -14,9 +14,9 @@ body:
validations:
required: true
- type: textarea
id: benifits-area
id: benefits-area
attributes:
label: Benifits
description: Please provide what benifits this feature would provide to the engine!
label: Benefits
description: Please provide what benefits this feature would provide to the engine!
validations:
required: true

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

Binary file not shown.

View File

@@ -32,7 +32,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
private void OnImageClicked(Image image, MouseButton button)
{
var texture = Values[0] as GPUTexture;
if (!texture)
if (!texture || button != MouseButton.Right)
return;
var menu = new ContextMenu();
menu.AddButton("Save...", () => Screenshot.Capture(Values[0] as GPUTexture));

View File

@@ -673,6 +673,7 @@ namespace FlaxEditor.Modules
pasteAction.Do(out _, out var nodeParents);
// Select spawned objects (parents only)
newSelection.Clear();
newSelection.AddRange(nodeParents);
var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo);
selectAction.Do();

View File

@@ -151,6 +151,8 @@ namespace FlaxEditor.Surface
/// </summary>
protected virtual Color FooterColor => GroupArchetype.Color;
private Float2 mouseDownMousePosition;
/// <summary>
/// Calculates the size of the node including header, footer, and margins.
/// </summary>
@@ -1121,7 +1123,7 @@ namespace FlaxEditor.Surface
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
return true;
if (button == MouseButton.Right)
return true;
mouseDownMousePosition = Input.Mouse.Position;
return false;
}
@@ -1143,6 +1145,10 @@ namespace FlaxEditor.Surface
// Secondary Context Menu
if (button == MouseButton.Right)
{
float distance = Float2.Distance(mouseDownMousePosition, Input.Mouse.Position);
if (distance > 2.5f)
return true;
if (!IsSelected)
Surface.Select(this);
var tmp = PointToParent(ref location);

View File

@@ -149,13 +149,13 @@ bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool h
bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches, Texture* heightmap, float heightmapScale, Texture* splatmap1, Texture* splatmap2)
{
PROFILE_CPU_NAMED("Terrain.GenerateTerrain");
CHECK_RETURN(terrain && terrain->GetChunkSize() != 0, true);
if (numberOfPatches.X < 1 || numberOfPatches.Y < 1)
{
LOG(Warning, "Cannot setup terain with no patches.");
LOG(Warning, "Cannot setup terrain with no patches.");
return false;
}
PROFILE_CPU_NAMED("Terrain.GenerateTerrain");
// Wait for assets to be loaded
if (heightmap && heightmap->WaitForLoaded())
@@ -178,7 +178,9 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
terrain->AddPatches(numberOfPatches);
// Prepare data
const auto heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
const int32 heightmapSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
const float heightmapSizeInv = 1.0f / (float)(heightmapSize - 1);
const Float2 uvPerPatch = Float2::One / Float2(numberOfPatches);
Array<float> heightmapData;
heightmapData.Resize(heightmapSize * heightmapSize);
@@ -192,19 +194,17 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
const auto sampler = PixelFormatSampler::Get(dataHeightmap.Format);
// Initialize with sub-range of the input heightmap
const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches);
const float heightmapSizeInv = 1.0f / (heightmapSize - 1);
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
{
auto patch = terrain->GetPatch(patchIndex);
const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
const Float2 uvStart = Float2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
// Sample heightmap pixels with interpolation to get actual heightmap vertices locations
for (int32 z = 0; z < heightmapSize; z++)
{
for (int32 x = 0; x < heightmapSize; x++)
{
const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
const Float2 uv = uvStart + Float2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
const Color color = sampler->SampleLinear(dataHeightmap.Mip0DataPtr->Get(), uv, dataHeightmap.Mip0Size, dataHeightmap.RowPitch);
heightmapData[z * heightmapSize + x] = color.R * heightmapScale;
}
@@ -230,37 +230,30 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
Texture* splatmaps[2] = { splatmap1, splatmap2 };
Array<Color32> splatmapData;
TextureDataResult data1;
const Vector2 uvPerPatch = Vector2::One / Vector2(numberOfPatches);
const float heightmapSizeInv = 1.0f / (heightmapSize - 1);
for (int32 index = 0; index < ARRAY_COUNT(splatmaps); index++)
{
const auto splatmap = splatmaps[index];
if (!splatmap)
continue;
// Prepare data
if (splatmapData.IsEmpty())
splatmapData.Resize(heightmapSize * heightmapSize);
// Get splatmap data
if (GetTextureDataForSampling(splatmap, data1))
return true;
const auto sampler = PixelFormatSampler::Get(data1.Format);
// Modify heightmap splatmaps with sub-range of the input splatmaps
splatmapData.Resize(heightmapSize * heightmapSize);
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
{
auto patch = terrain->GetPatch(patchIndex);
const Vector2 uvStart = Vector2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
const Float2 uvStart = Float2((float)patch->GetX(), (float)patch->GetZ()) * uvPerPatch;
// Sample splatmap pixels with interpolation to get actual splatmap values
for (int32 z = 0; z < heightmapSize; z++)
{
for (int32 x = 0; x < heightmapSize; x++)
{
const Vector2 uv = uvStart + Vector2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
const Float2 uv = uvStart + Float2(x * heightmapSizeInv, z * heightmapSizeInv) * uvPerPatch;
const Color color = sampler->SampleLinear(data1.Mip0DataPtr->Get(), uv, data1.Mip0Size, data1.RowPitch);
Color32 layers;
@@ -374,63 +367,38 @@ Color32* TerrainTools::GetSplatMapData(Terrain* terrain, const Int2& patchCoord,
bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
{
PROFILE_CPU_NAMED("Terrain.ExportTerrain");
CHECK_RETURN(terrain && terrain->GetPatchesCount() != 0, true);
const auto firstPatch = terrain->GetPatch(0);
// Calculate texture size
const int32 patchEdgeVertexCount = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
const int32 patchVertexCount = patchEdgeVertexCount * patchEdgeVertexCount;
// Find size of heightmap in patches
const auto firstPatch = terrain->GetPatch(0);
Int2 start(firstPatch->GetX(), firstPatch->GetZ());
Int2 end(start);
for (int32 i = 0; i < terrain->GetPatchesCount(); i++)
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
{
const int32 x = terrain->GetPatch(i)->GetX();
const int32 y = terrain->GetPatch(i)->GetZ();
if (x < start.X)
start.X = x;
if (y < start.Y)
start.Y = y;
if (x > end.X)
end.X = x;
if (y > end.Y)
end.Y = y;
const auto patch = terrain->GetPatch(patchIndex);
const Int2 pos(patch->GetX(), patch->GetZ());
start = Int2::Min(start, pos);
end = Int2::Max(end, pos);
}
const Int2 size = (end + 1) - start;
// Allocate - with space for non-existent patches
// Allocate heightmap for a whole terrain (NumberOfPatches * 4x4 * ChunkSize + 1)
const Int2 heightmapSize = size * Terrain::ChunksCountEdge * terrain->GetChunkSize() + 1;
Array<float> heightmap;
heightmap.Resize(patchVertexCount * size.X * size.Y);
// Set to any element, where: min < elem < max
heightmap.Resize(heightmapSize.X * heightmapSize.Y);
heightmap.SetAll(firstPatch->GetHeightmapData()[0]);
const int32 heightmapWidth = patchEdgeVertexCount * size.X;
// Fill heightmap with data
// Fill heightmap with data from all patches
const int32 rowSize = terrain->GetChunkSize() * Terrain::ChunksCountEdge + 1;
for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++)
{
// Pick a patch
const auto patch = terrain->GetPatch(patchIndex);
const float* data = patch->GetHeightmapData();
// Beginning of patch
int32 dstIndex = (patch->GetX() - start.X) * patchEdgeVertexCount +
(patch->GetZ() - start.Y) * size.Y * patchVertexCount;
// Iterate over lines in patch
for (int32 z = 0; z < patchEdgeVertexCount; z++)
{
// Iterate over vertices in line
for (int32 x = 0; x < patchEdgeVertexCount; x++)
{
heightmap[dstIndex + x] = data[z * patchEdgeVertexCount + x];
}
dstIndex += heightmapWidth;
}
const Int2 pos(patch->GetX() - start.X, patch->GetZ() - start.Y);
const float* src = patch->GetHeightmapData();
float* dst = heightmap.Get() + pos.X * (rowSize - 1) + pos.Y * heightmapSize.X * (rowSize - 1);
for (int32 row = 0; row < rowSize; row++)
Platform::MemoryCopy(dst + row * heightmapSize.X, src + row * rowSize, rowSize * sizeof(float));
}
// Interpolate to 16-bit int
@@ -438,44 +406,42 @@ bool TerrainTools::ExportTerrain(Terrain* terrain, String outputFolder)
maxHeight = minHeight = heightmap[0];
for (int32 i = 1; i < heightmap.Count(); i++)
{
float h = heightmap[i];
float h = heightmap.Get()[i];
if (maxHeight < h)
maxHeight = h;
else if (minHeight > h)
minHeight = h;
}
const float maxValue = 65535.0f;
const float alpha = maxValue / (maxHeight - minHeight);
const float alpha = MAX_uint16 / (maxHeight - minHeight);
// Storage for pixel data
Array<uint16> byteHeightmap(heightmap.Capacity());
for (auto& elem : heightmap)
Array<uint16> byteHeightmap;
byteHeightmap.Resize(heightmap.Count());
for (int32 i = 0; i < heightmap.Count(); i++)
{
byteHeightmap.Add(static_cast<uint16>(alpha * (elem - minHeight)));
float height = heightmap.Get()[i];
byteHeightmap.Get()[i] = static_cast<uint16>(alpha * (height - minHeight));
}
// Create texture
TextureData textureData;
textureData.Height = textureData.Width = heightmapWidth;
textureData.Width = heightmapSize.X;
textureData.Height = heightmapSize.Y;
textureData.Depth = 1;
textureData.Format = PixelFormat::R16_UNorm;
textureData.Items.Resize(1);
textureData.Items[0].Mips.Resize(1);
// Fill mip data
TextureMipData* srcMip = textureData.GetData(0, 0);
srcMip->Data.Link(byteHeightmap.Get());
srcMip->Lines = textureData.Height;
srcMip->RowPitch = textureData.Width * 2; // 2 bytes per pixel for format R16
srcMip->RowPitch = textureData.Width * sizeof(uint16);
srcMip->DepthPitch = srcMip->Lines * srcMip->RowPitch;
// Find next non-existing file heightmap file
FileSystem::NormalizePath(outputFolder);
const String baseFileName(TEXT("heightmap"));
String outputPath;
for (int32 i = 0; i < MAX_int32; i++)
for (int32 i = 0; i < 100; i++)
{
outputPath = outputFolder / baseFileName + StringUtils::ToString(i) + TEXT(".png");
if (!FileSystem::FileExists(outputPath))

View File

@@ -535,7 +535,7 @@ namespace FlaxEditor.Viewport
// Setup options
{
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
_editor.Options.OptionsChanged += OnEditorOptionsChanged;
SetupViewportOptions();
}
@@ -581,7 +581,7 @@ namespace FlaxEditor.Viewport
// Camera Settings Menu
var cameraCM = new ContextMenu();
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), Editor.Instance.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), _editor.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
{
Tag = this,
TooltipText = "Camera Settings",
@@ -590,7 +590,7 @@ namespace FlaxEditor.Viewport
_cameraWidget.Parent = this;
// Orthographic/Perspective Mode Widget
_orthographicModeButton = new ViewportWidgetButton(string.Empty, Editor.Instance.Icons.CamSpeed32, null, true)
_orthographicModeButton = new ViewportWidgetButton(string.Empty, _editor.Icons.CamSpeed32, null, true)
{
Checked = !_isOrtho,
TooltipText = "Toggle Orthographic/Perspective Mode",
@@ -863,8 +863,8 @@ namespace FlaxEditor.Viewport
{
}
});
viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32;
viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32;
viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = _editor.Icons.Rotate32;
viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0));
viewLayers.AddSeparator();
var layers = LayersAndTagsSettings.GetCurrentLayers();
if (layers != null && layers.Length > 0)
@@ -904,8 +904,8 @@ namespace FlaxEditor.Viewport
{
}
});
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = _editor.Icons.Rotate32;
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None);
viewFlags.AddSeparator();
for (int i = 0; i < ViewFlagsValues.Length; i++)
{
@@ -1085,7 +1085,7 @@ namespace FlaxEditor.Viewport
/// </summary>
private void SetupViewportOptions()
{
var options = Editor.Instance.Options.Options;
var options = _editor.Options.Options;
_minMovementSpeed = options.Viewport.MinMovementSpeed;
MovementSpeed = options.Viewport.MovementSpeed;
_maxMovementSpeed = options.Viewport.MaxMovementSpeed;
@@ -1701,7 +1701,7 @@ namespace FlaxEditor.Viewport
// Check if update mouse
var size = Size;
var options = Editor.Instance.Options.Options;
var options = _editor.Options.Options;
if (_isControllingMouse)
{
var rmbWheel = false;
@@ -1926,7 +1926,7 @@ namespace FlaxEditor.Viewport
return true;
// Custom input events
return InputActions.Process(Editor.Instance, this, key);
return InputActions.Process(_editor, this, key);
}
/// <inheritdoc />
@@ -1943,7 +1943,7 @@ namespace FlaxEditor.Viewport
base.Draw();
// Add overlay during debugger breakpoint hang
if (Editor.Instance.Simulation.IsDuringBreakpointHang)
if (_editor.Simulation.IsDuringBreakpointHang)
{
var bounds = new Rectangle(Float2.Zero, Size);
Render2D.FillRectangle(bounds, new Color(0.0f, 0.0f, 0.0f, 0.2f));
@@ -1967,7 +1967,7 @@ namespace FlaxEditor.Viewport
/// <inheritdoc />
public override void OnDestroy()
{
Editor.Instance.Options.OptionsChanged -= OnEditorOptionsChanged;
_editor.Options.OptionsChanged -= OnEditorOptionsChanged;
base.OnDestroy();
}

View File

@@ -97,6 +97,7 @@ namespace FlaxEditor.Windows
"Jean-Baptiste Perrier",
"Chandler Cox",
"Ari Vuollet",
"Vincent Saarmann",
});
authors.Sort();
var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70)

View File

@@ -93,6 +93,7 @@ void MultiBlendBucketInit(AnimGraphInstanceData::Bucket& bucket)
void BlendPoseBucketInit(AnimGraphInstanceData::Bucket& bucket)
{
bucket.BlendPose.TransitionPosition = 0.0f;
bucket.BlendPose.BlendPoseIndex = -1;
bucket.BlendPose.PreviousBlendPoseIndex = -1;
}

View File

@@ -239,7 +239,8 @@ public:
struct BlendPoseBucket
{
float TransitionPosition;
int32 PreviousBlendPoseIndex;
int16 BlendPoseIndex;
int16 PreviousBlendPoseIndex;
};
struct StateMachineBucket
@@ -810,6 +811,7 @@ public:
{
// Copy the node transformations
Platform::MemoryCopy(dstNodes->Nodes.Get(), srcNodes->Nodes.Get(), sizeof(Transform) * _skeletonNodesCount);
dstNodes->RootMotion = srcNodes->RootMotion;
// Copy the animation playback state
dstNodes->Position = srcNodes->Position;

View File

@@ -676,9 +676,12 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const
if (!ANIM_GRAPH_IS_VALID_PTR(poseB))
nodesB = GetEmptyNodes();
const Transform* srcA = nodesA->Nodes.Get();
const Transform* srcB = nodesB->Nodes.Get();
Transform* dst = nodes->Nodes.Get();
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]);
Transform::Lerp(srcA[i], srcB[i], alpha, dst[i]);
}
Transform::Lerp(nodesA->RootMotion, nodesB->RootMotion, alpha, nodes->RootMotion);
nodes->Position = Math::Lerp(nodesA->Position, nodesB->Position, alpha);
@@ -1263,21 +1266,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
{
const auto valueA = tryGetValue(node->GetBox(1), Value::Null);
const auto valueB = tryGetValue(node->GetBox(2), Value::Null);
const auto nodes = node->GetNodes(this);
auto nodesA = static_cast<AnimGraphImpulse*>(valueA.AsPointer);
auto nodesB = static_cast<AnimGraphImpulse*>(valueB.AsPointer);
if (!ANIM_GRAPH_IS_VALID_PTR(valueA))
nodesA = GetEmptyNodes();
if (!ANIM_GRAPH_IS_VALID_PTR(valueB))
nodesB = GetEmptyNodes();
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]);
}
Transform::Lerp(nodesA->RootMotion, nodesB->RootMotion, alpha, nodes->RootMotion);
value = nodes;
value = Blend(node, valueA, valueB, alpha, AlphaBlendMode::Linear);
}
break;
@@ -1758,35 +1747,38 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
// [2]: int Pose Count
// [3]: AlphaBlendMode Mode
// Prepare
auto& bucket = context.Data->State[node->BucketIndex].BlendPose;
const int32 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]);
const int16 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]);
const float blendDuration = (float)tryGetValue(node->GetBox(2), node->Values[1]);
const int32 poseCount = Math::Clamp(node->Values[2].AsInt, 0, MaxBlendPoses);
const AlphaBlendMode mode = (AlphaBlendMode)node->Values[3].AsInt;
// Skip if nothing to blend
if (poseCount == 0 || poseIndex < 0 || poseIndex >= poseCount)
{
break;
// Check if swap transition end points
if (bucket.PreviousBlendPoseIndex == poseIndex && bucket.BlendPoseIndex != poseIndex && bucket.TransitionPosition >= ANIM_GRAPH_BLEND_THRESHOLD)
{
bucket.TransitionPosition = blendDuration - bucket.TransitionPosition;
Swap(bucket.BlendPoseIndex, bucket.PreviousBlendPoseIndex);
}
// Check if transition is not active (first update, pose not changing or transition ended)
bucket.TransitionPosition += context.DeltaTime;
bucket.BlendPoseIndex = poseIndex;
if (bucket.PreviousBlendPoseIndex == -1 || bucket.PreviousBlendPoseIndex == poseIndex || bucket.TransitionPosition >= blendDuration || blendDuration <= ANIM_GRAPH_BLEND_THRESHOLD)
{
bucket.TransitionPosition = 0.0f;
bucket.BlendPoseIndex = poseIndex;
bucket.PreviousBlendPoseIndex = poseIndex;
value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null);
value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.BlendPoseIndex), Value::Null);
break;
}
ASSERT(bucket.PreviousBlendPoseIndex >= 0 && bucket.PreviousBlendPoseIndex < poseCount);
// Blend two animations
{
const float alpha = bucket.TransitionPosition / blendDuration;
const auto valueA = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.PreviousBlendPoseIndex), Value::Null);
const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null);
const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.BlendPoseIndex), Value::Null);
value = Blend(node, valueA, valueB, alpha, mode);
}

View File

@@ -63,9 +63,16 @@ void BoundingFrustum::SetMatrix(const Matrix& matrix)
Plane BoundingFrustum::GetPlane(int32 index) const
{
if (index > 5)
return Plane();
return _planes[index];
switch (index)
{
case 0: return _pLeft;
case 1: return _pRight;
case 2: return _pTop;
case 3: return _pBottom;
case 4: return _pNear;
case 5: return _pFar;
default: return Plane();
}
}
static Vector3 Get3PlanesInterPoint(const Plane& p1, const Plane& p2, const Plane& p3)

View File

@@ -182,7 +182,7 @@ namespace FlaxEngine
/// <summary>
/// Returns one of the 6 planes related to this frustum.
/// </summary>
/// <param name="index">Plane index where 0 fro Left, 1 for Right, 2 for Top, 3 for Bottom, 4 for Near, 5 for Far</param>
/// <param name="index">Plane index where 0 for Left, 1 for Right, 2 for Top, 3 for Bottom, 4 for Near, 5 for Far</param>
/// <returns>The frustum plane.</returns>
public Plane GetPlane(int index)
{

View File

@@ -148,13 +148,13 @@ public:
Plane GetPlane(int32 index) const;
/// <summary>
/// Gets the the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
/// Gets the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
/// </summary>
/// <param name="corners">The corners.</param>
void GetCorners(Float3 corners[8]) const;
/// <summary>
/// Gets the the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
/// Gets the 8 corners of the frustum: Near1 (near right down corner), Near2 (near right top corner), Near3 (near Left top corner), Near4 (near Left down corner), Far1 (far right down corner), Far2 (far right top corner), Far3 (far left top corner), Far4 (far left down corner).
/// </summary>
/// <param name="corners">The corners.</param>
void GetCorners(Double3 corners[8]) const;

View File

@@ -265,6 +265,14 @@ public:
return Projection.M44 >= 1.0f;
}
/// <summary>
/// Determines whether view Origin has been moved in this frame. Old history buffers/data might be invalid.
/// </summary>
FORCE_INLINE bool IsOriginTeleport() const
{
return Origin != PrevOrigin;
}
public:
// Ignore deprecation warnings in defaults
PRAGMA_DISABLE_DEPRECATION_WARNINGS

View File

@@ -21,7 +21,8 @@
#endif
GPU_CB_STRUCT(Data {
Matrix WVP;
Matrix WorldViewProjection;
Matrix InvViewProjection;
Float3 ViewOffset;
float Padding;
ShaderGBufferData GBuffer;
@@ -30,8 +31,6 @@ GPU_CB_STRUCT(Data {
Sky::Sky(const SpawnParams& params)
: Actor(params)
, _psSky(nullptr)
, _psFog(nullptr)
{
_drawNoCulling = 1;
_drawCategory = SceneRendering::PreRender;
@@ -51,7 +50,6 @@ Sky::Sky(const SpawnParams& params)
Sky::~Sky()
{
SAFE_DELETE_GPU_RESOURCE(_psSky);
SAFE_DELETE_GPU_RESOURCE(_psFog);
}
void Sky::InitConfig(ShaderAtmosphericFogData& config) const
@@ -90,7 +88,7 @@ void Sky::Draw(RenderContext& renderContext)
if (HasContentLoaded() && EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Sky))
{
// Ensure to have pipeline state cache created
if (_psSky == nullptr || _psFog == nullptr)
if (_psSky == nullptr)
{
const auto shader = _shader->GetShader();
@@ -112,21 +110,6 @@ void Sky::Draw(RenderContext& renderContext)
LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString());
}
}
if (_psFog == nullptr)
{
_psFog = GPUDevice::Instance->CreatePipelineState();
GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
psDesc.PS = shader->GetPS("PS_Fog");
psDesc.DepthWriteEnable = false;
psDesc.DepthClipEnable = false;
psDesc.BlendMode = BlendingMode::Additive;
if (_psFog->Init(psDesc))
{
LOG(Warning, "Cannot create graphics pipeline state object for '{0}'.", ToString());
}
}
}
// Register for the sky and fog pass
@@ -138,7 +121,6 @@ void Sky::Draw(RenderContext& renderContext)
void Sky::Serialize(SerializeStream& stream, const void* otherObj)
{
// Base
Actor::Serialize(stream, otherObj);
SERIALIZE_GET_OTHER_OBJ(Sky);
@@ -151,7 +133,6 @@ void Sky::Serialize(SerializeStream& stream, const void* otherObj)
void Sky::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
// Base
Actor::Deserialize(stream, modifier);
DESERIALIZE_MEMBER(Sun, SunLight);
@@ -172,40 +153,7 @@ bool Sky::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)
void Sky::DrawFog(GPUContext* context, RenderContext& renderContext, GPUTextureView* output)
{
// Get precomputed cache and bind it to the pipeline
AtmosphereCache cache;
if (!AtmospherePreCompute::GetCache(&cache))
return;
PROFILE_GPU_CPU("Sky Fog");
context->BindSR(4, cache.Transmittance);
context->BindSR(5, cache.Irradiance);
context->BindSR(6, cache.Inscatter->ViewVolume());
// Bind GBuffer inputs
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, renderContext.Buffers->DepthBuffer);
// Setup constants data
Data data;
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
data.ViewOffset = renderContext.View.Origin + GetPosition();
InitConfig(data.Fog);
data.Fog.AtmosphericFogSunPower *= SunLight ? SunLight->Brightness : 1.0f;
bool useSpecularLight = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::SpecularLight);
if (!useSpecularLight)
{
data.Fog.AtmosphericFogSunDiscScale = 0;
}
// Bind pipeline
auto cb = _shader->GetShader()->GetCB(0);
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
context->SetState(_psFog);
context->SetRenderTarget(output);
context->DrawFullscreenTriangle();
MISSING_CODE("sky fog");
}
bool Sky::IsDynamicSky() const
@@ -231,14 +179,14 @@ void Sky::ApplySky(GPUContext* context, RenderContext& renderContext, const Matr
// Setup constants data
Matrix m;
Data data;
Matrix::Multiply(world, renderContext.View.Frustum.GetMatrix(), m);
Matrix::Transpose(m, data.WVP);
Matrix::Multiply(world, renderContext.View.ViewProjection(), m);
Matrix::Transpose(m, data.WorldViewProjection);
Matrix::Transpose(renderContext.View.IVP, data.InvViewProjection);
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
data.ViewOffset = renderContext.View.Origin + GetPosition();
InitConfig(data.Fog);
//data.Fog.AtmosphericFogSunPower *= SunLight ? SunLight->Brightness : 1.0f;
bool useSpecularLight = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::SpecularLight);
if (!useSpecularLight)
if (EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::SpecularLight))
{
// Hide sun disc if specular light is disabled
data.Fog.AtmosphericFogSunDiscScale = 0;
@@ -253,11 +201,8 @@ void Sky::ApplySky(GPUContext* context, RenderContext& renderContext, const Matr
void Sky::EndPlay()
{
// Cleanup
SAFE_DELETE_GPU_RESOURCE(_psSky);
SAFE_DELETE_GPU_RESOURCE(_psFog);
// Base
Actor::EndPlay();
}
@@ -268,7 +213,6 @@ void Sky::OnEnable()
GetSceneRendering()->AddViewportIcon(this);
#endif
// Base
Actor::OnEnable();
}
@@ -279,13 +223,11 @@ void Sky::OnDisable()
#endif
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
// Base
Actor::OnDisable();
}
void Sky::OnTransformChanged()
{
// Base
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation);

View File

@@ -20,8 +20,7 @@ class FLAXENGINE_API Sky : public Actor, public IAtmosphericFogRenderer, public
DECLARE_SCENE_OBJECT(Sky);
private:
AssetReference<Shader> _shader;
GPUPipelineState* _psSky;
GPUPipelineState* _psFog;
GPUPipelineState* _psSky = nullptr;
int32 _sceneRenderingKey = -1;
public:
@@ -57,7 +56,6 @@ private:
void OnShaderReloading(Asset* obj)
{
_psSky = nullptr;
_psFog = nullptr;
}
#endif
void InitConfig(ShaderAtmosphericFogData& config) const;

View File

@@ -19,6 +19,7 @@
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Level/Actors/Decal.h"
#include "Engine/Level/Actors/Sky.h"
#include "Engine/Engine/Engine.h"
GPU_CB_STRUCT(GBufferPassData {
@@ -416,8 +417,17 @@ void GBufferPass::DrawSky(RenderContext& renderContext, GPUContext* context)
// Calculate sphere model transform to cover far plane
Matrix m1, m2;
Matrix::Scaling(renderContext.View.Far / ((float)box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum
Matrix::CreateWorld(renderContext.View.Position, Float3::Up, Float3::Backward, m2); // Rotate sphere model
float size = renderContext.View.Far;
Float3 origin = renderContext.View.Position;
if (dynamic_cast<Sky*>(renderContext.List->Sky)) // TODO: refactor sky rendering (eg. let sky draw with custom projection)
{
BoundingSphere frustumBounds;
renderContext.View.CullingFrustum.GetSphere(frustumBounds);
origin = frustumBounds.Center;
size = frustumBounds.Radius;
}
Matrix::Scaling(size / ((float)box.GetSize().Y * 0.5f) * 0.95f, m1); // Scale to fit whole view frustum
Matrix::CreateWorld(origin, Float3::Up, Float3::Backward, m2); // Rotate sphere model
m1 *= m2;
// Draw sky

View File

@@ -357,8 +357,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
const bool isGBufferDebug = GBufferPass::IsDebugView(renderContext.View.Mode);
{
PROFILE_CPU_NAMED("Setup");
if (renderContext.View.Origin != renderContext.View.PrevOrigin)
renderContext.Task->CameraCut(); // Cut any temporal effects on rendering origin change
const int32 screenWidth = renderContext.Buffers->GetWidth();
const int32 screenHeight = renderContext.Buffers->GetHeight();
setup.UpscaleLocation = renderContext.Task->UpscaleLocation;

View File

@@ -167,7 +167,7 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
(float)_cache.GridSizeZ);
auto& fogData = renderContext.Buffers->VolumetricFogData;
fogData.MaxDistance = options.Distance;
if (renderContext.Task->IsCameraCut)
if (renderContext.Task->IsCameraCut || renderContext.View.IsOriginTeleport())
_cache.HistoryWeight = 0.0f;
// Init data (partial, without directional light or sky light data);
@@ -301,7 +301,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
PROFILE_GPU_CPU("Volumetric Fog");
// TODO: test exponential depth distribution (should give better quality near the camera)
// TODO: use tiled light culling and render unshadowed lights in single pass
// TODO: use tiled light culling and render shadowed/unshadowed lights in single pass
// Try to get shadows atlas
GPUTexture* shadowMap;

View File

@@ -859,9 +859,7 @@ void Terrain::OnEnable()
{
auto patch = _patches[i];
if (patch->_physicsActor)
{
PhysicsBackend::AddSceneActor(scene, patch->_physicsActor);
}
}
// Base
@@ -880,9 +878,7 @@ void Terrain::OnDisable()
{
auto patch = _patches[i];
if (patch->_physicsActor)
{
PhysicsBackend::RemoveSceneActor(scene, patch->_physicsActor);
}
}
// Base

View File

@@ -2230,7 +2230,8 @@ void TerrainPatch::DestroyCollision()
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::RemoveCollider(_terrain);
PhysicsBackend::RemoveSceneActor(scene, _physicsActor);
if (_terrain->IsDuringPlay() && _terrain->IsActiveInHierarchy())
PhysicsBackend::RemoveSceneActor(scene, _physicsActor);
PhysicsBackend::DestroyActor(_physicsActor);
PhysicsBackend::DestroyShape(_physicsShape);
PhysicsBackend::DestroyObject(_physicsHeightField);

View File

@@ -181,8 +181,8 @@ namespace FlaxEngine.GUI
ImageColor = style.BorderSelected * 1.2f;
BorderColor = style.BorderNormal;
BorderColorHighlighted = style.BorderSelected;
CheckedImage = new SpriteBrush(style.CheckBoxTick);
IntermediateImage = new SpriteBrush(style.CheckBoxIntermediate);
CheckedImage = style.CheckBoxTick.IsValid ? new SpriteBrush(style.CheckBoxTick) : new SolidColorBrush(style.Foreground);
IntermediateImage = style.CheckBoxIntermediate.IsValid ? new SpriteBrush(style.CheckBoxIntermediate) : new SolidColorBrush(style.ForegroundGrey);
CacheBox();
}

View File

@@ -14,7 +14,7 @@
#include "Engine/Content/AssetsContainer.h"
#include "Engine/Animations/Curve.h"
#define SHADER_GRAPH_MAX_CALL_STACK 100
#define SHADER_GRAPH_MAX_CALL_STACK 50
enum class MaterialSceneTextures;
template<class BoxType>

View File

@@ -7,7 +7,8 @@
#include "./Flax/AtmosphereFog.hlsl"
META_CB_BEGIN(0, Data)
float4x4 WVP;
float4x4 WorldViewProjection;
float4x4 InvViewProjection;
float3 ViewOffset;
float Padding;
GBufferData GBuffer;
@@ -18,7 +19,7 @@ DECLARE_GBUFFERDATA_ACCESS(GBuffer)
struct MaterialInput
{
float4 Position : SV_Position;
float4 Position : SV_Position;
float4 ScreenPos : TEXCOORD0;
};
@@ -30,12 +31,9 @@ MaterialInput VS(ModelInput_PosOnly input)
MaterialInput output;
// Compute vertex position
output.Position = mul(float4(input.Position.xyz, 1), WVP);
output.Position = mul(float4(input.Position.xyz, 1), WorldViewProjection);
output.ScreenPos = output.Position;
// Place pixels on the far plane
output.Position = output.Position.xyzz;
return output;
}
@@ -45,15 +43,15 @@ GBufferOutput PS_Sky(MaterialInput input)
{
GBufferOutput output;
// Obtain UVs corresponding to the current pixel
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
// Calculate view vector (unproject at the far plane)
GBufferData gBufferData = GetGBufferData();
float4 clipPos = float4(input.ScreenPos.xy / input.ScreenPos.w, 1.0, 1.0);
clipPos = mul(clipPos, InvViewProjection);
float3 worldPos = clipPos.xyz / clipPos.w;
float3 viewVector = normalize(worldPos - gBufferData.ViewPos);
// Sample atmosphere color
GBufferData gBufferData = GetGBufferData();
float3 vsPos = GetViewPos(gBufferData, uv, LinearZ2DeviceDepth(gBufferData, 1));
float3 wsPos = mul(float4(vsPos, 1), gBufferData.InvViewMatrix).xyz;
float3 viewVector = wsPos - gBufferData.ViewPos;
float4 color = GetAtmosphericFog(AtmosphericFog, gBufferData.ViewFar, wsPos + ViewOffset, gBufferData.ViewPos + ViewOffset);
float4 color = GetAtmosphericFog(AtmosphericFog, gBufferData.ViewFar, gBufferData.ViewPos + ViewOffset, viewVector, gBufferData.ViewFar, float3(0, 0, 0));
// Pack GBuffer
output.Light = color;
@@ -64,36 +62,3 @@ GBufferOutput PS_Sky(MaterialInput input)
return output;
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Fog(Quad_VS2PS input) : SV_Target0
{
float4 result;
/*
// Sample GBuffer
GBufferSample gBuffer = SampleGBuffer(GBuffer, input.TexCoord);
// TODO: set valid scene color for better inscatter reflectance
//float3 sceneColor = gBuffer.Color * AtmosphericFogDensityOffset;
float3 sceneColor = float3(0, 0, 0);
// Sample atmosphere color
float3 viewVector = gBuffer.WorldPos - GBuffer.ViewPos;
float SceneDepth = length(ViewVector);
result = GetAtmosphericFog(AtmosphericFog, GBuffer.ViewFar, GBuffer.ViewPos, viewVector, SceneDepth, sceneColor);
//result.rgb = normalize(ViewVector);
//result.rgb = ViewVector;
//result.rgb = SceneDepth.xxx / GBuffer.ViewFar * 0.5f;
//result = float4(input.TexCoord, 0, 1);
//result = AtmosphereTransmittanceTexture.Sample(SamplerLinearClamp, input.TexCoord);
//result = float4(AtmosphereIrradianceTexture.Sample(SamplerLinearClamp, input.TexCoord).rgb*5.0, 1.0);
//result = AtmosphereInscatterTexture.Sample(SamplerLinearClamp, float3(input.TexCoord.xy, (AtmosphericFogSunDirection.x+1.0)/2.0));
*/
// TODO: finish fog
result = float4(1, 0, 0, 1);
return result;
}