Skip to content

Commit

Permalink
feat: improve camera for movements + init capsule polyhedron
Browse files Browse the repository at this point in the history
  • Loading branch information
Gugustinette committed Aug 21, 2024
1 parent 6f1a43c commit d08e8a7
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 29 deletions.
36 changes: 22 additions & 14 deletions apps/playground-3d/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { F3dShapes, FCube, FGameCamera, FScene3d, FSphere } from '@fibbojs/3d'
import { F3dShapes, FCapsule, FCube, FGameCamera, FScene3d, FSphere } from '@fibbojs/3d'
import { fDebug } from '@fibbojs/devtools'
import { FKeyboard } from '@fibbojs/event'
import Duck from './classes/Duck'
Expand All @@ -22,15 +22,15 @@ import './style.css'
ground.setColor(0x1F1F1F)
scene.addComponent(ground)

// Create a cube that will be controlled by the player
const cube = new FCube(scene, {
// Create a capsule that will be controlled by the player
const capsule = new FCapsule(scene, {
position: { x: -5, y: 5, z: 5 },
})
cube.setColor(0xA0FFA0)
cube.initRigidBody({
capsule.setColor(0xA0FFA0)
capsule.initRigidBody({
lockRotations: true,
})
scene.addComponent(cube)
scene.addComponent(capsule)

/**
* Create other objects
Expand Down Expand Up @@ -67,34 +67,42 @@ import './style.css'
/**
* Add collision events
*/
cube.onCollisionWith(GltfCube, () => {
capsule.onCollisionWith(GltfCube, () => {
console.log('Cube collided with a GltfCube !')
})
cube.onCollisionWith(sphere, () => {
capsule.onCollisionWith(sphere, () => {
console.log('Cube collided with the sphere!')
})

// Create a keyboard instance
const fKeyboard = new FKeyboard(scene)
// Detect inputs to move the cube
fKeyboard.on('ArrowUp', () => {
cube.rigidBody?.applyImpulse({ x: 0, y: 0, z: -0.2 }, true)
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(0.4), true)
})
fKeyboard.on('ArrowDown', () => {
cube.rigidBody?.applyImpulse({ x: 0, y: 0, z: 0.2 }, true)
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(-0.4), true)
})
fKeyboard.on('ArrowLeft', () => {
cube.rigidBody?.applyImpulse({ x: -0.2, y: 0, z: 0 }, true)
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(0.4).applyAxisAngle(new scene.THREE.Vector3(0, 1, 0), Math.PI / 2), true)
})
fKeyboard.on('ArrowRight', () => {
cube.rigidBody?.applyImpulse({ x: 0.2, y: 0, z: 0 }, true)
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(-0.4).applyAxisAngle(new scene.THREE.Vector3(0, 1, 0), Math.PI / 2), true)
})
fKeyboard.on(' ', () => {
cube.rigidBody?.applyImpulse({ x: 0, y: 1, z: 0 }, true)
capsule.rigidBody?.applyImpulse({ x: 0, y: 1, z: 0 }, true)
})

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

// After 3 seconds, add a third gltfCube
setTimeout(() => {
Expand Down
45 changes: 33 additions & 12 deletions packages/3d/src/FComponent3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export abstract class FComponent3d extends FComponent {
* @param options.scale The scale of the rigid body. If not defined, it will use the default scale of the FComponent3d.
* @param options.rotation The rotation of the rigid body. If not defined, it will use the default rotation of the FComponent3d.
* @param options.shape The shape of the rigid body. If not defined, it will default to F3dShapes.CUBE.
* @param options.rigidBodyType The type of the rigid body. If not defined, it will default to RAPIER.RigidBodyType.Dynamic.
* @param options.lockTranslations If true, the rigid body will not be able to move.
* @param options.lockRotations If true, the rigid body will not be able to rotate.
* @param options.enabledTranslations If defined, it will enable or disable translations on the x and y axis.
Expand All @@ -265,6 +266,7 @@ export abstract class FComponent3d extends FComponent {
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
rigidBodyType?: RAPIER.RigidBodyType
lockTranslations?: boolean
lockRotations?: boolean
enabledTranslations?: {
Expand All @@ -284,6 +286,7 @@ export abstract class FComponent3d extends FComponent {
scale: new THREE.Vector3(this.scale.x / 2, this.scale.y / 2, this.scale.z / 2),
rotation: new THREE.Vector3(this.rotation.x, this.rotation.y, this.rotation.z),
shape: F3dShapes.CUBE,
rigidBodyType: RAPIER.RigidBodyType.Dynamic,
lockTranslations: false,
lockRotations: false,
enabledTranslations: undefined,
Expand All @@ -292,19 +295,20 @@ export abstract class FComponent3d extends FComponent {
options = { ...DEFAULT_OPTIONS, ...options }
// Validate options
if (!options.position || !options.scale || !options.rotation || !options.shape)
throw new Error('initRigidBody requires position, scale, rotation and shape options')
throw new Error('initRigidBody requires position, scale, rotation, shape and rigidBodyType options')

// Check if the world exists
if (!this.scene.world)
throw new Error('FScene must have a world to create a rigid body')

// Create a dynamic rigid-body.
const rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setTranslation(options.position.x, options.position.y, options.position.z)
.setRotation(
// Create quaternion from Euler angles
new THREE.Quaternion().setFromEuler(new THREE.Euler(options.rotation.x, options.rotation.y, options.rotation.z)),
)
// Create a rigid body description according to the type
const rigidBodyDesc = new RAPIER.RigidBodyDesc(options.rigidBodyType as RAPIER.RigidBodyType)
// Set translation and rotation for the rigid body
rigidBodyDesc.setTranslation(options.position.x, options.position.y, options.position.z)
rigidBodyDesc.setRotation(
// Create quaternion from Euler angles
new THREE.Quaternion().setFromEuler(new THREE.Euler(options.rotation.x, options.rotation.y, options.rotation.z)),
)

this.rigidBody = this.scene.world.createRigidBody(rigidBodyDesc)

Expand Down Expand Up @@ -333,10 +337,27 @@ export abstract class FComponent3d extends FComponent {
)
}

// Create a collider description attached to the dynamic rigidBody
const colliderDesc = options.shape === F3dShapes.CUBE
? RAPIER.ColliderDesc.cuboid(options.scale.x, options.scale.y, options.scale.z)
: RAPIER.ColliderDesc.ball(options.scale.x)
// Create a collider description attached to the rigid body, according to the shape given
let colliderDesc
switch (options.shape) {
case F3dShapes.CUBE:
colliderDesc = RAPIER.ColliderDesc.cuboid(options.scale.x, options.scale.y, options.scale.z)
break
case F3dShapes.SPHERE:
colliderDesc = RAPIER.ColliderDesc.ball(options.scale.x)
break
case F3dShapes.CAPSULE:
colliderDesc = RAPIER.ColliderDesc.capsule(options.scale.x, options.scale.y)
break
case F3dShapes.MESH:
colliderDesc = RAPIER.ColliderDesc.trimesh(
this.mesh?.geometry.attributes.position.array as Float32Array,
this.mesh?.geometry.index?.array as Uint32Array,
)
break
default:
throw new Error(`Shape not supported : ${options.shape}`)
}
// Create the collider
this.collider = this.scene.world.createCollider(colliderDesc, this.rigidBody)
}
Expand Down
12 changes: 12 additions & 0 deletions packages/3d/src/cameras/FCamera3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,16 @@ export abstract class FCamera3d extends THREE.PerspectiveCamera implements FCame
emitCollisionWith(classOrObject: any) {
FCamera.prototype.emitCollisionWith.call(this, classOrObject)
}

/**
* @description Get the direction of the camera.
* This method is useful to get the direction of the camera to apply forces in the direction of the camera.
* @returns The direction of the camera. It is a normalized vector.
*/
getCameraDirection() {
const direction = new THREE.Vector3()
this.getWorldDirection(direction)
direction.normalize()
return direction
}
}
1 change: 1 addition & 0 deletions packages/3d/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { FScene3d } from './FScene3d'
export { FComponent3d } from './FComponent3d'

// Models
export { FCapsule } from './model/FCapsule'
export { FCube } from './model/FCube'
export { FGLTF } from './model/FGLTF'
export { FPolyhedron } from './model/FPolyhedron'
Expand Down
74 changes: 74 additions & 0 deletions packages/3d/src/model/FCapsule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as THREE from 'three'
import type * as RAPIER from '@dimforge/rapier3d'
import type { FScene3d } from '../FScene3d'
import { F3dShapes } from '../types/F3dShapes'
import { FPolyhedron } from './FPolyhedron'

/**
* @description A simple capsule model in FibboJS.
* @category Model
* @example
* ```ts
* import { FScene3d, FCapsule } from '@fibbojs/3d'
*
* const scene = new FScene3d()
*
* const capsule = new FCapsule(scene)
* scene.addComponent(capsule)
* ```
*/
export class FCapsule extends FPolyhedron {
constructor(scene: FScene3d, options?: {
position?: { x: number, y: number, z: number }
scale?: { x: number, y: number, z: number }
rotation?: { x: number, y: number, z: number }
rotationDegree?: { x: number, y: number, z: number }
}) {
super(scene, options)
// Create a capsule
const geometry = new THREE.CapsuleGeometry(0.5, 1, 32)
const material = new THREE.MeshBasicMaterial({ color: 0x666666 })
this.mesh = new THREE.Mesh(geometry, material)
}

onFrame(_delta: number): void {
super.onFrame(_delta)
}

initRigidBody(options?: {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
rigidBodyType?: RAPIER.RigidBodyType
lockTranslations?: boolean
lockRotations?: boolean
enabledTranslations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
enabledRotations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
}): void {
super.initRigidBody({
...options,
shape: F3dShapes.CAPSULE,
})
}

initCollider(options?: {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
}): void {
super.initCollider({
...options,
shape: F3dShapes.CAPSULE,
})
}
}
8 changes: 5 additions & 3 deletions packages/3d/src/model/FSphere.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as THREE from 'three'
import type * as RAPIER from '@dimforge/rapier3d'
import type { FScene3d } from '../FScene3d'
import { F3dShapes } from '../types/F3dShapes'
import { FPolyhedron } from './FPolyhedron'
Expand All @@ -12,8 +13,8 @@ import { FPolyhedron } from './FPolyhedron'
*
* const scene = new FScene3d()
*
* const cube = new FSphere(scene)
* scene.addComponent(cube)
* const sphere = new FSphere(scene)
* scene.addComponent(sphere)
* ```
*/
export class FSphere extends FPolyhedron {
Expand All @@ -24,7 +25,7 @@ export class FSphere extends FPolyhedron {
rotationDegree?: { x: number, y: number, z: number }
}) {
super(scene, options)
// Create a cube
// Create a sphere
const geometry = new THREE.SphereGeometry(0.5, 32, 32)
const material = new THREE.MeshBasicMaterial({ color: 0x666666 })
this.mesh = new THREE.Mesh(geometry, material)
Expand All @@ -39,6 +40,7 @@ export class FSphere extends FPolyhedron {
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
rigidBodyType?: RAPIER.RigidBodyType
lockTranslations?: boolean
lockRotations?: boolean
enabledTranslations?: {
Expand Down
2 changes: 2 additions & 0 deletions packages/3d/src/types/F3dShapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
export enum F3dShapes {
CUBE = 'Cube',
SPHERE = 'Sphere',
CAPSULE = 'Capsule',
MESH = 'Mesh',
}

0 comments on commit d08e8a7

Please sign in to comment.