Skip to content

Commit

Permalink
feat: add an abstraction component for marchingcubes (#872)
Browse files Browse the repository at this point in the history
Co-authored-by: drcmda <drcmda@gmail.com>
  • Loading branch information
servinlp and drcmda committed Jul 8, 2022
1 parent 416590e commit a2dfb04
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 0 deletions.
70 changes: 70 additions & 0 deletions .storybook/stories/MarchingCubes.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react'
import { Color, Group, Vector3 } from 'three'

import { Setup } from '../Setup'

import { MarchingCube, MarchingCubes, MarchingPlane, OrbitControls } from '../../src'
import { useFrame } from '@react-three/fiber'

export default {
title: 'Abstractions/MarchingCubes',
component: MarchingCubes,
decorators: [
(storyFn) => (
<Setup controls={false} cameraPosition={new Vector3(0, 0, 10)}>
{storyFn()}
</Setup>
),
],
}

const MarchingCubesScene = ({ resolution, maxPolyCount, planeX, planeY, planeZ }) => {
const cubeRefOne = React.useRef<Group>()
const cubeRefTwo = React.useRef<Group>()

useFrame(({ clock }) => {
if (!cubeRefOne.current || !cubeRefTwo.current) return
const time = clock.getElapsedTime()
cubeRefOne.current.position.set(0.5, Math.sin(time * 0.4) * 0.5 + 0.5, 0.5)
cubeRefTwo.current.position.set(0.5, Math.cos(time * 0.4) * 0.5 + 0.5, 0.5)
})

return (
<MarchingCubes resolution={resolution} maxPolyCount={maxPolyCount} enableColors={true} scale={2}>
<MarchingCube ref={cubeRefOne} color={new Color('#f0f')} position={[0.5, 0.6, 0.5]} />
<MarchingCube ref={cubeRefTwo} color={new Color('#ff0')} position={[0.5, 0.5, 0.5]} />

{planeX && <MarchingPlane planeType="x" />}
{planeY && <MarchingPlane planeType="y" />}
{planeZ && <MarchingPlane planeType="z" />}

<meshPhongMaterial specular={0xffffff} shininess={2} vertexColors={true} />
</MarchingCubes>
)
}

export const MarchingCubesStory = ({ resolution, maxPolyCount, planeX, planeY, planeZ }) => {
return (
<>
<MarchingCubesScene
resolution={resolution}
maxPolyCount={maxPolyCount}
planeX={planeX}
planeY={planeY}
planeZ={planeZ}
/>
<axesHelper />
<OrbitControls enablePan={false} zoomSpeed={0.5} />
</>
)
}

MarchingCubesStory.args = {
resolution: 40,
maxPolyCount: 40000,
planeX: false,
planeY: true,
planeZ: false,
}

MarchingCubesStory.storyName = 'Default'
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
<li><a href="#computedattribute">Computed Attribute</a></li>
<li><a href="#clone">Clone</a></li>
<li><a href="#useanimations">useAnimations</a></li>
<li><a href="#marchingcubes">MarchingCubes</a></li>
</ul>
<li><a href="#shaders">Shaders</a></li>
<ul>
Expand Down Expand Up @@ -895,6 +896,20 @@ const { actions } = useAnimations(animations, scene)
return <primitive object={scene} />
```

#### MarchingCubes

[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/abstractions-marchingcubes--marching-cubes-story)

An abstraction for threes [MarchingCubes](https://threejs.org/examples/#webgl_marchingcubes)

```jsx
<MarchingCubes resolution={50} maxPolyCount={20000} enableUvs={false} enableColors={true}>
<MarchingCube strength={0.5} subtract={12} color={new Color('#f0f')} position={[0.5, 0.5, 0.5]} />

<MarchingPlane planeType="y" strength={0.5} subtract={12} />
</MarchingCubes>
```

# Shaders

#### MeshReflectorMaterial
Expand Down
110 changes: 110 additions & 0 deletions src/core/MarchingCubes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import * as React from 'react'
import { Color, Group, MeshStandardMaterial } from 'three'
import mergeRefs from 'react-merge-refs'
import { MarchingCubes as MarchingCubesImpl } from 'three-stdlib'
import { useFrame } from '@react-three/fiber'

type Api = {
getParent: () => React.MutableRefObject<MarchingCubesImpl>
}

const globalContext = React.createContext<Api>(null!)

export type MarchingCubesProps = {
resolution?: number
maxPolyCount?: number
enableUvs?: boolean
enableColors?: boolean
} & JSX.IntrinsicElements['group']

export const MarchingCubes = React.forwardRef(
(
{
resolution = 28,
maxPolyCount = 10000,
enableUvs = false,
enableColors = false,
children,
...props
}: MarchingCubesProps,
ref
) => {
const marchingCubesRef = React.useRef<MarchingCubesImpl>(null!)
const marchingCubes = React.useMemo(
() => new MarchingCubesImpl(resolution, new MeshStandardMaterial(), enableUvs, enableColors, maxPolyCount),
[resolution, maxPolyCount, enableUvs, enableColors]
)

const api = React.useMemo(
() => ({
getParent: () => marchingCubesRef,
}),
[]
)

useFrame(() => {
marchingCubes.reset()
}, -1) // To make sure the reset runs before the balls or planes are added

return (
<>
<primitive object={marchingCubes} ref={mergeRefs([marchingCubesRef, ref])} {...props}>
<globalContext.Provider value={api}>{children}</globalContext.Provider>
</primitive>
</>
)
}
)

type MarchingCubeProps = {
strength?: number
subtract?: number
color?: Color
} & JSX.IntrinsicElements['group']

export const MarchingCube = React.forwardRef(
({ strength = 0.5, subtract = 12, color = new Color('#fff'), ...props }: MarchingCubeProps, ref) => {
const { getParent } = React.useContext(globalContext)
const parentRef = React.useMemo(() => getParent(), [getParent])
const cubeRef = React.useRef<Group>()

useFrame(() => {
if (!parentRef.current || !cubeRef.current) return

parentRef.current.addBall(
cubeRef.current.position.x,
cubeRef.current.position.y,
cubeRef.current.position.z,
strength,
subtract,
color
)
})

return <group ref={mergeRefs([ref, cubeRef])} {...props} />
}
)

type MarchingPlaneProps = {
planeType?: 'x' | 'y' | 'z'
strength?: number
subtract?: number
} & JSX.IntrinsicElements['group']

export const MarchingPlane = React.forwardRef(
({ planeType: _planeType = 'x', strength = 0.5, subtract = 12, ...props }: MarchingPlaneProps, ref) => {
const { getParent } = React.useContext(globalContext)
const parentRef = React.useMemo(() => getParent(), [getParent])
const wallRef = React.useRef<Group>()
const planeType = React.useMemo(
() => (_planeType === 'x' ? 'addPlaneX' : _planeType === 'y' ? 'addPlaneY' : 'addPlaneZ'),
[_planeType]
)

useFrame(() => {
if (!parentRef.current || !wallRef.current) return
parentRef.current[planeType](strength, subtract)
})
return <group ref={mergeRefs([ref, wallRef])} {...props} />
}
)
1 change: 1 addition & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export * from './Trail'
export * from './Sampler'
export * from './ComputedAttribute'
export * from './Clone'
export * from './MarchingCubes'

// Cameras
export * from './OrthographicCamera'
Expand Down

1 comment on commit a2dfb04

@vercel
Copy link

@vercel vercel bot commented on a2dfb04 Jul 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.