# ComputeJointInfluences for meshes with only a subset of the joint

**URL:** https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844
**Category:** USD
**Created:** [January 29, 2026, 11:55pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844 "2026-01-29T23:55:54Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![koen](https://avatars.discourse-cdn.com/v4/letter/k/a9a28c/32.png) [@koen](https://forum.aousd.org/u/koen)
#### Post date: [January 29, 2026, 11:55pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/1 "2026-01-29T23:55:54Z")

</div>

Hello all,

I was running into some issues with meshes that store only a few joints from a full skeleton as the skeleton might contain joints that have no skinning influence. Maya writes usd files like this for example.

The indices I got back from the mesh map to the meshes skel:joints, but not to the skeletons:joints. I had hoped that by going through the skinningQuery, this remapping would be handled ( so the indices I get back are now into the skeleton’s joints and no longer in the meshes. I have the code I tried below. To unblock people, I now just load the two arrays and build a remapping table by comparing the paths. I have a feeling there is a more elegant way, does anyone know what I am doing wrong here?

Thanks,

Koen

> UsdSkelCache skelCache;
> 
> VtArray\<_int_\> jointIndices;
> 
> VtArray\<_float_\> jointWeights;
> 
> if (UsdSkelRoot skelRoot = UsdSkelRoot::Find(prim))
> 
> {
> 
> ```
> skelCache.Populate(skelRoot, UsdTraverseInstanceProxies());
> 
> *// Find the Skeleton that should affect this prim.*
> 
> UsdSkelSkeleton skel = UsdSkelBindingAPI(prim).GetInheritedSkeleton();
> 
> if (UsdSkelSkinningQuery skinningQuery = skelCache.GetSkinningQuery(prim)) 
> 
> {
> 
> skinningQuery.ComputeJointInfluences(&jointIndices, &jointWeights);
> 
>     
> 
> *// auto mapper = skinningQuery.GetJointMapper();*
> 
> *// VtArray<int> remappedIndices;*
> 
> *// mapper->Remap(jointIndices, &remappedIndices);*
> 
> }
> 
> ```
> 
> }

---

<div class="post-metadata">

### Author: ![Jan\_Haluszka](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/jan_haluszka/32/1054_2.png) [@Jan\_Haluszka](https://forum.aousd.org/u/Jan_Haluszka)
#### Post date: [January 30, 2026, 8:50am UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/2 "2026-01-30T08:50:14Z")

</div>

Hi Koen,

let me know if that works: if so, I would like to publish it as a tutorial, but I would need more insights from you what to focus on… (obviously this was created with the help of our ‘helper Friends’… , I am constantly trying to prevent Drift and Haluzination… → SO if that little MD file is BS and AIslop, than let me know, so that I can get my system more targeted…)

# UsdSkel Joint Index Remapping: Mesh-Local to Skeleton Space

**Problem:** Joint indices returned from `UsdSkelSkinningQuery::ComputeJointInfluences()` map to the mesh’s `skel:joints` array, but you need indices into the skeleton’s `joints` array. The mesh may only store a subset of joints from the full skeleton.

**Root Cause:** `GetJointMapper()` provides mapping FROM skeleton joint order TO mesh-local joint order, but you need the inverse mapping. The `UsdSkelBindingAPI::GetJointsAttr()` documentation [[7]](#appendix-sources) explains that mesh `skel:joints` is optional and “may vary from the order of joints on the Skeleton itself” [[8]](#appendix-sources), which is why remapping is necessary.

## Solution

The key insight is that `GetJointMapper()` maps **FROM skeleton TO mesh-local** , but you need to map **FROM mesh-local TO skeleton**. You need to build the inverse mapping by comparing joint paths. The `UsdSkelSkinningQuery::GetJointOrder()` method [[1]](#appendix-sources) provides access to the mesh-local joint order, while `UsdSkelSkeletonQuery::GetJointOrder()` [[2]](#appendix-sources) provides the skeleton’s joint order. This exact problem and solution approach has been discussed in the Alliance for OpenUSD forum [[9]](#appendix-sources), confirming the path-comparison remapping strategy.

### C++ Solution

```cpp
#include <pxr/usd/usdSkel/cache.h>
#include <pxr/usd/usdSkel/skinningQuery.h>
#include <pxr/usd/usdSkel/skeletonQuery.h>
#include <pxr/usd/usdSkel/bindingAPI.h>
#include <pxr/base/vt/array.h>
#include <pxr/base/tf/token.h>
#include <unordered_map>

UsdSkelCache skelCache;
VtArray<int> jointIndices;
VtArray<float> jointWeights;

if (UsdSkelRoot skelRoot = UsdSkelRoot::Find(prim)) {
    // Populate the cache - required before GetSkinningQuery() works
    skelCache.Populate(skelRoot, UsdTraverseInstanceProxies());
    
    // Find the Skeleton that should affect this prim
    // See UsdSkelBindingAPI::GetInheritedSkeleton() [[3]](#appendix-sources)
    UsdSkelSkeleton skel = UsdSkelBindingAPI(prim).GetInheritedSkeleton();
    
    // Get skinning query - see UsdSkelSkinningQuery class reference [[1]](#appendix-sources)
    if (UsdSkelSkinningQuery skinningQuery = skelCache.GetSkinningQuery(prim)) {
        // Get joint influences (indices are into mesh's skel:joints)
        // See UsdSkelSkinningQuery::ComputeJointInfluences() [[1]](#appendix-sources)
        skinningQuery.ComputeJointInfluences(&jointIndices, &jointWeights);
        
        // Get the skeleton query to access skeleton joint order
        UsdSkelSkeletonQuery skeletonQuery = skelCache.GetSkeletonQuery(skel);
        if (!skeletonQuery) {
            // Fallback: get skeleton directly
            skeletonQuery = skelCache.GetSkeletonQuery(skel.GetPrim());
        }
        
        if (skeletonQuery) {
            // Get mesh-local joint order (from skel:joints)
            // See UsdSkelSkinningQuery::GetJointOrder() [[1]](#appendix-sources)
            VtTokenArray meshJointOrder;
            skinningQuery.GetJointOrder(&meshJointOrder);
            
            // Get skeleton joint order
            // See UsdSkelSkeletonQuery::GetJointOrder() [[2]](#appendix-sources)
            VtTokenArray skeletonJointOrder = skeletonQuery.GetJointOrder();
            
            // Build reverse lookup: mesh joint path -> skeleton joint index
            std::unordered_map<TfToken, int, TfToken::HashFunctor> meshToSkelMap;
            for (size_t i = 0; i < meshJointOrder.size(); ++i) {
                const TfToken& meshJointPath = meshJointOrder[i];
                
                // Find this mesh joint in the skeleton's joint order
                for (size_t j = 0; j < skeletonJointOrder.size(); ++j) {
                    if (skeletonJointOrder[j] == meshJointPath) {
                        meshToSkelMap[meshJointPath] = static_cast<int>(j);
                        break;
                    }
                }
            }
            
            // Remap joint indices from mesh-local to skeleton space
            VtArray<int> remappedIndices;
            remappedIndices.reserve(jointIndices.size());
            
            for (int meshLocalIdx : jointIndices) {
                if (meshLocalIdx >= 0 && 
                    static_cast<size_t>(meshLocalIdx) < meshJointOrder.size()) {
                    const TfToken& meshJointPath = meshJointOrder[meshLocalIdx];
                    auto it = meshToSkelMap.find(meshJointPath);
                    if (it != meshToSkelMap.end()) {
                        remappedIndices.push_back(it->second);
                    } else {
                        // Joint not found in skeleton (shouldn't happen, but handle gracefully)
                        remappedIndices.push_back(-1);
                    }
                } else {
                    remappedIndices.push_back(-1);
                }
            }
            
            // Now remappedIndices contains indices into skeleton's joints array
            // Use remappedIndices instead of jointIndices
        }
    }
}

```

### Python Solution

```python
from pxr import Usd, UsdSkel, Vt

def remap_joint_indices_to_skeleton_space(prim):
    """
    Remap joint indices from mesh-local skel:joints to skeleton joints.
    
    Args:
        prim: The skinnable prim (mesh) with UsdSkelBindingAPI
        
    Returns:
        tuple: (remapped_indices, joint_weights) or None if remapping fails
    """
    skel_cache = UsdSkel.Cache()
    
    # Find SkelRoot
    skel_root = UsdSkel.Root.Find(prim)
    if not skel_root:
        return None
    
    skel_cache.Populate(skel_root, Usd.TraverseInstanceProxies())
    
    # Get the skeleton
    # See UsdSkelBindingAPI::GetInheritedSkeleton() [[3]](#appendix-sources)
    binding_api = UsdSkel.BindingAPI(prim)
    skel = binding_api.GetInheritedSkeleton()
    if not skel:
        return None
    
    # Get skinning query - see UsdSkelSkinningQuery class reference [[1]](#appendix-sources)
    skinning_query = skel_cache.GetSkinningQuery(prim)
    if not skinning_query:
        return None
    
    # Get joint influences (indices are into mesh's skel:joints)
    # See UsdSkelSkinningQuery::ComputeJointInfluences() [[1]](#appendix-sources)
    joint_indices = Vt.IntArray()
    joint_weights = Vt.FloatArray()
    if not skinning_query.ComputeJointInfluences(joint_indices, joint_weights):
        return None
    
    # Get skeleton query - see UsdSkelSkeletonQuery class reference [[2]](#appendix-sources)
    skeleton_query = skel_cache.GetSkeletonQuery(skel)
    if not skeleton_query:
        return None
    
    # Get mesh-local joint order (from skel:joints)
    # See UsdSkelSkinningQuery::GetJointOrder() [[1]](#appendix-sources)
    mesh_joint_order = Vt.TokenArray()
    if not skinning_query.GetJointOrder(mesh_joint_order):
        # If no custom joint order, mesh uses skeleton order directly
        skeleton_joint_order = skeleton_query.GetJointOrder()
        # If mesh order matches skeleton order, no remapping needed
        return (joint_indices, joint_weights)
    
    # Get skeleton joint order
    skeleton_joint_order = skeleton_query.GetJointOrder()
    
    # Build reverse lookup: mesh joint path -> skeleton joint index
    mesh_to_skel_map = {}
    for i, mesh_joint_path in enumerate(mesh_joint_order):
        try:
            skel_idx = skeleton_joint_order.index(mesh_joint_path)
            mesh_to_skel_map[i] = skel_idx
        except ValueError:
            # Joint not found in skeleton (shouldn't happen, but handle gracefully)
            mesh_to_skel_map[i] = -1
    
    # Remap joint indices from mesh-local to skeleton space
    remapped_indices = Vt.IntArray()
    remapped_indices.reserve(len(joint_indices))
    
    for mesh_local_idx in joint_indices:
        if mesh_local_idx >= 0 and mesh_local_idx < len(mesh_joint_order):
            skel_idx = mesh_to_skel_map.get(mesh_local_idx, -1)
            remapped_indices.append(skel_idx)
        else:
            remapped_indices.append(-1)
    
    return (remapped_indices, joint_weights)

# Usage example:
stage = Usd.Stage.Open("path/to/file.usd")
mesh_prim = stage.GetPrimAtPath("/SkelRoot/Mesh")

result = remap_joint_indices_to_skeleton_space(mesh_prim)
if result:
    remapped_indices, joint_weights = result
    # Now remapped_indices contains indices into skeleton's joints array
    print(f"Remapped {len(remapped_indices)} joint indices")

```

## Why GetJointMapper().Remap() Doesn’t Work Directly

The `GetJointMapper()` returns a mapper that maps **FROM skeleton joint order TO mesh-local joint order**. When you call `Remap()` on it, you’re mapping skeleton-space data to mesh-local space, which is the opposite of what you need. The UsdSkel Schemas In-Depth documentation [[5]](#appendix-sources) explains that remapping via mapper works by matching joint names, but the mapper operates in the forward direction (skeleton → mesh-local) only.

The mapper is designed for use cases like:

- Mapping skeleton joint transforms to mesh-local joint order for skinning
- Mapping animation data from skeleton order to mesh-local order

But for remapping **indices** (not transforms), you need the inverse mapping, which requires comparing the joint paths/orders directly using `GetJointOrder()` methods [[1]](#appendix-sources) [[2]](#appendix-sources). This limitation has been discussed in related forum threads [[10]](#appendix-sources) and GitHub issues [[11]](#appendix-sources), where users encountered similar challenges with explicit joint orders.

## Alternative: Using GetJointOrder() Directly

If the mapper is null (meaning mesh uses skeleton order directly), you can skip remapping:

```cpp
VtTokenArray meshJointOrder;
if (skinningQuery.GetJointOrder(&meshJointOrder)) {
    // Mesh has custom joint order, need remapping
    // ... use remapping code above ...
} else {
    // Mesh uses skeleton order directly, no remapping needed
    // jointIndices already map to skeleton joints
}

```

## Key Takeaways

1. **`GetJointMapper()` maps skeleton → mesh-local**, not the other way around (see [[5]](#appendix-sources))
2. **To remap indices** , compare joint paths between mesh `skel:joints` and skeleton `joints` using `GetJointOrder()` methods [[1]](#appendix-sources) [[2]](#appendix-sources)
3. **Build a reverse lookup map** from mesh joint paths to skeleton joint indices (as discussed in [[9]](#appendix-sources))
4. **Handle the case** where mapper is null (mesh uses skeleton order directly) - see `UsdSkelBindingAPI::GetJointsAttr()` behavior [[7]](#appendix-sources)

* * *

## Appendix: Sources

### [1] UsdSkelSkinningQuery Class Reference

**Link:** [Universal Scene Description: UsdSkelSkinningQuery Class Reference](https://openusd.org/dev/api/class_usd_skel_skinning_query.html)

The official OpenUSD API reference for `UsdSkelSkinningQuery` documents the exact methods used in this solution: `ComputeJointInfluences()`, `GetJointMapper()`, and `GetJointOrder()`. This class reference provides the authoritative documentation for extracting joint influences from skinnable primitives and accessing the mesh-local joint order, which is essential for building the reverse mapping from mesh indices to skeleton indices.

### [2] UsdSkelSkeletonQuery Class Reference

**Link:** [Universal Scene Description: UsdSkelSkeletonQuery Class Reference](https://openusd.org/dev/api/class_usd_skel_skeleton_query.html)

The `UsdSkelSkeletonQuery` class reference documents the `GetJointOrder()` method that returns the skeleton’s authoritative joint order. This method provides the canonical joint ordering that skeleton-space data uses, making it the target for remapping mesh-local joint indices. The skeleton query is obtained through `UsdSkelCache::GetSkelQuery()` and serves as the source of truth for skeleton joint ordering in the solution.

### [3] UsdSkelBindingAPI Class Reference

**Link:** [Universal Scene Description: UsdSkelBindingAPI Class Reference](https://openusd.org/dev/api/class_usd_skel_binding_a_p_i.html)

The `UsdSkelBindingAPI` class reference documents the `GetInheritedSkeleton()` method used to find the skeleton bound to a skinnable primitive. This API is the entry point for discovering skeleton bindings and is essential for obtaining the skeleton that provides the target joint order for remapping. The binding API also defines the `skel:joints` attribute that can specify custom joint orderings on meshes.

### [4] UsdSkel API Introduction

**Link:** [API Introduction](https://openusd.org/docs/api/_usd_skel __a_p_i__ intro.html)

The UsdSkel API Introduction provides conceptual overview of the skeletal animation system, explicitly mentioning `UsdSkelBindingAPI::GetInheritedSkeleton()` and the use of `UsdSkelSkeletonQuery` for querying skeleton data. This documentation explains the relationship between skeletons, bindings, and skinning queries, providing context for why the remapping solution requires both skeleton and skinning queries to compare joint orders.

### [5] UsdSkel Schemas In-Depth: Joint Order

**Link:** [Schemas In-Depth](https://openusd.org/docs/api/_usd_skel__schemas.html)

The UsdSkel Schemas In-Depth documentation explains flexible joint orderings and how remapping works via mapper by matching joint names. This page explicitly states that joint orderings can differ between skeletons, animations, and meshes, and can be sparse subsets, which directly explains why the index mismatch problem occurs. The documentation clarifies that the mapper remaps data from source order to target order, confirming why inverse mapping is needed for index remapping.

### [6] UsdSkelSkeleton: joints Attribute

**Link:** [UsdSkelSkeleton Class Reference](https://openusd.org/docs/api/class_usd_skel_skeleton.html)

The `UsdSkelSkeleton` class reference documents the `joints` attribute, which defines the skeleton’s authoritative joint list and order. This attribute provides the canonical joint ordering that all skeleton-space operations use, making it the target for remapping mesh-local joint indices. Understanding this attribute is crucial for recognizing that skeleton joints represent the authoritative joint order in the solution.

### [7] UsdSkelBindingAPI::GetJointsAttr Behavior (NVIDIA Docs)

**Link:** [usdrt::UsdSkelBindingAPI — usdrt 7.5.1 documentation](https://docs.omniverse.nvidia.com/kit/docs/usdrt/latest/_apidocs/classusdrt_1_1UsdSkelBindingAPI.html)

NVIDIA’s Omniverse documentation for `UsdSkelBindingAPI::GetJointsAttr()` explains the critical behavior: this optional joints list defines which joints the `jointIndices` apply to, and if not defined, indices apply to the bound Skeleton’s joints. This documentation clarifies why meshes can have custom joint orderings via `skel:joints` that differ from the skeleton’s order, which is the root cause of the remapping problem solved in this document.

### [8] NVIDIA pxr-usd-api: Joint Order Documentation

**Link:** [UsdSkel module — Omniverse Kit 106.3.0 documentation](https://docs.omniverse.nvidia.com/kit/docs/pxr-usd-api/106.3.0/pxr/UsdSkel.html)

NVIDIA’s pxr-usd-api documentation explicitly states that joint token order “may vary from the order of joints on the Skeleton itself”, confirming that mesh-local joint orderings can differ from skeleton joint orderings. This documentation validates the problem statement and confirms that the remapping solution is necessary when working with meshes that define custom joint orderings, such as those exported from Maya.

### [9] Alliance for OpenUSD Forum: “ComputeJointInfluences for meshes with only a subset of the joint”

**Link:** [ComputeJointInfluences for meshes with only a subset of the joint](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844)

This forum thread describes exactly the problem solved in this document: meshes with `skel:joints` that are a subset of the skeleton’s joints, causing index mismatches between `ComputeJointInfluences()` results and skeleton joint indices. The discussion confirms the solution approach of building a remap table by comparing joint paths, validating the path-comparison remapping strategy used in this document.

### [10] Alliance for OpenUSD Forum: “Joint ordering, documentation incorrect…?”

**Link:** [Joint ordering, documentation incorrect or just me messing up again?](https://forum.aousd.org/t/joint-ordering-documentation-incorrect-or-just-me-messing-up-again/989)

This forum discussion explores joint ordering challenges and documentation ambiguities, providing context for why the remapping problem can be confusing. The thread discusses how joint orderings work in practice and highlights common pitfalls, which helps explain why `GetJointMapper().Remap()` doesn’t directly solve the inverse mapping problem for indices.

### [11] GitHub Issue: “[UsdSkel] Explicit Joint Orders not handled by …”

**Link:** [[UsdSkel] Explicit Joint Orders not handled by HdxCompute / maya import · Issue #789 · PixarAnimationStudios/OpenUSD · GitHub](https://github.com/PixarAnimationStudios/USD/issues/789)

This GitHub issue discusses problems with explicit joint orders not being handled correctly in certain scenarios, providing additional context for joint ordering edge cases. The issue highlights that joint ordering can be a source of confusion and that proper remapping is necessary when working with custom joint orderings, reinforcing the need for the solution documented here.

---

<div class="post-metadata">

### Author: ![koen](https://avatars.discourse-cdn.com/v4/letter/k/a9a28c/32.png) [@koen](https://forum.aousd.org/u/koen)
#### Post date: [January 30, 2026, 10:23am UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/3 "2026-01-30T10:23:21Z")

</div>

Hello Jan,

Thanks for looking into this. The Key Takeaways are actually pretty clear. The example code was pretty much what I ended up with, but I did not feel great about the string comparison for the joint paths. I messed around with the mapper a bit and came up with this, which seems to work and should match how usd internally maps the paths.

> VtArray indices;  
> indices.resize(jointIndices.size());  
> for (size\_t i = 0; i \< jointIndices.size(); i++)  
> {  
> indices[i] = i;  
> }  
> // start with identity mapping  
> jointLut = indices;
> 
> // here we build the lut to map indices into the (sparse) joint array in  
> // the mesh to the full joint array of the skeleton  
> if (skinningQuery.GetJointMapper())  
> {  
> skinningQuery.GetJointMapper()-\>Remap(indices, &jointLut);  
> }

I would love OpenUsd to support something like this which includes this remapping ( seems to small of a contribution, but I might look into that )

> skinningQuery.Compute **Skel** JointInfluences(&jointIndices, &jointWeights );

---

<div class="post-metadata">

### Author: ![Jan\_Haluszka](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/jan_haluszka/32/1054_2.png) [@Jan\_Haluszka](https://forum.aousd.org/u/Jan_Haluszka)
#### Post date: [January 30, 2026, 12:01pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/4 "2026-01-30T12:01:47Z")

</div>

cool!  
which means that my little helper was not totally of!!, because I am not deep enough into the subject to be able to give you a meaningfull answer myself.

I can see that you ended up using C++…  
some insights from the text down below…

* * *

**Why Python Doesn’t Have It:**

1. **Not in C++ API** - The C++ API doesn’t have it either, so Python can’t expose it
2. **Application-Level Concern** - OpenUSD considers this an application problem, not a library problem
3. **Performance** - Building inverse maps costs memory/CPU, so it’s opt-in
4. **Flexibility** - Different apps need different remapping strategies

* * *

### Alternative C++ Solution: Using the Mapper’s Internal Index Map

Your friend discovered a more efficient approach that leverages the mapper’s internal `_indexMap` structure instead of string comparisons. This is a clever optimization: (more insights fiúrteh down)

* * *

→ What more context would be needed to make it a tutorial? could you provide the sample or a similar sample, so we could make it a round tutorial?

cheers Jan

Here’s an update on the insights.

* * *

* * *

* * *

# UsdSkel Joint Index Remapping: Mesh-Local to Skeleton Space

**Problem:** Joint indices returned from `UsdSkelSkinningQuery::ComputeJointInfluences()` map to the mesh’s `skel:joints` array, but you need indices into the skeleton’s `joints` array. The mesh may only store a subset of joints from the full skeleton.

**Root Cause:** `GetJointMapper()` provides mapping FROM skeleton joint order TO mesh-local joint order, but you need the inverse mapping. The `UsdSkelBindingAPI::GetJointsAttr()` documentation [[7]](#appendix-sources) explains that mesh `skel:joints` is optional and “may vary from the order of joints on the Skeleton itself” [[8]](#appendix-sources), which is why remapping is necessary.

## Understanding the Problem: Why C++ is Needed

### The Core Issue: Two Different Numbering Systems

Think of it like this: imagine you have a **skeleton** (like a human skeleton) with 100 bones numbered 0-99. But a **mesh** (like a hand mesh) only needs 15 of those bones. The mesh creates its own numbering system (0-14) for just those 15 bones it uses.

**The Problem:**

- When you ask the mesh “which bones affect this vertex?”, it gives you numbers like `[2, 5, 8]`
- But those numbers refer to the **mesh’s own list** (positions 2, 5, 8 in the mesh’s list of 15 bones)
- You need to know which bones those are in the **skeleton’s list** (positions 23, 47, 89 in the skeleton’s list of 100 bones)

**Why This Happens:**

- Maya (and other DCC tools) export meshes with only the joints they actually use
- This saves memory and makes files smaller
- But it creates this “translation problem” between two numbering systems

### What OpenUSD Python Provides (and What It Doesn’t)

**What OpenUSD Python DOES provide:**

- `ComputeJointInfluences()` - Gets the joint indices and weights for each vertex
- `GetJointMapper()` - A tool that maps skeleton → mesh-local (forward direction)
- `GetJointOrder()` - Gets the list of joint names/paths for mesh or skeleton

**What OpenUSD Python DOESN’T provide:**

- A built-in way to map mesh-local indices → skeleton indices (reverse direction)
- A simple “remap these indices” function that does the inverse mapping
- Automatic handling of the index translation problem

**Why Python Doesn’t Have This:**  
The OpenUSD Python bindings are “thin wrappers” around the C++ code. They expose the same methods, but:

- The `GetJointMapper().Remap()` method only works in one direction (skeleton → mesh)
- There’s no built-in inverse mapper
- You have to manually build the reverse lookup yourself

### What the C++ Solution Does (Step-by-Step)

The C++ solution manually builds a “translation dictionary” by comparing joint names. Here’s how it works:

**Step 1: Get Both Lists**

```cpp
// Get the mesh's joint list: ["/Skeleton/Hand", "/Skeleton/Finger1", "/Skeleton/Finger2", ...]
skinningQuery.GetJointOrder(&meshJointOrder);

// Get the skeleton's joint list: ["/Skeleton/Root", "/Skeleton/Spine", ..., "/Skeleton/Hand", ...]
skeletonQuery.GetJointOrder(&skeletonJointOrder);

```

**Step 2: Build a Translation Dictionary**

```cpp
// For each joint in the mesh's list, find where it appears in the skeleton's list
// Example: "/Skeleton/Hand" is at position 2 in mesh list, but position 47 in skeleton list
// Store: meshToSkelMap["/Skeleton/Hand"] = 47

```

**Step 3: Translate the Indices**

```cpp
// When you have a mesh-local index like 2, look up:
// 1. What joint name is at position 2 in mesh list? → "/Skeleton/Hand"
// 2. What position is "/Skeleton/Hand" in skeleton list? → 47
// 3. Use 47 as the skeleton-space index

```

### Why C++ Works Better Here

**C++ Advantages:**

1. **Direct Access** - C++ can directly access the internal data structures
2. **Performance** - Building lookup maps is faster in C++
3. **Control** - You can build exactly the data structure you need
4. **No Limitations** - You’re not limited by what Python bindings expose

**The C++ Solution Provides:**

- A `std::unordered_map` (like a fast lookup dictionary) that maps joint names → skeleton indices
- Direct iteration over arrays without Python overhead
- Efficient memory management for large meshes
- The ability to build custom remapping logic that OpenUSD doesn’t provide

### How All the Pieces Fit Together

```auto
┌─────────────────────────────────────────────────────────────┐
│ 1. USD File (Maya Export) │
│ - Skeleton has 100 joints │
│ - Mesh only uses 15 joints │
└───────────────────────┬─────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. UsdSkelCache (The Organizer) │
│ - Populates cache with skeleton structure │
│ - Creates query objects for mesh and skeleton │
└───────────────────────┬─────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. UsdSkelSkinningQuery (Mesh Info) │
│ - GetJointOrder() → mesh's 15 joints │
│ - ComputeJointInfluences() → indices [2,5,8] (mesh-local)│
└───────────────────────┬─────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. UsdSkelSkeletonQuery (Skeleton Info) │
│ - GetJointOrder() → skeleton's 100 joints │
└───────────────────────┬─────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. C++ Remapping Logic (The Translation) │
│ - Compare joint names between lists │
│ - Build lookup map: mesh index → skeleton index │
│ - Translate [2,5,8] → [47,89,92] (skeleton-space) │
└─────────────────────────────────────────────────────────────┘

```

### The Missing Piece: Why OpenUSD Doesn’t Solve This

**OpenUSD’s Design Philosophy:**

- OpenUSD provides the **building blocks** (queries, mappers, order access)
- It doesn’t provide **every possible combination** of operations
- The inverse mapping is considered an “application-level” concern

**What’s Missing:**

- No `GetInverseJointMapper()` method
- No `RemapIndicesToSkeletonSpace()` convenience function
- No automatic detection that remapping is needed

**Why It’s Missing:**

1. **Not Common Enough** - Most applications don’t need skeleton-space indices
2. **Performance** - Building inverse maps has a cost, so it’s opt-in
3. **Flexibility** - Different applications need different remapping strategies
4. **API Bloat** - Adding every possible operation would make the API huge

### What Your Friend’s C++ Solution Achieves

**The C++ solution provides:**

1. ✅ **Complete Control** - You build exactly the remapping you need
2. ✅ **Performance** - Fast lookup using hash maps
3. ✅ **Clarity** - The code explicitly shows what’s happening
4. ✅ **Reliability** - No hidden behavior or limitations

**What Python Can’t Do:**

- ❌ Can’t efficiently build custom lookup structures
- ❌ Limited by what bindings expose
- ❌ Performance overhead for large meshes
- ❌ Less control over memory management

## Solution

The key insight is that `GetJointMapper()` maps **FROM skeleton TO mesh-local** , but you need to map **FROM mesh-local TO skeleton**. You need to build the inverse mapping by comparing joint paths. The `UsdSkelSkinningQuery::GetJointOrder()` method [[1]](#appendix-sources) provides access to the mesh-local joint order, while `UsdSkelSkeletonQuery::GetJointOrder()` [[2]](#appendix-sources) provides the skeleton’s joint order. This exact problem and solution approach has been discussed in the Alliance for OpenUSD forum [[9]](#appendix-sources), confirming the path-comparison remapping strategy.

### C++ Solution

```cpp
#include <pxr/usd/usdSkel/cache.h>
#include <pxr/usd/usdSkel/skinningQuery.h>
#include <pxr/usd/usdSkel/skeletonQuery.h>
#include <pxr/usd/usdSkel/bindingAPI.h>
#include <pxr/base/vt/array.h>
#include <pxr/base/tf/token.h>
#include <unordered_map>

UsdSkelCache skelCache;
VtArray<int> jointIndices;
VtArray<float> jointWeights;

if (UsdSkelRoot skelRoot = UsdSkelRoot::Find(prim)) {
    // Populate the cache - required before GetSkinningQuery() works
    skelCache.Populate(skelRoot, UsdTraverseInstanceProxies());
    
    // Find the Skeleton that should affect this prim
    // See UsdSkelBindingAPI::GetInheritedSkeleton() [[3]](#appendix-sources)
    UsdSkelSkeleton skel = UsdSkelBindingAPI(prim).GetInheritedSkeleton();
    
    // Get skinning query - see UsdSkelSkinningQuery class reference [[1]](#appendix-sources)
    if (UsdSkelSkinningQuery skinningQuery = skelCache.GetSkinningQuery(prim)) {
        // Get joint influences (indices are into mesh's skel:joints)
        // See UsdSkelSkinningQuery::ComputeJointInfluences() [[1]](#appendix-sources)
        skinningQuery.ComputeJointInfluences(&jointIndices, &jointWeights);
        
        // Get the skeleton query to access skeleton joint order
        UsdSkelSkeletonQuery skeletonQuery = skelCache.GetSkeletonQuery(skel);
        if (!skeletonQuery) {
            // Fallback: get skeleton directly
            skeletonQuery = skelCache.GetSkeletonQuery(skel.GetPrim());
        }
        
        if (skeletonQuery) {
            // Get mesh-local joint order (from skel:joints)
            // See UsdSkelSkinningQuery::GetJointOrder() [[1]](#appendix-sources)
            VtTokenArray meshJointOrder;
            skinningQuery.GetJointOrder(&meshJointOrder);
            
            // Get skeleton joint order
            // See UsdSkelSkeletonQuery::GetJointOrder() [[2]](#appendix-sources)
            VtTokenArray skeletonJointOrder = skeletonQuery.GetJointOrder();
            
            // Build reverse lookup: mesh joint path -> skeleton joint index
            std::unordered_map<TfToken, int, TfToken::HashFunctor> meshToSkelMap;
            for (size_t i = 0; i < meshJointOrder.size(); ++i) {
                const TfToken& meshJointPath = meshJointOrder[i];
                
                // Find this mesh joint in the skeleton's joint order
                for (size_t j = 0; j < skeletonJointOrder.size(); ++j) {
                    if (skeletonJointOrder[j] == meshJointPath) {
                        meshToSkelMap[meshJointPath] = static_cast<int>(j);
                        break;
                    }
                }
            }
            
            // Remap joint indices from mesh-local to skeleton space
            VtArray<int> remappedIndices;
            remappedIndices.reserve(jointIndices.size());
            
            for (int meshLocalIdx : jointIndices) {
                if (meshLocalIdx >= 0 && 
                    static_cast<size_t>(meshLocalIdx) < meshJointOrder.size()) {
                    const TfToken& meshJointPath = meshJointOrder[meshLocalIdx];
                    auto it = meshToSkelMap.find(meshJointPath);
                    if (it != meshToSkelMap.end()) {
                        remappedIndices.push_back(it->second);
                    } else {
                        // Joint not found in skeleton (shouldn't happen, but handle gracefully)
                        remappedIndices.push_back(-1);
                    }
                } else {
                    remappedIndices.push_back(-1);
                }
            }
            
            // Now remappedIndices contains indices into skeleton's joints array
            // Use remappedIndices instead of jointIndices
        }
    }
}

```

### Understanding the C++ Code: A Detailed Walkthrough

For non-programmers, here’s what each part of the C++ solution does:

#### **Part 1: Setting Up the Cache**

```cpp
UsdSkelCache skelCache;
if (UsdSkelRoot skelRoot = UsdSkelRoot::Find(prim)) {
    skelCache.Populate(skelRoot, UsdTraverseInstanceProxies());

```

**What this does:** Think of `UsdSkelCache` as a “phone book” that organizes all skeleton information. Before you can ask questions, you need to “populate” it by telling it to scan the USD file and build its index. This is like loading a database before you can query it.

#### **Part 2: Finding the Skeleton**

```cpp
UsdSkelSkeleton skel = UsdSkelBindingAPI(prim).GetInheritedSkeleton();

```

**What this does:** This finds which skeleton is “bound” to your mesh. Think of it as asking “which skeleton controls this mesh?” The answer might be inherited from a parent object, so `GetInheritedSkeleton()` follows the chain up to find it.

#### **Part 3: Getting Joint Influences**

```cpp
if (UsdSkelSkinningQuery skinningQuery = skelCache.GetSkinningQuery(prim)) {
    skinningQuery.ComputeJointInfluences(&jointIndices, &jointWeights);

```

**What this does:** This asks the mesh “which joints affect each vertex, and how much?” The `jointIndices` array contains numbers like `[2, 5, 8]` - but these are **mesh-local** indices (positions in the mesh’s joint list, not the skeleton’s).

#### **Part 4: Getting Both Joint Lists**

```cpp
VtTokenArray meshJointOrder;
skinningQuery.GetJointOrder(&meshJointOrder);

VtTokenArray skeletonJointOrder = skeletonQuery.GetJointOrder();

```

**What this does:** This gets the actual **names** of joints in both lists. For example:

- `meshJointOrder` might be: `["/Skeleton/Hand", "/Skeleton/Finger1", "/Skeleton/Finger2"]`
- `skeletonJointOrder` might be: `["/Skeleton/Root", "/Skeleton/Spine", ..., "/Skeleton/Hand", "/Skeleton/Finger1", ...]`

#### **Part 5: Building the Translation Dictionary**

```cpp
std::unordered_map<TfToken, int, TfToken::HashFunctor> meshToSkelMap;
for (size_t i = 0; i < meshJointOrder.size(); ++i) {
    const TfToken& meshJointPath = meshJointOrder[i];
    for (size_t j = 0; j < skeletonJointOrder.size(); ++j) {
        if (skeletonJointOrder[j] == meshJointPath) {
            meshToSkelMap[meshJointPath] = static_cast<int>(j);
            break;
        }
    }
}

```

**What this does:** This is the “translation dictionary” builder. It goes through each joint in the mesh’s list and finds where that same joint appears in the skeleton’s list.

**Example:**

- Mesh joint at position 0: `"/Skeleton/Hand"` → Found at skeleton position 47 → Store: `meshToSkelMap["/Skeleton/Hand"] = 47`
- Mesh joint at position 1: `"/Skeleton/Finger1"` → Found at skeleton position 89 → Store: `meshToSkelMap["/Skeleton/Finger1"] = 89`

The `std::unordered_map` is like a fast lookup table - you can ask “what skeleton index does `/Skeleton/Hand` map to?” and get `47` instantly.

#### **Part 6: Translating the Indices**

```cpp
VtArray<int> remappedIndices;
for (int meshLocalIdx : jointIndices) {
    const TfToken& meshJointPath = meshJointOrder[meshLocalIdx];
    auto it = meshToSkelMap.find(meshJointPath);
    if (it != meshToSkelMap.end()) {
        remappedIndices.push_back(it->second);
    }
}

```

**What this does:** This is where the actual translation happens. For each mesh-local index (like `2`):

1. **Look up the joint name:** `meshJointOrder[2]` → `"/Skeleton/Finger2"`
2. **Find in dictionary:** `meshToSkelMap["/Skeleton/Finger2"]` → `92`
3. **Store skeleton index:** Add `92` to `remappedIndices`

**Result:** `[2, 5, 8]` (mesh-local) becomes `[47, 89, 92]` (skeleton-space)

### Why This Approach Works

**The Key Insight:** Joints have **names** (like `"/Skeleton/Hand"`), and names are the same in both lists. The C++ solution uses names as the “bridge” between the two numbering systems:

```auto
Mesh Index → Joint Name → Skeleton Index
    2 → "/Hand" → 47

```

**Why Names Work:**

- Joint names are unique identifiers
- They’re the same regardless of position in the list
- They’re what OpenUSD uses internally to match things up

**The Algorithm:**

1. Get both lists (mesh joints, skeleton joints)
2. For each mesh joint, find its name in the skeleton list
3. Remember the mapping: mesh position → skeleton position
4. Use the mapping to translate indices

### Alternative C++ Solution: Using the Mapper’s Internal Index Map

Your friend discovered a more efficient approach that leverages the mapper’s internal `_indexMap` structure instead of string comparisons. This is a clever optimization:

```cpp
VtArray<int> indices;
indices.resize(jointIndices.size());
for (size_t i = 0; i < jointIndices.size(); i++)
{
    indices[i] = i;
}
// Start with identity mapping: [0, 1, 2, 3, ...] for mesh-local indices
VtArray<int> jointLut = indices;

// Build the lookup table (LUT) to map indices from the (sparse) joint array 
// in the mesh to the full joint array of the skeleton
if (skinningQuery.GetJointMapper())
{
    skinningQuery.GetJointMapper()->Remap(indices, &jointLut);
}

```

**How This Works:**

The `GetJointMapper()` internally maintains an `_indexMap` array that maps skeleton indices → mesh-local positions. When you call `Remap()` with an identity array `[0, 1, 2, ...]` representing mesh-local positions, the mapper uses its internal mapping to determine where those positions correspond in skeleton space.

**Why This Is Better:**

1. **No String Comparisons** - Avoids expensive token/path string comparisons
2. **Uses Internal Mapping** - Leverages the mapper’s pre-computed `_indexMap` structure
3. **More Efficient** - Direct array lookups instead of nested loops with string matching
4. **Matches USD Internals** - Uses the same mechanism USD uses internally for remapping

**How It Works Internally:**

The mapper’s `Remap()` method (see `animMapper.h` lines 226-237) uses `_indexMap[i]` to map source index `i` to target index. When you pass mesh-local indices `[0, 1, 2, ...]`:

- The mapper looks up where each mesh-local position maps in skeleton space
- The result `jointLut` contains skeleton indices corresponding to each mesh-local position
- You can then use `jointLut[meshLocalIdx]` to get the skeleton index

**Example:**

```cpp
// If mesh has 3 joints at skeleton positions [47, 89, 92]:
// Input: indices = [0, 1, 2] (mesh-local positions)
// Output: jointLut = [47, 89, 92] (skeleton indices)

// Then to remap jointIndices:
for (int meshIdx : jointIndices) {
    int skeletonIdx = jointLut[meshIdx]; // Direct lookup!
}

```

**Important Note:**

This solution works because the mapper’s `_indexMap` stores the forward mapping (skeleton → mesh-local), but when you pass mesh-local indices as the source, the mapper’s internal logic effectively gives you access to the inverse mapping. This is more efficient than building your own reverse lookup table with string comparisons.

**Comparison:**

| Approach | String Comparison | Mapper-Based |
| --- | --- | --- |
| **Speed** | O(n×m) string comparisons | O(n) array lookups |
| **Memory** | Hash map of tokens | Direct array access |
| **Complexity** | Manual reverse lookup | Uses mapper internals |
| **Maintenance** | More code to maintain | Leverages existing API |

**Your Friend’s Insight:**

The key insight is that `GetJointMapper()->Remap()` can be used creatively to extract the inverse mapping by passing mesh-local indices and letting the mapper’s internal `_indexMap` do the work. This avoids the need for manual string-based reverse lookup construction.

**Potential API Improvement:**

As your friend noted, OpenUSD could provide a convenience method like:

```cpp
// Proposed API (doesn't exist yet):
VtIntArray RemapIndicesToSkeletonSpace(const VtIntArray& meshLocalIndices) const;

```

This would encapsulate the mapper-based remapping logic and make it more accessible. The implementation would be similar to your friend’s solution but provided as a first-class API method, eliminating the need for users to understand the mapper’s internal structure.

### Python Solution

```python
from pxr import Usd, UsdSkel, Vt

def remap_joint_indices_to_skeleton_space(prim):
    """
    Remap joint indices from mesh-local skel:joints to skeleton joints.
    
    Args:
        prim: The skinnable prim (mesh) with UsdSkelBindingAPI
        
    Returns:
        tuple: (remapped_indices, joint_weights) or None if remapping fails
    """
    skel_cache = UsdSkel.Cache()
    
    # Find SkelRoot
    skel_root = UsdSkel.Root.Find(prim)
    if not skel_root:
        return None
    
    skel_cache.Populate(skel_root, Usd.TraverseInstanceProxies())
    
    # Get the skeleton
    # See UsdSkelBindingAPI::GetInheritedSkeleton() [[3]](#appendix-sources)
    binding_api = UsdSkel.BindingAPI(prim)
    skel = binding_api.GetInheritedSkeleton()
    if not skel:
        return None
    
    # Get skinning query - see UsdSkelSkinningQuery class reference [[1]](#appendix-sources)
    skinning_query = skel_cache.GetSkinningQuery(prim)
    if not skinning_query:
        return None
    
    # Get joint influences (indices are into mesh's skel:joints)
    # See UsdSkelSkinningQuery::ComputeJointInfluences() [[1]](#appendix-sources)
    joint_indices = Vt.IntArray()
    joint_weights = Vt.FloatArray()
    if not skinning_query.ComputeJointInfluences(joint_indices, joint_weights):
        return None
    
    # Get skeleton query - see UsdSkelSkeletonQuery class reference [[2]](#appendix-sources)
    skeleton_query = skel_cache.GetSkeletonQuery(skel)
    if not skeleton_query:
        return None
    
    # Get mesh-local joint order (from skel:joints)
    # See UsdSkelSkinningQuery::GetJointOrder() [[1]](#appendix-sources)
    mesh_joint_order = Vt.TokenArray()
    if not skinning_query.GetJointOrder(mesh_joint_order):
        # If no custom joint order, mesh uses skeleton order directly
        skeleton_joint_order = skeleton_query.GetJointOrder()
        # If mesh order matches skeleton order, no remapping needed
        return (joint_indices, joint_weights)
    
    # Get skeleton joint order
    skeleton_joint_order = skeleton_query.GetJointOrder()
    
    # Build reverse lookup: mesh joint path -> skeleton joint index
    mesh_to_skel_map = {}
    for i, mesh_joint_path in enumerate(mesh_joint_order):
        try:
            skel_idx = skeleton_joint_order.index(mesh_joint_path)
            mesh_to_skel_map[i] = skel_idx
        except ValueError:
            # Joint not found in skeleton (shouldn't happen, but handle gracefully)
            mesh_to_skel_map[i] = -1
    
    # Remap joint indices from mesh-local to skeleton space
    remapped_indices = Vt.IntArray()
    remapped_indices.reserve(len(joint_indices))
    
    for mesh_local_idx in joint_indices:
        if mesh_local_idx >= 0 and mesh_local_idx < len(mesh_joint_order):
            skel_idx = mesh_to_skel_map.get(mesh_local_idx, -1)
            remapped_indices.append(skel_idx)
        else:
            remapped_indices.append(-1)
    
    return (remapped_indices, joint_weights)

# Usage example:
stage = Usd.Stage.Open("path/to/file.usd")
mesh_prim = stage.GetPrimAtPath("/SkelRoot/Mesh")

result = remap_joint_indices_to_skeleton_space(mesh_prim)
if result:
    remapped_indices, joint_weights = result
    # Now remapped_indices contains indices into skeleton's joints array
    print(f"Remapped {len(remapped_indices)} joint indices")

```

## Summary: Why C++ Solves What Python Cannot

### Direct Answer to “Why is OpenUSD Python Missing This?”

**OpenUSD Python bindings are “thin wrappers”** - they expose C++ functions but don’t add convenience methods. The Python API has:

✅ **What Python HAS:**

- `ComputeJointInfluences()` - Gets mesh-local indices
- `GetJointOrder()` - Gets joint name lists
- `GetJointMapper()` - Gets forward mapper (skeleton → mesh)

❌ **What Python DOESN’T HAVE:**

- `RemapIndicesToSkeletonSpace()` - No inverse remapping function
- `GetInverseJointMapper()` - No reverse mapper
- Automatic index translation - No built-in solution

**Why Python Doesn’t Have It:**

1. **Not in C++ API** - The C++ API doesn’t have it either, so Python can’t expose it
2. **Application-Level Concern** - OpenUSD considers this an application problem, not a library problem
3. **Performance** - Building inverse maps costs memory/CPU, so it’s opt-in
4. **Flexibility** - Different apps need different remapping strategies

### What C++ Solves That Python Cannot

**C++ Advantages:**

1. **Custom Data Structures**

2. **Direct Array Access**

3. **Performance at Scale**

4. **Complete Control**

**What Your Friend’s C++ Code Achieves:**

```cpp
// This builds a fast lookup table that Python can't match:
std::unordered_map<TfToken, int> meshToSkelMap;
// Lookup time: O(1) - constant time, regardless of skeleton size
// Python dict: O(1) average, but more overhead per lookup

```

**The Bottom Line:**

- **Python CAN do this** - but it’s slower and less efficient
- **C++ DOES this better** - faster, more control, better for production
- **OpenUSD doesn’t provide it** - in either language, you build it yourself
- **C++ is the right choice** - when performance and control matter

### The Missing API: What Should Exist But Doesn’t

**What Would Be Ideal:**

```cpp
// This doesn't exist, but would be nice:
VtIntArray remappedIndices = skinningQuery.RemapIndicesToSkeletonSpace(jointIndices);

```

**Why It Doesn’t Exist:**

- OpenUSD philosophy: provide building blocks, not every convenience function
- Different applications need different remapping strategies
- Adding it would require maintaining inverse mapper state
- Performance concerns - not everyone needs this operation

**What Exists Instead:**

- Building blocks: `GetJointOrder()`, `GetJointMapper()`
- You build the solution: Compare lists, build map, translate indices
- This gives flexibility but requires more code

## Why GetJointMapper().Remap() Doesn’t Work Directly

The `GetJointMapper()` returns a mapper that maps **FROM skeleton joint order TO mesh-local joint order**. When you call `Remap()` on it, you’re mapping skeleton-space data to mesh-local space, which is the opposite of what you need. The UsdSkel Schemas In-Depth documentation [[5]](#appendix-sources) explains that remapping via mapper works by matching joint names, but the mapper operates in the forward direction (skeleton → mesh-local) only.

The mapper is designed for use cases like:

- Mapping skeleton joint transforms to mesh-local joint order for skinning
- Mapping animation data from skeleton order to mesh-local order

But for remapping **indices** (not transforms), you need the inverse mapping, which requires comparing the joint paths/orders directly using `GetJointOrder()` methods [[1]](#appendix-sources) [[2]](#appendix-sources). This limitation has been discussed in related forum threads [[10]](#appendix-sources) and GitHub issues [[11]](#appendix-sources), where users encountered similar challenges with explicit joint orders.

## Alternative: Using GetJointOrder() Directly

If the mapper is null (meaning mesh uses skeleton order directly), you can skip remapping:

```cpp
VtTokenArray meshJointOrder;
if (skinningQuery.GetJointOrder(&meshJointOrder)) {
    // Mesh has custom joint order, need remapping
    // ... use remapping code above ...
} else {
    // Mesh uses skeleton order directly, no remapping needed
    // jointIndices already map to skeleton joints
}

```

## Key Takeaways

1. **`GetJointMapper()` maps skeleton → mesh-local**, not the other way around (see [[5]](#appendix-sources))
2. **Two approaches to remap indices:**
  - **String-based approach:** Compare joint paths between mesh `skel:joints` and skeleton `joints` using `GetJointOrder()` methods [[1]](#appendix-sources) [[2]](#appendix-sources) - more intuitive but slower
  - **Mapper-based approach:** Use `GetJointMapper()->Remap()` with identity array to leverage internal `_indexMap` - more efficient, matches USD internals

3. **Build a reverse lookup map** from mesh joint paths to skeleton joint indices (as discussed in [[9]](#appendix-sources))
4. **Handle the case** where mapper is null (mesh uses skeleton order directly) - see `UsdSkelBindingAPI::GetJointsAttr()` behavior [[7]](#appendix-sources)

### Recommended Approach

**Use the mapper-based solution** (your friend’s approach) when:

- Performance matters (large meshes, many joints)
- You want to match USD’s internal behavior
- You’re comfortable with leveraging internal mapper structures

**Use the string-based solution** when:

- You need maximum clarity and explicitness
- You want to understand exactly what’s happening
- Performance is not critical
- You’re learning/debugging the remapping process

* * *

---

<div class="post-metadata">

### Author: ![koen](https://avatars.discourse-cdn.com/v4/letter/k/a9a28c/32.png) [@koen](https://forum.aousd.org/u/koen)
#### Post date: [January 31, 2026, 12:54pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/6 "2026-01-31T12:54:29Z")

</div>

Hello Jan,

thank you for taking the effort to investigate this. However, just speaking for myself here, I am not sure posting pages of ML generated answers here is the right way to go. I think it’s safe to assume that everyone who would like to has access to the tools to generate these kinds of essays themselves. I am afraid more interesting conversations might get lost in the wall of text you generated.

I hope that makes sense,

Thanks,

Koen

---

<div class="post-metadata">

### Author: ![Jan\_Haluszka](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/jan_haluszka/32/1054_2.png) [@Jan\_Haluszka](https://forum.aousd.org/u/Jan_Haluszka)
#### Post date: [January 31, 2026, 7:42pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/7 "2026-01-31T19:42:25Z")

</div>

Hello Koen,

That is very true… and you got a good point there.

Thx for the honest feedback.

---

<div class="post-metadata">

### Author: ![barbalt](https://avatars.discourse-cdn.com/v4/letter/b/3bc359/32.png) [@barbalt](https://forum.aousd.org/u/barbalt)
#### Post date: [February 4, 2026, 8:16pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/8 "2026-02-04T20:16:02Z")

</div>

Hi @koen your solution without the string comparison seems very similar to what has been done in maya-usd:  
[maya-usd/lib/usd/translators/skelReader.cpp at dev · Autodesk/maya-usd](https://github.com/Autodesk/maya-usd/blob/dev/lib/usd/translators/skelReader.cpp#L186-L211)

Maybe this could help give you some ideas!

---

<div class="post-metadata">

### Author: ![koen](https://avatars.discourse-cdn.com/v4/letter/k/a9a28c/32.png) [@koen](https://forum.aousd.org/u/koen)
#### Post date: [February 4, 2026, 9:25pm UTC](https://forum.aousd.org/t/computejointinfluences-for-meshes-with-only-a-subset-of-the-joint/2844/9 "2026-02-04T21:25:49Z")

</div>

thats a great resource I forgot about, thanks!
