Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add subdivide support #37

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ You can see working the examples from `.storybook/stories` [here](https://gabote
| `onFinishLoading` | `(ev: ModelDimensions) => any`| `false` | callback triggered when Stl is fully loaded |
| `onError` | `(err: Error) => any` | `false` | callback triggered when an error occurred while loading Stl|
| `canvasId` | `string` | `false` | id of the canvas element used for rendering the model |
| `subdivide` | `number` | `0` |subdivide-level, subdivide modifier ([support by three-subdivide](https://github.com/stevinz/three-subdivide)) iterations, total passes of subdivision to apply, generally between 1 to 5, more see [`SubdivideProps`](#subdivideprops)|
The component also accepts ```<div/>``` props

## Interfaces
Expand Down Expand Up @@ -95,3 +96,18 @@ The component also accepts ```<div/>``` props
| `width` | `number` | width of the 3d object |
| `height` | `number` | height of the 3d object |
| `length` | `number` | length of the 3d object |


### SubdivideProps
`react-stl-viewer` support subdivide modifier, you can smaller file sizes mesh to render smoother effects. So, why we need this? [-> more details #37](https://github.com/gabotechs/react-stl-viewer/pull/37#issuecomment-1704653068)


ref: [three-subdivide](https://github.com/stevinz/three-subdivide#modify)
| Prop | Type | Required | Notes |
| ---------------------- | :------------------------: | :----------: | :----------------------------------------------------------: |
| `split` | `boolean` |`false` | split coplanar faces at their shared edges before subdividing? |
| `uvSmooth` | `boolean` |`false` | smooth UV coordinates during subdivision?|
| `preserveEdges` | `boolean` |`false` | should edges / breaks in geometry be ignored during subdivision?|
| `flatOnly` | `boolean` |`false` | subdivide triangles but do not apply smoothing?|
| `maxTriangles` | `number` |`false` | limits subdivision to meshes with less than this number of triangles, default is `Infinity`|

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
},
"dependencies": {
"@react-three/fiber": "8.9.1",
"three-stdlib": "2.17.2"
"three-stdlib": "2.17.2",
"three-subdivide": "^1.1.5"
},
"peerDependencies": {
"react": ">=18.0",
Expand Down
109 changes: 68 additions & 41 deletions src/StlViewer/SceneSetup.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { CSSProperties, useEffect, useState } from 'react'
import React, { CSSProperties, useEffect, useState, useMemo } from 'react'
import { useFrame, useLoader } from '@react-three/fiber'
import { STLLoader } from 'three-stdlib/loaders/STLLoader'
import { Box3, Color, Group, Mesh } from 'three'
Expand All @@ -8,6 +8,7 @@ import Floor from './SceneElements/Floor'
import Lights from './SceneElements/Lights'
import Camera, { CameraInitialPosition } from './SceneElements/Camera'
import OrbitControls from './SceneElements/OrbitControls'
import { LoopSubdivision } from 'three-subdivide'

const INITIAL_LATITUDE = Math.PI / 8
const INITIAL_LONGITUDE = -Math.PI / 8
Expand Down Expand Up @@ -46,6 +47,16 @@ export interface SceneSetupProps {
onFinishLoading?: (ev: ModelDimensions) => any
modelProps?: ModelProps
floorProps?: FloorProps
subdivide?: number
subdivideProps?: SubdivideProps
}

export interface SubdivideProps {
split: boolean
uvSmooth: boolean
preserveEdges: boolean
flatOnly: boolean
maxTriangles: number
}

gabotechs marked this conversation as resolved.
Show resolved Hide resolved
const SceneSetup: React.FC<SceneSetupProps> = (
Expand All @@ -55,7 +66,8 @@ const SceneSetup: React.FC<SceneSetupProps> = (
shadows = false,
showAxes = false,
orbitControls = false,
onFinishLoading = () => {},
onFinishLoading = () => { },
subdivide = 0,
modelProps: {
ref,
scale = 1,
Expand All @@ -69,7 +81,14 @@ const SceneSetup: React.FC<SceneSetupProps> = (
floorProps: {
gridWidth,
gridLength
} = {}
} = {},
subdivideProps = {
split: true,
uvSmooth: false,
preserveEdges: false,
flatOnly: false,
maxTriangles: Infinity
}
}
) => {
const [mesh, setMesh] = useState<Mesh>()
Expand All @@ -95,11 +114,19 @@ const SceneSetup: React.FC<SceneSetupProps> = (
(loader) => loader.setRequestHeader(extraHeaders ?? {})
)

function onLoaded (dims: ModelDimensions, mesh: Mesh): void {
const subdividedGeometry = useMemo(
() => subdivide > 0
? LoopSubdivision.modify(geometry, subdivide, subdivideProps)
: undefined,
[geometry, subdivide]
gabotechs marked this conversation as resolved.
Show resolved Hide resolved
)


function onLoaded(dims: ModelDimensions, mesh: Mesh): void {
setMesh(mesh)
const { width, length, height, boundingRadius } = dims
setMeshDims(dims)
setModelCenter([positionX ?? width/2, positionY ?? length/2, height/2])
setModelCenter([positionX ?? width / 2, positionY ?? length / 2, height / 2])
const maxGridDimension = Math.max(gridWidth ?? 0, gridLength ?? 0)
const distance = maxGridDimension > 0
? maxGridDimension
Expand Down Expand Up @@ -128,49 +155,49 @@ const SceneSetup: React.FC<SceneSetupProps> = (
const mesh = scene.getObjectByName('mesh') as Mesh
const group = scene.getObjectByName('group') as Group
const bbox = new Box3().setFromObject(mesh)
const height = bbox.max.z-bbox.min.z
group.position.z = height/2
const height = bbox.max.z - bbox.min.z
group.position.z = height / 2
})

const modelPosition: [number, number, number] = [
positionX ?? (meshDims.width*scale)/2,
positionY ?? (meshDims.length*scale)/2,
positionX ?? (meshDims.width * scale) / 2,
positionY ?? (meshDims.length * scale) / 2,
0
]

return (
<>
<scene background={BACKGROUND}/>
{sceneReady && showAxes && <axesHelper scale={[50, 50, 50]}/>}
{(cameraInitialPosition != null) && <Camera
initialPosition={cameraInitialPosition}
center={modelCenter}
/>}
<Model3D
name={'group'}
meshProps={{ name: 'mesh' }}
scale={scale}
geometry={geometry}
position={modelPosition}
rotation={[rotationX, rotationY, rotationZ]}
visible={sceneReady}
materialProps={{ color }}
onLoaded={onLoaded}
/>
<Floor
width={gridWidth ?? gridLength}
length={gridLength ?? gridWidth}
visible={sceneReady}
noShadow={!shadows}
offset={FLOOR_DISTANCE}
/>
<Lights
distance={LIGHT_DISTANCE}
offsetX={modelPosition[0]}
offsetY={modelPosition[1]}
/>
{sceneReady && orbitControls && <OrbitControls target={modelCenter} />}
</>
<>
<scene background={BACKGROUND} />
{sceneReady && showAxes && <axesHelper scale={[50, 50, 50]} />}
{(cameraInitialPosition != null) && <Camera
initialPosition={cameraInitialPosition}
center={modelCenter}
/>}
<Model3D
name={'group'}
meshProps={{ name: 'mesh' }}
scale={scale}
geometry={subdivide > 0 ? subdividedGeometry : geometry}
position={modelPosition}
gabotechs marked this conversation as resolved.
Show resolved Hide resolved
rotation={[rotationX, rotationY, rotationZ]}
visible={sceneReady}
materialProps={{ color }}
onLoaded={onLoaded}
/>
<Floor
width={gridWidth ?? gridLength}
length={gridLength ?? gridWidth}
visible={sceneReady}
noShadow={!shadows}
offset={FLOOR_DISTANCE}
/>
<Lights
distance={LIGHT_DISTANCE}
offsetX={modelPosition[0]}
offsetY={modelPosition[1]}
/>
{sceneReady && orbitControls && <OrbitControls target={modelCenter} />}
</>
)
}

Expand Down
6 changes: 5 additions & 1 deletion src/StlViewer/StlViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const StlViewer: React.FC<StlViewerProps> = (
shadows,
showAxes,
orbitControls,
subdivide,
subdivideProps,
...otherProps
}
) => {
Expand All @@ -35,7 +37,9 @@ const StlViewer: React.FC<StlViewerProps> = (
onFinishLoading,
shadows,
showAxes,
orbitControls
orbitControls,
subdivide,
subdivideProps
}

return (
Expand Down
2 changes: 1 addition & 1 deletion src/StlViewer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export { ModelDimensions } from './SceneElements/Model3D'
export { CameraInitialPosition } from './SceneElements/Camera'
export { default as StlViewer } from './StlViewer'
export type { StlViewerProps } from './StlViewer'
export type { ModelProps, ModelRef, FloorProps } from './SceneSetup'
export type { ModelProps, ModelRef, FloorProps, SubdivideProps } from './SceneSetup'
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13881,6 +13881,7 @@ __metadata:
rollup-plugin-typescript2: ^0.34.1
three: ^0.133.0
three-stdlib: 2.17.2
three-subdivide: ^1.1.5
ts-jest: ^28.0.5
typescript: ^4.7.3
peerDependencies:
Expand Down Expand Up @@ -15774,6 +15775,15 @@ __metadata:
languageName: node
linkType: hard

"three-subdivide@npm:^1.1.5":
version: 1.1.5
resolution: "three-subdivide@npm:1.1.5"
peerDependencies:
three: ">= 0.125.0"
checksum: 5459b49943c9211ffaa0375ff8562692e9684f48459397a7215120da4f506429d8febbcf97faf934df9344121cf7145a6e432e79cb68c9aec239668c70285667
languageName: node
linkType: hard

"three@npm:^0.133.0":
version: 0.133.1
resolution: "three@npm:0.133.1"
Expand Down
Loading