Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle conflicting URIs during merge() #677

Merged
merged 1 commit into from
Sep 8, 2022
Merged
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
59 changes: 51 additions & 8 deletions packages/cli/src/transforms/merge.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import fs from 'fs';
import { Document, FileUtils, ImageUtils, NodeIO, Transform } from '@gltf-transform/core';
import { unpartition } from '@gltf-transform/functions';
import {
Document,
FileUtils,
ImageUtils,
NodeIO,
Transform,
Texture,
Buffer,
PropertyType,
} from '@gltf-transform/core';
import { dedup, unpartition } from '@gltf-transform/functions';

const NAME = 'merge';

Expand All @@ -13,8 +22,8 @@ export interface MergeOptions {
const merge = (options: MergeOptions): Transform => {
const { paths, io } = options;

return async (doc: Document): Promise<void> => {
const logger = doc.getLogger();
return async (document: Document): Promise<void> => {
const logger = document.getLogger();

for (let i = 0; i < paths.length; i++) {
const path = paths[i];
Expand All @@ -24,21 +33,28 @@ const merge = (options: MergeOptions): Transform => {
const basename = FileUtils.basename(path);
const extension = FileUtils.extension(path).toLowerCase();
if (['png', 'jpg', 'jpeg', 'webp', 'ktx2'].includes(extension)) {
doc.createTexture(basename)
document
.createTexture(basename)
.setImage(fs.readFileSync(path))
.setMimeType(ImageUtils.extensionToMimeType(extension))
.setURI(basename + '.' + extension);
} else if (['gltf', 'glb'].includes(extension)) {
doc.merge(renameScenes(basename, await io.read(path)));
document.merge(renameScenes(basename, await io.read(path)));
} else {
throw new Error(`Unknown file extension: "${extension}".`);
}
}

doc.getRoot().setDefaultScene(doc.getRoot().listScenes()[0]);
document.getRoot().setDefaultScene(document.getRoot().listScenes()[0]);

// De-duplicate textures, then ensure that all remaining textures and buffers
// have unique URIs. See https://github.com/donmccurdy/glTF-Transform/issues/586.
await document.transform(dedup({ propertyTypes: [PropertyType.TEXTURE] }));
createUniqueURIs(document.getRoot().listBuffers());
createUniqueURIs(document.getRoot().listTextures());

if (!options.partition) {
await doc.transform(unpartition());
await document.transform(unpartition());
}

logger.debug(`${NAME}: Complete.`);
Expand All @@ -57,4 +73,31 @@ function renameScenes(name: string, document: Document): Document {
return document;
}

/** Replaces conflicting URIs to ensure all URIs are unique. */
function createUniqueURIs(resources: Buffer[] | Texture[]): void {
const total = {} as Record<string, number>;
const used = {} as Record<string, boolean>;

for (const resource of resources) {
const uri = resource.getURI();
if (!uri) continue;
if (!total[uri]) total[uri] = 0;
total[uri]++;
used[uri] = false;
}

for (const resource of resources) {
let uri = resource.getURI();
if (!uri || total[uri] === 1) continue;

const extension = FileUtils.extension(uri);
const prefix = uri.replace(new RegExp(`\\.${extension}`), '');
for (let i = 2; used[uri]; i++) {
uri = `${prefix}_${i++}.${extension}`;
}
resource.setURI(uri);
used[uri] = true;
}
}

export { merge };