ThreeJs功能演示——几何体操作导入导出
1、内部创建几何体导出编辑能力
1)支持内部创建的面、正方体、球体
内部创建物体时,如果是三维物体,要创建集合形状geometry,和对应的材质material。再一起创建一个三维物体。
// 存储创建的几何体列表
const geometries = [];
createPlane()
createCube()
createSphere()
addGUIForGeometry(geometries)
// 几何体创建函数
function createPlane() {
const geometry = new THREE.PlaneGeometry(1,1);
const material = new THREE.MeshBasicMaterial({color:0xffff00, side:THREE.DoubleSide})
const plane = new THREE.Mesh(geometry, material)
plane.name = "Plane";
plane.position.set(-1, -1, -1)
scene.add(plane)
geometries.push(plane)
return plane;
}
// 创建正方体
function createCube() {
const geometry = new THREE.BoxGeometry(1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( geometry, material );
cube.name = 'Cube';
scene.add( cube );
geometries.push(cube);
return cube;
}
// 创建球体
function createSphere() {
const geometry = new THREE.SphereGeometry( 1, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0x0000ff} );
const sphere = new THREE.Mesh( geometry, material );
sphere.name = 'Sphere';
sphere.position.set(2,2,2)
scene.add( sphere );
geometries.push(sphere);
return sphere;
}
2)支持几何体的位置、角度、比例调整
通过GUI控制器调整创建物体的位置、角度、放大比例信息。
// GUI控制器
function addGUIForGeometry(geometryArr) {
const gui = new GUI();
gui.add({x:0}, 'x', -10, 10).name('Position X').onChange((value)=>{
for (let element of geometryArr) {
console.log(element.position)
element.position.set(value, element.position.y, element.position.z)
}
animate()
});
gui.add({scale:1}, 'scale', 0.1, 10).name('Scale').onChange((value)=>{
for (let element of geometryArr) {
element.scale.set(value, value, value)
}
animate()
});
gui.add({rotateX:0}, "rotateX", -Math.PI, Math.PI).name("Rotate X").onChange((value)=>{
for (let element of geometryArr) {
element.rotation.set(value, element.rotation.y, element.rotation.z)
}
animate()
})
gui.open();
}
3)支持几何体批量导出、重新导入
将3D物体的位置、形状、角度、放大序列化到json文件,支持导出。
导入时,根据物体的类型分别创建3D模型
// 导入几何体
function importGeometries() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const geometriesData = JSON.parse(e.target.result);
geometriesData.forEach(data => {
let geometry;
let material;
switch (data.type) {
case 'Mesh':
switch (data.name) {
case 'Plane':
geometry = new THREE.PlaneGeometry(data.geometryData.vertices, data.geometryData.indices);
material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
break;
case 'Cube':
geometry = new THREE.BoxGeometry(data.geometryData.vertices, data.geometryData.indices);
material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
break;
case 'Sphere':
geometry = new THREE.SphereGeometry(data.geometryData.vertices, data.geometryData.indices);
material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
break;
}
const mesh = new THREE.Mesh(geometry, material);
mesh.name = data.name;
mesh.position.fromArray(data.position);
mesh.rotation.fromArray(data.rotation);
mesh.scale.fromArray(data.scale);
scene.add(mesh);
geometries.push(mesh);
break;
}
});
};
reader.readAsText(file);
}
});
input.click();
}
2、整体代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js 几何体操作示例</title>
<style>
body { margin: 0; overflow: hidden; }
#camera-info {
position: absolute;
top: 10px;
left: 10px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 10px;
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<div id="camera-info"></div>
<script type="importmap">
{
"imports": {
"three": "./three.js-master/build/three.module.js",
"three/addons/": "./three.js-master/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three"
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
// 1) 创建画布
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 2) 设置 camera 位置,朝向角度
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 20); // 设置相机位置
camera.lookAt(scene.position); // 让相机朝向场景中心
// 设置控制轨道
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 0.1, 0 );
controls.update();
controls.minDistance = 0.5;
controls.maxDistance = 1000;
controls.maxPolarAngle = 0.5 * Math.PI;
// 5) 支持动态显示摄像头位置、角度、缩放信息
const cameraInfo = document.getElementById('camera-info');
function updateCameraInfo() {
cameraInfo.innerHTML = `
摄像头信息:<br>
位置: (${camera.position.x.toFixed(2)}, ${camera.position.y.toFixed(2)}, ${camera.position.z.toFixed(2)})<br>
角度: (${camera.rotation.x.toFixed(2)}, ${camera.rotation.y.toFixed(2)}, ${camera.rotation.z.toFixed(2)})<br>
缩放: ${camera.zoom.toFixed(2)}
`;
}
updateCameraInfo();
// 渲染循环
function animate() {
requestAnimationFrame(animate);
updateCameraInfo();
renderer.render(scene, camera);
}
animate();
// 存储创建的几何体列表
const geometries = [];
createPlane()
createCube()
createSphere()
addGUIForGeometry(geometries)
// 几何体创建函数
function createPlane() {
const geometry = new THREE.PlaneGeometry(1,1);
const material = new THREE.MeshBasicMaterial({color:0xffff00, side:THREE.DoubleSide})
const plane = new THREE.Mesh(geometry, material)
plane.name = "Plane";
plane.position.set(-1, -1, -1)
scene.add(plane)
geometries.push(plane)
return plane;
}
// 创建正方体
function createCube() {
const geometry = new THREE.BoxGeometry(1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( geometry, material );
cube.name = 'Cube';
scene.add( cube );
geometries.push(cube);
return cube;
}
// 创建球体
function createSphere() {
const geometry = new THREE.SphereGeometry( 1, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0x0000ff} );
const sphere = new THREE.Mesh( geometry, material );
sphere.name = 'Sphere';
sphere.position.set(2,2,2)
scene.add( sphere );
geometries.push(sphere);
return sphere;
}
// GUI控制器
function addGUIForGeometry(geometryArr) {
const gui = new GUI();
gui.add({x:0}, 'x', -10, 10).name('Position X').onChange((value)=>{
for (let element of geometryArr) {
console.log(element.position)
element.position.set(value, element.position.y, element.position.z)
}
animate()
});
gui.add({scale:1}, 'scale', 0.1, 10).name('Scale').onChange((value)=>{
for (let element of geometryArr) {
element.scale.set(value, value, value)
}
animate()
});
gui.add({rotateX:0}, "rotateX", -Math.PI, Math.PI).name("Rotate X").onChange((value)=>{
for (let element of geometryArr) {
element.rotation.set(value, element.rotation.y, element.rotation.z)
}
animate()
})
gui.open();
}
function handleKeyDown(event) {
switch (event.key) {
case 'e':
exportToJSON(geometries)
break;
case 'r':
clearGeometries(geometries)
break;
case 'i':
importGeometries(geometries)
break;
}
}
document.addEventListener('keydown', handleKeyDown);
function exportToJSON(geometryList) {
const geometriesData = geometryList.map(geometry => {
return {
name: geometry.name,
position: geometry.position.toArray(),
rotation: geometry.rotation.toArray(),
scale: geometry.scale.toArray(),
// 根据几何体类型添加更多特定信息
type: geometry.type,
geometryData: geometry.geometry.toJSON()
};
});
const blob = new Blob([JSON.stringify(geometriesData)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'geometries.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 清除几何体
function clearGeometries(geoArr) {
geometries.forEach(geometry => scene.remove(geometry));
geometries.length = 0;
}
// 导入几何体
function importGeometries() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const geometriesData = JSON.parse(e.target.result);
geometriesData.forEach(data => {
let geometry;
let material;
switch (data.type) {
case 'Mesh':
switch (data.name) {
case 'Plane':
geometry = new THREE.PlaneGeometry(data.geometryData.vertices, data.geometryData.indices);
material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
break;
case 'Cube':
geometry = new THREE.BoxGeometry(data.geometryData.vertices, data.geometryData.indices);
material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
break;
case 'Sphere':
geometry = new THREE.SphereGeometry(data.geometryData.vertices, data.geometryData.indices);
material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
break;
}
const mesh = new THREE.Mesh(geometry, material);
mesh.name = data.name;
mesh.position.fromArray(data.position);
mesh.rotation.fromArray(data.rotation);
mesh.scale.fromArray(data.scale);
scene.add(mesh);
geometries.push(mesh);
break;
}
});
};
reader.readAsText(file);
}
});
input.click();
}
</script>
</body>
</html>