Add rhino3dm support

This commit is contained in:
hrntsm committed 2023-08-11 20:43:30 +09:00
1 parent cb212970d1
commit 1fe1f6bb09
2 files changed
+70 -25

No files matched your search

+45 -19
View File
@@ -3,6 +3,7 @@ import React, { useState } from "react"
import { Canvas } from "@react-three/fiber"
import { GizmoHelper, GizmoViewport, OrbitControls } from "@react-three/drei"
import { STLLoader } from "three/examples/jsm/loaders/STLLoader"
import { Rhino3dmLoader } from "three/examples/jsm/loaders/3DMLoader"
import { PerspectiveCamera } from "three"
interface ModelViewerProps {
@@ -10,6 +11,7 @@ interface ModelViewerProps {
width: string
height: string
hasGizmo: boolean
filetype: string | undefined
}
function CustomGizmoHelper(): JSX.Element {
@@ -24,23 +26,46 @@ function CustomGizmoHelper(): JSX.Element {
}
export function ModelViewer(props: ModelViewerProps): JSX.Element {
const [geometry, setGeometry] = useState<THREE.BufferGeometry>()
const [geometry, setGeometry] = useState<THREE.BufferGeometry[]>([])
const [modelSize, setModelSize] = useState<THREE.Vector3>()
React.useEffect(() => {
const loader = new STLLoader()
loader.load(props.src, (geometry: THREE.BufferGeometry) => {
if (geometry) {
setGeometry(geometry)
geometry.computeBoundingBox()
if (geometry.boundingBox === null) {
setModelSize(new THREE.Vector3(10, 10, 10))
} else {
const size = geometry.boundingBox.getSize(new THREE.Vector3())
setModelSize(size)
if ("stl" === props.filetype) {
const stlLoader = new STLLoader()
stlLoader.load(props.src, (stlMesh: THREE.BufferGeometry) => {
if (stlMesh) {
setGeometry([stlMesh])
stlMesh.computeBoundingBox()
if (stlMesh.boundingBox === null) {
setModelSize(new THREE.Vector3(10, 10, 10))
} else {
const size = stlMesh.boundingBox.getSize(new THREE.Vector3())
setModelSize(size)
}
}
}
})
})
} else if ("3dm" === props.filetype) {
const loader = new Rhino3dmLoader()
loader.setLibraryPath("https://cdn.jsdelivr.net/npm/rhino3dm@7.15.0/")
loader.load(props.src, (object: THREE.Object3D) => {
object.traverse(function (child) {
// rotate to y-up
child.rotateX(-Math.PI / 4)
})
const meshes = object.children as THREE.Mesh[]
const rhinoMeshes = meshes.map((mesh) => mesh.geometry)
if (rhinoMeshes.length > 0) {
setGeometry(rhinoMeshes)
rhinoMeshes[0].computeBoundingBox()
if (rhinoMeshes[0].boundingBox === null) {
setModelSize(new THREE.Vector3(10, 10, 10))
} else {
const size = rhinoMeshes[0].boundingBox.getSize(new THREE.Vector3())
setModelSize(size)
}
}
})
}
}, [])
const cameraPosition = modelSize
? [modelSize.x * 1.5, modelSize.y * 1.5, modelSize.z * 1.5]
@@ -51,8 +76,8 @@ export function ModelViewer(props: ModelViewerProps): JSX.Element {
: 45,
aspect: window.innerWidth / window.innerHeight,
near: 0.1,
position: new THREE.Vector3(...cameraPosition),
far: 1000,
position: cameraPosition,
}
return (
@@ -67,11 +92,12 @@ export function ModelViewer(props: ModelViewerProps): JSX.Element {
/>
{props.hasGizmo && <CustomGizmoHelper />}
<axesHelper />
{geometry && (
<mesh geometry={geometry}>
<meshNormalMaterial />
</mesh>
)}
{geometry.length > 0 &&
geometry.map((geo, index) => (
<mesh key={index} geometry={geo}>
<meshNormalMaterial />
</mesh>
))}
</Canvas>
)
}
+25 -6
View File
@@ -330,7 +330,9 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
const [openDeleteArtifactDialog, renderDeleteArtifactDialog] =
useDeleteArtifactDialog()
const [dragOver, setDragOver] = useState<boolean>(false)
const [open3dModelViewer, setOpen3dModelViewer] = useState<boolean>(false)
const [open3dModelViewer, setOpen3dModelViewer] = useState<{
[key: string]: boolean
}>({})
const width = "200px"
const height = "150px"
@@ -442,7 +444,10 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
</CardContent>
</Card>
)
} else if (a.filename.endsWith(".stl")) {
} else if (
a.filename.endsWith(".stl") ||
a.filename.endsWith(".3dm")
) {
return (
<Card
key={a.artifact_id}
@@ -468,6 +473,7 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
width={width}
height={height}
hasGizmo={false}
filetype={a.filename.split(".").pop()}
/>
</Box>
<CardContent
@@ -488,20 +494,32 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
{a.filename}
</Typography>
<IconButton
aria-label="shoe artifact 3d model"
aria-label="show artifact 3d model"
size="small"
color="inherit"
sx={{ margin: "auto 0" }}
onClick={() => {
setOpen3dModelViewer(true)
setOpen3dModelViewer(() => {
const obj = { ...open3dModelViewer }
obj[a.filename] = true
return obj
})
}}
>
<OpenWithIcon />
</IconButton>
<Modal
open={open3dModelViewer}
open={
a.filename in open3dModelViewer
? open3dModelViewer[a.filename]
: false
}
onClose={() => {
setOpen3dModelViewer(false)
setOpen3dModelViewer(() => {
const obj = { ...open3dModelViewer }
obj[a.filename] = false
return obj
})
}}
>
<Box
@@ -519,6 +537,7 @@ const TrialArtifact: FC<{ trial: Trial }> = ({ trial }) => {
width={`${innerWidth * 0.8}px`}
height={`${innerHeight * 0.8}px`}
hasGizmo={true}
filetype={a.filename.split(".").pop()}
/>
</Box>
</Modal>