Skip to content

Commit d08e8a7

Browse files
committed
feat: improve camera for movements + init capsule polyhedron
1 parent 6f1a43c commit d08e8a7

File tree

7 files changed

+149
-29
lines changed

7 files changed

+149
-29
lines changed

apps/playground-3d/src/main.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { F3dShapes, FCube, FGameCamera, FScene3d, FSphere } from '@fibbojs/3d'
1+
import { F3dShapes, FCapsule, FCube, FGameCamera, FScene3d, FSphere } from '@fibbojs/3d'
22
import { fDebug } from '@fibbojs/devtools'
33
import { FKeyboard } from '@fibbojs/event'
44
import Duck from './classes/Duck'
@@ -22,15 +22,15 @@ import './style.css'
2222
ground.setColor(0x1F1F1F)
2323
scene.addComponent(ground)
2424

25-
// Create a cube that will be controlled by the player
26-
const cube = new FCube(scene, {
25+
// Create a capsule that will be controlled by the player
26+
const capsule = new FCapsule(scene, {
2727
position: { x: -5, y: 5, z: 5 },
2828
})
29-
cube.setColor(0xA0FFA0)
30-
cube.initRigidBody({
29+
capsule.setColor(0xA0FFA0)
30+
capsule.initRigidBody({
3131
lockRotations: true,
3232
})
33-
scene.addComponent(cube)
33+
scene.addComponent(capsule)
3434

3535
/**
3636
* Create other objects
@@ -67,34 +67,42 @@ import './style.css'
6767
/**
6868
* Add collision events
6969
*/
70-
cube.onCollisionWith(GltfCube, () => {
70+
capsule.onCollisionWith(GltfCube, () => {
7171
console.log('Cube collided with a GltfCube !')
7272
})
73-
cube.onCollisionWith(sphere, () => {
73+
capsule.onCollisionWith(sphere, () => {
7474
console.log('Cube collided with the sphere!')
7575
})
7676

7777
// Create a keyboard instance
7878
const fKeyboard = new FKeyboard(scene)
7979
// Detect inputs to move the cube
8080
fKeyboard.on('ArrowUp', () => {
81-
cube.rigidBody?.applyImpulse({ x: 0, y: 0, z: -0.2 }, true)
81+
const cameraDirection = scene.camera.getCameraDirection()
82+
cameraDirection.y = 0
83+
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(0.4), true)
8284
})
8385
fKeyboard.on('ArrowDown', () => {
84-
cube.rigidBody?.applyImpulse({ x: 0, y: 0, z: 0.2 }, true)
86+
const cameraDirection = scene.camera.getCameraDirection()
87+
cameraDirection.y = 0
88+
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(-0.4), true)
8589
})
8690
fKeyboard.on('ArrowLeft', () => {
87-
cube.rigidBody?.applyImpulse({ x: -0.2, y: 0, z: 0 }, true)
91+
const cameraDirection = scene.camera.getCameraDirection()
92+
cameraDirection.y = 0
93+
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(0.4).applyAxisAngle(new scene.THREE.Vector3(0, 1, 0), Math.PI / 2), true)
8894
})
8995
fKeyboard.on('ArrowRight', () => {
90-
cube.rigidBody?.applyImpulse({ x: 0.2, y: 0, z: 0 }, true)
96+
const cameraDirection = scene.camera.getCameraDirection()
97+
cameraDirection.y = 0
98+
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(-0.4).applyAxisAngle(new scene.THREE.Vector3(0, 1, 0), Math.PI / 2), true)
9199
})
92100
fKeyboard.on(' ', () => {
93-
cube.rigidBody?.applyImpulse({ x: 0, y: 1, z: 0 }, true)
101+
capsule.rigidBody?.applyImpulse({ x: 0, y: 1, z: 0 }, true)
94102
})
95103

96104
// Attach a camera to the cube
97-
scene.camera = new FGameCamera(cube, scene)
105+
scene.camera = new FGameCamera(capsule, scene)
98106

99107
// After 3 seconds, add a third gltfCube
100108
setTimeout(() => {

packages/3d/src/FComponent3d.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ export abstract class FComponent3d extends FComponent {
240240
* @param options.scale The scale of the rigid body. If not defined, it will use the default scale of the FComponent3d.
241241
* @param options.rotation The rotation of the rigid body. If not defined, it will use the default rotation of the FComponent3d.
242242
* @param options.shape The shape of the rigid body. If not defined, it will default to F3dShapes.CUBE.
243+
* @param options.rigidBodyType The type of the rigid body. If not defined, it will default to RAPIER.RigidBodyType.Dynamic.
243244
* @param options.lockTranslations If true, the rigid body will not be able to move.
244245
* @param options.lockRotations If true, the rigid body will not be able to rotate.
245246
* @param options.enabledTranslations If defined, it will enable or disable translations on the x and y axis.
@@ -265,6 +266,7 @@ export abstract class FComponent3d extends FComponent {
265266
scale?: THREE.Vector3
266267
rotation?: THREE.Vector3
267268
shape?: F3dShapes
269+
rigidBodyType?: RAPIER.RigidBodyType
268270
lockTranslations?: boolean
269271
lockRotations?: boolean
270272
enabledTranslations?: {
@@ -284,6 +286,7 @@ export abstract class FComponent3d extends FComponent {
284286
scale: new THREE.Vector3(this.scale.x / 2, this.scale.y / 2, this.scale.z / 2),
285287
rotation: new THREE.Vector3(this.rotation.x, this.rotation.y, this.rotation.z),
286288
shape: F3dShapes.CUBE,
289+
rigidBodyType: RAPIER.RigidBodyType.Dynamic,
287290
lockTranslations: false,
288291
lockRotations: false,
289292
enabledTranslations: undefined,
@@ -292,19 +295,20 @@ export abstract class FComponent3d extends FComponent {
292295
options = { ...DEFAULT_OPTIONS, ...options }
293296
// Validate options
294297
if (!options.position || !options.scale || !options.rotation || !options.shape)
295-
throw new Error('initRigidBody requires position, scale, rotation and shape options')
298+
throw new Error('initRigidBody requires position, scale, rotation, shape and rigidBodyType options')
296299

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

301-
// Create a dynamic rigid-body.
302-
const rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
303-
.setTranslation(options.position.x, options.position.y, options.position.z)
304-
.setRotation(
305-
// Create quaternion from Euler angles
306-
new THREE.Quaternion().setFromEuler(new THREE.Euler(options.rotation.x, options.rotation.y, options.rotation.z)),
307-
)
304+
// Create a rigid body description according to the type
305+
const rigidBodyDesc = new RAPIER.RigidBodyDesc(options.rigidBodyType as RAPIER.RigidBodyType)
306+
// Set translation and rotation for the rigid body
307+
rigidBodyDesc.setTranslation(options.position.x, options.position.y, options.position.z)
308+
rigidBodyDesc.setRotation(
309+
// Create quaternion from Euler angles
310+
new THREE.Quaternion().setFromEuler(new THREE.Euler(options.rotation.x, options.rotation.y, options.rotation.z)),
311+
)
308312

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

@@ -333,10 +337,27 @@ export abstract class FComponent3d extends FComponent {
333337
)
334338
}
335339

336-
// Create a collider description attached to the dynamic rigidBody
337-
const colliderDesc = options.shape === F3dShapes.CUBE
338-
? RAPIER.ColliderDesc.cuboid(options.scale.x, options.scale.y, options.scale.z)
339-
: RAPIER.ColliderDesc.ball(options.scale.x)
340+
// Create a collider description attached to the rigid body, according to the shape given
341+
let colliderDesc
342+
switch (options.shape) {
343+
case F3dShapes.CUBE:
344+
colliderDesc = RAPIER.ColliderDesc.cuboid(options.scale.x, options.scale.y, options.scale.z)
345+
break
346+
case F3dShapes.SPHERE:
347+
colliderDesc = RAPIER.ColliderDesc.ball(options.scale.x)
348+
break
349+
case F3dShapes.CAPSULE:
350+
colliderDesc = RAPIER.ColliderDesc.capsule(options.scale.x, options.scale.y)
351+
break
352+
case F3dShapes.MESH:
353+
colliderDesc = RAPIER.ColliderDesc.trimesh(
354+
this.mesh?.geometry.attributes.position.array as Float32Array,
355+
this.mesh?.geometry.index?.array as Uint32Array,
356+
)
357+
break
358+
default:
359+
throw new Error(`Shape not supported : ${options.shape}`)
360+
}
340361
// Create the collider
341362
this.collider = this.scene.world.createCollider(colliderDesc, this.rigidBody)
342363
}

packages/3d/src/cameras/FCamera3d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,16 @@ export abstract class FCamera3d extends THREE.PerspectiveCamera implements FCame
4646
emitCollisionWith(classOrObject: any) {
4747
FCamera.prototype.emitCollisionWith.call(this, classOrObject)
4848
}
49+
50+
/**
51+
* @description Get the direction of the camera.
52+
* This method is useful to get the direction of the camera to apply forces in the direction of the camera.
53+
* @returns The direction of the camera. It is a normalized vector.
54+
*/
55+
getCameraDirection() {
56+
const direction = new THREE.Vector3()
57+
this.getWorldDirection(direction)
58+
direction.normalize()
59+
return direction
60+
}
4961
}

packages/3d/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { FScene3d } from './FScene3d'
66
export { FComponent3d } from './FComponent3d'
77

88
// Models
9+
export { FCapsule } from './model/FCapsule'
910
export { FCube } from './model/FCube'
1011
export { FGLTF } from './model/FGLTF'
1112
export { FPolyhedron } from './model/FPolyhedron'

packages/3d/src/model/FCapsule.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as THREE from 'three'
2+
import type * as RAPIER from '@dimforge/rapier3d'
3+
import type { FScene3d } from '../FScene3d'
4+
import { F3dShapes } from '../types/F3dShapes'
5+
import { FPolyhedron } from './FPolyhedron'
6+
7+
/**
8+
* @description A simple capsule model in FibboJS.
9+
* @category Model
10+
* @example
11+
* ```ts
12+
* import { FScene3d, FCapsule } from '@fibbojs/3d'
13+
*
14+
* const scene = new FScene3d()
15+
*
16+
* const capsule = new FCapsule(scene)
17+
* scene.addComponent(capsule)
18+
* ```
19+
*/
20+
export class FCapsule extends FPolyhedron {
21+
constructor(scene: FScene3d, options?: {
22+
position?: { x: number, y: number, z: number }
23+
scale?: { x: number, y: number, z: number }
24+
rotation?: { x: number, y: number, z: number }
25+
rotationDegree?: { x: number, y: number, z: number }
26+
}) {
27+
super(scene, options)
28+
// Create a capsule
29+
const geometry = new THREE.CapsuleGeometry(0.5, 1, 32)
30+
const material = new THREE.MeshBasicMaterial({ color: 0x666666 })
31+
this.mesh = new THREE.Mesh(geometry, material)
32+
}
33+
34+
onFrame(_delta: number): void {
35+
super.onFrame(_delta)
36+
}
37+
38+
initRigidBody(options?: {
39+
position?: THREE.Vector3
40+
scale?: THREE.Vector3
41+
rotation?: THREE.Vector3
42+
shape?: F3dShapes
43+
rigidBodyType?: RAPIER.RigidBodyType
44+
lockTranslations?: boolean
45+
lockRotations?: boolean
46+
enabledTranslations?: {
47+
enableX: boolean
48+
enableY: boolean
49+
enableZ: boolean
50+
}
51+
enabledRotations?: {
52+
enableX: boolean
53+
enableY: boolean
54+
enableZ: boolean
55+
}
56+
}): void {
57+
super.initRigidBody({
58+
...options,
59+
shape: F3dShapes.CAPSULE,
60+
})
61+
}
62+
63+
initCollider(options?: {
64+
position?: THREE.Vector3
65+
scale?: THREE.Vector3
66+
rotation?: THREE.Vector3
67+
shape?: F3dShapes
68+
}): void {
69+
super.initCollider({
70+
...options,
71+
shape: F3dShapes.CAPSULE,
72+
})
73+
}
74+
}

packages/3d/src/model/FSphere.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as THREE from 'three'
2+
import type * as RAPIER from '@dimforge/rapier3d'
23
import type { FScene3d } from '../FScene3d'
34
import { F3dShapes } from '../types/F3dShapes'
45
import { FPolyhedron } from './FPolyhedron'
@@ -12,8 +13,8 @@ import { FPolyhedron } from './FPolyhedron'
1213
*
1314
* const scene = new FScene3d()
1415
*
15-
* const cube = new FSphere(scene)
16-
* scene.addComponent(cube)
16+
* const sphere = new FSphere(scene)
17+
* scene.addComponent(sphere)
1718
* ```
1819
*/
1920
export class FSphere extends FPolyhedron {
@@ -24,7 +25,7 @@ export class FSphere extends FPolyhedron {
2425
rotationDegree?: { x: number, y: number, z: number }
2526
}) {
2627
super(scene, options)
27-
// Create a cube
28+
// Create a sphere
2829
const geometry = new THREE.SphereGeometry(0.5, 32, 32)
2930
const material = new THREE.MeshBasicMaterial({ color: 0x666666 })
3031
this.mesh = new THREE.Mesh(geometry, material)
@@ -39,6 +40,7 @@ export class FSphere extends FPolyhedron {
3940
scale?: THREE.Vector3
4041
rotation?: THREE.Vector3
4142
shape?: F3dShapes
43+
rigidBodyType?: RAPIER.RigidBodyType
4244
lockTranslations?: boolean
4345
lockRotations?: boolean
4446
enabledTranslations?: {

packages/3d/src/types/F3dShapes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
export enum F3dShapes {
33
CUBE = 'Cube',
44
SPHERE = 'Sphere',
5+
CAPSULE = 'Capsule',
6+
MESH = 'Mesh',
57
}

0 commit comments

Comments
 (0)