| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- import { useThree } from '@react-three/fiber'
- import React, { useCallback, useEffect, useRef, useState } from 'react'
- import * as THREE from 'three'
- import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
- function Model() {
- const groupRef = useRef<THREE.Group>(null)
- const [loadingProgress, setLoadingProgress] = useState(0)
- const [model, setModel] = useState<THREE.Group | null>(null)
- const [modelParts, setModelParts] = useState<{ [name: string]: THREE.Object3D }>({})
- const [activePart, setActivePart] = useState<string | null>(null)
- const { camera, scene } = useThree()
- const handleModelLoaded = useCallback(
- (loadedModel: THREE.Group, parts: { [name: string]: THREE.Object3D }) => {
- setModel(loadedModel)
- setModelParts(parts)
- // 初始状态下显示所有模型
- Object.values(parts).forEach(part => {
- part.visible = true
- })
- scene.add(loadedModel)
- },
- [scene]
- )
- useEffect(() => {
- const manager = new THREE.LoadingManager()
- manager.onProgress = (url, itemsLoaded, itemsTotal) => {
- setLoadingProgress((itemsLoaded / itemsTotal) * 100)
- }
- const dracoLoader = new DRACOLoader()
- dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
- dracoLoader.setDecoderConfig({ type: 'js' })
- const loader = new GLTFLoader(manager)
- loader.setDRACOLoader(dracoLoader)
- loader.load(
- `${serverUrl}/0/model.glb`, // 替换为你的GLB模型路径
- gltf => {
- const model = gltf.scene
- // 设置模型初始位置 (x, y, z)
- model.position.set(0, 0, 0) // 设置为场景原点
- // // 设置模型初始缩放 (x, y, z)
- model.scale.set(0.003, 0.003, 0.003) // 原始大小设
- // // 设置模型初始旋转(单位:弧度)(x, y, z)
- model.rotation.set(0, 0, 0) // 例如,不旋转。绕Y轴旋转90度可设为(0, Math.PI / 2, 0)
- model.traverse((child: any) => {
- if (child.isMesh) {
- child.castShadow = true
- child.receiveShadow = true
- }
- })
- // 3. 假设模型中的三个部分分别命名为'bs1', 'bs2', 'bs3'
- const parts: { [name: string]: THREE.Object3D } = {}
- model.children.forEach(child => {
- if (['bs1', 'bs2', 'bs3'].includes(child.name)) {
- parts[child.name] = child
- }
- })
- setModelParts(parts)
- handleModelLoaded(model, parts)
- },
- xhr => {
- // 加载进度回调已在LoadingManager中处理
- },
- error => {
- console.error('加载模型出错:', error)
- }
- )
- }, [handleModelLoaded])
- // 点击不同部位
- const focusOnPart = useCallback(
- (partName: string) => {
- if (!model || !modelParts[partName]) return
- // 隐藏所有部分,然后显示选中的部分
- Object.entries(modelParts).forEach(([name, part]) => {
- part.visible = name === partName
- })
- // 获取选中部分的包围盒并居中相机
- const bbox = new THREE.Box3().setFromObject(modelParts[partName])
- const center = bbox.getCenter(new THREE.Vector3())
- const size = bbox.getSize(new THREE.Vector3())
- // 计算相机位置,使其能够完整看到模型
- const maxDim = Math.max(size.x, size.y, size.z)
- const fov =
- camera instanceof THREE.PerspectiveCamera ? (camera.fov * Math.PI) / 180 : 0
- const cameraDistance = maxDim / (2 * Math.tan(fov / 2))
- camera.position.copy(
- center.clone().add(new THREE.Vector3(0, 0, cameraDistance * 1.5))
- )
- camera.lookAt(center)
- setActivePart(partName)
- },
- [camera, model, modelParts]
- )
- // 显示全部模型
- const showAllModels = useCallback(() => {
- if (!model) return
- // 显示所有模型部分
- Object.values(modelParts).forEach(part => {
- part.visible = true
- })
- // 将相机定位到整个模型的中心
- const bbox = new THREE.Box3().setFromObject(model)
- const center = bbox.getCenter(new THREE.Vector3())
- const size = bbox.getSize(new THREE.Vector3())
- const maxDim = Math.max(size.x, size.y, size.z)
- const fov =
- camera instanceof THREE.PerspectiveCamera ? (camera.fov * Math.PI) / 180 : 0
- const cameraDistance = maxDim / (2 * Math.tan(fov / 2))
- camera.position.copy(
- center.clone().add(new THREE.Vector3(0, 0, cameraDistance * 1.5))
- )
- camera.lookAt(center)
- setActivePart(null)
- }, [camera, model, modelParts])
- return <>{groupRef.current ? <primitive object={groupRef.current} /> : null}</>
- }
- const MemoModel = React.memo(Model)
- export default MemoModel
|