Three.js r73-将多个对象作为唯一对象来回旋转

Three.js r73 - rotating multiple objects back and forth as a unique object

本文关键字:对象 唯一 回旋 旋转 Three r73- js      更新时间:2023-09-26

我模拟了一个盒子,它展开了每个不同的面,每个面都是一个不同的几何体,在几何体的底部有枢轴。

当我在一个面上拖动鼠标时,它应该在x轴上旋转90度。在MouseDown上再次显示相反的值,就好像长方体的面是闭合的一样。

问题是在tween上呈现的值不一致。

下面是鼠标下降功能:

    function onDocumentMouseDown( event ){
    event.preventDefault();
    mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
    raycaster.setFromCamera( mouse, camera );
    var intersects = raycaster.intersectObjects( objects );
    if ( intersects.length > 0 ) {
        controls.enabled = false;
        if (SELECTED) {     
            if (SELECTED == intersects[ 0 ].object) {
                //same selected;
                new TWEEN.Tween(rotationTween).to( targetB, 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                SELECTED.rotateOnAxis( new THREE.Vector3(1,0,0), rotationTween.x );
                }).start();
                //TWEEN.remove(Tween);
            } else {
                //new selected; 
                SELECTED = intersects[ 0 ].object;  
                new TWEEN.Tween(rotationTween).to( target, 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                SELECTED.rotateOnAxis( new THREE.Vector3(1,0,0), rotationTween.x );
                }).start();
                //TWEEN.remove(Tween);              
            }
        } else {
            //first time selected;
            SELECTED = intersects[ 0 ].object;
            new TWEEN.Tween( rotationTween ).to(target , 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
            SELECTED.rotateOnAxis( new THREE.Vector3(1,0,0), rotationTween.x );
            }).start();
            //TWEEN.remove(Tween);
        }
    }
}

任何其他细节都会给我一条消息。Thx。

我会尽量简短,但我必须解释我的情况:

目标:创建一个具有单独面的立方体,每次单击都会单独打开。此外,我已经到了一个点,只有一个面是打开点击。如果其他打开,它应该关闭。这与我的项目有点远,因为当人脸被"点击"时,洞的场景会保持冻结,没有听众。在上面的代码中,这些面也有相同的根层次结构,如果你在一个框中细化,顶部和底部的面将是背面的子面(在我的项目中),这感觉是错误的。

当我不得不在粗花呢上旋转面部时,我真的陷入了困境。第一个问题是在最初的面定位后,他们将保持他们的旋转值。因此,长方体的左侧将以旋转开始渲染。y=Math.PI/2,依此类推。下面是立方体创建代码:

interactFace.js

interactFace = function (fText, fSize, matColor) {
    //create face mesh
    this.mat = new THREE.MeshPhongMaterial({color: matColor, opacity: 0.3, transparent: true, side: THREE.DoubleSide});
    this.geometry = new THREE.PlaneGeometry(fSize,fSize);
    this.geometry = new THREE.Geometry();   
    this.geometry.vertices.push(
        new THREE.Vector3( fSize/2,  0, 0 ),
        new THREE.Vector3( -fSize/2,  0, 0 ),
        new THREE.Vector3( fSize/2,  fSize, 0 ),
        new THREE.Vector3( -fSize/2, fSize, 0 )
    );
    this.geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
    this.geometry.faces.push( new THREE.Face3( 1, 2, 3 ) );
    this.mesh = new THREE.Mesh(this.geometry, this.mat);
    this.textGeo = new THREE.TextGeometry( fText, {
        size: fSize/8,
        height: 0,
        curveSegments: 4,
        font: "verdana",
        weight: "normal",
        style: "normal",
        bevelThickness: 0,
        bevelSize: 0,
        bevelEnabled: false });
    this.TextMaterial = new THREE.MeshFaceMaterial( [
        new THREE.MeshPhongMaterial( { color: 0x000000, shading: THREE.FlatShading } ), // front
        new THREE.MeshPhongMaterial( { color: 0x000000, shading: THREE.SmoothShading } ) // side
    ] );
    this.textMesh = new THREE.Mesh( this.textGeo, this.TextMaterial);
    this.build(fSize);
};
interactFace.prototype = {
    constructor: interactFace,
    build: function (fSize) {   
        this.mesh.castShadow = true;    
        this.textGeo.computeBoundingBox();
        this.textGeo.computeVertexNormals();
        //this.geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0, -fSize/2 ) );
        //this.textGeo.position.set(  -fSize/4, fSize/2, 0.01 );
        this.textGeo.applyMatrix( new THREE.Matrix4().makeTranslation( -fSize/4, fSize/2, 0.01 ) );
        this.mesh.add(this.textMesh);
    }
};

然后将立方体与面组装在一起。我离开评论了我在绝望时刻尝试的选项。我早期在对象上使用旋转时遇到了一个问题,访问后,通过它们的父对象,它们按父轴旋转,这给我带来了问题。因此,我将旋转切换为rotateOnAxis,这解决了这个问题,但随着面的顶部轴的旋转顺序切换,这个选项变得毫无用处,因此在这种情况下不需要额外的矢量创建:

interactCube.js

interactCube = function (fSize, objects) {
    this.pivot = new THREE.Group();
    var menuItems = [ 'PG 01', 'PG 02', 'PG 03', 'PG 04', 'PG 05', 'PG 06' ];
    var menuItemsCoordinates = [
        [ 0, 0 , 0, 0, -Math.PI/2, -Math.PI/2], // rotation.x
        [ 0, Math.PI/2 , Math.PI, -Math.PI/2, Math.PI, Math.PI], // rotation.y
        [ 0, 0 , 0, 0, Math.PI, 0], // rotation.z
        [ 0, fSize/2 , 0, -fSize/2, 0, 0], //position.x
        [ 0, 0 , 0, 0, 0, fSize], //position.y
        [ fSize, fSize/2 , 0, fSize/2, 0, 0] //position.z   <-- last item is top face, child of front face
    ];      
    this.build(fSize, menuItems, menuItemsCoordinates); 
}
interactCube.prototype = {
    constructor: interactCube,
    build: function (fSize, menuItems, menuItemsCoordinates) {  
        for (var i = 0; i < menuItems.length; i++) {        
            this.face = new interactFace(menuItems[i], fSize, 0x585858);
            this.face.mesh.rotation.order = 'ZYX';      
    //<-- ROTATION BEFORE TRANSLATION
/*
            this.face.mesh.rotation.set( menuItemsCoordinates[0][i], menuItemsCoordinates[1][i], menuItemsCoordinates[2][i]);
            this.face.mesh.position.set( menuItemsCoordinates[3][i], menuItemsCoordinates[4][i], menuItemsCoordinates[5][i] );
*/  
            var rotation = new THREE.Matrix4().makeRotationX( menuItemsCoordinates[0][i] );
            this.face.mesh.applyMatrix(rotation);
            var rotation = new THREE.Matrix4().makeRotationY( menuItemsCoordinates[1][i] );
            this.face.mesh.applyMatrix(rotation);
            var rotation = new THREE.Matrix4().makeRotationZ( menuItemsCoordinates[2][i] );
            this.face.mesh.applyMatrix(rotation);   
            var translation = new THREE.Matrix4().makeTranslation(menuItemsCoordinates[3][i], menuItemsCoordinates[4][i], menuItemsCoordinates[5][i]);
            this.face.mesh.applyMatrix(translation);        
            /*
            this.face.mesh.rotateOnAxis( new THREE.Vector3(1,0,0), menuItemsCoordinates[0][i] );
            this.face.mesh.rotateOnAxis( new THREE.Vector3(0,1,0), menuItemsCoordinates[1][i] );
            this.face.mesh.rotateOnAxis( new THREE.Vector3(0,0,1), menuItemsCoordinates[2][i] );
            this.face.mesh.translateOnAxis( new THREE.Vector3(1,0,0), menuItemsCoordinates[3][i] );
            this.face.mesh.translateOnAxis( new THREE.Vector3(0,1,0), menuItemsCoordinates[4][i] );
            this.face.mesh.translateOnAxis( new THREE.Vector3(0,0,1), menuItemsCoordinates[5][i] );
            */
            /*
            if( ( (i + 1) < menuItems.length  ) ) {             
                this.pivot.add(this.face.mesh);
                objects.push(this.face.mesh);
            } else {                
                this.pivot.children[ i - 1 ].add(this.face.mesh);
                objects.push(this.face.mesh);
            }
            */
            this.pivot.add(this.face.mesh);
                objects.push(this.face.mesh);
        }
    }

}

然后我找到了模板,这个问题的前一个代码,我在为青少年创建一个独特的值以处理所有人脸方面遇到了问题。所有侧面都工作得很好,因为它们将.x值旋转到0。顶面和底面给我带来了问题,因为它们最初的旋转.x值与0不同。然后我开始将tween函数切换为初始值的增量。错了。函数内部角度的增加是一场噩梦,所以我开始研究sin和cos。我花了一些时间在flash中制作3d菜单,而我必须将动态按钮映射到圆形位置。我记得代码语言中的弧度值并没有像我们以度计算的那样工作。我必须给旋转角度一个固定的值。几乎像递增和递减,但没有吐温中给出的错误。

因此,我设法通过创建两个变量来解决这个问题,一个变量获得所选对象的rotation.x的初始值,另一个变量是第一个由Math.PI/2递增的值。因此,在onUpdate函数上,我可以定义一个动态固定值,由函数外的递增参数提供。

template.js

var container, stats;
var controls;
var camera, scene, renderer;
var cubeMenu;
var objects = [];
var raycaster;
var mouse, INTERSECTED, SELECTED;
var opened = false;
function renderTemplate() {
    init();
    animate();  
}
function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );
    camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
    camera.position.z = 100;
    camera.position.y = 20;
    controls = new THREE.TrackballControls( camera );
    controls.rotateSpeed = 1.0;
    controls.zoomSpeed = 1.2;
    controls.panSpeed = 0.8;
    controls.noZoom = false;
    controls.noPan = false;
    controls.staticMoving = true;
    controls.dynamicDampingFactor = 0.3;
    scene = new THREE.Scene();
    createScenario(scene);
    cubeMenu = new interactCube(18, objects);                   
    scene.add( cubeMenu.pivot );        
    raycaster = new THREE.Raycaster();
    mouse = new THREE.Vector2();
    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setClearColor( 0xf0f0f0 );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.sortObjects = false;
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFShadowMap;
    container.appendChild( renderer.domElement );
    var info = document.createElement( 'div' );
    info.style.position = 'absolute';
    info.style.top = '10px';
    info.style.width = '100%';
    info.style.textAlign = 'center';
    info.innerHTML = 'interactive faced cube menu';
    container.appendChild( info );
    stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.top = '0px';
    container.appendChild( stats.domElement );
    //
    document.addEventListener( 'mousemove', onDocumentMouseMove, false );
    document.addEventListener( 'mousedown', onDocumentMouseDown, false );
    document.addEventListener( 'mouseup', onDocumentMouseUp, false );
    document.addEventListener( 'touchstart', onDocumentTouchStart, false );
    window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
    event.preventDefault();
    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;     
    raycaster.setFromCamera( mouse, camera );           
    var intersects = raycaster.intersectObjects( objects );
    if ( intersects.length > 0 ) {
        if ( INTERSECTED != intersects[ 0 ].object ) {
            if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );          
            INTERSECTED = intersects[ 0 ].object;
            INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
            INTERSECTED.material.emissive.setHex( 0x22aa22 );               
        }
        container.style.cursor = 'pointer';         
    } else {
        if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
        INTERSECTED = null;
        container.style.cursor = 'auto';            
    }
}
function onDocumentTouchStart( event ) {                
    event.preventDefault();             
    event.clientX = event.touches[0].clientX;
    event.clientY = event.touches[0].clientY;
    onDocumentMouseDown( event );
}   
function onDocumentMouseDown( event ){
    event.preventDefault();
    mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
    raycaster.setFromCamera( mouse, camera );   
    var intersects = raycaster.intersectObjects( objects );
    TWEEN.removeAll();
    if ( intersects.length > 0 ) {  
        document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
        document.removeEventListener( 'mousedown', onDocumentMouseDown, false );            
        //controls.enabled = false;
        if (SELECTED) {     
            if (SELECTED == intersects[ 0 ].object) {
                if (opened == true) {
                    //same selected;
                    var angleSelectedInit = { x: SELECTED.rotation.x };
                    var angleSelectedEnd = { x: SELECTED.rotation.x - Math.PI/2 };                      
                    new TWEEN.Tween( angleSelectedInit ).to( angleSelectedEnd , 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                    SELECTED.rotation.x = angleSelectedInit.x ;
                    opened = false;
                    }).onComplete(function(){
                        addListeners();
                        SELECTED = 0; // so the new selected condition wont rotate the closed selected face
                    }).start();     
                }   
                if (opened == false) {
                    //same selected;
                    var angleSelectedInit = { x: SELECTED.rotation.x };
                    var angleSelectedEnd = { x: SELECTED.rotation.x + Math.PI/2 };                      
                    new TWEEN.Tween( angleSelectedInit ).to( angleSelectedEnd , 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                        //alert(SELECTED.rotation.x);
                    SELECTED.rotation.x = angleSelectedInit.x ;
                    opened = true;
                    }).onComplete(function(){
                        addListeners();
                    }).start();     
                }                   
            } else {
                if (SELECTED !== 0) {
                    var angleSelectedInit = { x: SELECTED.rotation.x };
                    var angleSelectedEnd = { x: SELECTED.rotation.x - Math.PI/2 };                      
                    new TWEEN.Tween( angleSelectedInit ).to( angleSelectedEnd , 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                        //alert(SELECTED.rotation.x);
                    SELECTED.rotation.x = angleSelectedInit.x ;
                    }).onComplete(function(){
                    }).start(); 
                }
                //new selected; 
                //INTERSECTED VAR IS USED ON MOUSEMOVE EVENT = intersects[ 0 ].object;              
                var angleSelectedInitNew = { x: intersects[ 0 ].object.rotation.x };
                var angleSelectedEndNew = { x: intersects[ 0 ].object.rotation.x + Math.PI/2 };         
                new TWEEN.Tween( angleSelectedInitNew ).to( angleSelectedEndNew , 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                    //alert(SELECTED.rotation.x);
                intersects[ 0 ].object.rotation.x = angleSelectedInitNew.x ;
                }).onComplete(function(){
                    SELECTED = intersects[ 0 ].object;
                    addListeners();
                }).start();             
            }
        } else {            
            //first time selected;
            SELECTED = intersects[ 0 ].object;
            var angleSelectedInit = { x: SELECTED.rotation.x };
            var angleSelectedEnd = { x: SELECTED.rotation.x + Math.PI/2 };          
            new TWEEN.Tween( angleSelectedInit ).to( angleSelectedEnd , 500).easing(TWEEN.Easing.Cubic.InOut).onUpdate(function(){
                //alert(SELECTED.rotation.x);
            SELECTED.rotation.x = angleSelectedInit.x ;
            opened = true;
            }).onComplete(function(){
                addListeners();
            }).start();     
        }       
    }   
}
function onDocumentMouseUp( event) {    
}
function addListeners() {
    document.addEventListener( 'mousemove', onDocumentMouseMove, false );
    document.addEventListener( 'mousedown', onDocumentMouseDown, false );   
}
function animate() {
    requestAnimationFrame( animate );
    render();
    stats.update();
    TWEEN.update();
    controls.update();
}
function render() {
    //cubeMenu.pivot.rotation.y += 0.02;
    renderer.render( scene, camera );               
}

我将场景保存在一个单独的文件中:

scenario.js

function createScenario(scene) {
    container = document.createElement( 'div' );
    document.body.appendChild( container );
    scene.add( new THREE.AmbientLight( 0x505050 ) );
    var light = new THREE.SpotLight( 0xffffff, 1.5 );
    light.position.set( 0, 500, 2000 );
    light.castShadow = true;
    light.shadowCameraNear = 200;
    light.shadowCameraFar = camera.far;
    light.shadowCameraFov = 50;
    light.shadowBias = -0.00022;
    light.shadowMapWidth = 1024;
    light.shadowMapHeight = 1024;
    scene.add( light );             
}

最重要的是,不要在tween中增加值。

我希望这个代码能帮助这个社区中的某个人。由于构建此代码的大多数答案都来自这里。

欢迎改进!

Thx。