Facade static class that chooses and runs a rigging solver for a mesh based on analysis and requested mode. It validates inputs, routes to specific solvers (Mechanical, Organic, Floor, DeepLearning, Transfer), falls back to geometric or floor rigs on failure, and returns a RigResult with degradations explained.
using AutoRig.Analyze;
using AutoRig.Rig;
namespace AutoRig.Solve;
/// <summary>Which solver family to use. Auto lets the classifier route.</summary>
public enum RigMode
{
Auto,
Mechanical,
Organic,
Floor,
/// <summary>Neural rigging; needs an installed model (see <see cref="RigNetBundle"/>).</summary>
DeepLearning,
/// <summary>Copy skeleton + weights from a rigged donor model (see <see cref="Rig.DonorRig"/>).</summary>
Transfer,
}
/// <summary>
/// The solver façade: routes an analyzed mesh to the right solver and guarantees a
/// valid result for any mesh that passed <see cref="Mesh.RigMesh.Validate"/> — solver
/// failures degrade to the floor rig instead of throwing.
/// </summary>
public static class AutoRigger
{
/// <param name="deepLearning">Loaded neural model; required for
/// <see cref="RigMode.DeepLearning"/>. Without one that mode degrades to the
/// geometric route (marked Degraded with an explanation).</param>
/// <param name="donor">Rigged donor; required for <see cref="RigMode.Transfer"/>
/// (same degrade rule).</param>
public static RigResult Rig(
AnalysisResult analysis, RigMode mode = RigMode.Auto,
RigNetBundle deepLearning = null, Rig.DonorRig donor = null )
{
ArgumentNullException.ThrowIfNull( analysis );
if ( (mode == RigMode.DeepLearning && deepLearning is null)
|| (mode == RigMode.Transfer && donor is null) )
{
var geometric = Rig( analysis, RigMode.Auto );
return new RigResult
{
Skeleton = geometric.Skeleton,
Weights = geometric.Weights,
SolverName = geometric.SolverName,
Degraded = true,
Explanation = (mode == RigMode.Transfer
? "No donor model was picked - used the geometric solver instead. "
: "No deep-learning model is installed - used the geometric solver instead. ")
+ geometric.Explanation,
};
}
try
{
var result = mode switch
{
RigMode.Mechanical => MechanicalSolver.Rig( analysis ),
RigMode.Organic => OrganicSolver.Rig( analysis ),
RigMode.Floor => FloorSolver.Rig( analysis ),
RigMode.DeepLearning => DeepLearningSolver.Rig( analysis, deepLearning ),
RigMode.Transfer => TransferSolver.Rig( analysis, donor ),
_ => analysis.Classification.Kind == MeshKind.Mechanical
? MechanicalSolver.Rig( analysis )
: OrganicSolver.Rig( analysis ),
};
result.Skeleton.Validate();
result.Weights.Validate( analysis.Mesh, result.Skeleton );
return result;
}
catch ( Exception e )
{
// Failed neural/transfer rigs fall back to the geometric route first.
if ( mode is RigMode.DeepLearning or RigMode.Transfer )
{
var geometric = Rig( analysis, RigMode.Auto );
return new RigResult
{
Skeleton = geometric.Skeleton,
Weights = geometric.Weights,
SolverName = geometric.SolverName,
Degraded = true,
Explanation = $"The {mode} solver failed ({FirstLine( e.Message )}) - "
+ "used the geometric solver instead.",
};
}
var floor = FloorSolver.Rig( analysis );
return new RigResult
{
Skeleton = floor.Skeleton,
Weights = floor.Weights,
SolverName = floor.SolverName,
Degraded = true,
Explanation = $"The {mode} solver failed ({FirstLine( e.Message )}) - "
+ "produced a single-bone rig instead.",
};
}
}
static string FirstLine( string text )
{
var i = text.IndexOfAny( [ '\r', '\n' ] );
return i < 0 ? text : text[..i];
}
}