UNPKG

mylingo3d

Version:

Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor

170 lines (141 loc) 6.05 kB
import { Object3D, PerspectiveCamera, Quaternion } from "three" import scene from "../../../engine/scene" import { onBeforeRender } from "../../../events/onBeforeRender" import ICharacterCamera, { characterCameraDefaults, characterCameraSchema, LockTargetRotationValue } from "../../../interface/ICharacterCamera" import { getSelectionTarget } from "../../../states/useSelectionTarget" import { getTransformControlsDragging } from "../../../states/useTransformControlsDragging" import OrbitCameraBase from "../OrbitCameraBase" import { euler, quaternion } from "../../utils/reusables" import MeshItem from "../MeshItem" import { getLoadedObject } from "../Loaded" import getWorldQuaternion from "../../utils/getWorldQuaternion" import { getEditorModeComputed } from "../../../states/useEditorModeComputed" import characterCameraPlaced from "./characterCameraPlaced" import { FAR, NEAR } from "../../../globals" import { getCentripetal } from "../../../states/useCentripetal" import applyCentripetalQuaternion from "../../utils/applyCentripetalQuaternion" import fpsAlpha from "../../utils/fpsAlpha" export default class CharacterCamera extends OrbitCameraBase implements ICharacterCamera { public static defaults = characterCameraDefaults public static schema = characterCameraSchema public constructor() { super(new PerspectiveCamera(75, 1, NEAR, FAR)) const midObject3d = (this.midObject3d = new Object3D()) this.outerObject3d.add(midObject3d) midObject3d.add(this.object3d) const cam = this.camera scene.attach(cam) this.then(() => scene.remove(cam)) this.createEffect(() => { const target = this.targetState.get() if (!target) return if ("frustumCulled" in target) target.frustumCulled = false }, [this.targetState.get]) const followTargetRotation = (target: MeshItem, slerp: boolean) => { euler.setFromQuaternion(target.outerObject3d.quaternion) euler.y += Math.PI if (slerp) { quaternion.setFromEuler(euler) midObject3d.quaternion.slerp(quaternion, fpsAlpha(0.1)) } else midObject3d.setRotationFromEuler(euler) this.updateAngle() } const lockTargetRotation = ( target: MeshItem, slerp: boolean, quat: Quaternion | undefined ) => { euler.setFromQuaternion(this.midObject3d.quaternion) euler.x = 0 euler.z = 0 euler.y += Math.PI if (quat) { const innerObject = getLoadedObject(target) quaternion.copy(target.outerObject3d.quaternion) const innerRotationY = innerObject.rotation.y target.outerObject3d.quaternion.copy(quat) innerObject.rotation.y = euler.y euler.setFromQuaternion(getWorldQuaternion(innerObject)) innerObject.rotation.y = innerRotationY target.outerObject3d.quaternion.copy(quaternion) } const placed = characterCameraPlaced.has(target) if (slerp && !placed) { quaternion.setFromEuler(euler) target.outerObject3d.quaternion.slerp(quaternion, fpsAlpha(0.1)) return } target.outerObject3d.setRotationFromEuler(euler) quat && placed && characterCameraPlaced.delete(target) } let transformControlRotating = false this.createEffect(() => { const target = this.targetState.get() if (!target) return followTargetRotation(target, false) let [xOld, yOld, zOld] = [0, 0, 0] const targetMoved = () => { const { x, y, z } = target.outerObject3d.position const result = x !== xOld || y !== yOld || z !== zOld ;[xOld, yOld, zOld] = [x, y, z] return result } const centripetal = getCentripetal() const handle = onBeforeRender(() => { this.outerObject3d.position.copy(target.outerObject3d.position) const quat = centripetal ? applyCentripetalQuaternion(this) : undefined if (!this.lockTargetRotation) return if ( this.lockTargetRotation === "follow" || transformControlRotating ) { followTargetRotation(target, false) return } if (this.lockTargetRotation === "dynamic-lock") { targetMoved() && lockTargetRotation(target, true, quat) return } if (this.lockTargetRotation === "dynamic-follow") { targetMoved() && followTargetRotation(target, true) return } lockTargetRotation(target, false, quat) }) return () => { handle.cancel() } }, [this.targetState.get, getCentripetal]) this.createEffect(() => { const target = this.targetState.get() const selectionTarget = getSelectionTarget() const dragging = getTransformControlsDragging() const mode = getEditorModeComputed() const rotating = target && target === selectionTarget && dragging && mode === "rotate" if (!rotating) return transformControlRotating = true return () => { transformControlRotating = false } }, [ this.targetState.get, getSelectionTarget, getTransformControlsDragging, getEditorModeComputed ]) } public lockTargetRotation: LockTargetRotationValue = true }