Can't move model with TransformControls

Hi there,

i have the problem, that my TransformControls doesn’t work. I see them, but i can’t move the model with it. What i am doing wrong?

Here is some of my code:

// Controls
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = 360;
controls.minPolarAngle = 0;
controls.enableDamping = true;
controls.enablePan = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false; 
controls.autoRotateSpeed = 15.5; // 30
controls.minDistance = 0;
controls.maxDistance = 10;
controls.update();
controls.addEventListener( 'change', light_update );

var transformControls = new THREE.TransformControls(camera, renderer.domElement);
transformControls.addEventListener('change', render_update );

transformControls.addEventListener( 'dragging-changed', function ( event ) {

  controls.enabled = ! event.value;

} );

transformControls.attach(theModel);
scene.add(transformControls);

function animate() { 

  const delta = clock.getDelta();  
  requestAnimationFrame(animate);  

  controls.update();
  renderer.render(scene, camera);  

  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;	
    camera.updateProjectionMatrix();	
  }  

  if (theModel != null && loaded == false) {
	initialRotation();	 
    DRAG_NOTICE.classList.add('start');       	 
  }
  
  if (theModel != null && loaded == true) {    
    mixer.update( delta );
  }  
}

animate();

function light_update()
{
    light.position.copy( camera.position );
}

function render_update()
{
    renderer.render(scene, camera);
}

I used this example for the project:
https://threejs.org/examples/#misc_controls_transform

I think that your transform control is not attached to the object when you hover it with your cursor.
I would try to implement something like this.

function onPointerMove( event ) {

				pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
				pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

				raycaster.setFromCamera( pointer, camera );

				const intersects = raycaster.intersectObjects( splineHelperObjects, false );

				if ( intersects.length > 0 ) {

					const object = intersects[ 0 ].object;

					if ( object !== transformControl.object ) {

						transformControl.attach( object );

					}

				}

			}

This is from this example here: three.js examples
It worked for me.

THX ! Will try that.

I just don’t understand why this example looks simple but half of the features are missing in the code-example. Like the hover effect when you hold you mouse over one of the direction arrow. It seems that all function of TransformControls are missing. Or is it maybe that the TransformControls somehow are in the background so the user just can’t click it?

Yes, you’re right, this one looks much simpler :smiley:
Idk maybe there is an easier way, but anyway, the other one should work as well

So i made a codepen so you guys can maybe test it and find the problem, why TransformControl don’t work like it should.

Hello! Model not loaded yet. Remove: transformControls.attach(theModel);
Add transformControls.attach(theModel);
before scene.add(theModel);

1 Like

Hello! Model not loaded yet. Remove: transformControls.attach(theModel);
Add transformControls.attach(theModel);
before scene.add(theModel);

Thank you very much ! So it was the order that was wrong. First add controls than add the model to the scene. OK got that. THX

1 Like

Ok i now have a small problem! I can use transformControls ONLY on time in a direction and the second time i drag the arrow a the same direction the model (mesh) disappear.

What can cause that ?

Here is the code:

var alphaBetString = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z";var alphaBetTable = alphaBetString.split(" ");var rot13String = "N O P Q R S T U V W X Y Z A B C D E F G H I J K L M n o p q r s t u v w x y z a b c d e f g h i j k l m";var rot13Table = rot13String.split(" ");var numberString = "1 2 3 4 5 6 7 8 9 0";var numberTable = numberString.split(" ");var rot5String = "6 7 8 9 0 1 2 3 4 5";var rot5Table = rot5String.split(" ");var symbolString = "~ ` ! @ # $ % ^ & * ( ) _ + - = { } [ ] \\ | ; \' : \" < > ? , . /";var symbolTable = symbolString.split(" ");

const LOADER = document.getElementById('js-loader');
const TRAY = document.getElementById('js-tray-slide');
const DRAG_NOTICE = document.getElementById('js-drag-notice');
var theModel;

//Values
var userip = document.getElementById('userip').value;
var username = document.getElementById('username').value;
var path = document.getElementById('path').value;
var size = document.getElementById('size').value;
var parts = document.getElementById('parts').value;
var colores = document.getElementById('colores').value;
var posX = document.getElementById('posX').value;
var posY = document.getElementById('posY').value;
var posZ = document.getElementById('posZ').value;
var rotaX = document.getElementById('rotaX').value;
var rotaY = document.getElementById('rotaY').value;
var rotaZ = document.getElementById('rotaZ').value;
var cameradistance = document.getElementById('kamaraZ').value;
var lightintensity = document.getElementById('lightintensity').value;
let mixer;

//Timer Animation Mixer
const clock = new THREE.Clock();

// Colors
COLOR_ARRAY = colores.split(",");
path = path.replace(userip,'');
path = path.replace(username,'');

// Model
var modelselect = document.getElementById('modelselect').value;
modelselect = modelselect.replace(userip,'');
modelselect = modelselect.replace(username,'');
const MODEL_PATH = "../" + path + "/" + rot13rot5Encode(modelselect) + ".glb";

// Start activeOption
var activeOption = 'part-001';
var loaded = false;

// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );
const canvas = document.querySelector('#c');

// Renderer
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });
renderer.shadowMap.enabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);

// Camera
var cameraFar = 5; // 5 Default

if(cameradistance)
{
	cameraFar = cameradistance;
}

var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = cameraFar;
camera.position.x = 0;
camera.position.y = 5;
camera.far = 10000;
camera.updateProjectionMatrix();

// Controls
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = 360;
controls.minPolarAngle = 0;
controls.enableDamping = false;
controls.enablePan = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false; 
controls.autoRotateSpeed = 15.5; // 30
controls.minDistance = 0;
controls.maxDistance = 10;
controls.addEventListener( 'change', light_update );

var transformControls = new THREE.TransformControls( camera, renderer.domElement );
transformControls.addEventListener( 'change', render_update );

transformControls.addEventListener( 'dragging-changed', function ( event ) {

controls.enabled = ! event.value;

} );

//Light
const light = new THREE.DirectionalLight( 0xffffff, 0.75 );
light.castShadow = false;
scene.add(light);

const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 0.25);
hemiLight.position.set( 0, 20, 0 );
scene.add(hemiLight);

const dirLight = new THREE.DirectionalLight( 0xffffff, 1.0);
dirLight.position.set( 3, 10, 10 );
dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024 * 2, 1024 * 2);
dirLight.shadow.camera.top = 4;
dirLight.shadow.camera.bottom = - 4;
dirLight.shadow.camera.left = - 4;
dirLight.shadow.camera.right = 4;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 40;
dirLight.shadow.bias= -0.002;
scene.add( dirLight );

// Shadow Helper
//var shadowHelper = new THREE.CameraHelper( dirLight.shadow.camera );
//scene.add( shadowHelper );

// Ground 
const ground = new THREE.Mesh( new THREE.PlaneGeometry( 1000, 1000, 100, 100 ), new THREE.MeshPhongMaterial( { color: 0xeeeeee, shininess: 1, depthWrite: false } ) );
ground.rotation.x = - Math.PI / 2;
ground.receiveShadow = true;
ground.material.transparent = false;
ground.position.y = -0;
scene.add( ground );

const grid = new THREE.GridHelper( 1000, 1000, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.receiveShadow = false;
grid.material.transparent = true;
grid.position.y = -0.01;
scene.add( grid );

const INITIAL_MAP = []; for (let i=0; i < parts;i++)

  // part-i set default colores from DB
	INITIAL_MAP.push({
    childID: `part-${String(i+1).padStart(3, '0')}` // padding the number
    //mtl: new THREE.MeshPhongMaterial({ color: parseInt(COLOR_ARRAY[i]), shininess: 10, transparent: true })           
  }) 

// Init the object loader
var loader = new THREE.GLTFLoader();

loader.load(MODEL_PATH, function (gltf) {
  theModel = gltf.scene;
  
  // Scale   
  theModel.scale.set(size, size, size);
  
  // Rotation 
	theModel.rotation.x = Math.PI * rotaX;
  theModel.rotation.y = Math.PI * rotaY; 
	theModel.rotation.z = Math.PI * rotaZ; 

  // Position
	theModel.position.x = posX;
	theModel.position.y = posY; 
	theModel.position.z = posZ;  

  theModel.traverse(o => {
    if (o.isMesh) {
      o.castShadow = true;
      o.receiveShadow = true;   
      o.needsUpdate = true;
      o.renderOrder = 10; 
    }
  });  

  // Set initial textures
  for (let object of INITIAL_MAP) {
    initColor(theModel, object.childID);
  }  

  // Add the model to the scene
  scene.add(theModel);

  transformControls.attach(theModel);
  scene.add(transformControls);  
  
  // Remove the loader  
  LOADER.remove();

  mixer = new THREE.AnimationMixer( theModel );
  mixer.clipAction( gltf.animations[ 0 ] ).play();

}, undefined, function (error) {
  console.error(error);
});

// Function - Add the textures to the models
function initColor(parent, type) {
  parent.traverse(o => {
    if (o.isMesh) {
      if (o.name.includes(type)) {
        //o.material = mtl;                
        o.nameID = type; // Set a new property to identify this object        
      }
    }
  });
}

function animate() { 

  const delta = clock.getDelta();  
  requestAnimationFrame(animate);

  controls.update();
  renderer.render(scene, camera);

  if (resizeRendererToDisplaySize(renderer)) {
    const canvas = renderer.domElement;
    camera.aspect = canvas.clientWidth / canvas.clientHeight;	
    camera.updateProjectionMatrix();	
  }  

  if (theModel != null && loaded == false) {
	  initialRotation();	 
    DRAG_NOTICE.classList.add('start');       	 
  }
  
  if (theModel != null && loaded == true) {    
    mixer.update( delta );
  }
}

animate();

// Function - New resizing method
function resizeRendererToDisplaySize(renderer) {
  const canvas = renderer.domElement;
  var width = window.innerWidth;
  var height = window.innerHeight;
  var canvasPixelWidth = canvas.width / window.devicePixelRatio;
  var canvasPixelHeight = canvas.height / window.devicePixelRatio;

  const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
  if (needResize) {

    renderer.setSize(width, height, false);
  }
  return needResize;
}

// Function - Build Colors

function buildColors(colors) {
  for (let [i, color] of colors.entries()) {
    let swatch = document.createElement('div');
    swatch.classList.add('tray__swatch');

    if (color.texture)
    {
      swatch.style.backgroundImage = "url(" + color.texture + ")";
    } else
    {
      swatch.style.background = "#" + color.color;
    }

    swatch.setAttribute('data-key', i);
    TRAY.append(swatch);
  }
}

if(parts > 0)
{
	buildColors(colors);
}

// Select Option
const options = document.querySelectorAll(".option");

for (const option of options) {
  option.addEventListener('click', selectOption);
}

function selectOption(e) {
  let option = e.target;
  activeOption = e.target.dataset.option;
  for (const otherOption of options) {
    otherOption.classList.remove('--is-active');
  }
  option.classList.add('--is-active');
}

// Swatches
const swatches = document.querySelectorAll(".tray__swatch");

for (const swatch of swatches) {
  swatch.addEventListener('click', selectSwatch);
}

function selectSwatch(e) {
  let color = colors[parseInt(e.target.dataset.key)];
  let new_mtl;
  let new_color = parseInt('0x' + color.color)

  if (color.texture) {

    let txt = new THREE.TextureLoader().load(color.texture);

    txt.repeat.set(color.size[0], color.size[1], color.size[2]);
    txt.wrapS = THREE.RepeatWrapping;
    txt.wrapT = THREE.RepeatWrapping;

    new_mtl = new THREE.MeshPhongMaterial({
      map: txt,
      shininess: color.shininess ? color.shininess : 10 });

  } else

  {    
    new_mtl = new THREE.MeshPhongMaterial({
      color: parseInt('0x' + color.color),
      shininess: color.shininess ? color.shininess : 10 });
  }

  setMaterial(theModel, activeOption, new_mtl, new_color);
}

function setMaterial(parent, type, mtl, new_color) {
  parent.traverse(o => {
    if (o.isMesh && o.nameID != null) {
      if (o.nameID == type) {
        //o.material = mtl;
        o.material.color.set(new_color);                       
      }
    }
  });
}

// Function - Opening rotate
let initRotate = 0;

function initialRotation() {
  initRotate++;
  if (initRotate <= 120) {
    theModel.rotation.y += Math.PI / 60;
    light_update();
  } else {
    loaded = true;

    setTimeout( function() {

      var imagedata = renderer.domElement.toDataURL();
      document.getElementById('downloadthumbnail').setAttribute('href',imagedata);
    
    }, 1000 );
  }
}

var slider = document.getElementById('js-tray'),sliderItems = document.getElementById('js-tray-slide'),difference;

function slide(wrapper, items) {
  var posX1 = 0,
  posX2 = 0,
  posInitial,
  threshold = 20,
  posFinal,
  slides = items.getElementsByClassName('tray__swatch');

  // Mouse events
  items.onmousedown = dragStart;

  // Touch events
  items.addEventListener('touchstart', dragStart);
  items.addEventListener('touchend', dragEnd);
  items.addEventListener('touchmove', dragAction);

  function dragStart(e) {
    e = e || window.event;
    posInitial = items.offsetLeft;
    difference = sliderItems.offsetWidth - slider.offsetWidth;
    difference = difference * -1;

    if (e.type == 'touchstart') {
      posX1 = e.touches[0].clientX;
    } else {
      posX1 = e.clientX;
      document.onmouseup = dragEnd;
      document.onmousemove = dragAction;
    }
  }

  function dragAction(e) {
    e = e || window.event;

    if (e.type == 'touchmove') {
      posX2 = posX1 - e.touches[0].clientX;
      posX1 = e.touches[0].clientX;
    } else {
      posX2 = posX1 - e.clientX;
      posX1 = e.clientX;
    }

    if (items.offsetLeft - posX2 <= 0 && items.offsetLeft - posX2 >= difference) {
      items.style.left = items.offsetLeft - posX2 + "px";
    }
  }

  function dragEnd(e) {
    posFinal = items.offsetLeft;
    if (posFinal - posInitial < -threshold) {

    } else if (posFinal - posInitial > threshold) {

    } else {
      items.style.left = posInitial + "px";
    }

    document.onmouseup = null;
    document.onmousemove = null;
  }

}

function light_update()
{
    light.position.copy( camera.position );
}

function render_update()
{
    renderer.render(scene, camera);
}

function rot13rot5Encode(input) {    var output = "";    for (var i=0; i<input.length; i++) {         for (var y=0; y<numberTable.length; y++) {            if (input[i]==numberTable[y]) {                output+=rot5Table[y];            }        }        for (var x=0; x<alphaBetTable.length; x++) {            if (input[i]==alphaBetTable[x]) {                output+=rot13Table[x];            }        }        for (var w=0; w<symbolTable.length; w++) {            if (input[i]==symbolTable[w]) {                output+=symbolTable[w];            }        }        if (input[i]==" ") {            output+=" ";        }    }    return output;};

if(parts > 0)
{
	slide(slider, sliderItems);
}

It seems that this is the problem ! Will give feedback, when i find a fix.

PS: I need to set the xyz positions, because not all my models are centered. So i get there position from a database. But i can’t set the xyz position of the model, if i am using TransformControls. How to fix that?

EDIT: This solved the last problem. Don’t know why they need parseFloat, when they get a float from a var, but it works, so i am fine with that :wink:

	theModel.position.x = parseFloat(posX);
	theModel.position.y = parseFloat(posY); 
	theModel.position.z = parseFloat(posZ);