一键复原,即恢复模型位置,旋转角度,缩放
// 假设 cube是你要复制的网格
const newMesh = cube.clone();
cube.position.copy(newMesh.position);
cube.rotation.copy(newMesh.rotation);
cube.scale.copy(newMesh.scale);
import { useState, useEffect, Component } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import * as THREE from 'three';
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 引入指针锁定控制器扩展库PointerLockControls.js
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'
// 导入动画库
import gsap from "gsap";
//导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
// function App() {
// useEffect(() => {
// const scene = new THREE.Scene();
// const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// //创建渲染器
// const renderer = new THREE.WebGLRenderer();
// renderer.setSize(window.innerWidth, window.innerHeight);
// document.body.appendChild(renderer.domElement);
// //创建几何体
// const geometry = new THREE.BoxGeometry(1, 1, 1);
// //创建材质
// const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// //创建网格=几何体+材质
// const cube = new THREE.Mesh(geometry, material);
// //将网格添加到场景
// scene.add(cube);
// //正对着我们的是z轴
// camera.position.z = 5;
// //默认看向原点
// camera.lookAt(0, 0, 0);
// //创建轨道控制器
// //const controls = new OrbitControls(camera, renderer.domElement);
// function animate() {
// //播放下一帧,继续调用animate函数
// requestAnimationFrame(animate);
// //旋转
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
// //渲染
// renderer.render(scene, camera);
// }
// //调用函数
// animate();
// }, [])
// return (
// <>
// <div className='App'></div>
// </>
// )
// }
class App extends Component {
render() {
return <div></div>
}
componentDidMount() {
// const scene = new THREE.Scene();
// const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// //创建渲染器
// const renderer = new THREE.WebGLRenderer();
// renderer.setSize(window.innerWidth, window.innerHeight);
// document.body.appendChild(renderer.domElement);
// //创建几何体
// const geometry = new THREE.BoxGeometry(1, 1, 1);
// //创建材质
// const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// //创建网格=几何体+材质
// const cube = new THREE.Mesh(geometry, material);
// //将网格添加到场景
// scene.add(cube);
// //正对着我们的是z轴
// camera.position.z = 5;
// //默认看向原点
// camera.lookAt(0, 0, 0);
// //创建轨道控制器
// //const controls = new OrbitControls(camera, renderer.domElement);
// function animate() {
// //播放下一帧,继续调用animate函数
// requestAnimationFrame(animate);
// //旋转
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
// //渲染
// renderer.render(scene, camera);
// }
// //调用函数
// animate();
// console.log(THREE);
// 设置场景、相机
const scene = new THREE.Scene();
// 设置场景颜色为蓝色
scene.background = new THREE.Color(0x0000ff);
//透视投影(透视投影会使得远离中心的物体看起来扭曲或拉伸,这是一个不可避免的数学事实)
//const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//解决办法是使用正交投影:
const camera = new THREE.OrthographicCamera(window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1, 1000);
//设置相机的缩放比例(正交投影会使得物体变得非常小,所以你需要设置一下缩放比例,比如放大一百倍)
camera.zoom = 100;
// 更新相机的投影矩阵(这句话好像不用加)
camera.updateProjectionMatrix();
// 设置相机位置
//camera.position.set(0, 0, 10);
//camera.position.set(0, 15, 70);
//camera.position.z = -6;
camera.position.y = 6;
camera.position.x = -6;
scene.add(camera);
//渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 监听窗口大小变化事件
window.addEventListener('resize', function () {
var width = window.innerWidth;
var height = window.innerHeight;
//1. 更新渲染器大小
renderer.setSize(width, height);
//2.更新相机的宽高比
camera.aspect = width / height;
// 设置渲染器的像素比(这个好像可有可无,不过加上确实更好一点,拉伸更自然了)
renderer.setPixelRatio(window.devicePixelRatio);
// 3.更新相机的投影矩阵
camera.updateProjectionMatrix();
});
/********************************************/
//1.创建纹理加载器
let textureLoader = new THREE.TextureLoader();
//2.加载纹理
//let texture = textureLoader.load("./picture/52ec610d2c388ddc9c07f02027231f1.png",
// let texture = textureLoader.load(
// '/root/myspace/three/react-three-app/vite-project/src/picture/52ec610d2c388ddc9c07f02027231f1.png',
let texture = textureLoader.load(
'./src/picture/2.png',
// onLoad回调
function (texture) {
// in this example we create the material when the texture is loaded
console.log('Texture loaded successfully.');
},
// 目前暂不支持 onProgress 的回调
undefined,
// onError回调
function (err) {
console.log('An error happened while loading the texture.');
console.log('Error details:{{{[', err, ']}}}');
}
);
/********************************************/
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
//材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
map: texture,
});
//设置材质为线框模式
material.wireframe = false;
const cube = new THREE.Mesh(geometry, material);
//cube.position.x = 2;
//等价于
cube.position.set(0, 0, 0);
// 假设 mesh0 是你要复制的网格
const newMesh = cube.clone();
scene.add(cube);
/**************/
// 创建轨道控制器(此时你可以使用右键让模型动起来)
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE, // 左键旋转
MIDDLE: THREE.MOUSE.PAN, // 中键平移
RIGHT: THREE.MOUSE.DOLLY // 右键缩放
};
controls.enableDamping = true;
//设置阻尼的系数
controls.dampingFactor = 0.05;
//让其主动的旋转
//controls.autoRotate = true;
// 添加坐标轴辅助器(相当于一个线段,看成一个物件)
const axesHelper = new THREE.AxesHelper(5);//线段的长度为5
scene.add(axesHelper);//添加到场景中
// 设置时钟
const clock = new THREE.Clock();
/**************/
// 鼠标交互
let isDragging = false;
let previousMousePosition = {
x: 0,
y: 0
};
function onDocumentMouseDown(event) {
isDragging = true;
}
function onDocumentMouseMove(event) {
if (isDragging) {
var deltaMove = {
x: event.clientX - previousMousePosition.x,
y: event.clientY - previousMousePosition.y
};
const deltaRotationQuaternion = new THREE.Quaternion()
.setFromEuler(new THREE.Euler(
toRadians(deltaMove.y * 1),
toRadians(deltaMove.x * 1),
0,
'XYZ'
));
cube.quaternion.multiplyQuaternions(deltaRotationQuaternion, cube.quaternion);
}
previousMousePosition = {
x: event.clientX,
y: event.clientY
};
}
function onDocumentMouseUp(event) {
isDragging = false;
}
// 将鼠标事件监听器添加到渲染器的DOM元素
// renderer.domElement.addEventListener('mousedown', onDocumentMouseDown, false);
// renderer.domElement.addEventListener('mousemove', onDocumentMouseMove, false);
// renderer.domElement.addEventListener('mouseup', onDocumentMouseUp, false);
// 动画循环渲染
function animate() {
// 如果没有鼠标交互,立方体会自动旋转
if (!isDragging) {
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
}
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate(); // 开始动画循环
// 辅助函数:将角度转换为弧度
function toRadians(angle) {
return angle * (Math.PI / 180);
}
//lil-gui
let eventObj = {
Fullscreen: function () {
renderer.domElement.requestFullscreen();
gui.show();
},
ExitFullscreen: function () {
document.exitFullscreen();
gui.show();
}
};
//创建GUI
const gui = new GUI();
//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
gui.close();
// 创建一个折叠的文件夹
const folder = gui.addFolder('全屏设置');
// 添加按钮到折叠文件夹
folder.add(eventObj, 'Fullscreen').name('全屏');
folder.add(eventObj, 'ExitFullscreen').name('退出全屏');
// 关闭折叠面板
folder.close();
const folder1 = gui.addFolder('立方体位置设置');
folder1.add(cube.position, "x", -5, 5).name("立方体x轴位置").onChange((val) => {
console.log("cube's x position:", val);
});
folder1.add(cube.position, "y", -5, 5).name("立方体y轴位置");
folder1.add(cube.position, "z", -5, 5).name("立方体z轴位置");
//.min(-10).max(10).step(1)
folder1.close();
const folder2 = gui.addFolder('线框模式设置');
folder2.add(material, "wireframe").name("线框模式");
folder2.close();
const folder3 = gui.addFolder('颜色设置');
let colorParams = {
cubeColor: "#ff0000",
};
folder3.addColor(colorParams, "cubeColor")
.name("颜色变化")
.onChange((val) => {
cube.material.color.set(val);
});
folder3.close();
window.addEventListener('mouseup', function () {
console.log('鼠标按下');
cube.position.x += 1;
cube.rotateY(Math.PI / 3);
cube.scale.x = 2; // 在 x 轴方向上缩放为原来的两倍
cube.scale.y = 0.5; // 在 y 轴方向上缩放为原来的一半
cube.scale.z = 1.5; // 在 z 轴方向上缩放为原来的1.5倍
renderer.render(scene, camera);
});
// 添加HTML按钮
const button = document.getElementById('myButton');
button.addEventListener('click', function () {
console.log('Button Clicked!');
// // 将新网格对象添加到场景中
// scene.add(newMesh);
// // 从场景中删除要替换的网格对象
// scene.remove(cube);
cube.position.copy(newMesh.position);
cube.rotation.copy(newMesh.rotation);
cube.scale.copy(newMesh.scale);
// 渲染场景
renderer.render(scene, camera);
});
}
}
export default App
答:不是,需要先建立一个THREE.Vector3对象,然后再调用copy函数!
var startPoint = new THREE.Vector3();
startPoint.copy(axesHelper1.position);
console.log("axesHelper1:", axesHelper1.position);
console.log("startPoint:", startPoint);
var objects = [];
//创建射线
const raycaster = new THREE.Raycaster();
//创建鼠标向量
const mouse = new THREE.Vector2();
//存放射线可射的范围
let intersects;
上面两行放全局变量
/下面两行放在mousedown鼠标事件中
//通过摄像机和鼠标为位置,更新射线
raycaster.setFromCamera(mouse, camera);
//计算物体和射线的焦点
intersects = raycaster.intersectObjects(objects);
scene.add(axesHelper1);//将箭头加入到场景中
//将箭头取消在我们的选中范围
objects.push(axesHelper1);
// 射线检测
intersects = raycaster.intersectObjects(objects);
scene.remove(ZTubeMesh);//将箭头"隐藏"到场景中
console.log('删掉了,兄弟们!');//调试代码
console.log('objects:', objects);//调试代码
objects.pop(ZTubeMesh);//将管道Z取消在我们的选中范围
console.log('objects:', objects);//调试代码
// 射线检测
intersects = raycaster.intersectObjects(objects);
注意,如果你不写下面这两行,你就会发现该模型在场景中消失了,但是还能点到(这就是误触!)
objects.pop(ZTubeMesh);//将管道Z取消在我们的选中范围
// 射线检测
intersects = raycaster.intersectObjects(objects);
下面这种方法算是初步实现,具体细节还需进行优化
// 初始化鼠标坐标
var mouseX = 0;
var mouseY = 0;
var m_firstMouse = true;
window.addEventListener('mousemove', function (event) {
console.log('鼠标移动');
console.log('objects:', objects);
/****************************
* //2024-01-09
*/
if (hasTeethSelected && mouseDown) {
//有牙齿被选中了,且鼠标已经按下了
if (event.buttons === 1)//左键旋转
{
//如果是第一次进来则记录一下当前位置
if (m_firstMouse) {
// 更新鼠标坐标
mouseX = event.clientX;
mouseY = event.clientY;
m_firstMouse = false;
}
// 计算鼠标坐标差值
var deltaX = event.clientX - mouseX;
var deltaY = event.clientY - mouseY;
console.log('x:', deltaX, 'y:', deltaY);
/***
* 得到模型的中心 center
*/
const boundingBox = new THREE.Box3().setFromObject(selectedObject);
const center = new THREE.Vector3();
boundingBox.getCenter(center);
// 更新鼠标坐标
mouseX = event.clientX;
mouseY = event.clientY;
// 创建矩阵进行旋转
var matrix = new THREE.Matrix4();
// 根据鼠标坐标差值进行旋转
matrix.makeRotationY(deltaX * 0.005); // Y轴旋转//左右拖动
matrix.multiply(new THREE.Matrix4().makeRotationX(deltaY * 0.005)); // X轴旋转//上下拖动
selectedObject.position.sub(center);//减去自身中心坐标,现在就回到了原点
// 应用变换矩阵到立方体
selectedObject.applyMatrix4(matrix);
selectedObject.position.add(center);//加上自身中心坐标,现在就回到了原来的点
}
}
});
这里我要强掉一下,只要你是移动场景中的"某一个物体",你就要像下面这个逻辑一样,先减去自身中心点,再添加自身中心点
selectedObject.position.sub(center);//减去自身中心坐标,现在就回到了原点
// 应用变换矩阵到立方体
selectedObject.applyMatrix4(matrix);
selectedObject.position.add(center);//加上自身中心坐标,现在就回到了原来的点