Skip to content

claude/bake-ao-single-hierarchy-27AwS - #103

Open
SashaRX wants to merge 57 commits into
mainfrom
claude/bake-ao-single-hierarchy-27AwS
Open

claude/bake-ao-single-hierarchy-27AwS#103
SashaRX wants to merge 57 commits into
mainfrom
claude/bake-ao-single-hierarchy-27AwS

Conversation

@SashaRX

@SashaRX SashaRX commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Bake AO across all active MeshRenderer descendants of a selected root
GameObject as one batch, without requiring a LODGroup. Apply writes
only the configured vertex AO channel — other mesh data is untouched.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F

@SashaRX

SashaRX commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2af5e5c191

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/Tools/VertexAOTool.cs Outdated
@SashaRX

SashaRX commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: befefd27ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/Tools/VertexColorBakingTool.cs
Comment thread Editor/Tools/VertexAOTool.cs Outdated
@SashaRX

SashaRX commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df9e50587f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/Tools/VertexColorBakingTool.cs
Comment thread Editor/Tools/LightmapTransferTool.cs Outdated
@SashaRX

SashaRX commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a44e2ae45

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/Tools/UvPackHierarchyTool.cs
Comment thread Editor/Tools/VertexAOTool.cs Outdated
@SashaRX

SashaRX commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e015e71b5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/BuildValidator.cs
Comment thread Editor/LodGroupUtility.cs
Comment thread Editor/Tools/VertexColorBakingTool.cs
@SashaRX
SashaRX force-pushed the claude/bake-ao-single-hierarchy-27AwS branch from 1e4f903 to 8dde2fe Compare April 24, 2026 14:49
claude added 19 commits April 24, 2026 19:42
Bake AO across all active MeshRenderer descendants of a selected root
GameObject as one batch, without requiring a LODGroup. Apply writes
only the configured vertex AO channel — other mesh data is untouched.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Rebuilding the MeshEntry list on every IMGUI pass allocates a fresh
renderer array and new MeshEntry per descendant, causing GC churn on
larger hierarchies. Cache by selection + mode and only rebuild when
they change.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
- Add per-renderer include checkbox list (reuses MeshEntry.include).
- Add "Rebake Selected" button: rebakes one hierarchy mesh with current
  UI settings, merges into existing bakedRaw/FaceArea dictionaries so
  other meshes' results are preserved.
- Add per-FBX overwrite picker after Apply: lists each unique source
  FBX referenced by the hierarchy, user checks which to rewrite.
- Add LightmapTransferTool.ExportVertexColorsToFbx(path, entries)
  overload that bypasses ResolveFbxPath and walks the supplied entry
  list — enables per-FBX batching from the Vertex AO tool.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
- Restore BuildLodBatches call in ExecuteBake; prior refactor dropped
  the batches variable and broke compilation (CS0103).
- Preserve per-renderer include flags across RefreshHierarchyEntries
  by snapshotting them by renderer instance ID. Previously, any
  selection change or refresh reset user exclusions to true.
- Drop unconditional RefreshHierarchyEntries calls in ExecuteBake /
  LoadFromMesh — OnDrawSidebar's IfNeeded path keeps the list fresh
  within the same OnGUI pass and won't clobber user edits.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
A hierarchy with empty MeshFilters (vertexCount == 0) tripped
ComputeBuffer construction in the GPU path (count must be > 0),
causing the whole GPU bake to fail and fall back to CPU.

- Filter empty meshes at hierarchy collection time.
- Defensive filter of zero-vertex targets/occluders in the GPU
  Prepare step to protect LODGroup / standalone paths too.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Clicking a mesh label in the hierarchy list pings the renderer in the
Hierarchy window. Clicking an FBX label in the overwrite picker pings
the asset in the Project window. Checkbox behaviour is unchanged.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
The UV2 sidecar postprocessor (Uv2AssetPostprocessor.OnPostprocessModel)
has a dedicated early-return for FBX overwrite flows — toggled by adding
the path to fbxOverwritePaths — but ExportVertexColorsToFbxCore never
registered the path. When persistent mode was on and an _uv2data.asset
sidecar existed (common after prior UV2 transfer), the postprocessor
rebuilt the mesh from stale sidecar data after ExportObjects, nuking any
fresh AO written into UV channels (e.g. UV2 X visible as "not saved"
after reimport).

Register sourceFbxPath in fbxOverwritePaths right before ExportObjects
so the postprocessor consumes the flag on the triggered reimport and
logs "Skipped sidecar … (FBX overwrite — UV2 already in file)".

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
CopyVertexDataToClone stopped at the first matching cloneMf per scene
entry. When an FBX contains multiple MeshFilters that share a single
mesh sub-asset (common in hierarchy-mode instancing), every scene
entry matched the SAME cloneMf. Repeated scene iterations overwrote
the same clone while the other sibling cloneMfs kept the original
un-modified sub-asset, so only one renderer ended up with AO after
reimport.

Invert the loop: index scene entries by mesh name once, then walk
every cloneMf and apply its matching entry's colors/UV. Each cloneMf
is visited exactly once, and instanced FBX parts all receive the AO
data.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
- Invalidate hierarchyEntries cache on EditorApplication.hierarchyChanged.
  Adding/removing/reparenting children under the selected root now
  prompts a refresh on the next OnGUI pass; previously stale renderers
  could be baked or fresh ones missed until the user re-selected the
  root. (Codex P2)

- Match scene entries to cloneMf by FBX sub-asset InstanceID first,
  falling back to mesh name. Prevents two distinct sub-assets that
  coincidentally share a name from being conflated during FBX
  overwrite, and also tolerates working-copy entries whose mesh
  identity has diverged from the FBX sub-asset. (Codex P2)

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Two symptoms in hierarchy mode:
1) Colliders found only when sitting as direct children of the selected
   root. MeshHygieneUtility.FindCollisionObjects scans two levels deep
   (legacy LODGroup convention) so deeper nests (Market/Props/_COL_*)
   were invisible to AO.
2) Range test relied on a combined-bbox check plus per-target pivot
   points. Candidates close to an individual target bbox but far from
   the combined centroid could fall through. Effective radius was also
   inflated by the encapsulated bounds, so spread-out target sets
   produced absurd radii that pulled in unrelated geometry while still
   missing nearby siblings of a single target.

Fixes, all local to VertexAOTool:
- New CollectCollisionDescendants recurses the full subtree using
  strict IsCollisionNodeName (suffix). Wired into
  CollectLiveCollisionOccluders.
- ComputeTargetAnchors also returns per-target world bounds.
- IsWithinOccluderRange adds a per-target bbox-vs-candidate-bbox check
  between the combined and pivot tests, so any single target bbox
  passing the radius accepts the candidate.
- Effective radius now scales the largest single-target extent rather
  than the encapsulated extent, keeping the neighbourhood tight and
  proportional to real mesh size.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Log the CopyVertexDataToClone decision tree so we can see whether the
clone mesh actually received UV data, or why it was skipped. Counts:
cloneMf visited, matched, actual UV writes, total updates.

Also surface warnings when UV copy is skipped because of vertex count
mismatch or missing scene UV data — both are silent today and would
let 'Vertex data (N updates)' report zero UV writes with a
colors-only fallback.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Fix "FBX overwrite drops AO":
  ApplyToMesh writes AO into the native buffer of the in-memory FBX
  sub-asset Mesh. ExportVertexColorsToFbxCore Phase 1 can call
  SaveAndReimport to fix up importer settings (weld/compression/
  generateSecondaryUV/isReadable). That reimport resets the native
  buffer from disk, wiping the UV/colors the user just applied.
  Diagnostic was showing visited=1, matched=1, uv2Writes=0 without
  warning — because GetUVs(2) returned an empty list on the reimported
  mesh even though the managed reference was still valid.

  Snapshot colors32/colors and the target UV channel per scene-entry
  at the start of ExportVertexColorsToFbxCore (keyed by sub-asset
  name, which is stable across reimport). CopyVertexDataToClone now
  sources from the snapshot instead of re-reading the live scene
  mesh, so Phase 1 reimport no longer loses data.

Expand Nearby occluder search to the whole scene root in Hierarchy
Mode. Previously the search was rooted at the user-selected
GameObject — a single-mesh selection had no siblings so nearby=0
even with sparse geometry around in the level. Walk transform.root
instead; target/alternate-LOD filtering plus the per-target bbox
range test keep the candidate set to actually-nearby geometry.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
When "Use collision meshes as occluders" is on, pre-walk collider
names to collect their stripped group keys (UvToolContext.
ExtractGroupKey strips _COL/_LOD suffixes). Renderers whose group
key matches a collider are skipped — the collider takes its place
in the BVH as a low-poly stand-in. Renderers without a matching
collider still contribute, so coverage is not lost.

Avoids doubling BVH geometry and the self-occlusion artifacts
from stacking detailed renderer + low-poly collider over the same
object.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Match rule: a collider node whose name ends with "_COL" (case-
insensitive) replaces the renderer with the same prefix. No more
ExtractGroupKey (which also strips _LOD and _Hull variants) — the
rule is exact and predictable, matching the user's convention.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
FBX convention used by the user puts the "_COL" suffix on the mesh
asset (MeshFilter.sharedMesh.name or MeshCollider.sharedMesh.name),
not on the GameObject name. Previous detection walked node names
via IsCollisionNodeName and so found nothing in these projects.

New CollectCollisionMeshCandidates iterates MeshColliders first,
then MeshFilters, yielding (GO, mesh) pairs where mesh.name ends
with "_COL". Deduped per (GO, mesh). CollectLiveCollisionOccluders
and the covered-keys pre-walk both consume it; renderer skip
compares against mesh.name (not renderer.name).

Drops unused TryGetCollisionMesh helper.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
CollectCollisionMeshCandidates already dedups per (GO, mesh), so the
secondary seen-set keyed by mesh.GetInstanceID() was dropping
instanced colliders — 10 GOs pointing the same _COL sub-asset at
different world positions would collapse to 1 occluder and the
other 9 placements would be missing from the BVH.

Remove the mesh-ID filter; per-GO iteration preserves each
placement with its own transform.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Toggle width 16 left no room for the IMGUI checkbox widget +
margin: the label rendered right on top of the checkbox and the
first letter was partially hidden. Widen toggle column to 22 and
add a 4px gap before the clickable label.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
SyncFbxOverwriteMap previously collected paths from every entry
under hierarchyRoot, so the FBX picker showed unrelated FBX files
that the user had unchecked in the Meshes list (or never planned
to bake). Filter by e.include and re-sync each paint so toggling
Meshes checkboxes refreshes the FBX list immediately.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Hierarchy-mode "Overwrite Selected FBX" is meant to update vertex
channels on existing FBX sub-assets without restructuring. The
shared NormalizeExportHierarchy step is destructive in this path:
- it walks every MeshFilter under tempRoot and bakes non-identity
  transforms into mesh vertices,
- in hierarchy export only the matched cloneMfs hold cloned meshes;
  unmatched cloneMfs still reference the live FBX sub-assets,
- so BakeTransformIntoMesh mutates the SHARED asset's vertices,
  visibly displacing every scene MeshFilter using it AND freezing
  one instance's transform into the exported FBX (broken on next
  reimport).

Add a normalizeHierarchy flag to ExportVertexColorsToFbxCore. The
zero-arg LOD path keeps normalization (true). The hierarchy
overload passes false — preserves FBX structure, leaves shared
asset meshes untouched, only the AO data we wrote into clones is
exported.

Reported by user: baking AO on a single ceiling mesh in
Basement_01.fbx (which has 82 sub-meshes referenced by many
scene renderers) corrupted the surrounding furniture transforms.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
@SashaRX

SashaRX commented Apr 27, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6a9905a7d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/Tools/VariantExportPipeline.cs Outdated
Comment thread Editor/Tools/LightmapTransferTool.cs Outdated
claude added 2 commits April 28, 2026 17:28
NormalizeExportHierarchy collected every direct child matching _LOD<N>$
into one flat list and renamed them all to <rootName>_LOD<i>. Hierarchies
with two or more LOD chains under one root (e.g. Ruins3th_Floor with
both Props_Ruins_LOD0..2 and SovietBuilding_BaseAtlas_LOD0..2 children)
were collapsed into a single Ruins3th_Floor_LOD0..5 chain — group
prefixes lost and unrelated meshes merged into one LOD sequence after
re-import.

Switch the regex to capture the per-child prefix and bucket children
by that prefix, so each LOD chain is normalised independently and its
distinct prefix is retained.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
The Overwrite FBX (Vertex Colors) entry point called
ExportVertexColorsToFbxCore with normalizeHierarchy: true, but the
confirmation dialog promises "Only vertex colors will be updated. UV2
and mesh topology stay unchanged." Normalization renames LOD children
and bakes node transforms into mesh vertices — both violate that
promise and broke users who relied on stable mesh names across runs.

Pass false instead. The two other call sites (hierarchy-mode export
and variant pipeline) already pass false.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
@SashaRX

SashaRX commented Apr 28, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a5bc09e007

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Editor/Tools/VariantExportPipeline.cs Outdated
Comment thread Editor/Tools/LightmapTransferTool.cs Outdated
claude and others added 11 commits April 28, 2026 19:04
- LightmapTransferTool.ExportVertexColorsToFbxCore: remove the overwrite
  marker by targetFbxPath, not sourceFbxPath. The marker is added with
  targetFbxPath, and variant export writes to a different output, so a
  failure would leave the wrong path in fbxOverwritePaths and skip
  sidecar UV2 application on the next normal import of that target.

- VariantExportPipeline.ExportVariants: drop the
  StartAssetEditing/StopAssetEditing wrapper around the batch loop.
  Each ExportSingleVariant calls AssetDatabase.ImportAsset and
  ModelImporter.SaveAndReimport, both of which are deferred while
  asset editing is paused, so BuildPrefabClone could load the fresh
  output FBX before its sub-meshes existed and fail with "contains
  no meshes". Single trailing Refresh keeps the project view in sync.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
…109)

* Add MeshLabArtifactValidator and document external MeshLab settings

Detects three classes of artifacts left by the desktop MeshLab tool when
processing FBX before Unity import:
- Collapsed UV seams (Merge Close / Remove Duplicate Vertices)
- Recalculated normals (UseExistingNormals OFF on FBX export)
- Broken or wiped UV1 parameterization (Flat Plane filter)

Exposed via Tools/Mesh Lab/Validators/Check Imported MeshLab Artifacts.
Operates on selected GameObject, Mesh asset, or imported FBX. Read-only;
logs through UvtLog.

EXPERIMENTS.md gets a new section listing the safe MeshLab filter and
export settings, mapped to the symptoms the validator emits.

* Preserve mesh.name in variant FBX export pipeline

Object.Instantiate(resultMesh) produced clones named "Original(Clone)"
which Unity FBX Exporter writes with empty/auto-generated mesh node
names; the FBX SDK then defaults the FbxMesh node to the scene name.
On reimport every mesh asset shows up as "Scene", erasing per-LOD and
per-renderer identifiers needed by sidecar lookup, scene relinking and
the new MeshLabArtifactValidator report.

Mirrors the existing fix at CopyVertexDataToClone (in-place overwrite
path): assign mesh.name = ResolveExportMeshName(...) before handing the
mesh to ExportObjects.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Source-of-truth document for FBX authoring + tooling. Each rule maps
to a patch_fbx_materials report category or a Unity import warning,
with notes on UnityMeshLab / TS_ plugin / desktop MeshLab compatibility.
Source-of-truth document for FBX authoring + tooling. Each rule maps
to a patch_fbx_materials report category or a Unity import warning,
with notes on UnityMeshLab / TS_ plugin / desktop MeshLab compatibility.

Co-authored-by: Claude <noreply@anthropic.com>
New FbxExportIntent flags enum + ExportIsolatedChannelsToFbx public
entry on LightmapTransferTool. Caller declares which per-vertex
channels (UV0-UV7, VertexColors, Normals, Tangents) the re-save is
allowed to write; everything else — node names, hierarchy,
transforms, materials, untouched per-vertex channels — is inherited
from the source FBX via clone-and-snapshot.

Mirrors the discipline of ExportVertexColorsToFbxCore (importer
prep, .meta backup/restore, Uv2 postprocessor coordination, scene
relink, working-copy restore) but never runs NormalizeExportHierarchy
or material trim. Existing ExportVertexColorsToFbx* and ExportFbx
paths untouched.
New §12 — every FBX-write path goes through the single isolated-
export core; per-tool intent recipe; hard rules ("rework, not
parallel"). Sets the contract that follow-up consolidation commits
implement.
ExportFbxIsolatedCore (renamed from ExportIsolatedChannelsToFbxCore)
is now the only FBX-write path. ExportVertexColorsToFbxCore is a
~10-line shim that computes FbxExportIntent from its legacy
"AO target UV channel" arg and delegates to the core. The 3 public
vcolor wrappers (ExportVertexColorsToFbx*) keep their signatures —
external callers (VariantExportPipeline / VertexColorBakingTool /
UvPackHierarchyTool) are unaffected.

Hierarchy / Materials gating moved inside the core (gated on intent
bits), removing the parallel snapshot/copy machinery
(VertexDataSnapshot + CopyVertexDataToClone, ~280 LoC) that
duplicated the new IsolatedExportSnapshot path. The narrow vcolor
intent (VertexColors + maybe one UV) preserves prior behavior; the
core can now also handle Hierarchy + Materials when the upcoming
ExportFbx LOD-pipeline refactor wires them in.

Net -219 LoC. No public signature changes.
ExportFbxIsolatedCore now writes to <target>.fbx.tmp first, verifies
the file is non-empty, then atomic File.Replace into the source
path (with a temp .fbx.bak rollback window). On exporter failure or
zero-byte output, the original FBX on disk is untouched and the
tmp file is reaped. Prior behavior overwrote the source directly,
leaving a corrupt FBX + stale .meta when the exporter mid-faulted.

Pre-export preflight scans the cloned hierarchy for FBX-pipeline-
checklist violations: empty/invalid node names (§5.5/§8), generic
mesh names like Scene/Geometry (§5.5), placeholder materials
Lit/Default/null (§1.5), vertex colors outside [0,1] when intent
overwrites VertexColors (§4.2), negative-determinant accumulated
scale (§7.8). All findings are logged via UvtLog.Warn and never
block the export — the atomic write provides the rollback safety,
preflight provides observability.

Both apply to every caller automatically (vcolor shim, isolated-
channel public API, future LOD-pipeline refactor) — there is no
parallel path to maintain.
ExportFbx(bool) is now a back-compat shim onto ExportFbx(bool,
FbxExportIntent) defaulting to FbxExportIntent.All — every existing
call site (UI buttons, ExportFbxPublic, UvToolHub keyboard
shortcut, PrefabBuilderTool) keeps current behavior.

When intent is narrow (no Hierarchy and no LodGroup bits) ExportFbx
groups entries by source FBX path and delegates each group to
ExportFbxIsolatedCore — atomic write, preflight, no Normalize-
ExportHierarchy / collision injection / material trim. New tool
callers can now pass `FbxExportIntent.UV2`, `VertexColors`, etc.
through ExportFbxPublic and get isolation guarantees end-to-end.

Wide intent (Hierarchy or LodGroup set) continues through the
unchanged LOD-rebuild pipeline. Migrating that path to atomic
write + folding mesh-name preservation into the snapshot logic is
queued as a follow-up — out of scope for this commit to keep the
diff reviewable. The wide path is not "another system"; it's the
Hierarchy-bit branch of the same intent-driven entry point.
Brings in the FbxExportIntent API + atomic FBX overwrite + isolated-
channel export core (5 code commits on top of the docs-only #110
that was already squash-merged). Required before migrating
PrefabBuilderTool.ExecBuildSave to the narrow intent path.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F

# Conflicts:
#	Documentation~/FBX_PIPELINE_CHECKLIST.md
PrefabBuilderTool.ExecBuildSave called LightmapTransferTool.ExportFbxPublic
without an intent, which defaulted to FbxExportIntent.All — the wide
LOD-rebuild path that mutates hierarchy, materials and the LODGroup
component on every save, even when the build session only renamed a
node or generated LODs.

- Add buildIntent bitfield on the tool, accumulated from the three
  Build pipeline mutations (ApplyPendingNames -> Hierarchy;
  NormalizeHierarchy -> Hierarchy | LodGroup | Normals | Tangents
  because NormalizeChildScales rewrites vertex positions; ExecBuild-
  GenerateLods -> Hierarchy | LodGroup | AnyUv | VertexColors |
  Normals | Tangents because new sub-meshes carry fresh channels).
- Reset on OnRefresh and after each save.
- Fall back to FbxExportIntent.All when nothing was tracked, since
  Save can be hit after edits performed by other tools (UV2 transfer,
  vertex color baking) that PrefabBuilder doesn't observe.

Also extend FbxExportIntent enum with Hierarchy / Materials / Collision
/ LodGroup flags. ExportFbx already references them in its narrow-vs-
wide dispatch (LightmapTransferTool.cs:2238 and 1911-1931); without
the additions in the enum the merged code did not compile.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
@SashaRX

SashaRX commented May 3, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b953f1534

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1470 to +1473
var intent = FbxExportIntent.VertexColors;
int aoUvIdx = ResolveLegacyAoUvChannel(uvChannelOverride);
if (aoUvIdx >= 0 && aoUvIdx <= 7)
intent |= (FbxExportIntent)(1 << aoUvIdx);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Export UV1 hierarchy updates without forcing vertex colors

ExportVertexColorsToFbxCore always seeds the export intent with FbxExportIntent.VertexColors, then optionally adds a UV channel. That means callers that only want UV1 updates (for example UvPackHierarchyTool passing uvChannelOverride: 1) still rewrite vertex colors on the FBX. In projects where scene mesh colors differ from the source FBX, this silently clobbers authored vertex colors despite the UI/message saying only UV1 is updated.

Useful? React with 👍 / 👎.

Comment thread Editor/Tools/LightmapTransferTool.cs Outdated
Comment on lines +1871 to +1878
if (intent.TouchesPerVertex())
{
if (srcImporter.weldVertices)
{ srcImporter.weldVertices = false; needsReimport = true; }
if (srcImporter.meshCompression != ModelImporterMeshCompression.Off)
{ srcImporter.meshCompression = ModelImporterMeshCompression.Off; needsReimport = true; }
if (srcImporter.meshOptimizationFlags != 0)
{ srcImporter.meshOptimizationFlags = 0; needsReimport = true; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not permanently clear importer optimizations on color-only export

This block disables weldVertices, meshCompression, and meshOptimizationFlags for any per-vertex intent, including VertexColors-only exports. Phase 5 only restores isReadable, so a plain vertex-color overwrite now leaves those importer settings permanently changed. That is a regression in behavior for color-only export and can unexpectedly increase vertex count/memory or alter downstream import characteristics after one export action.

Useful? React with 👍 / 👎.

- UvPackHierarchyTool: stop routing UV1-only pack through
  ExportVertexColorsToFbx. The vcolor wrapper unconditionally seeds
  FbxExportIntent.VertexColors, so the UV1 atlas pack also rewrote
  authored vertex colors in the source FBX. Switch to the explicit
  ExportIsolatedChannelsToFbx(path, entries, FbxExportIntent.UV1)
  entry point so only UV1 is touched.

- LightmapTransferTool.ExportFbxIsolatedCore: restore weldVertices,
  meshCompression, meshOptimizationFlags and generateSecondaryUV in
  Phase 5 if Phase 1 disabled them. Previously only isReadable was
  restored, so a single per-vertex isolated re-save (e.g. vertex
  color overwrite) permanently flipped these importer toggles on
  the user's source FBX, silently changing downstream import
  behaviour after one export click.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
claude added 2 commits May 3, 2026 20:56
ModelImporter.meshOptimizationFlags is typed MeshOptimizationFlags
(enum) — newer Unity versions reject the implicit two-way conversion
to/from int. Type the snapshot variable as the enum so the snapshot
and restore assignments compile cleanly.

https://claude.ai/code/session_01Cm3FDtRJqnEyDT2qaJUfkT
When a [MenuItem] action shares its path with a child submenu (e.g.
"Tools/Mesh Lab" alongside "Tools/Mesh Lab/Validators/..."), Unity
treats the parent path as a submenu only and silently drops the
parent action. The Open Mesh Lab entry was therefore invisible —
hovering Tools/Mesh Lab showed nothing but the Validators submenu.

Move the window opener under the same namespace ("Tools/Mesh Lab/
Open Mesh Lab") so the parent stays a submenu and both entries are
visible.

https://claude.ai/code/session_01Cm3FDtRJqnEyDT2qaJUfkT
@SashaRX

SashaRX commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

ctx.LodGroup.SetLODs(lods);

P2 Badge Record the LODGroup before merge rewrites LODs

When merging same-material renderers, this SetLODs call changes the LODGroup renderer arrays but the undo group only records the first MeshFilter and the destroyed GameObjects. If the user undoes the merge, the deleted renderers can come back while the LODGroup still points at the merged renderer list, leaving restored objects absent from LOD switching. Record ctx.LodGroup before mutating the LOD array, and persist prefab-instance modifications when applicable.


ctx.LodGroup.SetLODs(lods);

P2 Badge Persist LOD moves on prefab instances

Moving a renderer between LOD slots mutates the LODGroup array directly after Undo.RecordObject, but this path bypasses LodGroupUtility.ApplyLods and never calls PrefabUtility.RecordPrefabInstancePropertyModifications. On a prefab instance, the move can appear to work in the current scene but be lost when the scene reloads or the prefab is reapplied. Route this through the shared LOD utility or record prefab-instance modifications after SetLODs.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +93 to +94
RestoreScene();
DestroyPackedMeshes();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve packed UV1 results on selection refresh

When the selected root is just a container, ExecutePack changes Selection.activeGameObject to the first packed child so the canvas refreshes; UvToolHub.OnSelectionChange then calls ActiveTool.OnRefresh, and this refresh immediately restores/destroys all packed meshes. In that normal hierarchy-mode flow, packedMeshes is cleared before the user can Apply or Overwrite, so Pack UV1 appears to do nothing. Gate this cleanup when the new selection is still under hierarchyRoot, or avoid destructive cleanup for the tool’s own selection change.

Useful? React with 👍 / 👎.

Comment thread Editor/Tools/VertexColorBakingTool.cs Outdated
return;
}

string sourceFbxPath = ctx?.SourceFbxPath;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve variant source FBX from hierarchy entries

In Hierarchy Mode, the tool builds hierarchyEntries from the selected root even when the shared context has no current FBX path for that container selection. Bake & Export All then uses only ctx.SourceFbxPath, so selecting a prefab/container root either fails with “No source FBX path” or can reuse a stale path from a previous selection instead of the FBX represented by the included hierarchy entries. Resolve the source path from the included entries in hierarchy mode, and reject or group multi-FBX selections explicitly.

Useful? React with 👍 / 👎.

Comment on lines +1904 to +1905
Uv2AssetPostprocessor.bypassPaths.Add(sourceFbxPath);
srcImporter.SaveAndReimport();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Build isolated snapshots after importer prep

For FBXs whose importer settings change vertex count or ordering, this SaveAndReimport runs after the snapshots were already captured from the previous imported meshes. The clone loaded after importer prep can then have a different vertex count, so CopyIsolatedSnapshotsToClone skips the mesh as a mismatch and isolated UV/vertex-color exports report no matching updates. Rebuild the per-mesh snapshots after the importer settings are locked, or avoid reimporting between snapshot and clone.

Useful? React with 👍 / 👎.

Comment thread Editor/Tools/LightmapTransferTool.cs Outdated
Comment on lines +2087 to +2088
if (restoreGenerateSecondaryUV)
{ srcImporter.generateSecondaryUV = true; needsRestoreReimport = true; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid re-enabling generated UVs before validation

When the original importer had Generate Lightmap UVs enabled and the isolated export writes Unity UV channel 1, restoring generateSecondaryUV and immediately reimporting causes Unity to regenerate that channel and replace the UV data that was just written to the FBX. The export file may contain the packed UVs, but the imported mesh users see right after the tool runs is clobbered. Keep this setting disabled for channel-1 exports, or reapply the exported data after the final restore import.

Useful? React with 👍 / 👎.

Comment thread Editor/Tools/LightmapTransferTool.cs Outdated
Comment on lines +2087 to +2088
if (restoreGenerateSecondaryUV)
{ srcImporter.generateSecondaryUV = true; needsRestoreReimport = true; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid re-enabling generated UVs before final import

When the original importer had Generate Lightmap UVs enabled and the isolated export writes Unity UV channel 1, restoring generateSecondaryUV and immediately reimporting causes Unity to regenerate that channel and replace the UV data that was just written to the FBX. The export file may contain the packed UVs, but the imported mesh users see right after the tool runs is clobbered. Keep this setting disabled for channel-1 exports, or reapply the exported data after the final restore import.

Useful? React with 👍 / 👎.

Comment on lines +82 to +84
var positions = mesh.vertices;
var normals = mesh.normals;
var triangles = mesh.triangles;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip non-readable meshes in artifact validator

When the selected imported mesh has Read/Write disabled, Validate reads the CPU vertex and triangle buffers before checking readability, which Unity throws for common FBX imports. Running Tools/Mesh Lab/Validators/Check Imported MeshLab Artifacts on those assets now aborts instead of producing a report, so check mesh.isReadable before accessing these arrays or return a non-readable warning.

Useful? React with 👍 / 👎.

out string outFbxPath, out string outPrefabPath, out string err))
return Fail(variant.suffix, variant.color, err);

int painted = BakeSolidColorOnEntries(entries, variant.color);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore source colors after variant export

Batch variant export paints the shared source meshes in-place before each variant export and never restores their original vertex colors. After Bake & Export All, the scene/imported meshes are left with the last variant’s solid color, and later exports can start from that contaminated state; snapshot and restore colors/colors32 around the batch or apply colors only to temporary export clones.

Useful? React with 👍 / 👎.

claude added 2 commits July 18, 2026 20:17
Seven contained fixes from the latest Codex pass:

- LightmapTransferTool isolated core: stop restoring generateSecondaryUV
  in Phase 5. It is only ever disabled when the intent writes UV1, so
  re-enabling + reimporting regenerated channel 1 and clobbered the UV1
  just authored into the FBX. Authored UV1 must win, so the setting now
  stays off. (Regression from the prior importer-restore fix.)

- MeshLabArtifactValidator: guard mesh.isReadable before touching CPU
  buffers (vertices/normals/triangles/GetUVs). Non-readable imported
  meshes threw and aborted the whole validation menu; now reported as a
  "not readable" warning.

- PrefabBuilderTool merge: Undo.RecordObject(LodGroup) before rewriting
  its renderer arrays and RecordPrefabInstancePropertyModifications
  after SetLODs. Undo previously restored the merged GameObjects while
  the LODGroup still pointed at the merged list, dropping them from LOD
  switching.

- PrefabBuilderTool move-between-LODs: persist the LOD-array change on
  prefab instances via RecordPrefabInstancePropertyModifications so the
  move survives scene reload / prefab reapply.

- UvPackHierarchyTool.OnRefresh: skip the destructive packed-mesh
  cleanup when the new selection is still under hierarchyRoot. ExecutePack
  re-selects a packed child to refresh the canvas, and that selection
  change was wiping packed results before the user could Apply/Overwrite.

- VertexColorBakingTool variant export: in hierarchy mode resolve the
  source FBX from the included entries (ctx.SourceFbxPath can be empty
  or stale for container selections) and reject multi-FBX selections.

- VariantExportPipeline: snapshot and restore vertex colors around the
  batch so shared source/working meshes aren't left wearing the last
  variant's solid color.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
ExportFbxIsolatedCore captured per-mesh snapshots from the tool's
working mesh (built from the current import) but then disabled
weldVertices / meshCompression / meshOptimizationFlags and reimported
the source FBX before cloning it. weldVertices is ON by default, so for
typical imports the reimported clone had a different vertex count than
the snapshot, and CopyIsolatedSnapshotsToClone silently skipped every
mesh on the mismatch — isolated UV1 / vertex-color exports reported "no
matching updates" and wrote nothing.

Remove the weld/compression/optimization pre-export reimport from the
isolated path:

- The snapshot source (e.g. the packed UV1 clone in UvPackHierarchyTool,
  or the AO-baked working copy) shares its vertex layout with the
  CURRENT import; the clone is also loaded from the current import, so
  they match when we don't reimport.
- The wide LOD-rebuild path (ExportFbx) never did this reimport and
  works, so it's the correct precedent.
- Those settings were restored right after export anyway, so the final
  re-imported FBX kept the user's original weld/optimization state
  regardless. The pre-export reimport therefore never affected the
  exported result — it only desynced the clone from the snapshot.

isReadable handling stays (layout-safe) and generateSecondaryUV stays
disabled for UV1 intents (so the authored UV1 isn't regenerated). The
remaining, rare case where an FBX still re-imports at a different vertex
count (Generate Lightmap UVs splitting vertices for a UV1 export) now
logs an actionable warning instead of a bare mismatch line.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
SashaRX pushed a commit that referenced this pull request Jul 19, 2026
Consolidates the 3 bake-ao commits not yet in the prefab-builder branch
(bb8df3b Codex-review batch, 68fb1cd menu submenu, dab24f8 CS0266 fix)
into the #111 rework so the prefab-builder branch carries all of #103.

All files auto-merged except Editor/Tools/PrefabBuilderTool.cs, which the
rework rewrote. Resolved by keeping the rewrite and re-porting bb8df3b's
Undo/prefab-safety fixes into the surviving FixMerge path:
  - Undo.RecordObject(ctx.LodGroup, "Merge") before the renderer-array
    rewrite (so undo restores the LODGroup's renderer list, not just the
    GameObjects).
  - RecordPrefabInstancePropertyModifications after SetLODs (so the merge
    persists on prefab instances across scene reload / reapply).
bb8df3b's move-between-LODs hunk has no target — that path was removed in
the rework ("reorder removed"), so nothing to port there.

UNVERIFIED: no Unity compile in this environment. Staging branch for the
#111 consolidation — compile in Unity before finalizing.
claude and others added 2 commits July 19, 2026 01:14
Resolve conflicts between the PrefabBuilder / FbxExportIntent line and
main's transfer-pipeline / UvProgress work (1.0.3–1.0.8):

- LightmapTransferTool.cs: keep HEAD's meshName resolution at the loop
  top; add main's TangentValidator.EnforceTangentsMatchOriginal call
  (dropped main's duplicate meshName re-declaration).
- LodGenerationTool.cs: keep HEAD's refactor that delegates to
  LodPipelineOps.Generate (which PrefabBuilder also uses). Main's inline
  additions (CompactLodArray, MeshEntry registration, ClearAllCaches)
  already live in LodPipelineOps.
- LodPipelineOps.cs: port main's non-modal cancelable UvProgress into
  Generate (replacing the modal EditorUtility.DisplayProgressBar) so
  main's progress UX isn't lost by taking HEAD's LOD-gen side.
- VertexColorBakingTool.cs: keep HEAD's BuildSettingsFromUI() + color-
  space log; fold main's new binaryHit field into the helper (it was
  missing there, so the toggle was silently ignored on HEAD).
- CHANGELOG.md / EXPERIMENTS.md: union both sides' entries.

https://claude.ai/code/session_01CkRooSFA5QW3Kox9AAFe3F
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants