Autodesk / Autodesk/3dsmax-usd

[EMSUSD-3926] Exporting skinned mesh with hidden bones creates mismatched skel:joints list and invalid jointIndices

Open
#39 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C++
Stars
94
Forks
6
Avg merge
6d 20h
Merged PRs (30d)
1

Description

**Describe the bug**

When exporting a mesh skinned to hidden bones, joint weights indices are still exported but the joints attribute do not have the hidden bones, making the indices invalid.

In the example below:

- In the skeleton chain `BoneA`, `BoneA/BoneB` and `BoneA/BoneC`...
- ...`BoneA` does not skin any vertex
- ...`BoneB` and `BoneC` skin different vertices
- ...`BoneB` and `BoneC` are hidden

Image

When exporting to USD **without the `Hidden Objects` options**:

Image

then the exported USD does not export hidden bones, but still exports joints attributes with invalid indices.

Image

**Steps to reproduce**
Steps to reproduce the behavior:
1. Open the attached scene
2. Export USD
3. Do not select the `Hidden Objects`
4. Export

**Expected behavior**

Not sure what the right behavior should be:
1. still export `skel:joints` even if hidden
2. or export `primvars:skel:*` as an empty list `[]` to be consistent with the `skel:joints = []`

**Attachments**

Script to recreate the scene:

```
/*
recreate_issue39_scene.ms

Rebuilds the scene from GitHub issue Autodesk/3dsmax-usd#39:
a skinned mesh whose *deforming* bones are hidden.

Hierarchy that gets created:

SkelRootNode (Point helper, visible)
|-- BoneA (bone, VISIBLE, NOT skinned)
| |-- BoneB (bone, HIDDEN, skins the left/red half)
| +-- BoneC (bone, HIDDEN, skins the right/white half)
+-- SkinMesh (Editable Mesh + Skin bound to BoneB, BoneC)

The point of the repro: BoneB and BoneC are hidden but drive the Skin
modifier. Exporting with "Hidden Objects" OFF used to drop them from
skel:joints while jointIndices still referenced them (issue #39).

Run: MAXScript > Run Script, or drag into a viewport.
*/

(
resetMaxFile #noPrompt
units.SystemType = #Centimeters

-- --- geometry constants --------------------------------------------------
local meshLen = 120.0 -- length along X
local meshWidth = 24.0 -- width along Y
local meshThick = 1.5 -- thickness along Z
local widthSegs = 24 -- segments along X so the weight gradient is smooth
local halfLen = meshLen / 2.0

-- --- root helper -----------------------------------------------------------
local skelRoot = Point name:"SkelRootNode" pos:[0,0,0] \
size:30 box:true cross:true axistripod:false centermarker:false wirecolor:(color 180 180 180)

-- --- bones -----------------------------------------------------------------
-- BoneA: vertical, pointing straight down from the mesh centre. Visible,
-- acts as the parent / root joint but is NOT part of the skin.
local boneA = BoneSys.createBone [0,0,0] [0,0,-45] [0,1,0]
boneA.name = "BoneA"
boneA.width = 12.0
boneA.height = 12.0
boneA.wirecolor = color 226 200 96

-- BoneB: runs from centre out to the left (-X). Skins the left (red) half.
local boneB = BoneSys.createBone [0,0,0] [-halfLen,0,0] [0,0,1]
boneB.name = "BoneB"
boneB.width = 8.0
boneB.height = 8.0

-- BoneC: runs from centre out to the right (+X). Skins the right (white) half.
local boneC = BoneSys.createBone [0,0,0] [halfLen,0,0] [0,0,1]
boneC.name = "BoneC"
boneC.width = 8.0
boneC.height = 8.0

-- --- parenting (world transforms are preserved) ---------------------------
boneA.parent = skelRoot
boneB.parent = boneA
boneC.parent = boneA

-- --- mesh ------------------------------------------------------------------
-- Long thin strip centred on the origin, extended along X.
local skinMesh = Box name:"SkinMesh" length:meshWidth width:meshLen height:meshThick \
lengthsegs:1 widthsegs:widthSegs heightsegs:1 pos:[0,0,(-meshThick/2.0)]
convertToMesh skinMesh -- collapse to "Editable Mesh"
skinMesh.parent = skelRoot

-- --- skin ------------------------------------------------------------------
local skinMod = Skin()
addModifier skinMesh skinMod

-- Add ONLY the deforming bones (BoneB, BoneC). BoneA is intentionally left out.
select skinMesh
max modify mode
modPanel.setCurrentObject skinMod node:skinMesh
skinOps.addBone skinMod boneB 1 -- bone index 1
skinOps.addBone skinMod boneC 1 -- bone index 2

-- Paint an explicit red->white gradient along X:
-- left end (x = -halfLen) -> fully BoneB
-- right end (x = +halfLen) -> fully BoneC
local numVerts = skinMesh.mesh.numverts
for v = 1 to numVerts do
(
local p = getVert skinMesh.mesh v -- object space ~= world (mesh at origin)
local t = (p.x + halfLen) / meshLen -- 0 at left, 1 at right
t = amin (amax t 0.0) 1.0
local wB = 1.0 - t
local wC = t
skinOps.replaceVertexWeights skinMod v #(1, 2) #(wB, wC) node:skinMesh
)

-- --- visibility: hide the two deforming bones -----------------------------
boneB.isHidden = true
boneC.isHidden = true
-- BoneA, SkinMesh, SkelRootNode stay visible.

-- --- leave it looking like the screenshot ---------------------------------
select skinMesh
max modify mode
modPanel.setCurrentObject skinMod node:skinMesh
try ( subobjectLevel = 1 ) catch () -- enter Skin > Edit Envelopes (cosmetic)

format "Issue #39 repro scene created: SkelRootNode / BoneA / (hidden BoneB, BoneC) / SkinMesh\n"
)
```

**Specs (if applicable):**
- OS & version : Microsoft Windows 11 Entreprise 10.0.26100 Build 26100
- 3ds Max version: 2024.2.11 Security Fix, 26.0 - 26.2.11.20199
- USD for 3ds Max 2024: 0.15.0.14

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by running the provided recreate_issue39_scene.ms script in 3ds Max and exporting USD with Hidden Objects disabled. Inspect the exported skel:joints and primvars:skel:* attributes for SkinMesh, then trace the USD export path that gathers hidden bones and joint indices. Done means the exported joints and indices remain valid and consistent for the reproduced scene.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
computer-graphics
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.