Skip to content

Commit

Permalink
feat: clean camera + support pointer lock API
Browse files Browse the repository at this point in the history
  • Loading branch information
Gugustinette committed Aug 27, 2024
1 parent b244883 commit 2999abd
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 23 deletions.
2 changes: 1 addition & 1 deletion apps/playground-3d/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ import MyCustomCube from './classes/MyCustomCube'
scene.addComponent(character)

// Attach a camera to the character
scene.camera = new FGameCamera(character, scene)
scene.camera = new FGameCamera(character)

/**
* Create other objects
Expand Down
12 changes: 9 additions & 3 deletions packages/3d/src/FScene3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { FScene } from '@fibbojs/core'
import type RAPIER from '@dimforge/rapier3d'
import type { FComponent3d } from './FComponent3d'
import { FGLTF } from './model/FGLTF'
import type { FCamera3d } from './cameras/FCamera3d'
import { FFixedCamera } from './cameras/FFixedCamera'
import { FOBJ } from './model/FOBJ'
import { FModel } from './model/FModel'

export interface FScene3dOptions {
Expand Down Expand Up @@ -41,7 +39,7 @@ export interface FScene3dOptions {
* scene.addComponent(cube)
*
* // Attach a camera to the cube
* scene.camera = new FGameCamera(cube, scene)
* scene.camera = new FGameCamera(cube)
* })()
* ```
*/
Expand Down Expand Up @@ -86,6 +84,14 @@ export class FScene3d extends FScene {
this.__DOM_NODE__ = options.domNode
// Store the gravity
this.gravity = options.gravity

// Handle window resize
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()

this.renderer.setSize(window.innerWidth, window.innerHeight)
})
}

init() {
Expand Down
2 changes: 1 addition & 1 deletion packages/3d/src/cameras/FAttachedCamera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class FAttachedCamera extends FCamera3d {
// Component that the camera is attached to
attachedComponent: FComponent3d
// Offset from the component's position
offset: THREE.Vector3 = new THREE.Vector3(0, 5, 5)
offset: { x: number, y: number, z: number } = { x: 0, y: 5, z: 5 }

/**
* @param attachedComponent Model that the camera is attached to
Expand Down
44 changes: 39 additions & 5 deletions packages/3d/src/cameras/FGameCamera.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type * as THREE from 'three'
import type { FScene3d } from '../FScene3d'
import type { FComponent3d } from '../FComponent3d'
import { FOrbitCamera } from './FOrbitCamera'

Expand All @@ -15,21 +14,44 @@ import { FOrbitCamera } from './FOrbitCamera'
* const cube = new FCube(scene)
* scene.addComponent(cube)
*
* scene.camera = new FGameCamera(cube, scene)
* scene.camera = new FGameCamera(cube)
* ```
*/
export class FGameCamera extends FOrbitCamera {
// Previous position of the attached component (at each frame)
private previousModelPosition: THREE.Vector3
// Flag to track if the pointer is locked
isPointerLocked: boolean = false
// Last mouse move event
lastMouseMoveEvent: MouseEvent | undefined

/**
* @param attachedComponent Component that the camera is attached to
* @param scene Scene that the camera is in
*/
constructor(attachedComponent: FComponent3d, scene: FScene3d) {
super(attachedComponent, scene)
constructor(attachedComponent: FComponent3d) {
super(attachedComponent)
this.previousModelPosition = attachedComponent.position.clone()
this.setPosition(0, 5, 5)

this.controls.enableDamping = true
this.controls.maxDistance = 5

/**
* Add event listeners
*/
// Request pointer lock when the canvas is clicked
attachedComponent.scene.renderer.domElement.addEventListener('click', () => {
this.attachedComponent.scene.renderer.domElement.requestPointerLock()
})
// Update the pointer lock flag when the pointer lock state changes
document.addEventListener('pointerlockchange', () => {
this.isPointerLocked = document.pointerLockElement === this.attachedComponent.scene.renderer.domElement
})
// Store the last mouse move event
document.addEventListener('mousemove', (event: MouseEvent) => {
// Store the last mouse move event
this.lastMouseMoveEvent = event
})
}

onFrame(delta: number): void {
Expand All @@ -44,5 +66,17 @@ export class FGameCamera extends FOrbitCamera {
this.position.add(positionDifference)
// Update the previous position
this.previousModelPosition = this.attachedComponent.mesh.position.clone()

// Move the camera based on mouse movement if pointer is locked
if (this.isPointerLocked && this.lastMouseMoveEvent) {
// Get mouse movement deltas
const movementX = this.lastMouseMoveEvent.movementX || 0
const movementY = this.lastMouseMoveEvent.movementY || 0

// Rotate the camera based on mouse movement
this.translateX(-movementX * 0.01)
this.translateY(movementY * 0.01)
this.lastMouseMoveEvent = undefined
}
}
}
8 changes: 3 additions & 5 deletions packages/3d/src/cameras/FOrbitCamera.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import type { FScene3d } from '../FScene3d'
import type { FComponent3d } from '../FComponent3d'
import { FCamera3d } from './FCamera3d'

Expand All @@ -15,7 +14,7 @@ import { FCamera3d } from './FCamera3d'
* const cube = new FCube(scene)
* scene.addComponent(cube)
*
* scene.camera = new FOrbitCamera(cube, scene)
* scene.camera = new FOrbitCamera(cube)
* ```
*/
export class FOrbitCamera extends FCamera3d {
Expand All @@ -26,14 +25,13 @@ export class FOrbitCamera extends FCamera3d {

/**
* @param attachedComponent Component that the camera is attached to
* @param scene Scene that the camera is in
*/
constructor(attachedComponent: FComponent3d, scene: FScene3d) {
constructor(attachedComponent: FComponent3d) {
super()
this.attachedComponent = attachedComponent

// Create orbit controls
this.controls = new OrbitControls(this, scene.renderer.domElement)
this.controls = new OrbitControls(this, attachedComponent.scene.renderer.domElement)
}

onFrame(_delta: number): void {
Expand Down
53 changes: 53 additions & 0 deletions packages/3d/src/cameras/FPointerLockCamera.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'
import type { FComponent3d } from '../FComponent3d'
import { FCamera3d } from './FCamera3d'

/**
* @description A camera that can be attached to a FComponent3d and orbits around it.
* @category Camera
* @example
* ```ts
* import { FScene3d, FPointerLockCamera, FCube } from '@fibbojs/3d'
*
* const scene = new FScene3d()
*
* const cube = new FCube(scene)
* scene.addComponent(cube)
*
* scene.camera = new FPointerLockCamera(cube)
* ```
*/
export class FPointerLockCamera extends FCamera3d {
// Model that the camera is attached to
attachedComponent: FComponent3d
// Pointer Lock controls
controls: PointerLockControls

/**
* @param attachedComponent Component that the camera is attached to
*/
constructor(attachedComponent: FComponent3d) {
super()
this.attachedComponent = attachedComponent

// Create Pointer Lock controls
this.controls = new PointerLockControls(this, attachedComponent.scene.renderer.domElement)

attachedComponent.scene.scene.add(this.controls.getObject())

// Lock controls when clicking on the renderer
attachedComponent.scene.renderer.domElement.addEventListener('click', () => {
this.controls.lock()
})
}

onFrame(_delta: number): void {
if (this.attachedComponent.mesh === undefined)
return

// Position the camera at the model's position + offset
this.position.x = this.attachedComponent.mesh.position.x
this.position.y = this.attachedComponent.mesh.position.y
this.position.z = this.attachedComponent.mesh.position.z
}
}
30 changes: 22 additions & 8 deletions packages/3d/src/character/FCharacter3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,46 @@ export abstract class FCharacter3d extends FComponent3d {
const fKeyboard = new FKeyboard(scene)

// Key down
fKeyboard.onKeyDown('ArrowUp', () => {
fKeyboard.onKeyDown('w', () => {
this.inputs.forward = true
})
fKeyboard.onKeyDown('ArrowDown', () => {
fKeyboard.onKeyDown('s', () => {
this.inputs.backward = true
})
fKeyboard.onKeyDown('ArrowLeft', () => {
fKeyboard.onKeyDown('a', () => {
this.inputs.left = true
})
fKeyboard.onKeyDown('ArrowRight', () => {
fKeyboard.onKeyDown('d', () => {
this.inputs.right = true
})
// For AZERTY keyboards
fKeyboard.onKeyDown('z', () => {
this.inputs.forward = true
})
fKeyboard.onKeyDown('q', () => {
this.inputs.left = true
})

// Key up
fKeyboard.onKeyUp('ArrowUp', () => {
fKeyboard.onKeyUp('w', () => {
this.inputs.forward = false
})
fKeyboard.onKeyUp('ArrowDown', () => {
fKeyboard.onKeyUp('s', () => {
this.inputs.backward = false
})
fKeyboard.onKeyUp('ArrowLeft', () => {
fKeyboard.onKeyUp('a', () => {
this.inputs.left = false
})
fKeyboard.onKeyUp('ArrowRight', () => {
fKeyboard.onKeyUp('d', () => {
this.inputs.right = false
})
// For AZERTY keyboards
fKeyboard.onKeyUp('z', () => {
this.inputs.forward = false
})
fKeyboard.onKeyUp('q', () => {
this.inputs.left = false
})
}

initRigidBody(options?: FRigidBody3dOptions): void {
Expand Down
1 change: 1 addition & 0 deletions packages/3d/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { FCamera3d } from './cameras/FCamera3d'
export { FFixedCamera } from './cameras/FFixedCamera'
export { FGameCamera } from './cameras/FGameCamera'
export { FOrbitCamera } from './cameras/FOrbitCamera'
export { FPointerLockCamera } from './cameras/FPointerLockCamera'

// Types
export { F3dShapes } from './types/F3dShapes'
Expand Down

0 comments on commit 2999abd

Please sign in to comment.