Instancing fbx models

Is it possible to instance fbx/obj geometry? I haven’t been able to find a working example.

I’m building an online multiplayer game and need to load the same fbx file for each player.

I want to use instancing so that the renderer only needs to compute 1 set of polygons for all the players. My game hosts up to 50 players so I imagine it could get quite heavy without instancing.

I can’t publicly share the whole repository but here’s the code I’m using to populate the players, happy to share the running example privately:

        const loader2 = new THREE.FBXLoader2( loadingManager );
		const player = this;

		loader2.load( "Person.fbx", function ( object ) {

			object.mixer = new THREE.AnimationMixer( object );
			player.root = object;
			player.mixer = object.mixer;

			object.name = "Person";

			object.traverse( function ( child ) {
				if ( child.isMesh ) {
					child.castShadow = true;
					child.receiveShadow = false;
				}
			} );


			player.object = new THREE.Object3D();
			player.object.position.set(0, 0, 0);
			player.object.rotation.set(0, 3, 0);

			player.object.add(object);
			if (player.deleted===undefined) game.scene.add(player.object);

			if (player.local){
				game.createCameras();
				game.sun1.target = game.player.object;
				game.animations.Idle = object.animations[0];
				if (player.initSocket!==undefined) player.initSocket();
				player.object.userData.id = player.id;

			}else{
				player.object.userData.id = player.id;
				player.object.userData.remotePlayer = true;
				const players = game.initialisingPlayers.splice(game.initialisingPlayers.indexOf(this), 1);
				game.remotePlayers.push(players[0]);
			}
		)

This approach in three.js examples will work for any file format. Once a model is loaded it’s just plain three.js objects like THREE.Mesh.

But, be warned that three.js instancing does not support SkinnedMesh / animated characters. 50 draw calls is not really that many — how many draw calls does your environment require? — even if all characters are visible on screen at once, so I wouldn’t necessarily worry about this until you find a performance bottleneck.

Also note that you can reuse BufferGeometry and Material objects to avoid extra memory usage. Instancing (drawing every character in one draw call) isn’t necessary for that.

1 Like

Thanks so much, I’ve answered in quotes below

be warned that three.js instancing does not support SkinnedMesh / animated characters.

Would instancing work with an untextured .fbx file with animations applied? The fbx im using has ‘running’, ‘turning’, ‘walking’ animations, each are called upon independently by different players.

50 draw calls is not really that many — how many draw calls does your environment require?

My scene has 330 draw calls, each character adds:
+5 draw calls
+12000 polycount
+6 geometries

does this seem normal?
image

Also note that you can reuse BufferGeometry and Material objects to avoid extra memory usage.

Do you mean by using the .copy technique on the geometry?

The file format doesn’t matter much, it’s what kind of three.js object you are creating from the file. Animated characters are usually THREE.SkinnedMesh, in which case they cannot be instanced out of the box.

On the other hand, you can (and should) avoid loading the same FBX file more than once, and just clone it instead. For example player2 = player1.clone(). If that doesn’t work for some reason, also try player2 = SkeletonUtils.clone( player1 ). Both allow the characters to share the same geometry and materials, without consuming extra memory.

My scene has 330 draw calls, each character adds:
+5 draw calls
+12000 polycount
+6 geometries

That doesn’t sound unreasonable. Draw calls and polycount could be lower, as a rule of thumb I’d suggest <100 draw calls and <100K vertices, but if you aren’t having any performance issues yet, then it is probably not worth guessing what the bottleneck would be. If you aren’t supporting mobile devices these limits could be higher.

Ok got it, instancing not possible with animated fbx.

Going to try the .clone(); technique, I have my player constructor set up a different way so will have to rewrite it.

Sorry this may be an extremely n00b question but how do you reduce draw calls? My environment is quite small (3 very simple meshes) with about 3000 polygons total. So I’m not sure why it would take up over 300 draw calls.

And yes making this one especially for mobile, which is why I’m trying to do some optimizing :wink:

A draw call is basically a geometry:material pair drawn at a specific location, and environment.fbx has one mesh with ~13 materials, so the total draw calls for that model are probably ~15. I’m not sure why the total in the application would be as large as 330 though…

Ok great, thanks don. will do some digging around the internet, and report back!

Appreciate you taking the time :slight_smile:

Edit: it seems like it is indeed the environment geometry causing all the draw calls, I’ve made it invisible and the drawcalls goes down to 3

Ok so I figured out the draw call issue…

I had vertex assigned materials on my enviroment model, the three.js fbx loader somehow creates hundreds of meshes if you do that - which amounts to huge draw call.

It works better to have seperate meshes per material.

In Blender you can do this by selecting your meshes with vertex assigned materials and go: Mesh >> Seperate by Material.

got the draw calls down from 330 to 7:

More info here:


Ok going to dive into the fbx cloning now!