Adds wall mount generation options to the scaffolding editor: plate size, range, bolts, connector, tag filters and material, plus the anchor sites along each bay and level and the mesh build call. Also exposes a debug draw and status readout.
namespace AdvancedMappingTools;
public sealed partial class ScaffoldingEditor : IAnchorAwareSettings
{
[Property, Group( "Wall Mounts" ), WideMode, Description( "Generate mounts only where a real wall is found behind the platforms. Mount dimensions are world units." )]
public bool MountsEnabled { get; set; }
[Property, Group( "Wall Mounts" ), Range( 1, 4096, slider: false ), WideMode]
public float MountRange { get; set; } = 256;
[Property, Group( "Wall Mounts" ), Range( 0, 128, slider: false ), WideMode]
public float MountMinimum { get; set; } = .01f;
[Property, Group( "Wall Mounts" ), Range( 1, 512, slider: false ), WideMode]
public float MountWidth { get; set; } = 12;
[Property, Group( "Wall Mounts" ), Range( 1, 512, slider: false ), WideMode]
public float MountHeight { get; set; } = 16;
[Property, Group( "Wall Mounts" ), Range( .1f, 64, slider: false ), WideMode]
public float MountThickness { get; set; } = 1;
[Property, Group( "Wall Mounts" ), Range( 0, 128, slider: false ), WideMode]
public float MountClearance { get; set; }
[Property, Group( "Wall Mounts" ), Range( -180, 180, slider: false ), WideMode]
public float MountRotation { get; set; }
[Property, Group( "Wall Mounts" ), WideMode]
public bool MountBolts { get; set; } = true;
[Property, Group( "Wall Mounts" ), Range( .1f, 8, slider: false ), WideMode]
public float MountBoltRadius { get; set; } = .65f;
[Property, Group( "Wall Mounts" ), WideMode]
public bool MountConnector { get; set; } = true;
[Property, Group( "Wall Mounts" ), Range( .1f, 32, slider: false ), WideMode]
public float MountConnectorWidth { get; set; } = 2;
[Property, Group( "Wall Mounts" ), WideMode, Description( "Reject plates whose corners are not supported by a continuous surface." )]
public bool MountCheckEdges { get; set; } = true;
[Property, Group( "Wall Mounts" ), WideMode]
public string MountIncludeTags { get; set; } = "";
[Property, Group( "Wall Mounts" ), WideMode]
public string MountExcludeTags { get; set; } = "";
[Property, Group( "Wall Mounts" ), ResourceType( "vmat" ), WideMode]
public string MountMaterial { get; set; }
[Property, Group( "Wall Mounts" ), WideMode]
public bool ShowMountDebug { get; set; }
[System.Text.Json.Serialization.JsonIgnore, Property, ReadOnly, Group( "Wall Mounts" ), WideMode]
public string MountStatus => MountsEnabled ? AnchorIntegration.Status( MountOwner ) : "Wall mounts off.";
public AnchorOptions MountOptions() => new()
{
Range = MountRange,
Minimum = MountMinimum,
Width = MountWidth,
Height = MountHeight,
Thickness = MountThickness,
Clearance = MountClearance,
Rotation = MountRotation,
Bolts = MountBolts,
BoltRadius = MountBoltRadius,
Connector = MountConnector,
ConnectorWidth = MountConnectorWidth,
CheckEdges = MountCheckEdges,
IncludeTags = MountIncludeTags,
ExcludeTags = MountExcludeTags,
Material = MountMaterial
};
[Property, Group( "Wall Mounts" ), Range( 1, 16, slider: false ), WideMode]
public int MountEveryBays { get; set; } = 2;
[System.Text.Json.Serialization.JsonIgnore]
public string MountOwner => "Scaffolding Mounts";
public IEnumerable<AnchorSite> MountSites()
{
var length = MathF.Max( Length, 32 );
var depth = MathF.Max( Depth, 8 );
var height = MathF.Max( Height, 16 );
var levels = Math.Clamp( Levels, 1, 16 );
var bays = (int)MathF.Max( MathF.Round( length / Math.Clamp( BayWidth, 16, 256 ) ), 1 );
var every = Math.Clamp( MountEveryBays, 1, 16 );
for ( var level = 1; level <= levels; level++ )
for ( var bay = 0; bay <= bays; bay++ )
if ( bay % every == 0 || bay == bays )
yield return new( $"level{level}_bay{bay}", new Vector3( -length * .5f + length * bay / bays, -depth * .5f, height * level / levels ), new Vector3( 0, -1, 0 ) );
}
void BuildSurfaceMounts( MeshKit kit, Transform frame )
{
if ( !MountsEnabled )
return;
var context = new GenContext( new() )
{
Surface = new( SurfaceSolver.Begin( SceneEditorSession.Active?.Scene ?? Game.ActiveScene ), frame )
};
var root = context.Group( "root", "Scaffolding Mounts", "SurfaceMount", "mount" );
AnchorIntegration.Append( root, (IAnchorAwareSettings)this, context );
kit.ImportMesh( root.Flatten( frame ), Transform.Zero );
}
public override IReadOnlySet<string> UnusedOptions
{
get
{
var h = new HashSet<string>( base.UnusedOptions );
h.UnionWith( AnchorIntegration.Hidden( this ) );
if ( !MountsEnabled )
h.Add( nameof( MountEveryBays ) );
return h;
}
}
public override bool ChangesOtherSettings( string property ) => base.ChangesOtherSettings( property ) || property is nameof( MountsEnabled ) or nameof( MountBolts ) or nameof( MountConnector );
public override void OnUpdate( SceneTrace trace )
{
base.OnUpdate( trace );
if ( MountsEnabled && ShowMountDebug )
SurfaceDebug.Draw( MountOwner );
}
}