diff --git a/package.json b/package.json index 46b92ca..e7fffcd 100644 --- a/package.json +++ b/package.json @@ -18,31 +18,22 @@ "@supabase/auth-helpers-nextjs": "^0.2.9", "@supabase/auth-helpers-react": "^0.2.3", "@supabase/supabase-js": "^1.35.7", - "camera-controls": "^1.37.2", - "cannon-es": "^0.19.0", - "cannon-es-debugger": "^1.0.0", - "gamepads": "^1.2.2", "hamburger-react": "^2.5.0", "hash-wasm": "^4.9.0", "lazysizes": "^5.3.2", "lodash": "^4.17.21", "next": "12.3.1", "next-seo": "^5.5.0", - "nipplejs": "^0.10.0", "react": "18.2.0", "react-dom": "18.2.0", "react-draggable": "^4.4.5", "react-icons": "^4.4.0", "react-imgix": "^9.5.4", "react-query": "^3.39.2", - "react-scrolllock": "^5.0.1", "react-textarea-autosize": "^8.3.4", "react-transition-group": "^4.4.5", - "three": "^0.142.0", - "three-mesh-bvh": "^0.5.14", - "three-stdlib": "^2.12.1", - "three-to-cannon": "^4.1.0", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "web-worlding": "^0.1.3" }, "devDependencies": { "@types/lodash": "^4.14.186", diff --git a/src/components/Space/Space.tsx b/src/components/Space/Space.tsx index af1e12c..b81bcd6 100644 --- a/src/components/Space/Space.tsx +++ b/src/components/Space/Space.tsx @@ -5,11 +5,10 @@ * 2022 the nobot space, */ import { useEffect, useRef, useState } from 'react'; -import * as APIt from '../../supabase/types'; -import { World } from './game/world/World'; +import type World from 'web-worlding'; import { AiOutlineCamera, AiOutlineUser } from 'react-icons/ai'; import s from './Space.module.scss'; -import { InputButton } from './game/enums/UserInputs'; +import { InputButton } from 'web-worlding/dist/enums/UserInputs'; type SpaceProps = { world: string; @@ -44,16 +43,26 @@ const Space: React.FC = function Space({ world }) { worldRef.current.destroy(); } // now rebuild it with the better world - worldRef.current = new World(canvasRef.current!, world, { - onDownloadFinish: () => setIsLoading(false), - onDownloadProgress: (p: number, d: number, t: number) => { - if (loadingRef.current && loadingDRef.current && loadingTRef.current) { - loadingRef.current.style.transform = `scaleX(${p})`; - loadingDRef.current.textContent = formatBytes(d, 1); - loadingTRef.current.textContent = formatBytes(t, 1); - } - }, - }); + var WebWorld: typeof World = require('web-worlding').default; + worldRef.current = new WebWorld( + canvasRef.current!, + '/assets/characters/personspace.glb', + world, + { + onDownloadFinish: () => setIsLoading(false), + onDownloadProgress: (p: number, d: number, t: number) => { + if ( + loadingRef.current && + loadingDRef.current && + loadingTRef.current + ) { + loadingRef.current.style.transform = `scaleX(${p})`; + loadingDRef.current.textContent = formatBytes(d, 1); + loadingTRef.current.textContent = formatBytes(t, 1); + } + }, + } + ); }, [world]); return ( diff --git a/src/components/Space/game/core/CameraOperator.ts b/src/components/Space/game/core/CameraOperator.ts deleted file mode 100644 index e62ea57..0000000 --- a/src/components/Space/game/core/CameraOperator.ts +++ /dev/null @@ -1,284 +0,0 @@ -/* - * CameraOperator.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import CameraControls from 'camera-controls'; -import _ from 'lodash'; -import { EventData, JoystickOutputData } from 'nipplejs'; -import * as THREE from 'three'; -import { acceleratedRaycast } from 'three-mesh-bvh'; -import { InputButton } from '../enums/UserInputs'; -import { IInputReceiver } from '../interfaces/IInputReceiver'; -import { IUpdatable } from '../interfaces/IUpdatable'; -import { Nobot } from '../nobots/Nobot'; -import { World } from '../world/World'; -import * as Utils from './FunctionLibrary'; -import { InputManager } from './InputManager'; -import { KeyBinding } from './KeyBinding'; - -THREE.Mesh.prototype.raycast = acceleratedRaycast; -// initialize camera controller -CameraControls.install({ THREE }); - -export class CameraOperator - extends CameraControls - implements IInputReceiver, IUpdatable -{ - public updateOrder: number = 4; - public inputManager: InputManager; - - // scene / world properties - public world: World; - public nobotCaller?: Nobot; - - // statefuls - public target?: THREE.Object3D; - public freeTarget: THREE.Object3D; - public followMode: boolean = true; - public transitioning: boolean = false; - - // constraints - public movementSpeed: number; - - // free- state - public upVelocity: number = 0; - public forwardVelocity: number = 0; - public rightVelocity: number = 0; - // rotation velocity - public azimuthVelocity: number = 0; - public polarVelocity: number = 0; - - /** - * Constructs a CameraOperator which can be added as an updatable to the world. - * The CameraOperator follows the nobot from its position, allowing for a minor - * level of offset from mouse movement based on the mouse position in the canvas. - * @param world - * @param camera - */ - constructor( - world: World, - camera: THREE.PerspectiveCamera, - domElement: HTMLCanvasElement, - inputManager: InputManager - ) { - super(camera, domElement); - // set properties - this.world = world; - this.camera = camera; - this.inputManager = inputManager; - - // offset state - this.freeTarget = new THREE.Object3D(); - this.minZoom = 0.5; - this.maxZoom = 4; - this.movementSpeed = 0.06; - this.restThreshold = 0.1; - this.maxPolarAngle = Math.PI * 0.55; - } - - /* -------------------------------------------------------------------------- */ - /* UPDATE LOOP */ - /* -------------------------------------------------------------------------- */ - - /** - * Runs one step of updating the camera. TODO: add offset - */ - public update2(delta: number): void { - // during transitions, skip update - if (!this.transitioning) { - // wwhen following, keep position offset from target - if (this.followMode === true && this.target) { - const x = this.target.position.x; - const y = this.target.position.y; - const z = this.target.position.z; - this.moveTo(x, y, z, false); - // when free, just calculate the position based on targetPos + velocity - } else { - // calculate new target position - const speed = - this.movementSpeed * - (this.inputManager.buttons.speed.isPressed - ? delta * 600 - : delta * 120); - const up = Utils.getUp(this.camera); - const right = Utils.getRight(this.camera); - const forward = Utils.getBack(this.camera); - this.freeTarget.position.add( - up.multiplyScalar(speed * this.upVelocity) - ); - this.freeTarget.position.add( - forward.multiplyScalar(speed * this.forwardVelocity) - ); - this.freeTarget.position.add( - right.multiplyScalar(speed * this.rightVelocity) - ); - const x = this.freeTarget.position.x; - const y = this.freeTarget.position.y; - const z = this.freeTarget.position.z; - this.moveTo(x, y, z, false); - // decay the velocity of camera movement - this.rightVelocity = THREE.MathUtils.lerp(this.rightVelocity, 0, 0.3); - this.upVelocity = THREE.MathUtils.lerp(this.upVelocity, 0, 0.3); - this.forwardVelocity = THREE.MathUtils.lerp( - this.forwardVelocity, - 0, - 0.3 - ); - } - // calculate new rotation - this.updateCameraRotationControls(); - this.rotate(this.azimuthVelocity, this.polarVelocity, false); - this.azimuthVelocity = THREE.MathUtils.lerp(this.azimuthVelocity, 0, 0.3); - this.polarVelocity = THREE.MathUtils.lerp(this.polarVelocity, 0, 0.3); - } - // call the initial camera controls update func - this.update(delta); - } - - /* -------------------------------------------------------------------------- */ - /* LISTENERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Funnels a button event through to its action handler for the nobot. - * @param event The nipple event passed from an InputManager - * @param data The state of the joystick - */ - public handleButtonEvent(button: InputButton, pressed: boolean): void { - if (button === InputButton.VIEWTOGGLE && pressed) { - this.followNobot(); - } - } - - /* -------------------------------------------------------------------------- */ - /* INPUT RECEIVER */ - /* -------------------------------------------------------------------------- */ - - /** - * Initialize the input receiver, by simply placing the camera in the scene. - */ - public inputReceiverInit(): void { - this.freeTarget.position.copy(this.camera.position); - this.target = this.freeTarget; - this.distance = 1e-5; - // move target to the freetarget - this.moveTo( - this.target.position.x, - this.target.position.y, - this.target.position.z, - true - ); - this.minDistance = this.maxDistance = 1e-5; - this.azimuthRotateSpeed = 0.4; - this.polarRotateSpeed = 0.4; - this.maxPolarAngle = Math.PI; - this.followMode = false; - this.mouseButtons.wheel = CameraControls.ACTION.ZOOM; - this.touches.two = CameraControls.ACTION.TOUCH_ZOOM; - } - - /** - * When the camera is the input receiver, allow it to fly around - * @param timeStep - */ - public inputReceiverUpdate(timeStep: number): void { - // handle movement using main joystick - const joystick = this.inputManager.joysticks.main; - const buttons = this.inputManager.buttons; - this.upVelocity = THREE.MathUtils.lerp( - this.upVelocity, - Number(buttons.up.isPressed) - Number(buttons.down.isPressed), - 0.3 - ); - this.forwardVelocity = THREE.MathUtils.lerp( - this.forwardVelocity, - Number(joystick.isActive) * Math.sin(joystick.angle), - 0.3 - ); - this.rightVelocity = THREE.MathUtils.lerp( - this.rightVelocity, - Number(joystick.isActive) * Math.cos(joystick.angle), - 0.3 - ); - } - - /** - * No camera state, so we don't care about "just" values - */ - public inputReceiverChange(): void { - return; - } - - /* -------------------------------------------------------------------------- */ - /* HELPERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Moves the focus of the camera back to the nobot. - */ - private followNobot() { - if (this.nobotCaller !== undefined) { - // reset our things back to defaults - this.minDistance = 1; - this.maxDistance = 20; - this.maxPolarAngle = Math.PI * 0.55; - this.azimuthRotateSpeed = 1.0; - this.polarRotateSpeed = 1.0; - this.transitioning = true; - // get 1-distance offset between camera and nobot - const newPos = this.nobotCaller.position - .clone() - .sub(Utils.getForward(this.nobotCaller).multiplyScalar(1.5)) - .add(new THREE.Vector3(0, 1, 0)); - // move our target back to character - const nobot = this.nobotCaller; - this.zoomTo(1, true); - this.setLookAt( - newPos.x, - newPos.y, - newPos.z, - this.nobotCaller.position.x, - this.nobotCaller.position.y, - this.nobotCaller.position.z, - true - ).then(() => { - this.world.inputManager.setInputReceiver(nobot); - this.nobotCaller = undefined; - this.followMode = true; - this.transitioning = false; - this.distance = 2; - this.mouseButtons.wheel = CameraControls.ACTION.DOLLY; - this.touches.two = CameraControls.ACTION.TOUCH_DOLLY; - }); - } - } - - /** - * Updates the speed of rotation of the camera based on secondary joystick - * input, used for both nobot and camera controlling. Replaces usual mouse - * or touch controls. - */ - public updateCameraRotationControls() { - const joystick = this.inputManager.joysticks.secondary; - const azimuthMultiplier = this.followMode ? 0.1 : 0.05; - const polarMultiplier = -0.025; - this.azimuthVelocity = THREE.MathUtils.lerp( - this.azimuthVelocity, - Number(joystick.isActive) * - Math.cos(joystick.angle) * - joystick.magnitude * - azimuthMultiplier, - 0.3 - ); - this.polarVelocity = THREE.MathUtils.lerp( - this.polarVelocity, - Number(joystick.isActive) * - Math.sin(joystick.angle) * - joystick.magnitude * - polarMultiplier, - 0.3 - ); - } -} diff --git a/src/components/Space/game/core/FunctionLibrary.ts b/src/components/Space/game/core/FunctionLibrary.ts deleted file mode 100644 index 822a703..0000000 --- a/src/components/Space/game/core/FunctionLibrary.ts +++ /dev/null @@ -1,298 +0,0 @@ -/* - * FunctionLibrary.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as CANNON from 'cannon-es'; -import * as THREE from 'three'; -import { WorldSpace } from '../enums/WorldSpace'; -import { SimulationFrame } from '../physics/simulators/SimulationFrame'; - -/* -------------------------------------------------------------------------- */ -/* MESHES */ -/* -------------------------------------------------------------------------- */ - -/** - * Converts mesh materials in a GLTF scene to Phong materials. - * @param child The mesh object in the GLTF scene - */ -export function setUpMeshProperties(child: THREE.Mesh): void { - child.castShadow = true; - child.receiveShadow = true; - - // function for setting up phong materials - function phongifyMaterial( - material: THREE.MeshPhongMaterial - ): THREE.MeshPhongMaterial { - if (material.map !== null) { - const mat = new THREE.MeshPhongMaterial(); - mat.shininess = 0; - mat.name = material.name; - mat.map = material.map; - mat.map!.anisotropy = 4; - mat.aoMap = material.aoMap; - mat.transparent = material.transparent; - return mat; - } - return material; - } - - // apply function to all children of array - if (Array.isArray(child.material)) { - for (let i = 0; i < child.material.length; i += 1) { - child.material[i] = phongifyMaterial( - child.material[i] as THREE.MeshPhongMaterial - ); - } - // otherwise just apply to single material - } else { - child.material = phongifyMaterial( - child.material as THREE.MeshPhongMaterial - ); - } -} - -/* -------------------------------------------------------------------------- */ -/* CASTING */ -/* -------------------------------------------------------------------------- */ - -/* ----------------------------- CANNON to THREE ---------------------------- */ - -/** - * Converts a CANNON.Vec3 to a THREE.Vector3 - * @param vec The source CANNON vec3 - * @returns An equivalent THREE vector3 - */ -export function threeVector(vec: CANNON.Vec3): THREE.Vector3 { - return new THREE.Vector3(vec.x, vec.y, vec.z); -} - -/** - * Converts a CANNOON.Quaternion to a THREE.Quaternion - * @param vec The source CANNON quaternion - * @returns An equivalent THREE quaternion - */ -export function threeQuat(quat: CANNON.Quaternion): THREE.Quaternion { - return new THREE.Quaternion(quat.x, quat.y, quat.z, quat.w); -} - -/* ----------------------------- THREE to CANNON ---------------------------- */ - -/** - * Converts a THREE.Vector3 to a CANNON.Vec3 - * @param vec The source THREE vector3 - * @returns An equivalent CANNON vec3 - */ -export function cannonVector(vec: THREE.Vector3): CANNON.Vec3 { - return new CANNON.Vec3(vec.x, vec.y, vec.z); -} - -/** - * Converts a THREE.Quaternion to a CANNON.Quaternion - * @param vec The source THREE quaternion - * @returns An equivalent CANNON quaternion - */ -export function cannonQuat(quat: THREE.Quaternion): CANNON.Quaternion { - return new CANNON.Quaternion(quat.x, quat.y, quat.z, quat.w); -} - -/* -------------------------------------------------------------------------- */ -/* SIMULATION */ -/* -------------------------------------------------------------------------- */ - -/** - * Spring simulation function for single numbers - * @param source The initial value - * @param dest The destination value - * @param velocity The velocity moving towards the destination - * @param mass The mass of the object moving towards the destination - * @param damping How much to dampen the calculation - */ -export function spring( - source: number, - dest: number, - velocity: number, - mass: number, - damping: number -): SimulationFrame { - const acceleration = (dest - source) / mass; - velocity += acceleration; - velocity *= damping; - const position = source + velocity; - return new SimulationFrame(position, velocity); -} - -/** - * Spring simulation function for vectors. Operates in-place! - * @param source The initial value - * @param dest The destination value - * @param velocity The velocity moving towards the destination - * @param mass The mass of the object moving towards the destination - * @param damping How much to dampen the calculation - */ -export function springVector( - source: THREE.Vector3, - dest: THREE.Vector3, - velocity: THREE.Vector3, - mass: number, - damping: number -): void { - const acceleration = new THREE.Vector3().subVectors(dest, source); - acceleration.divideScalar(mass); - velocity.add(acceleration); - velocity.multiplyScalar(damping); - source.add(velocity); -} - -/* -------------------------------------------------------------------------- */ -/* MATH */ -/* -------------------------------------------------------------------------- */ - -/** - * Checks if two numbers have the same signs - * @param n1 The first number - * @param n2 The second number - */ -export function haveSameSigns(n1: number, n2: number): boolean { - return n1 < 0 === n2 < 0; -} - -/** - * Checks if two numbers have different signs - * @param n1 The first number - * @param n2 The second number - */ -export function haveDifferentSigns(n1: number, n2: number): boolean { - return n1 < 0 !== n2 < 0; -} - -/** - * Constructs a 2D matrix from first vector, replacing the Y axes with the global Y axis, - * and applies this matrix to the second vector. Saves performance when compared to full 3D matrix application. - * Useful for character rotation, as it only happens on the Y axis. - * @param a Vector to construct 2D matrix from - * @param b Vector to apply basis to - */ -export function applyVectorMatrixXZ( - a: THREE.Vector3, - b: THREE.Vector3 -): THREE.Vector3 { - return new THREE.Vector3(a.x * b.z + a.z * b.x, b.y, a.z * b.z + -a.x * b.x); -} - -/* -------------------------------------------------------------------------- */ -/* DIRECTIONS */ -/* -------------------------------------------------------------------------- */ - -/** - * Returns the local or global world matrix - * @param obj - * @param space - * @returns - */ -export function getMatrix( - obj: THREE.Object3D, - space: WorldSpace -): THREE.Matrix4 { - if (space === WorldSpace.Local) return obj.matrix; - return obj.matrixWorld; -} - -export function getRight( - obj: THREE.Object3D, - space: WorldSpace = WorldSpace.Global -): THREE.Vector3 { - const matrix = getMatrix(obj, space); - return new THREE.Vector3( - matrix.elements[0], - matrix.elements[1], - matrix.elements[2] - ); -} - -export function getUp( - obj: THREE.Object3D, - space: WorldSpace = WorldSpace.Global -): THREE.Vector3 { - const matrix = getMatrix(obj, space); - return new THREE.Vector3( - matrix.elements[4], - matrix.elements[5], - matrix.elements[6] - ); -} - -export function getForward( - obj: THREE.Object3D, - space: WorldSpace = WorldSpace.Global -): THREE.Vector3 { - const matrix = getMatrix(obj, space); - return new THREE.Vector3( - matrix.elements[8], - matrix.elements[9], - matrix.elements[10] - ); -} - -export function getBack( - obj: THREE.Object3D, - space: WorldSpace = WorldSpace.Global -): THREE.Vector3 { - const matrix = getMatrix(obj, space); - return new THREE.Vector3( - -matrix.elements[8], - -matrix.elements[9], - -matrix.elements[10] - ); -} - -/* -------------------------------------------------------------------------- */ -/* ANGLES BETWEEN VECTORS */ -/* -------------------------------------------------------------------------- */ - -/** - * Gets the angle between two vectors, unsigned - * @param v1 - * @param v2 - * @param dotTreshold - * @returns - */ -export function getAngleBetweenVectors( - v1: THREE.Vector3, - v2: THREE.Vector3, - dotTreshold: number = 0.0005 -): number { - let angle: number; - const dot = v1.dot(v2); - // If dot is close to 1, we'll round angle to zero - if (dot > 1 - dotTreshold) { - angle = 0; - } else if (dot < -1 + dotTreshold) { - angle = Math.PI; - } else { - // Get angle difference in radians - angle = Math.acos(dot); - } - - return angle; -} - -/** - * Finds an angle between two vectors with a sign relative to normal vector - */ -export function getSignedAngleBetweenVectors( - v1: THREE.Vector3, - v2: THREE.Vector3, - normal: THREE.Vector3 = new THREE.Vector3(0, 1, 0), - dotTreshold: number = 0.0005 -): number { - let angle = getAngleBetweenVectors(v1, v2, dotTreshold); - // Get vector pointing up or down - const cross = new THREE.Vector3().crossVectors(v1, v2); - // Compare cross with normal to find out direction - if (normal.dot(cross) < 0) { - angle = -angle; - } - return angle; -} diff --git a/src/components/Space/game/core/InputManager.ts b/src/components/Space/game/core/InputManager.ts deleted file mode 100644 index a9f5ab6..0000000 --- a/src/components/Space/game/core/InputManager.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - * InputManager.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import { IInputReceiver } from '../interfaces/IInputReceiver'; -import { IUpdatable } from '../interfaces/IUpdatable'; -import { World } from '../world/World'; -import type NippleJs from 'nipplejs'; -import { InputButton, InputJoystick } from '../enums/UserInputs'; -import { JoystickBinding } from '../input/JoystickBinding'; -import { ButtonBinding } from '../input/ButtonBinding'; -import { IInputProvider } from '../interfaces/IInputProvider'; -import KeyboardInputProvider from '../input/providers/KeyboardInputProvider'; -import TouchInputProvider from '../input/providers/TouchInputProvider'; -import GamepadInputProvider from '../input/providers/GamepadInputProvider'; - -let nipplejs: typeof NippleJs; - -export class InputManager implements IUpdatable { - public updateOrder: number = 3; - - // reference to the world and target - public world: World; - - /* -------------------------------------------------------------------------- */ - /* CONTROL ELEMENTS */ - /* -------------------------------------------------------------------------- */ - - // bindings to keep track of input state from input receivers - public joysticks: { [joystick in InputJoystick]: JoystickBinding } = { - main: new JoystickBinding(), - secondary: new JoystickBinding(), - }; - public buttons: { [button in InputButton]: ButtonBinding } = { - up: new ButtonBinding(), - down: new ButtonBinding(), - viewtoggle: new ButtonBinding(), - use: new ButtonBinding(), - speed: new ButtonBinding(), - }; - - // is listening to input? - public isListening: boolean = false; - public pointerLock: boolean = true; - public isLocked: boolean = false; - - // providers - public inputProviders: IInputProvider[] = []; - - // receiver of the inputs - public inputReceiver?: IInputReceiver; - - /** - * Initialize the listeners to the world - * @param world - */ - constructor(world: World, domElement?: HTMLElement) { - // init properties - this.world = world; - - // add input providers for different modes of use - this.inputProviders = [ - new KeyboardInputProvider(this), - new TouchInputProvider(this, domElement), - new GamepadInputProvider(this), - ]; - - // now start listening - this.listen(); - - // register as updatable - world.registerUpdatable(this); - } - - /** - * Update the input receiver by one timestep. - * @param timestep - * @param unscaledTimeStep - */ - public update(timestep: number): void { - if (!this.inputReceiver && this.world && this.world.cameraOperator) { - this.setInputReceiver(this.world.cameraOperator); - } - this.inputProviders.forEach((provider) => { - if (provider.update) provider.update(); - }); - this.inputReceiver?.inputReceiverUpdate(timestep); - } - - /** - * Bind an input receiver to the manager to consume all of its events. - * @param receiver The new receiver to handle key/mouse/wheel events - */ - public setInputReceiver(receiver: IInputReceiver): void { - this.inputReceiver = receiver; - this.inputReceiver.inputReceiverInit(); - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Applies all the mouse handlers to the DOM, if not already applied - */ - public listen(): void { - if (this.isListening) return; - this.isListening = true; - this.inputProviders.forEach((provider) => provider.listen()); - } - - /** - * Removes all the mouse handlers to the DOM, if not already removed - */ - public deafen(): void { - if (!this.isListening) return; - this.isListening = false; - this.inputProviders.forEach((provider) => provider.deafen()); - } - - /* -------------------------------------------------------------------------- */ - /* LISTENERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Listener for button events from input providers. - * @param button - * @param isPressed - */ - public handleButtonEvent(button: InputButton, value: boolean) { - this.buttons[button]?.handle( - value, - !!this.inputReceiver - ? () => this.inputReceiver!.inputReceiverChange() - : undefined - ); - if (this.inputReceiver?.handleButtonEvent) - this.inputReceiver.handleButtonEvent(button, value); - } - - /** - * Listener for joystick events from input providers. - * @param joystick - */ - public handleJoystickEvent( - joystick: InputJoystick, - angle: number, - magnitude: number, - active: boolean - ) { - this.joysticks[joystick]?.handle( - angle, - magnitude, - active, - !!this.inputReceiver - ? () => this.inputReceiver!.inputReceiverChange() - : undefined - ); - if (this.inputReceiver?.handleJoystickEvent) - this.inputReceiver.handleJoystickEvent( - joystick, - angle, - magnitude, - active - ); - } -} diff --git a/src/components/Space/game/core/KeyBinding.ts b/src/components/Space/game/core/KeyBinding.ts deleted file mode 100644 index f6d9f26..0000000 --- a/src/components/Space/game/core/KeyBinding.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * KeyBinding.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export class KeyBinding { - public eventCodes: string[]; - public isPressed: boolean = false; - public justPressed: boolean = false; - public justReleased: boolean = false; - - constructor(...code: string[]) { - this.eventCodes = code; - } -} diff --git a/src/components/Space/game/core/LoadingManager.ts b/src/components/Space/game/core/LoadingManager.ts deleted file mode 100644 index 713a983..0000000 --- a/src/components/Space/game/core/LoadingManager.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * LoadingManager.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ - -import { GLTF, GLTFLoader } from 'three-stdlib'; -import { World } from '../world/World'; -import { LoadingTrackerEntry } from './LoadingTrackerEntry'; - -export enum LoadingManagerEvent { - START = 'start', - PROGRESS = 'progress', - FINISH = 'finish', -} - -export class LoadingManager { - // statefuls - public firstLoad: boolean = true; - public onStart?: () => void; - public onProgress?: ( - progress: number, - downloaded: number, - total: number - ) => void; - public onFinished?: () => void; - - private world: World; - private gltfLoader: GLTFLoader; - private loadingTracker: LoadingTrackerEntry[] = []; - - /** - * Build a manager for loading new scenes and assets into the world. - * @param world - */ - constructor( - world: World, - callbacks: { - onStart?: () => void; - onProgress?: ( - progress: number, - downloaded: number, - total: number - ) => void; - onFinished?: () => void; - } = {} - ) { - // reference the world to load into - this.world = world; - this.gltfLoader = new GLTFLoader(); - this.world.setTimeScale(0); - - const { onStart, onProgress, onFinished } = callbacks; - this.onStart = onStart; - this.onProgress = onProgress; - this.onFinished = onFinished; - } - - /* -------------------------------------------------------------------------- */ - /* LOADING */ - /* -------------------------------------------------------------------------- */ - - /** - * Downloads a GLTF while tracking its success through a LoadingEntry. - * @param path Where to download the GLTF from. - * @param onProgress A callback to run on load progress - */ - public async loadGLTF(path: string): Promise { - return new Promise((res, rej) => { - this.onStart && this.onStart(); - const trackerEntry = this.addLoadingEntry(path); - this.gltfLoader.load( - path, - (gltf) => { - this.doneLoading(trackerEntry); - res(gltf); - }, - (xhr) => { - if (xhr.lengthComputable) { - trackerEntry.total = xhr.total; // set the total for relative sizes - trackerEntry.progress = xhr.loaded / xhr.total; - const [percentage, downloaded, total] = this.getLoadingPercentage(); - this.onProgress && this.onProgress(percentage, downloaded, total); - } - }, - (error) => { - rej(error); - } - ); - }); - } - - /* -------------------------------------------------------------------------- */ - /* DOWNLOAD STATE */ - /* -------------------------------------------------------------------------- */ - - /** - * Logs a step in the download process - * @param path - */ - public addLoadingEntry(path: string): LoadingTrackerEntry { - const entry = new LoadingTrackerEntry(path); - entry.finished = false; - this.loadingTracker.push(entry); - return entry; - } - - /** - * Sets a loading entry as completed - * @param trackerEntry - */ - public doneLoading(trackerEntry: LoadingTrackerEntry): void { - trackerEntry.finished = true; - trackerEntry.progress = 1; - this.isLoadingDone() && this.onFinished && this.onFinished(); - } - - /** - * Checks to see if all loading entries have finished downloading. - * @returns - */ - public isLoadingDone(): boolean { - for (let i = 0; i < this.loadingTracker.length; i += 1) { - if (!this.loadingTracker[i].finished) return false; - } - return true; - } - - /* -------------------------------------------------------------------------- */ - /* COMMUNICATION */ - /* -------------------------------------------------------------------------- */ - - /** - * Check on the total load status of all loading elements - * @returns A number from 0-1 telling how much has loaded - */ - public getLoadingPercentage(): [number, number, number] { - let done = true; - let total = 0; - let finished = 0; - // iterate over all loaders and calculate total size / progress - for (let i = 0; i < this.loadingTracker.length; i += 1) { - if (this.loadingTracker[i].finished) continue; - total += this.loadingTracker[i].total; - finished += - this.loadingTracker[i].progress * this.loadingTracker[i].total; - if (!this.loadingTracker[i].finished) done = false; - } - if (total === 0) return [0, 0, 0]; - return [finished / total, finished, total]; - } -} diff --git a/src/components/Space/game/core/LoadingTrackerEntry.ts b/src/components/Space/game/core/LoadingTrackerEntry.ts deleted file mode 100644 index c4c8a28..0000000 --- a/src/components/Space/game/core/LoadingTrackerEntry.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * LoadingTrackerEntry.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export class LoadingTrackerEntry { - public path: string; - public total: number = 0; - public progress: number = 0; - public finished: boolean = true; - - /** - * A simple class for wrapping a timestep in the download. - * @param path The path to the desired download item - */ - constructor(path: string) { - this.path = path; - } -} diff --git a/src/components/Space/game/enums/CollisionGroups.ts b/src/components/Space/game/enums/CollisionGroups.ts deleted file mode 100644 index 4f15cd4..0000000 --- a/src/components/Space/game/enums/CollisionGroups.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * CollisionGroups.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export enum CollisionGroups { - Default = 1, - Characters = 2, - TriMeshColliders = 4, -} diff --git a/src/components/Space/game/enums/EntityType.ts b/src/components/Space/game/enums/EntityType.ts deleted file mode 100644 index 3702a85..0000000 --- a/src/components/Space/game/enums/EntityType.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * EntityType.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export enum EntityType { - Nobot, - Decoration, - System, -} diff --git a/src/components/Space/game/enums/UserInputs.ts b/src/components/Space/game/enums/UserInputs.ts deleted file mode 100644 index a8717cc..0000000 --- a/src/components/Space/game/enums/UserInputs.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * UserInputs.ts - * author: evan kirkiles - * created on Thu Dec 08 2022 - * 2022 the nobot space, - */ -export enum InputJoystick { - MAIN = 'main', // right joystick / arcade movement - SECONDARY = 'secondary', // left joystick / camera movement -} - -export enum InputButton { - UP = 'up', // jump button / up movement - DOWN = 'down', // CTRL button / down movement - SPEED = 'speed', // SHIFT button / move faster - VIEWTOGGLE = 'viewtoggle', // C button / change camera controls - USE = 'use', // Use an item / interact -} diff --git a/src/components/Space/game/enums/WorldSpace.ts b/src/components/Space/game/enums/WorldSpace.ts deleted file mode 100644 index 196538f..0000000 --- a/src/components/Space/game/enums/WorldSpace.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Space.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ -export enum WorldSpace { - Local = 'local', - Global = 'global', -} diff --git a/src/components/Space/game/input/ButtonBinding.ts b/src/components/Space/game/input/ButtonBinding.ts deleted file mode 100644 index bab7491..0000000 --- a/src/components/Space/game/input/ButtonBinding.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * ButtonBinding.ts - * author: evan kirkiles - * created on Thu Dec 08 2022 - * 2022 the nobot space, - */ -export class ButtonBinding { - public isPressed: boolean = false; - public justPressed: boolean = false; - public justReleased: boolean = false; - - handle(pressed: boolean, immediateCallback?: () => void) { - if (this.isPressed === pressed) return; - this.isPressed = pressed; - // reset 'just' attributes - this.justPressed = false; - this.justReleased = false; - // set the 'just' attributes - if (pressed) this.justPressed = true; - else this.justReleased = true; - // perform the callback - if (immediateCallback) immediateCallback(); - // reset the 'just' attributes - this.justPressed = false; - this.justReleased = false; - } -} diff --git a/src/components/Space/game/input/JoystickBinding.ts b/src/components/Space/game/input/JoystickBinding.ts deleted file mode 100644 index 5174bc8..0000000 --- a/src/components/Space/game/input/JoystickBinding.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * ButtonBinding.ts - * author: evan kirkiles - * created on Thu Dec 08 2022 - * 2022 the nobot space, - */ -export class JoystickBinding { - public angle: number = 0; - public magnitude: number = 0; - public isActive: boolean = false; - public justActivated: boolean = false; - public justReleased: boolean = false; - - handle( - angle: number, - magnitude: number, - pressed: boolean, - immediateCallback?: () => void - ) { - this.angle = angle; - this.magnitude = magnitude; - this.isActive = pressed; - // reset 'just' attributes - this.justActivated = false; - this.justReleased = false; - // set the 'just' attributes - if (pressed) this.justActivated = true; - else this.justReleased = true; - // perform the callback - if (immediateCallback) immediateCallback(); - // reset the 'just' attributes - this.justActivated = false; - this.justReleased = false; - } -} diff --git a/src/components/Space/game/input/providers/GamepadInputProvider.ts b/src/components/Space/game/input/providers/GamepadInputProvider.ts deleted file mode 100644 index c535bc1..0000000 --- a/src/components/Space/game/input/providers/GamepadInputProvider.ts +++ /dev/null @@ -1,120 +0,0 @@ -/* - * GamepadInputProvider.ts - * author: evan kirkiles - * created on Thu Dec 08 2022 - * 2022 the nobot space, - */ -import { InputManager } from '../../core/InputManager'; -import { InputButton, InputJoystick } from '../../enums/UserInputs'; -import { IInputProvider } from '../../interfaces/IInputProvider'; -import type Gamepads_ from 'gamepads'; - -let Gamepads: typeof Gamepads_; - -export default class GamepadInputProvider implements IInputProvider { - private manager: InputManager; - isListening: boolean = false; - - joystickDeadzone: number = 0.15; - - // map controller joysticks (XY) - bindings_controllers: InputJoystick[] = [ - InputJoystick.MAIN, - InputJoystick.SECONDARY, - ]; - - // map controller buttons - bindings_buttons: (InputButton | null)[] = [ - null, - null, - null, - null, - InputButton.UP, - InputButton.DOWN, - InputButton.USE, - InputButton.VIEWTOGGLE, - ]; - - /** - * On construction, immediately add a listener that will continually check - * if a new gamepad has connected to the game. - * @param manager - */ - constructor(manager: InputManager) { - this.manager = manager; - Gamepads = require('gamepads'); - Gamepads.start(); // begin scanning for gamepads to connect with - Gamepads.addEventListener('connect', (e: any) => { - console.log('Gamepad connected.'); - e.gamepad.joystickDeadzone = this.joystickDeadzone; - // add listeners to gamepad - e.gamepad.addEventListener('buttonvaluechange', (evt: any) => { - console.log('hi'); - this.onButtonChange(evt); - }); - e.gamepad.addEventListener( - 'joystickmove', - (evt: any) => this.onJoystickMove(evt, InputJoystick.MAIN), - [0, 1] // js1 - ); - e.gamepad.addEventListener( - 'joystickmove', - (evt: any) => this.onJoystickMove(evt, InputJoystick.SECONDARY), - [2, 3] // js2 - ); - }); - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Applies all the handlers to the DOM, if not already applied - */ - listen() { - this.isListening = true; - } - - /** - * Removes all the mouse handlers to the DOM, if not already removed - */ - deafen(): void { - this.isListening = false; - } - - /* -------------------------------------------------------------------------- */ - /* LISTENERS */ - /* -------------------------------------------------------------------------- */ - - onButtonChange(e: any): void { - if (!this.isListening || !this.bindings_buttons[e.index]) return; - this.manager.handleButtonEvent(this.bindings_buttons[e.index]!, !!e.value); - } - - /** - * Emit joystick events to input manager - * @param e - * @param joystick - * @returns - */ - onJoystickMove(e: any, joystick: InputJoystick): void { - if (!this.isListening) return; - const invert = joystick === InputJoystick.MAIN ? -1 : 1; - const x = - Math.abs(e.horizontalValue) < this.joystickDeadzone - ? 0 - : e.horizontalValue * invert; - const y = - Math.abs(e.verticalValue) < this.joystickDeadzone - ? 0 - : e.verticalValue * invert; - this.manager.handleJoystickEvent( - joystick, - Math.atan2(y, x), - Math.hypot(x, y), - x !== 0 || y !== 0 - ); - return; - } -} diff --git a/src/components/Space/game/input/providers/KeyboardInputProvider.ts b/src/components/Space/game/input/providers/KeyboardInputProvider.ts deleted file mode 100644 index fcc02db..0000000 --- a/src/components/Space/game/input/providers/KeyboardInputProvider.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * KeyboardInputProvider.ts - * author: evan kirkiles - * created on Thu Dec 08 2022 - * 2022 the nobot space, - */ -import { InputManager } from '../../core/InputManager'; -import { InputJoystick, InputButton } from '../../enums/UserInputs'; -import { IInputProvider } from '../../interfaces/IInputProvider'; -import { ButtonBinding } from '../ButtonBinding'; - -export default class KeyboardInputProvider implements IInputProvider { - private manager: InputManager; - isListening: boolean = false; - - // keybindings to buttons - bindings: { [key: string]: InputButton } = { - Space: InputButton.UP, - ShiftLeft: InputButton.DOWN, - ControlLeft: InputButton.SPEED, - KeyC: InputButton.VIEWTOGGLE, - }; - - // actions for WASD movements - fakejoystick: { [key: string]: ButtonBinding } = { - KeyW: new ButtonBinding(), - KeyA: new ButtonBinding(), - KeyS: new ButtonBinding(), - KeyD: new ButtonBinding(), - }; - - // bound listeners - public boundOnKeyDown: (evt: KeyboardEvent) => void; - public boundOnKeyUp: (evt: KeyboardEvent) => void; - - constructor(manager: InputManager) { - this.manager = manager; - - // - keys - this.boundOnKeyDown = (evt) => this.onKeyDown(evt); - this.boundOnKeyUp = (evt) => this.onKeyUp(evt); - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Applies all the handlers to the DOM, if not already applied - */ - listen() { - if (this.isListening) return; - this.isListening = true; - document.addEventListener('keydown', this.boundOnKeyDown, false); - document.addEventListener('keyup', this.boundOnKeyUp, false); - } - - /** - * Removes all the mouse handlers to the DOM, if not already removed - */ - deafen(): void { - if (!this.isListening) return; - this.isListening = false; - document.removeEventListener('keydown', this.boundOnKeyDown, false); - document.removeEventListener('keyup', this.boundOnKeyUp, false); - } - - /* -------------------------------------------------------------------------- */ - /* LISTENERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Funnels an OnKeyDown event through to the input receiver - * @param event A KeyDown event - */ - public onKeyDown(event: KeyboardEvent): void { - // standard keyboard input (buttons) - if (this.bindings[event.code] !== undefined) - this.manager.handleButtonEvent( - this.bindings[event.code as InputButton], - true - ); - // fake joystick events (WASD) - else if (this.fakejoystick[event.code] !== undefined) { - this.fakejoystick[event.code].isPressed = true; - // recalculate joystick "angle" - const x = - Number(this.fakejoystick.KeyD.isPressed) - - Number(this.fakejoystick.KeyA.isPressed); - const y = - Number(this.fakejoystick.KeyW.isPressed) - - Number(this.fakejoystick.KeyS.isPressed); - this.manager.handleJoystickEvent( - InputJoystick.MAIN, - Math.atan2(y, x), - 1, - true - ); - } - } - - /** - * Funnels an OnKeyUp event through to the input receiver - * @param event A KeyUp event - */ - public onKeyUp(event: KeyboardEvent): void { - if (this.bindings[event.code] !== undefined) - this.manager.handleButtonEvent( - this.bindings[event.code as InputButton], - false - ); - // fake joystick events (WASD) - else if (this.fakejoystick[event.code] !== undefined) { - this.fakejoystick[event.code].isPressed = false; - // recalculate joystick "angle" - const x = - Number(this.fakejoystick.KeyD.isPressed) - - Number(this.fakejoystick.KeyA.isPressed); - const y = - Number(this.fakejoystick.KeyW.isPressed) - - Number(this.fakejoystick.KeyS.isPressed); - // possible that joystick is deactivated here - if (x !== 0 || y !== 0) { - this.manager.handleJoystickEvent( - InputJoystick.MAIN, - Math.atan2(y, x), - 1, - true - ); - } else { - this.manager.handleJoystickEvent(InputJoystick.MAIN, 0, 0, false); - } - } - } -} diff --git a/src/components/Space/game/input/providers/TouchInputProvider.ts b/src/components/Space/game/input/providers/TouchInputProvider.ts deleted file mode 100644 index e8659cc..0000000 --- a/src/components/Space/game/input/providers/TouchInputProvider.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * TouchInputProvider.ts - * author: evan kirkiles - * created on Thu Dec 08 2022 - * 2022 the nobot space, - */ -import { InputManager } from '../../core/InputManager'; -import { InputJoystick, InputButton } from '../../enums/UserInputs'; -import { IInputProvider } from '../../interfaces/IInputProvider'; -import type NippleJs from 'nipplejs'; - -let nipplejs: typeof NippleJs; - -export default class TouchInputProvider implements IInputProvider { - private manager: InputManager; - isListening: boolean = false; - - // dom element in which to place the nipple - public domElement: HTMLElement; - // nipple for main movement - public nippleDomElement: HTMLDivElement; - public nippleManager?: ReturnType; - public nippleState: string = 'end'; - - // is the device a touch screen? if not, we do not show anything - public isTouchScreen: boolean = false; - - constructor(manager: InputManager, domElement: HTMLElement = document.body) { - this.manager = manager; - this.domElement = domElement; - - // check if we're on a touch screen - this.isTouchScreen = - 'ontouchstart' in window || - navigator.maxTouchPoints > 0 || - (navigator as any).msMaxTouchPoints > 0 || - true; - - // only get nipplejs if we touch screen - if (this.isTouchScreen) { - nipplejs = require('nipplejs'); - } - - // create the nipple dom element - this.nippleDomElement = document.createElement('div'); - this.nippleDomElement.style.position = 'absolute'; - this.nippleDomElement.style.bottom = '0px'; - this.nippleDomElement.style.right = '0px'; - this.nippleDomElement.style.width = '75px'; - this.nippleDomElement.style.height = '75px'; - this.nippleDomElement.style.zIndex = '1'; - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Applies all the handlers to the DOM, if not already applied - */ - listen() { - if (this.isListening || !this.isTouchScreen) return; - this.isListening = true; - // add 360ยบ nipple - this.domElement.append(this.nippleDomElement); - this.nippleManager = nipplejs.create({ - zone: this.nippleDomElement, - mode: 'static', - dynamicPage: true, - shape: 'circle', - }); - this.nippleManager.on('end', (evt) => this.onNippleStop(evt)); - this.nippleManager.on('move', (evt, data) => this.onNippleMove(evt, data)); - } - - /** - * Removes all the mouse handlers to the DOM, if not already removed - */ - deafen(): void { - if (!this.isListening || !this.isTouchScreen) return; - this.isListening = false; - this.nippleDomElement.remove(); - if (this.nippleManager) this.nippleManager.destroy(); - } - - /* -------------------------------------------------------------------------- */ - /* LISTENERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Funnels an OnKeyDown event through to the input receiver - * @param event A KeyDown event - */ - public onNippleMove( - evt: NippleJs.EventData, - data: NippleJs.JoystickOutputData - ): void { - this.manager.handleJoystickEvent( - InputJoystick.MAIN, - data.angle.radian ?? 0, - data.distance / 75, - true - ); - } - - /** - * Funnels an OnKeyDown event through to the input receiver - * @param event A KeyDown event - */ - public onNippleStop(evt: NippleJs.EventData): void { - this.manager.handleJoystickEvent(InputJoystick.MAIN, 0, 0, false); - } -} diff --git a/src/components/Space/game/input/providers/gamepads.d.ts b/src/components/Space/game/input/providers/gamepads.d.ts deleted file mode 100644 index 2efddae..0000000 --- a/src/components/Space/game/input/providers/gamepads.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'gamepads' { - import Gamepads from 'gamepads'; - export default Gamepads; -} diff --git a/src/components/Space/game/interfaces/ICollider.ts b/src/components/Space/game/interfaces/ICollider.ts deleted file mode 100644 index dbfe7ab..0000000 --- a/src/components/Space/game/interfaces/ICollider.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * ICollider.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as CANNON from 'cannon-es'; - -export interface ICollider { - body: CANNON.Body; -} diff --git a/src/components/Space/game/interfaces/IControllable.ts b/src/components/Space/game/interfaces/IControllable.ts deleted file mode 100644 index 4a06c3e..0000000 --- a/src/components/Space/game/interfaces/IControllable.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * IControllable.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import { EntityType } from '../enums/EntityType'; -import { Nobot } from '../nobots/Nobot'; -import { IInputReceiver } from './IInputReceiver'; - -export interface IControllable extends IInputReceiver { - entityType: EntityType; - position: THREE.Vector3; - controllingNobot: Nobot; - - triggerAction(actionName: string, value: boolean): void; - resetControls(): void; - allowSleep(value: boolean): void; - onInputChange(): void; - noDirectionPressed(): boolean; -} diff --git a/src/components/Space/game/interfaces/IInputProvider.ts b/src/components/Space/game/interfaces/IInputProvider.ts deleted file mode 100644 index 2e01e70..0000000 --- a/src/components/Space/game/interfaces/IInputProvider.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * IInputProvider.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ - -import { KeyBinding } from '../core/KeyBinding'; - -export interface IInputProvider { - isListening: boolean; - - // for toggling input listening - listen(): void; - deafen(): void; - - // used for updating game pad state - update?(): void; -} diff --git a/src/components/Space/game/interfaces/IInputReceiver.ts b/src/components/Space/game/interfaces/IInputReceiver.ts deleted file mode 100644 index ca18d4a..0000000 --- a/src/components/Space/game/interfaces/IInputReceiver.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * IInputReceiver.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ - -import { KeyBinding } from '../core/KeyBinding'; -import { InputManager } from '../core/InputManager'; -import { InputButton, InputJoystick } from '../enums/UserInputs'; - -export interface IInputReceiver { - // control handlers - // handleButtonEvent(); - - // // event handlers - // handleKeyboardEvent(e: KeyboardEvent, code: string, pressed: boolean): void; - // handleNippleEvent(active: boolean, angle: number): void; - // handleVNippleEvent(active: boolean, distance: number): void; - // handleButtonEvent(): boolean; - inputManager: InputManager; - - // initialization and updating - inputReceiverInit(): void; - inputReceiverUpdate(timeStep: number): void; - inputReceiverChange(): void; - - // handling of input and button events - handleButtonEvent?(button: InputButton, pressed: boolean): void; - handleJoystickEvent?( - joystick: InputJoystick, - angle: number, - magnitude: number, - active: boolean - ): void; -} diff --git a/src/components/Space/game/interfaces/INobotAI.ts b/src/components/Space/game/interfaces/INobotAI.ts deleted file mode 100644 index fe54cdc..0000000 --- a/src/components/Space/game/interfaces/INobotAI.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * INobotAI.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import { Nobot } from '../nobots/Nobot'; - -export interface INobotAI { - nobot: Nobot; - update(timeStep: number): void; -} diff --git a/src/components/Space/game/interfaces/INobotState.ts b/src/components/Space/game/interfaces/INobotState.ts deleted file mode 100644 index de26438..0000000 --- a/src/components/Space/game/interfaces/INobotState.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * INobotState.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export interface INobotState { - canFindInteractions: boolean; - canEnterInteraction: boolean; - canLeaveInteraction: boolean; - - update(timeStep: number): void; - onInputChange(): void; -} diff --git a/src/components/Space/game/interfaces/ISpawnPoint.ts b/src/components/Space/game/interfaces/ISpawnPoint.ts deleted file mode 100644 index 8915825..0000000 --- a/src/components/Space/game/interfaces/ISpawnPoint.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * ISpawnPoint.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import { LoadingManager } from '../core/LoadingManager'; -import { World } from '../world/World'; - -export interface ISpawnPoint { - spawn(world: World): Promise; -} diff --git a/src/components/Space/game/interfaces/IUpdatable.ts b/src/components/Space/game/interfaces/IUpdatable.ts deleted file mode 100644 index be8e3cb..0000000 --- a/src/components/Space/game/interfaces/IUpdatable.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * IUpdatable.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export interface IUpdatable { - updateOrder: number; - update(timestep: number, unscaledTimeStep: number): void; -} diff --git a/src/components/Space/game/interfaces/IWorldEntity.ts b/src/components/Space/game/interfaces/IWorldEntity.ts deleted file mode 100644 index e615229..0000000 --- a/src/components/Space/game/interfaces/IWorldEntity.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * IWorldEntity.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import { EntityType } from '../enums/EntityType'; -import { World } from '../world/World'; -import { IUpdatable } from './IUpdatable'; - -export interface IWorldEntity extends IUpdatable { - entityType: EntityType; - addToWorld(world: World): void; - removeFromWorld(world: World): void; -} diff --git a/src/components/Space/game/nobots/GroundImpactData.ts b/src/components/Space/game/nobots/GroundImpactData.ts deleted file mode 100644 index d58e8c5..0000000 --- a/src/components/Space/game/nobots/GroundImpactData.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * GroundImpactData.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; - -export class GroundImpactData { - public velocity: THREE.Vector3 = new THREE.Vector3(); -} diff --git a/src/components/Space/game/nobots/InteractionEntryInstance.ts b/src/components/Space/game/nobots/InteractionEntryInstance.ts deleted file mode 100644 index b595ef6..0000000 --- a/src/components/Space/game/nobots/InteractionEntryInstance.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * InteractionEntryInstance.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; -import { Nobot } from './Nobot'; - -export class InteractionEntryInstance { - public nobot: Nobot; - public entryPoint?: THREE.Object3D; - - /** - * Create an interaction entry instance, which manages how a character enters - * into an interaction (i.e. sitting down, walking in, etc.). - * @param nobot - */ - constructor(nobot: Nobot) { - this.nobot = nobot; - } - - /** - * Updates the interaction every timestep - * @param timeStep The timestep to use in calculations - */ - public update(timeStep: number): void { - const entryPointWorldPos = new THREE.Vector3(); - this.entryPoint?.getWorldPosition(entryPointWorldPos); - const viewVector = new THREE.Vector3().subVectors( - entryPointWorldPos, - this.nobot.position - ); - this.nobot.setOrientation(viewVector); - } -} diff --git a/src/components/Space/game/nobots/Nobot.ts b/src/components/Space/game/nobots/Nobot.ts deleted file mode 100644 index 9312101..0000000 --- a/src/components/Space/game/nobots/Nobot.ts +++ /dev/null @@ -1,846 +0,0 @@ -/* - * NobotPlayer.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as CANNON from 'cannon-es'; -import _ from 'lodash'; -import { EventData, JoystickOutputData } from 'nipplejs'; -import * as THREE from 'three'; -import { GLTF } from 'three-stdlib'; -import * as Utils from '../core/FunctionLibrary'; -import { InputManager } from '../core/InputManager'; -import { KeyBinding } from '../core/KeyBinding'; -import { CollisionGroups } from '../enums/CollisionGroups'; -import { EntityType } from '../enums/EntityType'; -import { InputButton, InputJoystick } from '../enums/UserInputs'; -import { IControllable } from '../interfaces/IControllable'; -import { IInputReceiver } from '../interfaces/IInputReceiver'; -import { INobotAI } from '../interfaces/INobotAI'; -import { INobotState } from '../interfaces/INobotState'; -import { IWorldEntity } from '../interfaces/IWorldEntity'; -import { CapsuleCollider } from '../physics/colliders/CapsuleCollider'; -import { RelativeSpringSimulator } from '../physics/simulators/RelativeSpringSimulator'; -import { VectorSpringSimulator } from '../physics/simulators/VectorSpringSimulator'; -import { World } from '../world/World'; -import { GroundImpactData } from './GroundImpactData'; -import { InteractionEntryInstance } from './InteractionEntryInstance'; -import { - DropIdle, - Falling, - Idle, - NobotState, - Walk, -} from './states/_stateLibrary'; - -/** - * The NobotPlayer class is the controller interface for the Nobot, which is - * loaded into the scene based on the user's own Nobot model. It takes in - * information about the Nobot mesh and creates a corresponding collider that - * is scaled based on the Nobot's own proportions. - */ -export class Nobot - extends THREE.Object3D - implements IWorldEntity, IInputReceiver -{ - // worldentity + updatable properties - public updateOrder: number = 1; - public entityType: EntityType = EntityType.Nobot; - public inputManager: InputManager; - - /* -------------------------------- GEOMETRY -------------------------------- */ - - // model / geometry - public height: number = 0; - public tiltContainer: THREE.Group; - public modelContainer: THREE.Group; - public materials: THREE.Material[] = []; - public mixer: THREE.AnimationMixer; - public animations!: THREE.AnimationClip[]; - public currAnim?: THREE.AnimationClip; - - /* -------------------------------- MOVEMENT -------------------------------- */ - - // movement state - public moveSpeed: number = 2; - public angularVelocity: number = 0; - public acceleration: THREE.Vector3 = new THREE.Vector3(); - public velocity: THREE.Vector3 = new THREE.Vector3(); - public velocityTarget: THREE.Vector3 = new THREE.Vector3(); - // movement: velocity simulator - public defaultVelocitySimulatorDamping: number = 0.8; - public defaultVelocitySimulatorMass: number = 50; - public velocitySimulator: VectorSpringSimulator; - // movement: rotation simulator - public defaultRotationSimulatorDamping: number = 0.8; - public defaultRotationSimulatorMass: number = 50; - public rotationSimulator: RelativeSpringSimulator; - // rotation: - public orientation: THREE.Vector3 = new THREE.Vector3(0, 0, 1); - public orientationTarget: THREE.Vector3 = new THREE.Vector3(0, 0, 1); - public viewVector: THREE.Vector3; - // collision body - public nobotCapsule: CapsuleCollider; - // arcade velocity refers to top-down local XZ rotation - public arcadeVelocityInfluence: THREE.Vector3 = new THREE.Vector3(); - public arcadeVelocityIsAdditive: boolean = false; - // whether or not physics are enabled - private physicsEnabled: boolean = true; - - /* ------------------------------- RAYCASTING ------------------------------- */ - - // raycasting - public rayResult: CANNON.RaycastResult = new CANNON.RaycastResult(); - public rayHasHit: boolean = false; - public raySafeOffset: number = 0.03; - public raycastLength: number = 0.57; - public raycastBox: THREE.Mesh; - // jump state - public wantsToJump: boolean = false; - public initJumpSpeed: number = -1; - public groundImpactData: GroundImpactData = new GroundImpactData(); - - /* ------------------------------- INTERACTION ------------------------------ */ - - // world references - public world?: World; - public preStepListener?: () => void; - public postStepListener?: () => void; - - // current state - public nobotState!: INobotState; - public behavior?: INobotAI; - - // controlled items - public controlledObject?: IControllable; - public interactionEntryInstance: InteractionEntryInstance | null = null; - - /* -------------------------------------------------------------------------- */ - /* INITIALIZATION */ - /* -------------------------------------------------------------------------- */ - - /** - * Construct the Nobot Player from the GLTF model downloaded from the server. - * @param gltf The custom nobot GLTF generated by the blender pipeline - */ - constructor(gltf: GLTF, inputManager: InputManager) { - super(); - - // save the input manager - this.inputManager = inputManager; - - /* ------------------------------- GEOMETRIES ------------------------------- */ - - // initialize mesh + animation data - this.readNobotData(gltf); - this.readNobotAnimations(gltf); - - // the visuals group is centered for easy nobot tilting - this.tiltContainer = new THREE.Group(); - this.add(this.tiltContainer); - - // model container is used to reliably ground the character, as animation - // can alter the position of the model itself - this.modelContainer = new THREE.Group(); - this.modelContainer.position.y = -0.57; - this.tiltContainer.add(this.modelContainer); - this.modelContainer.add(gltf.scene); - - // set up animation mixer - this.mixer = new THREE.AnimationMixer(gltf.scene); - - // set up view vector - this.viewVector = new THREE.Vector3(); - - /* --------------------------------- PHYSICS -------------------------------- */ - - // set up simulators for velocity and rotation - this.velocitySimulator = new VectorSpringSimulator( - 60, - this.defaultVelocitySimulatorMass, - this.defaultVelocitySimulatorDamping - ); - this.rotationSimulator = new RelativeSpringSimulator( - 60, - this.defaultRotationSimulatorMass, - this.defaultRotationSimulatorDamping - ); - - // player capsule - this.nobotCapsule = new CapsuleCollider({ - mass: 1, - position: new CANNON.Vec3(), - height: 0.5, - radius: 0.25, - segments: 8, - friction: 0.0, - }); - // add collisions with trimesh colliders - this.nobotCapsule.body.shapes.forEach((shape) => { - shape.collisionFilterMask = ~CollisionGroups.TriMeshColliders; // eslint-disable-line no-bitwise - }); - this.nobotCapsule.body.allowSleep = false; - // now move nobot to different collision group for raycasting - this.nobotCapsule.body.collisionFilterGroup = CollisionGroups.Characters; - // disable character rotation - this.nobotCapsule.body.fixedRotation = true; - this.nobotCapsule.body.updateMassProperties(); - - // Ray cast debug - const boxGeo = new THREE.BoxGeometry(0.1, 0.1, 0.1); - const boxMat = new THREE.MeshLambertMaterial({ color: 0xff0000 }); - this.raycastBox = new THREE.Mesh(boxGeo, boxMat); - this.raycastBox.visible = false; - - /* ------------------------------- INTERACTION ------------------------------ */ - - // begin with the idle state - this.setState(new Idle(this)); - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Updates the influence of arcade controls on the nobot - */ - public setArcadeVelocityInfluence( - x: number, - y: number = x, - z: number = x - ): void { - this.arcadeVelocityInfluence.set(x, y, z); - } - - /** - * Updates the target arcade velocity - */ - public setArcadeVelocityTarget( - z: number, - x: number = 0, - y: number = 0 - ): void { - this.velocityTarget.x = x; - this.velocityTarget.y = y; - this.velocityTarget.z = z; - } - - /** - * Sets the direction in which the nobot is looking at - */ - public setViewVector(vector: THREE.Vector3): void { - this.viewVector.copy(vector).normalize(); - } - - /** - * Sets the orientation of the Nobot, usually for beginning an interaction - * @param vector The look vector - * @param instantly Whether or not to change orientation instantly - */ - public setOrientation( - vector: THREE.Vector3, - instantly: boolean = false - ): void { - const lookVector = new THREE.Vector3().copy(vector).setY(0).normalize(); - this.orientationTarget.copy(lookVector); - if (instantly) { - this.orientation.copy(lookVector); - } - } - - /** - * Sets the position immediately of the nobot - */ - public setPosition(x: number, y: number, z: number): void { - this.nobotCapsule.body.previousPosition = new CANNON.Vec3(x, y, z); - this.nobotCapsule.body.position = new CANNON.Vec3(x, y, z); - this.nobotCapsule.body.interpolatedPosition = new CANNON.Vec3(x, y, z); - } - - /** - * Sets the state of the Nobot. - * @param state - */ - public setState(state: INobotState): void { - this.nobotState = state; - this.nobotState.onInputChange(); - } - - /** - * Sets the state of the nobot in a serialized manner (for tree-shaking the - * state dict). - * @param state - */ - public setStateSerialized(state: NobotState): void { - switch (state) { - case NobotState.WALK: - this.setState(new Walk(this)); - break; - case NobotState.DROPIDLE: - this.setState(new DropIdle(this)); - break; - case NobotState.FALLING: - this.setState(new Falling(this)); - break; - case NobotState.IDLE: - this.setState(new Idle(this)); - break; - } - } - - /** - * Begins an animation, returning how long it will take. - * @param animName The name of the animation in the Nobot GLTF - * @param fadeIn How long to take in fading in the animation - */ - public setAnimation(animName: string, fadeIn: number): number { - if (!this.mixer) return 0; - const clip = THREE.AnimationClip.findByName(this.animations, animName); - const action = this.mixer.clipAction(clip); - if (action === null) { - console.error(`Animation ${animName} not found!`); - return 0; - } - this.mixer.stopAllAction(); - if (this.currAnim) { - const currAction = this.mixer.clipAction(this.currAnim); - currAction.loop = THREE.LoopPingPong; - currAction.timeScale = -1; - currAction.play(); - currAction.crossFadeTo(action, fadeIn, false); - action.timeScale = 1; - action.loop = THREE.LoopRepeat; - } else { - action.fadeIn(fadeIn); - } - action.play(); - this.currAnim = clip; - return action.getClip().duration; - } - - /* -------------------------------------------------------------------------- */ - /* WORLD ENTITY */ - /* -------------------------------------------------------------------------- */ - - /** - * Add the nobot into the world at a spawn position. - * @param world The target world instance - */ - public addToWorld(world: World): void { - // check to make sure the nobot is not already in the world - if (_.includes(world.nobots, this)) { - console.warn('Could not add NOBOT to world it already exists in!'); - // if not, then add the nobot to the world - } else { - this.world = world; - world.nobots.push(this); - world.physicsWorld.addBody(this.nobotCapsule.body); - world.graphicsWorld.add(this); - world.graphicsWorld.add(this.raycastBox); - - // add pre- and post- step listeners - this.preStepListener = () => this.physicsPreStep(); - this.postStepListener = () => this.physicsPostStep(); - world.physicsWorld.addEventListener('preStep', this.preStepListener); - world.physicsWorld.addEventListener('postStep', this.postStepListener); - } - } - - /** - * Remove the nobot from the world - * @param world The target world instance - */ - public removeFromWorld(world: World): void { - // check to make sure the nobot is still in the world - if (!_.includes(world.nobots, this)) { - console.warn('Could not remove NOBOT from a world it is not in!'); - // if so, then remove the nobot from the world - } else { - if (world.inputManager.inputReceiver === this) - world.inputManager.inputReceiver = undefined; - this.world = undefined; - - // remove from nobots, world, body - _.pull(world.nobots, this); - world.physicsWorld.removeBody(this.nobotCapsule.body); - world.graphicsWorld.remove(this.raycastBox); - world.graphicsWorld.remove(this); - - // remove pre- and post- step listeners - world.physicsWorld.removeEventListener('preStep', this.preStepListener!); - world.physicsWorld.removeEventListener( - 'postStep', - this.postStepListener! - ); - this.preStepListener = undefined; - this.postStepListener = undefined; - } - } - - /** - * Sets this character as the receiver for input - */ - public takeControl() { - if (this.world) this.world.inputManager.setInputReceiver(this); - } - - /* -------------------------------------------------------------------------- */ - /* UPDATE LOOP */ - /* -------------------------------------------------------------------------- */ - - /** - * Perform a single step on the nobot character - * @param timestep - * @param unscaledTimeStep - */ - public update(timestep: number): void { - // update current external states - this.behavior?.update(timestep); - this.interactionEntryInstance?.update(timestep); - this.nobotState?.update(timestep); - - // update position and animations - if (this.physicsEnabled) this.springMovement(timestep); - if (this.physicsEnabled) this.springRotation(timestep); - if (this.physicsEnabled) this.rotateModel(); - if (this.mixer) this.mixer.update(timestep); - - // sync physics with graphics - if (this.physicsEnabled) { - this.position.set( - this.nobotCapsule.body.interpolatedPosition.x, - this.nobotCapsule.body.interpolatedPosition.y, - this.nobotCapsule.body.interpolatedPosition.z - ); - } else { - const newPos = new THREE.Vector3(); - this.getWorldPosition(newPos); - this.nobotCapsule.body.position.copy(Utils.cannonVector(newPos)); - this.nobotCapsule.body.interpolatedPosition.copy( - Utils.cannonVector(newPos) - ); - } - // update the global transsform of the object - this.updateMatrixWorld(); - } - - /* -------------------------------------------------------------------------- */ - /* PHYSICS */ - /* -------------------------------------------------------------------------- */ - - /** - * Enables or disables physics for the nobot body - * @param value Physics enabled? - */ - public setPhysicsEnabled(value: boolean): void { - this.physicsEnabled = value; - if (value) { - this.world?.physicsWorld.addBody(this.nobotCapsule.body); - } else { - this.world?.physicsWorld.removeBody(this.nobotCapsule.body); - } - } - - /** - * Function called before a physics step in the CANNON world - */ - public physicsPreStep(): void { - // perform the feet raycast - this.feetRaycast(); - // raycast debug - if (this.rayHasHit) { - if (this.raycastBox.visible) { - this.raycastBox.position.x = this.rayResult.hitPointWorld.x; - this.raycastBox.position.y = this.rayResult.hitPointWorld.y; - this.raycastBox.position.z = this.rayResult.hitPointWorld.z; - } - } else if (this.raycastBox.visible) { - const { body } = this.nobotCapsule; - this.raycastBox.position.set( - body.position.x, - body.position.y - this.raycastLength - this.raySafeOffset, - body.position.z - ); - } - } - - /** - * Raycasts below the Nobot to see if it is on the ground - */ - public feetRaycast(): void { - // player raycasting - const { body } = this.nobotCapsule; - const start = body.position.clone(); - const end = new CANNON.Vec3( - body.position.x, - body.position.y - this.raycastLength - this.raySafeOffset, - body.position.z - ); - // raycast options - const rayCastOptions: CANNON.RayOptions = { - collisionFilterMask: CollisionGroups.Default, - skipBackfaces: true, // ignore back faces - }; - // cast the ray - this.rayHasHit = this.world!.physicsWorld.raycastClosest( - start, - end, - rayCastOptions, - this.rayResult - ); - } - - /** - * Function called after a step in the physics world. It updates the character's - * velocity and coordinates the player's orientation. - */ - public physicsPostStep(): void { - /* -------------------------------- velocity -------------------------------- */ - - // get velocities - const { body } = this.nobotCapsule; - const simulatedVelocity = new THREE.Vector3( - body.velocity.x, - body.velocity.y, - body.velocity.z - ); - // take local velocity - let arcadeVelocity = new THREE.Vector3().copy(this.velocity).multiplyScalar( - this.moveSpeed - // * this.inputManager.joysticks.main.magnitude // speed based on distance. - ); - // turn local into global - arcadeVelocity = Utils.applyVectorMatrixXZ( - this.orientation, - arcadeVelocity - ); - let newVelocity = new THREE.Vector3(); - // additive velocity mode - add arcade velocity to current velocity - if (this.arcadeVelocityIsAdditive) { - newVelocity.copy(simulatedVelocity); - const globalVelocityTarget = Utils.applyVectorMatrixXZ( - this.orientation, - this.velocityTarget - ); - const add = new THREE.Vector3() - .copy(arcadeVelocity) - .multiply(this.arcadeVelocityInfluence); - // add the arcade velocity to the current velocity - if ( - Math.abs(simulatedVelocity.x) < - Math.abs(globalVelocityTarget.x * this.moveSpeed) || - Utils.haveDifferentSigns(simulatedVelocity.x, arcadeVelocity.x) - ) - newVelocity.x += add.x; - if ( - Math.abs(simulatedVelocity.y) < - Math.abs(globalVelocityTarget.y * this.moveSpeed) || - Utils.haveDifferentSigns(simulatedVelocity.y, arcadeVelocity.y) - ) - newVelocity.y += add.y; - if ( - Math.abs(simulatedVelocity.z) < - Math.abs(globalVelocityTarget.z * this.moveSpeed) || - Utils.haveDifferentSigns(simulatedVelocity.z, arcadeVelocity.z) - ) - newVelocity.z += add.z; - // non-additive arcade velocity mode - arcade velocity is velocity - } else { - newVelocity = new THREE.Vector3( - THREE.MathUtils.lerp( - simulatedVelocity.x, - arcadeVelocity.x, - this.arcadeVelocityInfluence.x - ), - THREE.MathUtils.lerp( - simulatedVelocity.y, - arcadeVelocity.y, - this.arcadeVelocityInfluence.y - ), - THREE.MathUtils.lerp( - simulatedVelocity.z, - arcadeVelocity.z, - this.arcadeVelocityInfluence.z - ) - ); - } - - /* ----------------------------- grounded checks ---------------------------- */ - - // if we hit the ground, stay on the ground - if (this.rayHasHit) { - // flatten y-velocity - newVelocity.y = 0; - // move on top of moving objects - if (this.rayResult.body!.mass > 0) { - const pointVelocity = new CANNON.Vec3(); - this.rayResult.body!.getVelocityAtWorldPoint( - this.rayResult.hitPointWorld, - pointVelocity - ); - newVelocity.add(Utils.threeVector(pointVelocity)); - } - // measure the normal vector offset from direct "up vector" and transform - // it into a matrix - const up = new THREE.Vector3(0, 1, 0); - const normal = new THREE.Vector3( - this.rayResult.hitNormalWorld.x, - this.rayResult.hitNormalWorld.y, - this.rayResult.hitNormalWorld.z - ); - const q = new THREE.Quaternion().setFromUnitVectors(up, normal); - const m = new THREE.Matrix4().makeRotationFromQuaternion(q); - // rotate the velocity vector - newVelocity.applyMatrix4(m); - // Apply velocity - body.velocity.x = newVelocity.x; - body.velocity.y = newVelocity.y; - body.velocity.z = newVelocity.z; - // Ground character - body.position.y = - this.rayResult.hitPointWorld.y + - this.raycastLength + - newVelocity.y / this.world!.physicsFrameRate; - // otherwise, handle air maneuvering - } else { - // if nobot is in the air - body.velocity.x = newVelocity.x; - body.velocity.y = newVelocity.y; - body.velocity.z = newVelocity.z; - // Save last in-air information - this.groundImpactData.velocity.x = body.velocity.x; - this.groundImpactData.velocity.y = body.velocity.y; - this.groundImpactData.velocity.z = body.velocity.z; - } - - /* ------------------------------- jump checks ------------------------------ */ - - // if nobot wants to jump, add upwards velocity - if (this.wantsToJump) { - // if init jump speed is set - if (this.initJumpSpeed > -1) { - // flatten velocity - body.velocity.y = 0; - // apply momentum in orientation - const speed = Math.max( - this.velocitySimulator.position.length() * this.moveSpeed, - this.initJumpSpeed - ); - body.velocity = Utils.cannonVector( - this.orientation.clone().multiplyScalar(speed) - ); - } else { - // moving objects compensation - const add = new CANNON.Vec3(); - this.rayResult.body?.getVelocityAtWorldPoint( - this.rayResult.hitPointWorld, - add - ); - body.velocity.vsub(add, body.velocity); - } - - // add positive vertical velocity - body.velocity.y += 6; - // Move above ground by 2x safe offset value - body.position.y += this.raySafeOffset * 2; - // Reset flag - this.wantsToJump = false; - } - } - - /* -------------------------------------------------------------------------- */ - /* INITIALIZATION */ - /* -------------------------------------------------------------------------- */ - - /** - * Reads in Nobot mesh data from the GLTF - * @param gltf The server-generated GLTF nobot model - */ - public readNobotData(gltf: GLTF): void { - gltf.scene.traverse((child) => { - child.userData.ignoredByCamera = true; - if (child instanceof THREE.Mesh && child.isMesh) { - Utils.setUpMeshProperties(child); - if (child.material) { - this.materials.push(child.material); - } - } - }); - } - - /** - * Reads in Nobot animations from the GLTF - * @param gltf The server-generated GLTF nobot model - */ - public readNobotAnimations(gltf: GLTF): void { - this.animations = gltf.animations; - } - - /* -------------------------------------------------------------------------- */ - /* HANDLERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Funnels a button event through to its action handler for the nobot. - * @param event The nipple event passed from an InputManager - * @param data The state of the joystick - */ - public handleButtonEvent(button: InputButton, pressed: boolean): void { - if (button === InputButton.VIEWTOGGLE && pressed && this.world) { - this.world.cameraOperator.nobotCaller = this; - this.world.inputManager.setInputReceiver(this.world.cameraOperator); - } - } - - /* -------------------------------------------------------------------------- */ - /* INPUT RECEIVING */ - /* -------------------------------------------------------------------------- */ - - /** - * Initialize receiving of input - */ - public inputReceiverInit(): void { - if (!this.world) return; - // if an object is being controlled, pass off to that object - if (this.controlledObject) { - this.controlledObject.inputReceiverInit(); - return; - } - // otherwise, make the camera follow this (MIGHT NEED TUNING) - // this.world.cameraOperator.setRadius(1.6, true); - this.world.cameraOperator.followMode = true; - this.world.cameraOperator.target = this; - } - - /** - * Updates the input receiver to check for new events - * @param timeStep The timestep to use in calculations - */ - public inputReceiverUpdate(timeStep: number): void { - if (!this.world) return; - if (this.controlledObject !== undefined) { - this.controlledObject.inputReceiverUpdate(timeStep); - } else { - // look in the camera's direction - this.viewVector = new THREE.Vector3().subVectors( - this.position, - this.world.camera.position - ); - const v = new THREE.Vector3(); - this.getWorldPosition(v); - // this.world.cameraController.setTarget(v.x, v.y, v.z); - // this.getWorldPosition(this.world.cameraController.target); - } - } - - /** - * Funnel input receiver changes through to the nobot state. - */ - public inputReceiverChange(): void { - this.nobotState.onInputChange(); - } - - /* -------------------------------------------------------------------------- */ - /* CAMERA */ - /* -------------------------------------------------------------------------- */ - - /** - * The direction of local movement of the character - * @returns - */ - public getLocalMovementDirection(): THREE.Vector3 { - const { angle, isActive } = this.inputManager.joysticks.main; - const dx = Number(isActive) * -Math.cos(angle); - const dz = Number(isActive) * Math.sin(angle); - return new THREE.Vector3(dx, 0, dz).normalize(); - } - - /** - * Get orientation movement vector relative to the camera - */ - public getCameraRelativeMovementVector(): THREE.Vector3 { - const localDirection = this.getLocalMovementDirection(); - const flatViewVector = new THREE.Vector3( - this.viewVector.x, - 0, - this.viewVector.z - ).normalize(); - return Utils.applyVectorMatrixXZ(flatViewVector, localDirection); - } - - /** - * Get orientation movement vector relative to the camera - */ - public setCameraRelativeOrientationTarget(): void { - if (this.interactionEntryInstance) return; - const moveVector = this.getCameraRelativeMovementVector(); - if (moveVector.x === 0 && moveVector.y === 0 && moveVector.z === 0) { - this.setOrientation(this.orientation); - } else { - this.setOrientation(moveVector); - } - } - - /* -------------------------------------------------------------------------- */ - /* MOVEMENTS */ - /* -------------------------------------------------------------------------- */ - - /** - * Jump function for triggering a jump - */ - public jump(initJumpSpeed: number = -1): void { - this.wantsToJump = true; - this.initJumpSpeed = initJumpSpeed; - } - - /** - * Simulates movement based on the camera relative movement vector - * @param timestep - */ - public springMovement(timestep: number): void { - this.velocitySimulator.target.copy(this.velocityTarget); - this.velocitySimulator.simulate(timestep); - this.velocity.copy(this.velocitySimulator.position); - this.acceleration.copy(this.velocitySimulator.velocity); - } - - /** - * Simulates rotation - * @param timestep - */ - public springRotation(timestep: number): void { - // figure out angle between current and target orientation - const angle = Utils.getSignedAngleBetweenVectors( - this.orientation, - this.orientationTarget - ); - - // simulator - this.rotationSimulator.target = angle; - this.rotationSimulator.simulate(timestep); - const rot = this.rotationSimulator.position; - // updating values - this.orientation.applyAxisAngle(new THREE.Vector3(0, 1, 0), rot); - this.angularVelocity = this.rotationSimulator.velocity; - } - - /** - * Rotates the model to be in line with the orientation - */ - public rotateModel(): void { - this.lookAt( - this.position.x + this.orientation.x, - this.position.y + this.orientation.y, - this.position.z + this.orientation.z - ); - this.tiltContainer.rotation.z = - -this.angularVelocity * 1.2 * this.velocity.length(); - this.tiltContainer.position.setY( - Math.cos(Math.abs(this.angularVelocity * 2.3 * this.velocity.length())) / - 2 - - 0.5 - ); - } -} diff --git a/src/components/Space/game/nobots/states/DropIdle.ts b/src/components/Space/game/nobots/states/DropIdle.ts deleted file mode 100644 index 70f7aef..0000000 --- a/src/components/Space/game/nobots/states/DropIdle.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * DropIdle.ts - * author: evan kirkiles - * created on Tue Jun 28 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { Idle } from './Idle'; -import { JumpIdle } from './JumpIdle'; -import { NobotStateBase } from './NobotStateBase'; -import { StartWalkForward } from './StartWalkForward'; -import { Walk } from './Walk'; - -export class DropIdle extends NobotStateBase { - /** - * Drop state, when the Nobot is not moving too fast. - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.nobot.velocitySimulator.damping = 0.5; - this.nobot.velocitySimulator.mass = 7; - this.nobot.setArcadeVelocityTarget(0); - this.playAnimation('drop_idle', 0.1); - if (this.anyDirection()) { - this.nobot.setState(new StartWalkForward(nobot)); - } - } - - public update(timestep: number): void { - super.update(timestep); - this.nobot.setCameraRelativeOrientationTarget(); - if (this.animationEnded(timestep)) { - this.nobot.setState(new Idle(this.nobot)); - } - this.checkFallInAir(); - } - - public onInputChange(): void { - super.onInputChange(); - if (this.nobot.inputManager.buttons.up.justPressed) { - this.nobot.setState(new JumpIdle(this.nobot)); - } - if (this.anyDirection()) { - this.nobot.setState(new Walk(this.nobot)); - } - } -} diff --git a/src/components/Space/game/nobots/states/DropRunning.ts b/src/components/Space/game/nobots/states/DropRunning.ts deleted file mode 100644 index 3911ab6..0000000 --- a/src/components/Space/game/nobots/states/DropRunning.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * DropRunning.ts - * author: evan kirkiles - * created on Tue Jun 28 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { EndWalk } from './EndWalk'; -import { JumpRunning } from './JumpRunning'; -import { NobotStateBase } from './NobotStateBase'; -import { Walk } from './Walk'; - -export class DropRunning extends NobotStateBase { - constructor(nobot: Nobot) { - super(nobot); - this.nobot.setArcadeVelocityTarget(0.8); - this.playAnimation('drop_running', 0.1); - } - public update(timeStep: number): void { - super.update(timeStep); - this.nobot.setCameraRelativeOrientationTarget(); - if (this.animationEnded(timeStep)) { - this.nobot.setState(new Walk(this.nobot)); - } - } - public onInputChange(): void { - super.onInputChange(); - if (!this.anyDirection()) { - this.nobot.setState(new EndWalk(this.nobot)); - } - if (this.nobot.inputManager.buttons.up.justPressed) { - this.nobot.setState(new JumpRunning(this.nobot)); - } - } -} diff --git a/src/components/Space/game/nobots/states/EndWalk.ts b/src/components/Space/game/nobots/states/EndWalk.ts deleted file mode 100644 index 380bee6..0000000 --- a/src/components/Space/game/nobots/states/EndWalk.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * EndWalk.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ -import { Nobot } from '../Nobot'; -import { Idle } from './Idle'; -import { NobotStateBase } from './NobotStateBase'; - -export class EndWalk extends NobotStateBase { - /** - * Stops the nobot from walking. - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.nobot.setArcadeVelocityTarget(0); - this.animationLength = nobot.setAnimation('stop', 0.1); - } - - /** - * Check for animation finish and fall begins - * @param timeStep - */ - public update(timeStep: number): void { - super.update(timeStep); - if (this.animationEnded(timeStep)) { - this.nobot.setState(new Idle(this.nobot)); - } - this.checkFallInAir(); - } -} diff --git a/src/components/Space/game/nobots/states/Falling.ts b/src/components/Space/game/nobots/states/Falling.ts deleted file mode 100644 index 54da09f..0000000 --- a/src/components/Space/game/nobots/states/Falling.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Falling.ts - * author: evan kirkiles - * created on Mon Jun 27 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { NobotStateBase } from './NobotStateBase'; - -export class Falling extends NobotStateBase { - /** - * Add a falling state to the nobot - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.nobot.velocitySimulator.mass = 100; - this.nobot.rotationSimulator.damping = 0.3; - this.nobot.arcadeVelocityIsAdditive = true; - this.nobot.setArcadeVelocityTarget(0.05, 0, 0.05); - this.playAnimation('falling', 0.3); - } - - /** - * Update listeners for changing state on ground hit - * @param timestep - */ - public update(timestep: number): void { - super.update(timestep); - this.nobot.setCameraRelativeOrientationTarget(); - this.nobot.setArcadeVelocityTarget(this.anyDirection() ? 0.8 : 0); - if (this.nobot.rayHasHit) { - this.setAppropriateDropState(); - } - } -} diff --git a/src/components/Space/game/nobots/states/Idle.ts b/src/components/Space/game/nobots/states/Idle.ts deleted file mode 100644 index b5fb9ff..0000000 --- a/src/components/Space/game/nobots/states/Idle.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Idle.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { JumpIdle } from './JumpIdle'; -import { NobotStateBase } from './NobotStateBase'; -import { Walk } from './Walk'; - -export class Idle extends NobotStateBase { - /** - * Add an Idle state to the Nobot, which immediately plays it. - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - // set simulator options - this.nobot.velocitySimulator.damping = 0.6; - this.nobot.velocitySimulator.mass = 10; - this.nobot.setArcadeVelocityTarget(0); - this.playAnimation('idle', 0.1); - } - - /** - * Updates the animation and checks if the nobot should be falling - * @param timeStep The timestep to use in calculations - */ - public update(timeStep: number): void { - super.update(timeStep); - this.checkFallInAir(); - } - - /** - * When an input event happens, listen for new state transitions. - */ - public onInputChange(): void { - super.onInputChange(); - if (this.nobot.inputManager.buttons.up.justPressed) { - this.nobot.setState(new JumpIdle(this.nobot)); - } - if (this.anyDirection()) { - if (this.nobot.velocity.length() > 0.5) { - this.nobot.setState(new Walk(this.nobot)); - } else { - this.nobot.setState(new Walk(this.nobot)); - // this.setAppropriateStartWalkState(); - } - } - } -} diff --git a/src/components/Space/game/nobots/states/JumpIdle.ts b/src/components/Space/game/nobots/states/JumpIdle.ts deleted file mode 100644 index 21a006d..0000000 --- a/src/components/Space/game/nobots/states/JumpIdle.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * JumpIdle.ts - * author: evan kirkiles - * created on Mon Jun 27 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { Falling } from './Falling'; -import { NobotStateBase } from './NobotStateBase'; - -export class JumpIdle extends NobotStateBase { - private alreadyJumped: boolean; - - /** - * Begins a jump from idle nobot - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.nobot.velocitySimulator.mass = 30; - this.nobot.setArcadeVelocityTarget(0); - this.playAnimation('jump', 0.1); - this.alreadyJumped = false; - } - - /** - * Updates the nobot's Y position while in a jump - * @param timestep - */ - public update(timestep: number) { - super.update(timestep); - // move in air - if (this.alreadyJumped) { - this.nobot.setCameraRelativeOrientationTarget(); - this.nobot.setArcadeVelocityTarget(this.anyDirection() ? 0.8 : 0); - } - // physicall jump - if (this.timer > 0.2 && !this.alreadyJumped) { - this.nobot.jump(); - this.alreadyJumped = true; - this.nobot.velocitySimulator.mass = 100; - this.nobot.rotationSimulator.damping = 0.3; - if ( - this.nobot.rayResult.body && - this.nobot.rayResult.body.velocity.length() > 0 - ) { - this.nobot.setArcadeVelocityInfluence(0, 0, 0); - } else { - this.nobot.setArcadeVelocityInfluence(0.3, 0, 0.3); - } - } else if (this.timer > 0.3 && this.nobot.rayHasHit) { - this.setAppropriateDropState(); - } else if (this.animationEnded(timestep)) { - this.nobot.setState(new Falling(this.nobot)); - } - } -} diff --git a/src/components/Space/game/nobots/states/JumpRunning.ts b/src/components/Space/game/nobots/states/JumpRunning.ts deleted file mode 100644 index d85c19b..0000000 --- a/src/components/Space/game/nobots/states/JumpRunning.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * JumpRunning.ts - * author: evan kirkiles - * created on Tue Jun 28 2022 - * 2022 the nobot space, - */ -import { Nobot } from '../Nobot'; -import { Falling } from './Falling'; -import { NobotStateBase } from './NobotStateBase'; - -export class JumpRunning extends NobotStateBase { - private alreadyJumped: boolean; - - /** - * The nobot jump state when running - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.nobot.velocitySimulator.mass = 100; - this.playAnimation('jump', 0.03); - this.alreadyJumped = false; - } - - /** - * Recalculate the jump - * @param timeStep - */ - public update(timeStep: number): void { - super.update(timeStep); - this.nobot.setCameraRelativeOrientationTarget(); - // move in the air - if (this.alreadyJumped) { - this.nobot.setArcadeVelocityTarget(this.anyDirection() ? 0.8 : 0); - } - // physically jump - if (this.timer > 0.13 && !this.alreadyJumped) { - this.nobot.jump(4); - this.alreadyJumped = true; - this.nobot.rotationSimulator.damping = 0.3; - this.nobot.arcadeVelocityIsAdditive = true; - this.nobot.setArcadeVelocityInfluence(0.05, 0, 0.05); - } else if (this.timer > 0.24 && this.nobot.rayHasHit) { - this.setAppropriateDropState(); - } else if (this.animationEnded(timeStep)) { - this.nobot.setState(new Falling(this.nobot)); - } - } -} diff --git a/src/components/Space/game/nobots/states/NobotStateBase.ts b/src/components/Space/game/nobots/states/NobotStateBase.ts deleted file mode 100644 index e074dad..0000000 --- a/src/components/Space/game/nobots/states/NobotStateBase.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* - * NobotStateBase.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import { INobotState } from '../../interfaces/INobotState'; -import { Nobot } from '../Nobot'; - -export enum NobotState { - WALK = 'walk', - DROPIDLE = 'dropIdle', - IDLE = 'idle', - FALLING = 'falling', -} - -export abstract class NobotStateBase implements INobotState { - public nobot: Nobot; - public timer: number; - public animationLength: any; - - public canFindInteractions: boolean; - public canEnterInteraction: boolean; - public canLeaveInteraction: boolean; - - /** - * Builds the foundation of a state for a nobot - * @param nobot The nobot this state applies to - */ - constructor(nobot: Nobot) { - this.nobot = nobot; - - // apply default values to velocity simulator - this.nobot.velocitySimulator.damping = - this.nobot.defaultVelocitySimulatorDamping; - this.nobot.velocitySimulator.mass = this.nobot.defaultVelocitySimulatorMass; - // apply default values to rotation simulator - this.nobot.rotationSimulator.damping = - this.nobot.defaultRotationSimulatorDamping; - this.nobot.rotationSimulator.mass = this.nobot.defaultRotationSimulatorMass; - - // set arcade settings - this.nobot.arcadeVelocityIsAdditive = false; - this.nobot.setArcadeVelocityInfluence(1, 0, 1); - - // interaction settings - this.canFindInteractions = true; - this.canEnterInteraction = false; - this.canLeaveInteraction = true; - - // timer starts at 0 - this.timer = 0; - } - - /* -------------------------------------------------------------------------- */ - /* UPDATE LOOP */ - /* -------------------------------------------------------------------------- */ - - /** - * Increments the timer of the state - * @param timeStep The time step used for calculations - */ - public update(timeStep: number): void { - this.timer += timeStep; - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Gets whether or not a direction is pressed - * @returns - */ - public anyDirection(): boolean { - return this.nobot.inputManager.joysticks.main.isActive; - } - - /* -------------------------------------------------------------------------- */ - /* ANIMATIONS */ - /* -------------------------------------------------------------------------- */ - - /** - * Plays the animation of the state - * @param animName The name of the animation in the Nobot GLTF - * @param fadeIn How long to take in fading in the animation - */ - protected playAnimation(animName: string, fadeIn: number): void { - this.animationLength = this.nobot.setAnimation(animName, fadeIn); - } - - /** - * Returns whether or not the animation will have ended after the frame. - * @param timeStep The timestep this frame will take - */ - public animationEnded(timeStep: number): boolean { - if (!this.nobot.mixer) return true; - if (this.animationLength) { - return this.timer > this.animationLength - timeStep; - } - console.error('Error: Set this.animationLength in state constructor!'); - return false; - } - - /* -------------------------------------------------------------------------- */ - /* LISTENERS */ - /* -------------------------------------------------------------------------- */ - - /** - * Checks whether a user has attempted to begin an interaction - */ - public onInputChange(): void { - // if the nobot can find interactions and they press enter, look for them - if ( - this.canFindInteractions && - this.nobot.inputManager.buttons.use.justPressed - ) { - // this.nobot TODO: Find interaction - // if the nobot can enter interactions and they are in an interaction - } else if ( - this.canEnterInteraction && - this.nobot.interactionEntryInstance !== null - ) { - // if the nobot presses any movement key, get out of the interaction - if (this.nobot.inputManager.joysticks.main.isActive) { - this.nobot.interactionEntryInstance = null; - this.nobot.inputManager.buttons.up.isPressed = false; - } - } - } - - /* -------------------------------------------------------------------------- */ - /* STATE TRANSITIONS */ - /* -------------------------------------------------------------------------- */ - - /** - * Begins the adequate drop state a Nobot enters into after falling. - */ - public setAppropriateDropState(): void { - // if really falling hard, drop into a heavy impact - if (this.nobot.groundImpactData.velocity.y < -6) { - // console.log('hard drop'); - // STATE: Drop Hard - this.nobot.setStateSerialized(NobotState.WALK); - // otherwise check if moving in any direction - } else if (this.anyDirection()) { - // on a minor drop, drop into a run (carrying velocity) - if (this.nobot.groundImpactData.velocity.y < -2) { - // STATE: Drop into a run - this.nobot.setStateSerialized(NobotState.WALK); - // otherwise, continue the action the user was doing before - } else { - // STATE: Walk - this.nobot.setStateSerialized(NobotState.WALK); - } - } else { - // if not moving in any direction, drop into idle - this.nobot.setStateSerialized(NobotState.DROPIDLE); - } - } - - /** - * Sets the appropriate start walk state a Nobot enters into. - */ - public setAppropriateStartWalkState(): void { - // const range = Math.PI; - // const angle = Utils.getSignedAngleBetweenVectors( - // this.nobot.orientation, - // this.nobot.getCameraRelativeMovementVector() - // ); - this.nobot.setStateSerialized(NobotState.WALK); - // if (angle > range * 0.8) { - // this.nobot.setState(new StartWalkBackLeft(this.nobot)); - // } else if (angle < -range * 0.8) { - // this.nobot.setState(new StartWalkBackRight(this.nobot)); - // } else if (angle > range * 0.3) { - // this.nobot.setState(new StartWalkLeft(this.nobot)); - // } else if (angle < range * -0.3) { - // this.nobot.setState(new StartWalkRight(this.nobot)); - // } else { - // this.nobot.setState(new StartWalkForward(this.nobot)); - // } - } - - /** - * Runs the check if the Nobot should be falling, and, if so, begins the fall. - */ - public checkFallInAir(): void { - if (!this.nobot.rayHasHit) { - this.nobot.setStateSerialized(NobotState.FALLING); - } - } -} diff --git a/src/components/Space/game/nobots/states/StartWalkBackLeft.ts b/src/components/Space/game/nobots/states/StartWalkBackLeft.ts deleted file mode 100644 index 1a73986..0000000 --- a/src/components/Space/game/nobots/states/StartWalkBackLeft.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * StartWalkBackLeft.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { StartWalkBase } from './StartWalkBase'; - -export class StartWalkBackLeft extends StartWalkBase { - constructor(nobot: Nobot) { - super(nobot); - this.animationLength = nobot.setAnimation('start_back_left', 0.1); - } -} diff --git a/src/components/Space/game/nobots/states/StartWalkBackRight.ts b/src/components/Space/game/nobots/states/StartWalkBackRight.ts deleted file mode 100644 index a93849c..0000000 --- a/src/components/Space/game/nobots/states/StartWalkBackRight.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * StartWalkBackRight.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { StartWalkBase } from './StartWalkBase'; - -export class StartWalkBackRight extends StartWalkBase { - constructor(nobot: Nobot) { - super(nobot); - this.animationLength = nobot.setAnimation('start_back_right', 0.1); - } -} diff --git a/src/components/Space/game/nobots/states/StartWalkBase.ts b/src/components/Space/game/nobots/states/StartWalkBase.ts deleted file mode 100644 index 32e2516..0000000 --- a/src/components/Space/game/nobots/states/StartWalkBase.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * StartWalkBase.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ -import { Nobot } from '../Nobot'; -import { NobotStateBase } from './NobotStateBase'; -import { Walk } from './Walk'; - -export class StartWalkBase extends NobotStateBase { - /** - * Inherited constructor for a nobot state for beginning to walk. - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.canEnterInteraction = true; - this.nobot.rotationSimulator.mass = 20; - this.nobot.rotationSimulator.damping = 0.7; - this.nobot.setArcadeVelocityTarget(0.8); - } - - /* -------------------------------------------------------------------------- */ - /* UPDATE LOOP */ - /* -------------------------------------------------------------------------- */ - - /** - * Runs the start walk animation until it finishes, and then begins the real - * walk animation. - * @param timestep - */ - public update(timestep: number): void { - super.update(timestep); - if (this.animationEnded(timestep)) { - this.nobot.setState(new Walk(this.nobot)); - } - this.nobot.setCameraRelativeOrientationTarget(); - this.checkFallInAir(); - } - - /** - * When the input to the nobot from user changes - */ - public onInputChange(): void { - super.onInputChange(); - } -} diff --git a/src/components/Space/game/nobots/states/StartWalkForward.ts b/src/components/Space/game/nobots/states/StartWalkForward.ts deleted file mode 100644 index b23765d..0000000 --- a/src/components/Space/game/nobots/states/StartWalkForward.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * StartWalkForward.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { StartWalkBase } from './StartWalkBase'; - -export class StartWalkForward extends StartWalkBase { - constructor(nobot: Nobot) { - super(nobot); - this.animationLength = nobot.setAnimation('start_forward', 0.1); - } -} diff --git a/src/components/Space/game/nobots/states/StartWalkLeft.ts b/src/components/Space/game/nobots/states/StartWalkLeft.ts deleted file mode 100644 index c216493..0000000 --- a/src/components/Space/game/nobots/states/StartWalkLeft.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * StartWalkLeft.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { StartWalkBase } from './StartWalkBase'; - -export class StartWalkLeft extends StartWalkBase { - constructor(nobot: Nobot) { - super(nobot); - this.animationLength = nobot.setAnimation('start_left', 0.1); - } -} diff --git a/src/components/Space/game/nobots/states/StartWalkRight.ts b/src/components/Space/game/nobots/states/StartWalkRight.ts deleted file mode 100644 index ff7bb82..0000000 --- a/src/components/Space/game/nobots/states/StartWalkRight.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * StartWalkRight.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { StartWalkBase } from './StartWalkBase'; - -export class StartWalkRight extends StartWalkBase { - constructor(nobot: Nobot) { - super(nobot); - this.animationLength = nobot.setAnimation('start_right', 0.1); - } -} diff --git a/src/components/Space/game/nobots/states/Walk.ts b/src/components/Space/game/nobots/states/Walk.ts deleted file mode 100644 index 2a022d9..0000000 --- a/src/components/Space/game/nobots/states/Walk.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Walk.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ - -import { Nobot } from '../Nobot'; -import { EndWalk } from './EndWalk'; -import { NobotStateBase } from './NobotStateBase'; -// eslint-disable-next-line -import { Idle, JumpRunning } from './_stateLibrary'; - -export class Walk extends NobotStateBase { - /** - * Represents the walking animation for the nobot. - * @param nobot - */ - constructor(nobot: Nobot) { - super(nobot); - this.canEnterInteraction = true; - this.nobot.setArcadeVelocityTarget(0.8); - this.playAnimation('walk', 0.2); - } - - /* -------------------------------------------------------------------------- */ - /* UPDATE LOOP */ - /* -------------------------------------------------------------------------- */ - - /** - * Updates the camera and checks if falling - * @param timeStep - */ - public update(timeStep: number): void { - super.update(timeStep); - this.nobot.setCameraRelativeOrientationTarget(); - this.checkFallInAir(); - } - - /** - * When a button input changes - */ - public onInputChange(): void { - super.onInputChange(); - if (!this.anyDirection()) this.nobot.setState(new EndWalk(this.nobot)); - if (this.nobot.inputManager.buttons.up.justPressed) - this.nobot.setState(new JumpRunning(this.nobot)); - if (!this.anyDirection()) { - if (this.nobot.velocity.length() > 1) { - // this.nobot.setState(new EndWalk(this.nobot)); - this.nobot.setState(new Idle(this.nobot)); - } else { - this.nobot.setState(new Idle(this.nobot)); - } - } - } -} diff --git a/src/components/Space/game/nobots/states/_stateLibrary.ts b/src/components/Space/game/nobots/states/_stateLibrary.ts deleted file mode 100644 index 272a358..0000000 --- a/src/components/Space/game/nobots/states/_stateLibrary.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from './DropIdle'; -export * from './DropRunning'; -export * from './Falling'; -export * from './Idle'; -export * from './JumpIdle'; -export * from './JumpRunning'; -export * from './NobotStateBase'; -export * from './StartWalkBackLeft'; -export * from './StartWalkBackRight'; -export * from './StartWalkForward'; -export * from './StartWalkLeft'; -export * from './StartWalkRight'; -export * from './Walk'; diff --git a/src/components/Space/game/physics/colliders/BoxCollider.ts b/src/components/Space/game/physics/colliders/BoxCollider.ts deleted file mode 100644 index 19022c4..0000000 --- a/src/components/Space/game/physics/colliders/BoxCollider.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * BoxCollider.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as CANNON from 'cannon-es'; -import { ICollider } from '../../interfaces/ICollider'; - -// Cannon options for mesh + body -interface BoxColliderOptions { - mass: number; - position: CANNON.Vec3; - size: CANNON.Vec3; - friction: number; -} - -export class BoxCollider implements ICollider { - public options: BoxColliderOptions; - public body: CANNON.Body; - - /** - * Construct a box collider with the given options - * @param options - */ - constructor(opts: Partial) { - // combine defaults and options - this.options = { - mass: 0, - position: new CANNON.Vec3(), - size: new CANNON.Vec3(0.3, 0.3, 0.3), - friction: 0.3, - ...opts, - }; - - // create cannon options - this.options.position = new CANNON.Vec3( - this.options.position.x, - this.options.position.y, - this.options.position.z - ); - this.options.size = new CANNON.Vec3( - this.options.size.x, - this.options.size.y, - this.options.size.z - ); - - // apply friction to material - const mat = new CANNON.Material('boxMat'); - mat.friction = this.options.friction; - - // build shape and body - const shape = new CANNON.Box(this.options.size); - const physBox = new CANNON.Body({ - mass: this.options.mass, - position: this.options.position, - shape, - }); - physBox.material = mat; - - // now save the physics body - this.body = physBox; - } -} diff --git a/src/components/Space/game/physics/colliders/CapsuleCollider.ts b/src/components/Space/game/physics/colliders/CapsuleCollider.ts deleted file mode 100644 index 88e532e..0000000 --- a/src/components/Space/game/physics/colliders/CapsuleCollider.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * CapsuleCollider.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as CANNON from 'cannon-es'; -import { ICollider } from '../../interfaces/ICollider'; - -interface CapsuleColliderOptions { - mass: number; - position: CANNON.Vec3; - height: number; - radius: number; - segments: number; - friction: number; -} - -export class CapsuleCollider implements ICollider { - public options: CapsuleColliderOptions; - public body: CANNON.Body; - - /** - * Construct a capsule colliider (used for player collision) from optioons - * @param opts Options for the capsule collider - */ - constructor(options: Partial) { - // build options with defaults and partials - this.options = { - mass: 0, - position: new CANNON.Vec3(), - height: 0.5, - radius: 0.3, - segments: 8, - friction: 0.3, - ...options, - }; - - // build physics material - const mat = new CANNON.Material('capsuleMat'); - mat.friction = this.options.friction; - - // build physics body - const capsuleBody = new CANNON.Body({ - mass: this.options.mass, - position: this.options.position, - }); - capsuleBody.material = mat; - - // compound shape - const sphereShape = new CANNON.Sphere(this.options.radius); - capsuleBody.addShape(sphereShape, new CANNON.Vec3(0, 0, 0)); - capsuleBody.addShape( - sphereShape, - new CANNON.Vec3(0, this.options.height / 2, 0) - ); - capsuleBody.addShape( - sphereShape, - new CANNON.Vec3(0, -this.options.height / 2, 0) - ); - - // set body - this.body = capsuleBody; - } -} diff --git a/src/components/Space/game/physics/colliders/TrimeshCollider.ts b/src/components/Space/game/physics/colliders/TrimeshCollider.ts deleted file mode 100644 index 5136f92..0000000 --- a/src/components/Space/game/physics/colliders/TrimeshCollider.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * TriMeshCollider.ts - * author: evan kirkiles - * created on Sun Jun 26 2022 - * 2022 the nobot space, - */ -import * as CANNON from 'cannon-es'; -import * as THREE from 'three'; -import { ShapeType, threeToCannon } from 'three-to-cannon'; -import { cannonQuat, cannonVector } from '../../core/FunctionLibrary'; -import { ICollider } from '../../interfaces/ICollider'; - -interface TrimeshColliderOptions { - mass: number; - position: CANNON.Vec3; - rotation: CANNON.Quaternion; - friction: number; -} - -export class TrimeshCollider implements ICollider { - public mesh: THREE.Object3D; - public options: TrimeshColliderOptions; - public body: CANNON.Body; - - constructor(mesh: THREE.Object3D, options: Partial) { - this.mesh = mesh.clone(); - this.options = { - mass: 0, - position: cannonVector(mesh.position), - rotation: cannonQuat(mesh.quaternion), - friction: 0.3, - ...options, - }; - const mat = new CANNON.Material('triMat'); - mat.friction = this.options.friction; - const shape = threeToCannon(this.mesh, { type: ShapeType.MESH }); - const physBox = new CANNON.Body({ - mass: options.mass, - position: options.position, - quaternion: options.rotation, - shape: shape?.shape, - }); - physBox.material = mat; - this.body = physBox; - } -} diff --git a/src/components/Space/game/physics/simulators/RelativeSpringSimulator.ts b/src/components/Space/game/physics/simulators/RelativeSpringSimulator.ts deleted file mode 100644 index b49d15a..0000000 --- a/src/components/Space/game/physics/simulators/RelativeSpringSimulator.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * RelativeSpringSimulator.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; -import * as Utils from '../../core/FunctionLibrary'; -import { SimulationFrame } from './SimulationFrame'; -import { SimulatorBase } from './SimulatorBase'; - -export class RelativeSpringSimulator extends SimulatorBase< - SimulationFrame -> { - // simulator state - public position: number; - public velocity: number; - public target: number; - public lastLerp: number; - - /** - * Builds a spring simulator that works on vectors - * @param fps The frames per second of the simulator - * @param mass The mass of the object being simulated - * @param damping How much to dampen calculations - */ - constructor( - fps: number, - mass: number, - damping: number, - startPosition: number = 0, - startVelocity: number = 0 - ) { - // construct base - super(fps, mass, damping); - - // simulated values - this.position = startPosition; - this.velocity = startVelocity; - this.target = 0; - this.lastLerp = 0; - - // initialize cache by pushing two frames - for (let i = 0; i < 2; i += 1) { - this.cache.push( - new SimulationFrame(startPosition, startVelocity) - ); - } - } - - /* -------------------------------------------------------------------------- */ - /* OVERRIDES */ - /* -------------------------------------------------------------------------- */ - - /** - * Advances the simulation by a given time step - * @param timeStep The time step for the calculation - */ - public simulate(timeStep: number): void { - // generate new frames - this.generateFrames(timeStep); - // lerp from 0 to next frame - const lerp = THREE.MathUtils.lerp( - 0, - this.cache[1].position, - this.offset / this.frameTime - ); - // subtract last lerp from current to make output relative - this.position = lerp - this.lastLerp; - this.lastLerp = lerp; - // velocity functions as normal - this.velocity = THREE.MathUtils.lerp( - this.cache[0].velocity, - this.cache[1].velocity, - this.offset / this.frameTime - ); - } - - /** - * Gets another simulation frame - * @param isLastFrame - */ - public getFrame(isLastFrame: boolean): SimulationFrame { - // deep clone data from previous frame - const newFrame = { ...this.lastFrame() }; - if (isLastFrame) { - // reset position - newFrame.position = 0; - // transition to next frame - this.lastLerp -= this.lastFrame().position; - } - // return new spring - return Utils.spring( - newFrame.position, - this.target, - newFrame.velocity, - this.mass, - this.damping - ); - } -} diff --git a/src/components/Space/game/physics/simulators/SimulationFrame.ts b/src/components/Space/game/physics/simulators/SimulationFrame.ts deleted file mode 100644 index a5b573f..0000000 --- a/src/components/Space/game/physics/simulators/SimulationFrame.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * SimulationFrame.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ - -export class SimulationFrame { - public position: T; - public velocity: T; - - /** - * Holds a single frame generated by a simulator - * @param position The position calculated by the simulator - * @param velocity The velocity calculated by the simulator - */ - constructor(position: T, velocity: T) { - this.position = position; - this.velocity = velocity; - } -} diff --git a/src/components/Space/game/physics/simulators/SimulatorBase.ts b/src/components/Space/game/physics/simulators/SimulatorBase.ts deleted file mode 100644 index 31bed18..0000000 --- a/src/components/Space/game/physics/simulators/SimulatorBase.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SimulatorBase.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -export abstract class SimulatorBase { - public mass: number; - public damping: number; - public frameTime: number; - public offset: number; - public cache: T[] = []; - - /** - * A base class for all simulators to inherit frome - * @param fps The frames per second this simulator will run at - * @param mass The mass of the simulated object - * @param damping A damping factor for the simulation effects - */ - constructor(fps: number, mass: number, damping: number) { - this.mass = mass; - this.damping = damping; - this.frameTime = 1 / fps; - this.offset = 0; - } - - /* -------------------------------------------------------------------------- */ - /* STATEFULS */ - /* -------------------------------------------------------------------------- */ - - /** - * Reset the FPS of the simulator to a new value - * @param fps The frames per second of the simulator - */ - public setFPS(fps: number): void { - this.frameTime = 1 / fps; - } - - /** - * Returns the last frame from the cache. - */ - public lastFrame(): T { - return this.cache[this.cache.length - 1]; - } - - /* -------------------------------------------------------------------------- */ - /* FUNCTION */ - /* -------------------------------------------------------------------------- */ - - /** - * Generates frames between last simulation call and the current one - * @param timeStep The timestep of the calculation - */ - public generateFrames(timeStep: number): void { - // update the cache, find out how many frames need to be generated - const totalTimeStep = this.offset + timeStep; - const framesToGenerate = Math.floor(totalTimeStep / this.frameTime); - this.offset = totalTimeStep % this.frameTime; - // generate simulation frames - if (framesToGenerate > 0) { - for (let i = 0; i < framesToGenerate; i += 1) { - this.cache.push(this.getFrame(i + 1 === framesToGenerate)); - } - // only cache the last two frames - this.cache = this.cache.slice(-2); - } - } - - /* -------------------------------------------------------------------------- */ - /* OVERRIDES */ - /* -------------------------------------------------------------------------- */ - - /** - * Overridable function for getting a frame of the simulation - * @param isLastFrame Whether or not this frame is the last one - */ - public abstract getFrame(isLastFrame: boolean): T; - - /** - * A function to actually run the simulator for generating a frame - * @param timeStep The timestep of the calculation - */ - public abstract simulate(timeStep: number): void; -} diff --git a/src/components/Space/game/physics/simulators/SpringSimulator.ts b/src/components/Space/game/physics/simulators/SpringSimulator.ts deleted file mode 100644 index 3607662..0000000 --- a/src/components/Space/game/physics/simulators/SpringSimulator.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SpringSimulator.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; -import * as Utils from '../../core/FunctionLibrary'; -import { SimulationFrame } from './SimulationFrame'; -import { SimulatorBase } from './SimulatorBase'; - -export class SpringSimulator extends SimulatorBase> { - // simulator state - public position: number; - public velocity: number; - public target: number; - - /** - * Builds a spring simulator - * @param fps - * @param mass - * @param damping - */ - constructor( - fps: number, - mass: number, - damping: number, - startPosition: number = 0, - startVelocity: number = 0 - ) { - // construct base - super(fps, mass, damping); - - // simulated values - this.position = startPosition; - this.velocity = startVelocity; - this.target = 0; - - // initialize cache by pushing two frames - for (let i = 0; i < 2; i++) { - this.cache.push( - new SimulationFrame(startPosition, startVelocity) - ); - } - } - - /* -------------------------------------------------------------------------- */ - /* OVERRIDES */ - /* -------------------------------------------------------------------------- */ - - /** - * Advances the simulation by a given time step - * @param timeStep The time step for the calculation - */ - public simulate(timeStep: number): void { - // generate new frames - this.generateFrames(timeStep); - // return values interpolated between cached frames - this.position = THREE.MathUtils.lerp( - this.cache[0].position, - this.cache[1].position, - this.offset / this.frameTime - ); - this.velocity = THREE.MathUtils.lerp( - this.cache[0].velocity, - this.cache[1].velocity, - this.offset / this.frameTime - ); - } - - /** - * Gets another simulation frame - * @param isLastFrame - */ - public getFrame(isLastFrame: boolean): SimulationFrame { - return Utils.spring( - this.lastFrame().position, - this.target, - this.lastFrame().velocity, - this.mass, - this.damping - ); - } -} diff --git a/src/components/Space/game/physics/simulators/VectorSpringSimulator.ts b/src/components/Space/game/physics/simulators/VectorSpringSimulator.ts deleted file mode 100644 index bed9a8c..0000000 --- a/src/components/Space/game/physics/simulators/VectorSpringSimulator.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * VectorSpringSimulator.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; -import * as Utils from '../../core/FunctionLibrary'; -import { SimulationFrame } from './SimulationFrame'; -import { SimulatorBase } from './SimulatorBase'; - -export class VectorSpringSimulator extends SimulatorBase< - SimulationFrame -> { - // simulator state - public position: THREE.Vector3; - public velocity: THREE.Vector3; - public target: THREE.Vector3; - - /** - * Builds a spring simulator that works on vectors - * @param fps The frames per second of the simulator - * @param mass The mass of the object being simulated - * @param damping How much to dampen calculations - */ - constructor(fps: number, mass: number, damping: number) { - // construct base - super(fps, mass, damping); - - // simulated values - this.position = new THREE.Vector3(); - this.velocity = new THREE.Vector3(); - this.target = new THREE.Vector3(); - - // initialize cache by pushing two frames - for (let i = 0; i < 2; i += 1) { - this.cache.push( - new SimulationFrame( - new THREE.Vector3(), - new THREE.Vector3() - ) - ); - } - } - - /* -------------------------------------------------------------------------- */ - /* OVERRIDES */ - /* -------------------------------------------------------------------------- */ - - /** - * Advances the simulation by a given time step - * @param timeStep The time step for the calculation - */ - public simulate(timeStep: number): void { - // generate new frames - this.generateFrames(timeStep); - // return values interpolated between cached frames - this.position.lerpVectors( - this.cache[0].position, - this.cache[1].position, - this.offset / this.frameTime - ); - this.velocity.lerpVectors( - this.cache[0].velocity, - this.cache[1].velocity, - this.offset / this.frameTime - ); - } - - /** - * Gets another simulation frame - * @param isLastFrame - */ - public getFrame(isLastFrame: boolean): SimulationFrame { - // deep clone data from previous frame - const newSpring = new SimulationFrame( - this.lastFrame().position.clone(), - this.lastFrame().velocity.clone() - ); - // calculate new spring - Utils.springVector( - newSpring.position, - this.target, - newSpring.velocity, - this.mass, - this.damping - ); - // return new spring - return newSpring; - } -} diff --git a/src/components/Space/game/world/NobotSpawnPoint.ts b/src/components/Space/game/world/NobotSpawnPoint.ts deleted file mode 100644 index 0c8081d..0000000 --- a/src/components/Space/game/world/NobotSpawnPoint.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * NobotSpawnPoint.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; -import * as Utils from '../core/FunctionLibrary'; -import { LoadingManager } from '../core/LoadingManager'; -import { ISpawnPoint } from '../interfaces/ISpawnPoint'; -import { Nobot } from '../nobots/Nobot'; -import { World } from './World'; - -// const PLAYER_MODEL = '/assets/characters/nobot-anim.glb'; -const PLAYER_MODEL = '/assets/characters/personspace.glb'; -// const PLAYER_MODEL = '/models/nobot.glb'; -// const PLAYER_MODEL = '/models/boxman.glb'; - -export class NobotSpawnPoint implements ISpawnPoint { - private object: THREE.Object3D; - - /** - * Instantiate the spawn point from a GLTF scene / scenario - * @param object The object whose userData has nobot.spawn - */ - constructor(object: THREE.Object3D) { - this.object = object; - } - - /** - * Spawns the nobot player at this point in the scene - * @param loadingManager The loading manager for checking download state - * @param world The world in which the spawn point exists - */ - public async spawn(world: World) { - const playerGLTF = await world.loadingManager.loadGLTF(PLAYER_MODEL); - const nobot = new Nobot(playerGLTF, world.inputManager); - const worldPos = new THREE.Vector3(); - this.object.getWorldPosition(worldPos); - nobot.setPosition(worldPos.x, worldPos.y, worldPos.z); - const forward = Utils.getForward(this.object); - nobot.setOrientation(forward, true); - world.add(nobot); - nobot.takeControl(); - } -} diff --git a/src/components/Space/game/world/Scenario.ts b/src/components/Space/game/world/Scenario.ts deleted file mode 100644 index 50a66fd..0000000 --- a/src/components/Space/game/world/Scenario.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Scenario.ts - * author: evan kirkiles - * created on Sat Jun 25 2022 - * 2022 the nobot space, - */ -import * as THREE from 'three'; -import { LoadingManager } from '../core/LoadingManager'; -import { ISpawnPoint } from '../interfaces/ISpawnPoint'; -import { NobotSpawnPoint } from './NobotSpawnPoint'; -import { World } from './World'; - -export class Scenario { - // metadata - public id: string; - public spawnAlways: boolean = false; - public default: boolean = false; - public name?: string; - public descriptionTitle?: string; - public descriptionContent?: string; - - // world reference - public world: World; - - // private scenario information - private rootNode: THREE.Object3D; - private spawnPoints: ISpawnPoint[] = []; - private invisible: boolean = false; - private initialCameraAngle?: number; - - // build a scenario into the world - constructor(root: THREE.Object3D, world: World) { - this.rootNode = root; - this.world = world; - this.id = root.name; - - // parse the scenario metadata - if (Object.prototype.hasOwnProperty.call(root.userData, 'name')) - this.name = root.userData.name; - if (Object.prototype.hasOwnProperty.call(root.userData, 'default')) - this.default = root.userData.default; - if (Object.prototype.hasOwnProperty.call(root.userData, 'spawn_always')) - this.spawnAlways = root.userData.spawn_always; - if (Object.prototype.hasOwnProperty.call(root.userData, 'invisible')) - this.invisible = root.userData.invisible; - if (Object.prototype.hasOwnProperty.call(root.userData, 'desc_title')) - this.descriptionTitle = root.userData.desc_title; - if (Object.prototype.hasOwnProperty.call(root.userData, 'desc_content')) - this.descriptionContent = root.userData.desc_content; - if (Object.prototype.hasOwnProperty.call(root.userData, 'camera_angle')) - this.initialCameraAngle = root.userData.camera_angle; - - // find scenario spawns and entities - root.traverse((child) => { - if ( - Object.prototype.hasOwnProperty.call(child, 'userData') && - Object.prototype.hasOwnProperty.call(child.userData, 'data') - ) { - if (child.userData.data === 'spawn') { - if (child.userData.type === 'player') { - const sp = new NobotSpawnPoint(child); - this.spawnPoints.push(sp); - } - } - } - }); - } - - /** - * Launches the scenario. - */ - public async launch(world: World) { - await Promise.all(this.spawnPoints.map((sp) => sp.spawn(world))); - if (!this.spawnAlways) { - const theta = this.initialCameraAngle || 0; - world.cameraOperator.distance = 1.5; - world.cameraOperator.azimuthAngle = (Math.PI * theta) / 180; - world.cameraOperator.polarAngle = (Math.PI * 60) / 180; - } - } -} diff --git a/src/components/Space/game/world/World.ts b/src/components/Space/game/world/World.ts deleted file mode 100644 index 2ae143a..0000000 --- a/src/components/Space/game/world/World.ts +++ /dev/null @@ -1,434 +0,0 @@ -/* - * World.ts - * author: evan kirkiles - * created on Fri Jun 24 2022 - * 2022 the nobot space, - * INSPIRATION: https://github.com/swift502/Sketchbook - * so so so so so much credit and admiration to the guys who built Sketchbook. - * incredible work. this would not have been possible without their contributions. - */ -import * as CANNON from 'cannon-es'; -import CannonDebugger from 'cannon-es-debugger'; -import _ from 'lodash'; -import * as THREE from 'three'; -import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'; -import { GLTF } from 'three-stdlib'; -import { CameraOperator } from '../core/CameraOperator'; -import * as Utils from '../core/FunctionLibrary'; -import { InputManager } from '../core/InputManager'; -import { LoadingManager } from '../core/LoadingManager'; -import { CollisionGroups } from '../enums/CollisionGroups'; -import { IUpdatable } from '../interfaces/IUpdatable'; -import { IWorldEntity } from '../interfaces/IWorldEntity'; -import { Nobot } from '../nobots/Nobot'; -import { BoxCollider } from '../physics/colliders/BoxCollider'; -import { TrimeshCollider } from '../physics/colliders/TrimeshCollider'; -import { Scenario } from './Scenario'; -// Add the extension functions -THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; -THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; -export class World { - // game properties - public renderer: THREE.WebGLRenderer; - public camera: THREE.PerspectiveCamera; - public cameraOperator: CameraOperator; - - // world assets - public graphicsWorld: THREE.Scene; - public raycastScene: THREE.Mesh[] = []; - public physicsWorld: CANNON.World; - - // render loop - public clock: THREE.Clock; - public renderDelta: number; - public logicDelta: number; - public sinceLastFrame: number; - public requestDelta!: number; - public justRendered: boolean; - public timeScaleTarget: number = 1; - public timeScale: number = 1; - - // physics - public physicsFrameRate: number; - public physicsFrameTime: number; - public physicsMaxPrediction: number; - - // game entities - public nobots: Nobot[] = []; - public updatables: any[] = []; - public scenarios: Scenario[] = []; - - // custom elements - public inputManager: InputManager; - public loadingManager: LoadingManager; - - // scenarios - public lastScenarioID?: string; - - // html target - public target: HTMLDivElement; - - // debugger - public debugger?: ReturnType; - private debug: boolean = false; - - // listeners, so can remove once component exits - private listeners: { [key: string]: (() => void)[] } = {}; - - /** - * Construct a Nobot THREE.js world into the canvas! - * @param target The div element to draw the scene into - */ - constructor( - target: HTMLDivElement, - worldScenePath?: string, - callbacks: { - onDownloadStart?: () => void; - onDownloadProgress?: (p: number, d: number, t: number) => void; - onDownloadFinish?: () => void; - } = {} - ) { - this.target = target; - - // initialize Renderer - this.renderer = new THREE.WebGLRenderer({ alpha: true }); - // this.renderer.setPixelRatio(0.3); - this.renderer.shadowMap.enabled = true; - this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; - - // initialization - this.inputManager = new InputManager(this, this.target); - this.loadingManager = new LoadingManager(this, { - onStart: callbacks.onDownloadStart, - onProgress: callbacks.onDownloadProgress, - onFinished: callbacks.onDownloadFinish, - }); - - // set up resizing on window size change - const onWindowResize = () => { - // get the size of the canvas for aspect ratio - const { width, height } = target.getBoundingClientRect(); - this.camera.aspect = width / height; - this.camera.updateProjectionMatrix(); - this.renderer.setSize(width, height, false); - }; - this.listeners.resize = [onWindowResize]; - window.addEventListener('resize', onWindowResize, false); - - // THREE.js scene - this.graphicsWorld = new THREE.Scene(); - const { width, height } = target.getBoundingClientRect(); - this.camera = new THREE.PerspectiveCamera(80, width / height, 0.1, 1010); - this.cameraOperator = new CameraOperator( - this, - this.camera, - this.renderer.domElement, - this.inputManager - ); - this.cameraOperator.minDistance = 1; - this.cameraOperator.maxDistance = 20; - onWindowResize(); - - // physics - this.physicsWorld = new CANNON.World(); - this.physicsWorld.gravity.set(0, -9.81, 0); - this.physicsWorld.broadphase = new CANNON.SAPBroadphase(this.physicsWorld); - this.physicsWorld.solver = new CANNON.GSSolver(); - (this.physicsWorld.solver as CANNON.GSSolver).iterations = 10; - this.physicsWorld.allowSleep = true; - - this.physicsFrameRate = 60; - this.physicsFrameTime = 1 / this.physicsFrameRate; - this.physicsMaxPrediction = this.physicsFrameRate; - - // render loop - this.clock = new THREE.Clock(); - this.renderDelta = 0; - this.logicDelta = 0; - this.sinceLastFrame = 0; - this.justRendered = false; - - // load scene if path is supplied - if (worldScenePath) { - this.openScene(worldScenePath).then(() => { - // begin render cycle once loaded - this.render(this); - }); - } - - // create the cannon debugger - if (this.debug) { - this.debugger = CannonDebugger(this.graphicsWorld, this.physicsWorld, {}); - } - - // connect the canvas up - this.target.appendChild(this.renderer.domElement); - } - - /** - * Destroy the three.js instance, disposing of assets and removing the window - * event listeners. - */ - destroy() { - this.target.removeChild(this.renderer.domElement); - // remove input listener - this.inputManager.deafen(); - // remove event listeners from window - Object.keys(this.listeners).forEach((key) => { - this.listeners[key].forEach((listener) => { - window.removeEventListener(key, listener, false); - }); - }); - } - - /* -------------------------------- UPDATING -------------------------------- */ - - /** - * Draws the world as seen through the camera. - * @param world The world (this) to render - */ - public render(world: World): void { - this.requestDelta = this.clock.getDelta(); - requestAnimationFrame(() => { - world.render(world); - }); - - // get the timestep - const unscaledTimeStep = - this.requestDelta + this.renderDelta + this.logicDelta; - let timeStep = unscaledTimeStep * this.timeScale; - timeStep = Math.min(timeStep, 1 / 30); // min 30 fps - - // logic - world.update(timeStep, unscaledTimeStep); - - // measuring logic time - this.logicDelta = this.clock.getDelta(); - - // frame limiting - const interval = 1 / 60; - this.sinceLastFrame += - this.requestDelta + this.renderDelta + this.logicDelta; - this.sinceLastFrame %= interval; - - // render the world - this.renderer.render(this.graphicsWorld, this.camera); - this.renderDelta = this.clock.getDelta(); - } - - /** - * Updates the world and physics - * @param timeStep - */ - public update(timeStep: number, unscaledTimeStep: number): void { - this.updatePhysics(timeStep); - if (this.debug) { - this.debugger!.update(); - } - // update registered objects - this.updatables.forEach((entity) => { - entity.update(timeStep, unscaledTimeStep); - }); - // update camera - this.cameraOperator.update2(timeStep); - - // Lerp time scale - this.timeScale = THREE.MathUtils.lerp( - this.timeScale, - this.timeScaleTarget, - 0.2 - ); - } - - /** - * Updates the physics in the world - * @param timeStep Time step to use in calculations - */ - public updatePhysics(timeStep: number): void { - // step the physics world - this.physicsWorld.step(this.physicsFrameTime, timeStep); - // do other physics updates (respawns ?) here - // this.nobots.forEach((nobot) => { - // if (this.isOutOfBounds(nobot.nobotCapsule.body.position)) - // { - // this.outOfBoundsRespawn(nobot.nobotCapsule.body); - // } - // }); - } - - /* ---------------------------- STATE MANAGEMENT ---------------------------- */ - - /** - * Registers an entity for updates in the update cycle in this world. - * @param registree The entity to update every timestep - */ - public registerUpdatable(registree: IUpdatable) { - this.updatables.push(registree); - this.updatables.sort((a, b) => (a.updateOrder > b.updateOrder ? 1 : -1)); - } - - /** - * Unregisters an entity for updates from this world. - * @param registree The entity to remove from the update loop - */ - public unregisterUpdatable(registree: IUpdatable) { - _.pull(this.updatables, registree); - } - - /** - * Adds an entity to the world - * @param worldEntity The entity to add to the world - */ - public add(worldEntity: IWorldEntity): void { - worldEntity.addToWorld(this); - this.registerUpdatable(worldEntity); - } - - /** - * Removes an entity from the world - * @param worldEntity The entity to remove from the world - */ - public remove(worldEntity: IWorldEntity): void { - worldEntity.removeFromWorld(this); - this.unregisterUpdatable(worldEntity); - } - - /** - * Clears all entities from the world - */ - public clearEntities(): void { - for (let i = 0; i < this.nobots.length; i += 1) { - this.remove(this.nobots[i]); - i -= 1; - } - } - - /** - * Update the world's timescale to LERP towards this value of update speed. - * @param val - */ - public setTimeScale(val: number) { - this.timeScaleTarget = val; - } - - /* ------------------------------ SCENE LOADING ----------------------------- */ - - /** - * Begins the download of a scene. - */ - public async openScene(world: string): Promise { - const sceneGLTF = await this.loadingManager.loadGLTF(world); - await this.loadScene(sceneGLTF); - this.update(1, 1); - this.setTimeScale(1); - } - - /** - * Load in a scene from a GLB file. Each scene contains (in userData): - * - A scenario defining where users can spawn / actions - * - Meshes for visualization and rendering - * - Collision / physics data - * - Paths for AI - * @param loadingManager - */ - public async loadScene(gltf: GLTF) { - // traverse the scene to populate the world - this.raycastScene = []; - gltf.scene.traverse((child) => { - if (Object.prototype.hasOwnProperty.call(child, 'userData')) { - // for mesh children, update materials - if (child.type === 'Mesh') { - Utils.setUpMeshProperties(child as THREE.Mesh); - (child as THREE.Mesh).geometry.computeBoundsTree(); - // this.cameraController.colliderMeshes.push(child); - // this.raycastScene.push(child as THREE.Mesh); - } - // check if the child has physics data - if (Object.prototype.hasOwnProperty.call(child.userData, 'data')) { - switch (child.userData.data) { - // userData: PHYSICS - case 'physics': - if ( - Object.prototype.hasOwnProperty.call(child.userData, 'type') - ) { - // import box colliders from blender data and apply - if (child.userData.type === 'box') { - const phys = new BoxCollider({ - size: new CANNON.Vec3( - child.scale.x, - child.scale.y, - child.scale.z - ), - }); - phys.body.position.copy(Utils.cannonVector(child.position)); - phys.body.quaternion.copy(Utils.cannonQuat(child.quaternion)); - phys.body.updateAABB(); - phys.body.shapes.forEach((shape) => { - shape.collisionFilterMask = - ~CollisionGroups.TriMeshColliders; // eslint-disable-line no-bitwise - }); - this.physicsWorld.addBody(phys.body); - // import trimesh colliders frmo blender and apply - } else if (child.userData.type === 'trimesh') { - const phys = new TrimeshCollider(child, {}); - this.physicsWorld.addBody(phys.body); - } - // hide the physics mesh - child.visible = false; - } - break; - // userData: SCENARIO - case 'scenario': - this.scenarios.push(new Scenario(child, this)); - break; - default: - break; - } - } - } - }); - - // add the scene to the world - this.graphicsWorld.add(gltf.scene); - - // add a light to the scene - this.graphicsWorld.add(new THREE.AmbientLight(0xd0d0d0)); - this.graphicsWorld.add( - new THREE.HemisphereLight('#e6e1b8', '#595850', 0.5) - ); - // this.graphicsWorld.add(new THREE.HemisphereLight('#66B588', '#0D85AA')); - - // find default scenario - let defaultScenarioID: string | null = null; - for (let i = 0; i < this.scenarios.length; i += 1) { - if (this.scenarios[i].default) { - defaultScenarioID = this.scenarios[i].id; - break; - } - } - // launch default scenario - if (defaultScenarioID) await this.launchScenario(defaultScenarioID); - } - - /** - * Launches a scenario into the world, essentially spawning the player at the - * scenario position. - * @param scenarioID The ID of the scenario (from the name in the file) - * @param loadingManager The loading manager that downloaded it - */ - public async launchScenario(scenarioID: string) { - this.lastScenarioID = scenarioID; - this.clearEntities(); - // launch the scenario that matches by id - const scenarioPromises: Promise[] = []; - for (let i = 0; i < this.scenarios.length; i += 1) { - if ( - this.scenarios[i].id === scenarioID || - this.scenarios[i].spawnAlways - ) { - scenarioPromises.push(this.scenarios[i].launch(this)); - } - } - await Promise.all(scenarioPromises); - } -} diff --git a/src/components/Space/game/world/three-csm.d.ts b/src/components/Space/game/world/three-csm.d.ts deleted file mode 100644 index fd5bdb3..0000000 --- a/src/components/Space/game/world/three-csm.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'three-csm' { - import * as CSM from 'three-csm'; - export default CSM; -} diff --git a/yarn.lock b/yarn.lock index cff2689..2f0826b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1289,10 +1289,10 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/three@>=0.142.0": - version "0.144.0" - resolved "https://registry.yarnpkg.com/@types/three/-/three-0.144.0.tgz#a154f40122dbc3668c5424a5373f3965c6564557" - integrity sha512-psvEs6q5rLN50jUYZ3D4pZMfxTbdt3A243blt0my7/NcL6chaCZpHe2csbCtx0SOD9fI/XnF3wnVUAYZGqCSYg== +"@types/three@^0.146.0": + version "0.146.0" + resolved "https://registry.yarnpkg.com/@types/three/-/three-0.146.0.tgz#83813ba0d2fff6bdc6d7fda3a77993a932bba45f" + integrity sha512-75AgysUrIvTCB054eQa2pDVFurfeFW8CrMQjpzjt3yHBfuuknoSvvsESd/3EhQxPrz9si3+P0wiDUVsWUlljfA== dependencies: "@types/webxr" "*" @@ -1558,10 +1558,10 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camera-controls@^1.37.2: - version "1.37.2" - resolved "https://registry.yarnpkg.com/camera-controls/-/camera-controls-1.37.2.tgz#12d808c98b3fea1fffd72c8dfeb024f2495ab307" - integrity sha512-b+8DYJc/honm3b/PzjTQbVjsnkdgiDnI44/PRAWKlLhF0lzggGtaU9ZSGRM/lll2HkF0KN+OHx/ZN80wJmEbqA== +camera-controls@^1.37.4: + version "1.37.4" + resolved "https://registry.yarnpkg.com/camera-controls/-/camera-controls-1.37.4.tgz#5359ec8518f1a272e5fc0841b711635203479956" + integrity sha512-CwdkFgSRM6nBJtC1U+BIbETlK6rit/w4+RU8uKnEpPWnArm/sj1+1GL3im2GbgoR3ySjtD4fQ6eBK3vP6b90aQ== caniuse-lite@^1.0.30001406: version "1.0.30001406" @@ -1573,10 +1573,10 @@ cannon-es-debugger@^1.0.0: resolved "https://registry.yarnpkg.com/cannon-es-debugger/-/cannon-es-debugger-1.0.0.tgz#28c6679b30dcf608e978225239f447a8fe55ccb4" integrity sha512-sE9lDOBAYFKlh+0w+cvWKwUhJef8HYnUSVPWPL0jD15MAuVRQKno4QYZSGxgOoJkMR3mQqxL4bxys2b3RSWH8g== -cannon-es@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/cannon-es/-/cannon-es-0.19.0.tgz#f62fa04097e375877db8f3895f27bb6d499395c0" - integrity sha512-fu9UXrjFmAQXq3JyBVPbtyc53suX+QHzikZEJykxroVNnIx1xHq0jZenK8qwhdwbBAtDL9n/47TyPELuSQwZuA== +cannon-es@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/cannon-es/-/cannon-es-0.20.0.tgz#bf2f62a674701f37e3d861c90668a32d4d32f082" + integrity sha512-eZhWTZIkFOnMAJOgfXJa9+b3kVlvG+FX4mdkpePev/w/rP5V8NRquGyEozcjPfEoXUlb+p7d9SUcmDSn14prOA== chalk@^4.0.0: version "4.1.2" @@ -2125,11 +2125,6 @@ execa@^6.1.0: signal-exit "^3.0.7" strip-final-newline "^3.0.0" -exenv@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" - integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== - ext@^1.1.2: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -2250,11 +2245,6 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gamepads@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/gamepads/-/gamepads-1.2.2.tgz#6bfbbbded499f66db94c1fe1c0038a28ba1d2733" - integrity sha512-m0DihXzHh1+bLoex4Bz0tX3IaJ1OyIKaw/71Cj2lFRsTESMJpjLvmc+BluU3QE0UsMjMFiIK2j7c1Q08O/DYfg== - get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" @@ -3209,13 +3199,6 @@ react-query@^3.39.2: broadcast-channel "^3.4.1" match-sorter "^6.0.2" -react-scrolllock@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/react-scrolllock/-/react-scrolllock-5.0.1.tgz#da1cfb7b6d55c86ae41dbad5274b778c307752b7" - integrity sha512-poeEsjnZAlpA6fJlaNo4rZtcip2j6l5mUGU/SJe1FFlicEudS943++u7ZSdA7lk10hoyYK3grOD02/qqt5Lxhw== - dependencies: - exenv "^1.2.2" - react-textarea-autosize@^8.3.4: version "8.3.4" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz#270a343de7ad350534141b02c9cb78903e553524" @@ -3551,15 +3534,15 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -three-mesh-bvh@^0.5.14: - version "0.5.16" - resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.5.16.tgz#ed8518355fb70d7da6a5537558ba43f76a56b067" - integrity sha512-kN3QGKJNiQQcuiOsjVIkjtPnV3PJa+0Orih2E52afa+kQfs3zfoYt6Kxn6b6RNiAfaQ+7ckwPK0mTVFVl2F2LA== +three-mesh-bvh@^0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.5.19.tgz#72d489856561d01ef7d4add81f0c82d3fb644d09" + integrity sha512-Q8huT0NautyR4afW0oMlOz8a4qm3b1jQ1ccMLF/TZ0rA4i58HljsnBjCLEDLQtIINZyLtZPdw60mrD81V/TZDA== -three-stdlib@^2.12.1: - version "2.17.2" - resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.17.2.tgz#9c2a2914a16123853531612dac8870e6e242685b" - integrity sha512-7ZLCJJogtn1D1MlUi7q0iLUbrxj7K++YxjHIIz5AZ4wX4E137BgiiTmhH4XhAuvXGRk9ph3ZtoHTfJBXhqDX3w== +three-stdlib@^2.20.4: + version "2.20.4" + resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.20.4.tgz#89a006c53f12c0b037dc9242874bcada8865bd00" + integrity sha512-Ksy7h//nnH5dhohbmE6mT/gM54dtmcAjaC9Srx7AJ7xpmCEX3IASkZyUkVqNXv3NZFqx79Swq3sSSVqL4pF79Q== dependencies: "@babel/runtime" "^7.16.7" "@types/offscreencanvas" "^2019.6.4" @@ -3573,17 +3556,10 @@ three-stdlib@^2.12.1: potpack "^1.0.1" zstddec "^0.0.2" -three-to-cannon@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/three-to-cannon/-/three-to-cannon-4.2.0.tgz#7a42705ebde274e720718ce58d91efce49508edf" - integrity sha512-T44014VnIllo+gvPYz2eyVwSxCCvLooP+9Fbh+ykTNXCzgKuxswOQdyAcS8dlDQAR/xL5/uI6eLNIlZFu4cphQ== - dependencies: - "@types/three" ">=0.142.0" - -three@^0.142.0: - version "0.142.0" - resolved "https://registry.yarnpkg.com/three/-/three-0.142.0.tgz#89e226a16221f212eb1d40f0786604b711f28aed" - integrity sha512-ESjPO+3geFr+ZUfVMpMnF/eVU2uJPOh0e2ZpMFqjNca1wApS9lJb7E4MjwGIczgt9iuKd8PEm6Pfgp2bJ92Xtg== +three@^0.147.0: + version "0.147.0" + resolved "https://registry.yarnpkg.com/three/-/three-0.147.0.tgz#1974af9e8e0c1efb3a8561334d57c0b6c29a7951" + integrity sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw== through@^2.3.8: version "2.3.8" @@ -3754,6 +3730,20 @@ warning@^4.0.1: dependencies: loose-envify "^1.0.0" +web-worlding@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/web-worlding/-/web-worlding-0.1.3.tgz#ea4878e4eb95578d261ff6b0a58d4419ebdc3058" + integrity sha512-an17H+eljXHS73Nsl+tqJJTQo9vhZ5o51oEvq3PeRLbeEqKmi+4e3iKdScyRPPyu4oVuym45xYdgHZSkA5F4/g== + dependencies: + "@types/three" "^0.146.0" + camera-controls "^1.37.4" + cannon-es "^0.20.0" + cannon-es-debugger "^1.0.0" + nipplejs "^0.10.0" + three "^0.147.0" + three-mesh-bvh "^0.5.19" + three-stdlib "^2.20.4" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"