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

@@ -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>