Registry for soft-bone collision shapes. It stores published per-chain chain collider snapshots and collects shapes each physics step, filters out culled renderers, and provides APIs to publish, remove and gather colliders for chain-vs-chain and body-vs-body collision queries.
using System.Collections.Generic;
using Sandbox;
using System.Linq;
public readonly struct SoftBoneChainColliderSnapshot
{
public readonly SkinnedModelRenderer Renderer;
public readonly SoftBoneColliderShape Shape;
public SoftBoneChainColliderSnapshot( SkinnedModelRenderer renderer, SoftBoneColliderShape shape )
{
Renderer = renderer;
Shape = shape;
}
}
public readonly struct SoftBoneBodyColliderSnapshot
{
public readonly SkinnedModelRenderer Renderer;
public readonly SoftBoneColliderShape Shape;
public SoftBoneBodyColliderSnapshot( SkinnedModelRenderer renderer, SoftBoneColliderShape shape )
{
Renderer = renderer;
Shape = shape;
}
}
/// <summary>Publishes the previous solved link shapes used by cross-chain contacts.</summary>
public static class SoftBoneCollisionRegistry
{
private static readonly Dictionary<BoBiCoSoftBoneChain, List<SoftBoneChainColliderSnapshot>> PublishedShapes = new();
private static readonly Dictionary<BoBiCoSoftBoneChain, List<SoftBoneChainColliderSnapshot>> QueryShapes = new();
private static readonly List<SoftBoneBodyColliderSnapshot> QueryBodyShapes = new();
private static readonly HashSet<SkinnedModelRenderer> CulledRenderers = new();
public static void SetRendererCulled( SkinnedModelRenderer renderer, bool culled )
{
if ( renderer is null || !renderer.IsValid() ) return;
if ( culled ) CulledRenderers.Add( renderer );
else CulledRenderers.Remove( renderer );
}
public static void Publish( BoBiCoSoftBoneChain chain, SkinnedModelRenderer renderer, List<SoftBoneChainColliderSnapshot> shapes )
{
PublishedShapes[chain] = new List<SoftBoneChainColliderSnapshot>( shapes );
}
public static void BeginStep( Scene scene )
{
QueryShapes.Clear();
foreach ( var entry in PublishedShapes )
QueryShapes.Add( entry.Key, new List<SoftBoneChainColliderSnapshot>( entry.Value ) );
QueryBodyShapes.Clear();
if ( scene is null )
return;
foreach ( var collider in scene.GetAllComponents<BoBiCoSoftBoneCollider>() )
{
if ( collider is null || !collider.Enabled )
continue;
var renderer = collider.GetOwningRenderer();
if ( renderer is not null && CulledRenderers.Contains( renderer ) )
continue;
if ( collider.TryGetWorldShape( renderer, out var shape ) )
QueryBodyShapes.Add( new SoftBoneBodyColliderSnapshot( renderer, shape ) );
}
}
public static void Remove( BoBiCoSoftBoneChain chain )
{
PublishedShapes.Remove( chain );
QueryShapes.Remove( chain );
}
public static void CollectChainShapes( BoBiCoSoftBoneChain owner, SkinnedModelRenderer renderer, SoftBoneCollisionMode mode, List<SoftBoneColliderShape> output )
{
if ( mode == SoftBoneCollisionMode.BaseCollision )
return;
foreach ( var entry in QueryShapes )
{
if ( entry.Key == owner || !entry.Key.Enabled )
continue;
var sameRenderer = entry.Value.Count > 0 && entry.Value[0].Renderer == renderer;
if ( !sameRenderer && mode != SoftBoneCollisionMode.FullCollision )
continue;
foreach ( var snapshot in entry.Value )
output.Add( snapshot.Shape );
}
}
public static void CollectExternalBodyShapes( SkinnedModelRenderer ownerRenderer, List<SoftBoneColliderShape> output )
{
foreach ( var snapshot in QueryBodyShapes )
if ( snapshot.Renderer != ownerRenderer )
output.Add( snapshot.Shape );
}
}