Three.js r73 - 作为一个独特的物体来回旋转多个物体

时间:2015-12-11 15:58:22

标签: javascript 3d three.js

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

当我在一张脸上的MouseDown时,它应该在其x轴上旋转90度。而在MouseDown上再次出现相反的值,好像盒子面已关闭。

问题发生在补间上呈现的值不均匀。

下面是MouseDown函数:

    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。

1 个答案:

答案 0 :(得分:0)

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

目标:创建一个具有单独面的立方体,每个面单独打开。 此外,我得到了一个点,只有一个面打开。如果其他打开它应该关闭。距离我的项目有点远,因为当面部被点击时#34;洞场景会保持冻结,没有听众。面部在上面的代码中也有相同的根层次结构,如果你在一个盒子中进行细化,那么顶部和底部面将是背面的孩子(在我的项目案例中)。感觉不对。

当我不得不将面部旋转到补间时,我真的陷入了困境。第一个问题是在最初的面孔定位后,他们将保持他们的旋转价值。因此,框的左侧面将以rotation.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,这就解决了这个问题,但是随着rotation.order切换到面的顶部轴,这个选项变得无用,因此不需要为这种情况创建额外的矢量:

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);

        }
    }


}

然后我到了模板,这个问题的前一个代码,我遇到了为补间创建一个唯一值来处理所有面的问题。所有侧面都工作得非常好,因为它们的rotation.x值为0.顶面和底面给我的问题,因为它们的初始rotation.x值不同于0。 然后我开始将补间函数切换为初始值的增量。错误。在补间函数内增加角度是一场噩梦, 所以我开始研究罪和cos。我有时间在flash中使用3d菜单,而我必须将动态按钮映射到圆形位置。我记得有关代码语言的弧度值并没有像我们用度数计算的那样完全正常。而且我必须给旋转角度赋予固定值。几乎像递增和递减但没有在补间中给出的错误。

所以我设法通过创建2个变量来解决这个问题,一个获取所选对象的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 );             

}

底线,不要在补间内增加值。

我希望此代码可以帮助此社区中的某些人。由于构建此代码的大多数答案来自此处。

非常欢迎改进!

THX。