Разрешить полное вращение модели в Three с React

Я только начал с «три», «@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;

Заранее благодарю за любую помощь/совет

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема, с которой вы столкнулись, связана не с вашим кодом, а только с ограничениями и философией 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 />

Большое спасибо, это именно то решение, которое я искал! Определенно есть чем заняться на Three

Yohav Rn 06.07.2024 17:41

Другие вопросы по теме