-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
camera.ts
1548 lines (1332 loc) · 56 KB
/
camera.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import { serialize, SerializationHelper, serializeAsVector3 } from "../Misc/decorators";
import { SmartArray } from "../Misc/smartArray";
import { Tools } from "../Misc/tools";
import { Observable } from "../Misc/observable";
import type { Nullable } from "../types";
import type { CameraInputsManager } from "./cameraInputsManager";
import type { Scene } from "../scene";
import { Matrix, Vector3, Quaternion } from "../Maths/math.vector";
import { Node } from "../node";
import type { Mesh } from "../Meshes/mesh";
import type { AbstractMesh } from "../Meshes/abstractMesh";
import type { ICullable } from "../Culling/boundingInfo";
import { Logger } from "../Misc/logger";
import { GetClass } from "../Misc/typeStore";
import { _WarnImport } from "../Misc/devTools";
import { Viewport } from "../Maths/math.viewport";
import { Frustum } from "../Maths/math.frustum";
import type { Plane } from "../Maths/math.plane";
import { Constants } from "../Engines/constants";
import type { PostProcess } from "../PostProcesses/postProcess";
import type { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture";
import type { FreeCamera } from "./freeCamera";
import type { TargetCamera } from "./targetCamera";
import type { Ray } from "../Culling/ray";
import type { ArcRotateCamera } from "./arcRotateCamera";
/**
* Oblique projection values
*/
export interface IObliqueParams {
/** The angle of the plane */
angle: number;
/** The length of the plane */
length: number;
/** The offset of the plane */
offset: number;
}
/**
* This is the base class of all the camera used in the application.
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras
*/
export class Camera extends Node {
/**
* @internal
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public static _CreateDefaultParsedCamera = (name: string, scene: Scene): Camera => {
throw _WarnImport("UniversalCamera");
};
/**
* This is the default projection mode used by the cameras.
* It helps recreating a feeling of perspective and better appreciate depth.
* This is the best way to simulate real life cameras.
*/
public static readonly PERSPECTIVE_CAMERA = Constants.PERSPECTIVE_CAMERA;
/**
* This helps creating camera with an orthographic mode.
* Orthographic is commonly used in engineering as a means to produce object specifications that communicate dimensions unambiguously, each line of 1 unit length (cm, meter..whatever) will appear to have the same length everywhere on the drawing. This allows the drafter to dimension only a subset of lines and let the reader know that other lines of that length on the drawing are also that length in reality. Every parallel line in the drawing is also parallel in the object.
*/
public static readonly ORTHOGRAPHIC_CAMERA = Constants.ORTHOGRAPHIC_CAMERA;
/**
* This is the default FOV mode for perspective cameras.
* This setting aligns the upper and lower bounds of the viewport to the upper and lower bounds of the camera frustum.
*/
public static readonly FOVMODE_VERTICAL_FIXED = Constants.FOVMODE_VERTICAL_FIXED;
/**
* This setting aligns the left and right bounds of the viewport to the left and right bounds of the camera frustum.
*/
public static readonly FOVMODE_HORIZONTAL_FIXED = Constants.FOVMODE_HORIZONTAL_FIXED;
/**
* This specifies there is no need for a camera rig.
* Basically only one eye is rendered corresponding to the camera.
*/
public static readonly RIG_MODE_NONE = Constants.RIG_MODE_NONE;
/**
* Simulates a camera Rig with one blue eye and one red eye.
* This can be use with 3d blue and red glasses.
*/
public static readonly RIG_MODE_STEREOSCOPIC_ANAGLYPH = Constants.RIG_MODE_STEREOSCOPIC_ANAGLYPH;
/**
* Defines that both eyes of the camera will be rendered side by side with a parallel target.
*/
public static readonly RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL = Constants.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL;
/**
* Defines that both eyes of the camera will be rendered side by side with a none parallel target.
*/
public static readonly RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED = Constants.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;
/**
* Defines that both eyes of the camera will be rendered over under each other.
*/
public static readonly RIG_MODE_STEREOSCOPIC_OVERUNDER = Constants.RIG_MODE_STEREOSCOPIC_OVERUNDER;
/**
* Defines that both eyes of the camera will be rendered on successive lines interlaced for passive 3d monitors.
*/
public static readonly RIG_MODE_STEREOSCOPIC_INTERLACED = Constants.RIG_MODE_STEREOSCOPIC_INTERLACED;
/**
* Defines that both eyes of the camera should be renderered in a VR mode (carbox).
*/
public static readonly RIG_MODE_VR = Constants.RIG_MODE_VR;
/**
* Custom rig mode allowing rig cameras to be populated manually with any number of cameras
*/
public static readonly RIG_MODE_CUSTOM = Constants.RIG_MODE_CUSTOM;
/**
* Defines if by default attaching controls should prevent the default javascript event to continue.
*/
public static ForceAttachControlToAlwaysPreventDefault = false;
/**
* Define the input manager associated with the camera.
*/
public inputs: CameraInputsManager<Camera>;
/** @internal */
@serializeAsVector3("position")
public _position = Vector3.Zero();
/**
* Define the current local position of the camera in the scene
*/
public get position(): Vector3 {
return this._position;
}
public set position(newPosition: Vector3) {
this._position = newPosition;
}
@serializeAsVector3("upVector")
protected _upVector = Vector3.Up();
/**
* The vector the camera should consider as up.
* (default is Vector3(0, 1, 0) aka Vector3.Up())
*/
public set upVector(vec: Vector3) {
this._upVector = vec;
}
public get upVector() {
return this._upVector;
}
/**
* Object containing oblique projection values (only used with ORTHOGRAPHIC_CAMERA)
*/
public oblique: Nullable<IObliqueParams> = null;
/**
* The screen area in scene units squared
*/
public get screenArea(): number {
let x = 0;
let y = 0;
if (this.mode === Camera.PERSPECTIVE_CAMERA) {
if (this.fovMode === Camera.FOVMODE_VERTICAL_FIXED) {
y = this.minZ * 2 * Math.tan(this.fov / 2);
x = this.getEngine().getAspectRatio(this) * y;
} else {
x = this.minZ * 2 * Math.tan(this.fov / 2);
y = x / this.getEngine().getAspectRatio(this);
}
} else {
const halfWidth = this.getEngine().getRenderWidth() / 2.0;
const halfHeight = this.getEngine().getRenderHeight() / 2.0;
x = (this.orthoRight ?? halfWidth) - (this.orthoLeft ?? -halfWidth);
y = (this.orthoTop ?? halfHeight) - (this.orthoBottom ?? -halfHeight);
}
return x * y;
}
/**
* Define the current limit on the left side for an orthographic camera
* In scene unit
*/
private _orthoLeft: Nullable<number> = null;
public set orthoLeft(value: Nullable<number>) {
this._orthoLeft = value;
for (const rigCamera of this._rigCameras) {
rigCamera.orthoLeft = value;
}
}
@serialize()
public get orthoLeft(): Nullable<number> {
return this._orthoLeft;
}
/**
* Define the current limit on the right side for an orthographic camera
* In scene unit
*/
private _orthoRight: Nullable<number> = null;
public set orthoRight(value: Nullable<number>) {
this._orthoRight = value;
for (const rigCamera of this._rigCameras) {
rigCamera.orthoRight = value;
}
}
@serialize()
public get orthoRight(): Nullable<number> {
return this._orthoRight;
}
/**
* Define the current limit on the bottom side for an orthographic camera
* In scene unit
*/
private _orthoBottom: Nullable<number> = null;
public set orthoBottom(value: Nullable<number>) {
this._orthoBottom = value;
for (const rigCamera of this._rigCameras) {
rigCamera.orthoBottom = value;
}
}
@serialize()
public get orthoBottom(): Nullable<number> {
return this._orthoBottom;
}
/**
* Define the current limit on the top side for an orthographic camera
* In scene unit
*/
private _orthoTop: Nullable<number> = null;
public set orthoTop(value: Nullable<number>) {
this._orthoTop = value;
for (const rigCamera of this._rigCameras) {
rigCamera.orthoTop = value;
}
}
@serialize()
public get orthoTop(): Nullable<number> {
return this._orthoTop;
}
/**
* Field Of View is set in Radians. (default is 0.8)
*/
@serialize()
public fov = 0.8;
/**
* Projection plane tilt around the X axis (horizontal), set in Radians. (default is 0)
* Can be used to make vertical lines in world space actually vertical on the screen.
* See https://forum.babylonjs.com/t/add-vertical-shift-to-3ds-max-exporter-babylon-cameras/17480
*/
@serialize()
public projectionPlaneTilt = 0;
/**
* Define the minimum distance the camera can see from.
* This is important to note that the depth buffer are not infinite and the closer it starts
* the more your scene might encounter depth fighting issue.
*/
@serialize()
public minZ = 1;
/**
* Define the maximum distance the camera can see to.
* This is important to note that the depth buffer are not infinite and the further it end
* the more your scene might encounter depth fighting issue.
*/
@serialize()
public maxZ = 10000.0;
/**
* Define the default inertia of the camera.
* This helps giving a smooth feeling to the camera movement.
*/
@serialize()
public inertia = 0.9;
/**
* Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.ORTHOGRAPHIC_CAMERA)
*/
private _mode = Camera.PERSPECTIVE_CAMERA;
set mode(mode: number) {
this._mode = mode;
// Pass the mode down to the rig cameras
for (const rigCamera of this._rigCameras) {
rigCamera.mode = mode;
}
}
@serialize()
get mode(): number {
return this._mode;
}
/**
* Define whether the camera is intermediate.
* This is useful to not present the output directly to the screen in case of rig without post process for instance
*/
public isIntermediate = false;
/**
* Define the viewport of the camera.
* This correspond to the portion of the screen the camera will render to in normalized 0 to 1 unit.
*/
public viewport = new Viewport(0, 0, 1.0, 1.0);
/**
* Restricts the camera to viewing objects with the same layerMask.
* A camera with a layerMask of 1 will render mesh.layerMask & camera.layerMask!== 0
*/
@serialize()
public layerMask: number = 0x0fffffff;
/**
* fovMode sets the camera frustum bounds to the viewport bounds. (default is FOVMODE_VERTICAL_FIXED)
*/
@serialize()
public fovMode: number = Camera.FOVMODE_VERTICAL_FIXED;
/**
* Rig mode of the camera.
* This is useful to create the camera with two "eyes" instead of one to create VR or stereoscopic scenes.
* This is normally controlled byt the camera themselves as internal use.
*/
@serialize()
public cameraRigMode = Camera.RIG_MODE_NONE;
/**
* Defines the distance between both "eyes" in case of a RIG
*/
@serialize()
public interaxialDistance: number;
/**
* Defines if stereoscopic rendering is done side by side or over under.
*/
@serialize()
public isStereoscopicSideBySide: boolean;
/**
* Defines the list of custom render target which are rendered to and then used as the input to this camera's render. Eg. display another camera view on a TV in the main scene
* This is pretty helpful if you wish to make a camera render to a texture you could reuse somewhere
* else in the scene. (Eg. security camera)
*
* To change the final output target of the camera, camera.outputRenderTarget should be used instead (eg. webXR renders to a render target corresponding to an HMD)
*/
public customRenderTargets = new Array<RenderTargetTexture>();
/**
* When set, the camera will render to this render target instead of the default canvas
*
* If the desire is to use the output of a camera as a texture in the scene consider using camera.customRenderTargets instead
*/
public outputRenderTarget: Nullable<RenderTargetTexture> = null;
/**
* Observable triggered when the camera view matrix has changed.
*/
public onViewMatrixChangedObservable = new Observable<Camera>();
/**
* Observable triggered when the camera Projection matrix has changed.
*/
public onProjectionMatrixChangedObservable = new Observable<Camera>();
/**
* Observable triggered when the inputs have been processed.
*/
public onAfterCheckInputsObservable = new Observable<Camera>();
/**
* Observable triggered when reset has been called and applied to the camera.
*/
public onRestoreStateObservable = new Observable<Camera>();
/**
* Is this camera a part of a rig system?
*/
public isRigCamera: boolean = false;
/**
* If isRigCamera set to true this will be set with the parent camera.
* The parent camera is not (!) necessarily the .parent of this camera (like in the case of XR)
*/
public rigParent?: Camera;
/**
* Render pass id used by the camera to render into the main framebuffer
*/
public renderPassId: number;
/** @internal */
public _cameraRigParams: any;
/** @internal */
public _rigCameras = new Array<Camera>();
/** @internal */
public _rigPostProcess: Nullable<PostProcess>;
/** @internal */
public _skipRendering = false;
/** @internal */
public _projectionMatrix = new Matrix();
/** @internal */
public _postProcesses = new Array<Nullable<PostProcess>>();
/** @internal */
public _activeMeshes = new SmartArray<AbstractMesh>(256);
protected _globalPosition = Vector3.Zero();
/** @internal */
public _computedViewMatrix = Matrix.Identity();
private _doNotComputeProjectionMatrix = false;
private _transformMatrix = Matrix.Zero();
private _frustumPlanes: Plane[];
private _refreshFrustumPlanes = true;
private _storedFov: number;
private _stateStored: boolean;
private _absoluteRotation: Quaternion = Quaternion.Identity();
/**
* Instantiates a new camera object.
* This should not be used directly but through the inherited cameras: ArcRotate, Free...
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras
* @param name Defines the name of the camera in the scene
* @param position Defines the position of the camera
* @param scene Defines the scene the camera belongs too
* @param setActiveOnSceneIfNoneActive Defines if the camera should be set as active after creation if no other camera have been defined in the scene
*/
constructor(name: string, position: Vector3, scene?: Scene, setActiveOnSceneIfNoneActive = true) {
super(name, scene);
this.getScene().addCamera(this);
if (setActiveOnSceneIfNoneActive && !this.getScene().activeCamera) {
this.getScene().activeCamera = this;
}
this.position = position;
this.renderPassId = this.getScene().getEngine().createRenderPassId(`Camera ${name}`);
}
/**
* Store current camera state (fov, position, etc..)
* @returns the camera
*/
public storeState(): Camera {
this._stateStored = true;
this._storedFov = this.fov;
return this;
}
/**
* Restores the camera state values if it has been stored. You must call storeState() first
*/
protected _restoreStateValues(): boolean {
if (!this._stateStored) {
return false;
}
this.fov = this._storedFov;
return true;
}
/**
* Restored camera state. You must call storeState() first.
* @returns true if restored and false otherwise
*/
public restoreState(): boolean {
if (this._restoreStateValues()) {
this.onRestoreStateObservable.notifyObservers(this);
return true;
}
return false;
}
/**
* Gets the class name of the camera.
* @returns the class name
*/
public getClassName(): string {
return "Camera";
}
/** @internal */
public readonly _isCamera = true;
/**
* Gets a string representation of the camera useful for debug purpose.
* @param fullDetails Defines that a more verbose level of logging is required
* @returns the string representation
*/
public toString(fullDetails?: boolean): string {
let ret = "Name: " + this.name;
ret += ", type: " + this.getClassName();
if (this.animations) {
for (let i = 0; i < this.animations.length; i++) {
ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
}
}
return ret;
}
/**
* Automatically tilts the projection plane, using `projectionPlaneTilt`, to correct the perspective effect on vertical lines.
*/
public applyVerticalCorrection() {
const rot = this.absoluteRotation.toEulerAngles();
this.projectionPlaneTilt = this._scene.useRightHandedSystem ? -rot.x : rot.x;
}
/**
* Gets the current world space position of the camera.
*/
public get globalPosition(): Vector3 {
return this._globalPosition;
}
/**
* Gets the list of active meshes this frame (meshes no culled or excluded by lod s in the frame)
* @returns the active meshe list
*/
public getActiveMeshes(): SmartArray<AbstractMesh> {
return this._activeMeshes;
}
/**
* Check whether a mesh is part of the current active mesh list of the camera
* @param mesh Defines the mesh to check
* @returns true if active, false otherwise
*/
public isActiveMesh(mesh: Mesh): boolean {
return this._activeMeshes.indexOf(mesh) !== -1;
}
/**
* Is this camera ready to be used/rendered
* @param completeCheck defines if a complete check (including post processes) has to be done (false by default)
* @returns true if the camera is ready
*/
public isReady(completeCheck = false): boolean {
if (completeCheck) {
for (const pp of this._postProcesses) {
if (pp && !pp.isReady()) {
return false;
}
}
}
return super.isReady(completeCheck);
}
/** @internal */
public _initCache() {
super._initCache();
this._cache.position = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
this._cache.upVector = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
this._cache.mode = undefined;
this._cache.minZ = undefined;
this._cache.maxZ = undefined;
this._cache.fov = undefined;
this._cache.fovMode = undefined;
this._cache.aspectRatio = undefined;
this._cache.orthoLeft = undefined;
this._cache.orthoRight = undefined;
this._cache.orthoBottom = undefined;
this._cache.orthoTop = undefined;
this._cache.obliqueAngle = undefined;
this._cache.obliqueLength = undefined;
this._cache.obliqueOffset = undefined;
this._cache.renderWidth = undefined;
this._cache.renderHeight = undefined;
}
/**
* @internal
*/
public _updateCache(ignoreParentClass?: boolean): void {
if (!ignoreParentClass) {
super._updateCache();
}
this._cache.position.copyFrom(this.position);
this._cache.upVector.copyFrom(this.upVector);
}
/** @internal */
public _isSynchronized(): boolean {
return this._isSynchronizedViewMatrix() && this._isSynchronizedProjectionMatrix();
}
/** @internal */
public _isSynchronizedViewMatrix(): boolean {
if (!super._isSynchronized()) {
return false;
}
return this._cache.position.equals(this.position) && this._cache.upVector.equals(this.upVector) && this.isSynchronizedWithParent();
}
/** @internal */
public _isSynchronizedProjectionMatrix(): boolean {
let isSynchronized = this._cache.mode === this.mode && this._cache.minZ === this.minZ && this._cache.maxZ === this.maxZ;
if (!isSynchronized) {
return false;
}
const engine = this.getEngine();
if (this.mode === Camera.PERSPECTIVE_CAMERA) {
isSynchronized =
this._cache.fov === this.fov &&
this._cache.fovMode === this.fovMode &&
this._cache.aspectRatio === engine.getAspectRatio(this) &&
this._cache.projectionPlaneTilt === this.projectionPlaneTilt;
} else {
isSynchronized =
this._cache.orthoLeft === this.orthoLeft &&
this._cache.orthoRight === this.orthoRight &&
this._cache.orthoBottom === this.orthoBottom &&
this._cache.orthoTop === this.orthoTop &&
this._cache.renderWidth === engine.getRenderWidth() &&
this._cache.renderHeight === engine.getRenderHeight();
if (this.oblique) {
isSynchronized =
isSynchronized &&
this._cache.obliqueAngle === this.oblique.angle &&
this._cache.obliqueLength === this.oblique.length &&
this._cache.obliqueOffset === this.oblique.offset;
}
}
return isSynchronized;
}
/**
* 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)
*/
public attachControl(noPreventDefault?: boolean): void;
/**
* Attach the input controls to a specific dom element to get the input from.
* @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)
* BACK COMPAT SIGNATURE ONLY.
*/
public attachControl(ignored: any, noPreventDefault?: boolean): void;
/**
* Attach the input controls to a specific dom element to get the input from.
* This function is here because typescript removes the typing of the last function.
* @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)
*/
public attachControl(_ignored?: any, _noPreventDefault?: boolean): void {}
/**
* Detach the current controls from the specified dom element.
*/
public detachControl(): void;
/**
* Detach the current controls from the specified dom element.
* @param ignored defines an ignored parameter kept for backward compatibility.
*/
public detachControl(ignored?: any): void;
/**
* Detach the current controls from the specified dom element.
* This function is here because typescript removes the typing of the last function.
* @param _ignored defines an ignored parameter kept for backward compatibility.
*/
public detachControl(_ignored?: any): void {}
/**
* Update the camera state according to the different inputs gathered during the frame.
*/
public update(): void {
this._checkInputs();
if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
this._updateRigCameras();
}
// Attempt to update the camera's view and projection matrices.
// This call is being made because these matrices are no longer being updated
// as a part of the picking ray process (in addition to scene.render).
this.getViewMatrix();
this.getProjectionMatrix();
}
/** @internal */
public _checkInputs(): void {
this.onAfterCheckInputsObservable.notifyObservers(this);
}
/** @internal */
public get rigCameras(): Camera[] {
return this._rigCameras;
}
/**
* Gets the post process used by the rig cameras
*/
public get rigPostProcess(): Nullable<PostProcess> {
return this._rigPostProcess;
}
/**
* Internal, gets the first post process.
* @returns the first post process to be run on this camera.
*/
public _getFirstPostProcess(): Nullable<PostProcess> {
for (let ppIndex = 0; ppIndex < this._postProcesses.length; ppIndex++) {
if (this._postProcesses[ppIndex] !== null) {
return this._postProcesses[ppIndex];
}
}
return null;
}
private _cascadePostProcessesToRigCams(): void {
// invalidate framebuffer
const firstPostProcess = this._getFirstPostProcess();
if (firstPostProcess) {
firstPostProcess.markTextureDirty();
}
// glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
for (let i = 0, len = this._rigCameras.length; i < len; i++) {
const cam = this._rigCameras[i];
const rigPostProcess = cam._rigPostProcess;
// for VR rig, there does not have to be a post process
if (rigPostProcess) {
const isPass = rigPostProcess.getEffectName() === "pass";
if (isPass) {
// any rig which has a PassPostProcess for rig[0], cannot be isIntermediate when there are also user postProcesses
cam.isIntermediate = this._postProcesses.length === 0;
}
cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
rigPostProcess.markTextureDirty();
} else {
cam._postProcesses = this._postProcesses.slice(0);
}
}
}
/**
* Attach a post process to the camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/postProcesses/usePostProcesses#attach-postprocess
* @param postProcess The post process to attach to the camera
* @param insertAt The position of the post process in case several of them are in use in the scene
* @returns the position the post process has been inserted at
*/
public attachPostProcess(postProcess: PostProcess, insertAt: Nullable<number> = null): number {
if (!postProcess.isReusable() && this._postProcesses.indexOf(postProcess) > -1) {
Logger.Error("You're trying to reuse a post process not defined as reusable.");
return 0;
}
if (insertAt == null || insertAt < 0) {
this._postProcesses.push(postProcess);
} else if (this._postProcesses[insertAt] === null) {
this._postProcesses[insertAt] = postProcess;
} else {
this._postProcesses.splice(insertAt, 0, postProcess);
}
this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
// Update prePass
if (this._scene.prePassRenderer) {
this._scene.prePassRenderer.markAsDirty();
}
return this._postProcesses.indexOf(postProcess);
}
/**
* Detach a post process to the camera.
* @see https://doc.babylonjs.com/features/featuresDeepDive/postProcesses/usePostProcesses#attach-postprocess
* @param postProcess The post process to detach from the camera
*/
public detachPostProcess(postProcess: PostProcess): void {
const idx = this._postProcesses.indexOf(postProcess);
if (idx !== -1) {
this._postProcesses[idx] = null;
}
// Update prePass
if (this._scene.prePassRenderer) {
this._scene.prePassRenderer.markAsDirty();
}
this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
}
/**
* Gets the current world matrix of the camera
*/
public getWorldMatrix(): Matrix {
if (this._isSynchronizedViewMatrix()) {
return this._worldMatrix;
}
// Getting the the view matrix will also compute the world matrix.
this.getViewMatrix();
return this._worldMatrix;
}
/** @internal */
public _getViewMatrix(): Matrix {
return Matrix.Identity();
}
/**
* Gets the current view matrix of the camera.
* @param force forces the camera to recompute the matrix without looking at the cached state
* @returns the view matrix
*/
public getViewMatrix(force?: boolean): Matrix {
if (!force && this._isSynchronizedViewMatrix()) {
return this._computedViewMatrix;
}
this.updateCache();
this._computedViewMatrix = this._getViewMatrix();
this._currentRenderId = this.getScene().getRenderId();
this._childUpdateId++;
this._refreshFrustumPlanes = true;
if (this._cameraRigParams && this._cameraRigParams.vrPreViewMatrix) {
this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
}
// Notify parent camera if rig camera is changed
if (this.parent && (this.parent as Camera).onViewMatrixChangedObservable) {
(this.parent as Camera).onViewMatrixChangedObservable.notifyObservers(this.parent as Camera);
}
this.onViewMatrixChangedObservable.notifyObservers(this);
this._computedViewMatrix.invertToRef(this._worldMatrix);
return this._computedViewMatrix;
}
/**
* Freeze the projection matrix.
* It will prevent the cache check of the camera projection compute and can speed up perf
* if no parameter of the camera are meant to change
* @param projection Defines manually a projection if necessary
*/
public freezeProjectionMatrix(projection?: Matrix): void {
this._doNotComputeProjectionMatrix = true;
if (projection !== undefined) {
this._projectionMatrix = projection;
}
}
/**
* Unfreeze the projection matrix if it has previously been freezed by freezeProjectionMatrix.
*/
public unfreezeProjectionMatrix(): void {
this._doNotComputeProjectionMatrix = false;
}
/**
* Gets the current projection matrix of the camera.
* @param force forces the camera to recompute the matrix without looking at the cached state
* @returns the projection matrix
*/
public getProjectionMatrix(force?: boolean): Matrix {
if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
return this._projectionMatrix;
}
// Cache
this._cache.mode = this.mode;
this._cache.minZ = this.minZ;
this._cache.maxZ = this.maxZ;
// Matrix
this._refreshFrustumPlanes = true;
const engine = this.getEngine();
const scene = this.getScene();
const reverseDepth = engine.useReverseDepthBuffer;
if (this.mode === Camera.PERSPECTIVE_CAMERA) {
this._cache.fov = this.fov;
this._cache.fovMode = this.fovMode;
this._cache.aspectRatio = engine.getAspectRatio(this);
this._cache.projectionPlaneTilt = this.projectionPlaneTilt;
if (this.minZ <= 0) {
this.minZ = 0.1;
}
let getProjectionMatrix: (
fov: number,
aspect: number,
znear: number,
zfar: number,
result: Matrix,
isVerticalFovFixed: boolean,
halfZRange: boolean,
projectionPlaneTilt: number,
reverseDepthBufferMode: boolean
) => void;
if (scene.useRightHandedSystem) {
getProjectionMatrix = Matrix.PerspectiveFovRHToRef;
} else {
getProjectionMatrix = Matrix.PerspectiveFovLHToRef;
}
getProjectionMatrix(
this.fov,
engine.getAspectRatio(this),
reverseDepth ? this.maxZ : this.minZ,
reverseDepth ? this.minZ : this.maxZ,
this._projectionMatrix,
this.fovMode === Camera.FOVMODE_VERTICAL_FIXED,
engine.isNDCHalfZRange,
this.projectionPlaneTilt,
reverseDepth
);
} else {
const halfWidth = engine.getRenderWidth() / 2.0;
const halfHeight = engine.getRenderHeight() / 2.0;
if (scene.useRightHandedSystem) {
if (this.oblique) {
Matrix.ObliqueOffCenterRHToRef(
this.orthoLeft ?? -halfWidth,
this.orthoRight ?? halfWidth,
this.orthoBottom ?? -halfHeight,
this.orthoTop ?? halfHeight,
reverseDepth ? this.maxZ : this.minZ,
reverseDepth ? this.minZ : this.maxZ,
this.oblique.length,
this.oblique.angle,
this._computeObliqueDistance(this.oblique.offset),
this._projectionMatrix,
engine.isNDCHalfZRange
);
} else {
Matrix.OrthoOffCenterRHToRef(
this.orthoLeft ?? -halfWidth,
this.orthoRight ?? halfWidth,
this.orthoBottom ?? -halfHeight,
this.orthoTop ?? halfHeight,
reverseDepth ? this.maxZ : this.minZ,
reverseDepth ? this.minZ : this.maxZ,
this._projectionMatrix,
engine.isNDCHalfZRange
);
}
} else {
if (this.oblique) {
Matrix.ObliqueOffCenterLHToRef(
this.orthoLeft ?? -halfWidth,
this.orthoRight ?? halfWidth,
this.orthoBottom ?? -halfHeight,
this.orthoTop ?? halfHeight,
reverseDepth ? this.maxZ : this.minZ,
reverseDepth ? this.minZ : this.maxZ,
this.oblique.length,
this.oblique.angle,
this._computeObliqueDistance(this.oblique.offset),
this._projectionMatrix,
engine.isNDCHalfZRange
);
} else {
Matrix.OrthoOffCenterLHToRef(
this.orthoLeft ?? -halfWidth,
this.orthoRight ?? halfWidth,
this.orthoBottom ?? -halfHeight,
this.orthoTop ?? halfHeight,
reverseDepth ? this.maxZ : this.minZ,