AutoRig/Solve/OrganicSolver.cs

Solver entry point for organic character rigging. It voxelizes the input mesh, extracts a curve skeleton, detects body category and symmetry, tries to apply a humanoid/quadruped/winged template when symmetry is present, falls back to a graph-based skeleton, computes geodesic skin weights, and returns a RigResult with skeleton and weights.

using AutoRig.Analyze;
using AutoRig.Rig;
using AutoRig.Solve.Organic;
using AutoRig.Voxel;

namespace AutoRig.Solve;

/// <summary>
/// Rigs a creature/character: voxelize, extract the curve skeleton, detect the body
/// category, snap humanoids to the citizen-compatible template (generic graph rig
/// otherwise), and skin with geodesic voxel weights. Hybrid meshes run through the
/// same volume (rigid attachments merge into it and bind to the nearest bones).
/// </summary>
public static class OrganicSolver
{
    public static RigResult Rig( AnalysisResult analysis )
    {
        ArgumentNullException.ThrowIfNull( analysis );

        var resolution = analysis.Mesh.TriangleCount < 50_000 ? 96 : 64;
        var grid = VoxelGrid.Build( analysis.Mesh, resolution );
        var graph = CurveSkeleton.Extract( grid );
        var bounds = analysis.Mesh.ComputeBounds();

        var (category, _, why) = CategoryDetector.Detect( graph, analysis.Symmetry, bounds );

        RigSkeleton? skeleton = null;
        var usedTemplate = false;
        if ( analysis.Symmetry is { } symmetry )
        {
            skeleton = category switch
            {
                OrganicCategory.Humanoid => HumanoidTemplate.Build( graph, symmetry, bounds ),
                OrganicCategory.Quadruped => QuadrupedTemplate.Build( graph, symmetry, bounds ),
                OrganicCategory.Winged => WingedTemplate.Build( graph, symmetry, bounds ),
                _ => null,
            };
            usedTemplate = skeleton is not null;
        }
        skeleton ??= GraphSkeletonBuilder.Build( graph );

        var weights = OrganicSkinner.Skin( analysis.Mesh, grid, skeleton );

        var result = new RigResult
        {
            Skeleton = skeleton,
            Weights = weights,
            SolverName = "organic",
            Degraded = false,
            Explanation = usedTemplate
                ? category == OrganicCategory.Humanoid
                    ? $"Humanoid detected - citizen-compatible skeleton ({skeleton.Joints.Count} joints)."
                    : $"{category} detected - anatomical skeleton ({skeleton.Joints.Count} joints)."
                : $"{category} body ({why}) - {skeleton.Joints.Count}-joint skeleton from the volume centerline.",
        };
        result.Skeleton.Validate();
        result.Weights.Validate( analysis.Mesh, result.Skeleton );
        return result;
    }
}