initial commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
// Сведения о GLB-файле из его JSON-чанка (без загрузки геометрии).
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import type { FileRef } from '../shared/api.ts';
|
||||
import { COMFY_OUTPUT } from './config.ts';
|
||||
|
||||
interface Gltf {
|
||||
meshes?: { primitives?: { indices?: number; mode?: number; attributes?: { POSITION?: number } }[] }[];
|
||||
accessors?: { count?: number }[];
|
||||
}
|
||||
|
||||
/** Путь к файлу в ComfyUI/output или null (не output или выход за папку). */
|
||||
export function outputPath(ref: FileRef): string | null {
|
||||
if (ref.type !== 'output') return null;
|
||||
const file = path.resolve(COMFY_OUTPUT, ref.subfolder, ref.filename);
|
||||
return file.startsWith(path.resolve(COMFY_OUTPUT) + path.sep) ? file : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Тот же GLB без нормалей — для просмотра «без сглаживания»: по спецификации glTF при отсутствии NORMAL
|
||||
* просмотрщик считает плоские нормали (three.js в model-viewer включает flatShading). Геометрия не меняется,
|
||||
* переписывается только JSON-чанк.
|
||||
*/
|
||||
export function glbWithoutNormals(glb: Buffer): Buffer {
|
||||
if (glb.toString('latin1', 0, 4) !== 'glTF' || glb.toString('latin1', 16, 20) !== 'JSON') throw new Error('не GLB');
|
||||
const jsonLen = glb.readUInt32LE(12);
|
||||
const gltf = JSON.parse(glb.toString('utf8', 20, 20 + jsonLen)) as Gltf;
|
||||
for (const mesh of gltf.meshes ?? []) for (const prim of mesh.primitives ?? []) delete (prim.attributes as Record<string, unknown>)?.NORMAL;
|
||||
let json = Buffer.from(JSON.stringify(gltf), 'utf8');
|
||||
if (json.length % 4) json = Buffer.concat([json, Buffer.alloc(4 - (json.length % 4), 0x20)]); // выравнивание пробелами
|
||||
const rest = glb.subarray(20 + jsonLen); // BIN-чанк как есть
|
||||
const head = Buffer.alloc(20);
|
||||
head.write('glTF', 0, 'latin1');
|
||||
head.writeUInt32LE(2, 4);
|
||||
head.writeUInt32LE(20 + json.length + rest.length, 8);
|
||||
head.writeUInt32LE(json.length, 12);
|
||||
head.write('JSON', 16, 'latin1');
|
||||
return Buffer.concat([head, json, rest]);
|
||||
}
|
||||
|
||||
/** Число треугольников в GLB из ComfyUI/output (или null, если файла нет или формат не тот). */
|
||||
export function glbTriangles(ref: FileRef): number | null {
|
||||
const file = outputPath(ref);
|
||||
if (!file) return null;
|
||||
let fd: number | undefined;
|
||||
try {
|
||||
fd = fs.openSync(file, 'r');
|
||||
// заголовок GLB (12 байт) + заголовок первого чанка (8 байт): длина и тип 'JSON'
|
||||
const head = Buffer.alloc(20);
|
||||
fs.readSync(fd, head, 0, 20, 0);
|
||||
if (head.toString('latin1', 0, 4) !== 'glTF' || head.toString('latin1', 16, 20) !== 'JSON') return null;
|
||||
const json = Buffer.alloc(head.readUInt32LE(12));
|
||||
fs.readSync(fd, json, 0, json.length, 20);
|
||||
const gltf = JSON.parse(json.toString('utf8')) as Gltf;
|
||||
let triangles = 0;
|
||||
for (const mesh of gltf.meshes ?? []) {
|
||||
for (const prim of mesh.primitives ?? []) {
|
||||
if ((prim.mode ?? 4) !== 4) continue; // только TRIANGLES
|
||||
const acc = prim.indices ?? prim.attributes?.POSITION;
|
||||
triangles += Math.floor((gltf.accessors?.[acc ?? -1]?.count ?? 0) / 3);
|
||||
}
|
||||
}
|
||||
return triangles;
|
||||
} catch {
|
||||
return null;
|
||||
} finally {
|
||||
if (fd !== undefined) fs.closeSync(fd);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user