Skip to content

Instantly share code, notes, and snippets.

@w0wca7a
Created September 3, 2025 22:24
Show Gist options
  • Select an option

  • Save w0wca7a/6187196a0e4c7f86b5b17bb33a3fb0ca to your computer and use it in GitHub Desktop.

Select an option

Save w0wca7a/6187196a0e4c7f86b5b17bb33a3fb0ca to your computer and use it in GitHub Desktop.

Revisions

  1. w0wca7a created this gist Sep 3, 2025.
    6 changes: 6 additions & 0 deletions TargetComponent.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    // add this component to Entity, which must be marked as target
    using Stride.Core;
    using Stride.Engine;

    [DataContract]
    public class TargetComponent : EntityComponent {}
    116 changes: 116 additions & 0 deletions TargetRenderer.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,116 @@
    // Based on Stride Community Toolkit Example #9
    // https://github.com/stride3d/stride-community-toolkit/blob/08218340fb99b6d994e160d2d0af05bcce43fab1/examples/code-only/Example09_Renderer/MyCustomSceneRenderer.cs

    using Stride.Core.Mathematics;
    using Stride.Engine;
    using Stride.Graphics;
    using Stride.Rendering;
    using Stride.Rendering.Compositing;

    namespace BlurUI.CustomRenderer
    {
    public class TargetRenderer : SceneRendererBase, ISharedRenderer
    {
    public Texture TargetTexture;
    private SpriteBatch _spriteBatch;
    private Scene _scene;
    private CameraComponent _camera;

    protected override void InitializeCore()
    {
    base.InitializeCore();
    _spriteBatch = new SpriteBatch(GraphicsDevice);
    }

    protected override void DrawCore(RenderContext context, RenderDrawContext drawContext)
    {
    var graphicsCompositor = context.Tags.Get(GraphicsCompositor.Current);

    if (graphicsCompositor is null) return;

    _camera ??= context.GetCurrentCamera();

    _scene ??= SceneInstance.GetCurrent(context).RootScene;

    if (_spriteBatch is null || _camera is null || _scene is null) return;

    var viewProjection = _camera.ViewProjectionMatrix;

    _spriteBatch.Begin(
    drawContext.GraphicsContext);

    foreach (var entity in _scene.Entities)
    {
    /* using Stride.Core;
    * using Stride.Engine;
    * [DataContract]
    * public class TargetComponent : EntityComponent {}
    *
    * can be add from code
    */

    // Find object with TargetComponent
    if (entity.Components.Get<TargetComponent>() is null) continue;

    // original
    // var screenPosition = _camera.WorldToScreenPoint(ref entity.Transform.Position, GraphicsDevice);

    var worldPos = entity.Transform.Position;

    // Transform to clip space
    var clipPosition = Vector4.Transform(new Vector4(worldPos, 1f), viewProjection);
    if (clipPosition.W <= 0f)
    continue; // behind the camera

    // Perspective divide -> Normalized Device Coordinates (NDC)
    var inverseW = 1f / clipPosition.W;
    var normalizedDeviceCoordinatesX = clipPosition.X * inverseW;
    var normalizedDeviceCoordinatesY = clipPosition.Y * inverseW;
    var normalizedDeviceCoordinatesZ = clipPosition.Z * inverseW;

    // Clip test in NDC: x and y in [-1,1], z in [0,1]
    var outsideNdc = normalizedDeviceCoordinatesZ < 0f || normalizedDeviceCoordinatesZ > 1f || normalizedDeviceCoordinatesX < -1f || normalizedDeviceCoordinatesX > 1f || normalizedDeviceCoordinatesY < -1f || normalizedDeviceCoordinatesY > 1f;

    // culled
    if (outsideNdc) continue;

    // implementation
    // start
    Vector3.TransformCoordinate(ref worldPos, ref _camera.ViewProjectionMatrix, out Vector3 viewProject);

    Vector3.TransformCoordinate(ref worldPos, ref _camera.ViewMatrix, out Vector3 viewSpace);

    Vector3 result = new()
    {
    X = (viewProject.X + 1f) / 2f,
    Y = 1f - (viewProject.Y + 1f) / 2f,
    Z = viewSpace.Z + _camera.NearClipPlane,
    };

    var windowSize = new Int2(
    drawContext.GraphicsDevice.Presenter.BackBuffer.Width,
    drawContext.GraphicsDevice.Presenter.BackBuffer.Height);

    Vector2 screenPosition = new()
    {
    X = result.X * windowSize.X,
    Y = result.Y * windowSize.Y
    };
    // end

    // Draw a texture
    _spriteBatch.Draw(
    TargetTexture,
    new Rectangle(
    (int)screenPosition.X - TargetTexture.Width/2, // horizontal center of texture in center of object
    (int)screenPosition.Y - TargetTexture.Height/2, // vertical center of texture in center of object
    TargetTexture.Width, // rectangle width same as texture
    TargetTexture.Height), // rectangle height same as texture
    new(1.0f, 1.0f, 1.0f, 1.0f)); // Color
    }

    // End the SpriteBatch
    _spriteBatch.End();
    }
    }
    }