Skip to content

Commit

Permalink
feat: <Animator /> API (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
hannojg authored Jul 8, 2024
1 parent f172252 commit 7f1478f
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 24 deletions.
30 changes: 10 additions & 20 deletions package/example/Shared/src/NoneTransparent.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,24 @@
import * as React from 'react'
import { useCallback } from 'react'
import { StyleSheet } from 'react-native'
import { useModel, useAnimator, FilamentContext, FilamentView, Camera, RenderCallback, Skybox } from 'react-native-filament'
import { FilamentContext, FilamentView, Camera, Skybox, Model, Animator, AnimationItem } from 'react-native-filament'
import DroneGlb from '~/assets/buster_drone.glb'
import { DefaultLight } from './components/DefaultLight'
import { useCallback } from 'react'

function Renderer() {
const model = useModel(DroneGlb)
const modelAnimator = useAnimator(model)

const renderCallback: RenderCallback = useCallback(
({ passedSeconds }) => {
'worklet'

if (modelAnimator == null) {
return
}

// Update the animators to play the current animation
modelAnimator.applyAnimation(0, passedSeconds)
modelAnimator.updateBoneMatrices()
},
[modelAnimator]
)
const onAnimationsLoaded = useCallback((items: AnimationItem[]) => {
console.log('Animations loaded:', items)
}, [])

return (
<FilamentView style={styles.filamentView} renderCallback={renderCallback} enableTransparentRendering={false}>
<FilamentView style={styles.filamentView} enableTransparentRendering={false}>
<Camera />
<DefaultLight />
<Skybox colorInHex="#88defb" />

<Model source={DroneGlb}>
<Animator onAnimationsLoaded={onAnimationsLoaded} />
</Model>
</FilamentView>
)
}
Expand Down
4 changes: 2 additions & 2 deletions package/src/hooks/useAnimator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo } from 'react'
import { Animator, FilamentAsset, FilamentInstance } from '../types'
import { FilamentAnimator, FilamentAsset, FilamentInstance } from '../types'
import { useFilamentContext } from '../react/Context'
import { FilamentModel } from './useModel'

Expand All @@ -13,7 +13,7 @@ function isFilamentModel(asset: FilamentAsset | FilamentInstance | FilamentModel
*
* @note For each asset/instance you should only have one animator.
*/
export function useAnimator(modelOrAsset?: FilamentAsset | FilamentInstance | FilamentModel): Animator | undefined {
export function useAnimator(modelOrAsset?: FilamentAsset | FilamentInstance | FilamentModel): FilamentAnimator | undefined {
const { nameComponentManager } = useFilamentContext()
const animator = useMemo(() => {
if (modelOrAsset == null) return undefined
Expand Down
1 change: 1 addition & 0 deletions package/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export * from './bullet'
export { useFilamentContext } from './react/Context'
export { FilamentViewWithRenderCallbacks as FilamentView } from './react/FilamentViewWithRenderCallbacks'
export * from './react/Model'
export * from './react/Animator'
export * from './react/Camera'
export * from './react/RenderCallbackContext'
export * from './react/EnvironmentalLight'
Expand Down
84 changes: 84 additions & 0 deletions package/src/react/Animator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useEffect } from 'react'
import { ParentModelAssetContext } from './ParentModelAssetContext'
import { FilamentAsset } from '../types'
import { RenderCallbackContext } from './RenderCallbackContext'
import { useAnimator } from '../hooks/useAnimator'

export type AnimationItem = {
index: number
duration: number
name: string
}

type Props = {
/**
* The index of the animation to play. To find out the index for the animation you want to play, you can use the `onAnimationsLoaded` callback.
* @default 0
**/
animationIndex?: number

/**
* Returns a list of all animations for the model.
*/
onAnimationsLoaded?: (animations: AnimationItem[]) => unknown
}

/**
* Can be used to control the animation of a Model.
* For pausing, playing, it is recommended to conditionally render the Animator component.
*
* @example
* ```jsx
* <Model source={DroneGlb}>
* <Animator animationIndex={2} />
* </Model>
*/
export function Animator(props: Props) {
const parentAsset = React.useContext(ParentModelAssetContext)

if (parentAsset == null) {
throw new Error('Animator must be used inside a <Model> component.')
}

return <AnimatorImpl asset={parentAsset} {...props} />
}

type ImplProps = Props & {
asset: FilamentAsset
}

function AnimatorImpl({ asset, animationIndex = 0, onAnimationsLoaded }: ImplProps) {
const animator = useAnimator(asset)

RenderCallbackContext.useRenderCallback(
({ passedSeconds }) => {
'worklet'
if (animator == null) {
return
}

animator.applyAnimation(animationIndex, passedSeconds)
animator.updateBoneMatrices()
},
[animator]
)

useEffect(() => {
if (animator == null || onAnimationsLoaded == null) {
return
}

const animations: AnimationItem[] = []
for (let i = 0; i < animator.getAnimationCount(); i++) {
animations.push({
index: i,
duration: animator.getAnimationDuration(i),
name: animator.getAnimationName(i),
})
}

onAnimationsLoaded(animations)
}, [animator, onAnimationsLoaded])

return null
}
2 changes: 1 addition & 1 deletion package/src/react/Model.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { PropsWithChildren, useEffect, useMemo } from 'react'
import { BufferSource } from '../hooks/useBuffer'
import { ModelProps as UseModelProps, useModel } from '../hooks/useModel'
import { ParentModelAssetContext } from './ParentModelAssetContex'
import { ParentModelAssetContext } from './ParentModelAssetContext'
import { ParentEntityContext } from './ParentEntityContex'
import { getAssetFromModel } from '../utilities/getAssetFromModel'
import { useFilamentContext } from './Context'
Expand Down
2 changes: 1 addition & 1 deletion package/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export * from './Scene'
export * from './View'
export * from './SwapChain'
export * from './Math'
export * from './Animator'
export { Animator as FilamentAnimator } from './Animator'
export * from './FilamentAsset'
export * from './RenderableManager'
export * from './Material'
Expand Down

0 comments on commit 7f1478f

Please sign in to comment.