Code/MeshSliceResult.cs
namespace Sandbox;

public sealed class MeshSliceResult
{
	public Model UpperModel { get; }
	public Model LowerModel { get; }

	internal MeshSliceResult( Model upperModel, Model lowerModel )
	{
		UpperModel = upperModel;
		LowerModel = lowerModel;
	}

	public GameObject CreateUpperHull( GameObject source, string name = "Upper_Hull" )
	{
		return CreateUpperHull( source, name, autoCopyPhysics: false );
	}

	public GameObject CreateUpperHull( GameObject source, string name = "Upper_Hull", bool autoCopyPhysics = false )
	{
		return CreateHullObject( source, UpperModel, name, autoCopyPhysics );
	}

	public GameObject CreateLowerHull( GameObject source, string name = "Lower_Hull" )
	{
		return CreateLowerHull( source, name, autoCopyPhysics: false );
	}

	public GameObject CreateLowerHull( GameObject source, string name = "Lower_Hull", bool autoCopyPhysics = false )
	{
		return CreateHullObject( source, LowerModel, name, autoCopyPhysics );
	}

	public GameObject[] CreateHulls( GameObject source, bool disableSource = false, bool autoCopyPhysics = false )
	{
		var upper = CreateUpperHull( source, autoCopyPhysics: autoCopyPhysics );
		var lower = CreateLowerHull( source, autoCopyPhysics: autoCopyPhysics );

		if ( disableSource && source is not null )
		{
			source.Enabled = false;
		}

		if ( upper is not null && lower is not null )
			return new[] { upper, lower };

		if ( upper is not null )
			return new[] { upper };

		if ( lower is not null )
			return new[] { lower };

		return null;
	}

	private static GameObject CreateHullObject( GameObject source, Model model, string name, bool autoCopyPhysics )
	{
		if ( source is null || model is null )
			return null;

		var newObject = new GameObject( source.Parent, source.Enabled, name );
		newObject.WorldTransform = source.WorldTransform;
		newObject.Tags.SetFrom( source.Tags );

		var sourceRenderer = source.GetComponent<ModelRenderer>( true );
		var renderer = newObject.GetOrAddComponent<ModelRenderer>();
		renderer.Model = model;

		if ( sourceRenderer is not null )
		{
			renderer.Tint = sourceRenderer.Tint;
			renderer.RenderType = sourceRenderer.RenderType;
		}

		if ( autoCopyPhysics )
		{
			CopyPhysicsFromSource( source, newObject, model );
		}

		return newObject;
	}

	private static void CopyPhysicsFromSource( GameObject source, GameObject destination, Model hullModel )
	{
		var sourceBody = source.GetComponent<Rigidbody>( true );
		if ( sourceBody is not null )
		{
			var destinationBody = destination.AddComponent<Rigidbody>( false );
			CopyRigidbody( sourceBody, destinationBody );
			destinationBody.Enabled = sourceBody.Enabled;
		}

		Collider sourceColliderTemplate = null;
		foreach ( var sourceCollider in source.GetComponents<Collider>( true ) )
		{
			sourceColliderTemplate = sourceCollider;
			break;
		}

		if ( sourceColliderTemplate is not null && hullModel is not null )
		{
			var destinationCollider = destination.AddComponent<ModelCollider>( false );
			CopyCollider( sourceColliderTemplate, destinationCollider );
			destinationCollider.Model = hullModel;
			destinationCollider.Enabled = sourceColliderTemplate.Enabled;
		}
		else if ( sourceBody is not null && hullModel is not null )
		{
			var fallbackCollider = destination.AddComponent<ModelCollider>( false );
			fallbackCollider.Model = hullModel;
			fallbackCollider.Enabled = sourceBody.Enabled;
		}
	}

	private static void CopyCollider( Collider source, Collider destination )
	{
		destination.Static = source.Static;
		destination.IsTrigger = source.IsTrigger;
		destination.Surface = source.Surface;
		destination.SurfaceVelocity = source.SurfaceVelocity;
		destination.Friction = source.Friction;
		destination.Elasticity = source.Elasticity;
		destination.RollingResistance = source.RollingResistance;
		destination.ColliderFlags = source.ColliderFlags;
	}

	private static void CopyRigidbody( Rigidbody source, Rigidbody destination )
	{
		destination.Gravity = source.Gravity;
		destination.GravityScale = source.GravityScale;
		destination.LinearDamping = source.LinearDamping;
		destination.AngularDamping = source.AngularDamping;
		destination.MassOverride = source.MassOverride;
		destination.OverrideMassCenter = source.OverrideMassCenter;
		destination.MassCenterOverride = source.MassCenterOverride;
		destination.Locking = source.Locking;
		destination.StartAsleep = source.StartAsleep;
		destination.RigidbodyFlags = source.RigidbodyFlags;
		destination.EnableImpactDamage = source.EnableImpactDamage;
		destination.MinImpactDamageSpeed = source.MinImpactDamageSpeed;
		destination.ImpactDamage = source.ImpactDamage;
		destination.MotionEnabled = source.MotionEnabled;
		destination.CollisionEventsEnabled = source.CollisionEventsEnabled;
		destination.CollisionUpdateEventsEnabled = source.CollisionUpdateEventsEnabled;
		destination.EnhancedCcd = source.EnhancedCcd;
		destination.Velocity = source.Velocity;
		destination.AngularVelocity = source.AngularVelocity;
	}
}