OrbitControl ,TransformControl ,DragControl,select objects Performance issue

Hi guys.
I am creating a interior design project with three.js.I need these different controls And I have added all of them but after some time working with them, my app will slow down. I see some lags.
Do you know the reason? especially in dragging

Lets check my code :slight_smile:
Select

 const selectMode = () => {

        console.log('selectMode')
        selectMODE.current=true

        orbitRef.current.enabled = true

        transformControlRef.current.detach();
        transformControlRef.current.enabled = false
    
        if (dragControlRef.current) {
            dragControlRef.current.enabled = false;
            dragControlRef.current = ""
        }

    }

 function onMouseClick(event) {

        console.log('event', event.target)
        if(selectMODE.current){

            const rect = canvasRef.current.getBoundingClientRect();

            // Calculate mouse position in normalized device coordinates
            mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
            mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
    
            // Set the raycaster
            raycaster.setFromCamera(mouse, cameraRef.current);
    
            if (isPlacing && modelToPlace) {
                // If we're in placing mode, place the model at the intersection point
                const intersects = raycaster.intersectObjects(sceneRef.current.children, true); // Assuming floor is your ground plane or surface
                if (intersects.length > 0) {
                    const intersectPoint = intersects[0].point;
                    modelToPlace.position.copy(intersectPoint);  // Place the model at the clicked location
                    isPlacing = false;  // Exit placing mode
                    modelToPlace = null;  // Clear the model to place
                }
            } else {
                // Normal object selection logic (if not placing a model)
                const intersects = raycaster.intersectObjects(sceneRef.current.children, true);
    
                if (intersects.length > 0) {
                    selectedGroup = intersects[0].object;
                    console.log('Intersection found:', selectedGroup);
    
                    if (selectedGroup.isMesh && selectedGroup.material) {
                        const originalColor = selectedGroup.material?.color?.clone(); // Store original color
    
                        // If another timeout is active, clear it
                        if (activeTimeout) {
                            clearTimeout(activeTimeout);
                            if (selectedObject.current) {
                                selectedObject.current.material?.color?.copy(originalColor); // Reset immediately
                            }
                        }
    
                        // Change the color to green temporarily
                        selectedGroup.material?.color?.set(0x354E37);
                        selectedObject.current = selectedGroup;
                        if(transformControlRef.current.enabled){
                            transformControlRef.current.attach(selectedObject.current)
                        }
    
                        // Set a new timeout to revert the color after 100ms
                        activeTimeout = setTimeout(() => {
                            selectedGroup.material?.color?.copy(originalColor);
                            activeTimeout = null;
                        }, 100);
                    }
                } else {
                    console.log('No object selected');
                }
            }


        }

        console.log('selectedObject raycast', selectedObject.current)
    }

next part is dragging that I think maybe this part has performance issues:

   const dragMode = () => {

        console.log('dragmode', draggableObjects)
        selectMODE.current=false

        transformControlRef.current.enabled = false
        transformControlRef.current.detach();
        if (dragControlRef.current) dragControlRef.current.enabled = true;


        dragControlRef.current = new DragControls(draggableObjects, cameraRef.current, renderer.current.domElement);
        // dragControlRef.current.transformGroup = true;
        // dragControlRef.current.transformRoot=true
        let originalMaterial;
        const dragMaterial = new THREE.MeshBasicMaterial({ color: 0xD6A218, transparent: true, opacity: 0.5 });
        // Add event listeners for dragging
        dragControlRef.current.addEventListener('dragstart', function (event) {
            console.log('dragstart name >>>', event.object.name)
            orbitRef.current.enabled = false
            originalMaterial = event.object.material
            event.object.material = dragMaterial;
            const draggedObject = event.object;
            previousPosition.copy(draggedObject.position);
        });
        dragControlRef.current.addEventListener('drag', function (event) {
            orbitRef.current.enabled = false
        });

        dragControlRef.current.addEventListener('dragend', function (event) {
            console.log('Drag ended: ', event.object);
            orbitRef.current.enabled = true
            event.object.material = originalMaterial;

        });


    }

next one is transform Control mode for selected object:

 const transformMode = () => {

        console.log('transformMode', selectedObject.current)
        selectMODE.current=false

        if (dragControlRef.current) dragControlRef.current.enabled = false

        transformControlRef.current.reset()
        transformControlRef.current.enabled = true
        if(selectedObject.current.name.includes("LOCK")===false){

        
        transformControlRef.current.attach(selectedObject.current)
        sceneRef.current.add(transformControlRef.current.getHelper())


        transformControlRef.current.addEventListener('mouseDown', () => {
            orbitRef.current.enabled = false;
        });

        transformControlRef.current.addEventListener('mouseUp', () => {
            orbitRef.current.enabled = true;

        });

        transformControlRef.current.addEventListener('objectChange', () => {
            constrainPosition(selectedObject.current, minLimit, maxLimit);
        });

        window.addEventListener('keydown', (event) => {
            switch (event.key) {
                case 'r': // Press 'r' to switch to rotation mode
                    transformControlRef.current.setMode('rotate');
                    break;
                case 's': // Press 's' to switch to scale mode
                    transformControlRef.current.setMode('scale');
                    break;
                case 't': // Press 't' to switch to translate mode
                    transformControlRef.current.setMode('translate');
                    break;
            }
        });
    }
    else{
        console.log('you ant to change a lock object')
    }

    }

and orbit mode can be enable with other modes at the same time

 const orbitMode = () => {

        console.log('orbitMode', transformControlRef.current)

        orbitRef.current.enabled = true
        transformControlRef.current.detach();
        transformControlRef.current.enabled = false
        if (dragControlRef.current) {
            dragControlRef.current.enabled = false;
            dragControlRef.current = ""
        }



    }

I also encountered this problem. Have you ever encountered frame drops when adding new objects and changing their movements?

  • Audit renderer.info programs. You seem to redeclare a Material and not dispose of it.
  • Declare variables once, outside of the PointerEvents, and reuse them. For example, canvas.getBoundingClientRect is slow to repeat.
  • Remove old EventListeners. Debounce events.
  • Remove console.log from drag/raycast in production. It preserves many references to objects/groups, and prevents GC.
  • Raycast the ground, or a layer group, or a set of curated bounding boxes. Then a click wonโ€™t traverse secondary details like a toilet chain.

Try to move code outside of the useDrag handler.

For example, the declaration of the raycaster does not need to fire all the time.

Instead of declaring a new drag material, why not just alter the existing with a state?

Youโ€™re also adding a lot of event listeners. Since you are using R3F these are already on the mesh and do not need to be re-added.

Iโ€™m not really able to tell what dragging library you are using, it is probably 3JS Drag Controls? In any case, I would check out useGesture - here is an example

I think this coding style is extremely verbose and can be much shorter and declarative.

1 Like

when I disable orbit control.it is fine