feat(web): T-WEB-005 模型上传 + VRM 优先 + 版本识别
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { mkdtempSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { inspectVrm } from '../src/models/vrm-inspector';
|
||||
|
||||
const dir = mkdtempSync(join(tmpdir(), 'opendance-vrm-'));
|
||||
|
||||
/** 构造合法 GLB:magic + version + length + JSON chunk */
|
||||
function buildGlb(json: unknown): Buffer {
|
||||
const jsonBuf = Buffer.from(JSON.stringify(json), 'utf8');
|
||||
const pad = (4 - (jsonBuf.length % 4)) % 4;
|
||||
const padded = Buffer.concat([jsonBuf, Buffer.alloc(pad, 0x20)]);
|
||||
const header = Buffer.alloc(12);
|
||||
header.write('glTF', 0, 'ascii');
|
||||
header.writeUInt32LE(2, 4);
|
||||
header.writeUInt32LE(12 + 8 + padded.length, 8);
|
||||
const chunkHeader = Buffer.alloc(8);
|
||||
chunkHeader.writeUInt32LE(padded.length, 0);
|
||||
chunkHeader.writeUInt32LE(0x4e4f534a, 4);
|
||||
return Buffer.concat([header, chunkHeader, padded]);
|
||||
}
|
||||
|
||||
function write(name: string, content: Buffer): string {
|
||||
const path = join(dir, name);
|
||||
writeFileSync(path, content);
|
||||
return path;
|
||||
}
|
||||
|
||||
describe('VRM 版本识别', () => {
|
||||
it('识别 VRM 1.0(VRMC_vrm 扩展)', async () => {
|
||||
const path = write('v1.vrm', buildGlb({ extensions: { VRMC_vrm: { specVersion: '1.0' } } }));
|
||||
const result = await inspectVrm(path);
|
||||
expect(result.isGlb).toBe(true);
|
||||
expect(result.vrmVersion).toBe('1.0');
|
||||
});
|
||||
|
||||
it('识别 VRM 0.x(VRM 扩展)', async () => {
|
||||
const path = write('v0.vrm', buildGlb({ extensions: { VRM: { specVersion: '0.0', exporterVersion: '0.79' } } }));
|
||||
const result = await inspectVrm(path);
|
||||
expect(result.vrmVersion).toBe('0.0');
|
||||
});
|
||||
|
||||
it('VRM 扩展缺失时版本为 null(普通 GLB)', async () => {
|
||||
const path = write('plain.glb', buildGlb({ asset: { version: '2.0' }, meshes: [] }));
|
||||
const result = await inspectVrm(path);
|
||||
expect(result.isGlb).toBe(true);
|
||||
expect(result.vrmVersion).toBeNull();
|
||||
expect(result.generator).toBeNull();
|
||||
});
|
||||
|
||||
it('文本 glTF 不按 GLB 处理', async () => {
|
||||
const path = write('scene.gltf', Buffer.from('{"asset":{"version":"2.0"}}', 'utf8'));
|
||||
const result = await inspectVrm(path);
|
||||
expect(result.isGlb).toBe(false);
|
||||
expect(result.vrmVersion).toBeNull();
|
||||
});
|
||||
|
||||
it('文件过小时报错', async () => {
|
||||
const path = write('tiny.glb', Buffer.from('glTF', 'ascii'));
|
||||
await expect(inspectVrm(path)).rejects.toThrow(/过小/);
|
||||
});
|
||||
|
||||
it('首 chunk 非 JSON 时报错', async () => {
|
||||
const header = Buffer.alloc(12);
|
||||
header.write('glTF', 0, 'ascii');
|
||||
header.writeUInt32LE(2, 4);
|
||||
header.writeUInt32LE(32, 8);
|
||||
const chunk = Buffer.alloc(8);
|
||||
chunk.writeUInt32LE(4, 0);
|
||||
chunk.writeUInt32LE(0x004e4942, 4); // 'BIN\0'
|
||||
await expect(inspectVrm(write('bin-first.glb', Buffer.concat([header, chunk, Buffer.alloc(4)])))).rejects.toThrow(
|
||||
/JSON/,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user