Moriy
October 23, 2025, 8:55am
1
Hi~
I have a babylon project A and a vue project B. I write a script to load a glb file then compress model then export glb file with GLTF2Export.
When B use npm link to refer A, script export glb file correctly.
When B use npm install to refer A, script only export Transform Node to glb file.
It trouble me for long time, how should I do to fix it?
Moriy
October 23, 2025, 8:59am
2
This is export script
SceneCombineHelper.combine is in here, I think it may be not important.
import { MeshUtils } from '@/Demo/Utils/MeshUtils';
import * as BABYLON from 'babylonjs';
import { SceneCombineMaterial } from './SceneCombineMaterial';
export class SceneCombineHelper {
static metaKey: string[] = [
"SceneCombine_Root",
"SceneCombine_MeshIndex",
"SceneCombine_MeshCount",
"SceneCombine_MaterialData",
]
static combine(asset: BABYLON.AssetContainer) {
const root = asset.meshes[0];
const scene = asset.scene;
const newRoot = new BABYLON.TransformNode("SceneRoot", scene);
asset.transformNodes.push(newRoot);
newRoot.addChild(root);
newRoot.metadata = {
SceneCombine_Root: true,
This file has been truncated. show original
import { LoadAssetContainerAsync, Mesh, VertexData } from "babylonjs";
import { GLTF2Export, GLTFData } from 'babylonjs-serializers';
import { CameraViewScene } from "./CameraViewScene";
import { SceneCombineHelper } from "@/ExtTools/SceneCombine/Combine/SceneCombineHelper";
import { ImageMimeType } from "babylonjs-gltf2interface";
export class CompressViewScene extends CameraViewScene {
loadReadyStatus: { [key: string]: boolean; } = {};
GetMimeType(fileName: string): string | undefined {
if (fileName.endsWith(".glb")) {
return "model/gltf-binary";
} else if (fileName.endsWith(".bin")) {
return "application/octet-stream";
} else if (fileName.endsWith(".gltf")) {
return "model/gltf+json";
} else if (fileName.endsWith(".jpeg") || fileName.endsWith(".jpg")) {
return ImageMimeType.JPEG;
} else if (fileName.endsWith(".png")) {
return ImageMimeType.PNG;
} else if (fileName.endsWith(".webp")) {
return ImageMimeType.WEBP;
}
return undefined;
}
load(url: string, extension: string, gltfDataCallback?: (data: GLTFData) => void) {
return new Promise<string | null>((rs, rj) => {
LoadAssetContainerAsync(url, this.scene, {
pluginExtension: extension
}).then((container) => {
console.log("Model compress 加载完成");
container.addAllToScene();
console.log("Model compress 即将进行合并");
const root = SceneCombineHelper.combine(container);
console.log("Model compress 合并完成");
try {
this.scene.onReadyObservable.add(() => {
console.log("Scene Ready");
const meshes = root.getChildMeshes(false);
console.log("Mesh Count:", meshes.length);
meshes.forEach(absMesh => {
const mesh = absMesh as Mesh;
if (mesh._isMesh) {
const VD = VertexData.ExtractFromMesh(mesh);
if (VD.positions) {
console.log("Mesh Name:", mesh.name, VD.positions.length / 3);
}
else {
console.log("No Position Mesh Name:", mesh.name);
}
}
})
const metaKeys = [...SceneCombineHelper.metaKey];
GLTF2Export.GLBAsync(this.scene, "exportFile", {
exportUnusedUVs: true,
removeNoopRootNodes: false,
shouldExportNode(node) {
if (node === root) return true;
let parent = node.parent;
while (parent) {
if (parent === root) return true;
parent = parent.parent;
}
return false;
},
metadataSelector(metadata) {
if (!metadata) return;
const newMeta = {} as any;
metaKeys.forEach(key => {
if (metadata[key] !== undefined) {
newMeta[key] = metadata[key];
}
})
return newMeta;
},
}).then(async (data) => {
data.downloadFiles();
});
});
} catch (error) {
rj(error);
}
});
})
}
}
Moriy
October 23, 2025, 9:00am
3
ExportGLB.zip (195.8 KB) GLBFile
Moriy
October 23, 2025, 9:02am
5
Rollup config
import typescript from "@rollup/plugin-typescript";
import { babel } from "@rollup/plugin-babel";
import { dts } from "rollup-plugin-dts";
import { resolve } from "path";
import { copyFileSync, existsSync, mkdirSync } from "fs";
// 自定义插件用于复制 WASM 文件
function copyWasmFiles() {
return {
name: 'copy-wasm-files',
generateBundle() {
const wasmDir = resolve('./dist/wasm');
if (!existsSync(wasmDir)) {
mkdirSync(wasmDir, { recursive: true });
}
// 复制 WASM 文件
const files = ['web-ifc.wasm', 'web-ifc-mt.wasm', 'web-ifc-node.wasm'];
files.forEach(file => {
const src = resolve(`./node_modules/web-ifc/${file}`);
const dest = resolve(`./dist/wasm/${file}`);
if (existsSync(src)) {
copyFileSync(src, dest);
console.log(`Copied ${file} to dist/wasm/`);
} else {
console.warn(`Warning: ${file} not found in node_modules/web-ifc/`);
}
});
}
};
}
export default [
{
input: "./src/index.ts",
output: [
{
file: "./dist/kiwi3d.mjs",
format: "esm",
},
{
file: "./dist/kiwi3d.cjs",
format: "cjs",
exports: "named",
},
],
plugins: [
typescript({ tsconfig: "./tsconfig.json" }),
babel({
exclude: "node_modules/**",
// 使用打包内联的 helper,避免对 @babel/runtime 的外部依赖
babelHelpers: "bundled",
}),
copyWasmFiles()
],
},
{
input: "./dist/types/index.d.ts",
output: [{ file: "dist/kiwi3d.d.ts", format: "es" }],
plugins: [
dts(),
],
},
];
Moriy
October 23, 2025, 9:13am
6
Summon the God of GLTF2Export !!!
bghgary
October 23, 2025, 11:48pm
7
@alexchuber I guess that’s you
sebavan
October 24, 2025, 12:11am
8
the only diff between npm link and install is the version you are using are you sure you are relying on the exact same commit ?
Moriy
October 24, 2025, 1:25am
9
Yes, I check lots of times. I read the code of serializers, I think packages\dev\serializers\src\glTF\2.0\Extensions\index.ts is failed to be imported when npm install. I move all file in packages\dev\serializers\src\glTF into my project, it exports glb file correctly when npm install finally.
This feels like a UMD bundling issue, but I’m also not a bundler expert, so this is all just a guess
Both package A and package B are using the same UMD packages (babylonjs, babylonjs-serializers). When you use npm link , both Project A and Project B resolve to the same babylonjs instance. However, when you use npm install , Project A has its own copy of babylonjs bundled within it, and Project B has another copy. This creates two separate instances of BABYLON.
The issue might manifest in the glTF exporter because:
It uses many instanceof checks and internal type tracking
Objects created with one instance of Babylon aren’t recognized properly by another instance
The glTF exporter likely fails to recognize meshes created by Project A’s Babylon instance as valid meshes
I found some discussion about this sort of problem here :
That’s a reason to not use a bundled dependencies in your libraries. I always explicitly specify
import pkg from './package.json'
// ....
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
]
for my cjs and es targets.
So maybe something to try is 1. declaring babylonjs packages as peerDependencies in the package.json, then 2. doing some magic with externals in the bundler config.
2 Likes