😸

React+TypeScriptなWebアプリで、R3Fのtutorial18。(GLTFJSXでモデル制御)

2023/12/29に公開

Abstract

今回の参考はここ(GLTFJSX)
GLTFJSXは、gltf(glb)モデルをReact風にコーディングできるやつ。
やってみた。

結論

今回の成果物はココ↓
https://github.com/aaaa1597/react-r3f-tutorial018
モデルはこれをありがたく使わせてもらいます。

GLTFJSXとは

glTF(glb)モデルを操作するための、JSX(TSX)ソースを出力するライブラリ。
使い方は、
npm install -> "npx gltfjsx うんちゃら~"なコマンドを実行 -> 出力ソースを使う、って使い方。

前提

手順

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

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

フォルダリネームとか
$ BaseProject=react-r3f-tutorial017
$ NewProject=react-r3f-tutorial018
$ cd ~
$ rm -rf ${BaseProject}
$ git clone https://github.com/aaaa1597/${BaseProject}.git
$ rm -rf ${BaseProject}/.git
$ mv ${BaseProject} ${NewProject}
$ cd ${NewProject}

準備

$ npm install --save three
$ npm install --save @types/three
$ npm install --save @react-three/fiber
$ npm install --save @react-three/drei

準備2

  • ありがたくDLしてきた、uploads_files_3844681_Another+bedroom.glbをbedroom3d.glbにリネーム。
  • bedroom3d.glbを、"~/react-r3f-tutorial018/public/assets/"配下にコピー。
$ ls -l ~/react-r3f-tutorial018/public/assets/
合計 1400
-rw-rw-r-- 1 jun jun 1430076 1229 17:19 bedroom3d.glb

npx gltfjsxコマンドを実行してglbファイルに対応したTSXファイルを出力する

$ npx gltfjsx public/assets/bedroom3d.glb --types --shadows 
$ ls -l ~/react-r3f-tutorial018
合計 804
-rw-rw-r--   1 jun jun  13185 1229 17:22 Bedroom3d.tsx  # ← ここに出来てる。

Bedroom3d.tsxを修正(3か所)

Bedroom3d.tsx
### 1.どのライブラリか分からんのでコメント化
  // animations: GLTFAction[]
### 2.パスを修正
- const { nodes, materials } = useGLTF('/bedroom3d.glb') as GLTFResult
+ const { nodes, materials } = useGLTF('assets/bedroom3d.glb') as GLTFResult
### 3.パスを修正
- useGLTF.preload('/bedroom3d.glb')
+ useGLTF.preload('assets/bedroom3d.glb')

.eslintrc.jsを修正

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

.eslintrc.js
-        "react/no-unknown-property": ['error', { ignore: ['css', "args", 'rotation-x', "castShadow", 'receiveShadow'] }],
+        "react/no-unknown-property": ['error', { ignore: ['css', "args", 'rotation-x', "castShadow", 'receiveShadow', 'dispose', 'geometry', 'material', 'position', 'rotation'] }],

App.tsx

まず全体。

App.tsx
import React, {useRef} from 'react';
import './App.css';
-import { Canvas, useFrame, MeshProps, useLoader } from '@react-three/fiber'
+import { Canvas, useLoader } from '@react-three/fiber'
import * as THREE from 'three'
import { Stats, OrbitControls, Environment } from '@react-three/drei'
import { useControls } from 'leva'
+import { Model } from './Bedroom3d'

const Lights = () => {
  const directionalRef = useRef<THREE.DirectionalLight>(null!)

  useControls('Directional Light', {
    intensity: {
      value: 1, min: 0, max: 5, step: 0.1,
      onChange: (v) => {
        directionalRef.current.intensity = v
      },
    },
    position: {
      value: [3.3,1.0,4.4],
      onChange: (v) => {
        directionalRef.current.position.x = v[0]
        directionalRef.current.position.y = v[1]
        directionalRef.current.position.z = v[2]
      },
    },
  })
  
  return (
    <directionalLight ref={directionalRef} castShadow />
  )
}

-type BoxProps = {
-  props: MeshProps;
-  wireframe?: boolean;
-}
-
-const Box = (boxprops: BoxProps) => {
-  const ref = useRef<THREE.Mesh>(null!)
-
-  useFrame((_, delta) => {
-    if( !ref.current) return
-    ref.current.rotation.x += 1 * delta
-    ref.current.rotation.y += 0.5 * delta
-  })
-
-  return (
-    <mesh {...boxprops.props} ref={ref}>
-      <boxGeometry />
-    </mesh>
-  )
-}
-
-const Floor = () => {
-  return (
-    <mesh rotation-x={-Math.PI / 2} receiveShadow={true}>
-      <circleGeometry args={[10]} />
-      <meshStandardMaterial />
-    </mesh>
-  )
-}

const App = () => {
  const texture = useLoader(THREE.TextureLoader, './imgs/grid.png')

  return (
    <div style={{ width: "75vw", height: "75vh" }}>
      <Canvas camera={{ position: [4, 4, 3] }} shadows>
        <Lights />
        <Environment preset="forest" background />
-       <Box props={{position:[3, 1, 0], name:"meshBasicMaterial",    material: new THREE.MeshBasicMaterial({ map: texture })}}/>
-       <Box props={{position:[1, 1, 0], name:"meshNormalMaterial",   material: new THREE.MeshNormalMaterial({flatShading: true,})}}/>
-       <Box props={{position:[1, 3, 0], name:"meshPhongMaterial",    material: new THREE.MeshPhongMaterial({flatShading: true, map: texture,})}}/>
-       <Box props={{position:[3, 3, 0], name:"MeshStandardMaterial", material: new THREE.MeshStandardMaterial({flatShading: true, map: texture,})}}/>
-       <Floor />	
        <Model />
        <OrbitControls />
        <axesHelper args={[5]} />
        <gridHelper />
        <Stats />
      </Canvas>
    </div>
  );
}

export default App;

で、実行。


出来た!!

ポイント

gltf(glb)ファイルのパス設定がハマりポイントになりそうだけど、そこにハマらんかったら大丈夫!!


React+TypeScriptなWebアプリで、R3Fのtutorial17。(Environmentで簡単背景描画)


React+TypeScriptなWebアプリで、R3Fのtutorial19。(Custom Hooksで自作イベント)

Discussion