Create function to start babylon

I have same problem with ‘engine is null’ when I want to start babylon from function.

This is my code and error:

Cannot read properties of null (reading ‘runRenderLoop’)
at viewerjs_init.js:110:14
(anonimo) @ viewerjs_init.js:11

properties of null (reading ‘resize’)
at viewerjs_init.js:119:14
(anonimo) @ viewerjs_init.js:11

my code

function setupCameraForCollisions(camera) {
	camera.checkCollisions = true;
	camera.applyGravity = true;
	camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
}

function start3dview(url3d) {
	var canvas = document.getElementById("renderCanvas");

	var startRenderLoop = function (engine, canvas) {
		engine.runRenderLoop(function () {
			if (sceneToRender && sceneToRender.activeCamera) {
				sceneToRender.render();
			}
		});
	}

	var engine = null;
	var scene = null;
	var sceneToRender = null;
	var createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true,  disableWebGL2Support: false}); };
	var createScene = async function () {
		var scene = new BABYLON.Scene(engine);
		scene.gravity = new BABYLON.Vector3(0, -0.5, 0);
		scene.collisionsEnabled = true;
		scene.clearColor = new BABYLON.Color4(0.5, 0.5, 0.5, 0.6); //Gray
		//scene.clearColor = new BABYLON.Color4(0.9, 0.9, 0.1, 0.6); //yellow
		//scene.createDefaultEnvironment();
		//scene.createGround = true;
		//scene.skyboxSize = false;

		//Create a Camera
		const alpha =  Math.PI/2; //rotazione dell'oggetto (target)
		const beta = Math.PI/2;
		const radius = 20; //distanza dall'oggetto (target)
		const target = new BABYLON.Vector3(0, 7, 0);
		
		//const blueMaterial = new BABYLON.StandardMaterial('blueMat', scene);
		//blueMaterial.emissiveColor = new BABYLON.Color3(1, 0.9, 0);
	
		//Pavimento (Ground)
		//const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 70, height: 70}, scene);
		//ground.position.y = 0;  
		//ground.checkCollisions = true;
		//ground.material = blueMaterial;
		
		//Camera
		const camera = new BABYLON.ArcRotateCamera("camera", alpha, beta, radius, target, scene);
		camera.useAutoRotationBehavior = true;
		camera.autoRotationBehavior.idleRotationSpeed = 0.1;
		camera.autoRotationBehavior.idleRotationSpinupTime = 0;
		camera.autoRotationBehavior.zoomStopsAnimation = false;
		
		//const camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(alpha, beta, radius), scene);
		//camera.setTarget(target);
		
		camera.attachControl(canvas, true);

		setupCameraForCollisions(camera);
					
		//Light
		const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 3, 1), scene);
		light.diffuse = new BABYLON.Color3(1, 1, 1);
		light.specular = new BABYLON.Color3(0, 0, 0);
		//light.groundColor = new BABYLON.Color3(1, 1, 0);
		
		// Append glTF model to scene.
		BABYLON.SceneLoader.Append("", url3d, scene, function (scene) {
			
			// Create a default arc rotate camera and light.
			//scene.createDefaultCameraOrLight(true, true, true);
		
			// The default camera looks at the back of the asset.
			// Rotate the camera by 180 degrees to the front of the asset.
			//scene.activeCamera.alpha += Math.PI;
		});
		
		
		// XR
		const xrHelper = await scene.createDefaultXRExperienceAsync({
			//floorMeshes: [ground],
			disableTeleportation: true,
		});
		
		//Movimento
		const featureManager = xrHelper.baseExperience.featuresManager;
		featureManager.enableFeature(BABYLON.WebXRFeatureName.MOVEMENT, 'latest', {
			xrInput: xrHelper.input,					
		});
	
		return scene;
	};
	window.initFunction = async function() {            
		var asyncEngineCreation = async function() {
			try {
			return createDefaultEngine();
			} catch(e) {
			console.log("the available createEngine function failed. Creating the default engine instead");
			return createDefaultEngine();
			}
		}
		window.engine = await asyncEngineCreation();
		if (!engine) throw 'engine should not be null.';
		startRenderLoop(engine, canvas);
		window.scene = createScene();
	};
	
	initFunction().then(() => {scene.then(returnedScene => { sceneToRender = returnedScene; });
						
	});

	// Resize
	window.addEventListener("resize", function () {
		engine.resize();
	});
}

I’ve already answered you in another thread :upside_down_face: but are you calling the engine creation function?

what do you mean exactly?

The createDefaultEngine function. I see that it is being called in initFunction, but it’s async meaning the engine won’t be created immediately. So the engine can be null during the engine.resize call. I recommend checking this web app template which is simpler and doesn’t have async stuff that messes up timing: Getting Started - Chapter 1 - Setup Your First Web App | Babylon.js Documentation (babylonjs.com)

I need async for this:

await scene.createDefaultXRExperienceAsync

I change without async, but I have error on
window.initFunction @ viewerjs_init.js:103
‘engine should not be null’

My new code

function setupCameraForCollisions(camera) {
	camera.checkCollisions = true;
	camera.applyGravity = true;
	camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
}

function start3dview(url3d) {
	var canvas = document.getElementById("renderCanvas");

	var startRenderLoop = function (engine, canvas) {
		engine.runRenderLoop(function () {
			if (sceneToRender && sceneToRender.activeCamera) {
				sceneToRender.render();
			}
		});
	}

	var engine = null;
	var scene = null;
	var sceneToRender = null;
	var createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true,  disableWebGL2Support: false}); };
	var createScene = function () {
		var scene = new BABYLON.Scene(engine);
		scene.gravity = new BABYLON.Vector3(0, -0.5, 0);
		scene.collisionsEnabled = true;
		scene.clearColor = new BABYLON.Color4(0.5, 0.5, 0.5, 0.6); //Gray
		//scene.clearColor = new BABYLON.Color4(0.9, 0.9, 0.1, 0.6); //yellow
		//scene.createDefaultEnvironment();
		//scene.createGround = true;
		//scene.skyboxSize = false;

		//Create a Camera
		const alpha =  Math.PI/2; //rotazione dell'oggetto (target)
		const beta = Math.PI/2;
		const radius = 20; //distanza dall'oggetto (target)
		const target = new BABYLON.Vector3(0, 7, 0);
		
		//const blueMaterial = new BABYLON.StandardMaterial('blueMat', scene);
		//blueMaterial.emissiveColor = new BABYLON.Color3(1, 0.9, 0);
	
		//Pavimento (Ground)
		//const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 70, height: 70}, scene);
		//ground.position.y = 0;  
		//ground.checkCollisions = true;
		//ground.material = blueMaterial;
		
		//Camera
		const camera = new BABYLON.ArcRotateCamera("camera", alpha, beta, radius, target, scene);
		camera.useAutoRotationBehavior = true;
		camera.autoRotationBehavior.idleRotationSpeed = 0.1;
		camera.autoRotationBehavior.idleRotationSpinupTime = 0;
		camera.autoRotationBehavior.zoomStopsAnimation = false;
		
		//const camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(alpha, beta, radius), scene);
		//camera.setTarget(target);
		
		camera.attachControl(canvas, true);

		setupCameraForCollisions(camera);
					
		//Light
		const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 3, 1), scene);
		light.diffuse = new BABYLON.Color3(1, 1, 1);
		light.specular = new BABYLON.Color3(0, 0, 0);
		//light.groundColor = new BABYLON.Color3(1, 1, 0);
		
		// Append glTF model to scene.
		BABYLON.SceneLoader.Append("", url3d, scene, function (scene) {
			
			// Create a default arc rotate camera and light.
			//scene.createDefaultCameraOrLight(true, true, true);
		
			// The default camera looks at the back of the asset.
			// Rotate the camera by 180 degrees to the front of the asset.
			//scene.activeCamera.alpha += Math.PI;
		});
		
		
		// XR
		const xrHelper = scene.createDefaultXRExperienceAsync({
			//floorMeshes: [ground],
			disableTeleportation: true,
		});
		
		//Movimento
		const featureManager = xrHelper.baseExperience.featuresManager;
		featureManager.enableFeature(BABYLON.WebXRFeatureName.MOVEMENT, 'latest', {
			xrInput: xrHelper.input,					
		});
	
		return scene;
	};
	window.initFunction = function() {            
		var asyncEngineCreation = function() {
			try {
			return createDefaultEngine();
			} catch(e) {
			console.log("the available createEngine function failed. Creating the default engine instead");
			return createDefaultEngine();
			}
		}
		window.engine = asyncEngineCreation();
		if (!engine) throw 'engine should not be null.';
		startRenderLoop(engine, canvas);
		window.scene = createScene();
	};
	
	initFunction().then(() => {scene.then(returnedScene => { sceneToRender = returnedScene; });
						
	});

	// Resize
	window.addEventListener("resize", function () {
		engine.resize();
	});
}

You still need to check if engine exists before calling engine.resize.

1 Like

This code it’s working:

<script>
	async function start3dview(url3d) {
		const canvas = document.getElementById("renderCanvas"); // Get the canvas element
		const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine

		var createScene = async function (url3d) {
			var scene = new BABYLON.Scene(engine);
			
			//Create a Camera
			const alpha =  Math.PI/2; //rotazione dell'oggetto (target)
			const beta = Math.PI/2;
			const radius = 20; //distanza dall'oggetto (target)
			const target = new BABYLON.Vector3(0, 7, 0);
			const camera = new BABYLON.ArcRotateCamera("camera", alpha, beta, radius, target, scene);
			camera.useAutoRotationBehavior = true;
			camera.autoRotationBehavior.idleRotationSpeed = 0.1;
			camera.autoRotationBehavior.idleRotationSpinupTime = 0;
			camera.autoRotationBehavior.zoomStopsAnimation = false;		
			camera.attachControl(canvas, true);

			const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 3, 1), scene);
			light.diffuse = new BABYLON.Color3(1, 1, 1);
			light.specular = new BABYLON.Color3(0, 0, 0);


			// Append glTF model to scene.
			BABYLON.SceneLoader.Append("", url3d, scene, function (scene) {
				
				// Create a default arc rotate camera and light.
				//scene.createDefaultCameraOrLight(true, true, true);

				// The default camera looks at the back of the asset.
				// Rotate the camera by 180 degrees to the front of the asset.
				//scene.activeCamera.alpha += Math.PI;
			});
			
			// XR
			const xrHelper = await scene.createDefaultXRExperienceAsync({
				//floorMeshes: [ground],
				disableTeleportation: true,
			});
			
			//Movimento
			const featureManager = xrHelper.baseExperience.featuresManager;
			featureManager.enableFeature(BABYLON.WebXRFeatureName.MOVEMENT, 'latest', {
				xrInput: xrHelper.input,					
			});

			return scene;
		};

		const scene = await createScene(url3d); //Call the createScene function

		// Register a render loop to repeatedly render the scene
		engine.runRenderLoop(function () {
			scene.render();
		});

		// Watch for browser/canvas resize events
		window.addEventListener("resize", function () {
			engine.resize();
		});
	}
	
	start3dview("marino.glb");
    </script>

Yep because you only add the resize event listener AFTER the engine is created :slight_smile: