How do mesh instances work, and how to create cross-scene instances?

I’ve read this documentation, and wanted to understand it a bit more.
I currently have a Ship class, for Starships in my game. Large amounts of enemy ships can be created, and the player’s ships are created once every scene.
In the class, I have this method:

addToScene(scene=game.scene()){
	SceneLoader.LoadAssetContainer('','data:'+ship.generic[type].model, scene, imported => {
		scene = planet?planet._scene:scene;
		this.meshes[scene.id] = Object.assign(imported.meshes[0],{
			rotationQuaternion: null,
			material: Object.assign(imported.materials[0],{
				realTimeFiltering: true,
				realTimeFilteringQuality: [2,8,32][+settings.render_quality],
				reflectionTexture: scene.probe.cubeTexture,
				roughness: 0,
				metallic: 1
			}),
			position: planet?planet.position.add(planet.fleetLocation).add(this.position):Vector3.Zero()
		});
		scene.probe.renderList.push(this.meshes[scene.id]);
		scene.gl.addIncludedOnlyMesh(imported.meshes[1]);
		imported.addAllToScene();
	},0,'.gltf');
}

This seems like the perfect place to use instances, since the models are exactly the same. Since the models are used across multiple scenes, could it be possible to simply create one for each ship type, then instantiate it when it’s needed? If so, how? I’m fully aware of (Mesh).createInstance, though that does not allow for a different scene.

Hi Vortex,

Even if it would work (which I don’t think it will), I wouldn’t recommend trying to put an instance in a separate scene from the mesh from which it was instantiated. Meshes and instances share and rely on a lot of information that’s in their own scenes – materials, etc. – and I’m not sure what would be gained by trying to have those dependencies cross among many scenes. Furthermore, the lifetimes of scenes can be different, and the objects within a scene can only remain “alive” as long as the scenes that contain them persist. Instances with inter-scene dependencies would break this conception pretty significantly.

Instead of this, I’d recommend just creating one “instance prototype” for each scene that you’re using, then create the instances you use in that scene from that one. While the “instance prototype” will be loaded into the scene as many times as you have scenes, it won’t actually be downloaded multiple times because of caching mechanisms, so it should be pretty lightweight. This, I think, should give you pretty much all the benefits of using instances without requiring you to introduce dependencies across scene boundaries.

Hope this helps, and best of luck!

2 Likes

Thanks! Do you mean something like this:

const Level = class extends Scene{ //creates a new solar system / level / scene
	constructor(){ //ignoring the rest of the constructor code since it's not relavent
		this.genericShips = {};
		for(let i in ship.generic){
			SceneLoader.LoadAssetContainer('','data:'+ship.generic[i].model, this, imported => {
				this.genericShips[i] = Object.assign(imported.meshes[0], {
					rotationQuaternion: null,
					isVisible: false,
					material: Object.assign(imported.materials[0],{
						realTimeFiltering: true,
						realTimeFilteringQuality: [2,8,32][+settings.render_quality],
						reflectionTexture: scene.probe.cubeTexture,
						roughness: 0,
						metallic: 1
					})
				})
			}
		}
	}
}
//inside the ship class:
addToScene(scene = game.scene()){
	if(!scene instanceof Level) throw new TypeError('(Ship).addToScene(): scene must be a Level!')
	this.meshes[scene.id] = scene.genericShips[this.type].createInstance(this.name);
}
1 Like

Yep, that seems like it’ll probably work!

Hm… This has been working ok, except for a timing issue. Oversimplified:

class Planet extends StarBody{
	constructor(enemies = [], scene){ 
		//create the planet and stuff
		for(let e of enemies){
			let enemy = new Ship(e, scene);
			this.enemies.push(enemy);
		}
	}
}
class Level extends Scene{
	constructor(){
		//create a new scene and stuff
		for(let i in ship.generic){
			//load the ship template meshes into this.genericShips
		}
		for(let i in names){
			this.planets.push(new Planet (...)); //fails since the models have not been loaded yet.
		}
	}
}

Maybe wrapping an async around it would work? Any other and/or better way to do this?

class Level extends Scene{
	constructor(){
		(async function() {
			//do stuff
			for(let i in ship.generic){
				await SceneLoader.LoadAssetContainerAsync(...)
				...
			}
			//create planets
		})();
		
	}
}