Component that renders a scope lens. It creates a 1024x1024 render target, assigns it to a CameraComponent and sets that texture into the renderer's material. Optionally aligns the scope camera to a muzzle transform at a configured zeroing range.
using Sandbox;
using System;
public sealed class ScopeLens : Component
{
[RequireComponent] public ModelRenderer Renderer { get; set; }
/// <summary>Scope camera that renders into the lens texture.</summary>
[Property] public CameraComponent Camera { get; set; }
/// <summary>Aim the scope camera at the muzzle zero distance on start.</summary>
[Property, Group( "Zeroing" )] public bool ApplyZeroing { get; set; }
/// <summary>Zero distance in meters; camera looks at that point along the muzzle.</summary>
[Property, Group( "Zeroing" ), ShowIf( nameof( ApplyZeroing ), true )] public float Range { get; set; } = 100f;
/// <summary>Muzzle transform used for zeroing direction and roll alignment.</summary>
[Property, Group( "Zeroing" ), ShowIf( nameof( ApplyZeroing ), true )] public GameObject Muzzle;
private Texture RenderTarget;
protected override void OnStart()
{
SetupRenderTarget();
if ( ApplyZeroing )
{
ApplyZero();
}
}
private void SetupRenderTarget()
{
RenderTarget = Texture.CreateRenderTarget().WithDynamicUsage().WithSize( 1024, 1024 ).Create();
Camera.RenderTarget = RenderTarget;
var material = Renderer.MaterialOverride;
material.Set( "Color", RenderTarget );
Renderer.MaterialOverride = material;
}
private void ApplyZero()
{
Vector3 zeroPoint = Muzzle.WorldPosition +
Muzzle.WorldRotation.Forward * MathX.MeterToInch( Range );
// Use muzzle up so roll matches the weapon even if it starts on its side.
// World up would bake a twisted local rotation that stays crooked when picked up.
Camera.WorldRotation = Rotation.LookAt(
zeroPoint - Camera.WorldPosition,
Muzzle.WorldRotation.Up
);
}
}