🦁

第2回 React+TypeScriptなWebアプリで、QRコードをARしてみた。(3Dキャラクタ表示編)

2024/02/12に公開

<- [第1回]React+TypeScriptなWebアプリで、QRコードをARしてみた。(QRコード読み込み編)
[第3回]React+TypeScriptなWebアプリで、QRコードをARしてみた。(カメラ編) ->


↓出来上がり。
https://aaaa1597.github.io/react-r3f-advanced007

Abstract

React+TypescriptのWebアプリで、ARを実装してみた。第2回。
今回は、FBXのキャラクタをダウンロードして表示とアニメーションを実装する。
使ったモデルはMixiamoってサイトのモデルをありがたく使わせてもらいます。→ コレ

結論

今回の成果物はココ↓
https://github.com/aaaa1597/ReactTs-QrAr002

前提

手順

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

このテンプレコードから始める。react-r3f-advanced006
で、下記コマンドでフォルダ名とか整備する。

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

準備

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

準備2

  1. モデル一式を、MixamoからDLして、プロジェクトの"ReactTs-QrAr0027/public/assets"配下にコピー。




    そのまま、Ch09_nonPBR.fbxをプロジェクトフォルダの"ReactTs-QrAr002/public/assets"配下へ。

  2. モーションデータもMixamoからダウンロードする。

    腕の幅とか、早さとか、パラメータは適当に。


    Without Skinを選ぶとモーションデータだけになる。

    そのまま、DLしたファイルをプロジェクトフォルダの"ReactTs-QrAr002/public/assets"配下へ。

ちなみにモーションデータとモデルデータを判別するには。

Autodesk FBX Reviewで確認できる。

モデルのみ モーションデータのみ

再生ボタンを押してもTポーズから変化しない。

針人間みたいになる。再生ボタンで動き出す。

Autodesk FBX Review 1.5.3.0については、ここからダウンロードできる。

App.tsx

まず全体。

App.tsx
-import React, { useEffect, Suspense} from 'react';
+import React, { useEffect, Suspense, useRef, useState, useMemo } from 'react';
import './App.css';
-import { Canvas, useLoader, useThree } 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 { OrbitControls, Environment, useFBX } from '@react-three/drei'
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";

const FBXModel = () => {
  /* FBXモデル読込み */
  const fbx = useLoader(FBXLoader, "./assets/Ch09_nonPBR.fbx");
+  /* AnimationClip(s)読込み */
+  const animCrips: THREE.AnimationClip[][] = []
+  animCrips[0] = useFBX('/assets/BreakdanceEnding2.fbx').animations
+  animCrips[1] = useFBX('/assets/BreakdanceUprockVar1.fbx').animations
+  animCrips[2] = useFBX('/assets/HipHopDancing.fbx').animations
+  animCrips[3] = useFBX('/assets/NorthernSoulSpin.fbx').animations
+  animCrips[4] = useFBX('/assets/SwingDancing.fbx').animations
+  animCrips[5] = useFBX('/assets/BreakdanceEnding1.fbx').animations
+  /* 変数定義 */
+  const mixer = useRef<THREE.AnimationMixer>();
+  const [ animIdx, setAnimIdx ] = useState<number>(0);
+  const animActions = useMemo(() => [] as THREE.AnimationAction[], [])

  /* 初期化 */
  useEffect(() => {
    fbx.scale.multiplyScalar(0.02)
+    mixer.current = new THREE.AnimationMixer(fbx)
+    animCrips.forEach((val: THREE.AnimationClip[], idx: number) => {
+      animActions[idx] = mixer.current!.clipAction(val[0])
+    })
+    /* 1秒後に開始 */
+    new Promise((resolve) => setTimeout((resolve) => {0}, 1000)).then(()=>animActions[0].play())
  }, [])

+  /* モーション切替え処理 */
+  useEffect(() => {
+    const act: THREE.AnimationAction = animActions[animIdx]
+    act?.reset().fadeIn(0.3).play()
+    return () => {
+      act?.fadeOut(0.3)
+    }
+  }, [animIdx])
+
+  /* FPS処理 */
+  useFrame((state, delta) => {
+    mixer.current!.update(delta);
+    const durationtime: number= animActions[animIdx].getClip().duration
+    const currenttime: number = animActions[animIdx].time
+    if(currenttime/durationtime > 0.9/*90%を超えたら次のモーションへ*/) {
+      const index: number = (animIdx+1) % (animCrips.length)
+      console.log('index=', index)
+      setAnimIdx( index )
+    }
+  });

  return (
    <primitive object={fbx} position={[1, -1, 1]} />
  )
}

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

export default App;

で、実行。


出来た!!


<- [第1回]React+TypeScriptなWebアプリで、QRコードをARしてみた。(QRコード読み込み編)
[第3回]React+TypeScriptなWebアプリで、QRコードをARしてみた。(カメラ編) ->

Discussion