Skip to content

Commit

Permalink
Merge pull request #326 from shpowley/main
Browse files Browse the repository at this point in the history
minecraft example fixes + VR controller support
  • Loading branch information
bbohlender authored Aug 14, 2024
2 parents 7eadfe0 + 6fe0534 commit c854c78
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 105 deletions.
2 changes: 1 addition & 1 deletion examples/minecraft/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createRoot } from 'react-dom/client'
//@ts-ignore
import { App } from './src/App.jsx'
import { App } from './src/App.js'
import { StrictMode } from 'react'

createRoot(document.getElementById('root')!).render(
Expand Down
5 changes: 2 additions & 3 deletions examples/minecraft/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
{
"dependencies": {
"@dimforge/rapier3d-compat": "^0.13.1",
"@react-three/drei": "^9.108.3",
"@react-three/drei": "^9.108.4",
"@react-three/rapier": "^1.4.0",
"@react-three/xr": "workspace:^",
"zustand": "^4.5.2"
"zustand": "^4.5.4"
},
"type": "module",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas } from '@react-three/fiber'
import { Sky, PointerLockControls, KeyboardControls } from '@react-three/drei'
import { Physics } from '@react-three/rapier'
import { interactionGroups, Physics } from '@react-three/rapier'
import { Ground } from './Ground.jsx'
import { Player } from './Player.jsx'
import { Cube, Cubes } from './Cube.jsx'
Expand Down Expand Up @@ -51,9 +51,9 @@ export function App() {
<ambientLight intensity={0.8} />
<directionalLight intensity={5} position={[100, 60, 100]} />
<Physics gravity={[0, -30, 0]}>
<Ground />
<Ground collisionGroups={interactionGroups([0, 1], [0])} />
<Player />
<Cube position={[0, 0.5, -10]} />
<Cube collisionGroups={interactionGroups([0, 1], [0])} position={[0, 0.5, -10]} />
<Cubes />
</Physics>
<PointerLockControls />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ title: Minecraft Diamond Axe
*/

import { useGLTF } from '@react-three/drei'
import { GroupProps } from '@react-three/fiber'
import { Mesh } from 'three'

export function Axe(props) {
export function Axe(props: GroupProps) {
const { nodes, materials } = useGLTF('axe.glb')
return (
<group dispose={null} {...props}>
<group rotation={[0, Math.PI / 1.8, -0.3]} scale={0.5}>
<mesh geometry={nodes.Mesh_1001_1.geometry} material={materials.material_2} />
<mesh geometry={nodes.Mesh_1001_2.geometry} material={materials.material_3} />
<mesh geometry={(nodes.Mesh_1001_1 as Mesh).geometry} material={materials.material_2} />
<mesh geometry={(nodes.Mesh_1001_2 as Mesh).geometry} material={materials.material_3} />
</group>
</group>
)
Expand Down
36 changes: 23 additions & 13 deletions examples/minecraft/src/Cube.jsx → examples/minecraft/src/Cube.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
import { useCallback, useRef, useState } from 'react'
import { useTexture } from '@react-three/drei'
import { RigidBody } from '@react-three/rapier'
import create from 'zustand'
import { interactionGroups, RapierRigidBody, RigidBody, RigidBodyProps } from '@react-three/rapier'
import { create } from 'zustand'
import { Vector3Tuple } from 'three'
import { ThreeEvent } from '@react-three/fiber'

// This is a naive implementation and wouldn't allow for more than a few thousand boxes.
// In order to make this scale this has to be one instanced mesh, then it could easily be
// hundreds of thousands.

const useCubeStore = create((set) => ({
cubes: [],
addCube: (x, y, z) => set((state) => ({ cubes: [...state.cubes, [x, y, z]] })),
}))
const useCubeStore = create<{ cubes: Array<Vector3Tuple>; addCube: (x: number, y: number, z: number) => void }>(
(set) => ({
cubes: [],
addCube: (x: number, y: number, z: number) => set((state) => ({ cubes: [...state.cubes, [x, y, z]] })),
}),
)

export const Cubes = () => {
const cubes = useCubeStore((state) => state.cubes)
return cubes.map((coords, index) => <Cube key={index} position={coords} />)
}

export function Cube(props) {
const ref = useRef()
const [hover, set] = useState(null)
export function Cube(props: RigidBodyProps) {
const ref = useRef<RapierRigidBody>(null)
const [hover, set] = useState<number | null>(null)
const addCube = useCubeStore((state) => state.addCube)
const texture = useTexture('dirt.jpg')
const onMove = useCallback((e) => {
const onMove = useCallback((e: ThreeEvent<PointerEvent>) => {
if (e.faceIndex == null) {
return
}
e.stopPropagation()
set(Math.floor(e.faceIndex / 2))
}, [])
const onOut = useCallback(() => set(null), [])
const onClick = useCallback((e) => {
const onClick = useCallback((e: ThreeEvent<MouseEvent>) => {
if (ref.current == null || e.faceIndex == null) {
return
}
e.stopPropagation()
const { x, y, z } = ref.current.translation()
const dir = [
Expand All @@ -37,11 +47,11 @@ export function Cube(props) {
[x, y - 1, z],
[x, y, z + 1],
[x, y, z - 1],
]
] as const
addCube(...dir[Math.floor(e.faceIndex / 2)])
}, [])
return (
<RigidBody {...props} type="fixed" colliders="cuboid" ref={ref}>
<RigidBody {...props} type="fixed" colliders="cuboid" collisionGroups={interactionGroups([0, 1], [0])} ref={ref}>
<mesh receiveShadow castShadow onPointerMove={onMove} onPointerOut={onOut} onClick={onClick}>
{[...Array(6)].map((_, index) => (
<meshStandardMaterial
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as THREE from 'three'
import { useTexture } from '@react-three/drei'
import { CuboidCollider, RigidBody } from '@react-three/rapier'
import { CuboidCollider, RigidBody, RigidBodyProps } from '@react-three/rapier'

export function Ground(props) {
export function Ground(props: RigidBodyProps) {
const texture = useTexture('grass.jpg')
texture.wrapS = texture.wrapT = THREE.RepeatWrapping
return (
Expand Down
70 changes: 0 additions & 70 deletions examples/minecraft/src/Player.jsx

This file was deleted.

143 changes: 143 additions & 0 deletions examples/minecraft/src/Player.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import * as THREE from 'three'
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import { useKeyboardControls } from '@react-three/drei'
import { CapsuleCollider, interactionGroups, RapierRigidBody, RigidBody, useRapier } from '@react-three/rapier'
import { IfInSessionMode } from '@react-three/xr'

import { Axe } from './Axe.jsx'
import { VRPlayerControl } from './VRPlayerControl.jsx'

const SPEED = 5
const direction = new THREE.Vector3()
const frontVector = new THREE.Vector3()
const sideVector = new THREE.Vector3()
const rotation = new THREE.Vector3()

const vectorHelper = new THREE.Vector3()

export function Player({ lerp = THREE.MathUtils.lerp }) {
const axe = useRef<THREE.Group>(null)
const ref = useRef<RapierRigidBody>(null)
const { rapier, world } = useRapier()
const [, getKeys] = useKeyboardControls()

const playerMove = ({
forward,
backward,
left,
right,
rotation,
velocity,
}: {
forward: boolean
backward: boolean
left: boolean
right: boolean
rotation: THREE.Euler
velocity?: any
}) => {
if (!velocity) {
velocity = ref.current?.linvel()
}

frontVector.set(0, 0, (backward ? 1 : 0) - (forward ? 1 : 0))
sideVector.set((left ? 1 : 0) - (right ? 1 : 0), 0, 0)
direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(SPEED).applyEuler(rotation)
ref.current?.setLinvel({ x: direction.x, y: velocity.y, z: direction.z }, true)
}

const playerJump = () => {
if (ref.current == null) {
return
}
const ray = world.castRay(
new rapier.Ray(ref.current.translation(), { x: 0, y: -1, z: 0 }),
Infinity,
false,
undefined,
interactionGroups([1, 0], [1]),
)
const grounded = ray != null && Math.abs(ray.timeOfImpact) <= 1.25

if (grounded) {
ref.current.setLinvel({ x: 0, y: 7.5, z: 0 }, true)
}
}

useFrame((state) => {
if (ref.current == null) {
return
}
const { forward, backward, left, right, jump } = getKeys()
const velocity = ref.current.linvel()

vectorHelper.set(velocity.x, velocity.y, velocity.z)

// update camera
const { x, y, z } = ref.current.translation()
state.camera.position.set(x, y, z)

// update axe
if (axe.current != null) {
axe.current.children[0].rotation.x = lerp(
axe.current.children[0].rotation.x,
Math.sin((vectorHelper.length() > 1 ? 1 : 0) * state.clock.elapsedTime * 10) / 6,
0.1,
)
axe.current.rotation.copy(state.camera.rotation)
axe.current.position.copy(state.camera.position).add(state.camera.getWorldDirection(rotation).multiplyScalar(1))
}

// movement
if (ref.current) {
playerMove({
forward,
backward,
left,
right,
rotation: state.camera.rotation,
velocity,
})

if (jump) {
playerJump()
}
}
})

return (
<>
<RigidBody
ref={ref}
colliders={false}
mass={1}
type="dynamic"
position={[0, 10, 0]}
enabledRotations={[false, false, false]}
canSleep={false}
collisionGroups={interactionGroups([0], [0])}
>
<CapsuleCollider args={[0.75, 0.5]} />

<IfInSessionMode allow={['immersive-ar', 'immersive-vr']}>
<VRPlayerControl playerJump={playerJump} playerMove={playerMove} />
</IfInSessionMode>
</RigidBody>

<IfInSessionMode deny="immersive-vr">
<group
ref={axe}
onPointerMissed={(e) => {
if (axe.current == null) {
return
}
axe.current.children[0].rotation.x = -0.5
}}
>
<Axe position={[0.3, -0.35, 0.5]} />
</group>
</IfInSessionMode>
</>
)
}
Loading

0 comments on commit c854c78

Please sign in to comment.