Skip to content

Commit

Permalink
Add useBody hook that infers shape with three-to-cannon
Browse files Browse the repository at this point in the history
  • Loading branch information
isaac-mason committed Jun 26, 2022
1 parent 90dfc12 commit 7afee4b
Show file tree
Hide file tree
Showing 7 changed files with 455 additions and 39 deletions.
198 changes: 198 additions & 0 deletions packages/react-three-cannon-examples/src/demos/demo-ShapeInference.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import type { BodyProps, PlaneProps } from '@react-three/cannon'
import { Debug, Physics, useBody, usePlane } from '@react-three/cannon'
import { OrbitControls } from '@react-three/drei'
import { Canvas } from '@react-three/fiber'
import { Suspense, useRef } from 'react'
import type { Group, Mesh } from 'three'

function BoundingSphere(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
{ type: 'Sphere' },
)

return (
<mesh ref={ref} receiveShadow>
<boxBufferGeometry args={[0.75, 0.75, 0.75]} />
<meshNormalMaterial />
</mesh>
)
}

function Sphere(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
)

return (
<mesh ref={ref} receiveShadow>
<sphereBufferGeometry args={[0.6]} />
<meshNormalMaterial />
</mesh>
)
}

function BoundingBox(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
{ type: 'Box' },
)

return (
<mesh ref={ref} receiveShadow>
<sphereBufferGeometry args={[0.5]} />
<meshNormalMaterial />
</mesh>
)
}

function Box(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
)

return (
<mesh ref={ref} receiveShadow>
<boxBufferGeometry args={[1, 1, 1]} />
<meshNormalMaterial />
</mesh>
)
}

function BoundingCylinder(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
{ type: 'Cylinder' },
)

return (
<mesh ref={ref} receiveShadow>
<sphereBufferGeometry args={[0.5]} />
<meshNormalMaterial />
</mesh>
)
}

function Cylinder(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
)

return (
<mesh ref={ref} receiveShadow>
<cylinderBufferGeometry args={[0.4, 0.6, 1.2, 10]} />
<meshNormalMaterial />
</mesh>
)
}

function ConvexPolyhedron(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Group>(null),
{ type: 'ConvexPolyhedron' },
)

return (
<group ref={ref}>
<mesh receiveShadow>
<boxBufferGeometry args={[1.5, 0.5, 0.5]} />
<meshNormalMaterial />
</mesh>
<mesh receiveShadow rotation={[0, Math.PI / 2, 0]}>
<boxBufferGeometry args={[1.5, 0.5, 0.5]} />
<meshNormalMaterial />
</mesh>
</group>
)
}

function Trimesh(props: BodyProps) {
const [ref] = useBody(
() => ({
mass: 1,
...props,
}),
useRef<Mesh>(null),
{ type: 'Trimesh' },
)

return (
<mesh ref={ref} receiveShadow>
<torusKnotBufferGeometry args={[0.5, 0, 100, 100]} />
<meshNormalMaterial />
</mesh>
)
}

function Plane(props: PlaneProps) {
usePlane(() => ({ type: 'Static', ...props }))
return null
}

function ShapeInference() {
return (
<>
<Canvas shadows camera={{ fov: 50, position: [4, 7, 6] }}>
<color attach="background" args={['#555']} />
<ambientLight intensity={0.5} />
<spotLight
position={[15, 15, 15]}
angle={0.3}
penumbra={1}
intensity={2}
castShadow
shadow-mapSize-width={2048}
shadow-mapSize-height={2048}
/>
<Suspense fallback={null}>
<Physics gravity={[0, -10, 0]}>
<Debug color="white">
<Box position={[-4, 4, -1]} />
<Cylinder position={[-2, 6, -1]} />
<Sphere position={[-0, 8, -1]} />
<ConvexPolyhedron position={[2, 10, -1]} />
<Trimesh position={[4, 12, -1]} rotation={[Math.PI / 2, 0, 0]} />

<BoundingBox position={[-2, 14, 1]} />
<BoundingSphere position={[0, 16, 1]} />
<BoundingCylinder position={[2, 18, 1]} />

<Plane rotation={[-Math.PI / 2, 0, 0]} />
</Debug>
</Physics>
</Suspense>
<OrbitControls />
</Canvas>
</>
)
}

export default ShapeInference
1 change: 1 addition & 0 deletions packages/react-three-cannon-examples/src/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const fileDemos = [
'KinematicCube',
'Paused',
'SphereDebug',
'ShapeInference',
'Triggers',
'Trimesh',
] as const
Expand Down
3 changes: 2 additions & 1 deletion packages/react-three-cannon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"dependencies": {
"@pmndrs/cannon-worker-api": "^2.1.0",
"cannon-es": "^0.19.0",
"cannon-es-debugger": "^1.0.0"
"cannon-es-debugger": "^1.0.0",
"three-to-cannon": "^4.1.0"
},
"devDependencies": {
"@babel/core": "^7.17.8",
Expand Down
57 changes: 40 additions & 17 deletions packages/react-three-cannon/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import { DynamicDrawUsage, Euler, InstancedMesh, MathUtils, Object3D, Quaternion
import { useDebugContext } from './debug-context'
import type { CannonEvents } from './physics-context'
import { usePhysicsContext } from './physics-context'
import type { ThreeToCannonOptions } from './three-to-cannon'
import { threeToCannon } from './three-to-cannon'

export type AtomicApi<K extends AtomicName> = {
set: (value: AtomicProps[K]) => void
Expand Down Expand Up @@ -153,12 +155,13 @@ function setupCollision(
type GetByIndex<T extends BodyProps> = (index: number) => T
type ArgFn<T> = (args: T) => unknown[]

function useBody<B extends BodyProps<unknown[]>, O extends Object3D>(
type: BodyShapeType,
function useBodyCommon<B extends BodyProps<unknown[]>, O extends Object3D>(
type: BodyShapeType | null,
fn: GetByIndex<B>,
argsFn: ArgFn<B['args']>,
fwdRef: Ref<O> = null,
deps: DependencyList = [],
threeToCannonOptions?: ThreeToCannonOptions,
): Api<O> {
const ref = useForwardedRef(fwdRef)

Expand All @@ -184,23 +187,35 @@ function useBody<B extends BodyProps<unknown[]>, O extends Object3D>(
? new Array(objectCount).fill(0).map((_, i) => `${object.uuid}/${i}`)
: [object.uuid]

const props: (B & { args: unknown })[] =
let shapeType: BodyShapeType = type || 'Particle'
let inferredProps: BodyProps<unknown[]> = {}

if (!type) {
const result = threeToCannon(object, threeToCannonOptions)

if (result) {
shapeType = result.shape
inferredProps = result.props
}
}

const props =
object instanceof InstancedMesh
? uuid.map((id, i) => {
const props = fn(i)
const props = { ...inferredProps, ...fn(i) }
prepare(temp, props)
object.setMatrixAt(i, temp.matrix)
object.instanceMatrix.needsUpdate = true
refs[id] = object
debugApi?.add(id, props, type)
debugApi?.add(id, props, shapeType)
setupCollision(events, props, id)
return { ...props, args: argsFn(props.args) }
})
: uuid.map((id, i) => {
const props = fn(i)
const props = { ...inferredProps, ...fn(i) }
prepare(object, props)
refs[id] = object
debugApi?.add(id, props, type)
debugApi?.add(id, props, shapeType)
setupCollision(events, props, id)
return { ...props, args: argsFn(props.args) }
})
Expand All @@ -210,7 +225,7 @@ function useBody<B extends BodyProps<unknown[]>, O extends Object3D>(
props: props.map(({ onCollide, onCollideBegin, onCollideEnd, ...serializableProps }) => {
return { onCollide: Boolean(onCollide), ...serializableProps }
}),
type,
type: shapeType,
uuid,
})
return () => {
Expand Down Expand Up @@ -366,44 +381,52 @@ function makeTriplet(v: Vector3 | Triplet): Triplet {
return v instanceof Vector3 ? [v.x, v.y, v.z] : v
}

export function useBody<O extends Object3D>(
fn: GetByIndex<PlaneProps>,
fwdRef: Ref<O> = null,
threeToCannonOptions?: ThreeToCannonOptions,
deps?: DependencyList,
) {
return useBodyCommon(null, fn, (args) => args || [], fwdRef, deps, threeToCannonOptions)
}
export function usePlane<O extends Object3D>(
fn: GetByIndex<PlaneProps>,
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody('Plane', fn, () => [], fwdRef, deps)
return useBodyCommon('Plane', fn, () => [], fwdRef, deps)
}
export function useBox<O extends Object3D>(fn: GetByIndex<BoxProps>, fwdRef?: Ref<O>, deps?: DependencyList) {
const defaultBoxArgs: Triplet = [1, 1, 1]
return useBody('Box', fn, (args = defaultBoxArgs): Triplet => args, fwdRef, deps)
return useBodyCommon('Box', fn, (args = defaultBoxArgs): Triplet => args, fwdRef, deps)
}
export function useCylinder<O extends Object3D>(
fn: GetByIndex<CylinderProps>,
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody('Cylinder', fn, (args = [] as []) => args, fwdRef, deps)
return useBodyCommon('Cylinder', fn, (args = [] as []) => args, fwdRef, deps)
}
export function useHeightfield<O extends Object3D>(
fn: GetByIndex<HeightfieldProps>,
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody('Heightfield', fn, (args) => args, fwdRef, deps)
return useBodyCommon('Heightfield', fn, (args) => args, fwdRef, deps)
}
export function useParticle<O extends Object3D>(
fn: GetByIndex<ParticleProps>,
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody('Particle', fn, () => [], fwdRef, deps)
return useBodyCommon('Particle', fn, () => [], fwdRef, deps)
}
export function useSphere<O extends Object3D>(
fn: GetByIndex<SphereProps>,
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody(
return useBodyCommon(
'Sphere',
fn,
(args: SphereArgs = [1]): SphereArgs => {
Expand All @@ -419,15 +442,15 @@ export function useTrimesh<O extends Object3D>(
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody<TrimeshProps, O>('Trimesh', fn, (args) => args, fwdRef, deps)
return useBodyCommon<TrimeshProps, O>('Trimesh', fn, (args) => args, fwdRef, deps)
}

export function useConvexPolyhedron<O extends Object3D>(
fn: GetByIndex<ConvexPolyhedronProps>,
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody<ConvexPolyhedronProps, O>(
return useBodyCommon<ConvexPolyhedronProps, O>(
'ConvexPolyhedron',
fn,
([vertices, faces, normals, axes, boundingSphereRadius] = []): ConvexPolyhedronArgs<Triplet> => [
Expand All @@ -446,7 +469,7 @@ export function useCompoundBody<O extends Object3D>(
fwdRef?: Ref<O>,
deps?: DependencyList,
) {
return useBody('Compound', fn, (args) => args as unknown[], fwdRef, deps)
return useBodyCommon('Compound', fn, (args) => args as unknown[], fwdRef, deps)
}

type ConstraintApi<A extends Object3D, B extends Object3D> = [
Expand Down
Loading

0 comments on commit 7afee4b

Please sign in to comment.