💬

React+TypeScript+R3Fのtutorial応用編3(glTFで3Dアニメーション(単一モデル))

2024/01/04に公開

Abstract

今回の参考はここ(R3F Animation Mixer)。このソースをTypeScriptで実装しなおす。
といってもほとんどそのままで動いた。
必要最小で構成されてて、ホント感激!! 分かりやすい。

ポイント

  • *.glbの3Dモデル読込みと表示
  • *.glbのアニメーションデータ読込みと実行

結論

今回のソースコードはココ↓
https://github.com/aaaa1597/react-r3f-advanced003

前提

手順

1.プロジェクト生成 -> VSCodeで開く

めんどいから、このスケルトンコードから始める。react-r3f-base-onebox
で、下記コマンドでフォルダ名とか整備する。

フォルダリネームとか
$ D:
$ cd .\Products\React.js\            # ご自身の適当なフォルダで。
$ rd /q /s D:\Products\React.js\react-r3f-advanced003
$ git clone https://github.com/aaaa1597/react-r3f-base-onebox.git
$ rd /q /s react-r3f-base-onebox/.git
$ ren react-r3f-base-onebox react-r3f-advanced003
$ cd react-r3f-advanced003

準備

コマンドプロンプト
$ npm install --save three
$ npm install --save @types/three
$ npm install --save @react-three/fiber
$ npm install --save @react-three/drei

準備2

  • ここからDLしたanimate-bones.glb をプロジェクトの"react-r3f-advanced003/public/"配下にコピー。

.eslintrc.jsを修正

エラーになるので、ignoreに追加

.eslintrc.js
     "rules": {
-        "react/no-unknown-property": ['error', { ignore: ['css', "args", 'wireframe', 'rotation-x', 'rotation'] }],
+        "react/no-unknown-property": ['error', { ignore: ['css', "args", 'wireframe', 'rotation-x', 'rotation', 'object', 'position', 'intensity'] }],
     }

App.tsx

まず全体。

App.tsx
-import React, {useRef} from 'react';
+import React, { useRef, Suspense} from 'react';
import './App.css';
-import { Canvas, useFrame, MeshProps  } from '@react-three/fiber'
+import { Canvas, useLoader, useFrame } from '@react-three/fiber'
import * as THREE from 'three'
import { OrbitControls, Environment } from '@react-three/drei'
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

-const Box = (props: MeshProps) => {
+const TheModel = () => {
-  const ref = useRef<THREE.Mesh>(null!)
+  const { scene, animations } = useLoader(GLTFLoader, "/animate-bones.glb");
+  const mixer = new THREE.AnimationMixer(scene);
+  mixer.clipAction(animations[0]).play();
+  mixer.clipAction(animations[1]).play();

-  useFrame((_, delta) => {
+  useFrame((state, delta) => {
-   if( !ref.current) return
-   ref.current.rotation.x += 1 * delta
-   ref.current.rotation.y += 0.5 * delta
+   mixer.update(delta);
  });

  return (
-   <mesh {...props} ref={ref}>
-     <boxGeometry />
-     <meshNormalMaterial />
-   </mesh>
+   <primitive object={scene} position={[0, 0, 0]} />
  )
}

const App = () => {
  return (
    <div style={{ width: "100vw", height: "75vh" }}>
      <Canvas camera={{ position: [3, 1, 2] }}>
+       <ambientLight intensity={2} />
+       <pointLight position={[40, 40, 40]} />
-       <Box position={[1, 1, 1]} name="A" />
+       <Suspense fallback={null}>
+         <TheModel />
+       </Suspense>
        <Environment preset="forest" background />
        <OrbitControls />
        <axesHelper args={[5]} />
        <gridHelper />
      </Canvas>
    </div>
  );
}

export default App;

で、実行。


出来た!!

すばらしい、簡単にできた。

まとめ

3Dモデルのアニメーションを実行するには、THREE.AnimationMixerを使うのがポイント。
THREE.AnimationMixerの使い方は、以下の通り。

1. new THREE.AnimationMixer() ← 引数にsceneを渡す。

2. clipAction()で、THREE.AnimationMixerにアニメーション追加

3. 追加したアニメーションをstart()

4. useFrame()の中で、mixer.update()
で動くようになる。分かりやすっ!!


React+TypeScript+R3Fのtutorial応用編2(Octreeで衝突判定の絞り込み)


React+TypeScript+R3Fのtutorial応用編4(glTFで3Dアニメーション(モデルとモーション別々ファイル読込み))

Discussion