Three.js r73-将多个对象作为唯一对象来回旋转
Three.js r73 - rotating multiple objects back and forth as a unique object
我模拟了一个盒子,它展开了每个不同的面,每个面都是一个不同的几何体,在几何体的底部有枢轴。
当我在一个面上拖动鼠标时,它应该在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。
- 如何从数组中查找唯一对象
- JavaScript:从对象数组中获取唯一值及其计数
- JavaScript-用唯一属性标识对象
- “new”关键字是在构造对象时自动设置“constructor”属性的唯一方法吗
- 挖空映射 - 使用键唯一标识嵌套对象
- 映射对象数组,并且仅采用唯一
- 在键值对的唯一组合上合并对象
- 不同对象之间的递归,并将它们唯一地组合在一起,不重复
- 对具有唯一值的对象键的数组进行排序
- 基于单个属性从数组中获取唯一对象
- 将唯一对象推送到JavaScript数组
- 为客户端和服务器上的对象生成唯一 ID
- 在 Firebase 中通过对象的唯一 id 查找对象
- 按唯一键将对象数组排序为数组
- 通过将参数传递给我们的构造函数来创建唯一的对象
- 从多个对象获取唯一键
- 获取对象的唯一标识符
- 从对象数组中获取唯一元素数组
- 基于两个属性从 javascript 中的对象数组中获取唯一数组
- 对象数组中的 JavaScript 唯一键值