[英]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.