You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
4.9 KiB
182 lines
4.9 KiB
/*
|
|
Auto-generated by: https://github.com/pmndrs/gltfjsx
|
|
*/
|
|
|
|
import * as THREE from 'three'
|
|
import React, { useRef, useState, useEffect, useMemo } from 'react'
|
|
import { useFrame } from 'react-three-fiber'
|
|
import { useGLTF } from '@react-three/drei/useGLTF'
|
|
|
|
import cloneGltf from '../lib/cloneGltf'
|
|
|
|
import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader'
|
|
import { FrontSide } from 'three/src/constants'
|
|
|
|
import { HEIGHT } from '../Player'
|
|
|
|
import api, { State, Transform } from '../../store'
|
|
import { Color } from 'three/src/math/Color'
|
|
import { MeshNormalMaterial } from 'three/src/materials/MeshNormalMaterial'
|
|
import { Object3D } from 'three/src/core/Object3D'
|
|
import { SkinnedMesh } from 'three/src/objects/SkinnedMesh'
|
|
|
|
type GLTFResult = GLTF & {
|
|
nodes: {
|
|
mesh: THREE.SkinnedMesh
|
|
mixamorigHips: THREE.Bone
|
|
}
|
|
materials: {
|
|
['Material.001']: THREE.MeshStandardMaterial
|
|
}
|
|
}
|
|
|
|
export type ActionName =
|
|
| 'idle'
|
|
| 'jump'
|
|
| 'run_left'
|
|
| 'run_right'
|
|
| 'run'
|
|
| 'turn_left'
|
|
| 'turn_right'
|
|
| 'walk'
|
|
| 'walk_backwards'
|
|
| 'walk_left'
|
|
| 'walk_right'
|
|
|
|
type GLTFActions = Record<ActionName, THREE.AnimationAction>
|
|
|
|
const Human: React.FC<JSX.IntrinsicElements['group'] & { id: number }> = ({
|
|
id,
|
|
...rest
|
|
}) => {
|
|
const group = useRef<THREE.Group>()
|
|
const gltf = useGLTF('/model/human.glb') as GLTFResult
|
|
const currentAnimation = useRef<ActionName>('idle')
|
|
|
|
const { scene, animations } = cloneGltf(gltf as GLTFResult)
|
|
|
|
const [nodes] = useState<Record<string, SkinnedMesh[]>>(() => {
|
|
const n = {}
|
|
scene.children[0].children.forEach((child: Object3D) => {
|
|
//@ts-ignore
|
|
n[child.name] = child
|
|
})
|
|
return n
|
|
})
|
|
|
|
const material = useMemo(() => {
|
|
const m = new MeshNormalMaterial({
|
|
transparent: true,
|
|
side: FrontSide,
|
|
flatShading: false,
|
|
skinning: true,
|
|
})
|
|
|
|
m.onBeforeCompile = function (shader) {
|
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
'gl_FragColor = vec4( packNormalToRGB( normal ), opacity );',
|
|
[
|
|
'gl_FragColor = vec4( packNormalToRGB( normal ), opacity );',
|
|
'gl_FragColor.a = 1.0 - pow( gl_FragCoord.z, 4.75 );',
|
|
'gl_FragColor.r = 0.70;',
|
|
'gl_FragColor.g = 0.85;',
|
|
'gl_FragColor.b = 1.0;',
|
|
].join('\n'),
|
|
)
|
|
}
|
|
|
|
m.needsUpdate = true
|
|
return m
|
|
}, [])
|
|
|
|
const actions = useRef<GLTFActions>()
|
|
//@ts-ignore
|
|
const [mixer] = useState(() => new THREE.AnimationMixer(nodes.mesh))
|
|
|
|
useEffect(() => {
|
|
api.subscribe(
|
|
(state: Transform | undefined | null) => {
|
|
if (!state || !actions.current) {
|
|
return
|
|
}
|
|
if (state.animation === currentAnimation.current) return
|
|
|
|
const newAction = actions.current[state.animation]
|
|
const previousAction = actions.current[currentAnimation.current]
|
|
|
|
newAction.reset()
|
|
newAction.setEffectiveTimeScale(1)
|
|
|
|
const weight =
|
|
state.animation === 'turn_left' || state.animation === 'turn_right' ? 0.5 : 1
|
|
const time = state.animation === 'jump' ? 0 : 0.2
|
|
|
|
newAction.setEffectiveWeight(weight)
|
|
|
|
previousAction.crossFadeTo(newAction, time, true)
|
|
currentAnimation.current = state.animation
|
|
},
|
|
(state) => state?.userTransforms?.[id],
|
|
)
|
|
}, [id])
|
|
|
|
useEffect(() => {
|
|
if (actions.current && currentAnimation.current) {
|
|
}
|
|
}, [currentAnimation.current])
|
|
|
|
useFrame((_, delta) => {
|
|
mixer.update(delta)
|
|
})
|
|
|
|
useEffect(() => {
|
|
actions.current = {
|
|
idle: mixer.clipAction(animations[0], group.current),
|
|
jump: mixer.clipAction(animations[1], group.current),
|
|
run_left: mixer.clipAction(animations[2], group.current),
|
|
run_right: mixer.clipAction(animations[3], group.current),
|
|
run: mixer.clipAction(animations[4], group.current),
|
|
turn_left: mixer.clipAction(animations[5], group.current),
|
|
turn_right: mixer.clipAction(animations[6], group.current),
|
|
walk: mixer.clipAction(animations[7], group.current),
|
|
walk_backwards: mixer.clipAction(animations[8], group.current),
|
|
walk_left: mixer.clipAction(animations[9], group.current),
|
|
walk_right: mixer.clipAction(animations[10], group.current),
|
|
}
|
|
|
|
Object.values(actions.current).forEach((a) => {
|
|
a.setEffectiveWeight(0)
|
|
a.enabled = true
|
|
a.setEffectiveTimeScale(1)
|
|
a.play()
|
|
})
|
|
|
|
if (currentAnimation.current) {
|
|
actions.current[currentAnimation.current].play()
|
|
}
|
|
}, [group, animations, mixer])
|
|
|
|
return (
|
|
<group
|
|
ref={group}
|
|
{...rest}
|
|
dispose={null}
|
|
rotation={[0, -Math.PI, 0]}
|
|
position={[0, -HEIGHT, 0]}
|
|
>
|
|
<group name="root" rotation={[0, -0.09, 0]}>
|
|
<primitive object={nodes.mixamorigHips} />
|
|
<skinnedMesh
|
|
material={material} //@ts-ignore
|
|
geometry={nodes.mesh.geometry} //@ts-ignore
|
|
skeleton={nodes.mesh.skeleton}
|
|
></skinnedMesh>
|
|
</group>
|
|
</group>
|
|
)
|
|
}
|
|
|
|
useGLTF.preload('/model/human.glb')
|
|
|
|
export default Human
|