Skip to content

NDMF pass: SimplifyBatch throws InvalidOperationException when an entry's mesh has isReadable=false, failing the whole avatar build #39

Description

@Toxic-Cookie

日本語要約: NDMFパス実行時、isReadable == false のメッシュが Entries に含まれていると MeshSimplifier.SimplifyBatchInvalidOperationException を投げ、例外がパスの外へ伝播してビルド全体が失敗します (VRChat SDK の "Preprocess Callback Failed" モーダル)。VRCFury でリターゲットされたメッシュにおいて断続的に発生しました。該当エントリを警告付きでスキップするか、読み取り可能なコピーを作成する対応をご検討いただけないでしょうか。

Environment

  • Meshia.MeshSimplification 3.2.0 (VPM)
  • Unity 2022.3.22f1, VRChat Avatars SDK 3.10.4, NDMF (Modular Avatar 1.17.1), VRCFury 1.1353.0
  • Component: Meshia Cascading Avatar Mesh Simplifier, consumed at build time via the NDMF plugin

What happens

During an avatar build, if any renderer entry's mesh reports isReadable == false at the time the NDMF Optimizing pass runs, SimplifyBatch throws:

InvalidOperationException: Not allowed to access vertex data on mesh 'RETARGETED__Shats' at array index 18 (isReadable is false; Read/Write must be enabled in import settings)
  at Meshia.MeshSimplification.MeshSimplifier.SimplifyBatch (...) MeshSimplifier.cs:146
  at Meshia.MeshSimplification.Ndmf.Editor.NdmfPlugin+<>c.<Configure>b__2_1 (...) NdmfPlugin.cs:80

The exception escapes the "Simplify meshes" pass, NDMF's optimize hook reports failure, and the VRChat SDK aborts the build with a blocking "Preprocess Callback Failed" modal. One unlucky mesh therefore costs the user the entire upload rather than just that mesh's simplification.

Reproducibility notes

In our case the mesh was a VRCFury-retargeted clone (RETARGETED__*), and the failure was intermittent: the identical avatar built successfully three other times the same day with bit-identical triangle counts, so the clone's readability appears to be timing/ordering dependent within the build. A deterministic repro is any entry whose mesh has Read/Write disabled and no editor-side CPU copy when the pass runs.

This is a different trigger from #33 (entry invalidated by prior mesh deletion) but the same failure shape: entry state that changes between configuration and the Optimizing pass turns into a build-failing exception instead of a skip.

Suggestion

In the NDMF pass entry loop (NdmfPlugin.cs, where entry.IsValid(...) and entry.Enabled are already checked), treat a non-readable mesh as skippable: log a warning naming the renderer and continue, or fall back to simplifying a readable copy of the mesh. Skipping seems safest, since a runtime-created mesh whose CPU copy was discarded cannot be read at all.

For reference, we currently work around this downstream by checking every entry's shared mesh just before the pass and setting Enabled = false on non-readable ones, which restores the build; a fix in SimplifyBatch/NdmfPlugin would cover all consumers.

Thank you for the great tool!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions