@playcanvas/web-components 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +103 -32
- package/dist/components/camera-component.d.ts +13 -13
- package/dist/components/screen-component.d.ts +25 -4
- package/dist/components/scrollview-component.d.ts +6 -4
- package/dist/custom-elements.json +239 -96
- package/dist/entity.d.ts +13 -0
- package/dist/parse.d.ts +4 -2
- package/dist/pwc.cjs +308 -156
- package/dist/pwc.cjs.map +1 -1
- package/dist/pwc.js +308 -156
- package/dist/pwc.js.map +1 -1
- package/dist/pwc.min.js +1 -1
- package/dist/pwc.min.js.map +1 -1
- package/dist/pwc.min.mjs +1 -1
- package/dist/pwc.min.mjs.map +1 -1
- package/dist/pwc.mjs +309 -157
- package/dist/pwc.mjs.map +1 -1
- package/dist/sky.d.ts +12 -11
- package/dist/vscode.html-custom-data.json +40 -20
- package/dist/web-types.json +67 -48
- package/package.json +1 -1
- package/src/app.ts +189 -79
- package/src/components/camera-component.ts +31 -26
- package/src/components/screen-component.ts +42 -12
- package/src/components/scrollview-component.ts +6 -4
- package/src/entity.ts +35 -17
- package/src/parse.ts +5 -3
- package/src/sky.ts +26 -25
package/src/app.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CameraComponent,
|
|
5
5
|
createGraphicsDevice,
|
|
6
6
|
ElementInput,
|
|
7
|
+
Entity,
|
|
7
8
|
FILLMODE_FILL_WINDOW,
|
|
8
9
|
GraphNode,
|
|
9
10
|
Keyboard,
|
|
@@ -72,7 +73,7 @@ import { EntityElement } from './entity';
|
|
|
72
73
|
import { LoadingBar } from './loading-bar';
|
|
73
74
|
import { MaterialElement } from './material';
|
|
74
75
|
import { ModuleElement } from './module';
|
|
75
|
-
import { parseBool, parseEnum } from './parse';
|
|
76
|
+
import { parseBool, parseEnum, parseNumber } from './parse';
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
79
|
* The AppElement interface provides properties and methods for manipulating
|
|
@@ -97,18 +98,32 @@ class AppElement extends AsyncElement {
|
|
|
97
98
|
|
|
98
99
|
private _antialias = true;
|
|
99
100
|
|
|
100
|
-
private
|
|
101
|
+
private _depthBuffer = true;
|
|
101
102
|
|
|
102
|
-
private
|
|
103
|
+
private _stencilBuffer = true;
|
|
103
104
|
|
|
104
|
-
private
|
|
105
|
+
private _maxPixelRatio = Infinity;
|
|
105
106
|
|
|
106
107
|
private _loadingBar = true;
|
|
107
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Set once the graphics options above have been handed to `createGraphicsDevice`, after which
|
|
111
|
+
* writing any of them changes nothing. Guards the warning in {@link _warnIfBooted}, and is
|
|
112
|
+
* cleared on disconnect so a re-connected element boots from its current attributes.
|
|
113
|
+
*/
|
|
114
|
+
private _optionsLocked = false;
|
|
115
|
+
|
|
108
116
|
private _bar: LoadingBar | null = null;
|
|
109
117
|
|
|
110
118
|
private _hierarchyReady = false;
|
|
111
119
|
|
|
120
|
+
/**
|
|
121
|
+
* The elements backing this application's entities, keyed by the entity itself. Registered
|
|
122
|
+
* by EntityElement at creation and removed when an entity is destroyed, this joins engine
|
|
123
|
+
* scene nodes back to their owning elements by identity - never by name.
|
|
124
|
+
*/
|
|
125
|
+
private _entityElements = new Map<GraphNode, EntityElement>();
|
|
126
|
+
|
|
112
127
|
private _picker: Picker | null = null;
|
|
113
128
|
|
|
114
129
|
private _hasPointerListeners: { [key: string]: boolean } = {
|
|
@@ -192,15 +207,21 @@ class AppElement extends AsyncElement {
|
|
|
192
207
|
};
|
|
193
208
|
const deviceTypes = backendToDeviceTypes[this._backend] || [];
|
|
194
209
|
|
|
210
|
+
this._optionsLocked = true;
|
|
211
|
+
|
|
195
212
|
const device = await createGraphicsDevice(this._canvas, {
|
|
196
213
|
// @ts-ignore - alpha needs to be documented
|
|
197
214
|
alpha: this._alpha,
|
|
198
215
|
antialias: this._antialias,
|
|
199
|
-
depth: this.
|
|
216
|
+
depth: this._depthBuffer,
|
|
200
217
|
deviceTypes: deviceTypes,
|
|
201
|
-
stencil: this.
|
|
218
|
+
stencil: this._stencilBuffer
|
|
202
219
|
});
|
|
203
|
-
|
|
220
|
+
|
|
221
|
+
// Assigned rather than resolved to a number here: the engine caps against the live
|
|
222
|
+
// window.devicePixelRatio on every resize, so an uncapped Infinity keeps following the
|
|
223
|
+
// display when a window moves between monitors of differing density.
|
|
224
|
+
device.maxPixelRatio = this._maxPixelRatio;
|
|
204
225
|
|
|
205
226
|
const createOptions = new AppOptions();
|
|
206
227
|
createOptions.graphicsDevice = device;
|
|
@@ -344,13 +365,16 @@ class AppElement extends AsyncElement {
|
|
|
344
365
|
}
|
|
345
366
|
|
|
346
367
|
disconnectedCallback() {
|
|
368
|
+
this._optionsLocked = false;
|
|
347
369
|
this._pickerDestroy();
|
|
348
370
|
|
|
349
|
-
// Clean up the application
|
|
371
|
+
// Clean up the application. Destroying it destroys every entity, whose destroy hooks
|
|
372
|
+
// unregister them - clear() covers any entity the engine no longer reached.
|
|
350
373
|
if (this._app) {
|
|
351
374
|
this._app.destroy();
|
|
352
375
|
this._app = null;
|
|
353
376
|
}
|
|
377
|
+
this._entityElements.clear();
|
|
354
378
|
this._loadProgress = 0;
|
|
355
379
|
this._bar?.destroy();
|
|
356
380
|
this._bar = null;
|
|
@@ -427,6 +451,79 @@ class AppElement extends AsyncElement {
|
|
|
427
451
|
};
|
|
428
452
|
}
|
|
429
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Registers the element that created an entity. Called by EntityElement when it creates its
|
|
456
|
+
* entity.
|
|
457
|
+
*
|
|
458
|
+
* @param entity - The entity.
|
|
459
|
+
* @param element - The element that created it.
|
|
460
|
+
* @ignore
|
|
461
|
+
*/
|
|
462
|
+
_registerEntityElement(entity: Entity, element: EntityElement) {
|
|
463
|
+
this._entityElements.set(entity, element);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Removes the registration for a destroyed entity. Called by EntityElement.
|
|
468
|
+
*
|
|
469
|
+
* @param entity - The entity.
|
|
470
|
+
* @ignore
|
|
471
|
+
*/
|
|
472
|
+
_unregisterEntityElement(entity: Entity) {
|
|
473
|
+
this._entityElements.delete(entity);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Returns the `<pc-entity>` element whose backing entity is `entity`, or `null` if the
|
|
478
|
+
* entity was not created by an element of this application - for example, a node inside a
|
|
479
|
+
* model's instantiated hierarchy, or an entity created through the engine API.
|
|
480
|
+
*
|
|
481
|
+
* @param entity - The entity to look up.
|
|
482
|
+
* @returns The element backing the entity, or `null`.
|
|
483
|
+
*/
|
|
484
|
+
elementFromEntity(entity: Entity): EntityElement | null {
|
|
485
|
+
return this._entityElements.get(entity) ?? null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Resolves the element that owns a picked node: the nearest node up the parent chain -
|
|
490
|
+
* starting with the node itself - that was created by a `<pc-entity>` of this application.
|
|
491
|
+
* A hit inside a model's instantiated hierarchy therefore resolves to the element hosting
|
|
492
|
+
* the model.
|
|
493
|
+
*
|
|
494
|
+
* @param node - The picked node, or `null`.
|
|
495
|
+
* @returns The owning element, or `null`.
|
|
496
|
+
*/
|
|
497
|
+
private _elementFromNode(node: GraphNode | null): EntityElement | null {
|
|
498
|
+
while (node !== null) {
|
|
499
|
+
const element = this._entityElements.get(node);
|
|
500
|
+
if (element) {
|
|
501
|
+
return element;
|
|
502
|
+
}
|
|
503
|
+
node = node.parent;
|
|
504
|
+
}
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Like {@link _elementFromNode}, but skips elements without a listener for `type`, so a hit
|
|
510
|
+
* on an unlistened child still reaches a listening ancestor.
|
|
511
|
+
*
|
|
512
|
+
* @param node - The picked node, or `null`.
|
|
513
|
+
* @param type - The pointer event type a listener is required for.
|
|
514
|
+
* @returns The nearest listening element, or `null`.
|
|
515
|
+
*/
|
|
516
|
+
private _elementWithListener(node: GraphNode | null, type: string): EntityElement | null {
|
|
517
|
+
while (node !== null) {
|
|
518
|
+
const element = this._entityElements.get(node);
|
|
519
|
+
if (element?.hasListeners(type)) {
|
|
520
|
+
return element;
|
|
521
|
+
}
|
|
522
|
+
node = node.parent;
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
|
|
430
527
|
// New helper to convert CSS coordinates to canvas (picker) coordinates
|
|
431
528
|
private _getPickerCoordinates(event: PointerEvent): { x: number, y: number } {
|
|
432
529
|
// Get the canvas' bounding rectangle in CSS pixels.
|
|
@@ -475,17 +572,9 @@ class AppElement extends AsyncElement {
|
|
|
475
572
|
const node = await this._pickNode(event);
|
|
476
573
|
if (token !== this._pickToken || !this._picker) return;
|
|
477
574
|
|
|
478
|
-
//
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
while (currentNode !== null) {
|
|
482
|
-
const entityElement = this.querySelector(`pc-entity[name="${currentNode.name}"]`) as EntityElement;
|
|
483
|
-
if (entityElement) {
|
|
484
|
-
newHoverEntity = entityElement;
|
|
485
|
-
break;
|
|
486
|
-
}
|
|
487
|
-
currentNode = currentNode.parent;
|
|
488
|
-
}
|
|
575
|
+
// The hovered element is the nearest one up the node's parent chain, listening or not -
|
|
576
|
+
// dispatch is gated per event type below
|
|
577
|
+
const newHoverEntity = this._elementFromNode(node);
|
|
489
578
|
|
|
490
579
|
// Handle enter/leave events
|
|
491
580
|
if (this._hoveredEntity !== newHoverEntity) {
|
|
@@ -509,16 +598,12 @@ class AppElement extends AsyncElement {
|
|
|
509
598
|
async _onPointerDown(event: PointerEvent) {
|
|
510
599
|
if (!this._picker || !this.app) return;
|
|
511
600
|
|
|
512
|
-
|
|
601
|
+
const node = await this._pickNode(event);
|
|
513
602
|
if (!this._picker) return; // the element disconnected while the pick was in flight
|
|
514
603
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
entityElement.dispatchEvent(new PointerEvent('pointerdown', event));
|
|
519
|
-
break;
|
|
520
|
-
}
|
|
521
|
-
currentNode = currentNode.parent;
|
|
604
|
+
const entityElement = this._elementWithListener(node, 'pointerdown');
|
|
605
|
+
if (entityElement) {
|
|
606
|
+
entityElement.dispatchEvent(new PointerEvent('pointerdown', event));
|
|
522
607
|
}
|
|
523
608
|
}
|
|
524
609
|
|
|
@@ -526,10 +611,10 @@ class AppElement extends AsyncElement {
|
|
|
526
611
|
if (!this._picker || !this.app) return;
|
|
527
612
|
|
|
528
613
|
const node = await this._pickNode(event);
|
|
529
|
-
if (!
|
|
614
|
+
if (!this._picker) return; // the element disconnected while the pick was in flight
|
|
530
615
|
|
|
531
|
-
const entityElement = this.
|
|
532
|
-
if (entityElement
|
|
616
|
+
const entityElement = this._elementWithListener(node, 'pointerup');
|
|
617
|
+
if (entityElement) {
|
|
533
618
|
entityElement.dispatchEvent(new PointerEvent('pointerup', event));
|
|
534
619
|
}
|
|
535
620
|
}
|
|
@@ -567,15 +652,30 @@ class AppElement extends AsyncElement {
|
|
|
567
652
|
}
|
|
568
653
|
|
|
569
654
|
/**
|
|
570
|
-
*
|
|
655
|
+
* Warns that a graphics option was written too late to have any effect. These options are read
|
|
656
|
+
* once, when the element connects and creates its graphics device, so a later write updates
|
|
657
|
+
* only the element's own property - silently, without this.
|
|
658
|
+
*
|
|
659
|
+
* @param name - The name of the option, as its attribute.
|
|
660
|
+
*/
|
|
661
|
+
private _warnIfBooted(name: string) {
|
|
662
|
+
if (this._optionsLocked) {
|
|
663
|
+
console.warn(`Attribute '${name}' on <pc-app> is only read when the application boots, so this change has no effect. Set it before the element is connected, or remove and re-insert the element to reboot with the new value.`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Sets whether the frame buffer has an alpha channel, which is what lets the page show through
|
|
669
|
+
* wherever the scene has not drawn. Read only when the application boots.
|
|
571
670
|
* @param value - The alpha flag.
|
|
572
671
|
*/
|
|
573
672
|
set alpha(value: boolean) {
|
|
673
|
+
this._warnIfBooted('alpha');
|
|
574
674
|
this._alpha = value;
|
|
575
675
|
}
|
|
576
676
|
|
|
577
677
|
/**
|
|
578
|
-
* Gets the alpha
|
|
678
|
+
* Gets whether the frame buffer has an alpha channel.
|
|
579
679
|
* @returns The alpha flag.
|
|
580
680
|
*/
|
|
581
681
|
get alpha() {
|
|
@@ -583,15 +683,16 @@ class AppElement extends AsyncElement {
|
|
|
583
683
|
}
|
|
584
684
|
|
|
585
685
|
/**
|
|
586
|
-
* Sets the
|
|
686
|
+
* Sets whether the frame buffer is anti-aliased. Read only when the application boots.
|
|
587
687
|
* @param value - The antialias flag.
|
|
588
688
|
*/
|
|
589
689
|
set antialias(value: boolean) {
|
|
690
|
+
this._warnIfBooted('antialias');
|
|
590
691
|
this._antialias = value;
|
|
591
692
|
}
|
|
592
693
|
|
|
593
694
|
/**
|
|
594
|
-
* Gets the
|
|
695
|
+
* Gets whether the frame buffer is anti-aliased.
|
|
595
696
|
* @returns The antialias flag.
|
|
596
697
|
*/
|
|
597
698
|
get antialias() {
|
|
@@ -600,10 +701,11 @@ class AppElement extends AsyncElement {
|
|
|
600
701
|
|
|
601
702
|
/**
|
|
602
703
|
* Sets the graphics backend. Defaults to 'webgpu', which falls back to 'webgl2' if WebGPU
|
|
603
|
-
* is not supported by the browser.
|
|
704
|
+
* is not supported by the browser. Read only when the application boots.
|
|
604
705
|
* @param value - The graphics backend ('webgpu', 'webgl2', or 'null').
|
|
605
706
|
*/
|
|
606
707
|
set backend(value: 'webgpu' | 'webgl2' | 'null') {
|
|
708
|
+
this._warnIfBooted('backend');
|
|
607
709
|
this._backend = value;
|
|
608
710
|
}
|
|
609
711
|
|
|
@@ -616,19 +718,21 @@ class AppElement extends AsyncElement {
|
|
|
616
718
|
}
|
|
617
719
|
|
|
618
720
|
/**
|
|
619
|
-
* Sets the depth
|
|
620
|
-
*
|
|
721
|
+
* Sets whether the frame buffer has a depth buffer, which the renderer needs to resolve which
|
|
722
|
+
* surface is nearest the camera. Read only when the application boots.
|
|
723
|
+
* @param value - The depth buffer flag.
|
|
621
724
|
*/
|
|
622
|
-
set
|
|
623
|
-
this.
|
|
725
|
+
set depthBuffer(value: boolean) {
|
|
726
|
+
this._warnIfBooted('depth-buffer');
|
|
727
|
+
this._depthBuffer = value;
|
|
624
728
|
}
|
|
625
729
|
|
|
626
730
|
/**
|
|
627
|
-
* Gets the depth
|
|
628
|
-
* @returns The depth flag.
|
|
731
|
+
* Gets whether the frame buffer has a depth buffer.
|
|
732
|
+
* @returns The depth buffer flag.
|
|
629
733
|
*/
|
|
630
|
-
get
|
|
631
|
-
return this.
|
|
734
|
+
get depthBuffer() {
|
|
735
|
+
return this._depthBuffer;
|
|
632
736
|
}
|
|
633
737
|
|
|
634
738
|
/**
|
|
@@ -640,26 +744,6 @@ class AppElement extends AsyncElement {
|
|
|
640
744
|
return this._hierarchyReady;
|
|
641
745
|
}
|
|
642
746
|
|
|
643
|
-
/**
|
|
644
|
-
* Sets the high resolution flag. When true, the application will render at the device's
|
|
645
|
-
* physical resolution. When false, the application will render at CSS resolution.
|
|
646
|
-
* @param value - The high resolution flag.
|
|
647
|
-
*/
|
|
648
|
-
set highResolution(value: boolean) {
|
|
649
|
-
this._highResolution = value;
|
|
650
|
-
if (this.app) {
|
|
651
|
-
this.app.graphicsDevice.maxPixelRatio = value ? window.devicePixelRatio : 1;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/**
|
|
656
|
-
* Gets the high resolution flag.
|
|
657
|
-
* @returns The high resolution flag.
|
|
658
|
-
*/
|
|
659
|
-
get highResolution() {
|
|
660
|
-
return this._highResolution;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
747
|
/**
|
|
664
748
|
* Sets whether the application shows its built-in loading bar while it boots and preloads its
|
|
665
749
|
* assets. Enabled by default; setting `false` removes the bar immediately, while setting
|
|
@@ -686,23 +770,49 @@ class AppElement extends AsyncElement {
|
|
|
686
770
|
}
|
|
687
771
|
|
|
688
772
|
/**
|
|
689
|
-
* Sets the
|
|
690
|
-
*
|
|
773
|
+
* Sets the cap on the pixel ratio the application renders at. The canvas is sized by the
|
|
774
|
+
* smaller of this value and the display's own device pixel ratio, so the default of `Infinity`
|
|
775
|
+
* renders at full physical resolution, `1` renders at CSS resolution, and an intermediate
|
|
776
|
+
* value such as `2` keeps a dense display sharp without paying for every one of its pixels.
|
|
777
|
+
* Must be greater than 0. Unlike the other graphics options, this applies immediately.
|
|
778
|
+
* @param value - The maximum pixel ratio.
|
|
779
|
+
*/
|
|
780
|
+
set maxPixelRatio(value: number) {
|
|
781
|
+
this._maxPixelRatio = value;
|
|
782
|
+
if (this.app) {
|
|
783
|
+
this.app.graphicsDevice.maxPixelRatio = value;
|
|
784
|
+
this.app.resizeCanvas();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Gets the cap on the pixel ratio the application renders at.
|
|
790
|
+
* @returns The maximum pixel ratio.
|
|
691
791
|
*/
|
|
692
|
-
|
|
693
|
-
this.
|
|
792
|
+
get maxPixelRatio() {
|
|
793
|
+
return this._maxPixelRatio;
|
|
694
794
|
}
|
|
695
795
|
|
|
696
796
|
/**
|
|
697
|
-
*
|
|
698
|
-
*
|
|
797
|
+
* Sets whether the frame buffer has a stencil buffer, which stencil-based effects and UI
|
|
798
|
+
* masking need. Read only when the application boots.
|
|
799
|
+
* @param value - The stencil buffer flag.
|
|
699
800
|
*/
|
|
700
|
-
|
|
701
|
-
|
|
801
|
+
set stencilBuffer(value: boolean) {
|
|
802
|
+
this._warnIfBooted('stencil-buffer');
|
|
803
|
+
this._stencilBuffer = value;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Gets whether the frame buffer has a stencil buffer.
|
|
808
|
+
* @returns The stencil buffer flag.
|
|
809
|
+
*/
|
|
810
|
+
get stencilBuffer() {
|
|
811
|
+
return this._stencilBuffer;
|
|
702
812
|
}
|
|
703
813
|
|
|
704
814
|
static get observedAttributes() {
|
|
705
|
-
return ['alpha', 'antialias', 'backend', 'depth', '
|
|
815
|
+
return ['alpha', 'antialias', 'backend', 'depth-buffer', 'loading-bar', 'max-pixel-ratio', 'stencil-buffer'];
|
|
706
816
|
}
|
|
707
817
|
|
|
708
818
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
@@ -716,17 +826,17 @@ class AppElement extends AsyncElement {
|
|
|
716
826
|
case 'backend':
|
|
717
827
|
this.backend = parseEnum(newValue, ['webgpu', 'webgl2', 'null'], 'webgpu', name);
|
|
718
828
|
break;
|
|
719
|
-
case 'depth':
|
|
720
|
-
this.
|
|
721
|
-
break;
|
|
722
|
-
case 'high-resolution':
|
|
723
|
-
this.highResolution = parseBool(newValue, true);
|
|
829
|
+
case 'depth-buffer':
|
|
830
|
+
this.depthBuffer = parseBool(newValue, true);
|
|
724
831
|
break;
|
|
725
832
|
case 'loading-bar':
|
|
726
833
|
this.loadingBar = parseBool(newValue, true);
|
|
727
834
|
break;
|
|
728
|
-
case '
|
|
729
|
-
this.
|
|
835
|
+
case 'max-pixel-ratio':
|
|
836
|
+
this.maxPixelRatio = parseNumber(newValue, Infinity, name);
|
|
837
|
+
break;
|
|
838
|
+
case 'stencil-buffer':
|
|
839
|
+
this.stencilBuffer = parseBool(newValue, true);
|
|
730
840
|
break;
|
|
731
841
|
}
|
|
732
842
|
}
|
|
@@ -3,6 +3,11 @@ import { CameraComponent, Color, Vec4, GAMMA_NONE, GAMMA_SRGB, PROJECTION_ORTHOG
|
|
|
3
3
|
import { ComponentElement } from './component';
|
|
4
4
|
import { parseBool, parseColor, parseEnum, parseNumber, parseVec4 } from '../parse';
|
|
5
5
|
|
|
6
|
+
const projections = new Map<'perspective' | 'orthographic', number>([
|
|
7
|
+
['perspective', PROJECTION_PERSPECTIVE],
|
|
8
|
+
['orthographic', PROJECTION_ORTHOGRAPHIC]
|
|
9
|
+
]);
|
|
10
|
+
|
|
6
11
|
const tonemaps = new Map<'none' | 'linear' | 'filmic' | 'hejl' | 'aces' | 'aces2' | 'neutral', number>([
|
|
7
12
|
['none', TONEMAP_NONE],
|
|
8
13
|
['linear', TONEMAP_LINEAR],
|
|
@@ -46,7 +51,7 @@ class CameraComponentElement extends ComponentElement {
|
|
|
46
51
|
|
|
47
52
|
private _nearClip = 0.1;
|
|
48
53
|
|
|
49
|
-
private
|
|
54
|
+
private _projection: 'perspective' | 'orthographic' = 'perspective';
|
|
50
55
|
|
|
51
56
|
private _orthoHeight = 10;
|
|
52
57
|
|
|
@@ -77,12 +82,12 @@ class CameraComponentElement extends ComponentElement {
|
|
|
77
82
|
gammaCorrection: this._gamma === 'srgb' ? GAMMA_SRGB : GAMMA_NONE,
|
|
78
83
|
horizontalFov: this._horizontalFov,
|
|
79
84
|
nearClip: this._nearClip,
|
|
80
|
-
projection: this.
|
|
85
|
+
projection: projections.get(this._projection) ?? PROJECTION_PERSPECTIVE,
|
|
81
86
|
orthoHeight: this._orthoHeight,
|
|
82
87
|
priority: this._priority,
|
|
83
88
|
rect: this._rect,
|
|
84
89
|
scissorRect: this._scissorRect,
|
|
85
|
-
toneMapping: tonemaps.get(this._tonemap)
|
|
90
|
+
toneMapping: tonemaps.get(this._tonemap) ?? TONEMAP_NONE
|
|
86
91
|
};
|
|
87
92
|
}
|
|
88
93
|
|
|
@@ -352,25 +357,6 @@ class CameraComponentElement extends ComponentElement {
|
|
|
352
357
|
return this._nearClip;
|
|
353
358
|
}
|
|
354
359
|
|
|
355
|
-
/**
|
|
356
|
-
* Sets the orthographic projection of the camera.
|
|
357
|
-
* @param value - The orthographic projection.
|
|
358
|
-
*/
|
|
359
|
-
set orthographic(value) {
|
|
360
|
-
this._orthographic = value;
|
|
361
|
-
if (this.component) {
|
|
362
|
-
this.component.projection = value ? PROJECTION_ORTHOGRAPHIC : PROJECTION_PERSPECTIVE;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Gets the orthographic projection of the camera.
|
|
368
|
-
* @returns The orthographic projection.
|
|
369
|
-
*/
|
|
370
|
-
get orthographic(): boolean {
|
|
371
|
-
return this._orthographic;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
360
|
/**
|
|
375
361
|
* Sets the orthographic height of the camera.
|
|
376
362
|
* @param value - The orthographic height.
|
|
@@ -409,6 +395,25 @@ class CameraComponentElement extends ComponentElement {
|
|
|
409
395
|
return this._priority;
|
|
410
396
|
}
|
|
411
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Sets the projection of the camera. Use `orthoHeight` to size an orthographic projection.
|
|
400
|
+
* @param value - The projection ('perspective' or 'orthographic').
|
|
401
|
+
*/
|
|
402
|
+
set projection(value: 'perspective' | 'orthographic') {
|
|
403
|
+
this._projection = value;
|
|
404
|
+
if (this.component) {
|
|
405
|
+
this.component.projection = projections.get(value) ?? PROJECTION_PERSPECTIVE;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Gets the projection of the camera.
|
|
411
|
+
* @returns The projection.
|
|
412
|
+
*/
|
|
413
|
+
get projection() {
|
|
414
|
+
return this._projection;
|
|
415
|
+
}
|
|
416
|
+
|
|
412
417
|
/**
|
|
413
418
|
* Sets the rect of the camera.
|
|
414
419
|
* @param value - The rect.
|
|
@@ -481,9 +486,9 @@ class CameraComponentElement extends ComponentElement {
|
|
|
481
486
|
'gamma',
|
|
482
487
|
'horizontal-fov',
|
|
483
488
|
'near-clip',
|
|
484
|
-
'orthographic',
|
|
485
489
|
'ortho-height',
|
|
486
490
|
'priority',
|
|
491
|
+
'projection',
|
|
487
492
|
'rect',
|
|
488
493
|
'scissor-rect',
|
|
489
494
|
'tonemap'
|
|
@@ -530,15 +535,15 @@ class CameraComponentElement extends ComponentElement {
|
|
|
530
535
|
case 'near-clip':
|
|
531
536
|
this.nearClip = parseNumber(newValue, 0.1, name);
|
|
532
537
|
break;
|
|
533
|
-
case 'orthographic':
|
|
534
|
-
this.orthographic = parseBool(newValue, false);
|
|
535
|
-
break;
|
|
536
538
|
case 'ortho-height':
|
|
537
539
|
this.orthoHeight = parseNumber(newValue, 10, name);
|
|
538
540
|
break;
|
|
539
541
|
case 'priority':
|
|
540
542
|
this.priority = parseNumber(newValue, 0, name);
|
|
541
543
|
break;
|
|
544
|
+
case 'projection':
|
|
545
|
+
this.projection = parseEnum(newValue, projections, 'perspective', name);
|
|
546
|
+
break;
|
|
542
547
|
case 'rect':
|
|
543
548
|
this.rect = parseVec4(newValue, new Vec4(0, 0, 1, 1), name);
|
|
544
549
|
break;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { SCALEMODE_BLEND, SCALEMODE_NONE, ScreenComponent, Vec2 } from 'playcanvas';
|
|
2
2
|
|
|
3
3
|
import { ComponentElement } from './component';
|
|
4
|
-
import { parseBool, parseNumber, parseVec2 } from '../parse';
|
|
4
|
+
import { parseBool, parseEnum, parseNumber, parseVec2 } from '../parse';
|
|
5
|
+
|
|
6
|
+
// The engine's SCALEMODE_* constants are the strings 'none' and 'blend', so this map happens to be
|
|
7
|
+
// an identity. It is still the right shape: it supplies parseEnum's valid-name list, it is what the
|
|
8
|
+
// manifest generator reads the enum values from, and it keeps the attribute vocabulary independent
|
|
9
|
+
// of constants the engine is free to change.
|
|
10
|
+
const scaleModes = new Map<'none' | 'blend', string>([
|
|
11
|
+
['none', SCALEMODE_NONE],
|
|
12
|
+
['blend', SCALEMODE_BLEND]
|
|
13
|
+
]);
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
16
|
* The ScreenComponentElement interface provides properties and methods for manipulating
|
|
@@ -20,7 +29,7 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
20
29
|
|
|
21
30
|
private _priority = 0;
|
|
22
31
|
|
|
23
|
-
private
|
|
32
|
+
private _scaleMode: 'none' | 'blend' = 'none';
|
|
24
33
|
|
|
25
34
|
private _scaleBlend = 0.5;
|
|
26
35
|
|
|
@@ -35,7 +44,7 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
35
44
|
referenceResolution: this._referenceResolution,
|
|
36
45
|
resolution: this._resolution,
|
|
37
46
|
scaleBlend: this._scaleBlend,
|
|
38
|
-
scaleMode: this.
|
|
47
|
+
scaleMode: scaleModes.get(this._scaleMode) ?? SCALEMODE_NONE,
|
|
39
48
|
screenSpace: this._screenSpace
|
|
40
49
|
};
|
|
41
50
|
}
|
|
@@ -81,6 +90,12 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
81
90
|
return this._resolution;
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Sets how the screen's `resolution` and `referenceResolution` are weighted against each other
|
|
95
|
+
* when `scaleMode` is `blend`, from 0 (follow the resolution) to 1 (follow the reference
|
|
96
|
+
* resolution). Ignored while `scaleMode` is `none`.
|
|
97
|
+
* @param value - The scale blend factor.
|
|
98
|
+
*/
|
|
84
99
|
set scaleBlend(value: number) {
|
|
85
100
|
this._scaleBlend = value;
|
|
86
101
|
if (this.component) {
|
|
@@ -88,19 +103,34 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
88
103
|
}
|
|
89
104
|
}
|
|
90
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Gets how the screen's resolutions are weighted against each other.
|
|
108
|
+
* @returns The scale blend factor.
|
|
109
|
+
*/
|
|
91
110
|
get scaleBlend() {
|
|
92
111
|
return this._scaleBlend;
|
|
93
112
|
}
|
|
94
113
|
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Sets how the screen scales its contents. `none` renders at `resolution` and ignores
|
|
116
|
+
* `referenceResolution`; `blend` scales between the two, weighted by `scaleBlend`, which is what
|
|
117
|
+
* keeps a UI laid out at one resolution usable at another. Requires `screenSpace` - the engine
|
|
118
|
+
* forces `none` on a world-space screen, which does not support scaling.
|
|
119
|
+
* @param value - The scale mode ('none' or 'blend').
|
|
120
|
+
*/
|
|
121
|
+
set scaleMode(value: 'none' | 'blend') {
|
|
122
|
+
this._scaleMode = value;
|
|
97
123
|
if (this.component) {
|
|
98
|
-
this.component.scaleMode =
|
|
124
|
+
this.component.scaleMode = scaleModes.get(value) ?? SCALEMODE_NONE;
|
|
99
125
|
}
|
|
100
126
|
}
|
|
101
127
|
|
|
102
|
-
|
|
103
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Gets how the screen scales its contents.
|
|
130
|
+
* @returns The scale mode.
|
|
131
|
+
*/
|
|
132
|
+
get scaleMode() {
|
|
133
|
+
return this._scaleMode;
|
|
104
134
|
}
|
|
105
135
|
|
|
106
136
|
set screenSpace(value: boolean) {
|
|
@@ -117,12 +147,12 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
117
147
|
static get observedAttributes() {
|
|
118
148
|
return [
|
|
119
149
|
...super.observedAttributes,
|
|
120
|
-
'blend',
|
|
121
150
|
'screen-space',
|
|
122
151
|
'resolution',
|
|
123
152
|
'reference-resolution',
|
|
124
153
|
'priority',
|
|
125
|
-
'scale-blend'
|
|
154
|
+
'scale-blend',
|
|
155
|
+
'scale-mode'
|
|
126
156
|
];
|
|
127
157
|
}
|
|
128
158
|
|
|
@@ -142,8 +172,8 @@ class ScreenComponentElement extends ComponentElement {
|
|
|
142
172
|
case 'scale-blend':
|
|
143
173
|
this.scaleBlend = parseNumber(newValue, 0.5, name);
|
|
144
174
|
break;
|
|
145
|
-
case '
|
|
146
|
-
this.
|
|
175
|
+
case 'scale-mode':
|
|
176
|
+
this.scaleMode = parseEnum(newValue, scaleModes, 'none', name);
|
|
147
177
|
break;
|
|
148
178
|
case 'screen-space':
|
|
149
179
|
this.screenSpace = parseBool(newValue, false);
|