From 560a78d86627a98ca59ce9ab3eeb862c7bf9183a Mon Sep 17 00:00:00 2001 From: tebjan Date: Tue, 9 Jun 2020 02:12:51 +0200 Subject: [PATCH] [Rendering] Added ModelTransformUsage to decide how the original model transformation is used --- .../Stride.Engine/Engine/IInstancing.cs | 11 +++ .../Stride.Engine/Engine/InstanceComponent.cs | 3 +- .../Stride.Engine/Engine/InstancingBase.cs | 4 + .../Engine/InstancingComponent.cs | 2 +- .../Engine/InstancingEntiyTransform.cs | 6 ++ .../Engine/InstancingUserArray.cs | 2 +- .../Engine/Processors/InstancingProcessor.cs | 74 +++++++++++++++---- .../Rendering/InstancingRenderFeature.cs | 3 + .../Rendering/StrideEffectBase.sdfx | 1 + .../Rendering/StrideEffectBase.sdfx.cs | 1 + .../Rendering/StrideEffectBaseKeys.cs | 2 + .../TransformationInstancing.sdsl | 18 ++++- 12 files changed, 110 insertions(+), 17 deletions(-) diff --git a/sources/engine/Stride.Engine/Engine/IInstancing.cs b/sources/engine/Stride.Engine/Engine/IInstancing.cs index 65430a86aa..071b23801d 100644 --- a/sources/engine/Stride.Engine/Engine/IInstancing.cs +++ b/sources/engine/Stride.Engine/Engine/IInstancing.cs @@ -5,10 +5,21 @@ namespace Stride.Engine { + public enum ModelTransformUsage + { + Replace, + PreMultiply, + PostMultiply + } + public interface IInstancing { int InstanceCount { get; } + BoundingBox BoundingBox { get; } + + ModelTransformUsage ModelTransformUsage { get; } + void Update(); } } diff --git a/sources/engine/Stride.Engine/Engine/InstanceComponent.cs b/sources/engine/Stride.Engine/Engine/InstanceComponent.cs index fd887d61a1..7648305f74 100644 --- a/sources/engine/Stride.Engine/Engine/InstanceComponent.cs +++ b/sources/engine/Stride.Engine/Engine/InstanceComponent.cs @@ -32,11 +32,12 @@ public sealed class InstanceComponent : ActivableEntityComponent [Display("Instancing", Expand = ExpandRule.Always)] public InstancingComponent Master { - get => master; + get => master; set { if (value != master) { + // Reject instancing that isn't set if (value != null && value.Type == null) return; diff --git a/sources/engine/Stride.Engine/Engine/InstancingBase.cs b/sources/engine/Stride.Engine/Engine/InstancingBase.cs index b08df7a516..afb5466707 100644 --- a/sources/engine/Stride.Engine/Engine/InstancingBase.cs +++ b/sources/engine/Stride.Engine/Engine/InstancingBase.cs @@ -25,6 +25,10 @@ public abstract class InstancingBase : IInstancing [DataMemberIgnore] public virtual BoundingBox BoundingBox { get; set; } = BoundingBox.Empty; + [DataMember(10)] + [Display("Model Transformation Usage")] + public virtual ModelTransformUsage ModelTransformUsage { get; set; } + public virtual void Update() { diff --git a/sources/engine/Stride.Engine/Engine/InstancingComponent.cs b/sources/engine/Stride.Engine/Engine/InstancingComponent.cs index f03691510b..d79a4723ad 100644 --- a/sources/engine/Stride.Engine/Engine/InstancingComponent.cs +++ b/sources/engine/Stride.Engine/Engine/InstancingComponent.cs @@ -24,6 +24,6 @@ public sealed class InstancingComponent : ActivableEntityComponent [DataMember(10)] [NotNull] [Display("Instancing Type", Expand = ExpandRule.Always)] - public IInstancing Type { get; set; } + public IInstancing Type { get; set; } = new InstancingEntityTransform(); } } diff --git a/sources/engine/Stride.Engine/Engine/InstancingEntiyTransform.cs b/sources/engine/Stride.Engine/Engine/InstancingEntiyTransform.cs index f62bcb5103..7931f02234 100644 --- a/sources/engine/Stride.Engine/Engine/InstancingEntiyTransform.cs +++ b/sources/engine/Stride.Engine/Engine/InstancingEntiyTransform.cs @@ -15,6 +15,12 @@ namespace Stride.Engine [Display("EntityTransform")] public class InstancingEntityTransform : InstancingUserArray { + [DataMemberIgnore] + public override ModelTransformUsage ModelTransformUsage + { + get => ModelTransformUsage.Replace; + } + private readonly List instances = new List(); internal InstanceComponent GetInstanceAt(int instanceId) diff --git a/sources/engine/Stride.Engine/Engine/InstancingUserArray.cs b/sources/engine/Stride.Engine/Engine/InstancingUserArray.cs index 484ecd3855..f3767ecfad 100644 --- a/sources/engine/Stride.Engine/Engine/InstancingUserArray.cs +++ b/sources/engine/Stride.Engine/Engine/InstancingUserArray.cs @@ -61,7 +61,7 @@ public override void Update() { if (WorldMatrices != null) { - // Make sure inverse matrices are big enough + // Make sure inverse matrices array is big enough if (WorldInverseMatrices.Length < InstanceCount) { WorldInverseMatrices = new Matrix[InstanceCount]; diff --git a/sources/engine/Stride.Engine/Engine/Processors/InstancingProcessor.cs b/sources/engine/Stride.Engine/Engine/Processors/InstancingProcessor.cs index 485c5f455c..15213c5fae 100644 --- a/sources/engine/Stride.Engine/Engine/Processors/InstancingProcessor.cs +++ b/sources/engine/Stride.Engine/Engine/Processors/InstancingProcessor.cs @@ -17,6 +17,7 @@ namespace Stride.Engine.Processors public class InstancingProcessor : EntityProcessor, IEntityComponentRenderProcessor { private readonly Dictionary modelInstancingMap = new Dictionary(); + private ModelRenderProcessor ModelRenderProcessor; public class InstancingData { @@ -61,25 +62,70 @@ private void UpdateInstancing(InstancingComponent instancingComponent, Instancin var meshInfo = instancingData.ModelComponent.MeshInfos[i]; // This must reflect the transformations in the shaders - var ibb = new BoundingBoxExt(instancing.BoundingBox); - var mbb = new BoundingBoxExt(meshInfo.BoundingBox); - - // We need to remove the world transformation component - //if (mesh.Skinning != null && instancingData.ModelComponent.Skeleton != null) + // This is currently not entirely correct, it ignores cases with extreme scalings + switch (instancing.ModelTransformUsage) { - Matrix.Invert(ref instancingData.ModelComponent.Skeleton.NodeTransformations[0].LocalMatrix, out var invWorld); - mbb.Transform(invWorld); + case ModelTransformUsage.Replace: + BoundingBoxReplaceWorld(instancingData, instancing, meshInfo, mesh); + break; + case ModelTransformUsage.PreMultiply: + BoundingBoxPreMultiplyWorld(instancingData, instancing, meshInfo, mesh); + break; + case ModelTransformUsage.PostMultiply: + BoundingBoxPostMultiplyWorld(instancingData, instancing, meshInfo, mesh); + break; + default: + break; } - - // ibb.Transform(instancingData.TransformComponent.WorldMatrix); - var center = ibb.Center;// - instancingData.TransformComponent.WorldMatrix.TranslationVector; - var extend = mbb.Extent + ibb.Extent; - meshInfo.BoundingBox = new BoundingBox(center - extend, center + extend); - } + } } } } + private static void BoundingBoxReplaceWorld(InstancingData instancingData, IInstancing instancing, ModelComponent.MeshInfo meshInfo, Mesh mesh) + { + // We need to remove the world transformation component + if (instancingData.ModelComponent.Skeleton != null) + { + var ibb = instancing.BoundingBox; + var mbb = new BoundingBoxExt(meshInfo.BoundingBox); + + Matrix.Invert(ref instancingData.ModelComponent.Skeleton.NodeTransformations[0].LocalMatrix, out var invWorld); + mbb.Transform(invWorld); + + var center = ibb.Center; + var extend = ibb.Extent + mbb.Extent; + meshInfo.BoundingBox = new BoundingBox(center - extend, center + extend); + } + else // Just take the original mesh one + { + var ibb = instancing.BoundingBox; + + var center = ibb.Center; + var extend = ibb.Extent + mesh.BoundingBox.Extent; + meshInfo.BoundingBox = new BoundingBox(center - extend, center + extend); + } + } + + private static void BoundingBoxPreMultiplyWorld(InstancingData instancingData, IInstancing instancing, ModelComponent.MeshInfo meshInfo, Mesh mesh) + { + + var ibb = instancing.BoundingBox; + + var center = meshInfo.BoundingBox.Center; + var extend = ibb.Extent + meshInfo.BoundingBox.Extent; + meshInfo.BoundingBox = new BoundingBox(center - extend, center + extend); + } + + private static void BoundingBoxPostMultiplyWorld(InstancingData instancingData, IInstancing instancing, ModelComponent.MeshInfo meshInfo, Mesh mesh) + { + var ibb = new BoundingBoxExt(instancing.BoundingBox); + ibb.Transform(instancingData.TransformComponent.WorldMatrix); + var center = meshInfo.BoundingBox.Center + ibb.Center - instancingData.TransformComponent.WorldMatrix.TranslationVector; // World translation was applied twice now + var extend = meshInfo.BoundingBox.Extent + ibb.Extent; + meshInfo.BoundingBox = new BoundingBox(center - extend, center + extend); + } + protected override void OnEntityComponentAdding(Entity entity, [NotNull] InstancingComponent component, [NotNull] InstancingData data) { data.TransformComponent = component.Entity.Get(); @@ -110,6 +156,8 @@ protected internal override void OnSystemAdd() { base.OnSystemAdd(); VisibilityGroup.Tags.Set(InstancingRenderFeature.ModelToInstancingMap, modelInstancingMap); + + ModelRenderProcessor = EntityManager.GetProcessor(); } protected internal override void OnSystemRemove() diff --git a/sources/engine/Stride.Engine/Rendering/InstancingRenderFeature.cs b/sources/engine/Stride.Engine/Rendering/InstancingRenderFeature.cs index 69ff3d05f5..e000dc5383 100644 --- a/sources/engine/Stride.Engine/Rendering/InstancingRenderFeature.cs +++ b/sources/engine/Stride.Engine/Rendering/InstancingRenderFeature.cs @@ -22,6 +22,7 @@ public struct InstancingData public bool BuffersManagedByUser; public Buffer InstanceWorldBuffer; public Buffer InstanceWorldInverseBuffer; + public int ModelTransformUsage; } public class InstancingRenderFeature : SubRenderFeature @@ -79,6 +80,7 @@ public override void Extract() if (instancingComponent.Enabled && instancingBase.InstanceCount > 0) { instancingData.InstanceCount = instancingBase.InstanceCount; + instancingData.ModelTransformUsage = (int)instancingBase.ModelTransformUsage; if (instancingBase is InstancingUserBuffer instancingUserBuffer) { @@ -146,6 +148,7 @@ public override void PrepareEffectPermutations(RenderDrawContext context) if (renderEffect != null) { + renderEffect.EffectValidator.ValidateParameter(StrideEffectBaseKeys.ModelTransformUsage, instancingData.ModelTransformUsage); renderEffect.EffectValidator.ValidateParameter(StrideEffectBaseKeys.HasInstancing, instancingData.InstanceCount > 0); } } diff --git a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx index d222d9d71e..97dceebcef 100644 --- a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx +++ b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx @@ -40,6 +40,7 @@ namespace Stride.Rendering if (StrideEffectBaseKeys.HasInstancing) { + mixin macro StrideEffectBaseKeys.ModelTransformUsage; mixin TransformationWAndVPInstanced; } else diff --git a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs index a38cfed62e..479d24b262 100644 --- a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs +++ b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs @@ -51,6 +51,7 @@ public void Generate(ShaderMixinSource mixin, ShaderMixinContext context) context.Mixin(mixin, "NormalStream"); if (context.GetParam(StrideEffectBaseKeys.HasInstancing)) { + mixin.AddMacro("ModelTransformUsage", context.GetParam(StrideEffectBaseKeys.ModelTransformUsage)); context.Mixin(mixin, "TransformationWAndVPInstanced"); } else diff --git a/sources/engine/Stride.Rendering/Rendering/StrideEffectBaseKeys.cs b/sources/engine/Stride.Rendering/Rendering/StrideEffectBaseKeys.cs index 88d0666939..2f93c2112a 100644 --- a/sources/engine/Stride.Rendering/Rendering/StrideEffectBaseKeys.cs +++ b/sources/engine/Stride.Rendering/Rendering/StrideEffectBaseKeys.cs @@ -15,5 +15,7 @@ public static class StrideEffectBaseKeys public static readonly PermutationParameterKey RenderTargetExtensions = ParameterKeys.NewPermutation(); public static readonly PermutationParameterKey HasInstancing = ParameterKeys.NewPermutation(); + + public static readonly PermutationParameterKey ModelTransformUsage = ParameterKeys.NewPermutation(); } } diff --git a/sources/engine/Stride.Rendering/Rendering/Transformation/TransformationInstancing.sdsl b/sources/engine/Stride.Rendering/Rendering/Transformation/TransformationInstancing.sdsl index b1f72e2ed4..0c26c96a75 100644 --- a/sources/engine/Stride.Rendering/Rendering/Transformation/TransformationInstancing.sdsl +++ b/sources/engine/Stride.Rendering/Rendering/Transformation/TransformationInstancing.sdsl @@ -1,4 +1,8 @@ -shader TransformationInstancing : TransformationBase +#ifndef ModelTransformUsage +# define ModelTransformUsage 0 +#endif + +shader TransformationInstancing : TransformationBase, Transformation { struct InstanceTransform { @@ -13,11 +17,23 @@ shader TransformationInstancing : TransformationBase float4x4 GetInstanceWorld() { +#if ModelTransformUsage == 0 return InstanceWorld[streams.InstanceID].Matrix; +#elif ModelTransformUsage == 1 + return mul(Transformation.World, InstanceWorld[streams.InstanceID].Matrix); +#else + return mul(InstanceWorld[streams.InstanceID].Matrix, Transformation.World); +#endif } float4x4 GetInstanceWorldInverse() { +#if ModelTransformUsage == 0 return InstanceWorldInverse[streams.InstanceID].Matrix; +#elif ModelTransformUsage == 1 + return mul(InstanceWorldInverse[streams.InstanceID].Matrix, Transformation.WorldInverse); +#else + return mul(Transformation.WorldInverse, InstanceWorldInverse[streams.InstanceID].Matrix); +#endif } };