Add human model

master
Ian Mancini 4 years ago
parent 94bc4d114b
commit 9b29855a4a

BIN
packages/client/public/model/human.glb (Stored with Git LFS)

Binary file not shown.

@ -1,6 +1,7 @@
// @ts-nocheck // @ts-nocheck
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { Vector3, Euler } from 'three' import { Vector3, Euler } from 'three'
import Human from './models/Human'
import api, { Transform } from '../store' import api, { Transform } from '../store'
@ -10,6 +11,7 @@ type PhantomProps = {
const Phantom: React.FC<PhantomProps> = ({ id }) => { const Phantom: React.FC<PhantomProps> = ({ id }) => {
const ref = useRef<THREE.Mesh>(null) const ref = useRef<THREE.Mesh>(null)
const currentAnimation = useRef<number>(0)
useEffect(() => { useEffect(() => {
if (ref.current) { if (ref.current) {
@ -20,10 +22,11 @@ const Phantom: React.FC<PhantomProps> = ({ id }) => {
} }
ref.current?.position.fromArray([ ref.current?.position.fromArray([
state.position[0], state.position[0],
state.position[1] + 0.25, state.position[1],
state.position[2], state.position[2],
]) ])
ref.current?.quaternion.fromArray(state.rotation) ref.current?.quaternion.fromArray([0, state.rotation[1], 0, state.rotation[3]])
currentAnimation.current = state.a
}, },
(state) => state?.userTransforms?.[id], (state) => state?.userTransforms?.[id],
) )
@ -32,19 +35,7 @@ const Phantom: React.FC<PhantomProps> = ({ id }) => {
return ( return (
<group ref={ref}> <group ref={ref}>
<mesh scale={new Vector3(0.5, 0.5, 0.5)}> <Human animation={currentAnimation.current} id={id} />
<sphereBufferGeometry />
<meshStandardMaterial color="hotpink" />
<meshBasicMaterial color="white" emmisive="white" opacity={0.8} transparent />
</mesh>
<mesh
scale={new Vector3(0.15, 0.15, 0.15)}
position={new Vector3(0, 0, -0.8)}
rotation={new Euler(-Math.PI / 2, 0, 0)}
>
<coneBufferGeometry args={[1, 2, 4]} />
<meshBasicMaterial color="yellow" emmisive="yellow" opacity={0.8} transparent />
</mesh>
</group> </group>
) )
} }

@ -16,9 +16,10 @@ import WorldCollisions from './models/WorldCollisions'
import useStore from '../store' import useStore from '../store'
import { MeshBasicMaterial } from 'three/src/materials/MeshBasicMaterial' import { MeshBasicMaterial } from 'three/src/materials/MeshBasicMaterial'
import { Quaternion } from 'three/src/math/Quaternion' import { Quaternion } from 'three/src/math/Quaternion'
import { ActionName } from './models/Human'
const SPEED = 1 const SPEED = 0.6
const HEIGHT = 1.5 export const HEIGHT = 1.5
const CIRCLE_RADIUS = 1.0 const CIRCLE_RADIUS = 1.0
const CIRCLE_SEGMENTS = 8 const CIRCLE_SEGMENTS = 8
const InitialPosition = new Vector3(49.92, 3.15, 34.52) const InitialPosition = new Vector3(49.92, 3.15, 34.52)
@ -37,18 +38,18 @@ const keys: Record<string, string> = {
} }
const moveFieldByKey = (key: string) => keys[key] const moveFieldByKey = (key: string) => keys[key]
const animations: Record<string, number> = { export const animations: Record<ActionName, ActionName> = {
idle: 0, idle: 'idle',
turn_right: 1, turn_right: 'turn_right',
turn_left: 2, turn_left: 'turn_left',
walk_backwards: 3, walk_backwards: 'walk_backwards',
walk: 4, walk: 'walk',
walk_right: 5, walk_right: 'walk_right',
walk_left: 6, walk_left: 'walk_left',
run: 7, run: 'run',
run_right: 8, run_right: 'run_right',
run_left: 9, run_left: 'run_left',
jump: 10, jump: 'jump',
} }
function FirstPersonCamera(props: JSX.IntrinsicElements['perspectiveCamera']) { function FirstPersonCamera(props: JSX.IntrinsicElements['perspectiveCamera']) {
@ -146,9 +147,8 @@ const Player = () => {
pCameraQuaternion.current, pCameraQuaternion.current,
) )
if (Math.abs(rotationAngle) > 10) { if (Math.abs(rotationAngle) > 0.5) {
anim = anim = rotationAngle > 1 ? animations.turn_right : animations.turn_left
rotationAngle > 1 ? animations.turn_right : (anim = animations.turn_left)
} else { } else {
anim = animations.idle anim = animations.idle
} }
@ -170,12 +170,12 @@ const Player = () => {
groupRef.current?.position.z, groupRef.current?.position.z,
], ],
rotation: [...cameraRotation], rotation: [...cameraRotation],
a: anim, animation: anim,
}) })
pCameraQuaternion.current.copy(camera.quaternion) pCameraQuaternion.current.copy(camera.quaternion)
} }
}, 1000) }, 33)
return () => { return () => {
clearInterval(socketEmitTransformInterval) clearInterval(socketEmitTransformInterval)
@ -202,8 +202,8 @@ const Player = () => {
if (pointerLocked) { if (pointerLocked) {
// Slowdown // Slowdown
velocity.current.x -= velocity.current.x * 10.0 * delta velocity.current.x -= velocity.current.x * 9.0 * delta
velocity.current.z -= velocity.current.z * 10.0 * delta velocity.current.z -= velocity.current.z * 9.0 * delta
// Fall // Fall
bottomRaycaster.current.ray.origin.copy(groupRef.current.position) bottomRaycaster.current.ray.origin.copy(groupRef.current.position)
@ -215,7 +215,7 @@ const Player = () => {
) )
if (intersections.length < 1) { if (intersections.length < 1) {
velocity.current.y -= 9.8 * 10 * delta velocity.current.y -= 9.8 * 5 * delta
} else { } else {
velocity.current.y = 0 velocity.current.y = 0
groupRef.current.position.y = intersections[0].point.y + HEIGHT groupRef.current.position.y = intersections[0].point.y + HEIGHT
@ -227,7 +227,7 @@ const Player = () => {
direction.current.normalize() direction.current.normalize()
// Running // Running
speed.current = run && direction.current.x >= 0 ? 1.5 : 1 speed.current = run && direction.current.x >= 0 ? SPEED * 1.5 : SPEED
// Move // Move
if (forward || backward) if (forward || backward)
@ -278,7 +278,7 @@ const Player = () => {
return ( return (
<> <>
<group ref={groupRef} position={InitialPosition}> <group ref={groupRef} position={InitialPosition}>
<FirstPersonCamera /> <FirstPersonCamera position={[0, -0.1, 0]} />
<mesh <mesh
ref={collisionCircle} ref={collisionCircle}
position={[0, -HEIGHT / 2, 0]} position={[0, -HEIGHT / 2, 0]}

@ -0,0 +1,160 @@
/*
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 { 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'
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 { nodes, animations } = useGLTF('/model/human.glb') as GLTFResult
const currentAnimation = useRef<ActionName>('idle')
const material = useMemo(() => {
const m = new MeshNormalMaterial({
transparent: true,
side: FrontSide,
flatShading: false,
skinning: true,
})
m.onBeforeCompile = function (shader) {
console.log(shader.fragmentShader)
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, 0.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>()
const [mixer] = useState(() => new THREE.AnimationMixer(nodes.mesh))
useEffect(() => {
api.subscribe(
(state: Transform | 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.enabled = true
newAction.setEffectiveTimeScale(1)
newAction.setEffectiveWeight(1)
previousAction.crossFadeTo(newAction, 0.2, 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}
geometry={nodes.mesh.geometry}
skeleton={nodes.mesh.skeleton}
></skinnedMesh>
</group>
</group>
)
}
useGLTF.preload('/model/human.glb')
export default Human

@ -2,18 +2,21 @@ import create from 'zustand'
import { PointerLockControls } from '@react-three/drei' import { PointerLockControls } from '@react-three/drei'
import { Socket } from 'socket.io-client' import { Socket } from 'socket.io-client'
import { ActionName } from './3d/models/Human'
type Error = null | 'SOCKET' type Error = null | 'SOCKET'
export type Transform = { export type Transform = {
position: number[] position: number[]
rotation: number[] rotation: number[]
animation: ActionName
} }
type userId = string type userId = string
export type UserTransforms = Record<userId, Transform> export type UserTransforms = Record<userId, Transform>
type State = { export type State = {
pointerLockControls: PointerLockControls | undefined pointerLockControls: PointerLockControls | undefined
setPointerLockControls: (controls: PointerLockControls) => void setPointerLockControls: (controls: PointerLockControls) => void
pointerLocked: boolean pointerLocked: boolean

Loading…
Cancel
Save