Sandbox Logo
Page Header

3D hud element using ScenePanel

Thanks to support from Discord members, I got a working 3D model as a razor panel
This is done using ScenePanel, an undocumented technology used mostly by Facepunch and rarely by community devs.

However it is a shame that this is not getting used more so I'll explain you how you can setup your own.

Before we continue, there are some limitations with ScenePanels. It seems to only work with "vmdl" files. You can't really use a prefab for ScenePanel.
ScenePanels are also not regular components, you can't use Properties with them so if you need to manipulate objects in ScenePanels, you have to create a Singleton.

Now to setup your ScenePanel, all you have to do is create a new class in your UI folder next to other razor components. Create just regular .cs file and put this in there:


public class NameOfYourPanel: ScenePanel
{
    private SceneLight Light { get; set; }
    private SceneDirectionalLight Sun { get; set; }
    public static NameOfYourPanel Instance { get; set; }
    public SceneModel SomeModel { get; set; }

    public NameOfYourPanel()
    {
        Instance = this;
    }


    protected override void Tick()
    {
         // works just like OnUpdate
     }

    protected override void OnParametersSet()
    {
        World = new SceneWorld();

        SomeModel = new( World, "models/example.vmdl", new( Vector3.Zero, Rotation.Identity ) );

        if ( SomeModel == null || SomeModel.Model.IsError ) return;

        Light = new SceneLight( World, SomeModel.Transform.Position + Vector3.Up * 100f, 300f, Color.White );
        Sun = new SceneDirectionalLight( World, Rotation.From( 0, 50f, 0 ), Color.White );

        Camera.Position = SomeModel.Bounds.Center + Camera.Angles.Forward * -SomeModel.Bounds.Size.Length;

        Camera.FieldOfView = 70f;
    }
}


OnParametersSet is called when the panel is created. You have to provide the scene a vmdl file as either SceneModel or SceneObject. Then you have to setup lighting and from there it's pretty much smooth sailing.

Now you have to create regular razor component:


<root>
    <NameOfYourPanel />
</root>

@code
{
    [Property] public GameObject Example { get; set; }

    protected override void OnFixedUpdate()
    {
        if ( GimbalViewer.Instance == null
            || GimbalViewer.Instance.SomeModel.Model == null
            || GimbalViewer.Instance.SomeModel.Model.IsError) return;

        NameOfYourPanel.Instance.Gimbal.Rotation = Example.Transform.Rotation;
    }
}


This would be an example of applying rotation from some object within the scene to your ScenePanel object. To enable the ScenePanel you can do <NameOfYourPanel /> If your ScenePanel has a constructor, you can also do:

<NameOfYourPanel NameOfYourPanel="path/to/model.vmdl" />

which would then provide path to your model in case you want to use ScenePanel for multiple models.

The last thing required is obviously some scss to wrap it up:


YourRazorPanel {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    justify-content: center;
    align-items: center;

    > NameOfYourPanel {
        width: 100%;
        height: 100%;
    }
}


And just like that, you have made your own ScenePanel

Comments

Jan, 1 2023
320
Thanks, now i know how to make panel for razor