Files
sbox-public/engine/Sandbox.Engine/Resources/Model/Model.cs
Lorenz Junglas 6808d8768e Shutdown fixes (#3553)
* Stop generating solutions via -test flag add -generatesolution

* Add TestAppSystem remove Application.InitUnitTest

Avoids some hacks and also makes sure our tests are as close to a real AppSystem as possible.

* Add shutdown unit test

shuts down an re-inits the engine

* Properly dispose native resources hold by managed during shutdown

Should fix a bunch of crashes

* Fix filesystem and networking tests

* StandaloneTest does proper Game Close

* Make sure package tests clean up properly

* Make sure menu scene and resources are released on shutdown

* Report leaked scenes on shutdown

* Ensure DestroyImmediate is not used on scenes

* Fix unmounting in unit tests not clearing native refs

* Force destroy native resource on ResourceLib Clear
2025-12-08 15:55:11 +01:00

137 lines
2.8 KiB
C#

using NativeEngine;
using Sandbox.Engine;
using Sandbox.Engine.Utility.RayTrace;
namespace Sandbox;
/// <summary>
/// A model.
/// </summary>
public sealed partial class Model : Resource
{
internal IModel native;
internal bool procedural;
public override bool IsValid => native.IsValid;
/// <summary>
/// Private constructor, use <see cref="FromNative(IModel, bool, string)"/>
/// </summary>
private Model( IModel native, string name, bool procedural )
{
if ( native.IsNull ) throw new Exception( "Model pointer cannot be null!" );
this.native = native;
this.Name = name;
this.procedural = procedural;
SetIdFromResourcePath( Name );
}
internal void Dispose()
{
if ( !native.IsNull )
{
var n = native;
native = default;
MainThread.Queue( () => n.DestroyStrongHandle() );
}
}
~Model()
{
Dispose();
}
/// <summary>
/// Called when the resource is reloaded. We should clear any cached values.
/// </summary>
internal override void OnReloaded()
{
_data?.Dispose();
_data = null;
_physics?.Dispose();
_physics = default;
_morphs?.Dispose();
_morphs = null;
_attachments?.Dispose();
_attachments = default;
_animationNames?.Clear();
_animationNames = default;
_sequenceNames?.Clear();
_sequenceNames = default;
_materials = default;
_bones = default;
_hitboxset = default;
_parts?.Dispose();
_parts = default;
DataCache?.Clear();
IToolsDll.Current?.RunEvent( "model.reload", this );
foreach ( var scene in Scene.All )
{
using var scope = scene.Push();
var components = scene.GetAllComponents<IHasModel>();
foreach ( var c in components )
{
if ( c.Model != this ) continue;
c.OnModelReloaded();
}
}
}
/// <summary>
/// Whether this model is an error model or invalid or not.
/// </summary>
public bool IsError => native.IsNull || !native.IsStrongHandleValid() || native.IsError();
/// <summary>
/// Name of the model, usually being its file path.
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// Whether this model is procedural, i.e. it was created at runtime via <see cref="ModelBuilder.Create"/>.
/// </summary>
public bool IsProcedural => procedural;
/// <summary>
/// Total number of meshes this model is made out of.
/// </summary>
public int MeshCount => native.GetNumMeshes();
/// <summary>
/// Trace against the triangles in this mesh
/// </summary>
public MeshTraceRequest Trace => new() { targetModel = this };
}
internal interface IHasModel
{
/// <summary>
/// The <see cref="Model"/> associated with this object.
/// </summary>
Model Model { get; }
/// <summary>
/// Called by the engine when the associated <see cref="Model"/> is reloaded.
/// Implementing classes should clear or update any cached data that depends on the model.
/// </summary>
void OnModelReloaded();
}