Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/rig-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const LIMB_GROUPS = Object.freeze([
// `schema` maps onto the avatar-manifest `skeleton` enum
// (packages/avatar-schema/schema/avatar.v1.json), so the generated manifest
// declares the right value instead of defaulting everything to `custom`.
const CONVENTIONS = [
export const CONVENTIONS = [
{
id: 'rpm', label: 'Ready Player Me', schema: 'rpm',
test: (ctx) => ctx.meshNames.some((n) => /^Wolf3D_/i.test(n)) && ctx.joints.some((n) => /Hips$/i.test(n)),
Expand Down
25 changes: 25 additions & 0 deletions tests/rig-report.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
MIN_CANONICAL_BONES,
CANONICAL_TOTAL,
LIMB_GROUPS,
CONVENTIONS,
} from '../src/rig-report.js';

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
Expand All @@ -29,6 +30,17 @@ function report(name) {
return analyzeGlb(load(name), { fileName: `${name}.glb` });
}

function documentedRigConventions() {
const doc = readFileSync(join(ROOT, 'docs/rig-doctor.md'), 'utf8');
const rows = [];
for (const match of doc.matchAll(/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*`([^`]+)`\s*\|$/gm)) {
const convention = match[1].trim();
if (convention === 'Convention' || /^-+$/.test(convention)) continue;
rows.push({ convention, detectedBy: match[2].trim(), schema: match[3].trim() });
}
return rows;
}

describe('readGlbJson', () => {
it('rejects a file that is not a GLB with a message a creator can act on', () => {
const notGlb = new TextEncoder().encode('this is plainly not a binary glTF file');
Expand All @@ -55,6 +67,19 @@ describe('readGlbJson', () => {
describe('detectConvention fingerprints', () => {
const ctx = (joints, meshNames = []) => ({ joints, meshNames, json: {} });

it('keeps documented rig conventions in sync with the detector', () => {
const implemented = CONVENTIONS.filter((c) => c.id !== 'unknown');
const docs = documentedRigConventions();
const implementedLabels = implemented.map((c) => c.label);
const documentedLabels = docs.map((row) => row.convention);

expect(documentedLabels).toEqual(implementedLabels);
for (const convention of implemented) {
expect(convention.evidence, `${convention.label} evidence`).toEqual(expect.any(String));
expect(convention.evidence.trim(), `${convention.label} evidence`).not.toBe('');
}
});

it('identifies an MMD rig from its Japanese PMX bone names', () => {
const c = detectConvention(ctx(['センター', '上半身', '左腕', '左ひじ', '右ひざ']));
expect(c.id).toBe('mmd');
Expand Down