three 导入模型无法拖拽的终极解决方案

1,629 阅读1分钟

模型拖拽

由于导入的obj文件模型导入后生成的是一个group对象,而dragControls插件无法绑定在group对象,所以进行以下尝试:

解决导入模型无法拖拽尝试1

原理:将group对象下的所有网格模型mesh合并进一个新建的几何对象geometry,然后创建一个新的mesh,然后绑定拖拽事件;

缺点:模型可能只有单一的颜色,会失去模型文件中所有的材质信息;而且合并后的网格模型将因为有不可计的顶点,所以无法在合并后自己进行贴图;

let objLoader = new THREE.OBJLoader();
objLoader.load('assets/objs/室内/书桌.obj', function (obj) {
    // 控制台查看返回结构:包含一个网格模型Mesh的组Group
    console.log('---', obj);
    // 查看加载器生成的材质对象:MeshPhongMaterial
    console.log('===', obj.children[0].material);
    obj.position.y = 0;
    obj.rotation.y = 0.5;
    obj.scale.set(1, 1, 1);
    // scene.add(obj);

    let geometry = new THREE.Geometry()
    for (let i = 0; i < obj.children.length; i++) {
        let item = new THREE.Geometry().fromBufferGeometry(obj.children[i].geometry)
        geometry.merge(item, obj.children[i].matrix);
    }
    let material = new THREE.MeshLambertMaterial({
    }); //材质对象Material
    let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
    console.log('合并====', mesh.geometry.faceVertexUvs)
    scene.add(mesh); //网格模型添加到场景中
    objects.push(mesh)
})

解决导入模型无法拖拽尝试2

原理:经过尝试发现,dragControls对象只能接收mesh,但是在与平移控件绑定时可以绑定group,并且group与mesh可以通过parent、children属性进行相互查看;所以在new dragControls时先使用group中的某一个mesh进行创建,在后续将移动控件与当前选中对象绑定的操作中mesh通过parent属性找到其group进行绑定;

缺点:只有在鼠标略过以在new dragControls时绑定的mesh才会进行绑定操作,需要人为找到模型中体积较大的mesh,否则唤起拖拽会比较困难;

function initControls() {
    controls = new THREE.OrbitControls(camera, renderer.domElement);//创建控件对象
    controls.addEventListener('change', render);//监听鼠标、键盘事件
    console.log('------', controls)

    // 初始化拖拽控件
    console.log('objects', objects)
    dragControls = new THREE.DragControls(objects, camera, renderer.domElement);
    transformControls = new THREE.TransformControls(camera, renderer.domElement);
    scene.add(transformControls);
    // 鼠标略过事件
    dragControls.addEventListener('hoveron', function (event) {
        // 让变换控件对象和选中的mesh与其group绑定
        console.log('===', event)
        transformControls.attach( event.object.parent);
    });
    // 开始拖拽
    dragControls.addEventListener('dragstart', function (event) {
        controls.enabled = false;
    });
    // 拖拽结束
    dragControls.addEventListener('dragend', function (event) {
        controls.enabled = true;
    });
}

产生的问题:由于绑定的mesh与TransformControls控件并不重合,而dragControls控件可以不通过TransformControls控件的坐标系,直接通过拖拽mesh进行移动,导致当前用于绑定的mesh与group分离,通过取消dragControls源码中的拖拽方法onDocumentMouseDown解决;

解决导入模型无法拖拽尝试3(正在使用)

原理:创建模型,跟导入的模型设置相同的位置和大小,在创建dragControls对象时绑定创建的模型;

缺点:场景内的模型数量会大大增加,可能导致性能问题;

function addServer(list) {
    return new Promise((resolve, reject) => {
    let mtlLoader = new THREE.MTLLoader();
    let objLoader = new THREE.OBJLoader();
    mtlLoader.setPath('./ThreeJs/objs/test/');
    mtlLoader.load('server2.mtl', function (materials) {
        materials.preload();
        let objLoader = new THREE.OBJLoader();
        objLoader.setMaterials(materials);
        objLoader.setPath('./ThreeJs/objs/test/');
        objLoader.load('server2.obj', function (object) {
        object.scale.set(0.3, 0.5, 0.3);
        let {x, y, z} = getObjectSize(object)
        list.forEach((item) => {
            let { rotation, name } = item
            let pX = item.x
            let pY = item.y
            let pZ = item.z
            let obj = object.clone()

            let material = new THREE.MeshBasicMaterial({
            color: 0xfff,
            transparent: true,
            opacity: 0
            });
            let big = returnObject(x, y, z, rotation, material, pX, pY, pZ);
            big.name = 'test$1'
            scene.add(big)
            obj.position.x = pX;
            obj.position.y = pY;
            obj.position.z = pZ;
            obj.rotation.y = rotation;
            obj.name = name
            obj.siblingComponent = big
            big.siblingComponent = obj
            scene.add(obj);
        })
        resolve('addServer')
        });
    });
    })
}
function returnObject(width, height, depth, angle, material, x, y, z) {
    var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
    var cube = new THREE.Mesh(cubeGeometry, material);
    cube.position.x = x;
    cube.position.y = y;
    cube.position.z = z;
    cube.rotation.y = angle;
    return cube;
}

修改dragControls的监听事件,在拖动模型的同时改变导入的模型的位置

dragControls.addEventListener('drag', function (event) {
    let obj = event.object
    const { x, y, z } = obj.position
    if (obj.siblingComponent !== undefined && obj.siblingComponent !== null && obj.siblingComponent !== '') {
        // console.log('test')
        obj.siblingComponent.position.set(x, y, z);
    }
});