CodePenでesm.shでお手軽にReact Three Fiberを使用してみるテスト4 3Dモデルアニメーション切り替え表示
前回からのつづきです。 esm.shを使用したCodePenのコードで、React Three Fiberによる3Dモデルアニメーションの切り替え表示を行ってみました。 切り替え用3DモデルアニメーションはglTFファイル形式のものを使用しています。
概要
前回の、3Dモデルアニメーションを1つだけ再生するコードから、3Dモデルによる複数のアニメーションを切り替えるコードに進化?させてみました。
作成したコードのうち、Test01、Test04、Test05、といったあたりが自然に本来のアニメーションの再生・切替ができているように自分には見受けられました。
また、Test02やTest03のようにテクスチャがうまく貼れずに表示に失敗してしまっているコードもあります。
Test06はアニメーションは動いているようですが、本来は効果音も含むファイルを読み込んでいるのでその分の読込データが大きくなってしまっているかもです(元々Test03含めBabylon.js用のファイルをgithack.com経由で使わせてもらっていたものだったかもしれません Babylon.jsでは効果音も再生できていました 参考noteです)。
自分のコードを含む、以下のサイトを参考にさせていただきました(アニメーション切替コードとして一番うまくいったと思うTest05のCodeSandboxコードに残っていたコメントより作成しています ←作成したのが1年以上前なので自分でも記憶があいまいですw)。
・How to extract and play animation in react-three-fiber
・reactjs - react-three-fiberでアニメーションを抽出して再生する方法((現在サイトはなくなってしまっているようです))
・Three.js r110でglTF 3Dモデルのアニメーション切り替え その03
・How to Perform Inplace Animations in React Three Fiber
・【React】react-three-fibarで3D表現をする(Mixamoを使ったアニメーションモデル)
・Controlling Multiple Character Animations In React Three Fiber
参考:import文そのままになりますが、使用したライブラリ等のバージョンは以下になります
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
このnoteのそれぞれのコードの下にある参考サイトとして引用している自分noteは、参考にした自作CodeSandboxコードへのリンクをまとめただけのものです。 それらCodeSandboxコードはこのnoteのコード作成時に参考にしましたが、重いのでコードへの直接のリンクはやめておきました。
以下作成したコードのリストとなります。 各コードにて、タイトル文字によるリンクはコード付きCodePenへのリンク、サムネイル画像によるリンクはコードなしのコード実行結果の全画面表示へのリンク、となります。
あと、JavaScriptのコードをそのまま載せているので、冗長な感じでこのnoteエントリけっこう長いです、ご注意ください。
React Three Fiber 8.16.1 glTF Animation Change Test01
React Three Fiber 8.16.1 glTF Animation Change Test01
HTML
<div id="root"></div>
CSS
* {
box-sizing: border-box;
}
html,
body,
#root {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
background: #30ffff;
}
JS(JavaScript)
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
const ModelPath = 'https://rawcdn.githack.com/KhronosGroup/glTF-Sample-Models/8e9a5a6ad1a2790e2333e3eb48a1ee39f9e0e31b/2.0/Fox/glTF-Binary/Fox.glb'
const Loader = () => {
const { progress } = useProgress()
return (
<Html center>
{progress} % loaded
</Html>
)
}
const Model = (props) => {
const group = useRef()
const gltf = useGLTF(ModelPath)
const { actions } = useAnimations(gltf.animations, group)
useEffect(() => {
//console.log(actions) // find out the name of your action
if (props.previousAction) {
actions[props.previousAction].fadeOut(0.2)
actions[props.previousAction].stop()
}
actions[props.action].play()
actions[props.action].fadeIn(0.2)
}, [props.action, actions])
useFrame((state) => {
gltf.scene.rotation.x += 0.02
gltf.scene.rotation.y += 0.02
//gltf.scene.rotation.x = state.mouse.y * -2.5
//gltf.scene.rotation.y = state.mouse.x * 7.5
//gltf.scene.rotation.z = (state.mouse.x + state.mouse.y) / 2 * 5
}, [])
return (
<>
<group ref={group} dispose={null}>
<group scale={props.scale}>
<primitive object={gltf.scene} position={props.position} rotation={props.rotation} scale={props.scale} />
</group>
</group>
</>
)
}
const App = () => {
const [action, setAction] = useState('Walk')
const [previousAction, setPreviousAction] = useState('')
return (
<Canvas>
<Html fullscreen>
<button
onClick={() => {
setPreviousAction(action)
setAction('Survey')
}}>
Survey
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Walk')
}}>
Walk
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Run')
}}>
Run
</button>
</Html>
<ambientLight intensity={Math.PI / 2} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Suspense fallback={<Loader />}>
<Model
position={[0, -2, 0]}
rotation={[0, 0, 0]}
scale={0.18}
action={action}
previousAction={previousAction}
/>
</Suspense>
</Canvas>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
前に自分で作成した以下のサイトの記録を参考にして作成してみました
CodeSandboxでReact Three Fiber実験その5 glTF 3Dモデルアニメーション切り替え表示 > React Three Fiber Practice31 glTF Model Multiple Animations Change04
React Three Fiber 8.16.1 glTF Animation Change Test02
React Three Fiber 8.16.1 glTF Animation Change Test02
HTML Test01と同じです
CSS Test01と同じです
JS(JavaScript)
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
const ModelPath = 'https://rawcdn.githack.com/BabylonJS/MeshesLibrary/55f475726670be2e7e4017b5f88c5762a90508c2/shark.glb'
const Loader = () => {
const { progress } = useProgress()
return (
<Html center>
{progress} % loaded
</Html>
)
}
const Model = (props) => {
const group = useRef()
const gltf = useGLTF(ModelPath)
const { actions } = useAnimations(gltf.animations, group)
useEffect(() => {
//console.log(actions) // find out the name of your action
if (props.previousAction) {
actions[props.previousAction].fadeOut(0.2)
actions[props.previousAction].stop()
}
actions[props.action].play()
actions[props.action].fadeIn(0.2)
}, [props.action, actions])
useFrame((state) => {
//gltf.scene.rotation.x += 0.02
//gltf.scene.rotation.x = state.mouse.y * -2.5
gltf.scene.rotation.y = state.mouse.x * 7.5
//gltf.scene.rotation.z = (state.mouse.x + state.mouse.y) / 2 * 5
}, [])
return (
<>
<group ref={group} dispose={null}>
<group scale={props.scale}>
<primitive object={gltf.scene} position={props.position} rotation={props.rotation} scale={props.scale} />
</group>
</group>
</>
)
}
const App = () => {
const [action, setAction] = useState('swimming')
const [previousAction, setPreviousAction] = useState('')
return (
<Canvas>
<Html fullscreen>
<button
onClick={() => {
setPreviousAction(action)
setAction('swimming')
}}>
swimming
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('circling')
}}>
circling
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('bite')
}}>
bite
</button>
</Html>
<ambientLight intensity={Math.PI / 2} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Suspense fallback={<Loader />}>
<Model
position={[0, -2, 0]}
rotation={[0, 0, 0]}
scale={0.65}
action={action}
previousAction={previousAction}
/>
</Suspense>
</Canvas>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
前に自分で作成した以下のサイトの記録を参考にして作成してみました
CodeSandboxでReact Three Fiber実験その5 glTF 3Dモデルアニメーション切り替え表示 > React Three Fiber Practice37 glTF Model Multiple Animations Change05
React Three Fiber 8.16.1 glTF Animation Change Test03
React Three Fiber 8.16.1 glTF Animation Change Test03
HTML Test01と同じです
CSS Test01と同じです
JS(JavaScript)
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
const ModelPath = 'https://rawcdn.githack.com/BabylonJS/Babylon.js/e38e3cc4f2f91c7da05b45aa08103eaaf201cb58/Playground/scenes/ufo.glb'
const Loader = () => {
const { progress } = useProgress()
return (
<Html center>
{progress} % loaded
</Html>
)
}
const Model = (props) => {
const group = useRef()
const gltf = useGLTF(ModelPath)
const { actions } = useAnimations(gltf.animations, group)
useEffect(() => {
//console.log(actions) // find out the name of your action
if (props.previousAction) {
actions[props.previousAction].fadeOut(0.2)
actions[props.previousAction].stop()
}
actions[props.action].play()
actions[props.action].fadeIn(0.2)
}, [props.action, actions])
useFrame((state) => {
//gltf.scene.rotation.x += 0.02
//gltf.scene.rotation.x = state.mouse.y * -2.5
//gltf.scene.rotation.y = state.mouse.x * 7.5
//gltf.scene.rotation.z = (state.mouse.x + state.mouse.y) / 2 * 5
}, [])
return (
<>
<group ref={group} dispose={null}>
<group scale={props.scale}>
<primitive object={gltf.scene} position={props.position} rotation={props.rotation} scale={props.scale} />
</group>
</group>
</>
)
}
const App = () => {
const [action, setAction] = useState('hover')
const [previousAction, setPreviousAction] = useState('')
return (
<Canvas>
<Html fullscreen>
<button
onClick={() => {
setPreviousAction(action)
setAction('hover')
}}>
hover
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('flight')
}}>
flight
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('abduction_rings')
}}>
abduction_rings
</button>
</Html>
<ambientLight intensity={Math.PI / 2} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Suspense fallback={<Loader />}>
<Model
position={[0, -1.5, 0]}
rotation={[0, 0, 0]}
scale={2}
action={action}
previousAction={previousAction}
/>
</Suspense>
</Canvas>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
前に自分で作成した以下のサイトの記録を参考にして作成してみました
CodeSandboxでReact Three Fiber実験その5 glTF 3Dモデルアニメーション切り替え表示 > React Three Fiber Practice30 glTF Model Multiple Animations Change03
React Three Fiber 8.16.1 glTF Animation Change Test04
React Three Fiber 8.16.1 glTF Animation Change Test04
HTML Test01と同じです
CSS Test01と同じです
JS(JavaScript)
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
const ModelPath = 'https://cdn.jsdelivr.net/gh/mrdoob/three.js@r110/examples/models/gltf/Soldier.glb'
const Loader = () => {
const { progress } = useProgress()
return (
<Html center>
{progress} % loaded
</Html>
)
}
const Model = (props) => {
const group = useRef()
const gltf = useGLTF(ModelPath)
const { actions } = useAnimations(gltf.animations, group)
useEffect(() => {
//console.log(actions) // find out the name of your action
if (props.previousAction) {
actions[props.previousAction].fadeOut(0.2)
actions[props.previousAction].stop()
}
actions[props.action].play()
actions[props.action].fadeIn(0.2)
}, [props.action, actions])
useFrame((state) => {
//gltf.scene.rotation.x += 0.02
//gltf.scene.rotation.x = state.mouse.y * -2.5
gltf.scene.rotation.y = state.mouse.x * 7.5
//gltf.scene.rotation.z = (state.mouse.x + state.mouse.y) / 2 * 5
}, [])
return (
<>
<group ref={group} dispose={null}>
<group scale={props.scale}>
<primitive object={gltf.scene} position={props.position} rotation={props.rotation} scale={props.scale} />
</group>
</group>
</>
)
}
const App = () => {
const [action, setAction] = useState('Walk')
const [previousAction, setPreviousAction] = useState('')
return (
<Canvas>
<Html fullscreen>
<button
onClick={() => {
setPreviousAction(action)
setAction('Idle')
}}>
Idle
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Run')
}}>
Run
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('TPose')
}}>
TPose
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Walk')
}}>
Walk
</button>
</Html>
<ambientLight intensity={Math.PI / 2} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Suspense fallback={<Loader />}>
<Model
position={[0, -1.5, 0]}
rotation={[0, Math.PI, 0]}
scale={1.7}
action={action}
previousAction={previousAction}
/>
</Suspense>
</Canvas>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
前に自分で作成した以下のサイトの記録を参考にして作成してみました
CodeSandboxでReact Three Fiber実験その5 glTF 3Dモデルアニメーション切り替え表示 > React Three Fiber Practice29 glTF Model Multiple Animations Change02
React Three Fiber 8.16.1 glTF Animation Change Test05
React Three Fiber 8.16.1 glTF Animation Change Test05
HTML Test01と同じです
CSS Test01と同じです
JS(JavaScript)
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
const ModelPath = 'https://rawcdn.githack.com/mrdoob/three.js/36f9f34752a985359e2556c68a52234436cefdfa/examples/models/gltf/RobotExpressive/RobotExpressive.glb'
const Loader = () => {
const { progress } = useProgress()
return (
<Html center>
{progress} % loaded
</Html>
)
}
const Model = (props) => {
const group = useRef()
const gltf = useGLTF(ModelPath)
const { actions } = useAnimations(gltf.animations, group)
useEffect(() => {
//console.log(actions) // find out the name of your action
if (props.previousAction) {
actions[props.previousAction].fadeOut(0.2)
actions[props.previousAction].stop()
}
actions[props.action].play()
actions[props.action].fadeIn(0.2)
}, [props.action, actions])
useFrame((state) => {
//gltf.scene.rotation.x += 0.02
//gltf.scene.rotation.x = state.mouse.y * -2.5
gltf.scene.rotation.y = state.mouse.x * 7.5
//gltf.scene.rotation.z = (state.mouse.x + state.mouse.y) / 2 * 5
}, [])
return (
<>
<group ref={group} dispose={null}>
<group scale={props.scale}>
<primitive object={gltf.scene} position={props.position} rotation={props.rotation} scale={props.scale} />
</group>
</group>
</>
)
}
const App = () => {
const [action, setAction] = useState('Walking')
const [previousAction, setPreviousAction] = useState('')
return (
<Canvas>
<Html fullscreen>
<button
onClick={() => {
setPreviousAction(action)
setAction('Idle')
}}>
Idle
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Walking')
}}>
Walking
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Running')
}}>
Running
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Dance')
}}>
Dance
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Death')
}}>
Death
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Sitting')
}}>
Sitting
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Standing')
}}>
Standing
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Jump')
}}>
Jump
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Yes')
}}>
Yes
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('No')
}}>
No
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Wave')
}}>
Wave
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('Punch')
}}>
Punch
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('ThumbsUp')
}}>
ThumbsUp
</button>
</Html>
<ambientLight intensity={Math.PI / 2} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Suspense fallback={<Loader />}>
<Model
position={[0, -2.5, 0]}
scale={1}
action={action}
previousAction={previousAction}
/>
</Suspense>
</Canvas>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
前に自分で作成した以下のサイトの記録を参考にして作成してみました
CodeSandboxでReact Three Fiber実験その5 glTF 3Dモデルアニメーション切り替え表示 > React Three Fiber Practice28 glTF Model Multiple Animations Change01
React Three Fiber 8.16.1 glTF Animation Change Test06
React Three Fiber 8.16.1 glTF Animation Change Test06
HTML Test01と同じです
CSS Test01と同じです
JS(JavaScript)
import ReactDOM from 'https://esm.sh/react-dom@18.2.0'
import React, { useState, useEffect, useRef, Suspense } from 'https://esm.sh/react@18.2.0'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber@8.16.1'
import { Html, useProgress, useGLTF, useAnimations } from 'https://esm.sh/@react-three/drei@9.105.4'
const ModelPath = 'https://rawcdn.githack.com/najadojo/glTF/8109e86a26c9210814f8d236589ce92a2f9fa8f8/extensions/2.0/Vendor/MSFT_audio_emitter/samples/skateboard_character/glTF/skateboard_character_audio.gltf'
const Loader = () => {
const { progress } = useProgress()
return (
<Html center>
{progress} % loaded
</Html>
)
}
const Model = (props) => {
const group = useRef()
const gltf = useGLTF(ModelPath)
const { actions } = useAnimations(gltf.animations, group)
useEffect(() => {
//console.log(actions) // find out the name of your action
if (props.previousAction) {
actions[props.previousAction].fadeOut(0.2)
actions[props.previousAction].stop()
}
actions[props.action].play()
actions[props.action].fadeIn(0.2)
}, [props.action, actions])
useFrame((state) => {
//gltf.scene.rotation.x += 0.02
//gltf.scene.rotation.x = state.mouse.y * -2.5
gltf.scene.rotation.y = state.mouse.x * 7.5
//gltf.scene.rotation.z = (state.mouse.x + state.mouse.y) / 2 * 5
}, [])
return (
<>
<group ref={group} dispose={null}>
<group scale={props.scale}>
<primitive object={gltf.scene} position={props.position} rotation={props.rotation} scale={props.scale} />
</group>
</group>
</>
)
}
const App = () => {
const [action, setAction] = useState('idle_and_push')
const [previousAction, setPreviousAction] = useState('')
return (
<Canvas>
<Html fullscreen>
<button
onClick={() => {
setPreviousAction(action)
setAction('idle_and_push')
}}>
idle_and_push
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('tricks_1')
}}>
tricks_1
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('tricks_2')
}}>
tricks_2
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('walk')
}}>
walk
</button>
<button
onClick={() => {
setPreviousAction(action)
setAction('jump')
}}>
jump
</button>
</Html>
<ambientLight intensity={Math.PI / 2} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
<Suspense fallback={<Loader />}>
<Model
position={[0, -0.35, -0.25]}
scale={10}
action={action}
previousAction={previousAction}
/>
</Suspense>
</Canvas>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
前に自分で作成した以下のサイトの記録を参考にして作成してみました
Three.js r110でglTF形式の3Dモデルアニメーションを切り替えるCodePenへのリンク集