@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
2,796 lines • 139 kB
JavaScript
import { l as __runInitializers, aY as Tools, cd as KeyboardEventTypes, V as Vector3, q as __esDecorate, s as serialize, ce as CameraInputTypes, O as Observable, by as PointerEventTypes, cf as EventConstants, M as Matrix, T as TmpVectors, cg as CameraInputsManager, aE as _MissingSideEffect, A as AbstractEngine, b3 as Vector2, ch as TargetCamera, r as serializeAsVector3, a as EngineStore, o as SerializationHelper, G as GetClass, a7 as VertexBuffer, m as Texture, C as Constants, Q as SmartArray, y as Logger } from './index-HyNDfLMI.esm.js';
/**
* Manage the keyboard inputs to control the movement of a free camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
*/
let FreeCameraKeyboardMoveInput = (() => {
var _a;
let _keysUp_decorators;
let _keysUp_initializers = [];
let _keysUp_extraInitializers = [];
let _keysUpward_decorators;
let _keysUpward_initializers = [];
let _keysUpward_extraInitializers = [];
let _keysDown_decorators;
let _keysDown_initializers = [];
let _keysDown_extraInitializers = [];
let _keysDownward_decorators;
let _keysDownward_initializers = [];
let _keysDownward_extraInitializers = [];
let _keysLeft_decorators;
let _keysLeft_initializers = [];
let _keysLeft_extraInitializers = [];
let _keysRight_decorators;
let _keysRight_initializers = [];
let _keysRight_extraInitializers = [];
let _rotationSpeed_decorators;
let _rotationSpeed_initializers = [];
let _rotationSpeed_extraInitializers = [];
let _keysRotateLeft_decorators;
let _keysRotateLeft_initializers = [];
let _keysRotateLeft_extraInitializers = [];
let _keysRotateRight_decorators;
let _keysRotateRight_initializers = [];
let _keysRotateRight_extraInitializers = [];
let _keysRotateUp_decorators;
let _keysRotateUp_initializers = [];
let _keysRotateUp_extraInitializers = [];
let _keysRotateDown_decorators;
let _keysRotateDown_initializers = [];
let _keysRotateDown_extraInitializers = [];
return _a = class FreeCameraKeyboardMoveInput {
constructor() {
/**
* Gets or Set the list of keyboard keys used to control the forward move of the camera.
*/
this.keysUp = __runInitializers(this, _keysUp_initializers, [38]);
/**
* Gets or Set the list of keyboard keys used to control the upward move of the camera.
*/
this.keysUpward = (__runInitializers(this, _keysUp_extraInitializers), __runInitializers(this, _keysUpward_initializers, [33]));
/**
* Gets or Set the list of keyboard keys used to control the backward move of the camera.
*/
this.keysDown = (__runInitializers(this, _keysUpward_extraInitializers), __runInitializers(this, _keysDown_initializers, [40]));
/**
* Gets or Set the list of keyboard keys used to control the downward move of the camera.
*/
this.keysDownward = (__runInitializers(this, _keysDown_extraInitializers), __runInitializers(this, _keysDownward_initializers, [34]));
/**
* Gets or Set the list of keyboard keys used to control the left strafe move of the camera.
*/
this.keysLeft = (__runInitializers(this, _keysDownward_extraInitializers), __runInitializers(this, _keysLeft_initializers, [37]));
/**
* Gets or Set the list of keyboard keys used to control the right strafe move of the camera.
*/
this.keysRight = (__runInitializers(this, _keysLeft_extraInitializers), __runInitializers(this, _keysRight_initializers, [39]));
/**
* Defines the pointer angular sensibility along the X and Y axis or how fast is the camera rotating.
*/
this.rotationSpeed = (__runInitializers(this, _keysRight_extraInitializers), __runInitializers(this, _rotationSpeed_initializers, 0.5));
/**
* Gets or Set the list of keyboard keys used to control the left rotation move of the camera.
*/
this.keysRotateLeft = (__runInitializers(this, _rotationSpeed_extraInitializers), __runInitializers(this, _keysRotateLeft_initializers, []));
/**
* Gets or Set the list of keyboard keys used to control the right rotation move of the camera.
*/
this.keysRotateRight = (__runInitializers(this, _keysRotateLeft_extraInitializers), __runInitializers(this, _keysRotateRight_initializers, []));
/**
* Gets or Set the list of keyboard keys used to control the up rotation move of the camera.
*/
this.keysRotateUp = (__runInitializers(this, _keysRotateRight_extraInitializers), __runInitializers(this, _keysRotateUp_initializers, []));
/**
* Gets or Set the list of keyboard keys used to control the down rotation move of the camera.
*/
this.keysRotateDown = (__runInitializers(this, _keysRotateUp_extraInitializers), __runInitializers(this, _keysRotateDown_initializers, []));
this._keys = (__runInitializers(this, _keysRotateDown_extraInitializers), new Array());
}
/**
* Attach the input controls to a specific dom element to get the input from.
* @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
*/
attachControl(noPreventDefault) {
noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
if (this._onCanvasBlurObserver) {
return;
}
this._scene = this.camera.getScene();
this._engine = this._scene.getEngine();
this._onCanvasBlurObserver = this._engine.onCanvasBlurObservable.add(() => {
this._keys.length = 0;
});
this._onKeyboardObserver = this._scene.onKeyboardObservable.add((info) => {
const evt = info.event;
if (!evt.metaKey) {
if (info.type === KeyboardEventTypes.KEYDOWN) {
if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
this.keysDown.indexOf(evt.keyCode) !== -1 ||
this.keysLeft.indexOf(evt.keyCode) !== -1 ||
this.keysRight.indexOf(evt.keyCode) !== -1 ||
this.keysUpward.indexOf(evt.keyCode) !== -1 ||
this.keysDownward.indexOf(evt.keyCode) !== -1 ||
this.keysRotateLeft.indexOf(evt.keyCode) !== -1 ||
this.keysRotateRight.indexOf(evt.keyCode) !== -1 ||
this.keysRotateUp.indexOf(evt.keyCode) !== -1 ||
this.keysRotateDown.indexOf(evt.keyCode) !== -1) {
const index = this._keys.indexOf(evt.keyCode);
if (index === -1) {
this._keys.push(evt.keyCode);
}
if (!noPreventDefault) {
evt.preventDefault();
}
}
}
else {
if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
this.keysDown.indexOf(evt.keyCode) !== -1 ||
this.keysLeft.indexOf(evt.keyCode) !== -1 ||
this.keysRight.indexOf(evt.keyCode) !== -1 ||
this.keysUpward.indexOf(evt.keyCode) !== -1 ||
this.keysDownward.indexOf(evt.keyCode) !== -1 ||
this.keysRotateLeft.indexOf(evt.keyCode) !== -1 ||
this.keysRotateRight.indexOf(evt.keyCode) !== -1 ||
this.keysRotateUp.indexOf(evt.keyCode) !== -1 ||
this.keysRotateDown.indexOf(evt.keyCode) !== -1) {
const index = this._keys.indexOf(evt.keyCode);
if (index >= 0) {
this._keys.splice(index, 1);
}
if (!noPreventDefault) {
evt.preventDefault();
}
}
}
}
});
}
/**
* Detach the current controls from the specified dom element.
*/
detachControl() {
if (this._scene) {
if (this._onKeyboardObserver) {
this._scene.onKeyboardObservable.remove(this._onKeyboardObserver);
}
if (this._onCanvasBlurObserver) {
this._engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
}
this._onKeyboardObserver = null;
this._onCanvasBlurObserver = null;
}
this._keys.length = 0;
}
/**
* Update the current camera state depending on the inputs that have been used this frame.
* This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
*/
checkInputs() {
if (this._onKeyboardObserver) {
const camera = this.camera;
// Movement keys are gated on the keyboard→translate mapping and rotation keys on
// keyboard→rotate. Removing the corresponding entry disables that family of keys.
// An entry's optional `sensitivity` acts as a gain (default 1) over the legacy scaling.
const input = camera.movement.input;
const translateEntry = input.getEntry("keyboard", "translate");
const rotateEntry = input.getEntry("keyboard", "rotate");
const rotateGain = rotateEntry?.sensitivity ?? 1;
// Movement keys are accumulated into a single local direction and applied once below, so that
// holding two directions at once (e.g. forward + left) moves along a normalized diagonal at the
// same speed as a single direction, instead of the ~1.41x boost (sqrt(2)) that results from
// applying each movement key independently. Rotation keys act on separate angular axes and are
// applied directly as before.
const localDirection = camera._localDirection.copyFromFloats(0, 0, 0);
// Keyboard
for (let index = 0; index < this._keys.length; index++) {
const keyCode = this._keys[index];
if (this.keysLeft.indexOf(keyCode) !== -1) {
localDirection.x -= 1;
}
else if (this.keysUp.indexOf(keyCode) !== -1) {
localDirection.z += 1;
}
else if (this.keysRight.indexOf(keyCode) !== -1) {
localDirection.x += 1;
}
else if (this.keysDown.indexOf(keyCode) !== -1) {
localDirection.z -= 1;
}
else if (this.keysUpward.indexOf(keyCode) !== -1) {
localDirection.y += 1;
}
else if (this.keysDownward.indexOf(keyCode) !== -1) {
localDirection.y -= 1;
}
else if (this.keysRotateLeft.indexOf(keyCode) !== -1) {
if (rotateEntry) {
camera.cameraRotation.y -= this._getLocalRotation() * rotateGain;
}
}
else if (this.keysRotateRight.indexOf(keyCode) !== -1) {
if (rotateEntry) {
camera.cameraRotation.y += this._getLocalRotation() * rotateGain;
}
}
else if (this.keysRotateUp.indexOf(keyCode) !== -1) {
if (rotateEntry) {
camera.cameraRotation.x -= this._getLocalRotation() * rotateGain;
}
}
else if (this.keysRotateDown.indexOf(keyCode) !== -1) {
if (rotateEntry) {
camera.cameraRotation.x += this._getLocalRotation() * rotateGain;
}
}
}
// Apply a single, normalized movement once all keys for this frame have been accumulated.
// Translation is suppressed when the keyboard→translate mapping is removed.
if (translateEntry && (localDirection.x !== 0 || localDirection.y !== 0 || localDirection.z !== 0)) {
const speed = camera._computeLocalCameraSpeed() * (translateEntry.sensitivity ?? 1);
// Normalize so a diagonal isn't faster than an axis-aligned move, then scale to the per-frame speed.
localDirection.normalize().scaleInPlace(speed);
if (camera.getScene().useRightHandedSystem) {
localDirection.z *= -1;
}
camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
Vector3.TransformNormalToRef(localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
camera.cameraDirection.addInPlace(camera._transformedDirection);
}
}
}
/**
* Gets the class name of the current input.
* @returns the class name
*/
getClassName() {
return "FreeCameraKeyboardMoveInput";
}
/** @internal */
_onLostFocus() {
this._keys.length = 0;
}
/**
* Get the friendly name associated with the input class.
* @returns the input friendly name
*/
getSimpleName() {
return "keyboard";
}
_getLocalRotation() {
const handednessMultiplier = this.camera._calculateHandednessMultiplier();
const rotation = ((this.rotationSpeed * this._engine.getDeltaTime()) / 1000) * handednessMultiplier;
return rotation;
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_keysUp_decorators = [serialize()];
_keysUpward_decorators = [serialize()];
_keysDown_decorators = [serialize()];
_keysDownward_decorators = [serialize()];
_keysLeft_decorators = [serialize()];
_keysRight_decorators = [serialize()];
_rotationSpeed_decorators = [serialize()];
_keysRotateLeft_decorators = [serialize()];
_keysRotateRight_decorators = [serialize()];
_keysRotateUp_decorators = [serialize()];
_keysRotateDown_decorators = [serialize()];
__esDecorate(null, null, _keysUp_decorators, { kind: "field", name: "keysUp", static: false, private: false, access: { has: obj => "keysUp" in obj, get: obj => obj.keysUp, set: (obj, value) => { obj.keysUp = value; } }, metadata: _metadata }, _keysUp_initializers, _keysUp_extraInitializers);
__esDecorate(null, null, _keysUpward_decorators, { kind: "field", name: "keysUpward", static: false, private: false, access: { has: obj => "keysUpward" in obj, get: obj => obj.keysUpward, set: (obj, value) => { obj.keysUpward = value; } }, metadata: _metadata }, _keysUpward_initializers, _keysUpward_extraInitializers);
__esDecorate(null, null, _keysDown_decorators, { kind: "field", name: "keysDown", static: false, private: false, access: { has: obj => "keysDown" in obj, get: obj => obj.keysDown, set: (obj, value) => { obj.keysDown = value; } }, metadata: _metadata }, _keysDown_initializers, _keysDown_extraInitializers);
__esDecorate(null, null, _keysDownward_decorators, { kind: "field", name: "keysDownward", static: false, private: false, access: { has: obj => "keysDownward" in obj, get: obj => obj.keysDownward, set: (obj, value) => { obj.keysDownward = value; } }, metadata: _metadata }, _keysDownward_initializers, _keysDownward_extraInitializers);
__esDecorate(null, null, _keysLeft_decorators, { kind: "field", name: "keysLeft", static: false, private: false, access: { has: obj => "keysLeft" in obj, get: obj => obj.keysLeft, set: (obj, value) => { obj.keysLeft = value; } }, metadata: _metadata }, _keysLeft_initializers, _keysLeft_extraInitializers);
__esDecorate(null, null, _keysRight_decorators, { kind: "field", name: "keysRight", static: false, private: false, access: { has: obj => "keysRight" in obj, get: obj => obj.keysRight, set: (obj, value) => { obj.keysRight = value; } }, metadata: _metadata }, _keysRight_initializers, _keysRight_extraInitializers);
__esDecorate(null, null, _rotationSpeed_decorators, { kind: "field", name: "rotationSpeed", static: false, private: false, access: { has: obj => "rotationSpeed" in obj, get: obj => obj.rotationSpeed, set: (obj, value) => { obj.rotationSpeed = value; } }, metadata: _metadata }, _rotationSpeed_initializers, _rotationSpeed_extraInitializers);
__esDecorate(null, null, _keysRotateLeft_decorators, { kind: "field", name: "keysRotateLeft", static: false, private: false, access: { has: obj => "keysRotateLeft" in obj, get: obj => obj.keysRotateLeft, set: (obj, value) => { obj.keysRotateLeft = value; } }, metadata: _metadata }, _keysRotateLeft_initializers, _keysRotateLeft_extraInitializers);
__esDecorate(null, null, _keysRotateRight_decorators, { kind: "field", name: "keysRotateRight", static: false, private: false, access: { has: obj => "keysRotateRight" in obj, get: obj => obj.keysRotateRight, set: (obj, value) => { obj.keysRotateRight = value; } }, metadata: _metadata }, _keysRotateRight_initializers, _keysRotateRight_extraInitializers);
__esDecorate(null, null, _keysRotateUp_decorators, { kind: "field", name: "keysRotateUp", static: false, private: false, access: { has: obj => "keysRotateUp" in obj, get: obj => obj.keysRotateUp, set: (obj, value) => { obj.keysRotateUp = value; } }, metadata: _metadata }, _keysRotateUp_initializers, _keysRotateUp_extraInitializers);
__esDecorate(null, null, _keysRotateDown_decorators, { kind: "field", name: "keysRotateDown", static: false, private: false, access: { has: obj => "keysRotateDown" in obj, get: obj => obj.keysRotateDown, set: (obj, value) => { obj.keysRotateDown = value; } }, metadata: _metadata }, _keysRotateDown_initializers, _keysRotateDown_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
CameraInputTypes["FreeCameraKeyboardMoveInput"] = FreeCameraKeyboardMoveInput;
/**
* Manage the mouse inputs to control the movement of a free camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
*/
let FreeCameraMouseInput = (() => {
var _a;
let _buttons_decorators;
let _buttons_initializers = [];
let _buttons_extraInitializers = [];
let _angularSensibility_decorators;
let _angularSensibility_initializers = [];
let _angularSensibility_extraInitializers = [];
return _a = class FreeCameraMouseInput {
/**
* Applies a pointer-drag delta as camera rotation, but only if the camera's configurable
* input map resolves the current pointer interaction to "rotate". The map is consulted with
* the currently active mouse button so consumers can remap or disable pointer-driven rotation.
* The applied scale comes from the resolved entry's `sensitivity`/`sensitivityX`/`sensitivityY`,
* falling back to the legacy `angularSensibility` for backward compatibility. The rotation is
* still written to `camera.cameraRotation` (not the movement accumulators) so existing code that
* reads `cameraRotation` immediately after a pointer event keeps working.
* @param offsetX Horizontal pointer delta (already handedness-adjusted).
* @param offsetY Vertical pointer delta (already handedness-adjusted).
*/
_applyPointerRotation(offsetX, offsetY) {
this._pointerConditions.button = this._currentActiveButton;
const entry = this.camera.movement.input.resolveInteraction("pointer", this._pointerConditions);
if (!entry || entry.interaction !== "rotate") {
return;
}
const sensitivityX = entry.sensitivityX ?? entry.sensitivity ?? 1 / this.angularSensibility;
const sensitivityY = entry.sensitivityY ?? entry.sensitivity ?? 1 / this.angularSensibility;
this.camera.cameraRotation.y += offsetX * sensitivityX;
this.camera.cameraRotation.x += offsetY * sensitivityY;
}
/**
* Manage the mouse inputs to control the movement of a free camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
* @param touchEnabled Defines if touch is enabled or not
*/
constructor(
/**
* [true] Define if touch is enabled in the mouse input
*/
touchEnabled = true) {
this.touchEnabled = touchEnabled;
/**
* Defines the buttons associated with the input to handle camera move.
*/
this.buttons = __runInitializers(this, _buttons_initializers, [0, 1, 2]);
/**
* Defines the pointer angular sensibility along the X and Y axis or how fast is the camera rotating.
*/
this.angularSensibility = (__runInitializers(this, _buttons_extraInitializers), __runInitializers(this, _angularSensibility_initializers, 2000.0));
this._pointerInput = __runInitializers(this, _angularSensibility_extraInitializers);
this._previousPosition = null;
/**
* Observable for when a pointer move event occurs containing the move offset
*/
this.onPointerMovedObservable = new Observable();
/**
* @internal
* If the camera should be rotated automatically based on pointer movement
*/
this._allowCameraRotation = true;
this._currentActiveButton = -1;
this._activePointerId = -1;
/** Reused conditions object for `resolveInteraction` to avoid per-move allocations. */
this._pointerConditions = {};
}
/**
* Attach the input controls to a specific dom element to get the input from.
* @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
*/
attachControl(noPreventDefault) {
noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
const engine = this.camera.getEngine();
const element = engine.getInputElement();
if (!this._pointerInput) {
this._pointerInput = (p) => {
const evt = p.event;
const isTouch = evt.pointerType === "touch";
if (!this.touchEnabled && isTouch) {
return;
}
if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
return;
}
const srcElement = evt.target;
if (p.type === PointerEventTypes.POINTERDOWN) {
// If the input is touch with more than one touch OR if the input is mouse and there is already an active button, return
if ((isTouch && this._activePointerId !== -1) || (!isTouch && this._currentActiveButton !== -1)) {
return;
}
this._activePointerId = evt.pointerId;
try {
srcElement?.setPointerCapture(evt.pointerId);
}
catch (e) {
//Nothing to do with the error. Execution will continue.
}
if (this._currentActiveButton === -1) {
this._currentActiveButton = evt.button;
}
this._previousPosition = {
x: evt.clientX,
y: evt.clientY,
};
if (!noPreventDefault) {
evt.preventDefault();
if (element) {
element.focus();
}
}
// This is required to move while pointer button is down
if (engine.isPointerLock && this._onMouseMove) {
this._onMouseMove(p.event);
}
}
else if (p.type === PointerEventTypes.POINTERUP) {
// If input is touch with a different touch id OR if input is mouse with a different button, return
if ((isTouch && this._activePointerId !== evt.pointerId) || (!isTouch && this._currentActiveButton !== evt.button)) {
return;
}
try {
srcElement?.releasePointerCapture(evt.pointerId);
}
catch (e) {
//Nothing to do with the error.
}
this._currentActiveButton = -1;
this._previousPosition = null;
if (!noPreventDefault) {
evt.preventDefault();
}
this._activePointerId = -1;
}
else if (p.type === PointerEventTypes.POINTERMOVE && (this._activePointerId === evt.pointerId || !isTouch)) {
if (engine.isPointerLock && this._onMouseMove) {
this._onMouseMove(p.event);
}
else if (this._previousPosition) {
const handednessMultiplier = this.camera._calculateHandednessMultiplier();
const offsetX = (evt.clientX - this._previousPosition.x) * handednessMultiplier;
const offsetY = (evt.clientY - this._previousPosition.y) * handednessMultiplier;
if (this._allowCameraRotation) {
this._applyPointerRotation(offsetX, offsetY);
}
this.onPointerMovedObservable.notifyObservers({ offsetX: offsetX, offsetY: offsetY });
this._previousPosition = {
x: evt.clientX,
y: evt.clientY,
};
if (!noPreventDefault) {
evt.preventDefault();
}
}
}
};
}
this._onMouseMove = (evt) => {
if (!engine.isPointerLock) {
return;
}
const handednessMultiplier = this.camera._calculateHandednessMultiplier();
this._applyPointerRotation(evt.movementX * handednessMultiplier, evt.movementY * handednessMultiplier);
this._previousPosition = null;
if (!noPreventDefault) {
evt.preventDefault();
}
};
this._observer = this.camera
.getScene()
._inputManager._addCameraPointerObserver(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
if (element) {
this._contextMenuBind = (evt) => this.onContextMenu(evt);
element.addEventListener("contextmenu", this._contextMenuBind, false); // TODO: We need to figure out how to handle this for Native
}
}
/**
* Called on JS contextmenu event.
* Override this method to provide functionality.
* @param evt the context menu event
*/
onContextMenu(evt) {
evt.preventDefault();
}
/**
* Detach the current controls from the specified dom element.
*/
detachControl() {
if (this._observer) {
this.camera.getScene()._inputManager._removeCameraPointerObserver(this._observer);
if (this._contextMenuBind) {
const engine = this.camera.getEngine();
const element = engine.getInputElement();
if (element) {
element.removeEventListener("contextmenu", this._contextMenuBind);
}
}
if (this.onPointerMovedObservable) {
this.onPointerMovedObservable.clear();
}
this._observer = null;
this._onMouseMove = null;
this._previousPosition = null;
}
this._activePointerId = -1;
this._currentActiveButton = -1;
}
/**
* Gets the class name of the current input.
* @returns the class name
*/
getClassName() {
return "FreeCameraMouseInput";
}
/**
* Get the friendly name associated with the input class.
* @returns the input friendly name
*/
getSimpleName() {
return "mouse";
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_buttons_decorators = [serialize()];
_angularSensibility_decorators = [serialize()];
__esDecorate(null, null, _buttons_decorators, { kind: "field", name: "buttons", static: false, private: false, access: { has: obj => "buttons" in obj, get: obj => obj.buttons, set: (obj, value) => { obj.buttons = value; } }, metadata: _metadata }, _buttons_initializers, _buttons_extraInitializers);
__esDecorate(null, null, _angularSensibility_decorators, { kind: "field", name: "angularSensibility", static: false, private: false, access: { has: obj => "angularSensibility" in obj, get: obj => obj.angularSensibility, set: (obj, value) => { obj.angularSensibility = value; } }, metadata: _metadata }, _angularSensibility_initializers, _angularSensibility_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
CameraInputTypes["FreeCameraMouseInput"] = FreeCameraMouseInput;
/**
* Base class for mouse wheel input..
* See FollowCameraMouseWheelInput in src/Cameras/Inputs/freeCameraMouseWheelInput.ts
* for example usage.
*/
let BaseCameraMouseWheelInput = (() => {
var _a;
let _wheelPrecisionX_decorators;
let _wheelPrecisionX_initializers = [];
let _wheelPrecisionX_extraInitializers = [];
let _wheelPrecisionY_decorators;
let _wheelPrecisionY_initializers = [];
let _wheelPrecisionY_extraInitializers = [];
let _wheelPrecisionZ_decorators;
let _wheelPrecisionZ_initializers = [];
let _wheelPrecisionZ_extraInitializers = [];
return _a = class BaseCameraMouseWheelInput {
constructor() {
/**
* How fast is the camera moves in relation to X axis mouseWheel events.
* Use negative value to reverse direction.
*/
this.wheelPrecisionX = __runInitializers(this, _wheelPrecisionX_initializers, 3.0);
/**
* How fast is the camera moves in relation to Y axis mouseWheel events.
* Use negative value to reverse direction.
*/
this.wheelPrecisionY = (__runInitializers(this, _wheelPrecisionX_extraInitializers), __runInitializers(this, _wheelPrecisionY_initializers, 3.0));
/**
* How fast is the camera moves in relation to Z axis mouseWheel events.
* Use negative value to reverse direction.
*/
this.wheelPrecisionZ = (__runInitializers(this, _wheelPrecisionY_extraInitializers), __runInitializers(this, _wheelPrecisionZ_initializers, 3.0));
/**
* Observable for when a mouse wheel move event occurs.
*/
this.onChangedObservable = (__runInitializers(this, _wheelPrecisionZ_extraInitializers), new Observable());
/**
* Incremental value of multiple mouse wheel movements of the X axis.
* Should be zero-ed when read.
*/
this._wheelDeltaX = 0;
/**
* Incremental value of multiple mouse wheel movements of the Y axis.
* Should be zero-ed when read.
*/
this._wheelDeltaY = 0;
/**
* Incremental value of multiple mouse wheel movements of the Z axis.
* Should be zero-ed when read.
*/
this._wheelDeltaZ = 0;
/**
* Firefox uses a different scheme to report scroll distances to other
* browsers. Rather than use complicated methods to calculate the exact
* multiple we need to apply, let's just cheat and use a constant.
* https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode
* https://stackoverflow.com/questions/20110224/what-is-the-height-of-a-line-in-a-wheel-event-deltamode-dom-delta-line
*/
this._ffMultiplier = 12;
/**
* Different event attributes for wheel data fall into a few set ranges.
* Some relevant but dated date here:
* https://stackoverflow.com/questions/5527601/normalizing-mousewheel-speed-across-browsers
*/
this._normalize = 120;
}
/**
* Attach the input controls to a specific dom element to get the input from.
* @param noPreventDefault Defines whether event caught by the controls
* should call preventdefault().
* (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
*/
attachControl(noPreventDefault) {
noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
this._wheel = (pointer) => {
// sanity check - this should be a PointerWheel event.
if (pointer.type !== PointerEventTypes.POINTERWHEEL) {
return;
}
const event = pointer.event;
const platformScale = event.deltaMode === EventConstants.DOM_DELTA_LINE ? this._ffMultiplier : 1; // If this happens to be set to DOM_DELTA_LINE, adjust accordingly
this._wheelDeltaX += (this.wheelPrecisionX * platformScale * event.deltaX) / this._normalize;
this._wheelDeltaY -= (this.wheelPrecisionY * platformScale * event.deltaY) / this._normalize;
this._wheelDeltaZ += (this.wheelPrecisionZ * platformScale * event.deltaZ) / this._normalize;
if (event.preventDefault) {
if (!noPreventDefault) {
event.preventDefault();
}
}
};
this._observer = this.camera.getScene()._inputManager._addCameraPointerObserver(this._wheel, PointerEventTypes.POINTERWHEEL);
}
/**
* Detach the current controls from the specified dom element.
*/
detachControl() {
if (this._observer) {
this.camera.getScene()._inputManager._removeCameraPointerObserver(this._observer);
this._observer = null;
this._wheel = null;
}
if (this.onChangedObservable) {
this.onChangedObservable.clear();
}
}
/**
* Called for each rendered frame.
*/
checkInputs() {
this.onChangedObservable.notifyObservers({
wheelDeltaX: this._wheelDeltaX,
wheelDeltaY: this._wheelDeltaY,
wheelDeltaZ: this._wheelDeltaZ,
});
// Clear deltas.
this._wheelDeltaX = 0;
this._wheelDeltaY = 0;
this._wheelDeltaZ = 0;
}
/**
* Gets the class name of the current input.
* @returns the class name
*/
getClassName() {
return "BaseCameraMouseWheelInput";
}
/**
* Get the friendly name associated with the input class.
* @returns the input friendly name
*/
getSimpleName() {
return "mousewheel";
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_wheelPrecisionX_decorators = [serialize()];
_wheelPrecisionY_decorators = [serialize()];
_wheelPrecisionZ_decorators = [serialize()];
__esDecorate(null, null, _wheelPrecisionX_decorators, { kind: "field", name: "wheelPrecisionX", static: false, private: false, access: { has: obj => "wheelPrecisionX" in obj, get: obj => obj.wheelPrecisionX, set: (obj, value) => { obj.wheelPrecisionX = value; } }, metadata: _metadata }, _wheelPrecisionX_initializers, _wheelPrecisionX_extraInitializers);
__esDecorate(null, null, _wheelPrecisionY_decorators, { kind: "field", name: "wheelPrecisionY", static: false, private: false, access: { has: obj => "wheelPrecisionY" in obj, get: obj => obj.wheelPrecisionY, set: (obj, value) => { obj.wheelPrecisionY = value; } }, metadata: _metadata }, _wheelPrecisionY_initializers, _wheelPrecisionY_extraInitializers);
__esDecorate(null, null, _wheelPrecisionZ_decorators, { kind: "field", name: "wheelPrecisionZ", static: false, private: false, access: { has: obj => "wheelPrecisionZ" in obj, get: obj => obj.wheelPrecisionZ, set: (obj, value) => { obj.wheelPrecisionZ = value; } }, metadata: _metadata }, _wheelPrecisionZ_initializers, _wheelPrecisionZ_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
// eslint-disable-next-line @typescript-eslint/naming-convention
var _CameraProperty;
(function (_CameraProperty) {
_CameraProperty[_CameraProperty["MoveRelative"] = 0] = "MoveRelative";
_CameraProperty[_CameraProperty["RotateRelative"] = 1] = "RotateRelative";
_CameraProperty[_CameraProperty["MoveScene"] = 2] = "MoveScene";
})(_CameraProperty || (_CameraProperty = {}));
/**
* Manage the mouse wheel inputs to control a free camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
*/
let FreeCameraMouseWheelInput = (() => {
var _a;
let _classSuper = BaseCameraMouseWheelInput;
let _instanceExtraInitializers = [];
let _set_wheelXMoveRelative_decorators;
let _set_wheelYMoveRelative_decorators;
let _set_wheelZMoveRelative_decorators;
let _set_wheelXRotateRelative_decorators;
let _set_wheelYRotateRelative_decorators;
let _set_wheelZRotateRelative_decorators;
let _set_wheelXMoveScene_decorators;
let _set_wheelYMoveScene_decorators;
let _set_wheelZMoveScene_decorators;
return _a = class FreeCameraMouseWheelInput extends _classSuper {
constructor() {
super(...arguments);
/**
* Defines the camera the input is attached to.
*/
this.camera = __runInitializers(this, _instanceExtraInitializers);
this._moveRelative = Vector3.Zero();
this._rotateRelative = Vector3.Zero();
this._moveScene = Vector3.Zero();
/**
* These are set to the desired default behaviour.
*/
this._wheelXAction = _CameraProperty.MoveRelative;
this._wheelXActionCoordinate = 0 /* Coordinate.X */;
this._wheelYAction = _CameraProperty.MoveRelative;
this._wheelYActionCoordinate = 2 /* Coordinate.Z */;
this._wheelZAction = null;
this._wheelZActionCoordinate = null;
}
/**
* Gets the class name of the current input.
* @returns the class name
*/
getClassName() {
return "FreeCameraMouseWheelInput";
}
/**
* Set which movement axis (relative to camera's orientation) the mouse
* wheel's X axis controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelXMoveRelative(axis) {
if (axis === null && this._wheelXAction !== _CameraProperty.MoveRelative) {
// Attempting to clear different _wheelXAction.
return;
}
this._wheelXAction = _CameraProperty.MoveRelative;
this._wheelXActionCoordinate = axis;
}
/**
* Get the configured movement axis (relative to camera's orientation) the
* mouse wheel's X axis controls.
* @returns The configured axis or null if none.
*/
get wheelXMoveRelative() {
if (this._wheelXAction !== _CameraProperty.MoveRelative) {
return null;
}
return this._wheelXActionCoordinate;
}
/**
* Set which movement axis (relative to camera's orientation) the mouse
* wheel's Y axis controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelYMoveRelative(axis) {
if (axis === null && this._wheelYAction !== _CameraProperty.MoveRelative) {
// Attempting to clear different _wheelYAction.
return;
}
this._wheelYAction = _CameraProperty.MoveRelative;
this._wheelYActionCoordinate = axis;
}
/**
* Get the configured movement axis (relative to camera's orientation) the
* mouse wheel's Y axis controls.
* @returns The configured axis or null if none.
*/
get wheelYMoveRelative() {
if (this._wheelYAction !== _CameraProperty.MoveRelative) {
return null;
}
return this._wheelYActionCoordinate;
}
/**
* Set which movement axis (relative to camera's orientation) the mouse
* wheel's Z axis controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelZMoveRelative(axis) {
if (axis === null && this._wheelZAction !== _CameraProperty.MoveRelative) {
// Attempting to clear different _wheelZAction.
return;
}
this._wheelZAction = _CameraProperty.MoveRelative;
this._wheelZActionCoordinate = axis;
}
/**
* Get the configured movement axis (relative to camera's orientation) the
* mouse wheel's Z axis controls.
* @returns The configured axis or null if none.
*/
get wheelZMoveRelative() {
if (this._wheelZAction !== _CameraProperty.MoveRelative) {
return null;
}
return this._wheelZActionCoordinate;
}
/**
* Set which rotation axis (relative to camera's orientation) the mouse
* wheel's X axis controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelXRotateRelative(axis) {
if (axis === null && this._wheelXAction !== _CameraProperty.RotateRelative) {
// Attempting to clear different _wheelXAction.
return;
}
this._wheelXAction = _CameraProperty.RotateRelative;
this._wheelXActionCoordinate = axis;
}
/**
* Get the configured rotation axis (relative to camera's orientation) the
* mouse wheel's X axis controls.
* @returns The configured axis or null if none.
*/
get wheelXRotateRelative() {
if (this._wheelXAction !== _CameraProperty.RotateRelative) {
return null;
}
return this._wheelXActionCoordinate;
}
/**
* Set which rotation axis (relative to camera's orientation) the mouse
* wheel's Y axis controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelYRotateRelative(axis) {
if (axis === null && this._wheelYAction !== _CameraProperty.RotateRelative) {
// Attempting to clear different _wheelYAction.
return;
}
this._wheelYAction = _CameraProperty.RotateRelative;
this._wheelYActionCoordinate = axis;
}
/**
* Get the configured rotation axis (relative to camera's orientation) the
* mouse wheel's Y axis controls.
* @returns The configured axis or null if none.
*/
get wheelYRotateRelative() {
if (this._wheelYAction !== _CameraProperty.RotateRelative) {
return null;
}
return this._wheelYActionCoordinate;
}
/**
* Set which rotation axis (relative to camera's orientation) the mouse
* wheel's Z axis controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelZRotateRelative(axis) {
if (axis === null && this._wheelZAction !== _CameraProperty.RotateRelative) {
// Attempting to clear different _wheelZAction.
return;
}
this._wheelZAction = _CameraProperty.RotateRelative;
this._wheelZActionCoordinate = axis;
}
/**
* Get the configured rotation axis (relative to camera's orientation) the
* mouse wheel's Z axis controls.
* @returns The configured axis or null if none.
*/
get wheelZRotateRelative() {
if (this._wheelZAction !== _CameraProperty.RotateRelative) {
return null;
}
return this._wheelZActionCoordinate;
}
/**
* Set which movement axis (relative to the scene) the mouse wheel's X axis
* controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelXMoveScene(axis) {
if (axis === null && this._wheelXAction !== _CameraProperty.MoveScene) {
// Attempting to clear different _wheelXAction.
return;
}
this._wheelXAction = _CameraProperty.MoveScene;
this._wheelXActionCoordinate = axis;
}
/**
* Get the configured movement axis (relative to the scene) the mouse wheel's
* X axis controls.
* @returns The configured axis or null if none.
*/
get wheelXMoveScene() {
if (this._wheelXAction !== _CameraProperty.MoveScene) {
return null;
}
return this._wheelXActionCoordinate;
}
/**
* Set which movement axis (relative to the scene) the mouse wheel's Y axis
* controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelYMoveScene(axis) {
if (axis === null && this._wheelYAction !== _CameraProperty.MoveScene) {
// Attempting to clear different _wheelYAction.
return;
}
this._wheelYAction = _CameraProperty.MoveScene;
this._wheelYActionCoordinate = axis;
}
/**
* Get the configured movement axis (relative to the scene) the mouse wheel's
* Y axis controls.
* @returns The configured axis or null if none.
*/
get wheelYMoveScene() {
if (this._wheelYAction !== _CameraProperty.MoveScene) {
return null;
}
return this._wheelYActionCoordinate;
}
/**
* Set which movement axis (relative to the scene) the mouse wheel's Z axis
* controls.
* @param axis The axis to be moved. Set null to clear.
*/
set wheelZMoveScene(axis) {
if (axis === null && this._wheelZAction !== _CameraProperty.MoveScene) {
// Attempting to clear different _wheelZAction.
return;
}
this._wheelZAction = _CameraProperty.MoveScene;
this._wheelZActionCoordinate = axis;
}
/**
* Get the configured movement axis (relative to the scene) the mouse wheel's
* Z axis controls.
* @returns The configured axis or null if none.
*/
get wheelZMoveScene() {
if (this._wheelZAction !== _CameraProperty.MoveScene) {
return null;
}
return this._wheelZActionCoordinate;
}
/**
* Called for each rendered frame.
*/
checkInputs() {
if (this._wheelDeltaX === 0 && this._wheelDeltaY === 0 && this._wheelDeltaZ == 0) {
return;
}
// Clear the camera properties that we might be updating.
this._moveRelative.setAll(0);
this._rotateRelative.setAll(0);
this._moveScene.setAll(0);
// Set the camera properties that are to be updated.
this._updateCamera();
if (this.camera.getScene().useRightHandedSystem) {
// TODO: Does this need done for worldUpdate too?
this._moveRelative.z *= -1;
}
// Convert updates relative to camera to world position update.
const cameraTransformMatrix = Matrix.Zero();
this.camera.getViewMatrix().invertToRef(cameraTransformMatrix);
const transformedDirection = Vector3.Zero();
Vector3.TransformNormalToRef(this._moveRelative, cameraTransformMatrix, transformedDirection);
// Apply updates to camera position.
this.camera.cameraRotation.x += this._rotateRelative.x / 200;
this.camera.cameraRotation.y += this._rotateRelative.y / 200;
this.camera.cameraDirection.addInPlace(transformedDirection);
this.camera.cameraDirection.addInPlace(this._moveScene);
// Call the base class implementation to handle observers and do cleanup.
super.checkInputs();
}
/**
* Update the camera according to any configured properties for the 3
* mouse-wheel axis.
*/
_updateCamera() {
// Do the camera updates for each of the 3 touch-wheel axis.
this._updateCameraProperty(this._wheelDeltaX, this._wheelXAction, this._wheelXActionCoordinate);
this._updateCameraProperty(this._wheelDeltaY, this._wheelYAction, this._wheelYActionCoordinate);
this._updateCameraProperty(this._wheelDeltaZ, this._wheelZAction, this._wheelZActionCoordinate);
}
/**
* Update one property of the camera.
* @param value
* @param cameraProperty
* @param coordinate
*/
_updateCameraProperty(
/* Mouse-wheel delta. */
value,
/* Camera property to be changed. */
cameraProperty,
/* Axis of Camera property to be changed. */
coordinate) {
if (value === 0) {
// Mouse wheel has not moved.
return;
}
if (cameraProperty === null || coordinate === null) {
// Mouse wheel axis not configured.
return;
}
let action = null;
switch (cameraProperty) {
case _CameraProperty.MoveRelative:
action = this._moveRelative;
break;
case _CameraProperty.RotateRelative:
action = this._rotateRelative;
break;
case _CameraProperty.MoveScene:
action = this._moveScene;
break;
}
switch (coordinate) {
case 0 /* Coordinate.X */:
action.set(value, 0, 0);
break;
case 1 /* Coordinate.Y */:
action.set(0, value, 0);
break;
case 2 /* Coordinate.Z */:
action.set(0, 0, value);
break;
}
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_set_wheelXMoveRelative_decorators = [serialize()];
_set_wheelYMoveRelative_decorators = [serialize()];
_set_wheelZMoveRelative_decorators = [serialize()];
_set_wheelXRotateRelative_decorators = [serialize()];
_set_wheelYRotateRelative_decorators = [serialize()];
_set_wheelZRotateRelative_decorators = [serialize()];
_set_wheelXMoveScene_decorators = [serialize()];
_set_wheelYMoveScene_decorators = [serialize()];
_set_wheelZMoveScene_decorators = [serialize()];
__esDecorate(_a, null, _set_wheelXMoveRelative_decorators, { kind: "setter", name: "wheelXMoveRelative", static: false, private: false, access: { has: obj => "wheelXMoveRelative" in obj, set: (obj, value) => { obj.wheelXMoveRelative = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelYMoveRelative_decorators, { kind: "setter", name: "wheelYMoveRelative", static: false, private: false, access: { has: obj => "wheelYMoveRelative" in obj, set: (obj, value) => { obj.wheelYMoveRelative = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelZMoveRelative_decorators, { kind: "setter", name: "wheelZMoveRelative", static: false, private: false, access: { has: obj => "wheelZMoveRelative" in obj, set: (obj, value) => { obj.wheelZMoveRelative = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelXRotateRelative_decorators, { kind: "setter", name: "wheelXRotateRelative", static: false, private: false, access: { has: obj => "wheelXRotateRelative" in obj, set: (obj, value) => { obj.wheelXRotateRelative = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelYRotateRelative_decorators, { kind: "setter", name: "wheelYRotateRelative", static: false, private: false, access: { has: obj => "wheelYRotateRelative" in obj, set: (obj, value) => { obj.wheelYRotateRelative = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelZRotateRelative_decorators, { kind: "setter", name: "wheelZRotateRelative", static: false, private: false, access: { has: obj => "wheelZRotateRelative" in obj, set: (obj, value) => { obj.wheelZRotateRelative = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelXMoveScene_decorators, { kind: "setter", name: "wheelXMoveScene", static: false, private: false, access: { has: obj => "wheelXMoveScene" in obj, set: (obj, value) => { obj.wheelXMoveScene = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelYMoveScene_decorators, { kind: "setter", name: "wheelYMoveScene", static: false, private: false, access: { has: obj => "wheelYMoveScene" in obj, set: (obj, value) => { obj.wheelYMoveScene = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _set_wheelZMoveScene_decorators, { kind: "setter", name: "wheelZMoveScene", static: false, private: false, access: { has: obj => "wheelZMoveScene" in obj, set: (obj, value) => { obj.wheelZMoveScene = value; } }, metadata: _metadata }, null, _instanceExtraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
CameraInputTypes["FreeCameraMouseWheelInput"] = FreeCameraMouseWheelInput;
/**
* Manage the touch inputs to control the movement of a free camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
*/
let FreeCameraTouchInput = (() => {
var _a;
let _touchAngularSensibility_decorators;
let _touchAngularSensibility_initializers = [];
let _touchAngularSensibility_extraInitializers = [];
let _touchMoveSensibility_decorators;
let _touchMoveSensibility_initializers = [];
let _touchMoveSensibility_extraInitializers = [];
return _a = class FreeCameraTouchInput {
/**
* Manage the touch inputs to control the movement of a free camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
* @param allowMouse Defines if mouse events can be treated as touch events
*/
constructor(
/**
* [false] Define if mouse events can be treated as touch events
*/
allowMouse = false) {
this.allowMouse = allowMouse;
/**
* Defines the touch sensibility for rotation.
* The lower the faster.
*/
this.touchAngularSensibility = __runInitializers(this, _touchAngularSensibility_initializers, 200000.0);
/**
* Defines the touch sensibility for move.
* The lower the faster.
*/
this.touchMoveSensibility = (__runInitializers(this, _touchAngularSensibility_extraInitializers), __runInitializers(this, _touchMoveSensibility_initializers, 250.0));
/**
* Swap touch actions so that one touch is used for rotation and multiple for movement
*/
this.singleFingerRotate = (__runInitializers(this, _touchMoveSensibility_extraInitializers), false);
this._offsetX = null;
this._offsetY = null;
this._pointerPressed = new Array();
this._isSafari = Tools.IsSafari();
}
/**
* Attach the input controls to a specific dom element to get the input from.
* @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
*/
attachControl(noPreventDefault) {
noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
let previousPosition = null;
if (this._pointerInput === undefined) {
this._onLostFocus = () => {
this._offsetX = null;
this._offsetY = null;
};
this._pointerInput = (p) => {
const evt = p.event;
const isMouseEvent = evt.pointerType === "mouse" || (this._isSafari && typeof evt.pointerType === "undefined");
if (!this.allowMouse && isMouseEvent) {
return;
}
if (p.type === PointerEventTypes.POINTERDOWN) {
if (!noPreventDefault) {
evt.preventDefault();
}
this._pointerPressed.push(evt.pointerId);
if (this._pointerPressed.length !== 1) {
return;
}
previousPosition = {
x: evt.clientX,
y: evt.clientY,
};
}
else if (p.type === PointerEventTypes.POINTERUP) {
if (!noPreventDefault) {
evt.preventDefault();
}
const index = this._pointerPressed.indexOf(evt.pointerId);
if (index === -1) {
return;
}
this._pointerPressed.splice(index, 1);
if (index != 0) {
return;
}
previousPosition = null;
this._offsetX = null;
this._offsetY = null;
}
else if (p.type === PointerEventTypes.POINTERMOVE) {
if (!noPreventDefault) {
evt.preventDefault();
}
if (!previousPosition) {
return;
}
const index = this._pointerPressed.indexOf(evt.pointerId);
if (index != 0) {
return;
}
this._offsetX = evt.clientX - previousPosition.x;
this._offsetY = -(evt.clientY - previousPosition.y);
}
};
}
this._observer = this.camera
.getScene()
._inputManager._addCameraPointerObserver(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
if (this._onLostFocus) {
const engine = this.camera.getEngine();
const element = engine.getInputElement();
if (element) {
element.addEventListener("blur", this._onLostFocus);
}
}
}
/**
* Detach the current controls from the specified dom element.
*/
detachControl() {
if (this._pointerInput) {
if (this._observer) {
this.camera.getScene()._inputManager._removeCameraPointerObserver(this._observer);
this._observer = null;
}
if (this._onLostFocus) {
const engine = this.camera.getEngine();
const element = engine.getInputElement();
if (element) {
element.removeEventListener("blur", this._onLostFocus);
}
this._onLostFocus = null;
}
this._pointerPressed.length = 0;
this._offsetX = null;
this._offsetY = null;
}
}
/**
* Update the current camera state depending on the inputs that have been used this frame.
* This is a dynamically created lambda to avoid the performance penalty of looping for inputs in the render loop.
*/
checkInputs() {
if (this._offsetX === null || this._offsetY === null) {
return;
}
if (this._offsetX === 0 && this._offsetY === 0) {
return;
}
const camera = this.camera;
// Touch look (yaw/pitch) is gated on touch→rotate and the forward drag-move on
// touch→translate. Removing an entry disables that touch behavior. An entry's optional
// `sensitivity` replaces the legacy 1/touchAngularSensibility (rotate) or
// 1/touchMoveSensibility (translate) scale factor.
const input = camera.movement.input;
const rotateEntry = input.getEntry("touch", "rotate");
const translateEntry = input.getEntry("touch", "translate");
const handednessMultiplier = camera._calculateHandednessMultiplier();
const rotateCamera = (this.singleFingerRotate && this._pointerPressed.length === 1) || (!this.singleFingerRotate && this._pointerPressed.length > 1);
if (rotateEntry) {
const rotateSensitivity = rotateEntry.sensitivity ?? 1 / this.touchAngularSensibility;
camera.cameraRotation.y = this._offsetX * handednessMultiplier * rotateSensitivity;
if (rotateCamera) {
camera.cameraRotation.x = -(this._offsetY * handednessMultiplier) * rotateSensitivity;
}
}
if (!rotateCamera && translateEntry) {
const speed = camera._computeLocalCameraSpeed();
const moveSensitivity = translateEntry.sensitivity ?? (this.touchMoveSensibility !== 0 ? 1 / this.touchMoveSensibility : 0);
const direction = TmpVectors.Vector3[0];
direction.copyFromFloats(0, 0, speed * this._offsetY * moveSensitivity);
Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
Vector3.TransformCoordinatesToRef(direction, camera._cameraRotationMatrix, direction);
camera.cameraDirection.addInPlace(direction);
}
}
/**
* Gets the class name of the current input.
* @returns the class name
*/
getClassName() {
return "FreeCameraTouchInput";
}
/**
* Get the friendly name associated with the input class.
* @returns the input friendly name
*/
getSimpleName() {
return "touch";
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_touchAngularSensibility_decorators = [serialize()];
_touchMoveSensibility_decorators = [serialize()];
__esDecorate(null, null, _touchAngularSensibility_decorators, { kind: "field", name: "touchAngularSensibility", static: false, private: false, access: { has: obj => "touchAngularSensibility" in obj, get: obj => obj.touchAngularSensibility, set: (obj, value) => { obj.touchAngularSensibility = value; } }, metadata: _metadata }, _touchAngularSensibility_initializers, _touchAngularSensibility_extraInitializers);
__esDecorate(null, null, _touchMoveSensibility_decorators, { kind: "field", name: "touchMoveSensibility", static: false, private: false, access: { has: obj => "touchMoveSensibility" in obj, get: obj => obj.touchMoveSensibility, set: (obj, value) => { obj.touchMoveSensibility = value; } }, metadata: _metadata }, _touchMoveSensibility_initializers, _touchMoveSensibility_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
CameraInputTypes["FreeCameraTouchInput"] = FreeCameraTouchInput;
/** This file must only contain pure code and pure imports */
/**
* Default Inputs manager for the FreeCamera.
* It groups all the default supported inputs for ease of use.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
*/
class FreeCameraInputsManager extends CameraInputsManager {
/**
* Instantiates a new FreeCameraInputsManager.
* @param camera Defines the camera the inputs belong to
*/
constructor(camera) {
super(camera);
/**
* @internal
*/
this._mouseInput = null;
/**
* @internal
*/
this._mouseWheelInput = null;
}
/**
* Add keyboard input support to the input manager.
* @returns the current input manager
*/
addKeyboard() {
this.add(new FreeCameraKeyboardMoveInput());
return this;
}
/**
* Add mouse input support to the input manager.
* @param touchEnabled if the FreeCameraMouseInput should support touch (default: true)
* @returns the current input manager
*/
addMouse(touchEnabled = true) {
if (!this._mouseInput) {
this._mouseInput = new FreeCameraMouseInput(touchEnabled);
this.add(this._mouseInput);
}
return this;
}
/**
* Removes the mouse input support from the manager
* @returns the current input manager
*/
removeMouse() {
if (this._mouseInput) {
this.remove(this._mouseInput);
}
return this;
}
/**
* Add mouse wheel input support to the input manager.
* @returns the current input manager
*/
addMouseWheel() {
if (!this._mouseWheelInput) {
this._mouseWheelInput = new FreeCameraMouseWheelInput();
this.add(this._mouseWheelInput);
}
return this;
}
/**
* Removes the mouse wheel input support from the manager
* @returns the current input manager
*/
removeMouseWheel() {
if (this._mouseWheelInput) {
this.remove(this._mouseWheelInput);
}
return this;
}
/**
* Add touch input support to the input manager.
* @returns the current input manager
*/
addTouch() {
this.add(new FreeCameraTouchInput());
return this;
}
/**
* Remove all attached input methods from a camera
*/
clear() {
super.clear();
this._mouseInput = null;
}
}
FreeCameraInputsManager.prototype.addDeviceOrientation ??= _MissingSideEffect("FreeCameraInputsManager", "addDeviceOrientation");
FreeCameraInputsManager.prototype.addVirtualJoystick ??= _MissingSideEffect("FreeCameraInputsManager", "addVirtualJoystick");
FreeCameraInputsManager.prototype.addGamepad ??= _MissingSideEffect("FreeCameraInputsManager", "addGamepad");
// #endregion GENERATED_SIDE_EFFECT_STUBS
/** This file must only contain pure code and pure imports */
/**
* This represents a free type of camera. It can be useful in First Person Shooter game for instance.
* Please consider using the new UniversalCamera instead as it adds more functionality like the gamepad.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_introduction#universal-camera
*/
let FreeCamera = (() => {
var _a;
let _classSuper = TargetCamera;
let _ellipsoid_decorators;
let _ellipsoid_initializers = [];
let _ellipsoid_extraInitializers = [];
let _ellipsoidOffset_decorators;
let _ellipsoidOffset_initializers = [];
let _ellipsoidOffset_extraInitializers = [];
let _checkCollisions_decorators;
let _checkCollisions_initializers = [];
let _checkCollisions_extraInitializers = [];
let _applyGravity_decorators;
let _applyGravity_initializers = [];
let _applyGravity_extraInitializers = [];
return _a = class FreeCamera extends _classSuper {
/**
* Gets the input sensibility for a mouse input. (default is 2000.0)
* Higher values reduce sensitivity.
*/
get angularSensibility() {
const mouse = this.inputs.attached["mouse"];
if (mouse) {
return mouse.angularSensibility;
}
return 0;
}
/**
* Sets the input sensibility for a mouse input. (default is 2000.0)
* Higher values reduce sensitivity.
*/
set angularSensibility(value) {
const mouse = this.inputs.attached["mouse"];
if (mouse) {
mouse.angularSensibility = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the forward move of the camera.
*/
get keysUp() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysUp;
}
return [];
}
set keysUp(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysUp = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the upward move of the camera.
*/
get keysUpward() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysUpward;
}
return [];
}
set keysUpward(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysUpward = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the backward move of the camera.
*/
get keysDown() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysDown;
}
return [];
}
set keysDown(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysDown = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the downward move of the camera.
*/
get keysDownward() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysDownward;
}
return [];
}
set keysDownward(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysDownward = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the left strafe move of the camera.
*/
get keysLeft() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysLeft;
}
return [];
}
set keysLeft(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysLeft = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the right strafe move of the camera.
*/
get keysRight() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysRight;
}
return [];
}
set keysRight(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysRight = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the left rotation move of the camera.
*/
get keysRotateLeft() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysRotateLeft;
}
return [];
}
set keysRotateLeft(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysRotateLeft = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the right rotation move of the camera.
*/
get keysRotateRight() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysRotateRight;
}
return [];
}
set keysRotateRight(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysRotateRight = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the up rotation move of the camera.
*/
get keysRotateUp() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysRotateUp;
}
return [];
}
set keysRotateUp(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysRotateUp = value;
}
}
/**
* Gets or Set the list of keyboard keys used to control the down rotation move of the camera.
*/
get keysRotateDown() {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
return keyboard.keysRotateDown;
}
return [];
}
set keysRotateDown(value) {
const keyboard = this.inputs.attached["keyboard"];
if (keyboard) {
keyboard.keysRotateDown = value;
}
}
/**
* Instantiates a Free Camera.
* This represents a free type of camera. It can be useful in First Person Shooter game for instance.
* Please consider using the new UniversalCamera instead as it adds more functionality like touch to this camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_introduction#universal-camera
* @param name Define the name of the camera in the scene
* @param position Define the start position of the camera in the scene
* @param scene Define the scene the camera belongs to
* @param setActiveOnSceneIfNoneActive Defines whether the camera should be marked as active if not other active cameras have been defined
*/
constructor(name, position, scene, setActiveOnSceneIfNoneActive = true) {
super(name, position, scene, setActiveOnSceneIfNoneActive);
/**
* Define the collision ellipsoid of the camera.
* This is helpful to simulate a camera body like the player body around the camera
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_collisions#arcrotatecamera
*/
this.ellipsoid = __runInitializers(this, _ellipsoid_initializers, new Vector3(0.5, 1, 0.5));
/**
* Define an offset for the position of the ellipsoid around the camera.
* This can be helpful to determine the center of the body near the gravity center of the body
* instead of its head.
*/
this.ellipsoidOffset = (__runInitializers(this, _ellipsoid_extraInitializers), __runInitializers(this, _ellipsoidOffset_initializers, new Vector3(0, 0, 0)));
/**
* Enable or disable collisions of the camera with the rest of the scene objects.
*/
this.checkCollisions = (__runInitializers(this, _ellipsoidOffset_extraInitializers), __runInitializers(this, _checkCollisions_initializers, false));
/**
* Enable or disable gravity on the camera.
*/
this.applyGravity = (__runInitializers(this, _checkCollisions_extraInitializers), __runInitializers(this, _applyGravity_initializers, false));
/**
* Define the input manager associated to the camera.
*/
this.inputs = __runInitializers(this, _applyGravity_extraInitializers);
this._needMoveForGravity = false;
this._oldPosition = Vector3.Zero();
this._diffPosition = Vector3.Zero();
this._newPosition = Vector3.Zero();
// Collisions
this._collisionMask = -1;
this._onCollisionPositionChange = (collisionId, newPosition, collidedMesh = null) => {
this._newPosition.copyFrom(newPosition);
this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
if (this._diffPosition.length() > AbstractEngine.CollisionsEpsilon) {
this.position.addToRef(this._diffPosition, this._deferredPositionUpdate);
if (!this._deferOnly) {
this.position.copyFrom(this._deferredPositionUpdate);
}
else {
this._deferredUpdated = true;
}
// call onCollide, if defined. Note that in case of deferred update, the actual position change might happen in the next frame.
if (this.onCollide && collidedMesh) {
this.onCollide(collidedMesh);
}
}
};
this.inputs = new FreeCameraInputsManager(this);
this.inputs.addKeyboard().addMouse();
}
/**
* Attached controls to the current camera.
* @param ignored defines an ignored parameter kept for backward compatibility.
* @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
*/
attachControl(ignored, noPreventDefault) {
noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
this.inputs.attachElement(noPreventDefault);
}
/**
* Detach the current controls from the specified dom element.
*/
detachControl() {
this.inputs.detachElement();
this.cameraDirection = new Vector3(0, 0, 0);
this.cameraRotation = new Vector2(0, 0);
}
/**
* Define a collision mask to limit the list of object the camera can collide with
*/
get collisionMask() {
return this._collisionMask;
}
set collisionMask(mask) {
this._collisionMask = !isNaN(mask) ? mask : -1;
}
/**
* @internal
*/
_collideWithWorld(displacement) {
let globalPosition;
if (this.parent) {
globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
}
else {
globalPosition = this.position;
}
globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
this._oldPosition.addInPlace(this.ellipsoidOffset);
const coordinator = this.getScene().collisionCoordinator;
if (!this._collider) {
this._collider = coordinator.createCollider();
}
this._collider._radius = this.ellipsoid;
this._collider.collisionMask = this._collisionMask;
//no need for clone, as long as gravity is not on.
let actualDisplacement = displacement;
//add gravity to the direction to prevent the dual-collision checking
if (this.applyGravity) {
//this prevents mending with cameraDirection, a global variable of the free camera class.
actualDisplacement = displacement.add(this.getScene().gravity);
}
coordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
}
/** @internal */
_checkInputs() {
if (!this._localDirection) {
this._localDirection = Vector3.Zero();
this._transformedDirection = Vector3.Zero();
}
this.inputs.checkInputs();
super._checkInputs();
}
/**
* Enable movement without a user input. This allows gravity to always be applied.
*/
set needMoveForGravity(value) {
this._needMoveForGravity = value;
}
/**
* When true, gravity is applied whether there is user input or not.
*/
get needMoveForGravity() {
return this._needMoveForGravity;
}
/** @internal */
_decideIfNeedsToMove() {
return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
}
/** @internal */
_updatePosition() {
if (this.checkCollisions && this.getScene().collisionsEnabled) {
this._collideWithWorld(this.cameraDirection);
}
else {
super._updatePosition();
}
}
/**
* Destroy the camera and release the current resources hold by it.
*/
dispose() {
this.inputs.clear();
super.dispose();
}
/**
* Gets the current object class name.
* @returns the class name
*/
getClassName() {
return "FreeCamera";
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_ellipsoid_decorators = [serializeAsVector3()];
_ellipsoidOffset_decorators = [serializeAsVector3()];
_checkCollisions_decorators = [serialize()];
_applyGravity_decorators = [serialize()];
__esDecorate(null, null, _ellipsoid_decorators, { kind: "field", name: "ellipsoid", static: false, private: false, access: { has: obj => "ellipsoid" in obj, get: obj => obj.ellipsoid, set: (obj, value) => { obj.ellipsoid = value; } }, metadata: _metadata }, _ellipsoid_initializers, _ellipsoid_extraInitializers);
__esDecorate(null, null, _ellipsoidOffset_decorators, { kind: "field", name: "ellipsoidOffset", static: false, private: false, access: { has: obj => "ellipsoidOffset" in obj, get: obj => obj.ellipsoidOffset, set: (obj, value) => { obj.ellipsoidOffset = value; } }, metadata: _metadata }, _ellipsoidOffset_initializers, _ellipsoidOffset_extraInitializers);
__esDecorate(null, null, _checkCollisions_decorators, { kind: "field", name: "checkCollisions", static: false, private: false, access: { has: obj => "checkCollisions" in obj, get: obj => obj.checkCollisions, set: (obj, value) => { obj.checkCollisions = value; } }, metadata: _metadata }, _checkCollisions_initializers, _checkCollisions_extraInitializers);
__esDecorate(null, null, _applyGravity_decorators, { kind: "field", name: "applyGravity", static: false, private: false, access: { has: obj => "applyGravity" in obj, get: obj => obj.applyGravity, set: (obj, value) => { obj.applyGravity = value; } }, metadata: _metadata }, _applyGravity_initializers, _applyGravity_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
/**
* Defines a target to use with MorphTargetManager
* @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/morphTargets
*/
let MorphTarget = (() => {
var _a;
let _id_decorators;
let _id_initializers = [];
let _id_extraInitializers = [];
let _morphTargetManager_decorators;
let _morphTargetManager_initializers = [];
let _morphTargetManager_extraInitializers = [];
return _a = class MorphTarget {
/**
* Gets or sets the influence of this target (ie. its weight in the overall morphing)
*/
get influence() {
return this._influence;
}
set influence(influence) {
if (this._influence === influence) {
return;
}
const previous = this._influence;
this._influence = influence;
if (this.onInfluenceChanged.hasObservers()) {
this.onInfluenceChanged.notifyObservers(previous === 0 || influence === 0);
}
}
/**
* Gets or sets the animation properties override
*/
get animationPropertiesOverride() {
if (!this._animationPropertiesOverride && this._scene) {
return this._scene.animationPropertiesOverride;
}
return this._animationPropertiesOverride;
}
set animationPropertiesOverride(value) {
this._animationPropertiesOverride = value;
}
/**
* Creates a new MorphTarget
* @param name defines the name of the target
* @param influence defines the influence to use
* @param scene defines the scene the morphtarget belongs to
* @param morphTargetManager morph target manager this morph target is associated with
*/
constructor(name, influence = 0, scene = null, morphTargetManager = null) {
this.name = name;
/**
* Gets or sets the list of animations
*/
this.animations = [];
this._positions = null;
this._normals = null;
this._tangents = null;
this._uvs = null;
this._uv2s = null;
this._colors = null;
this._uniqueId = 0;
/**
* Observable raised when the influence changes
*/
this.onInfluenceChanged = new Observable();
/** @internal */
this._onDataLayoutChanged = new Observable();
/**
* Gets or sets the id of the morph Target
*/
this.id = __runInitializers(this, _id_initializers, void 0);
/**
* Gets or sets the morph target manager this morph target is associated with
*/
this.morphTargetManager = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _morphTargetManager_initializers, null));
this._animationPropertiesOverride = (__runInitializers(this, _morphTargetManager_extraInitializers), null);
this.id = name;
this.morphTargetManager = morphTargetManager;
this._scene = scene || EngineStore.LastCreatedScene;
this.influence = influence;
if (this._scene) {
this._uniqueId = this._scene.getUniqueId();
}
}
/**
* Gets the unique ID of this manager
*/
get uniqueId() {
return this._uniqueId;
}
/**
* Gets a boolean defining if the target contains position data
*/
get hasPositions() {
return !!this._positions;
}
/**
* Gets a boolean defining if the target contains normal data
*/
get hasNormals() {
return !!this._normals;
}
/**
* Gets a boolean defining if the target contains tangent data
*/
get hasTangents() {
return !!this._tangents;
}
/**
* Gets a boolean defining if the target contains texture coordinates data
*/
get hasUVs() {
return !!this._uvs;
}
/**
* Gets a boolean defining if the target contains texture coordinates 2 data
*/
get hasUV2s() {
return !!this._uv2s;
}
get hasColors() {
return !!this._colors;
}
/**
* Gets the number of vertices stored in this target
*/
get vertexCount() {
return this._positions
? this._positions.length / 3
: this._normals
? this._normals.length / 3
: this._tangents
? this._tangents.length / 3
: this._uvs
? this._uvs.length / 2
: this._uv2s
? this._uv2s.length / 2
: this._colors
? this._colors.length / 4
: 0;
}
/**
* Affects position data to this target
* @param data defines the position data to use
*/
setPositions(data) {
const hadPositions = this.hasPositions;
this._positions = data;
if (hadPositions !== this.hasPositions) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}
/**
* Gets the position data stored in this target
* @returns a FloatArray containing the position data (or null if not present)
*/
getPositions() {
return this._positions;
}
/**
* Affects normal data to this target
* @param data defines the normal data to use
*/
setNormals(data) {
const hadNormals = this.hasNormals;
this._normals = data;
if (hadNormals !== this.hasNormals) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}
/**
* Gets the normal data stored in this target
* @returns a FloatArray containing the normal data (or null if not present)
*/
getNormals() {
return this._normals;
}
/**
* Affects tangent data to this target
* @param data defines the tangent data to use
*/
setTangents(data) {
const hadTangents = this.hasTangents;
this._tangents = data;
if (hadTangents !== this.hasTangents) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}
/**
* Gets the tangent data stored in this target
* @returns a FloatArray containing the tangent data (or null if not present)
*/
getTangents() {
return this._tangents;
}
/**
* Affects texture coordinates data to this target
* @param data defines the texture coordinates data to use
*/
setUVs(data) {
const hadUVs = this.hasUVs;
this._uvs = data;
if (hadUVs !== this.hasUVs) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}
/**
* Gets the texture coordinates data stored in this target
* @returns a FloatArray containing the texture coordinates data (or null if not present)
*/
getUVs() {
return this._uvs;
}
/**
* Affects texture coordinates 2 data to this target
* @param data defines the texture coordinates 2 data to use
*/
setUV2s(data) {
const hadUV2s = this.hasUV2s;
this._uv2s = data;
if (hadUV2s !== this.hasUV2s) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}
/**
* Gets the texture coordinates 2 data stored in this target
* @returns a FloatArray containing the texture coordinates 2 data (or null if not present)
*/
getUV2s() {
return this._uv2s;
}
/**
* Affects color data to this target
* @param data defines the color data to use
*/
setColors(data) {
const hadColors = this.hasColors;
this._colors = data;
if (hadColors !== this.hasColors) {
this._onDataLayoutChanged.notifyObservers(undefined);
}
}
/**
* Gets the color data stored in this target
* @returns a FloatArray containing the color data (or null if not present)
*/
getColors() {
return this._colors;
}
/**
* Clone the current target
* @returns a new MorphTarget
*/
clone() {
const newOne = SerializationHelper.Clone(() => new _a(this.name, this.influence, this._scene, this.morphTargetManager), this);
newOne._positions = this._positions;
newOne._normals = this._normals;
newOne._tangents = this._tangents;
newOne._uvs = this._uvs;
newOne._uv2s = this._uv2s;
newOne._colors = this._colors;
return newOne;
}
/**
* Serializes the current target into a Serialization object
* @returns the serialized object
*/
serialize() {
const serializationObject = {};
serializationObject.name = this.name;
serializationObject.influence = this.influence;
if (this.id != null) {
serializationObject.id = this.id;
}
serializationObject.uniqueId = this.uniqueId;
serializationObject.positions = Array.prototype.slice.call(this.getPositions());
if (this.hasNormals) {
serializationObject.normals = Array.prototype.slice.call(this.getNormals());
}
if (this.hasTangents) {
serializationObject.tangents = Array.prototype.slice.call(this.getTangents());
}
if (this.hasUVs) {
serializationObject.uvs = Array.prototype.slice.call(this.getUVs());
}
if (this.hasUV2s) {
serializationObject.uv2s = Array.prototype.slice.call(this.getUV2s());
}
if (this.hasColors) {
serializationObject.colors = Array.prototype.slice.call(this.getColors());
}
// Animations
SerializationHelper.AppendSerializedAnimations(this, serializationObject);
return serializationObject;
}
/**
* Returns the string "MorphTarget"
* @returns "MorphTarget"
*/
getClassName() {
return "MorphTarget";
}
// Statics
/**
* Creates a new target from serialized data
* @param serializationObject defines the serialized data to use
* @param scene defines the hosting scene
* @param morphTargetManager morph target manager this morph target is associated with
* @returns a new MorphTarget
*/
static Parse(serializationObject, scene, morphTargetManager = null) {
const result = new _a(serializationObject.name, serializationObject.influence, scene, morphTargetManager);
result.setPositions(serializationObject.positions);
if (serializationObject.id != null) {
result.id = serializationObject.id;
}
if (serializationObject.normals) {
result.setNormals(serializationObject.normals);
}
if (serializationObject.tangents) {
result.setTangents(serializationObject.tangents);
}
if (serializationObject.uvs) {
result.setUVs(serializationObject.uvs);
}
if (serializationObject.uv2s) {
result.setUV2s(serializationObject.uv2s);
}
if (serializationObject.colors) {
result.setColors(serializationObject.colors);
}
// Animations
if (serializationObject.animations) {
for (let animationIndex = 0; animationIndex < serializationObject.animations.length; animationIndex++) {
const parsedAnimation = serializationObject.animations[animationIndex];
const internalClass = GetClass("BABYLON.Animation");
if (internalClass) {
result.animations.push(internalClass.Parse(parsedAnimation));
}
}
if (serializationObject.autoAnimate && scene) {
scene.beginAnimation(result, serializationObject.autoAnimateFrom, serializationObject.autoAnimateTo, serializationObject.autoAnimateLoop, serializationObject.autoAnimateSpeed || 1.0);
}
}
return result;
}
/**
* Creates a MorphTarget from mesh data
* @param mesh defines the source mesh
* @param name defines the name to use for the new target
* @param influence defines the influence to attach to the target
* @returns a new MorphTarget
*/
static FromMesh(mesh, name, influence) {
if (!name) {
name = mesh.name;
}
const result = new _a(name, influence, mesh.getScene(), mesh.morphTargetManager);
result.setPositions(mesh.getVerticesData(VertexBuffer.PositionKind));
if (mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
result.setNormals(mesh.getVerticesData(VertexBuffer.NormalKind));
}
if (mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
result.setTangents(mesh.getVerticesData(VertexBuffer.TangentKind));
}
if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
result.setUVs(mesh.getVerticesData(VertexBuffer.UVKind));
}
if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
result.setUV2s(mesh.getVerticesData(VertexBuffer.UV2Kind));
}
if (mesh.isVerticesDataPresent(VertexBuffer.ColorKind)) {
result.setColors(mesh.getVerticesData(VertexBuffer.ColorKind));
}
return result;
}
},
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_id_decorators = [serialize()];
_morphTargetManager_decorators = [serialize()];
__esDecorate(null, null, _id_decorators, { kind: "field", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
__esDecorate(null, null, _morphTargetManager_decorators, { kind: "field", name: "morphTargetManager", static: false, private: false, access: { has: obj => "morphTargetManager" in obj, get: obj => obj.morphTargetManager, set: (obj, value) => { obj.morphTargetManager = value; } }, metadata: _metadata }, _morphTargetManager_initializers, _morphTargetManager_extraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
})();
/**
* Class used to store 2D array textures containing user data
*/
class RawTexture2DArray extends Texture {
/**
* Gets the number of layers of the texture
*/
get depth() {
return this._depth;
}
/**
* Create a new RawTexture2DArray
* @param data defines the data of the texture
* @param width defines the width of the texture
* @param height defines the height of the texture
* @param depth defines the number of layers of the texture
* @param format defines the texture format to use
* @param scene defines the hosting scene
* @param generateMipMaps defines a boolean indicating if mip levels should be generated (true by default)
* @param invertY defines if texture must be stored with Y axis inverted
* @param samplingMode defines the sampling mode to use (Texture.TRILINEAR_SAMPLINGMODE by default)
* @param textureType defines the texture Type (Engine.TEXTURETYPE_UNSIGNED_BYTE, Engine.TEXTURETYPE_FLOAT...)
* @param creationFlags specific flags to use when creating the texture (Constants.TEXTURE_CREATIONFLAG_STORAGE for storage textures, for eg)
* @param mipLevelCount defines the number of mip levels to allocate for the texture
*/
constructor(data, width, height, depth,
/** Gets or sets the texture format to use */
format, scene, generateMipMaps = true, invertY = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE, creationFlags, mipLevelCount) {
super(null, scene, !generateMipMaps, invertY);
this.format = format;
this._texture = scene
.getEngine()
.createRawTexture2DArray(data, width, height, depth, format, generateMipMaps, invertY, samplingMode, null, textureType, creationFlags ?? 0, mipLevelCount);
this._depth = depth;
this.is2DArray = true;
}
/**
* Update the texture with new data
* @param data defines the data to store in the texture
*/
update(data) {
this.updateMipLevel(data, 0);
}
/**
* Updates a specific mip level of the texture.
* @param data The new data for the mip level
* @param mipLevel The mip level to update (0 is the base level)
*/
updateMipLevel(data, mipLevel) {
if (!this._texture) {
return;
}
this._getEngine().updateRawTexture2DArray(this._texture, data, this._texture.format, this._texture.invertY, null, this._texture.type, mipLevel);
}
/**
* Creates a RGBA texture from some data.
* @param data Define the texture data
* @param width Define the width of the texture
* @param height Define the height of the texture
* @param depth defines the number of layers of the texture
* @param scene defines the scene the texture will belong to
* @param generateMipMaps Define whether or not to create mip maps for the texture
* @param invertY define if the data should be flipped on Y when uploaded to the GPU
* @param samplingMode define the texture sampling mode (Texture.xxx_SAMPLINGMODE)
* @param type define the format of the data (int, float... Engine.TEXTURETYPE_xxx)
* @returns the RGBA texture
*/
static CreateRGBATexture(data, width, height, depth, scene, generateMipMaps = true, invertY = false, samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, type = Constants.TEXTURETYPE_UNSIGNED_BYTE) {
return new RawTexture2DArray(data, width, height, depth, Constants.TEXTUREFORMAT_RGBA, scene, generateMipMaps, invertY, samplingMode, type);
}
}
/**
* This class is used to deform meshes using morphing between different targets
* @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/morphTargets
*/
class MorphTargetManager {
/**
* Sets a boolean indicating that adding new target or updating an existing target will not update the underlying data buffers
*/
set areUpdatesFrozen(block) {
if (block) {
this._blockCounter++;
}
else {
this._blockCounter--;
if (this._blockCounter <= 0) {
this._blockCounter = 0;
this._syncActiveTargets(this._forceUpdateWhenUnfrozen);
this._forceUpdateWhenUnfrozen = false;
}
}
}
get areUpdatesFrozen() {
return this._blockCounter > 0;
}
/**
* Creates a new MorphTargetManager
* @param scene defines the current scene
* @param meshName name of the mesh this morph target manager is associated with
*/
constructor(scene = null, meshName) {
this.meshName = meshName;
this._targets = new Array();
this._targetInfluenceChangedObservers = new Array();
this._targetDataLayoutChangedObservers = new Array();
this._activeTargets = new SmartArray(16);
this._supportsPositions = false;
this._supportsNormals = false;
this._supportsTangents = false;
this._supportsUVs = false;
this._supportsUV2s = false;
this._supportsColors = false;
this._vertexCount = 0;
this._uniqueId = 0;
this._tempInfluences = new Array();
this._canUseTextureForTargets = false;
this._blockCounter = 0;
this._mustSynchronize = true;
this._forceUpdateWhenUnfrozen = false;
/** @internal */
this._textureVertexStride = 0;
/** @internal */
this._textureWidth = 0;
/** @internal */
this._textureHeight = 1;
/** @internal */
this._parentContainer = null;
/**
* Gets or sets a boolean indicating if influencers must be optimized (eg. recompiling the shader if less influencers are used)
*/
this.optimizeInfluencers = true;
/**
* Gets or sets a boolean indicating if positions must be morphed
*/
this.enablePositionMorphing = true;
/**
* Gets or sets a boolean indicating if normals must be morphed
*/
this.enableNormalMorphing = true;
/**
* Gets or sets a boolean indicating if tangents must be morphed
*/
this.enableTangentMorphing = true;
/**
* Gets or sets a boolean indicating if UV must be morphed
*/
this.enableUVMorphing = true;
/**
* Gets or sets a boolean indicating if UV2 must be morphed
*/
this.enableUV2Morphing = true;
/**
* Gets or sets a boolean indicating if colors must be morphed
*/
this.enableColorMorphing = true;
this._numMaxInfluencers = 0;
this._useTextureToStoreTargets = true;
/**
* Gets or sets an object used to store user defined information for the MorphTargetManager
*/
this.metadata = null;
this._influencesAreDirty = false;
this._needUpdateInfluences = false;
if (!scene) {
scene = EngineStore.LastCreatedScene;
}
this._scene = scene;
if (this._scene) {
this._scene.addMorphTargetManager(this);
this._uniqueId = this._scene.getUniqueId();
const engineCaps = this._scene.getEngine().getCaps();
this._canUseTextureForTargets =
engineCaps.canUseGLVertexID && engineCaps.textureFloat && engineCaps.maxVertexTextureImageUnits > 0 && engineCaps.texture2DArrayMaxLayerCount > 1;
}
}
/**
* Gets or sets the maximum number of influencers (targets) (default value: 0).
* Setting a value for this property can lead to a smoother experience, as only one shader will be compiled, which will use this value as the maximum number of influencers.
* If you leave the value at 0 (default), a new shader will be compiled every time the number of active influencers changes. This can cause problems, as compiling a shader takes time.
* If you assign a non-zero value to this property, you need to ensure that this value is greater than the maximum number of (active) influencers you'll need for this morph manager.
* Otherwise, the number of active influencers will be truncated at the value you set for this property, which can lead to unexpected results.
* Note that this property has no effect if "useTextureToStoreTargets" is false.
* Note as well that if MorphTargetManager.ConstantTargetCountForTextureMode is greater than 0, this property will be ignored and the constant value will be used instead.
*/
get numMaxInfluencers() {
if (MorphTargetManager.ConstantTargetCountForTextureMode > 0 && this.isUsingTextureForTargets) {
return MorphTargetManager.ConstantTargetCountForTextureMode;
}
return this._numMaxInfluencers;
}
set numMaxInfluencers(value) {
if (this._numMaxInfluencers === value) {
return;
}
this._numMaxInfluencers = value;
this._mustSynchronize = true;
this._syncActiveTargets();
}
/**
* Gets the unique ID of this manager
*/
get uniqueId() {
return this._uniqueId;
}
/**
* Gets the number of vertices handled by this manager
*/
get vertexCount() {
return this._vertexCount;
}
/**
* Gets a boolean indicating if this manager supports morphing of positions
*/
get supportsPositions() {
return this._supportsPositions && this.enablePositionMorphing;
}
/**
* Gets a boolean indicating if this manager supports morphing of normals
*/
get supportsNormals() {
return this._supportsNormals && this.enableNormalMorphing;
}
/**
* Gets a boolean indicating if this manager supports morphing of tangents
*/
get supportsTangents() {
return this._supportsTangents && this.enableTangentMorphing;
}
/**
* Gets a boolean indicating if this manager supports morphing of texture coordinates
*/
get supportsUVs() {
return this._supportsUVs && this.enableUVMorphing;
}
/**
* Gets a boolean indicating if this manager supports morphing of texture coordinates 2
*/
get supportsUV2s() {
return this._supportsUV2s && this.enableUV2Morphing;
}
/**
* Gets a boolean indicating if this manager supports morphing of colors
*/
get supportsColors() {
return this._supportsColors && this.enableColorMorphing;
}
/**
* Gets a boolean indicating if this manager has data for morphing positions
*/
get hasPositions() {
return this._supportsPositions;
}
/**
* Gets a boolean indicating if this manager has data for morphing normals
*/
get hasNormals() {
return this._supportsNormals;
}
/**
* Gets a boolean indicating if this manager has data for morphing tangents
*/
get hasTangents() {
return this._supportsTangents;
}
/**
* Gets a boolean indicating if this manager has data for morphing texture coordinates
*/
get hasUVs() {
return this._supportsUVs;
}
/**
* Gets a boolean indicating if this manager has data for morphing texture coordinates 2
*/
get hasUV2s() {
return this._supportsUV2s;
}
/**
* Gets a boolean indicating if this manager has data for morphing colors
*/
get hasColors() {
return this._supportsColors;
}
/**
* Gets the number of targets stored in this manager
*/
get numTargets() {
return this._targets.length;
}
/**
* Gets the number of influencers (ie. the number of targets with influences > 0)
*/
get numInfluencers() {
if (this._influencesAreDirty) {
this._syncActiveTargets();
}
return this._activeTargets.length;
}
/**
* Gets the list of influences (one per target)
*/
get influences() {
if (this._influencesAreDirty) {
this._syncActiveTargets();
}
return this._influences;
}
/**
* Gets or sets a boolean indicating that targets should be stored as a texture instead of using vertex attributes (default is true).
* Please note that this option is not available if the hardware does not support it
*/
get useTextureToStoreTargets() {
return this._useTextureToStoreTargets;
}
set useTextureToStoreTargets(value) {
if (this._useTextureToStoreTargets === value) {
return;
}
this._useTextureToStoreTargets = value;
this._mustSynchronize = true;
this._syncActiveTargets();
}
/**
* Gets a boolean indicating that the targets are stored into a texture (instead of as attributes)
*/
get isUsingTextureForTargets() {
return (MorphTargetManager.EnableTextureStorage &&
this.useTextureToStoreTargets &&
this._canUseTextureForTargets &&
!this._scene?.getEngine().getCaps().disableMorphTargetTexture);
}
/**
* Gets the active target at specified index. An active target is a target with an influence > 0
* @param index defines the index to check
* @returns the requested target
*/
getActiveTarget(index) {
if (this._influencesAreDirty) {
this._syncActiveTargets();
}
return this._activeTargets.data[index];
}
/**
* Gets the target at specified index
* @param index defines the index to check
* @returns the requested target
*/
getTarget(index) {
return this._targets[index];
}
/**
* Gets the first target with the specified name
* @param name defines the name to check
* @returns the requested target
*/
getTargetByName(name) {
for (const target of this._targets) {
if (target.name === name) {
return target;
}
}
return null;
}
/**
* Add a new target to this manager
* @param target defines the target to add
*/
addTarget(target) {
this._targets.push(target);
this._targetInfluenceChangedObservers.push(target.onInfluenceChanged.add((needUpdate) => {
if (this.areUpdatesFrozen && needUpdate) {
this._forceUpdateWhenUnfrozen = true;
}
this._influencesAreDirty = true;
this._needUpdateInfluences = this._needUpdateInfluences || needUpdate;
}));
this._targetDataLayoutChangedObservers.push(target._onDataLayoutChanged.add(() => {
this._mustSynchronize = true;
this._syncActiveTargets();
}));
this._mustSynchronize = true;
this._syncActiveTargets();
}
/**
* Removes a target from the manager
* @param target defines the target to remove
*/
removeTarget(target) {
const index = this._targets.indexOf(target);
if (index >= 0) {
this._targets.splice(index, 1);
target.onInfluenceChanged.remove(this._targetInfluenceChangedObservers.splice(index, 1)[0]);
target._onDataLayoutChanged.remove(this._targetDataLayoutChangedObservers.splice(index, 1)[0]);
this._mustSynchronize = true;
this._syncActiveTargets();
}
if (this._scene) {
this._scene.stopAnimation(target);
}
}
/**
* @internal
*/
_bind(effect) {
if (this._influencesAreDirty) {
this._syncActiveTargets();
}
effect.setFloat3("morphTargetTextureInfo", this._textureVertexStride, this._textureWidth, this._textureHeight);
effect.setFloatArray("morphTargetTextureIndices", this._morphTargetTextureIndices);
effect.setTexture("morphTargets", this._targetStoreTexture);
effect.setFloat("morphTargetCount", this.numInfluencers);
}
/**
* Clone the current manager
* @returns a new MorphTargetManager
*/
clone() {
const copy = new MorphTargetManager(this._scene);
copy.areUpdatesFrozen = true;
for (const target of this._targets) {
copy.addTarget(target.clone());
}
copy.areUpdatesFrozen = false;
copy.enablePositionMorphing = this.enablePositionMorphing;
copy.enableNormalMorphing = this.enableNormalMorphing;
copy.enableTangentMorphing = this.enableTangentMorphing;
copy.enableUVMorphing = this.enableUVMorphing;
copy.enableUV2Morphing = this.enableUV2Morphing;
copy.enableColorMorphing = this.enableColorMorphing;
copy.metadata = this.metadata;
return copy;
}
/**
* Serializes the current manager into a Serialization object
* @returns the serialized object
*/
serialize() {
const serializationObject = {};
serializationObject.id = this.uniqueId;
serializationObject.meshName = this.meshName;
serializationObject.targets = [];
for (const target of this._targets) {
serializationObject.targets.push(target.serialize());
}
if (this.metadata) {
serializationObject.metadata = this.metadata;
}
return serializationObject;
}
_syncActiveTargets(needUpdate = false) {
if (this.areUpdatesFrozen) {
return;
}
needUpdate = needUpdate || this._needUpdateInfluences;
this._needUpdateInfluences = false;
this._influencesAreDirty = false;
const wasUsingTextureForTargets = !!this._targetStoreTexture;
const isUsingTextureForTargets = this.isUsingTextureForTargets;
if (this._mustSynchronize || wasUsingTextureForTargets !== isUsingTextureForTargets) {
this._mustSynchronize = false;
this.synchronize();
}
let influenceCount = 0;
this._activeTargets.reset();
if (!this._morphTargetTextureIndices || this._morphTargetTextureIndices.length !== this._targets.length) {
this._morphTargetTextureIndices = new Float32Array(this._targets.length);
}
let targetIndex = -1;
for (const target of this._targets) {
targetIndex++;
if (target.influence === 0 && this.optimizeInfluencers) {
continue;
}
if (this._activeTargets.length >= MorphTargetManager.MaxActiveMorphTargetsInVertexAttributeMode && !this.isUsingTextureForTargets) {
break;
}
this._activeTargets.push(target);
this._morphTargetTextureIndices[influenceCount] = targetIndex;
this._tempInfluences[influenceCount++] = target.influence;
}
if (this._morphTargetTextureIndices.length !== influenceCount) {
this._morphTargetTextureIndices = this._morphTargetTextureIndices.slice(0, influenceCount);
}
if (!this._influences || this._influences.length !== influenceCount) {
this._influences = new Float32Array(influenceCount);
}
for (let index = 0; index < influenceCount; index++) {
this._influences[index] = this._tempInfluences[index];
}
if (needUpdate && this._scene) {
for (const mesh of this._scene.meshes) {
if (mesh.morphTargetManager === this) {
if (isUsingTextureForTargets) {
mesh._markSubMeshesAsAttributesDirty();
}
else {
mesh._syncGeometryWithMorphTargetManager();
}
}
}
}
}
/**
* Synchronize the targets with all the meshes using this morph target manager
*/
synchronize() {
if (!this._scene || this.areUpdatesFrozen) {
return;
}
const engine = this._scene.getEngine();
this._supportsPositions = true;
this._supportsNormals = true;
this._supportsTangents = true;
this._supportsUVs = true;
this._supportsUV2s = true;
this._supportsColors = true;
this._vertexCount = 0;
this._targetStoreTexture?.dispose();
this._targetStoreTexture = null;
if (this.isUsingTextureForTargets && this._targets.length > engine.getCaps().texture2DArrayMaxLayerCount) {
this.useTextureToStoreTargets = false;
}
for (const target of this._targets) {
this._supportsPositions = this._supportsPositions && target.hasPositions;
this._supportsNormals = this._supportsNormals && target.hasNormals;
this._supportsTangents = this._supportsTangents && target.hasTangents;
this._supportsUVs = this._supportsUVs && target.hasUVs;
this._supportsUV2s = this._supportsUV2s && target.hasUV2s;
this._supportsColors = this._supportsColors && target.hasColors;
const vertexCount = target.vertexCount;
if (this._vertexCount === 0) {
this._vertexCount = vertexCount;
}
else if (this._vertexCount !== vertexCount) {
Logger.Error(`Incompatible target. Targets must all have the same vertices count. Current vertex count: ${this._vertexCount}, vertex count for target "${target.name}": ${vertexCount}`);
return;
}
}
if (this.isUsingTextureForTargets) {
this._textureVertexStride = 0;
this._supportsPositions && this._textureVertexStride++;
this._supportsNormals && this._textureVertexStride++;
this._supportsTangents && this._textureVertexStride++;
this._supportsUVs && this._textureVertexStride++;
this._supportsUV2s && this._textureVertexStride++;
this._supportsColors && this._textureVertexStride++;
this._textureWidth = this._vertexCount * this._textureVertexStride || 1;
this._textureHeight = 1;
const maxTextureSize = engine.getCaps().maxTextureSize;
if (this._textureWidth > maxTextureSize) {
this._textureHeight = Math.ceil(this._textureWidth / maxTextureSize);
this._textureWidth = maxTextureSize;
}
const targetCount = this._targets.length;
const data = new Float32Array(targetCount * this._textureWidth * this._textureHeight * 4);
let offset;
for (let index = 0; index < targetCount; index++) {
const target = this._targets[index];
const positions = target.getPositions();
const normals = target.getNormals();
const uvs = target.getUVs();
const tangents = target.getTangents();
const uv2s = target.getUV2s();
const colors = target.getColors();
offset = index * this._textureWidth * this._textureHeight * 4;
for (let vertex = 0; vertex < this._vertexCount; vertex++) {
if (this._supportsPositions && positions) {
data[offset] = positions[vertex * 3];
data[offset + 1] = positions[vertex * 3 + 1];
data[offset + 2] = positions[vertex * 3 + 2];
offset += 4;
}
if (this._supportsNormals && normals) {
data[offset] = normals[vertex * 3];
data[offset + 1] = normals[vertex * 3 + 1];
data[offset + 2] = normals[vertex * 3 + 2];
offset += 4;
}
if (this._supportsUVs && uvs) {
data[offset] = uvs[vertex * 2];
data[offset + 1] = uvs[vertex * 2 + 1];
offset += 4;
}
if (this._supportsTangents && tangents) {
data[offset] = tangents[vertex * 3];
data[offset + 1] = tangents[vertex * 3 + 1];
data[offset + 2] = tangents[vertex * 3 + 2];
offset += 4;
}
if (this._supportsUV2s && uv2s) {
data[offset] = uv2s[vertex * 2];
data[offset + 1] = uv2s[vertex * 2 + 1];
offset += 4;
}
if (this._supportsColors && colors) {
data[offset] = colors[vertex * 4];
data[offset + 1] = colors[vertex * 4 + 1];
data[offset + 2] = colors[vertex * 4 + 2];
data[offset + 3] = colors[vertex * 4 + 3];
offset += 4;
}
}
}
this._targetStoreTexture = RawTexture2DArray.CreateRGBATexture(data, this._textureWidth, this._textureHeight, targetCount, this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
this._targetStoreTexture.name = `Morph texture_${this.uniqueId}`;
}
// Flag meshes as dirty to resync with the active targets
for (const mesh of this._scene.meshes) {
if (mesh.morphTargetManager === this) {
mesh._syncGeometryWithMorphTargetManager();
}
}
}
/**
* Release all resources
*/
dispose() {
if (this._targetStoreTexture) {
this._targetStoreTexture.dispose();
}
this._targetStoreTexture = null;
this.metadata = null;
// Remove from scene
if (this._scene) {
this._scene.removeMorphTargetManager(this);
if (this._parentContainer) {
const index = this._parentContainer.morphTargetManagers.indexOf(this);
if (index > -1) {
this._parentContainer.morphTargetManagers.splice(index, 1);
}
this._parentContainer = null;
}
for (const morph of this._targets) {
this._scene.stopAnimation(morph);
}
}
}
// Statics
/**
* Creates a new MorphTargetManager from serialized data
* @param serializationObject defines the serialized data
* @param scene defines the hosting scene
* @returns the new MorphTargetManager
*/
static Parse(serializationObject, scene) {
const result = new MorphTargetManager(scene);
for (const targetData of serializationObject.targets) {
result.addTarget(MorphTarget.Parse(targetData, scene, result));
}
if (serializationObject.metadata) {
result.metadata = serializationObject.metadata;
}
return result;
}
}
/** Enable storing morph target data into textures when set to true (true by default) */
MorphTargetManager.EnableTextureStorage = true;
/** Maximum number of active morph targets supported in the "vertex attribute" mode (i.e., not the "texture" mode) */
MorphTargetManager.MaxActiveMorphTargetsInVertexAttributeMode = 8;
/**
* When used in texture mode, if greather than 0, this will override the the morph manager numMaxInfluencers value.
*/
MorphTargetManager.ConstantTargetCountForTextureMode = 0;
export { FreeCamera as F, MorphTargetManager as M, MorphTarget as a };
//# sourceMappingURL=morphTargetManager-BcCzrruC.esm.js.map