Я только начал с «три», «@react-three/drei» и «@react-three/fiber», которые помогают мне визуализировать челюсть/зубы в 3D. Результат такой, как я ожидал, за исключением 2-х точек, как показано на видео, я застрял в одной точке на одной оси для вращения. и второй момент заключается в том, что исходное положение не обращено к зубам, как я ожидаю.
Я добавил помощника топора, который поможет мне визуализировать то, что я делаю, и боюсь, что застрял из-за отсутствия пространственного восприятия этого проекта.
Это GIF-изображение начальной позиции и той части, где я застрял при вращении модели.
И вот мой текущий код:
/* eslint-disable */
import React, { useState, useRef, useEffect, useMemo } from 'react';
// libraries
import { Canvas, useThree, useLoader } from '@react-three/fiber';
import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
import * as THREE from 'three';
// mui
import { Button, Stack } from '@mui/material';
// ----------------------------------------------------------------------
const STLModel = ({ geometry }) => {
useEffect(() => {
if (geometry) {
geometry.computeVertexNormals();
}
}, [geometry]);
return (
<mesh geometry = {geometry} receiveShadow castShadow>
<meshPhongMaterial
color = "#078DEE"
specular = "#ffffff"
shininess = {100}
side = {THREE.DoubleSide}
receiveShadow
castShadow
/>
</mesh>
);
};
// ----------------------------------------------------------------------
const Scene = ({ geometries, currentFileIndex, cameraPosition, onCameraChange }) => {
const controlsRef = useRef();
const { camera } = useThree();
useEffect(() => {
if (controlsRef.current) {
controlsRef.current.addEventListener('change', () => {
onCameraChange({
position: camera.position.toArray(),
target: controlsRef.current.target.toArray(),
});
});
// I Removed this two line to allow full rotation, but it did not work
// controlsRef.current.maxPolarAngle = Math.PI;
// controlsRef.current.minPolarAngle = 0;
controlsRef.current.enablePan = true;
}
}, [camera, onCameraChange]);
useEffect(() => {
if (cameraPosition && controlsRef.current) {
camera.position.fromArray(cameraPosition.position);
controlsRef.current.target.fromArray(cameraPosition.target);
controlsRef.current.update();
} else {
camera.position.set(0, 0, 100);
controlsRef.current.target.set(0, 0, 0);
controlsRef.current.update();
}
}, [camera, cameraPosition]);
return (
<>
<PerspectiveCamera makeDefault position = {[0, 0, 100]} />
<STLModel geometry = {geometries[currentFileIndex]} />
<OrbitControls ref = {controlsRef} />
<ambientLight intensity = {0.5} />
<pointLight position = {[10, 10, 10]} intensity = {0.8} />
<pointLight position = {[-10, -10, -10]} intensity = {0.5} />
<directionalLight position = {[0, 0, 5]} intensity = {0.5} />
<axesHelper scale = {[10, 10, 10]} />
</>
);
};
// ----------------------------------------------------------------------
const TreatmentPlan = () => {
const [currentFileIndex, setCurrentFileIndex] = useState(0);
const [cameraPosition, setCameraPosition] = useState(null);
const url = "https://myurl/";
const fileUrls = useMemo(() => Array.from({ length: 15 }, (_, i) => `${url}file${i + 1}.stl`), [url]);
const geometries = useLoader(STLLoader, fileUrls);
const handleNext = () => {
setCurrentFileIndex((prevIndex) => (prevIndex + 1) % geometries.length);
};
const handlePrevious = () => {
setCurrentFileIndex((prevIndex) => (prevIndex - 1 + geometries.length) % geometries.length);
};
return (
<div>
<Stack direction = "row" spacing = {2} mb = {2}>
<Button variant = "contained" onClick = {handlePrevious} disabled = {geometries.length === 0}>Previous</Button>
<Button variant = "contained" onClick = {handleNext} disabled = {geometries.length === 0}>Next</Button>
</Stack>
{geometries.length > 0 && (
<div style = {{ width: '50vw', height: '50vh' }}>
<Canvas>
<Scene
geometries = {geometries}
currentFileIndex = {currentFileIndex}
cameraPosition = {cameraPosition}
onCameraChange = {setCameraPosition}
/>
</Canvas>
</div>
)}
</div>
);
};
export default TreatmentPlan;
Заранее благодарю за любую помощь/совет
🤔 А знаете ли вы, что...
JavaScript поддерживает асинхронное программирование с использованием промисов и асинхронных функций.
Проблема, с которой вы столкнулись, связана не с вашим кодом, а только с ограничениями и философией OrbitControls
. OC поддерживает постоянный восходящий вектор, отсюда и ограничение. То, о чем вы пишете, или конкретно то, что вы ожидаете, можно найти в TrackballControls
, где этот вектор не является постоянным. Кроме того, как показывает ваш код, не имеет значения, удаляете ли вы закомментированные строки, поскольку они являются значениями по умолчанию.
TrackballControls похож на OrbitControls. Однако это не поддерживать постоянный вектор камеры вверх. Это означает, что если камера вращается по орбите над «северным» и «южным» полюсами он не переворачивается, чтобы оставаться «правым» стороной вверх".
В этой теме Pailhead довольно... наглядно объяснил эту проблему.
https://discourse.threejs.org/t/solved-orbitcontrols-max-polar-angle-infinity/2629
Взгляните на разницу
ОрбитаУправление
<script type = "importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
}
}
</script>
<script type = "module">
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
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 planeGeometry = new THREE.PlaneGeometry(5, 5);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
camera.position.z = 5;
const controls = new OrbitControls(camera, renderer.domElement);
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
ТрекболУправление
<script type = "importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
}
}
</script>
<script type = "module">
import * as THREE from "three";
import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
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 planeGeometry = new THREE.PlaneGeometry(5, 5);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
camera.position.z = 5;
const controls = new TrackballControls(camera, renderer.domElement);
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
Просто попробуйте переключить OrbitControls на TrackballControls, возможно, вы получите то, что ожидаете.
import { TrackballControls } from '@react-three/drei';
//…
<TrackballControls />