From 3edcd5b7ee954fc6c74b21fdc3f8940707c50945 Mon Sep 17 00:00:00 2001 From: SashaRX Date: Thu, 6 Aug 2026 13:52:43 +0200 Subject: [PATCH] Preserve optimized vertex colors during replay --- Editor/Tools/LightmapTransferTool.cs | 2 ++ Editor/Uv2AssetPostprocessor.cs | 14 ++++++++++---- Editor/Uv2DataAsset.cs | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Editor/Tools/LightmapTransferTool.cs b/Editor/Tools/LightmapTransferTool.cs index 4e680229..c580063e 100644 --- a/Editor/Tools/LightmapTransferTool.cs +++ b/Editor/Tools/LightmapTransferTool.cs @@ -2621,6 +2621,7 @@ bool TryBuildSidecarEntry(MeshEntry entry, Mesh resultMesh, out MeshUv2Entry sid return false; var positions = sidecarMesh.vertices; + var colors = sidecarMesh.colors32; var uv0List = new List(); (entry.originalMesh ?? resultMesh).GetUVs(0, uv0List); @@ -2637,6 +2638,7 @@ bool TryBuildSidecarEntry(MeshEntry entry, Mesh resultMesh, out MeshUv2Entry sid edgeWelded = entry.wasEdgeWelded, vertPositions = positions, vertUv0 = uv0List.ToArray(), + optimizedColors = colors.Length == sidecarMesh.vertexCount ? colors : null, schemaVersion = Uv2DataAsset.CurrentSchemaVersion, toolVersion = Uv2DataAsset.ToolVersionStr, sourceFingerprint = fp, diff --git a/Editor/Uv2AssetPostprocessor.cs b/Editor/Uv2AssetPostprocessor.cs index e79c45ca..a16f9af6 100644 --- a/Editor/Uv2AssetPostprocessor.cs +++ b/Editor/Uv2AssetPostprocessor.cs @@ -723,7 +723,7 @@ static bool ReplayOptimization(Mesh mesh, MeshUv2Entry entry, bool stale = false // ── Build optimized arrays via remap ── // If ground-truth vertex data is available, use it DIRECTLY for positions/normals/tangents. // This completely bypasses the remap for geometry, eliminating all remap-related vertex errors. - // The remap is only used for UV channels and colors (which aren't stored in ground truth). + // The remap is only used for UV channels and as a fallback for legacy color data. bool hasGroundTruth = entry.optimizedPositions != null && entry.optimizedPositions.Length == optCount; var optPos = new Vector3[optCount]; @@ -750,13 +750,19 @@ static bool ReplayOptimization(Mesh mesh, MeshUv2Entry entry, bool stale = false System.Array.Copy(entry.optimizedNormals, optNormals, optCount); if (optTangents != null && entry.optimizedTangents != null && entry.optimizedTangents.Length == optCount) System.Array.Copy(entry.optimizedTangents, optTangents, optCount); - - // UV channels, colors — from remap (UV0 is modified by weld, must come from raw FBX) + if (optColors != null && entry.optimizedColors != null && entry.optimizedColors.Length == optCount) + System.Array.Copy(entry.optimizedColors, optColors, optCount); + + // UV channels and legacy colors — from remap (UV0 is modified by weld, + // so it must come from the raw FBX). Optimized colors remain authoritative + // because merged and orphan vertices cannot always be reconstructed by remap. + bool remapColors = optColors != null && + (entry.optimizedColors == null || entry.optimizedColors.Length != optCount); for (int i = 0; i < rawCount; i++) { int dst = remap[i]; if (dst < 0) continue; - if (optColors != null) optColors[dst] = rawColors[i]; + if (remapColors) optColors[dst] = rawColors[i]; for (int ch = 0; ch < 8; ch++) { if (ch == 1 || optUvs[ch] == null) continue; diff --git a/Editor/Uv2DataAsset.cs b/Editor/Uv2DataAsset.cs index dab5eb79..17aafab9 100644 --- a/Editor/Uv2DataAsset.cs +++ b/Editor/Uv2DataAsset.cs @@ -175,6 +175,8 @@ public class MeshUv2Entry public Vector3[] optimizedNormals; /// All vertex tangents from the optimized mesh at Apply time. public Vector4[] optimizedTangents; + /// All vertex colors from the optimized mesh at Apply time. + public Color32[] optimizedColors; // ── Shell descriptors (v0.14.0+) ── /// Stable shell descriptors for the target mesh UV0 shells. @@ -418,6 +420,7 @@ static void CopyEntryFields(MeshUv2Entry src, MeshUv2Entry dst) dst.optimizedPositions = src.optimizedPositions; dst.optimizedNormals = src.optimizedNormals; dst.optimizedTangents = src.optimizedTangents; + dst.optimizedColors = src.optimizedColors; dst.shellDescriptors = src.shellDescriptors; dst.vertexToSourceShellDescriptor = src.vertexToSourceShellDescriptor; dst.targetShellToSourceShellDescriptor = src.targetShellToSourceShellDescriptor;