添加 Cannon.js Body 到 Three.js 网格导致组和多个形状的 Vec3 错误

Adding Cannon.js Body to Three.js Mesh causing Vec3 error with groups and multiple shapes

基本上,我在 Three.js 中创建了一组 2 个网格,在 Cannon 中创建了一个包含 2 个形状的 Body。

它们单独看起来很好,但是当我将 Body 位置复制到网格以添加物理时,代码中断,屏幕上没有任何显示,并且在记录位置时,对于 Body 一切正常,但是网格显示为未定义。

(最终目标是用模型替换网格)

任何帮助都会很棒,这里是有问题的代码:

import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import * as dat from 'lil-gui'

import * as CANNON from 'cannon-es'

/**
 * Base
 */
// Debug
const gui = new dat.GUI()

// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
const scene = new THREE.Scene()

/**
 * Models
 */
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('/draco/')

const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)

let mixer = null

let gunModel = null

gltfLoader.load(
    '/models/hamburger.glb',
    (gltf) =>
    {
        gltf.scene.position.set(0, 4, 0)
        gunModel = gltf.scene
        scene.add(gunModel)
        console.log(gunModel.position)
    }
)

/**
 * test wireframe cubes
 */
const YGeo = new THREE.BoxBufferGeometry(2, 6, 2);
const YMat = new THREE.MeshBasicMaterial({
    color: 'green',
    wireframe: true
})
const gun = new THREE.Group();

const blockY = new THREE.Mesh(YGeo, YMat);
blockY.position.x = 4;

const XGeo = new THREE.BoxBufferGeometry(6,2,2);
const XMat = new THREE.MeshBasicMaterial({
    color: 'green',
    wireframe: true
})

const blockX = new THREE.Mesh(XGeo, XMat);
blockX.position.y = 2

gun.add(blockY)
gun.add(blockX)

gui.add(gun.position, 'y', 0, 5, 0.01);
gui.add(gun.position, 'x', -3, 4, 0.01)
gui.add(gun.position, 'z', -3, 3, 0.01)

scene.add(gun)

/**
 * Floor
 */
const floor = new THREE.Mesh(
    new THREE.PlaneGeometry(50, 50),
    new THREE.MeshStandardMaterial({
        color: '#444444',
        metalness: 0,
        roughness: 0.5
    })
)
floor.receiveShadow = true
floor.rotation.x = - Math.PI * 0.5
scene.add(floor)

/**
 * Lights
 */
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
scene.add(ambientLight)

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6)
directionalLight.castShadow = true
directionalLight.shadow.mapSize.set(1024, 1024)
directionalLight.shadow.camera.far = 15
directionalLight.shadow.camera.left = - 7
directionalLight.shadow.camera.top = 7
directionalLight.shadow.camera.right = 7
directionalLight.shadow.camera.bottom = - 7
directionalLight.position.set(5, 5, 5)
scene.add(directionalLight)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100)
camera.position.set(- 8, 4, 8)
scene.add(camera)

// Controls
const controls = new OrbitControls(camera, canvas)
controls.target.set(0, 1, 0)
controls.enableDamping = true

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas
})
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/**
 * Physics
 */
 const world = new CANNON.World({
    gravity: new CANNON.Vec3(0, -9.81, 0)
});

const floorBody = new CANNON.Body({
    shape: new CANNON.Plane(),
    mass: 0
})
world.addBody(floorBody)

const shapeY = new CANNON.Box(new CANNON.Vec3(1,3,1))
const shapeX = new CANNON.Box(new CANNON.Vec3(3,1,1))

const boxBody = new CANNON.Body({
    mass: 1
})
boxBody.addShape(shapeY, new CANNON.Vec3(3, 0, 0), new CANNON.Quaternion())
boxBody.addShape(shapeX, new CANNON.Vec3(0,2,0), new CANNON.Quaternion())
world.addBody(boxBody)

boxBody.position.set(-0.56, 4.13, -0.02)

console.log(boxBody.position)

gui.add(boxBody.position, 'y', 0, 10, 0.01);

/**
 * Animate
 */
const clock = new THREE.Clock()
let previousTime = 0

const tick = () =>
{

    const elapsedTime = clock.getElapsedTime()
    const deltaTime = elapsedTime - previousTime
    previousTime = elapsedTime

    world.step(1/60, deltaTime, 3)

    floor.position.copy(floorBody.position);
    floorBody.quaternion.copy(floor.quaternion);

    // if(gunModel) {
    //     gunModel.position.copy(boxBody);
    //     gunModel.quaternion.copy(boxBody)
    //     console.log(gunModel.position)
    // }

    gun.position.copy(boxBody);
    gun.quaternion.copy(boxBody)
    console.log(gun.position.x)

    if(mixer)
    {
        mixer.update(deltaTime)
    }

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()
// access boxBody attributes?
gun.position.copy(boxBody.position);
gun.quaternion.copy(boxBody.quaternion);
console.log(gun.position.x)