UNPKG

@threejs-addons/threejs-addons

Version:

free & opensource threejs addons to boost productivity

233 lines (184 loc) 8.57 kB
import { World, KinematicCharacterController, Collider, CharacterCollision, QueryFilterFlags } from '@dimforge/rapier3d'; import { BaseController } from './Base'; import { Vector3 } from 'three/src/math/Vector3'; import { Quaternion } from 'three/src/math/Quaternion'; import { CameraControllerType } from '../../types'; import { CameraController } from './camera-controller'; interface RapierColliderControllerAPI { world: World; offset: number; controller: KinematicCharacterController; collider: Collider | null; toi: number; } export class RapierColliderController extends BaseController implements RapierColliderControllerAPI { world: World; offset: number; controller: KinematicCharacterController; collider: Collider | null = null; toi: number = 0; constructor({ world, offset, options, CCC } : { world: World, offset: number, options: object, CCC?: CameraController }){ super({ target: null, options, CCC }); this.world = world; this.offset = offset; this.controller = this.initCharacterController(); } /** * @description sets up the character controller * @returns CharacterController */ private initCharacterController(){ return this.world.createCharacterController(this.offset) as KinematicCharacterController; } /** * * @description compute movements of collider * @returns number of collisions */ private computeMovement({ collider, desiredMovementVector, filterFlags, filterGroups, filterPredicate } : { collider: Collider | null, desiredMovementVector: Vector3, filterFlags?: QueryFilterFlags | undefined, filterGroups?: number | undefined, filterPredicate?: (collider: Collider) => boolean }): null | number { let c = collider || this.collider; if(!c) return null; if(!desiredMovementVector) return null; this.controller.computeColliderMovement(c, desiredMovementVector, filterFlags, filterGroups, filterPredicate); return this.controller.numComputedCollisions(); } /** * @description get computed collisions * @returns Array of collisions */ private getComputedCollisions(): CharacterCollision[] { let collisions: CharacterCollision[] = []; for (let i = 0; i < this.controller.numComputedCollisions(); i++) { let collision = this.controller.computedCollision(i); collisions.push(collision as CharacterCollision); } return collisions; } /** * @description wether or not collision is imminent */ private isCollisionImminent(): boolean { let is_collision_imminent = false; for (let i = 0; i < this.controller.numComputedCollisions(); i++) { let collision = this.controller.computedCollision(i) as CharacterCollision; if(collision.toi <= this.toi) is_collision_imminent = true; } return is_collision_imminent; } /** * @description get corrected movement */ private getCorrectedMovement(): Vector3 { const { x, y, z } = this.controller.computedMovement(); return new Vector3(x, y, z); } /** * @param collider * @param updateTargetPosition Wether or not to update the target position. This forces the controller to try and update target position even if the collider is null * @description sets the collider */ setCollider(collider: Collider, updateTargetPosition?: boolean): void { this.collider = collider; if(!this.target && !updateTargetPosition)return; const { x, y, z} = this.collider.translation(); this.desiredMovementVector = new Vector3(x,y,z); this.updateTargetPosition(); } /** @description handle XYZ movements */ handleTranslateXYZDirection(): void { if(!this.collider) return; this.setColliderQuaternionFromCameraController(); const { x: cx, y: cy, z: cz } = this.collider.translation(); const computedTranslation = BaseController.computeTranslationXYZDirection({ speedFactor: this.speedFactor, SPEED_UP_CONSTANT: this.SPEED_UP_CONSTANT, delta: this.delta, direction: this.direction, translation: new Vector3(cx, cy, cz), cameraController: this.CCC?.activeController as CameraControllerType }); if(!computedTranslation) return; const { position: desiredMovementVector, velocity: desiredVelocityVector } = computedTranslation; const shape = this.collider?.shape const shapePosition = this.collider?.translation(); const shapeRotation = this.collider?.rotation(); const shapeVelocity = desiredVelocityVector.clone() let collisions = []; let hit = this.world.castShape(shapePosition, shapeRotation, shapeVelocity, shape, this.toi, false, undefined, this.options?.filterGroups, this.collider); if (hit != null) { collisions.push(hit); } let is_collision_imminent = false let collision_imminent = null for(const collision of collisions){ if(collision.toi <= this.toi) is_collision_imminent = true; if(is_collision_imminent) collision_imminent = collision if(is_collision_imminent) break; } if(is_collision_imminent && collision_imminent && collision_imminent?.toi <= this.toi){ this.desiredVelocityVector = new Vector3(0, 0, 0); }else{ this.desiredMovementVector = desiredMovementVector; /** carry out corrections based on max an min translation options */ // this.desiredMovementVector.y = Math.min(Math.max(this.desiredMovementVector.y, this.options?.translation?.max?.y), this.options?.translation?.max?.y); // this.desiredVelocityVector = desiredVelocityVector; // this.desiredVelocityVector.y = Math.min(Math.max(this.desiredMovementVector.y, this.options?.translation?.max?.y), this.options?.translation?.max?.y); this.desiredMovementVector.y = shapePosition.y this.desiredVelocityVector = desiredVelocityVector; this.desiredVelocityVector.y = 0 } /** update computational values */ this.updateColliderPosition(); // this current colliders position this.updateTargetPosition(); //if target is attached this.updateTargetQuaternion(); // if target is attached /** update internal values */ this._updateColliderPosition(); this._updateColliderQuaternion(); this._updateColliderVelocity(); } /** * @description set max time to impact for collider */ setToi(value: number){ this.toi = value; } /** set collider quartenion */ setColliderQuaternionFromCameraController(){ if(!this.collider) return; if(!this.CCC?.activeController) return; this.collider.setRotation({ x: this.CCC.activeController?.camera.quaternion.x as number, y: this.CCC.activeController?.camera.quaternion.y as number, z: this.CCC.activeController?.camera.quaternion.z as number, w: this.CCC.activeController?.camera.quaternion.w as number}); } updateColliderPosition(): void { if(this.collider) this.collider.setTranslation(this.desiredMovementVector); } /** update internal this.position value */ private _updateColliderPosition(): void { if(!this.collider) return; this.position.copy(this.desiredMovementVector); } /** update internal this.rotation value */ private _updateColliderQuaternion(): void { if(!this.collider) return; const { x, y, z, w } = this.collider.rotation(); this.rotation.copy(new Quaternion(x, y, z, w)); } /** update internal this.velocity */ private _updateColliderVelocity(): void { if(!this.collider) return; this.velocity.copy(this.desiredVelocityVector); } /** update filter groups on options */ updateFilterGroups(groups: number){ this.options.filterGroups = groups; } /** update options */ updateOptions(options: object){ this.options = options; } }