GLTFs exported from Blender are split into separate objects in Three.js by texture

I am using dragControls and OrbitControls with the WebGLRenderer. Our goal is to make the 3D objects we import move together when dragging using the drag controls.

All of the objects are modeled in blender and exported as GLTFs. Each model is made up of multiple distinct meshes in blender (for example, when exporting a table the table top, legs, and drawers are all distinct meshes within Blender). When the objects are exported from Blender and loaded into Three.js in a single GLTF file, using DragControls pulls the models apart (the table top, legs, and drawers all get dragged separately).

We’ve tried multiple solutions including parenting the objects in blender and using Ctrl + J to join them into a single mesh, but it seems that Three.js still splits up the meshes by texture. Faces that share a texture move together, but those that do not remain separate. Is there a way to disable Three.js from splitting them up in this way? It’s also possible that the GLTF exporter is what’s causing the problems, but when we load the exported GLTFs back into blender they behave correctly. Any help would be appreciated!

Here is a link to the github, all of the code is in the index.html script at the bottom


Two meshes with different materials or textures will always be separate THREE.Mesh instances in three.js. If you want to control them as a group, you’ll need to attach the controls to a THREE.Group or THREE.Object3D parent of the objects.

If you’re not sure how to identify which parts of the scene were “grouped” in Blender, consider assigning unique names, or setting Custom Properties in Blender, enabling the Custom Property export option, and then looking for that data in object.userData in three.js.

What do you mean when you say “attatch the controls”?
The parameters for working with DragControls are the following,
DragControls( objects : Array, camera : Camera, domElement : HTMLDOMElement )

should I pass in an array of parent objects? I’m unsure how I should approach this problem from the coding perspective.

Try opening your model in, and open the JavaScript console in your browser’s developer tools. You should see a printout like this:

That tree represents all of the objects that were created in three.js, based on the given model. They are nested — there’s a single root “Group” object, and everything else is underneath that. Some meshes are grouped, too. If you want them to be moved as a group, you need to give DragControls the parent of that group, rather than all of the individual meshes within it.

(this is being weird, click on the image to show what the console logs)

This is how my code currently shows an object being loaded in, so I would need to give DragControls the parent of Plane? And is the parent a 3D object or is the parent a group?

It looks like you need to give DragControls the object named Plane. If you’re giving it the meshes named Plane_0 and Plane_1, you’ll be letting it move things individually that were connected in Blender.

See THREE.Group for more information. The Group and Object3D classes are not rendered themselves, and are used to move their children around as a single unit.

Hey I am sorry but I am just so conufsed and I cannot find any resources on the internet that fix my issue, so I’m going to go more into depth on my issue and hopefully you can help me out.

    var currentDesk = new THREE.Object3D();
    var currentMonitor = new THREE.Object3D();
    var currentKeyboard = new THREE.Object3D();
    var currentMousepad = new THREE.Object3D();
    var currentMouse = new THREE.Object3D();
    var objectsArray = [currentDesk, currentMonitor, currentKeyboard, currentMousepad, currentMouse];        
    dragControls = new THREE.DragControls(objectsArray, camera, renderer.domElement);

This is how I’ve set up my objects array that gets passed into DragControls, my app is a furniture app so it consists of swapping out furniture. That means that the currentDesk gets changed after the dragControls has been declared, possibly that is an issue? Nevertheless the code above shows how I’m setting it up.

I’m not gonna paste the full furniture swapping code, but the part where I change the objectsArray looks like this

objectsArray[indexDictionary[objectType]] = gltf.scenes[0].children[0].parent;

I have used console.log for the gltf.scenes part of the code above to figure out that it is giving me the scene value I want. For the keyboard I am using the console logs this for the object it is being set to, which is the plane if I am not mistaken.

I also realize it has a parent which I have tried setting it equal to as well, when I set it equal to this parent I got an error that says “Uncaught TypeError: Cannot read property ‘layers’ of null” whenever the mouse moved over the canvas (thousands of errors really fast you would imagine). One possible thing that I could be doing wrong here is that the parent is of type Scene, which is not an Object3D or a Group.

It is also important I’m using a loader like this

loader.load(coordinateDictionaryArr[4], function (gltf) {

The coordinateDictionaryArr[4] just references a filepath, it is a path to a glb file and that is 100% I know that for a fact so don’t worry about that being a possible error.

I could also be doing something wrong in how I add the object to the scene, for now I am just using scene.add(gltf.scene); when adding to the scene, maybe I have to add the object directly?

If you want to take a look at the Github ( for loading, the loading function is selectNew3DObject(objectType, objectName, event) , I have tried making groups and I have tried setting Groups / Object3Ds to almost every combination of scenes or children arrays and I have to be missing something here.

It is also important to notice that the current version of the code doesn’t break, but it the objects don’t move correctly like in the first gif I posted.

Thank you for reading this post, hopefully I’ve provided enough information for you to point me in the right direction.

I’m afraid that’s too much code for me to debug for you… you may need to create a simpler example to test this?