diff --git a/examples/src/examples/camera/multi.controls.mjs b/examples/src/examples/camera/multi.controls.mjs index fd852d9e54d..fbf620c12f2 100644 --- a/examples/src/examples/camera/multi.controls.mjs +++ b/examples/src/examples/camera/multi.controls.mjs @@ -3,9 +3,18 @@ * @returns {JSX.Element} The returned JSX Element. */ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { - const { BindingTwoWay, LabelGroup, Panel, SliderInput } = ReactPCUI; + const { BindingTwoWay, LabelGroup, Panel, BooleanInput, SliderInput } = ReactPCUI; return fragment( + jsx( + LabelGroup, + { text: 'Zoom reset' }, + jsx(BooleanInput, { + type: 'toggle', + binding: new BindingTwoWay(), + link: { observer, path: 'example.zoomReset' } + }) + ), jsx( Panel, { headerText: 'Attributes' }, @@ -14,7 +23,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Focus FOV' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.focusFov' }, + link: { observer, path: 'attr.focusFov' }, min: 30, max: 120 }) @@ -24,7 +33,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Look sensitivity' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.lookSensitivity' }, + link: { observer, path: 'attr.lookSensitivity' }, min: 0.1, max: 1, step: 0.01 @@ -35,7 +44,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Look damping' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.lookDamping' }, + link: { observer, path: 'attr.lookDamping' }, min: 0, max: 0.99, step: 0.01 @@ -46,7 +55,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Move damping' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.moveDamping' }, + link: { observer, path: 'attr.moveDamping' }, min: 0, max: 0.99, step: 0.01 @@ -57,7 +66,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Pinch speed' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.pinchSpeed' }, + link: { observer, path: 'attr.pinchSpeed' }, min: 1, max: 10 }) @@ -67,7 +76,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Wheel speed' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.wheelSpeed' }, + link: { observer, path: 'attr.wheelSpeed' }, min: 0.001, max: 0.01, step: 0.001 @@ -78,7 +87,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Zoom min' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.zoomMin' }, + link: { observer, path: 'attr.zoomMin' }, min: 0.001, max: 0.01, step: 0.001 @@ -89,7 +98,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Zoom max' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.zoomMax' }, + link: { observer, path: 'attr.zoomMax' }, min: 1, max: 10 }) @@ -99,7 +108,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Zoom scale min' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.zoomScaleMin' }, + link: { observer, path: 'attr.zoomScaleMin' }, min: 0.001, max: 0.01, step: 0.001 @@ -110,7 +119,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Move speed' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.moveSpeed' }, + link: { observer, path: 'attr.moveSpeed' }, min: 1, max: 10 }) @@ -120,7 +129,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Sprint speed' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.sprintSpeed' }, + link: { observer, path: 'attr.sprintSpeed' }, min: 1, max: 10 }) @@ -130,7 +139,7 @@ export function controls({ observer, ReactPCUI, React, jsx, fragment }) { { text: 'Crouch speed' }, jsx(SliderInput, { binding: new BindingTwoWay(), - link: { observer, path: 'camera.crouchSpeed' }, + link: { observer, path: 'attr.crouchSpeed' }, min: 1, max: 10 }) diff --git a/examples/src/examples/camera/multi.example.mjs b/examples/src/examples/camera/multi.example.mjs index f178c1748e4..17a5e7985b2 100644 --- a/examples/src/examples/camera/multi.example.mjs +++ b/examples/src/examples/camera/multi.example.mjs @@ -60,27 +60,51 @@ await new Promise((resolve) => { new pc.AssetListLoader(Object.values(assets), app.assets).load(resolve); }); +/** + * Calculate the bounding box of an entity. + * + * @param {pc.BoundingBox} bbox - The bounding box. + * @param {pc.Entity} entity - The entity. + * @returns {pc.BoundingBox} The bounding box. + */ +const calcEntityAABB = (bbox, entity) => { + bbox.center.set(0, 0, 0); + bbox.halfExtents.set(0, 0, 0); + entity.findComponents('render').forEach((render) => { + render.meshInstances.forEach((/** @type {pc.MeshInstance} */ mi) => { + bbox.add(mi.aabb); + }); + }); + return bbox; +}; + /** * @param {pc.Entity} focus - The entity to focus the camera on. - * @returns {pc.Entity} The multi-camera entity. + * @returns {MultiCamera} The multi-camera script. */ const createMultiCamera = (focus) => { const camera = new pc.Entity(); camera.addComponent('camera'); + camera.addComponent('script'); + + const start = new pc.Vec3(0, 20, 30); + const bbox = calcEntityAABB(new pc.BoundingBox(), focus); + const cameraDist = start.distance(bbox.center); - const multiCamera = new pc.Entity(); - multiCamera.addComponent('script'); - const script = /** @type {MultiCamera} */ (multiCamera.script.create(MultiCamera, { + /** @type {MultiCamera} */ + const script = camera.script.create(MultiCamera, { attributes: { - target: canvas + sceneSize: bbox.halfExtents.length() } - })); - script.attach(camera); + }); // focus on entity when 'f' key is pressed const onKeyDown = (/** @type {KeyboardEvent} */ e) => { if (e.key === 'f') { - script.focusOnEntity(focus); + if (data.get('example.zoomReset')) { + script.resetZoom(cameraDist); + } + script.focus(bbox.center); } }; window.addEventListener('keydown', onKeyDown); @@ -90,12 +114,12 @@ const createMultiCamera = (focus) => { // wait until after canvas resized to focus on entity const resize = new ResizeObserver(() => { - script.focusOnEntity(focus, true); resize.disconnect(); + script.focus(bbox.center, start); }); resize.observe(canvas); - return multiCamera; + return script; }; app.start(); @@ -116,11 +140,13 @@ const statue = assets.statue.resource.instantiateRenderEntity(); statue.setLocalPosition(0, -0.5, 0); app.root.addChild(statue); -const multiCamera = createMultiCamera(statue); -app.root.addChild(multiCamera); +const multiCameraScript = createMultiCamera(statue); // Bind controls to camera attributes -data.set('camera', { +data.set('example', { + zoomReset: true +}); +data.set('attr', { focusFov: 75, lookSensitivity: 0.2, lookDamping: 0.97, @@ -136,10 +162,10 @@ data.set('camera', { }); data.on('*:set', (/** @type {string} */ path, /** @type {any} */ value) => { const [category, key] = path.split('.'); - if (category !== 'camera') { + if (category !== 'attr') { return; } - multiCamera.script.multiCamera[key] = value; + multiCameraScript[key] = value; }); export { app }; diff --git a/scripts/camera/base-camera.js b/scripts/camera/base-camera.js index b52f29c5de8..d1d6bafdfa2 100644 --- a/scripts/camera/base-camera.js +++ b/scripts/camera/base-camera.js @@ -1,82 +1,77 @@ -import { Script, Vec2, Vec3, math } from 'playcanvas'; +import { Entity, Script, Vec3, Vec2, math } from 'playcanvas'; + +/** @import { CameraComponent } from 'playcanvas' */ const LOOK_MAX_ANGLE = 90; class BaseCamera extends Script { /** - * @type {Entity} - */ - entity; - - /** - * @type {HTMLElement} + * @type {CameraComponent} + * @protected */ - target = document.documentElement; + _camera = null; /** - * @attribute - * @type {number} + * @type {Vec3} + * @protected */ - sceneSize = 100; + _origin = new Vec3(0, 1, 0); /** - * @attribute - * @type {number} + * @type {Vec3} + * @protected */ - lookSensitivity = 0.2; + _position = new Vec3(); /** - * @attribute - * @type {number} + * @type {Vec2} + * @protected */ - lookDamping = 0.97; + _dir = new Vec2(); /** - * @attribute - * @type {number} + * @type {Vec3} + * @protected */ - moveDamping = 0.98; + _angles = new Vec3(); /** * @type {Entity} - * @protected */ - _camera = null; + root; /** - * @type {Vec3} - * @protected + * @attribute + * @type {number} */ - _origin = new Vec3(0, 1, 0); + sceneSize = 100; /** - * @type {Vec3} - * @protected + * @attribute + * @type {number} */ - _position = new Vec3(); + lookSensitivity = 0.2; /** - * @type {Vec2} - * @protected + * @attribute + * @type {number} */ - _dir = new Vec2(); + lookDamping = 0.97; /** - * @type {Vec3} - * @protected + * @attribute + * @type {number} */ - _angles = new Vec3(); + moveDamping = 0.98; /** - * @param {Record} args - The script arguments + * @param {object} args - The script arguments. */ constructor(args) { super(args); - const { entity, attributes } = args; - const { target, sceneSize, lookSensitivity, lookDamping, moveDamping } = attributes; + const { name, sceneSize, lookSensitivity, lookDamping, moveDamping } = args.attributes; - this.entity = entity; - this.target = target; + this.root = new Entity(name ?? 'base-camera'); this.sceneSize = sceneSize ?? this.sceneSize; this.lookSensitivity = lookSensitivity ?? this.lookSensitivity; this.lookDamping = lookDamping ?? this.lookDamping; @@ -85,63 +80,71 @@ class BaseCamera extends Script { this._onPointerDown = this._onPointerDown.bind(this); this._onPointerMove = this._onPointerMove.bind(this); this._onPointerUp = this._onPointerUp.bind(this); + + this.app.root.addChild(this.root); } /** - * @param {number} dt - The delta time in seconds. * @private + * @param {number} dt - The delta time. */ _smoothLook(dt) { const lerpRate = 1 - Math.pow(this.lookDamping, dt * 1000); this._angles.x = math.lerp(this._angles.x, this._dir.x, lerpRate); this._angles.y = math.lerp(this._angles.y, this._dir.y, lerpRate); - this.entity.setEulerAngles(this._angles); + this.root.setEulerAngles(this._angles); } /** - * @param {number} dt - The delta time in seconds. * @private + * @param {number} dt - The delta time. */ _smoothMove(dt) { this._position.lerp(this._position, this._origin, 1 - Math.pow(this.moveDamping, dt * 1000)); - this.entity.setPosition(this._position); + this.root.setPosition(this._position); } /** - * @param {MouseEvent} event - The mouse event. * @private + * @param {MouseEvent} event - The mouse event. */ _onContextMenu(event) { event.preventDefault(); } /** - * @param {PointerEvent} event - The pointer event. * @protected * @abstract + * @param {PointerEvent} event - The pointer event. */ - _onPointerDown(event) {} + _onPointerDown(event) { + throw new Error('Method not implemented.'); + } /** - * @param {PointerEvent} event - The pointer move event. * @protected * @abstract + * @param {PointerEvent} event - The pointer event. */ - _onPointerMove(event) {} + _onPointerMove(event) { + throw new Error('Method not implemented.'); + } /** - * @param {PointerEvent} event - The pointer event. * @protected * @abstract + * @param {PointerEvent} event - The pointer event. */ - _onPointerUp(event) {} + _onPointerUp(event) { + throw new Error('Method not implemented.'); + } /** - * @param {PointerEvent} event - The pointer move event. * @protected + * @param {PointerEvent} event - The pointer event. */ _look(event) { - if (event.target !== this.target) { + if (event.target !== this.app.graphicsDevice.canvas) { return; } const movementX = event.movementX || 0; @@ -151,18 +154,18 @@ class BaseCamera extends Script { } /** - * @param {Entity} camera - The camera entity to attach. + * @param {CameraComponent} camera - The camera component. */ attach(camera) { this._camera = camera; - this._camera.setLocalEulerAngles(0, 0, 0); + this._camera.entity.setLocalEulerAngles(0, 0, 0); window.addEventListener('pointerdown', this._onPointerDown); window.addEventListener('pointermove', this._onPointerMove); window.addEventListener('pointerup', this._onPointerUp); window.addEventListener('contextmenu', this._onContextMenu); - this.entity.addChild(camera); + this.root.addChild(camera.entity); } detach() { @@ -171,7 +174,7 @@ class BaseCamera extends Script { window.removeEventListener('pointerup', this._onPointerUp); window.removeEventListener('contextmenu', this._onContextMenu); - this.entity.removeChild(this._camera); + this.root.removeChild(this._camera.entity); this._camera = null; this._dir.x = this._angles.x; @@ -181,7 +184,7 @@ class BaseCamera extends Script { } /** - * @param {number} dt - The delta time in seconds. + * @param {number} dt - The delta time. */ update(dt) { if (!this._camera) { diff --git a/scripts/camera/multi-camera.js b/scripts/camera/multi-camera.js index 46a297d8722..f722e842686 100644 --- a/scripts/camera/multi-camera.js +++ b/scripts/camera/multi-camera.js @@ -1,7 +1,9 @@ -import { BoundingBox, Vec2, Vec3, Ray, Plane, math } from 'playcanvas'; +import { Vec2, Vec3, Ray, Plane, math } from 'playcanvas'; import { BaseCamera } from './base-camera.js'; +/** @import { CameraComponent } from 'playcanvas' */ + const tmpVa = new Vec2(); const tmpV1 = new Vec3(); const tmpV2 = new Vec3(); @@ -10,159 +12,136 @@ const tmpP1 = new Plane(); const PASSIVE = { passive: false }; -/** - * Calculate the bounding box of an entity. - * - * @param {BoundingBox} bbox - The bounding box. - * @param {Entity} entity - The entity. - * @returns {BoundingBox} The bounding box. - */ -const calcEntityAABB = (bbox, entity) => { - bbox.center.set(0, 0, 0); - bbox.halfExtents.set(0, 0, 0); - entity.findComponents('render').forEach((render) => { - render.meshInstances.forEach((mi) => { - bbox.add(mi.aabb); - }); - }); - return bbox; -}; - class MultiCamera extends BaseCamera { /** - * @attribute * @type {number} + * @private */ - focusFov = 75; + _zoomDist = 0; /** - * @attribute * @type {number} + * @private */ - lookSensitivity = 0.2; + _cameraDist = 0; /** - * @attribute - * @type {number} + * @type {Map} + * @private */ - lookDamping = 0.97; + _pointerEvents = new Map(); /** - * @attribute * @type {number} + * @private */ - moveDamping = 0.98; + _lastPinchDist = -1; /** - * @attribute - * @type {number} + * @type {Vec2} + * @private */ - pinchSpeed = 5; + _lastPosition = new Vec2(); /** - * @attribute - * @type {number} + * @type {boolean} + * @private */ - wheelSpeed = 0.005; + _panning = false; /** - * @attribute - * @type {number} + * @type {boolean} + * @private */ - zoomMin = 0.001; + _flying = false; /** - * @attribute - * @type {number} + * @type {Record} + * @private */ - zoomMax = 10; + _key = { + forward: false, + backward: false, + left: false, + right: false, + up: false, + down: false, + sprint: false, + crouch: false + }; /** * @attribute * @type {number} */ - zoomScaleMin = 0.01; + lookSensitivity = 0.2; /** * @attribute * @type {number} */ - moveSpeed = 2; + lookDamping = 0.97; /** * @attribute * @type {number} */ - sprintSpeed = 4; + moveDamping = 0.98; /** * @attribute * @type {number} */ - crouchSpeed = 1; + pinchSpeed = 5; /** + * @attribute * @type {number} - * @private */ - _zoom = 0; + wheelSpeed = 0.005; /** + * @attribute * @type {number} - * @private - */ - _cameraDist = 0; - - /** - * @type {Map} - * @private */ - _pointerEvents = new Map(); + zoomMin = 0.001; /** + * @attribute * @type {number} - * @private */ - _lastPinchDist = -1; + zoomMax = 10; /** - * @type {Vec2} - * @private + * @attribute + * @type {number} */ - _lastPosition = new Vec2(); + zoomScaleMin = 0.01; /** - * @type {boolean} + * @attribute + * @type {number} */ - _panning = false; + moveSpeed = 2; /** - * @type {boolean} + * @attribute + * @type {number} */ - _flying = false; + sprintSpeed = 4; /** - * @type {Record} - * @private + * @attribute + * @type {number} */ - _key = { - forward: false, - backward: false, - left: false, - right: false, - up: false, - down: false, - sprint: false, - crouch: false - }; + crouchSpeed = 1; /** - * @param {Record} args - The script arguments + * @param {object} args - The script arguments. */ constructor(args) { super(args); - const { attributes } = args; - const { pinchSpeed, wheelSpeed, zoomMin, zoomMax, moveSpeed, sprintSpeed, crouchSpeed } = attributes; + const { pinchSpeed, wheelSpeed, zoomMin, zoomMax, moveSpeed, sprintSpeed, crouchSpeed } = args.attributes; this.pinchSpeed = pinchSpeed ?? this.pinchSpeed; this.wheelSpeed = wheelSpeed ?? this.wheelSpeed; @@ -175,6 +154,11 @@ class MultiCamera extends BaseCamera { this._onWheel = this._onWheel.bind(this); this._onKeyDown = this._onKeyDown.bind(this); this._onKeyUp = this._onKeyUp.bind(this); + + if (!this.entity.camera) { + throw new Error('MultiCamera script requires a camera component'); + } + this.attach(this.entity.camera); } /** @@ -196,10 +180,10 @@ class MultiCamera extends BaseCamera { this._panning = true; } if (event.button === 2) { - this._zoom = this._cameraDist; - this._origin.copy(this._camera.getPosition()); + this._zoomDist = this._cameraDist; + this._origin.copy(this._camera.entity.getPosition()); this._position.copy(this._origin); - this._camera.setLocalPosition(0, 0, 0); + this._camera.entity.setLocalPosition(0, 0, 0); this._flying = true; } } @@ -218,7 +202,7 @@ class MultiCamera extends BaseCamera { if (this._pointerEvents.size === 1) { if (this._panning) { // mouse pan - this._handlePan(tmpVa.set(event.clientX, event.clientY)); + this._pan(tmpVa.set(event.clientX, event.clientY)); } else { super._look(event); } @@ -227,15 +211,16 @@ class MultiCamera extends BaseCamera { if (this._pointerEvents.size === 2) { // touch pan - this._handlePan(this._getMidPoint(tmpVa)); + this._pan(this._getMidPoint(tmpVa)); // pinch zoom const pinchDist = this._getPinchDist(); if (this._lastPinchDist > 0) { - this._handleZoom((this._lastPinchDist - pinchDist) * this.pinchSpeed); + this._zoom((this._lastPinchDist - pinchDist) * this.pinchSpeed); } this._lastPinchDist = pinchDist; } + } /** @@ -252,7 +237,7 @@ class MultiCamera extends BaseCamera { this._panning = false; } if (this._flying) { - tmpV1.copy(this.entity.forward).mulScalar(this._zoom); + tmpV1.copy(this.root.forward).mulScalar(this._zoomDist); this._origin.add(tmpV1); this._position.add(tmpV1); this._flying = false; @@ -265,7 +250,7 @@ class MultiCamera extends BaseCamera { */ _onWheel(event) { event.preventDefault(); - this._handleZoom(event.deltaY); + this._zoom(event.deltaY); } /** @@ -337,28 +322,27 @@ class MultiCamera extends BaseCamera { } /** - * @param {number} dt - The time delta. - * @private + * @param {number} dt - The delta time. */ - _handleMove(dt) { + _move(dt) { tmpV1.set(0, 0, 0); if (this._key.forward) { - tmpV1.add(this.entity.forward); + tmpV1.add(this.root.forward); } if (this._key.backward) { - tmpV1.sub(this.entity.forward); + tmpV1.sub(this.root.forward); } if (this._key.left) { - tmpV1.sub(this.entity.right); + tmpV1.sub(this.root.right); } if (this._key.right) { - tmpV1.add(this.entity.right); + tmpV1.add(this.root.right); } if (this._key.up) { - tmpV1.add(this.entity.up); + tmpV1.add(this.root.up); } if (this._key.down) { - tmpV1.sub(this.entity.up); + tmpV1.sub(this.root.up); } tmpV1.normalize(); const speed = this._key.crouch ? this.crouchSpeed : this._key.sprint ? this.sprintSpeed : this.moveSpeed; @@ -379,8 +363,8 @@ class MultiCamera extends BaseCamera { } /** - * @returns {number} The pinch distance. * @private + * @returns {number} The pinch distance. */ _getPinchDist() { const [a, b] = this._pointerEvents.values(); @@ -390,15 +374,15 @@ class MultiCamera extends BaseCamera { } /** - * @param {Vec2} pos - The position. + * @param {Vec2} pos - The screen position. * @param {Vec3} point - The output point. * @private */ _screenToWorldPan(pos, point) { - const mouseW = this._camera.camera.screenToWorld(pos.x, pos.y, 1); - const cameraPos = this._camera.getPosition(); + const mouseW = this._camera.screenToWorld(pos.x, pos.y, 1); + const cameraPos = this._camera.entity.getPosition(); - const focusDirScaled = tmpV1.copy(this.entity.forward).mulScalar(this._zoom); + const focusDirScaled = tmpV1.copy(this.root.forward).mulScalar(this._zoomDist); const focalPos = tmpV2.add2(cameraPos, focusDirScaled); const planeNormal = focusDirScaled.mulScalar(-1).normalize(); @@ -409,10 +393,10 @@ class MultiCamera extends BaseCamera { } /** - * @param {Vec2} pos - The position. + * @param {Vec2} pos - The screen position. * @private */ - _handlePan(pos) { + _pan(pos) { const start = new Vec3(); const end = new Vec3(); @@ -429,45 +413,27 @@ class MultiCamera extends BaseCamera { * @param {number} delta - The delta. * @private */ - _handleZoom(delta) { - const min = this._camera.camera.nearClip + this.zoomMin * this.sceneSize; + _zoom(delta) { + if (!this._camera) { + return; + } + const min = this._camera.nearClip + this.zoomMin * this.sceneSize; const max = this.zoomMax * this.sceneSize; - const scale = math.clamp(this._zoom / (max - min), this.zoomScaleMin, 1); - this._zoom += delta * this.wheelSpeed * this.sceneSize * scale; - this._zoom = math.clamp(this._zoom, min, max); - } - - /** - * @returns {number} The zoom. - * @private - */ - _calcZoom() { - const camera = this._camera.camera; - const d1 = Math.tan(0.5 * this.focusFov * math.DEG_TO_RAD); - const d2 = Math.tan(0.5 * camera.fov * math.DEG_TO_RAD); - - const scale = (d1 / d2) * (1 / camera.aspectRatio); - return scale * this.sceneSize + this.sceneSize; + const scale = math.clamp(this._zoomDist / (max - min), this.zoomScaleMin, 1); + this._zoomDist += (delta * this.wheelSpeed * this.sceneSize * scale); + this._zoomDist = math.clamp(this._zoomDist, min, max); } /** - * @param {Vec3} point - The point to focus on. - * @param {Vec3} [start] - The start point. - * @param {boolean} [snap] - Whether to snap the focus. + * @param {Vec3} point - The point. + * @param {Vec3} [start] - The start. */ - focus(point, start, snap = false) { + focus(point, start) { if (!this._camera) { return; } - - this._origin.copy(point); - if (snap) { - this._position.copy(point); - } - this._camera.setPosition(start); - this._camera.setLocalEulerAngles(0, 0, 0); - if (!start) { + this._origin.copy(point); return; } @@ -475,34 +441,26 @@ class MultiCamera extends BaseCamera { const elev = Math.atan2(tmpV1.y, tmpV1.z) * math.RAD_TO_DEG; const azim = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG; this._dir.set(-elev, -azim); - if (snap) { - this._angles.copy(this._dir); - } - this._zoom = tmpV1.length(); + this._origin.copy(point); + this._camera.entity.setPosition(start); + this._camera.entity.setLocalEulerAngles(0, 0, 0); + + this._zoomDist = tmpV1.length(); } /** - * @param {Entity} entity - The entity to focus on. - * @param {boolean} [snap] - Whether to snap the focus. + * @param {number} [zoomDist] - The zoom distance. */ - focusOnEntity(entity, snap = false) { - const bbox = calcEntityAABB(new BoundingBox(), entity); - this.sceneSize = bbox.halfExtents.length(); - this.focus(bbox.center, undefined, snap); - this._zoom = this._calcZoom(); - if (snap) { - this._cameraDist = this._zoom; - } + resetZoom(zoomDist = 0) { + this._zoomDist = zoomDist; } /** - * @param {Entity} camera - The camera entity to attach. + * @param {CameraComponent} camera - The camera component. */ attach(camera) { super.attach(camera); - this._camera.setPosition(0, 0, 0); - this._camera.setLocalEulerAngles(0, 0, 0); window.addEventListener('wheel', this._onWheel, PASSIVE); window.addEventListener('keydown', this._onKeyDown, false); @@ -532,7 +490,7 @@ class MultiCamera extends BaseCamera { } /** - * @param {number} dt - The delta time in seconds. + * @param {number} dt - The delta time. */ update(dt) { if (!this._camera) { @@ -540,11 +498,11 @@ class MultiCamera extends BaseCamera { } if (!this._flying) { - this._cameraDist = math.lerp(this._cameraDist, this._zoom, 1 - Math.pow(this.moveDamping, dt * 1000)); - this._camera.setLocalPosition(0, 0, this._cameraDist); + this._cameraDist = math.lerp(this._cameraDist, this._zoomDist, 1 - Math.pow(this.moveDamping, dt * 1000)); + this._camera.entity.setLocalPosition(0, 0, this._cameraDist); } - this._handleMove(dt); + this._move(dt); super.update(dt); } diff --git a/src/framework/components/script/component.js b/src/framework/components/script/component.js index bcb897bfa36..bb987885c30 100644 --- a/src/framework/components/script/component.js +++ b/src/framework/components/script/component.js @@ -700,7 +700,7 @@ class ScriptComponent extends Component { app: this.system.app, entity: this.entity, enabled: args.hasOwnProperty('enabled') ? args.enabled : true, - attributes: args.attributes + attributes: args.attributes || {} }); if (args.properties && typeof args.properties === 'object') {