From 4fcf99f963bfb0a8fe3b7368881bd7998ff08924 Mon Sep 17 00:00:00 2001 From: vnoves Date: Wed, 12 Nov 2025 17:21:19 -0300 Subject: [PATCH 1/2] Adjusted Logic to include extras --- Common_glTF_Exporter/Export/Draco.cs | 492 +++++++++++++++++++++++++-- 1 file changed, 472 insertions(+), 20 deletions(-) diff --git a/Common_glTF_Exporter/Export/Draco.cs b/Common_glTF_Exporter/Export/Draco.cs index acebdd9..993be25 100644 --- a/Common_glTF_Exporter/Export/Draco.cs +++ b/Common_glTF_Exporter/Export/Draco.cs @@ -1,14 +1,441 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; -using System.Windows; using Common_glTF_Exporter.Model; using Common_glTF_Exporter.Windows.MainWindow; using dracowrapper; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Common_glTF_Exporter.Export { + // ------------------------------------------------------------ + // Util GLB simple para .NET Framework (sin tuples) + // ------------------------------------------------------------ + internal sealed class GlbData + { + public string Json; + public byte[] Bin; + public GlbData(string json, byte[] bin) + { + Json = json; + Bin = bin ?? new byte[0]; + } + } + + // ------------------------------------------------------------ + // Patcher: copia EXTRAS del original → temp, mergea extensiones + // y EMBEBE imágenes en .gltf (y .glb si hiciera falta), borrando archivos externos. + // ------------------------------------------------------------ + internal static class GltfExtrasPatcher + { + public static void PatchExtras(string originalPath, string tempPath) + { + if (string.IsNullOrEmpty(originalPath) || string.IsNullOrEmpty(tempPath)) + throw new ArgumentNullException(); + + string ext = (Path.GetExtension(originalPath) ?? "").ToLowerInvariant(); + if (ext == ".gltf") + { + PatchExtrasGltf(originalPath, tempPath); + } + else if (ext == ".glb") + { + PatchExtrasGlb(originalPath, tempPath); + } + } + + // --------- .gltf (JSON externo) ---------- + private static void PatchExtrasGltf(string originalGltf, string tempGltf) + { + JObject src = JObject.Parse(File.ReadAllText(originalGltf)); + JObject dst = JObject.Parse(File.ReadAllText(tempGltf)); + + // 1) copiar EXTRAS + extensiones desconocidas en NODES + PatchArrayByIndex(src, dst, "nodes", PatchNodeLike); + + // (Opcional) si querés también en meshes/primitives: + // PatchMeshesAndPrimitives(src, dst); + + // 2) mergear extensionsUsed / extensionsRequired + MergeExtensionsUsedAndRequired(src, dst); + + // 3) EMBEBER imágenes en el .bin del .gltf (usar SIEMPRE el bin TEMP) y BORRAR archivos externos + string baseDir = Path.GetDirectoryName(tempGltf); + string tempBinFileName = Path.GetFileName(tempGltf).Replace(".gltf", ".bin"); // ej: MyModelTemp.bin + + byte[] binBytes = null; + InlineExternalImagesIntoBin_JObject(dst, baseDir, tempBinFileName, ref binBytes, false, true); + + if (binBytes != null) + { + string binPath = Path.Combine(baseDir, tempBinFileName); + File.WriteAllBytes(binPath, binBytes); + } + + File.WriteAllText(tempGltf, dst.ToString(Formatting.None)); + } + + // --------- .glb (JSON embebido) ---------- + private static void PatchExtrasGlb(string originalGlb, string tempGlb) + { + GlbData srcGlb = ReadGlb(originalGlb); + GlbData dstGlb = ReadGlb(tempGlb); + + JObject src = JObject.Parse(srcGlb.Json); + JObject dst = JObject.Parse(dstGlb.Json); + + // 1) copiar EXTRAS + extensiones desconocidas en NODES + PatchArrayByIndex(src, dst, "nodes", PatchNodeLike); + + // (Opcional) meshes/primitives: + // PatchMeshesAndPrimitives(src, dst); + + // 2) mergear extensionsUsed/Required + MergeExtensionsUsedAndRequired(src, dst); + + // 3) EMBEBER imágenes en el BIN del GLB si por algún motivo vinieran por uri + byte[] glbBin = dstGlb.Bin; + string baseDir = Path.GetDirectoryName(tempGlb); + InlineExternalImagesIntoBin_JObject(dst, baseDir, null, ref glbBin, true, true); + + string newJson = dst.ToString(Formatting.None); + WriteGlb(tempGlb, newJson, glbBin); // re-escribe GLB con JSON parcheado + } + + // ---------- LÓGICA DE PARCHE ---------- + private static void PatchNodeLike(JObject srcNode, JObject dstNode) + { + CopyExtras(srcNode, dstNode); + CopyUnknownExtensions(srcNode, dstNode, new[] { "KHR_draco_mesh_compression" }); + } + + private static void PatchMeshesAndPrimitives(JObject src, JObject dst) + { + JArray srcMeshes = src["meshes"] as JArray; + JArray dstMeshes = dst["meshes"] as JArray; + if (srcMeshes == null || dstMeshes == null) return; + + int count = Math.Min(srcMeshes.Count, dstMeshes.Count); + for (int i = 0; i < count; i++) + { + JObject s = (JObject)srcMeshes[i]; + JObject d = (JObject)dstMeshes[i]; + + CopyExtras(s, d); + CopyUnknownExtensions(s, d, new[] { "KHR_draco_mesh_compression" }); + + JArray sPrims = s["primitives"] as JArray; + JArray dPrims = d["primitives"] as JArray; + if (sPrims == null || dPrims == null) continue; + + int pc = Math.Min(sPrims.Count, dPrims.Count); + for (int j = 0; j < pc; j++) + { + JObject sp = (JObject)sPrims[j]; + JObject dp = (JObject)dPrims[j]; + + // NO tocar KHR_draco_mesh_compression del temp + CopyExtras(sp, dp); + CopyUnknownExtensions(sp, dp, new[] { "KHR_draco_mesh_compression" }); + } + } + } + + private static void PatchArrayByIndex(JObject src, JObject dst, string name, Action patchItem) + { + JArray sa = src[name] as JArray; + JArray da = dst[name] as JArray; + if (sa == null || da == null) return; + + int count = Math.Min(sa.Count, da.Count); + for (int i = 0; i < count; i++) + patchItem((JObject)sa[i], (JObject)da[i]); + } + + private static void CopyExtras(JObject src, JObject dst) + { + if (src["extras"] != null) + dst["extras"] = src["extras"].DeepClone(); + // si src no tiene extras, no tocamos los del dst + } + + private static void CopyUnknownExtensions(JObject src, JObject dst, IEnumerable keepKnown) + { + JObject sExt = src["extensions"] as JObject; + if (sExt == null) return; + + JObject dExt = dst["extensions"] as JObject; + if (dExt == null) dExt = new JObject(); + + HashSet known = new HashSet(keepKnown ?? new string[0], StringComparer.Ordinal); + + foreach (JProperty prop in sExt.Properties()) + { + // no sobreescribir Draco si ya está en el destino + if (known.Contains(prop.Name) && dExt[prop.Name] != null) continue; + + dExt[prop.Name] = prop.Value.DeepClone(); + } + + if (dExt.HasValues) + dst["extensions"] = dExt; + } + + private static void MergeExtensionsUsedAndRequired(JObject src, JObject dst) + { + // merge extensionsUsed + JArray srcUsed = src["extensionsUsed"] as JArray; + if (srcUsed != null && srcUsed.Count > 0) + { + JArray dstUsed = dst["extensionsUsed"] as JArray; + if (dstUsed == null) { dstUsed = new JArray(); dst["extensionsUsed"] = dstUsed; } + + var set = new HashSet(StringComparer.Ordinal); + foreach (var t in dstUsed) set.Add((string)t); + + foreach (var t in srcUsed) + { + string name = (string)t; + if (!set.Contains(name)) { dstUsed.Add(name); set.Add(name); } + } + } + + // merge extensionsRequired (opcional pero recomendable) + JArray srcReq = src["extensionsRequired"] as JArray; + if (srcReq != null && srcReq.Count > 0) + { + JArray dstReq = dst["extensionsRequired"] as JArray; + if (dstReq == null) { dstReq = new JArray(); dst["extensionsRequired"] = dstReq; } + + var setR = new HashSet(StringComparer.Ordinal); + foreach (var t in dstReq) setR.Add((string)t); + + foreach (var t in srcReq) + { + string name = (string)t; + if (!setR.Contains(name)) { dstReq.Add(name); setR.Add(name); } + } + } + + // asegurar Draco en extensionsUsed + EnsureExtInArray(dst, "extensionsUsed", "KHR_draco_mesh_compression"); + } + + private static void EnsureExtInArray(JObject dst, string arrayName, string ext) + { + JArray arr = dst[arrayName] as JArray; + if (arr == null) { arr = new JArray(); dst[arrayName] = arr; } + foreach (var t in arr) if (string.Equals((string)t, ext, StringComparison.Ordinal)) return; + arr.Add(ext); + } + + // ====== INLINE DE IMÁGENES EN BIN / GLB ====== + // desiredBinFileName: nombre del BIN *TEMP* para .gltf (ej. "MyModelTemp.bin"). + // removeExternalImageFiles: si true, borra los .png/.jpg que se incrustaron. + private static void InlineExternalImagesIntoBin_JObject( + JObject model, + string baseDir, + string desiredBinFileName, + ref byte[] binBytes, + bool isGlb, + bool removeExternalImageFiles) + { + var buffers = model["buffers"] as JArray; + if (buffers == null) { buffers = new JArray(); model["buffers"] = buffers; } + if (buffers.Count == 0) buffers.Add(new JObject { ["byteLength"] = 0 }); + + var bufferViews = model["bufferViews"] as JArray; + if (bufferViews == null) { bufferViews = new JArray(); model["bufferViews"] = bufferViews; } + + var buf0 = (JObject)buffers[0]; + + if (!isGlb) + { + // Fuerza a usar SIEMPRE el bin TEMP (que luego renombrás) + if (string.IsNullOrEmpty(desiredBinFileName)) desiredBinFileName = "sceneTemp.bin"; + buf0["uri"] = desiredBinFileName; + + string desiredBinPath = Path.Combine(baseDir, desiredBinFileName); + if ((binBytes == null || binBytes.Length == 0) && File.Exists(desiredBinPath)) + binBytes = File.ReadAllBytes(desiredBinPath); + } + + int appendOffset = binBytes != null ? binBytes.Length : 0; + var images = model["images"] as JArray; + var consumedFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + + if (images != null && images.Count > 0) + { + for (int i = 0; i < images.Count; i++) + { + var img = images[i] as JObject; + if (img == null) continue; + if (img["bufferView"] != null) continue; // ya embebida + + string uri = (string)img["uri"]; + if (string.IsNullOrEmpty(uri)) continue; + if (uri.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) continue; // data URI + + string imgPath = Path.Combine(baseDir, uri); + if (!File.Exists(imgPath)) continue; + + byte[] imgBytes = File.ReadAllBytes(imgPath); + string mime = MimeFromExtension(Path.GetExtension(uri)); + + int thisOffset = appendOffset; + binBytes = AppendBytes(binBytes, imgBytes, true, 0x00); + int thisLength = imgBytes.Length; + appendOffset = binBytes.Length; + + int bvIndex = bufferViews.Count; + var bv = new JObject + { + ["buffer"] = 0, + ["byteOffset"] = thisOffset, + ["byteLength"] = thisLength + }; + bufferViews.Add(bv); + + img.Remove("uri"); + img["bufferView"] = bvIndex; + if (!string.IsNullOrEmpty(mime)) img["mimeType"] = mime; + + if (removeExternalImageFiles) + consumedFiles.Add(imgPath); + } + } + + buf0["byteLength"] = binBytes != null ? binBytes.Length : 0; + + // Eliminar los archivos externos que incrustamos + if (removeExternalImageFiles && consumedFiles.Count > 0) + { + foreach (var path in consumedFiles) + { + try { if (File.Exists(path)) File.Delete(path); } catch { /* swallow */ } + } + } + } + + private static string MimeFromExtension(string ext) + { + ext = (ext ?? "").ToLowerInvariant(); + if (ext == ".png") return "image/png"; + if (ext == ".jpg" || ext == ".jpeg") return "image/jpeg"; + if (ext == ".ktx2") return "image/ktx2"; + return null; + } + + private static byte[] AppendBytes(byte[] bin, byte[] add, bool padTo4, byte padByte) + { + if (bin == null) bin = new byte[0]; + int oldLen = bin.Length; + int newLen = oldLen + (add != null ? add.Length : 0); + byte[] outArr = new byte[newLen]; + if (oldLen > 0) Buffer.BlockCopy(bin, 0, outArr, 0, oldLen); + if (add != null && add.Length > 0) Buffer.BlockCopy(add, 0, outArr, oldLen, add.Length); + + if (padTo4) + { + int mod = outArr.Length % 4; + if (mod != 0) + { + int pad = 4 - mod; + byte[] padded = new byte[outArr.Length + pad]; + Buffer.BlockCopy(outArr, 0, padded, 0, outArr.Length); + for (int i = 0; i < pad; i++) padded[outArr.Length + i] = padByte; + return padded; + } + } + return outArr; + } + + // ------------- GLB IO mínimo: JSON chunk + BIN chunk ------------- + private static GlbData ReadGlb(string path) + { + using (FileStream fs = File.OpenRead(path)) + using (BinaryReader br = new BinaryReader(fs)) + { + uint magic = br.ReadUInt32(); // 'glTF' + uint version = br.ReadUInt32(); // 2 + uint length = br.ReadUInt32(); + + // chunk 0: JSON + uint chunkLen0 = br.ReadUInt32(); + uint chunkType0 = br.ReadUInt32(); // 'JSON' + byte[] jsonBytes = br.ReadBytes((int)chunkLen0); + string json = System.Text.Encoding.UTF8.GetString(jsonBytes); + + // chunk 1 (opcional): BIN + byte[] bin = new byte[0]; + if (fs.Position + 8 <= fs.Length) + { + uint chunkLen1 = br.ReadUInt32(); + uint chunkType1 = br.ReadUInt32(); // 'BIN\0' + bin = br.ReadBytes((int)chunkLen1); + } + + return new GlbData(json, bin); + } + } + + private static void WriteGlb(string path, string json, byte[] bin) + { + byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); + jsonBytes = PadTo4(jsonBytes, 0x20); // espacios + + byte[] binBytes = (bin ?? new byte[0]); + binBytes = PadTo4(binBytes, 0x00); + + using (FileStream fs = File.Create(path)) + using (BinaryWriter bw = new BinaryWriter(fs)) + { + uint magic = 0x46546C67; // 'glTF' + uint version = 2; + uint length = 12 // header + + 8 + (uint)jsonBytes.Length + + (binBytes.Length > 0 ? (8 + (uint)binBytes.Length) : 0); + + // header + bw.Write(magic); + bw.Write(version); + bw.Write(length); + + // chunk 0: JSON + bw.Write((uint)jsonBytes.Length); + bw.Write(0x4E4F534A); // 'JSON' + bw.Write(jsonBytes); + + // chunk 1: BIN (si hay) + if (binBytes.Length > 0) + { + bw.Write((uint)binBytes.Length); + bw.Write(0x004E4942); // 'BIN\0' + bw.Write(binBytes); + } + } + } + + private static byte[] PadTo4(byte[] data, byte padByte) + { + int mod = data.Length % 4; + if (mod == 0) return data; + + int pad = 4 - mod; + byte[] outArr = new byte[data.Length + pad]; + Buffer.BlockCopy(data, 0, outArr, 0, data.Length); + for (int i = 0; i < pad; i++) + outArr[data.Length + i] = padByte; + return outArr; + } + } + + // ------------------------------------------------------------ + // Tu compresor Draco con llamada al patch antes del cleanup + // ------------------------------------------------------------ public static class Draco { public static void Compress(Preferences preferences) @@ -19,26 +446,24 @@ public static void Compress(Preferences preferences) if (preferences.format == FormatEnum.gltf) { - fileToCompress = string.Concat(preferences.path, ".gltf"); - fileToCompressTemp = string.Concat(preferences.path, "Temp.gltf"); + fileToCompress = preferences.path + ".gltf"; + fileToCompressTemp = preferences.path + "Temp.gltf"; - files.Add(string.Concat(preferences.path, ".bin")); + files.Add(preferences.path + ".bin"); files.Add(fileToCompress); } else { - fileToCompress = string.Concat(preferences.path, ".glb"); - fileToCompressTemp = string.Concat(preferences.path, "Temp.glb"); + fileToCompress = preferences.path + ".glb"; + fileToCompressTemp = preferences.path + "Temp.glb"; files.Add(fileToCompress); } #if REVIT2025 || REVIT2026 - + // En .NET Framework se compila el #else; este bloque queda para tus builds nuevos var loadContext = new NonCollectibleAssemblyLoadContext(); - string programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string assemblyPath = Path.Combine(programDataPath, "Autodesk", "ApplicationPlugins", "leia.bundle", "Contents", "2025", "DracoWrapper.dll"); - Assembly mixedModeAssembly = loadContext.LoadFromAssemblyPath(assemblyPath); var gltfDecoderType = mixedModeAssembly.GetType("dracowrapper.GltfDecoder"); @@ -61,9 +486,7 @@ public static void Compress(Preferences preferences) var gltfEncoderInstance = Activator.CreateInstance(gltfEncoderType); var encodeSceneToFileMethod = gltfEncoderType.GetMethod("EncodeSceneToFile"); encodeSceneToFileMethod.Invoke(gltfEncoderInstance, new object[] { scene, fileToCompressTemp }); - #else - var decoder = new GltfDecoder(); var res = decoder.DecodeFromFileToScene(fileToCompress); var scene = res.Value(); @@ -71,22 +494,51 @@ public static void Compress(Preferences preferences) SceneUtils.SetDracoCompressionOptions(options, scene); var encoder = new GltfEncoder(); encoder.EncodeSceneToFile(scene, fileToCompressTemp); +#endif - #endif + // --- Parchea EXTRAS de NODES + mergea extensiones + EMBEBE imágenes (y borra las externas) --- + GltfExtrasPatcher.PatchExtras(fileToCompress, fileToCompressTemp); - files.ForEach(x => File.Delete(x)); - File.Move(fileToCompressTemp, fileToCompress); + // --- Cleanup / rename final --- + foreach (var x in files) + { + try { if (File.Exists(x)) File.Delete(x); } catch { /* ignore */ } + } + File_MoveOverwrite(fileToCompressTemp, fileToCompress); if (preferences.format == FormatEnum.gltf) { - string binToReplace = fileToCompressTemp.Replace(".gltf", ".bin"); - string binFinalName = fileToCompressTemp.Replace("Temp.gltf", ".bin"); - File.Move(binToReplace, binFinalName); + string binTemp = fileToCompressTemp.Replace(".gltf", ".bin"); // ej: MyModelTemp.bin + string binFinal = fileToCompressTemp.Replace("Temp.gltf", ".bin"); // ej: MyModel.bin - string text = File.ReadAllText(fileToCompress); - text = text.Replace(Path.GetFileName(binToReplace), Path.GetFileName(binFinalName)); - File.WriteAllText(fileToCompress, text); + if (File.Exists(binTemp)) + { + File_MoveOverwrite(binTemp, binFinal); + } + + // Corrige nombre del .bin referenciado en el JSON final (Temp → final) + if (File.Exists(fileToCompress)) + { + string text = File.ReadAllText(fileToCompress); + string binTempName = Path.GetFileName(binTemp); + string binFinalName = Path.GetFileName(binFinal); + if (!string.IsNullOrEmpty(binTempName) && !string.IsNullOrEmpty(binFinalName)) + { + text = text.Replace(binTempName, binFinalName); + File.WriteAllText(fileToCompress, text); + } + } + } + } + + // Helper .NET Framework: mover reemplazando si existe + private static void File_MoveOverwrite(string src, string dst) + { + if (File.Exists(dst)) + { + try { File.Delete(dst); } catch { /* ignore */ } } + File.Move(src, dst); } } } From 8d657fc5b3283306b80c9db4d9f32deaa577e641 Mon Sep 17 00:00:00 2001 From: vnoves Date: Mon, 17 Nov 2025 15:13:37 -0300 Subject: [PATCH 2/2] Remove Comments --- Common_glTF_Exporter/Export/Draco.cs | 80 +++------------------------- 1 file changed, 6 insertions(+), 74 deletions(-) diff --git a/Common_glTF_Exporter/Export/Draco.cs b/Common_glTF_Exporter/Export/Draco.cs index 993be25..ca070db 100644 --- a/Common_glTF_Exporter/Export/Draco.cs +++ b/Common_glTF_Exporter/Export/Draco.cs @@ -11,9 +11,7 @@ namespace Common_glTF_Exporter.Export { - // ------------------------------------------------------------ - // Util GLB simple para .NET Framework (sin tuples) - // ------------------------------------------------------------ + internal sealed class GlbData { public string Json; @@ -25,10 +23,6 @@ public GlbData(string json, byte[] bin) } } - // ------------------------------------------------------------ - // Patcher: copia EXTRAS del original → temp, mergea extensiones - // y EMBEBE imágenes en .gltf (y .glb si hiciera falta), borrando archivos externos. - // ------------------------------------------------------------ internal static class GltfExtrasPatcher { public static void PatchExtras(string originalPath, string tempPath) @@ -47,22 +41,15 @@ public static void PatchExtras(string originalPath, string tempPath) } } - // --------- .gltf (JSON externo) ---------- private static void PatchExtrasGltf(string originalGltf, string tempGltf) { JObject src = JObject.Parse(File.ReadAllText(originalGltf)); JObject dst = JObject.Parse(File.ReadAllText(tempGltf)); - // 1) copiar EXTRAS + extensiones desconocidas en NODES PatchArrayByIndex(src, dst, "nodes", PatchNodeLike); - // (Opcional) si querés también en meshes/primitives: - // PatchMeshesAndPrimitives(src, dst); - - // 2) mergear extensionsUsed / extensionsRequired MergeExtensionsUsedAndRequired(src, dst); - // 3) EMBEBER imágenes en el .bin del .gltf (usar SIEMPRE el bin TEMP) y BORRAR archivos externos string baseDir = Path.GetDirectoryName(tempGltf); string tempBinFileName = Path.GetFileName(tempGltf).Replace(".gltf", ".bin"); // ej: MyModelTemp.bin @@ -78,7 +65,6 @@ private static void PatchExtrasGltf(string originalGltf, string tempGltf) File.WriteAllText(tempGltf, dst.ToString(Formatting.None)); } - // --------- .glb (JSON embebido) ---------- private static void PatchExtrasGlb(string originalGlb, string tempGlb) { GlbData srcGlb = ReadGlb(originalGlb); @@ -87,63 +73,25 @@ private static void PatchExtrasGlb(string originalGlb, string tempGlb) JObject src = JObject.Parse(srcGlb.Json); JObject dst = JObject.Parse(dstGlb.Json); - // 1) copiar EXTRAS + extensiones desconocidas en NODES PatchArrayByIndex(src, dst, "nodes", PatchNodeLike); - // (Opcional) meshes/primitives: - // PatchMeshesAndPrimitives(src, dst); - // 2) mergear extensionsUsed/Required MergeExtensionsUsedAndRequired(src, dst); - // 3) EMBEBER imágenes en el BIN del GLB si por algún motivo vinieran por uri byte[] glbBin = dstGlb.Bin; string baseDir = Path.GetDirectoryName(tempGlb); InlineExternalImagesIntoBin_JObject(dst, baseDir, null, ref glbBin, true, true); string newJson = dst.ToString(Formatting.None); - WriteGlb(tempGlb, newJson, glbBin); // re-escribe GLB con JSON parcheado + WriteGlb(tempGlb, newJson, glbBin); } - // ---------- LÓGICA DE PARCHE ---------- private static void PatchNodeLike(JObject srcNode, JObject dstNode) { CopyExtras(srcNode, dstNode); CopyUnknownExtensions(srcNode, dstNode, new[] { "KHR_draco_mesh_compression" }); } - private static void PatchMeshesAndPrimitives(JObject src, JObject dst) - { - JArray srcMeshes = src["meshes"] as JArray; - JArray dstMeshes = dst["meshes"] as JArray; - if (srcMeshes == null || dstMeshes == null) return; - - int count = Math.Min(srcMeshes.Count, dstMeshes.Count); - for (int i = 0; i < count; i++) - { - JObject s = (JObject)srcMeshes[i]; - JObject d = (JObject)dstMeshes[i]; - - CopyExtras(s, d); - CopyUnknownExtensions(s, d, new[] { "KHR_draco_mesh_compression" }); - - JArray sPrims = s["primitives"] as JArray; - JArray dPrims = d["primitives"] as JArray; - if (sPrims == null || dPrims == null) continue; - - int pc = Math.Min(sPrims.Count, dPrims.Count); - for (int j = 0; j < pc; j++) - { - JObject sp = (JObject)sPrims[j]; - JObject dp = (JObject)dPrims[j]; - - // NO tocar KHR_draco_mesh_compression del temp - CopyExtras(sp, dp); - CopyUnknownExtensions(sp, dp, new[] { "KHR_draco_mesh_compression" }); - } - } - } - private static void PatchArrayByIndex(JObject src, JObject dst, string name, Action patchItem) { JArray sa = src[name] as JArray; @@ -159,7 +107,6 @@ private static void CopyExtras(JObject src, JObject dst) { if (src["extras"] != null) dst["extras"] = src["extras"].DeepClone(); - // si src no tiene extras, no tocamos los del dst } private static void CopyUnknownExtensions(JObject src, JObject dst, IEnumerable keepKnown) @@ -174,7 +121,6 @@ private static void CopyUnknownExtensions(JObject src, JObject dst, IEnumerable< foreach (JProperty prop in sExt.Properties()) { - // no sobreescribir Draco si ya está en el destino if (known.Contains(prop.Name) && dExt[prop.Name] != null) continue; dExt[prop.Name] = prop.Value.DeepClone(); @@ -186,7 +132,6 @@ private static void CopyUnknownExtensions(JObject src, JObject dst, IEnumerable< private static void MergeExtensionsUsedAndRequired(JObject src, JObject dst) { - // merge extensionsUsed JArray srcUsed = src["extensionsUsed"] as JArray; if (srcUsed != null && srcUsed.Count > 0) { @@ -203,7 +148,6 @@ private static void MergeExtensionsUsedAndRequired(JObject src, JObject dst) } } - // merge extensionsRequired (opcional pero recomendable) JArray srcReq = src["extensionsRequired"] as JArray; if (srcReq != null && srcReq.Count > 0) { @@ -232,9 +176,6 @@ private static void EnsureExtInArray(JObject dst, string arrayName, string ext) arr.Add(ext); } - // ====== INLINE DE IMÁGENES EN BIN / GLB ====== - // desiredBinFileName: nombre del BIN *TEMP* para .gltf (ej. "MyModelTemp.bin"). - // removeExternalImageFiles: si true, borra los .png/.jpg que se incrustaron. private static void InlineExternalImagesIntoBin_JObject( JObject model, string baseDir, @@ -254,7 +195,6 @@ private static void InlineExternalImagesIntoBin_JObject( if (!isGlb) { - // Fuerza a usar SIEMPRE el bin TEMP (que luego renombrás) if (string.IsNullOrEmpty(desiredBinFileName)) desiredBinFileName = "sceneTemp.bin"; buf0["uri"] = desiredBinFileName; @@ -353,7 +293,6 @@ private static byte[] AppendBytes(byte[] bin, byte[] add, bool padTo4, byte padB return outArr; } - // ------------- GLB IO mínimo: JSON chunk + BIN chunk ------------- private static GlbData ReadGlb(string path) { using (FileStream fs = File.OpenRead(path)) @@ -432,10 +371,6 @@ private static byte[] PadTo4(byte[] data, byte padByte) return outArr; } } - - // ------------------------------------------------------------ - // Tu compresor Draco con llamada al patch antes del cleanup - // ------------------------------------------------------------ public static class Draco { public static void Compress(Preferences preferences) @@ -460,7 +395,6 @@ public static void Compress(Preferences preferences) } #if REVIT2025 || REVIT2026 - // En .NET Framework se compila el #else; este bloque queda para tus builds nuevos var loadContext = new NonCollectibleAssemblyLoadContext(); string programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); string assemblyPath = Path.Combine(programDataPath, "Autodesk", "ApplicationPlugins", "leia.bundle", "Contents", "2025", "DracoWrapper.dll"); @@ -496,10 +430,8 @@ public static void Compress(Preferences preferences) encoder.EncodeSceneToFile(scene, fileToCompressTemp); #endif - // --- Parchea EXTRAS de NODES + mergea extensiones + EMBEBE imágenes (y borra las externas) --- GltfExtrasPatcher.PatchExtras(fileToCompress, fileToCompressTemp); - // --- Cleanup / rename final --- foreach (var x in files) { try { if (File.Exists(x)) File.Delete(x); } catch { /* ignore */ } @@ -508,15 +440,15 @@ public static void Compress(Preferences preferences) if (preferences.format == FormatEnum.gltf) { - string binTemp = fileToCompressTemp.Replace(".gltf", ".bin"); // ej: MyModelTemp.bin - string binFinal = fileToCompressTemp.Replace("Temp.gltf", ".bin"); // ej: MyModel.bin + string binTemp = fileToCompressTemp.Replace(".gltf", ".bin"); + string binFinal = fileToCompressTemp.Replace("Temp.gltf", ".bin"); if (File.Exists(binTemp)) { File_MoveOverwrite(binTemp, binFinal); } - // Corrige nombre del .bin referenciado en el JSON final (Temp → final) + if (File.Exists(fileToCompress)) { string text = File.ReadAllText(fileToCompress); @@ -531,7 +463,7 @@ public static void Compress(Preferences preferences) } } - // Helper .NET Framework: mover reemplazando si existe + private static void File_MoveOverwrite(string src, string dst) { if (File.Exists(dst))