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

/*
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