簡體   English   中英

Three.js R73-將多個對象作為唯一對象來回旋轉

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

我有一個盒子的模擬,該盒子可以展開每個不同的面,每個面都是一個不同的幾何體,在幾何體的底部具有樞軸。

當我將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);
        }
    }

}

其他細節請給我一個消息。 謝謝。

我會盡量簡短,但我必須解釋一下自己的情況:

目的:創建一個具有單獨面的多維數據集,該多維數據集在每次單擊時分別打開。 此外,我得出的結論是,單擊時只能打開一張臉。 如果其他打開,則應關閉。 這距離我的項目有點遠,因為當“單擊”面部時,洞場景將保持凍結,沒有聽眾。 這些面在上面的代碼中也具有相同的根層次,如果在一個框內稀疏,那會感覺不對,其中頂面和底面將是背面的子級(在我的項目案例中)。

當我不得不在補間上旋轉臉部時,我真的被卡住了。 第一個問題是在初始面部定位之后,它們將保持其旋轉值。 因此,該框的左側將以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而給我帶來了問題。然后我開始將補間功能切換為初始值。 錯誤。 補間函數內部遞增角度是一場噩夢,因此我開始研究正弦和余弦。 我花了一些時間來制作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 );               
}

我將方案保存在單獨的文件中:

腳本

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

}

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

我希望這段代碼可以幫助該社區中的某人。 由於構建此代碼的大多數答案都來自此處。

歡迎改進!

謝謝。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM