Files
suaveplan/openspec/changes/enterprise-gaming-bone-mapping/proposal.md
2026-05-27 23:29:53 +01:00

3.8 KiB
Raw Blame History

OpenSpec Change: enterprise-gaming-bone-mapping

  • Change ID: enterprise-gaming-bone-mapping
  • Package: @suaveplan/bone-mapping
  • Category: gaming/rigs
  • Target path: gaming/rigs/bone-mapping
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/bone-mapping.md

Summary

Deliver @suaveplan/bone-mapping on the genesis branch from scratch at enterprise quality. The package provides skeleton detection, bone matching by fuzzy name heuristics, and retargeting math — the foundation for animation clip transfer between different character rigs. detectSkeleton classifies a bone hierarchy as Mixamo/Humanoid/Custom; matchBones pairs source and target bones using edit-distance scoring with anatomical weight boosts; retargetPose transforms local-space bone rotations from source binding pose to target binding pose using the standard retargeting matrix R_target = T_offset * R_source * T_offset^-1. Genesis adds Zod schemas, structured errors, and property-based correctness tests.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) BoneMappingError; BONE_MAP.* codes
@suaveplan/types (workspace:^) Shared primitives
zod (catalog:forms) Bone map result schemas
@opentelemetry/api (catalog:observability) Detection + match spans

Public API surface

type SkeletonType = 'mixamo' | 'humanoid' | 'custom'

function detectSkeleton(bones: BoneDescriptor[]): SkeletonType

function matchBones(
  source: BoneDescriptor[],
  target: BoneDescriptor[],
  options?: MatchOptions
): BoneMatch[]

interface MatchOptions {
  minConfidence?: number     // 0–1, default 0.5
  preferExact?: boolean
}

interface BoneMatch {
  sourceBone: string
  targetBone: string
  confidence: number
}

function retargetPose(
  sourcePose: BonePose[],
  boneMatches: BoneMatch[],
  sourceBindPose: BonePose[],
  targetBindPose: BonePose[]
): BonePose[]

function buildRetargetMatrix(
  sourceBind: Quat,
  targetBind: Quat
): RetargetMatrix

interface RetargetMatrix {
  apply(rotation: Quat): Quat
}

interface BoneDescriptor {
  name: string
  parentIndex: number
  localPosition: Vec3
  localRotation: Quat
}

interface BonePose {
  boneIndex: number
  position: Vec3
  rotation: Quat
}

Error codes

type BoneMapErrorCode =
  | 'BONE_MAP_NO_BONES'
  | 'BONE_MAP_MATCH_FAILED'
  | 'BONE_MAP_RETARGET_FAILED'
  | 'BONE_MAP_INVALID_BIND_POSE'

New additions for genesis

  1. Fuzzy bone matching — Levenshtein distance + anatomical keyword boosts (spine, arm, leg, hand, foot).
  2. Humanoid auto-detect — classify skeleton as Humanoid if ≥ 60% of canonical human bone names match within edit distance 2.
  3. Retarget matrix — R_target = T_offset * R_source * T_offset^-1 where T_offset = target_bind * inverse(source_bind).

File layout

gaming/rigs/bone-mapping/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      detect/
        detect.ts
        detect.md
        detect.test.ts
        index.ts
      match/
        match.ts
        match.md
        match.test.ts
        index.ts
      retarget/
        retarget.ts
        retarget.md
        retarget.types.ts
        retarget.test.ts
        index.ts
    utils/
      quat-math.ts
      quat-math.md
      quat-math.test.ts
  tests/
    property.test.ts

Impact

Downstream: @suaveplan/mixamo imports detectSkeleton and retargetPose; @suaveplan/ik-solver uses quaternion math from utils. Prerequisites: @suaveplan/error, @suaveplan/types.