I want to combine different glb files to one to dynamically combine different 3d rooms into one. I’m doing this on server side with the Nullengine. If I want to export the scene the result is empty { glTFFiles: { 'Test.glb': {} } }. If I execute the same code on client side it works so I guess this is a bug. This is my complete code:
BABYLON = require('babylonjs');
var LOADERS = require('babylonjs-loaders');
require('babylonjs-serializers');
var fs = require('fs');
global.XMLHttpRequest = require('xhr2').XMLHttpRequest;
var createScene = function() {
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.FlyCamera('FlyCamera', new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
var light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
BABYLON.SceneLoader.ImportMesh('', 'http://localhost:9966/', 'SceneTest.glb', scene, function(meshes) {
for (var index = 0; index < meshes.length; index++) {
var mesh = meshes[index];
if (mesh.name === '__root__') {
var axis = new BABYLON.Vector3(0, 1, 0);
axis.normalize();
var theta = Math.PI / 2;
mesh.rotationQuaternion = new BABYLON.Quaternion.RotationAxis(axis, theta);
mesh.position = new BABYLON.Vector3(-10, 0, 0);
}
}
});
BABYLON.SceneLoader.ImportMesh('', 'http://localhost:9966/SceneTest.glb', null, scene, function(
meshes
) {});
return scene;
};
var engine = new BABYLON.NullEngine();
var scene = createScene();
BABYLON.GLTF2Export.GLBAsync(scene, 'Test.glb').then(data => {
console.log(data);
});
engine.runRenderLoop(function() {
if (scene) {
scene.render();
}
});
I don’t have an easy way to run your code, but this looks like an async issue. ImportMesh is an asynchronous function, so the code you’ve written there won’t actually be executed in the order it appears on the page. I think what’s happening is that your calls to ImportMesh are being executed, which starts the import process, but execution is continuing without waiting for the import to succeed. Execution then reaches the GLBAsync call and tries to export the scene, but the scene is still empty because the import process is ongoing.
If that’s the issue, you can fix it in a number of ways (callbacks, Promises, etc.), but probably the cleanest way will be to use the async-await syntax described in the first link I pasted above. You can declare your createScene function to be async, then await your ImportMesh calls, then await createScene(), and so on until your code has the execution order you’re going for.
I changed the code to to await everything, but the exported data is still not there. Im not sure what else to do. This is my tested code:
const BABYLON = require(‘babylonjs’);
require(‘babylonjs-loaders’);
const SERIALIZER = require(‘babylonjs-serializers’);
const fs = require(‘fs’);
global.XMLHttpRequest = require(‘xhr2’).XMLHttpRequest;
var createScene = async function() {
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.FlyCamera('FlyCamera', new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
var light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
const meshes = await new Promise(resolve =>
BABYLON.SceneLoader.ImportMesh('', 'http://localhost:9966/', 'SceneTest.glb', scene, resolve)
);
for (var index = 0; index < meshes.length; index++) {
var mesh = meshes[index];
if (mesh.name === '__root__') {
var axis = new BABYLON.Vector3(0, 1, 0);
axis.normalize();
var theta = Math.PI / 2;
mesh.rotationQuaternion = new BABYLON.Quaternion.RotationAxis(axis, theta);
mesh.position = new BABYLON.Vector3(-10, 0, 0);
}
}
await new Promise(resolve =>
BABYLON.SceneLoader.ImportMesh('', 'http://localhost:9966/', 'SceneTest.glb', scene, resolve)
);
return scene;
};
async function awaitCall() {
var scene = await createScene();
const data = await SERIALIZER.GLTF2Export.GLBAsync(scene, 'Test.glb');
console.log(data);
}
var engine = new BABYLON.NullEngine();
awaitCall();
TypeError: Cannot read property 'createFramebuffer' of undefined
at NullEngine.Engine._readTexturePixels (/Users/peterhenry/Documents/C9S/pxl/.next/server/static/development/pages/api/download.js:41001:22)
at Texture.BaseTexture.readPixels (/Users/peterhenry/Documents/C9S/pxl/.next/server/static/development/pages/api/download.js:96873:19)
at _GLTFMaterialExporter.getPixelsFromTexture (/Users/peterhenry/Documents/C9S/pxl/.next/server/static/development/pages/api/download.js:234352:198)
at /Users/peterhenry/Documents/C9S/pxl/.next/server/static/development/pages/api/download.js:234414:28
Does this mean it’s not possible to export a model with textures server side? I have an app that allows certain users to export models. I was hoping to do the export server side for better security.
This doesn’t seem to be any particular extension. I think we’re reading from the glTexture for every exported texture in scene. I’m not sure if there’s a graceful solution to this. I curently have a fix that will do some additional error checking in engine.readTexturePixels that will throw if there is no webGLContext, and will not export the texture in that case.
Also that means that nullEngine can’t export textures with the current implementation.
Do you have any idea of a better workaround for this?
Adding @Drigax to the party but all the texture work being done on the GPU for export, any exported feature requiring it to work would normally be disable.
@sebavan we have quite complex app doing a lot of manipulations on models (applying new materials, showing/hiding meshes, etc.) so first we used sample code from Server Side - Babylon.js Documentation, then used exporter from this page glTF Exporter - Babylon.js Documentation which raised exact error from this thread.
We are successfully using nullEngine for listing model meshes in the browser, but running it on backend is not working for gltf exporter.
I am pretty sure some of your features exported relies on texture then as it is using readPixels as we see in the stack and the only way around is to not export textures at all which would break your exported file in this case.
You could try relying on a real engine instead and xvfb on the server ??? this is what we use in our automated tests to benefit from a real rendering despite being slower. Would that work for you ?
Sounds fine, I think performance degradation is not an issue. CPU processing power is easier for scaling than GPU. How can we configure NullEngine to use xvfb? Our goal is to make it working on AWS Lambda if possible, but we can consider another options.