diff --git a/Community/StrideExamples.Community.CubeClicker/App.config b/Community/StrideExamples.Community.CubeClicker/App.config
new file mode 100644
index 0000000..49cc43e
--- /dev/null
+++ b/Community/StrideExamples.Community.CubeClicker/App.config
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Community/StrideExamples.Community.MeshOutlineShader/Program.cs b/Community/StrideExamples.Community.MeshOutlineShader/Program.cs
index 29285ff..cfc91a0 100644
--- a/Community/StrideExamples.Community.MeshOutlineShader/Program.cs
+++ b/Community/StrideExamples.Community.MeshOutlineShader/Program.cs
@@ -39,8 +39,7 @@ void CreateOutlinePrimitive(Scene rootScene, PrimitiveModelType modelType, Color
var entity = game.Create3DPrimitive(modelType, options: new()
{
RenderGroup = RenderGroup.Group5,
- }
- );
+ });
entity.Transform.Position = position;
entity.Add(
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/ClickData.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/ClickData.cs
index ecec679..9da5249 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/ClickData.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/ClickData.cs
@@ -1,6 +1,6 @@
using Stride.Core;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
[DataContract]
public sealed class ClickData
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/CubeData.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/CubeData.cs
index c01e703..b8c861b 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/CubeData.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/CubeData.cs
@@ -1,8 +1,7 @@
-using BepuUtilities.Collections;
using Stride.Core;
using Stride.Core.Mathematics;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
[DataContract]
public sealed class CubeData
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/DataSaver.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/DataSaver.cs
index 8b4bd8c..141c6a8 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/DataSaver.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/DataSaver.cs
@@ -1,7 +1,7 @@
using NexVYaml;
using Stride.Core.IO;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
public class DataSaver
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/IClickable.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/IClickable.cs
index 4cfe28f..f53ef7d 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/IClickable.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/IClickable.cs
@@ -1,6 +1,6 @@
using Stride.Input;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
public interface IClickable
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/LeftMouseButtonCounter.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/LeftMouseButtonCounter.cs
index a11d055..1a54e5f 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/LeftMouseButtonCounter.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/LeftMouseButtonCounter.cs
@@ -1,7 +1,7 @@
using Stride.Core;
using Stride.Input;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
///
/// Has to be directly [DataContract] tagged, else it won't detect it.
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/RightMouseButtonCounter.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/RightMouseButtonCounter.cs
index 371ca0a..a14ca54 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/RightMouseButtonCounter.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/RightMouseButtonCounter.cs
@@ -1,7 +1,7 @@
using Stride.Core;
using Stride.Input;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
[DataContract]
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Core/SimpleVector.cs b/Community/StrideExamples.Community.StrideUI.Grid/Core/SimpleVector.cs
index 1a016f9..90a455d 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Core/SimpleVector.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Core/SimpleVector.cs
@@ -1,6 +1,6 @@
using Stride.Core;
-namespace StrideExamples.StrideUI.Grid.Core;
+namespace StrideExamples.Community.StrideUI.Grid.Core;
///
/// Stride.Core.Vector isn't generated as the generator can't reach the core without being in it
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Managers/ClickDataManager.cs b/Community/StrideExamples.Community.StrideUI.Grid/Managers/ClickDataManager.cs
index cf3ee34..06b83a4 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Managers/ClickDataManager.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Managers/ClickDataManager.cs
@@ -1,6 +1,6 @@
-using StrideExamples.StrideUI.Grid.Core;
+using StrideExamples.Community.StrideUI.Grid.Core;
-namespace StrideExamples.StrideUI.Grid.Managers;
+namespace StrideExamples.Community.StrideUI.Grid.Managers;
public class ClickDataManager
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Managers/CubeDataManager.cs b/Community/StrideExamples.Community.StrideUI.Grid/Managers/CubeDataManager.cs
index f604e36..7596710 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Managers/CubeDataManager.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Managers/CubeDataManager.cs
@@ -1,8 +1,7 @@
using Stride.Core.Mathematics;
-using StrideExamples.StrideUI.Grid.Core;
-using System.Runtime.Serialization;
+using StrideExamples.Community.StrideUI.Grid.Core;
-namespace StrideExamples.StrideUI.Grid.Managers;
+namespace StrideExamples.Community.StrideUI.Grid.Managers;
public class CubeDataManager
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Managers/GameManager.cs b/Community/StrideExamples.Community.StrideUI.Grid/Managers/GameManager.cs
index fb40d68..dfbd0ed 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Managers/GameManager.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Managers/GameManager.cs
@@ -6,7 +6,7 @@
using System.Globalization;
using System.Text;
-namespace StrideExamples.StrideUI.Grid.Managers;
+namespace StrideExamples.Community.StrideUI.Grid.Managers;
public class GameManager
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Managers/UIManager.cs b/Community/StrideExamples.Community.StrideUI.Grid/Managers/UIManager.cs
index 46e2772..14412ee 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Managers/UIManager.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Managers/UIManager.cs
@@ -6,10 +6,9 @@
using Stride.UI;
using Stride.UI.Controls;
using Stride.UI.Events;
-using Stride.UI.Panels;
-using StrideExamples.StrideUI.Grid.Core;
+using StrideExamples.Community.StrideUI.Grid.Core;
-namespace StrideExamples.StrideUI.Grid.Managers;
+namespace StrideExamples.Community.StrideUI.Grid.Managers;
public class UIManager
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Program.cs b/Community/StrideExamples.Community.StrideUI.Grid/Program.cs
index 1057f17..3ddfaf6 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Program.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Program.cs
@@ -1,14 +1,12 @@
-using Stride.CommunityToolkit.Bepu;
+using NexVYaml;
+using Stride.CommunityToolkit.Bepu;
using Stride.CommunityToolkit.Engine;
+using Stride.CommunityToolkit.Renderers;
using Stride.CommunityToolkit.Skyboxes;
using Stride.Engine;
using Stride.Graphics;
-
-using NexVYaml;
-
-using StrideExamples.StrideUI.Grid.Managers;
-using StrideExamples.StrideUI.Grid.Scripts;
-using Stride.CommunityToolkit.Renderers;
+using StrideExamples.Community.StrideUI.Grid.Managers;
+using StrideExamples.Community.StrideUI.Grid.Scripts;
using var game = new Game();
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Scripts/ClickHandlerComponent.cs b/Community/StrideExamples.Community.StrideUI.Grid/Scripts/ClickHandlerComponent.cs
index 3cb3aa2..87035d6 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Scripts/ClickHandlerComponent.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Scripts/ClickHandlerComponent.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using Stride.CommunityToolkit.Bepu;
using Stride.CommunityToolkit.Engine;
using Stride.CommunityToolkit.Rendering.ProceduralModels;
@@ -5,10 +6,9 @@
using Stride.Engine;
using Stride.Input;
using Stride.Rendering;
-using StrideExamples.StrideUI.Grid.Managers;
-using System.Diagnostics;
+using StrideExamples.Community.StrideUI.Grid.Managers;
-namespace StrideExamples.StrideUI.Grid.Scripts;
+namespace StrideExamples.Community.StrideUI.Grid.Scripts;
public class ClickHandlerComponent : AsyncScript
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeGrower.cs b/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeGrower.cs
index f77886e..3679009 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeGrower.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeGrower.cs
@@ -3,7 +3,7 @@
using Stride.Engine;
using Stride.Physics;
-namespace StrideExamples.StrideUI.Grid.Scripts;
+namespace StrideExamples.Community.StrideUI.Grid.Scripts;
public class CubeGrower : AsyncScript
{
diff --git a/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeVanisher.cs b/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeVanisher.cs
index fed2598..1d1ba60 100644
--- a/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeVanisher.cs
+++ b/Community/StrideExamples.Community.StrideUI.Grid/Scripts/CubeVanisher.cs
@@ -3,7 +3,7 @@
using Stride.Engine;
using Stride.Physics;
-namespace StrideExamples.StrideUI.Grid.Scripts;
+namespace StrideExamples.Community.StrideUI.Grid.Scripts;
public class CubeVanisher : AsyncScript
{
diff --git a/Local/StrideExamples.Local.OcclusionTest/Components/MultiRaycastVisibility.cs b/Local/StrideExamples.Local.OcclusionTest/Components/MultiRaycastVisibility.cs
new file mode 100644
index 0000000..cc21238
--- /dev/null
+++ b/Local/StrideExamples.Local.OcclusionTest/Components/MultiRaycastVisibility.cs
@@ -0,0 +1,113 @@
+using Stride.Core;
+using Stride.Core.Mathematics;
+using Stride.Engine;
+
+namespace StrideExamples.Local.OcclusionTest.Components;
+
+public class MultiRaycastVisibilityComponent : SyncScript
+{
+ public required Game Game { get; init; }
+ public required Entity Target { get; init; }
+ public required CameraComponent Camera { get; init; }
+ public required Stride.BepuPhysics.BepuSimulation Simulation { get; init; }
+
+ public VisibilityResult LastVisibilityResult { get; private set; }
+
+ [DataMemberIgnore]
+ private Stride.CommunityToolkit.DebugShapes.Code.ImmediateDebugRenderSystem? _debugDraw;
+
+ public record struct VisibilityResult
+ {
+ public bool IsVisible;
+ public float VisibilityPercentage; // 0.0 to 1.0
+ public int RaysHit;
+ public int TotalRays;
+ }
+
+ public VisibilityResult CheckVisibility(Game game, Entity target, CameraComponent camera, Stride.BepuPhysics.BepuSimulation simulation)
+ {
+ _debugDraw ??= game.Services.GetService();
+ if (_debugDraw is not null)
+ {
+ _debugDraw.Enabled = true;
+ _debugDraw.Visible = true;
+ }
+
+ var cameraPos = camera.Entity.Transform.WorldMatrix.TranslationVector;
+
+ // Get target bounding box points
+ var targetTransform = target.Transform;
+ var targetModel = target.Get();
+ var worldMatrix = targetTransform.WorldMatrix;
+ var localBoundingBox = targetModel.Model.BoundingBox;
+ BoundingBox.Transform(ref localBoundingBox, ref worldMatrix, out var worldBoundingBox);
+
+ var testPoints = GenerateTestPoints(worldBoundingBox);
+ var visibleRays = 0;
+
+ foreach (var point in testPoints)
+ {
+ var direction = point - cameraPos;
+
+ direction.Normalize();
+ _debugDraw?.DrawRay(cameraPos, direction*100, Color.Yellow, 0, false);
+
+ // Use Bepu's raycast method
+ var hit = simulation.RayCast(
+ cameraPos,
+ direction,
+ float.MaxValue,
+ out var hitInfo
+ );
+
+ if (hit && hitInfo.Collidable?.Entity == target)
+ {
+ visibleRays++; // Ray hit the target
+ }
+ }
+
+ var visibilityPercentage = (float)visibleRays / testPoints.Length;
+
+ return new VisibilityResult
+ {
+ IsVisible = visibilityPercentage > 0.0f,
+ VisibilityPercentage = visibilityPercentage,
+ RaysHit = visibleRays,
+ TotalRays = testPoints.Length
+ };
+ }
+
+ private Vector3[] GenerateTestPoints(BoundingBox boundingBox)
+ {
+ var min = boundingBox.Minimum;
+ var max = boundingBox.Maximum;
+ var center = (min + max) * 0.5f;
+
+ return
+ [
+ // 8 corners of the bounding box
+ new(min.X, min.Y, min.Z), // 0: min corner
+ new(max.X, min.Y, min.Z), // 1: +X
+ new(min.X, max.Y, min.Z), // 2: +Y
+ new(min.X, min.Y, max.Z), // 3: +Z
+ new(max.X, max.Y, min.Z), // 4: +XY
+ new(max.X, min.Y, max.Z), // 5: +XZ
+ new(min.X, max.Y, max.Z), // 6: +YZ
+ new(max.X, max.Y, max.Z), // 7: max corner
+
+ // Face centers
+ center, // Center
+ new(center.X, center.Y, min.Z), // Front face center
+ new(center.X, center.Y, max.Z), // Back face center
+ new(min.X, center.Y, center.Z), // Left face center
+ new(max.X, center.Y, center.Z), // Right face center
+ new(center.X, min.Y, center.Z), // Bottom face center
+ new(center.X, max.Y, center.Z), // Top face center
+ ];
+ }
+
+ public override void Update()
+ {
+ LastVisibilityResult = CheckVisibility(Game, Target, Camera, Simulation);
+ }
+}
\ No newline at end of file
diff --git a/Local/StrideExamples.Local.OcclusionTest/Components/MultiRaycastVisibilityComponent.cs b/Local/StrideExamples.Local.OcclusionTest/Components/MultiRaycastVisibilityComponent.cs
new file mode 100644
index 0000000..aa2fbb3
--- /dev/null
+++ b/Local/StrideExamples.Local.OcclusionTest/Components/MultiRaycastVisibilityComponent.cs
@@ -0,0 +1,86 @@
+using Stride.Core.Mathematics;
+using Stride.Engine;
+
+public class MultiRaycastVisibility
+{
+ public record struct VisibilityResult
+ {
+ public bool IsVisible;
+ public float VisibilityPercentage; // 0.0 to 1.0
+ public int RaysHit;
+ public int TotalRays;
+ }
+
+ public VisibilityResult CheckVisibility(Game game, Entity target, CameraComponent camera, Stride.BepuPhysics.BepuSimulation simulation)
+ {
+ var cameraPos = camera.Entity.Transform.WorldMatrix.TranslationVector;
+
+ // Get target bounding box points
+ var targetTransform = target.Transform;
+ var targetModel = target.Get();
+ var worldMatrix = targetTransform.WorldMatrix;
+ var localBoundingBox = targetModel.Model.BoundingBox;
+ BoundingBox.Transform(ref localBoundingBox, ref worldMatrix, out var worldBoundingBox);
+
+ var testPoints = GenerateTestPoints(worldBoundingBox);
+ var visibleRays = 0;
+
+ foreach (var point in testPoints)
+ {
+ var direction = point - cameraPos;
+ direction.Normalize();
+
+ // Use Bepu's raycast method
+ var hit = simulation.RayCast(
+ cameraPos,
+ direction,
+ float.MaxValue,
+ out var hitInfo
+ );
+
+ if (hit && hitInfo.Collidable?.Entity == target)
+ {
+ visibleRays++; // Ray hit the target
+ }
+ }
+
+ var visibilityPercentage = (float)visibleRays / testPoints.Length;
+
+ return new VisibilityResult
+ {
+ IsVisible = visibilityPercentage > 0.0f,
+ VisibilityPercentage = visibilityPercentage,
+ RaysHit = visibleRays,
+ TotalRays = testPoints.Length
+ };
+ }
+
+ private Vector3[] GenerateTestPoints(BoundingBox boundingBox)
+ {
+ var min = boundingBox.Minimum;
+ var max = boundingBox.Maximum;
+ var center = (min + max) * 0.5f;
+
+ return
+ [
+ // 8 corners of the bounding box
+ new(min.X, min.Y, min.Z), // 0: min corner
+ new(max.X, min.Y, min.Z), // 1: +X
+ new(min.X, max.Y, min.Z), // 2: +Y
+ new(min.X, min.Y, max.Z), // 3: +Z
+ new(max.X, max.Y, min.Z), // 4: +XY
+ new(max.X, min.Y, max.Z), // 5: +XZ
+ new(min.X, max.Y, max.Z), // 6: +YZ
+ new(max.X, max.Y, max.Z), // 7: max corner
+
+ // Face centers
+ center, // Center
+ new(center.X, center.Y, min.Z), // Front face center
+ new(center.X, center.Y, max.Z), // Back face center
+ new(min.X, center.Y, center.Z), // Left face center
+ new(max.X, center.Y, center.Z), // Right face center
+ new(center.X, min.Y, center.Z), // Bottom face center
+ new(center.X, max.Y, center.Z), // Top face center
+ ];
+ }
+}
diff --git a/Local/StrideExamples.Local.OcclusionTest/Managers/GameManager.cs b/Local/StrideExamples.Local.OcclusionTest/Managers/GameManager.cs
new file mode 100644
index 0000000..51c9245
--- /dev/null
+++ b/Local/StrideExamples.Local.OcclusionTest/Managers/GameManager.cs
@@ -0,0 +1,18 @@
+using Stride.Engine;
+using Stride.Games;
+using Stride.Graphics;
+
+namespace StrideExamples.Local.OcclusionTest.Managers;
+
+public class GameManager
+{
+ private readonly MultiRaycastVisibility _visibility;
+
+ public UIManager UIManager { get; }
+
+ public GameManager(Scene rootScene, SpriteFont font)
+ {
+ UIManager = new UIManager(rootScene, font);
+ _visibility = new MultiRaycastVisibility();
+ }
+}
\ No newline at end of file
diff --git a/Local/StrideExamples.Local.OcclusionTest/Managers/UIManager.cs b/Local/StrideExamples.Local.OcclusionTest/Managers/UIManager.cs
new file mode 100644
index 0000000..9b285ee
--- /dev/null
+++ b/Local/StrideExamples.Local.OcclusionTest/Managers/UIManager.cs
@@ -0,0 +1,142 @@
+using Stride.Core.Mathematics;
+using Stride.Engine;
+using Stride.Graphics;
+using Stride.Input;
+using Stride.Rendering;
+using Stride.UI;
+using Stride.UI.Controls;
+using Stride.UI.Events;
+using Stride.UI.Panels;
+using StrideExamples.Local.OcclusionTest.Components;
+
+namespace StrideExamples.Local.OcclusionTest.Managers;
+
+public class UIManager
+{
+ private const string EntityName = "GameUI";
+ private readonly SpriteFont _font;
+ private readonly Color _gridBackgroundColor = Color.LightGray;
+ private readonly Grid _grid;
+
+ private readonly StackPanel _panel;
+
+ private List _monitoredEntities = [];
+
+ public UIManager(Scene rootScene, SpriteFont spriteFont)
+ {
+ _font = spriteFont;
+ _grid = CreateGrid();
+ _panel = CreatePanel(_grid);
+
+ var ui = CreateUI(_grid);
+ ui.Scene = rootScene;
+ }
+
+ internal Entity CreateUI(Grid grid) => new(EntityName)
+ {
+ new UIComponent
+ {
+ Page = new UIPage { RootElement = grid },
+ RenderGroup = RenderGroup.Group31
+ }
+ };
+
+ internal void UpdateUI()
+ {
+ _panel.Children.Clear();
+ foreach (var entity in _monitoredEntities)
+ {
+ var visibility = entity.Get();
+
+ if (visibility is not null)
+ {
+ _panel.Children.Add(CreateTextBlock($"Entity: {entity.Name} ({visibility.LastVisibilityResult.VisibilityPercentage:P}% visible, {visibility.LastVisibilityResult.RaysHit} / {visibility.LastVisibilityResult.TotalRays} rays hit)", 12, TextAlignment.Left));
+ }
+ }
+ }
+
+ public void MonitorEntity(Entity entity)
+ {
+ if (!_monitoredEntities.Contains(entity))
+ {
+ _monitoredEntities.Add(entity);
+ }
+ }
+
+ private StackPanel CreatePanel(Grid grid)
+ {
+ var panel = CreateVisibilityPanel();
+ panel.SetGridColumn(2);
+ panel.SetGridRow(1);
+ grid.Children.Add(panel);
+
+ return panel;
+ }
+
+ private static Grid CreateGrid() => new()
+ {
+ VerticalAlignment = VerticalAlignment.Center,
+ HorizontalAlignment = HorizontalAlignment.Stretch,
+ RowDefinitions = {
+ new StripDefinition() { Type = StripType.Auto },
+ new StripDefinition() { Type = StripType.Auto },
+ new StripDefinition() { Type = StripType.Auto },
+ new StripDefinition() { Type = StripType.Auto }
+ },
+ ColumnDefinitions =
+ {
+ new StripDefinition(StripType.Star, 1),
+ new StripDefinition(StripType.Star, 1),
+ new StripDefinition(StripType.Star, 1)
+ }
+ };
+
+ private StackPanel CreateVisibilityPanel()
+ {
+ var panel = new StackPanel
+ {
+ Orientation = Orientation.Vertical,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ VerticalAlignment = VerticalAlignment.Center,
+ BackgroundColor = _gridBackgroundColor,
+ Margin = new Thickness(0, 3, 0, 3),
+ };
+
+ panel.Children.Add(CreateTextBlock("Visibility:", 14, TextAlignment.Center));
+ panel.Children.Add(CreateSlider());
+
+ return panel;
+ }
+
+
+ private Slider CreateSlider()
+ {
+ var slider = new Slider
+ {
+ Minimum = 0,
+ Maximum = 100,
+ Value = 50,
+ Width = 200,
+ Margin = new Thickness(3, 3, 3, 3),
+ HorizontalAlignment = HorizontalAlignment.Center,
+ VerticalAlignment = VerticalAlignment.Center,
+ BackgroundColor = Color.Black,
+ MinimumWidth = 100,
+ Name = "VisibilitySlider"
+ };
+
+ return slider;
+ }
+
+ private TextBlock CreateTextBlock(string? text = null, float textSize = 20, TextAlignment textAlignment = TextAlignment.Left)
+ => new()
+ {
+ Text = text,
+ TextColor = Color.Black,
+ Margin = new Thickness(3, 0, 3, 0),
+ TextSize = textSize,
+ Font = _font,
+ TextAlignment = textAlignment,
+ VerticalAlignment = VerticalAlignment.Center
+ };
+}
\ No newline at end of file
diff --git a/Local/StrideExamples.Local.OcclusionTest/Program.cs b/Local/StrideExamples.Local.OcclusionTest/Program.cs
new file mode 100644
index 0000000..48fe14d
--- /dev/null
+++ b/Local/StrideExamples.Local.OcclusionTest/Program.cs
@@ -0,0 +1,95 @@
+using Stride.CommunityToolkit.Bepu;
+using Stride.CommunityToolkit.DebugShapes.Code;
+using Stride.CommunityToolkit.Engine;
+using Stride.CommunityToolkit.Games;
+using Stride.CommunityToolkit.Renderers;
+using Stride.CommunityToolkit.Rendering.ProceduralModels;
+using Stride.Core.Mathematics;
+using Stride.Engine;
+using Stride.Games;
+using Stride.Graphics;
+using Stride.Rendering;
+using Stride.Rendering.Materials;
+using Stride.Rendering.Materials.ComputeColors;
+using StrideExamples.Local.OcclusionTest.Components;
+using StrideExamples.Local.OcclusionTest.Managers;
+
+
+Console.WriteLine("Hello, World!");
+
+using var game = new Game();
+game.Run(start: Start, update: Update);
+
+void Start(Scene rootScene)
+{
+ game.SetupBase3DScene();
+ game.AddGroundGizmo();
+ game.Add3DGround();
+ game.AddProfiler();
+ game.AddAllDirectionLighting();
+ // game.AddEntityDebugSceneRenderer();
+ // game.AddDebugShapes();
+
+ var font = game.Content.Load("StrideDefaultFont");
+ var gameManager = new GameManager(rootScene, font);
+ game.Services.AddService(gameManager);
+
+ var greenCube = CreateCube(rootScene, game, "GreenCube", new(-5, 1, 0), Color.Green);
+ gameManager.UIManager.MonitorEntity(greenCube);
+
+ var blueCube = CreateCube(rootScene, game, "BlueCube", new(5, 1, 0), Color.Blue);
+ gameManager.UIManager.MonitorEntity(blueCube);
+}
+
+void Update(Scene rootScene, GameTime gameTime)
+{
+ var gameManager = game.Services.GetService();
+ gameManager?.UIManager.UpdateUI();
+}
+
+static Material CreateMaterial(Game game, Color? color = null, float specular = 1.0f, float microSurface = 0.65f)
+{
+ var lightmapMaterial = new MaterialDescriptor
+ {
+ Attributes =
+ {
+ Diffuse = new MaterialDiffuseMapFeature(new ComputeColor(color ?? GameDefaults.DefaultMaterialColor)),
+ // DiffuseModel = new MaterialLightmapModelFeature()
+ // {
+ // Intensity = 20,
+ // LightMap = new ComputeColor(color ?? GameDefaults.DefaultMaterialColor)
+ // },
+ Specular = new MaterialMetalnessMapFeature(new ComputeFloat(specular)),
+ SpecularModel = new MaterialSpecularMicrofacetModelFeature(),
+ MicroSurface = new MaterialGlossinessMapFeature(new ComputeFloat(microSurface))
+ }
+ };
+
+ return Material.New(game.GraphicsDevice, lightmapMaterial);
+}
+
+static Entity CreateCube(Scene rootScene, Game game, string name, Vector3 position, Color color)
+{
+ var cube = game.Create3DPrimitive(
+ PrimitiveModelType.Cube,
+ new Bepu3DPhysicsOptions
+ {
+ Material = game.CreateMaterial(color),
+ Size = new(2)
+ }
+ );
+
+ cube.Name = name;
+ cube.Transform.Position = position;
+ cube.Scene = rootScene;
+
+ cube.Add(new MultiRaycastVisibilityComponent
+ {
+ Game = game,
+ Target = cube,
+ Camera = rootScene.GetCamera() ?? throw new InvalidOperationException("No camera found in scene"),
+ Simulation = cube.GetSimulation()
+ });
+
+ return cube;
+}
\ No newline at end of file
diff --git a/Local/StrideExamples.Local.OcclusionTest/StrideExamples.Local.OcclusionTest.csproj b/Local/StrideExamples.Local.OcclusionTest/StrideExamples.Local.OcclusionTest.csproj
new file mode 100644
index 0000000..f5136ef
--- /dev/null
+++ b/Local/StrideExamples.Local.OcclusionTest/StrideExamples.Local.OcclusionTest.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/StrideExamples.slnx b/StrideExamples.slnx
index 41f304c..599b5f9 100644
--- a/StrideExamples.slnx
+++ b/StrideExamples.slnx
@@ -16,4 +16,7 @@
+
+
+
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000..3a9f6b3
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file