@3dsource/angular-unreal-module 0.0.132 → 0.0.133
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.
|
@@ -4493,6 +4493,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
4493
4493
|
}], ctorParameters: () => [] });
|
|
4494
4494
|
|
|
4495
4495
|
const INITIAL_FINGER_IDS = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
|
4496
|
+
const UNSIGNED_RANGE = 65536;
|
|
4497
|
+
const UNSIGNED_OUT_OF_RANGE = 65535;
|
|
4498
|
+
const SIGNED_RANGE = 32767;
|
|
4499
|
+
const WHEEL_DELTA_SCALE = -120;
|
|
4500
|
+
const RESIZE_DEBOUNCE_MS = 300;
|
|
4501
|
+
const FUNCTION_KEY_MIN = 112;
|
|
4502
|
+
const FUNCTION_KEY_MAX = 123;
|
|
4503
|
+
const TAB_KEY_CODE = 9;
|
|
4496
4504
|
class InputService extends SubService {
|
|
4497
4505
|
constructor() {
|
|
4498
4506
|
super();
|
|
@@ -4522,19 +4530,14 @@ class InputService extends SubService {
|
|
|
4522
4530
|
this.deactivatedVideoTrigger$ = NEVER;
|
|
4523
4531
|
this.normalizeAndQuantizeUnsigned = (_x, _y) => ({
|
|
4524
4532
|
inRange: false,
|
|
4525
|
-
x:
|
|
4526
|
-
y:
|
|
4533
|
+
x: UNSIGNED_OUT_OF_RANGE,
|
|
4534
|
+
y: UNSIGNED_OUT_OF_RANGE,
|
|
4527
4535
|
});
|
|
4528
4536
|
this.normalizeAndQuantizeSigned = (_x, _y) => ({ x: 0, y: 0 });
|
|
4529
4537
|
this.videoBoundingRect = new DOMRect(0, 0, 1, 1);
|
|
4530
4538
|
this.fingerIds = new Map();
|
|
4531
4539
|
this.maxByteValue = 255;
|
|
4532
4540
|
this.fingers = [...INITIAL_FINGER_IDS];
|
|
4533
|
-
// Captured straight from normalizeCoordinates inside the emit* methods —
|
|
4534
|
-
// i.e., the exact quantized values that just went out over the data channel.
|
|
4535
|
-
// Resending these on resize avoids any re-transformation through stale
|
|
4536
|
-
// offsetParams or container dimensions.
|
|
4537
|
-
this.lastNormalizedCoordinates = null;
|
|
4538
4541
|
this.offsetParams = {
|
|
4539
4542
|
scaleX: 1,
|
|
4540
4543
|
scaleY: 1,
|
|
@@ -4573,26 +4576,11 @@ class InputService extends SubService {
|
|
|
4573
4576
|
this.fingerIds.clear();
|
|
4574
4577
|
this.fingers = [...INITIAL_FINGER_IDS];
|
|
4575
4578
|
merge(fromResizeObserver(video), this.actions$.pipe(ofType(videoAdaptedToContainer)))
|
|
4576
|
-
.pipe(debounceTime$1(
|
|
4577
|
-
.subscribe(() =>
|
|
4578
|
-
this.setupNormalizeAndQuantize(video);
|
|
4579
|
-
this.resendLastMousePosition();
|
|
4580
|
-
});
|
|
4579
|
+
.pipe(debounceTime$1(RESIZE_DEBOUNCE_MS), takeUntil$1(this.deactivatedVideoTrigger$))
|
|
4580
|
+
.subscribe(() => this.setupNormalizeAndQuantize(video));
|
|
4581
4581
|
this.setupNormalizeAndQuantize(video);
|
|
4582
4582
|
this.registerInputs(video);
|
|
4583
4583
|
}
|
|
4584
|
-
resendLastMousePosition() {
|
|
4585
|
-
const last = this.lastNormalizedCoordinates;
|
|
4586
|
-
if (!last)
|
|
4587
|
-
return;
|
|
4588
|
-
const data = new DataView(new ArrayBuffer(9));
|
|
4589
|
-
data.setUint8(0, MessageType.MouseMove);
|
|
4590
|
-
data.setUint16(1, last.x, true);
|
|
4591
|
-
data.setUint16(3, last.y, true);
|
|
4592
|
-
data.setInt16(5, 0, true);
|
|
4593
|
-
data.setInt16(7, 0, true);
|
|
4594
|
-
this.sendInputData(data.buffer);
|
|
4595
|
-
}
|
|
4596
4584
|
/**
|
|
4597
4585
|
* We use `object-fit: cover` on the video element. Since the video can have
|
|
4598
4586
|
* a different aspect ratio from its container (and potentially be larger),
|
|
@@ -4610,18 +4598,13 @@ class InputService extends SubService {
|
|
|
4610
4598
|
videoWidth,
|
|
4611
4599
|
videoHeight,
|
|
4612
4600
|
});
|
|
4613
|
-
// Compute aspect ratios
|
|
4614
4601
|
const containerRatio = width / height;
|
|
4615
4602
|
const videoRatio = videoWidth / videoHeight;
|
|
4616
|
-
// Determine how much to scale the video to fit the container (cover mode)
|
|
4617
4603
|
const scale = videoRatio > containerRatio ? height / videoHeight : width / videoWidth;
|
|
4618
|
-
// Calculate the displayed (scaled) dimensions of the video
|
|
4619
4604
|
const scaledWidth = videoWidth * scale;
|
|
4620
4605
|
const scaledHeight = videoHeight * scale;
|
|
4621
|
-
// Offsets in x/y directions (like "padding") once the video is scaled
|
|
4622
4606
|
const offsetByX = (width - scaledWidth) / 2;
|
|
4623
4607
|
const offsetByY = (height - scaledHeight) / 2;
|
|
4624
|
-
// Scale factors for coordinate transformation from container space to video space
|
|
4625
4608
|
const scaleX = width / scaledWidth;
|
|
4626
4609
|
const scaleY = height / scaledHeight;
|
|
4627
4610
|
return {
|
|
@@ -4637,14 +4620,13 @@ class InputService extends SubService {
|
|
|
4637
4620
|
this.videoBoundingRect = videoElement.getBoundingClientRect();
|
|
4638
4621
|
this.offsetParams = this.getObjectFitOffset(videoElement);
|
|
4639
4622
|
// Unsigned XY positions are the ratio (0.0..1.0) along a viewport axis,
|
|
4640
|
-
// quantized into
|
|
4623
|
+
// quantized into a uint16 (0..65536).
|
|
4641
4624
|
// Signed XY deltas are the ratio (-1.0..1.0) along a viewport axis,
|
|
4642
4625
|
// quantized into an int16 (-32767..32767).
|
|
4643
|
-
// This allows the browser viewport and client viewport to have
|
|
4644
|
-
//
|
|
4645
|
-
//
|
|
4646
|
-
//
|
|
4647
|
-
// precisely inside a video with an aspect ratio which causes mattes.
|
|
4626
|
+
// This allows the browser viewport and client viewport to have different sizes.
|
|
4627
|
+
// Hack: an out-of-range position becomes UNSIGNED_OUT_OF_RANGE because we
|
|
4628
|
+
// can't yet accurately detect mouse enter/leave precisely inside a video
|
|
4629
|
+
// with an aspect ratio that causes mattes.
|
|
4648
4630
|
this.normalizeAndQuantizeUnsigned = (x, y) => {
|
|
4649
4631
|
const normalizedX = x / videoElement.clientWidth;
|
|
4650
4632
|
const normalizedY = y / videoElement.clientHeight;
|
|
@@ -4654,25 +4636,22 @@ class InputService extends SubService {
|
|
|
4654
4636
|
normalizedY > 1.0) {
|
|
4655
4637
|
return {
|
|
4656
4638
|
inRange: false,
|
|
4657
|
-
x:
|
|
4658
|
-
y:
|
|
4659
|
-
};
|
|
4660
|
-
}
|
|
4661
|
-
else {
|
|
4662
|
-
return {
|
|
4663
|
-
inRange: true,
|
|
4664
|
-
x: normalizedX * 65536,
|
|
4665
|
-
y: normalizedY * 65536,
|
|
4639
|
+
x: UNSIGNED_OUT_OF_RANGE,
|
|
4640
|
+
y: UNSIGNED_OUT_OF_RANGE,
|
|
4666
4641
|
};
|
|
4667
4642
|
}
|
|
4643
|
+
return {
|
|
4644
|
+
inRange: true,
|
|
4645
|
+
x: normalizedX * UNSIGNED_RANGE,
|
|
4646
|
+
y: normalizedY * UNSIGNED_RANGE,
|
|
4647
|
+
};
|
|
4668
4648
|
};
|
|
4669
|
-
// Signed.
|
|
4670
4649
|
this.normalizeAndQuantizeSigned = (x, y) => {
|
|
4671
4650
|
const normalizedX = x / (0.5 * videoElement.clientWidth);
|
|
4672
4651
|
const normalizedY = y / (0.5 * videoElement.clientHeight);
|
|
4673
4652
|
return {
|
|
4674
|
-
x: normalizedX *
|
|
4675
|
-
y: normalizedY *
|
|
4653
|
+
x: normalizedX * SIGNED_RANGE,
|
|
4654
|
+
y: normalizedY * SIGNED_RANGE,
|
|
4676
4655
|
};
|
|
4677
4656
|
};
|
|
4678
4657
|
}
|
|
@@ -4695,7 +4674,7 @@ class InputService extends SubService {
|
|
|
4695
4674
|
y: (clientY - offsetByY - this.videoBoundingRect.top) * scaleY,
|
|
4696
4675
|
};
|
|
4697
4676
|
}
|
|
4698
|
-
getMousePositionWithOffset({ offsetX, offsetY
|
|
4677
|
+
getMousePositionWithOffset({ offsetX, offsetY }) {
|
|
4699
4678
|
const { offsetByX, offsetByY, scaleX, scaleY } = this.offsetParams;
|
|
4700
4679
|
return {
|
|
4701
4680
|
x: (offsetX - offsetByX) * scaleX,
|
|
@@ -4703,8 +4682,6 @@ class InputService extends SubService {
|
|
|
4703
4682
|
};
|
|
4704
4683
|
}
|
|
4705
4684
|
registerTouchEvents(htmlVideoElement) {
|
|
4706
|
-
// We need to assign a unique identifier to each finger.
|
|
4707
|
-
// We do this by mapping each Touch object to the identifier.
|
|
4708
4685
|
const rememberTouch = (touch) => {
|
|
4709
4686
|
const finger = this.fingers.pop();
|
|
4710
4687
|
if (finger === undefined) {
|
|
@@ -4715,39 +4692,41 @@ class InputService extends SubService {
|
|
|
4715
4692
|
};
|
|
4716
4693
|
const forgetTouch = (touch) => {
|
|
4717
4694
|
const finger = this.fingerIds.get(touch.identifier);
|
|
4718
|
-
if (finger) {
|
|
4695
|
+
if (finger !== undefined) {
|
|
4719
4696
|
this.fingers.push(finger);
|
|
4697
|
+
// Re-sort descending so the lowest available id is popped next.
|
|
4698
|
+
this.fingers.sort((a, b) => b - a);
|
|
4720
4699
|
}
|
|
4721
|
-
// Sort array back into descending order. This means if finger '1' were to lift after finger '0', we would ensure that 0 will be the first index to pop
|
|
4722
|
-
this.fingers.sort((a, b) => b - a);
|
|
4723
4700
|
this.fingerIds.delete(touch.identifier);
|
|
4724
4701
|
};
|
|
4725
4702
|
const emitTouchData = (type, touches) => {
|
|
4726
4703
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
4727
4704
|
for (let t = 0; t < touches.length; t++) {
|
|
4728
4705
|
let byte = 2;
|
|
4729
|
-
const numTouches = 1;
|
|
4706
|
+
const numTouches = 1;
|
|
4730
4707
|
const touch = touches[t];
|
|
4708
|
+
const finger = this.fingerIds.get(touch.identifier);
|
|
4709
|
+
// Skip unknown identifiers (e.g., stale touch on a fast retap) — sending
|
|
4710
|
+
// 0 here would alias finger '0' and corrupt UE's touch state.
|
|
4711
|
+
if (finger === undefined) {
|
|
4712
|
+
continue;
|
|
4713
|
+
}
|
|
4731
4714
|
const { x, y } = this.getTouchPositionWithOffset(touch);
|
|
4732
|
-
Logger.info(`F${
|
|
4733
|
-
const
|
|
4715
|
+
Logger.info(`F${finger}=(${x}, ${y})`);
|
|
4716
|
+
const coord = this.normalizeAndQuantizeUnsigned(x, y);
|
|
4734
4717
|
const data = new DataView(new ArrayBuffer(9));
|
|
4735
4718
|
data.setUint8(0, type);
|
|
4736
4719
|
data.setUint8(1, numTouches);
|
|
4737
|
-
data.setUint16(byte,
|
|
4720
|
+
data.setUint16(byte, coord.x, true);
|
|
4738
4721
|
byte += 2;
|
|
4739
|
-
data.setUint16(byte,
|
|
4722
|
+
data.setUint16(byte, coord.y, true);
|
|
4740
4723
|
byte += 2;
|
|
4741
|
-
|
|
4742
|
-
if (finger) {
|
|
4743
|
-
data.setUint8(byte, finger);
|
|
4744
|
-
}
|
|
4724
|
+
data.setUint8(byte, finger);
|
|
4745
4725
|
byte += 1;
|
|
4746
|
-
// force is
|
|
4747
|
-
// iOs bug: Forced to be always 0.5 on all devices as we do not use forced touch any way;/
|
|
4726
|
+
// iOS bug: force is locked to 0.5 since forced touch is unused.
|
|
4748
4727
|
data.setUint8(byte, this.maxByteValue * 0.5);
|
|
4749
4728
|
byte += 1;
|
|
4750
|
-
data.setUint8(byte,
|
|
4729
|
+
data.setUint8(byte, coord.inRange ? 1 : 0);
|
|
4751
4730
|
byte += 1;
|
|
4752
4731
|
this.sendInputData(data.buffer);
|
|
4753
4732
|
}
|
|
@@ -4757,7 +4736,6 @@ class InputService extends SubService {
|
|
|
4757
4736
|
.subscribe((e) => {
|
|
4758
4737
|
this.focusWindow();
|
|
4759
4738
|
const touches = e.changedTouches;
|
|
4760
|
-
// Assign a unique identifier to each touch.
|
|
4761
4739
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
4762
4740
|
for (let t = 0; t < touches.length; t++) {
|
|
4763
4741
|
rememberTouch(touches[t]);
|
|
@@ -4772,7 +4750,6 @@ class InputService extends SubService {
|
|
|
4772
4750
|
const touches = e.changedTouches;
|
|
4773
4751
|
Logger.info('touch end');
|
|
4774
4752
|
emitTouchData(MessageType.TouchEnd, touches);
|
|
4775
|
-
// Re-cycle unique identifiers previously assigned to each touch.
|
|
4776
4753
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
4777
4754
|
for (let t = 0; t < touches.length; t++) {
|
|
4778
4755
|
forgetTouch(touches[t]);
|
|
@@ -4790,45 +4767,41 @@ class InputService extends SubService {
|
|
|
4790
4767
|
window.focus();
|
|
4791
4768
|
}
|
|
4792
4769
|
emitMouseMove(x, y, deltaX, deltaY) {
|
|
4793
|
-
const
|
|
4794
|
-
this.lastNormalizedCoordinates = normalizeCoordinates;
|
|
4770
|
+
const coord = this.normalizeAndQuantizeUnsigned(x, y);
|
|
4795
4771
|
const delta = this.normalizeAndQuantizeSigned(deltaX, deltaY);
|
|
4796
4772
|
const data = new DataView(new ArrayBuffer(9));
|
|
4797
4773
|
data.setUint8(0, MessageType.MouseMove);
|
|
4798
|
-
data.setUint16(1,
|
|
4799
|
-
data.setUint16(3,
|
|
4774
|
+
data.setUint16(1, coord.x, true);
|
|
4775
|
+
data.setUint16(3, coord.y, true);
|
|
4800
4776
|
data.setInt16(5, delta.x, true);
|
|
4801
4777
|
data.setInt16(7, delta.y, true);
|
|
4802
4778
|
this.sendInputData(data.buffer);
|
|
4803
4779
|
}
|
|
4804
4780
|
emitMouseDown(button, x, y) {
|
|
4805
|
-
const
|
|
4806
|
-
this.lastNormalizedCoordinates = normalizeCoordinates;
|
|
4781
|
+
const coord = this.normalizeAndQuantizeUnsigned(x, y);
|
|
4807
4782
|
const data = new DataView(new ArrayBuffer(6));
|
|
4808
4783
|
data.setUint8(0, MessageType.MouseDown);
|
|
4809
4784
|
data.setUint8(1, button);
|
|
4810
|
-
data.setUint16(2,
|
|
4811
|
-
data.setUint16(4,
|
|
4785
|
+
data.setUint16(2, coord.x, true);
|
|
4786
|
+
data.setUint16(4, coord.y, true);
|
|
4812
4787
|
this.sendInputData(data.buffer);
|
|
4813
4788
|
}
|
|
4814
4789
|
emitMouseUp(button, x, y) {
|
|
4815
|
-
const
|
|
4816
|
-
this.lastNormalizedCoordinates = normalizeCoordinates;
|
|
4790
|
+
const coord = this.normalizeAndQuantizeUnsigned(x, y);
|
|
4817
4791
|
const data = new DataView(new ArrayBuffer(6));
|
|
4818
4792
|
data.setUint8(0, MessageType.MouseUp);
|
|
4819
4793
|
data.setUint8(1, button);
|
|
4820
|
-
data.setUint16(2,
|
|
4821
|
-
data.setUint16(4,
|
|
4794
|
+
data.setUint16(2, coord.x, true);
|
|
4795
|
+
data.setUint16(4, coord.y, true);
|
|
4822
4796
|
this.sendInputData(data.buffer);
|
|
4823
4797
|
}
|
|
4824
4798
|
emitMouseWheel(delta, x, y) {
|
|
4825
|
-
const
|
|
4826
|
-
this.lastNormalizedCoordinates = normalizeCoordinates;
|
|
4799
|
+
const coord = this.normalizeAndQuantizeUnsigned(x, y);
|
|
4827
4800
|
const data = new DataView(new ArrayBuffer(7));
|
|
4828
4801
|
data.setUint8(0, MessageType.MouseWheel);
|
|
4829
4802
|
data.setInt16(1, delta, true);
|
|
4830
|
-
data.setUint16(3,
|
|
4831
|
-
data.setUint16(5,
|
|
4803
|
+
data.setUint16(3, coord.x, true);
|
|
4804
|
+
data.setUint16(5, coord.y, true);
|
|
4832
4805
|
this.sendInputData(data.buffer);
|
|
4833
4806
|
}
|
|
4834
4807
|
releaseMouseButtons(buttons, x, y) {
|
|
@@ -4848,7 +4821,6 @@ class InputService extends SubService {
|
|
|
4848
4821
|
this.emitMouseUp(MouseButton.FifthButton, x, y);
|
|
4849
4822
|
}
|
|
4850
4823
|
}
|
|
4851
|
-
// If the user has any mouse buttons pressed, then press them again.
|
|
4852
4824
|
pressMouseButtons(buttons, x, y) {
|
|
4853
4825
|
if (buttons & MouseButtonsMask.PrimaryButton) {
|
|
4854
4826
|
this.emitMouseDown(MouseButton.MainButton, x, y);
|
|
@@ -4892,7 +4864,7 @@ class InputService extends SubService {
|
|
|
4892
4864
|
fromEvent(htmlVideoElement, 'wheel')
|
|
4893
4865
|
.pipe(takeUntil$1(this.deactivatedVideoTrigger$))
|
|
4894
4866
|
.subscribe((e) => {
|
|
4895
|
-
this.emitMouseWheel(e.deltaY *
|
|
4867
|
+
this.emitMouseWheel(e.deltaY * WHEEL_DELTA_SCALE, e.offsetX, e.offsetY);
|
|
4896
4868
|
e.preventDefault();
|
|
4897
4869
|
});
|
|
4898
4870
|
}
|
|
@@ -4909,7 +4881,6 @@ class InputService extends SubService {
|
|
|
4909
4881
|
fromEvent(htmlVideoElement, 'mouseleave')
|
|
4910
4882
|
.pipe(takeUntil$1(this.deactivatedVideoTrigger$))
|
|
4911
4883
|
.subscribe((e) => {
|
|
4912
|
-
this.lastNormalizedCoordinates = null;
|
|
4913
4884
|
const data = new DataView(new ArrayBuffer(1));
|
|
4914
4885
|
data.setUint8(0, MessageType.MouseLeave);
|
|
4915
4886
|
this.sendInputData(data.buffer);
|
|
@@ -4918,8 +4889,8 @@ class InputService extends SubService {
|
|
|
4918
4889
|
}
|
|
4919
4890
|
// Browser keys do not have a charCode so we only need to test keyCode.
|
|
4920
4891
|
isKeyCodeBrowserKey(keyCode) {
|
|
4921
|
-
|
|
4922
|
-
|
|
4892
|
+
return ((keyCode >= FUNCTION_KEY_MIN && keyCode <= FUNCTION_KEY_MAX) ||
|
|
4893
|
+
keyCode === TAB_KEY_CODE);
|
|
4923
4894
|
}
|
|
4924
4895
|
getKeyCode(e) {
|
|
4925
4896
|
if (e.keyCode === SpecialKeyCodes.Shift && e.code === 'ShiftRight')
|
|
@@ -4932,9 +4903,7 @@ class InputService extends SubService {
|
|
|
4932
4903
|
return e.keyCode;
|
|
4933
4904
|
}
|
|
4934
4905
|
registerKeyboardEvents(availableKeys) {
|
|
4935
|
-
const availableKeyNames =
|
|
4936
|
-
return InvertedKeyMap[value];
|
|
4937
|
-
});
|
|
4906
|
+
const availableKeyNames = availableKeys.map((value) => InvertedKeyMap[value]);
|
|
4938
4907
|
const keyBlock = (event) => {
|
|
4939
4908
|
const code = event.code;
|
|
4940
4909
|
const isInputFieldFocused = event.target?.localName === 'input';
|
|
@@ -4949,7 +4918,6 @@ class InputService extends SubService {
|
|
|
4949
4918
|
}
|
|
4950
4919
|
startListenKeys(_filter) {
|
|
4951
4920
|
const filter = _filter ?? (() => false);
|
|
4952
|
-
// Use RxJS fromEvent with takeUntil to properly clean up subscriptions
|
|
4953
4921
|
fromEvent(document, 'keydown')
|
|
4954
4922
|
.pipe(takeUntil$1(this.deactivatedVideoTrigger$))
|
|
4955
4923
|
.subscribe((e) => {
|
|
@@ -4967,8 +4935,8 @@ class InputService extends SubService {
|
|
|
4967
4935
|
this.getKeyCode(e),
|
|
4968
4936
|
Number(e.repeat),
|
|
4969
4937
|
]).buffer);
|
|
4970
|
-
// Backspace
|
|
4971
|
-
// to
|
|
4938
|
+
// Backspace isn't a keypress in JS, but UE4 text-entry fields need it
|
|
4939
|
+
// to delete characters.
|
|
4972
4940
|
if (e.keyCode === SpecialKeyCodes.BackSpace) {
|
|
4973
4941
|
const data = new DataView(new ArrayBuffer(3));
|
|
4974
4942
|
data.setUint8(0, MessageType.KeyPress);
|