如何从 useFBX 渲染多个模型? @react-three/drei

How to render multiple models from useFBX? @react-three/drei

我正在尝试使用 @react-three/drei 中的 useFBX 显示多个组件。虽然我调用了两次该函数,但我在场景中只得到了一个组件。

我有以下代码在场景中渲染一棵树

import { useBox } from "@react-three/cannon";
import { useFBX } from "@react-three/drei";
import React, { Suspense } from "react";

const Trees = (props) => {
  const [ref] = useBox(() => ({
    mass: 1,
    type: "Static",
    ...props,
  }));
  let fbx = useFBX(props.fbx);
  return (
    <Suspense fallback={null}>
      <mesh ref={ref} scale={props.scale} position={props.position}>
        <primitive object={fbx} dispose={null} />
      </mesh>
    </Suspense>
  );
};

export default Trees;

我正在另一个文件中调用该函数

    import React from "react";
    import Trees from "./Trees";
    
    const TreeDisplay = () => {
      return (
        <>
          <Trees
            fbx="/FBX/Tree1.fbx"
            position={[10, 0, 20]}
            scale={[0.01, 0.01, 0.01]}
          />
          <Trees
            fbx="/FBX/Tree1.fbx"
            position={[20, 0, 0]}
            scale={[0.01, 0.01, 0.01]}
          />
        </>
      );
    };
   export default TreeDisplay;

上面的函数必须在场景中的定义位置添加两棵树,但我只得到一棵树。

在 javascript 中,当您使用一个对象时,您使用的是它的直接引用,因此您需要复制该对象:

let fbx = useFBX(props.fbx);
let fbxClone = {...fbx};

还有一个 object3D.clone() 函数在这种情况下可能会有用:

let fbxClone = fbx.clone();

除此之外,您可能还想使用 javascript .map 函数,如果您获得一个对象数组,您可以从中创建一个组件数组。

let mytrees  = [];
for(let i = 0;i<1000; i++)
   mytrees.push(fbx.clone());

return (<>{mytrees.map((tree)=>{return <instancedMesh material={tree.material} 
                                        geometry={tree.geometry} />})}</>)

这是一个完整的代码(带有 gltfloader,但它应该以与 fbx 相同的方式工作):

import React, { useRef, useState, useMemo, Suspense, useEffect, memo } from 'react'
import { Canvas, useFrame, useLoader, extend, useThree } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"
import * as THREE from 'three'

const Trees = memo((props) => {
    let listOfRefs = [];
    const tempObject = new THREE.Object3D();
    let treeTypes = ["three_1"];
    let loadedTrees = [];
    let trees = [];

    for(let cur of treeTypes)
    {
        loadedTrees.push(Object.values(useLoader(GLTFLoader, cur+'.glb',(loader) => {
          const dracoLoader = new DRACOLoader();
          dracoLoader.setDecoderPath("/draco-gltf/");
          loader.setDRACOLoader(dracoLoader);
        }).nodes).filter(m=>{return (m instanceof THREE.Mesh)})
        .map(m=>{return (<instancedMesh material={m.material} 
                                        geometry={m.geometry} 
                                        key={cur+""+Math.floor(Math.random()*10000)} 
                                        count={props.amount}
                                        ref={(()=>{
                                                listOfRefs.push(useRef()); 
                                                return listOfRefs[listOfRefs.length-1];
                                                })()}
                                        args={[null,null,props.amount]} />)}));
                                        
    }
    
    useEffect(()=>{
        if (listOfRefs.length < 1) return;
        let i = 0;
        for(let c = 0;c<props.amount;c++)
        {
            let currentPosition = [ Math.round(Math.random())?(Math.random()*props.halfSize):-(Math.random()*props.halfSize),
                                    13.75 , 
                                    Math.round(Math.random())?(Math.random()*props.halfSize):-(Math.random()*props.halfSize)];
            let currentRotation = [0,(Math.round(Math.random())*Math.PI/2),0]
            for(let curRef of listOfRefs) {
                const id = i++;
                tempObject.position.set(currentPosition[0],currentPosition[1],currentPosition[2])
                tempObject.rotation.set(currentRotation[0],currentRotation[1],currentRotation[2]);
                tempObject.updateMatrix();
                curRef.current.setMatrixAt(id, tempObject.matrix);
            }
        }
        for(let curRef of listOfRefs)
            curRef.current.instanceMatrix.needsUpdate = true;
        
    })
   return (
        <group>
            {loadedTrees[Math.floor(Math.random()*loadedTrees.length)]}
        </group>
   );
})
export default Trees;

另一个建议是使用此处建议的 instancedMesh : https://threejs.org/docs/index.html?q=instanced#api/en/objects/InstancedMesh 它作为反应三纤维成分存在

<instancedMesh />

问题出在 fbx 上,克隆 fbx 模型即可完成工作。

import { useBox } from "@react-three/cannon";
import { Loader, useFBX } from "@react-three/drei";
import React, { Suspense } from "react";

const Trees = (props) => {
  const [ref] = useBox(() => ({
    mass: 1,
    type: "Static",
    ...props,
  }));
  let fbx = useFBX(props.fbx);
  //Add this line
  let fbxClone = fbx.clone();
  return (
    <Suspense fallback={<Loader />}>
      <mesh ref={ref} scale={props.scale} material-reflectivity={1}>
        //pass the cloned object
        <primitive object={fbxClone} dispose={null} />
      </mesh>
    </Suspense>
  );
};

export default Trees;